summaryrefslogtreecommitdiffstats
path: root/sys/dev/asr/asr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/asr/asr.c')
-rw-r--r--sys/dev/asr/asr.c4373
1 files changed, 4373 insertions, 0 deletions
diff --git a/sys/dev/asr/asr.c b/sys/dev/asr/asr.c
new file mode 100644
index 0000000..e3493b8
--- /dev/null
+++ b/sys/dev/asr/asr.c
@@ -0,0 +1,4373 @@
+/* $FreeBSD$ */
+/*
+ * Copyright (c) 1996-2000 Distributed Processing Technology Corporation
+ * Copyright (c) 2000 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.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.
+ */
+
+#define ASR_VERSION 1
+#define ASR_REVISION '0'
+#define ASR_SUBREVISION '3'
+#define ASR_MONTH 7
+#define ASR_DAY 12
+#define ASR_YEAR 2000 - 1980
+
+/*
+ * Debug macros to resude the unsightly ifdefs
+ */
+#if (defined(DEBUG_ASR) || defined(DEBUG_ASR_USR_CMD) || defined(DEBUG_ASR_CMD))
+# define debug_asr_message(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 */
+
+#if (defined(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);
+ /* None fatal version of the ASSERT macro */
+# if (defined(__STDC__))
+# define ASSERT(phrase) if(!(phrase))printf(#phrase " at line %d file %s\n",__LINE__,__FILE__)
+# else
+# define ASSERT(phrase) if(!(phrase))printf("phrase" " at line %d file %s\n",__LINE__,__FILE__)
+# endif
+#else /* DEBUG_ASR */
+# define debug_asr_printf(fmt,args...)
+# define debug_asr_dump_message(message)
+# define debug_asr_print_path(ccb)
+# define ASSERT(x)
+#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)
+# define debug_asr_dump_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_cmd_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 */
+
+#define dsDescription_size 46 /* Snug as a bug in a rug */
+#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 */
+};
+
+#include <sys/param.h> /* TRUE=1 and FALSE=0 defined here */
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/disklabel.h>
+#include <sys/bus.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <sys/stat.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_xpt_periph.h>
+
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/cputypes.h>
+#include <machine/clock.h>
+#include <i386/include/vmparam.h>
+
+#include <pci/pcivar.h>
+#include <pci/pcireg.h>
+
+#define STATIC static
+#define INLINE
+
+#if (defined(DEBUG_ASR) && (DEBUG_ASR > 0))
+# undef STATIC
+# define STATIC
+# undef INLINE
+# define INLINE
+#endif
+#define IN
+#define OUT
+#define INOUT
+
+#define osdSwap4(x) ((u_long)ntohl((u_long)(x)))
+#define KVTOPHYS(x) vtophys(x)
+#include "dev/asr/dptalign.h"
+#include "dev/asr/i2oexec.h"
+#include "dev/asr/i2obscsi.h"
+#include "dev/asr/i2odpt.h"
+#include "dev/asr/i2oadptr.h"
+#include "opt_asr.h"
+
+#include "dev/asr/sys_info.h"
+
+/* 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 */
+
+/**************************************************************************
+** 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. **
+***************************************************************************/
+
+/* I2O register set */
+typedef struct {
+ U8 Address[0x30];
+ volatile U32 Status;
+ volatile U32 Mask;
+# define Mask_InterruptsDisabled 0x08
+ U32 x[2];
+ volatile U32 ToFIFO; /* In Bound FIFO */
+ volatile U32 FromFIFO; /* Out Bound FIFO */
+} i2oRegs_t;
+
+/*
+ * 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;
+};
+
+typedef struct Asr_softc {
+ u_int16_t ha_irq;
+ void * ha_Base; /* base port for each board */
+ u_int8_t * volatile ha_blinkLED;
+ i2oRegs_t * ha_Virt; /* Base address of adapter */
+ I2O_IOP_ENTRY ha_SystemTable;
+ LIST_HEAD(,ccb_hdr) ha_ccb; /* ccbs in use */
+ struct cam_path * ha_path[MAX_CHANNEL+1];
+ struct cam_sim * ha_sim[MAX_CHANNEL+1];
+#if __FreeBSD_version >= 400000
+ struct resource * ha_mem_res;
+ struct resource * ha_irq_res;
+ void * ha_intr;
+#endif
+ u_int8_t ha_adapter_target[MAX_CHANNEL+1];
+ 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_int16_t ha_Msgs_Count;
+
+ /* 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_int16_t ha_QueueSize; /* Max outstanding commands */
+
+ /* Links into other parents and HBAs */
+ struct Asr_softc * ha_next; /* HBA list */
+
+#ifdef ASR_MEASURE_PERFORMANCE
+#define MAX_TIMEQ_SIZE 256 // assumes MAX 256 scsi commands sent
+ asr_perf_t ha_performance;
+ u_int32_t ha_submitted_ccbs_count;
+
+ // Queueing macros for a circular queue
+#define TIMEQ_FREE_LIST_EMPTY(head, tail) (-1 == (head) && -1 == (tail))
+#define TIMEQ_FREE_LIST_FULL(head, tail) ((((tail) + 1) % MAX_TIMEQ_SIZE) == (head))
+#define ENQ_TIMEQ_FREE_LIST(item, Q, head, tail) \
+ if (!TIMEQ_FREE_LIST_FULL((head), (tail))) { \
+ if TIMEQ_FREE_LIST_EMPTY((head),(tail)) { \
+ (head) = (tail) = 0; \
+ } \
+ else (tail) = ((tail) + 1) % MAX_TIMEQ_SIZE; \
+ Q[(tail)] = (item); \
+ } \
+ else { \
+ debug_asr_printf("asr: Enqueueing when TimeQ Free List is full... This should not happen!\n"); \
+ }
+#define DEQ_TIMEQ_FREE_LIST(item, Q, head, tail) \
+ if (!TIMEQ_FREE_LIST_EMPTY((head), (tail))) { \
+ item = Q[(head)]; \
+ if ((head) == (tail)) { (head) = (tail) = -1; } \
+ else (head) = ((head) + 1) % MAX_TIMEQ_SIZE; \
+ } \
+ else { \
+ (item) = -1; \
+ debug_asr_printf("asr: Dequeueing when TimeQ Free List is empty... This should not happen!\n"); \
+ }
+
+ // Circular queue of time stamps
+ struct timeval ha_timeQ[MAX_TIMEQ_SIZE];
+ u_int32_t ha_timeQFreeList[MAX_TIMEQ_SIZE];
+ int ha_timeQFreeHead;
+ int ha_timeQFreeTail;
+#endif
+} Asr_softc_t;
+
+STATIC Asr_softc_t * Asr_softc;
+
+/*
+ * Prototypes of the routines we have in this object.
+ */
+
+/* Externally callable routines */
+#if __FreeBSD_version >= 400000
+#define PROBE_ARGS IN device_t tag
+#define PROBE_RET int
+#define PROBE_SET() u_long id = (pci_get_device(tag)<<16)|pci_get_vendor(tag)
+#define PROBE_RETURN(retval) if(retval){device_set_desc(tag,retval);return(0);}else{return(ENXIO);}
+#define ATTACH_ARGS IN device_t tag
+#define ATTACH_RET int
+#define ATTACH_SET() int unit = device_get_unit(tag)
+#define ATTACH_RETURN(retval) return(retval)
+#else
+#define PROBE_ARGS IN pcici_t tag, IN pcidi_t id
+#define PROBE_RET const char *
+#define PROBE_SET()
+#define PROBE_RETURN(retval) return(retval)
+#define ATTACH_ARGS IN pcici_t tag, IN int unit
+#define ATTACH_RET void
+#define ATTACH_SET()
+#define ATTACH_RETURN(retval) return
+#endif
+/* I2O HDM interface */
+STATIC PROBE_RET asr_probe __P((PROBE_ARGS));
+STATIC ATTACH_RET asr_attach __P((ATTACH_ARGS));
+/* DOMINO placeholder */
+STATIC PROBE_RET domino_probe __P((PROBE_ARGS));
+STATIC ATTACH_RET domino_attach __P((ATTACH_ARGS));
+/* MODE0 adapter placeholder */
+STATIC PROBE_RET mode0_probe __P((PROBE_ARGS));
+STATIC ATTACH_RET mode0_attach __P((ATTACH_ARGS));
+
+STATIC Asr_softc_t * ASR_get_sc __P((
+ IN dev_t dev));
+STATIC int asr_ioctl __P((
+ IN dev_t dev,
+ IN u_long cmd,
+ INOUT caddr_t data,
+ int flag,
+ struct proc * proc));
+STATIC int asr_open __P((
+ IN dev_t dev,
+ int32_t flags,
+ int32_t ifmt,
+ IN struct proc * proc));
+STATIC int asr_close __P((
+ dev_t dev,
+ int flags,
+ int ifmt,
+ struct proc * proc));
+STATIC int asr_intr __P((
+ IN Asr_softc_t * sc));
+STATIC void asr_timeout __P((
+ INOUT void * arg));
+STATIC int ASR_init __P((
+ IN Asr_softc_t * sc));
+STATIC INLINE int ASR_acquireLct __P((
+ INOUT Asr_softc_t * sc));
+STATIC INLINE int ASR_acquireHrt __P((
+ INOUT Asr_softc_t * sc));
+STATIC void asr_action __P((
+ IN struct cam_sim * sim,
+ IN union ccb * ccb));
+STATIC void asr_async __P((
+ void * callback_arg,
+ u_int32_t code,
+ struct cam_path * path,
+ void * arg));
+STATIC void asr_poll __P((
+ IN struct cam_sim * sim));
+
+/*
+ * Here is the auto-probe structure used to nest our tests appropriately
+ * during the startup phase of the operating system.
+ */
+#if __FreeBSD_version >= 400000
+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);
+
+STATIC device_method_t domino_methods[] = {
+ DEVMETHOD(device_probe, domino_probe),
+ DEVMETHOD(device_attach, domino_attach),
+ { 0, 0 }
+};
+
+STATIC driver_t domino_driver = {
+ "domino",
+ domino_methods,
+ 0
+};
+
+STATIC devclass_t domino_devclass;
+
+DRIVER_MODULE(domino, pci, domino_driver, domino_devclass, 0, 0);
+
+STATIC device_method_t mode0_methods[] = {
+ DEVMETHOD(device_probe, mode0_probe),
+ DEVMETHOD(device_attach, mode0_attach),
+ { 0, 0 }
+};
+
+STATIC driver_t mode0_driver = {
+ "mode0",
+ mode0_methods,
+ 0
+};
+
+STATIC devclass_t mode0_devclass;
+
+DRIVER_MODULE(mode0, pci, mode0_driver, mode0_devclass, 0, 0);
+#else
+STATIC u_long asr_pcicount = 0;
+STATIC struct pci_device asr_pcidev = {
+ "asr",
+ asr_probe,
+ asr_attach,
+ &asr_pcicount,
+ NULL
+};
+DATA_SET (asr_pciset, asr_pcidev);
+
+STATIC u_long domino_pcicount = 0;
+STATIC struct pci_device domino_pcidev = {
+ "domino",
+ domino_probe,
+ domino_attach,
+ &domino_pcicount,
+ NULL
+};
+DATA_SET (domino_pciset, domino_pcidev);
+
+STATIC u_long mode0_pcicount = 0;
+STATIC struct pci_device mode0_pcidev = {
+ "mode0",
+ mode0_probe,
+ mode0_attach,
+ &mode0_pcicount,
+ NULL
+};
+DATA_SET (mode0_pciset, mode0_pcidev);
+#endif
+
+/*
+ * devsw for asr hba driver
+ *
+ * only ioctl is used. the sd driver provides all other access.
+ */
+#define CDEV_MAJOR 97 /* prefered default character major */
+STATIC struct cdevsw asr_cdevsw = {
+ asr_open, /* open */
+ asr_close, /* close */
+ noread, /* read */
+ nowrite, /* write */
+ asr_ioctl, /* ioctl */
+ nopoll, /* poll */
+ nommap, /* mmap */
+ nostrategy, /* strategy */
+ "asr", /* name */
+ CDEV_MAJOR, /* maj */
+ nodump, /* dump */
+ nopsize, /* psize */
+ 0, /* flags */
+ -1 /* bmaj */
+};
+
+#ifdef ASR_MEASURE_PERFORMANCE
+STATIC u_int32_t asr_time_delta __P((IN struct timeval start,
+ IN struct timeval end));
+#endif
+
+/*
+ * Initialize the dynamic cdevsw hooks.
+ */
+STATIC void
+asr_drvinit (
+ void * unused)
+{
+ static int asr_devsw_installed = 0;
+
+ if (asr_devsw_installed) {
+ return;
+ }
+ asr_devsw_installed++;
+ /*
+ * Find a free spot (the report during driver load used by
+ * osd layer in engine to generate the controlling nodes).
+ */
+ while ((asr_cdevsw.d_maj < NUMCDEVSW)
+ && (devsw(makedev(asr_cdevsw.d_maj,0)) != (struct cdevsw *)NULL)) {
+ ++asr_cdevsw.d_maj;
+ }
+ if (asr_cdevsw.d_maj >= NUMCDEVSW) for (
+ asr_cdevsw.d_maj = 0;
+ (asr_cdevsw.d_maj < CDEV_MAJOR)
+ && (devsw(makedev(asr_cdevsw.d_maj,0)) != (struct cdevsw *)NULL);
+ ++asr_cdevsw.d_maj);
+ /*
+ * Come to papa
+ */
+ cdevsw_add(&asr_cdevsw);
+ /*
+ * delete any nodes that would attach to the primary adapter,
+ * let the adapter scans add them.
+ */
+ destroy_dev(makedev(asr_cdevsw.d_maj,0));
+} /* asr_drvinit */
+
+/* Must initialize before CAM layer picks up our HBA driver */
+SYSINIT(asrdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,asr_drvinit,NULL)
+
+/* I2O support routines */
+#define defAlignLong(STRUCT,NAME) char NAME[sizeof(STRUCT)]
+#define getAlignLong(STRUCT,NAME) ((STRUCT *)(NAME))
+
+/*
+ * Fill message with default.
+ */
+STATIC PI2O_MESSAGE_FRAME
+ASR_fillMessage (
+ IN char * Message,
+ IN u_int16_t size)
+{
+ OUT PI2O_MESSAGE_FRAME Message_Ptr;
+
+ Message_Ptr = getAlignLong(I2O_MESSAGE_FRAME, Message);
+ bzero ((void *)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);
+ return (Message_Ptr);
+} /* ASR_fillMessage */
+
+#define EMPTY_QUEUE ((U32)-1L)
+
+STATIC INLINE U32
+ASR_getMessage(
+ IN i2oRegs_t * virt)
+{
+ OUT U32 MessageOffset;
+
+ if ((MessageOffset = virt->ToFIFO) == EMPTY_QUEUE) {
+ MessageOffset = virt->ToFIFO;
+ }
+ return (MessageOffset);
+} /* ASR_getMessage */
+
+/* Issue a polled command */
+STATIC U32
+ASR_initiateCp (
+ INOUT i2oRegs_t * virt,
+ IN PI2O_MESSAGE_FRAME Message)
+{
+ OUT U32 Mask = -1L;
+ 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(virt)) == EMPTY_QUEUE)
+ && (--Delay != 0)) {
+ DELAY (10000);
+ }
+ if (MessageOffset != EMPTY_QUEUE) {
+ bcopy (Message, virt->Address + MessageOffset,
+ I2O_MESSAGE_FRAME_getMessageSize(Message) << 2);
+ /*
+ * Disable the Interrupts
+ */
+ virt->Mask = (Mask = virt->Mask) | Mask_InterruptsDisabled;
+ virt->ToFIFO = MessageOffset;
+ }
+ return (Mask);
+} /* ASR_initiateCp */
+
+/*
+ * Reset the adapter.
+ */
+STATIC U32
+ASR_resetIOP (
+ INOUT i2oRegs_t * virt)
+{
+ struct resetMessage {
+ I2O_EXEC_IOP_RESET_MESSAGE M;
+ U32 R;
+ };
+ defAlignLong(struct resetMessage,Message);
+ PI2O_EXEC_IOP_RESET_MESSAGE Message_Ptr;
+ OUT U32 * volatile 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 = (U32 *)((char *)Message_Ptr
+ + sizeof(I2O_EXEC_IOP_RESET_MESSAGE))) = 0;
+ I2O_EXEC_IOP_RESET_MESSAGE_setStatusWordLowAddress(Message_Ptr,
+ KVTOPHYS((void *)Reply_Ptr));
+ /*
+ * Send the Message out
+ */
+ if ((Old = ASR_initiateCp (virt, (PI2O_MESSAGE_FRAME)Message_Ptr)) != (U32)-1L) {
+ /*
+ * 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.
+ */
+ virt->Mask = Old;
+ ASSERT (*Reply_Ptr);
+ return (*Reply_Ptr);
+ }
+ ASSERT (Old != (U32)-1L);
+ return (0);
+} /* ASR_resetIOP */
+
+/*
+ * Get the curent state of the adapter
+ */
+STATIC INLINE PI2O_EXEC_STATUS_GET_REPLY
+ASR_getStatus (
+ INOUT i2oRegs_t * virt,
+ OUT PI2O_EXEC_STATUS_GET_REPLY buffer)
+{
+ defAlignLong(I2O_EXEC_STATUS_GET_MESSAGE,Message);
+ PI2O_EXEC_STATUS_GET_MESSAGE Message_Ptr;
+ 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,
+ KVTOPHYS((void *)buffer));
+ /* This one is a Byte Count */
+ I2O_EXEC_STATUS_GET_MESSAGE_setReplyBufferLength(Message_Ptr,
+ sizeof(I2O_EXEC_STATUS_GET_REPLY));
+ /*
+ * Reset the Reply Status
+ */
+ bzero ((void *)buffer, sizeof(I2O_EXEC_STATUS_GET_REPLY));
+ /*
+ * Send the Message out
+ */
+ if ((Old = ASR_initiateCp (virt, (PI2O_MESSAGE_FRAME)Message_Ptr)) != (U32)-1L) {
+ /*
+ * Wait for a response (Poll), timeouts are dangerous if
+ * the card is truly responsive. We assume response in 50ms.
+ */
+ u_int8_t Delay = 50;
+
+ while (*((U8 * volatile)&buffer->SyncByte) == 0) {
+ if (--Delay == 0) {
+ buffer = (PI2O_EXEC_STATUS_GET_REPLY)NULL;
+ break;
+ }
+ DELAY (1000);
+ }
+ /*
+ * Re-enable the interrupts.
+ */
+ virt->Mask = Old;
+ return (buffer);
+ }
+ return ((PI2O_EXEC_STATUS_GET_REPLY)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 PROBE_RET
+asr_probe(PROBE_ARGS)
+{
+ PROBE_SET();
+ if (id == 0xA5011044) {
+ PROBE_RETURN ("Adaptec Caching SCSI RAID");
+ }
+ PROBE_RETURN (NULL);
+} /* asr_probe */
+
+/*
+ * Probe/Attach for DOMINO chipset.
+ */
+STATIC PROBE_RET
+domino_probe(PROBE_ARGS)
+{
+ PROBE_SET();
+ if (id == 0x10121044) {
+ PROBE_RETURN ("Adaptec Caching Memory Controller");
+ }
+ PROBE_RETURN (NULL);
+} /* domino_probe */
+
+STATIC ATTACH_RET
+domino_attach (ATTACH_ARGS)
+{
+ ATTACH_RETURN (0);
+} /* domino_attach */
+
+/*
+ * Probe/Attach for MODE0 adapters.
+ */
+STATIC PROBE_RET
+mode0_probe(PROBE_ARGS)
+{
+ PROBE_SET();
+ if (id == 0x908010B5) {
+ PROBE_RETURN ("Adaptec Mode0 3xxx");
+ }
+#if 0 /* this would match any generic i960 -- mjs */
+ if (id == 0x19608086) {
+ PROBE_RETURN ("Adaptec Mode0 1xxx");
+ }
+#endif
+ PROBE_RETURN (NULL);
+} /* mode0_probe */
+
+STATIC ATTACH_RET
+mode0_attach (ATTACH_ARGS)
+{
+ ATTACH_RETURN (0);
+} /* mode0_attach */
+
+STATIC INLINE union asr_ccb *
+asr_alloc_ccb (
+ IN Asr_softc_t * sc)
+{
+ OUT union asr_ccb * new_ccb;
+
+ if ((new_ccb = (union asr_ccb *)malloc(sizeof(*new_ccb),
+ M_DEVBUF, M_WAITOK)) != (union asr_ccb *)NULL) {
+ bzero (new_ccb, sizeof(*new_ccb));
+ 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 (
+ IN 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 */
+
+/*
+ * Prototypes
+ */
+STATIC INLINE int ASR_queue __P((
+ IN Asr_softc_t * sc,
+ IN PI2O_MESSAGE_FRAME Message));
+/*
+ * Send a message synchronously and without Interrupt to a ccb.
+ */
+STATIC int
+ASR_queue_s (
+ INOUT union asr_ccb * ccb,
+ IN 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 ();
+ sc->ha_Virt->Mask = (Mask = sc->ha_Virt->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_REQ_INPROG) {
+ (void)asr_intr (sc);
+ }
+
+ /* Re-enable Interrupts */
+ sc->ha_Virt->Mask = Mask;
+ splx(s);
+
+ return (ccb->ccb_h.status);
+} /* ASR_queue_s */
+
+/*
+ * Send a message synchronously to a Asr_softc_t
+ */
+STATIC int
+ASR_queue_c (
+ IN Asr_softc_t * sc,
+ IN PI2O_MESSAGE_FRAME Message)
+{
+ union asr_ccb * ccb;
+ OUT int status;
+
+ if ((ccb = asr_alloc_ccb (sc)) == (union asr_ccb *)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 (
+ IN Asr_softc_t * sc,
+ INOUT union asr_ccb * ccb)
+{
+ 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);
+ }
+} /* ASR_ccbAdd */
+
+/*
+ * Remove the specified ccb from the active queue.
+ */
+STATIC INLINE void
+ASR_ccbRemove (
+ IN Asr_softc_t * sc,
+ INOUT union asr_ccb * ccb)
+{
+ untimeout(asr_timeout, (caddr_t)ccb, ccb->ccb_h.timeout_ch);
+ LIST_REMOVE(&ccb->ccb_h, sim_links.le);
+} /* ASR_ccbRemove */
+
+/*
+ * Fail all the active commands, so they get re-issued by the operating
+ * system.
+ */
+STATIC INLINE void
+ASR_failActiveCommands (
+ IN Asr_softc_t * sc)
+{
+ struct ccb_hdr * ccb;
+ defAlignLong(I2O_EXEC_LCT_NOTIFY_MESSAGE,Message);
+ PI2O_EXEC_LCT_NOTIFY_MESSAGE Message_Ptr;
+
+ /* Send a blind LCT command to wait for the enableSys to complete */
+ Message_Ptr = (PI2O_EXEC_LCT_NOTIFY_MESSAGE)ASR_fillMessage(Message,
+ sizeof(I2O_EXEC_LCT_NOTIFY_MESSAGE) - sizeof(I2O_SG_ELEMENT));
+ I2O_MESSAGE_FRAME_setFunction(&(Message_Ptr->StdMessageFrame),
+ I2O_EXEC_LCT_NOTIFY);
+ I2O_EXEC_LCT_NOTIFY_MESSAGE_setClassIdentifier(Message_Ptr,
+ I2O_CLASS_MATCH_ANYCLASS);
+ (void)ASR_queue_c(sc, (PI2O_MESSAGE_FRAME)Message_Ptr);
+
+ LIST_FOREACH(ccb, &(sc->ha_ccb), sim_links.le) {
+
+ ASR_ccbRemove (sc, (union asr_ccb *)ccb);
+
+ ccb->status &= ~CAM_STATUS_MASK;
+ ccb->status |= CAM_REQUEUE_REQ;
+ ((struct ccb_scsiio *)ccb)->resid
+ = ((struct ccb_scsiio *)ccb)->dxfer_len;
+
+ if (ccb->path) {
+ xpt_done ((union ccb *)ccb);
+ } else {
+ wakeup ((caddr_t)ccb);
+ }
+ }
+} /* ASR_failActiveCommands */
+
+/*
+ * The following command causes the HBA to reset the specific bus
+ */
+STATIC INLINE void
+ASR_resetBus(
+ IN Asr_softc_t * sc,
+ IN int bus)
+{
+ defAlignLong(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 (
+ IN Asr_softc_t * sc)
+{
+ if ((sc != (Asr_softc_t *)NULL)
+ && (sc->ha_blinkLED != (u_int8_t *)NULL)
+ && (sc->ha_blinkLED[1] == 0xBC)) {
+ return (sc->ha_blinkLED[0]);
+ }
+ return (0);
+} /* 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 INLINE tid_t *
+ASR_getTidAddress(
+ INOUT Asr_softc_t * sc,
+ IN int bus,
+ IN int target,
+ IN int lun,
+ IN 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 == (Asr_softc_t *)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 ((tid_t *)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]) == (target2lun_t *)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))
+ == (target2lun_t *)NULL)) {
+ debug_asr_printf("failed to allocate bus list\n");
+ return ((tid_t *)NULL);
+ }
+ bzero (bus_ptr, sizeof(*bus_ptr)
+ + (sizeof(bus_ptr->LUN) * new_size));
+ 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))
+ == (target2lun_t *)NULL)) {
+ debug_asr_printf("failed to reallocate bus list\n");
+ return ((tid_t *)NULL);
+ }
+ /*
+ * Zero and copy the whole thing, safer, simpler coding
+ * and not really performance critical at this point.
+ */
+ bzero (new_bus_ptr, sizeof(*bus_ptr)
+ + (sizeof(bus_ptr->LUN) * new_size));
+ 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]) == (lun2tid_t *)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))
+ == (lun2tid_t *)NULL)) {
+ debug_asr_printf("failed to allocate target list\n");
+ return ((tid_t *)NULL);
+ }
+ bzero (target_ptr, sizeof(*target_ptr)
+ + (sizeof(target_ptr->TID) * new_size));
+ 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))
+ == (lun2tid_t *)NULL)) {
+ debug_asr_printf("failed to reallocate target list\n");
+ return ((tid_t *)NULL);
+ }
+ /*
+ * Zero and copy the whole thing, safer, simpler coding
+ * and not really performance critical at this point.
+ */
+ bzero (new_target_ptr, sizeof(*target_ptr)
+ + (sizeof(target_ptr->TID) * new_size));
+ 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 (
+ IN Asr_softc_t * sc,
+ IN int bus,
+ IN int target,
+ IN int lun)
+{
+ tid_t * tid_ptr;
+ int s;
+ OUT tid_t retval;
+
+ s = splcam();
+ if (((tid_ptr = ASR_getTidAddress (sc, bus, target, lun, FALSE))
+ == (tid_t *)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 (
+ INOUT Asr_softc_t * sc,
+ IN int bus,
+ IN int target,
+ IN int lun,
+ INOUT 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))
+ == (tid_t *)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 INLINE int
+ASR_rescan(
+ IN Asr_softc_t * sc)
+{
+ int bus;
+ OUT 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;
+
+ /*
+ * 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;
+
+ for (lun = 0; lun <= sc->ha_MaxLun; ++lun) {
+ PI2O_LCT_ENTRY Device;
+ tid_t TID = (tid_t)-1;
+
+ /*
+ * 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.
+ * ASR_getTid (sc, bus, target, lun) != TI
+ */
+ /*
+ * 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);
+ }
+ }
+ } 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 INLINE void
+ASR_reset(
+ IN Asr_softc_t * sc)
+{
+ (void)ASR_resetIOP (sc->ha_Virt);
+ (void)ASR_init (sc);
+ (void)ASR_rescan (sc);
+ (void)ASR_failActiveCommands (sc);
+} /* ASR_reset */
+
+/*
+ * Device timeout handler.
+ */
+STATIC void
+asr_timeout(
+ INOUT 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) {
+ debug_asr_printf (
+ " due to adapter blinkled code %x\nresetting adapter\n", s);
+ ASR_reset (sc);
+ 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();
+ if (ccb->ccb_h.status == CAM_CMD_TIMEOUT) {
+ debug_asr_printf (" AGAIN\nreinitializing adapter\n");
+ ASR_reset (sc);
+ 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);
+ ASR_resetBus (sc, cam_sim_bus(xpt_path_sim(ccb->ccb_h.path)));
+ splx(s);
+} /* asr_timeout */
+
+/*
+ * send a message asynchronously
+ */
+STATIC INLINE int
+ASR_queue(
+ IN Asr_softc_t * sc,
+ IN PI2O_MESSAGE_FRAME Message)
+{
+ OUT U32 MessageOffset;
+ union asr_ccb * ccb;
+
+ debug_asr_printf ("Host Command Dump:\n");
+ debug_asr_dump_message (Message);
+
+ /*
+ * Limit the number of Messages sent to this HBA. Better to sleep,
+ * than to hardware loop like a nut! By limiting the number of
+ * messages to an individual HBA here, we manage to perform all
+ * the processing of the message ready to drop the next one into
+ * the controller. We could limit the messages we are allowed to
+ * take, but that may have a performance hit.
+ */
+ ccb = (union asr_ccb *)(long)
+ I2O_MESSAGE_FRAME_getInitiatorContext64(Message);
+
+ if (((MessageOffset = ASR_getMessage(sc->ha_Virt)) != EMPTY_QUEUE)
+ || ((MessageOffset = ASR_getMessage(sc->ha_Virt)) != EMPTY_QUEUE)) {
+#ifdef ASR_MEASURE_PERFORMANCE
+ int startTimeIndex;
+
+ if (ccb) {
+ ++sc->ha_performance.command_count[
+ (int) ccb->csio.cdb_io.cdb_bytes[0]];
+ DEQ_TIMEQ_FREE_LIST(startTimeIndex,
+ sc->ha_timeQFreeList,
+ sc->ha_timeQFreeHead,
+ sc->ha_timeQFreeTail);
+ if (-1 != startTimeIndex) {
+ microtime(&sc->ha_timeQ[startTimeIndex]);
+ }
+ /* Time stamp the command before we send it out */
+ ((PRIVATE_SCSI_SCB_EXECUTE_MESSAGE *) Message)->
+ PrivateMessageFrame.TransactionContext
+ = (I2O_TRANSACTION_CONTEXT) startTimeIndex;
+
+ ++sc->ha_submitted_ccbs_count;
+ if (sc->ha_performance.max_submit_count
+ < sc->ha_submitted_ccbs_count) {
+ sc->ha_performance.max_submit_count
+ = sc->ha_submitted_ccbs_count;
+ }
+ }
+#endif
+ bcopy (Message, sc->ha_Virt->Address + MessageOffset,
+ I2O_MESSAGE_FRAME_getMessageSize(Message) << 2);
+ if (ccb) {
+ ASR_ccbAdd (sc, ccb);
+ }
+ /* Post the command */
+ sc->ha_Virt->ToFIFO = MessageOffset;
+ } else {
+ if (ASR_getBlinkLedCode(sc)) {
+ 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) ? NULL : KVTOPHYS(Buffer))
+
+/*
+ * Retrieve Parameter Group.
+ * Buffer must be allocated using defAlignLong macro.
+ */
+STATIC void *
+ASR_getParams(
+ IN Asr_softc_t * sc,
+ IN tid_t TID,
+ IN int Group,
+ OUT void * Buffer,
+ IN 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;
+ };
+ defAlignLong(struct paramGetMessage, 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 ((void *)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);
+ bzero ((void *)(Buffer_Ptr = getAlignLong(struct ParamBuffer, Buffer)),
+ 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 ((void *)NULL);
+} /* ASR_getParams */
+
+/*
+ * Acquire the LCT information.
+ */
+STATIC INLINE int
+ASR_acquireLct (
+ INOUT 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))
+ == (PI2O_EXEC_LCT_NOTIFY_MESSAGE)NULL) {
+ return (ENOMEM);
+ }
+ (void)ASR_fillMessage((char *)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))
+ == (PI2O_LCT)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))
+ == (PI2O_EXEC_LCT_NOTIFY_MESSAGE)NULL) {
+ free (sc->ha_LCT, M_TEMP);
+ sc->ha_LCT = (PI2O_LCT)NULL;
+ free (Message_Ptr, M_TEMP);
+ return (ENOMEM);
+ }
+ span = ((caddr_t)sg) - (caddr_t)Message_Ptr;
+ bcopy ((caddr_t)Message_Ptr,
+ (caddr_t)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;
+ };
+ defAlignLong(struct ControllerInfo, 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)))
+ == (PI2O_HBA_SCSI_CONTROLLER_INFO_SCALAR)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;
+ };
+ defAlignLong (struct DeviceInfo, 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)))
+ == (PI2O_DPT_DEVICE_INFO_SCALAR)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 INLINE PI2O_MESSAGE_FRAME
+ASR_init_message(
+ IN union asr_ccb * ccb,
+ OUT PI2O_MESSAGE_FRAME Message)
+{
+ int next, span, base, rw;
+ OUT PI2O_MESSAGE_FRAME Message_Ptr;
+ Asr_softc_t * sc = (Asr_softc_t *)(ccb->ccb_h.spriv_ptr0);
+ PI2O_SGE_SIMPLE_ELEMENT sg;
+ caddr_t v;
+ vm_size_t size, len;
+ U32 MessageSize;
+
+ /* We only need to zero out the PRIVATE_SCSI_SCB_EXECUTE_MESSAGE */
+ bzero (Message_Ptr = getAlignLong(I2O_MESSAGE_FRAME, Message),
+ (sizeof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) - sizeof(I2O_SG_ELEMENT)));
+
+ {
+ 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;
+
+ if ((TID = ASR_getTid (sc, bus, target, lun)) == (tid_t)-1) {
+ PI2O_LCT_ENTRY Device;
+
+ TID = (tid_t)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 ((PI2O_MESSAGE_FRAME)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;
+ ASSERT (ccb->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 INLINE U32
+ASR_initOutBound (
+ INOUT Asr_softc_t * sc)
+{
+ struct initOutBoundMessage {
+ I2O_EXEC_OUTBOUND_INIT_MESSAGE M;
+ U32 R;
+ };
+ defAlignLong(struct initOutBoundMessage,Message);
+ PI2O_EXEC_OUTBOUND_INIT_MESSAGE Message_Ptr;
+ OUT 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->ha_Virt, (PI2O_MESSAGE_FRAME)Message_Ptr)) != (U32)-1L) {
+ u_long size, addr;
+
+ /*
+ * Wait for a response (Poll).
+ */
+ while (*Reply_Ptr < I2O_EXEC_OUTBOUND_INIT_REJECTED);
+ /*
+ * Re-enable the interrupts.
+ */
+ sc->ha_Virt->Mask = Old;
+ /*
+ * Populate the outbound table.
+ */
+ if (sc->ha_Msgs == (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)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))
+ != (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)NULL) {
+ (void)bzero ((char *)sc->ha_Msgs, size);
+ sc->ha_Msgs_Phys = KVTOPHYS(sc->ha_Msgs);
+ }
+ }
+
+ /* Initialize the outbound FIFO */
+ if (sc->ha_Msgs != (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)NULL)
+ for (size = sc->ha_Msgs_Count, addr = sc->ha_Msgs_Phys;
+ size; --size) {
+ sc->ha_Virt->FromFIFO = addr;
+ addr += sizeof(I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME);
+ }
+ return (*Reply_Ptr);
+ }
+ return (0);
+} /* ASR_initOutBound */
+
+/*
+ * Set the system table
+ */
+STATIC INLINE int
+ASR_setSysTab(
+ IN Asr_softc_t * sc)
+{
+ PI2O_EXEC_SYS_TAB_SET_MESSAGE Message_Ptr;
+ PI2O_SET_SYSTAB_HEADER SystemTable;
+ Asr_softc_t * ha;
+ PI2O_SGE_SIMPLE_ELEMENT sg;
+ int retVal;
+
+ if ((SystemTable = (PI2O_SET_SYSTAB_HEADER)malloc (
+ sizeof(I2O_SET_SYSTAB_HEADER), M_TEMP, M_WAITOK))
+ == (PI2O_SET_SYSTAB_HEADER)NULL) {
+ return (ENOMEM);
+ }
+ bzero (SystemTable, sizeof(I2O_SET_SYSTAB_HEADER));
+ for (ha = Asr_softc; ha; ha = ha->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)) == (PI2O_EXEC_SYS_TAB_SET_MESSAGE)NULL) {
+ free (SystemTable, M_TEMP);
+ return (ENOMEM);
+ }
+ (void)ASR_fillMessage((char *)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;
+ for (ha = Asr_softc; ha; ha = ha->ha_next) {
+ SG(sg, 0,
+ ((ha->ha_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 INLINE int
+ASR_acquireHrt (
+ INOUT Asr_softc_t * sc)
+{
+ defAlignLong(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 ((void *)&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 INLINE int
+ASR_enableSys (
+ IN Asr_softc_t * sc)
+{
+ defAlignLong(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(
+ IN 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 INLINE void
+ASR_sync (
+ IN Asr_softc_t * sc,
+ IN int bus,
+ IN int target,
+ IN 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 != (Asr_softc_t *)NULL)
+ && (sc->ha_ccb.lh_first != (struct ccb_hdr *)NULL)
+ && ((TID = ASR_getTid (sc, bus, target, lun)) != (tid_t)-1)
+ && (TID != (tid_t)0)) {
+ defAlignLong(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE,Message);
+ PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE Message_Ptr;
+
+ bzero (Message_Ptr
+ = getAlignLong(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE, Message),
+ 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 INLINE void
+ASR_synchronize (
+ IN 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(
+ IN Asr_softc_t * sc)
+{
+ ASR_synchronize (sc);
+ 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 (
+#if __FreeBSD_version >= 400000
+ IN device_t tag,
+#else
+ IN pcici_t tag,
+#endif
+ IN Asr_softc_t * sc)
+{
+ int rid;
+ u_int32_t p, l;
+
+#if __FreeBSD_version >= 400000
+ /*
+ * I2O specification says we must find first *memory* mapped BAR
+ */
+ for (rid = PCIR_MAPS;
+ rid < (PCIR_MAPS + 4 * sizeof(u_int32_t));
+ ++rid) {
+ p = pci_read_config(tag, rid, sizeof(p));
+ if ((p & 1) == 0) {
+ break;
+ }
+ }
+ /*
+ * Give up?
+ */
+ if (rid >= (PCIR_MAPS + 4 * sizeof(u_int32_t))) {
+ rid = PCIR_MAPS;
+ }
+ p = pci_read_config(tag, rid, sizeof(p));
+ pci_write_config(tag, rid, -1, sizeof(p));
+ l = 0 - (pci_read_config(tag, rid, sizeof(l)) & ~15);
+ pci_write_config(tag, rid, p, sizeof(p));
+ if (l > MAX_MAP) {
+ l = MAX_MAP;
+ }
+ p &= ~15;
+ sc->ha_mem_res = bus_alloc_resource(tag, SYS_RES_MEMORY, &rid,
+ p, p + l, l, RF_ACTIVE);
+ if (sc->ha_mem_res == (struct resource *)NULL) {
+ return (0);
+ }
+ sc->ha_Base = (void *)rman_get_start(sc->ha_mem_res);
+ if (sc->ha_Base == (void *)NULL) {
+ return (0);
+ }
+ sc->ha_Virt = (i2oRegs_t *) rman_get_virtual(sc->ha_mem_res);
+#else
+ vm_size_t psize, poffs;
+
+ /*
+ * I2O specification says we must find first *memory* mapped BAR
+ */
+ for (rid = PCI_MAP_REG_START;
+ rid < (PCI_MAP_REG_START + 4 * sizeof(u_int32_t));
+ ++rid) {
+ p = pci_conf_read (tag, rid);
+ if ((p & 1) == 0) {
+ break;
+ }
+ }
+ if (rid >= (PCI_MAP_REG_START + 4 * sizeof(u_int32_t))) {
+ rid = PCI_MAP_REG_START;
+ }
+ /*
+ ** save old mapping, get size and type of memory
+ **
+ ** type is in the lowest four bits.
+ ** If device requires 2^n bytes, the next
+ ** n-4 bits are read as 0.
+ */
+
+ sc->ha_Base = (void *)((p = pci_conf_read (tag, rid))
+ & PCI_MAP_MEMORY_ADDRESS_MASK);
+ pci_conf_write (tag, rid, 0xfffffffful);
+ l = pci_conf_read (tag, rid);
+ pci_conf_write (tag, rid, p);
+
+ /*
+ ** check the type
+ */
+
+ if (!((l & PCI_MAP_MEMORY_TYPE_MASK) == PCI_MAP_MEMORY_TYPE_32BIT_1M
+ && ((u_long)sc->ha_Base & ~0xfffff) == 0)
+ && ((l & PCI_MAP_MEMORY_TYPE_MASK) != PCI_MAP_MEMORY_TYPE_32BIT)) {
+ debug_asr_printf (
+ "asr_pci_map_mem failed: bad memory type=0x%x\n",
+ (unsigned) l);
+ return (0);
+ };
+
+ /*
+ ** get the size.
+ */
+
+ psize = -(l & PCI_MAP_MEMORY_ADDRESS_MASK);
+ if (psize > MAX_MAP) {
+ psize = MAX_MAP;
+ }
+
+ if ((sc->ha_Base == (void *)NULL)
+ || (sc->ha_Base == (void *)PCI_MAP_MEMORY_ADDRESS_MASK)) {
+ debug_asr_printf ("asr_pci_map_mem: not configured by bios.\n");
+ return (0);
+ };
+
+ /*
+ ** Truncate sc->ha_Base to page boundary.
+ ** (Or does pmap_mapdev the job?)
+ */
+
+ poffs = (u_long)sc->ha_Base - trunc_page ((u_long)sc->ha_Base);
+ sc->ha_Virt = (i2oRegs_t *)pmap_mapdev ((u_long)sc->ha_Base - poffs,
+ psize + poffs);
+
+ if (sc->ha_Virt == (i2oRegs_t *)NULL) {
+ return (0);
+ }
+
+ sc->ha_Virt = (i2oRegs_t *)((u_long)sc->ha_Virt + poffs);
+#endif
+ 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 (
+#if __FreeBSD_version >= 400000
+ IN device_t tag,
+#else
+ IN pcici_t tag,
+#endif
+ IN Asr_softc_t * sc)
+{
+#if __FreeBSD_version >= 400000
+ int rid = 0;
+
+ sc->ha_irq_res = bus_alloc_resource(tag, SYS_RES_IRQ, &rid,
+ 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (sc->ha_irq_res == (struct resource *)NULL) {
+ return (0);
+ }
+ if (bus_setup_intr(tag, sc->ha_irq_res, INTR_TYPE_CAM,
+ (driver_intr_t *)asr_intr, (void *)sc, &(sc->ha_intr))) {
+ return (0);
+ }
+ sc->ha_irq = pci_read_config(tag, PCIR_INTLINE, sizeof(char));
+#else
+ if (!pci_map_int(tag, (pci_inthand_t *)asr_intr,
+ (void *)sc, &cam_imask)) {
+ return (0);
+ }
+ sc->ha_irq = pci_conf_read(tag, PCIR_INTLINE);
+#endif
+ return (1);
+} /* asr_pci_map_int */
+
+/*
+ * Attach the devices, and virtual devices to the driver list.
+ */
+STATIC ATTACH_RET
+asr_attach (ATTACH_ARGS)
+{
+ Asr_softc_t * sc;
+ struct scsi_inquiry_data * iq;
+ ATTACH_SET();
+
+ if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT)) == (Asr_softc_t *)NULL) {
+ ATTACH_RETURN(ENOMEM);
+ }
+ if (Asr_softc == (Asr_softc_t *)NULL) {
+ /*
+ * Fixup the OS revision as saved in the dptsig for the
+ * engine (dptioctl.h) to pick up.
+ */
+ bcopy (osrelease, &ASR_sig.dsDescription[16], 5);
+ printf ("asr%d: major=%d\n", unit, asr_cdevsw.d_maj);
+ }
+ /*
+ * Initialize the software structure
+ */
+ bzero (sc, sizeof(*sc));
+ LIST_INIT(&sc->ha_ccb);
+# ifdef ASR_MEASURE_PERFORMANCE
+ {
+ u_int32_t i;
+
+ // initialize free list for timeQ
+ sc->ha_timeQFreeHead = 0;
+ sc->ha_timeQFreeTail = MAX_TIMEQ_SIZE - 1;
+ for (i = 0; i < MAX_TIMEQ_SIZE; i++) {
+ sc->ha_timeQFreeList[i] = i;
+ }
+ }
+# endif
+ /* Link us into the HA list */
+ {
+ Asr_softc_t **ha;
+
+ for (ha = &Asr_softc; *ha; ha = &((*ha)->ha_next));
+ *(ha) = sc;
+ }
+ {
+ PI2O_EXEC_STATUS_GET_REPLY status;
+ int size;
+
+ /*
+ * This is the real McCoy!
+ */
+ if (!asr_pci_map_mem(tag, sc)) {
+ printf ("asr%d: could not map memory\n", unit);
+ ATTACH_RETURN(ENXIO);
+ }
+ /* Enable if not formerly enabled */
+#if __FreeBSD_version >= 400000
+ pci_write_config (tag, PCIR_COMMAND,
+ pci_read_config (tag, PCIR_COMMAND, sizeof(char))
+ | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN, sizeof(char));
+ /* Knowledge is power, responsibility is direct */
+ {
+ struct pci_devinfo {
+ STAILQ_ENTRY(pci_devinfo) pci_links;
+ struct resource_list resources;
+ pcicfgregs cfg;
+ } * dinfo = device_get_ivars(tag);
+ sc->ha_pciBusNum = dinfo->cfg.bus;
+ sc->ha_pciDeviceNum = (dinfo->cfg.slot << 3)
+ | dinfo->cfg.func;
+ }
+#else
+ pci_conf_write (tag, PCIR_COMMAND,
+ pci_conf_read (tag, PCIR_COMMAND)
+ | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
+ /* Knowledge is power, responsibility is direct */
+ switch (pci_mechanism) {
+
+ case 1:
+ sc->ha_pciBusNum = tag.cfg1 >> 16;
+ sc->ha_pciDeviceNum = tag.cfg1 >> 8;
+
+ case 2:
+ sc->ha_pciBusNum = tag.cfg2.forward;
+ sc->ha_pciDeviceNum = ((tag.cfg2.enable >> 1) & 7)
+ | (tag.cfg2.port >> 5);
+ }
+#endif
+ /* Check if the device is there? */
+ if ((ASR_resetIOP(sc->ha_Virt) == 0)
+ || ((status = (PI2O_EXEC_STATUS_GET_REPLY)malloc (
+ sizeof(I2O_EXEC_STATUS_GET_REPLY), M_TEMP, M_WAITOK))
+ == (PI2O_EXEC_STATUS_GET_REPLY)NULL)
+ || (ASR_getStatus(sc->ha_Virt, status) == NULL)) {
+ printf ("asr%d: could not initialize hardware\n", unit);
+ ATTACH_RETURN(ENODEV); /* Get next, maybe better luck */
+ }
+ 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) + (U32)(&(((i2oRegs_t *)NULL)->ToFIFO));
+
+ if (!asr_pci_map_int(tag, (void *)sc)) {
+ printf ("asr%d: could not map interrupt\n", unit);
+ ATTACH_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;
+ }
+ free (status, M_TEMP);
+ 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;
+ };
+ defAlignLong (struct BufferInfo, 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)))
+ != (PI2O_DPT_EXEC_IOP_BUFFERS_SCALAR)NULL) {
+ sc->ha_blinkLED = sc->ha_Virt->Address
+ + I2O_DPT_EXEC_IOP_BUFFERS_SCALAR_getSerialOutputOffset(Info)
+ + FW_DEBUG_BLED_OFFSET;
+ }
+ if (ASR_acquireLct(sc) == 0) {
+ (void)ASR_acquireHrt(sc);
+ }
+ } else {
+ printf ("asr%d: failed to initialize\n", unit);
+ ATTACH_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.
+ */
+ { 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 (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.
+ */
+
+ printf ("asr%d:", unit);
+
+ if ((iq = (struct scsi_inquiry_data *)malloc (
+ sizeof(struct scsi_inquiry_data), M_TEMP, M_WAITOK))
+ != (struct scsi_inquiry_data *)NULL) {
+ defAlignLong(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE,Message);
+ PPRIVATE_SCSI_SCB_EXECUTE_MESSAGE Message_Ptr;
+ int posted = 0;
+
+ bzero (iq, sizeof(struct scsi_inquiry_data));
+ bzero (Message_Ptr
+ = getAlignLong(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE, Message),
+ 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 ((caddr_t)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);
+
+ /*
+ * fill in the prototype cam_path.
+ */
+ {
+ int bus;
+ union asr_ccb * ccb;
+
+ if ((ccb = asr_alloc_ccb (sc)) == (union asr_ccb *)NULL) {
+ printf ("asr%d: CAM could not be notified of asynchronous callback parameters\n", unit);
+ ATTACH_RETURN(ENOMEM);
+ }
+ 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, 1, QueueSize, devq);
+ if (sc->ha_sim[bus] == NULL) {
+ continue;
+ }
+
+ if (xpt_bus_register(sc->ha_sim[bus], 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;
+ }
+ xpt_setup_ccb(&(ccb->ccb_h),
+ sc->ha_path[bus], /*priority*/5);
+ ccb->ccb_h.func_code = XPT_SASYNC_CB;
+ ccb->csa.event_enable = AC_LOST_DEVICE;
+ ccb->csa.callback = asr_async;
+ ccb->csa.callback_arg = sc->ha_sim[bus];
+ xpt_action((union ccb *)ccb);
+ }
+ asr_free_ccb (ccb);
+ }
+ /*
+ * Generate the device node information
+ */
+ (void)make_dev(&asr_cdevsw, unit, 0, 0, S_IRWXU, "rasr%d", unit);
+ destroy_dev(makedev(asr_cdevsw.d_maj,unit+1));
+ ATTACH_RETURN(0);
+} /* asr_attach */
+
+#if (!defined(UNREFERENCED_PARAMETER))
+# define UNREFERENCED_PARAMETER(x) (void)(x)
+#endif
+
+STATIC void
+asr_async(
+ void * callback_arg,
+ u_int32_t code,
+ struct cam_path * path,
+ void * arg)
+{
+ UNREFERENCED_PARAMETER(callback_arg);
+ UNREFERENCED_PARAMETER(code);
+ UNREFERENCED_PARAMETER(path);
+ UNREFERENCED_PARAMETER(arg);
+} /* asr_async */
+
+STATIC void
+asr_poll(
+ IN struct cam_sim *sim)
+{
+ asr_intr(cam_sim_softc(sim));
+} /* asr_poll */
+
+STATIC void
+asr_action(
+ IN struct cam_sim * sim,
+ IN 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 (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];
+ };
+ defAlignLong(struct Message,Message);
+ PI2O_MESSAGE_FRAME Message_Ptr;
+
+ if (ccb->ccb_h.status != 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_cmd_dump_ccb(ccb);
+
+ if ((Message_Ptr = ASR_init_message ((union asr_ccb *)ccb,
+ (PI2O_MESSAGE_FRAME)Message)) != (PI2O_MESSAGE_FRAME)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) {
+#ifdef ASR_MEASURE_PERFORMANCE
+ ++sc->ha_performance.command_too_busy;
+#endif
+ 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;
+ u_int target_mask;
+
+ cts = &ccb->cts;
+ target_mask = 0x01 << ccb->ccb_h.target_id;
+ if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) {
+ cts->flags = CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB;
+ cts->bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ cts->sync_period = 6; /* 40MHz */
+ cts->sync_offset = 15;
+
+ cts->valid = CCB_TRANS_SYNC_RATE_VALID
+ | CCB_TRANS_SYNC_OFFSET_VALID
+ | CCB_TRANS_BUS_WIDTH_VALID
+ | CCB_TRANS_DISC_VALID
+ | CCB_TRANS_TQ_VALID;
+ 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;
+ xpt_done(ccb);
+ break;
+ }
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+} /* asr_action */
+
+#ifdef ASR_MEASURE_PERFORMANCE
+#define WRITE_OP 1
+#define READ_OP 2
+#define min_submitR sc->ha_performance.read_by_size_min_time[index]
+#define max_submitR sc->ha_performance.read_by_size_max_time[index]
+#define min_submitW sc->ha_performance.write_by_size_min_time[index]
+#define max_submitW sc->ha_performance.write_by_size_max_time[index]
+
+STATIC INLINE void
+asr_IObySize(
+ IN Asr_softc_t * sc,
+ IN u_int32_t submitted_time,
+ IN int op,
+ IN int index)
+{
+ struct timeval submitted_timeval;
+
+ submitted_timeval.tv_sec = 0;
+ submitted_timeval.tv_usec = submitted_time;
+
+ if ( op == READ_OP ) {
+ ++sc->ha_performance.read_by_size_count[index];
+
+ if ( submitted_time != 0xffffffff ) {
+ timevaladd(
+ &sc->ha_performance.read_by_size_total_time[index],
+ &submitted_timeval);
+ if ( (min_submitR == 0)
+ || (submitted_time < min_submitR) ) {
+ min_submitR = submitted_time;
+ }
+
+ if ( submitted_time > max_submitR ) {
+ max_submitR = submitted_time;
+ }
+ }
+ } else {
+ ++sc->ha_performance.write_by_size_count[index];
+ if ( submitted_time != 0xffffffff ) {
+ timevaladd(
+ &sc->ha_performance.write_by_size_total_time[index],
+ &submitted_timeval);
+ if ( (submitted_time < min_submitW)
+ || (min_submitW == 0) ) {
+ min_submitW = submitted_time;
+ }
+
+ if ( submitted_time > max_submitW ) {
+ max_submitW = submitted_time;
+ }
+ }
+ }
+} /* asr_IObySize */
+#endif
+
+/*
+ * Handle processing of current CCB as pointed to by the Status.
+ */
+STATIC int
+asr_intr (
+ IN Asr_softc_t * sc)
+{
+ OUT int processed;
+
+#ifdef ASR_MEASURE_PERFORMANCE
+ struct timeval junk;
+
+ microtime(&junk);
+ sc->ha_performance.intr_started = junk;
+#endif
+
+ for (processed = 0;
+ sc->ha_Virt->Status & Mask_InterruptsDisabled;
+ processed = 1) {
+ union asr_ccb * ccb;
+ U32 ReplyOffset;
+ PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME Reply;
+
+ if (((ReplyOffset = sc->ha_Virt->FromFIFO) == EMPTY_QUEUE)
+ && ((ReplyOffset = sc->ha_Virt->FromFIFO) == 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) {
+ defAlignLong(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
+ = ((PI2O_SINGLE_REPLY_MESSAGE_FRAME)
+ (sc->ha_Virt->Address + MessageOffset))
+ ->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
+ */
+ bcopy ((caddr_t)Message_Ptr,
+ sc->ha_Virt->Address + MessageOffset,
+ sizeof(I2O_UTIL_NOP_MESSAGE));
+ /*
+ * Issue the NOP
+ */
+ sc->ha_Virt->ToFIFO = MessageOffset;
+ }
+
+ /*
+ * Asynchronous command with no return requirements,
+ * and a generic handler for immunity against odd error
+ * returns from the adapter.
+ */
+ if (ccb == (union asr_ccb *)NULL) {
+ /*
+ * Return Reply so that it can be used for the
+ * next command
+ */
+ sc->ha_Virt->FromFIFO = ReplyOffset;
+ continue;
+ }
+
+ /* Welease Wadjah! (and stop timeouts) */
+ ASR_ccbRemove (sc, ccb);
+
+ switch (
+ I2O_SINGLE_REPLY_MESSAGE_FRAME_getDetailedStatusCode(
+ &(Reply->StdReplyFrame))) {
+
+ case I2O_SCSI_DSC_SUCCESS:
+ ccb->ccb_h.status &= ~CAM_STATUS_MASK;
+ ccb->ccb_h.status |= CAM_REQ_CMP;
+ break;
+
+ case I2O_SCSI_DSC_CHECK_CONDITION:
+ ccb->ccb_h.status &= ~CAM_STATUS_MASK;
+ ccb->ccb_h.status |= CAM_REQ_CMP|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_STATUS_MASK;
+ ccb->ccb_h.status |= CAM_SCSI_BUSY;
+ break;
+
+ case I2O_SCSI_HBA_DSC_SELECTION_TIMEOUT:
+ ccb->ccb_h.status &= ~CAM_STATUS_MASK;
+ 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_STATUS_MASK;
+ 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_STATUS_MASK;
+ ccb->ccb_h.status |= CAM_DATA_RUN_ERR;
+ break;
+
+ default:
+ ccb->ccb_h.status &= ~CAM_STATUS_MASK;
+ 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);
+ }
+
+#ifdef ASR_MEASURE_PERFORMANCE
+ {
+ struct timeval endTime;
+ u_int32_t submitted_time;
+ u_int32_t size;
+ int op_type;
+ int startTimeIndex;
+
+ --sc->ha_submitted_ccbs_count;
+ startTimeIndex
+ = (int)Reply->StdReplyFrame.TransactionContext;
+ if (-1 != startTimeIndex) {
+ /* Compute the time spent in device/adapter */
+ microtime(&endTime);
+ submitted_time = asr_time_delta(sc->ha_timeQ[
+ startTimeIndex], endTime);
+ /* put the startTimeIndex back on free list */
+ ENQ_TIMEQ_FREE_LIST(startTimeIndex,
+ sc->ha_timeQFreeList,
+ sc->ha_timeQFreeHead,
+ sc->ha_timeQFreeTail);
+ } else {
+ submitted_time = 0xffffffff;
+ }
+
+#define maxctime sc->ha_performance.max_command_time[ccb->csio.cdb_io.cdb_bytes[0]]
+#define minctime sc->ha_performance.min_command_time[ccb->csio.cdb_io.cdb_bytes[0]]
+ if (submitted_time != 0xffffffff) {
+ if ( maxctime < submitted_time ) {
+ maxctime = submitted_time;
+ }
+ if ( (minctime == 0)
+ || (minctime > submitted_time) ) {
+ minctime = submitted_time;
+ }
+
+ if ( sc->ha_performance.max_submit_time
+ < submitted_time ) {
+ sc->ha_performance.max_submit_time
+ = submitted_time;
+ }
+ if ( sc->ha_performance.min_submit_time == 0
+ || sc->ha_performance.min_submit_time
+ > submitted_time) {
+ sc->ha_performance.min_submit_time
+ = submitted_time;
+ }
+
+ switch ( ccb->csio.cdb_io.cdb_bytes[0] ) {
+
+ case 0xa8: /* 12-byte READ */
+ /* FALLTHRU */
+ case 0x08: /* 6-byte READ */
+ /* FALLTHRU */
+ case 0x28: /* 10-byte READ */
+ op_type = READ_OP;
+ break;
+
+ case 0x0a: /* 6-byte WRITE */
+ /* FALLTHRU */
+ case 0xaa: /* 12-byte WRITE */
+ /* FALLTHRU */
+ case 0x2a: /* 10-byte WRITE */
+ op_type = WRITE_OP;
+ break;
+
+ default:
+ op_type = 0;
+ break;
+ }
+
+ if ( op_type != 0 ) {
+ struct scsi_rw_big * cmd;
+
+ cmd = (struct scsi_rw_big *)
+ &(ccb->csio.cdb_io);
+
+ size = (((u_int32_t) cmd->length2 << 8)
+ | ((u_int32_t) cmd->length1)) << 9;
+
+ switch ( size ) {
+
+ case 512:
+ asr_IObySize(sc,
+ submitted_time, op_type,
+ SIZE_512);
+ break;
+
+ case 1024:
+ asr_IObySize(sc,
+ submitted_time, op_type,
+ SIZE_1K);
+ break;
+
+ case 2048:
+ asr_IObySize(sc,
+ submitted_time, op_type,
+ SIZE_2K);
+ break;
+
+ case 4096:
+ asr_IObySize(sc,
+ submitted_time, op_type,
+ SIZE_4K);
+ break;
+
+ case 8192:
+ asr_IObySize(sc,
+ submitted_time, op_type,
+ SIZE_8K);
+ break;
+
+ case 16384:
+ asr_IObySize(sc,
+ submitted_time, op_type,
+ SIZE_16K);
+ break;
+
+ case 32768:
+ asr_IObySize(sc,
+ submitted_time, op_type,
+ SIZE_32K);
+ break;
+
+ case 65536:
+ asr_IObySize(sc,
+ submitted_time, op_type,
+ SIZE_64K);
+ break;
+
+ default:
+ if ( size > (1 << 16) ) {
+ asr_IObySize(sc,
+ submitted_time,
+ op_type,
+ SIZE_BIGGER);
+ } else {
+ asr_IObySize(sc,
+ submitted_time,
+ op_type,
+ SIZE_OTHER);
+ }
+ break;
+ }
+ }
+ }
+ }
+#endif
+ /* 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;
+ }
+ bcopy ((caddr_t)Reply->SenseData,
+ (caddr_t)&(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
+ */
+ sc->ha_Virt->FromFIFO = ReplyOffset;
+
+ if (ccb->ccb_h.path) {
+ xpt_done ((union ccb *)ccb);
+ } else {
+ wakeup ((caddr_t)ccb);
+ }
+ }
+#ifdef ASR_MEASURE_PERFORMANCE
+ {
+ u_int32_t result;
+
+ microtime(&junk);
+ result = asr_time_delta(sc->ha_performance.intr_started, junk);
+
+ if (result != 0xffffffff) {
+ if ( sc->ha_performance.max_intr_time < result ) {
+ sc->ha_performance.max_intr_time = result;
+ }
+
+ if ( (sc->ha_performance.min_intr_time == 0)
+ || (sc->ha_performance.min_intr_time > result) ) {
+ sc->ha_performance.min_intr_time = result;
+ }
+ }
+ }
+#endif
+ 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) minor(dev)
+
+STATIC INLINE Asr_softc_t *
+ASR_get_sc (
+ IN dev_t dev)
+{
+ int unit = asr_unit(dev);
+ OUT Asr_softc_t * sc = Asr_softc;
+
+ while (sc && sc->ha_sim[0] && (cam_sim_unit(sc->ha_sim[0]) != unit)) {
+ sc = sc->ha_next;
+ }
+ return (sc);
+} /* ASR_get_sc */
+
+STATIC u_int8_t ASR_ctlr_held;
+
+STATIC int
+asr_open(
+ IN dev_t dev,
+ int32_t flags,
+ int32_t ifmt,
+ IN struct proc * proc)
+{
+ int s;
+ OUT int error;
+ UNREFERENCED_PARAMETER(flags);
+ UNREFERENCED_PARAMETER(ifmt);
+
+ if (ASR_get_sc (dev) == (Asr_softc_t *)NULL) {
+ return (ENODEV);
+ }
+ s = splcam ();
+ if (ASR_ctlr_held) {
+ error = EBUSY;
+ } else if ((error = suser(proc)) == 0) {
+ ++ASR_ctlr_held;
+ }
+ splx(s);
+ return (error);
+} /* asr_open */
+
+STATIC int
+asr_close(
+ dev_t dev,
+ int flags,
+ int ifmt,
+ struct proc * proc)
+{
+ UNREFERENCED_PARAMETER(dev);
+ UNREFERENCED_PARAMETER(flags);
+ UNREFERENCED_PARAMETER(ifmt);
+ UNREFERENCED_PARAMETER(proc);
+
+ 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 INLINE int
+ASR_queue_i(
+ IN Asr_softc_t * sc,
+ INOUT 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))
+ == (PI2O_MESSAGE_FRAME)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->ha_Virt);
+ ReplySizeInBytes = sizeof(status);
+ debug_usr_cmd_printf ("resetIOP done\n");
+ return (copyout ((caddr_t)&status, (caddr_t)Reply,
+ ReplySizeInBytes));
+ }
+
+ case I2O_EXEC_STATUS_GET:
+ { I2O_EXEC_STATUS_GET_REPLY status;
+
+ if (ASR_getStatus (sc->ha_Virt, &status)
+ == (PI2O_EXEC_STATUS_GET_REPLY)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)) == (PI2O_MESSAGE_FRAME)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))
+ == (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)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)) == (PI2O_SCSI_ERROR_REPLY_MESSAGE_FRAME)NULL) {
+ free (Message_Ptr, M_TEMP);
+ debug_usr_cmd_printf ("Failed to acquire frame[%d] memory\n",
+ ReplySizeInBytes);
+ return (ENOMEM);
+ }
+ (void)ASR_fillMessage ((char *)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))
+ == (struct ioctlSgList_S *)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))
+ == (PI2O_MESSAGE_FRAME)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 ((caddr_t)Message_Ptr,
+ (caddr_t)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))
+ != (struct ioctlSgList_S *)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)) == (union asr_ccb *)NULL) {
+ /* Free up in-kernel buffers */
+ while ((elm = SLIST_FIRST(&sgList))
+ != (struct ioctlSgList_S *)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_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));
+ ASR_reset (sc);
+ splx(s);
+ /* Command Cleanup */
+ ASR_ccbRemove(sc, ccb);
+ /* Free up in-kernel buffers */
+ while ((elm = SLIST_FIRST(&sgList))
+ != (struct ioctlSgList_S *)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((caddr_t)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);
+ }
+ bcopy ((caddr_t)&(ccb->csio.sense_data), (caddr_t)Reply_Ptr->SenseData,
+ size);
+ I2O_SCSI_ERROR_REPLY_MESSAGE_FRAME_setAutoSenseTransferCount(
+ Reply_Ptr, size);
+ }
+
+ /* Free up in-kernel buffers */
+ while ((elm = SLIST_FIRST(&sgList)) != (struct ioctlSgList_S *)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(
+ IN dev_t dev,
+ IN u_long cmd,
+ INOUT caddr_t data,
+ int flag,
+ struct proc * proc)
+{
+ int i, j;
+ OUT int error = 0;
+ Asr_softc_t * sc = ASR_get_sc (dev);
+ UNREFERENCED_PARAMETER(flag);
+ UNREFERENCED_PARAMETER(proc);
+
+ if (sc != (Asr_softc_t *)NULL)
+ switch(cmd) {
+
+ case DPT_SIGNATURE:
+# if (dsDescription_size != 50)
+ case DPT_SIGNATURE + ((50 - dsDescription_size) << 16):
+# endif
+ if (cmd & 0xFFFF0000) {
+ (void)bcopy ((caddr_t)(&ASR_sig), data,
+ sizeof(dpt_sig_S));
+ return (0);
+ }
+ /* Traditional version of the ioctl interface */
+ case DPT_SIGNATURE & 0x0000FFFF:
+ 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 = (u_long)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;
+ if (cmd & 0xFFFF0000) {
+ bcopy (&CtlrInfo, data, sizeof(CtlrInfo));
+ } else {
+ 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;
+ caddr_t c_addr;
+ /* Kernel Specific ptok `hack' */
+# define ptok(a) ((char *)(a) + KERNBASE)
+
+ bzero (&Info, sizeof(Info));
+
+ 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));
+
+ Info.processorFamily = ASR_sig.dsProcessorFamily;
+ 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;
+ }
+ 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_CMOS_Valid | SI_NumDrivesValid
+ | SI_OSversionValid |SI_BusTypeValid;
+
+ /* Go Out And Look For SmartROM */
+ for(i = 0; i < 3; ++i) {
+ int k;
+
+ if (i == 0) {
+ j = 0xC8000;
+ } else if (i == 1) {
+ j = 0xD8000;
+ } else {
+ j = 0xDC000;
+ }
+ c_addr = ptok(j);
+ if (*((unsigned short *)c_addr) != 0xAA55) {
+ continue;
+ }
+ if (*((u_long *)(c_addr + 6)) != 0x202053) {
+ continue;
+ }
+ if (*((u_long *)(c_addr + 10)) != 0x545044) {
+ continue;
+ }
+ c_addr += 0x24;
+ for (k = 0; k < 64; ++k) {
+ if ((*((unsigned char *)(c_addr++)) == ' ')
+ && (*((unsigned char *)(c_addr)) == 'v')) {
+ break;
+ }
+ }
+ if (k < 64) {
+ Info.smartROMMajorVersion
+ = *((unsigned char *)(c_addr += 3)) - '0';
+ Info.smartROMMinorVersion
+ = *((unsigned char *)(c_addr += 2));
+ Info.smartROMRevision
+ = *((unsigned char *)(++c_addr));
+ Info.flags |= SI_SmartROMverValid;
+ break;
+ }
+ }
+ if (i >= 3) {
+ Info.flags |= SI_NO_SmartROM;
+ }
+ /* 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;
+
+# if (defined(THIS_IS_BROKEN))
+ /* If There Is 1 or 2 Drives Found, Set Up Drive Parameters */
+ if (Info.numDrives > 0) {
+ /*
+ * Get The Pointer From Int 41 For The First
+ * Drive Parameters
+ */
+ j = ((unsigned)(*((unsigned short *)ptok(0x104+2))) << 4)
+ + (unsigned)(*((unsigned short *)ptok(0x104+0)));
+ /*
+ * It appears that SmartROM's Int41/Int46 pointers
+ * use memory that gets stepped on by the kernel
+ * loading. We no longer have access to this
+ * geometry information but try anyways (!?)
+ */
+ Info.drives[0].cylinders = *((unsigned char *)ptok(j));
+ ++j;
+ Info.drives[0].cylinders += ((int)*((unsigned char *)
+ ptok(j))) << 8;
+ ++j;
+ Info.drives[0].heads = *((unsigned char *)ptok(j));
+ j += 12;
+ Info.drives[0].sectors = *((unsigned char *)ptok(j));
+ Info.flags |= SI_DriveParamsValid;
+ if ((Info.drives[0].cylinders == 0)
+ || (Info.drives[0].heads == 0)
+ || (Info.drives[0].sectors == 0)) {
+ Info.flags &= ~SI_DriveParamsValid;
+ }
+ if (Info.numDrives > 1) {
+ /*
+ * Get The Pointer From Int 46 For The
+ * Second Drive Parameters
+ */
+ j = ((unsigned)(*((unsigned short *)ptok(0x118+2))) << 4)
+ + (unsigned)(*((unsigned short *)ptok(0x118+0)));
+ Info.drives[1].cylinders = *((unsigned char *)
+ ptok(j));
+ ++j;
+ Info.drives[1].cylinders += ((int)
+ *((unsigned char *)ptok(j))) << 8;
+ ++j;
+ Info.drives[1].heads = *((unsigned char *)
+ ptok(j));
+ j += 12;
+ Info.drives[1].sectors = *((unsigned char *)
+ ptok(j));
+ if ((Info.drives[1].cylinders == 0)
+ || (Info.drives[1].heads == 0)
+ || (Info.drives[1].sectors == 0)) {
+ Info.flags &= ~SI_DriveParamsValid;
+ }
+ }
+ }
+# endif
+ /* Copy Out The Info Structure To The User */
+ if (cmd & 0xFFFF0000) {
+ bcopy (&Info, data, sizeof(Info));
+ } else {
+ 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;
+ }
+ if (cmd & 0xFFFF0000) {
+ bcopy ((caddr_t)(&i), data, sizeof(i));
+ } else {
+ error = copyout (&i, *(caddr_t *)data, sizeof(i));
+ }
+ break;
+
+ /* Get performance metrics */
+#ifdef ASR_MEASURE_PERFORMANCE
+ case DPT_PERF_INFO:
+ bcopy((caddr_t) &sc->ha_performance, data,
+ sizeof(sc->ha_performance));
+ return (0);
+#endif
+
+ /* Send an I2O command */
+ case I2OUSRCMD:
+ return (ASR_queue_i (sc, *((PI2O_MESSAGE_FRAME *)data)));
+
+ /* Reset and re-initialize the adapter */
+ case I2ORESETCMD:
+ ASR_reset (sc);
+ return (0);
+
+ /* Rescan the LCT table and resynchronize the information */
+ case I2ORESCANCMD:
+ return (ASR_rescan (sc));
+ }
+ return (EINVAL);
+} /* asr_ioctl */
+
+#ifdef ASR_MEASURE_PERFORMANCE
+/*
+ * This function subtracts one timeval structure from another,
+ * Returning the result in usec.
+ * It assumes that less than 4 billion usecs passed form start to end.
+ * If times are sensless, 0xffffffff is returned.
+ */
+
+STATIC u_int32_t
+asr_time_delta(
+ IN struct timeval start,
+ IN struct timeval end)
+{
+ OUT u_int32_t result;
+
+ if (start.tv_sec > end.tv_sec) {
+ result = 0xffffffff;
+ }
+ else {
+ if (start.tv_sec == end.tv_sec) {
+ if (start.tv_usec > end.tv_usec) {
+ result = 0xffffffff;
+ } else {
+ return (end.tv_usec - start.tv_usec);
+ }
+ } else {
+ return (end.tv_sec - start.tv_sec) * 1000000 +
+ end.tv_usec + (1000000 - start.tv_usec);
+ }
+ }
+ return(result);
+} /* asr_time_delta */
+#endif
OpenPOWER on IntegriCloud