diff --git a/sys/dev/asr/asr.c b/sys/dev/asr/asr.c index 8909d0c086e2..1e4f1a618a41 100644 --- a/sys/dev/asr/asr.c +++ b/sys/dev/asr/asr.c @@ -1,3836 +1,3851 @@ /*- * Copyright (c) 1996-2000 Distributed Processing Technology Corporation * Copyright (c) 2000-2001 Adaptec Corporation * All rights reserved. * * TERMS AND CONDITIONS OF USE * * Redistribution and use in source form, with or without modification, are * permitted provided that redistributions of source code must retain the * above copyright notice, this list of conditions and the following disclaimer. * * This software is provided `as is' by Adaptec and any express or implied * warranties, including, but not limited to, the implied warranties of * merchantability and fitness for a particular purpose, are disclaimed. In no * event shall Adaptec be liable for any direct, indirect, incidental, special, * exemplary or consequential damages (including, but not limited to, * procurement of substitute goods or services; loss of use, data, or profits; * or business interruptions) however caused and on any theory of liability, * whether in contract, strict liability, or tort (including negligence or * otherwise) arising in any way out of the use of this driver software, even * if advised of the possibility of such damage. * * SCSI I2O host adapter driver * * V1.10 2004/05/05 scottl@freebsd.org * - Massive cleanup of the driver to remove dead code and * non-conformant style. * - Removed most i386-specific code to make it more portable. * - Converted to the bus_space API. * V1.08 2001/08/21 Mark_Salyzyn@adaptec.com * - The 2000S and 2005S do not initialize on some machines, * increased timeout to 255ms from 50ms for the StatusGet * command. * V1.07 2001/05/22 Mark_Salyzyn@adaptec.com * - I knew this one was too good to be true. The error return * on ioctl commands needs to be compared to CAM_REQ_CMP, not * to the bit masked status. * V1.06 2001/05/08 Mark_Salyzyn@adaptec.com * - The 2005S that was supported is affectionately called the * Conjoined BAR Firmware. In order to support RAID-5 in a * 16MB low-cost configuration, Firmware was forced to go * to a Split BAR Firmware. This requires a separate IOP and * Messaging base address. * V1.05 2001/04/25 Mark_Salyzyn@adaptec.com * - Handle support for 2005S Zero Channel RAID solution. * - System locked up if the Adapter locked up. Do not try * to send other commands if the resetIOP command fails. The * fail outstanding command discovery loop was flawed as the * removal of the command from the list prevented discovering * all the commands. * - Comment changes to clarify driver. * - SysInfo searched for an EATA SmartROM, not an I2O SmartROM. * - We do not use the AC_FOUND_DEV event because of I2O. * Removed asr_async. * V1.04 2000/09/22 Mark_Salyzyn@adaptec.com, msmith@freebsd.org, * lampa@fee.vutbr.cz and Scott_Long@adaptec.com. * - Removed support for PM1554, PM2554 and PM2654 in Mode-0 * mode as this is confused with competitor adapters in run * mode. * - critical locking needed in ASR_ccbAdd and ASR_ccbRemove * to prevent operating system panic. * - moved default major number to 154 from 97. * V1.03 2000/07/12 Mark_Salyzyn@adaptec.com * - The controller is not actually an ASR (Adaptec SCSI RAID) * series that is visible, it's more of an internal code name. * remove any visible references within reason for now. * - bus_ptr->LUN was not correctly zeroed when initially * allocated causing a possible panic of the operating system * during boot. * V1.02 2000/06/26 Mark_Salyzyn@adaptec.com * - Code always fails for ASR_getTid affecting performance. * - initiated a set of changes that resulted from a formal * code inspection by Mark_Salyzyn@adaptec.com, * George_Dake@adaptec.com, Jeff_Zeak@adaptec.com, * Martin_Wilson@adaptec.com and Vincent_Trandoan@adaptec.com. * Their findings were focussed on the LCT & TID handler, and * all resulting changes were to improve code readability, * consistency or have a positive effect on performance. * V1.01 2000/06/14 Mark_Salyzyn@adaptec.com * - Passthrough returned an incorrect error. * - Passthrough did not migrate the intrinsic scsi layer wakeup * on command completion. * - generate control device nodes using make_dev and delete_dev. * - Performance affected by TID caching reallocing. * - Made suggested changes by Justin_Gibbs@adaptec.com * - use splcam instead of splbio. * - use cam_imask instead of bio_imask. * - use u_int8_t instead of u_char. * - use u_int16_t instead of u_short. * - use u_int32_t instead of u_long where appropriate. * - use 64 bit context handler instead of 32 bit. * - create_ccb should only allocate the worst case * requirements for the driver since CAM may evolve * making union ccb much larger than needed here. * renamed create_ccb to asr_alloc_ccb. * - go nutz justifying all debug prints as macros * defined at the top and remove unsightly ifdefs. * - INLINE STATIC viewed as confusing. Historically * utilized to affect code performance and debug * issues in OS, Compiler or OEM specific situations. * V1.00 2000/05/31 Mark_Salyzyn@adaptec.com * - Ported from FreeBSD 2.2.X DPT I2O driver. * changed struct scsi_xfer to union ccb/struct ccb_hdr * changed variable name xs to ccb * changed struct scsi_link to struct cam_path * changed struct scsibus_data to struct cam_sim * stopped using fordriver for holding on to the TID * use proprietary packet creation instead of scsi_inquire * CAM layer sends synchronize commands. */ #include #include /* TRUE=1 and FALSE=0 defined here */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__i386__) #include "opt_asr.h" #include #if defined(ASR_COMPAT) #define ASR_IOCTL_COMPAT #endif /* ASR_COMPAT */ #endif #include #include #include #define osdSwap4(x) ((u_long)ntohl((u_long)(x))) #define KVTOPHYS(x) vtophys(x) #include #include #include #include #include #include __FBSDID("$FreeBSD$"); #define ASR_VERSION 1 #define ASR_REVISION '1' #define ASR_SUBREVISION '0' #define ASR_MONTH 5 #define ASR_DAY 5 #define ASR_YEAR (2004 - 1980) /* * Debug macros to reduce the unsightly ifdefs */ #if (defined(DEBUG_ASR) || defined(DEBUG_ASR_USR_CMD) || defined(DEBUG_ASR_CMD)) static __inline void debug_asr_message(PI2O_MESSAGE_FRAME message) { u_int32_t * pointer = (u_int32_t *)message; u_int32_t length = I2O_MESSAGE_FRAME_getMessageSize(message); u_int32_t counter = 0; while (length--) { printf("%08lx%c", (u_long)*(pointer++), (((++counter & 7) == 0) || (length == 0)) ? '\n' : ' '); } } #endif /* DEBUG_ASR || DEBUG_ASR_USR_CMD || DEBUG_ASR_CMD */ #ifdef DEBUG_ASR /* Breaks on none STDC based compilers :-( */ #define debug_asr_printf(fmt,args...) printf(fmt, ##args) #define debug_asr_dump_message(message) debug_asr_message(message) #define debug_asr_print_path(ccb) xpt_print_path(ccb->ccb_h.path); #else /* DEBUG_ASR */ #define debug_asr_printf(fmt,args...) #define debug_asr_dump_message(message) #define debug_asr_print_path(ccb) #endif /* DEBUG_ASR */ /* * If DEBUG_ASR_CMD is defined: * 0 - Display incoming SCSI commands * 1 - add in a quick character before queueing. * 2 - add in outgoing message frames. */ #if (defined(DEBUG_ASR_CMD)) #define debug_asr_cmd_printf(fmt,args...) printf(fmt,##args) static __inline void debug_asr_dump_ccb(union ccb *ccb) { u_int8_t *cp = (unsigned char *)&(ccb->csio.cdb_io); int len = ccb->csio.cdb_len; while (len) { debug_asr_cmd_printf (" %02x", *(cp++)); --len; } } #if (DEBUG_ASR_CMD > 0) #define debug_asr_cmd1_printf debug_asr_cmd_printf #else #define debug_asr_cmd1_printf(fmt,args...) #endif #if (DEBUG_ASR_CMD > 1) #define debug_asr_cmd2_printf debug_asr_cmd_printf #define debug_asr_cmd2_dump_message(message) debug_asr_message(message) #else #define debug_asr_cmd2_printf(fmt,args...) #define debug_asr_cmd2_dump_message(message) #endif #else /* DEBUG_ASR_CMD */ #define debug_asr_cmd_printf(fmt,args...) #define debug_asr_dump_ccb(ccb) #define debug_asr_cmd1_printf(fmt,args...) #define debug_asr_cmd2_printf(fmt,args...) #define debug_asr_cmd2_dump_message(message) #endif /* DEBUG_ASR_CMD */ #if (defined(DEBUG_ASR_USR_CMD)) #define debug_usr_cmd_printf(fmt,args...) printf(fmt,##args) #define debug_usr_cmd_dump_message(message) debug_usr_message(message) #else /* DEBUG_ASR_USR_CMD */ #define debug_usr_cmd_printf(fmt,args...) #define debug_usr_cmd_dump_message(message) #endif /* DEBUG_ASR_USR_CMD */ #ifdef ASR_IOCTL_COMPAT #define dsDescription_size 46 /* Snug as a bug in a rug */ #endif /* ASR_IOCTL_COMPAT */ #include "dev/asr/dptsig.h" static dpt_sig_S ASR_sig = { { 'd', 'P', 't', 'S', 'i', 'G'}, SIG_VERSION, PROC_INTEL, PROC_386 | PROC_486 | PROC_PENTIUM | PROC_SEXIUM, FT_HBADRVR, 0, OEM_DPT, OS_FREE_BSD, CAP_ABOVE16MB, DEV_ALL, ADF_ALL_SC5, 0, 0, ASR_VERSION, ASR_REVISION, ASR_SUBREVISION, ASR_MONTH, ASR_DAY, ASR_YEAR, /* 01234567890123456789012345678901234567890123456789 < 50 chars */ "Adaptec FreeBSD 4.0.0 Unix SCSI I2O HBA Driver" /* ^^^^^ asr_attach alters these to match OS */ }; /* Configuration Definitions */ #define SG_SIZE 58 /* Scatter Gather list Size */ #define MAX_TARGET_ID 126 /* Maximum Target ID supported */ #define MAX_LUN 255 /* Maximum LUN Supported */ #define MAX_CHANNEL 7 /* Maximum Channel # Supported by driver */ #define MAX_INBOUND 2000 /* Max CCBs, Also Max Queue Size */ #define MAX_OUTBOUND 256 /* Maximum outbound frames/adapter */ #define MAX_INBOUND_SIZE 512 /* Maximum inbound frame size */ #define MAX_MAP 4194304L /* Maximum mapping size of IOP */ /* Also serves as the minimum map for */ /* the 2005S zero channel RAID product */ /* I2O register set */ #define I2O_REG_STATUS 0x30 #define I2O_REG_MASK 0x34 #define I2O_REG_TOFIFO 0x40 #define I2O_REG_FROMFIFO 0x44 #define Mask_InterruptsDisabled 0x08 /* * A MIX of performance and space considerations for TID lookups */ typedef u_int16_t tid_t; typedef struct { u_int32_t size; /* up to MAX_LUN */ tid_t TID[1]; } lun2tid_t; typedef struct { u_int32_t size; /* up to MAX_TARGET */ lun2tid_t * LUN[1]; } target2lun_t; /* * To ensure that we only allocate and use the worst case ccb here, lets * make our own local ccb union. If asr_alloc_ccb is utilized for another * ccb type, ensure that you add the additional structures into our local * ccb union. To ensure strict type checking, we will utilize the local * ccb definition wherever possible. */ union asr_ccb { struct ccb_hdr ccb_h; /* For convenience */ struct ccb_scsiio csio; struct ccb_setasync csa; }; struct Asr_status_mem { I2O_EXEC_STATUS_GET_REPLY status; U32 rstatus; }; /************************************************************************** ** ASR Host Adapter structure - One Structure For Each Host Adapter That ** ** Is Configured Into The System. The Structure Supplies Configuration ** ** Information, Status Info, Queue Info And An Active CCB List Pointer. ** ***************************************************************************/ typedef struct Asr_softc { device_t ha_dev; u_int16_t ha_irq; u_long ha_Base; /* base port for each board */ bus_size_t ha_blinkLED; bus_space_handle_t ha_i2o_bhandle; bus_space_tag_t ha_i2o_btag; bus_space_handle_t ha_frame_bhandle; bus_space_tag_t ha_frame_btag; I2O_IOP_ENTRY ha_SystemTable; LIST_HEAD(,ccb_hdr) ha_ccb; /* ccbs in use */ bus_dma_tag_t ha_parent_dmat; bus_dma_tag_t ha_statusmem_dmat; bus_dmamap_t ha_statusmem_dmamap; struct Asr_status_mem * ha_statusmem; u_int32_t ha_rstatus_phys; u_int32_t ha_status_phys; struct cam_path * ha_path[MAX_CHANNEL+1]; struct cam_sim * ha_sim[MAX_CHANNEL+1]; struct resource * ha_mem_res; struct resource * ha_mes_res; struct resource * ha_irq_res; void * ha_intr; PI2O_LCT ha_LCT; /* Complete list of devices */ #define le_type IdentityTag[0] #define I2O_BSA 0x20 #define I2O_FCA 0x40 #define I2O_SCSI 0x00 #define I2O_PORT 0x80 #define I2O_UNKNOWN 0x7F #define le_bus IdentityTag[1] #define le_target IdentityTag[2] #define le_lun IdentityTag[3] target2lun_t * ha_targets[MAX_CHANNEL+1]; PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME ha_Msgs; u_long ha_Msgs_Phys; u_int8_t ha_in_reset; #define HA_OPERATIONAL 0 #define HA_IN_RESET 1 #define HA_OFF_LINE 2 #define HA_OFF_LINE_RECOVERY 3 /* Configuration information */ /* The target id maximums we take */ u_int8_t ha_MaxBus; /* Maximum bus */ u_int8_t ha_MaxId; /* Maximum target ID */ u_int8_t ha_MaxLun; /* Maximum target LUN */ u_int8_t ha_SgSize; /* Max SG elements */ u_int8_t ha_pciBusNum; u_int8_t ha_pciDeviceNum; u_int8_t ha_adapter_target[MAX_CHANNEL+1]; u_int16_t ha_QueueSize; /* Max outstanding commands */ u_int16_t ha_Msgs_Count; /* Links into other parents and HBAs */ STAILQ_ENTRY(Asr_softc) ha_next; /* HBA list */ struct cdev *ha_devt; } Asr_softc_t; static STAILQ_HEAD(, Asr_softc) Asr_softc_list = STAILQ_HEAD_INITIALIZER(Asr_softc_list); +static __inline void +set_ccb_timeout_ch(union asr_ccb *ccb, struct callout_handle ch) +{ + ccb->ccb_h.sim_priv.entries[0].ptr = ch.callout; +} + +static __inline struct callout_handle +get_ccb_timeout_ch(union asr_ccb *ccb) +{ + struct callout_handle ch; + + ch.callout = ccb->ccb_h.sim_priv.entries[0].ptr; + return ch; +} + /* * Prototypes of the routines we have in this object. */ /* I2O HDM interface */ static int asr_probe(device_t dev); static int asr_attach(device_t dev); static int asr_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td); static int asr_open(struct cdev *dev, int32_t flags, int32_t ifmt, struct thread *td); static int asr_close(struct cdev *dev, int flags, int ifmt, struct thread *td); static int asr_intr(Asr_softc_t *sc); static void asr_timeout(void *arg); static int ASR_init(Asr_softc_t *sc); static int ASR_acquireLct(Asr_softc_t *sc); static int ASR_acquireHrt(Asr_softc_t *sc); static void asr_action(struct cam_sim *sim, union ccb *ccb); static void asr_poll(struct cam_sim *sim); static int ASR_queue(Asr_softc_t *sc, PI2O_MESSAGE_FRAME Message); /* * Here is the auto-probe structure used to nest our tests appropriately * during the startup phase of the operating system. */ static device_method_t asr_methods[] = { DEVMETHOD(device_probe, asr_probe), DEVMETHOD(device_attach, asr_attach), { 0, 0 } }; static driver_t asr_driver = { "asr", asr_methods, sizeof(Asr_softc_t) }; static devclass_t asr_devclass; DRIVER_MODULE(asr, pci, asr_driver, asr_devclass, 0, 0); MODULE_DEPEND(asr, pci, 1, 1, 1); MODULE_DEPEND(asr, cam, 1, 1, 1); /* * devsw for asr hba driver * * only ioctl is used. the sd driver provides all other access. */ static struct cdevsw asr_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = asr_open, .d_close = asr_close, .d_ioctl = asr_ioctl, .d_name = "asr", }; /* I2O support routines */ static __inline u_int32_t asr_get_FromFIFO(Asr_softc_t *sc) { return (bus_space_read_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_FROMFIFO)); } static __inline u_int32_t asr_get_ToFIFO(Asr_softc_t *sc) { return (bus_space_read_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_TOFIFO)); } static __inline u_int32_t asr_get_intr(Asr_softc_t *sc) { return (bus_space_read_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_MASK)); } static __inline u_int32_t asr_get_status(Asr_softc_t *sc) { return (bus_space_read_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_STATUS)); } static __inline void asr_set_FromFIFO(Asr_softc_t *sc, u_int32_t val) { bus_space_write_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_FROMFIFO, val); } static __inline void asr_set_ToFIFO(Asr_softc_t *sc, u_int32_t val) { bus_space_write_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_TOFIFO, val); } static __inline void asr_set_intr(Asr_softc_t *sc, u_int32_t val) { bus_space_write_4(sc->ha_i2o_btag, sc->ha_i2o_bhandle, I2O_REG_MASK, val); } static __inline void asr_set_frame(Asr_softc_t *sc, void *frame, u_int32_t offset, int len) { bus_space_write_region_4(sc->ha_frame_btag, sc->ha_frame_bhandle, offset, (u_int32_t *)frame, len); } /* * Fill message with default. */ static PI2O_MESSAGE_FRAME ASR_fillMessage(void *Message, u_int16_t size) { PI2O_MESSAGE_FRAME Message_Ptr; Message_Ptr = (I2O_MESSAGE_FRAME *)Message; bzero(Message_Ptr, size); I2O_MESSAGE_FRAME_setVersionOffset(Message_Ptr, I2O_VERSION_11); I2O_MESSAGE_FRAME_setMessageSize(Message_Ptr, (size + sizeof(U32) - 1) >> 2); I2O_MESSAGE_FRAME_setInitiatorAddress (Message_Ptr, 1); KASSERT(Message_Ptr != NULL, ("Message_Ptr == NULL")); return (Message_Ptr); } /* ASR_fillMessage */ #define EMPTY_QUEUE (0xffffffff) static __inline U32 ASR_getMessage(Asr_softc_t *sc) { U32 MessageOffset; MessageOffset = asr_get_ToFIFO(sc); if (MessageOffset == EMPTY_QUEUE) MessageOffset = asr_get_ToFIFO(sc); return (MessageOffset); } /* ASR_getMessage */ /* Issue a polled command */ static U32 ASR_initiateCp(Asr_softc_t *sc, PI2O_MESSAGE_FRAME Message) { U32 Mask = 0xffffffff; U32 MessageOffset; u_int Delay = 1500; /* * ASR_initiateCp is only used for synchronous commands and will * be made more resiliant to adapter delays since commands like * resetIOP can cause the adapter to be deaf for a little time. */ while (((MessageOffset = ASR_getMessage(sc)) == EMPTY_QUEUE) && (--Delay != 0)) { DELAY (10000); } if (MessageOffset != EMPTY_QUEUE) { asr_set_frame(sc, Message, MessageOffset, I2O_MESSAGE_FRAME_getMessageSize(Message)); /* * Disable the Interrupts */ Mask = asr_get_intr(sc); asr_set_intr(sc, Mask | Mask_InterruptsDisabled); asr_set_ToFIFO(sc, MessageOffset); } return (Mask); } /* ASR_initiateCp */ /* * Reset the adapter. */ static U32 ASR_resetIOP(Asr_softc_t *sc) { I2O_EXEC_IOP_RESET_MESSAGE Message; PI2O_EXEC_IOP_RESET_MESSAGE Message_Ptr; U32 * Reply_Ptr; U32 Old; /* * Build up our copy of the Message. */ Message_Ptr = (PI2O_EXEC_IOP_RESET_MESSAGE)ASR_fillMessage(&Message, sizeof(I2O_EXEC_IOP_RESET_MESSAGE)); I2O_EXEC_IOP_RESET_MESSAGE_setFunction(Message_Ptr, I2O_EXEC_IOP_RESET); /* * Reset the Reply Status */ Reply_Ptr = &sc->ha_statusmem->rstatus; *Reply_Ptr = 0; I2O_EXEC_IOP_RESET_MESSAGE_setStatusWordLowAddress(Message_Ptr, sc->ha_rstatus_phys); /* * Send the Message out */ if ((Old = ASR_initiateCp(sc, (PI2O_MESSAGE_FRAME)Message_Ptr)) != 0xffffffff) { /* * Wait for a response (Poll), timeouts are dangerous if * the card is truly responsive. We assume response in 2s. */ u_int8_t Delay = 200; while ((*Reply_Ptr == 0) && (--Delay != 0)) { DELAY (10000); } /* * Re-enable the interrupts. */ asr_set_intr(sc, Old); KASSERT(*Reply_Ptr != 0, ("*Reply_Ptr == 0")); return(*Reply_Ptr); } KASSERT(Old != 0xffffffff, ("Old == -1")); return (0); } /* ASR_resetIOP */ /* * Get the curent state of the adapter */ static PI2O_EXEC_STATUS_GET_REPLY ASR_getStatus(Asr_softc_t *sc) { I2O_EXEC_STATUS_GET_MESSAGE Message; PI2O_EXEC_STATUS_GET_MESSAGE Message_Ptr; PI2O_EXEC_STATUS_GET_REPLY buffer; U32 Old; /* * Build up our copy of the Message. */ Message_Ptr = (PI2O_EXEC_STATUS_GET_MESSAGE)ASR_fillMessage(&Message, sizeof(I2O_EXEC_STATUS_GET_MESSAGE)); I2O_EXEC_STATUS_GET_MESSAGE_setFunction(Message_Ptr, I2O_EXEC_STATUS_GET); I2O_EXEC_STATUS_GET_MESSAGE_setReplyBufferAddressLow(Message_Ptr, sc->ha_status_phys); /* This one is a Byte Count */ I2O_EXEC_STATUS_GET_MESSAGE_setReplyBufferLength(Message_Ptr, sizeof(I2O_EXEC_STATUS_GET_REPLY)); /* * Reset the Reply Status */ buffer = &sc->ha_statusmem->status; bzero(buffer, sizeof(I2O_EXEC_STATUS_GET_REPLY)); /* * Send the Message out */ if ((Old = ASR_initiateCp(sc, (PI2O_MESSAGE_FRAME)Message_Ptr)) != 0xffffffff) { /* * Wait for a response (Poll), timeouts are dangerous if * the card is truly responsive. We assume response in 50ms. */ u_int8_t Delay = 255; while (*((U8 * volatile)&(buffer->SyncByte)) == 0) { if (--Delay == 0) { buffer = NULL; break; } DELAY (1000); } /* * Re-enable the interrupts. */ asr_set_intr(sc, Old); return (buffer); } return (NULL); } /* ASR_getStatus */ /* * Check if the device is a SCSI I2O HBA, and add it to the list. */ /* * Probe for ASR controller. If we find it, we will use it. * virtual adapters. */ static int asr_probe(device_t dev) { u_int32_t id; id = (pci_get_device(dev) << 16) | pci_get_vendor(dev); if ((id == 0xA5011044) || (id == 0xA5111044)) { device_set_desc(dev, "Adaptec Caching SCSI RAID"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } /* asr_probe */ static __inline union asr_ccb * asr_alloc_ccb(Asr_softc_t *sc) { union asr_ccb *new_ccb; if ((new_ccb = (union asr_ccb *)malloc(sizeof(*new_ccb), M_DEVBUF, M_WAITOK | M_ZERO)) != NULL) { new_ccb->ccb_h.pinfo.priority = 1; new_ccb->ccb_h.pinfo.index = CAM_UNQUEUED_INDEX; new_ccb->ccb_h.spriv_ptr0 = sc; } return (new_ccb); } /* asr_alloc_ccb */ static __inline void asr_free_ccb(union asr_ccb *free_ccb) { free(free_ccb, M_DEVBUF); } /* asr_free_ccb */ /* * Print inquiry data `carefully' */ static void ASR_prstring(u_int8_t *s, int len) { while ((--len >= 0) && (*s) && (*s != ' ') && (*s != '-')) { printf ("%c", *(s++)); } } /* ASR_prstring */ /* * Send a message synchronously and without Interrupt to a ccb. */ static int ASR_queue_s(union asr_ccb *ccb, PI2O_MESSAGE_FRAME Message) { int s; U32 Mask; Asr_softc_t *sc = (Asr_softc_t *)(ccb->ccb_h.spriv_ptr0); /* * We do not need any (optional byteswapping) method access to * the Initiator context field. */ I2O_MESSAGE_FRAME_setInitiatorContext64(Message, (long)ccb); /* Prevent interrupt service */ s = splcam (); Mask = asr_get_intr(sc); asr_set_intr(sc, Mask | Mask_InterruptsDisabled); if (ASR_queue(sc, Message) == EMPTY_QUEUE) { ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_REQUEUE_REQ; } /* * Wait for this board to report a finished instruction. */ while ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) { (void)asr_intr (sc); } /* Re-enable Interrupts */ asr_set_intr(sc, Mask); splx(s); return (ccb->ccb_h.status); } /* ASR_queue_s */ /* * Send a message synchronously to an Asr_softc_t. */ static int ASR_queue_c(Asr_softc_t *sc, PI2O_MESSAGE_FRAME Message) { union asr_ccb *ccb; int status; if ((ccb = asr_alloc_ccb (sc)) == NULL) { return (CAM_REQUEUE_REQ); } status = ASR_queue_s (ccb, Message); asr_free_ccb(ccb); return (status); } /* ASR_queue_c */ /* * Add the specified ccb to the active queue */ static __inline void ASR_ccbAdd(Asr_softc_t *sc, union asr_ccb *ccb) { int s; s = splcam(); LIST_INSERT_HEAD(&(sc->ha_ccb), &(ccb->ccb_h), sim_links.le); if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) { if (ccb->ccb_h.timeout == CAM_TIME_DEFAULT) { /* * RAID systems can take considerable time to * complete some commands given the large cache * flashes switching from write back to write thru. */ ccb->ccb_h.timeout = 6 * 60 * 1000; } - ccb->ccb_h.timeout_ch = timeout(asr_timeout, (caddr_t)ccb, - (ccb->ccb_h.timeout * hz) / 1000); + set_ccb_timeout_ch(ccb, timeout(asr_timeout, (caddr_t)ccb, + (ccb->ccb_h.timeout * hz) / 1000)); } splx(s); } /* ASR_ccbAdd */ /* * Remove the specified ccb from the active queue. */ static __inline void ASR_ccbRemove(Asr_softc_t *sc, union asr_ccb *ccb) { int s; s = splcam(); - untimeout(asr_timeout, (caddr_t)ccb, ccb->ccb_h.timeout_ch); + untimeout(asr_timeout, (caddr_t)ccb, get_ccb_timeout_ch(ccb)); LIST_REMOVE(&(ccb->ccb_h), sim_links.le); splx(s); } /* ASR_ccbRemove */ /* * Fail all the active commands, so they get re-issued by the operating * system. */ static void ASR_failActiveCommands(Asr_softc_t *sc) { struct ccb_hdr *ccb; int s; s = splcam(); /* * We do not need to inform the CAM layer that we had a bus * reset since we manage it on our own, this also prevents the * SCSI_DELAY settling that would be required on other systems. * The `SCSI_DELAY' has already been handled by the card via the * acquisition of the LCT table while we are at CAM priority level. * for (int bus = 0; bus <= sc->ha_MaxBus; ++bus) { * xpt_async (AC_BUS_RESET, sc->ha_path[bus], NULL); * } */ while ((ccb = LIST_FIRST(&(sc->ha_ccb))) != NULL) { ASR_ccbRemove (sc, (union asr_ccb *)ccb); ccb->status &= ~CAM_STATUS_MASK; ccb->status |= CAM_REQUEUE_REQ; /* Nothing Transfered */ ((struct ccb_scsiio *)ccb)->resid = ((struct ccb_scsiio *)ccb)->dxfer_len; if (ccb->path) { xpt_done ((union ccb *)ccb); } else { wakeup (ccb); } } splx(s); } /* ASR_failActiveCommands */ /* * The following command causes the HBA to reset the specific bus */ static void ASR_resetBus(Asr_softc_t *sc, int bus) { I2O_HBA_BUS_RESET_MESSAGE Message; I2O_HBA_BUS_RESET_MESSAGE *Message_Ptr; PI2O_LCT_ENTRY Device; Message_Ptr = (I2O_HBA_BUS_RESET_MESSAGE *)ASR_fillMessage(&Message, sizeof(I2O_HBA_BUS_RESET_MESSAGE)); I2O_MESSAGE_FRAME_setFunction(&Message_Ptr->StdMessageFrame, I2O_HBA_BUS_RESET); for (Device = sc->ha_LCT->LCTEntry; Device < (PI2O_LCT_ENTRY) (((U32 *)sc->ha_LCT)+I2O_LCT_getTableSize(sc->ha_LCT)); ++Device) { if (((Device->le_type & I2O_PORT) != 0) && (Device->le_bus == bus)) { I2O_MESSAGE_FRAME_setTargetAddress( &Message_Ptr->StdMessageFrame, I2O_LCT_ENTRY_getLocalTID(Device)); /* Asynchronous command, with no expectations */ (void)ASR_queue(sc, (PI2O_MESSAGE_FRAME)Message_Ptr); break; } } } /* ASR_resetBus */ static __inline int ASR_getBlinkLedCode(Asr_softc_t *sc) { U8 blink; if (sc == NULL) return (0); blink = bus_space_read_1(sc->ha_frame_btag, sc->ha_frame_bhandle, sc->ha_blinkLED + 1); if (blink != 0xBC) return (0); blink = bus_space_read_1(sc->ha_frame_btag, sc->ha_frame_bhandle, sc->ha_blinkLED); return (blink); } /* ASR_getBlinkCode */ /* * Determine the address of an TID lookup. Must be done at high priority * since the address can be changed by other threads of execution. * * Returns NULL pointer if not indexible (but will attempt to generate * an index if `new_entry' flag is set to TRUE). * * All addressible entries are to be guaranteed zero if never initialized. */ static tid_t * ASR_getTidAddress(Asr_softc_t *sc, int bus, int target, int lun, int new_entry) { target2lun_t *bus_ptr; lun2tid_t *target_ptr; unsigned new_size; /* * Validity checking of incoming parameters. More of a bound * expansion limit than an issue with the code dealing with the * values. * * sc must be valid before it gets here, so that check could be * dropped if speed a critical issue. */ if ((sc == NULL) || (bus > MAX_CHANNEL) || (target > sc->ha_MaxId) || (lun > sc->ha_MaxLun)) { debug_asr_printf("(%lx,%d,%d,%d) target out of range\n", (u_long)sc, bus, target, lun); return (NULL); } /* * See if there is an associated bus list. * * for performance, allocate in size of BUS_CHUNK chunks. * BUS_CHUNK must be a power of two. This is to reduce * fragmentation effects on the allocations. */ #define BUS_CHUNK 8 new_size = ((target + BUS_CHUNK - 1) & ~(BUS_CHUNK - 1)); if ((bus_ptr = sc->ha_targets[bus]) == NULL) { /* * Allocate a new structure? * Since one element in structure, the +1 * needed for size has been abstracted. */ if ((new_entry == FALSE) || ((sc->ha_targets[bus] = bus_ptr = (target2lun_t *)malloc ( sizeof(*bus_ptr) + (sizeof(bus_ptr->LUN) * new_size), M_TEMP, M_WAITOK | M_ZERO)) == NULL)) { debug_asr_printf("failed to allocate bus list\n"); return (NULL); } bus_ptr->size = new_size + 1; } else if (bus_ptr->size <= new_size) { target2lun_t * new_bus_ptr; /* * Reallocate a new structure? * Since one element in structure, the +1 * needed for size has been abstracted. */ if ((new_entry == FALSE) || ((new_bus_ptr = (target2lun_t *)malloc ( sizeof(*bus_ptr) + (sizeof(bus_ptr->LUN) * new_size), M_TEMP, M_WAITOK | M_ZERO)) == NULL)) { debug_asr_printf("failed to reallocate bus list\n"); return (NULL); } /* * Copy the whole thing, safer, simpler coding * and not really performance critical at this point. */ bcopy(bus_ptr, new_bus_ptr, sizeof(*bus_ptr) + (sizeof(bus_ptr->LUN) * (bus_ptr->size - 1))); sc->ha_targets[bus] = new_bus_ptr; free(bus_ptr, M_TEMP); bus_ptr = new_bus_ptr; bus_ptr->size = new_size + 1; } /* * We now have the bus list, lets get to the target list. * Since most systems have only *one* lun, we do not allocate * in chunks as above, here we allow one, then in chunk sizes. * TARGET_CHUNK must be a power of two. This is to reduce * fragmentation effects on the allocations. */ #define TARGET_CHUNK 8 if ((new_size = lun) != 0) { new_size = ((lun + TARGET_CHUNK - 1) & ~(TARGET_CHUNK - 1)); } if ((target_ptr = bus_ptr->LUN[target]) == NULL) { /* * Allocate a new structure? * Since one element in structure, the +1 * needed for size has been abstracted. */ if ((new_entry == FALSE) || ((bus_ptr->LUN[target] = target_ptr = (lun2tid_t *)malloc ( sizeof(*target_ptr) + (sizeof(target_ptr->TID) * new_size), M_TEMP, M_WAITOK | M_ZERO)) == NULL)) { debug_asr_printf("failed to allocate target list\n"); return (NULL); } target_ptr->size = new_size + 1; } else if (target_ptr->size <= new_size) { lun2tid_t * new_target_ptr; /* * Reallocate a new structure? * Since one element in structure, the +1 * needed for size has been abstracted. */ if ((new_entry == FALSE) || ((new_target_ptr = (lun2tid_t *)malloc ( sizeof(*target_ptr) + (sizeof(target_ptr->TID) * new_size), M_TEMP, M_WAITOK | M_ZERO)) == NULL)) { debug_asr_printf("failed to reallocate target list\n"); return (NULL); } /* * Copy the whole thing, safer, simpler coding * and not really performance critical at this point. */ bcopy(target_ptr, new_target_ptr, sizeof(*target_ptr) + (sizeof(target_ptr->TID) * (target_ptr->size - 1))); bus_ptr->LUN[target] = new_target_ptr; free(target_ptr, M_TEMP); target_ptr = new_target_ptr; target_ptr->size = new_size + 1; } /* * Now, acquire the TID address from the LUN indexed list. */ return (&(target_ptr->TID[lun])); } /* ASR_getTidAddress */ /* * Get a pre-existing TID relationship. * * If the TID was never set, return (tid_t)-1. * * should use mutex rather than spl. */ static __inline tid_t ASR_getTid(Asr_softc_t *sc, int bus, int target, int lun) { tid_t *tid_ptr; int s; tid_t retval; s = splcam(); if (((tid_ptr = ASR_getTidAddress(sc, bus, target, lun, FALSE)) == NULL) /* (tid_t)0 or (tid_t)-1 indicate no TID */ || (*tid_ptr == (tid_t)0)) { splx(s); return ((tid_t)-1); } retval = *tid_ptr; splx(s); return (retval); } /* ASR_getTid */ /* * Set a TID relationship. * * If the TID was not set, return (tid_t)-1. * * should use mutex rather than spl. */ static __inline tid_t ASR_setTid(Asr_softc_t *sc, int bus, int target, int lun, tid_t TID) { tid_t *tid_ptr; int s; if (TID != (tid_t)-1) { if (TID == 0) { return ((tid_t)-1); } s = splcam(); if ((tid_ptr = ASR_getTidAddress(sc, bus, target, lun, TRUE)) == NULL) { splx(s); return ((tid_t)-1); } *tid_ptr = TID; splx(s); } return (TID); } /* ASR_setTid */ /*-------------------------------------------------------------------------*/ /* Function ASR_rescan */ /*-------------------------------------------------------------------------*/ /* The Parameters Passed To This Function Are : */ /* Asr_softc_t * : HBA miniport driver's adapter data storage. */ /* */ /* This Function Will rescan the adapter and resynchronize any data */ /* */ /* Return : 0 For OK, Error Code Otherwise */ /*-------------------------------------------------------------------------*/ static int ASR_rescan(Asr_softc_t *sc) { int bus; int error; /* * Re-acquire the LCT table and synchronize us to the adapter. */ if ((error = ASR_acquireLct(sc)) == 0) { error = ASR_acquireHrt(sc); } if (error != 0) { return error; } bus = sc->ha_MaxBus; /* Reset all existing cached TID lookups */ do { int target, event = 0; /* * Scan for all targets on this bus to see if they * got affected by the rescan. */ for (target = 0; target <= sc->ha_MaxId; ++target) { int lun; /* Stay away from the controller ID */ if (target == sc->ha_adapter_target[bus]) { continue; } for (lun = 0; lun <= sc->ha_MaxLun; ++lun) { PI2O_LCT_ENTRY Device; tid_t TID = (tid_t)-1; tid_t LastTID; /* * See if the cached TID changed. Search for * the device in our new LCT. */ for (Device = sc->ha_LCT->LCTEntry; Device < (PI2O_LCT_ENTRY)(((U32 *)sc->ha_LCT) + I2O_LCT_getTableSize(sc->ha_LCT)); ++Device) { if ((Device->le_type != I2O_UNKNOWN) && (Device->le_bus == bus) && (Device->le_target == target) && (Device->le_lun == lun) && (I2O_LCT_ENTRY_getUserTID(Device) == 0xFFF)) { TID = I2O_LCT_ENTRY_getLocalTID( Device); break; } } /* * Indicate to the OS that the label needs * to be recalculated, or that the specific * open device is no longer valid (Merde) * because the cached TID changed. */ LastTID = ASR_getTid (sc, bus, target, lun); if (LastTID != TID) { struct cam_path * path; if (xpt_create_path(&path, /*periph*/NULL, cam_sim_path(sc->ha_sim[bus]), target, lun) != CAM_REQ_CMP) { if (TID == (tid_t)-1) { event |= AC_LOST_DEVICE; } else { event |= AC_INQ_CHANGED | AC_GETDEV_CHANGED; } } else { if (TID == (tid_t)-1) { xpt_async( AC_LOST_DEVICE, path, NULL); } else if (LastTID == (tid_t)-1) { struct ccb_getdev ccb; xpt_setup_ccb( &(ccb.ccb_h), path, /*priority*/5); xpt_async( AC_FOUND_DEVICE, path, &ccb); } else { xpt_async( AC_INQ_CHANGED, path, NULL); xpt_async( AC_GETDEV_CHANGED, path, NULL); } } } /* * We have the option of clearing the * cached TID for it to be rescanned, or to * set it now even if the device never got * accessed. We chose the later since we * currently do not use the condition that * the TID ever got cached. */ ASR_setTid (sc, bus, target, lun, TID); } } /* * The xpt layer can not handle multiple events at the * same call. */ if (event & AC_LOST_DEVICE) { xpt_async(AC_LOST_DEVICE, sc->ha_path[bus], NULL); } if (event & AC_INQ_CHANGED) { xpt_async(AC_INQ_CHANGED, sc->ha_path[bus], NULL); } if (event & AC_GETDEV_CHANGED) { xpt_async(AC_GETDEV_CHANGED, sc->ha_path[bus], NULL); } } while (--bus >= 0); return (error); } /* ASR_rescan */ /*-------------------------------------------------------------------------*/ /* Function ASR_reset */ /*-------------------------------------------------------------------------*/ /* The Parameters Passed To This Function Are : */ /* Asr_softc_t * : HBA miniport driver's adapter data storage. */ /* */ /* This Function Will reset the adapter and resynchronize any data */ /* */ /* Return : None */ /*-------------------------------------------------------------------------*/ static int ASR_reset(Asr_softc_t *sc) { int s, retVal; s = splcam(); if ((sc->ha_in_reset == HA_IN_RESET) || (sc->ha_in_reset == HA_OFF_LINE_RECOVERY)) { splx (s); return (EBUSY); } /* * Promotes HA_OPERATIONAL to HA_IN_RESET, * or HA_OFF_LINE to HA_OFF_LINE_RECOVERY. */ ++(sc->ha_in_reset); if (ASR_resetIOP(sc) == 0) { debug_asr_printf ("ASR_resetIOP failed\n"); /* * We really need to take this card off-line, easier said * than make sense. Better to keep retrying for now since if a * UART cable is connected the blinkLEDs the adapter is now in * a hard state requiring action from the monitor commands to * the HBA to continue. For debugging waiting forever is a * good thing. In a production system, however, one may wish * to instead take the card off-line ... */ /* Wait Forever */ while (ASR_resetIOP(sc) == 0); } retVal = ASR_init (sc); splx (s); if (retVal != 0) { debug_asr_printf ("ASR_init failed\n"); sc->ha_in_reset = HA_OFF_LINE; return (ENXIO); } if (ASR_rescan (sc) != 0) { debug_asr_printf ("ASR_rescan failed\n"); } ASR_failActiveCommands (sc); if (sc->ha_in_reset == HA_OFF_LINE_RECOVERY) { printf ("asr%d: Brining adapter back on-line\n", sc->ha_path[0] ? cam_sim_unit(xpt_path_sim(sc->ha_path[0])) : 0); } sc->ha_in_reset = HA_OPERATIONAL; return (0); } /* ASR_reset */ /* * Device timeout handler. */ static void asr_timeout(void *arg) { union asr_ccb *ccb = (union asr_ccb *)arg; Asr_softc_t *sc = (Asr_softc_t *)(ccb->ccb_h.spriv_ptr0); int s; debug_asr_print_path(ccb); debug_asr_printf("timed out"); /* * Check if the adapter has locked up? */ if ((s = ASR_getBlinkLedCode(sc)) != 0) { /* Reset Adapter */ printf ("asr%d: Blink LED 0x%x resetting adapter\n", cam_sim_unit(xpt_path_sim(ccb->ccb_h.path)), s); if (ASR_reset (sc) == ENXIO) { /* Try again later */ - ccb->ccb_h.timeout_ch = timeout(asr_timeout, + set_ccb_timeout_ch(ccb, timeout(asr_timeout, (caddr_t)ccb, - (ccb->ccb_h.timeout * hz) / 1000); + (ccb->ccb_h.timeout * hz) / 1000)); } return; } /* * Abort does not function on the ASR card!!! Walking away from * the SCSI command is also *very* dangerous. A SCSI BUS reset is * our best bet, followed by a complete adapter reset if that fails. */ s = splcam(); /* Check if we already timed out once to raise the issue */ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_CMD_TIMEOUT) { debug_asr_printf (" AGAIN\nreinitializing adapter\n"); if (ASR_reset (sc) == ENXIO) { - ccb->ccb_h.timeout_ch = timeout(asr_timeout, + set_ccb_timeout_ch(ccb, timeout(asr_timeout, (caddr_t)ccb, - (ccb->ccb_h.timeout * hz) / 1000); + (ccb->ccb_h.timeout * hz) / 1000)); } splx(s); return; } debug_asr_printf ("\nresetting bus\n"); /* If the BUS reset does not take, then an adapter reset is next! */ ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_CMD_TIMEOUT; - ccb->ccb_h.timeout_ch = timeout(asr_timeout, (caddr_t)ccb, - (ccb->ccb_h.timeout * hz) / 1000); + set_ccb_timeout_ch(ccb, timeout(asr_timeout, (caddr_t)ccb, + (ccb->ccb_h.timeout * hz) / 1000)); ASR_resetBus (sc, cam_sim_bus(xpt_path_sim(ccb->ccb_h.path))); xpt_async (AC_BUS_RESET, ccb->ccb_h.path, NULL); splx(s); } /* asr_timeout */ /* * send a message asynchronously */ static int ASR_queue(Asr_softc_t *sc, PI2O_MESSAGE_FRAME Message) { U32 MessageOffset; union asr_ccb *ccb; debug_asr_printf("Host Command Dump:\n"); debug_asr_dump_message(Message); ccb = (union asr_ccb *)(long) I2O_MESSAGE_FRAME_getInitiatorContext64(Message); if ((MessageOffset = ASR_getMessage(sc)) != EMPTY_QUEUE) { asr_set_frame(sc, Message, MessageOffset, I2O_MESSAGE_FRAME_getMessageSize(Message)); if (ccb) { ASR_ccbAdd (sc, ccb); } /* Post the command */ asr_set_ToFIFO(sc, MessageOffset); } else { if (ASR_getBlinkLedCode(sc)) { /* * Unlikely we can do anything if we can't grab a * message frame :-(, but lets give it a try. */ (void)ASR_reset(sc); } } return (MessageOffset); } /* ASR_queue */ /* Simple Scatter Gather elements */ #define SG(SGL,Index,Flags,Buffer,Size) \ I2O_FLAGS_COUNT_setCount( \ &(((PI2O_SG_ELEMENT)(SGL))->u.Simple[Index].FlagsCount), \ Size); \ I2O_FLAGS_COUNT_setFlags( \ &(((PI2O_SG_ELEMENT)(SGL))->u.Simple[Index].FlagsCount), \ I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT | (Flags)); \ I2O_SGE_SIMPLE_ELEMENT_setPhysicalAddress( \ &(((PI2O_SG_ELEMENT)(SGL))->u.Simple[Index]), \ (Buffer == NULL) ? 0 : KVTOPHYS(Buffer)) /* * Retrieve Parameter Group. */ static void * ASR_getParams(Asr_softc_t *sc, tid_t TID, int Group, void *Buffer, unsigned BufferSize) { struct paramGetMessage { I2O_UTIL_PARAMS_GET_MESSAGE M; char F[sizeof(I2O_SGE_SIMPLE_ELEMENT)*2 - sizeof(I2O_SG_ELEMENT)]; struct Operations { I2O_PARAM_OPERATIONS_LIST_HEADER Header; I2O_PARAM_OPERATION_ALL_TEMPLATE Template[1]; } O; } Message; struct Operations *Operations_Ptr; I2O_UTIL_PARAMS_GET_MESSAGE *Message_Ptr; struct ParamBuffer { I2O_PARAM_RESULTS_LIST_HEADER Header; I2O_PARAM_READ_OPERATION_RESULT Read; char Info[1]; } *Buffer_Ptr; Message_Ptr = (I2O_UTIL_PARAMS_GET_MESSAGE *)ASR_fillMessage(&Message, sizeof(I2O_UTIL_PARAMS_GET_MESSAGE) + sizeof(I2O_SGE_SIMPLE_ELEMENT)*2 - sizeof(I2O_SG_ELEMENT)); Operations_Ptr = (struct Operations *)((char *)Message_Ptr + sizeof(I2O_UTIL_PARAMS_GET_MESSAGE) + sizeof(I2O_SGE_SIMPLE_ELEMENT)*2 - sizeof(I2O_SG_ELEMENT)); bzero(Operations_Ptr, sizeof(struct Operations)); I2O_PARAM_OPERATIONS_LIST_HEADER_setOperationCount( &(Operations_Ptr->Header), 1); I2O_PARAM_OPERATION_ALL_TEMPLATE_setOperation( &(Operations_Ptr->Template[0]), I2O_PARAMS_OPERATION_FIELD_GET); I2O_PARAM_OPERATION_ALL_TEMPLATE_setFieldCount( &(Operations_Ptr->Template[0]), 0xFFFF); I2O_PARAM_OPERATION_ALL_TEMPLATE_setGroupNumber( &(Operations_Ptr->Template[0]), Group); Buffer_Ptr = (struct ParamBuffer *)Buffer; bzero(Buffer_Ptr, BufferSize); I2O_MESSAGE_FRAME_setVersionOffset(&(Message_Ptr->StdMessageFrame), I2O_VERSION_11 + (((sizeof(I2O_UTIL_PARAMS_GET_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4)); I2O_MESSAGE_FRAME_setTargetAddress (&(Message_Ptr->StdMessageFrame), TID); I2O_MESSAGE_FRAME_setFunction (&(Message_Ptr->StdMessageFrame), I2O_UTIL_PARAMS_GET); /* * Set up the buffers as scatter gather elements. */ SG(&(Message_Ptr->SGL), 0, I2O_SGL_FLAGS_DIR | I2O_SGL_FLAGS_END_OF_BUFFER, Operations_Ptr, sizeof(struct Operations)); SG(&(Message_Ptr->SGL), 1, I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER, Buffer_Ptr, BufferSize); if ((ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr) == CAM_REQ_CMP) && (Buffer_Ptr->Header.ResultCount)) { return ((void *)(Buffer_Ptr->Info)); } return (NULL); } /* ASR_getParams */ /* * Acquire the LCT information. */ static int ASR_acquireLct(Asr_softc_t *sc) { PI2O_EXEC_LCT_NOTIFY_MESSAGE Message_Ptr; PI2O_SGE_SIMPLE_ELEMENT sg; int MessageSizeInBytes; caddr_t v; int len; I2O_LCT Table; PI2O_LCT_ENTRY Entry; /* * sc value assumed valid */ MessageSizeInBytes = sizeof(I2O_EXEC_LCT_NOTIFY_MESSAGE) - sizeof(I2O_SG_ELEMENT) + sizeof(I2O_SGE_SIMPLE_ELEMENT); if ((Message_Ptr = (PI2O_EXEC_LCT_NOTIFY_MESSAGE)malloc( MessageSizeInBytes, M_TEMP, M_WAITOK)) == NULL) { return (ENOMEM); } (void)ASR_fillMessage((void *)Message_Ptr, MessageSizeInBytes); I2O_MESSAGE_FRAME_setVersionOffset(&(Message_Ptr->StdMessageFrame), (I2O_VERSION_11 + (((sizeof(I2O_EXEC_LCT_NOTIFY_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4))); I2O_MESSAGE_FRAME_setFunction(&(Message_Ptr->StdMessageFrame), I2O_EXEC_LCT_NOTIFY); I2O_EXEC_LCT_NOTIFY_MESSAGE_setClassIdentifier(Message_Ptr, I2O_CLASS_MATCH_ANYCLASS); /* * Call the LCT table to determine the number of device entries * to reserve space for. */ SG(&(Message_Ptr->SGL), 0, I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER, &Table, sizeof(I2O_LCT)); /* * since this code is reused in several systems, code efficiency * is greater by using a shift operation rather than a divide by * sizeof(u_int32_t). */ I2O_LCT_setTableSize(&Table, (sizeof(I2O_LCT) - sizeof(I2O_LCT_ENTRY)) >> 2); (void)ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr); /* * Determine the size of the LCT table. */ if (sc->ha_LCT) { free(sc->ha_LCT, M_TEMP); } /* * malloc only generates contiguous memory when less than a * page is expected. We must break the request up into an SG list ... */ if (((len = (I2O_LCT_getTableSize(&Table) << 2)) <= (sizeof(I2O_LCT) - sizeof(I2O_LCT_ENTRY))) || (len > (128 * 1024))) { /* Arbitrary */ free(Message_Ptr, M_TEMP); return (EINVAL); } if ((sc->ha_LCT = (PI2O_LCT)malloc (len, M_TEMP, M_WAITOK)) == NULL) { free(Message_Ptr, M_TEMP); return (ENOMEM); } /* * since this code is reused in several systems, code efficiency * is greater by using a shift operation rather than a divide by * sizeof(u_int32_t). */ I2O_LCT_setTableSize(sc->ha_LCT, (sizeof(I2O_LCT) - sizeof(I2O_LCT_ENTRY)) >> 2); /* * Convert the access to the LCT table into a SG list. */ sg = Message_Ptr->SGL.u.Simple; v = (caddr_t)(sc->ha_LCT); for (;;) { int next, base, span; span = 0; next = base = KVTOPHYS(v); I2O_SGE_SIMPLE_ELEMENT_setPhysicalAddress(sg, base); /* How far can we go contiguously */ while ((len > 0) && (base == next)) { int size; next = trunc_page(base) + PAGE_SIZE; size = next - base; if (size > len) { size = len; } span += size; v += size; len -= size; base = KVTOPHYS(v); } /* Construct the Flags */ I2O_FLAGS_COUNT_setCount(&(sg->FlagsCount), span); { int rw = I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT; if (len <= 0) { rw = (I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT | I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER); } I2O_FLAGS_COUNT_setFlags(&(sg->FlagsCount), rw); } if (len <= 0) { break; } /* * Incrementing requires resizing of the packet. */ ++sg; MessageSizeInBytes += sizeof(*sg); I2O_MESSAGE_FRAME_setMessageSize( &(Message_Ptr->StdMessageFrame), I2O_MESSAGE_FRAME_getMessageSize( &(Message_Ptr->StdMessageFrame)) + (sizeof(*sg) / sizeof(U32))); { PI2O_EXEC_LCT_NOTIFY_MESSAGE NewMessage_Ptr; if ((NewMessage_Ptr = (PI2O_EXEC_LCT_NOTIFY_MESSAGE) malloc(MessageSizeInBytes, M_TEMP, M_WAITOK)) == NULL) { free(sc->ha_LCT, M_TEMP); sc->ha_LCT = NULL; free(Message_Ptr, M_TEMP); return (ENOMEM); } span = ((caddr_t)sg) - (caddr_t)Message_Ptr; bcopy(Message_Ptr, NewMessage_Ptr, span); free(Message_Ptr, M_TEMP); sg = (PI2O_SGE_SIMPLE_ELEMENT) (((caddr_t)NewMessage_Ptr) + span); Message_Ptr = NewMessage_Ptr; } } { int retval; retval = ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr); free(Message_Ptr, M_TEMP); if (retval != CAM_REQ_CMP) { return (ENODEV); } } /* If the LCT table grew, lets truncate accesses */ if (I2O_LCT_getTableSize(&Table) < I2O_LCT_getTableSize(sc->ha_LCT)) { I2O_LCT_setTableSize(sc->ha_LCT, I2O_LCT_getTableSize(&Table)); } for (Entry = sc->ha_LCT->LCTEntry; Entry < (PI2O_LCT_ENTRY) (((U32 *)sc->ha_LCT)+I2O_LCT_getTableSize(sc->ha_LCT)); ++Entry) { Entry->le_type = I2O_UNKNOWN; switch (I2O_CLASS_ID_getClass(&(Entry->ClassID))) { case I2O_CLASS_RANDOM_BLOCK_STORAGE: Entry->le_type = I2O_BSA; break; case I2O_CLASS_SCSI_PERIPHERAL: Entry->le_type = I2O_SCSI; break; case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL: Entry->le_type = I2O_FCA; break; case I2O_CLASS_BUS_ADAPTER_PORT: Entry->le_type = I2O_PORT | I2O_SCSI; /* FALLTHRU */ case I2O_CLASS_FIBRE_CHANNEL_PORT: if (I2O_CLASS_ID_getClass(&(Entry->ClassID)) == I2O_CLASS_FIBRE_CHANNEL_PORT) { Entry->le_type = I2O_PORT | I2O_FCA; } { struct ControllerInfo { I2O_PARAM_RESULTS_LIST_HEADER Header; I2O_PARAM_READ_OPERATION_RESULT Read; I2O_HBA_SCSI_CONTROLLER_INFO_SCALAR Info; } Buffer; PI2O_HBA_SCSI_CONTROLLER_INFO_SCALAR Info; Entry->le_bus = 0xff; Entry->le_target = 0xff; Entry->le_lun = 0xff; if ((Info = (PI2O_HBA_SCSI_CONTROLLER_INFO_SCALAR) ASR_getParams(sc, I2O_LCT_ENTRY_getLocalTID(Entry), I2O_HBA_SCSI_CONTROLLER_INFO_GROUP_NO, &Buffer, sizeof(struct ControllerInfo))) == NULL) { continue; } Entry->le_target = I2O_HBA_SCSI_CONTROLLER_INFO_SCALAR_getInitiatorID( Info); Entry->le_lun = 0; } /* FALLTHRU */ default: continue; } { struct DeviceInfo { I2O_PARAM_RESULTS_LIST_HEADER Header; I2O_PARAM_READ_OPERATION_RESULT Read; I2O_DPT_DEVICE_INFO_SCALAR Info; } Buffer; PI2O_DPT_DEVICE_INFO_SCALAR Info; Entry->le_bus = 0xff; Entry->le_target = 0xff; Entry->le_lun = 0xff; if ((Info = (PI2O_DPT_DEVICE_INFO_SCALAR) ASR_getParams(sc, I2O_LCT_ENTRY_getLocalTID(Entry), I2O_DPT_DEVICE_INFO_GROUP_NO, &Buffer, sizeof(struct DeviceInfo))) == NULL) { continue; } Entry->le_type |= I2O_DPT_DEVICE_INFO_SCALAR_getDeviceType(Info); Entry->le_bus = I2O_DPT_DEVICE_INFO_SCALAR_getBus(Info); if ((Entry->le_bus > sc->ha_MaxBus) && (Entry->le_bus <= MAX_CHANNEL)) { sc->ha_MaxBus = Entry->le_bus; } Entry->le_target = I2O_DPT_DEVICE_INFO_SCALAR_getIdentifier(Info); Entry->le_lun = I2O_DPT_DEVICE_INFO_SCALAR_getLunInfo(Info); } } /* * A zero return value indicates success. */ return (0); } /* ASR_acquireLct */ /* * Initialize a message frame. * We assume that the CDB has already been set up, so all we do here is * generate the Scatter Gather list. */ static PI2O_MESSAGE_FRAME ASR_init_message(union asr_ccb *ccb, PI2O_MESSAGE_FRAME Message) { PI2O_MESSAGE_FRAME Message_Ptr; PI2O_SGE_SIMPLE_ELEMENT sg; Asr_softc_t *sc = (Asr_softc_t *)(ccb->ccb_h.spriv_ptr0); vm_size_t size, len; caddr_t v; U32 MessageSize; int next, span, base, rw; int target = ccb->ccb_h.target_id; int lun = ccb->ccb_h.target_lun; int bus =cam_sim_bus(xpt_path_sim(ccb->ccb_h.path)); tid_t TID; /* We only need to zero out the PRIVATE_SCSI_SCB_EXECUTE_MESSAGE */ Message_Ptr = (I2O_MESSAGE_FRAME *)Message; bzero(Message_Ptr, (sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT))); if ((TID = ASR_getTid (sc, bus, target, lun)) == (tid_t)-1) { PI2O_LCT_ENTRY Device; TID = 0; for (Device = sc->ha_LCT->LCTEntry; Device < (PI2O_LCT_ENTRY) (((U32 *)sc->ha_LCT) + I2O_LCT_getTableSize(sc->ha_LCT)); ++Device) { if ((Device->le_type != I2O_UNKNOWN) && (Device->le_bus == bus) && (Device->le_target == target) && (Device->le_lun == lun) && (I2O_LCT_ENTRY_getUserTID(Device) == 0xFFF)) { TID = I2O_LCT_ENTRY_getLocalTID(Device); ASR_setTid(sc, Device->le_bus, Device->le_target, Device->le_lun, TID); break; } } } if (TID == (tid_t)0) { return (NULL); } I2O_MESSAGE_FRAME_setTargetAddress(Message_Ptr, TID); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setTID( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr, TID); I2O_MESSAGE_FRAME_setVersionOffset(Message_Ptr, I2O_VERSION_11 | (((sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4)); I2O_MESSAGE_FRAME_setMessageSize(Message_Ptr, (sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)); I2O_MESSAGE_FRAME_setInitiatorAddress (Message_Ptr, 1); I2O_MESSAGE_FRAME_setFunction(Message_Ptr, I2O_PRIVATE_MESSAGE); I2O_PRIVATE_MESSAGE_FRAME_setXFunctionCode ( (PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr, I2O_SCSI_SCB_EXEC); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setSCBFlags ( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr, I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER); /* * We do not need any (optional byteswapping) method access to * the Initiator & Transaction context field. */ I2O_MESSAGE_FRAME_setInitiatorContext64(Message, (long)ccb); I2O_PRIVATE_MESSAGE_FRAME_setOrganizationID( (PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr, DPT_ORGANIZATION_ID); /* * copy the cdb over */ PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setCDBLength( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr, ccb->csio.cdb_len); bcopy(&(ccb->csio.cdb_io), ((PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr)->CDB, ccb->csio.cdb_len); /* * Given a buffer describing a transfer, set up a scatter/gather map * in a ccb to map that SCSI transfer. */ rw = (ccb->ccb_h.flags & CAM_DIR_IN) ? 0 : I2O_SGL_FLAGS_DIR; PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setSCBFlags ( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr, (ccb->csio.dxfer_len) ? ((rw) ? (I2O_SCB_FLAG_XFER_TO_DEVICE | I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER) : (I2O_SCB_FLAG_XFER_FROM_DEVICE | I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER)) : (I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER)); /* * Given a transfer described by a `data', fill in the SG list. */ sg = &((PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr)->SGL.u.Simple[0]; len = ccb->csio.dxfer_len; v = ccb->csio.data_ptr; KASSERT(ccb->csio.dxfer_len >= 0, ("csio.dxfer_len < 0")); MessageSize = I2O_MESSAGE_FRAME_getMessageSize(Message_Ptr); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setByteCount( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr, len); while ((len > 0) && (sg < &((PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE) Message_Ptr)->SGL.u.Simple[SG_SIZE])) { span = 0; next = base = KVTOPHYS(v); I2O_SGE_SIMPLE_ELEMENT_setPhysicalAddress(sg, base); /* How far can we go contiguously */ while ((len > 0) && (base == next)) { next = trunc_page(base) + PAGE_SIZE; size = next - base; if (size > len) { size = len; } span += size; v += size; len -= size; base = KVTOPHYS(v); } I2O_FLAGS_COUNT_setCount(&(sg->FlagsCount), span); if (len == 0) { rw |= I2O_SGL_FLAGS_LAST_ELEMENT; } I2O_FLAGS_COUNT_setFlags(&(sg->FlagsCount), I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT | rw); ++sg; MessageSize += sizeof(*sg) / sizeof(U32); } /* We always do the request sense ... */ if ((span = ccb->csio.sense_len) == 0) { span = sizeof(ccb->csio.sense_data); } SG(sg, 0, I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER, &(ccb->csio.sense_data), span); I2O_MESSAGE_FRAME_setMessageSize(Message_Ptr, MessageSize + (sizeof(*sg) / sizeof(U32))); return (Message_Ptr); } /* ASR_init_message */ /* * Reset the adapter. */ static U32 ASR_initOutBound(Asr_softc_t *sc) { struct initOutBoundMessage { I2O_EXEC_OUTBOUND_INIT_MESSAGE M; U32 R; } Message; PI2O_EXEC_OUTBOUND_INIT_MESSAGE Message_Ptr; U32 *volatile Reply_Ptr; U32 Old; /* * Build up our copy of the Message. */ Message_Ptr = (PI2O_EXEC_OUTBOUND_INIT_MESSAGE)ASR_fillMessage(&Message, sizeof(I2O_EXEC_OUTBOUND_INIT_MESSAGE)); I2O_MESSAGE_FRAME_setFunction(&(Message_Ptr->StdMessageFrame), I2O_EXEC_OUTBOUND_INIT); I2O_EXEC_OUTBOUND_INIT_MESSAGE_setHostPageFrameSize(Message_Ptr, PAGE_SIZE); I2O_EXEC_OUTBOUND_INIT_MESSAGE_setOutboundMFrameSize(Message_Ptr, sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)); /* * Reset the Reply Status */ *(Reply_Ptr = (U32 *)((char *)Message_Ptr + sizeof(I2O_EXEC_OUTBOUND_INIT_MESSAGE))) = 0; SG (&(Message_Ptr->SGL), 0, I2O_SGL_FLAGS_LAST_ELEMENT, Reply_Ptr, sizeof(U32)); /* * Send the Message out */ if ((Old = ASR_initiateCp(sc, (PI2O_MESSAGE_FRAME)Message_Ptr)) != 0xffffffff) { u_long size, addr; /* * Wait for a response (Poll). */ while (*Reply_Ptr < I2O_EXEC_OUTBOUND_INIT_REJECTED); /* * Re-enable the interrupts. */ asr_set_intr(sc, Old); /* * Populate the outbound table. */ if (sc->ha_Msgs == NULL) { /* Allocate the reply frames */ size = sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME) * sc->ha_Msgs_Count; /* * contigmalloc only works reliably at * initialization time. */ if ((sc->ha_Msgs = (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME) contigmalloc (size, M_DEVBUF, M_WAITOK, 0ul, 0xFFFFFFFFul, (u_long)sizeof(U32), 0ul)) != NULL) { bzero(sc->ha_Msgs, size); sc->ha_Msgs_Phys = KVTOPHYS(sc->ha_Msgs); } } /* Initialize the outbound FIFO */ if (sc->ha_Msgs != NULL) for(size = sc->ha_Msgs_Count, addr = sc->ha_Msgs_Phys; size; --size) { asr_set_FromFIFO(sc, addr); addr += sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME); } return (*Reply_Ptr); } return (0); } /* ASR_initOutBound */ /* * Set the system table */ static int ASR_setSysTab(Asr_softc_t *sc) { PI2O_EXEC_SYS_TAB_SET_MESSAGE Message_Ptr; PI2O_SET_SYSTAB_HEADER SystemTable; Asr_softc_t * ha, *next; PI2O_SGE_SIMPLE_ELEMENT sg; int retVal; if ((SystemTable = (PI2O_SET_SYSTAB_HEADER)malloc ( sizeof(I2O_SET_SYSTAB_HEADER), M_TEMP, M_WAITOK | M_ZERO)) == NULL) { return (ENOMEM); } STAILQ_FOREACH(ha, &Asr_softc_list, ha_next) { ++SystemTable->NumberEntries; } if ((Message_Ptr = (PI2O_EXEC_SYS_TAB_SET_MESSAGE)malloc ( sizeof(I2O_EXEC_SYS_TAB_SET_MESSAGE) - sizeof(I2O_SG_ELEMENT) + ((3+SystemTable->NumberEntries) * sizeof(I2O_SGE_SIMPLE_ELEMENT)), M_TEMP, M_WAITOK)) == NULL) { free(SystemTable, M_TEMP); return (ENOMEM); } (void)ASR_fillMessage((void *)Message_Ptr, sizeof(I2O_EXEC_SYS_TAB_SET_MESSAGE) - sizeof(I2O_SG_ELEMENT) + ((3+SystemTable->NumberEntries) * sizeof(I2O_SGE_SIMPLE_ELEMENT))); I2O_MESSAGE_FRAME_setVersionOffset(&(Message_Ptr->StdMessageFrame), (I2O_VERSION_11 + (((sizeof(I2O_EXEC_SYS_TAB_SET_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4))); I2O_MESSAGE_FRAME_setFunction(&(Message_Ptr->StdMessageFrame), I2O_EXEC_SYS_TAB_SET); /* * Call the LCT table to determine the number of device entries * to reserve space for. * since this code is reused in several systems, code efficiency * is greater by using a shift operation rather than a divide by * sizeof(u_int32_t). */ sg = (PI2O_SGE_SIMPLE_ELEMENT)((char *)Message_Ptr + ((I2O_MESSAGE_FRAME_getVersionOffset( &(Message_Ptr->StdMessageFrame)) & 0xF0) >> 2)); SG(sg, 0, I2O_SGL_FLAGS_DIR, SystemTable, sizeof(I2O_SET_SYSTAB_HEADER)); ++sg; STAILQ_FOREACH_SAFE(ha, &Asr_softc_list, ha_next, next) { SG(sg, 0, ((next) ? (I2O_SGL_FLAGS_DIR) : (I2O_SGL_FLAGS_DIR | I2O_SGL_FLAGS_END_OF_BUFFER)), &(ha->ha_SystemTable), sizeof(ha->ha_SystemTable)); ++sg; } SG(sg, 0, I2O_SGL_FLAGS_DIR | I2O_SGL_FLAGS_END_OF_BUFFER, NULL, 0); SG(sg, 1, I2O_SGL_FLAGS_DIR | I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER, NULL, 0); retVal = ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr); free(Message_Ptr, M_TEMP); free(SystemTable, M_TEMP); return (retVal); } /* ASR_setSysTab */ static int ASR_acquireHrt(Asr_softc_t *sc) { I2O_EXEC_HRT_GET_MESSAGE Message; I2O_EXEC_HRT_GET_MESSAGE *Message_Ptr; struct { I2O_HRT Header; I2O_HRT_ENTRY Entry[MAX_CHANNEL]; } Hrt; u_int8_t NumberOfEntries; PI2O_HRT_ENTRY Entry; bzero(&Hrt, sizeof (Hrt)); Message_Ptr = (I2O_EXEC_HRT_GET_MESSAGE *)ASR_fillMessage(&Message, sizeof(I2O_EXEC_HRT_GET_MESSAGE) - sizeof(I2O_SG_ELEMENT) + sizeof(I2O_SGE_SIMPLE_ELEMENT)); I2O_MESSAGE_FRAME_setVersionOffset(&(Message_Ptr->StdMessageFrame), (I2O_VERSION_11 + (((sizeof(I2O_EXEC_HRT_GET_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4))); I2O_MESSAGE_FRAME_setFunction (&(Message_Ptr->StdMessageFrame), I2O_EXEC_HRT_GET); /* * Set up the buffers as scatter gather elements. */ SG(&(Message_Ptr->SGL), 0, I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER, &Hrt, sizeof(Hrt)); if (ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr) != CAM_REQ_CMP) { return (ENODEV); } if ((NumberOfEntries = I2O_HRT_getNumberEntries(&Hrt.Header)) > (MAX_CHANNEL + 1)) { NumberOfEntries = MAX_CHANNEL + 1; } for (Entry = Hrt.Header.HRTEntry; NumberOfEntries != 0; ++Entry, --NumberOfEntries) { PI2O_LCT_ENTRY Device; for (Device = sc->ha_LCT->LCTEntry; Device < (PI2O_LCT_ENTRY) (((U32 *)sc->ha_LCT)+I2O_LCT_getTableSize(sc->ha_LCT)); ++Device) { if (I2O_LCT_ENTRY_getLocalTID(Device) == (I2O_HRT_ENTRY_getAdapterID(Entry) & 0xFFF)) { Device->le_bus = I2O_HRT_ENTRY_getAdapterID( Entry) >> 16; if ((Device->le_bus > sc->ha_MaxBus) && (Device->le_bus <= MAX_CHANNEL)) { sc->ha_MaxBus = Device->le_bus; } } } } return (0); } /* ASR_acquireHrt */ /* * Enable the adapter. */ static int ASR_enableSys(Asr_softc_t *sc) { I2O_EXEC_SYS_ENABLE_MESSAGE Message; PI2O_EXEC_SYS_ENABLE_MESSAGE Message_Ptr; Message_Ptr = (PI2O_EXEC_SYS_ENABLE_MESSAGE)ASR_fillMessage(&Message, sizeof(I2O_EXEC_SYS_ENABLE_MESSAGE)); I2O_MESSAGE_FRAME_setFunction(&(Message_Ptr->StdMessageFrame), I2O_EXEC_SYS_ENABLE); return (ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr) != 0); } /* ASR_enableSys */ /* * Perform the stages necessary to initialize the adapter */ static int ASR_init(Asr_softc_t *sc) { return ((ASR_initOutBound(sc) == 0) || (ASR_setSysTab(sc) != CAM_REQ_CMP) || (ASR_enableSys(sc) != CAM_REQ_CMP)); } /* ASR_init */ /* * Send a Synchronize Cache command to the target device. */ static void ASR_sync(Asr_softc_t *sc, int bus, int target, int lun) { tid_t TID; /* * We will not synchronize the device when there are outstanding * commands issued by the OS (this is due to a locked up device, * as the OS normally would flush all outstanding commands before * issuing a shutdown or an adapter reset). */ if ((sc != NULL) && (LIST_FIRST(&(sc->ha_ccb)) != NULL) && ((TID = ASR_getTid (sc, bus, target, lun)) != (tid_t)-1) && (TID != (tid_t)0)) { PRIVATE_SCSI_SCB_EXECUTE_MESSAGE Message; PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE Message_Ptr; Message_Ptr = (PRIVATE_SCSI_SCB_EXECUTE_MESSAGE *)&Message; bzero(Message_Ptr, sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT) + sizeof(I2O_SGE_SIMPLE_ELEMENT)); I2O_MESSAGE_FRAME_setVersionOffset( (PI2O_MESSAGE_FRAME)Message_Ptr, I2O_VERSION_11 | (((sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4)); I2O_MESSAGE_FRAME_setMessageSize( (PI2O_MESSAGE_FRAME)Message_Ptr, (sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)); I2O_MESSAGE_FRAME_setInitiatorAddress ( (PI2O_MESSAGE_FRAME)Message_Ptr, 1); I2O_MESSAGE_FRAME_setFunction( (PI2O_MESSAGE_FRAME)Message_Ptr, I2O_PRIVATE_MESSAGE); I2O_MESSAGE_FRAME_setTargetAddress( (PI2O_MESSAGE_FRAME)Message_Ptr, TID); I2O_PRIVATE_MESSAGE_FRAME_setXFunctionCode ( (PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr, I2O_SCSI_SCB_EXEC); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setTID(Message_Ptr, TID); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setSCBFlags (Message_Ptr, I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER); I2O_PRIVATE_MESSAGE_FRAME_setOrganizationID( (PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr, DPT_ORGANIZATION_ID); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setCDBLength(Message_Ptr, 6); Message_Ptr->CDB[0] = SYNCHRONIZE_CACHE; Message_Ptr->CDB[1] = (lun << 5); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setSCBFlags (Message_Ptr, (I2O_SCB_FLAG_XFER_FROM_DEVICE | I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER)); (void)ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr); } } static void ASR_synchronize(Asr_softc_t *sc) { int bus, target, lun; for (bus = 0; bus <= sc->ha_MaxBus; ++bus) { for (target = 0; target <= sc->ha_MaxId; ++target) { for (lun = 0; lun <= sc->ha_MaxLun; ++lun) { ASR_sync(sc,bus,target,lun); } } } } /* * Reset the HBA, targets and BUS. * Currently this resets *all* the SCSI busses. */ static __inline void asr_hbareset(Asr_softc_t *sc) { ASR_synchronize(sc); (void)ASR_reset(sc); } /* asr_hbareset */ /* * A reduced copy of the real pci_map_mem, incorporating the MAX_MAP * limit and a reduction in error checking (in the pre 4.0 case). */ static int asr_pci_map_mem(device_t dev, Asr_softc_t *sc) { int rid; u_int32_t p, l, s; /* * I2O specification says we must find first *memory* mapped BAR */ for (rid = 0; rid < 4; rid++) { p = pci_read_config(dev, PCIR_BAR(rid), sizeof(p)); if ((p & 1) == 0) { break; } } /* * Give up? */ if (rid >= 4) { rid = 0; } rid = PCIR_BAR(rid); p = pci_read_config(dev, rid, sizeof(p)); pci_write_config(dev, rid, -1, sizeof(p)); l = 0 - (pci_read_config(dev, rid, sizeof(l)) & ~15); pci_write_config(dev, rid, p, sizeof(p)); if (l > MAX_MAP) { l = MAX_MAP; } /* * The 2005S Zero Channel RAID solution is not a perfect PCI * citizen. It asks for 4MB on BAR0, and 0MB on BAR1, once * enabled it rewrites the size of BAR0 to 2MB, sets BAR1 to * BAR0+2MB and sets it's size to 2MB. The IOP registers are * accessible via BAR0, the messaging registers are accessible * via BAR1. If the subdevice code is 50 to 59 decimal. */ s = pci_read_config(dev, PCIR_DEVVENDOR, sizeof(s)); if (s != 0xA5111044) { s = pci_read_config(dev, PCIR_SUBVEND_0, sizeof(s)); if ((((ADPTDOMINATOR_SUB_ID_START ^ s) & 0xF000FFFF) == 0) && (ADPTDOMINATOR_SUB_ID_START <= s) && (s <= ADPTDOMINATOR_SUB_ID_END)) { l = MAX_MAP; /* Conjoined BAR Raptor Daptor */ } } p &= ~15; sc->ha_mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, p, p + l, l, RF_ACTIVE); if (sc->ha_mem_res == NULL) { return (0); } sc->ha_Base = rman_get_start(sc->ha_mem_res); sc->ha_i2o_bhandle = rman_get_bushandle(sc->ha_mem_res); sc->ha_i2o_btag = rman_get_bustag(sc->ha_mem_res); if (s == 0xA5111044) { /* Split BAR Raptor Daptor */ if ((rid += sizeof(u_int32_t)) >= PCIR_BAR(4)) { return (0); } p = pci_read_config(dev, rid, sizeof(p)); pci_write_config(dev, rid, -1, sizeof(p)); l = 0 - (pci_read_config(dev, rid, sizeof(l)) & ~15); pci_write_config(dev, rid, p, sizeof(p)); if (l > MAX_MAP) { l = MAX_MAP; } p &= ~15; sc->ha_mes_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, p, p + l, l, RF_ACTIVE); if (sc->ha_mes_res == NULL) { return (0); } sc->ha_frame_bhandle = rman_get_bushandle(sc->ha_mes_res); sc->ha_frame_btag = rman_get_bustag(sc->ha_mes_res); } else { sc->ha_frame_bhandle = sc->ha_i2o_bhandle; sc->ha_frame_btag = sc->ha_i2o_btag; } return (1); } /* asr_pci_map_mem */ /* * A simplified copy of the real pci_map_int with additional * registration requirements. */ static int asr_pci_map_int(device_t dev, Asr_softc_t *sc) { int rid = 0; sc->ha_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->ha_irq_res == NULL) { return (0); } if (bus_setup_intr(dev, sc->ha_irq_res, INTR_TYPE_CAM | INTR_ENTROPY, NULL, (driver_intr_t *)asr_intr, (void *)sc, &(sc->ha_intr))) { return (0); } sc->ha_irq = pci_read_config(dev, PCIR_INTLINE, sizeof(char)); return (1); } /* asr_pci_map_int */ static void asr_status_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { Asr_softc_t *sc; if (error) return; sc = (Asr_softc_t *)arg; /* XXX * The status word can be at a 64-bit address, but the existing * accessor macros simply cannot manipulate 64-bit addresses. */ sc->ha_status_phys = (u_int32_t)segs[0].ds_addr + offsetof(struct Asr_status_mem, status); sc->ha_rstatus_phys = (u_int32_t)segs[0].ds_addr + offsetof(struct Asr_status_mem, rstatus); } static int asr_alloc_dma(Asr_softc_t *sc) { device_t dev; dev = sc->ha_dev; if (bus_dma_tag_create(bus_get_dma_tag(dev), /* PCI parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ BUS_SPACE_UNRESTRICTED, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->ha_parent_dmat)) { device_printf(dev, "Cannot allocate parent DMA tag\n"); return (ENOMEM); } if (bus_dma_tag_create(sc->ha_parent_dmat, /* parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ sizeof(sc->ha_statusmem),/* maxsize */ 1, /* nsegments */ sizeof(sc->ha_statusmem),/* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->ha_statusmem_dmat)) { device_printf(dev, "Cannot allocate status DMA tag\n"); bus_dma_tag_destroy(sc->ha_parent_dmat); return (ENOMEM); } if (bus_dmamem_alloc(sc->ha_statusmem_dmat, (void **)&sc->ha_statusmem, BUS_DMA_NOWAIT, &sc->ha_statusmem_dmamap)) { device_printf(dev, "Cannot allocate status memory\n"); bus_dma_tag_destroy(sc->ha_statusmem_dmat); bus_dma_tag_destroy(sc->ha_parent_dmat); return (ENOMEM); } (void)bus_dmamap_load(sc->ha_statusmem_dmat, sc->ha_statusmem_dmamap, sc->ha_statusmem, sizeof(sc->ha_statusmem), asr_status_cb, sc, 0); return (0); } static void asr_release_dma(Asr_softc_t *sc) { if (sc->ha_rstatus_phys != 0) bus_dmamap_unload(sc->ha_statusmem_dmat, sc->ha_statusmem_dmamap); if (sc->ha_statusmem != NULL) bus_dmamem_free(sc->ha_statusmem_dmat, sc->ha_statusmem, sc->ha_statusmem_dmamap); if (sc->ha_statusmem_dmat != NULL) bus_dma_tag_destroy(sc->ha_statusmem_dmat); if (sc->ha_parent_dmat != NULL) bus_dma_tag_destroy(sc->ha_parent_dmat); } /* * Attach the devices, and virtual devices to the driver list. */ static int asr_attach(device_t dev) { PI2O_EXEC_STATUS_GET_REPLY status; PI2O_LCT_ENTRY Device; Asr_softc_t *sc; struct scsi_inquiry_data *iq; int bus, size, unit; int error; sc = device_get_softc(dev); unit = device_get_unit(dev); sc->ha_dev = dev; if (STAILQ_EMPTY(&Asr_softc_list)) { /* * Fixup the OS revision as saved in the dptsig for the * engine (dptioctl.h) to pick up. */ bcopy(osrelease, &ASR_sig.dsDescription[16], 5); } /* * Initialize the software structure */ LIST_INIT(&(sc->ha_ccb)); /* Link us into the HA list */ STAILQ_INSERT_TAIL(&Asr_softc_list, sc, ha_next); /* * This is the real McCoy! */ if (!asr_pci_map_mem(dev, sc)) { device_printf(dev, "could not map memory\n"); return(ENXIO); } /* Enable if not formerly enabled */ pci_enable_busmaster(dev); sc->ha_pciBusNum = pci_get_bus(dev); sc->ha_pciDeviceNum = (pci_get_slot(dev) << 3) | pci_get_function(dev); if ((error = asr_alloc_dma(sc)) != 0) return (error); /* Check if the device is there? */ if (ASR_resetIOP(sc) == 0) { device_printf(dev, "Cannot reset adapter\n"); asr_release_dma(sc); return (EIO); } status = &sc->ha_statusmem->status; if (ASR_getStatus(sc) == NULL) { device_printf(dev, "could not initialize hardware\n"); asr_release_dma(sc); return(ENODEV); } sc->ha_SystemTable.OrganizationID = status->OrganizationID; sc->ha_SystemTable.IOP_ID = status->IOP_ID; sc->ha_SystemTable.I2oVersion = status->I2oVersion; sc->ha_SystemTable.IopState = status->IopState; sc->ha_SystemTable.MessengerType = status->MessengerType; sc->ha_SystemTable.InboundMessageFrameSize = status->InboundMFrameSize; sc->ha_SystemTable.MessengerInfo.InboundMessagePortAddressLow = (U32)(sc->ha_Base + I2O_REG_TOFIFO); /* XXX 64-bit */ if (!asr_pci_map_int(dev, (void *)sc)) { device_printf(dev, "could not map interrupt\n"); asr_release_dma(sc); return(ENXIO); } /* Adjust the maximim inbound count */ if (((sc->ha_QueueSize = I2O_EXEC_STATUS_GET_REPLY_getMaxInboundMFrames(status)) > MAX_INBOUND) || (sc->ha_QueueSize == 0)) { sc->ha_QueueSize = MAX_INBOUND; } /* Adjust the maximum outbound count */ if (((sc->ha_Msgs_Count = I2O_EXEC_STATUS_GET_REPLY_getMaxOutboundMFrames(status)) > MAX_OUTBOUND) || (sc->ha_Msgs_Count == 0)) { sc->ha_Msgs_Count = MAX_OUTBOUND; } if (sc->ha_Msgs_Count > sc->ha_QueueSize) { sc->ha_Msgs_Count = sc->ha_QueueSize; } /* Adjust the maximum SG size to adapter */ if ((size = (I2O_EXEC_STATUS_GET_REPLY_getInboundMFrameSize(status) << 2)) > MAX_INBOUND_SIZE) { size = MAX_INBOUND_SIZE; } sc->ha_SgSize = (size - sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) + sizeof(I2O_SG_ELEMENT)) / sizeof(I2O_SGE_SIMPLE_ELEMENT); /* * Only do a bus/HBA reset on the first time through. On this * first time through, we do not send a flush to the devices. */ if (ASR_init(sc) == 0) { struct BufferInfo { I2O_PARAM_RESULTS_LIST_HEADER Header; I2O_PARAM_READ_OPERATION_RESULT Read; I2O_DPT_EXEC_IOP_BUFFERS_SCALAR Info; } Buffer; PI2O_DPT_EXEC_IOP_BUFFERS_SCALAR Info; #define FW_DEBUG_BLED_OFFSET 8 if ((Info = (PI2O_DPT_EXEC_IOP_BUFFERS_SCALAR) ASR_getParams(sc, 0, I2O_DPT_EXEC_IOP_BUFFERS_GROUP_NO, &Buffer, sizeof(struct BufferInfo))) != NULL) { sc->ha_blinkLED = FW_DEBUG_BLED_OFFSET + I2O_DPT_EXEC_IOP_BUFFERS_SCALAR_getSerialOutputOffset(Info); } if (ASR_acquireLct(sc) == 0) { (void)ASR_acquireHrt(sc); } } else { device_printf(dev, "failed to initialize\n"); asr_release_dma(sc); return(ENXIO); } /* * Add in additional probe responses for more channels. We * are reusing the variable `target' for a channel loop counter. * Done here because of we need both the acquireLct and * acquireHrt data. */ for (Device = sc->ha_LCT->LCTEntry; Device < (PI2O_LCT_ENTRY) (((U32 *)sc->ha_LCT)+I2O_LCT_getTableSize(sc->ha_LCT)); ++Device) { if (Device->le_type == I2O_UNKNOWN) { continue; } if (I2O_LCT_ENTRY_getUserTID(Device) == 0xFFF) { if (Device->le_target > sc->ha_MaxId) { sc->ha_MaxId = Device->le_target; } if (Device->le_lun > sc->ha_MaxLun) { sc->ha_MaxLun = Device->le_lun; } } if (((Device->le_type & I2O_PORT) != 0) && (Device->le_bus <= MAX_CHANNEL)) { /* Do not increase MaxId for efficiency */ sc->ha_adapter_target[Device->le_bus] = Device->le_target; } } /* * Print the HBA model number as inquired from the card. */ device_printf(dev, " "); if ((iq = (struct scsi_inquiry_data *)malloc( sizeof(struct scsi_inquiry_data), M_TEMP, M_WAITOK | M_ZERO)) != NULL) { PRIVATE_SCSI_SCB_EXECUTE_MESSAGE Message; PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE Message_Ptr; int posted = 0; Message_Ptr = (PRIVATE_SCSI_SCB_EXECUTE_MESSAGE *)&Message; bzero(Message_Ptr, sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT) + sizeof(I2O_SGE_SIMPLE_ELEMENT)); I2O_MESSAGE_FRAME_setVersionOffset( (PI2O_MESSAGE_FRAME)Message_Ptr, I2O_VERSION_11 | (((sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT)) / sizeof(U32)) << 4)); I2O_MESSAGE_FRAME_setMessageSize( (PI2O_MESSAGE_FRAME)Message_Ptr, (sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT) + sizeof(I2O_SGE_SIMPLE_ELEMENT)) / sizeof(U32)); I2O_MESSAGE_FRAME_setInitiatorAddress( (PI2O_MESSAGE_FRAME)Message_Ptr, 1); I2O_MESSAGE_FRAME_setFunction( (PI2O_MESSAGE_FRAME)Message_Ptr, I2O_PRIVATE_MESSAGE); I2O_PRIVATE_MESSAGE_FRAME_setXFunctionCode( (PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr, I2O_SCSI_SCB_EXEC); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setSCBFlags (Message_Ptr, I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setInterpret(Message_Ptr, 1); I2O_PRIVATE_MESSAGE_FRAME_setOrganizationID( (PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr, DPT_ORGANIZATION_ID); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setCDBLength(Message_Ptr, 6); Message_Ptr->CDB[0] = INQUIRY; Message_Ptr->CDB[4] = (unsigned char)sizeof(struct scsi_inquiry_data); if (Message_Ptr->CDB[4] == 0) { Message_Ptr->CDB[4] = 255; } PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setSCBFlags (Message_Ptr, (I2O_SCB_FLAG_XFER_FROM_DEVICE | I2O_SCB_FLAG_ENABLE_DISCONNECT | I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | I2O_SCB_FLAG_SENSE_DATA_IN_BUFFER)); PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_setByteCount( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr, sizeof(struct scsi_inquiry_data)); SG(&(Message_Ptr->SGL), 0, I2O_SGL_FLAGS_LAST_ELEMENT | I2O_SGL_FLAGS_END_OF_BUFFER, iq, sizeof(struct scsi_inquiry_data)); (void)ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr); if (iq->vendor[0] && (iq->vendor[0] != ' ')) { printf (" "); ASR_prstring (iq->vendor, 8); ++posted; } if (iq->product[0] && (iq->product[0] != ' ')) { printf (" "); ASR_prstring (iq->product, 16); ++posted; } if (iq->revision[0] && (iq->revision[0] != ' ')) { printf (" FW Rev. "); ASR_prstring (iq->revision, 4); ++posted; } free(iq, M_TEMP); if (posted) { printf (","); } } printf (" %d channel, %d CCBs, Protocol I2O\n", sc->ha_MaxBus + 1, (sc->ha_QueueSize > MAX_INBOUND) ? MAX_INBOUND : sc->ha_QueueSize); for (bus = 0; bus <= sc->ha_MaxBus; ++bus) { struct cam_devq * devq; int QueueSize = sc->ha_QueueSize; if (QueueSize > MAX_INBOUND) { QueueSize = MAX_INBOUND; } /* * Create the device queue for our SIM(s). */ if ((devq = cam_simq_alloc(QueueSize)) == NULL) { continue; } /* * Construct our first channel SIM entry */ sc->ha_sim[bus] = cam_sim_alloc(asr_action, asr_poll, "asr", sc, unit, &Giant, 1, QueueSize, devq); if (sc->ha_sim[bus] == NULL) { continue; } if (xpt_bus_register(sc->ha_sim[bus], dev, bus) != CAM_SUCCESS){ cam_sim_free(sc->ha_sim[bus], /*free_devq*/TRUE); sc->ha_sim[bus] = NULL; continue; } if (xpt_create_path(&(sc->ha_path[bus]), /*periph*/NULL, cam_sim_path(sc->ha_sim[bus]), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister( cam_sim_path(sc->ha_sim[bus])); cam_sim_free(sc->ha_sim[bus], /*free_devq*/TRUE); sc->ha_sim[bus] = NULL; continue; } } /* * Generate the device node information */ sc->ha_devt = make_dev(&asr_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640, "asr%d", unit); if (sc->ha_devt != NULL) (void)make_dev_alias(sc->ha_devt, "rdpti%d", unit); sc->ha_devt->si_drv1 = sc; return(0); } /* asr_attach */ static void asr_poll(struct cam_sim *sim) { asr_intr(cam_sim_softc(sim)); } /* asr_poll */ static void asr_action(struct cam_sim *sim, union ccb *ccb) { struct Asr_softc *sc; debug_asr_printf("asr_action(%lx,%lx{%x})\n", (u_long)sim, (u_long)ccb, ccb->ccb_h.func_code); CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("asr_action\n")); ccb->ccb_h.spriv_ptr0 = sc = (struct Asr_softc *)cam_sim_softc(sim); switch ((int)ccb->ccb_h.func_code) { /* Common cases first */ case XPT_SCSI_IO: /* Execute the requested I/O operation */ { struct Message { char M[MAX_INBOUND_SIZE]; } Message; PI2O_MESSAGE_FRAME Message_Ptr; /* Reject incoming commands while we are resetting the card */ if (sc->ha_in_reset != HA_OPERATIONAL) { ccb->ccb_h.status &= ~CAM_STATUS_MASK; if (sc->ha_in_reset >= HA_OFF_LINE) { /* HBA is now off-line */ ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR; } else { /* HBA currently resetting, try again later. */ ccb->ccb_h.status |= CAM_REQUEUE_REQ; } debug_asr_cmd_printf (" e\n"); xpt_done(ccb); debug_asr_cmd_printf (" q\n"); break; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG) { printf( "asr%d WARNING: scsi_cmd(%x) already done on b%dt%du%d\n", cam_sim_unit(xpt_path_sim(ccb->ccb_h.path)), ccb->csio.cdb_io.cdb_bytes[0], cam_sim_bus(sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); } debug_asr_cmd_printf("(%d,%d,%d,%d)", cam_sim_unit(sim), cam_sim_bus(sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); debug_asr_dump_ccb(ccb); if ((Message_Ptr = ASR_init_message((union asr_ccb *)ccb, (PI2O_MESSAGE_FRAME)&Message)) != NULL) { debug_asr_cmd2_printf ("TID=%x:\n", PRIVATE_SCSI_SCB_EXECUTE_MESSAGE_getTID( (PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE)Message_Ptr)); debug_asr_cmd2_dump_message(Message_Ptr); debug_asr_cmd1_printf (" q"); if (ASR_queue (sc, Message_Ptr) == EMPTY_QUEUE) { ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_REQUEUE_REQ; debug_asr_cmd_printf (" E\n"); xpt_done(ccb); } debug_asr_cmd_printf(" Q\n"); break; } /* * We will get here if there is no valid TID for the device * referenced in the scsi command packet. */ ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_SEL_TIMEOUT; debug_asr_cmd_printf (" B\n"); xpt_done(ccb); break; } case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ /* Rese HBA device ... */ asr_hbareset (sc); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; #if (defined(REPORT_LUNS)) case REPORT_LUNS: #endif case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: /* XXX Implement */ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts = &(ccb->cts); struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; if (cts->type == CTS_TYPE_USER_SETTINGS) { cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; cts->transport_version = 2; scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; spi->flags = CTS_SPI_FLAGS_DISC_ENB; spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; spi->sync_period = 6; /* 40MHz */ spi->sync_offset = 15; spi->valid = CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET | CTS_SPI_VALID_BUS_WIDTH | CTS_SPI_VALID_DISC; scsi->valid = CTS_SCSI_VALID_TQ; ccb->ccb_h.status = CAM_REQ_CMP; } else { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; } xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg; u_int32_t size_mb; u_int32_t secs_per_cylinder; ccg = &(ccb->ccg); size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size); if (size_mb > 4096) { ccg->heads = 255; ccg->secs_per_track = 63; } else if (size_mb > 2048) { ccg->heads = 128; ccg->secs_per_track = 63; } else if (size_mb > 1024) { ccg->heads = 65; ccg->secs_per_track = 63; } else { ccg->heads = 64; ccg->secs_per_track = 32; } secs_per_cylinder = ccg->heads * ccg->secs_per_track; ccg->cylinders = ccg->volume_size / secs_per_cylinder; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ ASR_resetBus (sc, cam_sim_bus(sim)); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &(ccb->cpi); cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; cpi->target_sprt = 0; /* Not necessary to reset bus, done by HDM initialization */ cpi->hba_misc = PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = sc->ha_MaxId; cpi->max_lun = sc->ha_MaxLun; cpi->initiator_id = sc->ha_adapter_target[cam_sim_bus(sim)]; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Adaptec", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } /* asr_action */ /* * Handle processing of current CCB as pointed to by the Status. */ static int asr_intr(Asr_softc_t *sc) { int processed; for(processed = 0; asr_get_status(sc) & Mask_InterruptsDisabled; processed = 1) { union asr_ccb *ccb; u_int dsc; U32 ReplyOffset; PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME Reply; if (((ReplyOffset = asr_get_FromFIFO(sc)) == EMPTY_QUEUE) && ((ReplyOffset = asr_get_FromFIFO(sc)) == EMPTY_QUEUE)) { break; } Reply = (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)(ReplyOffset - sc->ha_Msgs_Phys + (char *)(sc->ha_Msgs)); /* * We do not need any (optional byteswapping) method access to * the Initiator context field. */ ccb = (union asr_ccb *)(long) I2O_MESSAGE_FRAME_getInitiatorContext64( &(Reply->StdReplyFrame.StdMessageFrame)); if (I2O_MESSAGE_FRAME_getMsgFlags( &(Reply->StdReplyFrame.StdMessageFrame)) & I2O_MESSAGE_FLAGS_FAIL) { I2O_UTIL_NOP_MESSAGE Message; PI2O_UTIL_NOP_MESSAGE Message_Ptr; U32 MessageOffset; MessageOffset = (u_long) I2O_FAILURE_REPLY_MESSAGE_FRAME_getPreservedMFA( (PI2O_FAILURE_REPLY_MESSAGE_FRAME)Reply); /* * Get the Original Message Frame's address, and get * it's Transaction Context into our space. (Currently * unused at original authorship, but better to be * safe than sorry). Straight copy means that we * need not concern ourselves with the (optional * byteswapping) method access. */ Reply->StdReplyFrame.TransactionContext = bus_space_read_4(sc->ha_frame_btag, sc->ha_frame_bhandle, MessageOffset + offsetof(I2O_SINGLE_REPLY_MESSAGE_FRAME, TransactionContext)); /* * For 64 bit machines, we need to reconstruct the * 64 bit context. */ ccb = (union asr_ccb *)(long) I2O_MESSAGE_FRAME_getInitiatorContext64( &(Reply->StdReplyFrame.StdMessageFrame)); /* * Unique error code for command failure. */ I2O_SINGLE_REPLY_MESSAGE_FRAME_setDetailedStatusCode( &(Reply->StdReplyFrame), (u_int16_t)-2); /* * Modify the message frame to contain a NOP and * re-issue it to the controller. */ Message_Ptr = (PI2O_UTIL_NOP_MESSAGE)ASR_fillMessage( &Message, sizeof(I2O_UTIL_NOP_MESSAGE)); #if (I2O_UTIL_NOP != 0) I2O_MESSAGE_FRAME_setFunction ( &(Message_Ptr->StdMessageFrame), I2O_UTIL_NOP); #endif /* * Copy the packet out to the Original Message */ asr_set_frame(sc, Message_Ptr, MessageOffset, sizeof(I2O_UTIL_NOP_MESSAGE)); /* * Issue the NOP */ asr_set_ToFIFO(sc, MessageOffset); } /* * Asynchronous command with no return requirements, * and a generic handler for immunity against odd error * returns from the adapter. */ if (ccb == NULL) { /* * Return Reply so that it can be used for the * next command */ asr_set_FromFIFO(sc, ReplyOffset); continue; } /* Welease Wadjah! (and stop timeouts) */ ASR_ccbRemove (sc, ccb); dsc = I2O_SINGLE_REPLY_MESSAGE_FRAME_getDetailedStatusCode( &(Reply->StdReplyFrame)); ccb->csio.scsi_status = dsc & I2O_SCSI_DEVICE_DSC_MASK; ccb->ccb_h.status &= ~CAM_STATUS_MASK; switch (dsc) { case I2O_SCSI_DSC_SUCCESS: ccb->ccb_h.status |= CAM_REQ_CMP; break; case I2O_SCSI_DSC_CHECK_CONDITION: ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; break; case I2O_SCSI_DSC_BUSY: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_ADAPTER_BUSY: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_SCSI_BUS_RESET: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_BUS_BUSY: ccb->ccb_h.status |= CAM_SCSI_BUSY; break; case I2O_SCSI_HBA_DSC_SELECTION_TIMEOUT: ccb->ccb_h.status |= CAM_SEL_TIMEOUT; break; case I2O_SCSI_HBA_DSC_COMMAND_TIMEOUT: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_DEVICE_NOT_PRESENT: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_LUN_INVALID: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_SCSI_TID_INVALID: ccb->ccb_h.status |= CAM_CMD_TIMEOUT; break; case I2O_SCSI_HBA_DSC_DATA_OVERRUN: /* FALLTHRU */ case I2O_SCSI_HBA_DSC_REQUEST_LENGTH_ERROR: ccb->ccb_h.status |= CAM_DATA_RUN_ERR; break; default: ccb->ccb_h.status |= CAM_REQUEUE_REQ; break; } if ((ccb->csio.resid = ccb->csio.dxfer_len) != 0) { ccb->csio.resid -= I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME_getTransferCount( Reply); } /* Sense data in reply packet */ if (ccb->ccb_h.status & CAM_AUTOSNS_VALID) { u_int16_t size = I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME_getAutoSenseTransferCount(Reply); if (size) { if (size > sizeof(ccb->csio.sense_data)) { size = sizeof(ccb->csio.sense_data); } if (size > I2O_SCSI_SENSE_DATA_SZ) { size = I2O_SCSI_SENSE_DATA_SZ; } if ((ccb->csio.sense_len) && (size > ccb->csio.sense_len)) { size = ccb->csio.sense_len; } if (size < ccb->csio.sense_len) { ccb->csio.sense_resid = ccb->csio.sense_len - size; } else { ccb->csio.sense_resid = 0; } bzero(&(ccb->csio.sense_data), sizeof(ccb->csio.sense_data)); bcopy(Reply->SenseData, &(ccb->csio.sense_data), size); } } /* * Return Reply so that it can be used for the next command * since we have no more need for it now */ asr_set_FromFIFO(sc, ReplyOffset); if (ccb->ccb_h.path) { xpt_done ((union ccb *)ccb); } else { wakeup (ccb); } } return (processed); } /* asr_intr */ #undef QueueSize /* Grrrr */ #undef SG_Size /* Grrrr */ /* * Meant to be included at the bottom of asr.c !!! */ /* * Included here as hard coded. Done because other necessary include * files utilize C++ comment structures which make them a nuisance to * included here just to pick up these three typedefs. */ typedef U32 DPT_TAG_T; typedef U32 DPT_MSG_T; typedef U32 DPT_RTN_T; #undef SCSI_RESET /* Conflicts with "scsi/scsiconf.h" defintion */ #include "dev/asr/osd_unix.h" #define asr_unit(dev) dev2unit(dev) static u_int8_t ASR_ctlr_held; static int asr_open(struct cdev *dev, int32_t flags, int32_t ifmt, struct thread *td) { int s; int error; if (dev->si_drv1 == NULL) { return (ENODEV); } s = splcam (); if (ASR_ctlr_held) { error = EBUSY; } else if ((error = priv_check(td, PRIV_DRIVER)) == 0) { ++ASR_ctlr_held; } splx(s); return (error); } /* asr_open */ static int asr_close(struct cdev *dev, int flags, int ifmt, struct thread *td) { ASR_ctlr_held = 0; return (0); } /* asr_close */ /*-------------------------------------------------------------------------*/ /* Function ASR_queue_i */ /*-------------------------------------------------------------------------*/ /* The Parameters Passed To This Function Are : */ /* Asr_softc_t * : HBA miniport driver's adapter data storage. */ /* PI2O_MESSAGE_FRAME : Msg Structure Pointer For This Command */ /* I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME following the Msg Structure */ /* */ /* This Function Will Take The User Request Packet And Convert It To An */ /* I2O MSG And Send It Off To The Adapter. */ /* */ /* Return : 0 For OK, Error Code Otherwise */ /*-------------------------------------------------------------------------*/ static int ASR_queue_i(Asr_softc_t *sc, PI2O_MESSAGE_FRAME Packet) { union asr_ccb * ccb; PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME Reply; PI2O_MESSAGE_FRAME Message_Ptr; PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME Reply_Ptr; int MessageSizeInBytes; int ReplySizeInBytes; int error; int s; /* Scatter Gather buffer list */ struct ioctlSgList_S { SLIST_ENTRY(ioctlSgList_S) link; caddr_t UserSpace; I2O_FLAGS_COUNT FlagsCount; char KernelSpace[sizeof(long)]; } * elm; /* Generates a `first' entry */ SLIST_HEAD(ioctlSgListHead_S, ioctlSgList_S) sgList; if (ASR_getBlinkLedCode(sc)) { debug_usr_cmd_printf ("Adapter currently in BlinkLed %x\n", ASR_getBlinkLedCode(sc)); return (EIO); } /* Copy in the message into a local allocation */ if ((Message_Ptr = (PI2O_MESSAGE_FRAME)malloc ( sizeof(I2O_MESSAGE_FRAME), M_TEMP, M_WAITOK)) == NULL) { debug_usr_cmd_printf ( "Failed to acquire I2O_MESSAGE_FRAME memory\n"); return (ENOMEM); } if ((error = copyin ((caddr_t)Packet, (caddr_t)Message_Ptr, sizeof(I2O_MESSAGE_FRAME))) != 0) { free(Message_Ptr, M_TEMP); debug_usr_cmd_printf ("Can't copy in packet errno=%d\n", error); return (error); } /* Acquire information to determine type of packet */ MessageSizeInBytes = (I2O_MESSAGE_FRAME_getMessageSize(Message_Ptr)<<2); /* The offset of the reply information within the user packet */ Reply = (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)((char *)Packet + MessageSizeInBytes); /* Check if the message is a synchronous initialization command */ s = I2O_MESSAGE_FRAME_getFunction(Message_Ptr); free(Message_Ptr, M_TEMP); switch (s) { case I2O_EXEC_IOP_RESET: { U32 status; status = ASR_resetIOP(sc); ReplySizeInBytes = sizeof(status); debug_usr_cmd_printf ("resetIOP done\n"); return (copyout ((caddr_t)&status, (caddr_t)Reply, ReplySizeInBytes)); } case I2O_EXEC_STATUS_GET: { PI2O_EXEC_STATUS_GET_REPLY status; status = &sc->ha_statusmem->status; if (ASR_getStatus(sc) == NULL) { debug_usr_cmd_printf ("getStatus failed\n"); return (ENXIO); } ReplySizeInBytes = sizeof(status); debug_usr_cmd_printf ("getStatus done\n"); return (copyout ((caddr_t)status, (caddr_t)Reply, ReplySizeInBytes)); } case I2O_EXEC_OUTBOUND_INIT: { U32 status; status = ASR_initOutBound(sc); ReplySizeInBytes = sizeof(status); debug_usr_cmd_printf ("intOutBound done\n"); return (copyout ((caddr_t)&status, (caddr_t)Reply, ReplySizeInBytes)); } } /* Determine if the message size is valid */ if ((MessageSizeInBytes < sizeof(I2O_MESSAGE_FRAME)) || (MAX_INBOUND_SIZE < MessageSizeInBytes)) { debug_usr_cmd_printf ("Packet size %d incorrect\n", MessageSizeInBytes); return (EINVAL); } if ((Message_Ptr = (PI2O_MESSAGE_FRAME)malloc (MessageSizeInBytes, M_TEMP, M_WAITOK)) == NULL) { debug_usr_cmd_printf ("Failed to acquire frame[%d] memory\n", MessageSizeInBytes); return (ENOMEM); } if ((error = copyin ((caddr_t)Packet, (caddr_t)Message_Ptr, MessageSizeInBytes)) != 0) { free(Message_Ptr, M_TEMP); debug_usr_cmd_printf ("Can't copy in packet[%d] errno=%d\n", MessageSizeInBytes, error); return (error); } /* Check the size of the reply frame, and start constructing */ if ((Reply_Ptr = (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)malloc ( sizeof(I2O_MESSAGE_FRAME), M_TEMP, M_WAITOK)) == NULL) { free(Message_Ptr, M_TEMP); debug_usr_cmd_printf ( "Failed to acquire I2O_MESSAGE_FRAME memory\n"); return (ENOMEM); } if ((error = copyin ((caddr_t)Reply, (caddr_t)Reply_Ptr, sizeof(I2O_MESSAGE_FRAME))) != 0) { free(Reply_Ptr, M_TEMP); free(Message_Ptr, M_TEMP); debug_usr_cmd_printf ( "Failed to copy in reply frame, errno=%d\n", error); return (error); } ReplySizeInBytes = (I2O_MESSAGE_FRAME_getMessageSize( &(Reply_Ptr->StdReplyFrame.StdMessageFrame)) << 2); free(Reply_Ptr, M_TEMP); if (ReplySizeInBytes < sizeof(I2O_SINGLE_REPLY_MESSAGE_FRAME)) { free(Message_Ptr, M_TEMP); debug_usr_cmd_printf ( "Failed to copy in reply frame[%d], errno=%d\n", ReplySizeInBytes, error); return (EINVAL); } if ((Reply_Ptr = (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)malloc ( ((ReplySizeInBytes > sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)) ? ReplySizeInBytes : sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)), M_TEMP, M_WAITOK)) == NULL) { free(Message_Ptr, M_TEMP); debug_usr_cmd_printf ("Failed to acquire frame[%d] memory\n", ReplySizeInBytes); return (ENOMEM); } (void)ASR_fillMessage((void *)Reply_Ptr, ReplySizeInBytes); Reply_Ptr->StdReplyFrame.StdMessageFrame.InitiatorContext = Message_Ptr->InitiatorContext; Reply_Ptr->StdReplyFrame.TransactionContext = ((PI2O_PRIVATE_MESSAGE_FRAME)Message_Ptr)->TransactionContext; I2O_MESSAGE_FRAME_setMsgFlags( &(Reply_Ptr->StdReplyFrame.StdMessageFrame), I2O_MESSAGE_FRAME_getMsgFlags( &(Reply_Ptr->StdReplyFrame.StdMessageFrame)) | I2O_MESSAGE_FLAGS_REPLY); /* Check if the message is a special case command */ switch (I2O_MESSAGE_FRAME_getFunction(Message_Ptr)) { case I2O_EXEC_SYS_TAB_SET: /* Special Case of empty Scatter Gather */ if (MessageSizeInBytes == ((I2O_MESSAGE_FRAME_getVersionOffset( Message_Ptr) & 0xF0) >> 2)) { free(Message_Ptr, M_TEMP); I2O_SINGLE_REPLY_MESSAGE_FRAME_setDetailedStatusCode( &(Reply_Ptr->StdReplyFrame), (ASR_setSysTab(sc) != CAM_REQ_CMP)); I2O_MESSAGE_FRAME_setMessageSize( &(Reply_Ptr->StdReplyFrame.StdMessageFrame), sizeof(I2O_SINGLE_REPLY_MESSAGE_FRAME)); error = copyout ((caddr_t)Reply_Ptr, (caddr_t)Reply, ReplySizeInBytes); free(Reply_Ptr, M_TEMP); return (error); } } /* Deal in the general case */ /* First allocate and optionally copy in each scatter gather element */ SLIST_INIT(&sgList); if ((I2O_MESSAGE_FRAME_getVersionOffset(Message_Ptr) & 0xF0) != 0) { PI2O_SGE_SIMPLE_ELEMENT sg; /* * since this code is reused in several systems, code * efficiency is greater by using a shift operation rather * than a divide by sizeof(u_int32_t). */ sg = (PI2O_SGE_SIMPLE_ELEMENT)((char *)Message_Ptr + ((I2O_MESSAGE_FRAME_getVersionOffset(Message_Ptr) & 0xF0) >> 2)); while (sg < (PI2O_SGE_SIMPLE_ELEMENT)(((caddr_t)Message_Ptr) + MessageSizeInBytes)) { caddr_t v; int len; if ((I2O_FLAGS_COUNT_getFlags(&(sg->FlagsCount)) & I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT) == 0) { error = EINVAL; break; } len = I2O_FLAGS_COUNT_getCount(&(sg->FlagsCount)); debug_usr_cmd_printf ("SG[%d] = %x[%d]\n", sg - (PI2O_SGE_SIMPLE_ELEMENT)((char *)Message_Ptr + ((I2O_MESSAGE_FRAME_getVersionOffset( Message_Ptr) & 0xF0) >> 2)), I2O_SGE_SIMPLE_ELEMENT_getPhysicalAddress(sg), len); if ((elm = (struct ioctlSgList_S *)malloc ( sizeof(*elm) - sizeof(elm->KernelSpace) + len, M_TEMP, M_WAITOK)) == NULL) { debug_usr_cmd_printf ( "Failed to allocate SG[%d]\n", len); error = ENOMEM; break; } SLIST_INSERT_HEAD(&sgList, elm, link); elm->FlagsCount = sg->FlagsCount; elm->UserSpace = (caddr_t) (I2O_SGE_SIMPLE_ELEMENT_getPhysicalAddress(sg)); v = elm->KernelSpace; /* Copy in outgoing data (DIR bit could be invalid) */ if ((error = copyin (elm->UserSpace, (caddr_t)v, len)) != 0) { break; } /* * If the buffer is not contiguous, lets * break up the scatter/gather entries. */ while ((len > 0) && (sg < (PI2O_SGE_SIMPLE_ELEMENT) (((caddr_t)Message_Ptr) + MAX_INBOUND_SIZE))) { int next, base, span; span = 0; next = base = KVTOPHYS(v); I2O_SGE_SIMPLE_ELEMENT_setPhysicalAddress(sg, base); /* How far can we go physically contiguously */ while ((len > 0) && (base == next)) { int size; next = trunc_page(base) + PAGE_SIZE; size = next - base; if (size > len) { size = len; } span += size; v += size; len -= size; base = KVTOPHYS(v); } /* Construct the Flags */ I2O_FLAGS_COUNT_setCount(&(sg->FlagsCount), span); { int flags = I2O_FLAGS_COUNT_getFlags( &(elm->FlagsCount)); /* Any remaining length? */ if (len > 0) { flags &= ~(I2O_SGL_FLAGS_END_OF_BUFFER | I2O_SGL_FLAGS_LAST_ELEMENT); } I2O_FLAGS_COUNT_setFlags( &(sg->FlagsCount), flags); } debug_usr_cmd_printf ("sg[%d] = %x[%d]\n", sg - (PI2O_SGE_SIMPLE_ELEMENT) ((char *)Message_Ptr + ((I2O_MESSAGE_FRAME_getVersionOffset( Message_Ptr) & 0xF0) >> 2)), I2O_SGE_SIMPLE_ELEMENT_getPhysicalAddress(sg), span); if (len <= 0) { break; } /* * Incrementing requires resizing of the * packet, and moving up the existing SG * elements. */ ++sg; MessageSizeInBytes += sizeof(*sg); I2O_MESSAGE_FRAME_setMessageSize(Message_Ptr, I2O_MESSAGE_FRAME_getMessageSize(Message_Ptr) + (sizeof(*sg) / sizeof(U32))); { PI2O_MESSAGE_FRAME NewMessage_Ptr; if ((NewMessage_Ptr = (PI2O_MESSAGE_FRAME) malloc (MessageSizeInBytes, M_TEMP, M_WAITOK)) == NULL) { debug_usr_cmd_printf ( "Failed to acquire frame[%d] memory\n", MessageSizeInBytes); error = ENOMEM; break; } span = ((caddr_t)sg) - (caddr_t)Message_Ptr; bcopy(Message_Ptr,NewMessage_Ptr, span); bcopy((caddr_t)(sg-1), ((caddr_t)NewMessage_Ptr) + span, MessageSizeInBytes - span); free(Message_Ptr, M_TEMP); sg = (PI2O_SGE_SIMPLE_ELEMENT) (((caddr_t)NewMessage_Ptr) + span); Message_Ptr = NewMessage_Ptr; } } if ((error) || ((I2O_FLAGS_COUNT_getFlags(&(sg->FlagsCount)) & I2O_SGL_FLAGS_LAST_ELEMENT) != 0)) { break; } ++sg; } if (error) { while ((elm = SLIST_FIRST(&sgList)) != NULL) { SLIST_REMOVE_HEAD(&sgList, link); free(elm, M_TEMP); } free(Reply_Ptr, M_TEMP); free(Message_Ptr, M_TEMP); return (error); } } debug_usr_cmd_printf ("Inbound: "); debug_usr_cmd_dump_message(Message_Ptr); /* Send the command */ if ((ccb = asr_alloc_ccb (sc)) == NULL) { /* Free up in-kernel buffers */ while ((elm = SLIST_FIRST(&sgList)) != NULL) { SLIST_REMOVE_HEAD(&sgList, link); free(elm, M_TEMP); } free(Reply_Ptr, M_TEMP); free(Message_Ptr, M_TEMP); return (ENOMEM); } /* * We do not need any (optional byteswapping) method access to * the Initiator context field. */ I2O_MESSAGE_FRAME_setInitiatorContext64( (PI2O_MESSAGE_FRAME)Message_Ptr, (long)ccb); (void)ASR_queue (sc, (PI2O_MESSAGE_FRAME)Message_Ptr); free(Message_Ptr, M_TEMP); /* * Wait for the board to report a finished instruction. */ s = splcam(); while ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) { if (ASR_getBlinkLedCode(sc)) { /* Reset Adapter */ printf ("asr%d: Blink LED 0x%x resetting adapter\n", cam_sim_unit(xpt_path_sim(ccb->ccb_h.path)), ASR_getBlinkLedCode(sc)); if (ASR_reset (sc) == ENXIO) { /* Command Cleanup */ ASR_ccbRemove(sc, ccb); } splx(s); /* Free up in-kernel buffers */ while ((elm = SLIST_FIRST(&sgList)) != NULL) { SLIST_REMOVE_HEAD(&sgList, link); free(elm, M_TEMP); } free(Reply_Ptr, M_TEMP); asr_free_ccb(ccb); return (EIO); } /* Check every second for BlinkLed */ /* There is no PRICAM, but outwardly PRIBIO is functional */ tsleep(ccb, PRIBIO, "asr", hz); } splx(s); debug_usr_cmd_printf ("Outbound: "); debug_usr_cmd_dump_message(Reply_Ptr); I2O_SINGLE_REPLY_MESSAGE_FRAME_setDetailedStatusCode( &(Reply_Ptr->StdReplyFrame), (ccb->ccb_h.status != CAM_REQ_CMP)); if (ReplySizeInBytes >= (sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME) - I2O_SCSI_SENSE_DATA_SZ - sizeof(U32))) { I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME_setTransferCount(Reply_Ptr, ccb->csio.dxfer_len - ccb->csio.resid); } if ((ccb->ccb_h.status & CAM_AUTOSNS_VALID) && (ReplySizeInBytes > (sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME) - I2O_SCSI_SENSE_DATA_SZ))) { int size = ReplySizeInBytes - sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME) - I2O_SCSI_SENSE_DATA_SZ; if (size > sizeof(ccb->csio.sense_data)) { size = sizeof(ccb->csio.sense_data); } if (size < ccb->csio.sense_len) { ccb->csio.sense_resid = ccb->csio.sense_len - size; } else { ccb->csio.sense_resid = 0; } bzero(&(ccb->csio.sense_data), sizeof(ccb->csio.sense_data)); bcopy(&(ccb->csio.sense_data), Reply_Ptr->SenseData, size); I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME_setAutoSenseTransferCount( Reply_Ptr, size); } /* Free up in-kernel buffers */ while ((elm = SLIST_FIRST(&sgList)) != NULL) { /* Copy out as necessary */ if ((error == 0) /* DIR bit considered `valid', error due to ignorance works */ && ((I2O_FLAGS_COUNT_getFlags(&(elm->FlagsCount)) & I2O_SGL_FLAGS_DIR) == 0)) { error = copyout((caddr_t)(elm->KernelSpace), elm->UserSpace, I2O_FLAGS_COUNT_getCount(&(elm->FlagsCount))); } SLIST_REMOVE_HEAD(&sgList, link); free(elm, M_TEMP); } if (error == 0) { /* Copy reply frame to user space */ error = copyout((caddr_t)Reply_Ptr, (caddr_t)Reply, ReplySizeInBytes); } free(Reply_Ptr, M_TEMP); asr_free_ccb(ccb); return (error); } /* ASR_queue_i */ /*----------------------------------------------------------------------*/ /* Function asr_ioctl */ /*----------------------------------------------------------------------*/ /* The parameters passed to this function are : */ /* dev : Device number. */ /* cmd : Ioctl Command */ /* data : User Argument Passed In. */ /* flag : Mode Parameter */ /* proc : Process Parameter */ /* */ /* This function is the user interface into this adapter driver */ /* */ /* Return : zero if OK, error code if not */ /*----------------------------------------------------------------------*/ static int asr_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { Asr_softc_t *sc = dev->si_drv1; int i, error = 0; #ifdef ASR_IOCTL_COMPAT int j; #endif /* ASR_IOCTL_COMPAT */ if (sc != NULL) switch(cmd) { case DPT_SIGNATURE: #ifdef ASR_IOCTL_COMPAT #if (dsDescription_size != 50) case DPT_SIGNATURE + ((50 - dsDescription_size) << 16): #endif if (cmd & 0xFFFF0000) { bcopy(&ASR_sig, data, sizeof(dpt_sig_S)); return (0); } /* Traditional version of the ioctl interface */ case DPT_SIGNATURE & 0x0000FFFF: #endif return (copyout((caddr_t)(&ASR_sig), *((caddr_t *)data), sizeof(dpt_sig_S))); /* Traditional version of the ioctl interface */ case DPT_CTRLINFO & 0x0000FFFF: case DPT_CTRLINFO: { struct { u_int16_t length; u_int16_t drvrHBAnum; u_int32_t baseAddr; u_int16_t blinkState; u_int8_t pciBusNum; u_int8_t pciDeviceNum; u_int16_t hbaFlags; u_int16_t Interrupt; u_int32_t reserved1; u_int32_t reserved2; u_int32_t reserved3; } CtlrInfo; bzero(&CtlrInfo, sizeof(CtlrInfo)); CtlrInfo.length = sizeof(CtlrInfo) - sizeof(u_int16_t); CtlrInfo.drvrHBAnum = asr_unit(dev); CtlrInfo.baseAddr = sc->ha_Base; i = ASR_getBlinkLedCode (sc); if (i == -1) i = 0; CtlrInfo.blinkState = i; CtlrInfo.pciBusNum = sc->ha_pciBusNum; CtlrInfo.pciDeviceNum = sc->ha_pciDeviceNum; #define FLG_OSD_PCI_VALID 0x0001 #define FLG_OSD_DMA 0x0002 #define FLG_OSD_I2O 0x0004 CtlrInfo.hbaFlags = FLG_OSD_PCI_VALID|FLG_OSD_DMA|FLG_OSD_I2O; CtlrInfo.Interrupt = sc->ha_irq; #ifdef ASR_IOCTL_COMPAT if (cmd & 0xffff0000) bcopy(&CtlrInfo, data, sizeof(CtlrInfo)); else #endif /* ASR_IOCTL_COMPAT */ error = copyout(&CtlrInfo, *(caddr_t *)data, sizeof(CtlrInfo)); } return (error); /* Traditional version of the ioctl interface */ case DPT_SYSINFO & 0x0000FFFF: case DPT_SYSINFO: { sysInfo_S Info; #ifdef ASR_IOCTL_COMPAT char * cp; /* Kernel Specific ptok `hack' */ #define ptok(a) ((char *)(uintptr_t)(a) + KERNBASE) bzero(&Info, sizeof(Info)); /* Appears I am the only person in the Kernel doing this */ outb (0x70, 0x12); i = inb(0x71); j = i >> 4; if (i == 0x0f) { outb (0x70, 0x19); j = inb (0x71); } Info.drive0CMOS = j; j = i & 0x0f; if (i == 0x0f) { outb (0x70, 0x1a); j = inb (0x71); } Info.drive1CMOS = j; Info.numDrives = *((char *)ptok(0x475)); #else /* ASR_IOCTL_COMPAT */ bzero(&Info, sizeof(Info)); #endif /* ASR_IOCTL_COMPAT */ Info.processorFamily = ASR_sig.dsProcessorFamily; #if defined(__i386__) switch (cpu) { case CPU_386SX: case CPU_386: Info.processorType = PROC_386; break; case CPU_486SX: case CPU_486: Info.processorType = PROC_486; break; case CPU_586: Info.processorType = PROC_PENTIUM; break; case CPU_686: Info.processorType = PROC_SEXIUM; break; } #endif Info.osType = OS_BSDI_UNIX; Info.osMajorVersion = osrelease[0] - '0'; Info.osMinorVersion = osrelease[2] - '0'; /* Info.osRevision = 0; */ /* Info.osSubRevision = 0; */ Info.busType = SI_PCI_BUS; Info.flags = SI_OSversionValid|SI_BusTypeValid|SI_NO_SmartROM; #ifdef ASR_IOCTL_COMPAT Info.flags |= SI_CMOS_Valid | SI_NumDrivesValid; /* Go Out And Look For I2O SmartROM */ for(j = 0xC8000; j < 0xE0000; j += 2048) { int k; cp = ptok(j); if (*((unsigned short *)cp) != 0xAA55) { continue; } j += (cp[2] * 512) - 2048; if ((*((u_long *)(cp + 6)) != ('S' + (' ' * 256) + (' ' * 65536L))) || (*((u_long *)(cp + 10)) != ('I' + ('2' * 256) + ('0' * 65536L)))) { continue; } cp += 0x24; for (k = 0; k < 64; ++k) { if (*((unsigned short *)cp) == (' ' + ('v' * 256))) { break; } } if (k < 64) { Info.smartROMMajorVersion = *((unsigned char *)(cp += 4)) - '0'; Info.smartROMMinorVersion = *((unsigned char *)(cp += 2)); Info.smartROMRevision = *((unsigned char *)(++cp)); Info.flags |= SI_SmartROMverValid; Info.flags &= ~SI_NO_SmartROM; break; } } /* Get The Conventional Memory Size From CMOS */ outb (0x70, 0x16); j = inb (0x71); j <<= 8; outb (0x70, 0x15); j |= inb(0x71); Info.conventionalMemSize = j; /* Get The Extended Memory Found At Power On From CMOS */ outb (0x70, 0x31); j = inb (0x71); j <<= 8; outb (0x70, 0x30); j |= inb(0x71); Info.extendedMemSize = j; Info.flags |= SI_MemorySizeValid; /* Copy Out The Info Structure To The User */ if (cmd & 0xFFFF0000) bcopy(&Info, data, sizeof(Info)); else #endif /* ASR_IOCTL_COMPAT */ error = copyout(&Info, *(caddr_t *)data, sizeof(Info)); return (error); } /* Get The BlinkLED State */ case DPT_BLINKLED: i = ASR_getBlinkLedCode (sc); if (i == -1) i = 0; #ifdef ASR_IOCTL_COMPAT if (cmd & 0xffff0000) bcopy(&i, data, sizeof(i)); else #endif /* ASR_IOCTL_COMPAT */ error = copyout(&i, *(caddr_t *)data, sizeof(i)); break; /* Send an I2O command */ case I2OUSRCMD: return (ASR_queue_i(sc, *((PI2O_MESSAGE_FRAME *)data))); /* Reset and re-initialize the adapter */ case I2ORESETCMD: return (ASR_reset(sc)); /* Rescan the LCT table and resynchronize the information */ case I2ORESCANCMD: return (ASR_rescan(sc)); } return (EINVAL); } /* asr_ioctl */ diff --git a/sys/dev/firewire/sbp.c b/sys/dev/firewire/sbp.c index 6b1b4a17dada..00e780e9002c 100644 --- a/sys/dev/firewire/sbp.c +++ b/sys/dev/firewire/sbp.c @@ -1,2993 +1,2995 @@ /*- * Copyright (c) 2003 Hidetoshi Shimokawa * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the acknowledgement as bellow: * * This product includes software developed by K. Kobayashi and H. Shimokawa * * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) && __FreeBSD_version >= 501102 #include #include #endif #if defined(__DragonFly__) || __FreeBSD_version < 500106 #include /* for struct devstat */ #endif #ifdef __DragonFly__ #include #include #include #include #include #include #include #include #include #include #include #include "sbp.h" #else #include #include #include #include #include #include #include #include #include #include #include #include #endif #define ccb_sdev_ptr spriv_ptr0 #define ccb_sbp_ptr spriv_ptr1 #define SBP_NUM_TARGETS 8 /* MAX 64 */ /* * Scan_bus doesn't work for more than 8 LUNs * because of CAM_SCSI2_MAXLUN in cam_xpt.c */ #define SBP_NUM_LUNS 64 #define SBP_MAXPHYS MIN(MAXPHYS, (512*1024) /* 512KB */) #define SBP_DMA_SIZE PAGE_SIZE #define SBP_LOGIN_SIZE sizeof(struct sbp_login_res) #define SBP_QUEUE_LEN ((SBP_DMA_SIZE - SBP_LOGIN_SIZE) / sizeof(struct sbp_ocb)) #define SBP_NUM_OCB (SBP_QUEUE_LEN * SBP_NUM_TARGETS) /* * STATUS FIFO addressing * bit * ----------------------- * 0- 1( 2): 0 (alignment) * 2- 7( 6): target * 8-15( 8): lun * 16-31( 8): reserved * 32-47(16): SBP_BIND_HI * 48-64(16): bus_id, node_id */ #define SBP_BIND_HI 0x1 #define SBP_DEV2ADDR(t, l) \ (((u_int64_t)SBP_BIND_HI << 32) \ | (((l) & 0xff) << 8) \ | (((t) & 0x3f) << 2)) #define SBP_ADDR2TRG(a) (((a) >> 2) & 0x3f) #define SBP_ADDR2LUN(a) (((a) >> 8) & 0xff) #define SBP_INITIATOR 7 static char *orb_fun_name[] = { ORB_FUN_NAMES }; static int debug = 0; static int auto_login = 1; static int max_speed = -1; static int sbp_cold = 1; static int ex_login = 1; static int login_delay = 1000; /* msec */ static int scan_delay = 500; /* msec */ static int use_doorbell = 0; static int sbp_tags = 0; SYSCTL_DECL(_hw_firewire); static SYSCTL_NODE(_hw_firewire, OID_AUTO, sbp, CTLFLAG_RD, 0, "SBP-II Subsystem"); SYSCTL_INT(_debug, OID_AUTO, sbp_debug, CTLFLAG_RW, &debug, 0, "SBP debug flag"); SYSCTL_INT(_hw_firewire_sbp, OID_AUTO, auto_login, CTLFLAG_RW, &auto_login, 0, "SBP perform login automatically"); SYSCTL_INT(_hw_firewire_sbp, OID_AUTO, max_speed, CTLFLAG_RW, &max_speed, 0, "SBP transfer max speed"); SYSCTL_INT(_hw_firewire_sbp, OID_AUTO, exclusive_login, CTLFLAG_RW, &ex_login, 0, "SBP enable exclusive login"); SYSCTL_INT(_hw_firewire_sbp, OID_AUTO, login_delay, CTLFLAG_RW, &login_delay, 0, "SBP login delay in msec"); SYSCTL_INT(_hw_firewire_sbp, OID_AUTO, scan_delay, CTLFLAG_RW, &scan_delay, 0, "SBP scan delay in msec"); SYSCTL_INT(_hw_firewire_sbp, OID_AUTO, use_doorbell, CTLFLAG_RW, &use_doorbell, 0, "SBP use doorbell request"); SYSCTL_INT(_hw_firewire_sbp, OID_AUTO, tags, CTLFLAG_RW, &sbp_tags, 0, "SBP tagged queuing support"); TUNABLE_INT("hw.firewire.sbp.auto_login", &auto_login); TUNABLE_INT("hw.firewire.sbp.max_speed", &max_speed); TUNABLE_INT("hw.firewire.sbp.exclusive_login", &ex_login); TUNABLE_INT("hw.firewire.sbp.login_delay", &login_delay); TUNABLE_INT("hw.firewire.sbp.scan_delay", &scan_delay); TUNABLE_INT("hw.firewire.sbp.use_doorbell", &use_doorbell); TUNABLE_INT("hw.firewire.sbp.tags", &sbp_tags); #define NEED_RESPONSE 0 #define SBP_SEG_MAX rounddown(0xffff, PAGE_SIZE) #ifdef __sparc64__ /* iommu */ #define SBP_IND_MAX howmany(SBP_MAXPHYS, SBP_SEG_MAX) #else #define SBP_IND_MAX howmany(SBP_MAXPHYS, PAGE_SIZE) #endif struct sbp_ocb { STAILQ_ENTRY(sbp_ocb) ocb; union ccb *ccb; bus_addr_t bus_addr; uint32_t orb[8]; #define IND_PTR_OFFSET (8*sizeof(uint32_t)) struct ind_ptr ind_ptr[SBP_IND_MAX]; struct sbp_dev *sdev; int flags; /* XXX should be removed */ bus_dmamap_t dmamap; + struct callout_handle timeout_ch; }; #define OCB_ACT_MGM 0 #define OCB_ACT_CMD 1 #define OCB_MATCH(o,s) ((o)->bus_addr == ntohl((s)->orb_lo)) struct sbp_dev{ #define SBP_DEV_RESET 0 /* accept login */ #define SBP_DEV_LOGIN 1 /* to login */ #if 0 #define SBP_DEV_RECONN 2 /* to reconnect */ #endif #define SBP_DEV_TOATTACH 3 /* to attach */ #define SBP_DEV_PROBE 4 /* scan lun */ #define SBP_DEV_ATTACHED 5 /* in operation */ #define SBP_DEV_DEAD 6 /* unavailable unit */ #define SBP_DEV_RETRY 7 /* unavailable unit */ uint8_t status:4, timeout:4; uint8_t type; uint16_t lun_id; uint16_t freeze; #define ORB_LINK_DEAD (1 << 0) #define VALID_LUN (1 << 1) #define ORB_POINTER_ACTIVE (1 << 2) #define ORB_POINTER_NEED (1 << 3) #define ORB_DOORBELL_ACTIVE (1 << 4) #define ORB_DOORBELL_NEED (1 << 5) #define ORB_SHORTAGE (1 << 6) uint16_t flags; struct cam_path *path; struct sbp_target *target; struct fwdma_alloc dma; struct sbp_login_res *login; struct callout login_callout; struct sbp_ocb *ocb; STAILQ_HEAD(, sbp_ocb) ocbs; STAILQ_HEAD(, sbp_ocb) free_ocbs; struct sbp_ocb *last_ocb; char vendor[32]; char product[32]; char revision[10]; char bustgtlun[32]; }; struct sbp_target { int target_id; int num_lun; struct sbp_dev **luns; struct sbp_softc *sbp; struct fw_device *fwdev; uint32_t mgm_hi, mgm_lo; struct sbp_ocb *mgm_ocb_cur; STAILQ_HEAD(, sbp_ocb) mgm_ocb_queue; struct callout mgm_ocb_timeout; struct callout scan_callout; STAILQ_HEAD(, fw_xfer) xferlist; int n_xfer; }; struct sbp_softc { struct firewire_dev_comm fd; struct cam_sim *sim; struct cam_path *path; struct sbp_target targets[SBP_NUM_TARGETS]; struct fw_bind fwb; bus_dma_tag_t dmat; struct timeval last_busreset; #define SIMQ_FREEZED 1 int flags; struct mtx mtx; }; #define SBP_LOCK(sbp) mtx_lock(&(sbp)->mtx) #define SBP_UNLOCK(sbp) mtx_unlock(&(sbp)->mtx) static void sbp_post_explore (void *); static void sbp_recv (struct fw_xfer *); static void sbp_mgm_callback (struct fw_xfer *); #if 0 static void sbp_cmd_callback (struct fw_xfer *); #endif static void sbp_orb_pointer (struct sbp_dev *, struct sbp_ocb *); static void sbp_doorbell(struct sbp_dev *); static void sbp_execute_ocb (void *, bus_dma_segment_t *, int, int); static void sbp_free_ocb (struct sbp_dev *, struct sbp_ocb *); static void sbp_abort_ocb (struct sbp_ocb *, int); static void sbp_abort_all_ocbs (struct sbp_dev *, int); static struct fw_xfer * sbp_write_cmd_locked (struct sbp_dev *, int, int); static struct fw_xfer * sbp_write_cmd (struct sbp_dev *, int, int); static struct sbp_ocb * sbp_get_ocb (struct sbp_dev *); static struct sbp_ocb * sbp_enqueue_ocb (struct sbp_dev *, struct sbp_ocb *); static struct sbp_ocb * sbp_dequeue_ocb (struct sbp_dev *, struct sbp_status *); static void sbp_cam_detach_sdev(struct sbp_dev *); static void sbp_free_sdev(struct sbp_dev *); static void sbp_cam_detach_target (struct sbp_target *); static void sbp_free_target (struct sbp_target *); static void sbp_mgm_timeout (void *arg); static void sbp_timeout (void *arg); static void sbp_mgm_orb (struct sbp_dev *, int, struct sbp_ocb *); static MALLOC_DEFINE(M_SBP, "sbp", "SBP-II/FireWire"); /* cam related functions */ static void sbp_action(struct cam_sim *sim, union ccb *ccb); static void sbp_poll(struct cam_sim *sim); static void sbp_cam_scan_lun(struct cam_periph *, union ccb *); static void sbp_cam_scan_target(void *arg); static char *orb_status0[] = { /* 0 */ "No additional information to report", /* 1 */ "Request type not supported", /* 2 */ "Speed not supported", /* 3 */ "Page size not supported", /* 4 */ "Access denied", /* 5 */ "Logical unit not supported", /* 6 */ "Maximum payload too small", /* 7 */ "Reserved for future standardization", /* 8 */ "Resources unavailable", /* 9 */ "Function rejected", /* A */ "Login ID not recognized", /* B */ "Dummy ORB completed", /* C */ "Request aborted", /* FF */ "Unspecified error" #define MAX_ORB_STATUS0 0xd }; static char *orb_status1_object[] = { /* 0 */ "Operation request block (ORB)", /* 1 */ "Data buffer", /* 2 */ "Page table", /* 3 */ "Unable to specify" }; static char *orb_status1_serial_bus_error[] = { /* 0 */ "Missing acknowledge", /* 1 */ "Reserved; not to be used", /* 2 */ "Time-out error", /* 3 */ "Reserved; not to be used", /* 4 */ "Busy retry limit exceeded(X)", /* 5 */ "Busy retry limit exceeded(A)", /* 6 */ "Busy retry limit exceeded(B)", /* 7 */ "Reserved for future standardization", /* 8 */ "Reserved for future standardization", /* 9 */ "Reserved for future standardization", /* A */ "Reserved for future standardization", /* B */ "Tardy retry limit exceeded", /* C */ "Conflict error", /* D */ "Data error", /* E */ "Type error", /* F */ "Address error" }; static void sbp_identify(driver_t *driver, device_t parent) { SBP_DEBUG(0) printf("sbp_identify\n"); END_DEBUG BUS_ADD_CHILD(parent, 0, "sbp", device_get_unit(parent)); } /* * sbp_probe() */ static int sbp_probe(device_t dev) { device_t pa; SBP_DEBUG(0) printf("sbp_probe\n"); END_DEBUG pa = device_get_parent(dev); if(device_get_unit(dev) != device_get_unit(pa)){ return(ENXIO); } device_set_desc(dev, "SBP-2/SCSI over FireWire"); #if 0 if (bootverbose) debug = bootverbose; #endif return (0); } /* * Display device characteristics on the console */ static void sbp_show_sdev_info(struct sbp_dev *sdev) { struct fw_device *fwdev; fwdev = sdev->target->fwdev; device_printf(sdev->target->sbp->fd.dev, "%s: %s: ordered:%d type:%d EUI:%08x%08x node:%d " "speed:%d maxrec:%d\n", __func__, sdev->bustgtlun, (sdev->type & 0x40) >> 6, (sdev->type & 0x1f), fwdev->eui.hi, fwdev->eui.lo, fwdev->dst, fwdev->speed, fwdev->maxrec); device_printf(sdev->target->sbp->fd.dev, "%s: %s '%s' '%s' '%s'\n", __func__, sdev->bustgtlun, sdev->vendor, sdev->product, sdev->revision); } static struct { int bus; int target; struct fw_eui64 eui; } wired[] = { /* Bus Target EUI64 */ #if 0 {0, 2, {0x00018ea0, 0x01fd0154}}, /* Logitec HDD */ {0, 0, {0x00018ea6, 0x00100682}}, /* Logitec DVD */ {0, 1, {0x00d03200, 0xa412006a}}, /* Yano HDD */ #endif {-1, -1, {0,0}} }; static int sbp_new_target(struct sbp_softc *sbp, struct fw_device *fwdev) { int bus, i, target=-1; char w[SBP_NUM_TARGETS]; bzero(w, sizeof(w)); bus = device_get_unit(sbp->fd.dev); /* XXX wired-down configuration should be gotten from tunable or device hint */ for (i = 0; wired[i].bus >= 0; i ++) { if (wired[i].bus == bus) { w[wired[i].target] = 1; if (wired[i].eui.hi == fwdev->eui.hi && wired[i].eui.lo == fwdev->eui.lo) target = wired[i].target; } } if (target >= 0) { if(target < SBP_NUM_TARGETS && sbp->targets[target].fwdev == NULL) return(target); device_printf(sbp->fd.dev, "target %d is not free for %08x:%08x\n", target, fwdev->eui.hi, fwdev->eui.lo); target = -1; } /* non-wired target */ for (i = 0; i < SBP_NUM_TARGETS; i ++) if (sbp->targets[i].fwdev == NULL && w[i] == 0) { target = i; break; } return target; } static void sbp_alloc_lun(struct sbp_target *target) { struct crom_context cc; struct csrreg *reg; struct sbp_dev *sdev, **newluns; struct sbp_softc *sbp; int maxlun, lun, i; sbp = target->sbp; crom_init_context(&cc, target->fwdev->csrrom); /* XXX shoud parse appropriate unit directories only */ maxlun = -1; while (cc.depth >= 0) { reg = crom_search_key(&cc, CROM_LUN); if (reg == NULL) break; lun = reg->val & 0xffff; SBP_DEBUG(0) printf("target %d lun %d found\n", target->target_id, lun); END_DEBUG if (maxlun < lun) maxlun = lun; crom_next(&cc); } if (maxlun < 0) printf("%s:%d no LUN found\n", device_get_nameunit(target->sbp->fd.dev), target->target_id); maxlun ++; if (maxlun >= SBP_NUM_LUNS) maxlun = SBP_NUM_LUNS; /* Invalidiate stale devices */ for (lun = 0; lun < target->num_lun; lun ++) { sdev = target->luns[lun]; if (sdev == NULL) continue; sdev->flags &= ~VALID_LUN; if (lun >= maxlun) { /* lost device */ sbp_cam_detach_sdev(sdev); sbp_free_sdev(sdev); target->luns[lun] = NULL; } } /* Reallocate */ if (maxlun != target->num_lun) { newluns = (struct sbp_dev **) realloc(target->luns, sizeof(struct sbp_dev *) * maxlun, M_SBP, M_NOWAIT | M_ZERO); if (newluns == NULL) { printf("%s: realloc failed\n", __func__); newluns = target->luns; maxlun = target->num_lun; } /* * We must zero the extended region for the case * realloc() doesn't allocate new buffer. */ if (maxlun > target->num_lun) bzero(&newluns[target->num_lun], sizeof(struct sbp_dev *) * (maxlun - target->num_lun)); target->luns = newluns; target->num_lun = maxlun; } crom_init_context(&cc, target->fwdev->csrrom); while (cc.depth >= 0) { int new = 0; reg = crom_search_key(&cc, CROM_LUN); if (reg == NULL) break; lun = reg->val & 0xffff; if (lun >= SBP_NUM_LUNS) { printf("too large lun %d\n", lun); goto next; } sdev = target->luns[lun]; if (sdev == NULL) { sdev = malloc(sizeof(struct sbp_dev), M_SBP, M_NOWAIT | M_ZERO); if (sdev == NULL) { printf("%s: malloc failed\n", __func__); goto next; } target->luns[lun] = sdev; sdev->lun_id = lun; sdev->target = target; STAILQ_INIT(&sdev->ocbs); CALLOUT_INIT(&sdev->login_callout); sdev->status = SBP_DEV_RESET; new = 1; snprintf(sdev->bustgtlun, 32, "%s:%d:%d", device_get_nameunit(sdev->target->sbp->fd.dev), sdev->target->target_id, sdev->lun_id); } sdev->flags |= VALID_LUN; sdev->type = (reg->val & 0xff0000) >> 16; if (new == 0) goto next; fwdma_malloc(sbp->fd.fc, /* alignment */ sizeof(uint32_t), SBP_DMA_SIZE, &sdev->dma, BUS_DMA_NOWAIT | BUS_DMA_COHERENT); if (sdev->dma.v_addr == NULL) { printf("%s: dma space allocation failed\n", __func__); free(sdev, M_SBP); target->luns[lun] = NULL; goto next; } sdev->login = (struct sbp_login_res *) sdev->dma.v_addr; sdev->ocb = (struct sbp_ocb *) ((char *)sdev->dma.v_addr + SBP_LOGIN_SIZE); bzero((char *)sdev->ocb, sizeof (struct sbp_ocb) * SBP_QUEUE_LEN); STAILQ_INIT(&sdev->free_ocbs); for (i = 0; i < SBP_QUEUE_LEN; i++) { struct sbp_ocb *ocb; ocb = &sdev->ocb[i]; ocb->bus_addr = sdev->dma.bus_addr + SBP_LOGIN_SIZE + sizeof(struct sbp_ocb) * i + offsetof(struct sbp_ocb, orb[0]); if (bus_dmamap_create(sbp->dmat, 0, &ocb->dmamap)) { printf("sbp_attach: cannot create dmamap\n"); /* XXX */ goto next; } + callout_handle_init(&ocb->timeout_ch); sbp_free_ocb(sdev, ocb); } next: crom_next(&cc); } for (lun = 0; lun < target->num_lun; lun ++) { sdev = target->luns[lun]; if (sdev != NULL && (sdev->flags & VALID_LUN) == 0) { sbp_cam_detach_sdev(sdev); sbp_free_sdev(sdev); target->luns[lun] = NULL; } } } static struct sbp_target * sbp_alloc_target(struct sbp_softc *sbp, struct fw_device *fwdev) { int i; struct sbp_target *target; struct crom_context cc; struct csrreg *reg; SBP_DEBUG(1) printf("sbp_alloc_target\n"); END_DEBUG i = sbp_new_target(sbp, fwdev); if (i < 0) { device_printf(sbp->fd.dev, "increase SBP_NUM_TARGETS!\n"); return NULL; } /* new target */ target = &sbp->targets[i]; target->sbp = sbp; target->fwdev = fwdev; target->target_id = i; /* XXX we may want to reload mgm port after each bus reset */ /* XXX there might be multiple management agents */ crom_init_context(&cc, target->fwdev->csrrom); reg = crom_search_key(&cc, CROM_MGM); if (reg == NULL || reg->val == 0) { printf("NULL management address\n"); target->fwdev = NULL; return NULL; } target->mgm_hi = 0xffff; target->mgm_lo = 0xf0000000 | (reg->val << 2); target->mgm_ocb_cur = NULL; SBP_DEBUG(1) printf("target:%d mgm_port: %x\n", i, target->mgm_lo); END_DEBUG STAILQ_INIT(&target->xferlist); target->n_xfer = 0; STAILQ_INIT(&target->mgm_ocb_queue); CALLOUT_INIT(&target->mgm_ocb_timeout); CALLOUT_INIT(&target->scan_callout); target->luns = NULL; target->num_lun = 0; return target; } static void sbp_probe_lun(struct sbp_dev *sdev) { struct fw_device *fwdev; struct crom_context c, *cc = &c; struct csrreg *reg; bzero(sdev->vendor, sizeof(sdev->vendor)); bzero(sdev->product, sizeof(sdev->product)); fwdev = sdev->target->fwdev; crom_init_context(cc, fwdev->csrrom); /* get vendor string */ crom_search_key(cc, CSRKEY_VENDOR); crom_next(cc); crom_parse_text(cc, sdev->vendor, sizeof(sdev->vendor)); /* skip to the unit directory for SBP-2 */ while ((reg = crom_search_key(cc, CSRKEY_VER)) != NULL) { if (reg->val == CSRVAL_T10SBP2) break; crom_next(cc); } /* get firmware revision */ reg = crom_search_key(cc, CSRKEY_FIRM_VER); if (reg != NULL) snprintf(sdev->revision, sizeof(sdev->revision), "%06x", reg->val); /* get product string */ crom_search_key(cc, CSRKEY_MODEL); crom_next(cc); crom_parse_text(cc, sdev->product, sizeof(sdev->product)); } static void sbp_login_callout(void *arg) { struct sbp_dev *sdev = (struct sbp_dev *)arg; sbp_mgm_orb(sdev, ORB_FUN_LGI, NULL); } static void sbp_login(struct sbp_dev *sdev) { struct timeval delta; struct timeval t; int ticks = 0; microtime(&delta); timevalsub(&delta, &sdev->target->sbp->last_busreset); t.tv_sec = login_delay / 1000; t.tv_usec = (login_delay % 1000) * 1000; timevalsub(&t, &delta); if (t.tv_sec >= 0 && t.tv_usec > 0) ticks = (t.tv_sec * 1000 + t.tv_usec / 1000) * hz / 1000; SBP_DEBUG(0) printf("%s: sec = %jd usec = %ld ticks = %d\n", __func__, (intmax_t)t.tv_sec, t.tv_usec, ticks); END_DEBUG callout_reset(&sdev->login_callout, ticks, sbp_login_callout, (void *)(sdev)); } #define SBP_FWDEV_ALIVE(fwdev) (((fwdev)->status == FWDEVATTACHED) \ && crom_has_specver((fwdev)->csrrom, CSRVAL_ANSIT10, CSRVAL_T10SBP2)) static void sbp_probe_target(void *arg) { struct sbp_target *target = (struct sbp_target *)arg; struct sbp_softc *sbp = target->sbp; struct sbp_dev *sdev; int i, alive; alive = SBP_FWDEV_ALIVE(target->fwdev); SBP_DEBUG(1) device_printf(sbp->fd.dev, "%s %d%salive\n", __func__, target->target_id, (!alive) ? " not " : ""); END_DEBUG sbp = target->sbp; sbp_alloc_lun(target); /* XXX untimeout mgm_ocb and dequeue */ for (i=0; i < target->num_lun; i++) { sdev = target->luns[i]; if (sdev == NULL) continue; if (alive && (sdev->status != SBP_DEV_DEAD)) { if (sdev->path != NULL) { SBP_LOCK(sbp); xpt_freeze_devq(sdev->path, 1); sdev->freeze ++; SBP_UNLOCK(sbp); } sbp_probe_lun(sdev); sbp_show_sdev_info(sdev); sbp_abort_all_ocbs(sdev, CAM_SCSI_BUS_RESET); switch (sdev->status) { case SBP_DEV_RESET: /* new or revived target */ if (auto_login) sbp_login(sdev); break; case SBP_DEV_TOATTACH: case SBP_DEV_PROBE: case SBP_DEV_ATTACHED: case SBP_DEV_RETRY: default: sbp_mgm_orb(sdev, ORB_FUN_RCN, NULL); break; } } else { switch (sdev->status) { case SBP_DEV_ATTACHED: SBP_DEBUG(0) /* the device has gone */ device_printf(sbp->fd.dev, "%s: lost target\n", __func__); END_DEBUG if (sdev->path) { SBP_LOCK(sbp); xpt_freeze_devq(sdev->path, 1); sdev->freeze ++; SBP_UNLOCK(sbp); } sdev->status = SBP_DEV_RETRY; sbp_cam_detach_sdev(sdev); sbp_free_sdev(sdev); target->luns[i] = NULL; break; case SBP_DEV_PROBE: case SBP_DEV_TOATTACH: sdev->status = SBP_DEV_RESET; break; case SBP_DEV_RETRY: case SBP_DEV_RESET: case SBP_DEV_DEAD: break; } } } } static void sbp_post_busreset(void *arg) { struct sbp_softc *sbp; sbp = (struct sbp_softc *)arg; SBP_DEBUG(0) printf("sbp_post_busreset\n"); END_DEBUG if ((sbp->sim->flags & SIMQ_FREEZED) == 0) { SBP_LOCK(sbp); xpt_freeze_simq(sbp->sim, /*count*/1); sbp->sim->flags |= SIMQ_FREEZED; SBP_UNLOCK(sbp); } microtime(&sbp->last_busreset); } static void sbp_post_explore(void *arg) { struct sbp_softc *sbp = (struct sbp_softc *)arg; struct sbp_target *target; struct fw_device *fwdev; int i, alive; SBP_DEBUG(0) printf("sbp_post_explore (sbp_cold=%d)\n", sbp_cold); END_DEBUG /* We need physical access */ if (!firewire_phydma_enable) return; if (sbp_cold > 0) sbp_cold --; #if 0 /* * XXX don't let CAM the bus rest. * CAM tries to do something with freezed (DEV_RETRY) devices. */ xpt_async(AC_BUS_RESET, sbp->path, /*arg*/ NULL); #endif /* Garbage Collection */ for(i = 0 ; i < SBP_NUM_TARGETS ; i ++){ target = &sbp->targets[i]; STAILQ_FOREACH(fwdev, &sbp->fd.fc->devices, link) if (target->fwdev == NULL || target->fwdev == fwdev) break; if (fwdev == NULL) { /* device has removed in lower driver */ sbp_cam_detach_target(target); sbp_free_target(target); } } /* traverse device list */ STAILQ_FOREACH(fwdev, &sbp->fd.fc->devices, link) { SBP_DEBUG(0) device_printf(sbp->fd.dev,"%s:: EUI:%08x%08x %s attached, state=%d\n", __func__, fwdev->eui.hi, fwdev->eui.lo, (fwdev->status != FWDEVATTACHED) ? "not" : "", fwdev->status); END_DEBUG alive = SBP_FWDEV_ALIVE(fwdev); for(i = 0 ; i < SBP_NUM_TARGETS ; i ++){ target = &sbp->targets[i]; if(target->fwdev == fwdev ) { /* known target */ break; } } if(i == SBP_NUM_TARGETS){ if (alive) { /* new target */ target = sbp_alloc_target(sbp, fwdev); if (target == NULL) continue; } else { continue; } } sbp_probe_target((void *)target); if (target->num_lun == 0) sbp_free_target(target); } SBP_LOCK(sbp); xpt_release_simq(sbp->sim, /*run queue*/TRUE); sbp->sim->flags &= ~SIMQ_FREEZED; SBP_UNLOCK(sbp); } #if NEED_RESPONSE static void sbp_loginres_callback(struct fw_xfer *xfer){ int s; struct sbp_dev *sdev; sdev = (struct sbp_dev *)xfer->sc; SBP_DEBUG(1) device_printf(sdev->target->sbp->fd.dev,"%s\n", __func__); END_DEBUG /* recycle */ s = splfw(); STAILQ_INSERT_TAIL(&sdev->target->sbp->fwb.xferlist, xfer, link); splx(s); return; } #endif static __inline void sbp_xfer_free(struct fw_xfer *xfer) { struct sbp_dev *sdev; int s; sdev = (struct sbp_dev *)xfer->sc; fw_xfer_unload(xfer); s = splfw(); SBP_LOCK(sdev->target->sbp); STAILQ_INSERT_TAIL(&sdev->target->xferlist, xfer, link); SBP_UNLOCK(sdev->target->sbp); splx(s); } static void sbp_reset_start_callback(struct fw_xfer *xfer) { struct sbp_dev *tsdev, *sdev = (struct sbp_dev *)xfer->sc; struct sbp_target *target = sdev->target; int i; if (xfer->resp != 0) { device_printf(sdev->target->sbp->fd.dev, "%s: %s failed: resp=%d\n", __func__, sdev->bustgtlun, xfer->resp); } for (i = 0; i < target->num_lun; i++) { tsdev = target->luns[i]; if (tsdev != NULL && tsdev->status == SBP_DEV_LOGIN) sbp_login(tsdev); } } static void sbp_reset_start(struct sbp_dev *sdev) { struct fw_xfer *xfer; struct fw_pkt *fp; SBP_DEBUG(0) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__,sdev->bustgtlun); END_DEBUG xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0); xfer->hand = sbp_reset_start_callback; fp = &xfer->send.hdr; fp->mode.wreqq.dest_hi = 0xffff; fp->mode.wreqq.dest_lo = 0xf0000000 | RESET_START; fp->mode.wreqq.data = htonl(0xf); fw_asyreq(xfer->fc, -1, xfer); } static void sbp_mgm_callback(struct fw_xfer *xfer) { struct sbp_dev *sdev; int resp; sdev = (struct sbp_dev *)xfer->sc; SBP_DEBUG(1) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__, sdev->bustgtlun); END_DEBUG resp = xfer->resp; sbp_xfer_free(xfer); return; } static struct sbp_dev * sbp_next_dev(struct sbp_target *target, int lun) { struct sbp_dev **sdevp; int i; for (i = lun, sdevp = &target->luns[lun]; i < target->num_lun; i++, sdevp++) if (*sdevp != NULL && (*sdevp)->status == SBP_DEV_PROBE) return(*sdevp); return(NULL); } #define SCAN_PRI 1 static void sbp_cam_scan_lun(struct cam_periph *periph, union ccb *ccb) { struct sbp_target *target; struct sbp_dev *sdev; sdev = (struct sbp_dev *) ccb->ccb_h.ccb_sdev_ptr; target = sdev->target; SBP_DEBUG(0) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__, sdev->bustgtlun); END_DEBUG if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { sdev->status = SBP_DEV_ATTACHED; } else { device_printf(sdev->target->sbp->fd.dev, "%s:%s failed\n", __func__, sdev->bustgtlun); } sdev = sbp_next_dev(target, sdev->lun_id + 1); if (sdev == NULL) { free(ccb, M_SBP); return; } /* reuse ccb */ xpt_setup_ccb(&ccb->ccb_h, sdev->path, SCAN_PRI); ccb->ccb_h.ccb_sdev_ptr = sdev; xpt_action(ccb); xpt_release_devq(sdev->path, sdev->freeze, TRUE); sdev->freeze = 1; } static void sbp_cam_scan_target(void *arg) { struct sbp_target *target = (struct sbp_target *)arg; struct sbp_dev *sdev; union ccb *ccb; sdev = sbp_next_dev(target, 0); if (sdev == NULL) { printf("sbp_cam_scan_target: nothing to do for target%d\n", target->target_id); return; } SBP_DEBUG(0) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__, sdev->bustgtlun); END_DEBUG ccb = malloc(sizeof(union ccb), M_SBP, M_NOWAIT | M_ZERO); if (ccb == NULL) { printf("sbp_cam_scan_target: malloc failed\n"); return; } xpt_setup_ccb(&ccb->ccb_h, sdev->path, SCAN_PRI); ccb->ccb_h.func_code = XPT_SCAN_LUN; ccb->ccb_h.cbfcnp = sbp_cam_scan_lun; ccb->ccb_h.flags |= CAM_DEV_QFREEZE; ccb->crcn.flags = CAM_FLAG_NONE; ccb->ccb_h.ccb_sdev_ptr = sdev; /* The scan is in progress now. */ SBP_LOCK(target->sbp); xpt_action(ccb); xpt_release_devq(sdev->path, sdev->freeze, TRUE); sdev->freeze = 1; SBP_UNLOCK(target->sbp); } static __inline void sbp_scan_dev(struct sbp_dev *sdev) { sdev->status = SBP_DEV_PROBE; callout_reset(&sdev->target->scan_callout, scan_delay * hz / 1000, sbp_cam_scan_target, (void *)sdev->target); } static void sbp_do_attach(struct fw_xfer *xfer) { struct sbp_dev *sdev; struct sbp_target *target; struct sbp_softc *sbp; sdev = (struct sbp_dev *)xfer->sc; target = sdev->target; sbp = target->sbp; SBP_DEBUG(0) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__, sdev->bustgtlun); END_DEBUG sbp_xfer_free(xfer); if (sdev->path == NULL) xpt_create_path(&sdev->path, NULL, cam_sim_path(target->sbp->sim), target->target_id, sdev->lun_id); /* * Let CAM scan the bus if we are in the boot process. * XXX xpt_scan_bus cannot detect LUN larger than 0 * if LUN 0 doesn't exists. */ if (sbp_cold > 0) { sdev->status = SBP_DEV_ATTACHED; return; } sbp_scan_dev(sdev); return; } static void sbp_agent_reset_callback(struct fw_xfer *xfer) { struct sbp_dev *sdev; sdev = (struct sbp_dev *)xfer->sc; SBP_DEBUG(1) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__, sdev->bustgtlun); END_DEBUG if (xfer->resp != 0) { device_printf(sdev->target->sbp->fd.dev, "%s:%s resp=%d\n", __func__, sdev->bustgtlun, xfer->resp); } sbp_xfer_free(xfer); if (sdev->path) { SBP_LOCK(sdev->target->sbp); xpt_release_devq(sdev->path, sdev->freeze, TRUE); sdev->freeze = 0; SBP_UNLOCK(sdev->target->sbp); } } static void sbp_agent_reset(struct sbp_dev *sdev) { struct fw_xfer *xfer; struct fw_pkt *fp; SBP_DEBUG(0) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__, sdev->bustgtlun); END_DEBUG xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0x04); if (xfer == NULL) return; if (sdev->status == SBP_DEV_ATTACHED || sdev->status == SBP_DEV_PROBE) xfer->hand = sbp_agent_reset_callback; else xfer->hand = sbp_do_attach; fp = &xfer->send.hdr; fp->mode.wreqq.data = htonl(0xf); fw_asyreq(xfer->fc, -1, xfer); sbp_abort_all_ocbs(sdev, CAM_BDR_SENT); } static void sbp_busy_timeout_callback(struct fw_xfer *xfer) { struct sbp_dev *sdev; sdev = (struct sbp_dev *)xfer->sc; SBP_DEBUG(1) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__, sdev->bustgtlun); END_DEBUG sbp_xfer_free(xfer); sbp_agent_reset(sdev); } static void sbp_busy_timeout(struct sbp_dev *sdev) { struct fw_pkt *fp; struct fw_xfer *xfer; SBP_DEBUG(0) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__, sdev->bustgtlun); END_DEBUG xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0); xfer->hand = sbp_busy_timeout_callback; fp = &xfer->send.hdr; fp->mode.wreqq.dest_hi = 0xffff; fp->mode.wreqq.dest_lo = 0xf0000000 | BUSY_TIMEOUT; fp->mode.wreqq.data = htonl((1 << (13+12)) | 0xf); fw_asyreq(xfer->fc, -1, xfer); } static void sbp_orb_pointer_callback(struct fw_xfer *xfer) { struct sbp_dev *sdev; sdev = (struct sbp_dev *)xfer->sc; SBP_DEBUG(2) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__, sdev->bustgtlun); END_DEBUG if (xfer->resp != 0) { /* XXX */ printf("%s: xfer->resp = %d\n", __func__, xfer->resp); } sbp_xfer_free(xfer); SBP_LOCK(sdev->target->sbp); sdev->flags &= ~ORB_POINTER_ACTIVE; if ((sdev->flags & ORB_POINTER_NEED) != 0) { struct sbp_ocb *ocb; sdev->flags &= ~ORB_POINTER_NEED; ocb = STAILQ_FIRST(&sdev->ocbs); if (ocb != NULL) sbp_orb_pointer(sdev, ocb); } SBP_UNLOCK(sdev->target->sbp); return; } static void sbp_orb_pointer(struct sbp_dev *sdev, struct sbp_ocb *ocb) { struct fw_xfer *xfer; struct fw_pkt *fp; SBP_DEBUG(1) device_printf(sdev->target->sbp->fd.dev, "%s:%s 0x%08x\n", __func__, sdev->bustgtlun, (uint32_t)ocb->bus_addr); END_DEBUG mtx_assert(&sdev->target->sbp->mtx, MA_OWNED); if ((sdev->flags & ORB_POINTER_ACTIVE) != 0) { SBP_DEBUG(0) printf("%s: orb pointer active\n", __func__); END_DEBUG sdev->flags |= ORB_POINTER_NEED; return; } sdev->flags |= ORB_POINTER_ACTIVE; xfer = sbp_write_cmd_locked(sdev, FWTCODE_WREQB, 0x08); if (xfer == NULL) return; xfer->hand = sbp_orb_pointer_callback; fp = &xfer->send.hdr; fp->mode.wreqb.len = 8; fp->mode.wreqb.extcode = 0; xfer->send.payload[0] = htonl(((sdev->target->sbp->fd.fc->nodeid | FWLOCALBUS )<< 16)); xfer->send.payload[1] = htonl((uint32_t)ocb->bus_addr); /* * sbp_xfer_free() will attempt to acquire * the SBP lock on entrance. Also, this removes * a LOR between the firewire layer and sbp */ SBP_UNLOCK(sdev->target->sbp); if(fw_asyreq(xfer->fc, -1, xfer) != 0){ sbp_xfer_free(xfer); ocb->ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ocb->ccb); } SBP_LOCK(sdev->target->sbp); } static void sbp_doorbell_callback(struct fw_xfer *xfer) { struct sbp_dev *sdev; sdev = (struct sbp_dev *)xfer->sc; SBP_DEBUG(1) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__, sdev->bustgtlun); END_DEBUG if (xfer->resp != 0) { /* XXX */ device_printf(sdev->target->sbp->fd.dev, "%s: xfer->resp = %d\n", __func__, xfer->resp); } sbp_xfer_free(xfer); sdev->flags &= ~ORB_DOORBELL_ACTIVE; if ((sdev->flags & ORB_DOORBELL_NEED) != 0) { sdev->flags &= ~ORB_DOORBELL_NEED; SBP_LOCK(sdev->target->sbp); sbp_doorbell(sdev); SBP_UNLOCK(sdev->target->sbp); } return; } static void sbp_doorbell(struct sbp_dev *sdev) { struct fw_xfer *xfer; struct fw_pkt *fp; SBP_DEBUG(1) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__, sdev->bustgtlun); END_DEBUG if ((sdev->flags & ORB_DOORBELL_ACTIVE) != 0) { sdev->flags |= ORB_DOORBELL_NEED; return; } sdev->flags |= ORB_DOORBELL_ACTIVE; xfer = sbp_write_cmd_locked(sdev, FWTCODE_WREQQ, 0x10); if (xfer == NULL) return; xfer->hand = sbp_doorbell_callback; fp = &xfer->send.hdr; fp->mode.wreqq.data = htonl(0xf); fw_asyreq(xfer->fc, -1, xfer); } static struct fw_xfer * sbp_write_cmd_locked(struct sbp_dev *sdev, int tcode, int offset) { struct fw_xfer *xfer; struct fw_pkt *fp; struct sbp_target *target; int s, new = 0; mtx_assert(&sdev->target->sbp->mtx, MA_OWNED); target = sdev->target; s = splfw(); xfer = STAILQ_FIRST(&target->xferlist); if (xfer == NULL) { if (target->n_xfer > 5 /* XXX */) { printf("sbp: no more xfer for this target\n"); splx(s); return(NULL); } xfer = fw_xfer_alloc_buf(M_SBP, 8, 0); if(xfer == NULL){ printf("sbp: fw_xfer_alloc_buf failed\n"); splx(s); return NULL; } target->n_xfer ++; if (debug) printf("sbp: alloc %d xfer\n", target->n_xfer); new = 1; } else { STAILQ_REMOVE_HEAD(&target->xferlist, link); } splx(s); if (new) { xfer->recv.pay_len = 0; xfer->send.spd = min(sdev->target->fwdev->speed, max_speed); xfer->fc = sdev->target->sbp->fd.fc; } if (tcode == FWTCODE_WREQB) xfer->send.pay_len = 8; else xfer->send.pay_len = 0; xfer->sc = (caddr_t)sdev; fp = &xfer->send.hdr; fp->mode.wreqq.dest_hi = sdev->login->cmd_hi; fp->mode.wreqq.dest_lo = sdev->login->cmd_lo + offset; fp->mode.wreqq.tlrt = 0; fp->mode.wreqq.tcode = tcode; fp->mode.wreqq.pri = 0; fp->mode.wreqq.dst = FWLOCALBUS | sdev->target->fwdev->dst; return xfer; } static struct fw_xfer * sbp_write_cmd(struct sbp_dev *sdev, int tcode, int offset) { struct sbp_softc *sbp = sdev->target->sbp; struct fw_xfer *xfer; SBP_LOCK(sbp); xfer = sbp_write_cmd_locked(sdev, tcode, offset); SBP_UNLOCK(sbp); return (xfer); } static void sbp_mgm_orb(struct sbp_dev *sdev, int func, struct sbp_ocb *aocb) { struct fw_xfer *xfer; struct fw_pkt *fp; struct sbp_ocb *ocb; struct sbp_target *target; int s, nid; target = sdev->target; nid = target->sbp->fd.fc->nodeid | FWLOCALBUS; s = splfw(); SBP_LOCK(target->sbp); if (func == ORB_FUN_RUNQUEUE) { ocb = STAILQ_FIRST(&target->mgm_ocb_queue); if (target->mgm_ocb_cur != NULL || ocb == NULL) { SBP_UNLOCK(target->sbp); splx(s); return; } STAILQ_REMOVE_HEAD(&target->mgm_ocb_queue, ocb); SBP_UNLOCK(target->sbp); goto start; } if ((ocb = sbp_get_ocb(sdev)) == NULL) { SBP_UNLOCK(target->sbp); splx(s); /* XXX */ return; } SBP_UNLOCK(target->sbp); ocb->flags = OCB_ACT_MGM; ocb->sdev = sdev; bzero((void *)ocb->orb, sizeof(ocb->orb)); ocb->orb[6] = htonl((nid << 16) | SBP_BIND_HI); ocb->orb[7] = htonl(SBP_DEV2ADDR(target->target_id, sdev->lun_id)); SBP_DEBUG(0) device_printf(sdev->target->sbp->fd.dev, "%s:%s %s\n", __func__,sdev->bustgtlun, orb_fun_name[(func>>16)&0xf]); END_DEBUG switch (func) { case ORB_FUN_LGI: ocb->orb[0] = ocb->orb[1] = 0; /* password */ ocb->orb[2] = htonl(nid << 16); ocb->orb[3] = htonl(sdev->dma.bus_addr); ocb->orb[4] = htonl(ORB_NOTIFY | sdev->lun_id); if (ex_login) ocb->orb[4] |= htonl(ORB_EXV); ocb->orb[5] = htonl(SBP_LOGIN_SIZE); fwdma_sync(&sdev->dma, BUS_DMASYNC_PREREAD); break; case ORB_FUN_ATA: ocb->orb[0] = htonl((0 << 16) | 0); ocb->orb[1] = htonl(aocb->bus_addr & 0xffffffff); /* fall through */ case ORB_FUN_RCN: case ORB_FUN_LGO: case ORB_FUN_LUR: case ORB_FUN_RST: case ORB_FUN_ATS: ocb->orb[4] = htonl(ORB_NOTIFY | func | sdev->login->id); break; } if (target->mgm_ocb_cur != NULL) { /* there is a standing ORB */ SBP_LOCK(target->sbp); STAILQ_INSERT_TAIL(&sdev->target->mgm_ocb_queue, ocb, ocb); SBP_UNLOCK(target->sbp); splx(s); return; } start: target->mgm_ocb_cur = ocb; splx(s); callout_reset(&target->mgm_ocb_timeout, 5*hz, sbp_mgm_timeout, (caddr_t)ocb); xfer = sbp_write_cmd(sdev, FWTCODE_WREQB, 0); if(xfer == NULL){ return; } xfer->hand = sbp_mgm_callback; fp = &xfer->send.hdr; fp->mode.wreqb.dest_hi = sdev->target->mgm_hi; fp->mode.wreqb.dest_lo = sdev->target->mgm_lo; fp->mode.wreqb.len = 8; fp->mode.wreqb.extcode = 0; xfer->send.payload[0] = htonl(nid << 16); xfer->send.payload[1] = htonl(ocb->bus_addr & 0xffffffff); fw_asyreq(xfer->fc, -1, xfer); } static void sbp_print_scsi_cmd(struct sbp_ocb *ocb) { struct ccb_scsiio *csio; csio = &ocb->ccb->csio; printf("%s:%d:%d XPT_SCSI_IO: " "cmd: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x" ", flags: 0x%02x, " "%db cmd/%db data/%db sense\n", device_get_nameunit(ocb->sdev->target->sbp->fd.dev), ocb->ccb->ccb_h.target_id, ocb->ccb->ccb_h.target_lun, csio->cdb_io.cdb_bytes[0], csio->cdb_io.cdb_bytes[1], csio->cdb_io.cdb_bytes[2], csio->cdb_io.cdb_bytes[3], csio->cdb_io.cdb_bytes[4], csio->cdb_io.cdb_bytes[5], csio->cdb_io.cdb_bytes[6], csio->cdb_io.cdb_bytes[7], csio->cdb_io.cdb_bytes[8], csio->cdb_io.cdb_bytes[9], ocb->ccb->ccb_h.flags & CAM_DIR_MASK, csio->cdb_len, csio->dxfer_len, csio->sense_len); } static void sbp_scsi_status(struct sbp_status *sbp_status, struct sbp_ocb *ocb) { struct sbp_cmd_status *sbp_cmd_status; struct scsi_sense_data_fixed *sense; sbp_cmd_status = (struct sbp_cmd_status *)sbp_status->data; sense = (struct scsi_sense_data_fixed *)&ocb->ccb->csio.sense_data; SBP_DEBUG(0) sbp_print_scsi_cmd(ocb); /* XXX need decode status */ printf("%s: SCSI status %x sfmt %x valid %x key %x code %x qlfr %x len %d\n", ocb->sdev->bustgtlun, sbp_cmd_status->status, sbp_cmd_status->sfmt, sbp_cmd_status->valid, sbp_cmd_status->s_key, sbp_cmd_status->s_code, sbp_cmd_status->s_qlfr, sbp_status->len); END_DEBUG switch (sbp_cmd_status->status) { case SCSI_STATUS_CHECK_COND: case SCSI_STATUS_BUSY: case SCSI_STATUS_CMD_TERMINATED: if(sbp_cmd_status->sfmt == SBP_SFMT_CURR){ sense->error_code = SSD_CURRENT_ERROR; }else{ sense->error_code = SSD_DEFERRED_ERROR; } if(sbp_cmd_status->valid) sense->error_code |= SSD_ERRCODE_VALID; sense->flags = sbp_cmd_status->s_key; if(sbp_cmd_status->mark) sense->flags |= SSD_FILEMARK; if(sbp_cmd_status->eom) sense->flags |= SSD_EOM; if(sbp_cmd_status->ill_len) sense->flags |= SSD_ILI; bcopy(&sbp_cmd_status->info, &sense->info[0], 4); if (sbp_status->len <= 1) /* XXX not scsi status. shouldn't be happened */ sense->extra_len = 0; else if (sbp_status->len <= 4) /* add_sense_code(_qual), info, cmd_spec_info */ sense->extra_len = 6; else /* fru, sense_key_spec */ sense->extra_len = 10; bcopy(&sbp_cmd_status->cdb, &sense->cmd_spec_info[0], 4); sense->add_sense_code = sbp_cmd_status->s_code; sense->add_sense_code_qual = sbp_cmd_status->s_qlfr; sense->fru = sbp_cmd_status->fru; bcopy(&sbp_cmd_status->s_keydep[0], &sense->sense_key_spec[0], 3); ocb->ccb->csio.scsi_status = sbp_cmd_status->status; ocb->ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; /* { uint8_t j, *tmp; tmp = sense; for( j = 0 ; j < 32 ; j+=8){ printf("sense %02x%02x %02x%02x %02x%02x %02x%02x\n", tmp[j], tmp[j+1], tmp[j+2], tmp[j+3], tmp[j+4], tmp[j+5], tmp[j+6], tmp[j+7]); } } */ break; default: device_printf(ocb->sdev->target->sbp->fd.dev, "%s:%s unknown scsi status 0x%x\n", __func__, ocb->sdev->bustgtlun, sbp_cmd_status->status); } } static void sbp_fix_inq_data(struct sbp_ocb *ocb) { union ccb *ccb; struct sbp_dev *sdev; struct scsi_inquiry_data *inq; ccb = ocb->ccb; sdev = ocb->sdev; if (ccb->csio.cdb_io.cdb_bytes[1] & SI_EVPD) return; SBP_DEBUG(1) device_printf(sdev->target->sbp->fd.dev, "%s:%s\n", __func__, sdev->bustgtlun); END_DEBUG inq = (struct scsi_inquiry_data *) ccb->csio.data_ptr; switch (SID_TYPE(inq)) { case T_DIRECT: #if 0 /* * XXX Convert Direct Access device to RBC. * I've never seen FireWire DA devices which support READ_6. */ if (SID_TYPE(inq) == T_DIRECT) inq->device |= T_RBC; /* T_DIRECT == 0 */ #endif /* fall through */ case T_RBC: /* * Override vendor/product/revision information. * Some devices sometimes return strange strings. */ #if 1 bcopy(sdev->vendor, inq->vendor, sizeof(inq->vendor)); bcopy(sdev->product, inq->product, sizeof(inq->product)); bcopy(sdev->revision+2, inq->revision, sizeof(inq->revision)); #endif break; } /* * Force to enable/disable tagged queuing. * XXX CAM also checks SCP_QUEUE_DQUE flag in the control mode page. */ if (sbp_tags > 0) inq->flags |= SID_CmdQue; else if (sbp_tags < 0) inq->flags &= ~SID_CmdQue; } static void sbp_recv1(struct fw_xfer *xfer) { struct fw_pkt *rfp; #if NEED_RESPONSE struct fw_pkt *sfp; #endif struct sbp_softc *sbp; struct sbp_dev *sdev; struct sbp_ocb *ocb; struct sbp_login_res *login_res = NULL; struct sbp_status *sbp_status; struct sbp_target *target; int orb_fun, status_valid0, status_valid, t, l, reset_agent = 0; uint32_t addr; /* uint32_t *ld; ld = xfer->recv.buf; printf("sbp %x %d %d %08x %08x %08x %08x\n", xfer->resp, xfer->recv.len, xfer->recv.off, ntohl(ld[0]), ntohl(ld[1]), ntohl(ld[2]), ntohl(ld[3])); printf("sbp %08x %08x %08x %08x\n", ntohl(ld[4]), ntohl(ld[5]), ntohl(ld[6]), ntohl(ld[7])); printf("sbp %08x %08x %08x %08x\n", ntohl(ld[8]), ntohl(ld[9]), ntohl(ld[10]), ntohl(ld[11])); */ sbp = (struct sbp_softc *)xfer->sc; if (xfer->resp != 0){ printf("sbp_recv: xfer->resp = %d\n", xfer->resp); goto done0; } if (xfer->recv.payload == NULL){ printf("sbp_recv: xfer->recv.payload == NULL\n"); goto done0; } rfp = &xfer->recv.hdr; if(rfp->mode.wreqb.tcode != FWTCODE_WREQB){ printf("sbp_recv: tcode = %d\n", rfp->mode.wreqb.tcode); goto done0; } sbp_status = (struct sbp_status *)xfer->recv.payload; addr = rfp->mode.wreqb.dest_lo; SBP_DEBUG(2) printf("received address 0x%x\n", addr); END_DEBUG t = SBP_ADDR2TRG(addr); if (t >= SBP_NUM_TARGETS) { device_printf(sbp->fd.dev, "sbp_recv1: invalid target %d\n", t); goto done0; } target = &sbp->targets[t]; l = SBP_ADDR2LUN(addr); if (l >= target->num_lun || target->luns[l] == NULL) { device_printf(sbp->fd.dev, "sbp_recv1: invalid lun %d (target=%d)\n", l, t); goto done0; } sdev = target->luns[l]; ocb = NULL; switch (sbp_status->src) { case 0: case 1: /* check mgm_ocb_cur first */ ocb = target->mgm_ocb_cur; if (ocb != NULL) { if (OCB_MATCH(ocb, sbp_status)) { callout_stop(&target->mgm_ocb_timeout); target->mgm_ocb_cur = NULL; break; } } ocb = sbp_dequeue_ocb(sdev, sbp_status); if (ocb == NULL) { device_printf(sdev->target->sbp->fd.dev, #if defined(__DragonFly__) || __FreeBSD_version < 500000 "%s:%s No ocb(%lx) on the queue\n", #else "%s:%s No ocb(%x) on the queue\n", #endif __func__,sdev->bustgtlun, ntohl(sbp_status->orb_lo)); } break; case 2: /* unsolicit */ device_printf(sdev->target->sbp->fd.dev, "%s:%s unsolicit status received\n", __func__, sdev->bustgtlun); break; default: device_printf(sdev->target->sbp->fd.dev, "%s:%s unknown sbp_status->src\n", __func__, sdev->bustgtlun); } status_valid0 = (sbp_status->src < 2 && sbp_status->resp == ORB_RES_CMPL && sbp_status->dead == 0); status_valid = (status_valid0 && sbp_status->status == 0); if (!status_valid0 || debug > 2){ int status; SBP_DEBUG(0) device_printf(sdev->target->sbp->fd.dev, "%s:%s ORB status src:%x resp:%x dead:%x" #if defined(__DragonFly__) || __FreeBSD_version < 500000 " len:%x stat:%x orb:%x%08lx\n", #else " len:%x stat:%x orb:%x%08x\n", #endif __func__, sdev->bustgtlun, sbp_status->src, sbp_status->resp, sbp_status->dead, sbp_status->len, sbp_status->status, ntohs(sbp_status->orb_hi), ntohl(sbp_status->orb_lo)); END_DEBUG device_printf(sdev->target->sbp->fd.dev, "%s\n", sdev->bustgtlun); status = sbp_status->status; switch(sbp_status->resp) { case 0: if (status > MAX_ORB_STATUS0) printf("%s\n", orb_status0[MAX_ORB_STATUS0]); else printf("%s\n", orb_status0[status]); break; case 1: printf("Obj: %s, Error: %s\n", orb_status1_object[(status>>6) & 3], orb_status1_serial_bus_error[status & 0xf]); break; case 2: printf("Illegal request\n"); break; case 3: printf("Vendor dependent\n"); break; default: printf("unknown respose code %d\n", sbp_status->resp); } } /* we have to reset the fetch agent if it's dead */ if (sbp_status->dead) { if (sdev->path) { SBP_LOCK(sbp); xpt_freeze_devq(sdev->path, 1); sdev->freeze ++; SBP_UNLOCK(sbp); } reset_agent = 1; } if (ocb == NULL) goto done; switch(ntohl(ocb->orb[4]) & ORB_FMT_MSK){ case ORB_FMT_NOP: break; case ORB_FMT_VED: break; case ORB_FMT_STD: switch(ocb->flags) { case OCB_ACT_MGM: orb_fun = ntohl(ocb->orb[4]) & ORB_FUN_MSK; reset_agent = 0; switch(orb_fun) { case ORB_FUN_LGI: fwdma_sync(&sdev->dma, BUS_DMASYNC_POSTREAD); login_res = sdev->login; login_res->len = ntohs(login_res->len); login_res->id = ntohs(login_res->id); login_res->cmd_hi = ntohs(login_res->cmd_hi); login_res->cmd_lo = ntohl(login_res->cmd_lo); if (status_valid) { SBP_DEBUG(0) device_printf(sdev->target->sbp->fd.dev, "%s:%s login: len %d, ID %d, cmd %08x%08x, recon_hold %d\n", __func__, sdev->bustgtlun, login_res->len, login_res->id, login_res->cmd_hi, login_res->cmd_lo, ntohs(login_res->recon_hold)); END_DEBUG sbp_busy_timeout(sdev); } else { /* forgot logout? */ device_printf(sdev->target->sbp->fd.dev, "%s:%s login failed\n", __func__, sdev->bustgtlun); sdev->status = SBP_DEV_RESET; } break; case ORB_FUN_RCN: login_res = sdev->login; if (status_valid) { SBP_DEBUG(0) device_printf(sdev->target->sbp->fd.dev, "%s:%s reconnect: len %d, ID %d, cmd %08x%08x\n", __func__, sdev->bustgtlun, login_res->len, login_res->id, login_res->cmd_hi, login_res->cmd_lo); END_DEBUG if (sdev->status == SBP_DEV_ATTACHED) sbp_scan_dev(sdev); else sbp_agent_reset(sdev); } else { /* reconnection hold time exceed? */ SBP_DEBUG(0) device_printf(sdev->target->sbp->fd.dev, "%s:%s reconnect failed\n", __func__, sdev->bustgtlun); END_DEBUG sbp_login(sdev); } break; case ORB_FUN_LGO: sdev->status = SBP_DEV_RESET; break; case ORB_FUN_RST: sbp_busy_timeout(sdev); break; case ORB_FUN_LUR: case ORB_FUN_ATA: case ORB_FUN_ATS: sbp_agent_reset(sdev); break; default: device_printf(sdev->target->sbp->fd.dev, "%s:%s unknown function %d\n", __func__, sdev->bustgtlun, orb_fun); break; } sbp_mgm_orb(sdev, ORB_FUN_RUNQUEUE, NULL); break; case OCB_ACT_CMD: sdev->timeout = 0; if(ocb->ccb != NULL){ union ccb *ccb; ccb = ocb->ccb; if(sbp_status->len > 1){ sbp_scsi_status(sbp_status, ocb); }else{ if(sbp_status->resp != ORB_RES_CMPL){ ccb->ccb_h.status = CAM_REQ_CMP_ERR; }else{ ccb->ccb_h.status = CAM_REQ_CMP; } } /* fix up inq data */ if (ccb->csio.cdb_io.cdb_bytes[0] == INQUIRY) sbp_fix_inq_data(ocb); SBP_LOCK(sbp); xpt_done(ccb); SBP_UNLOCK(sbp); } break; default: break; } } if (!use_doorbell) sbp_free_ocb(sdev, ocb); done: if (reset_agent) sbp_agent_reset(sdev); done0: xfer->recv.pay_len = SBP_RECV_LEN; /* The received packet is usually small enough to be stored within * the buffer. In that case, the controller return ack_complete and * no respose is necessary. * * XXX fwohci.c and firewire.c should inform event_code such as * ack_complete or ack_pending to upper driver. */ #if NEED_RESPONSE xfer->send.off = 0; sfp = (struct fw_pkt *)xfer->send.buf; sfp->mode.wres.dst = rfp->mode.wreqb.src; xfer->dst = sfp->mode.wres.dst; xfer->spd = min(sdev->target->fwdev->speed, max_speed); xfer->hand = sbp_loginres_callback; sfp->mode.wres.tlrt = rfp->mode.wreqb.tlrt; sfp->mode.wres.tcode = FWTCODE_WRES; sfp->mode.wres.rtcode = 0; sfp->mode.wres.pri = 0; fw_asyreq(xfer->fc, -1, xfer); #else /* recycle */ /* we don't need a lock here because bottom half is serialized */ STAILQ_INSERT_TAIL(&sbp->fwb.xferlist, xfer, link); #endif return; } static void sbp_recv(struct fw_xfer *xfer) { int s; s = splcam(); sbp_recv1(xfer); splx(s); } /* * sbp_attach() */ static int sbp_attach(device_t dev) { struct sbp_softc *sbp; struct cam_devq *devq; struct firewire_comm *fc; int i, s, error; if (DFLTPHYS > SBP_MAXPHYS) device_printf(dev, "Warning, DFLTPHYS(%dKB) is larger than " "SBP_MAXPHYS(%dKB).\n", DFLTPHYS / 1024, SBP_MAXPHYS / 1024); if (!firewire_phydma_enable) device_printf(dev, "Warning, hw.firewire.phydma_enable must be 1 " "for SBP over FireWire.\n"); SBP_DEBUG(0) printf("sbp_attach (cold=%d)\n", cold); END_DEBUG if (cold) sbp_cold ++; sbp = ((struct sbp_softc *)device_get_softc(dev)); bzero(sbp, sizeof(struct sbp_softc)); sbp->fd.dev = dev; sbp->fd.fc = fc = device_get_ivars(dev); mtx_init(&sbp->mtx, "sbp", NULL, MTX_DEF); if (max_speed < 0) max_speed = fc->speed; error = bus_dma_tag_create(/*parent*/fc->dmat, /* XXX shoud be 4 for sane backend? */ /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/0x100000, /*nsegments*/SBP_IND_MAX, /*maxsegsz*/SBP_SEG_MAX, /*flags*/BUS_DMA_ALLOCNOW, #if defined(__FreeBSD__) && __FreeBSD_version >= 501102 /*lockfunc*/busdma_lock_mutex, /*lockarg*/&sbp->mtx, #endif &sbp->dmat); if (error != 0) { printf("sbp_attach: Could not allocate DMA tag " "- error %d\n", error); return (ENOMEM); } devq = cam_simq_alloc(/*maxopenings*/SBP_NUM_OCB); if (devq == NULL) return (ENXIO); for( i = 0 ; i < SBP_NUM_TARGETS ; i++){ sbp->targets[i].fwdev = NULL; sbp->targets[i].luns = NULL; } sbp->sim = cam_sim_alloc(sbp_action, sbp_poll, "sbp", sbp, device_get_unit(dev), &sbp->mtx, /*untagged*/ 1, /*tagged*/ SBP_QUEUE_LEN - 1, devq); if (sbp->sim == NULL) { cam_simq_free(devq); return (ENXIO); } SBP_LOCK(sbp); if (xpt_bus_register(sbp->sim, dev, /*bus*/0) != CAM_SUCCESS) goto fail; if (xpt_create_path(&sbp->path, NULL, cam_sim_path(sbp->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(sbp->sim)); goto fail; } SBP_UNLOCK(sbp); /* We reserve 16 bit space (4 bytes X 64 targets X 256 luns) */ sbp->fwb.start = ((u_int64_t)SBP_BIND_HI << 32) | SBP_DEV2ADDR(0, 0); sbp->fwb.end = sbp->fwb.start + 0xffff; /* pre-allocate xfer */ STAILQ_INIT(&sbp->fwb.xferlist); fw_xferlist_add(&sbp->fwb.xferlist, M_SBP, /*send*/ 0, /*recv*/ SBP_RECV_LEN, SBP_NUM_OCB/2, fc, (void *)sbp, sbp_recv); fw_bindadd(fc, &sbp->fwb); sbp->fd.post_busreset = sbp_post_busreset; sbp->fd.post_explore = sbp_post_explore; if (fc->status != -1) { s = splfw(); sbp_post_busreset((void *)sbp); sbp_post_explore((void *)sbp); splx(s); } SBP_LOCK(sbp); xpt_async(AC_BUS_RESET, sbp->path, /*arg*/ NULL); SBP_UNLOCK(sbp); return (0); fail: SBP_UNLOCK(sbp); cam_sim_free(sbp->sim, /*free_devq*/TRUE); return (ENXIO); } static int sbp_logout_all(struct sbp_softc *sbp) { struct sbp_target *target; struct sbp_dev *sdev; int i, j; SBP_DEBUG(0) printf("sbp_logout_all\n"); END_DEBUG for (i = 0 ; i < SBP_NUM_TARGETS ; i ++) { target = &sbp->targets[i]; if (target->luns == NULL) continue; for (j = 0; j < target->num_lun; j++) { sdev = target->luns[j]; if (sdev == NULL) continue; callout_stop(&sdev->login_callout); if (sdev->status >= SBP_DEV_TOATTACH && sdev->status <= SBP_DEV_ATTACHED) sbp_mgm_orb(sdev, ORB_FUN_LGO, NULL); } } return 0; } static int sbp_shutdown(device_t dev) { struct sbp_softc *sbp = ((struct sbp_softc *)device_get_softc(dev)); sbp_logout_all(sbp); return (0); } static void sbp_free_sdev(struct sbp_dev *sdev) { int i; if (sdev == NULL) return; for (i = 0; i < SBP_QUEUE_LEN; i++) bus_dmamap_destroy(sdev->target->sbp->dmat, sdev->ocb[i].dmamap); fwdma_free(sdev->target->sbp->fd.fc, &sdev->dma); free(sdev, M_SBP); sdev = NULL; } static void sbp_free_target(struct sbp_target *target) { struct sbp_softc *sbp; struct fw_xfer *xfer, *next; int i; if (target->luns == NULL) return; callout_stop(&target->mgm_ocb_timeout); sbp = target->sbp; for (i = 0; i < target->num_lun; i++) sbp_free_sdev(target->luns[i]); for (xfer = STAILQ_FIRST(&target->xferlist); xfer != NULL; xfer = next) { next = STAILQ_NEXT(xfer, link); fw_xfer_free_buf(xfer); } STAILQ_INIT(&target->xferlist); free(target->luns, M_SBP); target->num_lun = 0; target->luns = NULL; target->fwdev = NULL; } static int sbp_detach(device_t dev) { struct sbp_softc *sbp = ((struct sbp_softc *)device_get_softc(dev)); struct firewire_comm *fc = sbp->fd.fc; int i; SBP_DEBUG(0) printf("sbp_detach\n"); END_DEBUG for (i = 0; i < SBP_NUM_TARGETS; i ++) sbp_cam_detach_target(&sbp->targets[i]); SBP_LOCK(sbp); xpt_async(AC_LOST_DEVICE, sbp->path, NULL); xpt_free_path(sbp->path); xpt_bus_deregister(cam_sim_path(sbp->sim)); cam_sim_free(sbp->sim, /*free_devq*/ TRUE); SBP_UNLOCK(sbp); sbp_logout_all(sbp); /* XXX wait for logout completion */ pause("sbpdtc", hz/2); for (i = 0 ; i < SBP_NUM_TARGETS ; i ++) sbp_free_target(&sbp->targets[i]); fw_bindremove(fc, &sbp->fwb); fw_xferlist_remove(&sbp->fwb.xferlist); bus_dma_tag_destroy(sbp->dmat); mtx_destroy(&sbp->mtx); return (0); } static void sbp_cam_detach_sdev(struct sbp_dev *sdev) { if (sdev == NULL) return; if (sdev->status == SBP_DEV_DEAD) return; if (sdev->status == SBP_DEV_RESET) return; sbp_abort_all_ocbs(sdev, CAM_DEV_NOT_THERE); if (sdev->path) { SBP_LOCK(sdev->target->sbp); xpt_release_devq(sdev->path, sdev->freeze, TRUE); sdev->freeze = 0; xpt_async(AC_LOST_DEVICE, sdev->path, NULL); xpt_free_path(sdev->path); sdev->path = NULL; SBP_UNLOCK(sdev->target->sbp); } } static void sbp_cam_detach_target(struct sbp_target *target) { int i; if (target->luns != NULL) { SBP_DEBUG(0) printf("sbp_detach_target %d\n", target->target_id); END_DEBUG callout_stop(&target->scan_callout); for (i = 0; i < target->num_lun; i++) sbp_cam_detach_sdev(target->luns[i]); } } static void sbp_target_reset(struct sbp_dev *sdev, int method) { int i; struct sbp_target *target = sdev->target; struct sbp_dev *tsdev; for (i = 0; i < target->num_lun; i++) { tsdev = target->luns[i]; if (tsdev == NULL) continue; if (tsdev->status == SBP_DEV_DEAD) continue; if (tsdev->status == SBP_DEV_RESET) continue; SBP_LOCK(target->sbp); xpt_freeze_devq(tsdev->path, 1); tsdev->freeze ++; SBP_UNLOCK(target->sbp); sbp_abort_all_ocbs(tsdev, CAM_CMD_TIMEOUT); if (method == 2) tsdev->status = SBP_DEV_LOGIN; } switch(method) { case 1: printf("target reset\n"); sbp_mgm_orb(sdev, ORB_FUN_RST, NULL); break; case 2: printf("reset start\n"); sbp_reset_start(sdev); break; } } static void sbp_mgm_timeout(void *arg) { struct sbp_ocb *ocb = (struct sbp_ocb *)arg; struct sbp_dev *sdev = ocb->sdev; struct sbp_target *target = sdev->target; device_printf(sdev->target->sbp->fd.dev, "%s:%s request timeout(mgm orb:0x%08x)\n", __func__, sdev->bustgtlun, (uint32_t)ocb->bus_addr); target->mgm_ocb_cur = NULL; sbp_free_ocb(sdev, ocb); #if 0 /* XXX */ printf("run next request\n"); sbp_mgm_orb(sdev, ORB_FUN_RUNQUEUE, NULL); #endif device_printf(sdev->target->sbp->fd.dev, "%s:%s reset start\n", __func__, sdev->bustgtlun); sbp_reset_start(sdev); } static void sbp_timeout(void *arg) { struct sbp_ocb *ocb = (struct sbp_ocb *)arg; struct sbp_dev *sdev = ocb->sdev; device_printf(sdev->target->sbp->fd.dev, "%s:%s request timeout(cmd orb:0x%08x) ... ", __func__, sdev->bustgtlun, (uint32_t)ocb->bus_addr); sdev->timeout ++; switch(sdev->timeout) { case 1: printf("agent reset\n"); SBP_LOCK(sdev->target->sbp); xpt_freeze_devq(sdev->path, 1); sdev->freeze ++; SBP_UNLOCK(sdev->target->sbp); sbp_abort_all_ocbs(sdev, CAM_CMD_TIMEOUT); sbp_agent_reset(sdev); break; case 2: case 3: sbp_target_reset(sdev, sdev->timeout - 1); break; #if 0 default: /* XXX give up */ sbp_cam_detach_target(target); if (target->luns != NULL) free(target->luns, M_SBP); target->num_lun = 0; target->luns = NULL; target->fwdev = NULL; #endif } } static void sbp_action1(struct cam_sim *sim, union ccb *ccb) { struct sbp_softc *sbp = (struct sbp_softc *)sim->softc; struct sbp_target *target = NULL; struct sbp_dev *sdev = NULL; /* target:lun -> sdev mapping */ if (sbp != NULL && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD && ccb->ccb_h.target_id < SBP_NUM_TARGETS) { target = &sbp->targets[ccb->ccb_h.target_id]; if (target->fwdev != NULL && ccb->ccb_h.target_lun != CAM_LUN_WILDCARD && ccb->ccb_h.target_lun < target->num_lun) { sdev = target->luns[ccb->ccb_h.target_lun]; if (sdev != NULL && sdev->status != SBP_DEV_ATTACHED && sdev->status != SBP_DEV_PROBE) sdev = NULL; } } SBP_DEBUG(1) if (sdev == NULL) printf("invalid target %d lun %d\n", ccb->ccb_h.target_id, ccb->ccb_h.target_lun); END_DEBUG switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: case XPT_RESET_DEV: case XPT_GET_TRAN_SETTINGS: case XPT_SET_TRAN_SETTINGS: case XPT_CALC_GEOMETRY: if (sdev == NULL) { SBP_DEBUG(1) printf("%s:%d:%d:func_code 0x%04x: " "Invalid target (target needed)\n", device_get_nameunit(sbp->fd.dev), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb->ccb_h.func_code); END_DEBUG ccb->ccb_h.status = CAM_DEV_NOT_THERE; xpt_done(ccb); return; } break; case XPT_PATH_INQ: case XPT_NOOP: /* The opcodes sometimes aimed at a target (sc is valid), * sometimes aimed at the SIM (sc is invalid and target is * CAM_TARGET_WILDCARD) */ if (sbp == NULL && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) { SBP_DEBUG(0) printf("%s:%d:%d func_code 0x%04x: " "Invalid target (no wildcard)\n", device_get_nameunit(sbp->fd.dev), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb->ccb_h.func_code); END_DEBUG ccb->ccb_h.status = CAM_DEV_NOT_THERE; xpt_done(ccb); return; } break; default: /* XXX Hm, we should check the input parameters */ break; } switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: { struct ccb_scsiio *csio; struct sbp_ocb *ocb; int speed; void *cdb; csio = &ccb->csio; mtx_assert(sim->mtx, MA_OWNED); SBP_DEBUG(2) printf("%s:%d:%d XPT_SCSI_IO: " "cmd: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x" ", flags: 0x%02x, " "%db cmd/%db data/%db sense\n", device_get_nameunit(sbp->fd.dev), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, csio->cdb_io.cdb_bytes[0], csio->cdb_io.cdb_bytes[1], csio->cdb_io.cdb_bytes[2], csio->cdb_io.cdb_bytes[3], csio->cdb_io.cdb_bytes[4], csio->cdb_io.cdb_bytes[5], csio->cdb_io.cdb_bytes[6], csio->cdb_io.cdb_bytes[7], csio->cdb_io.cdb_bytes[8], csio->cdb_io.cdb_bytes[9], ccb->ccb_h.flags & CAM_DIR_MASK, csio->cdb_len, csio->dxfer_len, csio->sense_len); END_DEBUG if(sdev == NULL){ ccb->ccb_h.status = CAM_DEV_NOT_THERE; xpt_done(ccb); return; } #if 0 /* if we are in probe stage, pass only probe commands */ if (sdev->status == SBP_DEV_PROBE) { char *name; name = xpt_path_periph(ccb->ccb_h.path)->periph_name; printf("probe stage, periph name: %s\n", name); if (strcmp(name, "probe") != 0) { ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_done(ccb); return; } } #endif if ((ocb = sbp_get_ocb(sdev)) == NULL) { ccb->ccb_h.status = CAM_RESRC_UNAVAIL; if (sdev->freeze == 0) { SBP_LOCK(sdev->target->sbp); xpt_freeze_devq(sdev->path, 1); sdev->freeze ++; SBP_UNLOCK(sdev->target->sbp); } xpt_done(ccb); return; } ocb->flags = OCB_ACT_CMD; ocb->sdev = sdev; ocb->ccb = ccb; ccb->ccb_h.ccb_sdev_ptr = sdev; ocb->orb[0] = htonl(1 << 31); ocb->orb[1] = 0; ocb->orb[2] = htonl(((sbp->fd.fc->nodeid | FWLOCALBUS )<< 16) ); ocb->orb[3] = htonl(ocb->bus_addr + IND_PTR_OFFSET); speed = min(target->fwdev->speed, max_speed); ocb->orb[4] = htonl(ORB_NOTIFY | ORB_CMD_SPD(speed) | ORB_CMD_MAXP(speed + 7)); if((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN){ ocb->orb[4] |= htonl(ORB_CMD_IN); } if (csio->ccb_h.flags & CAM_CDB_POINTER) cdb = (void *)csio->cdb_io.cdb_ptr; else cdb = (void *)&csio->cdb_io.cdb_bytes; bcopy(cdb, (void *)&ocb->orb[5], csio->cdb_len); /* printf("ORB %08x %08x %08x %08x\n", ntohl(ocb->orb[0]), ntohl(ocb->orb[1]), ntohl(ocb->orb[2]), ntohl(ocb->orb[3])); printf("ORB %08x %08x %08x %08x\n", ntohl(ocb->orb[4]), ntohl(ocb->orb[5]), ntohl(ocb->orb[6]), ntohl(ocb->orb[7])); */ if (ccb->csio.dxfer_len > 0) { int error; error = bus_dmamap_load_ccb(/*dma tag*/sbp->dmat, /*dma map*/ocb->dmamap, ccb, sbp_execute_ocb, ocb, /*flags*/0); if (error) printf("sbp: bus_dmamap_load error %d\n", error); } else sbp_execute_ocb(ocb, NULL, 0, 0); break; } case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg; #if defined(__DragonFly__) || __FreeBSD_version < 501100 uint32_t size_mb; uint32_t secs_per_cylinder; int extended = 1; #endif ccg = &ccb->ccg; if (ccg->block_size == 0) { printf("sbp_action1: block_size is 0.\n"); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } SBP_DEBUG(1) printf("%s:%d:%d:%d:XPT_CALC_GEOMETRY: " #if defined(__DragonFly__) || __FreeBSD_version < 500000 "Volume size = %d\n", #else "Volume size = %jd\n", #endif device_get_nameunit(sbp->fd.dev), cam_sim_path(sbp->sim), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 (uintmax_t) #endif ccg->volume_size); END_DEBUG #if defined(__DragonFly__) || __FreeBSD_version < 501100 size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size); if (size_mb > 1024 && extended) { ccg->heads = 255; ccg->secs_per_track = 63; } else { ccg->heads = 64; ccg->secs_per_track = 32; } secs_per_cylinder = ccg->heads * ccg->secs_per_track; ccg->cylinders = ccg->volume_size / secs_per_cylinder; ccb->ccb_h.status = CAM_REQ_CMP; #else cam_calc_geometry(ccg, /*extended*/1); #endif xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ { SBP_DEBUG(1) printf("%s:%d:XPT_RESET_BUS: \n", device_get_nameunit(sbp->fd.dev), cam_sim_path(sbp->sim)); END_DEBUG ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; SBP_DEBUG(1) printf("%s:%d:%d XPT_PATH_INQ:.\n", device_get_nameunit(sbp->fd.dev), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); END_DEBUG cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_TAG_ABLE; cpi->target_sprt = 0; cpi->hba_misc = PIM_NOBUSRESET | PIM_NO_6_BYTE; cpi->hba_eng_cnt = 0; cpi->max_target = SBP_NUM_TARGETS - 1; cpi->max_lun = SBP_NUM_LUNS - 1; cpi->initiator_id = SBP_INITIATOR; cpi->bus_id = sim->bus_id; cpi->base_transfer_speed = 400 * 1000 / 8; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "SBP", HBA_IDLEN); strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); cpi->unit_number = sim->unit_number; cpi->transport = XPORT_SPI; /* XX should have a FireWire */ cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; /* should have a FireWire */ cts->transport_version = 2; spi->valid = CTS_SPI_VALID_DISC; spi->flags = CTS_SPI_FLAGS_DISC_ENB; scsi->valid = CTS_SCSI_VALID_TQ; scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; SBP_DEBUG(1) printf("%s:%d:%d XPT_GET_TRAN_SETTINGS:.\n", device_get_nameunit(sbp->fd.dev), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); END_DEBUG cts->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_ABORT: ccb->ccb_h.status = CAM_UA_ABORT; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: /* XXX */ default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } return; } static void sbp_action(struct cam_sim *sim, union ccb *ccb) { int s; s = splfw(); sbp_action1(sim, ccb); splx(s); } static void sbp_execute_ocb(void *arg, bus_dma_segment_t *segments, int seg, int error) { int i; struct sbp_ocb *ocb; struct sbp_ocb *prev; bus_dma_segment_t *s; if (error) printf("sbp_execute_ocb: error=%d\n", error); ocb = (struct sbp_ocb *)arg; SBP_DEBUG(2) printf("sbp_execute_ocb: seg %d", seg); for (i = 0; i < seg; i++) #if defined(__DragonFly__) || __FreeBSD_version < 500000 printf(", %x:%d", segments[i].ds_addr, segments[i].ds_len); #else printf(", %jx:%jd", (uintmax_t)segments[i].ds_addr, (uintmax_t)segments[i].ds_len); #endif printf("\n"); END_DEBUG if (seg == 1) { /* direct pointer */ s = &segments[0]; if (s->ds_len > SBP_SEG_MAX) panic("ds_len > SBP_SEG_MAX, fix busdma code"); ocb->orb[3] = htonl(s->ds_addr); ocb->orb[4] |= htonl(s->ds_len); } else if(seg > 1) { /* page table */ for (i = 0; i < seg; i++) { s = &segments[i]; SBP_DEBUG(0) /* XXX LSI Logic "< 16 byte" bug might be hit */ if (s->ds_len < 16) printf("sbp_execute_ocb: warning, " #if defined(__DragonFly__) || __FreeBSD_version < 500000 "segment length(%d) is less than 16." #else "segment length(%zd) is less than 16." #endif "(seg=%d/%d)\n", (size_t)s->ds_len, i+1, seg); END_DEBUG if (s->ds_len > SBP_SEG_MAX) panic("ds_len > SBP_SEG_MAX, fix busdma code"); ocb->ind_ptr[i].hi = htonl(s->ds_len << 16); ocb->ind_ptr[i].lo = htonl(s->ds_addr); } ocb->orb[4] |= htonl(ORB_CMD_PTBL | seg); } if (seg > 0) bus_dmamap_sync(ocb->sdev->target->sbp->dmat, ocb->dmamap, (ntohl(ocb->orb[4]) & ORB_CMD_IN) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); prev = sbp_enqueue_ocb(ocb->sdev, ocb); fwdma_sync(&ocb->sdev->dma, BUS_DMASYNC_PREWRITE); if (use_doorbell) { if (prev == NULL) { if (ocb->sdev->last_ocb != NULL) sbp_doorbell(ocb->sdev); else sbp_orb_pointer(ocb->sdev, ocb); } } else { if (prev == NULL || (ocb->sdev->flags & ORB_LINK_DEAD) != 0) { ocb->sdev->flags &= ~ORB_LINK_DEAD; sbp_orb_pointer(ocb->sdev, ocb); } } } static void sbp_poll(struct cam_sim *sim) { struct sbp_softc *sbp; struct firewire_comm *fc; sbp = (struct sbp_softc *)sim->softc; fc = sbp->fd.fc; fc->poll(fc, 0, -1); return; } static struct sbp_ocb * sbp_dequeue_ocb(struct sbp_dev *sdev, struct sbp_status *sbp_status) { struct sbp_ocb *ocb; struct sbp_ocb *next; int s = splfw(), order = 0; int flags; SBP_DEBUG(1) device_printf(sdev->target->sbp->fd.dev, #if defined(__DragonFly__) || __FreeBSD_version < 500000 "%s:%s 0x%08lx src %d\n", #else "%s:%s 0x%08x src %d\n", #endif __func__, sdev->bustgtlun, ntohl(sbp_status->orb_lo), sbp_status->src); END_DEBUG SBP_LOCK(sdev->target->sbp); for (ocb = STAILQ_FIRST(&sdev->ocbs); ocb != NULL; ocb = next) { next = STAILQ_NEXT(ocb, ocb); flags = ocb->flags; if (OCB_MATCH(ocb, sbp_status)) { /* found */ STAILQ_REMOVE(&sdev->ocbs, ocb, sbp_ocb, ocb); if (ocb->ccb != NULL) untimeout(sbp_timeout, (caddr_t)ocb, - ocb->ccb->ccb_h.timeout_ch); + ocb->timeout_ch); if (ntohl(ocb->orb[4]) & 0xffff) { bus_dmamap_sync(sdev->target->sbp->dmat, ocb->dmamap, (ntohl(ocb->orb[4]) & ORB_CMD_IN) ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sdev->target->sbp->dmat, ocb->dmamap); } if (!use_doorbell) { if (sbp_status->src == SRC_NO_NEXT) { if (next != NULL) sbp_orb_pointer(sdev, next); else if (order > 0) { /* * Unordered execution * We need to send pointer for * next ORB */ sdev->flags |= ORB_LINK_DEAD; } } } else { /* * XXX this is not correct for unordered * execution. */ if (sdev->last_ocb != NULL) { SBP_UNLOCK(sdev->target->sbp); sbp_free_ocb(sdev, sdev->last_ocb); SBP_LOCK(sdev->target->sbp); } sdev->last_ocb = ocb; if (next != NULL && sbp_status->src == SRC_NO_NEXT) sbp_doorbell(sdev); } break; } else order ++; } SBP_UNLOCK(sdev->target->sbp); splx(s); SBP_DEBUG(0) if (ocb && order > 0) { device_printf(sdev->target->sbp->fd.dev, "%s:%s unordered execution order:%d\n", __func__, sdev->bustgtlun, order); } END_DEBUG return (ocb); } static struct sbp_ocb * sbp_enqueue_ocb(struct sbp_dev *sdev, struct sbp_ocb *ocb) { int s = splfw(); struct sbp_ocb *prev, *prev2; mtx_assert(&sdev->target->sbp->mtx, MA_OWNED); SBP_DEBUG(1) device_printf(sdev->target->sbp->fd.dev, #if defined(__DragonFly__) || __FreeBSD_version < 500000 "%s:%s 0x%08x\n", __func__, sdev->bustgtlun, ocb->bus_addr); #else "%s:%s 0x%08jx\n", __func__, sdev->bustgtlun, (uintmax_t)ocb->bus_addr); #endif END_DEBUG prev2 = prev = STAILQ_LAST(&sdev->ocbs, sbp_ocb, ocb); STAILQ_INSERT_TAIL(&sdev->ocbs, ocb, ocb); if (ocb->ccb != NULL) - ocb->ccb->ccb_h.timeout_ch = timeout(sbp_timeout, (caddr_t)ocb, + ocb->timeout_ch = timeout(sbp_timeout, (caddr_t)ocb, (ocb->ccb->ccb_h.timeout * hz) / 1000); if (use_doorbell && prev == NULL) prev2 = sdev->last_ocb; if (prev2 != NULL && (ocb->sdev->flags & ORB_LINK_DEAD) == 0) { SBP_DEBUG(1) #if defined(__DragonFly__) || __FreeBSD_version < 500000 printf("linking chain 0x%x -> 0x%x\n", prev2->bus_addr, ocb->bus_addr); #else printf("linking chain 0x%jx -> 0x%jx\n", (uintmax_t)prev2->bus_addr, (uintmax_t)ocb->bus_addr); #endif END_DEBUG /* * Suppress compiler optimization so that orb[1] must be written first. * XXX We may need an explicit memory barrier for other architectures * other than i386/amd64. */ *(volatile uint32_t *)&prev2->orb[1] = htonl(ocb->bus_addr); *(volatile uint32_t *)&prev2->orb[0] = 0; } splx(s); return prev; } static struct sbp_ocb * sbp_get_ocb(struct sbp_dev *sdev) { struct sbp_ocb *ocb; int s = splfw(); mtx_assert(&sdev->target->sbp->mtx, MA_OWNED); ocb = STAILQ_FIRST(&sdev->free_ocbs); if (ocb == NULL) { sdev->flags |= ORB_SHORTAGE; printf("ocb shortage!!!\n"); splx(s); return NULL; } STAILQ_REMOVE_HEAD(&sdev->free_ocbs, ocb); splx(s); ocb->ccb = NULL; return (ocb); } static void sbp_free_ocb(struct sbp_dev *sdev, struct sbp_ocb *ocb) { ocb->flags = 0; ocb->ccb = NULL; SBP_LOCK(sdev->target->sbp); STAILQ_INSERT_TAIL(&sdev->free_ocbs, ocb, ocb); if ((sdev->flags & ORB_SHORTAGE) != 0) { int count; sdev->flags &= ~ORB_SHORTAGE; count = sdev->freeze; sdev->freeze = 0; xpt_release_devq(sdev->path, count, TRUE); } SBP_UNLOCK(sdev->target->sbp); } static void sbp_abort_ocb(struct sbp_ocb *ocb, int status) { struct sbp_dev *sdev; sdev = ocb->sdev; SBP_DEBUG(0) device_printf(sdev->target->sbp->fd.dev, #if defined(__DragonFly__) || __FreeBSD_version < 500000 "%s:%s 0x%x\n", __func__, sdev->bustgtlun, ocb->bus_addr); #else "%s:%s 0x%jx\n", __func__, sdev->bustgtlun, (uintmax_t)ocb->bus_addr); #endif END_DEBUG SBP_DEBUG(1) if (ocb->ccb != NULL) sbp_print_scsi_cmd(ocb); END_DEBUG if (ntohl(ocb->orb[4]) & 0xffff) { bus_dmamap_sync(sdev->target->sbp->dmat, ocb->dmamap, (ntohl(ocb->orb[4]) & ORB_CMD_IN) ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sdev->target->sbp->dmat, ocb->dmamap); } if (ocb->ccb != NULL) { untimeout(sbp_timeout, (caddr_t)ocb, - ocb->ccb->ccb_h.timeout_ch); + ocb->timeout_ch); ocb->ccb->ccb_h.status = status; SBP_LOCK(sdev->target->sbp); xpt_done(ocb->ccb); SBP_UNLOCK(sdev->target->sbp); } sbp_free_ocb(sdev, ocb); } static void sbp_abort_all_ocbs(struct sbp_dev *sdev, int status) { int s; struct sbp_ocb *ocb, *next; STAILQ_HEAD(, sbp_ocb) temp; s = splfw(); STAILQ_INIT(&temp); SBP_LOCK(sdev->target->sbp); STAILQ_CONCAT(&temp, &sdev->ocbs); STAILQ_INIT(&sdev->ocbs); SBP_UNLOCK(sdev->target->sbp); for (ocb = STAILQ_FIRST(&temp); ocb != NULL; ocb = next) { next = STAILQ_NEXT(ocb, ocb); sbp_abort_ocb(ocb, status); } if (sdev->last_ocb != NULL) { sbp_free_ocb(sdev, sdev->last_ocb); sdev->last_ocb = NULL; } splx(s); } static devclass_t sbp_devclass; static device_method_t sbp_methods[] = { /* device interface */ DEVMETHOD(device_identify, sbp_identify), DEVMETHOD(device_probe, sbp_probe), DEVMETHOD(device_attach, sbp_attach), DEVMETHOD(device_detach, sbp_detach), DEVMETHOD(device_shutdown, sbp_shutdown), { 0, 0 } }; static driver_t sbp_driver = { "sbp", sbp_methods, sizeof(struct sbp_softc), }; #ifdef __DragonFly__ DECLARE_DUMMY_MODULE(sbp); #endif DRIVER_MODULE(sbp, firewire, sbp_driver, sbp_devclass, 0, 0); MODULE_VERSION(sbp, 1); MODULE_DEPEND(sbp, firewire, 1, 1, 1); MODULE_DEPEND(sbp, cam, 1, 1, 1); diff --git a/sys/dev/hpt27xx/hpt27xx_osm_bsd.c b/sys/dev/hpt27xx/hpt27xx_osm_bsd.c index 3c9f0ace838b..d66db1888988 100644 --- a/sys/dev/hpt27xx/hpt27xx_osm_bsd.c +++ b/sys/dev/hpt27xx/hpt27xx_osm_bsd.c @@ -1,1361 +1,1361 @@ /*- * Copyright (c) 2011 HighPoint Technologies, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include static int hpt_probe(device_t dev) { PCI_ID pci_id; HIM *him; int i; PHBA hba; for (him = him_list; him; him = him->next) { for (i=0; him->get_supported_device_id(i, &pci_id); i++) { if (him->get_controller_count) him->get_controller_count(&pci_id,0,0); if ((pci_get_vendor(dev) == pci_id.vid) && (pci_get_device(dev) == pci_id.did)){ KdPrint(("hpt_probe: adapter at PCI %d:%d:%d, IRQ %d", pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev), pci_get_irq(dev) )); device_set_desc(dev, him->name); hba = (PHBA)device_get_softc(dev); memset(hba, 0, sizeof(HBA)); hba->ext_type = EXT_TYPE_HBA; hba->ldm_adapter.him = him; return (BUS_PROBE_DEFAULT); } } } return (ENXIO); } static int hpt_attach(device_t dev) { PHBA hba = (PHBA)device_get_softc(dev); HIM *him = hba->ldm_adapter.him; PCI_ID pci_id; HPT_UINT size; PVBUS vbus; PVBUS_EXT vbus_ext; KdPrint(("hpt_attach(%d/%d/%d)", pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev))); #if __FreeBSD_version >=440000 pci_enable_busmaster(dev); #endif pci_id.vid = pci_get_vendor(dev); pci_id.did = pci_get_device(dev); pci_id.rev = pci_get_revid(dev); pci_id.subsys = (HPT_U32)(pci_get_subdevice(dev)) << 16 | pci_get_subvendor(dev); size = him->get_adapter_size(&pci_id); hba->ldm_adapter.him_handle = malloc(size, M_DEVBUF, M_WAITOK); if (!hba->ldm_adapter.him_handle) return ENXIO; hba->pcidev = dev; hba->pciaddr.tree = 0; hba->pciaddr.bus = pci_get_bus(dev); hba->pciaddr.device = pci_get_slot(dev); hba->pciaddr.function = pci_get_function(dev); if (!him->create_adapter(&pci_id, hba->pciaddr, hba->ldm_adapter.him_handle, hba)) { free(hba->ldm_adapter.him_handle, M_DEVBUF); return -1; } os_printk("adapter at PCI %d:%d:%d, IRQ %d", hba->pciaddr.bus, hba->pciaddr.device, hba->pciaddr.function, pci_get_irq(dev)); if (!ldm_register_adapter(&hba->ldm_adapter)) { size = ldm_get_vbus_size(); vbus_ext = malloc(sizeof(VBUS_EXT) + size, M_DEVBUF, M_WAITOK); if (!vbus_ext) { free(hba->ldm_adapter.him_handle, M_DEVBUF); return -1; } memset(vbus_ext, 0, sizeof(VBUS_EXT)); vbus_ext->ext_type = EXT_TYPE_VBUS; ldm_create_vbus((PVBUS)vbus_ext->vbus, vbus_ext); ldm_register_adapter(&hba->ldm_adapter); } ldm_for_each_vbus(vbus, vbus_ext) { if (hba->ldm_adapter.vbus==vbus) { hba->vbus_ext = vbus_ext; hba->next = vbus_ext->hba_list; vbus_ext->hba_list = hba; break; } } return 0; } /* * Maybe we'd better to use the bus_dmamem_alloc to alloc DMA memory, * but there are some problems currently (alignment, etc). */ static __inline void *__get_free_pages(int order) { /* don't use low memory - other devices may get starved */ return contigmalloc(PAGE_SIZE<hba_list; hba; hba = hba->next) hba->ldm_adapter.him->get_meminfo(hba->ldm_adapter.him_handle); ldm_get_mem_info((PVBUS)vbus_ext->vbus, 0); for (f=vbus_ext->freelist_head; f; f=f->next) { KdPrint(("%s: %d*%d=%d bytes", f->tag, f->count, f->size, f->count*f->size)); for (i=0; icount; i++) { p = (void **)malloc(f->size, M_DEVBUF, M_WAITOK); if (!p) return (ENXIO); *p = f->head; f->head = p; } } for (f=vbus_ext->freelist_dma_head; f; f=f->next) { int order, size, j; HPT_ASSERT((f->size & (f->alignment-1))==0); for (order=0, size=PAGE_SIZE; sizesize; order++, size<<=1) ; KdPrint(("%s: %d*%d=%d bytes, order %d", f->tag, f->count, f->size, f->count*f->size, order)); HPT_ASSERT(f->alignment<=PAGE_SIZE); for (i=0; icount;) { p = (void **)__get_free_pages(order); if (!p) return -1; for (j = size/f->size; j && icount; i++,j--) { *p = f->head; *(BUS_ADDRESS *)(p+1) = (BUS_ADDRESS)vtophys(p); f->head = p; p = (void **)((unsigned long)p + f->size); } } } HPT_ASSERT(PAGE_SIZE==DMAPOOL_PAGE_SIZE); for (i=0; ivbus, p, (BUS_ADDRESS)vtophys(p)); } return 0; } static void hpt_free_mem(PVBUS_EXT vbus_ext) { struct freelist *f; void *p; int i; BUS_ADDRESS bus; for (f=vbus_ext->freelist_head; f; f=f->next) { #if DBG if (f->count!=f->reserved_count) { KdPrint(("memory leak for freelist %s (%d/%d)", f->tag, f->count, f->reserved_count)); } #endif while ((p=freelist_get(f))) free(p, M_DEVBUF); } for (i=0; ivbus, &bus); HPT_ASSERT(p); free_pages(p, 0); } for (f=vbus_ext->freelist_dma_head; f; f=f->next) { int order, size; #if DBG if (f->count!=f->reserved_count) { KdPrint(("memory leak for dma freelist %s (%d/%d)", f->tag, f->count, f->reserved_count)); } #endif for (order=0, size=PAGE_SIZE; sizesize; order++, size<<=1) ; while ((p=freelist_get_dma(f, &bus))) { if (order) free_pages(p, order); else { /* can't free immediately since other blocks in this page may still be in the list */ if (((HPT_UPTR)p & (PAGE_SIZE-1))==0) dmapool_put_page((PVBUS)vbus_ext->vbus, p, bus); } } } while ((p = dmapool_get_page((PVBUS)vbus_ext->vbus, &bus))) free_pages(p, 0); } static int hpt_init_vbus(PVBUS_EXT vbus_ext) { PHBA hba; for (hba = vbus_ext->hba_list; hba; hba = hba->next) if (!hba->ldm_adapter.him->initialize(hba->ldm_adapter.him_handle)) { KdPrint(("fail to initialize %p", hba)); return -1; } ldm_initialize_vbus((PVBUS)vbus_ext->vbus, &vbus_ext->hba_list->ldm_adapter); return 0; } static void hpt_flush_done(PCOMMAND pCmd) { PVDEV vd = pCmd->target; if (mIsArray(vd->type) && vd->u.array.transform && vd!=vd->u.array.transform->target) { vd = vd->u.array.transform->target; HPT_ASSERT(vd); pCmd->target = vd; pCmd->Result = RETURN_PENDING; vdev_queue_cmd(pCmd); return; } *(int *)pCmd->priv = 1; wakeup(pCmd); } /* * flush a vdev (without retry). */ static int hpt_flush_vdev(PVBUS_EXT vbus_ext, PVDEV vd) { PCOMMAND pCmd; int result = 0, done; HPT_UINT count; KdPrint(("flusing dev %p", vd)); hpt_lock_vbus(vbus_ext); if (mIsArray(vd->type) && vd->u.array.transform) count = MAX(vd->u.array.transform->source->cmds_per_request, vd->u.array.transform->target->cmds_per_request); else count = vd->cmds_per_request; pCmd = ldm_alloc_cmds(vd->vbus, count); if (!pCmd) { hpt_unlock_vbus(vbus_ext); return -1; } pCmd->type = CMD_TYPE_FLUSH; pCmd->flags.hard_flush = 1; pCmd->target = vd; pCmd->done = hpt_flush_done; done = 0; pCmd->priv = &done; ldm_queue_cmd(pCmd); if (!done) { while (hpt_sleep(vbus_ext, pCmd, PPAUSE, "hptfls", HPT_OSM_TIMEOUT)) { ldm_reset_vbus(vd->vbus); } } KdPrint(("flush result %d", pCmd->Result)); if (pCmd->Result!=RETURN_SUCCESS) result = -1; ldm_free_cmds(pCmd); hpt_unlock_vbus(vbus_ext); return result; } static void hpt_stop_tasks(PVBUS_EXT vbus_ext); static void hpt_shutdown_vbus(PVBUS_EXT vbus_ext, int howto) { PVBUS vbus = (PVBUS)vbus_ext->vbus; PHBA hba; int i; KdPrint(("hpt_shutdown_vbus")); /* stop all ctl tasks and disable the worker taskqueue */ hpt_stop_tasks(vbus_ext); vbus_ext->worker.ta_context = 0; /* flush devices */ for (i=0; ihba_list; hba; hba=hba->next) bus_teardown_intr(hba->pcidev, hba->irq_res, hba->irq_handle); hpt_free_mem(vbus_ext); while ((hba=vbus_ext->hba_list)) { vbus_ext->hba_list = hba->next; free(hba->ldm_adapter.him_handle, M_DEVBUF); } free(vbus_ext, M_DEVBUF); KdPrint(("hpt_shutdown_vbus done")); } static void __hpt_do_tasks(PVBUS_EXT vbus_ext) { OSM_TASK *tasks; tasks = vbus_ext->tasks; vbus_ext->tasks = 0; while (tasks) { OSM_TASK *t = tasks; tasks = t->next; t->next = 0; t->func(vbus_ext->vbus, t->data); } } static void hpt_do_tasks(PVBUS_EXT vbus_ext, int pending) { if(vbus_ext){ hpt_lock_vbus(vbus_ext); __hpt_do_tasks(vbus_ext); hpt_unlock_vbus(vbus_ext); } } static void hpt_action(struct cam_sim *sim, union ccb *ccb); static void hpt_poll(struct cam_sim *sim); static void hpt_async(void * callback_arg, u_int32_t code, struct cam_path * path, void * arg); static void hpt_pci_intr(void *arg); static __inline POS_CMDEXT cmdext_get(PVBUS_EXT vbus_ext) { POS_CMDEXT p = vbus_ext->cmdext_list; if (p) vbus_ext->cmdext_list = p->next; return p; } static __inline void cmdext_put(POS_CMDEXT p) { p->next = p->vbus_ext->cmdext_list; p->vbus_ext->cmdext_list = p; } static void hpt_timeout(void *arg) { PCOMMAND pCmd = (PCOMMAND)arg; POS_CMDEXT ext = (POS_CMDEXT)pCmd->priv; KdPrint(("pCmd %p timeout", pCmd)); ldm_reset_vbus((PVBUS)ext->vbus_ext->vbus); } static void os_cmddone(PCOMMAND pCmd) { POS_CMDEXT ext = (POS_CMDEXT)pCmd->priv; union ccb *ccb = ext->ccb; KdPrint(("os_cmddone(%p, %d)", pCmd, pCmd->Result)); - untimeout(hpt_timeout, pCmd, ccb->ccb_h.timeout_ch); + untimeout(hpt_timeout, pCmd, ext->timeout_ch); switch(pCmd->Result) { case RETURN_SUCCESS: ccb->ccb_h.status = CAM_REQ_CMP; break; case RETURN_BAD_DEVICE: ccb->ccb_h.status = CAM_DEV_NOT_THERE; break; case RETURN_DEVICE_BUSY: ccb->ccb_h.status = CAM_BUSY; break; case RETURN_INVALID_REQUEST: ccb->ccb_h.status = CAM_REQ_INVALID; break; case RETURN_SELECTION_TIMEOUT: ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; case RETURN_RETRY: ccb->ccb_h.status = CAM_BUSY; break; default: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; } if (pCmd->flags.data_in) { bus_dmamap_sync(ext->vbus_ext->io_dmat, ext->dma_map, BUS_DMASYNC_POSTREAD); } else if (pCmd->flags.data_out) { bus_dmamap_sync(ext->vbus_ext->io_dmat, ext->dma_map, BUS_DMASYNC_POSTWRITE); } bus_dmamap_unload(ext->vbus_ext->io_dmat, ext->dma_map); cmdext_put(ext); ldm_free_cmds(pCmd); xpt_done(ccb); } static int os_buildsgl(PCOMMAND pCmd, PSG pSg, int logical) { /* since we have provided physical sg, nobody will ask us to build physical sg */ HPT_ASSERT(0); return FALSE; } static void hpt_io_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { PCOMMAND pCmd = (PCOMMAND)arg; POS_CMDEXT ext = (POS_CMDEXT)pCmd->priv; PSG psg = pCmd->psg; int idx; HPT_ASSERT(pCmd->flags.physical_sg); if (error) panic("busdma error"); HPT_ASSERT(nsegs<=os_max_sg_descriptors); if (nsegs != 0) { for (idx = 0; idx < nsegs; idx++, psg++) { psg->addr.bus = segs[idx].ds_addr; psg->size = segs[idx].ds_len; psg->eot = 0; } psg[-1].eot = 1; if (pCmd->flags.data_in) { bus_dmamap_sync(ext->vbus_ext->io_dmat, ext->dma_map, BUS_DMASYNC_PREREAD); } else if (pCmd->flags.data_out) { bus_dmamap_sync(ext->vbus_ext->io_dmat, ext->dma_map, BUS_DMASYNC_PREWRITE); } } - - ext->ccb->ccb_h.timeout_ch = timeout(hpt_timeout, pCmd, HPT_OSM_TIMEOUT); + ext->timeout_ch = timeout(hpt_timeout, pCmd, HPT_OSM_TIMEOUT); ldm_queue_cmd(pCmd); } static void hpt_scsi_io(PVBUS_EXT vbus_ext, union ccb *ccb) { PVBUS vbus = (PVBUS)vbus_ext->vbus; PVDEV vd; PCOMMAND pCmd; POS_CMDEXT ext; HPT_U8 *cdb; if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; KdPrint(("hpt_scsi_io: ccb %x id %d lun %d cdb %x-%x-%x", ccb, ccb->ccb_h.target_id, ccb->ccb_h.target_lun, *(HPT_U32 *)&cdb[0], *(HPT_U32 *)&cdb[4], *(HPT_U32 *)&cdb[8] )); /* ccb->ccb_h.path_id is not our bus id - don't check it */ if (ccb->ccb_h.target_lun != 0 || ccb->ccb_h.target_id >= osm_max_targets || (ccb->ccb_h.flags & CAM_CDB_PHYS)) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return; } vd = ldm_find_target(vbus, ccb->ccb_h.target_id); if (!vd) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return; } switch (cdb[0]) { case TEST_UNIT_READY: case START_STOP_UNIT: case SYNCHRONIZE_CACHE: ccb->ccb_h.status = CAM_REQ_CMP; break; case INQUIRY: { PINQUIRYDATA inquiryData; memset(ccb->csio.data_ptr, 0, ccb->csio.dxfer_len); inquiryData = (PINQUIRYDATA)ccb->csio.data_ptr; inquiryData->AdditionalLength = 31; inquiryData->CommandQueue = 1; memcpy(&inquiryData->VendorId, "HPT ", 8); memcpy(&inquiryData->ProductId, "DISK 0_0 ", 16); if (vd->target_id / 10) { inquiryData->ProductId[7] = (vd->target_id % 100) / 10 + '0'; inquiryData->ProductId[8] = (vd->target_id % 100) % 10 + '0'; } else inquiryData->ProductId[7] = (vd->target_id % 100) % 10 + '0'; memcpy(&inquiryData->ProductRevisionLevel, "4.00", 4); ccb->ccb_h.status = CAM_REQ_CMP; } break; case READ_CAPACITY: { HPT_U8 *rbuf = ccb->csio.data_ptr; HPT_U32 cap; if (vd->capacity>0xfffffffful) cap = 0xfffffffful; else cap = vd->capacity - 1; rbuf[0] = (HPT_U8)(cap>>24); rbuf[1] = (HPT_U8)(cap>>16); rbuf[2] = (HPT_U8)(cap>>8); rbuf[3] = (HPT_U8)cap; rbuf[4] = 0; rbuf[5] = 0; rbuf[6] = 2; rbuf[7] = 0; ccb->ccb_h.status = CAM_REQ_CMP; break; } case SERVICE_ACTION_IN: { HPT_U8 *rbuf = ccb->csio.data_ptr; HPT_U64 cap = vd->capacity - 1; rbuf[0] = (HPT_U8)(cap>>56); rbuf[1] = (HPT_U8)(cap>>48); rbuf[2] = (HPT_U8)(cap>>40); rbuf[3] = (HPT_U8)(cap>>32); rbuf[4] = (HPT_U8)(cap>>24); rbuf[5] = (HPT_U8)(cap>>16); rbuf[6] = (HPT_U8)(cap>>8); rbuf[7] = (HPT_U8)cap; rbuf[8] = 0; rbuf[9] = 0; rbuf[10] = 2; rbuf[11] = 0; ccb->ccb_h.status = CAM_REQ_CMP; break; } case READ_6: case READ_10: case READ_16: case WRITE_6: case WRITE_10: case WRITE_16: case 0x13: case 0x2f: case 0x8f: /* VERIFY_16 */ { int error; pCmd = ldm_alloc_cmds(vbus, vd->cmds_per_request); if(!pCmd){ KdPrint(("Failed to allocate command!")); ccb->ccb_h.status = CAM_BUSY; break; } switch (cdb[0]) { case READ_6: case WRITE_6: case 0x13: pCmd->uCmd.Ide.Lba = ((HPT_U32)cdb[1] << 16) | ((HPT_U32)cdb[2] << 8) | (HPT_U32)cdb[3]; pCmd->uCmd.Ide.nSectors = (HPT_U16) cdb[4]; break; case READ_16: case WRITE_16: case 0x8f: /* VERIFY_16 */ { HPT_U64 block = ((HPT_U64)cdb[2]<<56) | ((HPT_U64)cdb[3]<<48) | ((HPT_U64)cdb[4]<<40) | ((HPT_U64)cdb[5]<<32) | ((HPT_U64)cdb[6]<<24) | ((HPT_U64)cdb[7]<<16) | ((HPT_U64)cdb[8]<<8) | ((HPT_U64)cdb[9]); pCmd->uCmd.Ide.Lba = block; pCmd->uCmd.Ide.nSectors = (HPT_U16)cdb[13] | ((HPT_U16)cdb[12]<<8); break; } default: pCmd->uCmd.Ide.Lba = (HPT_U32)cdb[5] | ((HPT_U32)cdb[4] << 8) | ((HPT_U32)cdb[3] << 16) | ((HPT_U32)cdb[2] << 24); pCmd->uCmd.Ide.nSectors = (HPT_U16) cdb[8] | ((HPT_U16)cdb[7]<<8); break; } switch (cdb[0]) { case READ_6: case READ_10: case READ_16: pCmd->flags.data_in = 1; break; case WRITE_6: case WRITE_10: case WRITE_16: pCmd->flags.data_out = 1; break; } pCmd->priv = ext = cmdext_get(vbus_ext); HPT_ASSERT(ext); ext->ccb = ccb; pCmd->target = vd; pCmd->done = os_cmddone; pCmd->buildsgl = os_buildsgl; pCmd->psg = ext->psg; pCmd->flags.physical_sg = 1; error = bus_dmamap_load_ccb(vbus_ext->io_dmat, ext->dma_map, ccb, hpt_io_dmamap_callback, pCmd, BUS_DMA_WAITOK ); KdPrint(("bus_dmamap_load return %d", error)); if (error && error!=EINPROGRESS) { os_printk("bus_dmamap_load error %d", error); cmdext_put(ext); ldm_free_cmds(pCmd); ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(ccb); } return; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); return; } static void hpt_action(struct cam_sim *sim, union ccb *ccb) { PVBUS_EXT vbus_ext = (PVBUS_EXT)cam_sim_softc(sim); KdPrint(("hpt_action(fn=%d, id=%d)", ccb->ccb_h.func_code, ccb->ccb_h.target_id)); switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: hpt_lock_vbus(vbus_ext); hpt_scsi_io(vbus_ext, ccb); hpt_unlock_vbus(vbus_ext); return; case XPT_RESET_BUS: hpt_lock_vbus(vbus_ext); ldm_reset_vbus((PVBUS)vbus_ext->vbus); hpt_unlock_vbus(vbus_ext); break; case XPT_GET_TRAN_SETTINGS: case XPT_SET_TRAN_SETTINGS: ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; case XPT_CALC_GEOMETRY: ccb->ccg.heads = 255; ccb->ccg.secs_per_track = 63; ccb->ccg.cylinders = ccb->ccg.volume_size / (ccb->ccg.heads * ccb->ccg.secs_per_track); ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_SDTR_ABLE; cpi->target_sprt = 0; cpi->hba_misc = PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = osm_max_targets; cpi->max_lun = 0; cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = cam_sim_bus(sim); cpi->initiator_id = osm_max_targets; cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "HPT ", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); #if (__FreeBSD_version >= 800000) cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; #endif cpi->ccb_h.status = CAM_REQ_CMP; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); return; } static void hpt_pci_intr(void *arg) { PVBUS_EXT vbus_ext = (PVBUS_EXT)arg; hpt_lock_vbus(vbus_ext); ldm_intr((PVBUS)vbus_ext->vbus); hpt_unlock_vbus(vbus_ext); } static void hpt_poll(struct cam_sim *sim) { hpt_pci_intr(cam_sim_softc(sim)); } static void hpt_async(void * callback_arg, u_int32_t code, struct cam_path * path, void * arg) { KdPrint(("hpt_async")); } static int hpt_shutdown(device_t dev) { KdPrint(("hpt_shutdown(dev=%p)", dev)); return 0; } static int hpt_detach(device_t dev) { /* we don't allow the driver to be unloaded. */ return EBUSY; } static void hpt_ioctl_done(struct _IOCTL_ARG *arg) { arg->ioctl_cmnd = 0; wakeup(arg); } static void __hpt_do_ioctl(PVBUS_EXT vbus_ext, IOCTL_ARG *ioctl_args) { ioctl_args->result = -1; ioctl_args->done = hpt_ioctl_done; ioctl_args->ioctl_cmnd = (void *)1; hpt_lock_vbus(vbus_ext); ldm_ioctl((PVBUS)vbus_ext->vbus, ioctl_args); while (ioctl_args->ioctl_cmnd) { if (hpt_sleep(vbus_ext, ioctl_args, PPAUSE, "hptctl", HPT_OSM_TIMEOUT)==0) break; ldm_reset_vbus((PVBUS)vbus_ext->vbus); __hpt_do_tasks(vbus_ext); } /* KdPrint(("ioctl %x result %d", ioctl_args->dwIoControlCode, ioctl_args->result)); */ hpt_unlock_vbus(vbus_ext); } static void hpt_do_ioctl(IOCTL_ARG *ioctl_args) { PVBUS vbus; PVBUS_EXT vbus_ext; ldm_for_each_vbus(vbus, vbus_ext) { __hpt_do_ioctl(vbus_ext, ioctl_args); if (ioctl_args->result!=HPT_IOCTL_RESULT_WRONG_VBUS) return; } } #define HPT_DO_IOCTL(code, inbuf, insize, outbuf, outsize) ({\ IOCTL_ARG arg;\ arg.dwIoControlCode = code;\ arg.lpInBuffer = inbuf;\ arg.lpOutBuffer = outbuf;\ arg.nInBufferSize = insize;\ arg.nOutBufferSize = outsize;\ arg.lpBytesReturned = 0;\ hpt_do_ioctl(&arg);\ arg.result;\ }) #define DEVICEID_VALID(id) ((id) && ((HPT_U32)(id)!=0xffffffff)) static int hpt_get_logical_devices(DEVICEID * pIds, int nMaxCount) { int i; HPT_U32 count = nMaxCount-1; if (HPT_DO_IOCTL(HPT_IOCTL_GET_LOGICAL_DEVICES, &count, sizeof(HPT_U32), pIds, sizeof(DEVICEID)*nMaxCount)) return -1; nMaxCount = (int)pIds[0]; for (i=0; i=503000 .d_version = D_VERSION, #endif #if (__FreeBSD_version>=503000 && __FreeBSD_version<600034) .d_flags = D_NEEDGIANT, #endif #if __FreeBSD_version<600034 #if __FreeBSD_version>501000 .d_maj = MAJOR_AUTO, #else .d_maj = HPT_DEV_MAJOR, #endif #endif }; static struct intr_config_hook hpt_ich; /* * hpt_final_init will be called after all hpt_attach. */ static void hpt_final_init(void *dummy) { int i,unit_number=0; PVBUS_EXT vbus_ext; PVBUS vbus; PHBA hba; /* Clear the config hook */ config_intrhook_disestablish(&hpt_ich); /* allocate memory */ i = 0; ldm_for_each_vbus(vbus, vbus_ext) { if (hpt_alloc_mem(vbus_ext)) { os_printk("out of memory"); return; } i++; } if (!i) { if (bootverbose) os_printk("no controller detected."); return; } /* initializing hardware */ ldm_for_each_vbus(vbus, vbus_ext) { /* make timer available here */ callout_handle_init(&vbus_ext->timer); if (hpt_init_vbus(vbus_ext)) { os_printk("fail to initialize hardware"); break; /* FIXME */ } } /* register CAM interface */ ldm_for_each_vbus(vbus, vbus_ext) { struct cam_devq *devq; struct ccb_setasync ccb; #if (__FreeBSD_version >= 500000) mtx_init(&vbus_ext->lock, "hptsleeplock", NULL, MTX_DEF); #endif if (bus_dma_tag_create(NULL,/* parent */ 4, /* alignment */ BUS_SPACE_MAXADDR_32BIT+1, /* boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ PAGE_SIZE * (os_max_sg_descriptors-1), /* maxsize */ os_max_sg_descriptors, /* nsegments */ 0x10000, /* maxsegsize */ BUS_DMA_WAITOK, /* flags */ #if __FreeBSD_version>502000 busdma_lock_mutex, /* lockfunc */ &vbus_ext->lock, /* lockfuncarg */ #endif &vbus_ext->io_dmat /* tag */)) { return ; } for (i=0; ivbus_ext = vbus_ext; ext->next = vbus_ext->cmdext_list; vbus_ext->cmdext_list = ext; if (bus_dmamap_create(vbus_ext->io_dmat, 0, &ext->dma_map)) { os_printk("Can't create dma map(%d)", i); return ; } + callout_handle_init(&ext->timeout_ch); } if ((devq = cam_simq_alloc(os_max_queue_comm)) == NULL) { os_printk("cam_simq_alloc failed"); return ; } #if __FreeBSD_version > 700025 vbus_ext->sim = cam_sim_alloc(hpt_action, hpt_poll, driver_name, vbus_ext, unit_number, &Giant, os_max_queue_comm, /*tagged*/8, devq); #else vbus_ext->sim = cam_sim_alloc(hpt_action, hpt_poll, driver_name, vbus_ext, unit_number, os_max_queue_comm, /*tagged*/8, devq); #endif unit_number++; if (!vbus_ext->sim) { os_printk("cam_sim_alloc failed"); cam_simq_free(devq); return ; } #if __FreeBSD_version > 700044 if (xpt_bus_register(vbus_ext->sim, NULL, 0) != CAM_SUCCESS) { #else if (xpt_bus_register(vbus_ext->sim, 0) != CAM_SUCCESS) { #endif os_printk("xpt_bus_register failed"); cam_sim_free(vbus_ext->sim, /*free devq*/ TRUE); vbus_ext->sim = NULL; return ; } if (xpt_create_path(&vbus_ext->path, /*periph */ NULL, cam_sim_path(vbus_ext->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { os_printk("xpt_create_path failed"); xpt_bus_deregister(cam_sim_path(vbus_ext->sim)); cam_sim_free(vbus_ext->sim, /*free_devq*/TRUE); vbus_ext->sim = NULL; return ; } xpt_setup_ccb(&ccb.ccb_h, vbus_ext->path, /*priority*/5); ccb.ccb_h.func_code = XPT_SASYNC_CB; ccb.event_enable = AC_LOST_DEVICE; ccb.callback = hpt_async; ccb.callback_arg = vbus_ext; xpt_action((union ccb *)&ccb); for (hba = vbus_ext->hba_list; hba; hba = hba->next) { int rid = 0; if ((hba->irq_res = bus_alloc_resource(hba->pcidev, SYS_RES_IRQ, &rid, 0, ~0ul, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) { os_printk("can't allocate interrupt"); return ; } if (bus_setup_intr(hba->pcidev, hba->irq_res, INTR_TYPE_CAM, #if __FreeBSD_version > 700025 NULL, hpt_pci_intr, vbus_ext, &hba->irq_handle)) #else hpt_pci_intr, vbus_ext, &hba->irq_handle)) #endif { os_printk("can't set up interrupt"); return ; } hba->ldm_adapter.him->intr_control(hba->ldm_adapter.him_handle, HPT_TRUE); } vbus_ext->shutdown_eh = EVENTHANDLER_REGISTER(shutdown_final, hpt_shutdown_vbus, vbus_ext, SHUTDOWN_PRI_DEFAULT); if (!vbus_ext->shutdown_eh) os_printk("Shutdown event registration failed"); } ldm_for_each_vbus(vbus, vbus_ext) { TASK_INIT(&vbus_ext->worker, 0, (task_fn_t *)hpt_do_tasks, vbus_ext); if (vbus_ext->tasks) TASK_ENQUEUE(&vbus_ext->worker); } make_dev(&hpt_cdevsw, DRIVER_MINOR, UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "%s", driver_name); } #if defined(KLD_MODULE) && (__FreeBSD_version >= 503000) typedef struct driverlink *driverlink_t; struct driverlink { kobj_class_t driver; TAILQ_ENTRY(driverlink) link; /* list of drivers in devclass */ }; typedef TAILQ_HEAD(driver_list, driverlink) driver_list_t; struct devclass { TAILQ_ENTRY(devclass) link; devclass_t parent; /* parent in devclass hierarchy */ driver_list_t drivers; /* bus devclasses store drivers for bus */ char *name; device_t *devices; /* array of devices indexed by unit */ int maxunit; /* size of devices array */ }; static void override_kernel_driver(void) { driverlink_t dl, dlfirst; driver_t *tmpdriver; devclass_t dc = devclass_find("pci"); if (dc){ dlfirst = TAILQ_FIRST(&dc->drivers); for (dl = dlfirst; dl; dl = TAILQ_NEXT(dl, link)) { if(strcmp(dl->driver->name, driver_name) == 0) { tmpdriver=dl->driver; dl->driver=dlfirst->driver; dlfirst->driver=tmpdriver; break; } } } } #else #define override_kernel_driver() #endif static void hpt_init(void *dummy) { if (bootverbose) os_printk("%s %s", driver_name_long, driver_ver); override_kernel_driver(); init_config(); hpt_ich.ich_func = hpt_final_init; hpt_ich.ich_arg = NULL; if (config_intrhook_establish(&hpt_ich) != 0) { printf("%s: cannot establish configuration hook\n", driver_name_long); } } SYSINIT(hptinit, SI_SUB_CONFIGURE, SI_ORDER_FIRST, hpt_init, NULL); /* * CAM driver interface */ static device_method_t driver_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hpt_probe), DEVMETHOD(device_attach, hpt_attach), DEVMETHOD(device_detach, hpt_detach), DEVMETHOD(device_shutdown, hpt_shutdown), { 0, 0 } }; static driver_t hpt_pci_driver = { driver_name, driver_methods, sizeof(HBA) }; static devclass_t hpt_devclass; #ifndef TARGETNAME #error "no TARGETNAME found" #endif /* use this to make TARGETNAME be expanded */ #define __DRIVER_MODULE(p1, p2, p3, p4, p5, p6) DRIVER_MODULE(p1, p2, p3, p4, p5, p6) #define __MODULE_VERSION(p1, p2) MODULE_VERSION(p1, p2) #define __MODULE_DEPEND(p1, p2, p3, p4, p5) MODULE_DEPEND(p1, p2, p3, p4, p5) __DRIVER_MODULE(TARGETNAME, pci, hpt_pci_driver, hpt_devclass, 0, 0); __MODULE_VERSION(TARGETNAME, 1); __MODULE_DEPEND(TARGETNAME, cam, 1, 1, 1); #if __FreeBSD_version>503000 typedef struct cdev * ioctl_dev_t; #else typedef dev_t ioctl_dev_t; #endif #if __FreeBSD_version >= 500000 typedef struct thread * ioctl_thread_t; #else typedef struct proc * ioctl_thread_t; #endif static int hpt_open(ioctl_dev_t dev, int flags, int devtype, ioctl_thread_t td) { return 0; } static int hpt_close(ioctl_dev_t dev, int flags, int devtype, ioctl_thread_t td) { return 0; } static int hpt_ioctl(ioctl_dev_t dev, u_long cmd, caddr_t data, int fflag, ioctl_thread_t td) { PHPT_IOCTL_PARAM piop=(PHPT_IOCTL_PARAM)data; IOCTL_ARG ioctl_args; HPT_U32 bytesReturned; switch (cmd){ case HPT_DO_IOCONTROL: { if (piop->Magic == HPT_IOCTL_MAGIC || piop->Magic == HPT_IOCTL_MAGIC32) { KdPrint(("ioctl=%x in=%p len=%d out=%p len=%d\n", piop->dwIoControlCode, piop->lpInBuffer, piop->nInBufferSize, piop->lpOutBuffer, piop->nOutBufferSize)); memset(&ioctl_args, 0, sizeof(ioctl_args)); ioctl_args.dwIoControlCode = piop->dwIoControlCode; ioctl_args.nInBufferSize = piop->nInBufferSize; ioctl_args.nOutBufferSize = piop->nOutBufferSize; ioctl_args.lpBytesReturned = &bytesReturned; if (ioctl_args.nInBufferSize) { ioctl_args.lpInBuffer = malloc(ioctl_args.nInBufferSize, M_DEVBUF, M_WAITOK); if (!ioctl_args.lpInBuffer) goto invalid; if (copyin((void*)piop->lpInBuffer, ioctl_args.lpInBuffer, piop->nInBufferSize)) goto invalid; } if (ioctl_args.nOutBufferSize) { ioctl_args.lpOutBuffer = malloc(ioctl_args.nOutBufferSize, M_DEVBUF, M_WAITOK); if (!ioctl_args.lpOutBuffer) goto invalid; } #if (__FreeBSD_version >= 500000) mtx_lock(&Giant); #endif hpt_do_ioctl(&ioctl_args); #if (__FreeBSD_version >= 500000) mtx_unlock(&Giant); #endif if (ioctl_args.result==HPT_IOCTL_RESULT_OK) { if (piop->nOutBufferSize) { if (copyout(ioctl_args.lpOutBuffer, (void*)piop->lpOutBuffer, piop->nOutBufferSize)) goto invalid; } if (piop->lpBytesReturned) { if (copyout(&bytesReturned, (void*)piop->lpBytesReturned, sizeof(HPT_U32))) goto invalid; } if (ioctl_args.lpInBuffer) free(ioctl_args.lpInBuffer, M_DEVBUF); if (ioctl_args.lpOutBuffer) free(ioctl_args.lpOutBuffer, M_DEVBUF); return 0; } invalid: if (ioctl_args.lpInBuffer) free(ioctl_args.lpInBuffer, M_DEVBUF); if (ioctl_args.lpOutBuffer) free(ioctl_args.lpOutBuffer, M_DEVBUF); return EFAULT; } return EFAULT; } case HPT_SCAN_BUS: { return hpt_rescan_bus(); } default: KdPrint(("invalid command!")); return EFAULT; } } static int hpt_rescan_bus(void) { union ccb *ccb; PVBUS vbus; PVBUS_EXT vbus_ext; mtx_lock(&Giant); ldm_for_each_vbus(vbus, vbus_ext) { if ((ccb = xpt_alloc_ccb()) == NULL) { return(ENOMEM); } if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(vbus_ext->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return(EIO); } xpt_rescan(ccb); } mtx_unlock(&Giant); return(0); } diff --git a/sys/dev/hpt27xx/os_bsd.h b/sys/dev/hpt27xx/os_bsd.h index e3f8e7dd6386..6de4dbbdd3fb 100644 --- a/sys/dev/hpt27xx/os_bsd.h +++ b/sys/dev/hpt27xx/os_bsd.h @@ -1,263 +1,264 @@ /*- * Copyright (c) 2011 HighPoint Technologies, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #ifndef _OS_BSD_H #define _OS_BSD_H #ifndef DBG #define DBG 0 #endif #include #include #include #if (__FreeBSD_version >= 500000) #include #include #else #include /*to support DELAY function under 4.x BSD versions*/ #endif #include #include #include #include #include #if (__FreeBSD_version >= 500000) #include #include #include #endif #include #include #include #include #include #if __FreeBSD_version >= 500043 #include #endif #include #include #include #include #include #if (__FreeBSD_version >= 500000) #include #include #else #include #include #endif #if (__FreeBSD_version <= 500043) #include #endif #include #include #include #include #include #include #include #include #include #if (__FreeBSD_version < 500043) #include #endif typedef struct _INQUIRYDATA { u_char DeviceType : 5; u_char DeviceTypeQualifier : 3; u_char DeviceTypeModifier : 7; u_char RemovableMedia : 1; u_char Versions; u_char ResponseDataFormat; u_char AdditionalLength; u_char Reserved[2]; u_char SoftReset : 1; u_char CommandQueue : 1; u_char Reserved2 : 1; u_char LinkedCommands : 1; u_char Synchronous : 1; u_char Wide16Bit : 1; u_char Wide32Bit : 1; u_char RelativeAddressing : 1; u_char VendorId[8]; u_char ProductId[16]; u_char ProductRevisionLevel[4]; u_char VendorSpecific[20]; u_char Reserved3[40]; } __attribute__((packed)) INQUIRYDATA, *PINQUIRYDATA; #endif /* private headers */ #include #include #include /* driver parameters */ extern const char driver_name[]; extern const char driver_name_long[]; extern const char driver_ver[]; extern int osm_max_targets; /* * adapter/vbus extensions: * each physical controller has an adapter_ext, passed to him.create_adapter() * each vbus has a vbus_ext passed to ldm_create_vbus(). */ #define EXT_TYPE_HBA 1 #define EXT_TYPE_VBUS 2 typedef struct _hba { int ext_type; LDM_ADAPTER ldm_adapter; device_t pcidev; PCI_ADDRESS pciaddr; struct _vbus_ext *vbus_ext; struct _hba *next; struct { struct resource *res; int type; int rid; void *base; } pcibar[6]; struct resource *irq_res; void *irq_handle; } HBA, *PHBA; typedef struct _os_cmdext { struct _vbus_ext *vbus_ext; struct _os_cmdext *next; union ccb *ccb; bus_dmamap_t dma_map; + struct callout_handle timeout_ch; SG psg[os_max_sg_descriptors]; } OS_CMDEXT, *POS_CMDEXT; typedef struct _vbus_ext { int ext_type; struct _vbus_ext *next; PHBA hba_list; struct freelist *freelist_head; struct freelist *freelist_dma_head; struct cam_sim *sim; /* sim for this vbus */ struct cam_path *path; /* peripheral, path, tgt, lun with this vbus */ #if (__FreeBSD_version >= 500000) struct mtx lock; /* general purpose lock */ #else int hpt_splx; #endif bus_dma_tag_t io_dmat; /* I/O buffer DMA tag */ POS_CMDEXT cmdext_list; OSM_TASK *tasks; struct task worker; struct callout_handle timer; eventhandler_tag shutdown_eh; /* the LDM vbus instance continues */ unsigned long vbus[0] __attribute__((aligned(sizeof(unsigned long)))); } VBUS_EXT, *PVBUS_EXT; #if __FreeBSD_version >= 500000 #define hpt_lock_vbus(vbus_ext) mtx_lock(&(vbus_ext)->lock) #define hpt_unlock_vbus(vbus_ext) mtx_unlock(&(vbus_ext)->lock) #else static __inline void hpt_lock_vbus(PVBUS_EXT vbus_ext) { vbus_ext->hpt_splx = splcam(); } static __inline void hpt_unlock_vbus(PVBUS_EXT vbus_ext) { splx(vbus_ext->hpt_splx); } #endif #define HPT_OSM_TIMEOUT (20*hz) /* timeout value for OS commands */ #define HPT_DO_IOCONTROL _IOW('H', 0, HPT_IOCTL_PARAM) #define HPT_SCAN_BUS _IO('H', 1) #if __FreeBSD_version >= 501000 #define TASK_ENQUEUE(task) taskqueue_enqueue(taskqueue_swi_giant,(task)); #else #define TASK_ENQUEUE(task) taskqueue_enqueue(taskqueue_swi,(task)); #endif #if __FreeBSD_version >= 500000 static __inline int hpt_sleep(PVBUS_EXT vbus_ext, void *ident, int priority, const char *wmesg, int timo) { return msleep(ident, &vbus_ext->lock, priority, wmesg, timo); } #else static __inline int hpt_sleep(PVBUS_EXT vbus_ext, void *ident, int priority, const char *wmesg, int timo) { int retval = 0; asleep(ident, priority, wmesg, timo); hpt_unlock_vbus(vbus_ext); retval = await(priority, timo); hpt_lock_vbus(vbus_ext); return retval; } #endif #if __FreeBSD_version < 501000 #define READ_16 0x88 #define WRITE_16 0x8a #define SERVICE_ACTION_IN 0x9e #endif #define HPT_DEV_MAJOR 200 diff --git a/sys/dev/hptiop/hptiop.c b/sys/dev/hptiop/hptiop.c index 50a9c43e7543..6b8377456df1 100644 --- a/sys/dev/hptiop/hptiop.c +++ b/sys/dev/hptiop/hptiop.c @@ -1,2844 +1,2845 @@ /* * HighPoint RR3xxx/4xxx RAID Driver for FreeBSD * Copyright (C) 2007-2012 HighPoint Technologies, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char driver_name[] = "hptiop"; static const char driver_version[] = "v1.9"; static devclass_t hptiop_devclass; static int hptiop_send_sync_msg(struct hpt_iop_hba *hba, u_int32_t msg, u_int32_t millisec); static void hptiop_request_callback_itl(struct hpt_iop_hba *hba, u_int32_t req); static void hptiop_request_callback_mv(struct hpt_iop_hba *hba, u_int64_t req); static void hptiop_request_callback_mvfrey(struct hpt_iop_hba *hba, u_int32_t req); static void hptiop_os_message_callback(struct hpt_iop_hba *hba, u_int32_t msg); static int hptiop_do_ioctl_itl(struct hpt_iop_hba *hba, struct hpt_iop_ioctl_param *pParams); static int hptiop_do_ioctl_mv(struct hpt_iop_hba *hba, struct hpt_iop_ioctl_param *pParams); static int hptiop_do_ioctl_mvfrey(struct hpt_iop_hba *hba, struct hpt_iop_ioctl_param *pParams); static int hptiop_rescan_bus(struct hpt_iop_hba *hba); static int hptiop_alloc_pci_res_itl(struct hpt_iop_hba *hba); static int hptiop_alloc_pci_res_mv(struct hpt_iop_hba *hba); static int hptiop_alloc_pci_res_mvfrey(struct hpt_iop_hba *hba); static int hptiop_get_config_itl(struct hpt_iop_hba *hba, struct hpt_iop_request_get_config *config); static int hptiop_get_config_mv(struct hpt_iop_hba *hba, struct hpt_iop_request_get_config *config); static int hptiop_get_config_mvfrey(struct hpt_iop_hba *hba, struct hpt_iop_request_get_config *config); static int hptiop_set_config_itl(struct hpt_iop_hba *hba, struct hpt_iop_request_set_config *config); static int hptiop_set_config_mv(struct hpt_iop_hba *hba, struct hpt_iop_request_set_config *config); static int hptiop_set_config_mvfrey(struct hpt_iop_hba *hba, struct hpt_iop_request_set_config *config); static int hptiop_internal_memalloc_mv(struct hpt_iop_hba *hba); static int hptiop_internal_memalloc_mvfrey(struct hpt_iop_hba *hba); static int hptiop_internal_memfree_itl(struct hpt_iop_hba *hba); static int hptiop_internal_memfree_mv(struct hpt_iop_hba *hba); static int hptiop_internal_memfree_mvfrey(struct hpt_iop_hba *hba); static int hptiop_post_ioctl_command_itl(struct hpt_iop_hba *hba, u_int32_t req32, struct hpt_iop_ioctl_param *pParams); static int hptiop_post_ioctl_command_mv(struct hpt_iop_hba *hba, struct hpt_iop_request_ioctl_command *req, struct hpt_iop_ioctl_param *pParams); static int hptiop_post_ioctl_command_mvfrey(struct hpt_iop_hba *hba, struct hpt_iop_request_ioctl_command *req, struct hpt_iop_ioctl_param *pParams); static void hptiop_post_req_itl(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb, bus_dma_segment_t *segs, int nsegs); static void hptiop_post_req_mv(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb, bus_dma_segment_t *segs, int nsegs); static void hptiop_post_req_mvfrey(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb, bus_dma_segment_t *segs, int nsegs); static void hptiop_post_msg_itl(struct hpt_iop_hba *hba, u_int32_t msg); static void hptiop_post_msg_mv(struct hpt_iop_hba *hba, u_int32_t msg); static void hptiop_post_msg_mvfrey(struct hpt_iop_hba *hba, u_int32_t msg); static void hptiop_enable_intr_itl(struct hpt_iop_hba *hba); static void hptiop_enable_intr_mv(struct hpt_iop_hba *hba); static void hptiop_enable_intr_mvfrey(struct hpt_iop_hba *hba); static void hptiop_disable_intr_itl(struct hpt_iop_hba *hba); static void hptiop_disable_intr_mv(struct hpt_iop_hba *hba); static void hptiop_disable_intr_mvfrey(struct hpt_iop_hba *hba); static void hptiop_free_srb(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb); static int hptiop_os_query_remove_device(struct hpt_iop_hba *hba, int tid); static int hptiop_probe(device_t dev); static int hptiop_attach(device_t dev); static int hptiop_detach(device_t dev); static int hptiop_shutdown(device_t dev); static void hptiop_action(struct cam_sim *sim, union ccb *ccb); static void hptiop_poll(struct cam_sim *sim); static void hptiop_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); static void hptiop_pci_intr(void *arg); static void hptiop_release_resource(struct hpt_iop_hba *hba); static void hptiop_reset_adapter(void *argv); static d_open_t hptiop_open; static d_close_t hptiop_close; static d_ioctl_t hptiop_ioctl; static struct cdevsw hptiop_cdevsw = { .d_open = hptiop_open, .d_close = hptiop_close, .d_ioctl = hptiop_ioctl, .d_name = driver_name, .d_version = D_VERSION, }; #define hba_from_dev(dev) \ ((struct hpt_iop_hba *)devclass_get_softc(hptiop_devclass, dev2unit(dev))) #define BUS_SPACE_WRT4_ITL(offset, value) bus_space_write_4(hba->bar0t,\ hba->bar0h, offsetof(struct hpt_iopmu_itl, offset), (value)) #define BUS_SPACE_RD4_ITL(offset) bus_space_read_4(hba->bar0t,\ hba->bar0h, offsetof(struct hpt_iopmu_itl, offset)) #define BUS_SPACE_WRT4_MV0(offset, value) bus_space_write_4(hba->bar0t,\ hba->bar0h, offsetof(struct hpt_iopmv_regs, offset), value) #define BUS_SPACE_RD4_MV0(offset) bus_space_read_4(hba->bar0t,\ hba->bar0h, offsetof(struct hpt_iopmv_regs, offset)) #define BUS_SPACE_WRT4_MV2(offset, value) bus_space_write_4(hba->bar2t,\ hba->bar2h, offsetof(struct hpt_iopmu_mv, offset), value) #define BUS_SPACE_RD4_MV2(offset) bus_space_read_4(hba->bar2t,\ hba->bar2h, offsetof(struct hpt_iopmu_mv, offset)) #define BUS_SPACE_WRT4_MVFREY2(offset, value) bus_space_write_4(hba->bar2t,\ hba->bar2h, offsetof(struct hpt_iopmu_mvfrey, offset), value) #define BUS_SPACE_RD4_MVFREY2(offset) bus_space_read_4(hba->bar2t,\ hba->bar2h, offsetof(struct hpt_iopmu_mvfrey, offset)) static int hptiop_open(ioctl_dev_t dev, int flags, int devtype, ioctl_thread_t proc) { struct hpt_iop_hba *hba = hba_from_dev(dev); if (hba==NULL) return ENXIO; if (hba->flag & HPT_IOCTL_FLAG_OPEN) return EBUSY; hba->flag |= HPT_IOCTL_FLAG_OPEN; return 0; } static int hptiop_close(ioctl_dev_t dev, int flags, int devtype, ioctl_thread_t proc) { struct hpt_iop_hba *hba = hba_from_dev(dev); hba->flag &= ~(u_int32_t)HPT_IOCTL_FLAG_OPEN; return 0; } static int hptiop_ioctl(ioctl_dev_t dev, u_long cmd, caddr_t data, int flags, ioctl_thread_t proc) { int ret = EFAULT; struct hpt_iop_hba *hba = hba_from_dev(dev); mtx_lock(&Giant); switch (cmd) { case HPT_DO_IOCONTROL: ret = hba->ops->do_ioctl(hba, (struct hpt_iop_ioctl_param *)data); break; case HPT_SCAN_BUS: ret = hptiop_rescan_bus(hba); break; } mtx_unlock(&Giant); return ret; } static u_int64_t hptiop_mv_outbound_read(struct hpt_iop_hba *hba) { u_int64_t p; u_int32_t outbound_tail = BUS_SPACE_RD4_MV2(outbound_tail); u_int32_t outbound_head = BUS_SPACE_RD4_MV2(outbound_head); if (outbound_tail != outbound_head) { bus_space_read_region_4(hba->bar2t, hba->bar2h, offsetof(struct hpt_iopmu_mv, outbound_q[outbound_tail]), (u_int32_t *)&p, 2); outbound_tail++; if (outbound_tail == MVIOP_QUEUE_LEN) outbound_tail = 0; BUS_SPACE_WRT4_MV2(outbound_tail, outbound_tail); return p; } else return 0; } static void hptiop_mv_inbound_write(u_int64_t p, struct hpt_iop_hba *hba) { u_int32_t inbound_head = BUS_SPACE_RD4_MV2(inbound_head); u_int32_t head = inbound_head + 1; if (head == MVIOP_QUEUE_LEN) head = 0; bus_space_write_region_4(hba->bar2t, hba->bar2h, offsetof(struct hpt_iopmu_mv, inbound_q[inbound_head]), (u_int32_t *)&p, 2); BUS_SPACE_WRT4_MV2(inbound_head, head); BUS_SPACE_WRT4_MV0(inbound_doorbell, MVIOP_MU_INBOUND_INT_POSTQUEUE); } static void hptiop_post_msg_itl(struct hpt_iop_hba *hba, u_int32_t msg) { BUS_SPACE_WRT4_ITL(inbound_msgaddr0, msg); BUS_SPACE_RD4_ITL(outbound_intstatus); } static void hptiop_post_msg_mv(struct hpt_iop_hba *hba, u_int32_t msg) { BUS_SPACE_WRT4_MV2(inbound_msg, msg); BUS_SPACE_WRT4_MV0(inbound_doorbell, MVIOP_MU_INBOUND_INT_MSG); BUS_SPACE_RD4_MV0(outbound_intmask); } static void hptiop_post_msg_mvfrey(struct hpt_iop_hba *hba, u_int32_t msg) { BUS_SPACE_WRT4_MVFREY2(f0_to_cpu_msg_a, msg); BUS_SPACE_RD4_MVFREY2(f0_to_cpu_msg_a); } static int hptiop_wait_ready_itl(struct hpt_iop_hba * hba, u_int32_t millisec) { u_int32_t req=0; int i; for (i = 0; i < millisec; i++) { req = BUS_SPACE_RD4_ITL(inbound_queue); if (req != IOPMU_QUEUE_EMPTY) break; DELAY(1000); } if (req!=IOPMU_QUEUE_EMPTY) { BUS_SPACE_WRT4_ITL(outbound_queue, req); BUS_SPACE_RD4_ITL(outbound_intstatus); return 0; } return -1; } static int hptiop_wait_ready_mv(struct hpt_iop_hba * hba, u_int32_t millisec) { if (hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec)) return -1; return 0; } static int hptiop_wait_ready_mvfrey(struct hpt_iop_hba * hba, u_int32_t millisec) { if (hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec)) return -1; return 0; } static void hptiop_request_callback_itl(struct hpt_iop_hba * hba, u_int32_t index) { struct hpt_iop_srb *srb; struct hpt_iop_request_scsi_command *req=0; union ccb *ccb; u_int8_t *cdb; u_int32_t result, temp, dxfer; u_int64_t temp64; if (index & IOPMU_QUEUE_MASK_HOST_BITS) { /*host req*/ if (hba->firmware_version > 0x01020000 || hba->interface_version > 0x01020000) { srb = hba->srb[index & ~(u_int32_t) (IOPMU_QUEUE_ADDR_HOST_BIT | IOPMU_QUEUE_REQUEST_RESULT_BIT)]; req = (struct hpt_iop_request_scsi_command *)srb; if (index & IOPMU_QUEUE_REQUEST_RESULT_BIT) result = IOP_RESULT_SUCCESS; else result = req->header.result; } else { srb = hba->srb[index & ~(u_int32_t)IOPMU_QUEUE_ADDR_HOST_BIT]; req = (struct hpt_iop_request_scsi_command *)srb; result = req->header.result; } dxfer = req->dataxfer_length; goto srb_complete; } /*iop req*/ temp = bus_space_read_4(hba->bar0t, hba->bar0h, index + offsetof(struct hpt_iop_request_header, type)); result = bus_space_read_4(hba->bar0t, hba->bar0h, index + offsetof(struct hpt_iop_request_header, result)); switch(temp) { case IOP_REQUEST_TYPE_IOCTL_COMMAND: { temp64 = 0; bus_space_write_region_4(hba->bar0t, hba->bar0h, index + offsetof(struct hpt_iop_request_header, context), (u_int32_t *)&temp64, 2); wakeup((void *)((unsigned long)hba->u.itl.mu + index)); break; } case IOP_REQUEST_TYPE_SCSI_COMMAND: bus_space_read_region_4(hba->bar0t, hba->bar0h, index + offsetof(struct hpt_iop_request_header, context), (u_int32_t *)&temp64, 2); srb = (struct hpt_iop_srb *)(unsigned long)temp64; dxfer = bus_space_read_4(hba->bar0t, hba->bar0h, index + offsetof(struct hpt_iop_request_scsi_command, dataxfer_length)); srb_complete: ccb = (union ccb *)srb->ccb; if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; if (cdb[0] == SYNCHRONIZE_CACHE) { /* ??? */ ccb->ccb_h.status = CAM_REQ_CMP; goto scsi_done; } switch (result) { case IOP_RESULT_SUCCESS: switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_IN: bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(hba->io_dmat, srb->dma_map); break; case CAM_DIR_OUT: bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(hba->io_dmat, srb->dma_map); break; } ccb->ccb_h.status = CAM_REQ_CMP; break; case IOP_RESULT_BAD_TARGET: ccb->ccb_h.status = CAM_DEV_NOT_THERE; break; case IOP_RESULT_BUSY: ccb->ccb_h.status = CAM_BUSY; break; case IOP_RESULT_INVALID_REQUEST: ccb->ccb_h.status = CAM_REQ_INVALID; break; case IOP_RESULT_FAIL: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; case IOP_RESULT_RESET: ccb->ccb_h.status = CAM_BUSY; break; case IOP_RESULT_CHECK_CONDITION: memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); if (dxfer < ccb->csio.sense_len) ccb->csio.sense_resid = ccb->csio.sense_len - dxfer; else ccb->csio.sense_resid = 0; if (srb->srb_flag & HPT_SRB_FLAG_HIGH_MEM_ACESS) {/*iop*/ bus_space_read_region_1(hba->bar0t, hba->bar0h, index + offsetof(struct hpt_iop_request_scsi_command, sg_list), (u_int8_t *)&ccb->csio.sense_data, MIN(dxfer, sizeof(ccb->csio.sense_data))); } else { memcpy(&ccb->csio.sense_data, &req->sg_list, MIN(dxfer, sizeof(ccb->csio.sense_data))); } ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; ccb->ccb_h.status |= CAM_AUTOSNS_VALID; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; break; default: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; } scsi_done: if (srb->srb_flag & HPT_SRB_FLAG_HIGH_MEM_ACESS) BUS_SPACE_WRT4_ITL(outbound_queue, index); ccb->csio.resid = ccb->csio.dxfer_len - dxfer; hptiop_free_srb(hba, srb); xpt_done(ccb); break; } } static void hptiop_drain_outbound_queue_itl(struct hpt_iop_hba *hba) { u_int32_t req, temp; while ((req = BUS_SPACE_RD4_ITL(outbound_queue)) !=IOPMU_QUEUE_EMPTY) { if (req & IOPMU_QUEUE_MASK_HOST_BITS) hptiop_request_callback_itl(hba, req); else { struct hpt_iop_request_header *p; p = (struct hpt_iop_request_header *) ((char *)hba->u.itl.mu + req); temp = bus_space_read_4(hba->bar0t, hba->bar0h,req + offsetof(struct hpt_iop_request_header, flags)); if (temp & IOP_REQUEST_FLAG_SYNC_REQUEST) { u_int64_t temp64; bus_space_read_region_4(hba->bar0t, hba->bar0h,req + offsetof(struct hpt_iop_request_header, context), (u_int32_t *)&temp64, 2); if (temp64) { hptiop_request_callback_itl(hba, req); } else { temp64 = 1; bus_space_write_region_4(hba->bar0t, hba->bar0h,req + offsetof(struct hpt_iop_request_header, context), (u_int32_t *)&temp64, 2); } } else hptiop_request_callback_itl(hba, req); } } } static int hptiop_intr_itl(struct hpt_iop_hba * hba) { u_int32_t status; int ret = 0; status = BUS_SPACE_RD4_ITL(outbound_intstatus); if (status & IOPMU_OUTBOUND_INT_MSG0) { u_int32_t msg = BUS_SPACE_RD4_ITL(outbound_msgaddr0); KdPrint(("hptiop: received outbound msg %x\n", msg)); BUS_SPACE_WRT4_ITL(outbound_intstatus, IOPMU_OUTBOUND_INT_MSG0); hptiop_os_message_callback(hba, msg); ret = 1; } if (status & IOPMU_OUTBOUND_INT_POSTQUEUE) { hptiop_drain_outbound_queue_itl(hba); ret = 1; } return ret; } static void hptiop_request_callback_mv(struct hpt_iop_hba * hba, u_int64_t _tag) { u_int32_t context = (u_int32_t)_tag; if (context & MVIOP_CMD_TYPE_SCSI) { struct hpt_iop_srb *srb; struct hpt_iop_request_scsi_command *req; union ccb *ccb; u_int8_t *cdb; srb = hba->srb[context >> MVIOP_REQUEST_NUMBER_START_BIT]; req = (struct hpt_iop_request_scsi_command *)srb; ccb = (union ccb *)srb->ccb; if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; if (cdb[0] == SYNCHRONIZE_CACHE) { /* ??? */ ccb->ccb_h.status = CAM_REQ_CMP; goto scsi_done; } if (context & MVIOP_MU_QUEUE_REQUEST_RESULT_BIT) req->header.result = IOP_RESULT_SUCCESS; switch (req->header.result) { case IOP_RESULT_SUCCESS: switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_IN: bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(hba->io_dmat, srb->dma_map); break; case CAM_DIR_OUT: bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(hba->io_dmat, srb->dma_map); break; } ccb->ccb_h.status = CAM_REQ_CMP; break; case IOP_RESULT_BAD_TARGET: ccb->ccb_h.status = CAM_DEV_NOT_THERE; break; case IOP_RESULT_BUSY: ccb->ccb_h.status = CAM_BUSY; break; case IOP_RESULT_INVALID_REQUEST: ccb->ccb_h.status = CAM_REQ_INVALID; break; case IOP_RESULT_FAIL: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; case IOP_RESULT_RESET: ccb->ccb_h.status = CAM_BUSY; break; case IOP_RESULT_CHECK_CONDITION: memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); if (req->dataxfer_length < ccb->csio.sense_len) ccb->csio.sense_resid = ccb->csio.sense_len - req->dataxfer_length; else ccb->csio.sense_resid = 0; memcpy(&ccb->csio.sense_data, &req->sg_list, MIN(req->dataxfer_length, sizeof(ccb->csio.sense_data))); ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; ccb->ccb_h.status |= CAM_AUTOSNS_VALID; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; break; default: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; } scsi_done: ccb->csio.resid = ccb->csio.dxfer_len - req->dataxfer_length; hptiop_free_srb(hba, srb); xpt_done(ccb); } else if (context & MVIOP_CMD_TYPE_IOCTL) { struct hpt_iop_request_ioctl_command *req = hba->ctlcfg_ptr; if (context & MVIOP_MU_QUEUE_REQUEST_RESULT_BIT) hba->config_done = 1; else hba->config_done = -1; wakeup(req); } else if (context & (MVIOP_CMD_TYPE_SET_CONFIG | MVIOP_CMD_TYPE_GET_CONFIG)) hba->config_done = 1; else { device_printf(hba->pcidev, "wrong callback type\n"); } } static void hptiop_request_callback_mvfrey(struct hpt_iop_hba * hba, u_int32_t _tag) { u_int32_t req_type = _tag & 0xf; struct hpt_iop_srb *srb; struct hpt_iop_request_scsi_command *req; union ccb *ccb; u_int8_t *cdb; switch (req_type) { case IOP_REQUEST_TYPE_GET_CONFIG: case IOP_REQUEST_TYPE_SET_CONFIG: hba->config_done = 1; break; case IOP_REQUEST_TYPE_SCSI_COMMAND: srb = hba->srb[(_tag >> 4) & 0xff]; req = (struct hpt_iop_request_scsi_command *)srb; ccb = (union ccb *)srb->ccb; - untimeout(hptiop_reset_adapter, hba, ccb->ccb_h.timeout_ch); + untimeout(hptiop_reset_adapter, hba, srb->timeout_ch); if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; if (cdb[0] == SYNCHRONIZE_CACHE) { /* ??? */ ccb->ccb_h.status = CAM_REQ_CMP; goto scsi_done; } if (_tag & MVFREYIOPMU_QUEUE_REQUEST_RESULT_BIT) req->header.result = IOP_RESULT_SUCCESS; switch (req->header.result) { case IOP_RESULT_SUCCESS: switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_IN: bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(hba->io_dmat, srb->dma_map); break; case CAM_DIR_OUT: bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(hba->io_dmat, srb->dma_map); break; } ccb->ccb_h.status = CAM_REQ_CMP; break; case IOP_RESULT_BAD_TARGET: ccb->ccb_h.status = CAM_DEV_NOT_THERE; break; case IOP_RESULT_BUSY: ccb->ccb_h.status = CAM_BUSY; break; case IOP_RESULT_INVALID_REQUEST: ccb->ccb_h.status = CAM_REQ_INVALID; break; case IOP_RESULT_FAIL: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; case IOP_RESULT_RESET: ccb->ccb_h.status = CAM_BUSY; break; case IOP_RESULT_CHECK_CONDITION: memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); if (req->dataxfer_length < ccb->csio.sense_len) ccb->csio.sense_resid = ccb->csio.sense_len - req->dataxfer_length; else ccb->csio.sense_resid = 0; memcpy(&ccb->csio.sense_data, &req->sg_list, MIN(req->dataxfer_length, sizeof(ccb->csio.sense_data))); ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; ccb->ccb_h.status |= CAM_AUTOSNS_VALID; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; break; default: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; } scsi_done: ccb->csio.resid = ccb->csio.dxfer_len - req->dataxfer_length; hptiop_free_srb(hba, srb); xpt_done(ccb); break; case IOP_REQUEST_TYPE_IOCTL_COMMAND: if (_tag & MVFREYIOPMU_QUEUE_REQUEST_RESULT_BIT) hba->config_done = 1; else hba->config_done = -1; wakeup((struct hpt_iop_request_ioctl_command *)hba->ctlcfg_ptr); break; default: device_printf(hba->pcidev, "wrong callback type\n"); break; } } static void hptiop_drain_outbound_queue_mv(struct hpt_iop_hba * hba) { u_int64_t req; while ((req = hptiop_mv_outbound_read(hba))) { if (req & MVIOP_MU_QUEUE_ADDR_HOST_BIT) { if (req & MVIOP_MU_QUEUE_REQUEST_RETURN_CONTEXT) { hptiop_request_callback_mv(hba, req); } } } } static int hptiop_intr_mv(struct hpt_iop_hba * hba) { u_int32_t status; int ret = 0; status = BUS_SPACE_RD4_MV0(outbound_doorbell); if (status) BUS_SPACE_WRT4_MV0(outbound_doorbell, ~status); if (status & MVIOP_MU_OUTBOUND_INT_MSG) { u_int32_t msg = BUS_SPACE_RD4_MV2(outbound_msg); KdPrint(("hptiop: received outbound msg %x\n", msg)); hptiop_os_message_callback(hba, msg); ret = 1; } if (status & MVIOP_MU_OUTBOUND_INT_POSTQUEUE) { hptiop_drain_outbound_queue_mv(hba); ret = 1; } return ret; } static int hptiop_intr_mvfrey(struct hpt_iop_hba * hba) { u_int32_t status, _tag, cptr; int ret = 0; if (hba->initialized) { BUS_SPACE_WRT4_MVFREY2(pcie_f0_int_enable, 0); } status = BUS_SPACE_RD4_MVFREY2(f0_doorbell); if (status) { BUS_SPACE_WRT4_MVFREY2(f0_doorbell, status); if (status & CPU_TO_F0_DRBL_MSG_A_BIT) { u_int32_t msg = BUS_SPACE_RD4_MVFREY2(cpu_to_f0_msg_a); hptiop_os_message_callback(hba, msg); } ret = 1; } status = BUS_SPACE_RD4_MVFREY2(isr_cause); if (status) { BUS_SPACE_WRT4_MVFREY2(isr_cause, status); do { cptr = *hba->u.mvfrey.outlist_cptr & 0xff; while (hba->u.mvfrey.outlist_rptr != cptr) { hba->u.mvfrey.outlist_rptr++; if (hba->u.mvfrey.outlist_rptr == hba->u.mvfrey.list_count) { hba->u.mvfrey.outlist_rptr = 0; } _tag = hba->u.mvfrey.outlist[hba->u.mvfrey.outlist_rptr].val; hptiop_request_callback_mvfrey(hba, _tag); ret = 2; } } while (cptr != (*hba->u.mvfrey.outlist_cptr & 0xff)); } if (hba->initialized) { BUS_SPACE_WRT4_MVFREY2(pcie_f0_int_enable, 0x1010); } return ret; } static int hptiop_send_sync_request_itl(struct hpt_iop_hba * hba, u_int32_t req32, u_int32_t millisec) { u_int32_t i; u_int64_t temp64; BUS_SPACE_WRT4_ITL(inbound_queue, req32); BUS_SPACE_RD4_ITL(outbound_intstatus); for (i = 0; i < millisec; i++) { hptiop_intr_itl(hba); bus_space_read_region_4(hba->bar0t, hba->bar0h, req32 + offsetof(struct hpt_iop_request_header, context), (u_int32_t *)&temp64, 2); if (temp64) return 0; DELAY(1000); } return -1; } static int hptiop_send_sync_request_mv(struct hpt_iop_hba *hba, void *req, u_int32_t millisec) { u_int32_t i; u_int64_t phy_addr; hba->config_done = 0; phy_addr = hba->ctlcfgcmd_phy | (u_int64_t)MVIOP_MU_QUEUE_ADDR_HOST_BIT; ((struct hpt_iop_request_get_config *)req)->header.flags |= IOP_REQUEST_FLAG_SYNC_REQUEST | IOP_REQUEST_FLAG_OUTPUT_CONTEXT; hptiop_mv_inbound_write(phy_addr, hba); BUS_SPACE_RD4_MV0(outbound_intmask); for (i = 0; i < millisec; i++) { hptiop_intr_mv(hba); if (hba->config_done) return 0; DELAY(1000); } return -1; } static int hptiop_send_sync_request_mvfrey(struct hpt_iop_hba *hba, void *req, u_int32_t millisec) { u_int32_t i, index; u_int64_t phy_addr; struct hpt_iop_request_header *reqhdr = (struct hpt_iop_request_header *)req; hba->config_done = 0; phy_addr = hba->ctlcfgcmd_phy; reqhdr->flags = IOP_REQUEST_FLAG_SYNC_REQUEST | IOP_REQUEST_FLAG_OUTPUT_CONTEXT | IOP_REQUEST_FLAG_ADDR_BITS | ((phy_addr >> 16) & 0xffff0000); reqhdr->context = ((phy_addr & 0xffffffff) << 32 ) | IOPMU_QUEUE_ADDR_HOST_BIT | reqhdr->type; hba->u.mvfrey.inlist_wptr++; index = hba->u.mvfrey.inlist_wptr & 0x3fff; if (index == hba->u.mvfrey.list_count) { index = 0; hba->u.mvfrey.inlist_wptr &= ~0x3fff; hba->u.mvfrey.inlist_wptr ^= CL_POINTER_TOGGLE; } hba->u.mvfrey.inlist[index].addr = phy_addr; hba->u.mvfrey.inlist[index].intrfc_len = (reqhdr->size + 3) / 4; BUS_SPACE_WRT4_MVFREY2(inbound_write_ptr, hba->u.mvfrey.inlist_wptr); BUS_SPACE_RD4_MVFREY2(inbound_write_ptr); for (i = 0; i < millisec; i++) { hptiop_intr_mvfrey(hba); if (hba->config_done) return 0; DELAY(1000); } return -1; } static int hptiop_send_sync_msg(struct hpt_iop_hba *hba, u_int32_t msg, u_int32_t millisec) { u_int32_t i; hba->msg_done = 0; hba->ops->post_msg(hba, msg); for (i=0; iops->iop_intr(hba); if (hba->msg_done) break; DELAY(1000); } return hba->msg_done? 0 : -1; } static int hptiop_get_config_itl(struct hpt_iop_hba * hba, struct hpt_iop_request_get_config * config) { u_int32_t req32; config->header.size = sizeof(struct hpt_iop_request_get_config); config->header.type = IOP_REQUEST_TYPE_GET_CONFIG; config->header.flags = IOP_REQUEST_FLAG_SYNC_REQUEST; config->header.result = IOP_RESULT_PENDING; config->header.context = 0; req32 = BUS_SPACE_RD4_ITL(inbound_queue); if (req32 == IOPMU_QUEUE_EMPTY) return -1; bus_space_write_region_4(hba->bar0t, hba->bar0h, req32, (u_int32_t *)config, sizeof(struct hpt_iop_request_header) >> 2); if (hptiop_send_sync_request_itl(hba, req32, 20000)) { KdPrint(("hptiop: get config send cmd failed")); return -1; } bus_space_read_region_4(hba->bar0t, hba->bar0h, req32, (u_int32_t *)config, sizeof(struct hpt_iop_request_get_config) >> 2); BUS_SPACE_WRT4_ITL(outbound_queue, req32); return 0; } static int hptiop_get_config_mv(struct hpt_iop_hba * hba, struct hpt_iop_request_get_config * config) { struct hpt_iop_request_get_config *req; if (!(req = hba->ctlcfg_ptr)) return -1; req->header.flags = 0; req->header.type = IOP_REQUEST_TYPE_GET_CONFIG; req->header.size = sizeof(struct hpt_iop_request_get_config); req->header.result = IOP_RESULT_PENDING; req->header.context = MVIOP_CMD_TYPE_GET_CONFIG; if (hptiop_send_sync_request_mv(hba, req, 20000)) { KdPrint(("hptiop: get config send cmd failed")); return -1; } *config = *req; return 0; } static int hptiop_get_config_mvfrey(struct hpt_iop_hba * hba, struct hpt_iop_request_get_config * config) { struct hpt_iop_request_get_config *info = hba->u.mvfrey.config; if (info->header.size != sizeof(struct hpt_iop_request_get_config) || info->header.type != IOP_REQUEST_TYPE_GET_CONFIG) { KdPrint(("hptiop: header size %x/%x type %x/%x", info->header.size, (int)sizeof(struct hpt_iop_request_get_config), info->header.type, IOP_REQUEST_TYPE_GET_CONFIG)); return -1; } config->interface_version = info->interface_version; config->firmware_version = info->firmware_version; config->max_requests = info->max_requests; config->request_size = info->request_size; config->max_sg_count = info->max_sg_count; config->data_transfer_length = info->data_transfer_length; config->alignment_mask = info->alignment_mask; config->max_devices = info->max_devices; config->sdram_size = info->sdram_size; KdPrint(("hptiop: maxreq %x reqsz %x datalen %x maxdev %x sdram %x", config->max_requests, config->request_size, config->data_transfer_length, config->max_devices, config->sdram_size)); return 0; } static int hptiop_set_config_itl(struct hpt_iop_hba *hba, struct hpt_iop_request_set_config *config) { u_int32_t req32; req32 = BUS_SPACE_RD4_ITL(inbound_queue); if (req32 == IOPMU_QUEUE_EMPTY) return -1; config->header.size = sizeof(struct hpt_iop_request_set_config); config->header.type = IOP_REQUEST_TYPE_SET_CONFIG; config->header.flags = IOP_REQUEST_FLAG_SYNC_REQUEST; config->header.result = IOP_RESULT_PENDING; config->header.context = 0; bus_space_write_region_4(hba->bar0t, hba->bar0h, req32, (u_int32_t *)config, sizeof(struct hpt_iop_request_set_config) >> 2); if (hptiop_send_sync_request_itl(hba, req32, 20000)) { KdPrint(("hptiop: set config send cmd failed")); return -1; } BUS_SPACE_WRT4_ITL(outbound_queue, req32); return 0; } static int hptiop_set_config_mv(struct hpt_iop_hba *hba, struct hpt_iop_request_set_config *config) { struct hpt_iop_request_set_config *req; if (!(req = hba->ctlcfg_ptr)) return -1; memcpy((u_int8_t *)req + sizeof(struct hpt_iop_request_header), (u_int8_t *)config + sizeof(struct hpt_iop_request_header), sizeof(struct hpt_iop_request_set_config) - sizeof(struct hpt_iop_request_header)); req->header.flags = 0; req->header.type = IOP_REQUEST_TYPE_SET_CONFIG; req->header.size = sizeof(struct hpt_iop_request_set_config); req->header.result = IOP_RESULT_PENDING; req->header.context = MVIOP_CMD_TYPE_SET_CONFIG; if (hptiop_send_sync_request_mv(hba, req, 20000)) { KdPrint(("hptiop: set config send cmd failed")); return -1; } return 0; } static int hptiop_set_config_mvfrey(struct hpt_iop_hba *hba, struct hpt_iop_request_set_config *config) { struct hpt_iop_request_set_config *req; if (!(req = hba->ctlcfg_ptr)) return -1; memcpy((u_int8_t *)req + sizeof(struct hpt_iop_request_header), (u_int8_t *)config + sizeof(struct hpt_iop_request_header), sizeof(struct hpt_iop_request_set_config) - sizeof(struct hpt_iop_request_header)); req->header.type = IOP_REQUEST_TYPE_SET_CONFIG; req->header.size = sizeof(struct hpt_iop_request_set_config); req->header.result = IOP_RESULT_PENDING; if (hptiop_send_sync_request_mvfrey(hba, req, 20000)) { KdPrint(("hptiop: set config send cmd failed")); return -1; } return 0; } static int hptiop_post_ioctl_command_itl(struct hpt_iop_hba *hba, u_int32_t req32, struct hpt_iop_ioctl_param *pParams) { u_int64_t temp64; struct hpt_iop_request_ioctl_command req; if ((((pParams->nInBufferSize + 3) & ~3) + pParams->nOutBufferSize) > (hba->max_request_size - offsetof(struct hpt_iop_request_ioctl_command, buf))) { device_printf(hba->pcidev, "request size beyond max value"); return -1; } req.header.size = offsetof(struct hpt_iop_request_ioctl_command, buf) + pParams->nInBufferSize; req.header.type = IOP_REQUEST_TYPE_IOCTL_COMMAND; req.header.flags = IOP_REQUEST_FLAG_SYNC_REQUEST; req.header.result = IOP_RESULT_PENDING; req.header.context = req32 + (u_int64_t)(unsigned long)hba->u.itl.mu; req.ioctl_code = HPT_CTL_CODE_BSD_TO_IOP(pParams->dwIoControlCode); req.inbuf_size = pParams->nInBufferSize; req.outbuf_size = pParams->nOutBufferSize; req.bytes_returned = 0; bus_space_write_region_4(hba->bar0t, hba->bar0h, req32, (u_int32_t *)&req, offsetof(struct hpt_iop_request_ioctl_command, buf)>>2); hptiop_lock_adapter(hba); BUS_SPACE_WRT4_ITL(inbound_queue, req32); BUS_SPACE_RD4_ITL(outbound_intstatus); bus_space_read_region_4(hba->bar0t, hba->bar0h, req32 + offsetof(struct hpt_iop_request_ioctl_command, header.context), (u_int32_t *)&temp64, 2); while (temp64) { if (hptiop_sleep(hba, (void *)((unsigned long)hba->u.itl.mu + req32), PPAUSE, "hptctl", HPT_OSM_TIMEOUT)==0) break; hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_RESET, 60000); bus_space_read_region_4(hba->bar0t, hba->bar0h,req32 + offsetof(struct hpt_iop_request_ioctl_command, header.context), (u_int32_t *)&temp64, 2); } hptiop_unlock_adapter(hba); return 0; } static int hptiop_bus_space_copyin(struct hpt_iop_hba *hba, u_int32_t bus, void *user, int size) { unsigned char byte; int i; for (i=0; ibar0t, hba->bar0h, bus + i, byte); } return 0; } static int hptiop_bus_space_copyout(struct hpt_iop_hba *hba, u_int32_t bus, void *user, int size) { unsigned char byte; int i; for (i=0; ibar0t, hba->bar0h, bus + i); if (copyout(&byte, (u_int8_t *)user + i, 1)) return -1; } return 0; } static int hptiop_do_ioctl_itl(struct hpt_iop_hba *hba, struct hpt_iop_ioctl_param * pParams) { u_int32_t req32; u_int32_t result; if ((pParams->Magic != HPT_IOCTL_MAGIC) && (pParams->Magic != HPT_IOCTL_MAGIC32)) return EFAULT; req32 = BUS_SPACE_RD4_ITL(inbound_queue); if (req32 == IOPMU_QUEUE_EMPTY) return EFAULT; if (pParams->nInBufferSize) if (hptiop_bus_space_copyin(hba, req32 + offsetof(struct hpt_iop_request_ioctl_command, buf), (void *)pParams->lpInBuffer, pParams->nInBufferSize)) goto invalid; if (hptiop_post_ioctl_command_itl(hba, req32, pParams)) goto invalid; result = bus_space_read_4(hba->bar0t, hba->bar0h, req32 + offsetof(struct hpt_iop_request_ioctl_command, header.result)); if (result == IOP_RESULT_SUCCESS) { if (pParams->nOutBufferSize) if (hptiop_bus_space_copyout(hba, req32 + offsetof(struct hpt_iop_request_ioctl_command, buf) + ((pParams->nInBufferSize + 3) & ~3), (void *)pParams->lpOutBuffer, pParams->nOutBufferSize)) goto invalid; if (pParams->lpBytesReturned) { if (hptiop_bus_space_copyout(hba, req32 + offsetof(struct hpt_iop_request_ioctl_command, bytes_returned), (void *)pParams->lpBytesReturned, sizeof(unsigned long))) goto invalid; } BUS_SPACE_WRT4_ITL(outbound_queue, req32); return 0; } else{ invalid: BUS_SPACE_WRT4_ITL(outbound_queue, req32); return EFAULT; } } static int hptiop_post_ioctl_command_mv(struct hpt_iop_hba *hba, struct hpt_iop_request_ioctl_command *req, struct hpt_iop_ioctl_param *pParams) { u_int64_t req_phy; int size = 0; if ((((pParams->nInBufferSize + 3) & ~3) + pParams->nOutBufferSize) > (hba->max_request_size - offsetof(struct hpt_iop_request_ioctl_command, buf))) { device_printf(hba->pcidev, "request size beyond max value"); return -1; } req->ioctl_code = HPT_CTL_CODE_BSD_TO_IOP(pParams->dwIoControlCode); req->inbuf_size = pParams->nInBufferSize; req->outbuf_size = pParams->nOutBufferSize; req->header.size = offsetof(struct hpt_iop_request_ioctl_command, buf) + pParams->nInBufferSize; req->header.context = (u_int64_t)MVIOP_CMD_TYPE_IOCTL; req->header.type = IOP_REQUEST_TYPE_IOCTL_COMMAND; req->header.result = IOP_RESULT_PENDING; req->header.flags = IOP_REQUEST_FLAG_OUTPUT_CONTEXT; size = req->header.size >> 8; size = size > 3 ? 3 : size; req_phy = hba->ctlcfgcmd_phy | MVIOP_MU_QUEUE_ADDR_HOST_BIT | size; hptiop_mv_inbound_write(req_phy, hba); BUS_SPACE_RD4_MV0(outbound_intmask); while (hba->config_done == 0) { if (hptiop_sleep(hba, req, PPAUSE, "hptctl", HPT_OSM_TIMEOUT)==0) continue; hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_RESET, 60000); } return 0; } static int hptiop_do_ioctl_mv(struct hpt_iop_hba *hba, struct hpt_iop_ioctl_param *pParams) { struct hpt_iop_request_ioctl_command *req; if ((pParams->Magic != HPT_IOCTL_MAGIC) && (pParams->Magic != HPT_IOCTL_MAGIC32)) return EFAULT; req = (struct hpt_iop_request_ioctl_command *)(hba->ctlcfg_ptr); hba->config_done = 0; hptiop_lock_adapter(hba); if (pParams->nInBufferSize) if (copyin((void *)pParams->lpInBuffer, req->buf, pParams->nInBufferSize)) goto invalid; if (hptiop_post_ioctl_command_mv(hba, req, pParams)) goto invalid; if (hba->config_done == 1) { if (pParams->nOutBufferSize) if (copyout(req->buf + ((pParams->nInBufferSize + 3) & ~3), (void *)pParams->lpOutBuffer, pParams->nOutBufferSize)) goto invalid; if (pParams->lpBytesReturned) if (copyout(&req->bytes_returned, (void*)pParams->lpBytesReturned, sizeof(u_int32_t))) goto invalid; hptiop_unlock_adapter(hba); return 0; } else{ invalid: hptiop_unlock_adapter(hba); return EFAULT; } } static int hptiop_post_ioctl_command_mvfrey(struct hpt_iop_hba *hba, struct hpt_iop_request_ioctl_command *req, struct hpt_iop_ioctl_param *pParams) { u_int64_t phy_addr; u_int32_t index; phy_addr = hba->ctlcfgcmd_phy; if ((((pParams->nInBufferSize + 3) & ~3) + pParams->nOutBufferSize) > (hba->max_request_size - offsetof(struct hpt_iop_request_ioctl_command, buf))) { device_printf(hba->pcidev, "request size beyond max value"); return -1; } req->ioctl_code = HPT_CTL_CODE_BSD_TO_IOP(pParams->dwIoControlCode); req->inbuf_size = pParams->nInBufferSize; req->outbuf_size = pParams->nOutBufferSize; req->header.size = offsetof(struct hpt_iop_request_ioctl_command, buf) + pParams->nInBufferSize; req->header.type = IOP_REQUEST_TYPE_IOCTL_COMMAND; req->header.result = IOP_RESULT_PENDING; req->header.flags = IOP_REQUEST_FLAG_SYNC_REQUEST | IOP_REQUEST_FLAG_OUTPUT_CONTEXT | IOP_REQUEST_FLAG_ADDR_BITS | ((phy_addr >> 16) & 0xffff0000); req->header.context = ((phy_addr & 0xffffffff) << 32 ) | IOPMU_QUEUE_ADDR_HOST_BIT | req->header.type; hba->u.mvfrey.inlist_wptr++; index = hba->u.mvfrey.inlist_wptr & 0x3fff; if (index == hba->u.mvfrey.list_count) { index = 0; hba->u.mvfrey.inlist_wptr &= ~0x3fff; hba->u.mvfrey.inlist_wptr ^= CL_POINTER_TOGGLE; } hba->u.mvfrey.inlist[index].addr = phy_addr; hba->u.mvfrey.inlist[index].intrfc_len = (req->header.size + 3) / 4; BUS_SPACE_WRT4_MVFREY2(inbound_write_ptr, hba->u.mvfrey.inlist_wptr); BUS_SPACE_RD4_MVFREY2(inbound_write_ptr); while (hba->config_done == 0) { if (hptiop_sleep(hba, req, PPAUSE, "hptctl", HPT_OSM_TIMEOUT)==0) continue; hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_RESET, 60000); } return 0; } static int hptiop_do_ioctl_mvfrey(struct hpt_iop_hba *hba, struct hpt_iop_ioctl_param *pParams) { struct hpt_iop_request_ioctl_command *req; if ((pParams->Magic != HPT_IOCTL_MAGIC) && (pParams->Magic != HPT_IOCTL_MAGIC32)) return EFAULT; req = (struct hpt_iop_request_ioctl_command *)(hba->ctlcfg_ptr); hba->config_done = 0; hptiop_lock_adapter(hba); if (pParams->nInBufferSize) if (copyin((void *)pParams->lpInBuffer, req->buf, pParams->nInBufferSize)) goto invalid; if (hptiop_post_ioctl_command_mvfrey(hba, req, pParams)) goto invalid; if (hba->config_done == 1) { if (pParams->nOutBufferSize) if (copyout(req->buf + ((pParams->nInBufferSize + 3) & ~3), (void *)pParams->lpOutBuffer, pParams->nOutBufferSize)) goto invalid; if (pParams->lpBytesReturned) if (copyout(&req->bytes_returned, (void*)pParams->lpBytesReturned, sizeof(u_int32_t))) goto invalid; hptiop_unlock_adapter(hba); return 0; } else{ invalid: hptiop_unlock_adapter(hba); return EFAULT; } } static int hptiop_rescan_bus(struct hpt_iop_hba * hba) { union ccb *ccb; if ((ccb = xpt_alloc_ccb()) == NULL) return(ENOMEM); if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(hba->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return(EIO); } xpt_rescan(ccb); return(0); } static bus_dmamap_callback_t hptiop_map_srb; static bus_dmamap_callback_t hptiop_post_scsi_command; static bus_dmamap_callback_t hptiop_mv_map_ctlcfg; static bus_dmamap_callback_t hptiop_mvfrey_map_ctlcfg; static int hptiop_alloc_pci_res_itl(struct hpt_iop_hba *hba) { hba->bar0_rid = 0x10; hba->bar0_res = bus_alloc_resource_any(hba->pcidev, SYS_RES_MEMORY, &hba->bar0_rid, RF_ACTIVE); if (hba->bar0_res == NULL) { device_printf(hba->pcidev, "failed to get iop base adrress.\n"); return -1; } hba->bar0t = rman_get_bustag(hba->bar0_res); hba->bar0h = rman_get_bushandle(hba->bar0_res); hba->u.itl.mu = (struct hpt_iopmu_itl *) rman_get_virtual(hba->bar0_res); if (!hba->u.itl.mu) { bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); device_printf(hba->pcidev, "alloc mem res failed\n"); return -1; } return 0; } static int hptiop_alloc_pci_res_mv(struct hpt_iop_hba *hba) { hba->bar0_rid = 0x10; hba->bar0_res = bus_alloc_resource_any(hba->pcidev, SYS_RES_MEMORY, &hba->bar0_rid, RF_ACTIVE); if (hba->bar0_res == NULL) { device_printf(hba->pcidev, "failed to get iop bar0.\n"); return -1; } hba->bar0t = rman_get_bustag(hba->bar0_res); hba->bar0h = rman_get_bushandle(hba->bar0_res); hba->u.mv.regs = (struct hpt_iopmv_regs *) rman_get_virtual(hba->bar0_res); if (!hba->u.mv.regs) { bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); device_printf(hba->pcidev, "alloc bar0 mem res failed\n"); return -1; } hba->bar2_rid = 0x18; hba->bar2_res = bus_alloc_resource_any(hba->pcidev, SYS_RES_MEMORY, &hba->bar2_rid, RF_ACTIVE); if (hba->bar2_res == NULL) { bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); device_printf(hba->pcidev, "failed to get iop bar2.\n"); return -1; } hba->bar2t = rman_get_bustag(hba->bar2_res); hba->bar2h = rman_get_bushandle(hba->bar2_res); hba->u.mv.mu = (struct hpt_iopmu_mv *)rman_get_virtual(hba->bar2_res); if (!hba->u.mv.mu) { bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar2_rid, hba->bar2_res); device_printf(hba->pcidev, "alloc mem bar2 res failed\n"); return -1; } return 0; } static int hptiop_alloc_pci_res_mvfrey(struct hpt_iop_hba *hba) { hba->bar0_rid = 0x10; hba->bar0_res = bus_alloc_resource_any(hba->pcidev, SYS_RES_MEMORY, &hba->bar0_rid, RF_ACTIVE); if (hba->bar0_res == NULL) { device_printf(hba->pcidev, "failed to get iop bar0.\n"); return -1; } hba->bar0t = rman_get_bustag(hba->bar0_res); hba->bar0h = rman_get_bushandle(hba->bar0_res); hba->u.mvfrey.config = (struct hpt_iop_request_get_config *) rman_get_virtual(hba->bar0_res); if (!hba->u.mvfrey.config) { bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); device_printf(hba->pcidev, "alloc bar0 mem res failed\n"); return -1; } hba->bar2_rid = 0x18; hba->bar2_res = bus_alloc_resource_any(hba->pcidev, SYS_RES_MEMORY, &hba->bar2_rid, RF_ACTIVE); if (hba->bar2_res == NULL) { bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); device_printf(hba->pcidev, "failed to get iop bar2.\n"); return -1; } hba->bar2t = rman_get_bustag(hba->bar2_res); hba->bar2h = rman_get_bushandle(hba->bar2_res); hba->u.mvfrey.mu = (struct hpt_iopmu_mvfrey *)rman_get_virtual(hba->bar2_res); if (!hba->u.mvfrey.mu) { bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar2_rid, hba->bar2_res); device_printf(hba->pcidev, "alloc mem bar2 res failed\n"); return -1; } return 0; } static void hptiop_release_pci_res_itl(struct hpt_iop_hba *hba) { if (hba->bar0_res) bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); } static void hptiop_release_pci_res_mv(struct hpt_iop_hba *hba) { if (hba->bar0_res) bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); if (hba->bar2_res) bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar2_rid, hba->bar2_res); } static void hptiop_release_pci_res_mvfrey(struct hpt_iop_hba *hba) { if (hba->bar0_res) bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); if (hba->bar2_res) bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar2_rid, hba->bar2_res); } static int hptiop_internal_memalloc_mv(struct hpt_iop_hba *hba) { if (bus_dma_tag_create(hba->parent_dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 0x800 - 0x8, 1, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW, NULL, NULL, &hba->ctlcfg_dmat)) { device_printf(hba->pcidev, "alloc ctlcfg_dmat failed\n"); return -1; } if (bus_dmamem_alloc(hba->ctlcfg_dmat, (void **)&hba->ctlcfg_ptr, BUS_DMA_WAITOK | BUS_DMA_COHERENT, &hba->ctlcfg_dmamap) != 0) { device_printf(hba->pcidev, "bus_dmamem_alloc failed!\n"); bus_dma_tag_destroy(hba->ctlcfg_dmat); return -1; } if (bus_dmamap_load(hba->ctlcfg_dmat, hba->ctlcfg_dmamap, hba->ctlcfg_ptr, MVIOP_IOCTLCFG_SIZE, hptiop_mv_map_ctlcfg, hba, 0)) { device_printf(hba->pcidev, "bus_dmamap_load failed!\n"); if (hba->ctlcfg_dmat) { bus_dmamem_free(hba->ctlcfg_dmat, hba->ctlcfg_ptr, hba->ctlcfg_dmamap); bus_dma_tag_destroy(hba->ctlcfg_dmat); } return -1; } return 0; } static int hptiop_internal_memalloc_mvfrey(struct hpt_iop_hba *hba) { u_int32_t list_count = BUS_SPACE_RD4_MVFREY2(inbound_conf_ctl); list_count >>= 16; if (list_count == 0) { return -1; } hba->u.mvfrey.list_count = list_count; hba->u.mvfrey.internal_mem_size = 0x800 + list_count * sizeof(struct mvfrey_inlist_entry) + list_count * sizeof(struct mvfrey_outlist_entry) + sizeof(int); if (bus_dma_tag_create(hba->parent_dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, hba->u.mvfrey.internal_mem_size, 1, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW, NULL, NULL, &hba->ctlcfg_dmat)) { device_printf(hba->pcidev, "alloc ctlcfg_dmat failed\n"); return -1; } if (bus_dmamem_alloc(hba->ctlcfg_dmat, (void **)&hba->ctlcfg_ptr, BUS_DMA_WAITOK | BUS_DMA_COHERENT, &hba->ctlcfg_dmamap) != 0) { device_printf(hba->pcidev, "bus_dmamem_alloc failed!\n"); bus_dma_tag_destroy(hba->ctlcfg_dmat); return -1; } if (bus_dmamap_load(hba->ctlcfg_dmat, hba->ctlcfg_dmamap, hba->ctlcfg_ptr, hba->u.mvfrey.internal_mem_size, hptiop_mvfrey_map_ctlcfg, hba, 0)) { device_printf(hba->pcidev, "bus_dmamap_load failed!\n"); if (hba->ctlcfg_dmat) { bus_dmamem_free(hba->ctlcfg_dmat, hba->ctlcfg_ptr, hba->ctlcfg_dmamap); bus_dma_tag_destroy(hba->ctlcfg_dmat); } return -1; } return 0; } static int hptiop_internal_memfree_itl(struct hpt_iop_hba *hba) { return 0; } static int hptiop_internal_memfree_mv(struct hpt_iop_hba *hba) { if (hba->ctlcfg_dmat) { bus_dmamap_unload(hba->ctlcfg_dmat, hba->ctlcfg_dmamap); bus_dmamem_free(hba->ctlcfg_dmat, hba->ctlcfg_ptr, hba->ctlcfg_dmamap); bus_dma_tag_destroy(hba->ctlcfg_dmat); } return 0; } static int hptiop_internal_memfree_mvfrey(struct hpt_iop_hba *hba) { if (hba->ctlcfg_dmat) { bus_dmamap_unload(hba->ctlcfg_dmat, hba->ctlcfg_dmamap); bus_dmamem_free(hba->ctlcfg_dmat, hba->ctlcfg_ptr, hba->ctlcfg_dmamap); bus_dma_tag_destroy(hba->ctlcfg_dmat); } return 0; } static int hptiop_reset_comm_mvfrey(struct hpt_iop_hba *hba) { u_int32_t i = 100; if (hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_RESET_COMM, 3000)) return -1; /* wait 100ms for MCU ready */ while(i--) { DELAY(1000); } BUS_SPACE_WRT4_MVFREY2(inbound_base, hba->u.mvfrey.inlist_phy & 0xffffffff); BUS_SPACE_WRT4_MVFREY2(inbound_base_high, (hba->u.mvfrey.inlist_phy >> 16) >> 16); BUS_SPACE_WRT4_MVFREY2(outbound_base, hba->u.mvfrey.outlist_phy & 0xffffffff); BUS_SPACE_WRT4_MVFREY2(outbound_base_high, (hba->u.mvfrey.outlist_phy >> 16) >> 16); BUS_SPACE_WRT4_MVFREY2(outbound_shadow_base, hba->u.mvfrey.outlist_cptr_phy & 0xffffffff); BUS_SPACE_WRT4_MVFREY2(outbound_shadow_base_high, (hba->u.mvfrey.outlist_cptr_phy >> 16) >> 16); hba->u.mvfrey.inlist_wptr = (hba->u.mvfrey.list_count - 1) | CL_POINTER_TOGGLE; *hba->u.mvfrey.outlist_cptr = (hba->u.mvfrey.list_count - 1) | CL_POINTER_TOGGLE; hba->u.mvfrey.outlist_rptr = hba->u.mvfrey.list_count - 1; return 0; } /* * CAM driver interface */ static device_method_t driver_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hptiop_probe), DEVMETHOD(device_attach, hptiop_attach), DEVMETHOD(device_detach, hptiop_detach), DEVMETHOD(device_shutdown, hptiop_shutdown), { 0, 0 } }; static struct hptiop_adapter_ops hptiop_itl_ops = { .family = INTEL_BASED_IOP, .iop_wait_ready = hptiop_wait_ready_itl, .internal_memalloc = 0, .internal_memfree = hptiop_internal_memfree_itl, .alloc_pci_res = hptiop_alloc_pci_res_itl, .release_pci_res = hptiop_release_pci_res_itl, .enable_intr = hptiop_enable_intr_itl, .disable_intr = hptiop_disable_intr_itl, .get_config = hptiop_get_config_itl, .set_config = hptiop_set_config_itl, .iop_intr = hptiop_intr_itl, .post_msg = hptiop_post_msg_itl, .post_req = hptiop_post_req_itl, .do_ioctl = hptiop_do_ioctl_itl, .reset_comm = 0, }; static struct hptiop_adapter_ops hptiop_mv_ops = { .family = MV_BASED_IOP, .iop_wait_ready = hptiop_wait_ready_mv, .internal_memalloc = hptiop_internal_memalloc_mv, .internal_memfree = hptiop_internal_memfree_mv, .alloc_pci_res = hptiop_alloc_pci_res_mv, .release_pci_res = hptiop_release_pci_res_mv, .enable_intr = hptiop_enable_intr_mv, .disable_intr = hptiop_disable_intr_mv, .get_config = hptiop_get_config_mv, .set_config = hptiop_set_config_mv, .iop_intr = hptiop_intr_mv, .post_msg = hptiop_post_msg_mv, .post_req = hptiop_post_req_mv, .do_ioctl = hptiop_do_ioctl_mv, .reset_comm = 0, }; static struct hptiop_adapter_ops hptiop_mvfrey_ops = { .family = MVFREY_BASED_IOP, .iop_wait_ready = hptiop_wait_ready_mvfrey, .internal_memalloc = hptiop_internal_memalloc_mvfrey, .internal_memfree = hptiop_internal_memfree_mvfrey, .alloc_pci_res = hptiop_alloc_pci_res_mvfrey, .release_pci_res = hptiop_release_pci_res_mvfrey, .enable_intr = hptiop_enable_intr_mvfrey, .disable_intr = hptiop_disable_intr_mvfrey, .get_config = hptiop_get_config_mvfrey, .set_config = hptiop_set_config_mvfrey, .iop_intr = hptiop_intr_mvfrey, .post_msg = hptiop_post_msg_mvfrey, .post_req = hptiop_post_req_mvfrey, .do_ioctl = hptiop_do_ioctl_mvfrey, .reset_comm = hptiop_reset_comm_mvfrey, }; static driver_t hptiop_pci_driver = { driver_name, driver_methods, sizeof(struct hpt_iop_hba) }; DRIVER_MODULE(hptiop, pci, hptiop_pci_driver, hptiop_devclass, 0, 0); MODULE_DEPEND(hptiop, cam, 1, 1, 1); static int hptiop_probe(device_t dev) { struct hpt_iop_hba *hba; u_int32_t id; static char buf[256]; int sas = 0; struct hptiop_adapter_ops *ops; if (pci_get_vendor(dev) != 0x1103) return (ENXIO); id = pci_get_device(dev); switch (id) { case 0x4520: case 0x4521: case 0x4522: sas = 1; case 0x3620: case 0x3622: case 0x3640: ops = &hptiop_mvfrey_ops; break; case 0x4210: case 0x4211: case 0x4310: case 0x4311: case 0x4320: case 0x4321: case 0x4322: sas = 1; case 0x3220: case 0x3320: case 0x3410: case 0x3520: case 0x3510: case 0x3511: case 0x3521: case 0x3522: case 0x3530: case 0x3540: case 0x3560: ops = &hptiop_itl_ops; break; case 0x3020: case 0x3120: case 0x3122: ops = &hptiop_mv_ops; break; default: return (ENXIO); } device_printf(dev, "adapter at PCI %d:%d:%d, IRQ %d\n", pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev), pci_get_irq(dev)); sprintf(buf, "RocketRAID %x %s Controller\n", id, sas ? "SAS" : "SATA"); device_set_desc_copy(dev, buf); hba = (struct hpt_iop_hba *)device_get_softc(dev); bzero(hba, sizeof(struct hpt_iop_hba)); hba->ops = ops; KdPrint(("hba->ops=%p\n", hba->ops)); return 0; } static int hptiop_attach(device_t dev) { struct hpt_iop_hba *hba = (struct hpt_iop_hba *)device_get_softc(dev); struct hpt_iop_request_get_config iop_config; struct hpt_iop_request_set_config set_config; int rid = 0; struct cam_devq *devq; struct ccb_setasync ccb; u_int32_t unit = device_get_unit(dev); device_printf(dev, "%d RocketRAID 3xxx/4xxx controller driver %s\n", unit, driver_version); KdPrint(("hptiop: attach(%d, %d/%d/%d) ops=%p\n", unit, pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev), hba->ops)); pci_enable_busmaster(dev); hba->pcidev = dev; hba->pciunit = unit; if (hba->ops->alloc_pci_res(hba)) return ENXIO; if (hba->ops->iop_wait_ready(hba, 2000)) { device_printf(dev, "adapter is not ready\n"); goto release_pci_res; } mtx_init(&hba->lock, "hptioplock", NULL, MTX_DEF); if (bus_dma_tag_create(bus_get_dma_tag(dev),/* PCI parent */ 1, /* alignment */ 0, /* boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ BUS_SPACE_UNRESTRICTED, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, /* lockfunc */ NULL, /* lockfuncarg */ &hba->parent_dmat /* tag */)) { device_printf(dev, "alloc parent_dmat failed\n"); goto release_pci_res; } if (hba->ops->family == MV_BASED_IOP) { if (hba->ops->internal_memalloc(hba)) { device_printf(dev, "alloc srb_dmat failed\n"); goto destroy_parent_tag; } } if (hba->ops->get_config(hba, &iop_config)) { device_printf(dev, "get iop config failed.\n"); goto get_config_failed; } hba->firmware_version = iop_config.firmware_version; hba->interface_version = iop_config.interface_version; hba->max_requests = iop_config.max_requests; hba->max_devices = iop_config.max_devices; hba->max_request_size = iop_config.request_size; hba->max_sg_count = iop_config.max_sg_count; if (hba->ops->family == MVFREY_BASED_IOP) { if (hba->ops->internal_memalloc(hba)) { device_printf(dev, "alloc srb_dmat failed\n"); goto destroy_parent_tag; } if (hba->ops->reset_comm(hba)) { device_printf(dev, "reset comm failed\n"); goto get_config_failed; } } if (bus_dma_tag_create(hba->parent_dmat,/* parent */ 4, /* alignment */ BUS_SPACE_MAXADDR_32BIT+1, /* boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ PAGE_SIZE * (hba->max_sg_count-1), /* maxsize */ hba->max_sg_count, /* nsegments */ 0x20000, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ busdma_lock_mutex, /* lockfunc */ &hba->lock, /* lockfuncarg */ &hba->io_dmat /* tag */)) { device_printf(dev, "alloc io_dmat failed\n"); goto get_config_failed; } if (bus_dma_tag_create(hba->parent_dmat,/* parent */ 1, /* alignment */ 0, /* boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ HPT_SRB_MAX_SIZE * HPT_SRB_MAX_QUEUE_SIZE + 0x20, 1, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, /* lockfunc */ NULL, /* lockfuncarg */ &hba->srb_dmat /* tag */)) { device_printf(dev, "alloc srb_dmat failed\n"); goto destroy_io_dmat; } if (bus_dmamem_alloc(hba->srb_dmat, (void **)&hba->uncached_ptr, BUS_DMA_WAITOK | BUS_DMA_COHERENT, &hba->srb_dmamap) != 0) { device_printf(dev, "srb bus_dmamem_alloc failed!\n"); goto destroy_srb_dmat; } if (bus_dmamap_load(hba->srb_dmat, hba->srb_dmamap, hba->uncached_ptr, (HPT_SRB_MAX_SIZE * HPT_SRB_MAX_QUEUE_SIZE) + 0x20, hptiop_map_srb, hba, 0)) { device_printf(dev, "bus_dmamap_load failed!\n"); goto srb_dmamem_free; } if ((devq = cam_simq_alloc(hba->max_requests - 1 )) == NULL) { device_printf(dev, "cam_simq_alloc failed\n"); goto srb_dmamap_unload; } hba->sim = cam_sim_alloc(hptiop_action, hptiop_poll, driver_name, hba, unit, &Giant, hba->max_requests - 1, 1, devq); if (!hba->sim) { device_printf(dev, "cam_sim_alloc failed\n"); cam_simq_free(devq); goto srb_dmamap_unload; } if (xpt_bus_register(hba->sim, dev, 0) != CAM_SUCCESS) { device_printf(dev, "xpt_bus_register failed\n"); goto free_cam_sim; } if (xpt_create_path(&hba->path, /*periph */ NULL, cam_sim_path(hba->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { device_printf(dev, "xpt_create_path failed\n"); goto deregister_xpt_bus; } bzero(&set_config, sizeof(set_config)); set_config.iop_id = unit; set_config.vbus_id = cam_sim_path(hba->sim); set_config.max_host_request_size = HPT_SRB_MAX_REQ_SIZE; if (hba->ops->set_config(hba, &set_config)) { device_printf(dev, "set iop config failed.\n"); goto free_hba_path; } xpt_setup_ccb(&ccb.ccb_h, hba->path, /*priority*/5); ccb.ccb_h.func_code = XPT_SASYNC_CB; ccb.event_enable = (AC_FOUND_DEVICE | AC_LOST_DEVICE); ccb.callback = hptiop_async; ccb.callback_arg = hba->sim; xpt_action((union ccb *)&ccb); rid = 0; if ((hba->irq_res = bus_alloc_resource(hba->pcidev, SYS_RES_IRQ, &rid, 0, ~0ul, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) { device_printf(dev, "allocate irq failed!\n"); goto free_hba_path; } if (bus_setup_intr(hba->pcidev, hba->irq_res, INTR_TYPE_CAM, NULL, hptiop_pci_intr, hba, &hba->irq_handle)) { device_printf(dev, "allocate intr function failed!\n"); goto free_irq_resource; } if (hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK, 5000)) { device_printf(dev, "fail to start background task\n"); goto teartown_irq_resource; } hba->ops->enable_intr(hba); hba->initialized = 1; hba->ioctl_dev = make_dev(&hptiop_cdevsw, unit, UID_ROOT, GID_WHEEL /*GID_OPERATOR*/, S_IRUSR | S_IWUSR, "%s%d", driver_name, unit); return 0; teartown_irq_resource: bus_teardown_intr(dev, hba->irq_res, hba->irq_handle); free_irq_resource: bus_release_resource(dev, SYS_RES_IRQ, 0, hba->irq_res); free_hba_path: xpt_free_path(hba->path); deregister_xpt_bus: xpt_bus_deregister(cam_sim_path(hba->sim)); free_cam_sim: cam_sim_free(hba->sim, /*free devq*/ TRUE); srb_dmamap_unload: if (hba->uncached_ptr) bus_dmamap_unload(hba->srb_dmat, hba->srb_dmamap); srb_dmamem_free: if (hba->uncached_ptr) bus_dmamem_free(hba->srb_dmat, hba->uncached_ptr, hba->srb_dmamap); destroy_srb_dmat: if (hba->srb_dmat) bus_dma_tag_destroy(hba->srb_dmat); destroy_io_dmat: if (hba->io_dmat) bus_dma_tag_destroy(hba->io_dmat); get_config_failed: hba->ops->internal_memfree(hba); destroy_parent_tag: if (hba->parent_dmat) bus_dma_tag_destroy(hba->parent_dmat); release_pci_res: if (hba->ops->release_pci_res) hba->ops->release_pci_res(hba); return ENXIO; } static int hptiop_detach(device_t dev) { struct hpt_iop_hba * hba = (struct hpt_iop_hba *)device_get_softc(dev); int i; int error = EBUSY; hptiop_lock_adapter(hba); for (i = 0; i < hba->max_devices; i++) if (hptiop_os_query_remove_device(hba, i)) { device_printf(dev, "%d file system is busy. id=%d", hba->pciunit, i); goto out; } if ((error = hptiop_shutdown(dev)) != 0) goto out; if (hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_STOP_BACKGROUND_TASK, 60000)) goto out; hptiop_release_resource(hba); error = 0; out: hptiop_unlock_adapter(hba); return error; } static int hptiop_shutdown(device_t dev) { struct hpt_iop_hba * hba = (struct hpt_iop_hba *)device_get_softc(dev); int error = 0; if (hba->flag & HPT_IOCTL_FLAG_OPEN) { device_printf(dev, "%d device is busy", hba->pciunit); return EBUSY; } hba->ops->disable_intr(hba); if (hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_SHUTDOWN, 60000)) error = EBUSY; return error; } static void hptiop_pci_intr(void *arg) { struct hpt_iop_hba * hba = (struct hpt_iop_hba *)arg; hptiop_lock_adapter(hba); hba->ops->iop_intr(hba); hptiop_unlock_adapter(hba); } static void hptiop_poll(struct cam_sim *sim) { hptiop_pci_intr(cam_sim_softc(sim)); } static void hptiop_async(void * callback_arg, u_int32_t code, struct cam_path * path, void * arg) { } static void hptiop_enable_intr_itl(struct hpt_iop_hba *hba) { BUS_SPACE_WRT4_ITL(outbound_intmask, ~(IOPMU_OUTBOUND_INT_POSTQUEUE | IOPMU_OUTBOUND_INT_MSG0)); } static void hptiop_enable_intr_mv(struct hpt_iop_hba *hba) { u_int32_t int_mask; int_mask = BUS_SPACE_RD4_MV0(outbound_intmask); int_mask |= MVIOP_MU_OUTBOUND_INT_POSTQUEUE | MVIOP_MU_OUTBOUND_INT_MSG; BUS_SPACE_WRT4_MV0(outbound_intmask,int_mask); } static void hptiop_enable_intr_mvfrey(struct hpt_iop_hba *hba) { BUS_SPACE_WRT4_MVFREY2(f0_doorbell_enable, CPU_TO_F0_DRBL_MSG_A_BIT); BUS_SPACE_RD4_MVFREY2(f0_doorbell_enable); BUS_SPACE_WRT4_MVFREY2(isr_enable, 0x1); BUS_SPACE_RD4_MVFREY2(isr_enable); BUS_SPACE_WRT4_MVFREY2(pcie_f0_int_enable, 0x1010); BUS_SPACE_RD4_MVFREY2(pcie_f0_int_enable); } static void hptiop_disable_intr_itl(struct hpt_iop_hba *hba) { u_int32_t int_mask; int_mask = BUS_SPACE_RD4_ITL(outbound_intmask); int_mask |= IOPMU_OUTBOUND_INT_POSTQUEUE | IOPMU_OUTBOUND_INT_MSG0; BUS_SPACE_WRT4_ITL(outbound_intmask, int_mask); BUS_SPACE_RD4_ITL(outbound_intstatus); } static void hptiop_disable_intr_mv(struct hpt_iop_hba *hba) { u_int32_t int_mask; int_mask = BUS_SPACE_RD4_MV0(outbound_intmask); int_mask &= ~(MVIOP_MU_OUTBOUND_INT_MSG | MVIOP_MU_OUTBOUND_INT_POSTQUEUE); BUS_SPACE_WRT4_MV0(outbound_intmask,int_mask); BUS_SPACE_RD4_MV0(outbound_intmask); } static void hptiop_disable_intr_mvfrey(struct hpt_iop_hba *hba) { BUS_SPACE_WRT4_MVFREY2(f0_doorbell_enable, 0); BUS_SPACE_RD4_MVFREY2(f0_doorbell_enable); BUS_SPACE_WRT4_MVFREY2(isr_enable, 0); BUS_SPACE_RD4_MVFREY2(isr_enable); BUS_SPACE_WRT4_MVFREY2(pcie_f0_int_enable, 0); BUS_SPACE_RD4_MVFREY2(pcie_f0_int_enable); } static void hptiop_reset_adapter(void *argv) { struct hpt_iop_hba * hba = (struct hpt_iop_hba *)argv; if (hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_RESET, 60000)) return; hptiop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK, 5000); } static void *hptiop_get_srb(struct hpt_iop_hba * hba) { struct hpt_iop_srb * srb; if (hba->srb_list) { srb = hba->srb_list; hba->srb_list = srb->next; return srb; } return NULL; } static void hptiop_free_srb(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb) { srb->next = hba->srb_list; hba->srb_list = srb; } static void hptiop_action(struct cam_sim *sim, union ccb *ccb) { struct hpt_iop_hba * hba = (struct hpt_iop_hba *)cam_sim_softc(sim); struct hpt_iop_srb * srb; int error; switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: hptiop_lock_adapter(hba); if (ccb->ccb_h.target_lun != 0 || ccb->ccb_h.target_id >= hba->max_devices || (ccb->ccb_h.flags & CAM_CDB_PHYS)) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); goto scsi_done; } if ((srb = hptiop_get_srb(hba)) == NULL) { device_printf(hba->pcidev, "srb allocated failed"); ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(ccb); goto scsi_done; } srb->ccb = ccb; error = bus_dmamap_load_ccb(hba->io_dmat, srb->dma_map, ccb, hptiop_post_scsi_command, srb, 0); if (error && error != EINPROGRESS) { device_printf(hba->pcidev, "%d bus_dmamap_load error %d", hba->pciunit, error); xpt_freeze_simq(hba->sim, 1); ccb->ccb_h.status = CAM_REQ_CMP_ERR; hptiop_free_srb(hba, srb); xpt_done(ccb); goto scsi_done; } scsi_done: hptiop_unlock_adapter(hba); return; case XPT_RESET_BUS: device_printf(hba->pcidev, "reset adapter"); hptiop_lock_adapter(hba); hba->msg_done = 0; hptiop_reset_adapter(hba); hptiop_unlock_adapter(hba); break; case XPT_GET_TRAN_SETTINGS: case XPT_SET_TRAN_SETTINGS: ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; case XPT_CALC_GEOMETRY: cam_calc_geometry(&ccb->ccg, 1); break; case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_SDTR_ABLE; cpi->target_sprt = 0; cpi->hba_misc = PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = hba->max_devices; cpi->max_lun = 0; cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = cam_sim_bus(sim); cpi->initiator_id = hba->max_devices; cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "HPT ", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->ccb_h.status = CAM_REQ_CMP; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); return; } static void hptiop_post_req_itl(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb, bus_dma_segment_t *segs, int nsegs) { int idx; union ccb *ccb = srb->ccb; u_int8_t *cdb; if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; KdPrint(("ccb=%p %x-%x-%x\n", ccb, *(u_int32_t *)cdb, *((u_int32_t *)cdb+1), *((u_int32_t *)cdb+2))); if (srb->srb_flag & HPT_SRB_FLAG_HIGH_MEM_ACESS) { u_int32_t iop_req32; struct hpt_iop_request_scsi_command req; iop_req32 = BUS_SPACE_RD4_ITL(inbound_queue); if (iop_req32 == IOPMU_QUEUE_EMPTY) { device_printf(hba->pcidev, "invaild req offset\n"); ccb->ccb_h.status = CAM_BUSY; bus_dmamap_unload(hba->io_dmat, srb->dma_map); hptiop_free_srb(hba, srb); xpt_done(ccb); return; } if (ccb->csio.dxfer_len && nsegs > 0) { struct hpt_iopsg *psg = req.sg_list; for (idx = 0; idx < nsegs; idx++, psg++) { psg->pci_address = (u_int64_t)segs[idx].ds_addr; psg->size = segs[idx].ds_len; psg->eot = 0; } psg[-1].eot = 1; } bcopy(cdb, req.cdb, ccb->csio.cdb_len); req.header.size = offsetof(struct hpt_iop_request_scsi_command, sg_list) + nsegs*sizeof(struct hpt_iopsg); req.header.type = IOP_REQUEST_TYPE_SCSI_COMMAND; req.header.flags = 0; req.header.result = IOP_RESULT_PENDING; req.header.context = (u_int64_t)(unsigned long)srb; req.dataxfer_length = ccb->csio.dxfer_len; req.channel = 0; req.target = ccb->ccb_h.target_id; req.lun = ccb->ccb_h.target_lun; bus_space_write_region_1(hba->bar0t, hba->bar0h, iop_req32, (u_int8_t *)&req, req.header.size); if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREREAD); } else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREWRITE); BUS_SPACE_WRT4_ITL(inbound_queue,iop_req32); } else { struct hpt_iop_request_scsi_command *req; req = (struct hpt_iop_request_scsi_command *)srb; if (ccb->csio.dxfer_len && nsegs > 0) { struct hpt_iopsg *psg = req->sg_list; for (idx = 0; idx < nsegs; idx++, psg++) { psg->pci_address = (u_int64_t)segs[idx].ds_addr; psg->size = segs[idx].ds_len; psg->eot = 0; } psg[-1].eot = 1; } bcopy(cdb, req->cdb, ccb->csio.cdb_len); req->header.type = IOP_REQUEST_TYPE_SCSI_COMMAND; req->header.result = IOP_RESULT_PENDING; req->dataxfer_length = ccb->csio.dxfer_len; req->channel = 0; req->target = ccb->ccb_h.target_id; req->lun = ccb->ccb_h.target_lun; req->header.size = offsetof(struct hpt_iop_request_scsi_command, sg_list) + nsegs*sizeof(struct hpt_iopsg); req->header.context = (u_int64_t)srb->index | IOPMU_QUEUE_ADDR_HOST_BIT; req->header.flags = IOP_REQUEST_FLAG_OUTPUT_CONTEXT; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREREAD); }else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) { bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREWRITE); } if (hba->firmware_version > 0x01020000 || hba->interface_version > 0x01020000) { u_int32_t size_bits; if (req->header.size < 256) size_bits = IOPMU_QUEUE_REQUEST_SIZE_BIT; else if (req->header.size < 512) size_bits = IOPMU_QUEUE_ADDR_HOST_BIT; else size_bits = IOPMU_QUEUE_REQUEST_SIZE_BIT | IOPMU_QUEUE_ADDR_HOST_BIT; BUS_SPACE_WRT4_ITL(inbound_queue, (u_int32_t)srb->phy_addr | size_bits); } else BUS_SPACE_WRT4_ITL(inbound_queue, (u_int32_t)srb->phy_addr |IOPMU_QUEUE_ADDR_HOST_BIT); } } static void hptiop_post_req_mv(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb, bus_dma_segment_t *segs, int nsegs) { int idx, size; union ccb *ccb = srb->ccb; u_int8_t *cdb; struct hpt_iop_request_scsi_command *req; u_int64_t req_phy; req = (struct hpt_iop_request_scsi_command *)srb; req_phy = srb->phy_addr; if (ccb->csio.dxfer_len && nsegs > 0) { struct hpt_iopsg *psg = req->sg_list; for (idx = 0; idx < nsegs; idx++, psg++) { psg->pci_address = (u_int64_t)segs[idx].ds_addr; psg->size = segs[idx].ds_len; psg->eot = 0; } psg[-1].eot = 1; } if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; bcopy(cdb, req->cdb, ccb->csio.cdb_len); req->header.type = IOP_REQUEST_TYPE_SCSI_COMMAND; req->header.result = IOP_RESULT_PENDING; req->dataxfer_length = ccb->csio.dxfer_len; req->channel = 0; req->target = ccb->ccb_h.target_id; req->lun = ccb->ccb_h.target_lun; req->header.size = sizeof(struct hpt_iop_request_scsi_command) - sizeof(struct hpt_iopsg) + nsegs * sizeof(struct hpt_iopsg); if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREREAD); } else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREWRITE); req->header.context = (u_int64_t)srb->index << MVIOP_REQUEST_NUMBER_START_BIT | MVIOP_CMD_TYPE_SCSI; req->header.flags = IOP_REQUEST_FLAG_OUTPUT_CONTEXT; size = req->header.size >> 8; hptiop_mv_inbound_write(req_phy | MVIOP_MU_QUEUE_ADDR_HOST_BIT | (size > 3 ? 3 : size), hba); } static void hptiop_post_req_mvfrey(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb, bus_dma_segment_t *segs, int nsegs) { int idx, index; union ccb *ccb = srb->ccb; u_int8_t *cdb; struct hpt_iop_request_scsi_command *req; u_int64_t req_phy; req = (struct hpt_iop_request_scsi_command *)srb; req_phy = srb->phy_addr; if (ccb->csio.dxfer_len && nsegs > 0) { struct hpt_iopsg *psg = req->sg_list; for (idx = 0; idx < nsegs; idx++, psg++) { psg->pci_address = (u_int64_t)segs[idx].ds_addr | 1; psg->size = segs[idx].ds_len; psg->eot = 0; } psg[-1].eot = 1; } if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; bcopy(cdb, req->cdb, ccb->csio.cdb_len); req->header.type = IOP_REQUEST_TYPE_SCSI_COMMAND; req->header.result = IOP_RESULT_PENDING; req->dataxfer_length = ccb->csio.dxfer_len; req->channel = 0; req->target = ccb->ccb_h.target_id; req->lun = ccb->ccb_h.target_lun; req->header.size = sizeof(struct hpt_iop_request_scsi_command) - sizeof(struct hpt_iopsg) + nsegs * sizeof(struct hpt_iopsg); if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREREAD); } else if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) bus_dmamap_sync(hba->io_dmat, srb->dma_map, BUS_DMASYNC_PREWRITE); req->header.flags = IOP_REQUEST_FLAG_OUTPUT_CONTEXT | IOP_REQUEST_FLAG_ADDR_BITS | ((req_phy >> 16) & 0xffff0000); req->header.context = ((req_phy & 0xffffffff) << 32 ) | srb->index << 4 | IOPMU_QUEUE_ADDR_HOST_BIT | req->header.type; hba->u.mvfrey.inlist_wptr++; index = hba->u.mvfrey.inlist_wptr & 0x3fff; if (index == hba->u.mvfrey.list_count) { index = 0; hba->u.mvfrey.inlist_wptr &= ~0x3fff; hba->u.mvfrey.inlist_wptr ^= CL_POINTER_TOGGLE; } hba->u.mvfrey.inlist[index].addr = req_phy; hba->u.mvfrey.inlist[index].intrfc_len = (req->header.size + 3) / 4; BUS_SPACE_WRT4_MVFREY2(inbound_write_ptr, hba->u.mvfrey.inlist_wptr); BUS_SPACE_RD4_MVFREY2(inbound_write_ptr); if (req->header.type == IOP_REQUEST_TYPE_SCSI_COMMAND) { - ccb->ccb_h.timeout_ch = timeout(hptiop_reset_adapter, hba, 20*hz); + srb->timeout_ch = timeout(hptiop_reset_adapter, hba, 20*hz); } } static void hptiop_post_scsi_command(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct hpt_iop_srb *srb = (struct hpt_iop_srb *)arg; union ccb *ccb = srb->ccb; struct hpt_iop_hba *hba = srb->hba; if (error || nsegs > hba->max_sg_count) { KdPrint(("hptiop: func_code=%x tid=%x lun=%x nsegs=%d\n", ccb->ccb_h.func_code, ccb->ccb_h.target_id, ccb->ccb_h.target_lun, nsegs)); ccb->ccb_h.status = CAM_BUSY; bus_dmamap_unload(hba->io_dmat, srb->dma_map); hptiop_free_srb(hba, srb); xpt_done(ccb); return; } hba->ops->post_req(hba, srb, segs, nsegs); } static void hptiop_mv_map_ctlcfg(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct hpt_iop_hba *hba = (struct hpt_iop_hba *)arg; hba->ctlcfgcmd_phy = ((u_int64_t)segs->ds_addr + 0x1F) & ~(u_int64_t)0x1F; hba->ctlcfg_ptr = (u_int8_t *)(((unsigned long)hba->ctlcfg_ptr + 0x1F) & ~0x1F); } static void hptiop_mvfrey_map_ctlcfg(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct hpt_iop_hba *hba = (struct hpt_iop_hba *)arg; char *p; u_int64_t phy; u_int32_t list_count = hba->u.mvfrey.list_count; phy = ((u_int64_t)segs->ds_addr + 0x1F) & ~(u_int64_t)0x1F; p = (u_int8_t *)(((unsigned long)hba->ctlcfg_ptr + 0x1F) & ~0x1F); hba->ctlcfgcmd_phy = phy; hba->ctlcfg_ptr = p; p += 0x800; phy += 0x800; hba->u.mvfrey.inlist = (struct mvfrey_inlist_entry *)p; hba->u.mvfrey.inlist_phy = phy; p += list_count * sizeof(struct mvfrey_inlist_entry); phy += list_count * sizeof(struct mvfrey_inlist_entry); hba->u.mvfrey.outlist = (struct mvfrey_outlist_entry *)p; hba->u.mvfrey.outlist_phy = phy; p += list_count * sizeof(struct mvfrey_outlist_entry); phy += list_count * sizeof(struct mvfrey_outlist_entry); hba->u.mvfrey.outlist_cptr = (u_int32_t *)p; hba->u.mvfrey.outlist_cptr_phy = phy; } static void hptiop_map_srb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct hpt_iop_hba * hba = (struct hpt_iop_hba *)arg; bus_addr_t phy_addr = (segs->ds_addr + 0x1F) & ~(bus_addr_t)0x1F; struct hpt_iop_srb *srb, *tmp_srb; int i; if (error || nsegs == 0) { device_printf(hba->pcidev, "hptiop_map_srb error"); return; } /* map srb */ srb = (struct hpt_iop_srb *) (((unsigned long)hba->uncached_ptr + 0x1F) & ~(unsigned long)0x1F); for (i = 0; i < HPT_SRB_MAX_QUEUE_SIZE; i++) { tmp_srb = (struct hpt_iop_srb *) ((char *)srb + i * HPT_SRB_MAX_SIZE); if (((unsigned long)tmp_srb & 0x1F) == 0) { if (bus_dmamap_create(hba->io_dmat, 0, &tmp_srb->dma_map)) { device_printf(hba->pcidev, "dmamap create failed"); return; } bzero(tmp_srb, sizeof(struct hpt_iop_srb)); tmp_srb->hba = hba; tmp_srb->index = i; if (hba->ctlcfg_ptr == 0) {/*itl iop*/ tmp_srb->phy_addr = (u_int64_t)(u_int32_t) (phy_addr >> 5); if (phy_addr & IOPMU_MAX_MEM_SUPPORT_MASK_32G) tmp_srb->srb_flag = HPT_SRB_FLAG_HIGH_MEM_ACESS; } else { tmp_srb->phy_addr = phy_addr; } + callout_handle_init(&tmp_srb->timeout_ch); hptiop_free_srb(hba, tmp_srb); hba->srb[i] = tmp_srb; phy_addr += HPT_SRB_MAX_SIZE; } else { device_printf(hba->pcidev, "invalid alignment"); return; } } } static void hptiop_os_message_callback(struct hpt_iop_hba * hba, u_int32_t msg) { hba->msg_done = 1; } static int hptiop_os_query_remove_device(struct hpt_iop_hba * hba, int target_id) { struct cam_periph *periph = NULL; struct cam_path *path; int status, retval = 0; status = xpt_create_path(&path, NULL, hba->sim->path_id, target_id, 0); if (status == CAM_REQ_CMP) { if ((periph = cam_periph_find(path, "da")) != NULL) { if (periph->refcount >= 1) { device_printf(hba->pcidev, "%d ," "target_id=0x%x," "refcount=%d", hba->pciunit, target_id, periph->refcount); retval = -1; } } xpt_free_path(path); } return retval; } static void hptiop_release_resource(struct hpt_iop_hba *hba) { int i; if (hba->path) { struct ccb_setasync ccb; xpt_setup_ccb(&ccb.ccb_h, hba->path, /*priority*/5); ccb.ccb_h.func_code = XPT_SASYNC_CB; ccb.event_enable = 0; ccb.callback = hptiop_async; ccb.callback_arg = hba->sim; xpt_action((union ccb *)&ccb); xpt_free_path(hba->path); } if (hba->sim) { xpt_bus_deregister(cam_sim_path(hba->sim)); cam_sim_free(hba->sim, TRUE); } if (hba->ctlcfg_dmat) { bus_dmamap_unload(hba->ctlcfg_dmat, hba->ctlcfg_dmamap); bus_dmamem_free(hba->ctlcfg_dmat, hba->ctlcfg_ptr, hba->ctlcfg_dmamap); bus_dma_tag_destroy(hba->ctlcfg_dmat); } for (i = 0; i < HPT_SRB_MAX_QUEUE_SIZE; i++) { struct hpt_iop_srb *srb = hba->srb[i]; if (srb->dma_map) bus_dmamap_destroy(hba->io_dmat, srb->dma_map); } if (hba->srb_dmat) { bus_dmamap_unload(hba->srb_dmat, hba->srb_dmamap); bus_dmamap_destroy(hba->srb_dmat, hba->srb_dmamap); bus_dma_tag_destroy(hba->srb_dmat); } if (hba->io_dmat) bus_dma_tag_destroy(hba->io_dmat); if (hba->parent_dmat) bus_dma_tag_destroy(hba->parent_dmat); if (hba->irq_handle) bus_teardown_intr(hba->pcidev, hba->irq_res, hba->irq_handle); if (hba->irq_res) bus_release_resource(hba->pcidev, SYS_RES_IRQ, 0, hba->irq_res); if (hba->bar0_res) bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar0_rid, hba->bar0_res); if (hba->bar2_res) bus_release_resource(hba->pcidev, SYS_RES_MEMORY, hba->bar2_rid, hba->bar2_res); if (hba->ioctl_dev) destroy_dev(hba->ioctl_dev); } diff --git a/sys/dev/hptiop/hptiop.h b/sys/dev/hptiop/hptiop.h index 8683620fa27b..67ff7fcd8646 100644 --- a/sys/dev/hptiop/hptiop.h +++ b/sys/dev/hptiop/hptiop.h @@ -1,489 +1,490 @@ /* * HighPoint RR3xxx/4xxx RAID Driver for FreeBSD * Copyright (C) 2007-2012 HighPoint Technologies, Inc. All Rights Reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _HPTIOP_H #define _HPTIOP_H #include __FBSDID("$FreeBSD$"); #define DBG 0 #ifdef DBG int hpt_iop_dbg_level = 0; #define KdPrint(x) do { if (hpt_iop_dbg_level) printf x; } while (0) #define HPT_ASSERT(x) assert(x) #else #define KdPrint(x) #define HPT_ASSERT(x) #endif #define HPT_SRB_MAX_REQ_SIZE 600 #define HPT_SRB_MAX_QUEUE_SIZE 0x100 /* beyond 64G mem */ #define HPT_SRB_FLAG_HIGH_MEM_ACESS 0x1 #define HPT_SRB_MAX_SIZE ((sizeof(struct hpt_iop_srb) + 0x1f) & ~0x1f) #ifndef offsetof #define offsetof(TYPE, MEM) ((size_t)&((TYPE*)0)->MEM) #endif #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #define HPT_IOCTL_MAGIC 0xA1B2C3D4 #define HPT_IOCTL_MAGIC32 0x1A2B3C4D struct hpt_iopmu_itl { u_int32_t resrved0[4]; u_int32_t inbound_msgaddr0; u_int32_t inbound_msgaddr1; u_int32_t outbound_msgaddr0; u_int32_t outbound_msgaddr1; u_int32_t inbound_doorbell; u_int32_t inbound_intstatus; u_int32_t inbound_intmask; u_int32_t outbound_doorbell; u_int32_t outbound_intstatus; u_int32_t outbound_intmask; u_int32_t reserved1[2]; u_int32_t inbound_queue; u_int32_t outbound_queue; }; #define IOPMU_QUEUE_EMPTY 0xffffffff #define IOPMU_QUEUE_MASK_HOST_BITS 0xf0000000 #define IOPMU_QUEUE_ADDR_HOST_BIT 0x80000000 #define IOPMU_QUEUE_REQUEST_SIZE_BIT 0x40000000 #define IOPMU_QUEUE_REQUEST_RESULT_BIT 0x40000000 #define IOPMU_MAX_MEM_SUPPORT_MASK_64G 0xfffffff000000000ull #define IOPMU_MAX_MEM_SUPPORT_MASK_32G 0xfffffff800000000ull #define IOPMU_OUTBOUND_INT_MSG0 1 #define IOPMU_OUTBOUND_INT_MSG1 2 #define IOPMU_OUTBOUND_INT_DOORBELL 4 #define IOPMU_OUTBOUND_INT_POSTQUEUE 8 #define IOPMU_OUTBOUND_INT_PCI 0x10 #define IOPMU_INBOUND_INT_MSG0 1 #define IOPMU_INBOUND_INT_MSG1 2 #define IOPMU_INBOUND_INT_DOORBELL 4 #define IOPMU_INBOUND_INT_ERROR 8 #define IOPMU_INBOUND_INT_POSTQUEUE 0x10 #define MVIOP_QUEUE_LEN 512 struct hpt_iopmu_mv { u_int32_t inbound_head; u_int32_t inbound_tail; u_int32_t outbound_head; u_int32_t outbound_tail; u_int32_t inbound_msg; u_int32_t outbound_msg; u_int32_t reserve[10]; u_int64_t inbound_q[MVIOP_QUEUE_LEN]; u_int64_t outbound_q[MVIOP_QUEUE_LEN]; }; struct hpt_iopmv_regs { u_int32_t reserved[0x20400 / 4]; u_int32_t inbound_doorbell; u_int32_t inbound_intmask; u_int32_t outbound_doorbell; u_int32_t outbound_intmask; }; #define CL_POINTER_TOGGLE 0x00004000 #define CPU_TO_F0_DRBL_MSG_A_BIT 0x02000000 #pragma pack(1) struct hpt_iopmu_mvfrey { u_int32_t reserved[0x4000 / 4]; /* hpt_frey_com_reg */ u_int32_t inbound_base; /* 0x4000 : 0 */ u_int32_t inbound_base_high; /* 4 */ u_int32_t reserved2[(0x18 - 8)/ 4]; u_int32_t inbound_write_ptr; /* 0x18 */ u_int32_t inbound_read_ptr; /* 0x1c */ u_int32_t reserved3[(0x2c - 0x20) / 4]; u_int32_t inbound_conf_ctl; /* 0x2c */ u_int32_t reserved4[(0x50 - 0x30) / 4]; u_int32_t outbound_base; /* 0x50 */ u_int32_t outbound_base_high; /* 0x54 */ u_int32_t outbound_shadow_base; /* 0x58 */ u_int32_t outbound_shadow_base_high; /* 0x5c */ u_int32_t reserved5[(0x68 - 0x60) / 4]; u_int32_t outbound_write; /* 0x68 */ u_int32_t reserved6[(0x70 - 0x6c) / 4]; u_int32_t outbound_read; /* 0x70 */ u_int32_t reserved7[(0x88 - 0x74) / 4]; u_int32_t isr_cause; /* 0x88 */ u_int32_t isr_enable; /* 0x8c */ u_int32_t reserved8[(0x10200 - 0x4090) / 4]; /* hpt_frey_intr_ctl intr_ctl */ u_int32_t main_int_cuase; /* 0x10200: 0 */ u_int32_t main_irq_enable; /* 4 */ u_int32_t main_fiq_enable; /* 8 */ u_int32_t pcie_f0_int_enable; /* 0xc */ u_int32_t pcie_f1_int_enable; /* 0x10 */ u_int32_t pcie_f2_int_enable; /* 0x14 */ u_int32_t pcie_f3_int_enable; /* 0x18 */ u_int32_t reserved9[(0x10400 - 0x1021c) / 4]; /* hpt_frey_msg_drbl */ u_int32_t f0_to_cpu_msg_a; /* 0x10400: 0 */ u_int32_t reserved10[(0x20 - 4) / 4]; u_int32_t cpu_to_f0_msg_a; /* 0x20 */ u_int32_t reserved11[(0x80 - 0x24) / 4]; u_int32_t f0_doorbell; /* 0x80 */ u_int32_t f0_doorbell_enable; /* 0x84 */ }; struct mvfrey_inlist_entry { u_int64_t addr; u_int32_t intrfc_len; u_int32_t reserved; }; struct mvfrey_outlist_entry { u_int32_t val; }; #pragma pack() #define MVIOP_IOCTLCFG_SIZE 0x800 #define MVIOP_MU_QUEUE_ADDR_HOST_MASK (~(0x1full)) #define MVIOP_MU_QUEUE_ADDR_HOST_BIT 4 #define MVIOP_MU_QUEUE_ADDR_IOP_HIGH32 0xffffffff #define MVIOP_MU_QUEUE_REQUEST_RESULT_BIT 1 #define MVIOP_MU_QUEUE_REQUEST_RETURN_CONTEXT 2 #define MVIOP_MU_INBOUND_INT_MSG 1 #define MVIOP_MU_INBOUND_INT_POSTQUEUE 2 #define MVIOP_MU_OUTBOUND_INT_MSG 1 #define MVIOP_MU_OUTBOUND_INT_POSTQUEUE 2 #define MVIOP_CMD_TYPE_GET_CONFIG (1 << 5) #define MVIOP_CMD_TYPE_SET_CONFIG (1 << 6) #define MVIOP_CMD_TYPE_SCSI (1 << 7) #define MVIOP_CMD_TYPE_IOCTL (1 << 8) #define MVIOP_CMD_TYPE_BLOCK (1 << 9) #define MVIOP_REQUEST_NUMBER_START_BIT 16 #define MVFREYIOPMU_QUEUE_REQUEST_RESULT_BIT 0x40000000 enum hpt_iopmu_message { /* host-to-iop messages */ IOPMU_INBOUND_MSG0_NOP = 0, IOPMU_INBOUND_MSG0_RESET, IOPMU_INBOUND_MSG0_FLUSH, IOPMU_INBOUND_MSG0_SHUTDOWN, IOPMU_INBOUND_MSG0_STOP_BACKGROUND_TASK, IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK, IOPMU_INBOUND_MSG0_RESET_COMM, IOPMU_INBOUND_MSG0_MAX = 0xff, /* iop-to-host messages */ IOPMU_OUTBOUND_MSG0_REGISTER_DEVICE_0 = 0x100, IOPMU_OUTBOUND_MSG0_REGISTER_DEVICE_MAX = 0x1ff, IOPMU_OUTBOUND_MSG0_UNREGISTER_DEVICE_0 = 0x200, IOPMU_OUTBOUND_MSG0_UNREGISTER_DEVICE_MAX = 0x2ff, IOPMU_OUTBOUND_MSG0_REVALIDATE_DEVICE_0 = 0x300, IOPMU_OUTBOUND_MSG0_REVALIDATE_DEVICE_MAX = 0x3ff, }; #define IOP_REQUEST_FLAG_SYNC_REQUEST 1 #define IOP_REQUEST_FLAG_BIST_REQUEST 2 #define IOP_REQUEST_FLAG_REMAPPED 4 #define IOP_REQUEST_FLAG_OUTPUT_CONTEXT 8 #define IOP_REQUEST_FLAG_ADDR_BITS 0x40 /* flags[31:16] is phy_addr[47:32] */ enum hpt_iop_request_type { IOP_REQUEST_TYPE_GET_CONFIG = 0, IOP_REQUEST_TYPE_SET_CONFIG, IOP_REQUEST_TYPE_BLOCK_COMMAND, IOP_REQUEST_TYPE_SCSI_COMMAND, IOP_REQUEST_TYPE_IOCTL_COMMAND, IOP_REQUEST_TYPE_MAX }; enum hpt_iop_result_type { IOP_RESULT_PENDING = 0, IOP_RESULT_SUCCESS, IOP_RESULT_FAIL, IOP_RESULT_BUSY, IOP_RESULT_RESET, IOP_RESULT_INVALID_REQUEST, IOP_RESULT_BAD_TARGET, IOP_RESULT_CHECK_CONDITION, }; #pragma pack(1) struct hpt_iop_request_header { u_int32_t size; u_int32_t type; u_int32_t flags; u_int32_t result; u_int64_t context; /* host context */ }; struct hpt_iop_request_get_config { struct hpt_iop_request_header header; u_int32_t interface_version; u_int32_t firmware_version; u_int32_t max_requests; u_int32_t request_size; u_int32_t max_sg_count; u_int32_t data_transfer_length; u_int32_t alignment_mask; u_int32_t max_devices; u_int32_t sdram_size; }; struct hpt_iop_request_set_config { struct hpt_iop_request_header header; u_int32_t iop_id; u_int16_t vbus_id; u_int16_t max_host_request_size; u_int32_t reserve[6]; }; struct hpt_iopsg { u_int32_t size; u_int32_t eot; /* non-zero: end of table */ u_int64_t pci_address; }; #define IOP_BLOCK_COMMAND_READ 1 #define IOP_BLOCK_COMMAND_WRITE 2 #define IOP_BLOCK_COMMAND_VERIFY 3 #define IOP_BLOCK_COMMAND_FLUSH 4 #define IOP_BLOCK_COMMAND_SHUTDOWN 5 struct hpt_iop_request_block_command { struct hpt_iop_request_header header; u_int8_t channel; u_int8_t target; u_int8_t lun; u_int8_t pad1; u_int16_t command; /* IOP_BLOCK_COMMAND_{READ,WRITE} */ u_int16_t sectors; u_int64_t lba; struct hpt_iopsg sg_list[1]; }; struct hpt_iop_request_scsi_command { struct hpt_iop_request_header header; u_int8_t channel; u_int8_t target; u_int8_t lun; u_int8_t pad1; u_int8_t cdb[16]; u_int32_t dataxfer_length; struct hpt_iopsg sg_list[1]; }; struct hpt_iop_request_ioctl_command { struct hpt_iop_request_header header; u_int32_t ioctl_code; u_int32_t inbuf_size; u_int32_t outbuf_size; u_int32_t bytes_returned; u_int8_t buf[1]; /* out data should be put at buf[(inbuf_size+3)&~3] */ }; struct hpt_iop_ioctl_param { u_int32_t Magic; /* used to check if it's a valid ioctl packet */ u_int32_t dwIoControlCode; /* operation control code */ unsigned long lpInBuffer; /* input data buffer */ u_int32_t nInBufferSize; /* size of input data buffer */ unsigned long lpOutBuffer; /* output data buffer */ u_int32_t nOutBufferSize; /* size of output data buffer */ unsigned long lpBytesReturned; /* count of HPT_U8s returned */ } __packed; #define HPT_IOCTL_FLAG_OPEN 1 #define HPT_CTL_CODE_BSD_TO_IOP(x) ((x)-0xff00) typedef struct cdev * ioctl_dev_t; typedef struct thread * ioctl_thread_t; struct hpt_iop_hba { struct hptiop_adapter_ops *ops; union { struct { struct hpt_iopmu_itl *mu; } itl; struct { struct hpt_iopmv_regs *regs; struct hpt_iopmu_mv *mu; } mv; struct { struct hpt_iop_request_get_config *config; struct hpt_iopmu_mvfrey *mu; int internal_mem_size; int list_count; struct mvfrey_inlist_entry *inlist; u_int64_t inlist_phy; u_int32_t inlist_wptr; struct mvfrey_outlist_entry *outlist; u_int64_t outlist_phy; u_int32_t *outlist_cptr; /* copy pointer shadow */ u_int64_t outlist_cptr_phy; u_int32_t outlist_rptr; } mvfrey; } u; struct hpt_iop_hba *next; u_int32_t firmware_version; u_int32_t interface_version; u_int32_t max_devices; u_int32_t max_requests; u_int32_t max_request_size; u_int32_t max_sg_count; u_int32_t msg_done; device_t pcidev; u_int32_t pciunit; ioctl_dev_t ioctl_dev; bus_dma_tag_t parent_dmat; bus_dma_tag_t io_dmat; bus_dma_tag_t srb_dmat; bus_dma_tag_t ctlcfg_dmat; bus_dmamap_t srb_dmamap; bus_dmamap_t ctlcfg_dmamap; struct resource *bar0_res; bus_space_tag_t bar0t; bus_space_handle_t bar0h; int bar0_rid; struct resource *bar2_res; bus_space_tag_t bar2t; bus_space_handle_t bar2h; int bar2_rid; /* to release */ u_int8_t *uncached_ptr; void *ctlcfg_ptr; /* for scsi request block */ struct hpt_iop_srb *srb_list; /* for interrupt */ struct resource *irq_res; void *irq_handle; /* for ioctl and set/get config */ struct resource *ctlcfg_res; void *ctlcfg_handle; u_int64_t ctlcfgcmd_phy; u_int32_t config_done; /* can be negative value */ u_int32_t initialized:1; /* other resources */ struct cam_sim *sim; struct cam_path *path; void *req; struct mtx lock; #define HPT_IOCTL_FLAG_OPEN 1 u_int32_t flag; struct hpt_iop_srb* srb[HPT_SRB_MAX_QUEUE_SIZE]; }; #pragma pack() enum hptiop_family { INTEL_BASED_IOP = 0, MV_BASED_IOP, MVFREY_BASED_IOP, UNKNOWN_BASED_IOP = 0xf }; struct hptiop_adapter_ops { enum hptiop_family family; int (*iop_wait_ready)(struct hpt_iop_hba *hba, u_int32_t millisec); int (*internal_memalloc)(struct hpt_iop_hba *hba); int (*internal_memfree)(struct hpt_iop_hba *hba); int (*alloc_pci_res)(struct hpt_iop_hba *hba); void (*release_pci_res)(struct hpt_iop_hba *hba); void (*enable_intr)(struct hpt_iop_hba *hba); void (*disable_intr)(struct hpt_iop_hba *hba); int (*get_config)(struct hpt_iop_hba *hba, struct hpt_iop_request_get_config *config); int (*set_config)(struct hpt_iop_hba *hba, struct hpt_iop_request_set_config *config); int (*iop_intr)(struct hpt_iop_hba *hba); void (*post_msg)(struct hpt_iop_hba *hba, u_int32_t msg); void (*post_req)(struct hpt_iop_hba *hba, struct hpt_iop_srb *srb, bus_dma_segment_t *segs, int nsegs); int (*do_ioctl)(struct hpt_iop_hba *hba, struct hpt_iop_ioctl_param * pParams); int (*reset_comm)(struct hpt_iop_hba *hba); }; struct hpt_iop_srb { u_int8_t req[HPT_SRB_MAX_REQ_SIZE]; struct hpt_iop_hba *hba; union ccb *ccb; struct hpt_iop_srb *next; bus_dmamap_t dma_map; u_int64_t phy_addr; u_int32_t srb_flag; int index; + struct callout_handle timeout_ch; }; #define hptiop_lock_adapter(hba) mtx_lock(&(hba)->lock) #define hptiop_unlock_adapter(hba) mtx_unlock(&(hba)->lock) #define HPT_OSM_TIMEOUT (20*hz) /* timeout value for OS commands */ #define HPT_DO_IOCONTROL _IOW('H', 0, struct hpt_iop_ioctl_param) #define HPT_SCAN_BUS _IO('H', 1) static __inline int hptiop_sleep(struct hpt_iop_hba *hba, void *ident, int priority, const char *wmesg, int timo) { int retval; retval = msleep(ident, &hba->lock, priority, wmesg, timo); return retval; } #define HPT_DEV_MAJOR 200 #endif diff --git a/sys/dev/hptmv/entry.c b/sys/dev/hptmv/entry.c index 80f513036a2b..85d658e16adb 100644 --- a/sys/dev/hptmv/entry.c +++ b/sys/dev/hptmv/entry.c @@ -1,3084 +1,3085 @@ /* * Copyright (c) 2004-2005 HighPoint Technologies, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #if (__FreeBSD_version >= 500000) #include #include #endif #if (__FreeBSD_version >= 500000) #include #include #else #include #include #include #include #endif #ifndef __KERNEL__ #define __KERNEL__ #endif #include #include #include #include #ifdef DEBUG #ifdef DEBUG_LEVEL int hpt_dbg_level = DEBUG_LEVEL; #else int hpt_dbg_level = 0; #endif #endif #define MV_ERROR printf /* * CAM SIM entry points */ static int hpt_probe (device_t dev); static void launch_worker_thread(void); static int hpt_attach(device_t dev); static int hpt_detach(device_t dev); static int hpt_shutdown(device_t dev); static void hpt_poll(struct cam_sim *sim); static void hpt_intr(void *arg); static void hpt_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); static void hpt_action(struct cam_sim *sim, union ccb *ccb); static device_method_t driver_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hpt_probe), DEVMETHOD(device_attach, hpt_attach), DEVMETHOD(device_detach, hpt_detach), DEVMETHOD(device_shutdown, hpt_shutdown), DEVMETHOD_END }; static driver_t hpt_pci_driver = { __str(PROC_DIR_NAME), driver_methods, sizeof(IAL_ADAPTER_T) }; static devclass_t hpt_devclass; #define __DRIVER_MODULE(p1, p2, p3, p4, p5, p6) DRIVER_MODULE(p1, p2, p3, p4, p5, p6) __DRIVER_MODULE(PROC_DIR_NAME, pci, hpt_pci_driver, hpt_devclass, 0, 0); MODULE_DEPEND(PROC_DIR_NAME, cam, 1, 1, 1); #define ccb_ccb_ptr spriv_ptr0 #define ccb_adapter ccb_h.spriv_ptr1 static void SetInquiryData(PINQUIRYDATA inquiryData, PVDevice pVDev); static void HPTLIBAPI OsSendCommand (_VBUS_ARG union ccb * ccb); static void HPTLIBAPI fOsCommandDone(_VBUS_ARG PCommand pCmd); static void ccb_done(union ccb *ccb); static void hpt_queue_ccb(union ccb **ccb_Q, union ccb *ccb); static void hpt_free_ccb(union ccb **ccb_Q, union ccb *ccb); static void hptmv_free_edma_queues(IAL_ADAPTER_T *pAdapter); static void hptmv_free_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum); static void handleEdmaError(_VBUS_ARG PCommand pCmd); static int hptmv_init_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum); static int fResetActiveCommands(PVBus _vbus_p); static void fRegisterVdevice(IAL_ADAPTER_T *pAdapter); static int hptmv_allocate_edma_queues(IAL_ADAPTER_T *pAdapter); static void hptmv_handle_event_disconnect(void *data); static void hptmv_handle_event_connect(void *data); static int start_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum); static void init_vdev_params(IAL_ADAPTER_T *pAdapter, MV_U8 channel); static int hptmv_parse_identify_results(MV_SATA_CHANNEL *pMvSataChannel); static int HPTLIBAPI fOsBuildSgl(_VBUS_ARG PCommand pCmd, FPSCAT_GATH pSg, int logical); static MV_BOOLEAN CommandCompletionCB(MV_SATA_ADAPTER *pMvSataAdapter, MV_U8 channelNum, MV_COMPLETION_TYPE comp_type, MV_VOID_PTR commandId, MV_U16 responseFlags, MV_U32 timeStamp, MV_STORAGE_DEVICE_REGISTERS *registerStruct); static MV_BOOLEAN hptmv_event_notify(MV_SATA_ADAPTER *pMvSataAdapter, MV_EVENT_TYPE eventType, MV_U32 param1, MV_U32 param2); #define ccb_ccb_ptr spriv_ptr0 #define ccb_adapter ccb_h.spriv_ptr1 IAL_ADAPTER_T *gIal_Adapter = 0; IAL_ADAPTER_T *pCurAdapter = 0; static MV_SATA_CHANNEL gMvSataChannels[MAX_VBUS][MV_SATA_CHANNELS_NUM]; typedef struct st_HPT_DPC { IAL_ADAPTER_T *pAdapter; void (*dpc)(IAL_ADAPTER_T *, void *, UCHAR); void *arg; UCHAR flags; } ST_HPT_DPC; #define MAX_DPC 16 UCHAR DPC_Request_Nums = 0; static ST_HPT_DPC DpcQueue[MAX_DPC]; static int DpcQueue_First=0; static int DpcQueue_Last = 0; char DRIVER_VERSION[] = "v1.16"; #if (__FreeBSD_version >= 500000) static struct mtx driver_lock; intrmask_t lock_driver() { intrmask_t spl = 0; mtx_lock(&driver_lock); return spl; } void unlock_driver(intrmask_t spl) { mtx_unlock(&driver_lock); } #else static int driver_locked = 0; intrmask_t lock_driver() { intrmask_t spl = splcam(); loop: while (driver_locked) tsleep(&driver_locked, PRIBIO, "hptlck", hz); atomic_add_int(&driver_locked, 1); if (driver_locked>1) { atomic_subtract_int(&driver_locked, 1); goto loop; } return spl; } void unlock_driver(intrmask_t spl) { atomic_subtract_int(&driver_locked, 1); if (driver_locked==0) { wakeup(&driver_locked); } splx(spl); } #endif /******************************************************************************* * Name: hptmv_free_channel * * Description: free allocated queues for the given channel * * Parameters: pMvSataAdapter - pointer to the RR18xx controler this * channel connected to. * channelNum - channel number. * ******************************************************************************/ static void hptmv_free_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum) { HPT_ASSERT(channelNum < MV_SATA_CHANNELS_NUM); pAdapter->mvSataAdapter.sataChannel[channelNum] = NULL; } static void failDevice(PVDevice pVDev) { PVBus _vbus_p = pVDev->pVBus; IAL_ADAPTER_T *pAdapter = (IAL_ADAPTER_T *)_vbus_p->OsExt; pVDev->u.disk.df_on_line = 0; pVDev->vf_online = 0; if (pVDev->pfnDeviceFailed) CallWhenIdle(_VBUS_P (DPC_PROC)pVDev->pfnDeviceFailed, pVDev); fNotifyGUI(ET_DEVICE_REMOVED, pVDev); #ifndef FOR_DEMO if (pAdapter->ver_601==2 && !pAdapter->beeping) { pAdapter->beeping = 1; BeepOn(pAdapter->mvSataAdapter.adapterIoBaseAddress); set_fail_led(&pAdapter->mvSataAdapter, pVDev->u.disk.mv->channelNumber, 1); } #endif } int MvSataResetChannel(MV_SATA_ADAPTER *pMvSataAdapter, MV_U8 channel); static void handleEdmaError(_VBUS_ARG PCommand pCmd) { PDevice pDevice = &pCmd->pVDevice->u.disk; MV_SATA_ADAPTER * pSataAdapter = pDevice->mv->mvSataAdapter; if (!pDevice->df_on_line) { KdPrint(("Device is offline")); pCmd->Result = RETURN_BAD_DEVICE; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); return; } if (pCmd->RetryCount++>5) { hpt_printk(("too many retries on channel(%d)\n", pDevice->mv->channelNumber)); failed: failDevice(pCmd->pVDevice); pCmd->Result = RETURN_IDE_ERROR; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); return; } /* reset the channel and retry the command */ if (MvSataResetChannel(pSataAdapter, pDevice->mv->channelNumber)) goto failed; fNotifyGUI(ET_DEVICE_ERROR, Map2pVDevice(pDevice)); hpt_printk(("Retry on channel(%d)\n", pDevice->mv->channelNumber)); fDeviceSendCommand(_VBUS_P pCmd); } /**************************************************************** * Name: hptmv_init_channel * * Description: allocate request and response queues for the EDMA of the * given channel and sets other fields. * * Parameters: * pAdapter - pointer to the emulated adapter data structure * channelNum - channel number. * Return: 0 on success, otherwise on failure ****************************************************************/ static int hptmv_init_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum) { MV_SATA_CHANNEL *pMvSataChannel; dma_addr_t req_dma_addr; dma_addr_t rsp_dma_addr; if (channelNum >= MV_SATA_CHANNELS_NUM) { MV_ERROR("RR18xx[%d]: Bad channelNum=%d", pAdapter->mvSataAdapter.adapterId, channelNum); return -1; } pMvSataChannel = &gMvSataChannels[pAdapter->mvSataAdapter.adapterId][channelNum]; pAdapter->mvSataAdapter.sataChannel[channelNum] = pMvSataChannel; pMvSataChannel->channelNumber = channelNum; pMvSataChannel->lba48Address = MV_FALSE; pMvSataChannel->maxReadTransfer = MV_FALSE; pMvSataChannel->requestQueue = (struct mvDmaRequestQueueEntry *) (pAdapter->requestsArrayBaseAlignedAddr + (channelNum * MV_EDMA_REQUEST_QUEUE_SIZE)); req_dma_addr = pAdapter->requestsArrayBaseDmaAlignedAddr + (channelNum * MV_EDMA_REQUEST_QUEUE_SIZE); KdPrint(("requestQueue addr is 0x%llX", (HPT_U64)(ULONG_PTR)req_dma_addr)); /* check the 1K alignment of the request queue*/ if (req_dma_addr & 0x3ff) { MV_ERROR("RR18xx[%d]: request queue allocated isn't 1 K aligned," " dma_addr=%llx channel=%d\n", pAdapter->mvSataAdapter.adapterId, (HPT_U64)(ULONG_PTR)req_dma_addr, channelNum); return -1; } pMvSataChannel->requestQueuePciLowAddress = req_dma_addr; pMvSataChannel->requestQueuePciHiAddress = 0; KdPrint(("RR18xx[%d,%d]: request queue allocated: 0x%p", pAdapter->mvSataAdapter.adapterId, channelNum, pMvSataChannel->requestQueue)); pMvSataChannel->responseQueue = (struct mvDmaResponseQueueEntry *) (pAdapter->responsesArrayBaseAlignedAddr + (channelNum * MV_EDMA_RESPONSE_QUEUE_SIZE)); rsp_dma_addr = pAdapter->responsesArrayBaseDmaAlignedAddr + (channelNum * MV_EDMA_RESPONSE_QUEUE_SIZE); /* check the 256 alignment of the response queue*/ if (rsp_dma_addr & 0xff) { MV_ERROR("RR18xx[%d,%d]: response queue allocated isn't 256 byte " "aligned, dma_addr=%llx\n", pAdapter->mvSataAdapter.adapterId, channelNum, (HPT_U64)(ULONG_PTR)rsp_dma_addr); return -1; } pMvSataChannel->responseQueuePciLowAddress = rsp_dma_addr; pMvSataChannel->responseQueuePciHiAddress = 0; KdPrint(("RR18xx[%d,%d]: response queue allocated: 0x%p", pAdapter->mvSataAdapter.adapterId, channelNum, pMvSataChannel->responseQueue)); pAdapter->mvChannel[channelNum].online = MV_TRUE; return 0; } /****************************************************************************** * Name: hptmv_parse_identify_results * * Description: this functions parses the identify command results, checks * that the connected deives can be accesed by RR18xx EDMA, * and updates the channel stucture accordingly. * * Parameters: pMvSataChannel, pointer to the channel data structure. * * Returns: =0 ->success, < 0 ->failure. * ******************************************************************************/ static int hptmv_parse_identify_results(MV_SATA_CHANNEL *pMvSataChannel) { MV_U16 *iden = pMvSataChannel->identifyDevice; /*LBA addressing*/ if (! (iden[IDEN_CAPACITY_1_OFFSET] & 0x200)) { KdPrint(("IAL Error in IDENTIFY info: LBA not supported\n")); return -1; } else { KdPrint(("%25s - %s\n", "Capabilities", "LBA supported")); } /*DMA support*/ if (! (iden[IDEN_CAPACITY_1_OFFSET] & 0x100)) { KdPrint(("IAL Error in IDENTIFY info: DMA not supported\n")); return -1; } else { KdPrint(("%25s - %s\n", "Capabilities", "DMA supported")); } /* PIO */ if ((iden[IDEN_VALID] & 2) == 0) { KdPrint(("IAL Error in IDENTIFY info: not able to find PIO mode\n")); return -1; } KdPrint(("%25s - 0x%02x\n", "PIO modes supported", iden[IDEN_PIO_MODE_SPPORTED] & 0xff)); /*UDMA*/ if ((iden[IDEN_VALID] & 4) == 0) { KdPrint(("IAL Error in IDENTIFY info: not able to find UDMA mode\n")); return -1; } /* 48 bit address */ if ((iden[IDEN_SUPPORTED_COMMANDS2] & 0x400)) { KdPrint(("%25s - %s\n", "LBA48 addressing", "supported")); pMvSataChannel->lba48Address = MV_TRUE; } else { KdPrint(("%25s - %s\n", "LBA48 addressing", "Not supported")); pMvSataChannel->lba48Address = MV_FALSE; } return 0; } static void init_vdev_params(IAL_ADAPTER_T *pAdapter, MV_U8 channel) { PVDevice pVDev = &pAdapter->VDevices[channel]; MV_SATA_CHANNEL *pMvSataChannel = pAdapter->mvSataAdapter.sataChannel[channel]; MV_U16_PTR IdentifyData = pMvSataChannel->identifyDevice; pMvSataChannel->outstandingCommands = 0; pVDev->u.disk.mv = pMvSataChannel; pVDev->u.disk.df_on_line = 1; pVDev->u.disk.pVBus = &pAdapter->VBus; pVDev->pVBus = &pAdapter->VBus; #ifdef SUPPORT_48BIT_LBA if (pMvSataChannel->lba48Address == MV_TRUE) pVDev->u.disk.dDeRealCapacity = ((IdentifyData[101]<<16) | IdentifyData[100]) - 1; else #endif if(IdentifyData[53] & 1) { pVDev->u.disk.dDeRealCapacity = (((IdentifyData[58]<<16 | IdentifyData[57]) < (IdentifyData[61]<<16 | IdentifyData[60])) ? (IdentifyData[61]<<16 | IdentifyData[60]) : (IdentifyData[58]<<16 | IdentifyData[57])) - 1; } else pVDev->u.disk.dDeRealCapacity = (IdentifyData[61]<<16 | IdentifyData[60]) - 1; pVDev->u.disk.bDeUsable_Mode = pVDev->u.disk.bDeModeSetting = pAdapter->mvChannel[channel].maxPioModeSupported - MV_ATA_TRANSFER_PIO_0; if (pAdapter->mvChannel[channel].maxUltraDmaModeSupported!=0xFF) { pVDev->u.disk.bDeUsable_Mode = pVDev->u.disk.bDeModeSetting = pAdapter->mvChannel[channel].maxUltraDmaModeSupported - MV_ATA_TRANSFER_UDMA_0 + 8; } } static void device_change(IAL_ADAPTER_T *pAdapter , MV_U8 channelIndex, int plugged) { PVDevice pVDev; MV_SATA_ADAPTER *pMvSataAdapter = &pAdapter->mvSataAdapter; MV_SATA_CHANNEL *pMvSataChannel = pMvSataAdapter->sataChannel[channelIndex]; if (!pMvSataChannel) return; if (plugged) { pVDev = &(pAdapter->VDevices[channelIndex]); init_vdev_params(pAdapter, channelIndex); pVDev->VDeviceType = pVDev->u.disk.df_atapi? VD_ATAPI : pVDev->u.disk.df_removable_drive? VD_REMOVABLE : VD_SINGLE_DISK; pVDev->VDeviceCapacity = pVDev->u.disk.dDeRealCapacity-SAVE_FOR_RAID_INFO; pVDev->pfnSendCommand = pfnSendCommand[pVDev->VDeviceType]; pVDev->pfnDeviceFailed = pfnDeviceFailed[pVDev->VDeviceType]; pVDev->vf_online = 1; #ifdef SUPPORT_ARRAY if(pVDev->pParent) { int iMember; for(iMember = 0; iMember < pVDev->pParent->u.array.bArnMember; iMember++) if((PVDevice)pVDev->pParent->u.array.pMember[iMember] == pVDev) pVDev->pParent->u.array.pMember[iMember] = NULL; pVDev->pParent = NULL; } #endif fNotifyGUI(ET_DEVICE_PLUGGED,pVDev); fCheckBootable(pVDev); RegisterVDevice(pVDev); #ifndef FOR_DEMO if (pAdapter->beeping) { pAdapter->beeping = 0; BeepOff(pAdapter->mvSataAdapter.adapterIoBaseAddress); } #endif } else { pVDev = &(pAdapter->VDevices[channelIndex]); failDevice(pVDev); } } static int start_channel(IAL_ADAPTER_T *pAdapter, MV_U8 channelNum) { MV_SATA_ADAPTER *pMvSataAdapter = &pAdapter->mvSataAdapter; MV_SATA_CHANNEL *pMvSataChannel = pMvSataAdapter->sataChannel[channelNum]; MV_CHANNEL *pChannelInfo = &(pAdapter->mvChannel[channelNum]); MV_U32 udmaMode,pioMode; KdPrint(("RR18xx [%d]: start channel (%d)", pMvSataAdapter->adapterId, channelNum)); /* Software reset channel */ if (mvStorageDevATASoftResetDevice(pMvSataAdapter, channelNum) == MV_FALSE) { MV_ERROR("RR18xx [%d,%d]: failed to perform Software reset\n", pMvSataAdapter->adapterId, channelNum); return -1; } /* Hardware reset channel */ if (mvSataChannelHardReset(pMvSataAdapter, channelNum) == MV_FALSE) { /* If failed, try again - this is when trying to hardreset a channel */ /* when drive is just spinning up */ StallExec(5000000); /* wait 5 sec before trying again */ if (mvSataChannelHardReset(pMvSataAdapter, channelNum) == MV_FALSE) { MV_ERROR("RR18xx [%d,%d]: failed to perform Hard reset\n", pMvSataAdapter->adapterId, channelNum); return -1; } } /* identify device*/ if (mvStorageDevATAIdentifyDevice(pMvSataAdapter, channelNum) == MV_FALSE) { MV_ERROR("RR18xx [%d,%d]: failed to perform ATA Identify command\n" , pMvSataAdapter->adapterId, channelNum); return -1; } if (hptmv_parse_identify_results(pMvSataChannel)) { MV_ERROR("RR18xx [%d,%d]: Error in parsing ATA Identify message\n" , pMvSataAdapter->adapterId, channelNum); return -1; } /* mvStorageDevATASetFeatures */ /* Disable 8 bit PIO in case CFA enabled */ if (pMvSataChannel->identifyDevice[86] & 4) { KdPrint(("RR18xx [%d]: Disable 8 bit PIO (CFA enabled) \n", pMvSataAdapter->adapterId)); if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_DISABLE_8_BIT_PIO, 0, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d]: channel %d: mvStorageDevATASetFeatures" " failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } } /* Write cache */ #ifdef ENABLE_WRITE_CACHE if (pMvSataChannel->identifyDevice[82] & 0x20) { if (!(pMvSataChannel->identifyDevice[85] & 0x20)) /* if not enabled by default */ { if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_ENABLE_WCACHE, 0, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d]: channel %d: mvStorageDevATASetFeatures failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } } KdPrint(("RR18xx [%d]: channel %d, write cache enabled\n", pMvSataAdapter->adapterId, channelNum)); } else { KdPrint(("RR18xx [%d]: channel %d, write cache not supported\n", pMvSataAdapter->adapterId, channelNum)); } #else /* disable write cache */ { if (pMvSataChannel->identifyDevice[85] & 0x20) { KdPrint(("RR18xx [%d]: channel =%d, disable write cache\n", pMvSataAdapter->adapterId, channelNum)); if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_DISABLE_WCACHE, 0, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d]: channel %d: mvStorageDevATASetFeatures failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } } KdPrint(("RR18xx [%d]: channel=%d, write cache disabled\n", pMvSataAdapter->adapterId, channelNum)); } #endif /* Set transfer mode */ KdPrint(("RR18xx [%d] Set transfer mode XFER_PIO_SLOW\n", pMvSataAdapter->adapterId)); if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_TRANSFER, MV_ATA_TRANSFER_PIO_SLOW, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d] channel %d: Set Features failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } if (pMvSataChannel->identifyDevice[IDEN_PIO_MODE_SPPORTED] & 1) { pioMode = MV_ATA_TRANSFER_PIO_4; } else if (pMvSataChannel->identifyDevice[IDEN_PIO_MODE_SPPORTED] & 2) { pioMode = MV_ATA_TRANSFER_PIO_3; } else { MV_ERROR("IAL Error in IDENTIFY info: PIO modes 3 and 4 not supported\n"); pioMode = MV_ATA_TRANSFER_PIO_SLOW; } KdPrint(("RR18xx [%d] Set transfer mode XFER_PIO_4\n", pMvSataAdapter->adapterId)); pAdapter->mvChannel[channelNum].maxPioModeSupported = pioMode; if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_TRANSFER, pioMode, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d] channel %d: Set Features failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } udmaMode = MV_ATA_TRANSFER_UDMA_0; if (pMvSataChannel->identifyDevice[IDEN_UDMA_MODE] & 0x40) { udmaMode = MV_ATA_TRANSFER_UDMA_6; } else if (pMvSataChannel->identifyDevice[IDEN_UDMA_MODE] & 0x20) { udmaMode = MV_ATA_TRANSFER_UDMA_5; } else if (pMvSataChannel->identifyDevice[IDEN_UDMA_MODE] & 0x10) { udmaMode = MV_ATA_TRANSFER_UDMA_4; } else if (pMvSataChannel->identifyDevice[IDEN_UDMA_MODE] & 8) { udmaMode = MV_ATA_TRANSFER_UDMA_3; } else if (pMvSataChannel->identifyDevice[IDEN_UDMA_MODE] & 4) { udmaMode = MV_ATA_TRANSFER_UDMA_2; } KdPrint(("RR18xx [%d] Set transfer mode XFER_UDMA_%d\n", pMvSataAdapter->adapterId, udmaMode & 0xf)); pChannelInfo->maxUltraDmaModeSupported = udmaMode; /*if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_TRANSFER, udmaMode, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d] channel %d: Set Features failed\n", pMvSataAdapter->adapterId, channelNum); return -1; }*/ if (pChannelInfo->maxUltraDmaModeSupported == 0xFF) return TRUE; else do { if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_TRANSFER, pChannelInfo->maxUltraDmaModeSupported, 0, 0, 0) == MV_FALSE) { if (pChannelInfo->maxUltraDmaModeSupported > MV_ATA_TRANSFER_UDMA_0) { if (mvStorageDevATASoftResetDevice(pMvSataAdapter, channelNum) == MV_FALSE) { MV_REG_WRITE_BYTE(pMvSataAdapter->adapterIoBaseAddress, pMvSataChannel->eDmaRegsOffset + 0x11c, /* command reg */ MV_ATA_COMMAND_IDLE_IMMEDIATE); mvMicroSecondsDelay(10000); mvSataChannelHardReset(pMvSataAdapter, channelNum); if (mvStorageDevATASoftResetDevice(pMvSataAdapter, channelNum) == MV_FALSE) return FALSE; } if (mvSataChannelHardReset(pMvSataAdapter, channelNum) == MV_FALSE) return FALSE; pChannelInfo->maxUltraDmaModeSupported--; continue; } else return FALSE; } break; }while (1); /* Read look ahead */ #ifdef ENABLE_READ_AHEAD if (pMvSataChannel->identifyDevice[82] & 0x40) { if (!(pMvSataChannel->identifyDevice[85] & 0x40)) /* if not enabled by default */ { if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_ENABLE_RLA, 0, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d] channel %d: Set Features failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } } KdPrint(("RR18xx [%d]: channel=%d, read look ahead enabled\n", pMvSataAdapter->adapterId, channelNum)); } else { KdPrint(("RR18xx [%d]: channel %d, Read Look Ahead not supported\n", pMvSataAdapter->adapterId, channelNum)); } #else { if (pMvSataChannel->identifyDevice[86] & 0x20) { KdPrint(("RR18xx [%d]:channel %d, disable read look ahead\n", pMvSataAdapter->adapterId, channelNum)); if (mvStorageDevATASetFeatures(pMvSataAdapter, channelNum, MV_ATA_SET_FEATURES_DISABLE_RLA, 0, 0, 0, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d]:channel %d: ATA Set Features failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } } KdPrint(("RR18xx [%d]:channel %d, read look ahead disabled\n", pMvSataAdapter->adapterId, channelNum)); } #endif { KdPrint(("RR18xx [%d]: channel %d config EDMA, Non Queued Mode\n", pMvSataAdapter->adapterId, channelNum)); if (mvSataConfigEdmaMode(pMvSataAdapter, channelNum, MV_EDMA_MODE_NOT_QUEUED, 0) == MV_FALSE) { MV_ERROR("RR18xx [%d] channel %d Error: mvSataConfigEdmaMode failed\n", pMvSataAdapter->adapterId, channelNum); return -1; } } /* Enable EDMA */ if (mvSataEnableChannelDma(pMvSataAdapter, channelNum) == MV_FALSE) { MV_ERROR("RR18xx [%d] Failed to enable DMA, channel=%d\n", pMvSataAdapter->adapterId, channelNum); return -1; } MV_ERROR("RR18xx [%d,%d]: channel started successfully\n", pMvSataAdapter->adapterId, channelNum); #ifndef FOR_DEMO set_fail_led(pMvSataAdapter, channelNum, 0); #endif return 0; } static void hptmv_handle_event(void * data, int flag) { IAL_ADAPTER_T *pAdapter = (IAL_ADAPTER_T *)data; MV_SATA_ADAPTER *pMvSataAdapter = &pAdapter->mvSataAdapter; MV_U8 channelIndex; /* mvOsSemTake(&pMvSataAdapter->semaphore); */ for (channelIndex = 0; channelIndex < MV_SATA_CHANNELS_NUM; channelIndex++) { switch(pAdapter->sataEvents[channelIndex]) { case SATA_EVENT_CHANNEL_CONNECTED: /* Handle only connects */ if (flag == 1) break; KdPrint(("RR18xx [%d,%d]: new device connected\n", pMvSataAdapter->adapterId, channelIndex)); hptmv_init_channel(pAdapter, channelIndex); if (mvSataConfigureChannel( pMvSataAdapter, channelIndex) == MV_FALSE) { MV_ERROR("RR18xx [%d,%d] Failed to configure\n", pMvSataAdapter->adapterId, channelIndex); hptmv_free_channel(pAdapter, channelIndex); } else { /*mvSataChannelHardReset(pMvSataAdapter, channel);*/ if (start_channel( pAdapter, channelIndex)) { MV_ERROR("RR18xx [%d,%d]Failed to start channel\n", pMvSataAdapter->adapterId, channelIndex); hptmv_free_channel(pAdapter, channelIndex); } else { device_change(pAdapter, channelIndex, TRUE); } } pAdapter->sataEvents[channelIndex] = SATA_EVENT_NO_CHANGE; break; case SATA_EVENT_CHANNEL_DISCONNECTED: /* Handle only disconnects */ if (flag == 0) break; KdPrint(("RR18xx [%d,%d]: device disconnected\n", pMvSataAdapter->adapterId, channelIndex)); /* Flush pending commands */ if(pMvSataAdapter->sataChannel[channelIndex]) { _VBUS_INST(&pAdapter->VBus) mvSataFlushDmaQueue (pMvSataAdapter, channelIndex, MV_FLUSH_TYPE_CALLBACK); CheckPendingCall(_VBUS_P0); mvSataRemoveChannel(pMvSataAdapter,channelIndex); hptmv_free_channel(pAdapter, channelIndex); pMvSataAdapter->sataChannel[channelIndex] = NULL; KdPrint(("RR18xx [%d,%d]: channel removed\n", pMvSataAdapter->adapterId, channelIndex)); if (pAdapter->outstandingCommands==0 && DPC_Request_Nums==0) Check_Idle_Call(pAdapter); } else { KdPrint(("RR18xx [%d,%d]: channel already removed!!\n", pMvSataAdapter->adapterId, channelIndex)); } pAdapter->sataEvents[channelIndex] = SATA_EVENT_NO_CHANGE; break; case SATA_EVENT_NO_CHANGE: break; default: break; } } /* mvOsSemRelease(&pMvSataAdapter->semaphore); */ } #define EVENT_CONNECT 1 #define EVENT_DISCONNECT 0 static void hptmv_handle_event_connect(void *data) { hptmv_handle_event (data, 0); } static void hptmv_handle_event_disconnect(void *data) { hptmv_handle_event (data, 1); } static MV_BOOLEAN hptmv_event_notify(MV_SATA_ADAPTER *pMvSataAdapter, MV_EVENT_TYPE eventType, MV_U32 param1, MV_U32 param2) { IAL_ADAPTER_T *pAdapter = pMvSataAdapter->IALData; switch (eventType) { case MV_EVENT_TYPE_SATA_CABLE: { MV_U8 channel = param2; if (param1 == EVENT_CONNECT) { pAdapter->sataEvents[channel] = SATA_EVENT_CHANNEL_CONNECTED; KdPrint(("RR18xx [%d,%d]: device connected event received\n", pMvSataAdapter->adapterId, channel)); /* Delete previous timers (if multiple drives connected in the same time */ pAdapter->event_timer_connect = timeout(hptmv_handle_event_connect, pAdapter, 10*hz); } else if (param1 == EVENT_DISCONNECT) { pAdapter->sataEvents[channel] = SATA_EVENT_CHANNEL_DISCONNECTED; KdPrint(("RR18xx [%d,%d]: device disconnected event received \n", pMvSataAdapter->adapterId, channel)); device_change(pAdapter, channel, FALSE); /* Delete previous timers (if multiple drives disconnected in the same time */ /*pAdapter->event_timer_disconnect = timeout(hptmv_handle_event_disconnect, pAdapter, 10*hz); */ /*It is not necessary to wait, handle it directly*/ hptmv_handle_event_disconnect(pAdapter); } else { MV_ERROR("RR18xx: illegal value for param1(%d) at " "connect/disconnect event, host=%d\n", param1, pMvSataAdapter->adapterId ); } } break; case MV_EVENT_TYPE_ADAPTER_ERROR: KdPrint(("RR18xx: DEVICE error event received, pci cause " "reg=%x, don't how to handle this\n", param1)); return MV_TRUE; default: MV_ERROR("RR18xx[%d]: unknown event type (%d)\n", pMvSataAdapter->adapterId, eventType); return MV_FALSE; } return MV_TRUE; } static int hptmv_allocate_edma_queues(IAL_ADAPTER_T *pAdapter) { pAdapter->requestsArrayBaseAddr = (MV_U8 *)contigmalloc(REQUESTS_ARRAY_SIZE, M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0ul); if (pAdapter->requestsArrayBaseAddr == NULL) { MV_ERROR("RR18xx[%d]: Failed to allocate memory for EDMA request" " queues\n", pAdapter->mvSataAdapter.adapterId); return -1; } pAdapter->requestsArrayBaseDmaAddr = fOsPhysicalAddress(pAdapter->requestsArrayBaseAddr); pAdapter->requestsArrayBaseAlignedAddr = pAdapter->requestsArrayBaseAddr; pAdapter->requestsArrayBaseAlignedAddr += MV_EDMA_REQUEST_QUEUE_SIZE; pAdapter->requestsArrayBaseAlignedAddr = (MV_U8 *) (((ULONG_PTR)pAdapter->requestsArrayBaseAlignedAddr) & ~(ULONG_PTR)(MV_EDMA_REQUEST_QUEUE_SIZE - 1)); pAdapter->requestsArrayBaseDmaAlignedAddr = pAdapter->requestsArrayBaseDmaAddr; pAdapter->requestsArrayBaseDmaAlignedAddr += MV_EDMA_REQUEST_QUEUE_SIZE; pAdapter->requestsArrayBaseDmaAlignedAddr &= ~(ULONG_PTR)(MV_EDMA_REQUEST_QUEUE_SIZE - 1); if ((pAdapter->requestsArrayBaseDmaAlignedAddr - pAdapter->requestsArrayBaseDmaAddr) != (pAdapter->requestsArrayBaseAlignedAddr - pAdapter->requestsArrayBaseAddr)) { MV_ERROR("RR18xx[%d]: Error in Request Quueues Alignment\n", pAdapter->mvSataAdapter.adapterId); contigfree(pAdapter->requestsArrayBaseAddr, REQUESTS_ARRAY_SIZE, M_DEVBUF); return -1; } /* response queues */ pAdapter->responsesArrayBaseAddr = (MV_U8 *)contigmalloc(RESPONSES_ARRAY_SIZE, M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0ul); if (pAdapter->responsesArrayBaseAddr == NULL) { MV_ERROR("RR18xx[%d]: Failed to allocate memory for EDMA response" " queues\n", pAdapter->mvSataAdapter.adapterId); contigfree(pAdapter->requestsArrayBaseAddr, RESPONSES_ARRAY_SIZE, M_DEVBUF); return -1; } pAdapter->responsesArrayBaseDmaAddr = fOsPhysicalAddress(pAdapter->responsesArrayBaseAddr); pAdapter->responsesArrayBaseAlignedAddr = pAdapter->responsesArrayBaseAddr; pAdapter->responsesArrayBaseAlignedAddr += MV_EDMA_RESPONSE_QUEUE_SIZE; pAdapter->responsesArrayBaseAlignedAddr = (MV_U8 *) (((ULONG_PTR)pAdapter->responsesArrayBaseAlignedAddr) & ~(ULONG_PTR)(MV_EDMA_RESPONSE_QUEUE_SIZE - 1)); pAdapter->responsesArrayBaseDmaAlignedAddr = pAdapter->responsesArrayBaseDmaAddr; pAdapter->responsesArrayBaseDmaAlignedAddr += MV_EDMA_RESPONSE_QUEUE_SIZE; pAdapter->responsesArrayBaseDmaAlignedAddr &= ~(ULONG_PTR)(MV_EDMA_RESPONSE_QUEUE_SIZE - 1); if ((pAdapter->responsesArrayBaseDmaAlignedAddr - pAdapter->responsesArrayBaseDmaAddr) != (pAdapter->responsesArrayBaseAlignedAddr - pAdapter->responsesArrayBaseAddr)) { MV_ERROR("RR18xx[%d]: Error in Response Queues Alignment\n", pAdapter->mvSataAdapter.adapterId); contigfree(pAdapter->requestsArrayBaseAddr, REQUESTS_ARRAY_SIZE, M_DEVBUF); contigfree(pAdapter->responsesArrayBaseAddr, RESPONSES_ARRAY_SIZE, M_DEVBUF); return -1; } return 0; } static void hptmv_free_edma_queues(IAL_ADAPTER_T *pAdapter) { contigfree(pAdapter->requestsArrayBaseAddr, REQUESTS_ARRAY_SIZE, M_DEVBUF); contigfree(pAdapter->responsesArrayBaseAddr, RESPONSES_ARRAY_SIZE, M_DEVBUF); } static PVOID AllocatePRDTable(IAL_ADAPTER_T *pAdapter) { PVOID ret; if (pAdapter->pFreePRDLink) { KdPrint(("pAdapter->pFreePRDLink:%p\n",pAdapter->pFreePRDLink)); ret = pAdapter->pFreePRDLink; pAdapter->pFreePRDLink = *(void**)ret; return ret; } return NULL; } static void FreePRDTable(IAL_ADAPTER_T *pAdapter, PVOID PRDTable) { *(void**)PRDTable = pAdapter->pFreePRDLink; pAdapter->pFreePRDLink = PRDTable; } extern PVDevice fGetFirstChild(PVDevice pLogical); extern void fResetBootMark(PVDevice pLogical); static void fRegisterVdevice(IAL_ADAPTER_T *pAdapter) { PVDevice pPhysical, pLogical; PVBus pVBus; int i,j; for(i=0;iVDevices[i]); pLogical = pPhysical; while (pLogical->pParent) pLogical = pLogical->pParent; if (pLogical->vf_online==0) { pPhysical->vf_bootmark = pLogical->vf_bootmark = 0; continue; } if (pLogical->VDeviceType==VD_SPARE || pPhysical!=fGetFirstChild(pLogical)) continue; pVBus = &pAdapter->VBus; if(pVBus) { j=0; while(jpVDevice[j]) j++; if(jpVDevice[j] = pLogical; pLogical->pVBus = pVBus; if (j>0 && pLogical->vf_bootmark) { if (pVBus->pVDevice[0]->vf_bootmark) { fResetBootMark(pLogical); } else { do { pVBus->pVDevice[j] = pVBus->pVDevice[j-1]; } while (--j); pVBus->pVDevice[0] = pLogical; } } } } } } PVDevice GetSpareDisk(_VBUS_ARG PVDevice pArray) { IAL_ADAPTER_T *pAdapter = (IAL_ADAPTER_T *)pArray->pVBus->OsExt; LBA_T capacity = LongDiv(pArray->VDeviceCapacity, pArray->u.array.bArnMember-1); LBA_T thiscap, maxcap = MAX_LBA_T; PVDevice pVDevice, pFind = NULL; int i; for(i=0;iVDevices[i]; if(!pVDevice) continue; thiscap = pArray->vf_format_v2? pVDevice->u.disk.dDeRealCapacity : pVDevice->VDeviceCapacity; /* find the smallest usable spare disk */ if (pVDevice->VDeviceType==VD_SPARE && pVDevice->u.disk.df_on_line && thiscap < maxcap && thiscap >= capacity) { maxcap = pVDevice->VDeviceCapacity; pFind = pVDevice; } } return pFind; } /****************************************************************** * IO ATA Command *******************************************************************/ int HPTLIBAPI fDeReadWrite(PDevice pDev, ULONG Lba, UCHAR Cmd, void *tmpBuffer) { return mvReadWrite(pDev->mv, Lba, Cmd, tmpBuffer); } void HPTLIBAPI fDeSelectMode(PDevice pDev, UCHAR NewMode) { MV_SATA_CHANNEL *pSataChannel = pDev->mv; MV_SATA_ADAPTER *pSataAdapter = pSataChannel->mvSataAdapter; MV_U8 channelIndex = pSataChannel->channelNumber; UCHAR mvMode; /* 508x don't use MW-DMA? */ if (NewMode>4 && NewMode<8) NewMode = 4; pDev->bDeModeSetting = NewMode; if (NewMode<=4) mvMode = MV_ATA_TRANSFER_PIO_0 + NewMode; else mvMode = MV_ATA_TRANSFER_UDMA_0 + (NewMode-8); /*To fix 88i8030 bug*/ if (mvMode > MV_ATA_TRANSFER_UDMA_0 && mvMode < MV_ATA_TRANSFER_UDMA_4) mvMode = MV_ATA_TRANSFER_UDMA_0; mvSataDisableChannelDma(pSataAdapter, channelIndex); /* Flush pending commands */ mvSataFlushDmaQueue (pSataAdapter, channelIndex, MV_FLUSH_TYPE_NONE); if (mvStorageDevATASetFeatures(pSataAdapter, channelIndex, MV_ATA_SET_FEATURES_TRANSFER, mvMode, 0, 0, 0) == MV_FALSE) { KdPrint(("channel %d: Set Features failed\n", channelIndex)); } /* Enable EDMA */ if (mvSataEnableChannelDma(pSataAdapter, channelIndex) == MV_FALSE) KdPrint(("Failed to enable DMA, channel=%d", channelIndex)); } int HPTLIBAPI fDeSetTCQ(PDevice pDev, int enable, int depth) { MV_SATA_CHANNEL *pSataChannel = pDev->mv; MV_SATA_ADAPTER *pSataAdapter = pSataChannel->mvSataAdapter; MV_U8 channelIndex = pSataChannel->channelNumber; IAL_ADAPTER_T *pAdapter = pSataAdapter->IALData; MV_CHANNEL *channelInfo = &(pAdapter->mvChannel[channelIndex]); int dmaActive = pSataChannel->queueCommandsEnabled; int ret = 0; if (dmaActive) { mvSataDisableChannelDma(pSataAdapter, channelIndex); mvSataFlushDmaQueue(pSataAdapter,channelIndex,MV_FLUSH_TYPE_CALLBACK); } if (enable) { if (pSataChannel->queuedDMA == MV_EDMA_MODE_NOT_QUEUED && (pSataChannel->identifyDevice[IDEN_SUPPORTED_COMMANDS2] & (0x2))) { UCHAR depth = ((pSataChannel->identifyDevice[IDEN_QUEUE_DEPTH]) & 0x1f) + 1; channelInfo->queueDepth = (depth==32)? 31 : depth; mvSataConfigEdmaMode(pSataAdapter, channelIndex, MV_EDMA_MODE_QUEUED, depth); ret = 1; } } else { if (pSataChannel->queuedDMA != MV_EDMA_MODE_NOT_QUEUED) { channelInfo->queueDepth = 2; mvSataConfigEdmaMode(pSataAdapter, channelIndex, MV_EDMA_MODE_NOT_QUEUED, 0); ret = 1; } } if (dmaActive) mvSataEnableChannelDma(pSataAdapter,channelIndex); return ret; } int HPTLIBAPI fDeSetNCQ(PDevice pDev, int enable, int depth) { return 0; } int HPTLIBAPI fDeSetWriteCache(PDevice pDev, int enable) { MV_SATA_CHANNEL *pSataChannel = pDev->mv; MV_SATA_ADAPTER *pSataAdapter = pSataChannel->mvSataAdapter; MV_U8 channelIndex = pSataChannel->channelNumber; IAL_ADAPTER_T *pAdapter = pSataAdapter->IALData; MV_CHANNEL *channelInfo = &(pAdapter->mvChannel[channelIndex]); int dmaActive = pSataChannel->queueCommandsEnabled; int ret = 0; if (dmaActive) { mvSataDisableChannelDma(pSataAdapter, channelIndex); mvSataFlushDmaQueue(pSataAdapter,channelIndex,MV_FLUSH_TYPE_CALLBACK); } if ((pSataChannel->identifyDevice[82] & (0x20))) { if (enable) { if (mvStorageDevATASetFeatures(pSataAdapter, channelIndex, MV_ATA_SET_FEATURES_ENABLE_WCACHE, 0, 0, 0, 0)) { channelInfo->writeCacheEnabled = MV_TRUE; ret = 1; } } else { if (mvStorageDevATASetFeatures(pSataAdapter, channelIndex, MV_ATA_SET_FEATURES_DISABLE_WCACHE, 0, 0, 0, 0)) { channelInfo->writeCacheEnabled = MV_FALSE; ret = 1; } } } if (dmaActive) mvSataEnableChannelDma(pSataAdapter,channelIndex); return ret; } int HPTLIBAPI fDeSetReadAhead(PDevice pDev, int enable) { MV_SATA_CHANNEL *pSataChannel = pDev->mv; MV_SATA_ADAPTER *pSataAdapter = pSataChannel->mvSataAdapter; MV_U8 channelIndex = pSataChannel->channelNumber; IAL_ADAPTER_T *pAdapter = pSataAdapter->IALData; MV_CHANNEL *channelInfo = &(pAdapter->mvChannel[channelIndex]); int dmaActive = pSataChannel->queueCommandsEnabled; int ret = 0; if (dmaActive) { mvSataDisableChannelDma(pSataAdapter, channelIndex); mvSataFlushDmaQueue(pSataAdapter,channelIndex,MV_FLUSH_TYPE_CALLBACK); } if ((pSataChannel->identifyDevice[82] & (0x40))) { if (enable) { if (mvStorageDevATASetFeatures(pSataAdapter, channelIndex, MV_ATA_SET_FEATURES_ENABLE_RLA, 0, 0, 0, 0)) { channelInfo->readAheadEnabled = MV_TRUE; ret = 1; } } else { if (mvStorageDevATASetFeatures(pSataAdapter, channelIndex, MV_ATA_SET_FEATURES_DISABLE_RLA, 0, 0, 0, 0)) { channelInfo->readAheadEnabled = MV_FALSE; ret = 1; } } } if (dmaActive) mvSataEnableChannelDma(pSataAdapter,channelIndex); return ret; } #ifdef SUPPORT_ARRAY #define IdeRegisterVDevice fCheckArray #else void IdeRegisterVDevice(PDevice pDev) { PVDevice pVDev = Map2pVDevice(pDev); pVDev->VDeviceType = pDev->df_atapi? VD_ATAPI : pDev->df_removable_drive? VD_REMOVABLE : VD_SINGLE_DISK; pVDev->vf_online = 1; pVDev->VDeviceCapacity = pDev->dDeRealCapacity; pVDev->pfnSendCommand = pfnSendCommand[pVDev->VDeviceType]; pVDev->pfnDeviceFailed = pfnDeviceFailed[pVDev->VDeviceType]; } #endif static __inline PBUS_DMAMAP dmamap_get(struct IALAdapter * pAdapter) { PBUS_DMAMAP p = pAdapter->pbus_dmamap_list; if (p) pAdapter->pbus_dmamap_list = p-> next; return p; } static __inline void dmamap_put(PBUS_DMAMAP p) { p->next = p->pAdapter->pbus_dmamap_list; p->pAdapter->pbus_dmamap_list = p; } /*Since mtx not provide the initialize when declare, so we Final init here to initialize the global mtx*/ #if __FreeBSD_version >= 500000 #define override_kernel_driver() static void hpt_init(void *dummy) { override_kernel_driver(); mtx_init(&driver_lock, "hptsleeplock", NULL, MTX_DEF); } SYSINIT(hptinit, SI_SUB_CONFIGURE, SI_ORDER_FIRST, hpt_init, NULL); #endif static int num_adapters = 0; static int init_adapter(IAL_ADAPTER_T *pAdapter) { PVBus _vbus_p = &pAdapter->VBus; MV_SATA_ADAPTER *pMvSataAdapter; int i, channel, rid; PVDevice pVDev; intrmask_t oldspl = lock_driver(); pAdapter->next = 0; if(gIal_Adapter == 0){ gIal_Adapter = pAdapter; pCurAdapter = gIal_Adapter; } else { pCurAdapter->next = pAdapter; pCurAdapter = pAdapter; } pAdapter->outstandingCommands = 0; pMvSataAdapter = &(pAdapter->mvSataAdapter); _vbus_p->OsExt = (void *)pAdapter; pMvSataAdapter->IALData = pAdapter; if (bus_dma_tag_create(bus_get_dma_tag(pAdapter->hpt_dev),/* parent */ 4, /* alignment */ BUS_SPACE_MAXADDR_32BIT+1, /* boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ PAGE_SIZE * (MAX_SG_DESCRIPTORS-1), /* maxsize */ MAX_SG_DESCRIPTORS, /* nsegments */ 0x10000, /* maxsegsize */ BUS_DMA_WAITOK, /* flags */ #if __FreeBSD_version>502000 busdma_lock_mutex, /* lockfunc */ &driver_lock, /* lockfuncarg */ #endif &pAdapter->io_dma_parent /* tag */)) { return ENXIO; } if (hptmv_allocate_edma_queues(pAdapter)) { MV_ERROR("RR18xx: Failed to allocate memory for EDMA queues\n"); unlock_driver(oldspl); return ENOMEM; } /* also map EPROM address */ rid = 0x10; if (!(pAdapter->mem_res = bus_alloc_resource(pAdapter->hpt_dev, SYS_RES_MEMORY, &rid, 0, ~0, MV_SATA_PCI_BAR0_SPACE_SIZE+0x40000, RF_ACTIVE)) || !(pMvSataAdapter->adapterIoBaseAddress = rman_get_virtual(pAdapter->mem_res))) { MV_ERROR("RR18xx: Failed to remap memory space\n"); hptmv_free_edma_queues(pAdapter); unlock_driver(oldspl); return ENXIO; } else { KdPrint(("RR18xx: io base address 0x%p\n", pMvSataAdapter->adapterIoBaseAddress)); } pMvSataAdapter->adapterId = num_adapters++; /* get the revision ID */ pMvSataAdapter->pciConfigRevisionId = pci_read_config(pAdapter->hpt_dev, PCIR_REVID, 1); pMvSataAdapter->pciConfigDeviceId = pci_get_device(pAdapter->hpt_dev); /* init RR18xx */ pMvSataAdapter->intCoalThre[0]= 1; pMvSataAdapter->intCoalThre[1]= 1; pMvSataAdapter->intTimeThre[0] = 1; pMvSataAdapter->intTimeThre[1] = 1; pMvSataAdapter->pciCommand = 0x0107E371; pMvSataAdapter->pciSerrMask = 0xd77fe6ul; pMvSataAdapter->pciInterruptMask = 0xd77fe6ul; pMvSataAdapter->mvSataEventNotify = hptmv_event_notify; if (mvSataInitAdapter(pMvSataAdapter) == MV_FALSE) { MV_ERROR("RR18xx[%d]: core failed to initialize the adapter\n", pMvSataAdapter->adapterId); unregister: bus_release_resource(pAdapter->hpt_dev, SYS_RES_MEMORY, rid, pAdapter->mem_res); hptmv_free_edma_queues(pAdapter); unlock_driver(oldspl); return ENXIO; } pAdapter->ver_601 = pMvSataAdapter->pcbVersion; #ifndef FOR_DEMO set_fail_leds(pMvSataAdapter, 0); #endif /* setup command blocks */ KdPrint(("Allocate command blocks\n")); _vbus_(pFreeCommands) = 0; pAdapter->pCommandBlocks = malloc(sizeof(struct _Command) * MAX_COMMAND_BLOCKS_FOR_EACH_VBUS, M_DEVBUF, M_NOWAIT); KdPrint(("pCommandBlocks:%p\n",pAdapter->pCommandBlocks)); if (!pAdapter->pCommandBlocks) { MV_ERROR("insufficient memory\n"); goto unregister; } for (i=0; ipCommandBlocks[i])); } /*Set up the bus_dmamap*/ pAdapter->pbus_dmamap = (PBUS_DMAMAP)malloc (sizeof(struct _BUS_DMAMAP) * MAX_QUEUE_COMM, M_DEVBUF, M_NOWAIT); if(!pAdapter->pbus_dmamap) { MV_ERROR("insufficient memory\n"); free(pAdapter->pCommandBlocks, M_DEVBUF); goto unregister; } memset((void *)pAdapter->pbus_dmamap, 0, sizeof(struct _BUS_DMAMAP) * MAX_QUEUE_COMM); pAdapter->pbus_dmamap_list = 0; for (i=0; i < MAX_QUEUE_COMM; i++) { PBUS_DMAMAP pmap = &(pAdapter->pbus_dmamap[i]); pmap->pAdapter = pAdapter; dmamap_put(pmap); if(bus_dmamap_create(pAdapter->io_dma_parent, 0, &pmap->dma_map)) { MV_ERROR("Can not allocate dma map\n"); free(pAdapter->pCommandBlocks, M_DEVBUF); free(pAdapter->pbus_dmamap, M_DEVBUF); goto unregister; } + callout_handle_init(&pmap->timeout_ch); } /* setup PRD Tables */ KdPrint(("Allocate PRD Tables\n")); pAdapter->pFreePRDLink = 0; pAdapter->prdTableAddr = (PUCHAR)contigmalloc( (PRD_ENTRIES_SIZE*PRD_TABLES_FOR_VBUS + 32), M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0ul); KdPrint(("prdTableAddr:%p\n",pAdapter->prdTableAddr)); if (!pAdapter->prdTableAddr) { MV_ERROR("insufficient PRD Tables\n"); goto unregister; } pAdapter->prdTableAlignedAddr = (PUCHAR)(((ULONG_PTR)pAdapter->prdTableAddr + 0x1f) & ~(ULONG_PTR)0x1fL); { PUCHAR PRDTable = pAdapter->prdTableAlignedAddr; for (i=0; ipFreePRDLink=%p\n",i,pAdapter->pFreePRDLink)); */ FreePRDTable(pAdapter, PRDTable); PRDTable += PRD_ENTRIES_SIZE; } } /* enable the adapter interrupts */ /* configure and start the connected channels*/ for (channel = 0; channel < MV_SATA_CHANNELS_NUM; channel++) { pAdapter->mvChannel[channel].online = MV_FALSE; if (mvSataIsStorageDeviceConnected(pMvSataAdapter, channel) == MV_TRUE) { KdPrint(("RR18xx[%d]: channel %d is connected\n", pMvSataAdapter->adapterId, channel)); if (hptmv_init_channel(pAdapter, channel) == 0) { if (mvSataConfigureChannel(pMvSataAdapter, channel) == MV_FALSE) { MV_ERROR("RR18xx[%d]: Failed to configure channel" " %d\n",pMvSataAdapter->adapterId, channel); hptmv_free_channel(pAdapter, channel); } else { if (start_channel(pAdapter, channel)) { MV_ERROR("RR18xx[%d]: Failed to start channel," " channel=%d\n",pMvSataAdapter->adapterId, channel); hptmv_free_channel(pAdapter, channel); } pAdapter->mvChannel[channel].online = MV_TRUE; /* mvSataChannelSetEdmaLoopBackMode(pMvSataAdapter, channel, MV_TRUE);*/ } } } KdPrint(("pAdapter->mvChannel[channel].online:%x, channel:%d\n", pAdapter->mvChannel[channel].online, channel)); } #ifdef SUPPORT_ARRAY for(i = MAX_ARRAY_DEVICE - 1; i >= 0; i--) { pVDev = ArrayTables(i); mArFreeArrayTable(pVDev); } #endif KdPrint(("Initialize Devices\n")); for (channel = 0; channel < MV_SATA_CHANNELS_NUM; channel++) { MV_SATA_CHANNEL *pMvSataChannel = pMvSataAdapter->sataChannel[channel]; if (pMvSataChannel) { init_vdev_params(pAdapter, channel); IdeRegisterVDevice(&pAdapter->VDevices[channel].u.disk); } } #ifdef SUPPORT_ARRAY CheckArrayCritical(_VBUS_P0); #endif _vbus_p->nInstances = 1; fRegisterVdevice(pAdapter); for (channel=0;channelpVDevice[channel]; if (pVDev && pVDev->vf_online) fCheckBootable(pVDev); } #if defined(SUPPORT_ARRAY) && defined(_RAID5N_) init_raid5_memory(_VBUS_P0); _vbus_(r5).enable_write_back = 1; printf("RR18xx: RAID5 write-back %s\n", _vbus_(r5).enable_write_back? "enabled" : "disabled"); #endif mvSataUnmaskAdapterInterrupt(pMvSataAdapter); unlock_driver(oldspl); return 0; } int MvSataResetChannel(MV_SATA_ADAPTER *pMvSataAdapter, MV_U8 channel) { IAL_ADAPTER_T *pAdapter = (IAL_ADAPTER_T *)pMvSataAdapter->IALData; mvSataDisableChannelDma(pMvSataAdapter, channel); /* Flush pending commands */ mvSataFlushDmaQueue (pMvSataAdapter, channel, MV_FLUSH_TYPE_CALLBACK); /* Software reset channel */ if (mvStorageDevATASoftResetDevice(pMvSataAdapter, channel) == MV_FALSE) { MV_ERROR("RR18xx [%d,%d]: failed to perform Software reset\n", pMvSataAdapter->adapterId, channel); hptmv_free_channel(pAdapter, channel); return -1; } /* Hardware reset channel */ if (mvSataChannelHardReset(pMvSataAdapter, channel)== MV_FALSE) { MV_ERROR("RR18xx [%d,%d] Failed to Hard reser the SATA channel\n", pMvSataAdapter->adapterId, channel); hptmv_free_channel(pAdapter, channel); return -1; } if (mvSataIsStorageDeviceConnected(pMvSataAdapter, channel) == MV_FALSE) { MV_ERROR("RR18xx [%d,%d] Failed to Connect Device\n", pMvSataAdapter->adapterId, channel); hptmv_free_channel(pAdapter, channel); return -1; }else { MV_ERROR("channel %d: perform recalibrate command", channel); if (!mvStorageDevATAExecuteNonUDMACommand(pMvSataAdapter, channel, MV_NON_UDMA_PROTOCOL_NON_DATA, MV_FALSE, NULL, /* pBuffer*/ 0, /* count */ 0, /*features*/ /* sectorCount */ 0, 0, /* lbaLow */ 0, /* lbaMid */ /* lbaHigh */ 0, 0, /* device */ /* command */ 0x10)) MV_ERROR("channel %d: recalibrate failed", channel); /* Set transfer mode */ if((mvStorageDevATASetFeatures(pMvSataAdapter, channel, MV_ATA_SET_FEATURES_TRANSFER, MV_ATA_TRANSFER_PIO_SLOW, 0, 0, 0) == MV_FALSE) || (mvStorageDevATASetFeatures(pMvSataAdapter, channel, MV_ATA_SET_FEATURES_TRANSFER, pAdapter->mvChannel[channel].maxPioModeSupported, 0, 0, 0) == MV_FALSE) || (mvStorageDevATASetFeatures(pMvSataAdapter, channel, MV_ATA_SET_FEATURES_TRANSFER, pAdapter->mvChannel[channel].maxUltraDmaModeSupported, 0, 0, 0) == MV_FALSE) ) { MV_ERROR("channel %d: Set Features failed", channel); hptmv_free_channel(pAdapter, channel); return -1; } /* Enable EDMA */ if (mvSataEnableChannelDma(pMvSataAdapter, channel) == MV_FALSE) { MV_ERROR("Failed to enable DMA, channel=%d", channel); hptmv_free_channel(pAdapter, channel); return -1; } } return 0; } static int fResetActiveCommands(PVBus _vbus_p) { MV_SATA_ADAPTER *pMvSataAdapter = &((IAL_ADAPTER_T *)_vbus_p->OsExt)->mvSataAdapter; MV_U8 channel; for (channel=0;channel< MV_SATA_CHANNELS_NUM;channel++) { if (pMvSataAdapter->sataChannel[channel] && pMvSataAdapter->sataChannel[channel]->outstandingCommands) MvSataResetChannel(pMvSataAdapter,channel); } return 0; } void fCompleteAllCommandsSynchronously(PVBus _vbus_p) { UINT cont; ULONG ticks = 0; MV_U8 channel; MV_SATA_ADAPTER *pMvSataAdapter = &((IAL_ADAPTER_T *)_vbus_p->OsExt)->mvSataAdapter; MV_SATA_CHANNEL *pMvSataChannel; do { check_cmds: cont = 0; CheckPendingCall(_VBUS_P0); #ifdef _RAID5N_ dataxfer_poll(); xor_poll(); #endif for (channel=0;channel< MV_SATA_CHANNELS_NUM;channel++) { pMvSataChannel = pMvSataAdapter->sataChannel[channel]; if (pMvSataChannel && pMvSataChannel->outstandingCommands) { while (pMvSataChannel->outstandingCommands) { if (!mvSataInterruptServiceRoutine(pMvSataAdapter)) { StallExec(1000); if (ticks++ > 3000) { MvSataResetChannel(pMvSataAdapter,channel); goto check_cmds; } } else ticks = 0; } cont = 1; } } } while (cont); } void fResetVBus(_VBUS_ARG0) { KdPrint(("fMvResetBus(%p)", _vbus_p)); /* some commands may already finished. */ CheckPendingCall(_VBUS_P0); fResetActiveCommands(_vbus_p); /* * the other pending commands may still be finished successfully. */ fCompleteAllCommandsSynchronously(_vbus_p); /* Now there should be no pending commands. No more action needed. */ CheckIdleCall(_VBUS_P0); KdPrint(("fMvResetBus() done")); } /*No rescan function*/ void fRescanAllDevice(_VBUS_ARG0) { } static MV_BOOLEAN CommandCompletionCB(MV_SATA_ADAPTER *pMvSataAdapter, MV_U8 channelNum, MV_COMPLETION_TYPE comp_type, MV_VOID_PTR commandId, MV_U16 responseFlags, MV_U32 timeStamp, MV_STORAGE_DEVICE_REGISTERS *registerStruct) { PCommand pCmd = (PCommand) commandId; _VBUS_INST(pCmd->pVDevice->pVBus) if (pCmd->uScratch.sata_param.prdAddr) FreePRDTable(pMvSataAdapter->IALData,pCmd->uScratch.sata_param.prdAddr); switch (comp_type) { case MV_COMPLETION_TYPE_NORMAL: pCmd->Result = RETURN_SUCCESS; break; case MV_COMPLETION_TYPE_ABORT: pCmd->Result = RETURN_BUS_RESET; break; case MV_COMPLETION_TYPE_ERROR: MV_ERROR("IAL: COMPLETION ERROR, adapter %d, channel %d, flags=%x\n", pMvSataAdapter->adapterId, channelNum, responseFlags); if (responseFlags & 4) { MV_ERROR("ATA regs: error %x, sector count %x, LBA low %x, LBA mid %x," " LBA high %x, device %x, status %x\n", registerStruct->errorRegister, registerStruct->sectorCountRegister, registerStruct->lbaLowRegister, registerStruct->lbaMidRegister, registerStruct->lbaHighRegister, registerStruct->deviceRegister, registerStruct->statusRegister); } /*We can't do handleEdmaError directly here, because CommandCompletionCB is called by * mv's ISR, if we retry the command, than the internel data structure may be destroyed*/ pCmd->uScratch.sata_param.responseFlags = responseFlags; pCmd->uScratch.sata_param.bIdeStatus = registerStruct->statusRegister; pCmd->uScratch.sata_param.errorRegister = registerStruct->errorRegister; pCmd->pVDevice->u.disk.QueueLength--; CallAfterReturn(_VBUS_P (DPC_PROC)handleEdmaError,pCmd); return TRUE; default: MV_ERROR(" Unknown completion type (%d)\n", comp_type); return MV_FALSE; } if (pCmd->uCmd.Ide.Command == IDE_COMMAND_VERIFY && pCmd->uScratch.sata_param.cmd_priv > 1) { pCmd->uScratch.sata_param.cmd_priv --; return TRUE; } pCmd->pVDevice->u.disk.QueueLength--; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); return TRUE; } void fDeviceSendCommand(_VBUS_ARG PCommand pCmd) { MV_SATA_EDMA_PRD_ENTRY *pPRDTable = 0; MV_SATA_ADAPTER *pMvSataAdapter; MV_SATA_CHANNEL *pMvSataChannel; PVDevice pVDevice = pCmd->pVDevice; PDevice pDevice = &pVDevice->u.disk; LBA_T Lba = pCmd->uCmd.Ide.Lba; USHORT nSector = pCmd->uCmd.Ide.nSectors; MV_QUEUE_COMMAND_RESULT result; MV_QUEUE_COMMAND_INFO commandInfo; MV_UDMA_COMMAND_PARAMS *pUdmaParams = &commandInfo.commandParams.udmaCommand; MV_NONE_UDMA_COMMAND_PARAMS *pNoUdmaParams = &commandInfo.commandParams.NoneUdmaCommand; MV_BOOLEAN is48bit; MV_U8 channel; int i=0; DECLARE_BUFFER(FPSCAT_GATH, tmpSg); if (!pDevice->df_on_line) { MV_ERROR("Device is offline"); pCmd->Result = RETURN_BAD_DEVICE; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); return; } pDevice->HeadPosition = pCmd->uCmd.Ide.Lba + pCmd->uCmd.Ide.nSectors; pMvSataChannel = pDevice->mv; pMvSataAdapter = pMvSataChannel->mvSataAdapter; channel = pMvSataChannel->channelNumber; /* old RAID0 has hidden lba. Remember to clear dDeHiddenLba when delete array! */ Lba += pDevice->dDeHiddenLba; /* check LBA */ if (Lba+nSector-1 > pDevice->dDeRealCapacity) { pCmd->Result = RETURN_INVALID_REQUEST; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); return; } /* * always use 48bit LBA if drive supports it. * Some Seagate drives report error if you use a 28-bit command * to access sector 0xfffffff. */ is48bit = pMvSataChannel->lba48Address; switch (pCmd->uCmd.Ide.Command) { case IDE_COMMAND_READ: case IDE_COMMAND_WRITE: if (pDevice->bDeModeSetting<8) goto pio; commandInfo.type = MV_QUEUED_COMMAND_TYPE_UDMA; pUdmaParams->isEXT = is48bit; pUdmaParams->numOfSectors = nSector; pUdmaParams->lowLBAAddress = Lba; pUdmaParams->highLBAAddress = 0; pUdmaParams->prdHighAddr = 0; pUdmaParams->callBack = CommandCompletionCB; pUdmaParams->commandId = (MV_VOID_PTR )pCmd; if(pCmd->uCmd.Ide.Command == IDE_COMMAND_READ) pUdmaParams->readWrite = MV_UDMA_TYPE_READ; else pUdmaParams->readWrite = MV_UDMA_TYPE_WRITE; if (pCmd->pSgTable && pCmd->cf_physical_sg) { FPSCAT_GATH sg1=tmpSg, sg2=pCmd->pSgTable; do { *sg1++=*sg2; } while ((sg2++->wSgFlag & SG_FLAG_EOT)==0); } else { if (!pCmd->pfnBuildSgl || !pCmd->pfnBuildSgl(_VBUS_P pCmd, tmpSg, 0)) { pio: mvSataDisableChannelDma(pMvSataAdapter, channel); mvSataFlushDmaQueue(pMvSataAdapter, channel, MV_FLUSH_TYPE_CALLBACK); if (pCmd->pSgTable && pCmd->cf_physical_sg==0) { FPSCAT_GATH sg1=tmpSg, sg2=pCmd->pSgTable; do { *sg1++=*sg2; } while ((sg2++->wSgFlag & SG_FLAG_EOT)==0); } else { if (!pCmd->pfnBuildSgl || !pCmd->pfnBuildSgl(_VBUS_P pCmd, tmpSg, 1)) { pCmd->Result = RETURN_NEED_LOGICAL_SG; goto finish_cmd; } } do { ULONG size = tmpSg->wSgSize? tmpSg->wSgSize : 0x10000; ULONG_PTR addr = tmpSg->dSgAddress; if (size & 0x1ff) { pCmd->Result = RETURN_INVALID_REQUEST; goto finish_cmd; } if (mvStorageDevATAExecuteNonUDMACommand(pMvSataAdapter, channel, (pCmd->cf_data_out)?MV_NON_UDMA_PROTOCOL_PIO_DATA_OUT:MV_NON_UDMA_PROTOCOL_PIO_DATA_IN, is48bit, (MV_U16_PTR)addr, size >> 1, /* count */ 0, /* features N/A */ (MV_U16)(size>>9), /*sector count*/ (MV_U16)( (is48bit? (MV_U16)((Lba >> 16) & 0xFF00) : 0 ) | (UCHAR)(Lba & 0xFF) ), /*lbalow*/ (MV_U16)((Lba >> 8) & 0xFF), /* lbaMid */ (MV_U16)((Lba >> 16) & 0xFF),/* lbaHigh */ (MV_U8)(0x40 | (is48bit ? 0 : (UCHAR)(Lba >> 24) & 0xFF )),/* device */ (MV_U8)(is48bit ? (pCmd->cf_data_in?IDE_COMMAND_READ_EXT:IDE_COMMAND_WRITE_EXT):pCmd->uCmd.Ide.Command) )==MV_FALSE) { pCmd->Result = RETURN_IDE_ERROR; goto finish_cmd; } Lba += size>>9; if(Lba & 0xF0000000) is48bit = MV_TRUE; } while ((tmpSg++->wSgFlag & SG_FLAG_EOT)==0); pCmd->Result = RETURN_SUCCESS; finish_cmd: mvSataEnableChannelDma(pMvSataAdapter,channel); CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); return; } } pPRDTable = (MV_SATA_EDMA_PRD_ENTRY *) AllocatePRDTable(pMvSataAdapter->IALData); KdPrint(("pPRDTable:%p\n",pPRDTable)); if (!pPRDTable) { pCmd->Result = RETURN_DEVICE_BUSY; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); HPT_ASSERT(0); return; } do{ pPRDTable[i].highBaseAddr = (sizeof(tmpSg->dSgAddress)>4 ? (MV_U32)(tmpSg->dSgAddress>>32) : 0); pPRDTable[i].flags = (MV_U16)tmpSg->wSgFlag; pPRDTable[i].byteCount = (MV_U16)tmpSg->wSgSize; pPRDTable[i].lowBaseAddr = (MV_U32)tmpSg->dSgAddress; pPRDTable[i].reserved = 0; i++; }while((tmpSg++->wSgFlag & SG_FLAG_EOT)==0); pUdmaParams->prdLowAddr = (ULONG)fOsPhysicalAddress(pPRDTable); if ((pUdmaParams->numOfSectors == 256) && (pMvSataChannel->lba48Address == MV_FALSE)) { pUdmaParams->numOfSectors = 0; } pCmd->uScratch.sata_param.prdAddr = (PVOID)pPRDTable; result = mvSataQueueCommand(pMvSataAdapter, channel, &commandInfo); if (result != MV_QUEUE_COMMAND_RESULT_OK) { queue_failed: switch (result) { case MV_QUEUE_COMMAND_RESULT_BAD_LBA_ADDRESS: MV_ERROR("IAL Error: Edma Queue command failed. Bad LBA " "LBA[31:0](0x%08x)\n", pUdmaParams->lowLBAAddress); pCmd->Result = RETURN_IDE_ERROR; break; case MV_QUEUE_COMMAND_RESULT_QUEUED_MODE_DISABLED: MV_ERROR("IAL Error: Edma Queue command failed. EDMA" " disabled adapter %d channel %d\n", pMvSataAdapter->adapterId, channel); mvSataEnableChannelDma(pMvSataAdapter,channel); pCmd->Result = RETURN_IDE_ERROR; break; case MV_QUEUE_COMMAND_RESULT_FULL: MV_ERROR("IAL Error: Edma Queue command failed. Queue is" " Full adapter %d channel %d\n", pMvSataAdapter->adapterId, channel); pCmd->Result = RETURN_DEVICE_BUSY; break; case MV_QUEUE_COMMAND_RESULT_BAD_PARAMS: MV_ERROR("IAL Error: Edma Queue command failed. (Bad " "Params), pMvSataAdapter: %p, pSataChannel: %p.\n", pMvSataAdapter, pMvSataAdapter->sataChannel[channel]); pCmd->Result = RETURN_IDE_ERROR; break; default: MV_ERROR("IAL Error: Bad result value (%d) from queue" " command\n", result); pCmd->Result = RETURN_IDE_ERROR; } if(pPRDTable) FreePRDTable(pMvSataAdapter->IALData,pPRDTable); CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); } pDevice->QueueLength++; return; case IDE_COMMAND_VERIFY: commandInfo.type = MV_QUEUED_COMMAND_TYPE_NONE_UDMA; pNoUdmaParams->bufPtr = NULL; pNoUdmaParams->callBack = CommandCompletionCB; pNoUdmaParams->commandId = (MV_VOID_PTR)pCmd; pNoUdmaParams->count = 0; pNoUdmaParams->features = 0; pNoUdmaParams->protocolType = MV_NON_UDMA_PROTOCOL_NON_DATA; pCmd->uScratch.sata_param.cmd_priv = 1; if (pMvSataChannel->lba48Address == MV_TRUE){ pNoUdmaParams->command = MV_ATA_COMMAND_READ_VERIFY_SECTORS_EXT; pNoUdmaParams->isEXT = MV_TRUE; pNoUdmaParams->lbaHigh = (MV_U16)((Lba & 0xff0000) >> 16); pNoUdmaParams->lbaMid = (MV_U16)((Lba & 0xff00) >> 8); pNoUdmaParams->lbaLow = (MV_U16)(((Lba & 0xff000000) >> 16)| (Lba & 0xff)); pNoUdmaParams->sectorCount = nSector; pNoUdmaParams->device = 0x40; result = mvSataQueueCommand(pMvSataAdapter, channel, &commandInfo); if (result != MV_QUEUE_COMMAND_RESULT_OK){ goto queue_failed; } return; } else{ pNoUdmaParams->command = MV_ATA_COMMAND_READ_VERIFY_SECTORS; pNoUdmaParams->isEXT = MV_FALSE; pNoUdmaParams->lbaHigh = (MV_U16)((Lba & 0xff0000) >> 16); pNoUdmaParams->lbaMid = (MV_U16)((Lba & 0xff00) >> 8); pNoUdmaParams->lbaLow = (MV_U16)(Lba & 0xff); pNoUdmaParams->sectorCount = 0xff & nSector; pNoUdmaParams->device = (MV_U8)(0x40 | ((Lba & 0xf000000) >> 24)); pNoUdmaParams->callBack = CommandCompletionCB; result = mvSataQueueCommand(pMvSataAdapter, channel, &commandInfo); /*FIXME: how about the commands already queued? but marvel also forgets to consider this*/ if (result != MV_QUEUE_COMMAND_RESULT_OK){ goto queue_failed; } } break; default: pCmd->Result = RETURN_INVALID_REQUEST; CallAfterReturn(_VBUS_P (DPC_PROC)pCmd->pfnCompletion, pCmd); break; } } /********************************************************** * * Probe the hostadapter. * **********************************************************/ static int hpt_probe(device_t dev) { if ((pci_get_vendor(dev) == MV_SATA_VENDOR_ID) && (pci_get_device(dev) == MV_SATA_DEVICE_ID_5081 #ifdef FOR_DEMO || pci_get_device(dev) == MV_SATA_DEVICE_ID_5080 #endif )) { KdPrintI((CONTROLLER_NAME " found\n")); device_set_desc(dev, CONTROLLER_NAME); return 0; } else return(ENXIO); } /*********************************************************** * * Auto configuration: attach and init a host adapter. * ***********************************************************/ static int hpt_attach(device_t dev) { IAL_ADAPTER_T * pAdapter = device_get_softc(dev); int rid; union ccb *ccb; struct cam_devq *devq; struct cam_sim *hpt_vsim; printf("%s Version %s \n", DRIVER_NAME, DRIVER_VERSION); if (!pAdapter) { pAdapter = (IAL_ADAPTER_T *)malloc(sizeof (IAL_ADAPTER_T), M_DEVBUF, M_NOWAIT); #if __FreeBSD_version > 410000 device_set_softc(dev, (void *)pAdapter); #else device_set_driver(dev, (driver_t *)pAdapter); #endif } if (!pAdapter) return (ENOMEM); bzero(pAdapter, sizeof(IAL_ADAPTER_T)); pAdapter->hpt_dev = dev; rid = init_adapter(pAdapter); if (rid) return rid; rid = 0; if ((pAdapter->hpt_irq = bus_alloc_resource(pAdapter->hpt_dev, SYS_RES_IRQ, &rid, 0, ~0ul, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) { hpt_printk(("can't allocate interrupt\n")); return(ENXIO); } #if __FreeBSD_version <700000 if (bus_setup_intr(pAdapter->hpt_dev, pAdapter->hpt_irq, INTR_TYPE_CAM, hpt_intr, pAdapter, &pAdapter->hpt_intr)) #else if (bus_setup_intr(pAdapter->hpt_dev, pAdapter->hpt_irq, INTR_TYPE_CAM, NULL, hpt_intr, pAdapter, &pAdapter->hpt_intr)) #endif { hpt_printk(("can't set up interrupt\n")); free(pAdapter, M_DEVBUF); return(ENXIO); } if((ccb = (union ccb *)malloc(sizeof(*ccb), M_DEVBUF, M_WAITOK)) != (union ccb*)NULL) { bzero(ccb, sizeof(*ccb)); ccb->ccb_h.pinfo.priority = 1; ccb->ccb_h.pinfo.index = CAM_UNQUEUED_INDEX; } else { return ENOMEM; } /* * Create the device queue for our SIM(s). */ if((devq = cam_simq_alloc(8/*MAX_QUEUE_COMM*/)) == NULL) { KdPrint(("ENXIO\n")); return ENOMEM; } /* * Construct our SIM entry */ #if __FreeBSD_version <700000 hpt_vsim = cam_sim_alloc(hpt_action, hpt_poll, __str(PROC_DIR_NAME), pAdapter, device_get_unit(pAdapter->hpt_dev), 1, 8, devq); #else hpt_vsim = cam_sim_alloc(hpt_action, hpt_poll, __str(PROC_DIR_NAME), pAdapter, device_get_unit(pAdapter->hpt_dev), &Giant, 1, 8, devq); #endif if (hpt_vsim == NULL) { cam_simq_free(devq); return ENOMEM; } #if __FreeBSD_version <700000 if (xpt_bus_register(hpt_vsim, 0) != CAM_SUCCESS) #else if (xpt_bus_register(hpt_vsim, dev, 0) != CAM_SUCCESS) #endif { cam_sim_free(hpt_vsim, /*free devq*/ TRUE); hpt_vsim = NULL; return ENXIO; } if(xpt_create_path(&pAdapter->path, /*periph */ NULL, cam_sim_path(hpt_vsim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(hpt_vsim)); cam_sim_free(hpt_vsim, /*free_devq*/TRUE); hpt_vsim = NULL; return ENXIO; } xpt_setup_ccb(&(ccb->ccb_h), pAdapter->path, /*priority*/5); ccb->ccb_h.func_code = XPT_SASYNC_CB; ccb->csa.event_enable = AC_LOST_DEVICE; ccb->csa.callback = hpt_async; ccb->csa.callback_arg = hpt_vsim; xpt_action((union ccb *)ccb); free(ccb, M_DEVBUF); if (device_get_unit(dev) == 0) { /* Start the work thread. XXX */ launch_worker_thread(); } return 0; } static int hpt_detach(device_t dev) { return (EBUSY); } /*************************************************************** * The poll function is used to simulate the interrupt when * the interrupt subsystem is not functioning. * ***************************************************************/ static void hpt_poll(struct cam_sim *sim) { hpt_intr((void *)cam_sim_softc(sim)); } /**************************************************************** * Name: hpt_intr * Description: Interrupt handler. ****************************************************************/ static void hpt_intr(void *arg) { IAL_ADAPTER_T *pAdapter = (IAL_ADAPTER_T *)arg; intrmask_t oldspl = lock_driver(); /* KdPrintI(("----- Entering Isr() -----\n")); */ if (mvSataInterruptServiceRoutine(&pAdapter->mvSataAdapter) == MV_TRUE) { _VBUS_INST(&pAdapter->VBus) CheckPendingCall(_VBUS_P0); } /* KdPrintI(("----- Leaving Isr() -----\n")); */ unlock_driver(oldspl); } /********************************************************** * Asynchronous Events *********************************************************/ #if (!defined(UNREFERENCED_PARAMETER)) #define UNREFERENCED_PARAMETER(x) (void)(x) #endif static void hpt_async(void * callback_arg, u_int32_t code, struct cam_path * path, void * arg) { /* debug XXXX */ panic("Here"); UNREFERENCED_PARAMETER(callback_arg); UNREFERENCED_PARAMETER(code); UNREFERENCED_PARAMETER(path); UNREFERENCED_PARAMETER(arg); } static void FlushAdapter(IAL_ADAPTER_T *pAdapter) { int i; hpt_printk(("flush all devices\n")); /* flush all devices */ for (i=0; iVBus.pVDevice[i]; if(pVDev) fFlushVDev(pVDev); } } static int hpt_shutdown(device_t dev) { IAL_ADAPTER_T *pAdapter; pAdapter = device_get_softc(dev); if (pAdapter == NULL) return (EINVAL); EVENTHANDLER_DEREGISTER(shutdown_final, pAdapter->eh); FlushAdapter(pAdapter); /* give the flush some time to happen, *otherwise "shutdown -p now" will make file system corrupted */ DELAY(1000 * 1000 * 5); return 0; } void Check_Idle_Call(IAL_ADAPTER_T *pAdapter) { _VBUS_INST(&pAdapter->VBus) if (mWaitingForIdle(_VBUS_P0)) { CheckIdleCall(_VBUS_P0); #ifdef SUPPORT_ARRAY { int i; PVDevice pArray; for(i = 0; i < MAX_ARRAY_PER_VBUS; i++){ if ((pArray=ArrayTables(i))->u.array.dArStamp==0) continue; else if (pArray->u.array.rf_auto_rebuild) { KdPrint(("auto rebuild.\n")); pArray->u.array.rf_auto_rebuild = 0; hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapter, pArray, DUPLICATE); } } } #endif } /* launch the awaiting commands blocked by mWaitingForIdle */ while(pAdapter->pending_Q!= NULL) { _VBUS_INST(&pAdapter->VBus) union ccb *ccb = (union ccb *)pAdapter->pending_Q->ccb_h.ccb_ccb_ptr; hpt_free_ccb(&pAdapter->pending_Q, ccb); CallAfterReturn(_VBUS_P (DPC_PROC)OsSendCommand, ccb); } } static void ccb_done(union ccb *ccb) { PBUS_DMAMAP pmap = (PBUS_DMAMAP)ccb->ccb_adapter; IAL_ADAPTER_T * pAdapter = pmap->pAdapter; KdPrintI(("ccb_done: ccb %p status %x\n", ccb, ccb->ccb_h.status)); dmamap_put(pmap); xpt_done(ccb); pAdapter->outstandingCommands--; if (pAdapter->outstandingCommands == 0) { if(DPC_Request_Nums == 0) Check_Idle_Call(pAdapter); } } /**************************************************************** * Name: hpt_action * Description: Process a queued command from the CAM layer. * Parameters: sim - Pointer to SIM object * ccb - Pointer to SCSI command structure. ****************************************************************/ void hpt_action(struct cam_sim *sim, union ccb *ccb) { intrmask_t oldspl; IAL_ADAPTER_T * pAdapter = (IAL_ADAPTER_T *) cam_sim_softc(sim); PBUS_DMAMAP pmap; _VBUS_INST(&pAdapter->VBus) CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("hpt_action\n")); KdPrint(("hpt_action(%lx,%lx{%x})\n", (u_long)sim, (u_long)ccb, ccb->ccb_h.func_code)); switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: /* Execute the requested I/O operation */ { /* ccb->ccb_h.path_id is not our bus id - don't check it */ if (ccb->ccb_h.target_lun) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return; } if (ccb->ccb_h.target_id >= MAX_VDEVICE_PER_VBUS || pAdapter->VBus.pVDevice[ccb->ccb_h.target_id]==0) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return; } oldspl = lock_driver(); if (pAdapter->outstandingCommands==0 && DPC_Request_Nums==0) Check_Idle_Call(pAdapter); pmap = dmamap_get(pAdapter); HPT_ASSERT(pmap); ccb->ccb_adapter = pmap; memset((void *)pmap->psg, 0, sizeof(pmap->psg)); if (mWaitingForIdle(_VBUS_P0)) hpt_queue_ccb(&pAdapter->pending_Q, ccb); else OsSendCommand(_VBUS_P ccb); unlock_driver(oldspl); /* KdPrint(("leave scsiio\n")); */ break; } case XPT_RESET_BUS: KdPrint(("reset bus\n")); oldspl = lock_driver(); fResetVBus(_VBUS_P0); unlock_driver(oldspl); xpt_done(ccb); break; case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_ABORT: /* Abort the specified CCB */ case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_GET_TRAN_SETTINGS: case XPT_SET_TRAN_SETTINGS: /* XXX Implement */ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; case XPT_CALC_GEOMETRY: #if __FreeBSD_version >= 500000 cam_calc_geometry(&ccb->ccg, 1); #else { struct ccb_calc_geometry *ccg; u_int32_t size_mb; u_int32_t secs_per_cylinder; ccg = &ccb->ccg; size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size); if (size_mb > 1024 ) { ccg->heads = 255; ccg->secs_per_track = 63; } else { ccg->heads = 64; ccg->secs_per_track = 32; } secs_per_cylinder = ccg->heads * ccg->secs_per_track; ccg->cylinders = ccg->volume_size / secs_per_cylinder; ccb->ccb_h.status = CAM_REQ_CMP; } #endif xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE; cpi->target_sprt = 0; /* Not necessary to reset bus */ cpi->hba_misc = PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = MAX_VDEVICE_PER_VBUS; cpi->max_lun = 0; cpi->initiator_id = MAX_VDEVICE_PER_VBUS; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "HPT ", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: KdPrint(("invalid cmd\n")); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } /* KdPrint(("leave hpt_action..............\n")); */ } /* shall be called at lock_driver() */ static void hpt_queue_ccb(union ccb **ccb_Q, union ccb *ccb) { if(*ccb_Q == NULL) ccb->ccb_h.ccb_ccb_ptr = ccb; else { ccb->ccb_h.ccb_ccb_ptr = (*ccb_Q)->ccb_h.ccb_ccb_ptr; (*ccb_Q)->ccb_h.ccb_ccb_ptr = (char *)ccb; } *ccb_Q = ccb; } /* shall be called at lock_driver() */ static void hpt_free_ccb(union ccb **ccb_Q, union ccb *ccb) { union ccb *TempCCB; TempCCB = *ccb_Q; if(ccb->ccb_h.ccb_ccb_ptr == ccb) /*it means SCpnt is the last one in CURRCMDs*/ *ccb_Q = NULL; else { while(TempCCB->ccb_h.ccb_ccb_ptr != (char *)ccb) TempCCB = (union ccb *)TempCCB->ccb_h.ccb_ccb_ptr; TempCCB->ccb_h.ccb_ccb_ptr = ccb->ccb_h.ccb_ccb_ptr; if(*ccb_Q == ccb) *ccb_Q = TempCCB; } } #ifdef SUPPORT_ARRAY /*************************************************************************** * Function: hpt_worker_thread * Description: Do background rebuilding. Execute in kernel thread context. * Returns: None ***************************************************************************/ static void hpt_worker_thread(void) { intrmask_t oldspl; for(;;) { while (DpcQueue_First!=DpcQueue_Last) { ST_HPT_DPC p; oldspl = lock_driver(); p = DpcQueue[DpcQueue_First]; DpcQueue_First++; DpcQueue_First %= MAX_DPC; DPC_Request_Nums++; unlock_driver(oldspl); p.dpc(p.pAdapter, p.arg, p.flags); oldspl = lock_driver(); DPC_Request_Nums--; /* since we may have prevented Check_Idle_Call, do it here */ if (DPC_Request_Nums==0) { if (p.pAdapter->outstandingCommands == 0) { _VBUS_INST(&p.pAdapter->VBus); Check_Idle_Call(p.pAdapter); CheckPendingCall(_VBUS_P0); } } unlock_driver(oldspl); /*Schedule out*/ #if (__FreeBSD_version < 500000) YIELD_THREAD; #else #if (__FreeBSD_version > 700033) pause("sched", 1); #else tsleep((caddr_t)hpt_worker_thread, PPAUSE, "sched", 1); #endif #endif if (SIGISMEMBER(curproc->p_siglist, SIGSTOP)) { /* abort rebuilding process. */ IAL_ADAPTER_T *pAdapter; PVDevice pArray; PVBus _vbus_p; int i; pAdapter = gIal_Adapter; while(pAdapter != 0){ _vbus_p = &pAdapter->VBus; for (i=0;iu.array.dArStamp==0) continue; else if (pArray->u.array.rf_rebuilding || pArray->u.array.rf_verifying || pArray->u.array.rf_initializing) { pArray->u.array.rf_abort_rebuild = 1; } } pAdapter = pAdapter->next; } } } /*Remove this debug option*/ /* #ifdef DEBUG if (SIGISMEMBER(curproc->p_siglist, SIGSTOP)) #if (__FreeBSD_version > 700033) pause("hptrdy", 2*hz); #else tsleep((caddr_t)hpt_worker_thread, PPAUSE, "hptrdy", 2*hz); #endif #endif */ #if (__FreeBSD_version >= 800002) kproc_suspend_check(curproc); #elif (__FreeBSD_version >= 500043) kthread_suspend_check(curproc); #else kproc_suspend_loop(curproc); #endif #if (__FreeBSD_version > 700033) pause("hptrdy", 2*hz); /* wait for something to do */ #else tsleep((caddr_t)hpt_worker_thread, PPAUSE, "hptrdy", 2*hz); /* wait for something to do */ #endif } } static struct proc *hptdaemonproc; static struct kproc_desc hpt_kp = { "hpt_wt", hpt_worker_thread, &hptdaemonproc }; /*Start this thread in the hpt_attach, to prevent kernel from loading it without our controller.*/ static void launch_worker_thread(void) { IAL_ADAPTER_T *pAdapTemp; kproc_start(&hpt_kp); for (pAdapTemp = gIal_Adapter; pAdapTemp; pAdapTemp = pAdapTemp->next) { _VBUS_INST(&pAdapTemp->VBus) int i; PVDevice pVDev; for(i = 0; i < MAX_ARRAY_PER_VBUS; i++) if ((pVDev=ArrayTables(i))->u.array.dArStamp==0) continue; else{ if (pVDev->u.array.rf_need_rebuild && !pVDev->u.array.rf_rebuilding) hpt_queue_dpc((HPT_DPC)hpt_rebuild_data_block, pAdapTemp, pVDev, (UCHAR)((pVDev->u.array.CriticalMembers || pVDev->VDeviceType == VD_RAID_1)? DUPLICATE : REBUILD_PARITY)); } } /* * hpt_worker_thread needs to be suspended after shutdown sync, when fs sync finished. */ #if (__FreeBSD_version < 500043) EVENTHANDLER_REGISTER(shutdown_post_sync, shutdown_kproc, hptdaemonproc, SHUTDOWN_PRI_LAST); #else EVENTHANDLER_REGISTER(shutdown_post_sync, kproc_shutdown, hptdaemonproc, SHUTDOWN_PRI_LAST); #endif } /* *SYSINIT(hptwt, SI_SUB_KTHREAD_IDLE, SI_ORDER_FIRST, launch_worker_thread, NULL); */ #endif /********************************************************************************/ int HPTLIBAPI fOsBuildSgl(_VBUS_ARG PCommand pCmd, FPSCAT_GATH pSg, int logical) { /* since we have provided physical sg, nobody will ask us to build physical sg */ HPT_ASSERT(0); return FALSE; } /*******************************************************************************/ ULONG HPTLIBAPI GetStamp(void) { /* * the system variable, ticks, can't be used since it hasn't yet been active * when our driver starts (ticks==0, it's a invalid stamp value) */ ULONG stamp; do { stamp = random(); } while (stamp==0); return stamp; } static void SetInquiryData(PINQUIRYDATA inquiryData, PVDevice pVDev) { int i; IDENTIFY_DATA2 *pIdentify = (IDENTIFY_DATA2*)pVDev->u.disk.mv->identifyDevice; inquiryData->DeviceType = T_DIRECT; /*DIRECT_ACCESS_DEVICE*/ inquiryData->AdditionalLength = (UCHAR)(sizeof(INQUIRYDATA) - 5); #ifndef SERIAL_CMDS inquiryData->CommandQueue = 1; #endif switch(pVDev->VDeviceType) { case VD_SINGLE_DISK: case VD_ATAPI: case VD_REMOVABLE: /* Set the removable bit, if applicable. */ if ((pVDev->u.disk.df_removable_drive) || (pIdentify->GeneralConfiguration & 0x80)) inquiryData->RemovableMedia = 1; /* Fill in vendor identification fields. */ for (i = 0; i < 20; i += 2) { inquiryData->VendorId[i] = ((PUCHAR)pIdentify->ModelNumber)[i + 1]; inquiryData->VendorId[i+1] = ((PUCHAR)pIdentify->ModelNumber)[i]; } /* Initialize unused portion of product id. */ for (i = 0; i < 4; i++) inquiryData->ProductId[12+i] = ' '; /* firmware revision */ for (i = 0; i < 4; i += 2) { inquiryData->ProductRevisionLevel[i] = ((PUCHAR)pIdentify->FirmwareRevision)[i+1]; inquiryData->ProductRevisionLevel[i+1] = ((PUCHAR)pIdentify->FirmwareRevision)[i]; } break; default: memcpy(&inquiryData->VendorId, "RR18xx ", 8); #ifdef SUPPORT_ARRAY switch(pVDev->VDeviceType){ case VD_RAID_0: if ((pVDev->u.array.pMember[0] && mIsArray(pVDev->u.array.pMember[0])) || (pVDev->u.array.pMember[1] && mIsArray(pVDev->u.array.pMember[1]))) memcpy(&inquiryData->ProductId, "RAID 1/0 Array ", 16); else memcpy(&inquiryData->ProductId, "RAID 0 Array ", 16); break; case VD_RAID_1: if ((pVDev->u.array.pMember[0] && mIsArray(pVDev->u.array.pMember[0])) || (pVDev->u.array.pMember[1] && mIsArray(pVDev->u.array.pMember[1]))) memcpy(&inquiryData->ProductId, "RAID 0/1 Array ", 16); else memcpy(&inquiryData->ProductId, "RAID 1 Array ", 16); break; case VD_RAID_5: memcpy(&inquiryData->ProductId, "RAID 5 Array ", 16); break; case VD_JBOD: memcpy(&inquiryData->ProductId, "JBOD Array ", 16); break; } #endif memcpy(&inquiryData->ProductRevisionLevel, "3.00", 4); break; } } static void hpt_timeout(void *arg) { _VBUS_INST(&((PBUS_DMAMAP)((union ccb *)arg)->ccb_adapter)->pAdapter->VBus) intrmask_t oldspl = lock_driver(); fResetVBus(_VBUS_P0); unlock_driver(oldspl); } static void hpt_io_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { PCommand pCmd = (PCommand)arg; union ccb *ccb = pCmd->pOrgCommand; struct ccb_hdr *ccb_h = &ccb->ccb_h; PBUS_DMAMAP pmap = (PBUS_DMAMAP) ccb->ccb_adapter; IAL_ADAPTER_T *pAdapter = pmap->pAdapter; PVDevice pVDev = pAdapter->VBus.pVDevice[ccb_h->target_id]; FPSCAT_GATH psg = pCmd->pSgTable; int idx; _VBUS_INST(pVDev->pVBus) HPT_ASSERT(pCmd->cf_physical_sg); if (error) panic("busdma error"); HPT_ASSERT(nsegs<= MAX_SG_DESCRIPTORS); if (nsegs != 0) { for (idx = 0; idx < nsegs; idx++, psg++) { psg->dSgAddress = (ULONG_PTR)(UCHAR *)segs[idx].ds_addr; psg->wSgSize = segs[idx].ds_len; psg->wSgFlag = (idx == nsegs-1)? SG_FLAG_EOT: 0; /* KdPrint(("psg[%d]:add=%p,size=%x,flag=%x\n", idx, psg->dSgAddress,psg->wSgSize,psg->wSgFlag)); */ } /* psg[-1].wSgFlag = SG_FLAG_EOT; */ if (pCmd->cf_data_in) { bus_dmamap_sync(pAdapter->io_dma_parent, pmap->dma_map, BUS_DMASYNC_PREREAD); } else if (pCmd->cf_data_out) { bus_dmamap_sync(pAdapter->io_dma_parent, pmap->dma_map, BUS_DMASYNC_PREWRITE); } } - ccb->ccb_h.timeout_ch = timeout(hpt_timeout, (caddr_t)ccb, 20*hz); + pmap->timeout_ch = timeout(hpt_timeout, (caddr_t)ccb, 20*hz); pVDev->pfnSendCommand(_VBUS_P pCmd); CheckPendingCall(_VBUS_P0); } static void HPTLIBAPI OsSendCommand(_VBUS_ARG union ccb *ccb) { PBUS_DMAMAP pmap = (PBUS_DMAMAP)ccb->ccb_adapter; IAL_ADAPTER_T *pAdapter = pmap->pAdapter; struct ccb_hdr *ccb_h = &ccb->ccb_h; struct ccb_scsiio *csio = &ccb->csio; PVDevice pVDev = pAdapter->VBus.pVDevice[ccb_h->target_id]; KdPrintI(("OsSendCommand: ccb %p cdb %x-%x-%x\n", ccb, *(ULONG *)&ccb->csio.cdb_io.cdb_bytes[0], *(ULONG *)&ccb->csio.cdb_io.cdb_bytes[4], *(ULONG *)&ccb->csio.cdb_io.cdb_bytes[8] )); pAdapter->outstandingCommands++; if (pVDev == NULL || pVDev->vf_online == 0) { ccb->ccb_h.status = CAM_REQ_INVALID; ccb_done(ccb); goto Command_Complished; } switch(ccb->csio.cdb_io.cdb_bytes[0]) { case TEST_UNIT_READY: case START_STOP_UNIT: case SYNCHRONIZE_CACHE: /* FALLTHROUGH */ ccb->ccb_h.status = CAM_REQ_CMP; break; case INQUIRY: ZeroMemory(ccb->csio.data_ptr, ccb->csio.dxfer_len); SetInquiryData((PINQUIRYDATA)ccb->csio.data_ptr, pVDev); ccb_h->status = CAM_REQ_CMP; break; case READ_CAPACITY: { UCHAR *rbuf=csio->data_ptr; unsigned int cap; if (pVDev->VDeviceCapacity > 0xfffffffful) { cap = 0xfffffffful; } else { cap = pVDev->VDeviceCapacity - 1; } rbuf[0] = (UCHAR)(cap>>24); rbuf[1] = (UCHAR)(cap>>16); rbuf[2] = (UCHAR)(cap>>8); rbuf[3] = (UCHAR)cap; /* Claim 512 byte blocks (big-endian). */ rbuf[4] = 0; rbuf[5] = 0; rbuf[6] = 2; rbuf[7] = 0; ccb_h->status = CAM_REQ_CMP; break; } case 0x9e: /*SERVICE_ACTION_IN*/ { UCHAR *rbuf = csio->data_ptr; LBA_T cap = pVDev->VDeviceCapacity - 1; rbuf[0] = (UCHAR)(cap>>56); rbuf[1] = (UCHAR)(cap>>48); rbuf[2] = (UCHAR)(cap>>40); rbuf[3] = (UCHAR)(cap>>32); rbuf[4] = (UCHAR)(cap>>24); rbuf[5] = (UCHAR)(cap>>16); rbuf[6] = (UCHAR)(cap>>8); rbuf[7] = (UCHAR)cap; rbuf[8] = 0; rbuf[9] = 0; rbuf[10] = 2; rbuf[11] = 0; ccb_h->status = CAM_REQ_CMP; break; } case READ_6: case WRITE_6: case READ_10: case WRITE_10: case 0x88: /* READ_16 */ case 0x8a: /* WRITE_16 */ case 0x13: case 0x2f: { UCHAR Cdb[16]; UCHAR CdbLength; _VBUS_INST(pVDev->pVBus) PCommand pCmd = AllocateCommand(_VBUS_P0); int error; HPT_ASSERT(pCmd); CdbLength = csio->cdb_len; if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { if ((ccb->ccb_h.flags & CAM_CDB_PHYS) == 0) { bcopy(csio->cdb_io.cdb_ptr, Cdb, CdbLength); } else { KdPrintE(("ERROR!!!\n")); ccb->ccb_h.status = CAM_REQ_INVALID; break; } } else { bcopy(csio->cdb_io.cdb_bytes, Cdb, CdbLength); } pCmd->pOrgCommand = ccb; pCmd->pVDevice = pVDev; pCmd->pfnCompletion = fOsCommandDone; pCmd->pfnBuildSgl = fOsBuildSgl; pCmd->pSgTable = pmap->psg; switch (Cdb[0]) { case READ_6: case WRITE_6: case 0x13: pCmd->uCmd.Ide.Lba = ((ULONG)Cdb[1] << 16) | ((ULONG)Cdb[2] << 8) | (ULONG)Cdb[3]; pCmd->uCmd.Ide.nSectors = (USHORT) Cdb[4]; break; case 0x88: /* READ_16 */ case 0x8a: /* WRITE_16 */ pCmd->uCmd.Ide.Lba = (HPT_U64)Cdb[2] << 56 | (HPT_U64)Cdb[3] << 48 | (HPT_U64)Cdb[4] << 40 | (HPT_U64)Cdb[5] << 32 | (HPT_U64)Cdb[6] << 24 | (HPT_U64)Cdb[7] << 16 | (HPT_U64)Cdb[8] << 8 | (HPT_U64)Cdb[9]; pCmd->uCmd.Ide.nSectors = (USHORT)Cdb[12] << 8 | (USHORT)Cdb[13]; break; default: pCmd->uCmd.Ide.Lba = (ULONG)Cdb[5] | ((ULONG)Cdb[4] << 8) | ((ULONG)Cdb[3] << 16) | ((ULONG)Cdb[2] << 24); pCmd->uCmd.Ide.nSectors = (USHORT) Cdb[8] | ((USHORT)Cdb[7]<<8); break; } switch (Cdb[0]) { case READ_6: case READ_10: case 0x88: /* READ_16 */ pCmd->uCmd.Ide.Command = IDE_COMMAND_READ; pCmd->cf_data_in = 1; break; case WRITE_6: case WRITE_10: case 0x8a: /* WRITE_16 */ pCmd->uCmd.Ide.Command = IDE_COMMAND_WRITE; pCmd->cf_data_out = 1; break; case 0x13: case 0x2f: pCmd->uCmd.Ide.Command = IDE_COMMAND_VERIFY; break; } /*///////////////////////// */ pCmd->cf_physical_sg = 1; error = bus_dmamap_load_ccb(pAdapter->io_dma_parent, pmap->dma_map, ccb, hpt_io_dmamap_callback, pCmd, BUS_DMA_WAITOK ); KdPrint(("bus_dmamap_load return %d\n", error)); if (error && error!=EINPROGRESS) { hpt_printk(("bus_dmamap_load error %d\n", error)); FreeCommand(_VBUS_P pCmd); ccb->ccb_h.status = CAM_REQ_CMP_ERR; dmamap_put(pmap); pAdapter->outstandingCommands--; xpt_done(ccb); } goto Command_Complished; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } ccb_done(ccb); Command_Complished: CheckPendingCall(_VBUS_P0); return; } static void HPTLIBAPI fOsCommandDone(_VBUS_ARG PCommand pCmd) { union ccb *ccb = pCmd->pOrgCommand; PBUS_DMAMAP pmap = (PBUS_DMAMAP)ccb->ccb_adapter; IAL_ADAPTER_T *pAdapter = pmap->pAdapter; KdPrint(("fOsCommandDone(pcmd=%p, result=%d)\n", pCmd, pCmd->Result)); - untimeout(hpt_timeout, (caddr_t)ccb, ccb->ccb_h.timeout_ch); + untimeout(hpt_timeout, (caddr_t)ccb, pmap->timeout_ch); switch(pCmd->Result) { case RETURN_SUCCESS: ccb->ccb_h.status = CAM_REQ_CMP; break; case RETURN_BAD_DEVICE: ccb->ccb_h.status = CAM_DEV_NOT_THERE; break; case RETURN_DEVICE_BUSY: ccb->ccb_h.status = CAM_BUSY; break; case RETURN_INVALID_REQUEST: ccb->ccb_h.status = CAM_REQ_INVALID; break; case RETURN_SELECTION_TIMEOUT: ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; case RETURN_RETRY: ccb->ccb_h.status = CAM_BUSY; break; default: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; } if (pCmd->cf_data_in) { bus_dmamap_sync(pAdapter->io_dma_parent, pmap->dma_map, BUS_DMASYNC_POSTREAD); } else if (pCmd->cf_data_out) { bus_dmamap_sync(pAdapter->io_dma_parent, pmap->dma_map, BUS_DMASYNC_POSTWRITE); } bus_dmamap_unload(pAdapter->io_dma_parent, pmap->dma_map); FreeCommand(_VBUS_P pCmd); ccb_done(ccb); } int hpt_queue_dpc(HPT_DPC dpc, IAL_ADAPTER_T * pAdapter, void *arg, UCHAR flags) { int p; p = (DpcQueue_Last + 1) % MAX_DPC; if (p==DpcQueue_First) { KdPrint(("DPC Queue full!\n")); return -1; } DpcQueue[DpcQueue_Last].dpc = dpc; DpcQueue[DpcQueue_Last].pAdapter = pAdapter; DpcQueue[DpcQueue_Last].arg = arg; DpcQueue[DpcQueue_Last].flags = flags; DpcQueue_Last = p; return 0; } #ifdef _RAID5N_ /* * Allocate memory above 16M, otherwise we may eat all low memory for ISA devices. * How about the memory for 5081 request/response array and PRD table? */ void *os_alloc_page(_VBUS_ARG0) { return (void *)contigmalloc(0x1000, M_DEVBUF, M_NOWAIT, 0x1000000, 0xffffffff, PAGE_SIZE, 0ul); } void *os_alloc_dma_page(_VBUS_ARG0) { return (void *)contigmalloc(0x1000, M_DEVBUF, M_NOWAIT, 0x1000000, 0xffffffff, PAGE_SIZE, 0ul); } void os_free_page(_VBUS_ARG void *p) { contigfree(p, 0x1000, M_DEVBUF); } void os_free_dma_page(_VBUS_ARG void *p) { contigfree(p, 0x1000, M_DEVBUF); } void DoXor1(ULONG *p0, ULONG *p1, ULONG *p2, UINT nBytes) { UINT i; for (i = 0; i < nBytes / 4; i++) *p0++ = *p1++ ^ *p2++; } void DoXor2(ULONG *p0, ULONG *p2, UINT nBytes) { UINT i; for (i = 0; i < nBytes / 4; i++) *p0++ ^= *p2++; } #endif diff --git a/sys/dev/hptmv/osbsd.h b/sys/dev/hptmv/osbsd.h index e71e3edfe3f8..a0232e52efd7 100644 --- a/sys/dev/hptmv/osbsd.h +++ b/sys/dev/hptmv/osbsd.h @@ -1,334 +1,335 @@ /* * Copyright (c) 2004-2005 HighPoint Technologies, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _OSBSD_H_ #define _OSBSD_H_ #include #include #include #include #if (__FreeBSD_version < 500043) #include #include #endif #include #include #include #if (__FreeBSD_version < 600000) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include extern intrmask_t lock_driver(void); extern void unlock_driver(intrmask_t spl); typedef struct { UCHAR status; /* 0 nonbootable; 80h bootable */ UCHAR start_head; USHORT start_sector; UCHAR type; UCHAR end_head; USHORT end_sector; ULONG start_abs_sector; ULONG num_of_sector; } partition; typedef struct _INQUIRYDATA { UCHAR DeviceType : 5; UCHAR DeviceTypeQualifier : 3; UCHAR DeviceTypeModifier : 7; UCHAR RemovableMedia : 1; UCHAR Versions; UCHAR ResponseDataFormat; UCHAR AdditionalLength; UCHAR Reserved[2]; UCHAR SoftReset : 1; UCHAR CommandQueue : 1; UCHAR Reserved2 : 1; UCHAR LinkedCommands : 1; UCHAR Synchronous : 1; UCHAR Wide16Bit : 1; UCHAR Wide32Bit : 1; UCHAR RelativeAddressing : 1; UCHAR VendorId[8]; UCHAR ProductId[16]; UCHAR ProductRevisionLevel[4]; UCHAR VendorSpecific[20]; UCHAR Reserved3[40]; } INQUIRYDATA, *PINQUIRYDATA; #define MV_IAL_HT_SACOALT_DEFAULT 1 #define MV_IAL_HT_SAITMTH_DEFAULT 1 /****************************************/ /* GENERAL Definitions */ /****************************************/ /* Bits for HD_ERROR */ #define NM_ERR 0x02 /* media present */ #define ABRT_ERR 0x04 /* Command aborted */ #define MCR_ERR 0x08 /* media change request */ #define IDNF_ERR 0x10 /* ID field not found */ #define MC_ERR 0x20 /* media changed */ #define UNC_ERR 0x40 /* Uncorrect data */ #define WP_ERR 0x40 /* write protect */ #define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */ #define REQUESTS_ARRAY_SIZE (9 * MV_EDMA_REQUEST_QUEUE_SIZE) /* 9 K bytes */ #define RESPONSES_ARRAY_SIZE (12 * MV_EDMA_RESPONSE_QUEUE_SIZE) /* 3 K bytes */ #define PRD_ENTRIES_PER_CMD (MAX_SG_DESCRIPTORS+1) #define PRD_ENTRIES_SIZE (MV_EDMA_PRD_ENTRY_SIZE*PRD_ENTRIES_PER_CMD) #define PRD_TABLES_FOR_VBUS (MV_SATA_CHANNELS_NUM*MV_EDMA_QUEUE_LENGTH) typedef enum _SataEvent { SATA_EVENT_NO_CHANGE = 0, SATA_EVENT_CHANNEL_CONNECTED, SATA_EVENT_CHANNEL_DISCONNECTED } SATA_EVENT; typedef ULONG_PTR dma_addr_t; typedef struct _MV_CHANNEL { unsigned int maxUltraDmaModeSupported; unsigned int maxDmaModeSupported; unsigned int maxPioModeSupported; MV_BOOLEAN online; MV_BOOLEAN writeCacheSupported; MV_BOOLEAN writeCacheEnabled; MV_BOOLEAN readAheadSupported; MV_BOOLEAN readAheadEnabled; MV_U8 queueDepth; } MV_CHANNEL; typedef struct _BUS_DMAMAP { struct _BUS_DMAMAP *next; struct IALAdapter *pAdapter; bus_dmamap_t dma_map; + struct callout_handle timeout_ch; SCAT_GATH psg[MAX_SG_DESCRIPTORS]; } BUS_DMAMAP, *PBUS_DMAMAP; typedef struct IALAdapter { struct cam_path *path; bus_dma_tag_t io_dma_parent; /* I/O buffer DMA tag */ PBUS_DMAMAP pbus_dmamap_list; PBUS_DMAMAP pbus_dmamap; device_t hpt_dev; /* bus device */ struct resource *hpt_irq; /* interrupt */ struct resource *mem_res; void *hpt_intr; /* interrupt handle */ struct IALAdapter *next; MV_SATA_ADAPTER mvSataAdapter; MV_CHANNEL mvChannel[MV_SATA_CHANNELS_NUM]; MV_U8 *requestsArrayBaseAddr; MV_U8 *requestsArrayBaseAlignedAddr; dma_addr_t requestsArrayBaseDmaAddr; dma_addr_t requestsArrayBaseDmaAlignedAddr; MV_U8 *responsesArrayBaseAddr; MV_U8 *responsesArrayBaseAlignedAddr; dma_addr_t responsesArrayBaseDmaAddr; dma_addr_t responsesArrayBaseDmaAlignedAddr; SATA_EVENT sataEvents[MV_SATA_CHANNELS_NUM]; struct callout_handle event_timer_connect; struct callout_handle event_timer_disconnect; struct _VBus VBus; struct _VDevice VDevices[MV_SATA_CHANNELS_NUM]; PCommand pCommandBlocks; PUCHAR prdTableAddr; PUCHAR prdTableAlignedAddr; void* pFreePRDLink; union ccb *pending_Q; MV_U8 outstandingCommands; UCHAR status; UCHAR ver_601; UCHAR beeping; eventhandler_tag eh; } IAL_ADAPTER_T; extern IAL_ADAPTER_T *gIal_Adapter; /*entry.c*/ typedef void (*HPT_DPC)(IAL_ADAPTER_T *,void*,UCHAR); int hpt_queue_dpc(HPT_DPC dpc, IAL_ADAPTER_T *pAdapter, void *arg, UCHAR flags); void hpt_rebuild_data_block(IAL_ADAPTER_T *pAdapter, PVDevice pArray, UCHAR flags); void Check_Idle_Call(IAL_ADAPTER_T *pAdapter); void fRescanAllDevice(_VBUS_ARG0); int hpt_add_disk_to_array(_VBUS_ARG DEVICEID idArray, DEVICEID idDisk); int Kernel_DeviceIoControl(_VBUS_ARG DWORD dwIoControlCode, /* operation control code */ PVOID lpInBuffer, /* input data buffer */ DWORD nInBufferSize, /* size of input data buffer */ PVOID lpOutBuffer, /* output data buffer */ DWORD nOutBufferSize, /* size of output data buffer */ PDWORD lpBytesReturned /* byte count */ ); #define __str_direct(x) #x #define __str(x) __str_direct(x) #define KMSG_LEADING __str(PROC_DIR_NAME) ": " #define hpt_printk(_x_) do { printf(KMSG_LEADING); printf _x_ ; } while (0) #define DUPLICATE 0 #define INITIALIZE 1 #define REBUILD_PARITY 2 #define VERIFY 3 /***********************************************************/ static __inline struct cam_periph * hpt_get_periph(int path_id,int target_id) { struct cam_periph *periph = NULL; struct cam_path *path; int status; status = xpt_create_path(&path, NULL, path_id, target_id, 0); if (status == CAM_REQ_CMP) { periph = cam_periph_find(path, "da"); xpt_free_path(path); } return periph; } #if (__FreeBSD_version < 500000) #define YIELD_THREAD yield(curproc, 0) #endif #ifdef __i386__ #define BITS_PER_LONG 32 #define VDEV_TO_ID(pVDev) (DEVICEID)(pVDev) #define ID_TO_VDEV(id) (PVDevice)(id) #else /*Only support x86_64(AMD64 and EM64T)*/ #define BITS_PER_LONG 64 #define VDEV_TO_ID(pVDev) (DEVICEID)(ULONG_PTR)(pVDev) #define ID_TO_VDEV(id) (PVDevice)(((ULONG_PTR)gIal_Adapter & 0xffffffff00000000) | (id)) #endif #define INVALID_DEVICEID (-1) #define INVALID_STRIPSIZE (-1) #define shortswap(w) ((WORD)((w)>>8 | ((w) & 0xFF)<<8)) #ifndef MinBlockSizeShift #define MinBlockSizeShift 5 #define MaxBlockSizeShift 12 #endif #pragma pack(1) typedef struct _HPT_IOCTL_TRANSFER_PARAM { ULONG nInBufferSize; ULONG nOutBufferSize; UCHAR buffer[0]; }HPT_IOCTL_TRANSFER_PARAM, *PHPT_IOCTL_TRANSFER_PARAM; typedef struct _HPT_SET_STATE_PARAM { DEVICEID idArray; DWORD state; } HPT_SET_STATE_PARAM, *PHPT_SET_STATE_PARAM; typedef struct _HPT_SET_ARRAY_INFO { DEVICEID idArray; ALTERABLE_ARRAY_INFO Info; } HPT_SET_ARRAY_INFO, *PHPT_SET_ARRAY_INFO; typedef struct _HPT_SET_DEVICE_INFO { DEVICEID idDisk; ALTERABLE_DEVICE_INFO Info; } HPT_SET_DEVICE_INFO, *PHPT_SET_DEVICE_INFO; typedef struct _HPT_SET_DEVICE_INFO_V2 { DEVICEID idDisk; ALTERABLE_DEVICE_INFO_V2 Info; } HPT_SET_DEVICE_INFO_V2, *PHPT_SET_DEVICE_INFO_V2; typedef struct _HPT_ADD_DISK_TO_ARRAY { DEVICEID idArray; DEVICEID idDisk; } HPT_ADD_DISK_TO_ARRAY, *PHPT_ADD_DISK_TO_ARRAY; typedef struct _HPT_DEVICE_IO { DEVICEID id; int cmd; ULONG lba; DWORD nSector; UCHAR buffer[0]; } HPT_DEVICE_IO, *PHPT_DEVICE_IO; int check_VDevice_valid(PVDevice); int hpt_default_ioctl(_VBUS_ARG DWORD, PVOID, DWORD, PVOID, DWORD, PDWORD); #define HPT_NULL_ID 0 #pragma pack() #endif diff --git a/sys/dev/hptnr/hptnr_osm_bsd.c b/sys/dev/hptnr/hptnr_osm_bsd.c index 7021abeb1375..87441be5a31a 100644 --- a/sys/dev/hptnr/hptnr_osm_bsd.c +++ b/sys/dev/hptnr/hptnr_osm_bsd.c @@ -1,1360 +1,1361 @@ /* $Id: osm_bsd.c,v 1.36 2010/05/11 03:12:11 lcn Exp $ */ /*- * HighPoint RAID Driver for FreeBSD * Copyright (C) 2005-2011 HighPoint Technologies, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include static int hpt_probe(device_t dev) { PCI_ID pci_id; HIM *him; int i; PHBA hba; for (him = him_list; him; him = him->next) { for (i=0; him->get_supported_device_id(i, &pci_id); i++) { if (him->get_controller_count) him->get_controller_count(&pci_id,0,0); if ((pci_get_vendor(dev) == pci_id.vid) && (pci_get_device(dev) == pci_id.did)){ KdPrint(("hpt_probe: adapter at PCI %d:%d:%d, IRQ %d", pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev), pci_get_irq(dev) )); device_set_desc(dev, him->name); hba = (PHBA)device_get_softc(dev); memset(hba, 0, sizeof(HBA)); hba->ext_type = EXT_TYPE_HBA; hba->ldm_adapter.him = him; return 0; } } } return (ENXIO); } static int hpt_attach(device_t dev) { PHBA hba = (PHBA)device_get_softc(dev); HIM *him = hba->ldm_adapter.him; PCI_ID pci_id; HPT_UINT size; PVBUS vbus; PVBUS_EXT vbus_ext; KdPrint(("hpt_attach(%d/%d/%d)", pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev))); #if __FreeBSD_version >=440000 pci_enable_busmaster(dev); #endif pci_id.vid = pci_get_vendor(dev); pci_id.did = pci_get_device(dev); pci_id.rev = pci_get_revid(dev); pci_id.subsys = (HPT_U32)(pci_get_subdevice(dev)) << 16 | pci_get_subvendor(dev); size = him->get_adapter_size(&pci_id); hba->ldm_adapter.him_handle = malloc(size, M_DEVBUF, M_WAITOK); if (!hba->ldm_adapter.him_handle) return ENXIO; hba->pcidev = dev; hba->pciaddr.tree = 0; hba->pciaddr.bus = pci_get_bus(dev); hba->pciaddr.device = pci_get_slot(dev); hba->pciaddr.function = pci_get_function(dev); if (!him->create_adapter(&pci_id, hba->pciaddr, hba->ldm_adapter.him_handle, hba)) { free(hba->ldm_adapter.him_handle, M_DEVBUF); return -1; } os_printk("adapter at PCI %d:%d:%d, IRQ %d", hba->pciaddr.bus, hba->pciaddr.device, hba->pciaddr.function, pci_get_irq(dev)); if (!ldm_register_adapter(&hba->ldm_adapter)) { size = ldm_get_vbus_size(); vbus_ext = malloc(sizeof(VBUS_EXT) + size, M_DEVBUF, M_WAITOK); if (!vbus_ext) { free(hba->ldm_adapter.him_handle, M_DEVBUF); return -1; } memset(vbus_ext, 0, sizeof(VBUS_EXT)); vbus_ext->ext_type = EXT_TYPE_VBUS; ldm_create_vbus((PVBUS)vbus_ext->vbus, vbus_ext); ldm_register_adapter(&hba->ldm_adapter); } ldm_for_each_vbus(vbus, vbus_ext) { if (hba->ldm_adapter.vbus==vbus) { hba->vbus_ext = vbus_ext; hba->next = vbus_ext->hba_list; vbus_ext->hba_list = hba; break; } } return 0; } /* * Maybe we'd better to use the bus_dmamem_alloc to alloc DMA memory, * but there are some problems currently (alignment, etc). */ static __inline void *__get_free_pages(int order) { /* don't use low memory - other devices may get starved */ return contigmalloc(PAGE_SIZE<hba_list; hba; hba = hba->next) hba->ldm_adapter.him->get_meminfo(hba->ldm_adapter.him_handle); ldm_get_mem_info((PVBUS)vbus_ext->vbus, 0); for (f=vbus_ext->freelist_head; f; f=f->next) { KdPrint(("%s: %d*%d=%d bytes", f->tag, f->count, f->size, f->count*f->size)); for (i=0; icount; i++) { p = (void **)malloc(f->size, M_DEVBUF, M_WAITOK); if (!p) return (ENXIO); *p = f->head; f->head = p; } } for (f=vbus_ext->freelist_dma_head; f; f=f->next) { int order, size, j; HPT_ASSERT((f->size & (f->alignment-1))==0); for (order=0, size=PAGE_SIZE; sizesize; order++, size<<=1) ; KdPrint(("%s: %d*%d=%d bytes, order %d", f->tag, f->count, f->size, f->count*f->size, order)); HPT_ASSERT(f->alignment<=PAGE_SIZE); for (i=0; icount;) { p = (void **)__get_free_pages(order); if (!p) return -1; for (j = size/f->size; j && icount; i++,j--) { *p = f->head; *(BUS_ADDRESS *)(p+1) = (BUS_ADDRESS)vtophys(p); f->head = p; p = (void **)((unsigned long)p + f->size); } } } HPT_ASSERT(PAGE_SIZE==DMAPOOL_PAGE_SIZE); for (i=0; ivbus, p, (BUS_ADDRESS)vtophys(p)); } return 0; } static void hpt_free_mem(PVBUS_EXT vbus_ext) { struct freelist *f; void *p; int i; BUS_ADDRESS bus; for (f=vbus_ext->freelist_head; f; f=f->next) { #if DBG if (f->count!=f->reserved_count) { KdPrint(("memory leak for freelist %s (%d/%d)", f->tag, f->count, f->reserved_count)); } #endif while ((p=freelist_get(f))) free(p, M_DEVBUF); } for (i=0; ivbus, &bus); HPT_ASSERT(p); free_pages(p, 0); } for (f=vbus_ext->freelist_dma_head; f; f=f->next) { int order, size; #if DBG if (f->count!=f->reserved_count) { KdPrint(("memory leak for dma freelist %s (%d/%d)", f->tag, f->count, f->reserved_count)); } #endif for (order=0, size=PAGE_SIZE; sizesize; order++, size<<=1) ; while ((p=freelist_get_dma(f, &bus))) { if (order) free_pages(p, order); else { /* can't free immediately since other blocks in this page may still be in the list */ if (((HPT_UPTR)p & (PAGE_SIZE-1))==0) dmapool_put_page((PVBUS)vbus_ext->vbus, p, bus); } } } while ((p = dmapool_get_page((PVBUS)vbus_ext->vbus, &bus))) free_pages(p, 0); } static int hpt_init_vbus(PVBUS_EXT vbus_ext) { PHBA hba; for (hba = vbus_ext->hba_list; hba; hba = hba->next) if (!hba->ldm_adapter.him->initialize(hba->ldm_adapter.him_handle)) { KdPrint(("fail to initialize %p", hba)); return -1; } ldm_initialize_vbus((PVBUS)vbus_ext->vbus, &vbus_ext->hba_list->ldm_adapter); return 0; } static void hpt_flush_done(PCOMMAND pCmd) { PVDEV vd = pCmd->target; if (mIsArray(vd->type) && vd->u.array.transform && vd!=vd->u.array.transform->target) { vd = vd->u.array.transform->target; HPT_ASSERT(vd); pCmd->target = vd; pCmd->Result = RETURN_PENDING; vdev_queue_cmd(pCmd); return; } *(int *)pCmd->priv = 1; wakeup(pCmd); } /* * flush a vdev (without retry). */ static int hpt_flush_vdev(PVBUS_EXT vbus_ext, PVDEV vd) { PCOMMAND pCmd; int result = 0, done; HPT_UINT count; KdPrint(("flusing dev %p", vd)); hpt_lock_vbus(vbus_ext); if (mIsArray(vd->type) && vd->u.array.transform) count = MAX(vd->u.array.transform->source->cmds_per_request, vd->u.array.transform->target->cmds_per_request); else count = vd->cmds_per_request; pCmd = ldm_alloc_cmds(vd->vbus, count); if (!pCmd) { hpt_unlock_vbus(vbus_ext); return -1; } pCmd->type = CMD_TYPE_FLUSH; pCmd->flags.hard_flush = 1; pCmd->target = vd; pCmd->done = hpt_flush_done; done = 0; pCmd->priv = &done; ldm_queue_cmd(pCmd); if (!done) { while (hpt_sleep(vbus_ext, pCmd, PPAUSE, "hptfls", HPT_OSM_TIMEOUT)) { ldm_reset_vbus(vd->vbus); } } KdPrint(("flush result %d", pCmd->Result)); if (pCmd->Result!=RETURN_SUCCESS) result = -1; ldm_free_cmds(pCmd); hpt_unlock_vbus(vbus_ext); return result; } static void hpt_stop_tasks(PVBUS_EXT vbus_ext); static void hpt_shutdown_vbus(PVBUS_EXT vbus_ext, int howto) { PVBUS vbus = (PVBUS)vbus_ext->vbus; PHBA hba; int i; KdPrint(("hpt_shutdown_vbus")); /* stop all ctl tasks and disable the worker taskqueue */ hpt_stop_tasks(vbus_ext); vbus_ext->worker.ta_context = 0; /* flush devices */ for (i=0; ihba_list; hba; hba=hba->next) bus_teardown_intr(hba->pcidev, hba->irq_res, hba->irq_handle); hpt_free_mem(vbus_ext); while ((hba=vbus_ext->hba_list)) { vbus_ext->hba_list = hba->next; free(hba->ldm_adapter.him_handle, M_DEVBUF); } free(vbus_ext, M_DEVBUF); KdPrint(("hpt_shutdown_vbus done")); } static void __hpt_do_tasks(PVBUS_EXT vbus_ext) { OSM_TASK *tasks; tasks = vbus_ext->tasks; vbus_ext->tasks = 0; while (tasks) { OSM_TASK *t = tasks; tasks = t->next; t->next = 0; t->func(vbus_ext->vbus, t->data); } } static void hpt_do_tasks(PVBUS_EXT vbus_ext, int pending) { if(vbus_ext){ hpt_lock_vbus(vbus_ext); __hpt_do_tasks(vbus_ext); hpt_unlock_vbus(vbus_ext); } } static void hpt_action(struct cam_sim *sim, union ccb *ccb); static void hpt_poll(struct cam_sim *sim); static void hpt_async(void * callback_arg, u_int32_t code, struct cam_path * path, void * arg); static void hpt_pci_intr(void *arg); static __inline POS_CMDEXT cmdext_get(PVBUS_EXT vbus_ext) { POS_CMDEXT p = vbus_ext->cmdext_list; if (p) vbus_ext->cmdext_list = p->next; return p; } static __inline void cmdext_put(POS_CMDEXT p) { p->next = p->vbus_ext->cmdext_list; p->vbus_ext->cmdext_list = p; } static void hpt_timeout(void *arg) { PCOMMAND pCmd = (PCOMMAND)arg; POS_CMDEXT ext = (POS_CMDEXT)pCmd->priv; KdPrint(("pCmd %p timeout", pCmd)); ldm_reset_vbus((PVBUS)ext->vbus_ext->vbus); } static void os_cmddone(PCOMMAND pCmd) { POS_CMDEXT ext = (POS_CMDEXT)pCmd->priv; union ccb *ccb = ext->ccb; KdPrint(("os_cmddone(%p, %d)", pCmd, pCmd->Result)); - untimeout(hpt_timeout, pCmd, ccb->ccb_h.timeout_ch); + untimeout(hpt_timeout, pCmd, ext->timeout_ch); switch(pCmd->Result) { case RETURN_SUCCESS: ccb->ccb_h.status = CAM_REQ_CMP; break; case RETURN_BAD_DEVICE: ccb->ccb_h.status = CAM_DEV_NOT_THERE; break; case RETURN_DEVICE_BUSY: ccb->ccb_h.status = CAM_BUSY; break; case RETURN_INVALID_REQUEST: ccb->ccb_h.status = CAM_REQ_INVALID; break; case RETURN_SELECTION_TIMEOUT: ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; case RETURN_RETRY: ccb->ccb_h.status = CAM_BUSY; break; default: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; } if (pCmd->flags.data_in) { bus_dmamap_sync(ext->vbus_ext->io_dmat, ext->dma_map, BUS_DMASYNC_POSTREAD); } else if (pCmd->flags.data_out) { bus_dmamap_sync(ext->vbus_ext->io_dmat, ext->dma_map, BUS_DMASYNC_POSTWRITE); } bus_dmamap_unload(ext->vbus_ext->io_dmat, ext->dma_map); cmdext_put(ext); ldm_free_cmds(pCmd); xpt_done(ccb); } static int os_buildsgl(PCOMMAND pCmd, PSG pSg, int logical) { /* since we have provided physical sg, nobody will ask us to build physical sg */ HPT_ASSERT(0); return FALSE; } static void hpt_io_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { PCOMMAND pCmd = (PCOMMAND)arg; POS_CMDEXT ext = (POS_CMDEXT)pCmd->priv; PSG psg = pCmd->psg; int idx; HPT_ASSERT(pCmd->flags.physical_sg); if (error) panic("busdma error"); HPT_ASSERT(nsegs<=os_max_sg_descriptors); if (nsegs != 0) { for (idx = 0; idx < nsegs; idx++, psg++) { psg->addr.bus = segs[idx].ds_addr; psg->size = segs[idx].ds_len; psg->eot = 0; } psg[-1].eot = 1; if (pCmd->flags.data_in) { bus_dmamap_sync(ext->vbus_ext->io_dmat, ext->dma_map, BUS_DMASYNC_PREREAD); } else if (pCmd->flags.data_out) { bus_dmamap_sync(ext->vbus_ext->io_dmat, ext->dma_map, BUS_DMASYNC_PREWRITE); } } - ext->ccb->ccb_h.timeout_ch = timeout(hpt_timeout, pCmd, HPT_OSM_TIMEOUT); + ext->timeout_ch = timeout(hpt_timeout, pCmd, HPT_OSM_TIMEOUT); ldm_queue_cmd(pCmd); } static void hpt_scsi_io(PVBUS_EXT vbus_ext, union ccb *ccb) { PVBUS vbus = (PVBUS)vbus_ext->vbus; PVDEV vd; PCOMMAND pCmd; POS_CMDEXT ext; HPT_U8 *cdb; if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; KdPrint(("hpt_scsi_io: ccb %x id %d lun %d cdb %x-%x-%x", ccb, ccb->ccb_h.target_id, ccb->ccb_h.target_lun, *(HPT_U32 *)&cdb[0], *(HPT_U32 *)&cdb[4], *(HPT_U32 *)&cdb[8] )); /* ccb->ccb_h.path_id is not our bus id - don't check it */ if (ccb->ccb_h.target_lun != 0 || ccb->ccb_h.target_id >= osm_max_targets || (ccb->ccb_h.flags & CAM_CDB_PHYS)) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return; } vd = ldm_find_target(vbus, ccb->ccb_h.target_id); if (!vd) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return; } switch (cdb[0]) { case TEST_UNIT_READY: case START_STOP_UNIT: case SYNCHRONIZE_CACHE: ccb->ccb_h.status = CAM_REQ_CMP; break; case INQUIRY: { PINQUIRYDATA inquiryData; memset(ccb->csio.data_ptr, 0, ccb->csio.dxfer_len); inquiryData = (PINQUIRYDATA)ccb->csio.data_ptr; inquiryData->AdditionalLength = 31; inquiryData->CommandQueue = 1; memcpy(&inquiryData->VendorId, "HPT ", 8); memcpy(&inquiryData->ProductId, "DISK 0_0 ", 16); if (vd->target_id / 10) { inquiryData->ProductId[7] = (vd->target_id % 100) / 10 + '0'; inquiryData->ProductId[8] = (vd->target_id % 100) % 10 + '0'; } else inquiryData->ProductId[7] = (vd->target_id % 100) % 10 + '0'; memcpy(&inquiryData->ProductRevisionLevel, "4.00", 4); ccb->ccb_h.status = CAM_REQ_CMP; } break; case READ_CAPACITY: { HPT_U8 *rbuf = ccb->csio.data_ptr; HPT_U32 cap; if (vd->capacity>0xfffffffful) cap = 0xfffffffful; else cap = vd->capacity - 1; rbuf[0] = (HPT_U8)(cap>>24); rbuf[1] = (HPT_U8)(cap>>16); rbuf[2] = (HPT_U8)(cap>>8); rbuf[3] = (HPT_U8)cap; rbuf[4] = 0; rbuf[5] = 0; rbuf[6] = 2; rbuf[7] = 0; ccb->ccb_h.status = CAM_REQ_CMP; break; } case SERVICE_ACTION_IN: { HPT_U8 *rbuf = ccb->csio.data_ptr; HPT_U64 cap = vd->capacity - 1; rbuf[0] = (HPT_U8)(cap>>56); rbuf[1] = (HPT_U8)(cap>>48); rbuf[2] = (HPT_U8)(cap>>40); rbuf[3] = (HPT_U8)(cap>>32); rbuf[4] = (HPT_U8)(cap>>24); rbuf[5] = (HPT_U8)(cap>>16); rbuf[6] = (HPT_U8)(cap>>8); rbuf[7] = (HPT_U8)cap; rbuf[8] = 0; rbuf[9] = 0; rbuf[10] = 2; rbuf[11] = 0; ccb->ccb_h.status = CAM_REQ_CMP; break; } case READ_6: case READ_10: case READ_16: case WRITE_6: case WRITE_10: case WRITE_16: case 0x13: case 0x2f: case 0x8f: /* VERIFY_16 */ { int error; pCmd = ldm_alloc_cmds(vbus, vd->cmds_per_request); if(!pCmd){ KdPrint(("Failed to allocate command!")); ccb->ccb_h.status = CAM_BUSY; break; } switch (cdb[0]) { case READ_6: case WRITE_6: case 0x13: pCmd->uCmd.Ide.Lba = ((HPT_U32)cdb[1] << 16) | ((HPT_U32)cdb[2] << 8) | (HPT_U32)cdb[3]; pCmd->uCmd.Ide.nSectors = (HPT_U16) cdb[4]; break; case READ_16: case WRITE_16: case 0x8f: /* VERIFY_16 */ { HPT_U64 block = ((HPT_U64)cdb[2]<<56) | ((HPT_U64)cdb[3]<<48) | ((HPT_U64)cdb[4]<<40) | ((HPT_U64)cdb[5]<<32) | ((HPT_U64)cdb[6]<<24) | ((HPT_U64)cdb[7]<<16) | ((HPT_U64)cdb[8]<<8) | ((HPT_U64)cdb[9]); pCmd->uCmd.Ide.Lba = block; pCmd->uCmd.Ide.nSectors = (HPT_U16)cdb[13] | ((HPT_U16)cdb[12]<<8); break; } default: pCmd->uCmd.Ide.Lba = (HPT_U32)cdb[5] | ((HPT_U32)cdb[4] << 8) | ((HPT_U32)cdb[3] << 16) | ((HPT_U32)cdb[2] << 24); pCmd->uCmd.Ide.nSectors = (HPT_U16) cdb[8] | ((HPT_U16)cdb[7]<<8); break; } switch (cdb[0]) { case READ_6: case READ_10: case READ_16: pCmd->flags.data_in = 1; break; case WRITE_6: case WRITE_10: case WRITE_16: pCmd->flags.data_out = 1; break; } pCmd->priv = ext = cmdext_get(vbus_ext); HPT_ASSERT(ext); ext->ccb = ccb; pCmd->target = vd; pCmd->done = os_cmddone; pCmd->buildsgl = os_buildsgl; pCmd->psg = ext->psg; pCmd->flags.physical_sg = 1; error = bus_dmamap_load_ccb(vbus_ext->io_dmat, ext->dma_map, ccb, hpt_io_dmamap_callback, pCmd, BUS_DMA_WAITOK ); KdPrint(("bus_dmamap_load return %d", error)); if (error && error!=EINPROGRESS) { os_printk("bus_dmamap_load error %d", error); cmdext_put(ext); ldm_free_cmds(pCmd); ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(ccb); } return; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); return; } static void hpt_action(struct cam_sim *sim, union ccb *ccb) { PVBUS_EXT vbus_ext = (PVBUS_EXT)cam_sim_softc(sim); KdPrint(("hpt_action(fn=%d, id=%d)", ccb->ccb_h.func_code, ccb->ccb_h.target_id)); switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: hpt_lock_vbus(vbus_ext); hpt_scsi_io(vbus_ext, ccb); hpt_unlock_vbus(vbus_ext); return; case XPT_RESET_BUS: hpt_lock_vbus(vbus_ext); ldm_reset_vbus((PVBUS)vbus_ext->vbus); hpt_unlock_vbus(vbus_ext); break; case XPT_GET_TRAN_SETTINGS: case XPT_SET_TRAN_SETTINGS: ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; case XPT_CALC_GEOMETRY: ccb->ccg.heads = 255; ccb->ccg.secs_per_track = 63; ccb->ccg.cylinders = ccb->ccg.volume_size / (ccb->ccg.heads * ccb->ccg.secs_per_track); ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_SDTR_ABLE; cpi->target_sprt = 0; cpi->hba_misc = PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = osm_max_targets; cpi->max_lun = 0; cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = cam_sim_bus(sim); cpi->initiator_id = osm_max_targets; cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "HPT ", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); #if (__FreeBSD_version >= 800000) cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; #endif cpi->ccb_h.status = CAM_REQ_CMP; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); return; } static void hpt_pci_intr(void *arg) { PVBUS_EXT vbus_ext = (PVBUS_EXT)arg; hpt_lock_vbus(vbus_ext); ldm_intr((PVBUS)vbus_ext->vbus); hpt_unlock_vbus(vbus_ext); } static void hpt_poll(struct cam_sim *sim) { hpt_pci_intr(cam_sim_softc(sim)); } static void hpt_async(void * callback_arg, u_int32_t code, struct cam_path * path, void * arg) { KdPrint(("hpt_async")); } static int hpt_shutdown(device_t dev) { KdPrint(("hpt_shutdown(dev=%p)", dev)); return 0; } static int hpt_detach(device_t dev) { /* we don't allow the driver to be unloaded. */ return EBUSY; } static void hpt_ioctl_done(struct _IOCTL_ARG *arg) { arg->ioctl_cmnd = 0; wakeup(arg); } static void __hpt_do_ioctl(PVBUS_EXT vbus_ext, IOCTL_ARG *ioctl_args) { ioctl_args->result = -1; ioctl_args->done = hpt_ioctl_done; ioctl_args->ioctl_cmnd = (void *)1; hpt_lock_vbus(vbus_ext); ldm_ioctl((PVBUS)vbus_ext->vbus, ioctl_args); while (ioctl_args->ioctl_cmnd) { if (hpt_sleep(vbus_ext, ioctl_args, PPAUSE, "hptctl", HPT_OSM_TIMEOUT)==0) break; ldm_reset_vbus((PVBUS)vbus_ext->vbus); __hpt_do_tasks(vbus_ext); } /* KdPrint(("ioctl %x result %d", ioctl_args->dwIoControlCode, ioctl_args->result)); */ hpt_unlock_vbus(vbus_ext); } static void hpt_do_ioctl(IOCTL_ARG *ioctl_args) { PVBUS vbus; PVBUS_EXT vbus_ext; ldm_for_each_vbus(vbus, vbus_ext) { __hpt_do_ioctl(vbus_ext, ioctl_args); if (ioctl_args->result!=HPT_IOCTL_RESULT_WRONG_VBUS) return; } } #define HPT_DO_IOCTL(code, inbuf, insize, outbuf, outsize) ({\ IOCTL_ARG arg;\ arg.dwIoControlCode = code;\ arg.lpInBuffer = inbuf;\ arg.lpOutBuffer = outbuf;\ arg.nInBufferSize = insize;\ arg.nOutBufferSize = outsize;\ arg.lpBytesReturned = 0;\ hpt_do_ioctl(&arg);\ arg.result;\ }) #define DEVICEID_VALID(id) ((id) && ((HPT_U32)(id)!=0xffffffff)) static int hpt_get_logical_devices(DEVICEID * pIds, int nMaxCount) { int i; HPT_U32 count = nMaxCount-1; if (HPT_DO_IOCTL(HPT_IOCTL_GET_LOGICAL_DEVICES, &count, sizeof(HPT_U32), pIds, sizeof(DEVICEID)*nMaxCount)) return -1; nMaxCount = (int)pIds[0]; for (i=0; i=503000 .d_version = D_VERSION, #endif #if (__FreeBSD_version>=503000 && __FreeBSD_version<600034) .d_flags = D_NEEDGIANT, #endif #if __FreeBSD_version<600034 #if __FreeBSD_version>501000 .d_maj = MAJOR_AUTO, #else .d_maj = HPT_DEV_MAJOR, #endif #endif }; static struct intr_config_hook hpt_ich; /* * hpt_final_init will be called after all hpt_attach. */ static void hpt_final_init(void *dummy) { int i,unit_number=0; PVBUS_EXT vbus_ext; PVBUS vbus; PHBA hba; /* Clear the config hook */ config_intrhook_disestablish(&hpt_ich); /* allocate memory */ i = 0; ldm_for_each_vbus(vbus, vbus_ext) { if (hpt_alloc_mem(vbus_ext)) { os_printk("out of memory"); return; } i++; } if (!i) { if (bootverbose) os_printk("no controller detected."); return; } /* initializing hardware */ ldm_for_each_vbus(vbus, vbus_ext) { /* make timer available here */ callout_handle_init(&vbus_ext->timer); if (hpt_init_vbus(vbus_ext)) { os_printk("fail to initialize hardware"); break; /* FIXME */ } } /* register CAM interface */ ldm_for_each_vbus(vbus, vbus_ext) { struct cam_devq *devq; struct ccb_setasync ccb; #if (__FreeBSD_version >= 500000) mtx_init(&vbus_ext->lock, "hptsleeplock", NULL, MTX_DEF); #endif if (bus_dma_tag_create(NULL,/* parent */ 4, /* alignment */ BUS_SPACE_MAXADDR_32BIT+1, /* boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ PAGE_SIZE * (os_max_sg_descriptors-1), /* maxsize */ os_max_sg_descriptors, /* nsegments */ 0x10000, /* maxsegsize */ BUS_DMA_WAITOK, /* flags */ #if __FreeBSD_version>502000 busdma_lock_mutex, /* lockfunc */ &vbus_ext->lock, /* lockfuncarg */ #endif &vbus_ext->io_dmat /* tag */)) { return ; } for (i=0; ivbus_ext = vbus_ext; ext->next = vbus_ext->cmdext_list; vbus_ext->cmdext_list = ext; if (bus_dmamap_create(vbus_ext->io_dmat, 0, &ext->dma_map)) { os_printk("Can't create dma map(%d)", i); return ; } + callout_handle_init(&ext->timeout_ch); } if ((devq = cam_simq_alloc(os_max_queue_comm)) == NULL) { os_printk("cam_simq_alloc failed"); return ; } #if __FreeBSD_version > 700025 vbus_ext->sim = cam_sim_alloc(hpt_action, hpt_poll, driver_name, vbus_ext, unit_number, &Giant, os_max_queue_comm, /*tagged*/8, devq); #else vbus_ext->sim = cam_sim_alloc(hpt_action, hpt_poll, driver_name, vbus_ext, unit_number, os_max_queue_comm, /*tagged*/8, devq); #endif unit_number++; if (!vbus_ext->sim) { os_printk("cam_sim_alloc failed"); cam_simq_free(devq); return ; } #if __FreeBSD_version > 700044 if (xpt_bus_register(vbus_ext->sim, NULL, 0) != CAM_SUCCESS) { #else if (xpt_bus_register(vbus_ext->sim, 0) != CAM_SUCCESS) { #endif os_printk("xpt_bus_register failed"); cam_sim_free(vbus_ext->sim, /*free devq*/ TRUE); vbus_ext->sim = NULL; return ; } if (xpt_create_path(&vbus_ext->path, /*periph */ NULL, cam_sim_path(vbus_ext->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { os_printk("xpt_create_path failed"); xpt_bus_deregister(cam_sim_path(vbus_ext->sim)); cam_sim_free(vbus_ext->sim, /*free_devq*/TRUE); vbus_ext->sim = NULL; return ; } xpt_setup_ccb(&ccb.ccb_h, vbus_ext->path, /*priority*/5); ccb.ccb_h.func_code = XPT_SASYNC_CB; ccb.event_enable = AC_LOST_DEVICE; ccb.callback = hpt_async; ccb.callback_arg = vbus_ext; xpt_action((union ccb *)&ccb); for (hba = vbus_ext->hba_list; hba; hba = hba->next) { int rid = 0; if ((hba->irq_res = bus_alloc_resource(hba->pcidev, SYS_RES_IRQ, &rid, 0, ~0ul, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) { os_printk("can't allocate interrupt"); return ; } if (bus_setup_intr(hba->pcidev, hba->irq_res, INTR_TYPE_CAM, #if __FreeBSD_version > 700025 NULL, hpt_pci_intr, vbus_ext, &hba->irq_handle)) #else hpt_pci_intr, vbus_ext, &hba->irq_handle)) #endif { os_printk("can't set up interrupt"); return ; } hba->ldm_adapter.him->intr_control(hba->ldm_adapter.him_handle, HPT_TRUE); } vbus_ext->shutdown_eh = EVENTHANDLER_REGISTER(shutdown_final, hpt_shutdown_vbus, vbus_ext, SHUTDOWN_PRI_DEFAULT); if (!vbus_ext->shutdown_eh) os_printk("Shutdown event registration failed"); } ldm_for_each_vbus(vbus, vbus_ext) { TASK_INIT(&vbus_ext->worker, 0, (task_fn_t *)hpt_do_tasks, vbus_ext); if (vbus_ext->tasks) TASK_ENQUEUE(&vbus_ext->worker); } make_dev(&hpt_cdevsw, DRIVER_MINOR, UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "%s", driver_name); } #if defined(KLD_MODULE) && (__FreeBSD_version >= 503000) typedef struct driverlink *driverlink_t; struct driverlink { kobj_class_t driver; TAILQ_ENTRY(driverlink) link; /* list of drivers in devclass */ }; typedef TAILQ_HEAD(driver_list, driverlink) driver_list_t; struct devclass { TAILQ_ENTRY(devclass) link; devclass_t parent; /* parent in devclass hierarchy */ driver_list_t drivers; /* bus devclasses store drivers for bus */ char *name; device_t *devices; /* array of devices indexed by unit */ int maxunit; /* size of devices array */ }; static void override_kernel_driver(void) { driverlink_t dl, dlfirst; driver_t *tmpdriver; devclass_t dc = devclass_find("pci"); if (dc){ dlfirst = TAILQ_FIRST(&dc->drivers); for (dl = dlfirst; dl; dl = TAILQ_NEXT(dl, link)) { if(strcmp(dl->driver->name, driver_name) == 0) { tmpdriver=dl->driver; dl->driver=dlfirst->driver; dlfirst->driver=tmpdriver; break; } } } } #else #define override_kernel_driver() #endif static void hpt_init(void *dummy) { if (bootverbose) os_printk("%s %s", driver_name_long, driver_ver); override_kernel_driver(); init_config(); hpt_ich.ich_func = hpt_final_init; hpt_ich.ich_arg = NULL; if (config_intrhook_establish(&hpt_ich) != 0) { printf("%s: cannot establish configuration hook\n", driver_name_long); } } SYSINIT(hptinit, SI_SUB_CONFIGURE, SI_ORDER_FIRST, hpt_init, NULL); /* * CAM driver interface */ static device_method_t driver_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hpt_probe), DEVMETHOD(device_attach, hpt_attach), DEVMETHOD(device_detach, hpt_detach), DEVMETHOD(device_shutdown, hpt_shutdown), { 0, 0 } }; static driver_t hpt_pci_driver = { driver_name, driver_methods, sizeof(HBA) }; static devclass_t hpt_devclass; #ifndef TARGETNAME #error "no TARGETNAME found" #endif /* use this to make TARGETNAME be expanded */ #define __DRIVER_MODULE(p1, p2, p3, p4, p5, p6) DRIVER_MODULE(p1, p2, p3, p4, p5, p6) #define __MODULE_VERSION(p1, p2) MODULE_VERSION(p1, p2) #define __MODULE_DEPEND(p1, p2, p3, p4, p5) MODULE_DEPEND(p1, p2, p3, p4, p5) __DRIVER_MODULE(TARGETNAME, pci, hpt_pci_driver, hpt_devclass, 0, 0); __MODULE_VERSION(TARGETNAME, 1); __MODULE_DEPEND(TARGETNAME, cam, 1, 1, 1); #if __FreeBSD_version>503000 typedef struct cdev * ioctl_dev_t; #else typedef dev_t ioctl_dev_t; #endif #if __FreeBSD_version >= 500000 typedef struct thread * ioctl_thread_t; #else typedef struct proc * ioctl_thread_t; #endif static int hpt_open(ioctl_dev_t dev, int flags, int devtype, ioctl_thread_t td) { return 0; } static int hpt_close(ioctl_dev_t dev, int flags, int devtype, ioctl_thread_t td) { return 0; } static int hpt_ioctl(ioctl_dev_t dev, u_long cmd, caddr_t data, int fflag, ioctl_thread_t td) { PHPT_IOCTL_PARAM piop=(PHPT_IOCTL_PARAM)data; IOCTL_ARG ioctl_args; HPT_U32 bytesReturned; switch (cmd){ case HPT_DO_IOCONTROL: { if (piop->Magic == HPT_IOCTL_MAGIC || piop->Magic == HPT_IOCTL_MAGIC32) { KdPrint(("ioctl=%x in=%p len=%d out=%p len=%d\n", piop->dwIoControlCode, piop->lpInBuffer, piop->nInBufferSize, piop->lpOutBuffer, piop->nOutBufferSize)); memset(&ioctl_args, 0, sizeof(ioctl_args)); ioctl_args.dwIoControlCode = piop->dwIoControlCode; ioctl_args.nInBufferSize = piop->nInBufferSize; ioctl_args.nOutBufferSize = piop->nOutBufferSize; ioctl_args.lpBytesReturned = &bytesReturned; if (ioctl_args.nInBufferSize) { ioctl_args.lpInBuffer = malloc(ioctl_args.nInBufferSize, M_DEVBUF, M_WAITOK); if (!ioctl_args.lpInBuffer) goto invalid; if (copyin((void*)piop->lpInBuffer, ioctl_args.lpInBuffer, piop->nInBufferSize)) goto invalid; } if (ioctl_args.nOutBufferSize) { ioctl_args.lpOutBuffer = malloc(ioctl_args.nOutBufferSize, M_DEVBUF, M_WAITOK); if (!ioctl_args.lpOutBuffer) goto invalid; } #if (__FreeBSD_version >= 500000) mtx_lock(&Giant); #endif hpt_do_ioctl(&ioctl_args); #if (__FreeBSD_version >= 500000) mtx_unlock(&Giant); #endif if (ioctl_args.result==HPT_IOCTL_RESULT_OK) { if (piop->nOutBufferSize) { if (copyout(ioctl_args.lpOutBuffer, (void*)piop->lpOutBuffer, piop->nOutBufferSize)) goto invalid; } if (piop->lpBytesReturned) { if (copyout(&bytesReturned, (void*)piop->lpBytesReturned, sizeof(HPT_U32))) goto invalid; } if (ioctl_args.lpInBuffer) free(ioctl_args.lpInBuffer, M_DEVBUF); if (ioctl_args.lpOutBuffer) free(ioctl_args.lpOutBuffer, M_DEVBUF); return 0; } invalid: if (ioctl_args.lpInBuffer) free(ioctl_args.lpInBuffer, M_DEVBUF); if (ioctl_args.lpOutBuffer) free(ioctl_args.lpOutBuffer, M_DEVBUF); return EFAULT; } return EFAULT; } case HPT_SCAN_BUS: { return hpt_rescan_bus(); } default: KdPrint(("invalid command!")); return EFAULT; } } static int hpt_rescan_bus(void) { union ccb *ccb; PVBUS vbus; PVBUS_EXT vbus_ext; mtx_lock(&Giant); ldm_for_each_vbus(vbus, vbus_ext) { if ((ccb = xpt_alloc_ccb()) == NULL) { return(ENOMEM); } if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(vbus_ext->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return(EIO); } xpt_rescan(ccb); } mtx_unlock(&Giant); return(0); } diff --git a/sys/dev/hptnr/os_bsd.h b/sys/dev/hptnr/os_bsd.h index bdf9be7295da..8fda2677c7cb 100644 --- a/sys/dev/hptnr/os_bsd.h +++ b/sys/dev/hptnr/os_bsd.h @@ -1,266 +1,267 @@ /* $Id: os_bsd.h,v 1.20 2010/05/11 03:12:11 lcn Exp $ */ /*- * HighPoint RAID Driver for FreeBSD * Copyright (C) 2005-2011 HighPoint Technologies, Inc. All Rights Reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #ifndef _OS_BSD_H #define _OS_BSD_H #ifndef DBG #define DBG 0 #endif #include #include #include #if (__FreeBSD_version >= 500000) #include #include #else #include /*to support DELAY function under 4.x BSD versions*/ #endif #include #include #include #include #include #if (__FreeBSD_version >= 500000) #include #include #include #endif #include #include #include #include #include #if __FreeBSD_version >= 500043 #include #endif #include #include #include #include #include #if (__FreeBSD_version >= 500000) #include #include #else #include #include #endif #if (__FreeBSD_version <= 500043) #include #endif #include #include #include #include #include #include #include #include #include #if (__FreeBSD_version < 500043) #include #endif typedef struct _INQUIRYDATA { u_char DeviceType : 5; u_char DeviceTypeQualifier : 3; u_char DeviceTypeModifier : 7; u_char RemovableMedia : 1; u_char Versions; u_char ResponseDataFormat; u_char AdditionalLength; u_char Reserved[2]; u_char SoftReset : 1; u_char CommandQueue : 1; u_char Reserved2 : 1; u_char LinkedCommands : 1; u_char Synchronous : 1; u_char Wide16Bit : 1; u_char Wide32Bit : 1; u_char RelativeAddressing : 1; u_char VendorId[8]; u_char ProductId[16]; u_char ProductRevisionLevel[4]; u_char VendorSpecific[20]; u_char Reserved3[40]; } __attribute__((packed)) INQUIRYDATA, *PINQUIRYDATA; #endif /* private headers */ #include #include #include /* driver parameters */ extern const char driver_name[]; extern const char driver_name_long[]; extern const char driver_ver[]; extern int osm_max_targets; /* * adapter/vbus extensions: * each physical controller has an adapter_ext, passed to him.create_adapter() * each vbus has a vbus_ext passed to ldm_create_vbus(). */ #define EXT_TYPE_HBA 1 #define EXT_TYPE_VBUS 2 typedef struct _hba { int ext_type; LDM_ADAPTER ldm_adapter; device_t pcidev; PCI_ADDRESS pciaddr; struct _vbus_ext *vbus_ext; struct _hba *next; struct { struct resource *res; int type; int rid; void *base; } pcibar[6]; struct resource *irq_res; void *irq_handle; } HBA, *PHBA; typedef struct _os_cmdext { struct _vbus_ext *vbus_ext; struct _os_cmdext *next; union ccb *ccb; bus_dmamap_t dma_map; + struct callout_handle timeout_ch; SG psg[os_max_sg_descriptors]; } OS_CMDEXT, *POS_CMDEXT; typedef struct _vbus_ext { int ext_type; struct _vbus_ext *next; PHBA hba_list; struct freelist *freelist_head; struct freelist *freelist_dma_head; struct cam_sim *sim; /* sim for this vbus */ struct cam_path *path; /* peripheral, path, tgt, lun with this vbus */ #if (__FreeBSD_version >= 500000) struct mtx lock; /* general purpose lock */ #else int hpt_splx; #endif bus_dma_tag_t io_dmat; /* I/O buffer DMA tag */ POS_CMDEXT cmdext_list; OSM_TASK *tasks; struct task worker; struct callout_handle timer; eventhandler_tag shutdown_eh; /* the LDM vbus instance continues */ unsigned long vbus[0] __attribute__((aligned(sizeof(unsigned long)))); } VBUS_EXT, *PVBUS_EXT; #if __FreeBSD_version >= 500000 #define hpt_lock_vbus(vbus_ext) mtx_lock(&(vbus_ext)->lock) #define hpt_unlock_vbus(vbus_ext) mtx_unlock(&(vbus_ext)->lock) #else static __inline void hpt_lock_vbus(PVBUS_EXT vbus_ext) { vbus_ext->hpt_splx = splcam(); } static __inline void hpt_unlock_vbus(PVBUS_EXT vbus_ext) { splx(vbus_ext->hpt_splx); } #endif #define HPT_OSM_TIMEOUT (20*hz) /* timeout value for OS commands */ #define HPT_DO_IOCONTROL _IOW('H', 0, HPT_IOCTL_PARAM) #define HPT_SCAN_BUS _IO('H', 1) #if __FreeBSD_version >= 501000 #define TASK_ENQUEUE(task) taskqueue_enqueue(taskqueue_swi_giant,(task)); #else #define TASK_ENQUEUE(task) taskqueue_enqueue(taskqueue_swi,(task)); #endif #if __FreeBSD_version >= 500000 static __inline int hpt_sleep(PVBUS_EXT vbus_ext, void *ident, int priority, const char *wmesg, int timo) { return msleep(ident, &vbus_ext->lock, priority, wmesg, timo); } #else static __inline int hpt_sleep(PVBUS_EXT vbus_ext, void *ident, int priority, const char *wmesg, int timo) { int retval = 0; asleep(ident, priority, wmesg, timo); hpt_unlock_vbus(vbus_ext); retval = await(priority, timo); hpt_lock_vbus(vbus_ext); return retval; } #endif #if __FreeBSD_version < 501000 #define READ_16 0x88 #define WRITE_16 0x8a #define SERVICE_ACTION_IN 0x9e #endif #define HPT_DEV_MAJOR 200 diff --git a/sys/dev/hptrr/hptrr_osm_bsd.c b/sys/dev/hptrr/hptrr_osm_bsd.c index 9af256e0d9e8..3d83de60477b 100644 --- a/sys/dev/hptrr/hptrr_osm_bsd.c +++ b/sys/dev/hptrr/hptrr_osm_bsd.c @@ -1,1373 +1,1374 @@ /* * Copyright (c) HighPoint Technologies, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include /* $Id: osm_bsd.c,v 1.27 2007/11/22 07:35:49 gmm Exp $ * * HighPoint RAID Driver for FreeBSD * Copyright (C) 2005 HighPoint Technologies, Inc. All Rights Reserved. */ #include #include static int attach_generic = 0; TUNABLE_INT("hw.hptrr.attach_generic", &attach_generic); static int hpt_probe(device_t dev) { PCI_ID pci_id; HIM *him; int i; PHBA hba; /* Some of supported chips are used not only by HPT. */ if (pci_get_vendor(dev) != 0x1103 && !attach_generic) return (ENXIO); for (him = him_list; him; him = him->next) { for (i=0; him->get_supported_device_id(i, &pci_id); i++) { if ((pci_get_vendor(dev) == pci_id.vid) && (pci_get_device(dev) == pci_id.did)){ KdPrint(("hpt_probe: adapter at PCI %d:%d:%d, IRQ %d", pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev), pci_get_irq(dev) )); device_set_desc(dev, him->name); hba = (PHBA)device_get_softc(dev); memset(hba, 0, sizeof(HBA)); hba->ext_type = EXT_TYPE_HBA; hba->ldm_adapter.him = him; return 0; } } } return (ENXIO); } static int hpt_attach(device_t dev) { PHBA hba = (PHBA)device_get_softc(dev); HIM *him = hba->ldm_adapter.him; PCI_ID pci_id; HPT_UINT size; PVBUS vbus; PVBUS_EXT vbus_ext; KdPrint(("hpt_attach(%d/%d/%d)", pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev))); #if __FreeBSD_version >=440000 pci_enable_busmaster(dev); #endif pci_id.vid = pci_get_vendor(dev); pci_id.did = pci_get_device(dev); pci_id.rev = pci_get_revid(dev); size = him->get_adapter_size(&pci_id); hba->ldm_adapter.him_handle = malloc(size, M_DEVBUF, M_WAITOK); if (!hba->ldm_adapter.him_handle) return ENXIO; hba->pcidev = dev; hba->pciaddr.tree = 0; hba->pciaddr.bus = pci_get_bus(dev); hba->pciaddr.device = pci_get_slot(dev); hba->pciaddr.function = pci_get_function(dev); if (!him->create_adapter(&pci_id, hba->pciaddr, hba->ldm_adapter.him_handle, hba)) { free(hba->ldm_adapter.him_handle, M_DEVBUF); return -1; } os_printk("adapter at PCI %d:%d:%d, IRQ %d", hba->pciaddr.bus, hba->pciaddr.device, hba->pciaddr.function, pci_get_irq(dev)); if (!ldm_register_adapter(&hba->ldm_adapter)) { size = ldm_get_vbus_size(); vbus_ext = malloc(sizeof(VBUS_EXT) + size, M_DEVBUF, M_WAITOK); if (!vbus_ext) { free(hba->ldm_adapter.him_handle, M_DEVBUF); return -1; } memset(vbus_ext, 0, sizeof(VBUS_EXT)); vbus_ext->ext_type = EXT_TYPE_VBUS; ldm_create_vbus((PVBUS)vbus_ext->vbus, vbus_ext); ldm_register_adapter(&hba->ldm_adapter); } ldm_for_each_vbus(vbus, vbus_ext) { if (hba->ldm_adapter.vbus==vbus) { hba->vbus_ext = vbus_ext; hba->next = vbus_ext->hba_list; vbus_ext->hba_list = hba; break; } } return 0; } /* * Maybe we'd better to use the bus_dmamem_alloc to alloc DMA memory, * but there are some problems currently (alignment, etc). */ static __inline void *__get_free_pages(int order) { /* don't use low memory - other devices may get starved */ return contigmalloc(PAGE_SIZE<hba_list; hba; hba = hba->next) hba->ldm_adapter.him->get_meminfo(hba->ldm_adapter.him_handle); ldm_get_mem_info((PVBUS)vbus_ext->vbus, 0); for (f=vbus_ext->freelist_head; f; f=f->next) { KdPrint(("%s: %d*%d=%d bytes", f->tag, f->count, f->size, f->count*f->size)); for (i=0; icount; i++) { p = (void **)malloc(f->size, M_DEVBUF, M_WAITOK); if (!p) return (ENXIO); *p = f->head; f->head = p; } } for (f=vbus_ext->freelist_dma_head; f; f=f->next) { int order, size, j; HPT_ASSERT((f->size & (f->alignment-1))==0); for (order=0, size=PAGE_SIZE; sizesize; order++, size<<=1) ; KdPrint(("%s: %d*%d=%d bytes, order %d", f->tag, f->count, f->size, f->count*f->size, order)); HPT_ASSERT(f->alignment<=PAGE_SIZE); for (i=0; icount;) { p = (void **)__get_free_pages(order); if (!p) return -1; for (j = size/f->size; j && icount; i++,j--) { *p = f->head; *(BUS_ADDRESS *)(p+1) = (BUS_ADDRESS)vtophys(p); f->head = p; p = (void **)((unsigned long)p + f->size); } } } HPT_ASSERT(PAGE_SIZE==DMAPOOL_PAGE_SIZE); for (i=0; ivbus, p, (BUS_ADDRESS)vtophys(p)); } return 0; } static void hpt_free_mem(PVBUS_EXT vbus_ext) { struct freelist *f; void *p; int i; BUS_ADDRESS bus; for (f=vbus_ext->freelist_head; f; f=f->next) { #if DBG if (f->count!=f->reserved_count) { KdPrint(("memory leak for freelist %s (%d/%d)", f->tag, f->count, f->reserved_count)); } #endif while ((p=freelist_get(f))) free(p, M_DEVBUF); } for (i=0; ivbus, &bus); HPT_ASSERT(p); free_pages(p, 0); } for (f=vbus_ext->freelist_dma_head; f; f=f->next) { int order, size; #if DBG if (f->count!=f->reserved_count) { KdPrint(("memory leak for dma freelist %s (%d/%d)", f->tag, f->count, f->reserved_count)); } #endif for (order=0, size=PAGE_SIZE; sizesize; order++, size<<=1) ; while ((p=freelist_get_dma(f, &bus))) { if (order) free_pages(p, order); else { /* can't free immediately since other blocks in this page may still be in the list */ if (((HPT_UPTR)p & (PAGE_SIZE-1))==0) dmapool_put_page((PVBUS)vbus_ext->vbus, p, bus); } } } while ((p = dmapool_get_page((PVBUS)vbus_ext->vbus, &bus))) free_pages(p, 0); } static int hpt_init_vbus(PVBUS_EXT vbus_ext) { PHBA hba; for (hba = vbus_ext->hba_list; hba; hba = hba->next) if (!hba->ldm_adapter.him->initialize(hba->ldm_adapter.him_handle)) { KdPrint(("fail to initialize %p", hba)); return -1; } ldm_initialize_vbus((PVBUS)vbus_ext->vbus, &vbus_ext->hba_list->ldm_adapter); return 0; } static void hpt_flush_done(PCOMMAND pCmd) { PVDEV vd = pCmd->target; if (mIsArray(vd->type) && vd->u.array.transform && vd!=vd->u.array.transform->target) { vd = vd->u.array.transform->target; HPT_ASSERT(vd); pCmd->target = vd; pCmd->Result = RETURN_PENDING; vdev_queue_cmd(pCmd); return; } *(int *)pCmd->priv = 1; wakeup(pCmd); } /* * flush a vdev (without retry). */ static int hpt_flush_vdev(PVBUS_EXT vbus_ext, PVDEV vd) { PCOMMAND pCmd; int result = 0, done; HPT_UINT count; KdPrint(("flusing dev %p", vd)); hpt_lock_vbus(vbus_ext); if (mIsArray(vd->type) && vd->u.array.transform) count = MAX(vd->u.array.transform->source->cmds_per_request, vd->u.array.transform->target->cmds_per_request); else count = vd->cmds_per_request; pCmd = ldm_alloc_cmds(vd->vbus, count); if (!pCmd) { hpt_unlock_vbus(vbus_ext); return -1; } pCmd->type = CMD_TYPE_FLUSH; pCmd->flags.hard_flush = 1; pCmd->target = vd; pCmd->done = hpt_flush_done; done = 0; pCmd->priv = &done; ldm_queue_cmd(pCmd); if (!done) { while (hpt_sleep(vbus_ext, pCmd, PPAUSE, "hptfls", HPT_OSM_TIMEOUT)) { ldm_reset_vbus(vd->vbus); } } KdPrint(("flush result %d", pCmd->Result)); if (pCmd->Result!=RETURN_SUCCESS) result = -1; ldm_free_cmds(pCmd); hpt_unlock_vbus(vbus_ext); return result; } static void hpt_stop_tasks(PVBUS_EXT vbus_ext); static void hpt_shutdown_vbus(PVBUS_EXT vbus_ext, int howto) { PVBUS vbus = (PVBUS)vbus_ext->vbus; PHBA hba; int i; KdPrint(("hpt_shutdown_vbus")); /* stop all ctl tasks and disable the worker taskqueue */ hpt_stop_tasks(vbus_ext); vbus_ext->worker.ta_context = 0; /* flush devices */ for (i=0; ihba_list; hba; hba=hba->next) bus_teardown_intr(hba->pcidev, hba->irq_res, hba->irq_handle); hpt_free_mem(vbus_ext); while ((hba=vbus_ext->hba_list)) { vbus_ext->hba_list = hba->next; free(hba->ldm_adapter.him_handle, M_DEVBUF); } free(vbus_ext, M_DEVBUF); KdPrint(("hpt_shutdown_vbus done")); } static void __hpt_do_tasks(PVBUS_EXT vbus_ext) { OSM_TASK *tasks; tasks = vbus_ext->tasks; vbus_ext->tasks = 0; while (tasks) { OSM_TASK *t = tasks; tasks = t->next; t->next = 0; t->func(vbus_ext->vbus, t->data); } } static void hpt_do_tasks(PVBUS_EXT vbus_ext, int pending) { if(vbus_ext){ hpt_lock_vbus(vbus_ext); __hpt_do_tasks(vbus_ext); hpt_unlock_vbus(vbus_ext); } } static void hpt_action(struct cam_sim *sim, union ccb *ccb); static void hpt_poll(struct cam_sim *sim); static void hpt_async(void * callback_arg, u_int32_t code, struct cam_path * path, void * arg); static void hpt_pci_intr(void *arg); static __inline POS_CMDEXT cmdext_get(PVBUS_EXT vbus_ext) { POS_CMDEXT p = vbus_ext->cmdext_list; if (p) vbus_ext->cmdext_list = p->next; return p; } static __inline void cmdext_put(POS_CMDEXT p) { p->next = p->vbus_ext->cmdext_list; p->vbus_ext->cmdext_list = p; } static void hpt_timeout(void *arg) { PCOMMAND pCmd = (PCOMMAND)arg; POS_CMDEXT ext = (POS_CMDEXT)pCmd->priv; KdPrint(("pCmd %p timeout", pCmd)); ldm_reset_vbus((PVBUS)ext->vbus_ext->vbus); } static void os_cmddone(PCOMMAND pCmd) { POS_CMDEXT ext = (POS_CMDEXT)pCmd->priv; union ccb *ccb = ext->ccb; KdPrint(("os_cmddone(%p, %d)", pCmd, pCmd->Result)); - untimeout(hpt_timeout, pCmd, ccb->ccb_h.timeout_ch); + untimeout(hpt_timeout, pCmd, ext->timeout_ch); switch(pCmd->Result) { case RETURN_SUCCESS: ccb->ccb_h.status = CAM_REQ_CMP; break; case RETURN_BAD_DEVICE: ccb->ccb_h.status = CAM_DEV_NOT_THERE; break; case RETURN_DEVICE_BUSY: ccb->ccb_h.status = CAM_BUSY; break; case RETURN_INVALID_REQUEST: ccb->ccb_h.status = CAM_REQ_INVALID; break; case RETURN_SELECTION_TIMEOUT: ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; case RETURN_RETRY: ccb->ccb_h.status = CAM_BUSY; break; default: ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; } if (pCmd->flags.data_in) { bus_dmamap_sync(ext->vbus_ext->io_dmat, ext->dma_map, BUS_DMASYNC_POSTREAD); } else if (pCmd->flags.data_out) { bus_dmamap_sync(ext->vbus_ext->io_dmat, ext->dma_map, BUS_DMASYNC_POSTWRITE); } bus_dmamap_unload(ext->vbus_ext->io_dmat, ext->dma_map); cmdext_put(ext); ldm_free_cmds(pCmd); xpt_done(ccb); } static int os_buildsgl(PCOMMAND pCmd, PSG pSg, int logical) { /* since we have provided physical sg, nobody will ask us to build physical sg */ HPT_ASSERT(0); return FALSE; } static void hpt_io_dmamap_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { PCOMMAND pCmd = (PCOMMAND)arg; POS_CMDEXT ext = (POS_CMDEXT)pCmd->priv; PSG psg = pCmd->psg; int idx; HPT_ASSERT(pCmd->flags.physical_sg); if (error) panic("busdma error"); HPT_ASSERT(nsegs<=os_max_sg_descriptors); if (nsegs != 0) { for (idx = 0; idx < nsegs; idx++, psg++) { psg->addr.bus = segs[idx].ds_addr; psg->size = segs[idx].ds_len; psg->eot = 0; } psg[-1].eot = 1; if (pCmd->flags.data_in) { bus_dmamap_sync(ext->vbus_ext->io_dmat, ext->dma_map, BUS_DMASYNC_PREREAD); } else if (pCmd->flags.data_out) { bus_dmamap_sync(ext->vbus_ext->io_dmat, ext->dma_map, BUS_DMASYNC_PREWRITE); } } - ext->ccb->ccb_h.timeout_ch = timeout(hpt_timeout, pCmd, HPT_OSM_TIMEOUT); + ext->timeout_ch = timeout(hpt_timeout, pCmd, HPT_OSM_TIMEOUT); ldm_queue_cmd(pCmd); } static void hpt_scsi_io(PVBUS_EXT vbus_ext, union ccb *ccb) { PVBUS vbus = (PVBUS)vbus_ext->vbus; PVDEV vd; PCOMMAND pCmd; POS_CMDEXT ext; HPT_U8 *cdb; if (ccb->ccb_h.flags & CAM_CDB_POINTER) cdb = ccb->csio.cdb_io.cdb_ptr; else cdb = ccb->csio.cdb_io.cdb_bytes; KdPrint(("hpt_scsi_io: ccb %x id %d lun %d cdb %x-%x-%x", ccb, ccb->ccb_h.target_id, ccb->ccb_h.target_lun, *(HPT_U32 *)&cdb[0], *(HPT_U32 *)&cdb[4], *(HPT_U32 *)&cdb[8] )); /* ccb->ccb_h.path_id is not our bus id - don't check it */ if (ccb->ccb_h.target_lun != 0 || ccb->ccb_h.target_id >= osm_max_targets || (ccb->ccb_h.flags & CAM_CDB_PHYS)) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return; } vd = ldm_find_target(vbus, ccb->ccb_h.target_id); if (!vd) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return; } switch (cdb[0]) { case TEST_UNIT_READY: case START_STOP_UNIT: case SYNCHRONIZE_CACHE: ccb->ccb_h.status = CAM_REQ_CMP; break; case INQUIRY: { PINQUIRYDATA inquiryData; memset(ccb->csio.data_ptr, 0, ccb->csio.dxfer_len); inquiryData = (PINQUIRYDATA)ccb->csio.data_ptr; inquiryData->AdditionalLength = 31; inquiryData->CommandQueue = 1; memcpy(&inquiryData->VendorId, "HPT ", 8); memcpy(&inquiryData->ProductId, "DISK 0_0 ", 16); if (vd->target_id / 10) { inquiryData->ProductId[7] = (vd->target_id % 100) / 10 + '0'; inquiryData->ProductId[8] = (vd->target_id % 100) % 10 + '0'; } else inquiryData->ProductId[7] = (vd->target_id % 100) % 10 + '0'; memcpy(&inquiryData->ProductRevisionLevel, "4.00", 4); ccb->ccb_h.status = CAM_REQ_CMP; } break; case READ_CAPACITY: { HPT_U8 *rbuf = ccb->csio.data_ptr; HPT_U32 cap; if (vd->capacity>0xfffffffful) cap = 0xfffffffful; else cap = vd->capacity - 1; rbuf[0] = (HPT_U8)(cap>>24); rbuf[1] = (HPT_U8)(cap>>16); rbuf[2] = (HPT_U8)(cap>>8); rbuf[3] = (HPT_U8)cap; rbuf[4] = 0; rbuf[5] = 0; rbuf[6] = 2; rbuf[7] = 0; ccb->ccb_h.status = CAM_REQ_CMP; break; } case SERVICE_ACTION_IN: { HPT_U8 *rbuf = ccb->csio.data_ptr; HPT_U64 cap = vd->capacity - 1; rbuf[0] = (HPT_U8)(cap>>56); rbuf[1] = (HPT_U8)(cap>>48); rbuf[2] = (HPT_U8)(cap>>40); rbuf[3] = (HPT_U8)(cap>>32); rbuf[4] = (HPT_U8)(cap>>24); rbuf[5] = (HPT_U8)(cap>>16); rbuf[6] = (HPT_U8)(cap>>8); rbuf[7] = (HPT_U8)cap; rbuf[8] = 0; rbuf[9] = 0; rbuf[10] = 2; rbuf[11] = 0; ccb->ccb_h.status = CAM_REQ_CMP; break; } case READ_6: case READ_10: case READ_16: case WRITE_6: case WRITE_10: case WRITE_16: case 0x13: case 0x2f: { int error; pCmd = ldm_alloc_cmds(vbus, vd->cmds_per_request); if(!pCmd){ KdPrint(("Failed to allocate command!")); ccb->ccb_h.status = CAM_BUSY; break; } switch (cdb[0]) { case READ_6: case WRITE_6: case 0x13: pCmd->uCmd.Ide.Lba = ((HPT_U32)cdb[1] << 16) | ((HPT_U32)cdb[2] << 8) | (HPT_U32)cdb[3]; pCmd->uCmd.Ide.nSectors = (HPT_U16) cdb[4]; break; case READ_16: case WRITE_16: { HPT_U64 block = ((HPT_U64)cdb[2]<<56) | ((HPT_U64)cdb[3]<<48) | ((HPT_U64)cdb[4]<<40) | ((HPT_U64)cdb[5]<<32) | ((HPT_U64)cdb[6]<<24) | ((HPT_U64)cdb[7]<<16) | ((HPT_U64)cdb[8]<<8) | ((HPT_U64)cdb[9]); pCmd->uCmd.Ide.Lba = block; pCmd->uCmd.Ide.nSectors = (HPT_U16)cdb[13] | ((HPT_U16)cdb[12]<<8); break; } default: pCmd->uCmd.Ide.Lba = (HPT_U32)cdb[5] | ((HPT_U32)cdb[4] << 8) | ((HPT_U32)cdb[3] << 16) | ((HPT_U32)cdb[2] << 24); pCmd->uCmd.Ide.nSectors = (HPT_U16) cdb[8] | ((HPT_U16)cdb[7]<<8); break; } switch (cdb[0]) { case READ_6: case READ_10: case READ_16: pCmd->flags.data_in = 1; break; case WRITE_6: case WRITE_10: case WRITE_16: pCmd->flags.data_out = 1; break; } pCmd->priv = ext = cmdext_get(vbus_ext); HPT_ASSERT(ext); ext->ccb = ccb; pCmd->target = vd; pCmd->done = os_cmddone; pCmd->buildsgl = os_buildsgl; pCmd->psg = ext->psg; pCmd->flags.physical_sg = 1; error = bus_dmamap_load_ccb(vbus_ext->io_dmat, ext->dma_map, ccb, hpt_io_dmamap_callback, pCmd, BUS_DMA_WAITOK ); KdPrint(("bus_dmamap_load return %d", error)); if (error && error!=EINPROGRESS) { os_printk("bus_dmamap_load error %d", error); cmdext_put(ext); ldm_free_cmds(pCmd); ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(ccb); } return; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); return; } static void hpt_action(struct cam_sim *sim, union ccb *ccb) { PVBUS_EXT vbus_ext = (PVBUS_EXT)cam_sim_softc(sim); KdPrint(("hpt_action(fn=%d, id=%d)", ccb->ccb_h.func_code, ccb->ccb_h.target_id)); switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: hpt_lock_vbus(vbus_ext); hpt_scsi_io(vbus_ext, ccb); hpt_unlock_vbus(vbus_ext); return; case XPT_RESET_BUS: hpt_lock_vbus(vbus_ext); ldm_reset_vbus((PVBUS)vbus_ext->vbus); hpt_unlock_vbus(vbus_ext); break; case XPT_GET_TRAN_SETTINGS: case XPT_SET_TRAN_SETTINGS: ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; break; case XPT_CALC_GEOMETRY: #if __FreeBSD_version >= 500000 cam_calc_geometry(&ccb->ccg, 1); #else ccb->ccg.heads = 255; ccb->ccg.secs_per_track = 63; ccb->ccg.cylinders = ccb->ccg.volume_size / (ccb->ccg.heads * ccb->ccg.secs_per_track); ccb->ccb_h.status = CAM_REQ_CMP; #endif break; case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_SDTR_ABLE; cpi->target_sprt = 0; cpi->hba_misc = PIM_NOBUSRESET; cpi->hba_eng_cnt = 0; cpi->max_target = osm_max_targets; cpi->max_lun = 0; cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = cam_sim_bus(sim); cpi->initiator_id = osm_max_targets; cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "HPT ", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->ccb_h.status = CAM_REQ_CMP; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); return; } static void hpt_pci_intr(void *arg) { PVBUS_EXT vbus_ext = (PVBUS_EXT)arg; hpt_lock_vbus(vbus_ext); ldm_intr((PVBUS)vbus_ext->vbus); hpt_unlock_vbus(vbus_ext); } static void hpt_poll(struct cam_sim *sim) { hpt_pci_intr(cam_sim_softc(sim)); } static void hpt_async(void * callback_arg, u_int32_t code, struct cam_path * path, void * arg) { KdPrint(("hpt_async")); } static int hpt_shutdown(device_t dev) { KdPrint(("hpt_shutdown(dev=%p)", dev)); return 0; } static int hpt_detach(device_t dev) { /* we don't allow the driver to be unloaded. */ return EBUSY; } static void hpt_ioctl_done(struct _IOCTL_ARG *arg) { arg->ioctl_cmnd = 0; wakeup(arg); } static void __hpt_do_ioctl(PVBUS_EXT vbus_ext, IOCTL_ARG *ioctl_args) { ioctl_args->result = -1; ioctl_args->done = hpt_ioctl_done; ioctl_args->ioctl_cmnd = (void *)1; hpt_lock_vbus(vbus_ext); ldm_ioctl((PVBUS)vbus_ext->vbus, ioctl_args); while (ioctl_args->ioctl_cmnd) { if (hpt_sleep(vbus_ext, ioctl_args, PPAUSE, "hptctl", HPT_OSM_TIMEOUT)==0) break; ldm_reset_vbus((PVBUS)vbus_ext->vbus); __hpt_do_tasks(vbus_ext); } /* KdPrint(("ioctl %x result %d", ioctl_args->dwIoControlCode, ioctl_args->result)); */ hpt_unlock_vbus(vbus_ext); } static void hpt_do_ioctl(IOCTL_ARG *ioctl_args) { PVBUS vbus; PVBUS_EXT vbus_ext; ldm_for_each_vbus(vbus, vbus_ext) { __hpt_do_ioctl(vbus_ext, ioctl_args); if (ioctl_args->result!=HPT_IOCTL_RESULT_WRONG_VBUS) return; } } #define HPT_DO_IOCTL(code, inbuf, insize, outbuf, outsize) ({\ IOCTL_ARG arg;\ arg.dwIoControlCode = code;\ arg.lpInBuffer = inbuf;\ arg.lpOutBuffer = outbuf;\ arg.nInBufferSize = insize;\ arg.nOutBufferSize = outsize;\ arg.lpBytesReturned = 0;\ hpt_do_ioctl(&arg);\ arg.result;\ }) #define DEVICEID_VALID(id) ((id) && ((HPT_U32)(id)!=0xffffffff)) static int hpt_get_logical_devices(DEVICEID * pIds, int nMaxCount) { int i; HPT_U32 count = nMaxCount-1; if (HPT_DO_IOCTL(HPT_IOCTL_GET_LOGICAL_DEVICES, &count, sizeof(HPT_U32), pIds, sizeof(DEVICEID)*nMaxCount)) return -1; nMaxCount = (int)pIds[0]; for (i=0; i=503000 .d_version = D_VERSION, #endif #if (__FreeBSD_version>=503000 && __FreeBSD_version<600034) .d_flags = D_NEEDGIANT, #endif #if __FreeBSD_version<600034 #if __FreeBSD_version>501000 .d_maj = MAJOR_AUTO, #else .d_maj = HPT_DEV_MAJOR, #endif #endif }; static struct intr_config_hook hpt_ich; /* * hpt_final_init will be called after all hpt_attach. */ static void hpt_final_init(void *dummy) { int i; PVBUS_EXT vbus_ext; PVBUS vbus; PHBA hba; /* Clear the config hook */ config_intrhook_disestablish(&hpt_ich); /* allocate memory */ i = 0; ldm_for_each_vbus(vbus, vbus_ext) { if (hpt_alloc_mem(vbus_ext)) { os_printk("out of memory"); return; } i++; } if (!i) { if (bootverbose) os_printk("no controller detected."); return; } /* initializing hardware */ ldm_for_each_vbus(vbus, vbus_ext) { /* make timer available here */ callout_handle_init(&vbus_ext->timer); if (hpt_init_vbus(vbus_ext)) { os_printk("fail to initialize hardware"); break; /* FIXME */ } } /* register CAM interface */ ldm_for_each_vbus(vbus, vbus_ext) { struct cam_devq *devq; struct ccb_setasync ccb; #if (__FreeBSD_version >= 500000) mtx_init(&vbus_ext->lock, "hptsleeplock", NULL, MTX_DEF); #endif if (bus_dma_tag_create(NULL,/* parent */ 4, /* alignment */ BUS_SPACE_MAXADDR_32BIT+1, /* boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ PAGE_SIZE * (os_max_sg_descriptors-1), /* maxsize */ os_max_sg_descriptors, /* nsegments */ 0x10000, /* maxsegsize */ BUS_DMA_WAITOK, /* flags */ #if __FreeBSD_version>502000 busdma_lock_mutex, /* lockfunc */ &vbus_ext->lock, /* lockfuncarg */ #endif &vbus_ext->io_dmat /* tag */)) { return ; } for (i=0; ivbus_ext = vbus_ext; ext->next = vbus_ext->cmdext_list; vbus_ext->cmdext_list = ext; if (bus_dmamap_create(vbus_ext->io_dmat, 0, &ext->dma_map)) { os_printk("Can't create dma map(%d)", i); return ; } + callout_handle_init(&ext->timeout_ch); } if ((devq = cam_simq_alloc(os_max_queue_comm)) == NULL) { os_printk("cam_simq_alloc failed"); return ; } #if __FreeBSD_version > 700025 vbus_ext->sim = cam_sim_alloc(hpt_action, hpt_poll, driver_name, vbus_ext, 0, &Giant, os_max_queue_comm, /*tagged*/8, devq); #else vbus_ext->sim = cam_sim_alloc(hpt_action, hpt_poll, driver_name, vbus_ext, 0, os_max_queue_comm, /*tagged*/8, devq); #endif if (!vbus_ext->sim) { os_printk("cam_sim_alloc failed"); cam_simq_free(devq); return ; } #if __FreeBSD_version > 700044 if (xpt_bus_register(vbus_ext->sim, NULL, 0) != CAM_SUCCESS) { #else if (xpt_bus_register(vbus_ext->sim, 0) != CAM_SUCCESS) { #endif os_printk("xpt_bus_register failed"); cam_sim_free(vbus_ext->sim, /*free devq*/ TRUE); vbus_ext->sim = NULL; return ; } if (xpt_create_path(&vbus_ext->path, /*periph */ NULL, cam_sim_path(vbus_ext->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { os_printk("xpt_create_path failed"); xpt_bus_deregister(cam_sim_path(vbus_ext->sim)); cam_sim_free(vbus_ext->sim, /*free_devq*/TRUE); vbus_ext->sim = NULL; return ; } xpt_setup_ccb(&ccb.ccb_h, vbus_ext->path, /*priority*/5); ccb.ccb_h.func_code = XPT_SASYNC_CB; ccb.event_enable = AC_LOST_DEVICE; ccb.callback = hpt_async; ccb.callback_arg = vbus_ext; xpt_action((union ccb *)&ccb); for (hba = vbus_ext->hba_list; hba; hba = hba->next) { int rid = 0; if ((hba->irq_res = bus_alloc_resource(hba->pcidev, SYS_RES_IRQ, &rid, 0, ~0ul, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) { os_printk("can't allocate interrupt"); return ; } if (bus_setup_intr(hba->pcidev, hba->irq_res, INTR_TYPE_CAM, #if __FreeBSD_version > 700025 NULL, hpt_pci_intr, vbus_ext, &hba->irq_handle)) #else hpt_pci_intr, vbus_ext, &hba->irq_handle)) #endif { os_printk("can't set up interrupt"); return ; } hba->ldm_adapter.him->intr_control(hba->ldm_adapter.him_handle, HPT_TRUE); } vbus_ext->shutdown_eh = EVENTHANDLER_REGISTER(shutdown_final, hpt_shutdown_vbus, vbus_ext, SHUTDOWN_PRI_DEFAULT); if (!vbus_ext->shutdown_eh) os_printk("Shutdown event registration failed"); } ldm_for_each_vbus(vbus, vbus_ext) { TASK_INIT(&vbus_ext->worker, 0, (task_fn_t *)hpt_do_tasks, vbus_ext); if (vbus_ext->tasks) TASK_ENQUEUE(&vbus_ext->worker); } make_dev(&hpt_cdevsw, DRIVER_MINOR, UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "%s", driver_name); } #if defined(KLD_MODULE) && (__FreeBSD_version >= 503000) typedef struct driverlink *driverlink_t; struct driverlink { kobj_class_t driver; TAILQ_ENTRY(driverlink) link; /* list of drivers in devclass */ }; typedef TAILQ_HEAD(driver_list, driverlink) driver_list_t; struct devclass { TAILQ_ENTRY(devclass) link; devclass_t parent; /* parent in devclass hierarchy */ driver_list_t drivers; /* bus devclasses store drivers for bus */ char *name; device_t *devices; /* array of devices indexed by unit */ int maxunit; /* size of devices array */ }; static void override_kernel_driver(void) { driverlink_t dl, dlfirst; driver_t *tmpdriver; devclass_t dc = devclass_find("pci"); if (dc){ dlfirst = TAILQ_FIRST(&dc->drivers); for (dl = dlfirst; dl; dl = TAILQ_NEXT(dl, link)) { if(strcmp(dl->driver->name, driver_name) == 0) { tmpdriver=dl->driver; dl->driver=dlfirst->driver; dlfirst->driver=tmpdriver; break; } } } } #else #define override_kernel_driver() #endif static void hpt_init(void *dummy) { if (bootverbose) os_printk("%s %s", driver_name_long, driver_ver); override_kernel_driver(); init_config(); hpt_ich.ich_func = hpt_final_init; hpt_ich.ich_arg = NULL; if (config_intrhook_establish(&hpt_ich) != 0) { printf("%s: cannot establish configuration hook\n", driver_name_long); } } SYSINIT(hptinit, SI_SUB_CONFIGURE, SI_ORDER_FIRST, hpt_init, NULL); /* * CAM driver interface */ static device_method_t driver_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hpt_probe), DEVMETHOD(device_attach, hpt_attach), DEVMETHOD(device_detach, hpt_detach), DEVMETHOD(device_shutdown, hpt_shutdown), DEVMETHOD_END }; static driver_t hpt_pci_driver = { driver_name, driver_methods, sizeof(HBA) }; static devclass_t hpt_devclass; #ifndef TARGETNAME #error "no TARGETNAME found" #endif /* use this to make TARGETNAME be expanded */ #define __DRIVER_MODULE(p1, p2, p3, p4, p5, p6) DRIVER_MODULE(p1, p2, p3, p4, p5, p6) #define __MODULE_VERSION(p1, p2) MODULE_VERSION(p1, p2) #define __MODULE_DEPEND(p1, p2, p3, p4, p5) MODULE_DEPEND(p1, p2, p3, p4, p5) __DRIVER_MODULE(TARGETNAME, pci, hpt_pci_driver, hpt_devclass, 0, 0); __MODULE_VERSION(TARGETNAME, 1); __MODULE_DEPEND(TARGETNAME, cam, 1, 1, 1); #if __FreeBSD_version>503000 typedef struct cdev * ioctl_dev_t; #else typedef dev_t ioctl_dev_t; #endif #if __FreeBSD_version >= 500000 typedef struct thread * ioctl_thread_t; #else typedef struct proc * ioctl_thread_t; #endif static int hpt_open(ioctl_dev_t dev, int flags, int devtype, ioctl_thread_t td) { return 0; } static int hpt_close(ioctl_dev_t dev, int flags, int devtype, ioctl_thread_t td) { return 0; } static int hpt_ioctl(ioctl_dev_t dev, u_long cmd, caddr_t data, int fflag, ioctl_thread_t td) { PHPT_IOCTL_PARAM piop=(PHPT_IOCTL_PARAM)data; IOCTL_ARG ioctl_args; HPT_U32 bytesReturned; switch (cmd){ case HPT_DO_IOCONTROL: { if (piop->Magic == HPT_IOCTL_MAGIC || piop->Magic == HPT_IOCTL_MAGIC32) { KdPrint(("ioctl=%x in=%p len=%d out=%p len=%d\n", piop->dwIoControlCode, piop->lpInBuffer, piop->nInBufferSize, piop->lpOutBuffer, piop->nOutBufferSize)); memset(&ioctl_args, 0, sizeof(ioctl_args)); ioctl_args.dwIoControlCode = piop->dwIoControlCode; ioctl_args.nInBufferSize = piop->nInBufferSize; ioctl_args.nOutBufferSize = piop->nOutBufferSize; ioctl_args.lpBytesReturned = &bytesReturned; if (ioctl_args.nInBufferSize) { ioctl_args.lpInBuffer = malloc(ioctl_args.nInBufferSize, M_DEVBUF, M_WAITOK); if (!ioctl_args.lpInBuffer) goto invalid; if (copyin((void*)piop->lpInBuffer, ioctl_args.lpInBuffer, piop->nInBufferSize)) goto invalid; } if (ioctl_args.nOutBufferSize) { ioctl_args.lpOutBuffer = malloc(ioctl_args.nOutBufferSize, M_DEVBUF, M_WAITOK); if (!ioctl_args.lpOutBuffer) goto invalid; } #if (__FreeBSD_version >= 500000) mtx_lock(&Giant); #endif hpt_do_ioctl(&ioctl_args); #if (__FreeBSD_version >= 500000) mtx_unlock(&Giant); #endif if (ioctl_args.result==HPT_IOCTL_RESULT_OK) { if (piop->nOutBufferSize) { if (copyout(ioctl_args.lpOutBuffer, (void*)piop->lpOutBuffer, piop->nOutBufferSize)) goto invalid; } if (piop->lpBytesReturned) { if (copyout(&bytesReturned, (void*)piop->lpBytesReturned, sizeof(HPT_U32))) goto invalid; } if (ioctl_args.lpInBuffer) free(ioctl_args.lpInBuffer, M_DEVBUF); if (ioctl_args.lpOutBuffer) free(ioctl_args.lpOutBuffer, M_DEVBUF); return 0; } invalid: if (ioctl_args.lpInBuffer) free(ioctl_args.lpInBuffer, M_DEVBUF); if (ioctl_args.lpOutBuffer) free(ioctl_args.lpOutBuffer, M_DEVBUF); return EFAULT; } return EFAULT; } case HPT_SCAN_BUS: { return hpt_rescan_bus(); } default: KdPrint(("invalid command!")); return EFAULT; } } static int hpt_rescan_bus(void) { union ccb *ccb; PVBUS vbus; PVBUS_EXT vbus_ext; #if (__FreeBSD_version >= 500000) mtx_lock(&Giant); #endif ldm_for_each_vbus(vbus, vbus_ext) { if ((ccb = xpt_alloc_ccb()) == NULL) return(ENOMEM); if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(vbus_ext->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return(EIO); } xpt_rescan(ccb); } #if (__FreeBSD_version >= 500000) mtx_unlock(&Giant); #endif return(0); } diff --git a/sys/dev/hptrr/os_bsd.h b/sys/dev/hptrr/os_bsd.h index ec221f39b90a..1c6760e8edaa 100644 --- a/sys/dev/hptrr/os_bsd.h +++ b/sys/dev/hptrr/os_bsd.h @@ -1,263 +1,264 @@ /* * Copyright (c) HighPoint Technologies, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include /* $Id: os_bsd.h,v 1.18 2006/04/11 08:19:02 gmm Exp $ * * HighPoint RAID Driver for FreeBSD * Copyright (C) 2005 HighPoint Technologies, Inc. All Rights Reserved. */ #ifndef _OS_BSD_H #define _OS_BSD_H #ifndef DBG #define DBG 0 #endif #include #include #include #if (__FreeBSD_version >= 500000) #include #include #else #include /*to support DELAY function under 4.x BSD versions*/ #endif #include #include #include #include #include #if (__FreeBSD_version >= 500000) #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #if (__FreeBSD_version >= 500000) #include #include #else #include #include #endif #if (__FreeBSD_version <= 500043) #include #endif #include #include #include #include #include #include #include #include #if (__FreeBSD_version < 500043) #include #endif typedef struct _INQUIRYDATA { u_char DeviceType : 5; u_char DeviceTypeQualifier : 3; u_char DeviceTypeModifier : 7; u_char RemovableMedia : 1; u_char Versions; u_char ResponseDataFormat; u_char AdditionalLength; u_char Reserved[2]; u_char SoftReset : 1; u_char CommandQueue : 1; u_char Reserved2 : 1; u_char LinkedCommands : 1; u_char Synchronous : 1; u_char Wide16Bit : 1; u_char Wide32Bit : 1; u_char RelativeAddressing : 1; u_char VendorId[8]; u_char ProductId[16]; u_char ProductRevisionLevel[4]; u_char VendorSpecific[20]; u_char Reserved3[40]; } __attribute__((packed)) INQUIRYDATA, *PINQUIRYDATA; #endif /* private headers */ #include #include #include /* driver parameters */ extern char driver_name[]; extern char driver_name_long[]; extern char driver_ver[]; extern int osm_max_targets; /* * adapter/vbus extensions: * each physical controller has an adapter_ext, passed to him.create_adapter() * each vbus has a vbus_ext passed to ldm_create_vbus(). */ #define EXT_TYPE_HBA 1 #define EXT_TYPE_VBUS 2 typedef struct _hba { int ext_type; LDM_ADAPTER ldm_adapter; device_t pcidev; PCI_ADDRESS pciaddr; struct _vbus_ext *vbus_ext; struct _hba *next; struct { struct resource *res; int type; int rid; void *base; } pcibar[6]; struct resource *irq_res; void *irq_handle; } HBA, *PHBA; typedef struct _os_cmdext { struct _vbus_ext *vbus_ext; struct _os_cmdext *next; union ccb *ccb; bus_dmamap_t dma_map; + struct callout_handle timeout_ch; SG psg[os_max_sg_descriptors]; } OS_CMDEXT, *POS_CMDEXT; typedef struct _vbus_ext { int ext_type; struct _vbus_ext *next; PHBA hba_list; struct freelist *freelist_head; struct freelist *freelist_dma_head; struct cam_sim *sim; /* sim for this vbus */ struct cam_path *path; /* peripheral, path, tgt, lun with this vbus */ #if (__FreeBSD_version >= 500000) struct mtx lock; /* general purpose lock */ #else int hpt_splx; #endif bus_dma_tag_t io_dmat; /* I/O buffer DMA tag */ POS_CMDEXT cmdext_list; OSM_TASK *tasks; struct task worker; struct callout_handle timer; eventhandler_tag shutdown_eh; /* the LDM vbus instance continues */ unsigned long vbus[0] __attribute__((aligned(sizeof(unsigned long)))); } VBUS_EXT, *PVBUS_EXT; #if __FreeBSD_version >= 500000 #define hpt_lock_vbus(vbus_ext) mtx_lock(&(vbus_ext)->lock) #define hpt_unlock_vbus(vbus_ext) mtx_unlock(&(vbus_ext)->lock) #else static __inline void hpt_lock_vbus(PVBUS_EXT vbus_ext) { vbus_ext->hpt_splx = splcam(); } static __inline void hpt_unlock_vbus(PVBUS_EXT vbus_ext) { splx(vbus_ext->hpt_splx); } #endif #define HPT_OSM_TIMEOUT (20*hz) /* timeout value for OS commands */ #define HPT_DO_IOCONTROL _IOW('H', 0, HPT_IOCTL_PARAM) #define HPT_SCAN_BUS _IO('H', 1) #if __FreeBSD_version >= 501000 #define TASK_ENQUEUE(task) taskqueue_enqueue(taskqueue_swi_giant,(task)); #else #define TASK_ENQUEUE(task) taskqueue_enqueue(taskqueue_swi,(task)); #endif #if __FreeBSD_version >= 500000 static __inline int hpt_sleep(PVBUS_EXT vbus_ext, void *ident, int priority, const char *wmesg, int timo) { return msleep(ident, &vbus_ext->lock, priority, wmesg, timo); } #else static __inline int hpt_sleep(PVBUS_EXT vbus_ext, void *ident, int priority, const char *wmesg, int timo) { int retval = 0; asleep(ident, priority, wmesg, timo); hpt_unlock_vbus(vbus_ext); retval = await(priority, timo); hpt_lock_vbus(vbus_ext); return retval; } #endif #if __FreeBSD_version < 501000 #define READ_16 0x88 #define WRITE_16 0x8a #define SERVICE_ACTION_IN 0x9e #endif #define HPT_DEV_MAJOR 200 diff --git a/sys/dev/iir/iir.c b/sys/dev/iir/iir.c index 684fce3298e1..1090042ff511 100644 --- a/sys/dev/iir/iir.c +++ b/sys/dev/iir/iir.c @@ -1,1930 +1,1931 @@ /*- * Copyright (c) 2000-04 ICP vortex GmbH * Copyright (c) 2002-04 Intel Corporation * Copyright (c) 2003-04 Adaptec Inc. * All Rights Reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * iir.c: SCSI dependant code for the Intel Integrated RAID Controller driver * * Written by: Achim Leubner * Fixes/Additions: Boji Tony Kannanthanam * * credits: Niklas Hallqvist; OpenBSD driver for the ICP Controllers. * Mike Smith; Some driver source code. * FreeBSD.ORG; Great O/S to work on and for. * * $Id: iir.c 1.5 2004/03/30 10:17:53 achim Exp $" */ #include __FBSDID("$FreeBSD$"); #define _IIR_C_ /* #include "opt_iir.h" */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_GDTBUF, "iirbuf", "iir driver buffer"); struct gdt_softc *gdt_wait_gdt; int gdt_wait_index; #ifdef GDT_DEBUG int gdt_debug = GDT_DEBUG; #ifdef __SERIAL__ #define MAX_SERBUF 160 static void ser_init(void); static void ser_puts(char *str); static void ser_putc(int c); static char strbuf[MAX_SERBUF+1]; #ifdef __COM2__ #define COM_BASE 0x2f8 #else #define COM_BASE 0x3f8 #endif static void ser_init() { unsigned port=COM_BASE; outb(port+3, 0x80); outb(port+1, 0); /* 19200 Baud, if 9600: outb(12,port) */ outb(port, 6); outb(port+3, 3); outb(port+1, 0); } static void ser_puts(char *str) { char *ptr; ser_init(); for (ptr=str;*ptr;++ptr) ser_putc((int)(*ptr)); } static void ser_putc(int c) { unsigned port=COM_BASE; while ((inb(port+5) & 0x20)==0); outb(port, c); if (c==0x0a) { while ((inb(port+5) & 0x20)==0); outb(port, 0x0d); } } int ser_printf(const char *fmt, ...) { va_list args; int i; va_start(args,fmt); i = vsprintf(strbuf,fmt,args); ser_puts(strbuf); va_end(args); return i; } #endif #endif /* The linked list of softc structures */ struct gdt_softc_list gdt_softcs = TAILQ_HEAD_INITIALIZER(gdt_softcs); /* controller cnt. */ int gdt_cnt = 0; /* event buffer */ static gdt_evt_str ebuffer[GDT_MAX_EVENTS]; static int elastidx, eoldidx; /* statistics */ gdt_statist_t gdt_stat; /* Definitions for our use of the SIM private CCB area */ #define ccb_sim_ptr spriv_ptr0 #define ccb_priority spriv_field1 static void iir_action(struct cam_sim *sim, union ccb *ccb); static void iir_poll(struct cam_sim *sim); static void iir_shutdown(void *arg, int howto); static void iir_timeout(void *arg); static void gdt_eval_mapping(u_int32_t size, int *cyls, int *heads, int *secs); static int gdt_internal_cmd(struct gdt_softc *gdt, struct gdt_ccb *gccb, u_int8_t service, u_int16_t opcode, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3); static int gdt_wait(struct gdt_softc *gdt, struct gdt_ccb *ccb, int timeout); static struct gdt_ccb *gdt_get_ccb(struct gdt_softc *gdt); static int gdt_sync_event(struct gdt_softc *gdt, int service, u_int8_t index, struct gdt_ccb *gccb); static int gdt_async_event(struct gdt_softc *gdt, int service); static struct gdt_ccb *gdt_raw_cmd(struct gdt_softc *gdt, union ccb *ccb, int *lock); static struct gdt_ccb *gdt_cache_cmd(struct gdt_softc *gdt, union ccb *ccb, int *lock); static struct gdt_ccb *gdt_ioctl_cmd(struct gdt_softc *gdt, gdt_ucmd_t *ucmd, int *lock); static void gdt_internal_cache_cmd(struct gdt_softc *gdt,union ccb *ccb); static void gdtmapmem(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error); static void gdtexecuteccb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error); int iir_init(struct gdt_softc *gdt) { u_int16_t cdev_cnt; int i, id, drv_cyls, drv_hds, drv_secs; struct gdt_ccb *gccb; GDT_DPRINTF(GDT_D_DEBUG, ("iir_init()\n")); gdt->sc_state = GDT_POLLING; gdt_clear_events(); bzero(&gdt_stat, sizeof(gdt_statist_t)); SLIST_INIT(&gdt->sc_free_gccb); SLIST_INIT(&gdt->sc_pending_gccb); TAILQ_INIT(&gdt->sc_ccb_queue); TAILQ_INIT(&gdt->sc_ucmd_queue); TAILQ_INSERT_TAIL(&gdt_softcs, gdt, links); /* DMA tag for mapping buffers into device visible space. */ if (bus_dma_tag_create(gdt->sc_parent_dmat, /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/MAXBSIZE, /*nsegments*/GDT_MAXSG, /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT, /*flags*/BUS_DMA_ALLOCNOW, /*lockfunc*/busdma_lock_mutex, /*lockarg*/&Giant, &gdt->sc_buffer_dmat) != 0) { printf("iir%d: bus_dma_tag_create(...,gdt->sc_buffer_dmat) failed\n", gdt->sc_hanum); return (1); } gdt->sc_init_level++; /* DMA tag for our ccb structures */ if (bus_dma_tag_create(gdt->sc_parent_dmat, /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, GDT_MAXCMDS * GDT_SCRATCH_SZ, /* maxsize */ /*nsegments*/1, /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT, /*flags*/0, /*lockfunc*/busdma_lock_mutex, /*lockarg*/&Giant, &gdt->sc_gcscratch_dmat) != 0) { printf("iir%d: bus_dma_tag_create(...,gdt->sc_gcscratch_dmat) failed\n", gdt->sc_hanum); return (1); } gdt->sc_init_level++; /* Allocation for our ccb scratch area */ if (bus_dmamem_alloc(gdt->sc_gcscratch_dmat, (void **)&gdt->sc_gcscratch, BUS_DMA_NOWAIT, &gdt->sc_gcscratch_dmamap) != 0) { printf("iir%d: bus_dmamem_alloc(...,&gdt->sc_gccbs,...) failed\n", gdt->sc_hanum); return (1); } gdt->sc_init_level++; /* And permanently map them */ bus_dmamap_load(gdt->sc_gcscratch_dmat, gdt->sc_gcscratch_dmamap, gdt->sc_gcscratch, GDT_MAXCMDS * GDT_SCRATCH_SZ, gdtmapmem, &gdt->sc_gcscratch_busbase, /*flags*/0); gdt->sc_init_level++; /* Clear them out. */ bzero(gdt->sc_gcscratch, GDT_MAXCMDS * GDT_SCRATCH_SZ); /* Initialize the ccbs */ gdt->sc_gccbs = malloc(sizeof(struct gdt_ccb) * GDT_MAXCMDS, M_GDTBUF, M_NOWAIT | M_ZERO); if (gdt->sc_gccbs == NULL) { printf("iir%d: no memory for gccbs.\n", gdt->sc_hanum); return (1); } for (i = GDT_MAXCMDS-1; i >= 0; i--) { gccb = &gdt->sc_gccbs[i]; gccb->gc_cmd_index = i + 2; gccb->gc_flags = GDT_GCF_UNUSED; gccb->gc_map_flag = FALSE; if (bus_dmamap_create(gdt->sc_buffer_dmat, /*flags*/0, &gccb->gc_dmamap) != 0) return(1); gccb->gc_map_flag = TRUE; gccb->gc_scratch = &gdt->sc_gcscratch[GDT_SCRATCH_SZ * i]; gccb->gc_scratch_busbase = gdt->sc_gcscratch_busbase + GDT_SCRATCH_SZ * i; + callout_handle_init(&gccb->gc_timeout_ch); SLIST_INSERT_HEAD(&gdt->sc_free_gccb, gccb, sle); } gdt->sc_init_level++; /* create the control device */ gdt->sc_dev = gdt_make_dev(gdt->sc_hanum); /* allocate ccb for gdt_internal_cmd() */ gccb = gdt_get_ccb(gdt); if (gccb == NULL) { printf("iir%d: No free command index found\n", gdt->sc_hanum); return (1); } bzero(gccb->gc_cmd, GDT_CMD_SZ); if (!gdt_internal_cmd(gdt, gccb, GDT_SCREENSERVICE, GDT_INIT, 0, 0, 0)) { printf("iir%d: Screen service initialization error %d\n", gdt->sc_hanum, gdt->sc_status); gdt_free_ccb(gdt, gccb); return (1); } gdt_internal_cmd(gdt, gccb, GDT_CACHESERVICE, GDT_UNFREEZE_IO, 0, 0, 0); if (!gdt_internal_cmd(gdt, gccb, GDT_CACHESERVICE, GDT_INIT, GDT_LINUX_OS, 0, 0)) { printf("iir%d: Cache service initialization error %d\n", gdt->sc_hanum, gdt->sc_status); gdt_free_ccb(gdt, gccb); return (1); } cdev_cnt = (u_int16_t)gdt->sc_info; gdt->sc_fw_vers = gdt->sc_service; /* Detect number of buses */ gdt_enc32(gccb->gc_scratch + GDT_IOC_VERSION, GDT_IOC_NEWEST); gccb->gc_scratch[GDT_IOC_LIST_ENTRIES] = GDT_MAXBUS; gccb->gc_scratch[GDT_IOC_FIRST_CHAN] = 0; gccb->gc_scratch[GDT_IOC_LAST_CHAN] = GDT_MAXBUS - 1; gdt_enc32(gccb->gc_scratch + GDT_IOC_LIST_OFFSET, GDT_IOC_HDR_SZ); if (gdt_internal_cmd(gdt, gccb, GDT_CACHESERVICE, GDT_IOCTL, GDT_IOCHAN_RAW_DESC, GDT_INVALID_CHANNEL, GDT_IOC_HDR_SZ + GDT_MAXBUS * GDT_RAWIOC_SZ)) { gdt->sc_bus_cnt = gccb->gc_scratch[GDT_IOC_CHAN_COUNT]; for (i = 0; i < gdt->sc_bus_cnt; i++) { id = gccb->gc_scratch[GDT_IOC_HDR_SZ + i * GDT_RAWIOC_SZ + GDT_RAWIOC_PROC_ID]; gdt->sc_bus_id[i] = id < GDT_MAXID_FC ? id : 0xff; } } else { /* New method failed, use fallback. */ for (i = 0; i < GDT_MAXBUS; i++) { gdt_enc32(gccb->gc_scratch + GDT_GETCH_CHANNEL_NO, i); if (!gdt_internal_cmd(gdt, gccb, GDT_CACHESERVICE, GDT_IOCTL, GDT_SCSI_CHAN_CNT | GDT_L_CTRL_PATTERN, GDT_IO_CHANNEL | GDT_INVALID_CHANNEL, GDT_GETCH_SZ)) { if (i == 0) { printf("iir%d: Cannot get channel count, " "error %d\n", gdt->sc_hanum, gdt->sc_status); gdt_free_ccb(gdt, gccb); return (1); } break; } gdt->sc_bus_id[i] = (gccb->gc_scratch[GDT_GETCH_SIOP_ID] < GDT_MAXID_FC) ? gccb->gc_scratch[GDT_GETCH_SIOP_ID] : 0xff; } gdt->sc_bus_cnt = i; } /* add one "virtual" channel for the host drives */ gdt->sc_virt_bus = gdt->sc_bus_cnt; gdt->sc_bus_cnt++; if (!gdt_internal_cmd(gdt, gccb, GDT_SCSIRAWSERVICE, GDT_INIT, 0, 0, 0)) { printf("iir%d: Raw service initialization error %d\n", gdt->sc_hanum, gdt->sc_status); gdt_free_ccb(gdt, gccb); return (1); } /* Set/get features raw service (scatter/gather) */ gdt->sc_raw_feat = 0; if (gdt_internal_cmd(gdt, gccb, GDT_SCSIRAWSERVICE, GDT_SET_FEAT, GDT_SCATTER_GATHER, 0, 0)) { if (gdt_internal_cmd(gdt, gccb, GDT_SCSIRAWSERVICE, GDT_GET_FEAT, 0, 0, 0)) { gdt->sc_raw_feat = gdt->sc_info; if (!(gdt->sc_info & GDT_SCATTER_GATHER)) { panic("iir%d: Scatter/Gather Raw Service " "required but not supported!\n", gdt->sc_hanum); gdt_free_ccb(gdt, gccb); return (1); } } } /* Set/get features cache service (scatter/gather) */ gdt->sc_cache_feat = 0; if (gdt_internal_cmd(gdt, gccb, GDT_CACHESERVICE, GDT_SET_FEAT, 0, GDT_SCATTER_GATHER, 0)) { if (gdt_internal_cmd(gdt, gccb, GDT_CACHESERVICE, GDT_GET_FEAT, 0, 0, 0)) { gdt->sc_cache_feat = gdt->sc_info; if (!(gdt->sc_info & GDT_SCATTER_GATHER)) { panic("iir%d: Scatter/Gather Cache Service " "required but not supported!\n", gdt->sc_hanum); gdt_free_ccb(gdt, gccb); return (1); } } } /* OEM */ gdt_enc32(gccb->gc_scratch + GDT_OEM_VERSION, 0x01); gdt_enc32(gccb->gc_scratch + GDT_OEM_BUFSIZE, sizeof(gdt_oem_record_t)); if (gdt_internal_cmd(gdt, gccb, GDT_CACHESERVICE, GDT_IOCTL, GDT_OEM_STR_RECORD, GDT_INVALID_CHANNEL, sizeof(gdt_oem_str_record_t))) { strncpy(gdt->oem_name, ((gdt_oem_str_record_t *) gccb->gc_scratch)->text.scsi_host_drive_inquiry_vendor_id, 7); gdt->oem_name[7]='\0'; } else { /* Old method, based on PCI ID */ if (gdt->sc_vendor == INTEL_VENDOR_ID_IIR) strcpy(gdt->oem_name,"Intel "); else strcpy(gdt->oem_name,"ICP "); } /* Scan for cache devices */ for (i = 0; i < cdev_cnt && i < GDT_MAX_HDRIVES; i++) { if (gdt_internal_cmd(gdt, gccb, GDT_CACHESERVICE, GDT_INFO, i, 0, 0)) { gdt->sc_hdr[i].hd_present = 1; gdt->sc_hdr[i].hd_size = gdt->sc_info; /* * Evaluate mapping (sectors per head, heads per cyl) */ gdt->sc_hdr[i].hd_size &= ~GDT_SECS32; if (gdt->sc_info2 == 0) gdt_eval_mapping(gdt->sc_hdr[i].hd_size, &drv_cyls, &drv_hds, &drv_secs); else { drv_hds = gdt->sc_info2 & 0xff; drv_secs = (gdt->sc_info2 >> 8) & 0xff; drv_cyls = gdt->sc_hdr[i].hd_size / drv_hds / drv_secs; } gdt->sc_hdr[i].hd_heads = drv_hds; gdt->sc_hdr[i].hd_secs = drv_secs; /* Round the size */ gdt->sc_hdr[i].hd_size = drv_cyls * drv_hds * drv_secs; if (gdt_internal_cmd(gdt, gccb, GDT_CACHESERVICE, GDT_DEVTYPE, i, 0, 0)) gdt->sc_hdr[i].hd_devtype = gdt->sc_info; } } GDT_DPRINTF(GDT_D_INIT, ("dpmem %x %d-bus %d cache device%s\n", gdt->sc_dpmembase, gdt->sc_bus_cnt, cdev_cnt, cdev_cnt == 1 ? "" : "s")); gdt_free_ccb(gdt, gccb); gdt_cnt++; return (0); } void iir_free(struct gdt_softc *gdt) { int i; GDT_DPRINTF(GDT_D_INIT, ("iir_free()\n")); switch (gdt->sc_init_level) { default: gdt_destroy_dev(gdt->sc_dev); case 5: for (i = GDT_MAXCMDS-1; i >= 0; i--) if (gdt->sc_gccbs[i].gc_map_flag) bus_dmamap_destroy(gdt->sc_buffer_dmat, gdt->sc_gccbs[i].gc_dmamap); bus_dmamap_unload(gdt->sc_gcscratch_dmat, gdt->sc_gcscratch_dmamap); free(gdt->sc_gccbs, M_GDTBUF); case 4: bus_dmamem_free(gdt->sc_gcscratch_dmat, gdt->sc_gcscratch, gdt->sc_gcscratch_dmamap); case 3: bus_dma_tag_destroy(gdt->sc_gcscratch_dmat); case 2: bus_dma_tag_destroy(gdt->sc_buffer_dmat); case 1: bus_dma_tag_destroy(gdt->sc_parent_dmat); case 0: break; } TAILQ_REMOVE(&gdt_softcs, gdt, links); } void iir_attach(struct gdt_softc *gdt) { struct cam_devq *devq; int i; GDT_DPRINTF(GDT_D_INIT, ("iir_attach()\n")); /* * Create the device queue for our SIM. * XXX Throttle this down since the card has problems under load. */ devq = cam_simq_alloc(32); if (devq == NULL) return; for (i = 0; i < gdt->sc_bus_cnt; i++) { /* * Construct our SIM entry */ gdt->sims[i] = cam_sim_alloc(iir_action, iir_poll, "iir", gdt, gdt->sc_hanum, &Giant, /*untagged*/1, /*tagged*/GDT_MAXCMDS, devq); if (xpt_bus_register(gdt->sims[i], gdt->sc_devnode, i) != CAM_SUCCESS) { cam_sim_free(gdt->sims[i], /*free_devq*/i == 0); break; } if (xpt_create_path(&gdt->paths[i], /*periph*/NULL, cam_sim_path(gdt->sims[i]), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(gdt->sims[i])); cam_sim_free(gdt->sims[i], /*free_devq*/i == 0); break; } } if (i > 0) EVENTHANDLER_REGISTER(shutdown_final, iir_shutdown, gdt, SHUTDOWN_PRI_DEFAULT); /* iir_watchdog(gdt); */ gdt->sc_state = GDT_NORMAL; } static void gdt_eval_mapping(u_int32_t size, int *cyls, int *heads, int *secs) { *cyls = size / GDT_HEADS / GDT_SECS; if (*cyls < GDT_MAXCYLS) { *heads = GDT_HEADS; *secs = GDT_SECS; } else { /* Too high for 64 * 32 */ *cyls = size / GDT_MEDHEADS / GDT_MEDSECS; if (*cyls < GDT_MAXCYLS) { *heads = GDT_MEDHEADS; *secs = GDT_MEDSECS; } else { /* Too high for 127 * 63 */ *cyls = size / GDT_BIGHEADS / GDT_BIGSECS; *heads = GDT_BIGHEADS; *secs = GDT_BIGSECS; } } } static int gdt_wait(struct gdt_softc *gdt, struct gdt_ccb *gccb, int timeout) { int rv = 0; GDT_DPRINTF(GDT_D_INIT, ("gdt_wait(%p, %p, %d)\n", gdt, gccb, timeout)); gdt->sc_state |= GDT_POLL_WAIT; do { iir_intr(gdt); if (gdt == gdt_wait_gdt && gccb->gc_cmd_index == gdt_wait_index) { rv = 1; break; } DELAY(1); } while (--timeout); gdt->sc_state &= ~GDT_POLL_WAIT; while (gdt->sc_test_busy(gdt)) DELAY(1); /* XXX correct? */ return (rv); } static int gdt_internal_cmd(struct gdt_softc *gdt, struct gdt_ccb *gccb, u_int8_t service, u_int16_t opcode, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3) { int retries; GDT_DPRINTF(GDT_D_CMD, ("gdt_internal_cmd(%p, %d, %d, %d, %d, %d)\n", gdt, service, opcode, arg1, arg2, arg3)); bzero(gccb->gc_cmd, GDT_CMD_SZ); for (retries = GDT_RETRIES; ; ) { gccb->gc_service = service; gccb->gc_flags = GDT_GCF_INTERNAL; gdt_enc32(gccb->gc_cmd + GDT_CMD_COMMANDINDEX, gccb->gc_cmd_index); gdt_enc16(gccb->gc_cmd + GDT_CMD_OPCODE, opcode); switch (service) { case GDT_CACHESERVICE: if (opcode == GDT_IOCTL) { gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_IOCTL_SUBFUNC, arg1); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_IOCTL_CHANNEL, arg2); gdt_enc16(gccb->gc_cmd + GDT_CMD_UNION + GDT_IOCTL_PARAM_SIZE, (u_int16_t)arg3); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_IOCTL_P_PARAM, gccb->gc_scratch_busbase); } else { gdt_enc16(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_DEVICENO, (u_int16_t)arg1); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_BLOCKNO, arg2); } break; case GDT_SCSIRAWSERVICE: gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_DIRECTION, arg1); gccb->gc_cmd[GDT_CMD_UNION + GDT_RAW_BUS] = (u_int8_t)arg2; gccb->gc_cmd[GDT_CMD_UNION + GDT_RAW_TARGET] = (u_int8_t)arg3; gccb->gc_cmd[GDT_CMD_UNION + GDT_RAW_LUN] = (u_int8_t)(arg3 >> 8); } gdt->sc_set_sema0(gdt); gccb->gc_cmd_len = GDT_CMD_SZ; gdt->sc_cmd_off = 0; gdt->sc_cmd_cnt = 0; gdt->sc_copy_cmd(gdt, gccb); gdt->sc_release_event(gdt); DELAY(20); if (!gdt_wait(gdt, gccb, GDT_POLL_TIMEOUT)) return (0); if (gdt->sc_status != GDT_S_BSY || --retries == 0) break; DELAY(1); } return (gdt->sc_status == GDT_S_OK); } static struct gdt_ccb * gdt_get_ccb(struct gdt_softc *gdt) { struct gdt_ccb *gccb; int lock; GDT_DPRINTF(GDT_D_QUEUE, ("gdt_get_ccb(%p)\n", gdt)); lock = splcam(); gccb = SLIST_FIRST(&gdt->sc_free_gccb); if (gccb != NULL) { SLIST_REMOVE_HEAD(&gdt->sc_free_gccb, sle); SLIST_INSERT_HEAD(&gdt->sc_pending_gccb, gccb, sle); ++gdt_stat.cmd_index_act; if (gdt_stat.cmd_index_act > gdt_stat.cmd_index_max) gdt_stat.cmd_index_max = gdt_stat.cmd_index_act; } splx(lock); return (gccb); } void gdt_free_ccb(struct gdt_softc *gdt, struct gdt_ccb *gccb) { int lock; GDT_DPRINTF(GDT_D_QUEUE, ("gdt_free_ccb(%p, %p)\n", gdt, gccb)); lock = splcam(); gccb->gc_flags = GDT_GCF_UNUSED; SLIST_REMOVE(&gdt->sc_pending_gccb, gccb, gdt_ccb, sle); SLIST_INSERT_HEAD(&gdt->sc_free_gccb, gccb, sle); --gdt_stat.cmd_index_act; splx(lock); if (gdt->sc_state & GDT_SHUTDOWN) wakeup(gccb); } void gdt_next(struct gdt_softc *gdt) { int lock; union ccb *ccb; gdt_ucmd_t *ucmd; struct cam_sim *sim; int bus, target, lun; int next_cmd; struct ccb_scsiio *csio; struct ccb_hdr *ccbh; struct gdt_ccb *gccb = NULL; u_int8_t cmd; GDT_DPRINTF(GDT_D_QUEUE, ("gdt_next(%p)\n", gdt)); lock = splcam(); if (gdt->sc_test_busy(gdt)) { if (!(gdt->sc_state & GDT_POLLING)) { splx(lock); return; } while (gdt->sc_test_busy(gdt)) DELAY(1); } gdt->sc_cmd_cnt = gdt->sc_cmd_off = 0; next_cmd = TRUE; for (;;) { /* I/Os in queue? controller ready? */ if (!TAILQ_FIRST(&gdt->sc_ucmd_queue) && !TAILQ_FIRST(&gdt->sc_ccb_queue)) break; /* 1.: I/Os without ccb (IOCTLs) */ ucmd = TAILQ_FIRST(&gdt->sc_ucmd_queue); if (ucmd != NULL) { TAILQ_REMOVE(&gdt->sc_ucmd_queue, ucmd, links); if ((gccb = gdt_ioctl_cmd(gdt, ucmd, &lock)) == NULL) { TAILQ_INSERT_HEAD(&gdt->sc_ucmd_queue, ucmd, links); break; } break; /* wenn mehrere Kdos. zulassen: if (!gdt_polling) continue; */ } /* 2.: I/Os with ccb */ ccb = (union ccb *)TAILQ_FIRST(&gdt->sc_ccb_queue); /* ist dann immer != NULL, da oben getestet */ sim = (struct cam_sim *)ccb->ccb_h.ccb_sim_ptr; bus = cam_sim_bus(sim); target = ccb->ccb_h.target_id; lun = ccb->ccb_h.target_lun; TAILQ_REMOVE(&gdt->sc_ccb_queue, &ccb->ccb_h, sim_links.tqe); --gdt_stat.req_queue_act; /* ccb->ccb_h.func_code is XPT_SCSI_IO */ GDT_DPRINTF(GDT_D_QUEUE, ("XPT_SCSI_IO flags 0x%x)\n", ccb->ccb_h.flags)); csio = &ccb->csio; ccbh = &ccb->ccb_h; cmd = csio->cdb_io.cdb_bytes[0]; /* Max CDB length is 12 bytes */ if (csio->cdb_len > 12) { ccbh->status = CAM_REQ_INVALID; --gdt_stat.io_count_act; xpt_done(ccb); } else if (bus != gdt->sc_virt_bus) { /* raw service command */ if ((gccb = gdt_raw_cmd(gdt, ccb, &lock)) == NULL) { TAILQ_INSERT_HEAD(&gdt->sc_ccb_queue, &ccb->ccb_h, sim_links.tqe); ++gdt_stat.req_queue_act; if (gdt_stat.req_queue_act > gdt_stat.req_queue_max) gdt_stat.req_queue_max = gdt_stat.req_queue_act; next_cmd = FALSE; } } else if (target >= GDT_MAX_HDRIVES || !gdt->sc_hdr[target].hd_present || lun != 0) { ccbh->status = CAM_DEV_NOT_THERE; --gdt_stat.io_count_act; xpt_done(ccb); } else { /* cache service command */ if (cmd == READ_6 || cmd == WRITE_6 || cmd == READ_10 || cmd == WRITE_10) { if ((gccb = gdt_cache_cmd(gdt, ccb, &lock)) == NULL) { TAILQ_INSERT_HEAD(&gdt->sc_ccb_queue, &ccb->ccb_h, sim_links.tqe); ++gdt_stat.req_queue_act; if (gdt_stat.req_queue_act > gdt_stat.req_queue_max) gdt_stat.req_queue_max = gdt_stat.req_queue_act; next_cmd = FALSE; } } else { splx(lock); gdt_internal_cache_cmd(gdt, ccb); lock = splcam(); } } if ((gdt->sc_state & GDT_POLLING) || !next_cmd) break; } if (gdt->sc_cmd_cnt > 0) gdt->sc_release_event(gdt); splx(lock); if ((gdt->sc_state & GDT_POLLING) && gdt->sc_cmd_cnt > 0) { gdt_wait(gdt, gccb, GDT_POLL_TIMEOUT); } } static struct gdt_ccb * gdt_raw_cmd(struct gdt_softc *gdt, union ccb *ccb, int *lock) { struct gdt_ccb *gccb; struct cam_sim *sim; int error; GDT_DPRINTF(GDT_D_CMD, ("gdt_raw_cmd(%p, %p)\n", gdt, ccb)); if (roundup(GDT_CMD_UNION + GDT_RAW_SZ, sizeof(u_int32_t)) + gdt->sc_cmd_off + GDT_DPMEM_COMMAND_OFFSET > gdt->sc_ic_all_size) { GDT_DPRINTF(GDT_D_INVALID, ("iir%d: gdt_raw_cmd(): DPMEM overflow\n", gdt->sc_hanum)); return (NULL); } gccb = gdt_get_ccb(gdt); if (gccb == NULL) { GDT_DPRINTF(GDT_D_INVALID, ("iir%d: No free command index found\n", gdt->sc_hanum)); return (gccb); } bzero(gccb->gc_cmd, GDT_CMD_SZ); sim = (struct cam_sim *)ccb->ccb_h.ccb_sim_ptr; gccb->gc_ccb = ccb; gccb->gc_service = GDT_SCSIRAWSERVICE; gccb->gc_flags = GDT_GCF_SCSI; if (gdt->sc_cmd_cnt == 0) gdt->sc_set_sema0(gdt); splx(*lock); gdt_enc32(gccb->gc_cmd + GDT_CMD_COMMANDINDEX, gccb->gc_cmd_index); gdt_enc16(gccb->gc_cmd + GDT_CMD_OPCODE, GDT_WRITE); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_DIRECTION, (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN ? GDT_DATA_IN : GDT_DATA_OUT); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SDLEN, ccb->csio.dxfer_len); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_CLEN, ccb->csio.cdb_len); bcopy(ccb->csio.cdb_io.cdb_bytes, gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_CMD, ccb->csio.cdb_len); gccb->gc_cmd[GDT_CMD_UNION + GDT_RAW_TARGET] = ccb->ccb_h.target_id; gccb->gc_cmd[GDT_CMD_UNION + GDT_RAW_LUN] = ccb->ccb_h.target_lun; gccb->gc_cmd[GDT_CMD_UNION + GDT_RAW_BUS] = cam_sim_bus(sim); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SENSE_LEN, sizeof(struct scsi_sense_data)); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SENSE_DATA, gccb->gc_scratch_busbase); error = bus_dmamap_load_ccb(gdt->sc_buffer_dmat, gccb->gc_dmamap, ccb, gdtexecuteccb, gccb, /*flags*/0); if (error == EINPROGRESS) { xpt_freeze_simq(sim, 1); gccb->gc_ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } *lock = splcam(); return (gccb); } static struct gdt_ccb * gdt_cache_cmd(struct gdt_softc *gdt, union ccb *ccb, int *lock) { struct gdt_ccb *gccb; struct cam_sim *sim; u_int8_t *cmdp; u_int16_t opcode; u_int32_t blockno, blockcnt; int error; GDT_DPRINTF(GDT_D_CMD, ("gdt_cache_cmd(%p, %p)\n", gdt, ccb)); if (roundup(GDT_CMD_UNION + GDT_CACHE_SZ, sizeof(u_int32_t)) + gdt->sc_cmd_off + GDT_DPMEM_COMMAND_OFFSET > gdt->sc_ic_all_size) { GDT_DPRINTF(GDT_D_INVALID, ("iir%d: gdt_cache_cmd(): DPMEM overflow\n", gdt->sc_hanum)); return (NULL); } gccb = gdt_get_ccb(gdt); if (gccb == NULL) { GDT_DPRINTF(GDT_D_DEBUG, ("iir%d: No free command index found\n", gdt->sc_hanum)); return (gccb); } bzero(gccb->gc_cmd, GDT_CMD_SZ); sim = (struct cam_sim *)ccb->ccb_h.ccb_sim_ptr; gccb->gc_ccb = ccb; gccb->gc_service = GDT_CACHESERVICE; gccb->gc_flags = GDT_GCF_SCSI; if (gdt->sc_cmd_cnt == 0) gdt->sc_set_sema0(gdt); splx(*lock); gdt_enc32(gccb->gc_cmd + GDT_CMD_COMMANDINDEX, gccb->gc_cmd_index); cmdp = ccb->csio.cdb_io.cdb_bytes; opcode = (*cmdp == WRITE_6 || *cmdp == WRITE_10) ? GDT_WRITE : GDT_READ; if ((gdt->sc_state & GDT_SHUTDOWN) && opcode == GDT_WRITE) opcode = GDT_WRITE_THR; gdt_enc16(gccb->gc_cmd + GDT_CMD_OPCODE, opcode); gdt_enc16(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_DEVICENO, ccb->ccb_h.target_id); if (ccb->csio.cdb_len == 6) { struct scsi_rw_6 *rw = (struct scsi_rw_6 *)cmdp; blockno = scsi_3btoul(rw->addr) & ((SRW_TOPADDR<<16) | 0xffff); blockcnt = rw->length ? rw->length : 0x100; } else { struct scsi_rw_10 *rw = (struct scsi_rw_10 *)cmdp; blockno = scsi_4btoul(rw->addr); blockcnt = scsi_2btoul(rw->length); } gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_BLOCKNO, blockno); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_BLOCKCNT, blockcnt); error = bus_dmamap_load_ccb(gdt->sc_buffer_dmat, gccb->gc_dmamap, ccb, gdtexecuteccb, gccb, /*flags*/0); if (error == EINPROGRESS) { xpt_freeze_simq(sim, 1); gccb->gc_ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } *lock = splcam(); return (gccb); } static struct gdt_ccb * gdt_ioctl_cmd(struct gdt_softc *gdt, gdt_ucmd_t *ucmd, int *lock) { struct gdt_ccb *gccb; u_int32_t cnt; GDT_DPRINTF(GDT_D_DEBUG, ("gdt_ioctl_cmd(%p, %p)\n", gdt, ucmd)); gccb = gdt_get_ccb(gdt); if (gccb == NULL) { GDT_DPRINTF(GDT_D_DEBUG, ("iir%d: No free command index found\n", gdt->sc_hanum)); return (gccb); } bzero(gccb->gc_cmd, GDT_CMD_SZ); gccb->gc_ucmd = ucmd; gccb->gc_service = ucmd->service; gccb->gc_flags = GDT_GCF_IOCTL; /* check DPMEM space, copy data buffer from user space */ if (ucmd->service == GDT_CACHESERVICE) { if (ucmd->OpCode == GDT_IOCTL) { gccb->gc_cmd_len = roundup(GDT_CMD_UNION + GDT_IOCTL_SZ, sizeof(u_int32_t)); cnt = ucmd->u.ioctl.param_size; if (cnt > GDT_SCRATCH_SZ) { printf("iir%d: Scratch buffer too small (%d/%d)\n", gdt->sc_hanum, GDT_SCRATCH_SZ, cnt); gdt_free_ccb(gdt, gccb); return (NULL); } } else { gccb->gc_cmd_len = roundup(GDT_CMD_UNION + GDT_CACHE_SG_LST + GDT_SG_SZ, sizeof(u_int32_t)); cnt = ucmd->u.cache.BlockCnt * GDT_SECTOR_SIZE; if (cnt > GDT_SCRATCH_SZ) { printf("iir%d: Scratch buffer too small (%d/%d)\n", gdt->sc_hanum, GDT_SCRATCH_SZ, cnt); gdt_free_ccb(gdt, gccb); return (NULL); } } } else { gccb->gc_cmd_len = roundup(GDT_CMD_UNION + GDT_RAW_SG_LST + GDT_SG_SZ, sizeof(u_int32_t)); cnt = ucmd->u.raw.sdlen; if (cnt + ucmd->u.raw.sense_len > GDT_SCRATCH_SZ) { printf("iir%d: Scratch buffer too small (%d/%d)\n", gdt->sc_hanum, GDT_SCRATCH_SZ, cnt + ucmd->u.raw.sense_len); gdt_free_ccb(gdt, gccb); return (NULL); } } if (cnt != 0) bcopy(ucmd->data, gccb->gc_scratch, cnt); if (gdt->sc_cmd_off + gccb->gc_cmd_len + GDT_DPMEM_COMMAND_OFFSET > gdt->sc_ic_all_size) { GDT_DPRINTF(GDT_D_INVALID, ("iir%d: gdt_ioctl_cmd(): DPMEM overflow\n", gdt->sc_hanum)); gdt_free_ccb(gdt, gccb); return (NULL); } if (gdt->sc_cmd_cnt == 0) gdt->sc_set_sema0(gdt); splx(*lock); /* fill cmd structure */ gdt_enc32(gccb->gc_cmd + GDT_CMD_COMMANDINDEX, gccb->gc_cmd_index); gdt_enc16(gccb->gc_cmd + GDT_CMD_OPCODE, ucmd->OpCode); if (ucmd->service == GDT_CACHESERVICE) { if (ucmd->OpCode == GDT_IOCTL) { /* IOCTL */ gdt_enc16(gccb->gc_cmd + GDT_CMD_UNION + GDT_IOCTL_PARAM_SIZE, ucmd->u.ioctl.param_size); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_IOCTL_SUBFUNC, ucmd->u.ioctl.subfunc); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_IOCTL_CHANNEL, ucmd->u.ioctl.channel); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_IOCTL_P_PARAM, gccb->gc_scratch_busbase); } else { /* cache service command */ gdt_enc16(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_DEVICENO, ucmd->u.cache.DeviceNo); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_BLOCKNO, ucmd->u.cache.BlockNo); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_BLOCKCNT, ucmd->u.cache.BlockCnt); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_DESTADDR, 0xffffffffUL); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_SG_CANZ, 1); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_SG_LST + GDT_SG_PTR, gccb->gc_scratch_busbase); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_SG_LST + GDT_SG_LEN, ucmd->u.cache.BlockCnt * GDT_SECTOR_SIZE); } } else { /* raw service command */ gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_DIRECTION, ucmd->u.raw.direction); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SDATA, 0xffffffffUL); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SDLEN, ucmd->u.raw.sdlen); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_CLEN, ucmd->u.raw.clen); bcopy(ucmd->u.raw.cmd, gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_CMD, 12); gccb->gc_cmd[GDT_CMD_UNION + GDT_RAW_TARGET] = ucmd->u.raw.target; gccb->gc_cmd[GDT_CMD_UNION + GDT_RAW_LUN] = ucmd->u.raw.lun; gccb->gc_cmd[GDT_CMD_UNION + GDT_RAW_BUS] = ucmd->u.raw.bus; gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SENSE_LEN, ucmd->u.raw.sense_len); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SENSE_DATA, gccb->gc_scratch_busbase + ucmd->u.raw.sdlen); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SG_RANZ, 1); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SG_LST + GDT_SG_PTR, gccb->gc_scratch_busbase); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SG_LST + GDT_SG_LEN, ucmd->u.raw.sdlen); } *lock = splcam(); gdt_stat.sg_count_act = 1; gdt->sc_copy_cmd(gdt, gccb); return (gccb); } static void gdt_internal_cache_cmd(struct gdt_softc *gdt,union ccb *ccb) { int t; t = ccb->ccb_h.target_id; GDT_DPRINTF(GDT_D_CMD, ("gdt_internal_cache_cmd(%p, %p, 0x%x, %d)\n", gdt, ccb, ccb->csio.cdb_io.cdb_bytes[0], t)); switch (ccb->csio.cdb_io.cdb_bytes[0]) { case TEST_UNIT_READY: case START_STOP: break; case REQUEST_SENSE: GDT_DPRINTF(GDT_D_MISC, ("REQUEST_SENSE\n")); break; case INQUIRY: { struct scsi_inquiry_data inq; size_t copylen = MIN(sizeof(inq), ccb->csio.dxfer_len); bzero(&inq, sizeof(inq)); inq.device = (gdt->sc_hdr[t].hd_devtype & 4) ? T_CDROM : T_DIRECT; inq.dev_qual2 = (gdt->sc_hdr[t].hd_devtype & 1) ? 0x80 : 0; inq.version = SCSI_REV_2; inq.response_format = 2; inq.additional_length = 32; inq.flags = SID_CmdQue | SID_Sync; strncpy(inq.vendor, gdt->oem_name, sizeof(inq.vendor)); snprintf(inq.product, sizeof(inq.product), "Host Drive #%02d", t); strncpy(inq.revision, " ", sizeof(inq.revision)); bcopy(&inq, ccb->csio.data_ptr, copylen ); if( ccb->csio.dxfer_len > copylen ) bzero( ccb->csio.data_ptr+copylen, ccb->csio.dxfer_len - copylen ); break; } case MODE_SENSE_6: { struct mpd_data { struct scsi_mode_hdr_6 hd; struct scsi_mode_block_descr bd; struct scsi_control_page cp; } mpd; size_t copylen = MIN(sizeof(mpd), ccb->csio.dxfer_len); u_int8_t page; /*mpd = (struct mpd_data *)ccb->csio.data_ptr;*/ bzero(&mpd, sizeof(mpd)); mpd.hd.datalen = sizeof(struct scsi_mode_hdr_6) + sizeof(struct scsi_mode_block_descr); mpd.hd.dev_specific = (gdt->sc_hdr[t].hd_devtype & 2) ? 0x80 : 0; mpd.hd.block_descr_len = sizeof(struct scsi_mode_block_descr); mpd.bd.block_len[0] = (GDT_SECTOR_SIZE & 0x00ff0000) >> 16; mpd.bd.block_len[1] = (GDT_SECTOR_SIZE & 0x0000ff00) >> 8; mpd.bd.block_len[2] = (GDT_SECTOR_SIZE & 0x000000ff); bcopy(&mpd, ccb->csio.data_ptr, copylen ); if( ccb->csio.dxfer_len > copylen ) bzero( ccb->csio.data_ptr+copylen, ccb->csio.dxfer_len - copylen ); page=((struct scsi_mode_sense_6 *)ccb->csio.cdb_io.cdb_bytes)->page; switch (page) { default: GDT_DPRINTF(GDT_D_MISC, ("MODE_SENSE_6: page 0x%x\n", page)); break; } break; } case READ_CAPACITY: { struct scsi_read_capacity_data rcd; size_t copylen = MIN(sizeof(rcd), ccb->csio.dxfer_len); /*rcd = (struct scsi_read_capacity_data *)ccb->csio.data_ptr;*/ bzero(&rcd, sizeof(rcd)); scsi_ulto4b(gdt->sc_hdr[t].hd_size - 1, rcd.addr); scsi_ulto4b(GDT_SECTOR_SIZE, rcd.length); bcopy(&rcd, ccb->csio.data_ptr, copylen ); if( ccb->csio.dxfer_len > copylen ) bzero( ccb->csio.data_ptr+copylen, ccb->csio.dxfer_len - copylen ); break; } default: GDT_DPRINTF(GDT_D_MISC, ("gdt_internal_cache_cmd(%d) unknown\n", ccb->csio.cdb_io.cdb_bytes[0])); break; } ccb->ccb_h.status |= CAM_REQ_CMP; --gdt_stat.io_count_act; xpt_done(ccb); } static void gdtmapmem(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) { bus_addr_t *busaddrp; busaddrp = (bus_addr_t *)arg; *busaddrp = dm_segs->ds_addr; } static void gdtexecuteccb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) { struct gdt_ccb *gccb; union ccb *ccb; struct gdt_softc *gdt; int i, lock; lock = splcam(); gccb = (struct gdt_ccb *)arg; ccb = gccb->gc_ccb; gdt = cam_sim_softc((struct cam_sim *)ccb->ccb_h.ccb_sim_ptr); GDT_DPRINTF(GDT_D_CMD, ("gdtexecuteccb(%p, %p, %p, %d, %d)\n", gdt, gccb, dm_segs, nseg, error)); gdt_stat.sg_count_act = nseg; if (nseg > gdt_stat.sg_count_max) gdt_stat.sg_count_max = nseg; /* Copy the segments into our SG list */ if (gccb->gc_service == GDT_CACHESERVICE) { for (i = 0; i < nseg; ++i) { gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_SG_LST + i * GDT_SG_SZ + GDT_SG_PTR, dm_segs->ds_addr); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_SG_LST + i * GDT_SG_SZ + GDT_SG_LEN, dm_segs->ds_len); dm_segs++; } gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_SG_CANZ, nseg); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_CACHE_DESTADDR, 0xffffffffUL); gccb->gc_cmd_len = roundup(GDT_CMD_UNION + GDT_CACHE_SG_LST + nseg * GDT_SG_SZ, sizeof(u_int32_t)); } else { for (i = 0; i < nseg; ++i) { gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SG_LST + i * GDT_SG_SZ + GDT_SG_PTR, dm_segs->ds_addr); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SG_LST + i * GDT_SG_SZ + GDT_SG_LEN, dm_segs->ds_len); dm_segs++; } gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SG_RANZ, nseg); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_RAW_SDATA, 0xffffffffUL); gccb->gc_cmd_len = roundup(GDT_CMD_UNION + GDT_RAW_SG_LST + nseg * GDT_SG_SZ, sizeof(u_int32_t)); } if (nseg != 0) { bus_dmamap_sync(gdt->sc_buffer_dmat, gccb->gc_dmamap, (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); } /* We must NOT abort the command here if CAM_REQ_INPROG is not set, * because command semaphore is already set! */ ccb->ccb_h.status |= CAM_SIM_QUEUED; /* timeout handling */ - ccb->ccb_h.timeout_ch = + gccb->gc_timeout_ch = timeout(iir_timeout, (caddr_t)gccb, (ccb->ccb_h.timeout * hz) / 1000); gdt->sc_copy_cmd(gdt, gccb); splx(lock); } static void iir_action( struct cam_sim *sim, union ccb *ccb ) { struct gdt_softc *gdt; int lock, bus, target, lun; gdt = (struct gdt_softc *)cam_sim_softc( sim ); ccb->ccb_h.ccb_sim_ptr = sim; bus = cam_sim_bus(sim); target = ccb->ccb_h.target_id; lun = ccb->ccb_h.target_lun; GDT_DPRINTF(GDT_D_CMD, ("iir_action(%p) func 0x%x cmd 0x%x bus %d target %d lun %d\n", gdt, ccb->ccb_h.func_code, ccb->csio.cdb_io.cdb_bytes[0], bus, target, lun)); ++gdt_stat.io_count_act; if (gdt_stat.io_count_act > gdt_stat.io_count_max) gdt_stat.io_count_max = gdt_stat.io_count_act; switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: lock = splcam(); TAILQ_INSERT_TAIL(&gdt->sc_ccb_queue, &ccb->ccb_h, sim_links.tqe); ++gdt_stat.req_queue_act; if (gdt_stat.req_queue_act > gdt_stat.req_queue_max) gdt_stat.req_queue_max = gdt_stat.req_queue_act; splx(lock); gdt_next(gdt); break; case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; --gdt_stat.io_count_act; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; --gdt_stat.io_count_act; xpt_done(ccb); break; case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts = &ccb->cts; struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi; struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi; cts->protocol = PROTO_SCSI; cts->protocol_version = SCSI_REV_2; cts->transport = XPORT_SPI; cts->transport_version = 2; if (cts->type == CTS_TYPE_USER_SETTINGS) { spi->flags = CTS_SPI_FLAGS_DISC_ENB; scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; spi->sync_period = 25; /* 10MHz */ if (spi->sync_period != 0) spi->sync_offset = 15; spi->valid = CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET | CTS_SPI_VALID_BUS_WIDTH | CTS_SPI_VALID_DISC; scsi->valid = CTS_SCSI_VALID_TQ; ccb->ccb_h.status = CAM_REQ_CMP; } else { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; } --gdt_stat.io_count_act; xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg; u_int32_t secs_per_cylinder; ccg = &ccb->ccg; ccg->heads = gdt->sc_hdr[target].hd_heads; ccg->secs_per_track = gdt->sc_hdr[target].hd_secs; secs_per_cylinder = ccg->heads * ccg->secs_per_track; ccg->cylinders = ccg->volume_size / secs_per_cylinder; ccb->ccb_h.status = CAM_REQ_CMP; --gdt_stat.io_count_act; xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ { /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_CMP; --gdt_stat.io_count_act; xpt_done(ccb); break; } case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; --gdt_stat.io_count_act; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE; cpi->hba_inquiry |= PI_WIDE_16; cpi->target_sprt = 1; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; if (bus == gdt->sc_virt_bus) cpi->max_target = GDT_MAX_HDRIVES - 1; else if (gdt->sc_class & GDT_FC) cpi->max_target = GDT_MAXID_FC - 1; else cpi->max_target = GDT_MAXID - 1; cpi->max_lun = 7; cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = bus; cpi->initiator_id = (bus == gdt->sc_virt_bus ? 127 : gdt->sc_bus_id[bus]); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); if (gdt->sc_vendor == INTEL_VENDOR_ID_IIR) strncpy(cpi->hba_vid, "Intel Corp.", HBA_IDLEN); else strncpy(cpi->hba_vid, "ICP vortex ", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->transport = XPORT_SPI; cpi->transport_version = 2; cpi->protocol = PROTO_SCSI; cpi->protocol_version = SCSI_REV_2; cpi->ccb_h.status = CAM_REQ_CMP; --gdt_stat.io_count_act; xpt_done(ccb); break; } default: GDT_DPRINTF(GDT_D_INVALID, ("gdt_next(%p) cmd 0x%x invalid\n", gdt, ccb->ccb_h.func_code)); ccb->ccb_h.status = CAM_REQ_INVALID; --gdt_stat.io_count_act; xpt_done(ccb); break; } } static void iir_poll( struct cam_sim *sim ) { struct gdt_softc *gdt; gdt = (struct gdt_softc *)cam_sim_softc( sim ); GDT_DPRINTF(GDT_D_CMD, ("iir_poll sim %p gdt %p\n", sim, gdt)); iir_intr(gdt); } static void iir_timeout(void *arg) { GDT_DPRINTF(GDT_D_TIMEOUT, ("iir_timeout(%p)\n", gccb)); } static void iir_shutdown( void *arg, int howto ) { struct gdt_softc *gdt; struct gdt_ccb *gccb; gdt_ucmd_t *ucmd; int lock, i; gdt = (struct gdt_softc *)arg; GDT_DPRINTF(GDT_D_CMD, ("iir_shutdown(%p, %d)\n", gdt, howto)); printf("iir%d: Flushing all Host Drives. Please wait ... ", gdt->sc_hanum); /* allocate ucmd buffer */ ucmd = malloc(sizeof(gdt_ucmd_t), M_GDTBUF, M_NOWAIT); if (ucmd == NULL) { printf("iir%d: iir_shutdown(): Cannot allocate resource\n", gdt->sc_hanum); return; } bzero(ucmd, sizeof(gdt_ucmd_t)); /* wait for pending IOs */ lock = splcam(); gdt->sc_state = GDT_SHUTDOWN; splx(lock); if ((gccb = SLIST_FIRST(&gdt->sc_pending_gccb)) != NULL) (void) tsleep((void *)gccb, PCATCH | PRIBIO, "iirshw", 100 * hz); /* flush */ for (i = 0; i < GDT_MAX_HDRIVES; ++i) { if (gdt->sc_hdr[i].hd_present) { ucmd->service = GDT_CACHESERVICE; ucmd->OpCode = GDT_FLUSH; ucmd->u.cache.DeviceNo = i; lock = splcam(); TAILQ_INSERT_TAIL(&gdt->sc_ucmd_queue, ucmd, links); ucmd->complete_flag = FALSE; splx(lock); gdt_next(gdt); if (!ucmd->complete_flag) (void) tsleep((void *)ucmd, PCATCH|PRIBIO, "iirshw", 10*hz); } } free(ucmd, M_DEVBUF); printf("Done.\n"); } void iir_intr(void *arg) { struct gdt_softc *gdt = arg; struct gdt_intr_ctx ctx; int lock = 0; struct gdt_ccb *gccb; gdt_ucmd_t *ucmd; u_int32_t cnt; GDT_DPRINTF(GDT_D_INTR, ("gdt_intr(%p)\n", gdt)); /* If polling and we were not called from gdt_wait, just return */ if ((gdt->sc_state & GDT_POLLING) && !(gdt->sc_state & GDT_POLL_WAIT)) return; if (!(gdt->sc_state & GDT_POLLING)) lock = splcam(); gdt_wait_index = 0; ctx.istatus = gdt->sc_get_status(gdt); if (ctx.istatus == 0x00) { if (!(gdt->sc_state & GDT_POLLING)) splx(lock); gdt->sc_status = GDT_S_NO_STATUS; return; } gdt->sc_intr(gdt, &ctx); gdt->sc_status = ctx.cmd_status; gdt->sc_service = ctx.service; gdt->sc_info = ctx.info; gdt->sc_info2 = ctx.info2; if (gdt->sc_state & GDT_POLL_WAIT) { gdt_wait_gdt = gdt; gdt_wait_index = ctx.istatus; } if (ctx.istatus == GDT_ASYNCINDEX) { gdt_async_event(gdt, ctx.service); if (!(gdt->sc_state & GDT_POLLING)) splx(lock); return; } if (ctx.istatus == GDT_SPEZINDEX) { GDT_DPRINTF(GDT_D_INVALID, ("iir%d: Service unknown or not initialized!\n", gdt->sc_hanum)); gdt->sc_dvr.size = sizeof(gdt->sc_dvr.eu.driver); gdt->sc_dvr.eu.driver.ionode = gdt->sc_hanum; gdt_store_event(GDT_ES_DRIVER, 4, &gdt->sc_dvr); if (!(gdt->sc_state & GDT_POLLING)) splx(lock); return; } gccb = &gdt->sc_gccbs[ctx.istatus - 2]; ctx.service = gccb->gc_service; switch (gccb->gc_flags) { case GDT_GCF_UNUSED: GDT_DPRINTF(GDT_D_INVALID, ("iir%d: Index (%d) to unused command!\n", gdt->sc_hanum, ctx.istatus)); gdt->sc_dvr.size = sizeof(gdt->sc_dvr.eu.driver); gdt->sc_dvr.eu.driver.ionode = gdt->sc_hanum; gdt->sc_dvr.eu.driver.index = ctx.istatus; gdt_store_event(GDT_ES_DRIVER, 1, &gdt->sc_dvr); gdt_free_ccb(gdt, gccb); /* fallthrough */ case GDT_GCF_INTERNAL: if (!(gdt->sc_state & GDT_POLLING)) splx(lock); break; case GDT_GCF_IOCTL: ucmd = gccb->gc_ucmd; if (gdt->sc_status == GDT_S_BSY) { GDT_DPRINTF(GDT_D_DEBUG, ("iir_intr(%p) ioctl: gccb %p busy\n", gdt, gccb)); TAILQ_INSERT_HEAD(&gdt->sc_ucmd_queue, ucmd, links); if (!(gdt->sc_state & GDT_POLLING)) splx(lock); } else { ucmd->status = gdt->sc_status; ucmd->info = gdt->sc_info; ucmd->complete_flag = TRUE; if (ucmd->service == GDT_CACHESERVICE) { if (ucmd->OpCode == GDT_IOCTL) { cnt = ucmd->u.ioctl.param_size; if (cnt != 0) bcopy(gccb->gc_scratch, ucmd->data, cnt); } else { cnt = ucmd->u.cache.BlockCnt * GDT_SECTOR_SIZE; if (cnt != 0) bcopy(gccb->gc_scratch, ucmd->data, cnt); } } else { cnt = ucmd->u.raw.sdlen; if (cnt != 0) bcopy(gccb->gc_scratch, ucmd->data, cnt); if (ucmd->u.raw.sense_len != 0) bcopy(gccb->gc_scratch, ucmd->data, cnt); } gdt_free_ccb(gdt, gccb); if (!(gdt->sc_state & GDT_POLLING)) splx(lock); /* wakeup */ wakeup(ucmd); } gdt_next(gdt); break; default: gdt_free_ccb(gdt, gccb); gdt_sync_event(gdt, ctx.service, ctx.istatus, gccb); if (!(gdt->sc_state & GDT_POLLING)) splx(lock); gdt_next(gdt); break; } } int gdt_async_event(struct gdt_softc *gdt, int service) { struct gdt_ccb *gccb; GDT_DPRINTF(GDT_D_INTR, ("gdt_async_event(%p, %d)\n", gdt, service)); if (service == GDT_SCREENSERVICE) { if (gdt->sc_status == GDT_MSG_REQUEST) { while (gdt->sc_test_busy(gdt)) DELAY(1); gccb = gdt_get_ccb(gdt); if (gccb == NULL) { printf("iir%d: No free command index found\n", gdt->sc_hanum); return (1); } bzero(gccb->gc_cmd, GDT_CMD_SZ); gccb->gc_service = service; gccb->gc_flags = GDT_GCF_SCREEN; gdt_enc32(gccb->gc_cmd + GDT_CMD_COMMANDINDEX, gccb->gc_cmd_index); gdt_enc16(gccb->gc_cmd + GDT_CMD_OPCODE, GDT_READ); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_SCREEN_MSG_HANDLE, GDT_MSG_INV_HANDLE); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_SCREEN_MSG_ADDR, gccb->gc_scratch_busbase); gdt->sc_set_sema0(gdt); gdt->sc_cmd_off = 0; gccb->gc_cmd_len = roundup(GDT_CMD_UNION + GDT_SCREEN_SZ, sizeof(u_int32_t)); gdt->sc_cmd_cnt = 0; gdt->sc_copy_cmd(gdt, gccb); printf("iir%d: [PCI %d/%d] ", gdt->sc_hanum,gdt->sc_bus,gdt->sc_slot); gdt->sc_release_event(gdt); } } else { if ((gdt->sc_fw_vers & 0xff) >= 0x1a) { gdt->sc_dvr.size = 0; gdt->sc_dvr.eu.async.ionode = gdt->sc_hanum; gdt->sc_dvr.eu.async.status = gdt->sc_status; /* severity and event_string already set! */ } else { gdt->sc_dvr.size = sizeof(gdt->sc_dvr.eu.async); gdt->sc_dvr.eu.async.ionode = gdt->sc_hanum; gdt->sc_dvr.eu.async.service = service; gdt->sc_dvr.eu.async.status = gdt->sc_status; gdt->sc_dvr.eu.async.info = gdt->sc_info; *(u_int32_t *)gdt->sc_dvr.eu.async.scsi_coord = gdt->sc_info2; } gdt_store_event(GDT_ES_ASYNC, service, &gdt->sc_dvr); printf("iir%d: %s\n", gdt->sc_hanum, gdt->sc_dvr.event_string); } return (0); } int gdt_sync_event(struct gdt_softc *gdt, int service, u_int8_t index, struct gdt_ccb *gccb) { union ccb *ccb; GDT_DPRINTF(GDT_D_INTR, ("gdt_sync_event(%p, %d, %d, %p)\n", gdt,service,index,gccb)); ccb = gccb->gc_ccb; if (service == GDT_SCREENSERVICE) { u_int32_t msg_len; msg_len = gdt_dec32(gccb->gc_scratch + GDT_SCR_MSG_LEN); if (msg_len) if (!(gccb->gc_scratch[GDT_SCR_MSG_ANSWER] && gccb->gc_scratch[GDT_SCR_MSG_EXT])) { gccb->gc_scratch[GDT_SCR_MSG_TEXT + msg_len] = '\0'; printf("%s",&gccb->gc_scratch[GDT_SCR_MSG_TEXT]); } if (gccb->gc_scratch[GDT_SCR_MSG_EXT] && !gccb->gc_scratch[GDT_SCR_MSG_ANSWER]) { while (gdt->sc_test_busy(gdt)) DELAY(1); bzero(gccb->gc_cmd, GDT_CMD_SZ); gccb = gdt_get_ccb(gdt); if (gccb == NULL) { printf("iir%d: No free command index found\n", gdt->sc_hanum); return (1); } gccb->gc_service = service; gccb->gc_flags = GDT_GCF_SCREEN; gdt_enc32(gccb->gc_cmd + GDT_CMD_COMMANDINDEX, gccb->gc_cmd_index); gdt_enc16(gccb->gc_cmd + GDT_CMD_OPCODE, GDT_READ); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_SCREEN_MSG_HANDLE, gccb->gc_scratch[GDT_SCR_MSG_HANDLE]); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_SCREEN_MSG_ADDR, gccb->gc_scratch_busbase); gdt->sc_set_sema0(gdt); gdt->sc_cmd_off = 0; gccb->gc_cmd_len = roundup(GDT_CMD_UNION + GDT_SCREEN_SZ, sizeof(u_int32_t)); gdt->sc_cmd_cnt = 0; gdt->sc_copy_cmd(gdt, gccb); gdt->sc_release_event(gdt); return (0); } if (gccb->gc_scratch[GDT_SCR_MSG_ANSWER] && gdt_dec32(gccb->gc_scratch + GDT_SCR_MSG_ALEN)) { /* default answers (getchar() not possible) */ if (gdt_dec32(gccb->gc_scratch + GDT_SCR_MSG_ALEN) == 1) { gdt_enc32(gccb->gc_scratch + GDT_SCR_MSG_ALEN, 0); gdt_enc32(gccb->gc_scratch + GDT_SCR_MSG_LEN, 1); gccb->gc_scratch[GDT_SCR_MSG_TEXT] = 0; } else { gdt_enc32(gccb->gc_scratch + GDT_SCR_MSG_ALEN, gdt_dec32(gccb->gc_scratch + GDT_SCR_MSG_ALEN) - 2); gdt_enc32(gccb->gc_scratch + GDT_SCR_MSG_LEN, 2); gccb->gc_scratch[GDT_SCR_MSG_TEXT] = 1; gccb->gc_scratch[GDT_SCR_MSG_TEXT + 1] = 0; } gccb->gc_scratch[GDT_SCR_MSG_EXT] = 0; gccb->gc_scratch[GDT_SCR_MSG_ANSWER] = 0; while (gdt->sc_test_busy(gdt)) DELAY(1); bzero(gccb->gc_cmd, GDT_CMD_SZ); gccb = gdt_get_ccb(gdt); if (gccb == NULL) { printf("iir%d: No free command index found\n", gdt->sc_hanum); return (1); } gccb->gc_service = service; gccb->gc_flags = GDT_GCF_SCREEN; gdt_enc32(gccb->gc_cmd + GDT_CMD_COMMANDINDEX, gccb->gc_cmd_index); gdt_enc16(gccb->gc_cmd + GDT_CMD_OPCODE, GDT_WRITE); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_SCREEN_MSG_HANDLE, gccb->gc_scratch[GDT_SCR_MSG_HANDLE]); gdt_enc32(gccb->gc_cmd + GDT_CMD_UNION + GDT_SCREEN_MSG_ADDR, gccb->gc_scratch_busbase); gdt->sc_set_sema0(gdt); gdt->sc_cmd_off = 0; gccb->gc_cmd_len = roundup(GDT_CMD_UNION + GDT_SCREEN_SZ, sizeof(u_int32_t)); gdt->sc_cmd_cnt = 0; gdt->sc_copy_cmd(gdt, gccb); gdt->sc_release_event(gdt); return (0); } printf("\n"); return (0); } else { - untimeout(iir_timeout, gccb, ccb->ccb_h.timeout_ch); + untimeout(iir_timeout, gccb, gccb->gc_timeout_ch); if (gdt->sc_status == GDT_S_BSY) { GDT_DPRINTF(GDT_D_DEBUG, ("gdt_sync_event(%p) gccb %p busy\n", gdt, gccb)); TAILQ_INSERT_HEAD(&gdt->sc_ccb_queue, &ccb->ccb_h, sim_links.tqe); ++gdt_stat.req_queue_act; if (gdt_stat.req_queue_act > gdt_stat.req_queue_max) gdt_stat.req_queue_max = gdt_stat.req_queue_act; return (2); } bus_dmamap_sync(gdt->sc_buffer_dmat, gccb->gc_dmamap, (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(gdt->sc_buffer_dmat, gccb->gc_dmamap); ccb->csio.resid = 0; if (gdt->sc_status == GDT_S_OK) { ccb->ccb_h.status |= CAM_REQ_CMP; ccb->ccb_h.status &= ~CAM_SIM_QUEUED; } else { /* error */ if (gccb->gc_service == GDT_CACHESERVICE) { struct scsi_sense_data *sense; ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; ccb->ccb_h.status &= ~CAM_SIM_QUEUED; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; bzero(&ccb->csio.sense_data, ccb->csio.sense_len); sense = &ccb->csio.sense_data; scsi_set_sense_data(sense, /*sense_format*/ SSD_TYPE_NONE, /*current_error*/ 1, /*sense_key*/ SSD_KEY_NOT_READY, /*asc*/ 0x4, /*ascq*/ 0x01, SSD_ELEM_NONE); gdt->sc_dvr.size = sizeof(gdt->sc_dvr.eu.sync); gdt->sc_dvr.eu.sync.ionode = gdt->sc_hanum; gdt->sc_dvr.eu.sync.service = service; gdt->sc_dvr.eu.sync.status = gdt->sc_status; gdt->sc_dvr.eu.sync.info = gdt->sc_info; gdt->sc_dvr.eu.sync.hostdrive = ccb->ccb_h.target_id; if (gdt->sc_status >= 0x8000) gdt_store_event(GDT_ES_SYNC, 0, &gdt->sc_dvr); else gdt_store_event(GDT_ES_SYNC, service, &gdt->sc_dvr); } else { /* raw service */ if (gdt->sc_status != GDT_S_RAW_SCSI || gdt->sc_info >= 0x100) { ccb->ccb_h.status = CAM_DEV_NOT_THERE; } else { ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR|CAM_AUTOSNS_VALID; ccb->ccb_h.status &= ~CAM_SIM_QUEUED; ccb->csio.scsi_status = gdt->sc_info; bcopy(gccb->gc_scratch, &ccb->csio.sense_data, ccb->csio.sense_len); } } } --gdt_stat.io_count_act; xpt_done(ccb); } return (0); } /* Controller event handling functions */ gdt_evt_str *gdt_store_event(u_int16_t source, u_int16_t idx, gdt_evt_data *evt) { gdt_evt_str *e; struct timeval tv; GDT_DPRINTF(GDT_D_MISC, ("gdt_store_event(%d, %d)\n", source, idx)); if (source == 0) /* no source -> no event */ return 0; if (ebuffer[elastidx].event_source == source && ebuffer[elastidx].event_idx == idx && ((evt->size != 0 && ebuffer[elastidx].event_data.size != 0 && !memcmp((char *)&ebuffer[elastidx].event_data.eu, (char *)&evt->eu, evt->size)) || (evt->size == 0 && ebuffer[elastidx].event_data.size == 0 && !strcmp((char *)&ebuffer[elastidx].event_data.event_string, (char *)&evt->event_string)))) { e = &ebuffer[elastidx]; getmicrotime(&tv); e->last_stamp = tv.tv_sec; ++e->same_count; } else { if (ebuffer[elastidx].event_source != 0) { /* entry not free ? */ ++elastidx; if (elastidx == GDT_MAX_EVENTS) elastidx = 0; if (elastidx == eoldidx) { /* reached mark ? */ ++eoldidx; if (eoldidx == GDT_MAX_EVENTS) eoldidx = 0; } } e = &ebuffer[elastidx]; e->event_source = source; e->event_idx = idx; getmicrotime(&tv); e->first_stamp = e->last_stamp = tv.tv_sec; e->same_count = 1; e->event_data = *evt; e->application = 0; } return e; } int gdt_read_event(int handle, gdt_evt_str *estr) { gdt_evt_str *e; int eindex, lock; GDT_DPRINTF(GDT_D_MISC, ("gdt_read_event(%d)\n", handle)); lock = splcam(); if (handle == -1) eindex = eoldidx; else eindex = handle; estr->event_source = 0; if (eindex >= GDT_MAX_EVENTS) { splx(lock); return eindex; } e = &ebuffer[eindex]; if (e->event_source != 0) { if (eindex != elastidx) { if (++eindex == GDT_MAX_EVENTS) eindex = 0; } else { eindex = -1; } memcpy(estr, e, sizeof(gdt_evt_str)); } splx(lock); return eindex; } void gdt_readapp_event(u_int8_t application, gdt_evt_str *estr) { gdt_evt_str *e; int found = FALSE; int eindex, lock; GDT_DPRINTF(GDT_D_MISC, ("gdt_readapp_event(%d)\n", application)); lock = splcam(); eindex = eoldidx; for (;;) { e = &ebuffer[eindex]; if (e->event_source == 0) break; if ((e->application & application) == 0) { e->application |= application; found = TRUE; break; } if (eindex == elastidx) break; if (++eindex == GDT_MAX_EVENTS) eindex = 0; } if (found) memcpy(estr, e, sizeof(gdt_evt_str)); else estr->event_source = 0; splx(lock); } void gdt_clear_events() { GDT_DPRINTF(GDT_D_MISC, ("gdt_clear_events\n")); eoldidx = elastidx = 0; ebuffer[0].event_source = 0; } diff --git a/sys/dev/iir/iir.h b/sys/dev/iir/iir.h index dbae7d2a80f5..de7b641257e9 100644 --- a/sys/dev/iir/iir.h +++ b/sys/dev/iir/iir.h @@ -1,754 +1,755 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2000-04 ICP vortex GmbH * Copyright (c) 2002-04 Intel Corporation * Copyright (c) 2003-04 Adaptec Inc. * All Rights Reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * * iir.h: Definitions/Constants used by the Intel Integrated RAID driver * * Written by: Achim Leubner * Fixes/Additions: Boji Tony Kannanthanam * * credits: Niklas Hallqvist; OpenBSD driver for the ICP Controllers. * FreeBSD.ORG; Great O/S to work on and for. * * $Id: iir.h 1.6 2004/03/30 10:19:44 achim Exp $" */ #ifndef _IIR_H #define _IIR_H #ifndef _SYS_CDEFS_H_ #error this file needs sys/cdefs.h as a prerequisite #endif #define IIR_DRIVER_VERSION 1 #define IIR_DRIVER_SUBVERSION 5 /* OEM IDs */ #define OEM_ID_ICP 0x941c #define OEM_ID_INTEL 0x8000 #define GDT_VENDOR_ID 0x1119 #define GDT_DEVICE_ID_MIN 0x100 #define GDT_DEVICE_ID_MAX 0x2ff #define GDT_DEVICE_ID_NEWRX 0x300 #define INTEL_VENDOR_ID_IIR 0x8086 #define INTEL_DEVICE_ID_IIR 0x600 #define GDT_MAXBUS 6 /* XXX Why not 5? */ #define GDT_MAX_HDRIVES 100 /* max 100 host drives */ #define GDT_MAXID_FC 127 /* Fibre-channel IDs */ #define GDT_MAXID 16 /* SCSI IDs */ #define GDT_MAXOFFSETS 128 #define GDT_MAXSG 32 /* Max. s/g elements */ #define GDT_PROTOCOL_VERSION 1 #define GDT_LINUX_OS 8 /* Used for cache optimization */ #define GDT_SCATTER_GATHER 1 /* s/g feature */ #define GDT_SECS32 0x1f /* round capacity */ #define GDT_LOCALBOARD 0 /* Board node always 0 */ #define GDT_MAXCMDS 124 #define GDT_SECTOR_SIZE 0x200 /* Always 512 bytes for cache devs */ #define GDT_MAX_EVENTS 0x100 /* event buffer */ /* DPMEM constants */ #define GDT_MPR_MAGIC 0xc0ffee11 #define GDT_IC_HEADER_BYTES 48 #define GDT_IC_QUEUE_BYTES 4 #define GDT_DPMEM_COMMAND_OFFSET \ (GDT_IC_HEADER_BYTES + GDT_IC_QUEUE_BYTES * GDT_MAXOFFSETS) /* geometry constants */ #define GDT_MAXCYLS 1024 #define GDT_HEADS 64 #define GDT_SECS 32 /* mapping 64*32 */ #define GDT_MEDHEADS 127 #define GDT_MEDSECS 63 /* mapping 127*63 */ #define GDT_BIGHEADS 255 #define GDT_BIGSECS 63 /* mapping 255*63 */ /* data direction raw service */ #define GDT_DATA_IN 0x01000000L #define GDT_DATA_OUT 0x00000000L /* Cache/raw service commands */ #define GDT_INIT 0 /* service initialization */ #define GDT_READ 1 /* read command */ #define GDT_WRITE 2 /* write command */ #define GDT_INFO 3 /* information about devices */ #define GDT_FLUSH 4 /* flush dirty cache buffers */ #define GDT_IOCTL 5 /* ioctl command */ #define GDT_DEVTYPE 9 /* additional information */ #define GDT_MOUNT 10 /* mount cache device */ #define GDT_UNMOUNT 11 /* unmount cache device */ #define GDT_SET_FEAT 12 /* set features (scatter/gather) */ #define GDT_GET_FEAT 13 /* get features */ #define GDT_WRITE_THR 16 /* write through */ #define GDT_READ_THR 17 /* read through */ #define GDT_EXT_INFO 18 /* extended info */ #define GDT_RESET 19 /* controller reset */ #define GDT_FREEZE_IO 25 /* freeze all IOs */ #define GDT_UNFREEZE_IO 26 /* unfreeze all IOs */ /* Additional raw service commands */ #define GDT_RESERVE 14 /* reserve device to raw service */ #define GDT_RELEASE 15 /* release device */ #define GDT_RESERVE_ALL 16 /* reserve all devices */ #define GDT_RELEASE_ALL 17 /* release all devices */ #define GDT_RESET_BUS 18 /* reset bus */ #define GDT_SCAN_START 19 /* start device scan */ #define GDT_SCAN_END 20 /* stop device scan */ /* IOCTL command defines */ #define GDT_SCSI_DR_INFO 0x00 /* SCSI drive info */ #define GDT_SCSI_CHAN_CNT 0x05 /* SCSI channel count */ #define GDT_SCSI_DR_LIST 0x06 /* SCSI drive list */ #define GDT_SCSI_DEF_CNT 0x15 /* grown/primary defects */ #define GDT_DSK_STATISTICS 0x4b /* SCSI disk statistics */ #define GDT_IOCHAN_DESC 0x5d /* description of IO channel */ #define GDT_IOCHAN_RAW_DESC 0x5e /* description of raw IO channel */ #define GDT_L_CTRL_PATTERN 0x20000000 /* SCSI IOCTL mask */ #define GDT_ARRAY_INFO 0x12 /* array drive info */ #define GDT_ARRAY_DRV_LIST 0x0f /* array drive list */ #define GDT_LA_CTRL_PATTERN 0x10000000 /* array IOCTL mask */ #define GDT_CACHE_DRV_CNT 0x01 /* cache drive count */ #define GDT_CACHE_DRV_LIST 0x02 /* cache drive list */ #define GDT_CACHE_INFO 0x04 /* cache info */ #define GDT_CACHE_CONFIG 0x05 /* cache configuration */ #define GDT_CACHE_DRV_INFO 0x07 /* cache drive info */ #define GDT_BOARD_FEATURES 0x15 /* controller features */ #define GDT_BOARD_INFO 0x28 /* controller info */ #define GDT_OEM_STR_RECORD 0x84 /* OEM info */ #define GDT_HOST_GET 0x10001 /* get host drive list */ #define GDT_IO_CHANNEL 0x20000 /* default IO channel */ #define GDT_INVALID_CHANNEL 0xffff /* invalid channel */ /* IOCTLs */ #define GDT_IOCTL_GENERAL _IOWR('J', 0, gdt_ucmd_t) /* general IOCTL */ #define GDT_IOCTL_DRVERS _IOR('J', 1, int) /* get driver version */ #define GDT_IOCTL_CTRTYPE _IOWR('J', 2, gdt_ctrt_t) /* get ctr. type */ #define GDT_IOCTL_DRVERS_OLD _IOWR('J', 1, int) /* get driver version */ #define GDT_IOCTL_CTRTYPE_OLD _IOR('J', 2, gdt_ctrt_t) /* get ctr. type */ #define GDT_IOCTL_OSVERS _IOR('J', 3, gdt_osv_t) /* get OS version */ #define GDT_IOCTL_CTRCNT _IOR('J', 5, int) /* get ctr. count */ #define GDT_IOCTL_EVENT _IOWR('J', 8, gdt_event_t) /* get event */ #define GDT_IOCTL_STATIST _IOR('J', 9, gdt_statist_t) /* get statistics */ /* Service errors */ #define GDT_S_OK 1 /* no error */ #define GDT_S_BSY 7 /* controller busy */ #define GDT_S_RAW_SCSI 12 /* raw service: target error */ #define GDT_S_RAW_ILL 0xff /* raw service: illegal */ #define GDT_S_NO_STATUS 0x1000 /* got no status (driver-generated) */ /* Controller services */ #define GDT_SCSIRAWSERVICE 3 #define GDT_CACHESERVICE 9 #define GDT_SCREENSERVICE 11 /* Scatter/gather element */ #define GDT_SG_PTR 0x00 /* u_int32_t, address */ #define GDT_SG_LEN 0x04 /* u_int32_t, length */ #define GDT_SG_SZ 0x08 /* Cache service command */ #define GDT_CACHE_DEVICENO 0x00 /* u_int16_t, number of cache drive */ #define GDT_CACHE_BLOCKNO 0x02 /* u_int32_t, block number */ #define GDT_CACHE_BLOCKCNT 0x06 /* u_int32_t, block count */ #define GDT_CACHE_DESTADDR 0x0a /* u_int32_t, dest. addr. (-1: s/g) */ #define GDT_CACHE_SG_CANZ 0x0e /* u_int32_t, s/g element count */ #define GDT_CACHE_SG_LST 0x12 /* [GDT_MAXSG], s/g list */ #define GDT_CACHE_SZ (0x12 + GDT_MAXSG * GDT_SG_SZ) /* Ioctl command */ #define GDT_IOCTL_PARAM_SIZE 0x00 /* u_int16_t, size of buffer */ #define GDT_IOCTL_SUBFUNC 0x02 /* u_int32_t, ioctl function */ #define GDT_IOCTL_CHANNEL 0x06 /* u_int32_t, device */ #define GDT_IOCTL_P_PARAM 0x0a /* u_int32_t, buffer */ #define GDT_IOCTL_SZ 0x0e /* Screen service defines */ #define GDT_MSG_INV_HANDLE -1 /* special message handle */ #define GDT_MSGLEN 16 /* size of message text */ #define GDT_MSG_SIZE 34 /* size of message structure */ #define GDT_MSG_REQUEST 0 /* async. event. message */ /* Screen service command */ #define GDT_SCREEN_MSG_HANDLE 0x02 /* u_int32_t, message handle */ #define GDT_SCREEN_MSG_ADDR 0x06 /* u_int32_t, message buffer address */ #define GDT_SCREEN_SZ 0x0a /* Screen service message */ #define GDT_SCR_MSG_HANDLE 0x00 /* u_int32_t, message handle */ #define GDT_SCR_MSG_LEN 0x04 /* u_int32_t, size of message */ #define GDT_SCR_MSG_ALEN 0x08 /* u_int32_t, answer length */ #define GDT_SCR_MSG_ANSWER 0x0c /* u_int8_t, answer flag */ #define GDT_SCR_MSG_EXT 0x0d /* u_int8_t, more messages? */ #define GDT_SCR_MSG_RES 0x0e /* u_int16_t, reserved */ #define GDT_SCR_MSG_TEXT 0x10 /* GDT_MSGLEN+2, message text */ #define GDT_SCR_MSG_SZ (0x12 + GDT_MSGLEN) /* Raw service command */ #define GDT_RAW_DIRECTION 0x02 /* u_int32_t, data direction */ #define GDT_RAW_MDISC_TIME 0x06 /* u_int32_t, disc. time (0: none) */ #define GDT_RAW_MCON_TIME 0x0a /* u_int32_t, conn. time (0: none) */ #define GDT_RAW_SDATA 0x0e /* u_int32_t, dest. addr. (-1: s/g) */ #define GDT_RAW_SDLEN 0x12 /* u_int32_t, data length */ #define GDT_RAW_CLEN 0x16 /* u_int32_t, SCSI cmd len (6/10/12) */ #define GDT_RAW_CMD 0x1a /* u_int8_t [12], SCSI command */ #define GDT_RAW_TARGET 0x26 /* u_int8_t, target ID */ #define GDT_RAW_LUN 0x27 /* u_int8_t, LUN */ #define GDT_RAW_BUS 0x28 /* u_int8_t, SCSI bus number */ #define GDT_RAW_PRIORITY 0x29 /* u_int8_t, only 0 used */ #define GDT_RAW_SENSE_LEN 0x2a /* u_int32_t, sense data length */ #define GDT_RAW_SENSE_DATA 0x2e /* u_int32_t, sense data address */ #define GDT_RAW_SG_RANZ 0x36 /* u_int32_t, s/g element count */ #define GDT_RAW_SG_LST 0x3a /* [GDT_MAXSG], s/g list */ #define GDT_RAW_SZ (0x3a + GDT_MAXSG * GDT_SG_SZ) /* Command structure */ #define GDT_CMD_BOARDNODE 0x00 /* u_int32_t, board node (always 0) */ #define GDT_CMD_COMMANDINDEX 0x04 /* u_int32_t, command number */ #define GDT_CMD_OPCODE 0x08 /* u_int16_t, opcode (READ, ...) */ #define GDT_CMD_UNION 0x0a /* cache/screen/raw service command */ #define GDT_CMD_UNION_SZ GDT_RAW_SZ #define GDT_CMD_SZ (0x0a + GDT_CMD_UNION_SZ) /* Command queue entries */ #define GDT_OFFSET 0x00 /* u_int16_t, command offset in the DP RAM */ #define GDT_SERV_ID 0x02 /* u_int16_t, service */ #define GDT_COMM_Q_SZ 0x04 /* Interface area */ #define GDT_S_CMD_INDX 0x00 /* u_int8_t, special command */ #define GDT_S_STATUS 0x01 /* volatile u_int8_t, status special command */ #define GDT_S_INFO 0x04 /* u_int32_t [4], add. info special command */ #define GDT_SEMA0 0x14 /* volatile u_int8_t, command semaphore */ #define GDT_CMD_INDEX 0x18 /* u_int8_t, command number */ #define GDT_STATUS 0x1c /* volatile u_int16_t, command status */ #define GDT_SERVICE 0x1e /* u_int16_t, service (for asynch. events) */ #define GDT_DPR_INFO 0x20 /* u_int32_t [2], additional info */ #define GDT_COMM_QUEUE 0x28 /* command queue */ #define GDT_DPR_CMD (0x30 + GDT_MAXOFFSETS * GDT_COMM_Q_SZ) /* u_int8_t [], commands */ /* I/O channel header */ #define GDT_IOC_VERSION 0x00 /* u_int32_t, version (~0: newest) */ #define GDT_IOC_LIST_ENTRIES 0x04 /* u_int8_t, list entry count */ #define GDT_IOC_FIRST_CHAN 0x05 /* u_int8_t, first channel number */ #define GDT_IOC_LAST_CHAN 0x06 /* u_int8_t, last channel number */ #define GDT_IOC_CHAN_COUNT 0x07 /* u_int8_t, (R) channel count */ #define GDT_IOC_LIST_OFFSET 0x08 /* u_int32_t, offset of list[0] */ #define GDT_IOC_HDR_SZ 0x0c #define GDT_IOC_NEWEST 0xffffffff /* goes into GDT_IOC_VERSION */ /* Get I/O channel description */ #define GDT_IOC_ADDRESS 0x00 /* u_int32_t, channel address */ #define GDT_IOC_TYPE 0x04 /* u_int8_t, type (SCSI/FCSL) */ #define GDT_IOC_LOCAL_NO 0x05 /* u_int8_t, local number */ #define GDT_IOC_FEATURES 0x06 /* u_int16_t, channel features */ #define GDT_IOC_SZ 0x08 /* Get raw I/O channel description */ #define GDT_RAWIOC_PROC_ID 0x00 /* u_int8_t, processor id */ #define GDT_RAWIOC_PROC_DEFECT 0x01 /* u_int8_t, defect? */ #define GDT_RAWIOC_SZ 0x04 /* Get SCSI channel count */ #define GDT_GETCH_CHANNEL_NO 0x00 /* u_int32_t, channel number */ #define GDT_GETCH_DRIVE_CNT 0x04 /* u_int32_t, drive count */ #define GDT_GETCH_SIOP_ID 0x08 /* u_int8_t, SCSI processor ID */ #define GDT_GETCH_SIOP_STATE 0x09 /* u_int8_t, SCSI processor state */ #define GDT_GETCH_SZ 0x0a /* Cache info/config IOCTL structures */ #define GDT_CPAR_VERSION 0x00 /* u_int32_t, firmware version */ #define GDT_CPAR_STATE 0x04 /* u_int16_t, cache state (on/off) */ #define GDT_CPAR_STRATEGY 0x06 /* u_int16_t, cache strategy */ #define GDT_CPAR_WRITE_BACK 0x08 /* u_int16_t, write back (on/off) */ #define GDT_CPAR_BLOCK_SIZE 0x0a /* u_int16_t, cache block size */ #define GDT_CPAR_SZ 0x0c #define GDT_CSTAT_CSIZE 0x00 /* u_int32_t, cache size */ #define GDT_CSTAT_READ_CNT 0x04 /* u_int32_t, read counter */ #define GDT_CSTAT_WRITE_CNT 0x08 /* u_int32_t, write counter */ #define GDT_CSTAT_TR_HITS 0x0c /* u_int32_t, track hits */ #define GDT_CSTAT_SEC_HITS 0x10 /* u_int32_t, sector hits */ #define GDT_CSTAT_SEC_MISS 0x14 /* u_int32_t, sector misses */ #define GDT_CSTAT_SZ 0x18 /* Get cache info */ #define GDT_CINFO_CPAR 0x00 #define GDT_CINFO_CSTAT GDT_CPAR_SZ #define GDT_CINFO_SZ (GDT_CPAR_SZ + GDT_CSTAT_SZ) /* Get board info */ #define GDT_BINFO_SER_NO 0x00 /* u_int32_t, serial number */ #define GDT_BINFO_OEM_ID 0x04 /* u_int8_t [2], OEM ID */ #define GDT_BINFO_EP_FLAGS 0x06 /* u_int16_t, eprom flags */ #define GDT_BINFO_PROC_ID 0x08 /* u_int32_t, processor ID */ #define GDT_BINFO_MEMSIZE 0x0c /* u_int32_t, memory size (bytes) */ #define GDT_BINFO_MEM_BANKS 0x10 /* u_int8_t, memory banks */ #define GDT_BINFO_CHAN_TYPE 0x11 /* u_int8_t, channel type */ #define GDT_BINFO_CHAN_COUNT 0x12 /* u_int8_t, channel count */ #define GDT_BINFO_RDONGLE_PRES 0x13 /* u_int8_t, dongle present */ #define GDT_BINFO_EPR_FW_VER 0x14 /* u_int32_t, (eprom) firmware ver */ #define GDT_BINFO_UPD_FW_VER 0x18 /* u_int32_t, (update) firmware ver */ #define GDT_BINFO_UPD_REVISION 0x1c /* u_int32_t, update revision */ #define GDT_BINFO_TYPE_STRING 0x20 /* char [16], controller name */ #define GDT_BINFO_RAID_STRING 0x30 /* char [16], RAID firmware name */ #define GDT_BINFO_UPDATE_PRES 0x40 /* u_int8_t, update present? */ #define GDT_BINFO_XOR_PRES 0x41 /* u_int8_t, XOR engine present */ #define GDT_BINFO_PROM_TYPE 0x42 /* u_int8_t, ROM type (eprom/flash) */ #define GDT_BINFO_PROM_COUNT 0x43 /* u_int8_t, number of ROM devices */ #define GDT_BINFO_DUP_PRES 0x44 /* u_int32_t, duplexing module pres? */ #define GDT_BINFO_CHAN_PRES 0x48 /* u_int32_t, # of exp. channels */ #define GDT_BINFO_MEM_PRES 0x4c /* u_int32_t, memory expansion inst? */ #define GDT_BINFO_FT_BUS_SYSTEM 0x50 /* u_int8_t, fault bus supported? */ #define GDT_BINFO_SUBTYPE_VALID 0x51 /* u_int8_t, board_subtype valid */ #define GDT_BINFO_BOARD_SUBTYPE 0x52 /* u_int8_t, subtype/hardware level */ #define GDT_BINFO_RAMPAR_PRES 0x53 /* u_int8_t, RAM parity check hw? */ #define GDT_BINFO_SZ 0x54 /* Get board features */ #define GDT_BFEAT_CHAINING 0x00 /* u_int8_t, chaining supported */ #define GDT_BFEAT_STRIPING 0x01 /* u_int8_t, striping (RAID-0) supp. */ #define GDT_BFEAT_MIRRORING 0x02 /* u_int8_t, mirroring (RAID-1) supp */ #define GDT_BFEAT_RAID 0x03 /* u_int8_t, RAID-4/5/10 supported */ #define GDT_BFEAT_SZ 0x04 /* Other defines */ #define GDT_ASYNCINDEX 0 /* command index asynchronous event */ #define GDT_SPEZINDEX 1 /* command index unknown service */ /* Debugging */ #ifdef GDT_DEBUG #define GDT_D_INTR 0x01 #define GDT_D_MISC 0x02 #define GDT_D_CMD 0x04 #define GDT_D_QUEUE 0x08 #define GDT_D_TIMEOUT 0x10 #define GDT_D_INIT 0x20 #define GDT_D_INVALID 0x40 #define GDT_D_DEBUG 0x80 extern int gdt_debug; #ifdef __SERIAL__ extern int ser_printf(const char *fmt, ...); #define GDT_DPRINTF(mask, args) if (gdt_debug & (mask)) ser_printf args #else #define GDT_DPRINTF(mask, args) if (gdt_debug & (mask)) printf args #endif #else #define GDT_DPRINTF(mask, args) #endif /* Miscellaneous constants */ #define GDT_RETRIES 100000000 /* 100000 * 1us = 100s */ #define GDT_TIMEOUT 100000000 /* 100000 * 1us = 100s */ #define GDT_POLL_TIMEOUT 10000000 /* 10000 * 1us = 10s */ #define GDT_WATCH_TIMEOUT 10000000 /* 10000 * 1us = 10s */ #define GDT_SCRATCH_SZ 3072 /* 3KB scratch buffer */ /* Map minor numbers to device identity */ #define LUN_MASK 0x0007 #define TARGET_MASK 0x03f8 #define BUS_MASK 0x1c00 #define HBA_MASK 0xe000 #define minor2lun(minor) ( minor & LUN_MASK ) #define minor2target(minor) ( (minor & TARGET_MASK) >> 3 ) #define minor2bus(minor) ( (minor & BUS_MASK) >> 10 ) #define minor2hba(minor) ( (minor & HBA_MASK) >> 13 ) #define hba2minor(hba) ( (hba << 13) & HBA_MASK ) /* struct for GDT_IOCTL_GENERAL */ #pragma pack(1) typedef struct gdt_ucmd { u_int16_t io_node; u_int16_t service; u_int32_t timeout; u_int16_t status; u_int32_t info; u_int32_t BoardNode; /* board node (always 0) */ u_int32_t CommandIndex; /* command number */ u_int16_t OpCode; /* the command (READ,..) */ union { struct { u_int16_t DeviceNo; /* number of cache drive */ u_int32_t BlockNo; /* block number */ u_int32_t BlockCnt; /* block count */ void *DestAddr; /* data */ } cache; /* cache service cmd. str. */ struct { u_int16_t param_size; /* size of p_param buffer */ u_int32_t subfunc; /* IOCTL function */ u_int32_t channel; /* device */ void *p_param; /* data */ } ioctl; /* IOCTL command structure */ struct { u_int16_t reserved; u_int32_t direction; /* data direction */ u_int32_t mdisc_time; /* disc. time (0: no timeout)*/ u_int32_t mcon_time; /* connect time(0: no to.) */ void *sdata; /* dest. addr. (if s/g: -1) */ u_int32_t sdlen; /* data length (bytes) */ u_int32_t clen; /* SCSI cmd. length(6,10,12) */ u_int8_t cmd[12]; /* SCSI command */ u_int8_t target; /* target ID */ u_int8_t lun; /* LUN */ u_int8_t bus; /* SCSI bus number */ u_int8_t priority; /* only 0 used */ u_int32_t sense_len; /* sense data length */ void *sense_data; /* sense data addr. */ u_int32_t link_p; /* linked cmds (not supp.) */ } raw; /* raw service cmd. struct. */ } u; u_int8_t data[GDT_SCRATCH_SZ]; int complete_flag; TAILQ_ENTRY(gdt_ucmd) links; } gdt_ucmd_t; /* struct for GDT_IOCTL_CTRTYPE */ typedef struct gdt_ctrt { u_int16_t io_node; u_int16_t oem_id; u_int16_t type; u_int32_t info; u_int8_t access; u_int8_t remote; u_int16_t ext_type; u_int16_t device_id; u_int16_t sub_device_id; } gdt_ctrt_t; /* struct for GDT_IOCTL_OSVERS */ typedef struct gdt_osv { u_int8_t oscode; u_int8_t version; u_int8_t subversion; u_int16_t revision; char name[64]; } gdt_osv_t; /* OEM */ #define GDT_OEM_VERSION 0x00 #define GDT_OEM_BUFSIZE 0x0c typedef struct { u_int32_t ctl_version; u_int32_t file_major_version; u_int32_t file_minor_version; u_int32_t buffer_size; u_int32_t cpy_count; u_int32_t ext_error; u_int32_t oem_id; u_int32_t board_id; } gdt_oem_param_t; typedef struct { char product_0_1_name[16]; char product_4_5_name[16]; char product_cluster_name[16]; char product_reserved[16]; char scsi_cluster_target_vendor_id[16]; char cluster_raid_fw_name[16]; char oem_brand_name[16]; char oem_raid_type[16]; char bios_type[13]; char bios_title[50]; char oem_company_name[37]; u_int32_t pci_id_1; u_int32_t pci_id_2; char validation_status[80]; char reserved_1[4]; char scsi_host_drive_inquiry_vendor_id[16]; char library_file_template[32]; char tool_name_1[32]; char tool_name_2[32]; char tool_name_3[32]; char oem_contact_1[84]; char oem_contact_2[84]; char oem_contact_3[84]; } gdt_oem_record_t; typedef struct { gdt_oem_param_t parameters; gdt_oem_record_t text; } gdt_oem_str_record_t; /* controller event structure */ #define GDT_ES_ASYNC 1 #define GDT_ES_DRIVER 2 #define GDT_ES_TEST 3 #define GDT_ES_SYNC 4 typedef struct { u_int16_t size; /* size of structure */ union { char stream[16]; struct { u_int16_t ionode; u_int16_t service; u_int32_t index; } driver; struct { u_int16_t ionode; u_int16_t service; u_int16_t status; u_int32_t info; u_int8_t scsi_coord[3]; } async; struct { u_int16_t ionode; u_int16_t service; u_int16_t status; u_int32_t info; u_int16_t hostdrive; u_int8_t scsi_coord[3]; u_int8_t sense_key; } sync; struct { u_int32_t l1, l2, l3, l4; } test; } eu; u_int32_t severity; u_int8_t event_string[256]; } gdt_evt_data; /* dvrevt structure */ typedef struct { u_int32_t first_stamp; u_int32_t last_stamp; u_int16_t same_count; u_int16_t event_source; u_int16_t event_idx; u_int8_t application; u_int8_t reserved; gdt_evt_data event_data; } gdt_evt_str; /* struct for GDT_IOCTL_EVENT */ typedef struct gdt_event { int erase; int handle; gdt_evt_str dvr; } gdt_event_t; /* struct for GDT_IOCTL_STATIST */ typedef struct gdt_statist { u_int16_t io_count_act; u_int16_t io_count_max; u_int16_t req_queue_act; u_int16_t req_queue_max; u_int16_t cmd_index_act; u_int16_t cmd_index_max; u_int16_t sg_count_act; u_int16_t sg_count_max; } gdt_statist_t; #pragma pack() /* Context structure for interrupt services */ struct gdt_intr_ctx { u_int32_t info, info2; u_int16_t cmd_status, service; u_int8_t istatus; }; /* softc structure */ struct gdt_softc { device_t sc_devnode; int sc_hanum; int sc_class; /* Controller class */ #define GDT_MPR 0x05 #define GDT_CLASS_MASK 0x07 #define GDT_FC 0x10 #define GDT_CLASS(gdt) ((gdt)->sc_class & GDT_CLASS_MASK) int sc_bus, sc_slot; u_int16_t sc_vendor; u_int16_t sc_device, sc_subdevice; u_int16_t sc_fw_vers; int sc_init_level; int sc_state; #define GDT_NORMAL 0x00 #define GDT_POLLING 0x01 #define GDT_SHUTDOWN 0x02 #define GDT_POLL_WAIT 0x80 struct cdev *sc_dev; bus_space_tag_t sc_dpmemt; bus_space_handle_t sc_dpmemh; bus_addr_t sc_dpmembase; bus_dma_tag_t sc_parent_dmat; bus_dma_tag_t sc_buffer_dmat; bus_dma_tag_t sc_gcscratch_dmat; bus_dmamap_t sc_gcscratch_dmamap; bus_addr_t sc_gcscratch_busbase; struct gdt_ccb *sc_gccbs; u_int8_t *sc_gcscratch; SLIST_HEAD(, gdt_ccb) sc_free_gccb, sc_pending_gccb; TAILQ_HEAD(, ccb_hdr) sc_ccb_queue; TAILQ_HEAD(, gdt_ucmd) sc_ucmd_queue; u_int16_t sc_ic_all_size; u_int16_t sc_cmd_off; u_int16_t sc_cmd_cnt; u_int32_t sc_info; u_int32_t sc_info2; u_int16_t sc_status; u_int16_t sc_service; u_int8_t sc_bus_cnt; u_int8_t sc_virt_bus; u_int8_t sc_bus_id[GDT_MAXBUS]; u_int8_t sc_more_proc; struct { u_int8_t hd_present; u_int8_t hd_is_logdrv; u_int8_t hd_is_arraydrv; u_int8_t hd_is_master; u_int8_t hd_is_parity; u_int8_t hd_is_hotfix; u_int8_t hd_master_no; u_int8_t hd_lock; u_int8_t hd_heads; u_int8_t hd_secs; u_int16_t hd_devtype; u_int32_t hd_size; u_int8_t hd_ldr_no; u_int8_t hd_rw_attribs; u_int32_t hd_start_sec; } sc_hdr[GDT_MAX_HDRIVES]; u_int16_t sc_raw_feat; u_int16_t sc_cache_feat; gdt_evt_data sc_dvr; char oem_name[8]; struct cam_sim *sims[GDT_MAXBUS]; struct cam_path *paths[GDT_MAXBUS]; void (*sc_copy_cmd)(struct gdt_softc *, struct gdt_ccb *); u_int8_t (*sc_get_status)(struct gdt_softc *); void (*sc_intr)(struct gdt_softc *, struct gdt_intr_ctx *); void (*sc_release_event)(struct gdt_softc *); void (*sc_set_sema0)(struct gdt_softc *); int (*sc_test_busy)(struct gdt_softc *); TAILQ_ENTRY(gdt_softc) links; }; /* * A command control block, one for each corresponding command index of the * controller. */ struct gdt_ccb { u_int8_t *gc_scratch; bus_addr_t gc_scratch_busbase; union ccb *gc_ccb; gdt_ucmd_t *gc_ucmd; bus_dmamap_t gc_dmamap; + struct callout_handle gc_timeout_ch; int gc_map_flag; int gc_timeout; u_int8_t gc_service; u_int8_t gc_cmd_index; u_int8_t gc_flags; #define GDT_GCF_UNUSED 0 #define GDT_GCF_INTERNAL 1 #define GDT_GCF_SCREEN 2 #define GDT_GCF_SCSI 3 #define GDT_GCF_IOCTL 4 u_int16_t gc_cmd_len; u_int8_t gc_cmd[GDT_CMD_SZ]; SLIST_ENTRY(gdt_ccb) sle; }; int iir_init(struct gdt_softc *); void iir_free(struct gdt_softc *); void iir_attach(struct gdt_softc *); void iir_intr(void *arg); #ifdef __CC_SUPPORTS___INLINE__ /* These all require correctly aligned buffers */ static __inline__ void gdt_enc16(u_int8_t *, u_int16_t); static __inline__ void gdt_enc32(u_int8_t *, u_int32_t); static __inline__ u_int16_t gdt_dec16(u_int8_t *); static __inline__ u_int32_t gdt_dec32(u_int8_t *); static __inline__ void gdt_enc16(u_int8_t *addr, u_int16_t value) { *(u_int16_t *)addr = htole16(value); } static __inline__ void gdt_enc32(u_int8_t *addr, u_int32_t value) { *(u_int32_t *)addr = htole32(value); } static __inline__ u_int16_t gdt_dec16(u_int8_t *addr) { return le16toh(*(u_int16_t *)addr); } static __inline__ u_int32_t gdt_dec32(u_int8_t *addr) { return le32toh(*(u_int32_t *)addr); } #endif extern TAILQ_HEAD(gdt_softc_list, gdt_softc) gdt_softcs; extern u_int8_t gdt_polling; struct cdev *gdt_make_dev(int unit); void gdt_destroy_dev(struct cdev *dev); void gdt_next(struct gdt_softc *gdt); void gdt_free_ccb(struct gdt_softc *gdt, struct gdt_ccb *gccb); gdt_evt_str *gdt_store_event(u_int16_t source, u_int16_t idx, gdt_evt_data *evt); int gdt_read_event(int handle, gdt_evt_str *estr); void gdt_readapp_event(u_int8_t app, gdt_evt_str *estr); void gdt_clear_events(void); #endif diff --git a/sys/dev/tws/tws.c b/sys/dev/tws/tws.c index 409631f28b48..6ebde7574448 100644 --- a/sys/dev/tws/tws.c +++ b/sys/dev/tws/tws.c @@ -1,898 +1,899 @@ /* * Copyright (c) 2010, LSI Corp. * All rights reserved. * Author : Manjunath Ranganathaiah * Support: freebsdraid@lsi.com * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include MALLOC_DEFINE(M_TWS, "twsbuf", "buffers used by tws driver"); int tws_queue_depth = TWS_MAX_REQS; int tws_enable_msi = 0; int tws_enable_msix = 0; /* externs */ extern int tws_cam_attach(struct tws_softc *sc); extern void tws_cam_detach(struct tws_softc *sc); extern int tws_init_ctlr(struct tws_softc *sc); extern boolean tws_ctlr_ready(struct tws_softc *sc); extern void tws_turn_off_interrupts(struct tws_softc *sc); extern void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ); extern struct tws_request *tws_q_remove_request(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ); extern struct tws_request *tws_q_remove_head(struct tws_softc *sc, u_int8_t q_type ); extern boolean tws_get_response(struct tws_softc *sc, u_int16_t *req_id); extern boolean tws_ctlr_reset(struct tws_softc *sc); extern void tws_intr(void *arg); extern int tws_use_32bit_sgls; struct tws_request *tws_get_request(struct tws_softc *sc, u_int16_t type); int tws_init_connect(struct tws_softc *sc, u_int16_t mc); void tws_send_event(struct tws_softc *sc, u_int8_t event); uint8_t tws_get_state(struct tws_softc *sc); void tws_release_request(struct tws_request *req); /* Function prototypes */ static d_open_t tws_open; static d_close_t tws_close; static d_read_t tws_read; static d_write_t tws_write; extern d_ioctl_t tws_ioctl; static int tws_init(struct tws_softc *sc); static void tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs, int nseg, int error); static int tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size); static int tws_init_aen_q(struct tws_softc *sc); static int tws_init_trace_q(struct tws_softc *sc); static int tws_setup_irq(struct tws_softc *sc); int tws_setup_intr(struct tws_softc *sc, int irqs); int tws_teardown_intr(struct tws_softc *sc); /* Character device entry points */ static struct cdevsw tws_cdevsw = { .d_version = D_VERSION, .d_open = tws_open, .d_close = tws_close, .d_read = tws_read, .d_write = tws_write, .d_ioctl = tws_ioctl, .d_name = "tws", }; /* * In the cdevsw routines, we find our softc by using the si_drv1 member * of struct cdev. We set this variable to point to our softc in our * attach routine when we create the /dev entry. */ int tws_open(struct cdev *dev, int oflags, int devtype, d_thread_t *td) { struct tws_softc *sc = dev->si_drv1; if ( sc ) TWS_TRACE_DEBUG(sc, "entry", dev, oflags); return (0); } int tws_close(struct cdev *dev, int fflag, int devtype, d_thread_t *td) { struct tws_softc *sc = dev->si_drv1; if ( sc ) TWS_TRACE_DEBUG(sc, "entry", dev, fflag); return (0); } int tws_read(struct cdev *dev, struct uio *uio, int ioflag) { struct tws_softc *sc = dev->si_drv1; if ( sc ) TWS_TRACE_DEBUG(sc, "entry", dev, ioflag); return (0); } int tws_write(struct cdev *dev, struct uio *uio, int ioflag) { struct tws_softc *sc = dev->si_drv1; if ( sc ) TWS_TRACE_DEBUG(sc, "entry", dev, ioflag); return (0); } /* PCI Support Functions */ /* * Compare the device ID of this device against the IDs that this driver * supports. If there is a match, set the description and return success. */ static int tws_probe(device_t dev) { static u_int8_t first_ctlr = 1; if ((pci_get_vendor(dev) == TWS_VENDOR_ID) && (pci_get_device(dev) == TWS_DEVICE_ID)) { device_set_desc(dev, "LSI 3ware SAS/SATA Storage Controller"); if (first_ctlr) { printf("LSI 3ware device driver for SAS/SATA storage " "controllers, version: %s\n", TWS_DRIVER_VERSION_STRING); first_ctlr = 0; } return(BUS_PROBE_DEFAULT); } return (ENXIO); } /* Attach function is only called if the probe is successful. */ static int tws_attach(device_t dev) { struct tws_softc *sc = device_get_softc(dev); u_int32_t bar; int error=0,i; /* no tracing yet */ /* Look up our softc and initialize its fields. */ sc->tws_dev = dev; sc->device_id = pci_get_device(dev); sc->subvendor_id = pci_get_subvendor(dev); sc->subdevice_id = pci_get_subdevice(dev); /* Intialize mutexes */ mtx_init( &sc->q_lock, "tws_q_lock", NULL, MTX_DEF); mtx_init( &sc->sim_lock, "tws_sim_lock", NULL, MTX_DEF); mtx_init( &sc->gen_lock, "tws_gen_lock", NULL, MTX_DEF); mtx_init( &sc->io_lock, "tws_io_lock", NULL, MTX_DEF | MTX_RECURSE); if ( tws_init_trace_q(sc) == FAILURE ) printf("trace init failure\n"); /* send init event */ mtx_lock(&sc->gen_lock); tws_send_event(sc, TWS_INIT_START); mtx_unlock(&sc->gen_lock); #if _BYTE_ORDER == _BIG_ENDIAN TWS_TRACE(sc, "BIG endian", 0, 0); #endif /* sysctl context setup */ sysctl_ctx_init(&sc->tws_clist); sc->tws_oidp = SYSCTL_ADD_NODE(&sc->tws_clist, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, device_get_nameunit(dev), CTLFLAG_RD, 0, ""); if ( sc->tws_oidp == NULL ) { tws_log(sc, SYSCTL_TREE_NODE_ADD); goto attach_fail_1; } SYSCTL_ADD_STRING(&sc->tws_clist, SYSCTL_CHILDREN(sc->tws_oidp), OID_AUTO, "driver_version", CTLFLAG_RD, TWS_DRIVER_VERSION_STRING, 0, "TWS driver version"); pci_enable_busmaster(dev); bar = pci_read_config(dev, TWS_PCI_BAR0, 4); TWS_TRACE_DEBUG(sc, "bar0 ", bar, 0); bar = pci_read_config(dev, TWS_PCI_BAR1, 4); bar = bar & ~TWS_BIT2; TWS_TRACE_DEBUG(sc, "bar1 ", bar, 0); /* MFA base address is BAR2 register used for * push mode. Firmware will evatualy move to * pull mode during witch this needs to change */ #ifndef TWS_PULL_MODE_ENABLE sc->mfa_base = (u_int64_t)pci_read_config(dev, TWS_PCI_BAR2, 4); sc->mfa_base = sc->mfa_base & ~TWS_BIT2; TWS_TRACE_DEBUG(sc, "bar2 ", sc->mfa_base, 0); #endif /* allocate MMIO register space */ sc->reg_res_id = TWS_PCI_BAR1; /* BAR1 offset */ if ((sc->reg_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->reg_res_id), 0, ~0, 1, RF_ACTIVE)) == NULL) { tws_log(sc, ALLOC_MEMORY_RES); goto attach_fail_1; } sc->bus_tag = rman_get_bustag(sc->reg_res); sc->bus_handle = rman_get_bushandle(sc->reg_res); #ifndef TWS_PULL_MODE_ENABLE /* Allocate bus space for inbound mfa */ sc->mfa_res_id = TWS_PCI_BAR2; /* BAR2 offset */ if ((sc->mfa_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &(sc->mfa_res_id), 0, ~0, 0x100000, RF_ACTIVE)) == NULL) { tws_log(sc, ALLOC_MEMORY_RES); goto attach_fail_2; } sc->bus_mfa_tag = rman_get_bustag(sc->mfa_res); sc->bus_mfa_handle = rman_get_bushandle(sc->mfa_res); #endif /* Allocate and register our interrupt. */ sc->intr_type = TWS_INTx; /* default */ if ( tws_enable_msi ) sc->intr_type = TWS_MSI; if ( tws_setup_irq(sc) == FAILURE ) { tws_log(sc, ALLOC_MEMORY_RES); goto attach_fail_3; } /* * Create a /dev entry for this device. The kernel will assign us * a major number automatically. We use the unit number of this * device as the minor number and name the character device * "tws". */ sc->tws_cdev = make_dev(&tws_cdevsw, device_get_unit(dev), UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "tws%u", device_get_unit(dev)); sc->tws_cdev->si_drv1 = sc; if ( tws_init(sc) == FAILURE ) { tws_log(sc, TWS_INIT_FAILURE); goto attach_fail_4; } if ( tws_init_ctlr(sc) == FAILURE ) { tws_log(sc, TWS_CTLR_INIT_FAILURE); goto attach_fail_4; } if ((error = tws_cam_attach(sc))) { tws_log(sc, TWS_CAM_ATTACH); goto attach_fail_4; } /* send init complete event */ mtx_lock(&sc->gen_lock); tws_send_event(sc, TWS_INIT_COMPLETE); mtx_unlock(&sc->gen_lock); TWS_TRACE_DEBUG(sc, "attached successfully", 0, sc->device_id); return(0); attach_fail_4: tws_teardown_intr(sc); destroy_dev(sc->tws_cdev); attach_fail_3: for(i=0;iirqs;i++) { if ( sc->irq_res[i] ){ if (bus_release_resource(sc->tws_dev, SYS_RES_IRQ, sc->irq_res_id[i], sc->irq_res[i])) TWS_TRACE(sc, "bus irq res", 0, 0); } } #ifndef TWS_PULL_MODE_ENABLE attach_fail_2: #endif if ( sc->mfa_res ){ if (bus_release_resource(sc->tws_dev, SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res)) TWS_TRACE(sc, "bus release ", 0, sc->mfa_res_id); } if ( sc->reg_res ){ if (bus_release_resource(sc->tws_dev, SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res)) TWS_TRACE(sc, "bus release2 ", 0, sc->reg_res_id); } attach_fail_1: mtx_destroy(&sc->q_lock); mtx_destroy(&sc->sim_lock); mtx_destroy(&sc->gen_lock); mtx_destroy(&sc->io_lock); sysctl_ctx_free(&sc->tws_clist); return (ENXIO); } /* Detach device. */ static int tws_detach(device_t dev) { struct tws_softc *sc = device_get_softc(dev); int i; u_int32_t reg; TWS_TRACE_DEBUG(sc, "entry", 0, 0); mtx_lock(&sc->gen_lock); tws_send_event(sc, TWS_UNINIT_START); mtx_unlock(&sc->gen_lock); /* needs to disable interrupt before detaching from cam */ tws_turn_off_interrupts(sc); /* clear door bell */ tws_write_reg(sc, TWS_I2O0_HOBDBC, ~0, 4); reg = tws_read_reg(sc, TWS_I2O0_HIMASK, 4); TWS_TRACE_DEBUG(sc, "turn-off-intr", reg, 0); sc->obfl_q_overrun = false; tws_init_connect(sc, 1); /* Teardown the state in our softc created in our attach routine. */ /* Disconnect the interrupt handler. */ tws_teardown_intr(sc); /* Release irq resource */ for(i=0;iirqs;i++) { if ( sc->irq_res[i] ){ if (bus_release_resource(sc->tws_dev, SYS_RES_IRQ, sc->irq_res_id[i], sc->irq_res[i])) TWS_TRACE(sc, "bus release irq resource", i, sc->irq_res_id[i]); } } if ( sc->intr_type == TWS_MSI ) { pci_release_msi(sc->tws_dev); } tws_cam_detach(sc); /* Release memory resource */ if ( sc->mfa_res ){ if (bus_release_resource(sc->tws_dev, SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res)) TWS_TRACE(sc, "bus release mem resource", 0, sc->mfa_res_id); } if ( sc->reg_res ){ if (bus_release_resource(sc->tws_dev, SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res)) TWS_TRACE(sc, "bus release mem resource", 0, sc->reg_res_id); } free(sc->reqs, M_TWS); free(sc->sense_bufs, M_TWS); free(sc->scan_ccb, M_TWS); if (sc->ioctl_data_mem) bus_dmamem_free(sc->data_tag, sc->ioctl_data_mem, sc->ioctl_data_map); free(sc->aen_q.q, M_TWS); free(sc->trace_q.q, M_TWS); mtx_destroy(&sc->q_lock); mtx_destroy(&sc->sim_lock); mtx_destroy(&sc->gen_lock); mtx_destroy(&sc->io_lock); destroy_dev(sc->tws_cdev); sysctl_ctx_free(&sc->tws_clist); return (0); } int tws_setup_intr(struct tws_softc *sc, int irqs) { int i, error; for(i=0;iintr_handle[i])) { if ((error = bus_setup_intr(sc->tws_dev, sc->irq_res[i], INTR_TYPE_CAM | INTR_MPSAFE, #if (__FreeBSD_version >= 700000) NULL, #endif tws_intr, sc, &sc->intr_handle[i]))) { tws_log(sc, SETUP_INTR_RES); return(FAILURE); } } } return(SUCCESS); } int tws_teardown_intr(struct tws_softc *sc) { int i, error; for(i=0;iirqs;i++) { if (sc->intr_handle[i]) { error = bus_teardown_intr(sc->tws_dev, sc->irq_res[i], sc->intr_handle[i]); sc->intr_handle[i] = NULL; } } return(SUCCESS); } static int tws_setup_irq(struct tws_softc *sc) { int messages; switch(sc->intr_type) { case TWS_INTx : sc->irqs = 1; sc->irq_res_id[0] = 0; sc->irq_res[0] = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ, &sc->irq_res_id[0], RF_SHAREABLE | RF_ACTIVE); if ( ! sc->irq_res[0] ) return(FAILURE); if ( tws_setup_intr(sc, sc->irqs) == FAILURE ) return(FAILURE); device_printf(sc->tws_dev, "Using legacy INTx\n"); break; case TWS_MSI : sc->irqs = 1; sc->irq_res_id[0] = 1; messages = 1; if (pci_alloc_msi(sc->tws_dev, &messages) != 0 ) { TWS_TRACE(sc, "pci alloc msi fail", 0, messages); return(FAILURE); } sc->irq_res[0] = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ, &sc->irq_res_id[0], RF_SHAREABLE | RF_ACTIVE); if ( !sc->irq_res[0] ) return(FAILURE); if ( tws_setup_intr(sc, sc->irqs) == FAILURE ) return(FAILURE); device_printf(sc->tws_dev, "Using MSI\n"); break; } return(SUCCESS); } static int tws_init(struct tws_softc *sc) { u_int32_t max_sg_elements; u_int32_t dma_mem_size; int error; u_int32_t reg; sc->seq_id = 0; if ( tws_queue_depth > TWS_MAX_REQS ) tws_queue_depth = TWS_MAX_REQS; if (tws_queue_depth < TWS_RESERVED_REQS+1) tws_queue_depth = TWS_RESERVED_REQS+1; sc->is64bit = (sizeof(bus_addr_t) == 8) ? true : false; max_sg_elements = (sc->is64bit && !tws_use_32bit_sgls) ? TWS_MAX_64BIT_SG_ELEMENTS : TWS_MAX_32BIT_SG_ELEMENTS; dma_mem_size = (sizeof(struct tws_command_packet) * tws_queue_depth) + (TWS_SECTOR_SIZE) ; if ( bus_dma_tag_create(bus_get_dma_tag(sc->tws_dev), /* PCI parent */ TWS_ALIGNMENT, /* alignment */ 0, /* boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE, /* maxsize */ max_sg_elements, /* numsegs */ BUS_SPACE_MAXSIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockfuncarg */ &sc->parent_tag /* tag */ )) { TWS_TRACE_DEBUG(sc, "DMA parent tag Create fail", max_sg_elements, sc->is64bit); return(ENOMEM); } /* In bound message frame requires 16byte alignment. * Outbound MF's can live with 4byte alignment - for now just * use 16 for both. */ if ( bus_dma_tag_create(sc->parent_tag, /* parent */ TWS_IN_MF_ALIGNMENT, /* alignment */ 0, /* boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ dma_mem_size, /* maxsize */ 1, /* numsegs */ BUS_SPACE_MAXSIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockfuncarg */ &sc->cmd_tag /* tag */ )) { TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit); return(ENOMEM); } if (bus_dmamem_alloc(sc->cmd_tag, &sc->dma_mem, BUS_DMA_NOWAIT, &sc->cmd_map)) { TWS_TRACE_DEBUG(sc, "DMA mem alloc fail", max_sg_elements, sc->is64bit); return(ENOMEM); } /* if bus_dmamem_alloc succeeds then bus_dmamap_load will succeed */ sc->dma_mem_phys=0; error = bus_dmamap_load(sc->cmd_tag, sc->cmd_map, sc->dma_mem, dma_mem_size, tws_dmamap_cmds_load_cbfn, &sc->dma_mem_phys, 0); /* * Create a dma tag for data buffers; size will be the maximum * possible I/O size (128kB). */ if (bus_dma_tag_create(sc->parent_tag, /* parent */ TWS_ALIGNMENT, /* alignment */ 0, /* boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ TWS_MAX_IO_SIZE, /* maxsize */ max_sg_elements, /* nsegments */ TWS_MAX_IO_SIZE, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ busdma_lock_mutex, /* lockfunc */ &sc->io_lock, /* lockfuncarg */ &sc->data_tag /* tag */)) { TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit); return(ENOMEM); } sc->reqs = malloc(sizeof(struct tws_request) * tws_queue_depth, M_TWS, M_WAITOK | M_ZERO); if ( sc->reqs == NULL ) { TWS_TRACE_DEBUG(sc, "malloc failed", 0, sc->is64bit); return(ENOMEM); } sc->sense_bufs = malloc(sizeof(struct tws_sense) * tws_queue_depth, M_TWS, M_WAITOK | M_ZERO); if ( sc->sense_bufs == NULL ) { TWS_TRACE_DEBUG(sc, "sense malloc failed", 0, sc->is64bit); return(ENOMEM); } sc->scan_ccb = malloc(sizeof(union ccb), M_TWS, M_WAITOK | M_ZERO); if ( sc->scan_ccb == NULL ) { TWS_TRACE_DEBUG(sc, "ccb malloc failed", 0, sc->is64bit); return(ENOMEM); } if (bus_dmamem_alloc(sc->data_tag, (void **)&sc->ioctl_data_mem, (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &sc->ioctl_data_map)) { device_printf(sc->tws_dev, "Cannot allocate ioctl data mem\n"); return(ENOMEM); } if ( !tws_ctlr_ready(sc) ) if( !tws_ctlr_reset(sc) ) return(FAILURE); bzero(&sc->stats, sizeof(struct tws_stats)); tws_init_qs(sc); tws_turn_off_interrupts(sc); /* * enable pull mode by setting bit1 . * setting bit0 to 1 will enable interrupt coalesing * will revisit. */ #ifdef TWS_PULL_MODE_ENABLE reg = tws_read_reg(sc, TWS_I2O0_CTL, 4); TWS_TRACE_DEBUG(sc, "i20 ctl", reg, TWS_I2O0_CTL); tws_write_reg(sc, TWS_I2O0_CTL, reg | TWS_BIT1, 4); #endif TWS_TRACE_DEBUG(sc, "dma_mem_phys", sc->dma_mem_phys, TWS_I2O0_CTL); if ( tws_init_reqs(sc, dma_mem_size) == FAILURE ) return(FAILURE); if ( tws_init_aen_q(sc) == FAILURE ) return(FAILURE); return(SUCCESS); } static int tws_init_aen_q(struct tws_softc *sc) { sc->aen_q.head=0; sc->aen_q.tail=0; sc->aen_q.depth=256; sc->aen_q.overflow=0; sc->aen_q.q = malloc(sizeof(struct tws_event_packet)*sc->aen_q.depth, M_TWS, M_WAITOK | M_ZERO); if ( ! sc->aen_q.q ) return(FAILURE); return(SUCCESS); } static int tws_init_trace_q(struct tws_softc *sc) { sc->trace_q.head=0; sc->trace_q.tail=0; sc->trace_q.depth=256; sc->trace_q.overflow=0; sc->trace_q.q = malloc(sizeof(struct tws_trace_rec)*sc->trace_q.depth, M_TWS, M_WAITOK | M_ZERO); if ( ! sc->trace_q.q ) return(FAILURE); return(SUCCESS); } static int tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size) { struct tws_command_packet *cmd_buf; cmd_buf = (struct tws_command_packet *)sc->dma_mem; int i; bzero(cmd_buf, dma_mem_size); TWS_TRACE_DEBUG(sc, "phy cmd", sc->dma_mem_phys, 0); mtx_lock(&sc->q_lock); for ( i=0; i< tws_queue_depth; i++) { if (bus_dmamap_create(sc->data_tag, 0, &sc->reqs[i].dma_map)) { /* log a ENOMEM failure msg here */ mtx_unlock(&sc->q_lock); return(FAILURE); } sc->reqs[i].cmd_pkt = &cmd_buf[i]; sc->sense_bufs[i].hdr = &cmd_buf[i].hdr ; sc->sense_bufs[i].hdr_pkt_phy = sc->dma_mem_phys + (i * sizeof(struct tws_command_packet)); sc->reqs[i].cmd_pkt_phy = sc->dma_mem_phys + sizeof(struct tws_command_header) + (i * sizeof(struct tws_command_packet)); sc->reqs[i].request_id = i; sc->reqs[i].sc = sc; sc->reqs[i].cmd_pkt->hdr.header_desc.size_header = 128; + callout_handle_init(&sc->reqs[i].thandle); sc->reqs[i].state = TWS_REQ_STATE_FREE; if ( i >= TWS_RESERVED_REQS ) tws_q_insert_tail(sc, &sc->reqs[i], TWS_FREE_Q); } mtx_unlock(&sc->q_lock); return(SUCCESS); } static void tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs, int nseg, int error) { /* printf("command load done \n"); */ *((bus_addr_t *)arg) = segs[0].ds_addr; } void tws_send_event(struct tws_softc *sc, u_int8_t event) { mtx_assert(&sc->gen_lock, MA_OWNED); TWS_TRACE_DEBUG(sc, "received event ", 0, event); switch (event) { case TWS_INIT_START: sc->tws_state = TWS_INIT; break; case TWS_INIT_COMPLETE: if (sc->tws_state != TWS_INIT) { device_printf(sc->tws_dev, "invalid state transition %d => TWS_ONLINE\n", sc->tws_state); } else { sc->tws_state = TWS_ONLINE; } break; case TWS_RESET_START: /* We can transition to reset state from any state except reset*/ if (sc->tws_state != TWS_RESET) { sc->tws_prev_state = sc->tws_state; sc->tws_state = TWS_RESET; } break; case TWS_RESET_COMPLETE: if (sc->tws_state != TWS_RESET) { device_printf(sc->tws_dev, "invalid state transition %d => %d (previous state)\n", sc->tws_state, sc->tws_prev_state); } else { sc->tws_state = sc->tws_prev_state; } break; case TWS_SCAN_FAILURE: if (sc->tws_state != TWS_ONLINE) { device_printf(sc->tws_dev, "invalid state transition %d => TWS_OFFLINE\n", sc->tws_state); } else { sc->tws_state = TWS_OFFLINE; } break; case TWS_UNINIT_START: if ((sc->tws_state != TWS_ONLINE) && (sc->tws_state != TWS_OFFLINE)) { device_printf(sc->tws_dev, "invalid state transition %d => TWS_UNINIT\n", sc->tws_state); } else { sc->tws_state = TWS_UNINIT; } break; } } uint8_t tws_get_state(struct tws_softc *sc) { return((u_int8_t)sc->tws_state); } /* Called during system shutdown after sync. */ static int tws_shutdown(device_t dev) { struct tws_softc *sc = device_get_softc(dev); TWS_TRACE_DEBUG(sc, "entry", 0, 0); tws_turn_off_interrupts(sc); tws_init_connect(sc, 1); return (0); } /* * Device suspend routine. */ static int tws_suspend(device_t dev) { struct tws_softc *sc = device_get_softc(dev); if ( sc ) TWS_TRACE_DEBUG(sc, "entry", 0, 0); return (0); } /* * Device resume routine. */ static int tws_resume(device_t dev) { struct tws_softc *sc = device_get_softc(dev); if ( sc ) TWS_TRACE_DEBUG(sc, "entry", 0, 0); return (0); } struct tws_request * tws_get_request(struct tws_softc *sc, u_int16_t type) { struct mtx *my_mutex = ((type == TWS_REQ_TYPE_SCSI_IO) ? &sc->q_lock : &sc->gen_lock); struct tws_request *r = NULL; mtx_lock(my_mutex); if (type == TWS_REQ_TYPE_SCSI_IO) { r = tws_q_remove_head(sc, TWS_FREE_Q); } else { if ( sc->reqs[type].state == TWS_REQ_STATE_FREE ) { r = &sc->reqs[type]; } } if ( r ) { bzero(&r->cmd_pkt->cmd, sizeof(struct tws_command_apache)); r->data = NULL; r->length = 0; r->type = type; r->flags = TWS_DIR_UNKNOWN; r->error_code = TWS_REQ_RET_INVALID; r->cb = NULL; r->ccb_ptr = NULL; r->thandle.callout = NULL; r->next = r->prev = NULL; r->state = ((type == TWS_REQ_TYPE_SCSI_IO) ? TWS_REQ_STATE_TRAN : TWS_REQ_STATE_BUSY); } mtx_unlock(my_mutex); return(r); } void tws_release_request(struct tws_request *req) { struct tws_softc *sc = req->sc; TWS_TRACE_DEBUG(sc, "entry", sc, 0); mtx_lock(&sc->q_lock); tws_q_insert_tail(sc, req, TWS_FREE_Q); mtx_unlock(&sc->q_lock); } static device_method_t tws_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tws_probe), DEVMETHOD(device_attach, tws_attach), DEVMETHOD(device_detach, tws_detach), DEVMETHOD(device_shutdown, tws_shutdown), DEVMETHOD(device_suspend, tws_suspend), DEVMETHOD(device_resume, tws_resume), DEVMETHOD_END }; static driver_t tws_driver = { "tws", tws_methods, sizeof(struct tws_softc) }; static devclass_t tws_devclass; /* DEFINE_CLASS_0(tws, tws_driver, tws_methods, sizeof(struct tws_softc)); */ DRIVER_MODULE(tws, pci, tws_driver, tws_devclass, 0, 0); MODULE_DEPEND(tws, cam, 1, 1, 1); MODULE_DEPEND(tws, pci, 1, 1, 1); TUNABLE_INT("hw.tws.queue_depth", &tws_queue_depth); TUNABLE_INT("hw.tws.enable_msi", &tws_enable_msi); diff --git a/sys/dev/tws/tws_cam.c b/sys/dev/tws/tws_cam.c index 46b91f47cdab..b134d760e813 100644 --- a/sys/dev/tws/tws_cam.c +++ b/sys/dev/tws/tws_cam.c @@ -1,1318 +1,1318 @@ /* * Copyright (c) 2010 LSI Corp. * All rights reserved. * Author : Manjunath Ranganathaiah * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include static int tws_cam_depth=(TWS_MAX_REQS - TWS_RESERVED_REQS); static char tws_sev_str[5][8]={"","ERROR","WARNING","INFO","DEBUG"}; static void tws_action(struct cam_sim *sim, union ccb *ccb); static void tws_poll(struct cam_sim *sim); static void tws_scsi_complete(struct tws_request *req); void tws_unmap_request(struct tws_softc *sc, struct tws_request *req); int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req); int tws_bus_scan(struct tws_softc *sc); int tws_cam_attach(struct tws_softc *sc); void tws_cam_detach(struct tws_softc *sc); void tws_reset(void *arg); static void tws_reset_cb(void *arg); static void tws_reinit(void *arg); static int32_t tws_execute_scsi(struct tws_softc *sc, union ccb *ccb); static void tws_freeze_simq(struct tws_softc *sc, struct tws_request *req); static void tws_dmamap_data_load_cbfn(void *arg, bus_dma_segment_t *segs, int nseg, int error); static void tws_fill_sg_list(struct tws_softc *sc, void *sgl_src, void *sgl_dest, u_int16_t num_sgl_entries); static void tws_err_complete(struct tws_softc *sc, u_int64_t mfa); static void tws_scsi_err_complete(struct tws_request *req, struct tws_command_header *hdr); static void tws_passthru_err_complete(struct tws_request *req, struct tws_command_header *hdr); void tws_timeout(void *arg); static void tws_intr_attn_aen(struct tws_softc *sc); static void tws_intr_attn_error(struct tws_softc *sc); static void tws_intr_resp(struct tws_softc *sc); void tws_intr(void *arg); void tws_cmd_complete(struct tws_request *req); void tws_aen_complete(struct tws_request *req); int tws_send_scsi_cmd(struct tws_softc *sc, int cmd); void tws_getset_param_complete(struct tws_request *req); int tws_set_param(struct tws_softc *sc, u_int32_t table_id, u_int32_t param_id, u_int32_t param_size, void *data); int tws_get_param(struct tws_softc *sc, u_int32_t table_id, u_int32_t param_id, u_int32_t param_size, void *data); extern struct tws_request *tws_get_request(struct tws_softc *sc, u_int16_t type); extern void *tws_release_request(struct tws_request *req); extern int tws_submit_command(struct tws_softc *sc, struct tws_request *req); extern boolean tws_get_response(struct tws_softc *sc, u_int16_t *req_id, u_int64_t *mfa); extern void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ); extern struct tws_request * tws_q_remove_request(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ); extern void tws_send_event(struct tws_softc *sc, u_int8_t event); extern struct tws_sense * tws_find_sense_from_mfa(struct tws_softc *sc, u_int64_t mfa); extern void tws_fetch_aen(void *arg); extern void tws_disable_db_intr(struct tws_softc *sc); extern void tws_enable_db_intr(struct tws_softc *sc); extern void tws_passthru_complete(struct tws_request *req); extern void tws_aen_synctime_with_host(struct tws_softc *sc); extern void tws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq, struct tws_event_packet *aen); extern int tws_use_32bit_sgls; extern boolean tws_ctlr_reset(struct tws_softc *sc); extern struct tws_request * tws_q_remove_tail(struct tws_softc *sc, u_int8_t q_type ); extern void tws_turn_off_interrupts(struct tws_softc *sc); extern void tws_turn_on_interrupts(struct tws_softc *sc); extern int tws_init_connect(struct tws_softc *sc, u_int16_t mc); extern void tws_init_obfl_q(struct tws_softc *sc); extern uint8_t tws_get_state(struct tws_softc *sc); extern void tws_assert_soft_reset(struct tws_softc *sc); extern boolean tws_ctlr_ready(struct tws_softc *sc); extern u_int16_t tws_poll4_response(struct tws_softc *sc, u_int64_t *mfa); extern int tws_setup_intr(struct tws_softc *sc, int irqs); extern int tws_teardown_intr(struct tws_softc *sc); int tws_cam_attach(struct tws_softc *sc) { struct cam_devq *devq; TWS_TRACE_DEBUG(sc, "entry", 0, sc); /* Create a device queue for sim */ /* * if the user sets cam depth to less than 1 * cam may get confused */ if ( tws_cam_depth < 1 ) tws_cam_depth = 1; if ( tws_cam_depth > (tws_queue_depth - TWS_RESERVED_REQS) ) tws_cam_depth = tws_queue_depth - TWS_RESERVED_REQS; TWS_TRACE_DEBUG(sc, "depths,ctlr,cam", tws_queue_depth, tws_cam_depth); if ((devq = cam_simq_alloc(tws_cam_depth)) == NULL) { tws_log(sc, CAM_SIMQ_ALLOC); return(ENOMEM); } /* * Create a SIM entry. Though we can support tws_cam_depth * simultaneous requests, we claim to be able to handle only * (tws_cam_depth), so that we always have reserved requests * packet available to service ioctls and internal commands. */ sc->sim = cam_sim_alloc(tws_action, tws_poll, "tws", sc, device_get_unit(sc->tws_dev), #if (__FreeBSD_version >= 700000) &sc->sim_lock, #endif tws_cam_depth, 1, devq); /* 1, 1, devq); */ if (sc->sim == NULL) { cam_simq_free(devq); tws_log(sc, CAM_SIM_ALLOC); } /* Register the bus. */ mtx_lock(&sc->sim_lock); if (xpt_bus_register(sc->sim, #if (__FreeBSD_version >= 700000) sc->tws_dev, #endif 0) != CAM_SUCCESS) { cam_sim_free(sc->sim, TRUE); /* passing true will free the devq */ sc->sim = NULL; /* so cam_detach will not try to free it */ mtx_unlock(&sc->sim_lock); tws_log(sc, TWS_XPT_BUS_REGISTER); return(ENXIO); } if (xpt_create_path(&sc->path, NULL, cam_sim_path(sc->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(sc->sim)); /* Passing TRUE to cam_sim_free will free the devq as well. */ cam_sim_free(sc->sim, TRUE); tws_log(sc, TWS_XPT_CREATE_PATH); mtx_unlock(&sc->sim_lock); return(ENXIO); } mtx_unlock(&sc->sim_lock); return(0); } void tws_cam_detach(struct tws_softc *sc) { TWS_TRACE_DEBUG(sc, "entry", 0, 0); mtx_lock(&sc->sim_lock); if (sc->path) xpt_free_path(sc->path); if (sc->sim) { xpt_bus_deregister(cam_sim_path(sc->sim)); cam_sim_free(sc->sim, TRUE); } mtx_unlock(&sc->sim_lock); } int tws_bus_scan(struct tws_softc *sc) { union ccb *ccb; TWS_TRACE_DEBUG(sc, "entry", sc, 0); if (!(sc->sim)) return(ENXIO); ccb = xpt_alloc_ccb(); mtx_lock(&sc->sim_lock); if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(sc->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { mtx_unlock(&sc->sim_lock); xpt_free_ccb(ccb); return(EIO); } xpt_rescan(ccb); mtx_unlock(&sc->sim_lock); return(0); } static void tws_action(struct cam_sim *sim, union ccb *ccb) { struct tws_softc *sc = (struct tws_softc *)cam_sim_softc(sim); switch( ccb->ccb_h.func_code ) { case XPT_SCSI_IO: { if ( tws_execute_scsi(sc, ccb) ) TWS_TRACE_DEBUG(sc, "execute scsi failed", 0, 0); break; } case XPT_ABORT: { TWS_TRACE_DEBUG(sc, "abort i/o", 0, 0); ccb->ccb_h.status = CAM_UA_ABORT; xpt_done(ccb); break; } case XPT_RESET_BUS: { TWS_TRACE_DEBUG(sc, "reset bus", sim, ccb); break; } case XPT_SET_TRAN_SETTINGS: { TWS_TRACE_DEBUG(sc, "set tran settings", sim, ccb); ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: { TWS_TRACE_DEBUG(sc, "get tran settings", sim, ccb); #if (__FreeBSD_version >= 700000 ) ccb->cts.protocol = PROTO_SCSI; ccb->cts.protocol_version = SCSI_REV_2; ccb->cts.transport = XPORT_SPI; ccb->cts.transport_version = 2; ccb->cts.xport_specific.spi.valid = CTS_SPI_VALID_DISC; ccb->cts.xport_specific.spi.flags = CTS_SPI_FLAGS_DISC_ENB; ccb->cts.proto_specific.scsi.valid = CTS_SCSI_VALID_TQ; ccb->cts.proto_specific.scsi.flags = CTS_SCSI_FLAGS_TAG_ENB; #else ccb->cts.valid = (CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID); ccb->cts.flags &= ~(CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB); #endif ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { TWS_TRACE_DEBUG(sc, "calc geometry(ccb,block-size)", ccb, ccb->ccg.block_size); cam_calc_geometry(&ccb->ccg, 1/* extended */); xpt_done(ccb); break; } case XPT_PATH_INQ: { TWS_TRACE_DEBUG(sc, "path inquiry", sim, ccb); ccb->cpi.version_num = 1; ccb->cpi.hba_inquiry = 0; ccb->cpi.target_sprt = 0; ccb->cpi.hba_misc = 0; ccb->cpi.hba_eng_cnt = 0; ccb->cpi.max_target = TWS_MAX_NUM_UNITS; ccb->cpi.max_lun = TWS_MAX_NUM_LUNS - 1; ccb->cpi.unit_number = cam_sim_unit(sim); ccb->cpi.bus_id = cam_sim_bus(sim); ccb->cpi.initiator_id = TWS_SCSI_INITIATOR_ID; ccb->cpi.base_transfer_speed = 6000000; strncpy(ccb->cpi.sim_vid, "FreeBSD", SIM_IDLEN); strncpy(ccb->cpi.hba_vid, "3ware", HBA_IDLEN); strncpy(ccb->cpi.dev_name, cam_sim_name(sim), DEV_IDLEN); #if (__FreeBSD_version >= 700000 ) ccb->cpi.transport = XPORT_SPI; ccb->cpi.transport_version = 2; ccb->cpi.protocol = PROTO_SCSI; ccb->cpi.protocol_version = SCSI_REV_2; ccb->cpi.maxio = TWS_MAX_IO_SIZE; #endif ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: TWS_TRACE_DEBUG(sc, "default", sim, ccb); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; } } static void tws_scsi_complete(struct tws_request *req) { struct tws_softc *sc = req->sc; mtx_lock(&sc->q_lock); tws_q_remove_request(sc, req, TWS_BUSY_Q); mtx_unlock(&sc->q_lock); - untimeout(tws_timeout, req, req->ccb_ptr->ccb_h.timeout_ch); + untimeout(tws_timeout, req, req->thandle); tws_unmap_request(req->sc, req); req->ccb_ptr->ccb_h.status = CAM_REQ_CMP; mtx_lock(&sc->sim_lock); xpt_done(req->ccb_ptr); mtx_unlock(&sc->sim_lock); mtx_lock(&sc->q_lock); tws_q_insert_tail(sc, req, TWS_FREE_Q); mtx_unlock(&sc->q_lock); } void tws_getset_param_complete(struct tws_request *req) { struct tws_softc *sc = req->sc; TWS_TRACE_DEBUG(sc, "getset complete", req, req->request_id); untimeout(tws_timeout, req, req->thandle); tws_unmap_request(sc, req); free(req->data, M_TWS); req->state = TWS_REQ_STATE_FREE; } void tws_aen_complete(struct tws_request *req) { struct tws_softc *sc = req->sc; struct tws_command_header *sense; struct tws_event_packet event; u_int16_t aen_code=0; TWS_TRACE_DEBUG(sc, "aen complete", 0, req->request_id); untimeout(tws_timeout, req, req->thandle); tws_unmap_request(sc, req); sense = (struct tws_command_header *)req->data; TWS_TRACE_DEBUG(sc,"sense code, key",sense->sense_data[0], sense->sense_data[2]); TWS_TRACE_DEBUG(sc,"sense rid, seve",sense->header_desc.request_id, sense->status_block.res__severity); TWS_TRACE_DEBUG(sc,"sense srcnum, error",sense->status_block.srcnum, sense->status_block.error); TWS_TRACE_DEBUG(sc,"sense shdr, ssense",sense->header_desc.size_header, sense->header_desc.size_sense); aen_code = sense->status_block.error; switch ( aen_code ) { case TWS_AEN_SYNC_TIME_WITH_HOST : tws_aen_synctime_with_host(sc); break; case TWS_AEN_QUEUE_EMPTY : break; default : bzero(&event, sizeof(struct tws_event_packet)); event.sequence_id = sc->seq_id; event.time_stamp_sec = (u_int32_t)TWS_LOCAL_TIME; event.aen_code = sense->status_block.error; event.severity = sense->status_block.res__severity & 0x7; event.event_src = TWS_SRC_CTRL_EVENT; strcpy(event.severity_str, tws_sev_str[event.severity]); event.retrieved = TWS_AEN_NOT_RETRIEVED; bcopy(sense->err_specific_desc, event.parameter_data, TWS_ERROR_SPECIFIC_DESC_LEN); event.parameter_data[TWS_ERROR_SPECIFIC_DESC_LEN - 1] = '\0'; event.parameter_len = (u_int8_t)strlen(event.parameter_data)+1; if ( event.parameter_len < TWS_ERROR_SPECIFIC_DESC_LEN ) { event.parameter_len += ((u_int8_t)strlen(event.parameter_data + event.parameter_len) + 1); } device_printf(sc->tws_dev, "%s: (0x%02X: 0x%04X): %s: %s\n", event.severity_str, event.event_src, event.aen_code, event.parameter_data + (strlen(event.parameter_data) + 1), event.parameter_data); mtx_lock(&sc->gen_lock); tws_circular_aenq_insert(sc, &sc->aen_q, &event); sc->seq_id++; mtx_unlock(&sc->gen_lock); break; } free(req->data, M_TWS); req->state = TWS_REQ_STATE_FREE; if ( aen_code != TWS_AEN_QUEUE_EMPTY ) { /* timeout(tws_fetch_aen, sc, 1);*/ sc->stats.num_aens++; tws_fetch_aen((void *)sc); } } void tws_cmd_complete(struct tws_request *req) { struct tws_softc *sc = req->sc; - untimeout(tws_timeout, req, req->ccb_ptr->ccb_h.timeout_ch); + untimeout(tws_timeout, req, req->thandle); tws_unmap_request(sc, req); } static void tws_err_complete(struct tws_softc *sc, u_int64_t mfa) { struct tws_command_header *hdr; struct tws_sense *sen; struct tws_request *req; u_int16_t req_id; u_int32_t reg, status; if ( !mfa ) { TWS_TRACE_DEBUG(sc, "null mfa", 0, mfa); return; } else { /* lookup the sense */ sen = tws_find_sense_from_mfa(sc, mfa); if ( sen == NULL ) { TWS_TRACE_DEBUG(sc, "found null req", 0, mfa); return; } hdr = sen->hdr; TWS_TRACE_DEBUG(sc, "sen, hdr", sen, hdr); req_id = hdr->header_desc.request_id; req = &sc->reqs[req_id]; TWS_TRACE_DEBUG(sc, "req, id", req, req_id); if ( req->error_code != TWS_REQ_RET_SUBMIT_SUCCESS ) TWS_TRACE_DEBUG(sc, "submit failure?", 0, req->error_code); } switch (req->type) { case TWS_REQ_TYPE_PASSTHRU : tws_passthru_err_complete(req, hdr); break; case TWS_REQ_TYPE_GETSET_PARAM : tws_getset_param_complete(req); break; case TWS_REQ_TYPE_SCSI_IO : tws_scsi_err_complete(req, hdr); break; } mtx_lock(&sc->io_lock); hdr->header_desc.size_header = 128; reg = (u_int32_t)( mfa>>32); tws_write_reg(sc, TWS_I2O0_HOBQPH, reg, 4); reg = (u_int32_t)(mfa); tws_write_reg(sc, TWS_I2O0_HOBQPL, reg, 4); status = tws_read_reg(sc, TWS_I2O0_STATUS, 4); if ( status & TWS_BIT13 ) { device_printf(sc->tws_dev, "OBFL Overrun\n"); sc->obfl_q_overrun = true; } mtx_unlock(&sc->io_lock); } static void tws_scsi_err_complete(struct tws_request *req, struct tws_command_header *hdr) { u_int8_t *sense_data; struct tws_softc *sc = req->sc; union ccb *ccb = req->ccb_ptr; TWS_TRACE_DEBUG(sc, "sbe, cmd_status", hdr->status_block.error, req->cmd_pkt->cmd.pkt_a.status); if ( hdr->status_block.error == TWS_ERROR_LOGICAL_UNIT_NOT_SUPPORTED || hdr->status_block.error == TWS_ERROR_UNIT_OFFLINE ) { if ( ccb->ccb_h.target_lun ) { TWS_TRACE_DEBUG(sc, "invalid lun error",0,0); ccb->ccb_h.status |= CAM_DEV_NOT_THERE; } else { TWS_TRACE_DEBUG(sc, "invalid target error",0,0); ccb->ccb_h.status |= CAM_SEL_TIMEOUT; } } else { TWS_TRACE_DEBUG(sc, "scsi status error",0,0); ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; if (((ccb->csio.cdb_io.cdb_bytes[0] == 0x1A) && (hdr->status_block.error == TWS_ERROR_NOT_SUPPORTED))) { ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; TWS_TRACE_DEBUG(sc, "page mode not supported",0,0); } } /* if there were no error simply mark complete error */ if (ccb->ccb_h.status == 0) ccb->ccb_h.status = CAM_REQ_CMP_ERR; sense_data = (u_int8_t *)&ccb->csio.sense_data; if (sense_data) { memcpy(sense_data, hdr->sense_data, TWS_SENSE_DATA_LENGTH ); ccb->csio.sense_len = TWS_SENSE_DATA_LENGTH; ccb->ccb_h.status |= CAM_AUTOSNS_VALID; } ccb->csio.scsi_status = req->cmd_pkt->cmd.pkt_a.status; ccb->ccb_h.status &= ~CAM_SIM_QUEUED; mtx_lock(&sc->sim_lock); xpt_done(ccb); mtx_unlock(&sc->sim_lock); - untimeout(tws_timeout, req, req->ccb_ptr->ccb_h.timeout_ch); + untimeout(tws_timeout, req, req->thandle); tws_unmap_request(req->sc, req); mtx_lock(&sc->q_lock); tws_q_remove_request(sc, req, TWS_BUSY_Q); tws_q_insert_tail(sc, req, TWS_FREE_Q); mtx_unlock(&sc->q_lock); } static void tws_passthru_err_complete(struct tws_request *req, struct tws_command_header *hdr) { TWS_TRACE_DEBUG(req->sc, "entry", hdr, req->request_id); req->error_code = hdr->status_block.error; memcpy(&(req->cmd_pkt->hdr), hdr, sizeof(struct tws_command_header)); tws_passthru_complete(req); } static void tws_drain_busy_queue(struct tws_softc *sc) { struct tws_request *req; union ccb *ccb; TWS_TRACE_DEBUG(sc, "entry", 0, 0); mtx_lock(&sc->q_lock); req = tws_q_remove_tail(sc, TWS_BUSY_Q); mtx_unlock(&sc->q_lock); while ( req ) { TWS_TRACE_DEBUG(sc, "moved to TWS_COMPLETE_Q", 0, req->request_id); - untimeout(tws_timeout, req, req->ccb_ptr->ccb_h.timeout_ch); + untimeout(tws_timeout, req, req->thandle); req->error_code = TWS_REQ_RET_RESET; ccb = (union ccb *)(req->ccb_ptr); ccb->ccb_h.status &= ~CAM_SIM_QUEUED; ccb->ccb_h.status |= CAM_REQUEUE_REQ; ccb->ccb_h.status |= CAM_SCSI_BUS_RESET; tws_unmap_request(req->sc, req); mtx_lock(&sc->sim_lock); xpt_done(req->ccb_ptr); mtx_unlock(&sc->sim_lock); mtx_lock(&sc->q_lock); tws_q_insert_tail(sc, req, TWS_FREE_Q); req = tws_q_remove_tail(sc, TWS_BUSY_Q); mtx_unlock(&sc->q_lock); } } static void tws_drain_reserved_reqs(struct tws_softc *sc) { struct tws_request *r; r = &sc->reqs[TWS_REQ_TYPE_AEN_FETCH]; if ( r->state != TWS_REQ_STATE_FREE ) { TWS_TRACE_DEBUG(sc, "reset aen req", 0, 0); untimeout(tws_timeout, r, r->thandle); tws_unmap_request(sc, r); free(r->data, M_TWS); r->state = TWS_REQ_STATE_FREE; r->error_code = TWS_REQ_RET_RESET; } r = &sc->reqs[TWS_REQ_TYPE_PASSTHRU]; if ( r->state == TWS_REQ_STATE_BUSY ) { TWS_TRACE_DEBUG(sc, "reset passthru req", 0, 0); r->error_code = TWS_REQ_RET_RESET; } r = &sc->reqs[TWS_REQ_TYPE_GETSET_PARAM]; if ( r->state != TWS_REQ_STATE_FREE ) { TWS_TRACE_DEBUG(sc, "reset setparam req", 0, 0); untimeout(tws_timeout, r, r->thandle); tws_unmap_request(sc, r); free(r->data, M_TWS); r->state = TWS_REQ_STATE_FREE; r->error_code = TWS_REQ_RET_RESET; } } static void tws_drain_response_queue(struct tws_softc *sc) { u_int16_t req_id; u_int64_t mfa; while ( tws_get_response(sc, &req_id, &mfa) ); } static int32_t tws_execute_scsi(struct tws_softc *sc, union ccb *ccb) { struct tws_command_packet *cmd_pkt; struct tws_request *req; struct ccb_hdr *ccb_h = &(ccb->ccb_h); struct ccb_scsiio *csio = &(ccb->csio); int error; u_int16_t lun; mtx_assert(&sc->sim_lock, MA_OWNED); if (ccb_h->target_id >= TWS_MAX_NUM_UNITS) { TWS_TRACE_DEBUG(sc, "traget id too big", ccb_h->target_id, ccb_h->target_lun); ccb_h->status |= CAM_TID_INVALID; xpt_done(ccb); return(0); } if (ccb_h->target_lun >= TWS_MAX_NUM_LUNS) { TWS_TRACE_DEBUG(sc, "target lun 2 big", ccb_h->target_id, ccb_h->target_lun); ccb_h->status |= CAM_LUN_INVALID; xpt_done(ccb); return(0); } if(ccb_h->flags & CAM_CDB_PHYS) { TWS_TRACE_DEBUG(sc, "cdb phy", ccb_h->target_id, ccb_h->target_lun); ccb_h->status = CAM_REQ_INVALID; xpt_done(ccb); return(0); } /* * We are going to work on this request. Mark it as enqueued (though * we don't actually queue it...) */ ccb_h->status |= CAM_SIM_QUEUED; req = tws_get_request(sc, TWS_REQ_TYPE_SCSI_IO); if ( !req ) { TWS_TRACE_DEBUG(sc, "no reqs", ccb_h->target_id, ccb_h->target_lun); ccb_h->status |= CAM_REQUEUE_REQ; xpt_done(ccb); return(0); } if((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if(ccb_h->flags & CAM_DIR_IN) req->flags |= TWS_DIR_IN; if(ccb_h->flags & CAM_DIR_OUT) req->flags |= TWS_DIR_OUT; } else { req->flags = TWS_DIR_NONE; /* no data */ } req->type = TWS_REQ_TYPE_SCSI_IO; req->cb = tws_scsi_complete; cmd_pkt = req->cmd_pkt; /* cmd_pkt->hdr.header_desc.size_header = 128; */ cmd_pkt->cmd.pkt_a.res__opcode = TWS_FW_CMD_EXECUTE_SCSI; cmd_pkt->cmd.pkt_a.unit = ccb_h->target_id; cmd_pkt->cmd.pkt_a.status = 0; cmd_pkt->cmd.pkt_a.sgl_offset = 16; /* lower nibble */ lun = ccb_h->target_lun & 0XF; lun = lun << 12; cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun | req->request_id; /* upper nibble */ lun = ccb_h->target_lun & 0XF0; lun = lun << 8; cmd_pkt->cmd.pkt_a.lun_h4__sgl_entries = lun; #ifdef TWS_DEBUG if ( csio->cdb_len > 16 ) TWS_TRACE(sc, "cdb len too big", ccb_h->target_id, csio->cdb_len); #endif if(ccb_h->flags & CAM_CDB_POINTER) bcopy(csio->cdb_io.cdb_ptr, cmd_pkt->cmd.pkt_a.cdb, csio->cdb_len); else bcopy(csio->cdb_io.cdb_bytes, cmd_pkt->cmd.pkt_a.cdb, csio->cdb_len); req->data = ccb; req->flags |= TWS_DATA_CCB; /* save ccb ptr */ req->ccb_ptr = ccb; /* * tws_map_load_data_callback will fill in the SGL, * and submit the I/O. */ sc->stats.scsi_ios++; - ccb_h->timeout_ch = timeout(tws_timeout, req, (ccb_h->timeout * hz)/1000); + req->thandle = timeout(tws_timeout, req, (ccb_h->timeout * hz)/1000); error = tws_map_request(sc, req); return(error); } int tws_send_scsi_cmd(struct tws_softc *sc, int cmd) { struct tws_request *req; struct tws_command_packet *cmd_pkt; int error; TWS_TRACE_DEBUG(sc, "entry",sc, cmd); req = tws_get_request(sc, TWS_REQ_TYPE_AEN_FETCH); if ( req == NULL ) return(ENOMEM); req->cb = tws_aen_complete; cmd_pkt = req->cmd_pkt; cmd_pkt->cmd.pkt_a.res__opcode = TWS_FW_CMD_EXECUTE_SCSI; cmd_pkt->cmd.pkt_a.status = 0; cmd_pkt->cmd.pkt_a.unit = 0; cmd_pkt->cmd.pkt_a.sgl_offset = 16; cmd_pkt->cmd.pkt_a.lun_l4__req_id = req->request_id; cmd_pkt->cmd.pkt_a.cdb[0] = (u_int8_t)cmd; cmd_pkt->cmd.pkt_a.cdb[4] = 128; req->length = TWS_SECTOR_SIZE; req->data = malloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT); if ( req->data == NULL ) return(ENOMEM); bzero(req->data, TWS_SECTOR_SIZE); req->flags = TWS_DIR_IN; req->thandle = timeout(tws_timeout, req, (TWS_IO_TIMEOUT * hz)); error = tws_map_request(sc, req); return(error); } int tws_set_param(struct tws_softc *sc, u_int32_t table_id, u_int32_t param_id, u_int32_t param_size, void *data) { struct tws_request *req; struct tws_command_packet *cmd_pkt; union tws_command_giga *cmd; struct tws_getset_param *param; int error; req = tws_get_request(sc, TWS_REQ_TYPE_GETSET_PARAM); if ( req == NULL ) { TWS_TRACE_DEBUG(sc, "null req", 0, 0); return(ENOMEM); } req->length = TWS_SECTOR_SIZE; req->data = malloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT); if ( req->data == NULL ) return(ENOMEM); bzero(req->data, TWS_SECTOR_SIZE); param = (struct tws_getset_param *)req->data; req->cb = tws_getset_param_complete; req->flags = TWS_DIR_OUT; cmd_pkt = req->cmd_pkt; cmd = &cmd_pkt->cmd.pkt_g; cmd->param.sgl_off__opcode = BUILD_SGL_OFF__OPCODE(2, TWS_FW_CMD_SET_PARAM); cmd->param.request_id = (u_int8_t)req->request_id; cmd->param.host_id__unit = 0; cmd->param.param_count = 1; cmd->param.size = 2; /* map routine will add sgls */ /* Specify which parameter we want to set. */ param->table_id = (table_id | TWS_9K_PARAM_DESCRIPTOR); param->parameter_id = (u_int8_t)(param_id); param->parameter_size_bytes = (u_int16_t)param_size; memcpy(param->data, data, param_size); req->thandle = timeout(tws_timeout, req, (TWS_IOCTL_TIMEOUT * hz)); error = tws_map_request(sc, req); return(error); } int tws_get_param(struct tws_softc *sc, u_int32_t table_id, u_int32_t param_id, u_int32_t param_size, void *data) { struct tws_request *req; struct tws_command_packet *cmd_pkt; union tws_command_giga *cmd; struct tws_getset_param *param; u_int16_t reqid; u_int64_t mfa; int error = SUCCESS; req = tws_get_request(sc, TWS_REQ_TYPE_GETSET_PARAM); if ( req == NULL ) { TWS_TRACE_DEBUG(sc, "null req", 0, 0); return(FAILURE); } req->length = TWS_SECTOR_SIZE; req->data = malloc(TWS_SECTOR_SIZE, M_TWS, M_NOWAIT); if ( req->data == NULL ) return(FAILURE); bzero(req->data, TWS_SECTOR_SIZE); param = (struct tws_getset_param *)req->data; req->cb = NULL; req->flags = TWS_DIR_IN; cmd_pkt = req->cmd_pkt; cmd = &cmd_pkt->cmd.pkt_g; cmd->param.sgl_off__opcode = BUILD_SGL_OFF__OPCODE(2, TWS_FW_CMD_GET_PARAM); cmd->param.request_id = (u_int8_t)req->request_id; cmd->param.host_id__unit = 0; cmd->param.param_count = 1; cmd->param.size = 2; /* map routine will add sgls */ /* Specify which parameter we want to set. */ param->table_id = (table_id | TWS_9K_PARAM_DESCRIPTOR); param->parameter_id = (u_int8_t)(param_id); param->parameter_size_bytes = (u_int16_t)param_size; error = tws_map_request(sc, req); if (!error) { reqid = tws_poll4_response(sc, &mfa); tws_unmap_request(sc, req); if ( reqid == TWS_REQ_TYPE_GETSET_PARAM ) { memcpy(data, param->data, param_size); } else { error = FAILURE; } } free(req->data, M_TWS); req->state = TWS_REQ_STATE_FREE; return(error); } void tws_unmap_request(struct tws_softc *sc, struct tws_request *req) { if (req->data != NULL) { if ( req->flags & TWS_DIR_IN ) bus_dmamap_sync(sc->data_tag, req->dma_map, BUS_DMASYNC_POSTREAD); if ( req->flags & TWS_DIR_OUT ) bus_dmamap_sync(sc->data_tag, req->dma_map, BUS_DMASYNC_POSTWRITE); mtx_lock(&sc->io_lock); bus_dmamap_unload(sc->data_tag, req->dma_map); mtx_unlock(&sc->io_lock); } } int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req) { int32_t error = 0; /* If the command involves data, map that too. */ if (req->data != NULL) { int my_flags = ((req->type == TWS_REQ_TYPE_SCSI_IO) ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT); /* * Map the data buffer into bus space and build the SG list. */ mtx_lock(&sc->io_lock); if (req->flags & TWS_DATA_CCB) error = bus_dmamap_load_ccb(sc->data_tag, req->dma_map, req->data, tws_dmamap_data_load_cbfn, req, my_flags); else error = bus_dmamap_load(sc->data_tag, req->dma_map, req->data, req->length, tws_dmamap_data_load_cbfn, req, my_flags); mtx_unlock(&sc->io_lock); if (error == EINPROGRESS) { TWS_TRACE(sc, "in progress", 0, error); tws_freeze_simq(sc, req); error = 0; // EINPROGRESS is not a fatal error. } } else { /* no data involved */ error = tws_submit_command(sc, req); } return(error); } static void tws_dmamap_data_load_cbfn(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct tws_request *req = (struct tws_request *)arg; struct tws_softc *sc = req->sc; u_int16_t sgls = nseg; void *sgl_ptr; struct tws_cmd_generic *gcmd; if ( error ) { TWS_TRACE(sc, "SOMETHING BAD HAPPENED! error = %d\n", error, 0); } if ( error == EFBIG ) { TWS_TRACE(sc, "not enough data segs", 0, nseg); req->error_code = error; req->ccb_ptr->ccb_h.status = CAM_REQ_TOO_BIG; return; } if ( req->flags & TWS_DIR_IN ) bus_dmamap_sync(req->sc->data_tag, req->dma_map, BUS_DMASYNC_PREREAD); if ( req->flags & TWS_DIR_OUT ) bus_dmamap_sync(req->sc->data_tag, req->dma_map, BUS_DMASYNC_PREWRITE); if ( segs ) { if ( (req->type == TWS_REQ_TYPE_PASSTHRU && GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) != TWS_FW_CMD_EXECUTE_SCSI) || req->type == TWS_REQ_TYPE_GETSET_PARAM) { gcmd = &req->cmd_pkt->cmd.pkt_g.generic; sgl_ptr = (u_int32_t *)(gcmd) + gcmd->size; gcmd->size += sgls * ((req->sc->is64bit && !tws_use_32bit_sgls) ? 4 : 2 ); tws_fill_sg_list(req->sc, (void *)segs, sgl_ptr, sgls); } else { tws_fill_sg_list(req->sc, (void *)segs, (void *)&(req->cmd_pkt->cmd.pkt_a.sg_list), sgls); req->cmd_pkt->cmd.pkt_a.lun_h4__sgl_entries |= sgls ; } } req->error_code = tws_submit_command(req->sc, req); } static void tws_fill_sg_list(struct tws_softc *sc, void *sgl_src, void *sgl_dest, u_int16_t num_sgl_entries) { int i; if ( sc->is64bit ) { struct tws_sg_desc64 *sgl_s = (struct tws_sg_desc64 *)sgl_src; if ( !tws_use_32bit_sgls ) { struct tws_sg_desc64 *sgl_d = (struct tws_sg_desc64 *)sgl_dest; if ( num_sgl_entries > TWS_MAX_64BIT_SG_ELEMENTS ) TWS_TRACE(sc, "64bit sg overflow", num_sgl_entries, 0); for (i = 0; i < num_sgl_entries; i++) { sgl_d[i].address = sgl_s->address; sgl_d[i].length = sgl_s->length; sgl_d[i].flag = 0; sgl_d[i].reserved = 0; sgl_s = (struct tws_sg_desc64 *) (((u_int8_t *)sgl_s) + sizeof(bus_dma_segment_t)); } } else { struct tws_sg_desc32 *sgl_d = (struct tws_sg_desc32 *)sgl_dest; if ( num_sgl_entries > TWS_MAX_32BIT_SG_ELEMENTS ) TWS_TRACE(sc, "32bit sg overflow", num_sgl_entries, 0); for (i = 0; i < num_sgl_entries; i++) { sgl_d[i].address = sgl_s->address; sgl_d[i].length = sgl_s->length; sgl_d[i].flag = 0; sgl_s = (struct tws_sg_desc64 *) (((u_int8_t *)sgl_s) + sizeof(bus_dma_segment_t)); } } } else { struct tws_sg_desc32 *sgl_s = (struct tws_sg_desc32 *)sgl_src; struct tws_sg_desc32 *sgl_d = (struct tws_sg_desc32 *)sgl_dest; if ( num_sgl_entries > TWS_MAX_32BIT_SG_ELEMENTS ) TWS_TRACE(sc, "32bit sg overflow", num_sgl_entries, 0); for (i = 0; i < num_sgl_entries; i++) { sgl_d[i].address = sgl_s[i].address; sgl_d[i].length = sgl_s[i].length; sgl_d[i].flag = 0; } } } void tws_intr(void *arg) { struct tws_softc *sc = (struct tws_softc *)arg; u_int32_t histat=0, db=0; if (!(sc)) { device_printf(sc->tws_dev, "null softc!!!\n"); return; } if ( tws_get_state(sc) == TWS_RESET ) { return; } if ( tws_get_state(sc) != TWS_ONLINE ) { return; } sc->stats.num_intrs++; histat = tws_read_reg(sc, TWS_I2O0_HISTAT, 4); if ( histat & TWS_BIT2 ) { TWS_TRACE_DEBUG(sc, "door bell :)", histat, TWS_I2O0_HISTAT); db = tws_read_reg(sc, TWS_I2O0_IOBDB, 4); if ( db & TWS_BIT21 ) { tws_intr_attn_error(sc); return; } if ( db & TWS_BIT18 ) { tws_intr_attn_aen(sc); } } if ( histat & TWS_BIT3 ) { tws_intr_resp(sc); } } static void tws_intr_attn_aen(struct tws_softc *sc) { u_int32_t db=0; /* maskoff db intrs untill all the aens are fetched */ /* tws_disable_db_intr(sc); */ tws_fetch_aen((void *)sc); tws_write_reg(sc, TWS_I2O0_HOBDBC, TWS_BIT18, 4); db = tws_read_reg(sc, TWS_I2O0_IOBDB, 4); } static void tws_intr_attn_error(struct tws_softc *sc) { u_int32_t db=0; TWS_TRACE(sc, "attn error", 0, 0); tws_write_reg(sc, TWS_I2O0_HOBDBC, ~0, 4); db = tws_read_reg(sc, TWS_I2O0_IOBDB, 4); device_printf(sc->tws_dev, "Micro controller error.\n"); tws_reset(sc); } static void tws_intr_resp(struct tws_softc *sc) { u_int16_t req_id; u_int64_t mfa; while ( tws_get_response(sc, &req_id, &mfa) ) { sc->stats.reqs_out++; if ( req_id == TWS_INVALID_REQID ) { TWS_TRACE_DEBUG(sc, "invalid req_id", mfa, req_id); sc->stats.reqs_errored++; tws_err_complete(sc, mfa); continue; } sc->reqs[req_id].cb(&sc->reqs[req_id]); } } static void tws_poll(struct cam_sim *sim) { struct tws_softc *sc = (struct tws_softc *)cam_sim_softc(sim); TWS_TRACE_DEBUG(sc, "entry", 0, 0); tws_intr((void *) sc); } void tws_timeout(void *arg) { struct tws_request *req = (struct tws_request *)arg; struct tws_softc *sc = req->sc; if ( req->error_code == TWS_REQ_RET_RESET ) { return; } mtx_lock(&sc->gen_lock); if ( req->error_code == TWS_REQ_RET_RESET ) { mtx_unlock(&sc->gen_lock); return; } if ( tws_get_state(sc) == TWS_RESET ) { mtx_unlock(&sc->gen_lock); return; } tws_teardown_intr(sc); xpt_freeze_simq(sc->sim, 1); tws_send_event(sc, TWS_RESET_START); if (req->type == TWS_REQ_TYPE_SCSI_IO) { device_printf(sc->tws_dev, "I/O Request timed out... Resetting controller\n"); } else if (req->type == TWS_REQ_TYPE_PASSTHRU) { device_printf(sc->tws_dev, "IOCTL Request timed out... Resetting controller\n"); } else { device_printf(sc->tws_dev, "Internal Request timed out... Resetting controller\n"); } tws_assert_soft_reset(sc); tws_turn_off_interrupts(sc); tws_reset_cb( (void*) sc ); tws_reinit( (void*) sc ); // device_printf(sc->tws_dev, "Controller Reset complete!\n"); tws_send_event(sc, TWS_RESET_COMPLETE); mtx_unlock(&sc->gen_lock); xpt_release_simq(sc->sim, 1); tws_setup_intr(sc, sc->irqs); } void tws_reset(void *arg) { struct tws_softc *sc = (struct tws_softc *)arg; mtx_lock(&sc->gen_lock); if ( tws_get_state(sc) == TWS_RESET ) { mtx_unlock(&sc->gen_lock); return; } tws_teardown_intr(sc); xpt_freeze_simq(sc->sim, 1); tws_send_event(sc, TWS_RESET_START); device_printf(sc->tws_dev, "Resetting controller\n"); tws_assert_soft_reset(sc); tws_turn_off_interrupts(sc); tws_reset_cb( (void*) sc ); tws_reinit( (void*) sc ); // device_printf(sc->tws_dev, "Controller Reset complete!\n"); tws_send_event(sc, TWS_RESET_COMPLETE); mtx_unlock(&sc->gen_lock); xpt_release_simq(sc->sim, 1); tws_setup_intr(sc, sc->irqs); } static void tws_reset_cb(void *arg) { struct tws_softc *sc = (struct tws_softc *)arg; time_t endt; int found = 0; u_int32_t reg; if ( tws_get_state(sc) != TWS_RESET ) { return; } // device_printf(sc->tws_dev, "Draining Busy Queue\n"); tws_drain_busy_queue(sc); // device_printf(sc->tws_dev, "Draining Reserved Reqs\n"); tws_drain_reserved_reqs(sc); // device_printf(sc->tws_dev, "Draining Response Queue\n"); tws_drain_response_queue(sc); // device_printf(sc->tws_dev, "Looking for controller ready flag...\n"); endt = TWS_LOCAL_TIME + TWS_POLL_TIMEOUT; while ((TWS_LOCAL_TIME <= endt) && (!found)) { reg = tws_read_reg(sc, TWS_I2O0_SCRPD3, 4); if ( reg & TWS_BIT13 ) { found = 1; // device_printf(sc->tws_dev, " ... Got it!\n"); } } if ( !found ) device_printf(sc->tws_dev, " ... Controller ready flag NOT found!\n"); } static void tws_reinit(void *arg) { struct tws_softc *sc = (struct tws_softc *)arg; int timeout_val=0; int try=2; int done=0; // device_printf(sc->tws_dev, "Waiting for Controller Ready\n"); while ( !done && try ) { if ( tws_ctlr_ready(sc) ) { done = 1; break; } else { timeout_val += 5; if ( timeout_val >= TWS_RESET_TIMEOUT ) { timeout_val = 0; if ( try ) tws_assert_soft_reset(sc); try--; } mtx_sleep(sc, &sc->gen_lock, 0, "tws_reinit", 5*hz); } } if (!done) { device_printf(sc->tws_dev, "FAILED to get Controller Ready!\n"); return; } sc->obfl_q_overrun = false; // device_printf(sc->tws_dev, "Sending initConnect\n"); if ( tws_init_connect(sc, tws_queue_depth) ) { TWS_TRACE_DEBUG(sc, "initConnect failed", 0, sc->is64bit); } tws_init_obfl_q(sc); tws_turn_on_interrupts(sc); wakeup_one(sc->chan); } static void tws_freeze_simq(struct tws_softc *sc, struct tws_request *req) { /* Only for IO commands */ if (req->type == TWS_REQ_TYPE_SCSI_IO) { union ccb *ccb = (union ccb *)(req->ccb_ptr); xpt_freeze_simq(sc->sim, 1); ccb->ccb_h.status |= CAM_RELEASE_SIMQ; ccb->ccb_h.status |= CAM_REQUEUE_REQ; } } TUNABLE_INT("hw.tws.cam_depth", &tws_cam_depth);