aboutsummaryrefslogtreecommitdiff
path: root/linux/src/drivers/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'linux/src/drivers/scsi')
-rw-r--r--linux/src/drivers/scsi/53c7,8xx.h1584
-rw-r--r--linux/src/drivers/scsi/53c78xx.c6401
-rw-r--r--linux/src/drivers/scsi/53c8xx_d.h2677
-rw-r--r--linux/src/drivers/scsi/53c8xx_u.h97
-rw-r--r--linux/src/drivers/scsi/AM53C974.c2274
-rw-r--r--linux/src/drivers/scsi/AM53C974.h413
-rw-r--r--linux/src/drivers/scsi/BusLogic.c5003
-rw-r--r--linux/src/drivers/scsi/BusLogic.h1775
-rw-r--r--linux/src/drivers/scsi/FlashPoint.c12159
-rw-r--r--linux/src/drivers/scsi/NCR5380.c3247
-rw-r--r--linux/src/drivers/scsi/NCR5380.h373
-rw-r--r--linux/src/drivers/scsi/NCR53c406a.c1079
-rw-r--r--linux/src/drivers/scsi/NCR53c406a.h83
-rw-r--r--linux/src/drivers/scsi/advansys.c15547
-rw-r--r--linux/src/drivers/scsi/advansys.h174
-rw-r--r--linux/src/drivers/scsi/aha152x.c3277
-rw-r--r--linux/src/drivers/scsi/aha152x.h357
-rw-r--r--linux/src/drivers/scsi/aha1542.c1325
-rw-r--r--linux/src/drivers/scsi/aha1542.h171
-rw-r--r--linux/src/drivers/scsi/aha1740.c614
-rw-r--r--linux/src/drivers/scsi/aha1740.h196
-rw-r--r--linux/src/drivers/scsi/aic7xxx.c11300
-rw-r--r--linux/src/drivers/scsi/aic7xxx.h114
-rw-r--r--linux/src/drivers/scsi/aic7xxx/scsi_message.h41
-rw-r--r--linux/src/drivers/scsi/aic7xxx/sequencer.h135
-rw-r--r--linux/src/drivers/scsi/aic7xxx_proc.c397
-rw-r--r--linux/src/drivers/scsi/aic7xxx_reg.h587
-rw-r--r--linux/src/drivers/scsi/aic7xxx_seq.c769
-rw-r--r--linux/src/drivers/scsi/constants.c683
-rw-r--r--linux/src/drivers/scsi/constants.h6
-rw-r--r--linux/src/drivers/scsi/dc390.h147
-rw-r--r--linux/src/drivers/scsi/dc390w.h145
-rw-r--r--linux/src/drivers/scsi/dtc.c400
-rw-r--r--linux/src/drivers/scsi/dtc.h169
-rw-r--r--linux/src/drivers/scsi/eata.c2329
-rw-r--r--linux/src/drivers/scsi/eata.h60
-rw-r--r--linux/src/drivers/scsi/eata_dma.c1603
-rw-r--r--linux/src/drivers/scsi/eata_dma.h128
-rw-r--r--linux/src/drivers/scsi/eata_dma_proc.c493
-rw-r--r--linux/src/drivers/scsi/eata_dma_proc.h260
-rw-r--r--linux/src/drivers/scsi/eata_generic.h414
-rw-r--r--linux/src/drivers/scsi/eata_pio.c1042
-rw-r--r--linux/src/drivers/scsi/eata_pio.h116
-rw-r--r--linux/src/drivers/scsi/eata_pio_proc.c135
-rw-r--r--linux/src/drivers/scsi/fdomain.c2082
-rw-r--r--linux/src/drivers/scsi/fdomain.h61
-rw-r--r--linux/src/drivers/scsi/g_NCR5380.c733
-rw-r--r--linux/src/drivers/scsi/g_NCR5380.h166
-rw-r--r--linux/src/drivers/scsi/gdth.c3598
-rw-r--r--linux/src/drivers/scsi/gdth.h819
-rw-r--r--linux/src/drivers/scsi/gdth_ioctl.h86
-rw-r--r--linux/src/drivers/scsi/gdth_proc.c656
-rw-r--r--linux/src/drivers/scsi/gdth_proc.h24
-rw-r--r--linux/src/drivers/scsi/hosts.c545
-rw-r--r--linux/src/drivers/scsi/hosts.h405
-rw-r--r--linux/src/drivers/scsi/in2000.c2376
-rw-r--r--linux/src/drivers/scsi/in2000.h460
-rw-r--r--linux/src/drivers/scsi/ncr53c8xx.c10793
-rw-r--r--linux/src/drivers/scsi/ncr53c8xx.h1220
-rw-r--r--linux/src/drivers/scsi/pas16.c576
-rw-r--r--linux/src/drivers/scsi/pas16.h196
-rw-r--r--linux/src/drivers/scsi/ppa.c1550
-rw-r--r--linux/src/drivers/scsi/ppa.h176
-rw-r--r--linux/src/drivers/scsi/qlogicfas.c679
-rw-r--r--linux/src/drivers/scsi/qlogicfas.h43
-rw-r--r--linux/src/drivers/scsi/qlogicisp.c1768
-rw-r--r--linux/src/drivers/scsi/qlogicisp.h99
-rw-r--r--linux/src/drivers/scsi/qlogicisp_asm.c1304
-rw-r--r--linux/src/drivers/scsi/scripts.h1357
-rw-r--r--linux/src/drivers/scsi/scsi.c3585
-rw-r--r--linux/src/drivers/scsi/scsi.h632
-rw-r--r--linux/src/drivers/scsi/scsi_ioctl.c452
-rw-r--r--linux/src/drivers/scsi/scsi_proc.c302
-rw-r--r--linux/src/drivers/scsi/scsicam.c230
-rw-r--r--linux/src/drivers/scsi/scsiio.c1537
-rw-r--r--linux/src/drivers/scsi/scsiiom.c1540
-rw-r--r--linux/src/drivers/scsi/sd.c1635
-rw-r--r--linux/src/drivers/scsi/sd.h65
-rw-r--r--linux/src/drivers/scsi/sd_ioctl.c119
-rw-r--r--linux/src/drivers/scsi/seagate.c1755
-rw-r--r--linux/src/drivers/scsi/seagate.h139
-rw-r--r--linux/src/drivers/scsi/sr.c1281
-rw-r--r--linux/src/drivers/scsi/sr.h40
-rw-r--r--linux/src/drivers/scsi/sr_ioctl.c607
-rw-r--r--linux/src/drivers/scsi/t128.c404
-rw-r--r--linux/src/drivers/scsi/t128.h173
-rw-r--r--linux/src/drivers/scsi/tmscsim.c1930
-rw-r--r--linux/src/drivers/scsi/tmscsim.h680
-rw-r--r--linux/src/drivers/scsi/tmscsiw.c2096
-rw-r--r--linux/src/drivers/scsi/tmscsiw.h1082
-rw-r--r--linux/src/drivers/scsi/u14-34f.c1995
-rw-r--r--linux/src/drivers/scsi/u14-34f.h60
-rw-r--r--linux/src/drivers/scsi/ultrastor.c1165
-rw-r--r--linux/src/drivers/scsi/ultrastor.h102
-rw-r--r--linux/src/drivers/scsi/wd7000.c1452
-rw-r--r--linux/src/drivers/scsi/wd7000.h446
96 files changed, 137575 insertions, 0 deletions
diff --git a/linux/src/drivers/scsi/53c7,8xx.h b/linux/src/drivers/scsi/53c7,8xx.h
new file mode 100644
index 00000000..80fbad3b
--- /dev/null
+++ b/linux/src/drivers/scsi/53c7,8xx.h
@@ -0,0 +1,1584 @@
+/*
+ * NCR 53c{7,8}0x0 driver, header file
+ *
+ * Sponsored by
+ * iX Multiuser Multitasking Magazine
+ * Hannover, Germany
+ * hm@ix.de
+ *
+ * Copyright 1993, 1994, 1995 Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@PoohSticks.ORG
+ * +1 (303) 786-7975
+ *
+ * TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+ *
+ * PRE-ALPHA
+ *
+ * For more information, please consult
+ *
+ * NCR 53C700/53C700-66
+ * SCSI I/O Processor
+ * Data Manual
+ *
+ * NCR 53C810
+ * PCI-SCSI I/O Processor
+ * Data Manual
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * +1 (719) 578-3400
+ *
+ * Toll free literature number
+ * +1 (800) 334-5454
+ *
+ */
+
+#ifndef NCR53c7x0_H
+#define NCR53c7x0_H
+#if !defined(LINUX_1_2) && !defined(LINUX_1_3)
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > 65536 + 3 * 256
+#define LINUX_1_3
+#else
+#define LINUX_1_2
+#endif
+#endif
+
+/*
+ * Prevent name space pollution in hosts.c, and only provide the
+ * define we need to get the NCR53c7x0 driver into the host template
+ * array.
+ */
+
+#if defined(HOSTS_C) || defined(MODULE)
+#include <scsi/scsicam.h>
+
+extern int NCR53c7xx_abort(Scsi_Cmnd *);
+extern int NCR53c7xx_detect(Scsi_Host_Template *tpnt);
+extern int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int NCR53c7xx_reset(Scsi_Cmnd *, unsigned int);
+#ifdef MODULE
+extern int NCR53c7xx_release(struct Scsi_Host *);
+#else
+#define NCR53c7xx_release NULL
+#endif
+
+#ifdef LINUX_1_2
+#define NCR53c7xx {NULL, NULL, "NCR53c{7,8}xx (rel 17)", NCR53c7xx_detect,\
+ NULL, /* info */ NULL, /* command, deprecated */ NULL, \
+ NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset, \
+ NULL /* slave attach */, scsicam_bios_param, /* can queue */ 24, \
+ /* id */ 7, 127 /* old SG_ALL */, /* cmd per lun */ 3, \
+ /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING}
+#else
+#define NCR53c7xx {NULL, NULL, NULL, NULL, \
+ "NCR53c{7,8}xx (rel 17)", NCR53c7xx_detect,\
+ NULL, /* info */ NULL, /* command, deprecated */ NULL, \
+ NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset, \
+ NULL /* slave attach */, scsicam_bios_param, /* can queue */ 24, \
+ /* id */ 7, 127 /* old SG_ALL */, /* cmd per lun */ 3, \
+ /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING}
+#endif
+
+#endif /* defined(HOSTS_C) || defined(MODULE) */
+
+#ifndef HOSTS_C
+#ifdef LINUX_1_2
+/*
+ * Change virtual addresses to physical addresses and vv.
+ * These are trivial on the 1:1 Linux/i386 mapping (but if we ever
+ * make the kernel segment mapped at 0, we need to do translation
+ * on the i386 as well)
+ */
+extern inline unsigned long virt_to_phys(volatile void * address)
+{
+ return (unsigned long) address;
+}
+
+extern inline void * phys_to_virt(unsigned long address)
+{
+ return (void *) address;
+}
+
+/*
+ * IO bus memory addresses are also 1:1 with the physical address
+ */
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
+
+/*
+ * readX/writeX() are used to access memory mapped devices. On some
+ * architectures the memory mapped IO stuff needs to be accessed
+ * differently. On the x86 architecture, we just read/write the
+ * memory location directly.
+ */
+#define readb(addr) (*(volatile unsigned char *) (addr))
+#define readw(addr) (*(volatile unsigned short *) (addr))
+#define readl(addr) (*(volatile unsigned int *) (addr))
+
+#define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b))
+#define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b))
+#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b))
+
+#define mb()
+
+#endif /* def LINUX_1_2 */
+
+/* Register addresses, ordered numerically */
+
+/* SCSI control 0 rw, default = 0xc0 */
+#define SCNTL0_REG 0x00
+#define SCNTL0_ARB1 0x80 /* 0 0 = simple arbitration */
+#define SCNTL0_ARB2 0x40 /* 1 1 = full arbitration */
+#define SCNTL0_STRT 0x20 /* Start Sequence */
+#define SCNTL0_WATN 0x10 /* Select with ATN */
+#define SCNTL0_EPC 0x08 /* Enable parity checking */
+/* Bit 2 is reserved on 800 series chips */
+#define SCNTL0_EPG_700 0x04 /* Enable parity generation */
+#define SCNTL0_AAP 0x02 /* ATN/ on parity error */
+#define SCNTL0_TRG 0x01 /* Target mode */
+
+/* SCSI control 1 rw, default = 0x00 */
+
+#define SCNTL1_REG 0x01
+#define SCNTL1_EXC 0x80 /* Extra Clock Cycle of Data setup */
+#define SCNTL1_ADB 0x40 /* contents of SODL on bus */
+#define SCNTL1_ESR_700 0x20 /* Enable SIOP response to selection
+ and reselection */
+#define SCNTL1_DHP_800 0x20 /* Disable halt on parity error or ATN
+ target mode only */
+#define SCNTL1_CON 0x10 /* Connected */
+#define SCNTL1_RST 0x08 /* SCSI RST/ */
+#define SCNTL1_AESP 0x04 /* Force bad parity */
+#define SCNTL1_SND_700 0x02 /* Start SCSI send */
+#define SCNTL1_IARB_800 0x02 /* Immediate Arbitration, start
+ arbitration immediately after
+ busfree is detected */
+#define SCNTL1_RCV_700 0x01 /* Start SCSI receive */
+#define SCNTL1_SST_800 0x01 /* Start SCSI transfer */
+
+/* SCSI control 2 rw, */
+
+#define SCNTL2_REG_800 0x02
+#define SCNTL2_800_SDU 0x80 /* SCSI disconnect unexpected */
+
+/* SCSI control 3 rw */
+
+#define SCNTL3_REG_800 0x03
+#define SCNTL3_800_SCF_SHIFT 4
+#define SCNTL3_800_SCF_MASK 0x70
+#define SCNTL3_800_SCF2 0x40 /* Synchronous divisor */
+#define SCNTL3_800_SCF1 0x20 /* 0x00 = SCLK/3 */
+#define SCNTL3_800_SCF0 0x10 /* 0x10 = SCLK/1 */
+ /* 0x20 = SCLK/1.5
+ 0x30 = SCLK/2
+ 0x40 = SCLK/3 */
+
+#define SCNTL3_800_CCF_SHIFT 0
+#define SCNTL3_800_CCF_MASK 0x07
+#define SCNTL3_800_CCF2 0x04 /* 0x00 50.01 to 66 */
+#define SCNTL3_800_CCF1 0x02 /* 0x01 16.67 to 25 */
+#define SCNTL3_800_CCF0 0x01 /* 0x02 25.01 - 37.5
+ 0x03 37.51 - 50
+ 0x04 50.01 - 66 */
+
+/*
+ * SCSI destination ID rw - the appropriate bit is set for the selected
+ * target ID. This is written by the SCSI SCRIPTS processor.
+ * default = 0x00
+ */
+#define SDID_REG_700 0x02
+#define SDID_REG_800 0x06
+
+#define GP_REG_800 0x07 /* General purpose IO */
+#define GP_800_IO1 0x02
+#define GP_800_IO2 0x01
+
+
+/* SCSI interrupt enable rw, default = 0x00 */
+#define SIEN_REG_700 0x03
+#define SIEN0_REG_800 0x40
+#define SIEN_MA 0x80 /* Phase mismatch (ini) or ATN (tgt) */
+#define SIEN_FC 0x40 /* Function complete */
+#define SIEN_700_STO 0x20 /* Selection or reselection timeout */
+#define SIEN_800_SEL 0x20 /* Selected */
+#define SIEN_700_SEL 0x10 /* Selected or reselected */
+#define SIEN_800_RESEL 0x10 /* Reselected */
+#define SIEN_SGE 0x08 /* SCSI gross error */
+#define SIEN_UDC 0x04 /* Unexpected disconnect */
+#define SIEN_RST 0x02 /* SCSI RST/ received */
+#define SIEN_PAR 0x01 /* Parity error */
+
+/*
+ * SCSI chip ID rw
+ * NCR53c700 :
+ * When arbitrating, the highest bit is used, when reselection or selection
+ * occurs, the chip responds to all IDs for which a bit is set.
+ * default = 0x00
+ * NCR53c810 :
+ * Uses bit mapping
+ */
+#define SCID_REG 0x04
+/* Bit 7 is reserved on 800 series chips */
+#define SCID_800_RRE 0x40 /* Enable response to reselection */
+#define SCID_800_SRE 0x20 /* Enable response to selection */
+/* Bits four and three are reserved on 800 series chips */
+#define SCID_800_ENC_MASK 0x07 /* Encoded SCSI ID */
+
+/* SCSI transfer rw, default = 0x00 */
+#define SXFER_REG 0x05
+#define SXFER_DHP 0x80 /* Disable halt on parity */
+
+#define SXFER_TP2 0x40 /* Transfer period msb */
+#define SXFER_TP1 0x20
+#define SXFER_TP0 0x10 /* lsb */
+#define SXFER_TP_MASK 0x70
+/* FIXME : SXFER_TP_SHIFT == 5 is right for '8xx chips */
+#define SXFER_TP_SHIFT 5
+#define SXFER_TP_4 0x00 /* Divisors */
+#define SXFER_TP_5 0x10<<1
+#define SXFER_TP_6 0x20<<1
+#define SXFER_TP_7 0x30<<1
+#define SXFER_TP_8 0x40<<1
+#define SXFER_TP_9 0x50<<1
+#define SXFER_TP_10 0x60<<1
+#define SXFER_TP_11 0x70<<1
+
+#define SXFER_MO3 0x08 /* Max offset msb */
+#define SXFER_MO2 0x04
+#define SXFER_MO1 0x02
+#define SXFER_MO0 0x01 /* lsb */
+#define SXFER_MO_MASK 0x0f
+#define SXFER_MO_SHIFT 0
+
+/*
+ * SCSI output data latch rw
+ * The contents of this register are driven onto the SCSI bus when
+ * the Assert Data Bus bit of the SCNTL1 register is set and
+ * the CD, IO, and MSG bits of the SOCL register match the SCSI phase
+ */
+#define SODL_REG_700 0x06
+#define SODL_REG_800 0x54
+
+
+/*
+ * SCSI output control latch rw, default = 0
+ * Note that when the chip is being manually programmed as an initiator,
+ * the MSG, CD, and IO bits must be set correctly for the phase the target
+ * is driving the bus in. Otherwise no data transfer will occur due to
+ * phase mismatch.
+ */
+
+#define SBCL_REG 0x0b
+#define SBCL_REQ 0x80 /* REQ */
+#define SBCL_ACK 0x40 /* ACK */
+#define SBCL_BSY 0x20 /* BSY */
+#define SBCL_SEL 0x10 /* SEL */
+#define SBCL_ATN 0x08 /* ATN */
+#define SBCL_MSG 0x04 /* MSG */
+#define SBCL_CD 0x02 /* C/D */
+#define SBCL_IO 0x01 /* I/O */
+#define SBCL_PHASE_CMDOUT SBCL_CD
+#define SBCL_PHASE_DATAIN SBCL_IO
+#define SBCL_PHASE_DATAOUT 0
+#define SBCL_PHASE_MSGIN (SBCL_CD|SBCL_IO|SBCL_MSG)
+#define SBCL_PHASE_MSGOUT (SBCL_CD|SBCL_MSG)
+#define SBCL_PHASE_STATIN (SBCL_CD|SBCL_IO)
+#define SBCL_PHASE_MASK (SBCL_CD|SBCL_IO|SBCL_MSG)
+
+/*
+ * SCSI first byte received latch ro
+ * This register contains the first byte received during a block MOVE
+ * SCSI SCRIPTS instruction, including
+ *
+ * Initiator mode Target mode
+ * Message in Command
+ * Status Message out
+ * Data in Data out
+ *
+ * It also contains the selecting or reselecting device's ID and our
+ * ID.
+ *
+ * Note that this is the register the various IF conditionals can
+ * operate on.
+ */
+#define SFBR_REG 0x08
+
+/*
+ * SCSI input data latch ro
+ * In initiator mode, data is latched into this register on the rising
+ * edge of REQ/. In target mode, data is latched on the rising edge of
+ * ACK/
+ */
+#define SIDL_REG_700 0x09
+#define SIDL_REG_800 0x50
+
+/*
+ * SCSI bus data lines ro
+ * This register reflects the instantaneous status of the SCSI data
+ * lines. Note that SCNTL0 must be set to disable parity checking,
+ * otherwise reading this register will latch new parity.
+ */
+#define SBDL_REG_700 0x0a
+#define SBDL_REG_800 0x58
+
+#define SSID_REG_800 0x0a
+#define SSID_800_VAL 0x80 /* Exactly two bits asserted at sel */
+#define SSID_800_ENCID_MASK 0x07 /* Device which performed operation */
+
+
+/*
+ * SCSI bus control lines rw,
+ * instantaneous readout of control lines
+ */
+#define SOCL_REG 0x0b
+#define SOCL_REQ 0x80 /* REQ ro */
+#define SOCL_ACK 0x40 /* ACK ro */
+#define SOCL_BSY 0x20 /* BSY ro */
+#define SOCL_SEL 0x10 /* SEL ro */
+#define SOCL_ATN 0x08 /* ATN ro */
+#define SOCL_MSG 0x04 /* MSG ro */
+#define SOCL_CD 0x02 /* C/D ro */
+#define SOCL_IO 0x01 /* I/O ro */
+/*
+ * Synchronous SCSI Clock Control bits
+ * 0 - set by DCNTL
+ * 1 - SCLK / 1.0
+ * 2 - SCLK / 1.5
+ * 3 - SCLK / 2.0
+ */
+#define SBCL_SSCF1 0x02 /* wo, -66 only */
+#define SBCL_SSCF0 0x01 /* wo, -66 only */
+#define SBCL_SSCF_MASK 0x03
+
+/*
+ * XXX note : when reading the DSTAT and STAT registers to clear interrupts,
+ * insure that 10 clocks elapse between the two
+ */
+/* DMA status ro */
+#define DSTAT_REG 0x0c
+#define DSTAT_DFE 0x80 /* DMA FIFO empty */
+#define DSTAT_800_MDPE 0x40 /* Master Data Parity Error */
+#define DSTAT_800_BF 0x20 /* Bus Fault */
+#define DSTAT_ABRT 0x10 /* Aborted - set on error */
+#define DSTAT_SSI 0x08 /* SCRIPTS single step interrupt */
+#define DSTAT_SIR 0x04 /* SCRIPTS interrupt received -
+ set when INT instruction is
+ executed */
+#define DSTAT_WTD 0x02 /* Watchdog timeout detected */
+#define DSTAT_OPC 0x01 /* Illegal instruction */
+#define DSTAT_800_IID 0x01 /* Same thing, different name */
+
+
+/* NCR53c800 moves this stuff into SIST0 */
+#define SSTAT0_REG 0x0d /* SCSI status 0 ro */
+#define SIST0_REG_800 0x42
+#define SSTAT0_MA 0x80 /* ini : phase mismatch,
+ * tgt : ATN/ asserted
+ */
+#define SSTAT0_CMP 0x40 /* function complete */
+#define SSTAT0_700_STO 0x20 /* Selection or reselection timeout */
+#define SIST0_800_SEL 0x20 /* Selected */
+#define SSTAT0_700_SEL 0x10 /* Selected or reselected */
+#define SIST0_800_RSL 0x10 /* Reselected */
+#define SSTAT0_SGE 0x08 /* SCSI gross error */
+#define SSTAT0_UDC 0x04 /* Unexpected disconnect */
+#define SSTAT0_RST 0x02 /* SCSI RST/ received */
+#define SSTAT0_PAR 0x01 /* Parity error */
+
+/* And uses SSTAT0 for what was SSTAT1 */
+
+#define SSTAT1_REG 0x0e /* SCSI status 1 ro */
+#define SSTAT1_ILF 0x80 /* SIDL full */
+#define SSTAT1_ORF 0x40 /* SODR full */
+#define SSTAT1_OLF 0x20 /* SODL full */
+#define SSTAT1_AIP 0x10 /* Arbitration in progress */
+#define SSTAT1_LOA 0x08 /* Lost arbitration */
+#define SSTAT1_WOA 0x04 /* Won arbitration */
+#define SSTAT1_RST 0x02 /* Instant readout of RST/ */
+#define SSTAT1_SDP 0x01 /* Instant readout of SDP/ */
+
+#define SSTAT2_REG 0x0f /* SCSI status 2 ro */
+#define SSTAT2_FF3 0x80 /* number of bytes in synchronous */
+#define SSTAT2_FF2 0x40 /* data FIFO */
+#define SSTAT2_FF1 0x20
+#define SSTAT2_FF0 0x10
+#define SSTAT2_FF_MASK 0xf0
+#define SSTAT2_FF_SHIFT 4
+
+/*
+ * Latched signals, latched on the leading edge of REQ/ for initiators,
+ * ACK/ for targets.
+ */
+#define SSTAT2_SDP 0x08 /* SDP */
+#define SSTAT2_MSG 0x04 /* MSG */
+#define SSTAT2_CD 0x02 /* C/D */
+#define SSTAT2_IO 0x01 /* I/O */
+#define SSTAT2_PHASE_CMDOUT SSTAT2_CD
+#define SSTAT2_PHASE_DATAIN SSTAT2_IO
+#define SSTAT2_PHASE_DATAOUT 0
+#define SSTAT2_PHASE_MSGIN (SSTAT2_CD|SSTAT2_IO|SSTAT2_MSG)
+#define SSTAT2_PHASE_MSGOUT (SSTAT2_CD|SSTAT2_MSG)
+#define SSTAT2_PHASE_STATIN (SSTAT2_CD|SSTAT2_IO)
+#define SSTAT2_PHASE_MASK (SSTAT2_CD|SSTAT2_IO|SSTAT2_MSG)
+
+
+/* NCR53c700-66 only */
+#define SCRATCHA_REG_00 0x10 /* through 0x13 Scratch A rw */
+/* NCR53c710 and higher */
+#define DSA_REG 0x10 /* DATA structure address */
+
+#define CTEST0_REG_700 0x14 /* Chip test 0 ro */
+#define CTEST0_REG_800 0x18 /* Chip test 0 rw, general purpose */
+/* 0x80 - 0x04 are reserved */
+#define CTEST0_700_RTRG 0x02 /* Real target mode */
+#define CTEST0_700_DDIR 0x01 /* Data direction, 1 =
+ * SCSI bus to host, 0 =
+ * host to SCSI.
+ */
+
+#define CTEST1_REG_700 0x15 /* Chip test 1 ro */
+#define CTEST1_REG_800 0x19 /* Chip test 1 ro */
+#define CTEST1_FMT3 0x80 /* Identify which byte lanes are empty */
+#define CTEST1_FMT2 0x40 /* in the DMA FIFO */
+#define CTEST1_FMT1 0x20
+#define CTEST1_FMT0 0x10
+
+#define CTEST1_FFL3 0x08 /* Identify which bytes lanes are full */
+#define CTEST1_FFL2 0x04 /* in the DMA FIFO */
+#define CTEST1_FFL1 0x02
+#define CTEST1_FFL0 0x01
+
+#define CTEST2_REG_700 0x16 /* Chip test 2 ro */
+#define CTEST2_REG_800 0x1a /* Chip test 2 ro */
+
+#define CTEST2_800_DDIR 0x80 /* 1 = SCSI->host */
+#define CTEST2_800_SIGP 0x40 /* A copy of SIGP in ISTAT.
+ Reading this register clears */
+#define CTEST2_800_CIO 0x20 /* Configured as IO */.
+#define CTEST2_800_CM 0x10 /* Configured as memory */
+
+/* 0x80 - 0x40 are reserved on 700 series chips */
+#define CTEST2_700_SOFF 0x20 /* SCSI Offset Compare,
+ * As an initiator, this bit is
+ * one when the synchronous offset
+ * is zero, as a target this bit
+ * is one when the synchronous
+ * offset is at the maximum
+ * defined in SXFER
+ */
+#define CTEST2_700_SFP 0x10 /* SCSI FIFO parity bit,
+ * reading CTEST3 unloads a byte
+ * from the FIFO and sets this
+ */
+#define CTEST2_700_DFP 0x08 /* DMA FIFO parity bit,
+ * reading CTEST6 unloads a byte
+ * from the FIFO and sets this
+ */
+#define CTEST2_TEOP 0x04 /* SCSI true end of process,
+ * indicates a totally finished
+ * transfer
+ */
+#define CTEST2_DREQ 0x02 /* Data request signal */
+/* 0x01 is reserved on 700 series chips */
+#define CTEST2_800_DACK 0x01
+
+/*
+ * Chip test 3 ro
+ * Unloads the bottom byte of the eight deep SCSI synchronous FIFO,
+ * check SSTAT2 FIFO full bits to determine size. Note that a GROSS
+ * error results if a read is attempted on this register. Also note
+ * that 16 and 32 bit reads of this register will cause corruption.
+ */
+#define CTEST3_REG_700 0x17
+/* Chip test 3 rw */
+#define CTEST3_REG_800 0x1b
+#define CTEST3_800_V3 0x80 /* Chip revision */
+#define CTEST3_800_V2 0x40
+#define CTEST3_800_V1 0x20
+#define CTEST3_800_V0 0x10
+#define CTEST3_800_FLF 0x08 /* Flush DMA FIFO */
+#define CTEST3_800_CLF 0x04 /* Clear DMA FIFO */
+#define CTEST3_800_FM 0x02 /* Fetch mode pin */
+/* bit 0 is reserved on 800 series chips */
+
+#define CTEST4_REG_700 0x18 /* Chip test 4 rw */
+#define CTEST4_REG_800 0x21 /* Chip test 4 rw */
+/* 0x80 is reserved on 700 series chips */
+#define CTEST4_800_BDIS 0x80 /* Burst mode disable */
+#define CTEST4_ZMOD 0x40 /* High impedance mode */
+#define CTEST4_SZM 0x20 /* SCSI bus high impedance */
+#define CTEST4_700_SLBE 0x10 /* SCSI loopback enabled */
+#define CTEST4_800_SRTM 0x10 /* Shadow Register Test Mode */
+#define CTEST4_700_SFWR 0x08 /* SCSI FIFO write enable,
+ * redirects writes from SODL
+ * to the SCSI FIFO.
+ */
+#define CTEST4_800_MPEE 0x08 /* Enable parity checking
+ during master cycles on PCI
+ bus */
+
+/*
+ * These bits send the contents of the CTEST6 register to the appropriate
+ * byte lane of the 32 bit DMA FIFO. Normal operation is zero, otherwise
+ * the high bit means the low two bits select the byte lane.
+ */
+#define CTEST4_FBL2 0x04
+#define CTEST4_FBL1 0x02
+#define CTEST4_FBL0 0x01
+#define CTEST4_FBL_MASK 0x07
+#define CTEST4_FBL_0 0x04 /* Select DMA FIFO byte lane 0 */
+#define CTEST4_FBL_1 0x05 /* Select DMA FIFO byte lane 1 */
+#define CTEST4_FBL_2 0x06 /* Select DMA FIFO byte lane 2 */
+#define CTEST4_FBL_3 0x07 /* Select DMA FIFO byte lane 3 */
+#define CTEST4_800_SAVE (CTEST4_800_BDIS)
+
+
+#define CTEST5_REG_700 0x19 /* Chip test 5 rw */
+#define CTEST5_REG_800 0x22 /* Chip test 5 rw */
+/*
+ * Clock Address Incrementor. When set, it increments the
+ * DNAD register to the next bus size boundary. It automatically
+ * resets itself when the operation is complete.
+ */
+#define CTEST5_ADCK 0x80
+/*
+ * Clock Byte Counter. When set, it decrements the DBC register to
+ * the next bus size boundary.
+ */
+#define CTEST5_BBCK 0x40
+/*
+ * Reset SCSI Offset. Setting this bit to 1 clears the current offset
+ * pointer in the SCSI synchronous offset counter (SSTAT). This bit
+ * is set to 1 if a SCSI Gross Error Condition occurs. The offset should
+ * be cleared when a synchronous transfer fails. When written, it is
+ * automatically cleared after the SCSI synchronous offset counter is
+ * reset.
+ */
+/* Bit 5 is reserved on 800 series chips */
+#define CTEST5_700_ROFF 0x20
+/*
+ * Master Control for Set or Reset pulses. When 1, causes the low
+ * four bits of register to set when set, 0 causes the low bits to
+ * clear when set.
+ */
+#define CTEST5_MASR 0x10
+#define CTEST5_DDIR 0x08 /* DMA direction */
+/*
+ * Bits 2-0 are reserved on 800 series chips
+ */
+#define CTEST5_700_EOP 0x04 /* End of process */
+#define CTEST5_700_DREQ 0x02 /* Data request */
+#define CTEST5_700_DACK 0x01 /* Data acknowledge */
+
+/*
+ * Chip test 6 rw - writing to this register writes to the byte
+ * lane in the DMA FIFO as determined by the FBL bits in the CTEST4
+ * register.
+ */
+#define CTEST6_REG_700 0x1a
+#define CTEST6_REG_800 0x23
+
+#define CTEST7_REG 0x1b /* Chip test 7 rw */
+/* 0x80 - 0x40 are reserved on NCR53c700 and NCR53c700-66 chips */
+#define CTEST7_10_CDIS 0x80 /* Cache burst disable */
+#define CTEST7_10_SC1 0x40 /* Snoop control bits */
+#define CTEST7_10_SC0 0x20
+#define CTEST7_10_SC_MASK 0x60
+/* 0x20 is reserved on the NCR53c700 */
+#define CTEST7_0060_FM 0x20 /* Fetch mode */
+#define CTEST7_STD 0x10 /* Selection timeout disable */
+#define CTEST7_DFP 0x08 /* DMA FIFO parity bit for CTEST6 */
+#define CTEST7_EVP 0x04 /* 1 = host bus even parity, 0 = odd */
+#define CTEST7_10_TT1 0x02 /* Transfer type */
+#define CTEST7_00_DC 0x02 /* Set to drive DC low during instruction
+ fetch */
+#define CTEST7_DIFF 0x01 /* Differential mode */
+
+#define CTEST7_SAVE ( CTEST7_EVP | CTEST7_DIFF )
+
+
+#define TEMP_REG 0x1c /* through 0x1f Temporary stack rw */
+
+#define DFIFO_REG 0x20 /* DMA FIFO rw */
+/*
+ * 0x80 is reserved on the NCR53c710, the CLF and FLF bits have been
+ * moved into the CTEST8 register.
+ */
+#define DFIFO_00_FLF 0x80 /* Flush DMA FIFO to memory */
+#define DFIFO_00_CLF 0x40 /* Clear DMA and SCSI FIFOs */
+#define DFIFO_BO6 0x40
+#define DFIFO_BO5 0x20
+#define DFIFO_BO4 0x10
+#define DFIFO_BO3 0x08
+#define DFIFO_BO2 0x04
+#define DFIFO_BO1 0x02
+#define DFIFO_BO0 0x01
+#define DFIFO_10_BO_MASK 0x7f /* 7 bit counter */
+#define DFIFO_00_BO_MASK 0x3f /* 6 bit counter */
+
+/*
+ * Interrupt status rw
+ * Note that this is the only register which can be read while SCSI
+ * SCRIPTS are being executed.
+ */
+#define ISTAT_REG_700 0x21
+#define ISTAT_REG_800 0x14
+#define ISTAT_ABRT 0x80 /* Software abort, write
+ *1 to abort, wait for interrupt. */
+/* 0x40 and 0x20 are reserved on NCR53c700 and NCR53c700-66 chips */
+#define ISTAT_10_SRST 0x40 /* software reset */
+#define ISTAT_10_SIGP 0x20 /* signal script */
+/* 0x10 is reserved on NCR53c700 series chips */
+#define ISTAT_800_SEM 0x10 /* semaphore */
+#define ISTAT_CON 0x08 /* 1 when connected */
+#define ISTAT_800_INTF 0x04 /* Interrupt on the fly */
+#define ISTAT_700_PRE 0x04 /* Pointer register empty.
+ * Set to 1 when DSPS and DSP
+ * registers are empty in pipeline
+ * mode, always set otherwise.
+ */
+#define ISTAT_SIP 0x02 /* SCSI interrupt pending from
+ * SCSI portion of SIOP see
+ * SSTAT0
+ */
+#define ISTAT_DIP 0x01 /* DMA interrupt pending
+ * see DSTAT
+ */
+
+/* NCR53c700-66 and NCR53c710 only */
+#define CTEST8_REG 0x22 /* Chip test 8 rw */
+#define CTEST8_0066_EAS 0x80 /* Enable alternate SCSI clock,
+ * ie read from SCLK/ rather than CLK/
+ */
+#define CTEST8_0066_EFM 0x40 /* Enable fetch and master outputs */
+#define CTEST8_0066_GRP 0x20 /* Generate Receive Parity for
+ * pass through. This insures that
+ * bad parity won't reach the host
+ * bus.
+ */
+#define CTEST8_0066_TE 0x10 /* TolerANT enable. Enable
+ * active negation, should only
+ * be used for slow SCSI
+ * non-differential.
+ */
+#define CTEST8_0066_HSC 0x08 /* Halt SCSI clock */
+#define CTEST8_0066_SRA 0x04 /* Shorten REQ/ACK filtering,
+ * must be set for fast SCSI-II
+ * speeds.
+ */
+#define CTEST8_0066_DAS 0x02 /* Disable automatic target/initiator
+ * switching.
+ */
+#define CTEST8_0066_LDE 0x01 /* Last disconnect enable.
+ * The status of pending
+ * disconnect is maintained by
+ * the core, eliminating
+ * the possibility of missing a
+ * selection or reselection
+ * while waiting to fetch a
+ * WAIT DISCONNECT opcode.
+ */
+
+#define CTEST8_10_V3 0x80 /* Chip revision */
+#define CTEST8_10_V2 0x40
+#define CTEST8_10_V1 0x20
+#define CTEST8_10_V0 0x10
+#define CTEST8_10_V_MASK 0xf0
+#define CTEST8_10_FLF 0x08 /* Flush FIFOs */
+#define CTEST8_10_CLF 0x04 /* Clear FIFOs */
+#define CTEST8_10_FM 0x02 /* Fetch pin mode */
+#define CTEST8_10_SM 0x01 /* Snoop pin mode */
+
+
+/*
+ * The CTEST9 register may be used to differentiate between a
+ * NCR53c700 and a NCR53c710.
+ *
+ * Write 0xff to this register.
+ * Read it.
+ * If the contents are 0xff, it is a NCR53c700
+ * If the contents are 0x00, it is a NCR53c700-66 first revision
+ * If the contents are some other value, it is some other NCR53c700-66
+ */
+#define CTEST9_REG_00 0x23 /* Chip test 9 ro */
+#define LCRC_REG_10 0x23
+
+/*
+ * 0x24 through 0x27 are the DMA byte counter register. Instructions
+ * write their high 8 bits into the DCMD register, the low 24 bits into
+ * the DBC register.
+ *
+ * Function is dependent on the command type being executed.
+ */
+
+
+#define DBC_REG 0x24
+/*
+ * For Block Move Instructions, DBC is a 24 bit quantity representing
+ * the number of bytes to transfer.
+ * For Transfer Control Instructions, DBC is bit fielded as follows :
+ */
+/* Bits 20 - 23 should be clear */
+#define DBC_TCI_TRUE (1 << 19) /* Jump when true */
+#define DBC_TCI_COMPARE_DATA (1 << 18) /* Compare data */
+#define DBC_TCI_COMPARE_PHASE (1 << 17) /* Compare phase with DCMD field */
+#define DBC_TCI_WAIT_FOR_VALID (1 << 16) /* Wait for REQ */
+/* Bits 8 - 15 are reserved on some implementations ? */
+#define DBC_TCI_MASK_MASK 0xff00 /* Mask for data compare */
+#define DBC_TCI_MASK_SHIFT 8
+#define DBC_TCI_DATA_MASK 0xff /* Data to be compared */
+#define DBC_TCI_DATA_SHIFT 0
+
+#define DBC_RWRI_IMMEDIATE_MASK 0xff00 /* Immediate data */
+#define DBC_RWRI_IMMEDIATE_SHIFT 8 /* Amount to shift */
+#define DBC_RWRI_ADDRESS_MASK 0x3f0000 /* Register address */
+#define DBC_RWRI_ADDRESS_SHIFT 16
+
+
+/*
+ * DMA command r/w
+ */
+#define DCMD_REG 0x27
+#define DCMD_TYPE_MASK 0xc0 /* Masks off type */
+#define DCMD_TYPE_BMI 0x00 /* Indicates a Block Move instruction */
+#define DCMD_BMI_IO 0x01 /* I/O, CD, and MSG bits selecting */
+#define DCMD_BMI_CD 0x02 /* the phase for the block MOVE */
+#define DCMD_BMI_MSG 0x04 /* instruction */
+
+#define DCMD_BMI_OP_MASK 0x18 /* mask for opcode */
+#define DCMD_BMI_OP_MOVE_T 0x00 /* MOVE */
+#define DCMD_BMI_OP_MOVE_I 0x08 /* MOVE Initiator */
+
+#define DCMD_BMI_INDIRECT 0x20 /* Indirect addressing */
+
+#define DCMD_TYPE_TCI 0x80 /* Indicates a Transfer Control
+ instruction */
+#define DCMD_TCI_IO 0x01 /* I/O, CD, and MSG bits selecting */
+#define DCMD_TCI_CD 0x02 /* the phase for the block MOVE */
+#define DCMD_TCI_MSG 0x04 /* instruction */
+#define DCMD_TCI_OP_MASK 0x38 /* mask for opcode */
+#define DCMD_TCI_OP_JUMP 0x00 /* JUMP */
+#define DCMD_TCI_OP_CALL 0x08 /* CALL */
+#define DCMD_TCI_OP_RETURN 0x10 /* RETURN */
+#define DCMD_TCI_OP_INT 0x18 /* INT */
+
+#define DCMD_TYPE_RWRI 0x40 /* Indicates I/O or register Read/Write
+ instruction */
+#define DCMD_RWRI_OPC_MASK 0x38 /* Opcode mask */
+#define DCMD_RWRI_OPC_WRITE 0x28 /* Write SFBR to register */
+#define DCMD_RWRI_OPC_READ 0x30 /* Read register to SFBR */
+#define DCMD_RWRI_OPC_MODIFY 0x38 /* Modify in place */
+
+#define DCMD_RWRI_OP_MASK 0x07
+#define DCMD_RWRI_OP_MOVE 0x00
+#define DCMD_RWRI_OP_SHL 0x01
+#define DCMD_RWRI_OP_OR 0x02
+#define DCMD_RWRI_OP_XOR 0x03
+#define DCMD_RWRI_OP_AND 0x04
+#define DCMD_RWRI_OP_SHR 0x05
+#define DCMD_RWRI_OP_ADD 0x06
+#define DCMD_RWRI_OP_ADDC 0x07
+
+#define DCMD_TYPE_MMI 0xc0 /* Indicates a Memory Move instruction
+ (three words) */
+
+
+#define DNAD_REG 0x28 /* through 0x2b DMA next address for
+ data */
+#define DSP_REG 0x2c /* through 0x2f DMA SCRIPTS pointer rw */
+#define DSPS_REG 0x30 /* through 0x33 DMA SCRIPTS pointer
+ save rw */
+#define DMODE_REG_00 0x34 /* DMA mode rw */
+#define DMODE_00_BL1 0x80 /* Burst length bits */
+#define DMODE_00_BL0 0x40
+#define DMODE_BL_MASK 0xc0
+/* Burst lengths (800) */
+#define DMODE_BL_2 0x00 /* 2 transfer */
+#define DMODE_BL_4 0x40 /* 4 transfers */
+#define DMODE_BL_8 0x80 /* 8 transfers */
+#define DMODE_BL_16 0xc0 /* 16 transfers */
+
+#define DMODE_700_BW16 0x20 /* Host buswidth = 16 */
+#define DMODE_700_286 0x10 /* 286 mode */
+#define DMODE_700_IOM 0x08 /* Transfer to IO port */
+#define DMODE_700_FAM 0x04 /* Fixed address mode */
+#define DMODE_700_PIPE 0x02 /* Pipeline mode disables
+ * automatic fetch / exec
+ */
+#define DMODE_MAN 0x01 /* Manual start mode,
+ * requires a 1 to be written
+ * to the start DMA bit in the DCNTL
+ * register to run scripts
+ */
+
+#define DMODE_700_SAVE ( DMODE_00_BL_MASK | DMODE_00_BW16 | DMODE_00_286 )
+
+/* NCR53c800 series only */
+#define SCRATCHA_REG_800 0x34 /* through 0x37 Scratch A rw */
+/* NCR53c710 only */
+#define SCRATCB_REG_10 0x34 /* through 0x37 scratch B rw */
+
+#define DMODE_REG_10 0x38 /* DMA mode rw, NCR53c710 and newer */
+#define DMODE_800_SIOM 0x20 /* Source IO = 1 */
+#define DMODE_800_DIOM 0x10 /* Destination IO = 1 */
+#define DMODE_800_ERL 0x08 /* Enable Read Line */
+
+/* 35-38 are reserved on 700 and 700-66 series chips */
+#define DIEN_REG 0x39 /* DMA interrupt enable rw */
+/* 0x80, 0x40, and 0x20 are reserved on 700-series chips */
+#define DIEN_800_MDPE 0x40 /* Master data parity error */
+#define DIEN_800_BF 0x20 /* BUS fault */
+#define DIEN_ABRT 0x10 /* Enable aborted interrupt */
+#define DIEN_SSI 0x08 /* Enable single step interrupt */
+#define DIEN_SIR 0x04 /* Enable SCRIPTS INT command
+ * interrupt
+ */
+/* 0x02 is reserved on 800 series chips */
+#define DIEN_700_WTD 0x02 /* Enable watchdog timeout interrupt */
+#define DIEN_700_OPC 0x01 /* Enable illegal instruction
+ * interrupt
+ */
+#define DIEN_800_IID 0x01 /* Same meaning, different name */
+
+/*
+ * DMA watchdog timer rw
+ * set in 16 CLK input periods.
+ */
+#define DWT_REG 0x3a
+
+/* DMA control rw */
+#define DCNTL_REG 0x3b
+#define DCNTL_700_CF1 0x80 /* Clock divisor bits */
+#define DCNTL_700_CF0 0x40
+#define DCNTL_700_CF_MASK 0xc0
+/* Clock divisors Divisor SCLK range (MHZ) */
+#define DCNTL_700_CF_2 0x00 /* 2.0 37.51-50.00 */
+#define DCNTL_700_CF_1_5 0x40 /* 1.5 25.01-37.50 */
+#define DCNTL_700_CF_1 0x80 /* 1.0 16.67-25.00 */
+#define DCNTL_700_CF_3 0xc0 /* 3.0 50.01-66.67 (53c700-66) */
+
+#define DCNTL_700_S16 0x20 /* Load scripts 16 bits at a time */
+#define DCNTL_SSM 0x10 /* Single step mode */
+#define DCNTL_700_LLM 0x08 /* Low level mode, can only be set
+ * after selection */
+#define DCNTL_800_IRQM 0x08 /* Totem pole IRQ pin */
+#define DCNTL_STD 0x04 /* Start DMA / SCRIPTS */
+/* 0x02 is reserved */
+#define DCNTL_00_RST 0x01 /* Software reset, resets everything
+ * but 286 mode bit in DMODE. On the
+ * NCR53c710, this bit moved to CTEST8
+ */
+#define DCNTL_10_COM 0x01 /* 700 software compatibility mode */
+
+#define DCNTL_700_SAVE ( DCNTL_CF_MASK | DCNTL_S16)
+
+
+/* NCR53c700-66 only */
+#define SCRATCHB_REG_00 0x3c /* through 0x3f scratch b rw */
+#define SCRATCHB_REG_800 0x5c /* through 0x5f scratch b rw */
+/* NCR53c710 only */
+#define ADDER_REG_10 0x3c /* Adder, NCR53c710 only */
+
+#define SIEN1_REG_800 0x41
+#define SIEN1_800_STO 0x04 /* selection/reselection timeout */
+#define SIEN1_800_GEN 0x02 /* general purpose timer */
+#define SIEN1_800_HTH 0x01 /* handshake to handshake */
+
+#define SIST1_REG_800 0x43
+#define SIST1_800_STO 0x04 /* selection/reselection timeout */
+#define SIST1_800_GEN 0x02 /* general purpose timer */
+#define SIST1_800_HTH 0x01 /* handshake to handshake */
+
+#define SLPAR_REG_800 0x44 /* Parity */
+
+#define MACNTL_REG_800 0x46 /* Memory access control */
+#define MACNTL_800_TYP3 0x80
+#define MACNTL_800_TYP2 0x40
+#define MACNTL_800_TYP1 0x20
+#define MACNTL_800_TYP0 0x10
+#define MACNTL_800_DWR 0x08
+#define MACNTL_800_DRD 0x04
+#define MACNTL_800_PSCPT 0x02
+#define MACNTL_800_SCPTS 0x01
+
+#define GPCNTL_REG_800 0x47 /* General Purpose Pin Control */
+
+/* Timeouts are expressed such that 0=off, 1=100us, doubling after that */
+#define STIME0_REG_800 0x48 /* SCSI Timer Register 0 */
+#define STIME0_800_HTH_MASK 0xf0 /* Handshake to Handshake timeout */
+#define STIME0_800_HTH_SHIFT 4
+#define STIME0_800_SEL_MASK 0x0f /* Selection timeout */
+#define STIME0_800_SEL_SHIFT 0
+
+#define STIME1_REG_800 0x49
+#define STIME1_800_GEN_MASK 0x0f /* General purpose timer */
+
+#define RESPID_REG_800 0x4a /* Response ID, bit fielded. 8
+ bits on narrow chips, 16 on WIDE */
+
+#define STEST0_REG_800 0x4c
+#define STEST0_800_SLT 0x08 /* Selection response logic test */
+#define STEST0_800_ART 0x04 /* Arbitration priority encoder test */
+#define STEST0_800_SOZ 0x02 /* Synchronous offset zero */
+#define STEST0_800_SOM 0x01 /* Synchronous offset maximum */
+
+#define STEST1_REG_800 0x4d
+#define STEST1_800_SCLK 0x80 /* Disable SCSI clock */
+
+#define STEST2_REG_800 0x4e
+#define STEST2_800_SCE 0x80 /* Enable SOCL/SODL */
+#define STEST2_800_ROF 0x40 /* Reset SCSI sync offset */
+#define STEST2_800_SLB 0x10 /* Enable SCSI loopback mode */
+#define STEST2_800_SZM 0x08 /* SCSI high impedance mode */
+#define STEST2_800_EXT 0x02 /* Extend REQ/ACK filter 30 to 60ns */
+#define STEST2_800_LOW 0x01 /* SCSI low level mode */
+
+#define STEST3_REG_800 0x4f
+#define STEST3_800_TE 0x80 /* Enable active negation */
+#define STEST3_800_STR 0x40 /* SCSI FIFO test read */
+#define STEST3_800_HSC 0x20 /* Halt SCSI clock */
+#define STEST3_800_DSI 0x10 /* Disable single initiator response */
+#define STEST3_800_TTM 0x04 /* Time test mode */
+#define STEST3_800_CSF 0x02 /* Clear SCSI FIFO */
+#define STEST3_800_STW 0x01 /* SCSI FIFO test write */
+
+#define OPTION_PARITY 0x1 /* Enable parity checking */
+#define OPTION_TAGGED_QUEUE 0x2 /* Enable SCSI-II tagged queuing */
+#define OPTION_700 0x8 /* Always run NCR53c700 scripts */
+#define OPTION_INTFLY 0x10 /* Use INTFLY interrupts */
+#define OPTION_DEBUG_INTR 0x20 /* Debug interrupts */
+#define OPTION_DEBUG_INIT_ONLY 0x40 /* Run initialization code and
+ simple test code, return
+ DID_NO_CONNECT if any SCSI
+ commands are attempted. */
+#define OPTION_DEBUG_READ_ONLY 0x80 /* Return DID_ERROR if any
+ SCSI write is attempted */
+#define OPTION_DEBUG_TRACE 0x100 /* Animated trace mode, print
+ each address and instruction
+ executed to debug buffer. */
+#define OPTION_DEBUG_SINGLE 0x200 /* stop after executing one
+ instruction */
+#define OPTION_SYNCHRONOUS 0x400 /* Enable sync SCSI. */
+#define OPTION_MEMORY_MAPPED 0x800 /* NCR registers have valid
+ memory mapping */
+#define OPTION_IO_MAPPED 0x1000 /* NCR registers have valid
+ I/O mapping */
+#define OPTION_DEBUG_PROBE_ONLY 0x2000 /* Probe only, don't even init */
+#define OPTION_DEBUG_TESTS_ONLY 0x4000 /* Probe, init, run selected tests */
+#define OPTION_DEBUG_TEST0 0x08000 /* Run test 0 */
+#define OPTION_DEBUG_TEST1 0x10000 /* Run test 1 */
+#define OPTION_DEBUG_TEST2 0x20000 /* Run test 2 */
+#define OPTION_DEBUG_DUMP 0x40000 /* Dump commands */
+#define OPTION_DEBUG_TARGET_LIMIT 0x80000 /* Only talk to target+luns specified */
+#define OPTION_DEBUG_NCOMMANDS_LIMIT 0x100000 /* Limit the number of commands */
+#define OPTION_DEBUG_SCRIPT 0x200000 /* Print when checkpoints are passed */
+#define OPTION_DEBUG_FIXUP 0x400000 /* print fixup values */
+#define OPTION_DEBUG_DSA 0x800000
+#define OPTION_DEBUG_CORRUPTION 0x1000000 /* Detect script corruption */
+#define OPTION_DEBUG_SDTR 0x2000000 /* Debug SDTR problem */
+#define OPTION_DEBUG_MISMATCH 0x4000000 /* Debug phase mismatches */
+#define OPTION_DISCONNECT 0x8000000 /* Allow disconnect */
+#define OPTION_DEBUG_DISCONNECT 0x10000000
+#define OPTION_ALWAYS_SYNCHRONOUS 0x20000000 /* Negotiate sync. transfers
+ on power up */
+#define OPTION_DEBUG_QUEUES 0x80000000
+#define OPTION_DEBUG_ALLOCATION 0x100000000LL
+#define OPTION_DEBUG_SYNCHRONOUS 0x200000000LL /* Sanity check SXFER and
+ SCNTL3 registers */
+#define OPTION_NO_ASYNC 0x400000000LL /* Don't automagically send
+ SDTR for async transfers when
+ we haven't been told to do
+ a synchronous transfer. */
+#define OPTION_NO_PRINT_RACE 0x800000000LL /* Don't print message when
+ the reselect/WAIT DISCONNECT
+ race condition hits */
+#if !defined(PERM_OPTIONS)
+#define PERM_OPTIONS 0
+#endif
+
+struct NCR53c7x0_synchronous {
+ u32 select_indirect; /* Value used for indirect selection */
+ u32 script[8]; /* Size ?? Script used when target is
+ reselected */
+ unsigned char synchronous_want[5]; /* Per target desired SDTR */
+/*
+ * Set_synchronous programs these, select_indirect and current settings after
+ * int_debug_should show a match.
+ */
+ unsigned char sxfer_sanity, scntl3_sanity;
+};
+
+#define CMD_FLAG_SDTR 1 /* Initiating synchronous
+ transfer negotiation */
+#define CMD_FLAG_WDTR 2 /* Initiating wide transfer
+ negotiation */
+#define CMD_FLAG_DID_SDTR 4 /* did SDTR */
+#define CMD_FLAG_DID_WDTR 8 /* did WDTR */
+
+struct NCR53c7x0_table_indirect {
+ u32 count;
+ void *address;
+};
+
+enum ncr_event {
+ EVENT_NONE = 0,
+/*
+ * Order is IMPORTANT, since these must correspond to the event interrupts
+ * in 53c7,8xx.scr
+ */
+
+ EVENT_ISSUE_QUEUE = 0x5000000, /* Command was added to issue queue */
+ EVENT_START_QUEUE, /* Command moved to start queue */
+ EVENT_SELECT, /* Command completed selection */
+ EVENT_DISCONNECT, /* Command disconnected */
+ EVENT_RESELECT, /* Command reselected */
+ EVENT_COMPLETE, /* Command completed */
+ EVENT_IDLE,
+ EVENT_SELECT_FAILED,
+ EVENT_BEFORE_SELECT,
+ EVENT_RESELECT_FAILED
+};
+
+struct NCR53c7x0_event {
+ enum ncr_event event; /* What type of event */
+ unsigned char target;
+ unsigned char lun;
+ struct timeval time;
+ u32 *dsa; /* What's in the DSA register now (virt) */
+/*
+ * A few things from that SCSI pid so we know what happened after
+ * the Scsi_Cmnd structure in question may have disappeared.
+ */
+ unsigned long pid; /* The SCSI PID which caused this
+ event */
+ unsigned char cmnd[12];
+};
+
+/*
+ * Things in the NCR53c7x0_cmd structure are split into two parts :
+ *
+ * 1. A fixed portion, for things which are not accessed directly by static NCR
+ * code (ie, are referenced only by the Linux side of the driver,
+ * or only by dynamically generated code).
+ *
+ * 2. The DSA portion, for things which are accessed directly by static NCR
+ * code.
+ *
+ * This is a little ugly, but it
+ * 1. Avoids conflicts between the NCR code's picture of the structure, and
+ * Linux code's idea of what it looks like.
+ *
+ * 2. Minimizes the pain in the Linux side of the code needed
+ * to calculate real dsa locations for things, etc.
+ *
+ */
+
+struct NCR53c7x0_cmd {
+ void *real; /* Real, unaligned address for
+ free function */
+ void (* free)(void *, int); /* Command to deallocate; NULL
+ for structures allocated with
+ scsi_register, etc. */
+ Scsi_Cmnd *cmd; /* Associated Scsi_Cmnd
+ structure, Scsi_Cmnd points
+ at NCR53c7x0_cmd using
+ host_scribble structure */
+
+ int size; /* scsi_malloc'd size of this
+ structure */
+
+ int flags; /* CMD_* flags */
+
+/*
+ * SDTR and WIDE messages are an either/or affair
+ * in this message, since we will go into message out and send
+ * _the whole mess_ without dropping out of message out to
+ * let the target go into message in after sending the first
+ * message.
+ */
+
+ unsigned char select[11]; /* Select message, includes
+ IDENTIFY
+ (optional) QUEUE TAG
+ (optional) SDTR or WDTR
+ */
+
+
+ volatile struct NCR53c7x0_cmd *next; /* Linux maintained lists (free,
+ running, eventually finished */
+
+
+ u32 *data_transfer_start; /* Start of data transfer routines */
+ u32 *data_transfer_end; /* Address after end of data transfer o
+ routines */
+/*
+ * The following three fields were moved from the DSA proper to here
+ * since only dynamically generated NCR code refers to them, meaning
+ * we don't need dsa_* absolutes, and it is simpler to let the
+ * host code refer to them directly.
+ */
+
+/*
+ * HARD CODED : residual and saved_residual need to agree with the sizes
+ * used in NCR53c7,8xx.scr.
+ *
+ * FIXME: we want to consider the case where we have odd-length
+ * scatter/gather buffers and a WIDE transfer, in which case
+ * we'll need to use the CHAIN MOVE instruction. Ick.
+ */
+ u32 residual[6]; /* Residual data transfer which
+ allows pointer code to work
+ right.
+
+ [0-1] : Conditional call to
+ appropriate other transfer
+ routine.
+ [2-3] : Residual block transfer
+ instruction.
+ [4-5] : Jump to instruction
+ after splice.
+ */
+ u32 saved_residual[6]; /* Copy of old residual, so we
+ can get another partial
+ transfer and still recover
+ */
+
+ u32 saved_data_pointer; /* Saved data pointer */
+
+ u32 dsa_next_addr; /* _Address_ of dsa_next field
+ in this dsa for RISCy
+ style constant. */
+
+ u32 dsa_addr; /* Address of dsa; RISCy style
+ constant */
+
+ u32 dsa[0]; /* Variable length (depending
+ on host type, number of scatter /
+ gather buffers, etc). */
+};
+
+struct NCR53c7x0_break {
+ u32 *address, old_instruction[2];
+ struct NCR53c7x0_break *next;
+ unsigned char old_size; /* Size of old instruction */
+};
+
+/* Indicates that the NCR is not executing code */
+#define STATE_HALTED 0
+/*
+ * Indicates that the NCR is executing the wait for select / reselect
+ * script. Only used when running NCR53c700 compatible scripts, only
+ * state during which an ABORT is _not_ considered an error condition.
+ */
+#define STATE_WAITING 1
+/* Indicates that the NCR is executing other code. */
+#define STATE_RUNNING 2
+/*
+ * Indicates that the NCR was being aborted.
+ */
+#define STATE_ABORTING 3
+/* Indicates that the NCR was successfully aborted. */
+#define STATE_ABORTED 4
+/* Indicates that the NCR has been disabled due to a fatal error */
+#define STATE_DISABLED 5
+
+/*
+ * Where knowledge of SCSI SCRIPT(tm) specified values are needed
+ * in an interrupt handler, an interrupt handler exists for each
+ * different SCSI script so we don't have name space problems.
+ *
+ * Return values of these handlers are as follows :
+ */
+#define SPECIFIC_INT_NOTHING 0 /* don't even restart */
+#define SPECIFIC_INT_RESTART 1 /* restart at the next instruction */
+#define SPECIFIC_INT_ABORT 2 /* recoverable error, abort cmd */
+#define SPECIFIC_INT_PANIC 3 /* unrecoverable error, panic */
+#define SPECIFIC_INT_DONE 4 /* normal command completion */
+#define SPECIFIC_INT_BREAK 5 /* break point encountered */
+
+struct NCR53c7x0_hostdata {
+ int size; /* Size of entire Scsi_Host
+ structure */
+ int board; /* set to board type, useful if
+ we have host specific things,
+ ie, a general purpose I/O
+ bit is being used to enable
+ termination, etc. */
+
+ int chip; /* set to chip type; 700-66 is
+ 700-66, rest are last three
+ digits of part number */
+ /*
+ * PCI bus, device, function, only for NCR53c8x0 chips.
+ * pci_valid indicates that the PCI configuration information
+ * is valid, and we can twiddle MAX_LAT, etc. as recommended
+ * for maximum performance in the NCR documentation.
+ */
+ unsigned char pci_bus, pci_device_fn;
+ unsigned pci_valid:1;
+
+ u32 *dsp; /* dsp to restart with after
+ all stacked interrupts are
+ handled. */
+
+ unsigned dsp_changed:1; /* Has dsp changed within this
+ set of stacked interrupts ? */
+
+ unsigned char dstat; /* Most recent value of dstat */
+ unsigned dstat_valid:1;
+
+ unsigned expecting_iid:1; /* Expect IID interrupt */
+ unsigned expecting_sto:1; /* Expect STO interrupt */
+
+ /*
+ * The code stays cleaner if we use variables with function
+ * pointers and offsets that are unique for the different
+ * scripts rather than having a slew of switch(hostdata->chip)
+ * statements.
+ *
+ * It also means that the #defines from the SCSI SCRIPTS(tm)
+ * don't have to be visible outside of the script-specific
+ * instructions, preventing name space pollution.
+ */
+
+ void (* init_fixup)(struct Scsi_Host *host);
+ void (* init_save_regs)(struct Scsi_Host *host);
+ void (* dsa_fixup)(struct NCR53c7x0_cmd *cmd);
+ void (* soft_reset)(struct Scsi_Host *host);
+ int (* run_tests)(struct Scsi_Host *host);
+
+ /*
+ * Called when DSTAT_SIR is set, indicating an interrupt generated
+ * by the INT instruction, where values are unique for each SCSI
+ * script. Should return one of the SPEC_* values.
+ */
+
+ int (* dstat_sir_intr)(struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd);
+
+ int dsa_len; /* Size of DSA structure */
+
+ /*
+ * Location of DSA fields for the SCSI SCRIPT corresponding to this
+ * chip.
+ */
+
+ s32 dsa_start;
+ s32 dsa_end;
+ s32 dsa_next;
+ s32 dsa_prev;
+ s32 dsa_cmnd;
+ s32 dsa_select;
+ s32 dsa_msgout;
+ s32 dsa_cmdout;
+ s32 dsa_dataout;
+ s32 dsa_datain;
+ s32 dsa_msgin;
+ s32 dsa_msgout_other;
+ s32 dsa_write_sync;
+ s32 dsa_write_resume;
+ s32 dsa_check_reselect;
+ s32 dsa_status;
+ s32 dsa_saved_pointer;
+ s32 dsa_jump_dest;
+
+ /*
+ * Important entry points that generic fixup code needs
+ * to know about, fixed up.
+ */
+
+ s32 E_accept_message;
+ s32 E_command_complete;
+ s32 E_data_transfer;
+ s32 E_dsa_code_template;
+ s32 E_dsa_code_template_end;
+ s32 E_end_data_transfer;
+ s32 E_msg_in;
+ s32 E_initiator_abort;
+ s32 E_other_transfer;
+ s32 E_other_in;
+ s32 E_other_out;
+ s32 E_target_abort;
+ s32 E_debug_break;
+ s32 E_reject_message;
+ s32 E_respond_message;
+ s32 E_select;
+ s32 E_select_msgout;
+ s32 E_test_0;
+ s32 E_test_1;
+ s32 E_test_2;
+ s32 E_test_3;
+ s32 E_dsa_zero;
+ s32 E_cmdout_cmdout;
+ s32 E_wait_reselect;
+ s32 E_dsa_code_begin;
+
+ long long options; /* Bitfielded set of options enabled */
+ volatile u32 test_completed; /* Test completed */
+ int test_running; /* Test currently running */
+ s32 test_source;
+ volatile s32 test_dest;
+
+ volatile int state; /* state of driver, only used for
+ OPTION_700 */
+
+ unsigned char dmode; /*
+ * set to the address of the DMODE
+ * register for this chip.
+ */
+ unsigned char istat; /*
+ * set to the address of the ISTAT
+ * register for this chip.
+ */
+
+ int scsi_clock; /*
+ * SCSI clock in HZ. 0 may be used
+ * for unknown, although this will
+ * disable synchronous negotiation.
+ */
+
+ volatile int intrs; /* Number of interrupts */
+ volatile int resets; /* Number of SCSI resets */
+ unsigned char saved_dmode;
+ unsigned char saved_ctest4;
+ unsigned char saved_ctest7;
+ unsigned char saved_dcntl;
+ unsigned char saved_scntl3;
+
+ unsigned char this_id_mask;
+
+ /* Debugger information */
+ struct NCR53c7x0_break *breakpoints, /* Linked list of all break points */
+ *breakpoint_current; /* Current breakpoint being stepped
+ through, NULL if we are running
+ normally. */
+#ifdef NCR_DEBUG
+ int debug_size; /* Size of debug buffer */
+ volatile int debug_count; /* Current data count */
+ volatile char *debug_buf; /* Output ring buffer */
+ volatile char *debug_write; /* Current write pointer */
+ volatile char *debug_read; /* Current read pointer */
+#endif /* def NCR_DEBUG */
+
+ /* XXX - primitive debugging junk, remove when working ? */
+ int debug_print_limit; /* Number of commands to print
+ out exhaustive debugging
+ information for if
+ OPTION_DEBUG_DUMP is set */
+
+ unsigned char debug_lun_limit[16]; /* If OPTION_DEBUG_TARGET_LIMIT
+ set, puke if commands are sent
+ to other target/lun combinations */
+
+ int debug_count_limit; /* Number of commands to execute
+ before puking to limit debugging
+ output */
+
+
+ volatile unsigned idle:1; /* set to 1 if idle */
+
+ /*
+ * Table of synchronous+wide transfer parameters set on a per-target
+ * basis.
+ */
+
+ volatile struct NCR53c7x0_synchronous sync[16];
+
+ volatile Scsi_Cmnd *issue_queue;
+ /* waiting to be issued by
+ Linux driver */
+ volatile struct NCR53c7x0_cmd *running_list;
+ /* commands running, maintained
+ by Linux driver */
+
+ volatile struct NCR53c7x0_cmd *current; /* currently connected
+ nexus, ONLY valid for
+ NCR53c700/NCR53c700-66
+ */
+
+ volatile struct NCR53c7x0_cmd *spare; /* pointer to spare,
+ allocated at probe time,
+ which we can use for
+ initialization */
+ volatile struct NCR53c7x0_cmd *free;
+ int max_cmd_size; /* Maximum size of NCR53c7x0_cmd
+ based on number of
+ scatter/gather segments, etc.
+ */
+ volatile int num_cmds; /* Number of commands
+ allocated */
+ volatile int extra_allocate;
+ volatile unsigned char cmd_allocated[16]; /* Have we allocated commands
+ for this target yet? If not,
+ do so ASAP */
+ volatile unsigned char busy[16][8]; /* number of commands
+ executing on each target
+ */
+ /*
+ * Eventually, I'll switch to a coroutine for calling
+ * cmd->done(cmd), etc. so that we can overlap interrupt
+ * processing with this code for maximum performance.
+ */
+
+ volatile struct NCR53c7x0_cmd *finished_queue;
+
+
+ /* Shared variables between SCRIPT and host driver */
+ volatile u32 *schedule; /* Array of JUMPs to dsa_begin
+ routines of various DSAs.
+ When not in use, replace
+ with jump to next slot */
+
+
+ volatile unsigned char msg_buf[16]; /* buffer for messages
+ other than the command
+ complete message */
+
+ /* Per-target default synchronous and WIDE messages */
+ volatile unsigned char synchronous_want[16][5];
+ volatile unsigned char wide_want[16][4];
+
+ /* Bit fielded set of targets we want to speak synchronously with */
+ volatile u16 initiate_sdtr;
+ /* Bit fielded set of targets we want to speak wide with */
+ volatile u16 initiate_wdtr;
+ /* Bit fielded list of targets we've talked to. */
+ volatile u16 talked_to;
+
+ /* Array of bit-fielded lun lists that we need to request_sense */
+ volatile unsigned char request_sense[16];
+
+ u32 addr_reconnect_dsa_head; /* RISCy style constant,
+ address of following */
+ volatile u32 reconnect_dsa_head;
+ /* Data identifying nexus we are trying to match during reselection */
+ volatile unsigned char reselected_identify; /* IDENTIFY message */
+ volatile unsigned char reselected_tag; /* second byte of queue tag
+ message or 0 */
+ /* These were static variables before we moved them */
+
+ s32 NCR53c7xx_zero;
+ s32 NCR53c7xx_sink;
+ u32 NOP_insn;
+ char NCR53c7xx_msg_reject;
+ char NCR53c7xx_msg_abort;
+ char NCR53c7xx_msg_nop;
+
+ volatile int event_size, event_index;
+ volatile struct NCR53c7x0_event *events;
+
+ /* If we need to generate code to kill off the currently connected
+ command, this is where we do it. Should have a BMI instruction
+ to source or sink the current data, followed by a JUMP
+ to abort_connected */
+
+ u32 *abort_script;
+
+ int script_count; /* Size of script in words */
+ u32 script[0]; /* Relocated SCSI script */
+
+};
+
+#define IRQ_NONE 255
+#define DMA_NONE 255
+#define IRQ_AUTO 254
+#define DMA_AUTO 254
+
+#define BOARD_GENERIC 0
+
+#define NCR53c7x0_insn_size(insn) \
+ (((insn) & DCMD_TYPE_MASK) == DCMD_TYPE_MMI ? 3 : 2)
+
+
+#define NCR53c7x0_local_declare() \
+ volatile unsigned char *NCR53c7x0_address_memory; \
+ unsigned int NCR53c7x0_address_io; \
+ int NCR53c7x0_memory_mapped
+
+#define NCR53c7x0_local_setup(host) \
+ NCR53c7x0_address_memory = (void *) (host)->base; \
+ NCR53c7x0_address_io = (unsigned int) (host)->io_port; \
+ NCR53c7x0_memory_mapped = ((struct NCR53c7x0_hostdata *) \
+ host->hostdata)-> options & OPTION_MEMORY_MAPPED
+
+#define NCR53c7x0_read8(address) \
+ (NCR53c7x0_memory_mapped ? \
+ (unsigned int)readb(NCR53c7x0_address_memory + (address)) : \
+ inb(NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_read16(address) \
+ (NCR53c7x0_memory_mapped ? \
+ (unsigned int)readw(NCR53c7x0_address_memory + (address)) : \
+ inw(NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_read32(address) \
+ (NCR53c7x0_memory_mapped ? \
+ (unsigned int) readl(NCR53c7x0_address_memory + (address)) : \
+ inl(NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_write8(address,value) \
+ (NCR53c7x0_memory_mapped ? \
+ ({writeb((value), NCR53c7x0_address_memory + (address)); mb();}) : \
+ outb((value), NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_write16(address,value) \
+ (NCR53c7x0_memory_mapped ? \
+ ({writew((value), NCR53c7x0_address_memory + (address)); mb();}) : \
+ outw((value), NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_write32(address,value) \
+ (NCR53c7x0_memory_mapped ? \
+ ({writel((value), NCR53c7x0_address_memory + (address)); mb();}) : \
+ outl((value), NCR53c7x0_address_io + (address)))
+
+/* Patch arbitrary 32 bit words in the script */
+#define patch_abs_32(script, offset, symbol, value) \
+ for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \
+ (u32)); ++i) { \
+ (script)[A_##symbol##_used[i] - (offset)] += (value); \
+ if (hostdata->options & OPTION_DEBUG_FIXUP) \
+ printk("scsi%d : %s reference %d at 0x%x in %s is now 0x%x\n",\
+ host->host_no, #symbol, i, A_##symbol##_used[i] - \
+ (int)(offset), #script, (script)[A_##symbol##_used[i] - \
+ (offset)]); \
+ }
+
+/* Patch read/write instruction immediate field */
+#define patch_abs_rwri_data(script, offset, symbol, value) \
+ for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \
+ (u32)); ++i) \
+ (script)[A_##symbol##_used[i] - (offset)] = \
+ ((script)[A_##symbol##_used[i] - (offset)] & \
+ ~DBC_RWRI_IMMEDIATE_MASK) | \
+ (((value) << DBC_RWRI_IMMEDIATE_SHIFT) & \
+ DBC_RWRI_IMMEDIATE_MASK)
+
+/* Patch transfer control instruction data field */
+#define patch_abs_tci_data(script, offset, symbol, value) \
+ for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \
+ (u32)); ++i) \
+ (script)[A_##symbol##_used[i] - (offset)] = \
+ ((script)[A_##symbol##_used[i] - (offset)] & \
+ ~DBC_TCI_DATA_MASK) | \
+ (((value) << DBC_TCI_DATA_SHIFT) & \
+ DBC_TCI_DATA_MASK)
+
+/* Patch field in dsa structure (assignment should be +=?) */
+#define patch_dsa_32(dsa, symbol, word, value) \
+ { \
+ (dsa)[(hostdata->##symbol - hostdata->dsa_start) / sizeof(u32) \
+ + (word)] = (value); \
+ if (hostdata->options & OPTION_DEBUG_DSA) \
+ printk("scsi : dsa %s symbol %s(%d) word %d now 0x%x\n", \
+ #dsa, #symbol, hostdata->##symbol, \
+ (word), (u32) (value)); \
+ }
+
+/* Paranoid people could use panic() here. */
+#define FATAL(host) shutdown((host));
+
+#endif /* NCR53c7x0_C */
+#endif /* NCR53c7x0_H */
diff --git a/linux/src/drivers/scsi/53c78xx.c b/linux/src/drivers/scsi/53c78xx.c
new file mode 100644
index 00000000..8925ee52
--- /dev/null
+++ b/linux/src/drivers/scsi/53c78xx.c
@@ -0,0 +1,6401 @@
+/*
+ * PERM_OPTIONS are driver options which will be enabled for all NCR boards
+ * in the system at driver initialization time.
+ *
+ * Don't THINK about touching these in PERM_OPTIONS :
+ * OPTION_IO_MAPPED
+ * Memory mapped IO does not work under i86 Linux.
+ *
+ * OPTION_DEBUG_TEST1
+ * Test 1 does bus mastering and interrupt tests, which will help weed
+ * out brain damaged main boards.
+ *
+ * These are development kernel changes. Code for them included in this
+ * driver release may or may not work. If you turn them on, you should be
+ * running the latest copy of the development sources from
+ *
+ * ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/53c7,8xx
+ *
+ * and be subscribed to the ncr53c810@colorado.edu mailing list. To
+ * subscribe, send mail to majordomo@colorado.edu with
+ *
+ * subscribe ncr53c810
+ *
+ * in the text.
+ *
+ *
+ * OPTION_NO_ASYNC
+ * Don't negotiate for asynchronous transfers on the first command
+ * when OPTION_ALWAYS_SYNCHRONOUS is set. Useful for dain bramaged
+ * devices which do something bad rather than sending a MESSAGE
+ * REJECT back to us like they should if they can't cope.
+ *
+ * OPTION_SYNCHRONOUS
+ * Enable support for synchronous transfers. Target negotiated
+ * synchronous transfers will be responded to. To initiate
+ * a synchronous transfer request, call
+ *
+ * request_synchronous (hostno, target)
+ *
+ * from within KGDB.
+ *
+ * OPTION_ALWAYS_SYNCHRONOUS
+ * Negotiate for synchronous transfers with every target after
+ * driver initialization or a SCSI bus reset. This is a bit dangerous,
+ * since there are some dain bramaged SCSI devices which will accept
+ * SDTR messages but keep talking asynchronously.
+ *
+ * OPTION_DISCONNECT
+ * Enable support for disconnect/reconnect. To change the
+ * default setting on a given host adapter, call
+ *
+ * request_disconnect (hostno, allow)
+ *
+ * where allow is non-zero to allow, 0 to disallow.
+ *
+ * If you really want to run 10MHz FAST SCSI-II transfers, you should
+ * know that the NCR driver currently ignores parity information. Most
+ * systems do 5MHz SCSI fine. I've seen a lot that have problems faster
+ * than 8MHz. To play it safe, we only request 5MHz transfers.
+ *
+ * If you'd rather get 10MHz transfers, edit sdtr_message and change
+ * the fourth byte from 50 to 25.
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_SCSI_NCR53C7xx_sync
+#ifdef CONFIG_SCSI_NCR53C7xx_DISCONNECT
+#define PERM_OPTIONS (OPTION_IO_MAPPED|OPTION_DEBUG_TEST1|OPTION_DISCONNECT|\
+ OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS)
+#else
+#define PERM_OPTIONS (OPTION_IO_MAPPED|OPTION_DEBUG_TEST1|\
+ OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS)
+#endif
+#else
+#ifdef CONFIG_SCSI_NCR53C7xx_DISCONNECT
+#define PERM_OPTIONS (OPTION_IO_MAPPED|OPTION_DEBUG_TEST1|OPTION_DISCONNECT|\
+ OPTION_SYNCHRONOUS)
+#else
+#define PERM_OPTIONS (OPTION_IO_MAPPED|OPTION_DEBUG_TEST1|OPTION_SYNCHRONOUS)
+#endif
+#endif
+
+/*
+ * Sponsored by
+ * iX Multiuser Multitasking Magazine
+ * Hannover, Germany
+ * hm@ix.de
+ *
+ * Copyright 1993, 1994, 1995 Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@PoohSticks.ORG
+ * +1 (303) 786-7975
+ *
+ * TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+ *
+ * For more information, please consult
+ *
+ * NCR53C810
+ * SCSI I/O Processor
+ * Programmer's Guide
+ *
+ * NCR 53C810
+ * PCI-SCSI I/O Processor
+ * Data Manual
+ *
+ * NCR 53C810/53C820
+ * PCI-SCSI I/O Processor Design In Guide
+ *
+ * For literature on Symbios Logic Inc. formerly NCR, SCSI,
+ * and Communication products please call (800) 334-5454 or
+ * (719) 536-3300.
+ *
+ * PCI BIOS Specification Revision
+ * PCI Local Bus Specification
+ * PCI System Design Guide
+ *
+ * PCI Special Interest Group
+ * M/S HF3-15A
+ * 5200 N.E. Elam Young Parkway
+ * Hillsboro, Oregon 97124-6497
+ * +1 (503) 696-2000
+ * +1 (800) 433-5177
+ */
+
+/*
+ * Design issues :
+ * The cumulative latency needed to propagate a read/write request
+ * through the file system, buffer cache, driver stacks, SCSI host, and
+ * SCSI device is ultimately the limiting factor in throughput once we
+ * have a sufficiently fast host adapter.
+ *
+ * So, to maximize performance we want to keep the ratio of latency to data
+ * transfer time to a minimum by
+ * 1. Minimizing the total number of commands sent (typical command latency
+ * including drive and bus mastering host overhead is as high as 4.5ms)
+ * to transfer a given amount of data.
+ *
+ * This is accomplished by placing no arbitrary limit on the number
+ * of scatter/gather buffers supported, since we can transfer 1K
+ * per scatter/gather buffer without Eric's cluster patches,
+ * 4K with.
+ *
+ * 2. Minimizing the number of fatal interrupts serviced, since
+ * fatal interrupts halt the SCSI I/O processor. Basically,
+ * this means offloading the practical maximum amount of processing
+ * to the SCSI chip.
+ *
+ * On the NCR53c810/820/720, this is accomplished by using
+ * interrupt-on-the-fly signals when commands complete,
+ * and only handling fatal errors and SDTR / WDTR messages
+ * in the host code.
+ *
+ * On the NCR53c710, interrupts are generated as on the NCR53c8x0,
+ * only the lack of a interrupt-on-the-fly facility complicates
+ * things. Also, SCSI ID registers and commands are
+ * bit fielded rather than binary encoded.
+ *
+ * On the NCR53c700 and NCR53c700-66, operations that are done via
+ * indirect, table mode on the more advanced chips must be
+ * replaced by calls through a jump table which
+ * acts as a surrogate for the DSA. Unfortunately, this
+ * will mean that we must service an interrupt for each
+ * disconnect/reconnect.
+ *
+ * 3. Eliminating latency by pipelining operations at the different levels.
+ *
+ * This driver allows a configurable number of commands to be enqueued
+ * for each target/lun combination (experimentally, I have discovered
+ * that two seems to work best) and will ultimately allow for
+ * SCSI-II tagged queuing.
+ *
+ *
+ * Architecture :
+ * This driver is built around a Linux queue of commands waiting to
+ * be executed, and a shared Linux/NCR array of commands to start. Commands
+ * are transfered to the array by the run_process_issue_queue() function
+ * which is called whenever a command completes.
+ *
+ * As commands are completed, the interrupt routine is triggered,
+ * looks for commands in the linked list of completed commands with
+ * valid status, removes these commands from a list of running commands,
+ * calls the done routine, and flags their target/luns as not busy.
+ *
+ * Due to limitations in the intelligence of the NCR chips, certain
+ * concessions are made. In many cases, it is easier to dynamically
+ * generate/fix-up code rather than calculate on the NCR at run time.
+ * So, code is generated or fixed up for
+ *
+ * - Handling data transfers, using a variable number of MOVE instructions
+ * interspersed with CALL MSG_IN, WHEN MSGIN instructions.
+ *
+ * The DATAIN and DATAOUT routines are separate, so that an incorrect
+ * direction can be trapped, and space isn't wasted.
+ *
+ * It may turn out that we're better off using some sort
+ * of table indirect instruction in a loop with a variable
+ * sized table on the NCR53c710 and newer chips.
+ *
+ * - Checking for reselection (NCR53c710 and better)
+ *
+ * - Handling the details of SCSI context switches (NCR53c710 and better),
+ * such as reprogramming appropriate synchronous parameters,
+ * removing the dsa structure from the NCR's queue of outstanding
+ * commands, etc.
+ *
+ */
+
+/*
+ * Accommodate differences between stock 1.2.x and 1.3.x asm-i386/types.h
+ * so lusers can drop in 53c7,8xx.* and get something which compiles
+ * without warnings.
+ */
+
+#if !defined(LINUX_1_2) && !defined(LINUX_1_3)
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > 65536 + 3 * 256
+#define LINUX_1_3
+#else
+#define LINUX_1_2
+#endif
+#endif
+
+#ifdef LINUX_1_2
+#define u32 bogus_u32
+#define s32 bogus_s32
+#include <asm/types.h>
+#undef u32
+#undef s32
+typedef __signed__ int s32;
+typedef unsigned int u32;
+#endif /* def LINUX_1_2 */
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#ifdef LINUX_1_2
+#include "../block/blk.h"
+#else
+#include <linux/blk.h>
+#endif
+#undef current
+
+#include "scsi.h"
+#include "hosts.h"
+#include "53c7,8xx.h"
+#include "constants.h"
+#include "sd.h"
+#include <linux/stat.h>
+#include <linux/stddef.h>
+
+#ifndef LINUX_1_2
+struct proc_dir_entry proc_scsi_ncr53c7xx = {
+ PROC_SCSI_NCR53C7xx, 9, "ncr53c7xx",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#endif
+
+static int check_address (unsigned long addr, int size);
+static void dump_events (struct Scsi_Host *host, int count);
+static Scsi_Cmnd * return_outstanding_commands (struct Scsi_Host *host,
+ int free, int issue);
+static void hard_reset (struct Scsi_Host *host);
+static void ncr_scsi_reset (struct Scsi_Host *host);
+static void print_lots (struct Scsi_Host *host);
+static void set_synchronous (struct Scsi_Host *host, int target, int sxfer,
+ int scntl3, int now_connected);
+static int datapath_residual (struct Scsi_Host *host);
+static const char * sbcl_to_phase (int sbcl);
+static void print_progress (Scsi_Cmnd *cmd);
+static void print_queues (struct Scsi_Host *host);
+static void process_issue_queue (unsigned long flags);
+static int shutdown (struct Scsi_Host *host);
+static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int result);
+static int disable (struct Scsi_Host *host);
+static int NCR53c8xx_run_tests (struct Scsi_Host *host);
+static int NCR53c8xx_script_len;
+static int NCR53c8xx_dsa_len;
+static void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
+static int ncr_halt (struct Scsi_Host *host);
+static void intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd
+ *cmd);
+static void intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd);
+static void print_dsa (struct Scsi_Host *host, u32 *dsa,
+ const char *prefix);
+static int print_insn (struct Scsi_Host *host, const u32 *insn,
+ const char *prefix, int kernel);
+
+static void NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd);
+static void NCR53c8x0_init_fixup (struct Scsi_Host *host);
+static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
+ NCR53c7x0_cmd *cmd);
+static void NCR53c8x0_soft_reset (struct Scsi_Host *host);
+
+/* INSMOD variables */
+static long long perm_options = PERM_OPTIONS;
+/* 14 = .5s; 15 is max; decreasing divides by two. */
+static int selection_timeout = 14;
+/* Size of event list (per host adapter) */
+static int track_events = 0;
+
+static struct Scsi_Host *first_host = NULL; /* Head of list of NCR boards */
+static Scsi_Host_Template *the_template = NULL;
+
+/*
+ * KNOWN BUGS :
+ * - There is some sort of conflict when the PPP driver is compiled with
+ * support for 16 channels?
+ *
+ * - On systems which predate the 1.3.x initialization order change,
+ * the NCR driver will cause Cannot get free page messages to appear.
+ * These are harmless, but I don't know of an easy way to avoid them.
+ *
+ * - With OPTION_DISCONNECT, on two systems under unknown circumstances,
+ * we get a PHASE MISMATCH with DSA set to zero (suggests that we
+ * are occurring somewhere in the reselection code) where
+ * DSP=some value DCMD|DBC=same value.
+ *
+ * Closer inspection suggests that we may be trying to execute
+ * some portion of the DSA?
+ * scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO)
+ * scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO)
+ * scsi0 : no current command : unexpected phase MSGIN.
+ * DSP=0x1c46cc, DCMD|DBC=0x1c46ac, DSA=0x0
+ * DSPS=0x0, TEMP=0x1c3e70, DMODE=0x80
+ * scsi0 : DSP->
+ * 001c46cc : 0x001c46cc 0x00000000
+ * 001c46d4 : 0x001c5ea0 0x000011f8
+ *
+ * Changed the print code in the phase_mismatch handler so
+ * that we call print_lots to try to diagnose this.
+ *
+ */
+
+/*
+ * Possible future direction of architecture for max performance :
+ *
+ * We're using a single start array for the NCR chip. This is
+ * sub-optimal, because we cannot add a command which would conflict with
+ * an executing command to this start queue, and therefore must insert the
+ * next command for a given I/T/L combination after the first has completed;
+ * incurring our interrupt latency between SCSI commands.
+ *
+ * To allow further pipelining of the NCR and host CPU operation, we want
+ * to set things up so that immediately on termination of a command destined
+ * for a given LUN, we get that LUN busy again.
+ *
+ * To do this, we need to add a 32 bit pointer to which is jumped to
+ * on completion of a command. If no new command is available, this
+ * would point to the usual DSA issue queue select routine.
+ *
+ * If one were, it would point to a per-NCR53c7x0_cmd select routine
+ * which starts execution immediately, inserting the command at the head
+ * of the start queue if the NCR chip is selected or reselected.
+ *
+ * We would change so that we keep a list of outstanding commands
+ * for each unit, rather than a single running_list. We'd insert
+ * a new command into the right running list; if the NCR didn't
+ * have something running for that yet, we'd put it in the
+ * start queue as well. Some magic needs to happen to handle the
+ * race condition between the first command terminating before the
+ * new one is written.
+ *
+ * Potential for profiling :
+ * Call do_gettimeofday(struct timeval *tv) to get 800ns resolution.
+ */
+
+
+/*
+ * TODO :
+ * 1. To support WIDE transfers, not much needs to happen. We
+ * should do CHMOVE instructions instead of MOVEs when
+ * we have scatter/gather segments of uneven length. When
+ * we do this, we need to handle the case where we disconnect
+ * between segments.
+ *
+ * 2. Currently, when Icky things happen we do a FATAL(). Instead,
+ * we want to do an integrity check on the parts of the NCR hostdata
+ * structure which were initialized at boot time; FATAL() if that
+ * fails, and otherwise try to recover. Keep track of how many
+ * times this has happened within a single SCSI command; if it
+ * gets excessive, then FATAL().
+ *
+ * 3. Parity checking is currently disabled, and a few things should
+ * happen here now that we support synchronous SCSI transfers :
+ * 1. On soft-reset, we should set the EPC (Enable Parity Checking)
+ * and AAP (Assert SATN/ on parity error) bits in SCNTL0.
+ *
+ * 2. We should enable the parity interrupt in the SIEN0 register.
+ *
+ * 3. intr_phase_mismatch() needs to believe that message out is
+ * always an "acceptable" phase to have a mismatch in. If
+ * the old phase was MSG_IN, we should send a MESSAGE PARITY
+ * error. If the old phase was something else, we should send
+ * a INITIATOR_DETECTED_ERROR message. Note that this could
+ * cause a RESTORE POINTERS message; so we should handle that
+ * correctly first. Instead, we should probably do an
+ * initiator_abort.
+ *
+ * 4. MPEE bit of CTEST4 should be set so we get interrupted if
+ * we detect an error.
+ *
+ *
+ * 5. The initial code has been tested on the NCR53c810. I don't
+ * have access to NCR53c700, 700-66 (Forex boards), NCR53c710
+ * (NCR Pentium systems), NCR53c720, NCR53c820, or NCR53c825 boards to
+ * finish development on those platforms.
+ *
+ * NCR53c820/825/720 - need to add wide transfer support, including WDTR
+ * negotiation, programming of wide transfer capabilities
+ * on reselection and table indirect selection.
+ *
+ * NCR53c710 - need to add fatal interrupt or GEN code for
+ * command completion signaling. Need to modify all
+ * SDID, SCID, etc. registers, and table indirect select code
+ * since these use bit fielded (ie 1<<target) instead of
+ * binary encoded target ids. Need to accommodate
+ * different register mappings, probably scan through
+ * the SCRIPT code and change the non SFBR register operand
+ * of all MOVE instructions.
+ *
+ * NCR53c700/700-66 - need to add code to refix addresses on
+ * every nexus change, eliminate all table indirect code,
+ * very messy.
+ *
+ * 6. The NCR53c7x0 series is very popular on other platforms that
+ * could be running Linux - ie, some high performance AMIGA SCSI
+ * boards use it.
+ *
+ * So, I should include #ifdef'd code so that it is
+ * compatible with these systems.
+ *
+ * Specifically, the little Endian assumptions I made in my
+ * bit fields need to change, and if the NCR doesn't see memory
+ * the right way, we need to provide options to reverse words
+ * when the scripts are relocated.
+ *
+ * 7. Use vremap() to access memory mapped boards.
+ */
+
+/*
+ * Allow for simultaneous existence of multiple SCSI scripts so we
+ * can have a single driver binary for all of the family.
+ *
+ * - one for NCR53c700 and NCR53c700-66 chips (not yet supported)
+ * - one for rest (only the NCR53c810, 815, 820, and 825 are currently
+ * supported)
+ *
+ * So that we only need two SCSI scripts, we need to modify things so
+ * that we fixup register accesses in READ/WRITE instructions, and
+ * we'll also have to accommodate the bit vs. binary encoding of IDs
+ * with the 7xx chips.
+ */
+
+/*
+ * Use pci_chips_ids to translate in both directions between PCI device ID
+ * and chip numbers.
+ */
+
+static struct {
+ unsigned short pci_device_id;
+ int chip;
+/*
+ * The revision field of the PCI_CLASS_REVISION register is compared
+ * against each of these fields if the field is not -1. If it
+ * is less than min_revision or larger than max_revision, a warning
+ * message is printed.
+ */
+ int max_revision;
+ int min_revision;
+} pci_chip_ids[] = {
+ {PCI_DEVICE_ID_NCR_53C810, 810, 2, 1},
+ {PCI_DEVICE_ID_NCR_53C815, 815, 3, 2},
+ {PCI_DEVICE_ID_NCR_53C820, 820, -1, -1},
+ {PCI_DEVICE_ID_NCR_53C825, 825, -1, -1}
+};
+
+#define NPCI_CHIP_IDS (sizeof (pci_chip_ids) / sizeof(pci_chip_ids[0]))
+
+#define ROUNDUP(adr,type) \
+ ((void *) (((long) (adr) + sizeof(type) - 1) & ~(sizeof(type) - 1)))
+
+/*
+ * Forced detection and autoprobe code for various hardware. Currently,
+ * entry points for these are not included in init/main.c because if the
+ * PCI BIOS code isn't working right, you're not going to be able to use
+ * the hardware anyways; this way we force users to solve their
+ * problems rather than forcing detection and blaming us when it
+ * does not work.
+ */
+
+static struct override {
+ int chip; /* 700, 70066, 710, 720, 810, 820 */
+ int board; /* Any special board level gunk */
+ unsigned pci:1;
+ union {
+ struct {
+ int base; /* Memory address - indicates memory mapped regs */
+ int io_port;/* I/O port address - indicates I/O mapped regs */
+ int irq; /* IRQ line */
+ int dma; /* DMA channel - often none */
+ } normal;
+ struct {
+ int bus;
+ int device;
+ int function;
+ } pci;
+ } data;
+ long long options;
+} overrides [4] = {{0,},};
+static int commandline_current = 0;
+static int no_overrides = 0;
+
+#if 0
+#define OVERRIDE_LIMIT (sizeof(overrides) / sizeof(struct override))
+#else
+#define OVERRIDE_LIMIT commandline_current
+#endif
+
+/*
+ * Function: issue_to_cmd
+ *
+ * Purpose: convert jump instruction in issue array to NCR53c7x0_cmd
+ * structure pointer.
+ *
+ * Inputs; issue - pointer to start of NOP or JUMP instruction
+ * in issue array.
+ *
+ * Returns: pointer to command on success; 0 if opcode is NOP.
+ */
+
+static inline struct NCR53c7x0_cmd *
+issue_to_cmd (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
+ u32 *issue)
+{
+ return (issue[0] != hostdata->NOP_insn) ?
+ /*
+ * If the IF TRUE bit is set, it's a JUMP instruction. The
+ * operand is a bus pointer to the dsa_begin routine for this DSA. The
+ * dsa field of the NCR53c7x0_cmd structure starts with the
+ * DSA code template. By converting to a virtual address,
+ * subtracting the code template size, and offset of the
+ * dsa field, we end up with a pointer to the start of the
+ * structure (alternatively, we could use the
+ * dsa_cmnd field, an anachronism from when we weren't
+ * sure what the relationship between the NCR structures
+ * and host structures were going to be.
+ */
+ (struct NCR53c7x0_cmd *) ((char *) bus_to_virt (issue[1]) -
+ (hostdata->E_dsa_code_begin - hostdata->E_dsa_code_template) -
+ offsetof(struct NCR53c7x0_cmd, dsa))
+ /* If the IF TRUE bit is not set, it's a NOP */
+ : NULL;
+}
+
+
+/*
+ * Function : static internal_setup(int board, int chip, char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : board - currently, unsupported. chip - 700, 70066, 710, 720
+ * 810, 815, 820, 825, although currently only the NCR53c810 is
+ * supported.
+ *
+ */
+
+static void
+internal_setup(int board, int chip, char *str, int *ints) {
+ unsigned char pci; /* Specifies a PCI override, with bus, device,
+ function */
+
+ pci = (str && !strcmp (str, "pci")) ? 1 : 0;
+
+/*
+ * Override syntaxes are as follows :
+ * ncr53c700,ncr53c700-66,ncr53c710,ncr53c720=mem,io,irq,dma
+ * ncr53c810,ncr53c820,ncr53c825=mem,io,irq or pci,bus,device,function
+ */
+
+ if (commandline_current < OVERRIDE_LIMIT) {
+ overrides[commandline_current].pci = pci ? 1 : 0;
+ if (!pci) {
+ overrides[commandline_current].data.normal.base = ints[1];
+ overrides[commandline_current].data.normal.io_port = ints[2];
+ overrides[commandline_current].data.normal.irq = ints[3];
+ overrides[commandline_current].data.normal.dma = (ints[0] >= 4) ?
+ ints[4] : DMA_NONE;
+ /* FIXME: options is now a long long */
+ overrides[commandline_current].options = (ints[0] >= 5) ?
+ ints[5] : 0;
+ } else {
+ overrides[commandline_current].data.pci.bus = ints[1];
+ overrides[commandline_current].data.pci.device = ints[2];
+ overrides[commandline_current].data.pci.function = ints[3];
+ /* FIXME: options is now a long long */
+ overrides[commandline_current].options = (ints[0] >= 4) ?
+ ints[4] : 0;
+ }
+ overrides[commandline_current].board = board;
+ overrides[commandline_current].chip = chip;
+ ++commandline_current;
+ ++no_overrides;
+ } else {
+ printk ("53c7,7x0.c:internal_setup() : too many overrides\n");
+ }
+}
+
+/*
+ * XXX - we might want to implement a single override function
+ * with a chip type field, revamp the command line configuration,
+ * etc.
+ */
+
+#define setup_wrapper(x) \
+void ncr53c##x##_setup (char *str, int *ints) { \
+ internal_setup (BOARD_GENERIC, x, str, ints); \
+}
+
+setup_wrapper(700)
+setup_wrapper(70066)
+setup_wrapper(710)
+setup_wrapper(720)
+setup_wrapper(810)
+setup_wrapper(815)
+setup_wrapper(820)
+setup_wrapper(825)
+
+/*
+ * FIXME: we should junk these, in favor of synchronous_want and
+ * wide_want in the NCR53c7x0_hostdata structure.
+ */
+
+/* Template for "preferred" synchronous transfer parameters. */
+
+static const unsigned char sdtr_message[] = {
+#ifdef CONFIG_SCSI_NCR53C7xx_FAST
+ EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 25 /* *4ns */, 8 /* off */
+#else
+ EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 50 /* *4ns */, 8 /* off */
+#endif
+};
+
+/* Template to request asynchronous transfers */
+
+static const unsigned char async_message[] = {
+ EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 0, 0 /* asynchronous */
+};
+
+/* Template for "preferred" WIDE transfer parameters */
+
+static const unsigned char wdtr_message[] = {
+ EXTENDED_MESSAGE, 2 /* length */, EXTENDED_WDTR, 1 /* 2^1 bytes */
+};
+
+/*
+ * Function : struct Scsi_Host *find_host (int host)
+ *
+ * Purpose : KGDB support function which translates a host number
+ * to a host structure.
+ *
+ * Inputs : host - number of SCSI host
+ *
+ * Returns : NULL on failure, pointer to host structure on success.
+ */
+
+static struct Scsi_Host *
+find_host (int host) {
+ struct Scsi_Host *h;
+ for (h = first_host; h && h->host_no != host; h = h->next);
+ if (!h) {
+ printk (KERN_ALERT "scsi%d not found\n", host);
+ return NULL;
+ } else if (h->hostt != the_template) {
+ printk (KERN_ALERT "scsi%d is not a NCR board\n", host);
+ return NULL;
+ }
+ return h;
+}
+
+/*
+ * Function : request_synchronous (int host, int target)
+ *
+ * Purpose : KGDB interface which will allow us to negotiate for
+ * synchronous transfers. This ill be replaced with a more
+ * integrated function; perhaps a new entry in the scsi_host
+ * structure, accessible via an ioctl() or perhaps /proc/scsi.
+ *
+ * Inputs : host - number of SCSI host; target - number of target.
+ *
+ * Returns : 0 when negotiation has been setup for next SCSI command,
+ * -1 on failure.
+ */
+
+static int
+request_synchronous (int host, int target) {
+ struct Scsi_Host *h;
+ struct NCR53c7x0_hostdata *hostdata;
+ unsigned long flags;
+ if (target < 0) {
+ printk (KERN_ALERT "target %d is bogus\n", target);
+ return -1;
+ }
+ if (!(h = find_host (host)))
+ return -1;
+ else if (h->this_id == target) {
+ printk (KERN_ALERT "target %d is host ID\n", target);
+ return -1;
+ }
+#ifndef LINUX_1_2
+ else if (target > h->max_id) {
+ printk (KERN_ALERT "target %d exceeds maximum of %d\n", target,
+ h->max_id);
+ return -1;
+ }
+#endif
+ hostdata = (struct NCR53c7x0_hostdata *)h->hostdata;
+
+ save_flags(flags);
+ cli();
+ if (hostdata->initiate_sdtr & (1 << target)) {
+ restore_flags(flags);
+ printk (KERN_ALERT "target %d already doing SDTR\n", target);
+ return -1;
+ }
+ hostdata->initiate_sdtr |= (1 << target);
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * Function : request_disconnect (int host, int on_or_off)
+ *
+ * Purpose : KGDB support function, tells us to allow or disallow
+ * disconnections.
+ *
+ * Inputs : host - number of SCSI host; on_or_off - non-zero to allow,
+ * zero to disallow.
+ *
+ * Returns : 0 on success, * -1 on failure.
+ */
+
+static int
+request_disconnect (int host, int on_or_off) {
+ struct Scsi_Host *h;
+ struct NCR53c7x0_hostdata *hostdata;
+ if (!(h = find_host (host)))
+ return -1;
+ hostdata = (struct NCR53c7x0_hostdata *) h->hostdata;
+ if (on_or_off)
+ hostdata->options |= OPTION_DISCONNECT;
+ else
+ hostdata->options &= ~OPTION_DISCONNECT;
+ return 0;
+}
+
+/*
+ * Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host)
+ *
+ * Purpose : Initialize internal structures, as required on startup, or
+ * after a SCSI bus reset.
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ */
+
+static void
+NCR53c7x0_driver_init (struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int i, j;
+ u32 *current;
+ for (i = 0; i < 16; ++i) {
+ hostdata->request_sense[i] = 0;
+ for (j = 0; j < 8; ++j)
+ hostdata->busy[i][j] = 0;
+ set_synchronous (host, i, /* sxfer */ 0, hostdata->saved_scntl3, 0);
+ }
+ hostdata->issue_queue = NULL;
+ hostdata->running_list = hostdata->finished_queue =
+ hostdata->current = NULL;
+ for (i = 0, current = (u32 *) hostdata->schedule;
+ i < host->can_queue; ++i, current += 2) {
+ current[0] = hostdata->NOP_insn;
+ current[1] = 0xdeadbeef;
+ }
+ current[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | DBC_TCI_TRUE;
+ current[1] = (u32) virt_to_bus (hostdata->script) +
+ hostdata->E_wait_reselect;
+ hostdata->reconnect_dsa_head = 0;
+ hostdata->addr_reconnect_dsa_head = (u32)
+ virt_to_bus((void *) &(hostdata->reconnect_dsa_head));
+ hostdata->expecting_iid = 0;
+ hostdata->expecting_sto = 0;
+ if (hostdata->options & OPTION_ALWAYS_SYNCHRONOUS)
+ hostdata->initiate_sdtr = 0xffff;
+ else
+ hostdata->initiate_sdtr = 0;
+ hostdata->talked_to = 0;
+ hostdata->idle = 1;
+}
+
+/*
+ * Function : static int ccf_to_clock (int ccf)
+ *
+ * Purpose : Return the largest SCSI clock allowable for a given
+ * clock conversion factor, allowing us to do synchronous periods
+ * when we don't know what the SCSI clock is by taking at least
+ * as long as the device says we can.
+ *
+ * Inputs : ccf
+ *
+ * Returns : clock on success, -1 on failure.
+ */
+
+static int
+ccf_to_clock (int ccf) {
+ switch (ccf) {
+ case 1: return 25000000; /* Divide by 1.0 */
+ case 2: return 37500000; /* Divide by 1.5 */
+ case 3: return 50000000; /* Divide by 2.0 */
+ case 0: /* Divide by 3.0 */
+ case 4: return 66000000;
+ default: return -1;
+ }
+}
+
+/*
+ * Function : static int clock_to_ccf (int clock)
+ *
+ * Purpose : Return the clock conversion factor for a given SCSI clock.
+ *
+ * Inputs : clock - SCSI clock expressed in Hz.
+ *
+ * Returns : ccf on success, -1 on failure.
+ */
+
+static int
+clock_to_ccf (int clock) {
+ if (clock < 16666666)
+ return -1;
+ if (clock < 25000000)
+ return 1; /* Divide by 1.0 */
+ else if (clock < 37500000)
+ return 2; /* Divide by 1.5 */
+ else if (clock < 50000000)
+ return 3; /* Divide by 2.0 */
+ else if (clock < 66000000)
+ return 4; /* Divide by 3.0 */
+ else
+ return -1;
+}
+
+/*
+ * Function : static int NCR53c7x0_init (struct Scsi_Host *host)
+ *
+ * Purpose : initialize the internal structures for a given SCSI host
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ *
+ * Preconditions : when this function is called, the chip_type
+ * field of the hostdata structure MUST have been set.
+ *
+ * Returns : 0 on success, -1 on failure.
+ */
+
+static int
+NCR53c7x0_init (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ int i, ccf, expected_ccf;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ struct Scsi_Host *search;
+ /*
+ * There are some things which we need to know about in order to provide
+ * a semblance of support. Print 'em if they aren't what we expect,
+ * otherwise don't add to the noise.
+ *
+ * -1 means we don't know what to expect.
+ */
+ int expected_id = -1;
+ int expected_clock = -1;
+ int uninitialized = 0;
+ /*
+ * FIXME : this is only on Intel boxes. On other platforms, this
+ * will differ.
+ */
+ int expected_mapping = OPTION_IO_MAPPED;
+ NCR53c7x0_local_setup(host);
+
+ switch (hostdata->chip) {
+ case 820:
+ case 825:
+#ifdef notyet
+ host->max_id = 15;
+#endif
+ /* Fall through */
+ case 810:
+ case 815:
+ hostdata->dstat_sir_intr = NCR53c8x0_dstat_sir_intr;
+ hostdata->init_save_regs = NULL;
+ hostdata->dsa_fixup = NCR53c8xx_dsa_fixup;
+ hostdata->init_fixup = NCR53c8x0_init_fixup;
+ hostdata->soft_reset = NCR53c8x0_soft_reset;
+ hostdata->run_tests = NCR53c8xx_run_tests;
+/* Is the SCSI clock ever anything else on these chips? */
+ expected_clock = hostdata->scsi_clock = 40000000;
+ expected_id = 7;
+ break;
+ default:
+ printk ("scsi%d : chip type of %d is not supported yet, detaching.\n",
+ host->host_no, hostdata->chip);
+ scsi_unregister (host);
+ return -1;
+ }
+
+ /* Assign constants accessed by NCR */
+ hostdata->NCR53c7xx_zero = 0;
+ hostdata->NCR53c7xx_msg_reject = MESSAGE_REJECT;
+ hostdata->NCR53c7xx_msg_abort = ABORT;
+ hostdata->NCR53c7xx_msg_nop = NOP;
+ hostdata->NOP_insn = (DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24;
+
+ if (expected_mapping == -1 ||
+ (hostdata->options & (OPTION_MEMORY_MAPPED)) !=
+ (expected_mapping & OPTION_MEMORY_MAPPED))
+ printk ("scsi%d : using %s mapped access\n", host->host_no,
+ (hostdata->options & OPTION_MEMORY_MAPPED) ? "memory" :
+ "io");
+
+ hostdata->dmode = (hostdata->chip == 700 || hostdata->chip == 70066) ?
+ DMODE_REG_00 : DMODE_REG_10;
+ hostdata->istat = ((hostdata->chip / 100) == 8) ?
+ ISTAT_REG_800 : ISTAT_REG_700;
+
+/* Only the ISTAT register is readable when the NCR is running, so make
+ sure it's halted. */
+ ncr_halt(host);
+
+/*
+ * XXX - the NCR53c700 uses bitfielded registers for SCID, SDID, etc,
+ * as does the 710 with one bit per SCSI ID. Conversely, the NCR
+ * uses a normal, 3 bit binary representation of these values.
+ *
+ * Get the rest of the NCR documentation, and FIND OUT where the change
+ * was.
+ */
+#if 0
+ tmp = hostdata->this_id_mask = NCR53c7x0_read8(SCID_REG);
+ for (host->this_id = 0; tmp != 1; tmp >>=1, ++host->this_id);
+#else
+ host->this_id = NCR53c7x0_read8(SCID_REG) & 15;
+ if (host->this_id == 0)
+ host->this_id = 7; /* sanitize hostid---0 doesn't make sense */
+ hostdata->this_id_mask = 1 << host->this_id;
+#endif
+
+/*
+ * Note : we should never encounter a board setup for ID0. So,
+ * if we see ID0, assume that it was uninitialized and set it
+ * to the industry standard 7.
+ */
+ if (!host->this_id) {
+ printk("scsi%d : initiator ID was %d, changing to 7\n",
+ host->host_no, host->this_id);
+ host->this_id = 7;
+ hostdata->this_id_mask = 1 << 7;
+ uninitialized = 1;
+ };
+
+ if (expected_id == -1 || host->this_id != expected_id)
+ printk("scsi%d : using initiator ID %d\n", host->host_no,
+ host->this_id);
+
+ /*
+ * Save important registers to allow a soft reset.
+ */
+
+ if ((hostdata->chip / 100) == 8) {
+ /*
+ * CTEST4 controls burst mode disable.
+ */
+ hostdata->saved_ctest4 = NCR53c7x0_read8(CTEST4_REG_800) &
+ CTEST4_800_SAVE;
+ } else {
+ /*
+ * CTEST7 controls cache snooping, burst mode, and support for
+ * external differential drivers.
+ */
+ hostdata->saved_ctest7 = NCR53c7x0_read8(CTEST7_REG) & CTEST7_SAVE;
+ }
+
+ /*
+ * On NCR53c700 series chips, DCNTL controls the SCSI clock divisor,
+ * on 800 series chips, it allows for a totem-pole IRQ driver.
+ */
+
+ hostdata->saved_dcntl = NCR53c7x0_read8(DCNTL_REG);
+
+ /*
+ * DCNTL_800_IRQM controls weather we are using an open drain
+ * driver (reset) or totem pole driver (set). In all cases,
+ * it's level active. I suppose this is an issue when we're trying to
+ * wire-or the same PCI INTx line?
+ */
+ if ((hostdata->chip / 100) == 8)
+ hostdata->saved_dcntl &= ~DCNTL_800_IRQM;
+
+ /*
+ * DMODE controls DMA burst length, and on 700 series chips,
+ * 286 mode and bus width
+ */
+ hostdata->saved_dmode = NCR53c7x0_read8(hostdata->dmode);
+
+ /*
+ * Now that burst length and enabled/disabled status is known,
+ * clue the user in on it.
+ */
+
+ if ((hostdata->chip / 100) == 8) {
+ if (hostdata->saved_ctest4 & CTEST4_800_BDIS) {
+ printk ("scsi%d : burst mode disabled\n", host->host_no);
+ } else {
+ switch (hostdata->saved_dmode & DMODE_BL_MASK) {
+ case DMODE_BL_2: i = 2; break;
+ case DMODE_BL_4: i = 4; break;
+ case DMODE_BL_8: i = 8; break;
+ case DMODE_BL_16: i = 16; break;
+ default: i = 0;
+ }
+ printk ("scsi%d : burst length %d\n", host->host_no, i);
+ }
+ }
+
+ /*
+ * On NCR53c810 and NCR53c820 chips, SCNTL3 contails the synchronous
+ * and normal clock conversion factors.
+ */
+ if (hostdata->chip / 100 == 8) {
+ expected_ccf = clock_to_ccf (expected_clock);
+ hostdata->saved_scntl3 = NCR53c7x0_read8(SCNTL3_REG_800);
+ ccf = hostdata->saved_scntl3 & SCNTL3_800_CCF_MASK;
+ if (expected_ccf != -1 && ccf != expected_ccf && !ccf) {
+ hostdata->saved_scntl3 = (hostdata->saved_scntl3 &
+ ~SCNTL3_800_CCF_MASK) | expected_ccf;
+ if (!uninitialized) {
+ printk ("scsi%d : reset ccf to %d from %d\n",
+ host->host_no, expected_ccf, ccf);
+ uninitialized = 1;
+ }
+ }
+ } else
+ ccf = 0;
+
+ /*
+ * If we don't have a SCSI clock programmed, pick one on the upper
+ * bound of that allowed by NCR so that our transfers err on the
+ * slow side, since transfer period must be >= the agreed
+ * upon period.
+ */
+
+ if ((!hostdata->scsi_clock) && (hostdata->scsi_clock = ccf_to_clock (ccf))
+ == -1) {
+ printk ("scsi%d : clock conversion factor %d unknown.\n"
+ " synchronous transfers disabled\n",
+ host->host_no, ccf);
+ hostdata->options &= ~OPTION_SYNCHRONOUS;
+ hostdata->scsi_clock = 0;
+ }
+
+ if (expected_clock == -1 || hostdata->scsi_clock != expected_clock)
+ printk ("scsi%d : using %dMHz SCSI clock\n", host->host_no,
+ hostdata->scsi_clock / 1000000);
+
+ for (i = 0; i < 16; ++i)
+ hostdata->cmd_allocated[i] = 0;
+
+ if (hostdata->init_save_regs)
+ hostdata->init_save_regs (host);
+ if (hostdata->init_fixup)
+ hostdata->init_fixup (host);
+
+ if (!the_template) {
+ the_template = host->hostt;
+ first_host = host;
+ }
+
+ /*
+ * Linux SCSI drivers have always been plagued with initialization
+ * problems - some didn't work with the BIOS disabled since they expected
+ * initialization from it, some didn't work when the networking code
+ * was enabled and registers got scrambled, etc.
+ *
+ * To avoid problems like this, in the future, we will do a soft
+ * reset on the SCSI chip, taking it back to a sane state.
+ */
+
+ hostdata->soft_reset (host);
+
+#if 1
+ hostdata->debug_count_limit = -1;
+#else
+ hostdata->debug_count_limit = 1;
+#endif
+ hostdata->intrs = -1;
+ hostdata->resets = -1;
+ memcpy ((void *) hostdata->synchronous_want, (void *) sdtr_message,
+ sizeof (hostdata->synchronous_want));
+
+ NCR53c7x0_driver_init (host);
+
+ /*
+ * Set up an interrupt handler if we aren't already sharing an IRQ
+ * with another board.
+ */
+
+ for (search = first_host; search && !(search->hostt == the_template &&
+ search->irq == host->irq && search != host); search=search->next);
+
+ if (!search) {
+ if (request_irq(host->irq, NCR53c7x0_intr, SA_INTERRUPT, "53c7,8xx", NULL)) {
+ printk("scsi%d : IRQ%d not free, detaching\n"
+ " You have either a configuration problem, or a\n"
+ " broken BIOS. You may wish to manually assign\n"
+ " an interrupt to the NCR board rather than using\n"
+ " an automatic setting.\n",
+ host->host_no, host->irq);
+ scsi_unregister (host);
+ return -1;
+ }
+ } else {
+ printk("scsi%d : using interrupt handler previously installed for scsi%d\n",
+ host->host_no, search->host_no);
+ }
+
+
+ if ((hostdata->run_tests && hostdata->run_tests(host) == -1) ||
+ (hostdata->options & OPTION_DEBUG_TESTS_ONLY)) {
+ /* XXX Should disable interrupts, etc. here */
+ scsi_unregister (host);
+ return -1;
+ } else {
+ if (host->io_port) {
+ host->n_io_port = 128;
+ request_region (host->io_port, host->n_io_port, "ncr53c7,8xx");
+ }
+ }
+
+ if (NCR53c7x0_read8 (SBCL_REG) & SBCL_BSY) {
+ printk ("scsi%d : bus wedge, doing SCSI reset\n", host->host_no);
+ hard_reset (host);
+ }
+ return 0;
+}
+
+/*
+ * Function : static int normal_init(Scsi_Host_Template *tpnt, int board,
+ * int chip, u32 base, int io_port, int irq, int dma, int pcivalid,
+ * unsigned char pci_bus, unsigned char pci_device_fn,
+ * long long options);
+ *
+ * Purpose : initializes a NCR53c7,8x0 based on base addresses,
+ * IRQ, and DMA channel.
+ *
+ * Useful where a new NCR chip is backwards compatible with
+ * a supported chip, but the DEVICE ID has changed so it
+ * doesn't show up when the autoprobe does a pcibios_find_device.
+ *
+ * Inputs : tpnt - Template for this SCSI adapter, board - board level
+ * product, chip - 810, 820, or 825, bus - PCI bus, device_fn -
+ * device and function encoding as used by PCI BIOS calls.
+ *
+ * Returns : 0 on success, -1 on failure.
+ *
+ */
+
+static int
+normal_init (Scsi_Host_Template *tpnt, int board, int chip,
+ u32 base, int io_port, int irq, int dma, int pci_valid,
+ unsigned char pci_bus, unsigned char pci_device_fn, long long options) {
+ struct Scsi_Host *instance;
+ struct NCR53c7x0_hostdata *hostdata;
+ char chip_str[80];
+ int script_len = 0, dsa_len = 0, size = 0, max_cmd_size = 0,
+ schedule_size = 0, ok = 0;
+ void *tmp;
+
+ options |= perm_options;
+
+ switch (chip) {
+ case 825:
+ case 820:
+ case 815:
+ case 810:
+ schedule_size = (tpnt->can_queue + 1) * 8 /* JUMP instruction size */;
+ script_len = NCR53c8xx_script_len;
+ dsa_len = NCR53c8xx_dsa_len;
+ options |= OPTION_INTFLY;
+ sprintf (chip_str, "NCR53c%d", chip);
+ break;
+ default:
+ printk("scsi-ncr53c7,8xx : unsupported SCSI chip %d\n", chip);
+ return -1;
+ }
+
+ printk("scsi-ncr53c7,8xx : %s at memory 0x%x, io 0x%x, irq %d",
+ chip_str, (unsigned) base, io_port, irq);
+ if (dma == DMA_NONE)
+ printk("\n");
+ else
+ printk(", dma %d\n", dma);
+
+ if ((chip / 100 == 8) && !pci_valid)
+ printk ("scsi-ncr53c7,8xx : for better reliability and performance, please use the\n"
+ " PCI override instead.\n"
+ " Syntax : ncr53c8{10,15,20,25}=pci,<bus>,<device>,<function>\n"
+ " <bus> and <device> are usually 0.\n");
+
+ if (options & OPTION_DEBUG_PROBE_ONLY) {
+ printk ("scsi-ncr53c7,8xx : probe only enabled, aborting initialization\n");
+ return -1;
+ }
+
+ max_cmd_size = sizeof(struct NCR53c7x0_cmd) + dsa_len +
+ /* Size of dynamic part of command structure : */
+ 2 * /* Worst case : we don't know if we need DATA IN or DATA out */
+ ( 2 * /* Current instructions per scatter/gather segment */
+ tpnt->sg_tablesize +
+ 3 /* Current startup / termination required per phase */
+ ) *
+ 8 /* Each instruction is eight bytes */;
+
+ /* Allocate fixed part of hostdata, dynamic part to hold appropriate
+ SCSI SCRIPT(tm) plus a single, maximum-sized NCR53c7x0_cmd structure.
+
+ We need a NCR53c7x0_cmd structure for scan_scsis() when we are
+ not loaded as a module, and when we're loaded as a module, we
+ can't use a non-dynamically allocated structure because modules
+ are vmalloc()'d, which can allow structures to cross page
+ boundaries and breaks our physical/virtual address assumptions
+ for DMA.
+
+ So, we stick it past the end of our hostdata structure.
+
+ ASSUMPTION :
+ Regardless of how many simultaneous SCSI commands we allow,
+ the probe code only executes a _single_ instruction at a time,
+ so we only need one here, and don't need to allocate NCR53c7x0_cmd
+ structures for each target until we are no longer in scan_scsis
+ and kmalloc() has become functional (memory_init() happens
+ after all device driver initialization).
+ */
+
+ size = sizeof(struct NCR53c7x0_hostdata) + script_len +
+ /* Note that alignment will be guaranteed, since we put the command
+ allocated at probe time after the fixed-up SCSI script, which
+ consists of 32 bit words, aligned on a 32 bit boundary. But
+ on a 64bit machine we need 8 byte alignment for hostdata->free, so
+ we add in another 4 bytes to take care of potential misalignment
+ */
+ (sizeof(void *) - sizeof(u32)) + max_cmd_size + schedule_size;
+
+ instance = scsi_register (tpnt, size);
+ if (!instance)
+ return -1;
+
+ /* FIXME : if we ever support an ISA NCR53c7xx based board, we
+ need to check if the chip is running in a 16 bit mode, and if so
+ unregister it if it is past the 16M (0x1000000) mark */
+
+ hostdata = (struct NCR53c7x0_hostdata *)
+ instance->hostdata;
+ hostdata->size = size;
+ hostdata->script_count = script_len / sizeof(u32);
+ hostdata = (struct NCR53c7x0_hostdata *) instance->hostdata;
+ hostdata->board = board;
+ hostdata->chip = chip;
+ if ((hostdata->pci_valid = pci_valid)) {
+ hostdata->pci_bus = pci_bus;
+ hostdata->pci_device_fn = pci_device_fn;
+ }
+
+ /*
+ * Being memory mapped is more desirable, since
+ *
+ * - Memory accesses may be faster.
+ *
+ * - The destination and source address spaces are the same for
+ * all instructions, meaning we don't have to twiddle dmode or
+ * any other registers.
+ *
+ * So, we try for memory mapped, and if we don't get it,
+ * we go for port mapped, and that failing we tell the user
+ * it can't work.
+ */
+
+ if (base) {
+ instance->base = (unsigned char *) (unsigned long) base;
+ /* Check for forced I/O mapping */
+ if (!(options & OPTION_IO_MAPPED)) {
+ options |= OPTION_MEMORY_MAPPED;
+ ok = 1;
+ }
+ } else {
+ options &= ~OPTION_MEMORY_MAPPED;
+ }
+
+ if (io_port) {
+ instance->io_port = io_port;
+ options |= OPTION_IO_MAPPED;
+ ok = 1;
+ } else {
+ options &= ~OPTION_IO_MAPPED;
+ }
+
+ if (!ok) {
+ printk ("scsi%d : not initializing, no I/O or memory mapping known \n",
+ instance->host_no);
+ scsi_unregister (instance);
+ return -1;
+ }
+ instance->irq = irq;
+ instance->dma_channel = dma;
+
+ hostdata->options = options;
+ hostdata->dsa_len = dsa_len;
+ hostdata->max_cmd_size = max_cmd_size;
+ hostdata->num_cmds = 1;
+ /* Initialize single command */
+ tmp = (hostdata->script + hostdata->script_count);
+ hostdata->free = ROUNDUP(tmp, void *);
+ hostdata->free->real = tmp;
+ hostdata->free->size = max_cmd_size;
+ hostdata->free->free = NULL;
+ hostdata->free->next = NULL;
+ hostdata->extra_allocate = 0;
+
+ /* Allocate command start code space */
+ hostdata->schedule = (chip == 700 || chip == 70066) ?
+ NULL : (u32 *) ((char *)hostdata->free + max_cmd_size);
+
+/*
+ * For diagnostic purposes, we don't really care how fast things blaze.
+ * For profiling, we want to access the 800ns resolution system clock,
+ * using a 'C' call on the host processor.
+ *
+ * Therefore, there's no need for the NCR chip to directly manipulate
+ * this data, and we should put it wherever is most convenient for
+ * Linux.
+ */
+ if (track_events)
+ hostdata->events = (struct NCR53c7x0_event *) (track_events ?
+ vmalloc (sizeof (struct NCR53c7x0_event) * track_events) : NULL);
+ else
+ hostdata->events = NULL;
+
+ if (hostdata->events) {
+ memset ((void *) hostdata->events, 0, sizeof(struct NCR53c7x0_event) *
+ track_events);
+ hostdata->event_size = track_events;
+ hostdata->event_index = 0;
+ } else
+ hostdata->event_size = 0;
+
+ return NCR53c7x0_init(instance);
+}
+
+
+/*
+ * Function : static int ncr_pci_init(Scsi_Host_Template *tpnt, int board,
+ * int chip, int bus, int device_fn, long long options)
+ *
+ * Purpose : initializes a NCR53c800 family based on the PCI
+ * bus, device, and function location of it. Allows
+ * reprogramming of latency timer and determining addresses
+ * and whether bus mastering, etc. are OK.
+ *
+ * Useful where a new NCR chip is backwards compatible with
+ * a supported chip, but the DEVICE ID has changed so it
+ * doesn't show up when the autoprobe does a pcibios_find_device.
+ *
+ * Inputs : tpnt - Template for this SCSI adapter, board - board level
+ * product, chip - 810, 820, or 825, bus - PCI bus, device_fn -
+ * device and function encoding as used by PCI BIOS calls.
+ *
+ * Returns : 0 on success, -1 on failure.
+ *
+ */
+
+static int
+ncr_pci_init (Scsi_Host_Template *tpnt, int board, int chip,
+ unsigned char bus, unsigned char device_fn, long long options) {
+ unsigned short vendor_id, device_id, command;
+#ifdef LINUX_1_2
+ unsigned long
+#else
+ unsigned int
+#endif
+ base, io_port;
+ unsigned char irq, revision;
+ int error, expected_chip;
+ int expected_id = -1, max_revision = -1, min_revision = -1;
+ int i;
+
+ printk("scsi-ncr53c7,8xx : at PCI bus %d, device %d, function %d\n",
+ bus, (int) (device_fn & 0xf8) >> 3,
+ (int) device_fn & 7);
+
+ if (!pcibios_present()) {
+ printk("scsi-ncr53c7,8xx : not initializing due to lack of PCI BIOS,\n"
+ " try using memory, port, irq override instead.\n");
+ return -1;
+ }
+
+ if ((error = pcibios_read_config_word (bus, device_fn, PCI_VENDOR_ID,
+ &vendor_id)) ||
+ (error = pcibios_read_config_word (bus, device_fn, PCI_DEVICE_ID,
+ &device_id)) ||
+ (error = pcibios_read_config_word (bus, device_fn, PCI_COMMAND,
+ &command)) ||
+ (error = pcibios_read_config_dword (bus, device_fn,
+ PCI_BASE_ADDRESS_0, &io_port)) ||
+ (error = pcibios_read_config_dword (bus, device_fn,
+ PCI_BASE_ADDRESS_1, &base)) ||
+ (error = pcibios_read_config_byte (bus, device_fn, PCI_CLASS_REVISION,
+ &revision)) ||
+ (error = pcibios_read_config_byte (bus, device_fn, PCI_INTERRUPT_LINE,
+ &irq))) {
+ printk ("scsi-ncr53c7,8xx : error %s not initializing due to error reading configuration space\n"
+ " perhaps you specified an incorrect PCI bus, device, or function.\n"
+ , pcibios_strerror(error));
+ return -1;
+ }
+
+ /* If any one ever clones the NCR chips, this will have to change */
+
+ if (vendor_id != PCI_VENDOR_ID_NCR) {
+ printk ("scsi-ncr53c7,8xx : not initializing, 0x%04x is not NCR vendor ID\n",
+ (int) vendor_id);
+ return -1;
+ }
+
+
+ /*
+ * Bit 0 is the address space indicator and must be one for I/O
+ * space mappings, bit 1 is reserved, discard them after checking
+ * that they have the correct value of 1.
+ */
+
+ if (command & PCI_COMMAND_IO) {
+ if ((io_port & 3) != 1) {
+ printk ("scsi-ncr53c7,8xx : disabling I/O mapping since base address 0 (0x%x)\n"
+ " bits 0..1 indicate a non-IO mapping\n",
+ (unsigned) io_port);
+ io_port = 0;
+ } else
+ io_port &= PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ io_port = 0;
+ }
+
+ if (command & PCI_COMMAND_MEMORY) {
+ if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+ printk("scsi-ncr53c7,8xx : disabling memory mapping since base address 1\n"
+ " contains a non-memory mapping\n");
+ base = 0;
+ } else
+ base &= PCI_BASE_ADDRESS_MEM_MASK;
+ } else {
+ base = 0;
+ }
+
+ if (!io_port && !base) {
+ printk ("scsi-ncr53c7,8xx : not initializing, both I/O and memory mappings disabled\n");
+ return -1;
+ }
+
+ if (!(command & PCI_COMMAND_MASTER)) {
+ printk ("scsi-ncr53c7,8xx : not initializing, BUS MASTERING was disabled\n");
+ return -1;
+ }
+
+ for (i = 0; i < NPCI_CHIP_IDS; ++i) {
+ if (device_id == pci_chip_ids[i].pci_device_id) {
+ max_revision = pci_chip_ids[i].max_revision;
+ min_revision = pci_chip_ids[i].min_revision;
+ expected_chip = pci_chip_ids[i].chip;
+ }
+ if (chip == pci_chip_ids[i].chip)
+ expected_id = pci_chip_ids[i].pci_device_id;
+ }
+
+ if (chip && device_id != expected_id)
+ printk ("scsi-ncr53c7,8xx : warning : device id of 0x%04x doesn't\n"
+ " match expected 0x%04x\n",
+ (unsigned int) device_id, (unsigned int) expected_id );
+
+ if (max_revision != -1 && revision > max_revision)
+ printk ("scsi-ncr53c7,8xx : warning : revision of %d is greater than %d.\n",
+ (int) revision, max_revision);
+ else if (min_revision != -1 && revision < min_revision)
+ printk ("scsi-ncr53c7,8xx : warning : revision of %d is less than %d.\n",
+ (int) revision, min_revision);
+
+ if (io_port && check_region (io_port, 128)) {
+ printk ("scsi-ncr53c7,8xx : IO region 0x%x to 0x%x is in use\n",
+ (unsigned) io_port, (unsigned) io_port + 127);
+ return -1;
+ }
+
+ return normal_init (tpnt, board, chip, (int) base, io_port,
+ (int) irq, DMA_NONE, 1, bus, device_fn, options);
+}
+
+
+/*
+ * Function : int NCR53c7xx_detect(Scsi_Host_Template *tpnt)
+ *
+ * Purpose : detects and initializes NCR53c7,8x0 SCSI chips
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : tpnt - template for this SCSI adapter
+ *
+ * Returns : number of host adapters detected
+ *
+ */
+
+int
+NCR53c7xx_detect(Scsi_Host_Template *tpnt) {
+ int i;
+ int current_override;
+ int count; /* Number of boards detected */
+ unsigned char pci_bus, pci_device_fn;
+ static short pci_index=0; /* Device index to PCI BIOS calls */
+
+#ifndef LINUX_1_2
+ tpnt->proc_dir = &proc_scsi_ncr53c7xx;
+#endif
+
+ for (current_override = count = 0; current_override < OVERRIDE_LIMIT;
+ ++current_override) {
+ if (overrides[current_override].pci ?
+ !ncr_pci_init (tpnt, overrides[current_override].board,
+ overrides[current_override].chip,
+ (unsigned char) overrides[current_override].data.pci.bus,
+ (((overrides[current_override].data.pci.device
+ << 3) & 0xf8)|(overrides[current_override].data.pci.function &
+ 7)), overrides[current_override].options):
+ !normal_init (tpnt, overrides[current_override].board,
+ overrides[current_override].chip,
+ overrides[current_override].data.normal.base,
+ overrides[current_override].data.normal.io_port,
+ overrides[current_override].data.normal.irq,
+ overrides[current_override].data.normal.dma,
+ 0 /* PCI data invalid */, 0 /* PCI bus place holder */,
+ 0 /* PCI device_function place holder */,
+ overrides[current_override].options)) {
+ ++count;
+ }
+ }
+
+ if (pcibios_present()) {
+ for (i = 0; i < NPCI_CHIP_IDS; ++i)
+ for (pci_index = 0;
+ !pcibios_find_device (PCI_VENDOR_ID_NCR,
+ pci_chip_ids[i].pci_device_id, pci_index, &pci_bus,
+ &pci_device_fn);
+ ++pci_index)
+ if (!ncr_pci_init (tpnt, BOARD_GENERIC, pci_chip_ids[i].chip,
+ pci_bus, pci_device_fn, /* no options */ 0))
+ ++count;
+ }
+ return count;
+}
+
+/* NCR53c810 and NCR53c820 script handling code */
+
+#include "53c8xx_d.h"
+#ifdef A_int_debug_sync
+#define DEBUG_SYNC_INTR A_int_debug_sync
+#endif
+static int NCR53c8xx_script_len = sizeof (SCRIPT);
+static int NCR53c8xx_dsa_len = A_dsa_end + Ent_dsa_zero - Ent_dsa_code_template;
+
+/*
+ * Function : static void NCR53c8x0_init_fixup (struct Scsi_Host *host)
+ *
+ * Purpose : copy and fixup the SCSI SCRIPTS(tm) code for this device.
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ *
+ */
+
+static void
+NCR53c8x0_init_fixup (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned char tmp;
+ int i, ncr_to_memory, memory_to_ncr;
+ u32 base;
+ NCR53c7x0_local_setup(host);
+
+
+ /* XXX - NOTE : this code MUST be made endian aware */
+ /* Copy code into buffer that was allocated at detection time. */
+ memcpy ((void *) hostdata->script, (void *) SCRIPT,
+ sizeof(SCRIPT));
+ /* Fixup labels */
+ for (i = 0; i < PATCHES; ++i)
+ hostdata->script[LABELPATCHES[i]] +=
+ virt_to_bus(hostdata->script);
+ /* Fixup addresses of constants that used to be EXTERNAL */
+
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_abort,
+ virt_to_bus(&(hostdata->NCR53c7xx_msg_abort)));
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_reject,
+ virt_to_bus(&(hostdata->NCR53c7xx_msg_reject)));
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_zero,
+ virt_to_bus(&(hostdata->NCR53c7xx_zero)));
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_sink,
+ virt_to_bus(&(hostdata->NCR53c7xx_sink)));
+ patch_abs_32 (hostdata->script, 0, NOP_insn,
+ virt_to_bus(&(hostdata->NOP_insn)));
+ patch_abs_32 (hostdata->script, 0, schedule,
+ virt_to_bus((void *) hostdata->schedule));
+
+ /* Fixup references to external variables: */
+ for (i = 0; i < EXTERNAL_PATCHES_LEN; ++i)
+ hostdata->script[EXTERNAL_PATCHES[i].offset] +=
+ virt_to_bus(EXTERNAL_PATCHES[i].address);
+
+ /*
+ * Fixup absolutes set at boot-time.
+ *
+ * All non-code absolute variables suffixed with "dsa_" and "int_"
+ * are constants, and need no fixup provided the assembler has done
+ * it for us (I don't know what the "real" NCR assembler does in
+ * this case, my assembler does the right magic).
+ */
+
+ patch_abs_rwri_data (hostdata->script, 0, dsa_save_data_pointer,
+ Ent_dsa_code_save_data_pointer - Ent_dsa_zero);
+ patch_abs_rwri_data (hostdata->script, 0, dsa_restore_pointers,
+ Ent_dsa_code_restore_pointers - Ent_dsa_zero);
+ patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect,
+ Ent_dsa_code_check_reselect - Ent_dsa_zero);
+
+ /*
+ * Just for the hell of it, preserve the settings of
+ * Burst Length and Enable Read Line bits from the DMODE
+ * register. Make sure SCRIPTS start automagically.
+ */
+
+ tmp = NCR53c7x0_read8(DMODE_REG_10);
+ tmp &= (DMODE_800_ERL | DMODE_BL_MASK);
+
+ if (!(hostdata->options & OPTION_MEMORY_MAPPED)) {
+ base = (u32) host->io_port;
+ memory_to_ncr = tmp|DMODE_800_DIOM;
+ ncr_to_memory = tmp|DMODE_800_SIOM;
+ } else {
+ base = virt_to_bus(host->base);
+ memory_to_ncr = ncr_to_memory = tmp;
+ }
+
+ patch_abs_32 (hostdata->script, 0, addr_scratch, base + SCRATCHA_REG_800);
+ patch_abs_32 (hostdata->script, 0, addr_temp, base + TEMP_REG);
+
+ /*
+ * I needed some variables in the script to be accessible to
+ * both the NCR chip and the host processor. For these variables,
+ * I made the arbitrary decision to store them directly in the
+ * hostdata structure rather than in the RELATIVE area of the
+ * SCRIPTS.
+ */
+
+
+ patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_memory, tmp);
+ patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_ncr, memory_to_ncr);
+ patch_abs_rwri_data (hostdata->script, 0, dmode_ncr_to_memory, ncr_to_memory);
+
+ patch_abs_32 (hostdata->script, 0, msg_buf,
+ virt_to_bus((void *)&(hostdata->msg_buf)));
+ patch_abs_32 (hostdata->script, 0, reconnect_dsa_head,
+ virt_to_bus((void *)&(hostdata->reconnect_dsa_head)));
+ patch_abs_32 (hostdata->script, 0, addr_reconnect_dsa_head,
+ virt_to_bus((void *)&(hostdata->addr_reconnect_dsa_head)));
+ patch_abs_32 (hostdata->script, 0, reselected_identify,
+ virt_to_bus((void *)&(hostdata->reselected_identify)));
+/* reselected_tag is currently unused */
+#if 0
+ patch_abs_32 (hostdata->script, 0, reselected_tag,
+ virt_to_bus((void *)&(hostdata->reselected_tag)));
+#endif
+
+ patch_abs_32 (hostdata->script, 0, test_dest,
+ virt_to_bus((void*)&hostdata->test_dest));
+ patch_abs_32 (hostdata->script, 0, test_src,
+ virt_to_bus(&hostdata->test_source));
+
+ patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect,
+ (unsigned char)(Ent_dsa_code_check_reselect - Ent_dsa_zero));
+
+/* These are for event logging; the ncr_event enum contains the
+ actual interrupt numbers. */
+#ifdef A_int_EVENT_SELECT
+ patch_abs_32 (hostdata->script, 0, int_EVENT_SELECT, (u32) EVENT_SELECT);
+#endif
+#ifdef A_int_EVENT_DISCONNECT
+ patch_abs_32 (hostdata->script, 0, int_EVENT_DISCONNECT, (u32) EVENT_DISCONNECT);
+#endif
+#ifdef A_int_EVENT_RESELECT
+ patch_abs_32 (hostdata->script, 0, int_EVENT_RESELECT, (u32) EVENT_RESELECT);
+#endif
+#ifdef A_int_EVENT_COMPLETE
+ patch_abs_32 (hostdata->script, 0, int_EVENT_COMPLETE, (u32) EVENT_COMPLETE);
+#endif
+#ifdef A_int_EVENT_IDLE
+ patch_abs_32 (hostdata->script, 0, int_EVENT_IDLE, (u32) EVENT_IDLE);
+#endif
+#ifdef A_int_EVENT_SELECT_FAILED
+ patch_abs_32 (hostdata->script, 0, int_EVENT_SELECT_FAILED,
+ (u32) EVENT_SELECT_FAILED);
+#endif
+#ifdef A_int_EVENT_BEFORE_SELECT
+ patch_abs_32 (hostdata->script, 0, int_EVENT_BEFORE_SELECT,
+ (u32) EVENT_BEFORE_SELECT);
+#endif
+#ifdef A_int_EVENT_RESELECT_FAILED
+ patch_abs_32 (hostdata->script, 0, int_EVENT_RESELECT_FAILED,
+ (u32) EVENT_RESELECT_FAILED);
+#endif
+
+ /*
+ * Make sure the NCR and Linux code agree on the location of
+ * certain fields.
+ */
+
+ hostdata->E_accept_message = Ent_accept_message;
+ hostdata->E_command_complete = Ent_command_complete;
+ hostdata->E_cmdout_cmdout = Ent_cmdout_cmdout;
+ hostdata->E_data_transfer = Ent_data_transfer;
+ hostdata->E_debug_break = Ent_debug_break;
+ hostdata->E_dsa_code_template = Ent_dsa_code_template;
+ hostdata->E_dsa_code_template_end = Ent_dsa_code_template_end;
+ hostdata->E_end_data_transfer = Ent_end_data_transfer;
+ hostdata->E_initiator_abort = Ent_initiator_abort;
+ hostdata->E_msg_in = Ent_msg_in;
+ hostdata->E_other_transfer = Ent_other_transfer;
+ hostdata->E_other_in = Ent_other_in;
+ hostdata->E_other_out = Ent_other_out;
+ hostdata->E_reject_message = Ent_reject_message;
+ hostdata->E_respond_message = Ent_respond_message;
+ hostdata->E_select = Ent_select;
+ hostdata->E_select_msgout = Ent_select_msgout;
+ hostdata->E_target_abort = Ent_target_abort;
+#ifdef Ent_test_0
+ hostdata->E_test_0 = Ent_test_0;
+#endif
+ hostdata->E_test_1 = Ent_test_1;
+ hostdata->E_test_2 = Ent_test_2;
+#ifdef Ent_test_3
+ hostdata->E_test_3 = Ent_test_3;
+#endif
+ hostdata->E_wait_reselect = Ent_wait_reselect;
+ hostdata->E_dsa_code_begin = Ent_dsa_code_begin;
+
+ hostdata->dsa_cmdout = A_dsa_cmdout;
+ hostdata->dsa_cmnd = A_dsa_cmnd;
+ hostdata->dsa_datain = A_dsa_datain;
+ hostdata->dsa_dataout = A_dsa_dataout;
+ hostdata->dsa_end = A_dsa_end;
+ hostdata->dsa_msgin = A_dsa_msgin;
+ hostdata->dsa_msgout = A_dsa_msgout;
+ hostdata->dsa_msgout_other = A_dsa_msgout_other;
+ hostdata->dsa_next = A_dsa_next;
+ hostdata->dsa_select = A_dsa_select;
+ hostdata->dsa_start = Ent_dsa_code_template - Ent_dsa_zero;
+ hostdata->dsa_status = A_dsa_status;
+ hostdata->dsa_jump_dest = Ent_dsa_code_fix_jump - Ent_dsa_zero +
+ 8 /* destination operand */;
+
+ /* sanity check */
+ if (A_dsa_fields_start != Ent_dsa_code_template_end -
+ Ent_dsa_zero)
+ printk("scsi%d : NCR dsa_fields start is %d not %d\n",
+ host->host_no, A_dsa_fields_start, Ent_dsa_code_template_end -
+ Ent_dsa_zero);
+
+ printk("scsi%d : NCR code relocated to 0x%lx (virt 0x%p)\n", host->host_no,
+ virt_to_bus(hostdata->script), hostdata->script);
+}
+
+/*
+ * Function : static int NCR53c8xx_run_tests (struct Scsi_Host *host)
+ *
+ * Purpose : run various verification tests on the NCR chip,
+ * including interrupt generation, and proper bus mastering
+ * operation.
+ *
+ * Inputs : host - a properly initialized Scsi_Host structure
+ *
+ * Preconditions : the NCR chip must be in a halted state.
+ *
+ * Returns : 0 if all tests were successful, -1 on error.
+ *
+ */
+
+static int
+NCR53c8xx_run_tests (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned long timeout;
+ u32 start;
+ int failed, i;
+ unsigned long flags;
+ NCR53c7x0_local_setup(host);
+
+ /* The NCR chip _must_ be idle to run the test scripts */
+
+ save_flags(flags);
+ cli();
+ if (!hostdata->idle) {
+ printk ("scsi%d : chip not idle, aborting tests\n", host->host_no);
+ restore_flags(flags);
+ return -1;
+ }
+
+ /*
+ * Check for functional interrupts, this could work as an
+ * autoprobe routine.
+ */
+
+ if ((hostdata->options & OPTION_DEBUG_TEST1) &&
+ hostdata->state != STATE_DISABLED) {
+ hostdata->idle = 0;
+ hostdata->test_running = 1;
+ hostdata->test_completed = -1;
+ hostdata->test_dest = 0;
+ hostdata->test_source = 0xdeadbeef;
+ start = virt_to_bus (hostdata->script) + hostdata->E_test_1;
+ hostdata->state = STATE_RUNNING;
+ printk ("scsi%d : test 1", host->host_no);
+ NCR53c7x0_write32 (DSP_REG, start);
+ printk (" started\n");
+ sti();
+
+ /*
+ * This is currently a .5 second timeout, since (in theory) no slow
+ * board will take that long. In practice, we've seen one
+ * pentium which occasionally fails with this, but works with
+ * 10 times as much?
+ */
+
+ timeout = jiffies + 5 * HZ / 10;
+ while ((hostdata->test_completed == -1) && jiffies < timeout)
+ barrier();
+
+ failed = 1;
+ if (hostdata->test_completed == -1)
+ printk ("scsi%d : driver test 1 timed out%s\n",host->host_no ,
+ (hostdata->test_dest == 0xdeadbeef) ?
+ " due to lost interrupt.\n"
+ " Please verify that the correct IRQ is being used for your board,\n"
+ " and that the motherboard IRQ jumpering matches the PCI setup on\n"
+ " PCI systems.\n"
+ " If you are using a NCR53c810 board in a PCI system, you should\n"
+ " also verify that the board is jumpered to use PCI INTA, since\n"
+ " most PCI motherboards lack support for INTB, INTC, and INTD.\n"
+ : "");
+ else if (hostdata->test_completed != 1)
+ printk ("scsi%d : test 1 bad interrupt value (%d)\n",
+ host->host_no, hostdata->test_completed);
+ else
+ failed = (hostdata->test_dest != 0xdeadbeef);
+
+ if (hostdata->test_dest != 0xdeadbeef) {
+ printk ("scsi%d : driver test 1 read 0x%x instead of 0xdeadbeef indicating a\n"
+ " probable cache invalidation problem. Please configure caching\n"
+ " as write-through or disabled\n",
+ host->host_no, hostdata->test_dest);
+ }
+
+ if (failed) {
+ printk ("scsi%d : DSP = 0x%p (script at 0x%p, start at 0x%x)\n",
+ host->host_no, bus_to_virt(NCR53c7x0_read32(DSP_REG)),
+ hostdata->script, start);
+ printk ("scsi%d : DSPS = 0x%x\n", host->host_no,
+ NCR53c7x0_read32(DSPS_REG));
+ restore_flags(flags);
+ return -1;
+ }
+ hostdata->test_running = 0;
+ }
+
+ if ((hostdata->options & OPTION_DEBUG_TEST2) &&
+ hostdata->state != STATE_DISABLED) {
+ u32 dsa[48];
+ unsigned char identify = IDENTIFY(0, 0);
+ unsigned char cmd[6];
+ unsigned char data[36];
+ unsigned char status = 0xff;
+ unsigned char msg = 0xff;
+
+ cmd[0] = INQUIRY;
+ cmd[1] = cmd[2] = cmd[3] = cmd[5] = 0;
+ cmd[4] = sizeof(data);
+
+ dsa[2] = 1;
+ dsa[3] = virt_to_bus(&identify);
+ dsa[4] = 6;
+ dsa[5] = virt_to_bus(&cmd);
+ dsa[6] = sizeof(data);
+ dsa[7] = virt_to_bus(&data);
+ dsa[8] = 1;
+ dsa[9] = virt_to_bus(&status);
+ dsa[10] = 1;
+ dsa[11] = virt_to_bus(&msg);
+
+ for (i = 0; i < 3; ++i) {
+ cli();
+ if (!hostdata->idle) {
+ printk ("scsi%d : chip not idle, aborting tests\n", host->host_no);
+ restore_flags(flags);
+ return -1;
+ }
+
+ /* SCNTL3 SDID */
+ dsa[0] = (0x33 << 24) | (i << 16) ;
+ hostdata->idle = 0;
+ hostdata->test_running = 2;
+ hostdata->test_completed = -1;
+ start = virt_to_bus(hostdata->script) + hostdata->E_test_2;
+ hostdata->state = STATE_RUNNING;
+ NCR53c7x0_write32 (DSA_REG, virt_to_bus(dsa));
+ NCR53c7x0_write32 (DSP_REG, start);
+ sti();
+
+ timeout = jiffies + 5 * HZ; /* arbitrary */
+ while ((hostdata->test_completed == -1) && jiffies < timeout)
+ barrier();
+ NCR53c7x0_write32 (DSA_REG, 0);
+
+ if (hostdata->test_completed == 2) {
+ data[35] = 0;
+ printk ("scsi%d : test 2 INQUIRY to target %d, lun 0 : %s\n",
+ host->host_no, i, data + 8);
+ printk ("scsi%d : status ", host->host_no);
+ print_status (status);
+ printk ("\nscsi%d : message ", host->host_no);
+ print_msg (&msg);
+ printk ("\n");
+ } else if (hostdata->test_completed == 3) {
+ printk("scsi%d : test 2 no connection with target %d\n",
+ host->host_no, i);
+ if (!hostdata->idle) {
+ printk("scsi%d : not idle\n", host->host_no);
+ restore_flags(flags);
+ return -1;
+ }
+ } else if (hostdata->test_completed == -1) {
+ printk ("scsi%d : test 2 timed out\n", host->host_no);
+ restore_flags(flags);
+ return -1;
+ }
+ hostdata->test_running = 0;
+ }
+ }
+
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * Function : static void NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : copy the NCR53c8xx dsa structure into cmd's dsa buffer,
+ * performing all necessary relocation.
+ *
+ * Inputs : cmd, a NCR53c7x0_cmd structure with a dsa area large
+ * enough to hold the NCR53c8xx dsa.
+ */
+
+static void
+NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) {
+ Scsi_Cmnd *c = cmd->cmd;
+ struct Scsi_Host *host = c->host;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int i;
+
+ memcpy (cmd->dsa, hostdata->script + (hostdata->E_dsa_code_template / 4),
+ hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template);
+
+ /*
+ * Note : within the NCR 'C' code, dsa points to the _start_
+ * of the DSA structure, and _not_ the offset of dsa_zero within
+ * that structure used to facilitate shorter signed offsets
+ * for the 8 bit ALU.
+ *
+ * The implications of this are that
+ *
+ * - 32 bit A_dsa_* absolute values require an additional
+ * dsa_zero added to their value to be correct, since they are
+ * relative to dsa_zero which is in essentially a separate
+ * space from the code symbols.
+ *
+ * - All other symbols require no special treatment.
+ */
+
+ patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_lun, c->lun);
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_next, virt_to_bus(&cmd->dsa_next_addr));
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_next, virt_to_bus(cmd->dsa) + Ent_dsa_zero -
+ Ent_dsa_code_template + A_dsa_next);
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_sync, virt_to_bus((void *)hostdata->sync[c->target].script));
+ patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_target, c->target);
+ /* XXX - new pointer stuff */
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_saved_pointer, virt_to_bus(&cmd->saved_data_pointer));
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_saved_residual, virt_to_bus(&cmd->saved_residual));
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_residual, virt_to_bus(&cmd->residual));
+
+ /* XXX - new start stuff */
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_dsa_value, virt_to_bus(&cmd->dsa_addr));
+
+}
+
+/*
+ * Function : run_process_issue_queue (void)
+ *
+ * Purpose : insure that the coroutine is running and will process our
+ * request. process_issue_queue_running is checked/set here (in an
+ * inline function) rather than in process_issue_queue itself to reduce
+ * the chances of stack overflow.
+ *
+ */
+
+static volatile int process_issue_queue_running = 0;
+
+static __inline__ void
+run_process_issue_queue(void) {
+ unsigned long flags;
+ save_flags (flags);
+ cli();
+ if (!process_issue_queue_running) {
+ process_issue_queue_running = 1;
+ process_issue_queue(flags);
+ /*
+ * process_issue_queue_running is cleared in process_issue_queue
+ * once it can't do more work, and process_issue_queue exits with
+ * interrupts disabled.
+ */
+ }
+ restore_flags (flags);
+}
+
+/*
+ * Function : static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int
+ * result)
+ *
+ * Purpose : mark SCSI command as finished, OR'ing the host portion
+ * of the result word into the result field of the corresponding
+ * Scsi_Cmnd structure, and removing it from the internal queues.
+ *
+ * Inputs : cmd - command, result - entire result field
+ *
+ * Preconditions : the NCR chip should be in a halted state when
+ * abnormal_finished is run, since it modifies structures which
+ * the NCR expects to have exclusive access to.
+ */
+
+static void
+abnormal_finished (struct NCR53c7x0_cmd *cmd, int result) {
+ Scsi_Cmnd *c = cmd->cmd;
+ struct Scsi_Host *host = c->host;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned long flags;
+ int left, found;
+ volatile struct NCR53c7x0_cmd * linux_search;
+ volatile struct NCR53c7x0_cmd * volatile *linux_prev;
+ volatile u32 *ncr_prev, *current, ncr_search;
+
+#if 0
+ printk ("scsi%d: abnormal finished\n", host->host_no);
+#endif
+
+ save_flags(flags);
+ cli();
+ found = 0;
+ /*
+ * Traverse the NCR issue array until we find a match or run out
+ * of instructions. Instructions in the NCR issue array are
+ * either JUMP or NOP instructions, which are 2 words in length.
+ */
+
+
+ for (found = 0, left = host->can_queue, current = hostdata->schedule;
+ left > 0; --left, current += 2)
+ {
+ if (issue_to_cmd (host, hostdata, (u32 *) current) == cmd)
+ {
+ current[0] = hostdata->NOP_insn;
+ current[1] = 0xdeadbeef;
+ ++found;
+ break;
+ }
+ }
+
+ /*
+ * Traverse the NCR reconnect list of DSA structures until we find
+ * a pointer to this dsa or have found too many command structures.
+ * We let prev point at the next field of the previous element or
+ * head of the list, so we don't do anything different for removing
+ * the head element.
+ */
+
+ for (left = host->can_queue,
+ ncr_search = hostdata->reconnect_dsa_head,
+ ncr_prev = &hostdata->reconnect_dsa_head;
+ left >= 0 && ncr_search &&
+ ((char*)bus_to_virt(ncr_search) + hostdata->dsa_start)
+ != (char *) cmd->dsa;
+ ncr_prev = (u32*) ((char*)bus_to_virt(ncr_search) +
+ hostdata->dsa_next), ncr_search = *ncr_prev, --left);
+
+ if (left < 0)
+ printk("scsi%d: loop detected in ncr reconnect list\n",
+ host->host_no);
+ else if (ncr_search)
+ if (found)
+ printk("scsi%d: scsi %ld in ncr issue array and reconnect lists\n",
+ host->host_no, c->pid);
+ else {
+ volatile u32 * next = (u32 *)
+ ((char *)bus_to_virt(ncr_search) + hostdata->dsa_next);
+ *ncr_prev = *next;
+/* If we're at the tail end of the issue queue, update that pointer too. */
+ found = 1;
+ }
+
+ /*
+ * Traverse the host running list until we find this command or discover
+ * we have too many elements, pointing linux_prev at the next field of the
+ * linux_previous element or head of the list, search at this element.
+ */
+
+ for (left = host->can_queue, linux_search = hostdata->running_list,
+ linux_prev = &hostdata->running_list;
+ left >= 0 && linux_search && linux_search != cmd;
+ linux_prev = &(linux_search->next),
+ linux_search = linux_search->next, --left);
+
+ if (left < 0)
+ printk ("scsi%d: loop detected in host running list for scsi pid %ld\n",
+ host->host_no, c->pid);
+ else if (linux_search) {
+ *linux_prev = linux_search->next;
+ --hostdata->busy[c->target][c->lun];
+ }
+
+ /* Return the NCR command structure to the free list */
+ cmd->next = hostdata->free;
+ hostdata->free = cmd;
+ c->host_scribble = NULL;
+
+ /* And return */
+ c->result = result;
+ c->scsi_done(c);
+
+ restore_flags(flags);
+ run_process_issue_queue();
+}
+
+/*
+ * Function : static void intr_break (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : Handler for breakpoint interrupts from a SCSI script
+ *
+ * Inputs : host - pointer to this host adapter's structure,
+ * cmd - pointer to the command (if any) dsa was pointing
+ * to.
+ *
+ */
+
+static void
+intr_break (struct Scsi_Host *host, struct
+ NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_break *bp;
+#if 0
+ Scsi_Cmnd *c = cmd ? cmd->cmd : NULL;
+#endif
+ u32 *dsp;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned long flags;
+ NCR53c7x0_local_setup(host);
+
+ /*
+ * Find the break point corresponding to this address, and
+ * dump the appropriate debugging information to standard
+ * output.
+ */
+ save_flags(flags);
+ cli();
+ dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG));
+ for (bp = hostdata->breakpoints; bp && bp->address != dsp;
+ bp = bp->next);
+ if (!bp)
+ panic("scsi%d : break point interrupt from %p with no breakpoint!",
+ host->host_no, dsp);
+
+ /*
+ * Configure the NCR chip for manual start mode, so that we can
+ * point the DSP register at the instruction that follows the
+ * INT int_debug_break instruction.
+ */
+
+ NCR53c7x0_write8 (hostdata->dmode,
+ NCR53c7x0_read8(hostdata->dmode)|DMODE_MAN);
+
+ /*
+ * And update the DSP register, using the size of the old
+ * instruction in bytes.
+ */
+
+ restore_flags(flags);
+}
+/*
+ * Function : static void print_synchronous (const char *prefix,
+ * const unsigned char *msg)
+ *
+ * Purpose : print a pretty, user and machine parsable representation
+ * of a SDTR message, including the "real" parameters, data
+ * clock so we can tell transfer rate at a glance.
+ *
+ * Inputs ; prefix - text to prepend, msg - SDTR message (5 bytes)
+ */
+
+static void
+print_synchronous (const char *prefix, const unsigned char *msg) {
+ if (msg[4]) {
+ int Hz = 1000000000 / (msg[3] * 4);
+ int integer = Hz / 1000000;
+ int fraction = (Hz - (integer * 1000000)) / 10000;
+ printk ("%speriod %dns offset %d %d.%02dMHz %s SCSI%s\n",
+ prefix, (int) msg[3] * 4, (int) msg[4], integer, fraction,
+ (((msg[3] * 4) < 200) ? "FAST" : "synchronous"),
+ (((msg[3] * 4) < 200) ? "-II" : ""));
+ } else
+ printk ("%sasynchronous SCSI\n", prefix);
+}
+
+/*
+ * Function : static void set_synchronous (struct Scsi_Host *host,
+ * int target, int sxfer, int scntl3, int now_connected)
+ *
+ * Purpose : reprogram transfers between the selected SCSI initiator and
+ * target with the given register values; in the indirect
+ * select operand, reselection script, and chip registers.
+ *
+ * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id,
+ * sxfer and scntl3 - NCR registers. now_connected - if non-zero,
+ * we should reprogram the registers now too.
+ */
+
+static void
+set_synchronous (struct Scsi_Host *host, int target, int sxfer, int scntl3,
+ int now_connected) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ u32 *script;
+ NCR53c7x0_local_setup(host);
+
+ /* These are eight bit registers */
+ sxfer &= 0xff;
+ scntl3 &= 0xff;
+
+ hostdata->sync[target].sxfer_sanity = sxfer;
+ hostdata->sync[target].scntl3_sanity = scntl3;
+
+/*
+ * HARD CODED : synchronous script is EIGHT words long. This
+ * must agree with 53c7.8xx.h
+ */
+
+ if ((hostdata->chip != 700) && (hostdata->chip != 70066)) {
+ hostdata->sync[target].select_indirect = (scntl3 << 24) |
+ (target << 16) | (sxfer << 8);
+
+ script = (u32 *) hostdata->sync[target].script;
+
+ /* XXX - add NCR53c7x0 code to reprogram SCF bits if we want to */
+ if ((hostdata->chip / 100) == 8) {
+ script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY |
+ DCMD_RWRI_OP_MOVE) << 24) |
+ (SCNTL3_REG_800 << 16) | (scntl3 << 8);
+ script[1] = 0;
+ script += 2;
+ }
+
+ script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY |
+ DCMD_RWRI_OP_MOVE) << 24) |
+ (SXFER_REG << 16) | (sxfer << 8);
+ script[1] = 0;
+ script += 2;
+
+#ifdef DEBUG_SYNC_INTR
+ if (hostdata->options & OPTION_DEBUG_DISCONNECT) {
+ script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_INT) << 24) | DBC_TCI_TRUE;
+ script[1] = DEBUG_SYNC_INTR;
+ script += 2;
+ }
+#endif
+
+ script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_RETURN) << 24) | DBC_TCI_TRUE;
+ script[1] = 0;
+ script += 2;
+ }
+
+ if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS)
+ printk ("scsi%d : target %d sync parameters are sxfer=0x%x, scntl3=0x%x\n",
+ host->host_no, target, sxfer, scntl3);
+
+ if (now_connected) {
+ if ((hostdata->chip / 100) == 8)
+ NCR53c7x0_write8(SCNTL3_REG_800, scntl3);
+ NCR53c7x0_write8(SXFER_REG, sxfer);
+ }
+}
+
+
+/*
+ * Function : static int asynchronous (struct Scsi_Host *host, int target)
+ *
+ * Purpose : reprogram between the selected SCSI Host adapter and target
+ * (assumed to be currently connected) for asynchronous transfers.
+ *
+ * Inputs : host - SCSI host structure, target - numeric target ID.
+ *
+ * Preconditions : the NCR chip should be in one of the halted states
+ */
+
+static void
+asynchronous (struct Scsi_Host *host, int target) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ NCR53c7x0_local_setup(host);
+ set_synchronous (host, target, /* no offset */ 0, hostdata->saved_scntl3,
+ 1);
+ printk ("scsi%d : setting target %d to asynchronous SCSI\n",
+ host->host_no, target);
+}
+
+/*
+ * XXX - do we want to go out of our way (ie, add extra code to selection
+ * in the NCR53c710/NCR53c720 script) to reprogram the synchronous
+ * conversion bits, or can we be content in just setting the
+ * sxfer bits?
+ */
+
+/* Table for NCR53c8xx synchronous values */
+static const struct {
+ int div; /* Total clock divisor * 10 */
+ unsigned char scf; /* */
+ unsigned char tp; /* 4 + tp = xferp divisor */
+} syncs[] = {
+/* div scf tp div scf tp div scf tp */
+ { 40, 1, 0}, { 50, 1, 1}, { 60, 1, 2},
+ { 70, 1, 3}, { 75, 2, 1}, { 80, 1, 4},
+ { 90, 1, 5}, { 100, 1, 6}, { 105, 2, 3},
+ { 110, 1, 7}, { 120, 2, 4}, { 135, 2, 5},
+ { 140, 3, 3}, { 150, 2, 6}, { 160, 3, 4},
+ { 165, 2, 7}, { 180, 3, 5}, { 200, 3, 6},
+ { 210, 4, 3}, { 220, 3, 7}, { 240, 4, 4},
+ { 270, 4, 5}, { 300, 4, 6}, { 330, 4, 7}
+};
+
+/*
+ * Function : static void synchronous (struct Scsi_Host *host, int target,
+ * char *msg)
+ *
+ * Purpose : reprogram transfers between the selected SCSI initiator and
+ * target for synchronous SCSI transfers such that the synchronous
+ * offset is less than that requested and period at least as long
+ * as that requested. Also modify *msg such that it contains
+ * an appropriate response.
+ *
+ * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id,
+ * msg - synchronous transfer request.
+ */
+
+
+static void
+synchronous (struct Scsi_Host *host, int target, char *msg) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int desire, divisor, i, limit;
+ unsigned char scntl3, sxfer;
+/* The diagnostic message fits on one line, even with max. width integers */
+ char buf[80];
+
+/* Desired transfer clock in Hz */
+ desire = 1000000000L / (msg[3] * 4);
+/* Scale the available SCSI clock by 10 so we get tenths */
+ divisor = (hostdata->scsi_clock * 10) / desire;
+
+/* NCR chips can handle at most an offset of 8 */
+ if (msg[4] > 8)
+ msg[4] = 8;
+
+ if (hostdata->options & OPTION_DEBUG_SDTR)
+ printk("scsi%d : optimal synchronous divisor of %d.%01d\n",
+ host->host_no, divisor / 10, divisor % 10);
+
+ limit = (sizeof(syncs) / sizeof(syncs[0]) -1);
+ for (i = 0; (i < limit) && (divisor > syncs[i].div); ++i);
+
+ if (hostdata->options & OPTION_DEBUG_SDTR)
+ printk("scsi%d : selected synchronous divisor of %d.%01d\n",
+ host->host_no, syncs[i].div / 10, syncs[i].div % 10);
+
+ msg[3] = ((1000000000L / hostdata->scsi_clock) * syncs[i].div / 10 / 4);
+
+ if (hostdata->options & OPTION_DEBUG_SDTR)
+ printk("scsi%d : selected synchronous period of %dns\n", host->host_no,
+ msg[3] * 4);
+
+ scntl3 = (hostdata->chip / 100 == 8) ? ((hostdata->saved_scntl3 &
+ ~SCNTL3_800_SCF_MASK) | (syncs[i].scf << SCNTL3_800_SCF_SHIFT)) : 0;
+ sxfer = (msg[4] << SXFER_MO_SHIFT) | ((syncs[i].tp) << SXFER_TP_SHIFT);
+ if (hostdata->options & OPTION_DEBUG_SDTR)
+ printk ("scsi%d : sxfer=0x%x scntl3=0x%x\n",
+ host->host_no, (int) sxfer, (int) scntl3);
+ set_synchronous (host, target, sxfer, scntl3, 1);
+ sprintf (buf, "scsi%d : setting target %d to ", host->host_no, target);
+ print_synchronous (buf, msg);
+}
+
+/*
+ * Function : static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : Handler for INT generated instructions for the
+ * NCR53c810/820 SCSI SCRIPT
+ *
+ * Inputs : host - pointer to this host adapter's structure,
+ * cmd - pointer to the command (if any) dsa was pointing
+ * to.
+ *
+ */
+
+static int
+NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
+ NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ int print;
+ Scsi_Cmnd *c = cmd ? cmd->cmd : NULL;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ u32 dsps,*dsp; /* Argument of the INT instruction */
+ NCR53c7x0_local_setup(host);
+ dsps = NCR53c7x0_read32(DSPS_REG);
+ dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG));
+
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : DSPS = 0x%x\n", host->host_no, dsps);
+
+ switch (dsps) {
+ case A_int_msg_1:
+ print = 1;
+ switch (hostdata->msg_buf[0]) {
+ /*
+ * Unless we've initiated synchronous negotiation, I don't
+ * think that this should happen.
+ */
+ case MESSAGE_REJECT:
+ hostdata->dsp = hostdata->script + hostdata->E_accept_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ if (cmd && (cmd->flags & CMD_FLAG_SDTR)) {
+ printk ("scsi%d : target %d rejected SDTR\n", host->host_no,
+ c->target);
+ cmd->flags &= ~CMD_FLAG_SDTR;
+ asynchronous (host, c->target);
+ print = 0;
+ }
+ break;
+ case INITIATE_RECOVERY:
+ printk ("scsi%d : extended contingent allegiance not supported yet, rejecting\n",
+ host->host_no);
+ /* Fall through to default */
+ hostdata->dsp = hostdata->script + hostdata->E_reject_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ break;
+ default:
+ printk ("scsi%d : unsupported message, rejecting\n",
+ host->host_no);
+ hostdata->dsp = hostdata->script + hostdata->E_reject_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ }
+ if (print) {
+ printk ("scsi%d : received message", host->host_no);
+ if (c)
+ printk (" from target %d lun %d ", c->target, c->lun);
+ print_msg ((unsigned char *) hostdata->msg_buf);
+ printk("\n");
+ }
+
+ return SPECIFIC_INT_NOTHING;
+
+
+ case A_int_msg_sdtr:
+/*
+ * At this point, hostdata->msg_buf contains
+ * 0 EXTENDED MESSAGE
+ * 1 length
+ * 2 SDTR
+ * 3 period * 4ns
+ * 4 offset
+ */
+
+ if (cmd) {
+ char buf[80];
+ sprintf (buf, "scsi%d : target %d %s ", host->host_no, c->target,
+ (cmd->flags & CMD_FLAG_SDTR) ? "accepting" : "requesting");
+ print_synchronous (buf, (unsigned char *) hostdata->msg_buf);
+
+ /*
+ * Initiator initiated, won't happen unless synchronous
+ * transfers are enabled. If we get a SDTR message in
+ * response to our SDTR, we should program our parameters
+ * such that
+ * offset <= requested offset
+ * period >= requested period
+ */
+ if (cmd->flags & CMD_FLAG_SDTR) {
+ cmd->flags &= ~CMD_FLAG_SDTR;
+ if (hostdata->msg_buf[4])
+ synchronous (host, c->target, (unsigned char *)
+ hostdata->msg_buf);
+ else
+ asynchronous (host, c->target);
+ hostdata->dsp = hostdata->script + hostdata->E_accept_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+ } else {
+ if (hostdata->options & OPTION_SYNCHRONOUS) {
+ cmd->flags |= CMD_FLAG_DID_SDTR;
+ synchronous (host, c->target, (unsigned char *)
+ hostdata->msg_buf);
+ } else {
+ hostdata->msg_buf[4] = 0; /* 0 offset = async */
+ asynchronous (host, c->target);
+ }
+ patch_dsa_32 (cmd->dsa, dsa_msgout_other, 0, 5);
+ patch_dsa_32 (cmd->dsa, dsa_msgout_other, 1, (u32)
+ virt_to_bus ((void *)&hostdata->msg_buf));
+ hostdata->dsp = hostdata->script +
+ hostdata->E_respond_message / sizeof(u32);
+ hostdata->dsp_changed = 1;
+ }
+ return SPECIFIC_INT_NOTHING;
+ }
+ /* Fall through to abort if we couldn't find a cmd, and
+ therefore a dsa structure to twiddle */
+ case A_int_msg_wdtr:
+ hostdata->dsp = hostdata->script + hostdata->E_reject_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+ case A_int_err_unexpected_phase:
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : unexpected phase\n", host->host_no);
+ return SPECIFIC_INT_ABORT;
+ case A_int_err_selected:
+ printk ("scsi%d : selected by target %d\n", host->host_no,
+ (int) NCR53c7x0_read8(SDID_REG_800) &7);
+ hostdata->dsp = hostdata->script + hostdata->E_target_abort /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+ case A_int_err_unexpected_reselect:
+ printk ("scsi%d : unexpected reselect by target %d lun %d\n",
+ host->host_no, (int) NCR53c7x0_read8(SDID_REG_800) & 7,
+ hostdata->reselected_identify & 7);
+ hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+/*
+ * Since contingent allegiance conditions are cleared by the next
+ * command issued to a target, we must issue a REQUEST SENSE
+ * command after receiving a CHECK CONDITION status, before
+ * another command is issued.
+ *
+ * Since this NCR53c7x0_cmd will be freed after use, we don't
+ * care if we step on the various fields, so modify a few things.
+ */
+ case A_int_err_check_condition:
+#if 0
+ if (hostdata->options & OPTION_DEBUG_INTR)
+#endif
+ printk ("scsi%d : CHECK CONDITION\n", host->host_no);
+ if (!c) {
+ printk("scsi%d : CHECK CONDITION with no SCSI command\n",
+ host->host_no);
+ return SPECIFIC_INT_PANIC;
+ }
+
+ /*
+ * FIXME : this uses the normal one-byte selection message.
+ * We may want to renegotiate for synchronous & WIDE transfers
+ * since these could be the crux of our problem.
+ *
+ hostdata->NOP_insn* FIXME : once SCSI-II tagged queuing is implemented, we'll
+ * have to set this up so that the rest of the DSA
+ * agrees with this being an untagged queue'd command.
+ */
+
+ patch_dsa_32 (cmd->dsa, dsa_msgout, 0, 1);
+
+ /*
+ * Modify the table indirect for COMMAND OUT phase, since
+ * Request Sense is a six byte command.
+ */
+
+ patch_dsa_32 (cmd->dsa, dsa_cmdout, 0, 6);
+
+ c->cmnd[0] = REQUEST_SENSE;
+ c->cmnd[1] &= 0xe0; /* Zero all but LUN */
+ c->cmnd[2] = 0;
+ c->cmnd[3] = 0;
+ c->cmnd[4] = sizeof(c->sense_buffer);
+ c->cmnd[5] = 0;
+
+ /*
+ * Disable dataout phase, and program datain to transfer to the
+ * sense buffer, and add a jump to other_transfer after the
+ * command so overflow/underrun conditions are detected.
+ */
+
+ patch_dsa_32 (cmd->dsa, dsa_dataout, 0,
+ virt_to_bus(hostdata->script) + hostdata->E_other_transfer);
+ patch_dsa_32 (cmd->dsa, dsa_datain, 0,
+ virt_to_bus(cmd->data_transfer_start));
+ cmd->data_transfer_start[0] = (((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I |
+ DCMD_BMI_IO)) << 24) | sizeof(c->sense_buffer);
+ cmd->data_transfer_start[1] = (u32) virt_to_bus(c->sense_buffer);
+
+ cmd->data_transfer_start[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP)
+ << 24) | DBC_TCI_TRUE;
+ cmd->data_transfer_start[3] = (u32) virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer;
+
+ /*
+ * Currently, this command is flagged as completed, ie
+ * it has valid status and message data. Reflag it as
+ * incomplete. Q - need to do something so that original
+ * status, etc are used.
+ */
+
+ cmd->cmd->result = 0xffff;
+
+ /*
+ * Restart command as a REQUEST SENSE.
+ */
+ hostdata->dsp = (u32 *) hostdata->script + hostdata->E_select /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+ case A_int_debug_break:
+ return SPECIFIC_INT_BREAK;
+ case A_int_norm_aborted:
+ hostdata->dsp = (u32 *) hostdata->schedule;
+ hostdata->dsp_changed = 1;
+ if (cmd)
+ abnormal_finished (cmd, DID_ERROR << 16);
+ return SPECIFIC_INT_NOTHING;
+ case A_int_test_1:
+ case A_int_test_2:
+ hostdata->idle = 1;
+ hostdata->test_completed = (dsps - A_int_test_1) / 0x00010000 + 1;
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk("scsi%d : test%d complete\n", host->host_no,
+ hostdata->test_completed);
+ return SPECIFIC_INT_NOTHING;
+#ifdef A_int_debug_reselected_ok
+ case A_int_debug_reselected_ok:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ /*
+ * Note - this dsa is not based on location relative to
+ * the command structure, but to location relative to the
+ * DSA register
+ */
+ u32 *dsa;
+ dsa = (u32 *) bus_to_virt (NCR53c7x0_read32(DSA_REG));
+
+ printk("scsi%d : reselected_ok (DSA = 0x%x (virt 0x%p)\n",
+ host->host_no, NCR53c7x0_read32(DSA_REG), dsa);
+ printk("scsi%d : resume address is 0x%x (virt 0x%p)\n",
+ host->host_no, cmd->saved_data_pointer,
+ bus_to_virt(cmd->saved_data_pointer));
+ print_insn (host, hostdata->script + Ent_reselected_ok /
+ sizeof(u32), "", 1);
+ printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n",
+ host->host_no, NCR53c7x0_read8(SXFER_REG),
+ NCR53c7x0_read8(SCNTL3_REG_800));
+ if (c) {
+ print_insn (host, (u32 *)
+ hostdata->sync[c->target].script, "", 1);
+ print_insn (host, (u32 *)
+ hostdata->sync[c->target].script + 2, "", 1);
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_reselect_check
+ case A_int_debug_reselect_check:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ u32 *dsa;
+#if 0
+ u32 *code;
+#endif
+ /*
+ * Note - this dsa is not based on location relative to
+ * the command structure, but to location relative to the
+ * DSA register
+ */
+ dsa = bus_to_virt (NCR53c7x0_read32(DSA_REG));
+ printk("scsi%d : reselected_check_next (DSA = 0x%lx (virt 0x%p))\n",
+ host->host_no, virt_to_bus(dsa), dsa);
+ if (dsa) {
+ printk("scsi%d : resume address is 0x%x (virt 0x%p)\n",
+ host->host_no, cmd->saved_data_pointer,
+ bus_to_virt (cmd->saved_data_pointer));
+#if 0
+ printk("scsi%d : template code :\n", host->host_no);
+ for (code = dsa + (Ent_dsa_code_check_reselect - Ent_dsa_zero)
+ / sizeof(u32); code < (dsa + Ent_dsa_zero / sizeof(u32));
+ code += print_insn (host, code, "", 1));
+#endif
+ }
+ print_insn (host, hostdata->script + Ent_reselected_ok /
+ sizeof(u32), "", 1);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_dsa_schedule
+ case A_int_debug_dsa_schedule:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ u32 *dsa;
+ /*
+ * Note - this dsa is not based on location relative to
+ * the command structure, but to location relative to the
+ * DSA register
+ */
+ dsa = (u32 *) bus_to_virt (NCR53c7x0_read32(DSA_REG));
+ printk("scsi%d : dsa_schedule (old DSA = 0x%lx (virt 0x%p))\n",
+ host->host_no, virt_to_bus(dsa), dsa);
+ if (dsa)
+ printk("scsi%d : resume address is 0x%x (virt 0x%p)\n"
+ " (temp was 0x%x (virt 0x%p))\n",
+ host->host_no, cmd->saved_data_pointer,
+ bus_to_virt (cmd->saved_data_pointer),
+ NCR53c7x0_read32 (TEMP_REG),
+ bus_to_virt (NCR53c7x0_read32(TEMP_REG)));
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_scheduled
+ case A_int_debug_scheduled:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ printk("scsi%d : new I/O 0x%x (virt 0x%p) scheduled\n",
+ host->host_no, NCR53c7x0_read32(DSA_REG),
+ bus_to_virt(NCR53c7x0_read32(DSA_REG)));
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_idle
+ case A_int_debug_idle:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ printk("scsi%d : idle\n", host->host_no);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_cmd
+ case A_int_debug_cmd:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ printk("scsi%d : command sent\n");
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_dsa_loaded
+ case A_int_debug_dsa_loaded:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ printk("scsi%d : DSA loaded with 0x%x (virt 0x%p)\n", host->host_no,
+ NCR53c7x0_read32(DSA_REG),
+ bus_to_virt(NCR53c7x0_read32(DSA_REG)));
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_reselected
+ case A_int_debug_reselected:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ printk("scsi%d : reselected by target %d lun %d\n",
+ host->host_no, (int) NCR53c7x0_read8(SDID_REG_800) & ~0x80,
+ (int) hostdata->reselected_identify & 7);
+ print_queues(host);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_disconnect_msg
+ case A_int_debug_disconnect_msg:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ if (c)
+ printk("scsi%d : target %d lun %d disconnecting\n",
+ host->host_no, c->target, c->lun);
+ else
+ printk("scsi%d : unknown target disconnecting\n",
+ host->host_no);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_disconnected
+ case A_int_debug_disconnected:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ printk ("scsi%d : disconnected, new queues are\n",
+ host->host_no);
+ print_queues(host);
+#if 0
+ printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n",
+ host->host_no, NCR53c7x0_read8(SXFER_REG),
+ NCR53c7x0_read8(SCNTL3_REG_800));
+#endif
+ if (c) {
+ print_insn (host, (u32 *)
+ hostdata->sync[c->target].script, "", 1);
+ print_insn (host, (u32 *)
+ hostdata->sync[c->target].script + 2, "", 1);
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_panic
+ case A_int_debug_panic:
+ printk("scsi%d : int_debug_panic received\n", host->host_no);
+ print_lots (host);
+ return SPECIFIC_INT_PANIC;
+#endif
+#ifdef A_int_debug_saved
+ case A_int_debug_saved:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ printk ("scsi%d : saved data pointer 0x%x (virt 0x%p)\n",
+ host->host_no, cmd->saved_data_pointer,
+ bus_to_virt (cmd->saved_data_pointer));
+ print_progress (c);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_restored
+ case A_int_debug_restored:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ if (cmd) {
+ int size;
+ printk ("scsi%d : restored data pointer 0x%x (virt 0x%p)\n",
+ host->host_no, cmd->saved_data_pointer, bus_to_virt (
+ cmd->saved_data_pointer));
+ size = print_insn (host, (u32 *)
+ bus_to_virt(cmd->saved_data_pointer), "", 1);
+ size = print_insn (host, (u32 *)
+ bus_to_virt(cmd->saved_data_pointer) + size, "", 1);
+ print_progress (c);
+ }
+#if 0
+ printk ("scsi%d : datapath residual %d\n",
+ host->host_no, datapath_residual (host)) ;
+#endif
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_sync
+ case A_int_debug_sync:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT|OPTION_DEBUG_SDTR)) {
+ unsigned char sxfer = NCR53c7x0_read8 (SXFER_REG),
+ scntl3 = NCR53c7x0_read8 (SCNTL3_REG_800);
+ if (c) {
+ if (sxfer != hostdata->sync[c->target].sxfer_sanity ||
+ scntl3 != hostdata->sync[c->target].scntl3_sanity) {
+ printk ("scsi%d : sync sanity check failed sxfer=0x%x, scntl3=0x%x",
+ host->host_no, sxfer, scntl3);
+ NCR53c7x0_write8 (SXFER_REG, sxfer);
+ NCR53c7x0_write8 (SCNTL3_REG_800, scntl3);
+ }
+ } else
+ printk ("scsi%d : unknown command sxfer=0x%x, scntl3=0x%x\n",
+ host->host_no, (int) sxfer, (int) scntl3);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_datain
+ case A_int_debug_datain:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT|OPTION_DEBUG_SDTR)) {
+ int size;
+ printk ("scsi%d : In do_datain (%s) sxfer=0x%x, scntl3=0x%x\n"
+ " datapath residual=%d\n",
+ host->host_no, sbcl_to_phase (NCR53c7x0_read8 (SBCL_REG)),
+ (int) NCR53c7x0_read8(SXFER_REG),
+ (int) NCR53c7x0_read8(SCNTL3_REG_800),
+ datapath_residual (host)) ;
+ print_insn (host, dsp, "", 1);
+ size = print_insn (host, (u32 *) bus_to_virt(dsp[1]), "", 1);
+ print_insn (host, (u32 *) bus_to_virt(dsp[1]) + size, "", 1);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+/*
+ * FIXME : for 7xx support, we need to read SDID_REG_700 and handle
+ * the comparison as bitfielded, not binary.
+ */
+#ifdef A_int_debug_check_dsa
+ case A_int_debug_check_dsa:
+ if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) {
+ int sdid = NCR53c7x0_read8 (SDID_REG_800) & 15;
+ char *where = dsp - NCR53c7x0_insn_size(NCR53c7x0_read8
+ (DCMD_REG)) == hostdata->script +
+ Ent_select_check_dsa / sizeof(u32) ?
+ "selection" : "reselection";
+ if (c && sdid != c->target) {
+ printk ("scsi%d : SDID target %d != DSA target %d at %s\n",
+ host->host_no, sdid, c->target, where);
+ print_lots(host);
+ dump_events (host, 20);
+ return SPECIFIC_INT_PANIC;
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+ default:
+ if ((dsps & 0xff000000) == 0x03000000) {
+ printk ("scsi%d : misc debug interrupt 0x%x\n",
+ host->host_no, dsps);
+ return SPECIFIC_INT_RESTART;
+ } else if ((dsps & 0xff000000) == 0x05000000) {
+ if (hostdata->events) {
+ struct NCR53c7x0_event *event;
+ ++hostdata->event_index;
+ if (hostdata->event_index >= hostdata->event_size)
+ hostdata->event_index = 0;
+ event = (struct NCR53c7x0_event *) hostdata->events +
+ hostdata->event_index;
+ event->event = (enum ncr_event) dsps;
+ event->dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+ /* FIXME : this needs to change for the '7xx family */
+ if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON)
+ event->target = NCR53c7x0_read8(SSID_REG_800);
+ else
+ event->target = 255;
+
+ if (event->event == EVENT_RESELECT)
+ event->lun = hostdata->reselected_identify & 0xf;
+ else if (c)
+ event->lun = c->lun;
+ else
+ event->lun = 255;
+ do_gettimeofday(&(event->time));
+ if (c) {
+ event->pid = c->pid;
+ memcpy ((void *) event->cmnd, (void *) c->cmnd,
+ sizeof (event->cmnd));
+ } else {
+ event->pid = -1;
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+ }
+
+ printk ("scsi%d : unknown user interrupt 0x%x\n",
+ host->host_no, (unsigned) dsps);
+ return SPECIFIC_INT_PANIC;
+ }
+}
+
+/*
+ * XXX - the stock NCR assembler won't output the scriptu.h file,
+ * which undefine's all #define'd CPP symbols from the script.h
+ * file, which will create problems if you use multiple scripts
+ * with the same symbol names.
+ *
+ * If you insist on using NCR's assembler, you could generate
+ * scriptu.h from script.h using something like
+ *
+ * grep #define script.h | \
+ * sed 's/#define[ ][ ]*\([_a-zA-Z][_a-zA-Z0-9]*\).*$/#undefine \1/' \
+ * > scriptu.h
+ */
+
+#include "53c8xx_u.h"
+
+/* XXX - add alternate script handling code here */
+
+
+#ifdef NCR_DEBUG
+/*
+ * Debugging without a debugger is no fun. So, I've provided
+ * a debugging interface in the NCR53c7x0 driver. To avoid
+ * kernel cruft, there's just enough here to act as an interface
+ * to a user level debugger (aka, GDB).
+ *
+ *
+ * The following restrictions apply to debugger commands :
+ * 1. The command must be terminated by a newline.
+ * 2. Command length must be less than 80 bytes including the
+ * newline.
+ * 3. The entire command must be written with one system call.
+ */
+
+static const char debugger_help =
+"bc <addr> - clear breakpoint\n"
+"bl - list breakpoints\n"
+"bs <addr> - set breakpoint\n"
+"g - start\n"
+"h - halt\n"
+"? - this message\n"
+"i - info\n"
+"mp <addr> <size> - print memory\n"
+"ms <addr> <size> <value> - store memory\n"
+"rp <num> <size> - print register\n"
+"rs <num> <size> <value> - store register\n"
+"s - single step\n"
+"tb - begin trace \n"
+"te - end trace\n";
+
+/*
+ * Whenever we change a break point, we should probably
+ * set the NCR up so that it is in a single step mode.
+ */
+
+static int debugger_fn_bc (struct Scsi_Host *host, struct debugger_token *token,
+ u32 args[]) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ instance->hostdata;
+ struct NCR53c7x0_break *bp, **prev;
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ for (bp = (struct NCR53c7x0_break *) instance->breakpoints,
+ prev = (struct NCR53c7x0_break **) &instance->breakpoints;
+ bp; prev = (struct NCR53c7x0_break **) &(bp->next),
+ bp = (struct NCR53c7x0_break *) bp->next);
+
+ if (!bp) {
+ restore_flags(flags);
+ return -EIO;
+ }
+
+ /*
+ * XXX - we need to insure that the processor is halted
+ * here in order to prevent a race condition.
+ */
+
+ memcpy ((void *) bp->addr, (void *) bp->old, sizeof(bp->old));
+ if (prev)
+ *prev = bp->next;
+
+ restore_flags(flags);
+ return 0;
+}
+
+
+static int
+debugger_fn_bl (struct Scsi_Host *host, struct debugger_token *token,
+ u32 args[]) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ struct NCR53c7x0_break *bp;
+ char buf[80];
+ size_t len;
+ unsigned long flags;
+ /*
+ * XXX - we need to insure that the processor is halted
+ * here in order to prevent a race condition. So, if the
+ * processor isn't halted, print an error message and continue.
+ */
+
+ sprintf (buf, "scsi%d : bp : warning : processor not halted\b",
+ host->host_no);
+ debugger_kernel_write (host, buf, strlen(buf));
+
+ save_flags(flags);
+ cli();
+ for (bp = (struct NCR53c7x0_break *) host->breakpoints;
+ bp; bp = (struct NCR53c7x0_break *) bp->next); {
+ sprintf (buf, "scsi%d : bp : success : at %08x, replaces %08x %08x",
+ bp->addr, bp->old[0], bp->old[1]);
+ len = strlen(buf);
+ if ((bp->old[0] & (DCMD_TYPE_MASK << 24)) ==
+ (DCMD_TYPE_MMI << 24)) {
+ sprintf(buf + len, "%08x\n", * (u32 *) bp->addr);
+ } else {
+ sprintf(buf + len, "\n");
+ }
+ len = strlen(buf);
+ debugger_kernel_write (host, buf, len);
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+static int
+debugger_fn_bs (struct Scsi_Host *host, struct debugger_token *token,
+ u32 args[]) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ struct NCR53c7x0_break *bp;
+ char buf[80];
+ size_t len;
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+
+ if (hostdata->state != STATE_HALTED) {
+ sprintf (buf, "scsi%d : bs : failure : NCR not halted\n", host->host_no);
+ debugger_kernel_write (host, buf, strlen(buf));
+ restore_flags(flags);
+ return -1;
+ }
+
+ if (!(bp = kmalloc (sizeof (struct NCR53c7x0_break)))) {
+ printk ("scsi%d : kmalloc(%d) of breakpoint structure failed, try again\n",
+ host->host_no, sizeof(struct NCR53c7x0_break));
+ restore_flags(flags);
+ return -1;
+ }
+
+ bp->address = (u32 *) args[0];
+ memcpy ((void *) bp->old_instruction, (void *) bp->address, 8);
+ bp->old_size = (((bp->old_instruction[0] >> 24) & DCMD_TYPE_MASK) ==
+ DCMD_TYPE_MMI ? 3 : 2;
+ bp->next = hostdata->breakpoints;
+ hostdata->breakpoints = bp->next;
+ memcpy ((void *) bp->address, (void *) hostdata->E_debug_break, 8);
+
+ restore_flags(flags);
+ return 0;
+}
+
+#define TOKEN(name,nargs) {#name, nargs, debugger_fn_##name}
+static const struct debugger_token {
+ char *name;
+ int numargs;
+ int (*fn)(struct debugger_token *token, u32 args[]);
+} debugger_tokens[] = {
+ TOKEN(bc,1), TOKEN(bl,0), TOKEN(bs,1), TOKEN(g,0), TOKEN(halt,0),
+ {DT_help, "?", 0} , TOKEN(h,0), TOKEN(i,0), TOKEN(mp,2),
+ TOKEN(ms,3), TOKEN(rp,2), TOKEN(rs,2), TOKEN(s,0), TOKEN(tb,0), TOKEN(te,0)
+};
+
+#define NDT sizeof(debugger_tokens / sizeof(struct debugger_token))
+
+static struct Scsi_Host * inode_to_host (struct inode *inode) {
+ int dev;
+ struct Scsi_Host *tmp;
+ for (dev = MINOR(inode->rdev), host = first_host;
+ (host->hostt == the_template); --dev, host = host->next)
+ if (!dev) return host;
+ return NULL;
+}
+
+
+static int
+debugger_user_write (struct inode *inode,struct file *filp,
+ char *buf,int count) {
+ struct Scsi_Host *host; /* This SCSI host */
+ struct NCR53c7x0_hostadata *hostdata;
+ char input_buf[80], /* Kernel space copy of buf */
+ *ptr; /* Pointer to argument list */
+ u32 args[3]; /* Arguments */
+ int i, j, error, len;
+
+ if (!(host = inode_to_host(inode)))
+ return -ENXIO;
+
+ hostdata = (struct NCR53c7x0_hostdata *) host->hostdata;
+
+ if (error = verify_area(VERIFY_READ,buf,count))
+ return error;
+
+ if (count > 80)
+ return -EIO;
+
+ memcpy_from_fs(input_buf, buf, count);
+
+ if (input_buf[count - 1] != '\n')
+ return -EIO;
+
+ input_buf[count - 1]=0;
+
+ for (i = 0; i < NDT; ++i) {
+ len = strlen (debugger_tokens[i].name);
+ if (!strncmp(input_buf, debugger_tokens[i].name, len))
+ break;
+ };
+
+ if (i == NDT)
+ return -EIO;
+
+ for (ptr = input_buf + len, j = 0; j < debugger_tokens[i].nargs && *ptr;) {
+ if (*ptr == ' ' || *ptr == '\t') {
+ ++ptr;
+ } else if (isdigit(*ptr)) {
+ args[j++] = simple_strtoul (ptr, &ptr, 0);
+ } else {
+ return -EIO;
+ }
+ }
+
+ if (j != debugger_tokens[i].nargs)
+ return -EIO;
+
+ return count;
+}
+
+static int
+debugger_user_read (struct inode *inode,struct file *filp,
+ char *buf,int count) {
+ struct Scsi_Host *instance;
+
+}
+
+static int
+debugger_kernel_write (struct Scsi_Host *host, char *buf, size_t
+ buflen) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int copy, left;
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ while (buflen) {
+ left = (hostdata->debug_buf + hostdata->debug_size - 1) -
+ hostdata->debug_write;
+ copy = (buflen <= left) ? buflen : left;
+ memcpy (hostdata->debug_write, buf, copy);
+ buf += copy;
+ buflen -= copy;
+ hostdata->debug_count += copy;
+ if ((hostdata->debug_write += copy) ==
+ (hostdata->debug_buf + hostdata->debug_size))
+ hosdata->debug_write = hostdata->debug_buf;
+ }
+ restore_flags(flags);
+}
+
+#endif /* def NCRDEBUG */
+
+/*
+ * Function : static void NCR538xx_soft_reset (struct Scsi_Host *host)
+ *
+ * Purpose : perform a soft reset of the NCR53c8xx chip
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ *
+ * Preconditions : NCR53c7x0_init must have been called for this
+ * host.
+ *
+ */
+
+static void
+NCR53c8x0_soft_reset (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ NCR53c7x0_local_setup(host);
+
+
+ /*
+ * Do a soft reset of the chip so that everything is
+ * reinitialized to the power-on state.
+ *
+ * Basically follow the procedure outlined in the NCR53c700
+ * data manual under Chapter Six, How to Use, Steps Necessary to
+ * Start SCRIPTS, with the exception of actually starting the
+ * script and setting up the synchronous transfer gunk.
+ */
+
+ NCR53c7x0_write8(ISTAT_REG_800, ISTAT_10_SRST);
+ NCR53c7x0_write8(ISTAT_REG_800, 0);
+ NCR53c7x0_write8(hostdata->dmode, hostdata->saved_dmode & ~DMODE_MAN);
+
+
+ /*
+ * Respond to reselection by targets and use our _initiator_ SCSI ID
+ * for arbitration. If notyet, also respond to SCSI selection.
+ *
+ * XXX - Note : we must reprogram this when reselecting as
+ * a target.
+ */
+
+#ifdef notyet
+ NCR53c7x0_write8(SCID_REG, (host->this_id & 7)|SCID_800_RRE|SCID_800_SRE);
+#else
+ NCR53c7x0_write8(SCID_REG, (host->this_id & 7)|SCID_800_RRE);
+#endif
+ NCR53c7x0_write8(RESPID_REG_800, hostdata->this_id_mask);
+
+ /*
+ * Use a maximum (1.6) second handshake to handshake timeout,
+ * and SCSI recommended .5s selection timeout.
+ */
+
+ /*
+ * The new gcc won't recognize preprocessing directives
+ * within macro args.
+ */
+#if 0
+ NCR53c7x0_write8(STIME0_REG_800,
+ ((selection_timeout << STIME0_800_SEL_SHIFT) & STIME0_800_SEL_MASK)
+ | ((15 << STIME0_800_HTH_SHIFT) & STIME0_800_HTH_MASK));
+#else
+/* Disable HTH interrupt */
+ NCR53c7x0_write8(STIME0_REG_800,
+ ((selection_timeout << STIME0_800_SEL_SHIFT) & STIME0_800_SEL_MASK));
+#endif
+
+
+ /*
+ * Enable active negation for happy synchronous transfers.
+ */
+
+ NCR53c7x0_write8(STEST3_REG_800, STEST3_800_TE);
+
+ /*
+ * Enable all interrupts, except parity which we only want when
+ * the user requests it.
+ */
+
+ NCR53c7x0_write8(DIEN_REG, DIEN_800_MDPE | DIEN_800_BF |
+ DIEN_ABRT | DIEN_SSI | DIEN_SIR | DIEN_800_IID);
+
+
+ NCR53c7x0_write8(SIEN0_REG_800, ((hostdata->options & OPTION_PARITY) ?
+ SIEN_PAR : 0) | SIEN_RST | SIEN_UDC | SIEN_SGE | SIEN_MA);
+ NCR53c7x0_write8(SIEN1_REG_800, SIEN1_800_STO | SIEN1_800_HTH);
+
+ /*
+ * Use saved clock frequency divisor and scripts loaded in 16 bit
+ * mode flags from the saved dcntl.
+ */
+
+ NCR53c7x0_write8(DCNTL_REG, hostdata->saved_dcntl);
+ NCR53c7x0_write8(CTEST4_REG_800, hostdata->saved_ctest4);
+
+ /* Enable active negation */
+ NCR53c7x0_write8(STEST3_REG_800, STEST3_800_TE);
+}
+
+/*
+ * Function static struct NCR53c7x0_cmd *allocate_cmd (Scsi_Cmnd *cmd)
+ *
+ * Purpose : Return the first free NCR53c7x0_cmd structure (which are
+ * reused in a LIFO manner to minimize cache thrashing).
+ *
+ * Side effects : If we haven't yet scheduled allocation of NCR53c7x0_cmd
+ * structures for this device, do so. Attempt to complete all scheduled
+ * allocations using kmalloc(), putting NCR53c7x0_cmd structures on
+ * the free list. Teach programmers not to drink and hack.
+ *
+ * Inputs : cmd - SCSI command
+ *
+ * Returns : NCR53c7x0_cmd structure allocated on behalf of cmd;
+ * NULL on failure.
+ */
+
+static struct NCR53c7x0_cmd *
+allocate_cmd (Scsi_Cmnd *cmd) {
+ struct Scsi_Host *host = cmd->host;
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata;
+ void *real; /* Real address */
+ int size; /* Size of *tmp */
+ struct NCR53c7x0_cmd *tmp;
+ unsigned long flags;
+
+ if (hostdata->options & OPTION_DEBUG_ALLOCATION)
+ printk ("scsi%d : num_cmds = %d, can_queue = %d\n"
+ " target = %d, lun = %d, %s\n",
+ host->host_no, hostdata->num_cmds, host->can_queue,
+ cmd->target, cmd->lun, (hostdata->cmd_allocated[cmd->target] &
+ (1 << cmd->lun)) ? "already allocated" : "not allocated");
+
+/*
+ * If we have not yet reserved commands for this I_T_L nexus, and
+ * the device exists (as indicated by permanent Scsi_Cmnd structures
+ * being allocated under 1.3.x, or being outside of scan_scsis in
+ * 1.2.x), do so now.
+ */
+ if (!(hostdata->cmd_allocated[cmd->target] & (1 << cmd->lun)) &&
+#ifdef LINUX_1_2
+ !in_scan_scsis
+#else
+ cmd->device && cmd->device->has_cmdblocks
+#endif
+ ) {
+ if ((hostdata->extra_allocate + hostdata->num_cmds) < host->can_queue)
+ hostdata->extra_allocate += host->cmd_per_lun;
+ hostdata->cmd_allocated[cmd->target] |= (1 << cmd->lun);
+ }
+
+ for (; hostdata->extra_allocate > 0 ; --hostdata->extra_allocate,
+ ++hostdata->num_cmds) {
+ /* historically, kmalloc has returned unaligned addresses; pad so we
+ have enough room to ROUNDUP */
+ size = hostdata->max_cmd_size + sizeof (void *);
+/* FIXME: for ISA bus '7xx chips, we need to or GFP_DMA in here */
+ real = kmalloc (size, GFP_ATOMIC);
+ if (!real) {
+ if (hostdata->options & OPTION_DEBUG_ALLOCATION)
+ printk ("scsi%d : kmalloc(%d) failed\n",
+ host->host_no, size);
+ break;
+ }
+ tmp = ROUNDUP(real, void *);
+ tmp->real = real;
+ tmp->size = size;
+#ifdef LINUX_1_2
+ tmp->free = ((void (*)(void *, int)) kfree_s);
+#else
+ tmp->free = ((void (*)(void *, int)) kfree);
+#endif
+ save_flags (flags);
+ cli();
+ tmp->next = hostdata->free;
+ hostdata->free = tmp;
+ restore_flags (flags);
+ }
+ save_flags(flags);
+ cli();
+ tmp = (struct NCR53c7x0_cmd *) hostdata->free;
+ if (tmp) {
+ hostdata->free = tmp->next;
+ }
+ restore_flags(flags);
+ if (!tmp)
+ printk ("scsi%d : can't allocate command for target %d lun %d\n",
+ host->host_no, cmd->target, cmd->lun);
+ return tmp;
+}
+
+/*
+ * Function static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd)
+ *
+ *
+ * Purpose : allocate a NCR53c7x0_cmd structure, initialize it based on the
+ * Scsi_Cmnd structure passed in cmd, including dsa and Linux field
+ * initialization, and dsa code relocation.
+ *
+ * Inputs : cmd - SCSI command
+ *
+ * Returns : NCR53c7x0_cmd structure corresponding to cmd,
+ * NULL on failure.
+ */
+
+static struct NCR53c7x0_cmd *
+create_cmd (Scsi_Cmnd *cmd) {
+ NCR53c7x0_local_declare();
+ struct Scsi_Host *host = cmd->host;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ struct NCR53c7x0_cmd *tmp; /* NCR53c7x0_cmd structure for this command */
+ int datain, /* Number of instructions per phase */
+ dataout;
+ int data_transfer_instructions, /* Count of dynamic instructions */
+ i; /* Counter */
+ u32 *cmd_datain, /* Address of datain/dataout code */
+ *cmd_dataout; /* Incremented as we assemble */
+#ifdef notyet
+ unsigned char *msgptr; /* Current byte in select message */
+ int msglen; /* Length of whole select message */
+#endif
+ unsigned long flags;
+ NCR53c7x0_local_setup(cmd->host);
+
+ if (!(tmp = allocate_cmd (cmd)))
+ return NULL;
+
+
+ /*
+ * Decide whether we need to generate commands for DATA IN,
+ * DATA OUT, neither, or both based on the SCSI command
+ */
+
+ switch (cmd->cmnd[0]) {
+ /* These commands do DATA IN */
+ case INQUIRY:
+ case MODE_SENSE:
+ case READ_6:
+ case READ_10:
+ case READ_CAPACITY:
+ case REQUEST_SENSE:
+ datain = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3;
+ dataout = 0;
+ break;
+ /* These commands do DATA OUT */
+ case MODE_SELECT:
+ case WRITE_6:
+ case WRITE_10:
+ case START_STOP: /* also SCAN, which may do DATA OUT */
+#if 0
+ printk("scsi%d : command is ", host->host_no);
+ print_command(cmd->cmnd);
+#endif
+#if 0
+ printk ("scsi%d : %d scatter/gather segments\n", host->host_no,
+ cmd->use_sg);
+#endif
+ datain = 0;
+ dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3;
+#if 0
+ hostdata->options |= OPTION_DEBUG_INTR;
+#endif
+ break;
+ /*
+ * These commands do no data transfer, we should force an
+ * interrupt if a data phase is attempted on them.
+ */
+ case TEST_UNIT_READY:
+ datain = dataout = 0;
+ break;
+ /*
+ * We don't know about these commands, so generate code to handle
+ * both DATA IN and DATA OUT phases.
+ */
+ default:
+ datain = dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3;
+ }
+
+ /*
+ * New code : so that active pointers work correctly regardless
+ * of where the saved data pointer is at, we want to immediately
+ * enter the dynamic code after selection, and on a non-data
+ * phase perform a CALL to the non-data phase handler, with
+ * returns back to this address.
+ *
+ * If a phase mismatch is encountered in the middle of a
+ * Block MOVE instruction, we want to _leave_ that instruction
+ * unchanged as the current case is, modify a temporary buffer,
+ * and point the active pointer (TEMP) at that.
+ *
+ * Furthermore, we want to implement a saved data pointer,
+ * set by the SAVE_DATA_POINTERs message.
+ *
+ * So, the data transfer segments will change to
+ * CALL data_transfer, WHEN NOT data phase
+ * MOVE x, x, WHEN data phase
+ * ( repeat )
+ * JUMP other_transfer
+ */
+
+ data_transfer_instructions = datain + dataout;
+
+ /*
+ * When we perform a request sense, we overwrite various things,
+ * including the data transfer code. Make sure we have enough
+ * space to do that.
+ */
+
+ if (data_transfer_instructions < 2)
+ data_transfer_instructions = 2;
+
+
+ /*
+ * The saved data pointer is set up so that a RESTORE POINTERS message
+ * will start the data transfer over at the beginning.
+ */
+
+ tmp->saved_data_pointer = virt_to_bus (hostdata->script) +
+ hostdata->E_data_transfer;
+
+ /*
+ * Initialize Linux specific fields.
+ */
+
+ tmp->cmd = cmd;
+ tmp->next = NULL;
+ tmp->flags = 0;
+ tmp->dsa_next_addr = virt_to_bus(tmp->dsa) + hostdata->dsa_next -
+ hostdata->dsa_start;
+ tmp->dsa_addr = virt_to_bus(tmp->dsa) - hostdata->dsa_start;
+
+ /*
+ * Calculate addresses of dynamic code to fill in DSA
+ */
+
+ tmp->data_transfer_start = tmp->dsa + (hostdata->dsa_end -
+ hostdata->dsa_start) / sizeof(u32);
+ tmp->data_transfer_end = tmp->data_transfer_start +
+ 2 * data_transfer_instructions;
+
+ cmd_datain = datain ? tmp->data_transfer_start : NULL;
+ cmd_dataout = dataout ? (datain ? cmd_datain + 2 * datain : tmp->
+ data_transfer_start) : NULL;
+
+ /*
+ * Fill in the NCR53c7x0_cmd structure as follows
+ * dsa, with fixed up DSA code
+ * datain code
+ * dataout code
+ */
+
+ /* Copy template code into dsa and perform all necessary fixups */
+ if (hostdata->dsa_fixup)
+ hostdata->dsa_fixup(tmp);
+
+ patch_dsa_32(tmp->dsa, dsa_next, 0, 0);
+ patch_dsa_32(tmp->dsa, dsa_cmnd, 0, virt_to_bus(cmd));
+
+ if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS)
+ if (hostdata->sync[cmd->target].select_indirect !=
+ ((hostdata->sync[cmd->target].scntl3_sanity << 24) |
+ (cmd->target << 16) |
+ (hostdata->sync[cmd->target].sxfer_sanity << 8))) {
+ printk ("scsi%d : sanity check failed select_indirect=0x%x\n",
+ host->host_no, hostdata->sync[cmd->target].select_indirect);
+ FATAL(host);
+
+ }
+
+ patch_dsa_32(tmp->dsa, dsa_select, 0, hostdata->sync[cmd->target].
+ select_indirect);
+ /*
+ * Right now, we'll do the WIDE and SYNCHRONOUS negotiations on
+ * different commands; although it should be trivial to do them
+ * both at the same time.
+ */
+ if (hostdata->initiate_wdtr & (1 << cmd->target)) {
+ memcpy ((void *) (tmp->select + 1), (void *) wdtr_message,
+ sizeof(wdtr_message));
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(wdtr_message));
+ save_flags(flags);
+ cli();
+ hostdata->initiate_wdtr &= ~(1 << cmd->target);
+ restore_flags(flags);
+ } else if (hostdata->initiate_sdtr & (1 << cmd->target)) {
+ memcpy ((void *) (tmp->select + 1), (void *) sdtr_message,
+ sizeof(sdtr_message));
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(sdtr_message));
+ tmp->flags |= CMD_FLAG_SDTR;
+ save_flags(flags);
+ cli();
+ hostdata->initiate_sdtr &= ~(1 << cmd->target);
+ restore_flags(flags);
+
+ }
+#if 1
+ else if (!(hostdata->talked_to & (1 << cmd->target)) &&
+ !(hostdata->options & OPTION_NO_ASYNC)) {
+ memcpy ((void *) (tmp->select + 1), (void *) async_message,
+ sizeof(async_message));
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(async_message));
+ tmp->flags |= CMD_FLAG_SDTR;
+ }
+#endif
+ else
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1);
+ hostdata->talked_to |= (1 << cmd->target);
+ tmp->select[0] = (hostdata->options & OPTION_DISCONNECT) ?
+ IDENTIFY (1, cmd->lun) : IDENTIFY (0, cmd->lun);
+ patch_dsa_32(tmp->dsa, dsa_msgout, 1, virt_to_bus(tmp->select));
+ patch_dsa_32(tmp->dsa, dsa_cmdout, 0, cmd->cmd_len);
+ patch_dsa_32(tmp->dsa, dsa_cmdout, 1, virt_to_bus(cmd->cmnd));
+ patch_dsa_32(tmp->dsa, dsa_dataout, 0, cmd_dataout ?
+ virt_to_bus (cmd_dataout)
+ : virt_to_bus (hostdata->script) + hostdata->E_other_transfer);
+ patch_dsa_32(tmp->dsa, dsa_datain, 0, cmd_datain ?
+ virt_to_bus (cmd_datain)
+ : virt_to_bus (hostdata->script) + hostdata->E_other_transfer);
+ /*
+ * XXX - need to make endian aware, should use separate variables
+ * for both status and message bytes.
+ */
+ patch_dsa_32(tmp->dsa, dsa_msgin, 0, 1);
+/*
+ * FIXME : these only works for little endian. We probably want to
+ * provide message and status fields in the NCR53c7x0_cmd
+ * structure, and assign them to cmd->result when we're done.
+ */
+ patch_dsa_32(tmp->dsa, dsa_msgin, 1, virt_to_bus(&cmd->result) + 1);
+ patch_dsa_32(tmp->dsa, dsa_status, 0, 1);
+ patch_dsa_32(tmp->dsa, dsa_status, 1, virt_to_bus(&cmd->result));
+ patch_dsa_32(tmp->dsa, dsa_msgout_other, 0, 1);
+ patch_dsa_32(tmp->dsa, dsa_msgout_other, 1,
+ virt_to_bus(&(hostdata->NCR53c7xx_msg_nop)));
+
+ /*
+ * Generate code for zero or more of the DATA IN, DATA OUT phases
+ * in the format
+ *
+ * CALL data_transfer, WHEN NOT phase
+ * MOVE first buffer length, first buffer address, WHEN phase
+ * ...
+ * MOVE last buffer length, last buffer address, WHEN phase
+ * JUMP other_transfer
+ */
+
+/*
+ * See if we're getting to data transfer by generating an unconditional
+ * interrupt.
+ */
+#if 0
+ if (datain) {
+ cmd_datain[0] = 0x98080000;
+ cmd_datain[1] = 0x03ffd00d;
+ cmd_datain += 2;
+ }
+#endif
+
+/*
+ * XXX - I'm undecided whether all of this nonsense is faster
+ * in the long run, or whether I should just go and implement a loop
+ * on the NCR chip using table indirect mode?
+ *
+ * In any case, this is how it _must_ be done for 53c700/700-66 chips,
+ * so this stays even when we come up with something better.
+ *
+ * When we're limited to 1 simultaneous command, no overlapping processing,
+ * we're seeing 630K/sec, with 7% CPU usage on a slow Syquest 45M
+ * drive.
+ *
+ * Not bad, not good. We'll see.
+ */
+
+ for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; cmd_datain += 4,
+ cmd_dataout += 4, ++i) {
+ u32 buf = cmd->use_sg ?
+ virt_to_bus(((struct scatterlist *)cmd->buffer)[i].address) :
+ virt_to_bus(cmd->request_buffer);
+ u32 count = cmd->use_sg ?
+ ((struct scatterlist *)cmd->buffer)[i].length :
+ cmd->request_bufflen;
+
+ if (datain) {
+ /* CALL other_in, WHEN NOT DATA_IN */
+ cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
+ DCMD_TCI_IO) << 24) |
+ DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE;
+ cmd_datain[1] = virt_to_bus (hostdata->script) +
+ hostdata->E_other_in;
+ /* MOVE count, buf, WHEN DATA_IN */
+ cmd_datain[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO)
+ << 24) | count;
+ cmd_datain[3] = buf;
+#if 0
+ print_insn (host, cmd_datain, "dynamic ", 1);
+ print_insn (host, cmd_datain + 2, "dynamic ", 1);
+#endif
+ }
+ if (dataout) {
+ /* CALL other_out, WHEN NOT DATA_OUT */
+ cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL) << 24) |
+ DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE;
+ cmd_dataout[1] = virt_to_bus(hostdata->script) +
+ hostdata->E_other_out;
+ /* MOVE count, buf, WHEN DATA+OUT */
+ cmd_dataout[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I) << 24)
+ | count;
+ cmd_dataout[3] = buf;
+#if 0
+ print_insn (host, cmd_dataout, "dynamic ", 1);
+ print_insn (host, cmd_dataout + 2, "dynamic ", 1);
+#endif
+ }
+ }
+
+ /*
+ * Install JUMP instructions after the data transfer routines to return
+ * control to the do_other_transfer routines.
+ */
+
+
+ if (datain) {
+ cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
+ DBC_TCI_TRUE;
+ cmd_datain[1] = virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer;
+#if 0
+ print_insn (host, cmd_datain, "dynamic jump ", 1);
+#endif
+ cmd_datain += 2;
+ }
+#if 0
+ if (datain) {
+ cmd_datain[0] = 0x98080000;
+ cmd_datain[1] = 0x03ffdeed;
+ cmd_datain += 2;
+ }
+#endif
+ if (dataout) {
+ cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
+ DBC_TCI_TRUE;
+ cmd_dataout[1] = virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer;
+#if 0
+ print_insn (host, cmd_dataout, "dynamic jump ", 1);
+#endif
+ cmd_dataout += 2;
+ }
+ return tmp;
+}
+
+/*
+ * Function : int NCR53c7xx_queue_command (Scsi_Cmnd *cmd,
+ * void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ * a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ * Side effects :
+ * cmd is added to the per instance driver issue_queue, with major
+ * twiddling done to the host specific fields of cmd. If the
+ * process_issue_queue coroutine isn't running, it is restarted.
+ *
+ * NOTE : we use the host_scribble field of the Scsi_Cmnd structure to
+ * hold our own data, and pervert the ptr field of the SCp field
+ * to create a linked list.
+ */
+
+int
+NCR53c7xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) {
+ struct Scsi_Host *host = cmd->host;
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata;
+ unsigned long flags;
+ Scsi_Cmnd *tmp;
+
+ cmd->scsi_done = done;
+ cmd->host_scribble = NULL;
+ cmd->SCp.ptr = NULL;
+ cmd->SCp.buffer = NULL;
+
+ save_flags(flags);
+ cli();
+ if ((hostdata->options & (OPTION_DEBUG_INIT_ONLY|OPTION_DEBUG_PROBE_ONLY))
+ || ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) &&
+ !(hostdata->debug_lun_limit[cmd->target] & (1 << cmd->lun)))
+#ifdef LINUX_1_2
+ || cmd->target > 7
+#else
+ || cmd->target > host->max_id
+#endif
+ || cmd->target == host->this_id
+ || hostdata->state == STATE_DISABLED) {
+ printk("scsi%d : disabled or bad target %d lun %d\n", host->host_no,
+ cmd->target, cmd->lun);
+ cmd->result = (DID_BAD_TARGET << 16);
+ } else if ((hostdata->options & OPTION_DEBUG_NCOMMANDS_LIMIT) &&
+ (hostdata->debug_count_limit == 0)) {
+ printk("scsi%d : maximum commands exceeded\n", host->host_no);
+ cmd->result = (DID_BAD_TARGET << 16);
+ cmd->result = (DID_BAD_TARGET << 16);
+ } else if (hostdata->options & OPTION_DEBUG_READ_ONLY) {
+ switch (cmd->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n",
+ host->host_no);
+ cmd->result = (DID_BAD_TARGET << 16);
+ }
+ } else {
+ if ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) &&
+ hostdata->debug_count_limit != -1)
+ --hostdata->debug_count_limit;
+ restore_flags (flags);
+ cmd->result = 0xffff; /* The NCR will overwrite message
+ and status with valid data */
+ cmd->host_scribble = (unsigned char *) tmp = create_cmd (cmd);
+ }
+ cli();
+ /*
+ * REQUEST SENSE commands are inserted at the head of the queue
+ * so that we do not clear the contingent allegiance condition
+ * they may be looking at.
+ */
+
+ if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ cmd->SCp.ptr = (unsigned char *) hostdata->issue_queue;
+ hostdata->issue_queue = cmd;
+ } else {
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->SCp.ptr;
+ tmp = (Scsi_Cmnd *) tmp->SCp.ptr);
+ tmp->SCp.ptr = (unsigned char *) cmd;
+ }
+ restore_flags (flags);
+ run_process_issue_queue();
+ return 0;
+}
+
+/*
+ * Function : void to_schedule_list (struct Scsi_Host *host,
+ * struct NCR53c7x0_hostdata * hostdata, Scsi_Cmnd *cmd)
+ *
+ * Purpose : takes a SCSI command which was just removed from the
+ * issue queue, and deals with it by inserting it in the first
+ * free slot in the schedule list or by terminating it immediately.
+ *
+ * Inputs :
+ * host - SCSI host adapter; hostdata - hostdata structure for
+ * this adapter; cmd - a pointer to the command; should have
+ * the host_scribble field initialized to point to a valid
+ *
+ * Side effects :
+ * cmd is added to the per instance schedule list, with minor
+ * twiddling done to the host specific fields of cmd.
+ *
+ */
+
+static __inline__ void
+to_schedule_list (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
+ struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ Scsi_Cmnd *tmp = cmd->cmd;
+ unsigned long flags;
+ /* dsa start is negative, so subtraction is used */
+ volatile u32 *current;
+
+ int i;
+ NCR53c7x0_local_setup(host);
+#if 0
+ printk("scsi%d : new dsa is 0x%lx (virt 0x%p)\n", host->host_no,
+ virt_to_bus(dsa), dsa);
+#endif
+
+ save_flags(flags);
+ cli();
+
+ /*
+ * Work around race condition : if an interrupt fired and we
+ * got disabled forget about this command.
+ */
+
+ if (hostdata->state == STATE_DISABLED) {
+ printk("scsi%d : driver disabled\n", host->host_no);
+ tmp->result = (DID_BAD_TARGET << 16);
+ cmd->next = (struct NCR53c7x0_cmd *) hostdata->free;
+ hostdata->free = cmd;
+ tmp->scsi_done(tmp);
+ restore_flags (flags);
+ return;
+ }
+
+ for (i = host->can_queue, current = hostdata->schedule;
+ i > 0 && current[0] != hostdata->NOP_insn;
+ --i, current += 2 /* JUMP instructions are two words */);
+
+ if (i > 0) {
+ ++hostdata->busy[tmp->target][tmp->lun];
+ cmd->next = hostdata->running_list;
+ hostdata->running_list = cmd;
+
+ /* Restore this instruction to a NOP once the command starts */
+ cmd->dsa [(hostdata->dsa_jump_dest - hostdata->dsa_start) /
+ sizeof(u32)] = (u32) virt_to_bus ((void *)current);
+ /* Replace the current jump operand. */
+ current[1] =
+ virt_to_bus ((void *) cmd->dsa) + hostdata->E_dsa_code_begin -
+ hostdata->E_dsa_code_template;
+ /* Replace the NOP instruction with a JUMP */
+ current[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) |
+ DBC_TCI_TRUE;
+ } else {
+ printk ("scsi%d: no free slot\n", host->host_no);
+ disable(host);
+ tmp->result = (DID_ERROR << 16);
+ cmd->next = (struct NCR53c7x0_cmd *) hostdata->free;
+ hostdata->free = cmd;
+ tmp->scsi_done(tmp);
+ restore_flags (flags);
+ return;
+ }
+
+ /*
+ * If the NCR chip is in an idle state, start it running the scheduler
+ * immediately. Otherwise, signal the chip to jump to schedule as
+ * soon as it is idle.
+ */
+ if (hostdata->idle) {
+ hostdata->idle = 0;
+ hostdata->state = STATE_RUNNING;
+ NCR53c7x0_write32 (DSP_REG, virt_to_bus ((void *)hostdata->schedule));
+ } else {
+ NCR53c7x0_write8(hostdata->istat, ISTAT_10_SIGP);
+ }
+
+ restore_flags(flags);
+}
+
+/*
+ * Function : busyp (struct Scsi_Host *host, struct NCR53c7x0_hostdata
+ * *hostdata, Scsi_Cmnd *cmd)
+ *
+ * Purpose : decide if we can pass the given SCSI command on to the
+ * device in question or not.
+ *
+ * Returns : non-zero when we're busy, 0 when we aren't.
+ */
+
+static __inline__ int
+busyp (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
+ Scsi_Cmnd *cmd) {
+ /* FIXME : in the future, this needs to accommodate SCSI-II tagged
+ queuing, and we may be able to play with fairness here a bit.
+ */
+ return hostdata->busy[cmd->target][cmd->lun];
+}
+
+/*
+ * Function : process_issue_queue (void)
+ *
+ * Purpose : transfer commands from the issue queue to NCR start queue
+ * of each NCR53c7/8xx in the system, avoiding kernel stack
+ * overflows when the scsi_done() function is invoked recursively.
+ *
+ * NOTE : process_issue_queue exits with interrupts *disabled*, so the
+ * caller must reenable them if it desires.
+ *
+ * NOTE : process_issue_queue should be called from both
+ * NCR53c7x0_queue_command() and from the interrupt handler
+ * after command completion in case NCR53c7x0_queue_command()
+ * isn't invoked again but we've freed up resources that are
+ * needed.
+ */
+
+static void
+process_issue_queue (unsigned long flags) {
+ Scsi_Cmnd *tmp, *prev;
+ struct Scsi_Host *host;
+ struct NCR53c7x0_hostdata *hostdata;
+ int done;
+
+ /*
+ * We run (with interrupts disabled) until we're sure that none of
+ * the host adapters have anything that can be done, at which point
+ * we set process_issue_queue_running to 0 and exit.
+ *
+ * Interrupts are enabled before doing various other internal
+ * instructions, after we've decided that we need to run through
+ * the loop again.
+ *
+ */
+
+ do {
+ cli(); /* Freeze request queues */
+ done = 1;
+ for (host = first_host; host && host->hostt == the_template;
+ host = host->next) {
+ hostdata = (struct NCR53c7x0_hostdata *) host->hostdata;
+ cli();
+ if (hostdata->issue_queue) {
+ if (hostdata->state == STATE_DISABLED) {
+ tmp = (Scsi_Cmnd *) hostdata->issue_queue;
+ hostdata->issue_queue = (Scsi_Cmnd *) tmp->SCp.ptr;
+ tmp->result = (DID_BAD_TARGET << 16);
+ if (tmp->host_scribble) {
+ ((struct NCR53c7x0_cmd *)tmp->host_scribble)->next =
+ hostdata->free;
+ hostdata->free =
+ (struct NCR53c7x0_cmd *)tmp->host_scribble;
+ tmp->host_scribble = NULL;
+ }
+ tmp->scsi_done (tmp);
+ done = 0;
+ } else
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
+ prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *)
+ tmp->SCp.ptr)
+ if (!tmp->host_scribble ||
+ !busyp (host, hostdata, tmp)) {
+ if (prev)
+ prev->SCp.ptr = tmp->SCp.ptr;
+ else
+ hostdata->issue_queue = (Scsi_Cmnd *)
+ tmp->SCp.ptr;
+ tmp->SCp.ptr = NULL;
+ if (tmp->host_scribble) {
+ if (hostdata->options & OPTION_DEBUG_QUEUES)
+ printk ("scsi%d : moving command for target %d lun %d to start list\n",
+ host->host_no, tmp->target, tmp->lun);
+
+
+ to_schedule_list (host, hostdata,
+ (struct NCR53c7x0_cmd *)
+ tmp->host_scribble);
+ } else {
+ if (((tmp->result & 0xff) == 0xff) ||
+ ((tmp->result & 0xff00) == 0xff00)) {
+ printk ("scsi%d : danger Will Robinson!\n",
+ host->host_no);
+ tmp->result = DID_ERROR << 16;
+ disable (host);
+ }
+ tmp->scsi_done(tmp);
+ }
+ done = 0;
+ } /* if target/lun is not busy */
+ } /* if hostdata->issue_queue */
+ if (!done)
+ restore_flags (flags);
+ } /* for host */
+ } while (!done);
+ process_issue_queue_running = 0;
+}
+
+/*
+ * Function : static void intr_scsi (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : handle all SCSI interrupts, indicated by the setting
+ * of the SIP bit in the ISTAT register.
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ * may be NULL.
+ */
+
+static void
+intr_scsi (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata;
+ unsigned char sstat0_sist0, sist1, /* Registers */
+ fatal; /* Did a fatal interrupt
+ occur ? */
+
+ int is_8xx_chip;
+ NCR53c7x0_local_setup(host);
+
+ fatal = 0;
+
+ is_8xx_chip = ((unsigned) (hostdata->chip - 800)) < 100;
+ if (is_8xx_chip) {
+ sstat0_sist0 = NCR53c7x0_read8(SIST0_REG_800);
+ udelay(1);
+ sist1 = NCR53c7x0_read8(SIST1_REG_800);
+ } else {
+ sstat0_sist0 = NCR53c7x0_read8(SSTAT0_REG);
+ sist1 = 0;
+ }
+
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : SIST0 0x%0x, SIST1 0x%0x\n", host->host_no,
+ sstat0_sist0, sist1);
+
+ /* 250ms selection timeout */
+ if ((is_8xx_chip && (sist1 & SIST1_800_STO)) ||
+ (!is_8xx_chip && (sstat0_sist0 & SSTAT0_700_STO))) {
+ fatal = 1;
+ if (hostdata->options & OPTION_DEBUG_INTR) {
+ printk ("scsi%d : Selection Timeout\n", host->host_no);
+ if (cmd) {
+ printk("scsi%d : target %d, lun %d, command ",
+ host->host_no, cmd->cmd->target, cmd->cmd->lun);
+ print_command (cmd->cmd->cmnd);
+ printk("scsi%d : dsp = 0x%x (virt 0x%p)\n", host->host_no,
+ NCR53c7x0_read32(DSP_REG),
+ bus_to_virt(NCR53c7x0_read32(DSP_REG)));
+ } else {
+ printk("scsi%d : no command\n", host->host_no);
+ }
+ }
+/*
+ * XXX - question : how do we want to handle the Illegal Instruction
+ * interrupt, which may occur before or after the Selection Timeout
+ * interrupt?
+ */
+
+ if (1) {
+ hostdata->idle = 1;
+ hostdata->expecting_sto = 0;
+
+ if (hostdata->test_running) {
+ hostdata->test_running = 0;
+ hostdata->test_completed = 3;
+ } else if (cmd) {
+ abnormal_finished(cmd, DID_BAD_TARGET << 16);
+ }
+#if 0
+ hostdata->intrs = 0;
+#endif
+ }
+ }
+
+/*
+ * FIXME : in theory, we can also get a UDC when a STO occurs.
+ */
+ if (sstat0_sist0 & SSTAT0_UDC) {
+ fatal = 1;
+ if (cmd) {
+ printk("scsi%d : target %d lun %d unexpected disconnect\n",
+ host->host_no, cmd->cmd->target, cmd->cmd->lun);
+ print_lots (host);
+ abnormal_finished(cmd, DID_ERROR << 16);
+ } else
+ printk("scsi%d : unexpected disconnect (no command)\n",
+ host->host_no);
+
+ hostdata->dsp = (u32 *) hostdata->schedule;
+ hostdata->dsp_changed = 1;
+ }
+
+ /* SCSI PARITY error */
+ if (sstat0_sist0 & SSTAT0_PAR) {
+ fatal = 1;
+ if (cmd && cmd->cmd) {
+ printk("scsi%d : target %d lun %d parity error.\n",
+ host->host_no, cmd->cmd->target, cmd->cmd->lun);
+ abnormal_finished (cmd, DID_PARITY << 16);
+ } else
+ printk("scsi%d : parity error\n", host->host_no);
+ /* Should send message out, parity error */
+
+ /* XXX - Reduce synchronous transfer rate! */
+ hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ /* SCSI GROSS error */
+ }
+
+ if (sstat0_sist0 & SSTAT0_SGE) {
+ fatal = 1;
+ printk("scsi%d : gross error\n", host->host_no);
+ /* Reset SCSI offset */
+ if ((hostdata->chip / 100) == 8) {
+ NCR53c7x0_write8 (STEST2_REG_800, STEST2_800_ROF);
+ }
+
+ /*
+ * A SCSI gross error may occur when we have
+ *
+ * - A synchronous offset which causes the SCSI FIFO to be overwritten.
+ *
+ * - A REQ which causes the maximum synchronous offset programmed in
+ * the SXFER register to be exceeded.
+ *
+ * - A phase change with an outstanding synchronous offset.
+ *
+ * - Residual data in the synchronous data FIFO, with a transfer
+ * other than a synchronous receive is started.$#
+ */
+
+
+ /* XXX Should deduce synchronous transfer rate! */
+ hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ /* Phase mismatch */
+ }
+
+ if (sstat0_sist0 & SSTAT0_MA) {
+ fatal = 1;
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : SSTAT0_MA\n", host->host_no);
+ intr_phase_mismatch (host, cmd);
+ }
+
+#if 0
+ if (sstat0_sist0 & SIST0_800_RSL)
+ printk ("scsi%d : Oh no Mr. Bill!\n", host->host_no);
+#endif
+
+/*
+ * If a fatal SCSI interrupt occurs, we must insure that the DMA and
+ * SCSI FIFOs were flushed.
+ */
+
+ if (fatal) {
+ if (!hostdata->dstat_valid) {
+ hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+ hostdata->dstat_valid = 1;
+ }
+
+/* XXX - code check for 700/800 chips */
+ if (!(hostdata->dstat & DSTAT_DFE)) {
+ printk ("scsi%d : DMA FIFO not empty\n", host->host_no);
+ if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) {
+ printk ("scsi%d: Flushing DMA FIFO\n",
+ host->host_no);
+ NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF);
+ while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) &
+ DSTAT_DFE));
+ } else {
+ NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF);
+ while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF);
+ }
+ hostdata->dstat |= DSTAT_DFE;
+ }
+ }
+}
+
+/*
+ * Function : static void NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs)
+ *
+ * Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing
+ * the same IRQ line.
+ *
+ * Inputs : Since we're using the SA_INTERRUPT interrupt handler
+ * semantics, irq indicates the interrupt which invoked
+ * this handler.
+ */
+
+static void
+NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) {
+ NCR53c7x0_local_declare();
+ struct Scsi_Host *host; /* Host we are looking at */
+ unsigned char istat; /* Values of interrupt regs */
+ struct NCR53c7x0_hostdata *hostdata; /* host->hostdata */
+ struct NCR53c7x0_cmd *cmd, /* command which halted */
+ **cmd_prev_ptr;
+ u32 *dsa; /* DSA */
+ int done = 1; /* Indicates when handler
+ should terminate */
+ int interrupted = 0; /* This HA generated
+ an interrupt */
+ int have_intfly; /* Don't print warning
+ messages when we stack
+ INTFLYs */
+ unsigned long flags;
+
+#ifdef NCR_DEBUG
+ char buf[80]; /* Debugging sprintf buffer */
+ size_t buflen; /* Length of same */
+#endif
+
+ do {
+ done = 1;
+ for (host = first_host; host; host = host->next)
+ if (host->hostt == the_template && host->irq == irq) {
+ NCR53c7x0_local_setup(host);
+
+ hostdata = (struct NCR53c7x0_hostdata *) host->hostdata;
+ hostdata->dsp_changed = 0;
+ interrupted = 0;
+ have_intfly = 0;
+
+ do {
+ int is_8xx_chip;
+
+ hostdata->dstat_valid = 0;
+ interrupted = 0;
+ /*
+ * Only read istat once, since reading it again will unstack
+ * interrupts?
+ */
+ istat = NCR53c7x0_read8(hostdata->istat);
+
+ /*
+ * INTFLY interrupts are used by the NCR53c720, NCR53c810,
+ * and NCR53c820 to signify completion of a command. Since
+ * the SCSI processor continues running, we can't just look
+ * at the contents of the DSA register and continue running.
+ */
+/* XXX - this is too big, offends my sense of aesthetics, and should
+ move to intr_intfly() */
+ is_8xx_chip = ((unsigned) (hostdata->chip - 800)) < 100;
+ if ((hostdata->options & OPTION_INTFLY) &&
+ (is_8xx_chip && (istat & ISTAT_800_INTF))) {
+ char search_found = 0; /* Got at least one ? */
+ done = 0;
+ interrupted = 1;
+
+ /*
+ * Clear the INTF bit by writing a one.
+ * This reset operation is self-clearing.
+ */
+ NCR53c7x0_write8(hostdata->istat, istat|ISTAT_800_INTF);
+
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : INTFLY\n", host->host_no);
+
+ /*
+ * Traverse our list of running commands, and look
+ * for those with valid (non-0xff ff) status and message
+ * bytes encoded in the result which signify command
+ * completion.
+ */
+
+
+ save_flags(flags);
+ cli();
+restart:
+ for (cmd_prev_ptr = (struct NCR53c7x0_cmd **)
+ &(hostdata->running_list), cmd =
+ (struct NCR53c7x0_cmd *) hostdata->running_list; cmd ;
+ cmd_prev_ptr = (struct NCR53c7x0_cmd **) &(cmd->next),
+ cmd = (struct NCR53c7x0_cmd *) cmd->next) {
+ Scsi_Cmnd *tmp;
+
+ if (!cmd) {
+ printk("scsi%d : very weird.\n", host->host_no);
+ break;
+ }
+
+ if (!(tmp = cmd->cmd)) {
+ printk("scsi%d : weird. NCR53c7x0_cmd has no Scsi_Cmnd\n",
+ host->host_no);
+ continue;
+ }
+#if 0
+ printk ("scsi%d : looking at result of 0x%x\n",
+ host->host_no, cmd->cmd->result);
+#endif
+
+ if (((tmp->result & 0xff) == 0xff) ||
+ ((tmp->result & 0xff00) == 0xff00))
+ continue;
+
+ search_found = 1;
+
+ /* Important - remove from list _before_ done is called */
+ if (cmd_prev_ptr)
+ *cmd_prev_ptr = (struct NCR53c7x0_cmd *) cmd->next;
+
+ --hostdata->busy[tmp->target][tmp->lun];
+ cmd->next = hostdata->free;
+ hostdata->free = cmd;
+
+ tmp->host_scribble = NULL;
+
+ if (hostdata->options & OPTION_DEBUG_INTR) {
+ printk ("scsi%d : command complete : pid %lu, id %d,lun %d result 0x%x ",
+ host->host_no, tmp->pid, tmp->target, tmp->lun, tmp->result);
+ print_command (tmp->cmnd);
+ }
+
+#if 0
+ hostdata->options &= ~OPTION_DEBUG_INTR;
+#endif
+ tmp->scsi_done(tmp);
+ goto restart;
+
+ }
+ restore_flags(flags);
+
+ /*
+ * I think that we're stacking INTFLY interrupts; taking care of
+ * all the finished commands on the first one, and then getting
+ * worried when we see the next one. The magic with have_intfly
+ * should tell if this is the case..
+ */
+
+ if (!search_found && !have_intfly) {
+ printk ("scsi%d : WARNING : INTFLY with no completed commands.\n",
+ host->host_no);
+ } else if (!have_intfly) {
+ have_intfly = 1;
+ run_process_issue_queue();
+ }
+ }
+
+ if (istat & (ISTAT_SIP|ISTAT_DIP)) {
+ done = 0;
+ interrupted = 1;
+ hostdata->state = STATE_HALTED;
+
+ if (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ?
+ SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK)
+ printk ("scsi%d : SCSI FIFO not empty\n",
+ host->host_no);
+
+ /*
+ * NCR53c700 and NCR53c700-66 change the current SCSI
+ * process, hostdata->current, in the Linux driver so
+ * cmd = hostdata->current.
+ *
+ * With other chips, we must look through the commands
+ * executing and find the command structure which
+ * corresponds to the DSA register.
+ */
+
+ if (hostdata->options & OPTION_700) {
+ cmd = (struct NCR53c7x0_cmd *) hostdata->current;
+ } else {
+ dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+ for (cmd = (struct NCR53c7x0_cmd *)
+ hostdata->running_list; cmd &&
+ (dsa + (hostdata->dsa_start / sizeof(u32))) !=
+ cmd->dsa;
+ cmd = (struct NCR53c7x0_cmd *)(cmd->next));
+ }
+ if (hostdata->options & OPTION_DEBUG_INTR) {
+ if (cmd) {
+ printk("scsi%d : interrupt for pid %lu, id %d, lun %d ",
+ host->host_no, cmd->cmd->pid, (int) cmd->cmd->target,
+ (int) cmd->cmd->lun);
+ print_command (cmd->cmd->cmnd);
+ } else {
+ printk("scsi%d : no active command\n", host->host_no);
+ }
+ }
+
+ if (istat & ISTAT_SIP) {
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : ISTAT_SIP\n", host->host_no);
+ intr_scsi (host, cmd);
+ }
+
+ if (istat & ISTAT_DIP) {
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : ISTAT_DIP\n", host->host_no);
+ intr_dma (host, cmd);
+ }
+
+ if (!hostdata->dstat_valid) {
+ hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+ hostdata->dstat_valid = 1;
+ }
+
+ /* XXX - code check for 700/800 chips */
+ if (!(hostdata->dstat & DSTAT_DFE)) {
+ printk ("scsi%d : DMA FIFO not empty\n", host->host_no);
+ if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) {
+ printk ("scsi%d: Flushing DMA FIFO\n",
+ host->host_no);
+ NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF);
+ while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) &
+ DSTAT_DFE));
+ } else
+ {
+ NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF);
+ while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF);
+ }
+ hostdata->dstat |= DSTAT_DFE;
+ }
+ }
+ } while (interrupted);
+
+
+
+ if (hostdata->intrs != -1)
+ hostdata->intrs++;
+#if 0
+ if (hostdata->intrs > 40) {
+ printk("scsi%d : too many interrupts, halting", host->host_no);
+ disable(host);
+ }
+#endif
+
+ if (!hostdata->idle && hostdata->state == STATE_HALTED) {
+ if (!hostdata->dsp_changed) {
+ hostdata->dsp = (u32 *)
+ bus_to_virt(NCR53c7x0_read32(DSP_REG));
+ }
+
+#if 0
+ printk("scsi%d : new dsp is 0x%lx (virt 0x%p)\n",
+ host->host_no, virt_to_bus(hostdata->dsp), hostdata->dsp);
+#endif
+
+ hostdata->state = STATE_RUNNING;
+ NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp));
+ }
+ }
+ } while (!done);
+}
+
+
+/*
+ * Function : static int abort_connected (struct Scsi_Host *host)
+ *
+ * Purpose : Assuming that the NCR SCSI processor is currently
+ * halted, break the currently established nexus. Clean
+ * up of the NCR53c7x0_cmd and Scsi_Cmnd structures should
+ * be done on receipt of the abort interrupt.
+ *
+ * Inputs : host - SCSI host
+ *
+ */
+
+static int
+abort_connected (struct Scsi_Host *host) {
+#ifdef NEW_ABORT
+ NCR53c7x0_local_declare();
+#endif
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+/* FIXME : this probably should change for production kernels; at the
+ least, counter should move to a per-host structure. */
+ static int counter = 5;
+#ifdef NEW_ABORT
+ int sstat, phase, offset;
+ u32 *script;
+ NCR53c7x0_local_setup(host);
+#endif
+
+ if (--counter <= 0) {
+ disable(host);
+ return 0;
+ }
+
+ printk ("scsi%d : DANGER : abort_connected() called \n",
+ host->host_no);
+
+#ifdef NEW_ABORT
+
+/*
+ * New strategy : Rather than using a generic abort routine,
+ * we'll specifically try to source or sink the appropriate
+ * amount of data for the phase we're currently in (taking into
+ * account the current synchronous offset)
+ */
+
+ sstat = (NCR53c8x0_read8 ((chip / 100) == 8 ? SSTAT1_REG : SSTAT2_REG);
+ offset = OFFSET (sstat & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT;
+ phase = sstat & SSTAT2_PHASE_MASK;
+
+/*
+ * SET ATN
+ * MOVE source_or_sink, WHEN CURRENT PHASE
+ * < repeat for each outstanding byte >
+ * JUMP send_abort_message
+ */
+
+ script = hostdata->abort_script = kmalloc (
+ 8 /* instruction size */ * (
+ 1 /* set ATN */ +
+ (!offset ? 1 : offset) /* One transfer per outstanding byte */ +
+ 1 /* send abort message */),
+ GFP_ATOMIC);
+
+
+#else /* def NEW_ABORT */
+ hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+ sizeof(u32);
+#endif /* def NEW_ABORT */
+ hostdata->dsp_changed = 1;
+
+/* XXX - need to flag the command as aborted after the abort_connected
+ code runs
+ */
+ return 0;
+}
+
+/*
+ * Function : static int datapath_residual (Scsi_Host *host)
+ *
+ * Purpose : return residual data count of what's in the chip.
+ *
+ * Inputs : host - SCSI host
+ */
+
+static int
+datapath_residual (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int count, synchronous, sstat;
+ NCR53c7x0_local_setup(host);
+ /* COMPAT : the 700 and 700-66 need to use DFIFO_00_BO_MASK */
+ count = ((NCR53c7x0_read8 (DFIFO_REG) & DFIFO_10_BO_MASK) -
+ (NCR53c7x0_read32 (DBC_REG) & DFIFO_10_BO_MASK)) & DFIFO_10_BO_MASK;
+ synchronous = NCR53c7x0_read8 (SXFER_REG) & SXFER_MO_MASK;
+ /* COMPAT : DDIR is elsewhere on non-'8xx chips. */
+ if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) {
+ /* Receive */
+ if (synchronous)
+ count += (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ?
+ SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT;
+ else
+ if (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ?
+ SSTAT0_REG : SSTAT1_REG) & SSTAT1_ILF)
+ ++count;
+ } else {
+ /* Send */
+ sstat = ((hostdata->chip / 100) == 8) ? NCR53c7x0_read8 (SSTAT0_REG) :
+ NCR53c7x0_read8 (SSTAT1_REG);
+ if (sstat & SSTAT1_OLF)
+ ++count;
+ if (synchronous && (sstat & SSTAT1_ORF))
+ ++count;
+ }
+ return count;
+}
+
+/*
+ * Function : static const char * sbcl_to_phase (int sbcl)_
+ *
+ * Purpose : Convert SBCL register to user-parsable phase representation
+ *
+ * Inputs : sbcl - value of sbcl register
+ */
+
+
+static const char *
+sbcl_to_phase (int sbcl) {
+ switch (sbcl & SBCL_PHASE_MASK) {
+ case SBCL_PHASE_DATAIN:
+ return "DATAIN";
+ case SBCL_PHASE_DATAOUT:
+ return "DATAOUT";
+ case SBCL_PHASE_MSGIN:
+ return "MSGIN";
+ case SBCL_PHASE_MSGOUT:
+ return "MSGOUT";
+ case SBCL_PHASE_CMDOUT:
+ return "CMDOUT";
+ case SBCL_PHASE_STATIN:
+ return "STATUSIN";
+ default:
+ return "unknown";
+ }
+}
+
+/*
+ * Function : static const char * sstat2_to_phase (int sstat)_
+ *
+ * Purpose : Convert SSTAT2 register to user-parsable phase representation
+ *
+ * Inputs : sstat - value of sstat register
+ */
+
+
+static const char *
+sstat2_to_phase (int sstat) {
+ switch (sstat & SSTAT2_PHASE_MASK) {
+ case SSTAT2_PHASE_DATAIN:
+ return "DATAIN";
+ case SSTAT2_PHASE_DATAOUT:
+ return "DATAOUT";
+ case SSTAT2_PHASE_MSGIN:
+ return "MSGIN";
+ case SSTAT2_PHASE_MSGOUT:
+ return "MSGOUT";
+ case SSTAT2_PHASE_CMDOUT:
+ return "CMDOUT";
+ case SSTAT2_PHASE_STATIN:
+ return "STATUSIN";
+ default:
+ return "unknown";
+ }
+}
+
+/*
+ * Function : static void intr_phase_mismatch (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : Handle phase mismatch interrupts
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ * may be NULL.
+ *
+ * Side effects : The abort_connected() routine is called or the NCR chip
+ * is restarted, jumping to the command_complete entry point, or
+ * patching the address and transfer count of the current instruction
+ * and calling the msg_in entry point as appropriate.
+ */
+
+static void
+intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ u32 dbc_dcmd, *dsp, *dsp_next;
+ unsigned char dcmd, sbcl;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int residual;
+ enum {ACTION_ABORT, ACTION_ABORT_PRINT, ACTION_CONTINUE} action =
+ ACTION_ABORT_PRINT;
+ const char *where = NULL;
+ NCR53c7x0_local_setup(host);
+
+ /*
+ * Corrective action is based on where in the SCSI SCRIPT(tm) the error
+ * occurred, as well as which SCSI phase we are currently in.
+ */
+ dsp_next = bus_to_virt(NCR53c7x0_read32(DSP_REG));
+
+ /*
+ * Fetch the current instruction, and remove the operands for easier
+ * interpretation.
+ */
+ dbc_dcmd = NCR53c7x0_read32(DBC_REG);
+ dcmd = (dbc_dcmd & 0xff000000) >> 24;
+ /*
+ * Like other processors, the NCR adjusts the instruction pointer before
+ * instruction decode. Set the DSP address back to what it should
+ * be for this instruction based on its size (2 or 3 32 bit words).
+ */
+ dsp = dsp_next - NCR53c7x0_insn_size(dcmd);
+
+
+ /*
+ * Read new SCSI phase from the SBCL lines. Since all of our code uses
+ * a WHEN conditional instead of an IF conditional, we don't need to
+ * wait for a new REQ.
+ */
+ sbcl = NCR53c7x0_read8(SBCL_REG) & SBCL_PHASE_MASK;
+
+ if (!cmd) {
+ action = ACTION_ABORT_PRINT;
+ where = "no current command";
+ /*
+ * The way my SCSI SCRIPTS(tm) are architected, recoverable phase
+ * mismatches should only occur where we're doing a multi-byte
+ * BMI instruction. Specifically, this means
+ *
+ * - select messages (a SCSI-I target may ignore additional messages
+ * after the IDENTIFY; any target may reject a SDTR or WDTR)
+ *
+ * - command out (targets may send a message to signal an error
+ * condition, or go into STATUSIN after they've decided
+ * they don't like the command.
+ *
+ * - reply_message (targets may reject a multi-byte message in the
+ * middle)
+ *
+ * - data transfer routines (command completion with buffer space
+ * left, disconnect message, or error message)
+ */
+ } else if (((dsp >= cmd->data_transfer_start &&
+ dsp < cmd->data_transfer_end)) || dsp == (cmd->residual + 2)) {
+ if ((dcmd & (DCMD_TYPE_MASK|DCMD_BMI_OP_MASK|DCMD_BMI_INDIRECT|
+ DCMD_BMI_MSG|DCMD_BMI_CD)) == (DCMD_TYPE_BMI|
+ DCMD_BMI_OP_MOVE_I)) {
+ residual = datapath_residual (host);
+ if (hostdata->options & OPTION_DEBUG_DISCONNECT)
+ printk ("scsi%d : handling residual transfer (+ %d bytes from DMA FIFO)\n",
+ host->host_no, residual);
+
+ /*
+ * The first instruction is a CALL to the alternate handler for
+ * this data transfer phase, so we can do calls to
+ * munge_msg_restart as we would if control were passed
+ * from normal dynamic code.
+ */
+ if (dsp != cmd->residual + 2) {
+ cmd->residual[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
+ ((dcmd & DCMD_BMI_IO) ? DCMD_TCI_IO : 0)) << 24) |
+ DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE;
+ cmd->residual[1] = virt_to_bus(hostdata->script)
+ + ((dcmd & DCMD_BMI_IO)
+ ? hostdata->E_other_in : hostdata->E_other_out);
+ }
+
+ /*
+ * The second instruction is the a data transfer block
+ * move instruction, reflecting the pointer and count at the
+ * time of the phase mismatch.
+ */
+ cmd->residual[2] = dbc_dcmd + residual;
+ cmd->residual[3] = NCR53c7x0_read32(DNAD_REG) - residual;
+
+ /*
+ * The third and final instruction is a jump to the instruction
+ * which follows the instruction which had to be 'split'
+ */
+ if (dsp != cmd->residual + 2) {
+ cmd->residual[4] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP)
+ << 24) | DBC_TCI_TRUE;
+ cmd->residual[5] = virt_to_bus(dsp_next);
+ }
+
+ /*
+ * For the sake of simplicity, transfer control to the
+ * conditional CALL at the start of the residual buffer.
+ */
+ hostdata->dsp = cmd->residual;
+ hostdata->dsp_changed = 1;
+ action = ACTION_CONTINUE;
+ } else {
+ where = "non-BMI dynamic DSA code";
+ action = ACTION_ABORT_PRINT;
+ }
+ } else if (dsp == (hostdata->script + hostdata->E_select_msgout / 4)) {
+ /* Release ATN */
+ NCR53c7x0_write8 (SOCL_REG, 0);
+ switch (sbcl) {
+ /*
+ * Some devices (SQ555 come to mind) grab the IDENTIFY message
+ * sent on selection, and decide to go into COMMAND OUT phase
+ * rather than accepting the rest of the messages or rejecting
+ * them. Handle these devices gracefully.
+ */
+ case SBCL_PHASE_CMDOUT:
+ hostdata->dsp = dsp + 2 /* two _words_ */;
+ hostdata->dsp_changed = 1;
+ printk ("scsi%d : target %d ignored SDTR and went into COMMAND OUT\n",
+ host->host_no, cmd->cmd->target);
+ cmd->flags &= ~CMD_FLAG_SDTR;
+ action = ACTION_CONTINUE;
+ break;
+ case SBCL_PHASE_MSGIN:
+ hostdata->dsp = hostdata->script + hostdata->E_msg_in /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ action = ACTION_CONTINUE;
+ break;
+ default:
+ where="select message out";
+ action = ACTION_ABORT_PRINT;
+ }
+ /*
+ * Some SCSI devices will interpret a command as they read the bytes
+ * off the SCSI bus, and may decide that the command is Bogus before
+ * they've read the entire command off the bus.
+ */
+ } else if (dsp == hostdata->script + hostdata->E_cmdout_cmdout / sizeof
+ (u32)) {
+ hostdata->dsp = hostdata->script + hostdata->E_data_transfer /
+ sizeof (u32);
+ hostdata->dsp_changed = 1;
+ action = ACTION_CONTINUE;
+ /* FIXME : we need to handle message reject, etc. within msg_respond. */
+#ifdef notyet
+ } else if (dsp == hostdata->script + hostdata->E_reply_message) {
+ switch (sbcl) {
+ /* Any other phase mismatches abort the currently executing command. */
+#endif
+ } else {
+ where = "unknown location";
+ action = ACTION_ABORT_PRINT;
+ }
+
+ /* Flush DMA FIFO */
+ if (!hostdata->dstat_valid) {
+ hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+ hostdata->dstat_valid = 1;
+ }
+ if (!(hostdata->dstat & DSTAT_DFE)) {
+ if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) {
+ printk ("scsi%d: Flushing DMA FIFO\n",
+ host->host_no);
+ NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF);
+ /* FIXME : what about stacked DMA interrupts? */
+ while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) &
+ DSTAT_DFE));
+ } else {
+ NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF);
+ while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF);
+ }
+ hostdata->dstat |= DSTAT_DFE;
+ }
+
+ switch (action) {
+ case ACTION_ABORT_PRINT:
+ printk("scsi%d : %s : unexpected phase %s.\n",
+ host->host_no, where ? where : "unknown location",
+ sbcl_to_phase(sbcl));
+ print_lots (host);
+ /* Fall through to ACTION_ABORT */
+ case ACTION_ABORT:
+ abort_connected (host);
+ break;
+ case ACTION_CONTINUE:
+ break;
+ }
+
+#if 0
+ if (hostdata->dsp_changed) {
+ printk("scsi%d: new dsp 0x%p\n", host->host_no, hostdata->dsp);
+ print_insn (host, hostdata->dsp, "", 1);
+ }
+#endif
+
+}
+
+/*
+ * Function : static void intr_bf (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : handle BUS FAULT interrupts
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ * may be NULL.
+ */
+
+static void
+intr_bf (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ u32 *dsp,
+ *next_dsp, /* Current dsp */
+ *dsa,
+ dbc_dcmd; /* DCMD (high eight bits) + DBC */
+ unsigned short pci_status;
+ int tmp;
+ unsigned long flags;
+ char *reason = NULL;
+ /* Default behavior is for a silent error, with a retry until we've
+ exhausted retries. */
+ enum {MAYBE, ALWAYS, NEVER} retry = MAYBE;
+ int report = 0;
+ NCR53c7x0_local_setup(host);
+
+ dbc_dcmd = NCR53c7x0_read32 (DBC_REG);
+ next_dsp = bus_to_virt (NCR53c7x0_read32(DSP_REG));
+ dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff);
+/* FIXME - check chip type */
+ dsa = bus_to_virt (NCR53c7x0_read32(DSA_REG));
+
+ /*
+ * Bus faults can be caused by either a Bad Address or
+ * Target Abort. We should check the Received Target Abort
+ * bit of the PCI status register and Master Abort Bit.
+ *
+ * - Master Abort bit indicates that no device claimed
+ * the address with DEVSEL within five clocks
+ *
+ * - Target Abort bit indicates that a target claimed it,
+ * but changed its mind once it saw the byte enables.
+ *
+ */
+
+ if ((hostdata->chip / 100) == 8) {
+ save_flags (flags);
+ cli();
+ tmp = pcibios_read_config_word (hostdata->pci_bus,
+ hostdata->pci_device_fn, PCI_STATUS, &pci_status);
+ restore_flags (flags);
+ if (tmp == PCIBIOS_SUCCESSFUL) {
+ if (pci_status & PCI_STATUS_REC_TARGET_ABORT) {
+ reason = "PCI target abort";
+ pci_status &= ~PCI_STATUS_REC_TARGET_ABORT;
+ } else if (pci_status & PCI_STATUS_REC_MASTER_ABORT) {
+ reason = "No device asserted PCI DEVSEL within five bus clocks";
+ pci_status &= ~PCI_STATUS_REC_MASTER_ABORT;
+ } else if (pci_status & PCI_STATUS_PARITY) {
+ report = 1;
+ pci_status &= ~PCI_STATUS_PARITY;
+ }
+ } else {
+ printk ("scsi%d : couldn't read status register : %s\n",
+ host->host_no, pcibios_strerror (tmp));
+ retry = NEVER;
+ }
+ }
+
+#ifndef notyet
+ report = 1;
+#endif
+ if (report && reason) {
+ printk(KERN_ALERT "scsi%d : BUS FAULT reason = %s\n",
+ host->host_no, reason ? reason : "unknown");
+ print_lots (host);
+ }
+
+#ifndef notyet
+ retry = NEVER;
+#endif
+
+ /*
+ * TODO : we should attempt to recover from any spurious bus
+ * faults. After X retries, we should figure that things are
+ * sufficiently wedged, and call NCR53c7xx_reset.
+ *
+ * This code should only get executed once we've decided that we
+ * cannot retry.
+ */
+
+ if (retry == NEVER) {
+ printk(KERN_ALERT " mail drew@PoohSticks.ORG\n");
+ FATAL (host);
+ }
+}
+
+/*
+ * Function : static void intr_dma (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : handle all DMA interrupts, indicated by the setting
+ * of the DIP bit in the ISTAT register.
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ * may be NULL.
+ */
+
+static void
+intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned char dstat; /* DSTAT */
+ u32 *dsp,
+ *next_dsp, /* Current dsp */
+ *dsa,
+ dbc_dcmd; /* DCMD (high eight bits) + DBC */
+ int tmp;
+ unsigned long flags;
+ NCR53c7x0_local_setup(host);
+
+ if (!hostdata->dstat_valid) {
+ hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+ hostdata->dstat_valid = 1;
+ }
+
+ dstat = hostdata->dstat;
+
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk("scsi%d : DSTAT=0x%x\n", host->host_no, (int) dstat);
+
+ dbc_dcmd = NCR53c7x0_read32 (DBC_REG);
+ next_dsp = bus_to_virt(NCR53c7x0_read32(DSP_REG));
+ dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff);
+/* XXX - check chip type */
+ dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+
+ /*
+ * DSTAT_ABRT is the aborted interrupt. This is set whenever the
+ * SCSI chip is aborted.
+ *
+ * With NCR53c700 and NCR53c700-66 style chips, we should only
+ * get this when the chip is currently running the accept
+ * reselect/select code and we have set the abort bit in the
+ * ISTAT register.
+ *
+ */
+
+ if (dstat & DSTAT_ABRT) {
+#if 0
+ /* XXX - add code here to deal with normal abort */
+ if ((hostdata->options & OPTION_700) && (hostdata->state ==
+ STATE_ABORTING)) {
+ } else
+#endif
+ {
+ printk(KERN_ALERT "scsi%d : unexpected abort interrupt at\n"
+ " ", host->host_no);
+ print_insn (host, dsp, KERN_ALERT "s ", 1);
+ FATAL (host);
+ }
+ }
+
+ /*
+ * DSTAT_SSI is the single step interrupt. Should be generated
+ * whenever we have single stepped or are tracing.
+ */
+
+ if (dstat & DSTAT_SSI) {
+ if (hostdata->options & OPTION_DEBUG_TRACE) {
+ } else if (hostdata->options & OPTION_DEBUG_SINGLE) {
+ print_insn (host, dsp, "s ", 0);
+ save_flags(flags);
+ cli();
+/* XXX - should we do this, or can we get away with writing dsp? */
+
+ NCR53c7x0_write8 (DCNTL_REG, (NCR53c7x0_read8(DCNTL_REG) &
+ ~DCNTL_SSM) | DCNTL_STD);
+ restore_flags(flags);
+ } else {
+ printk(KERN_ALERT "scsi%d : unexpected single step interrupt at\n"
+ " ", host->host_no);
+ print_insn (host, dsp, KERN_ALERT "", 1);
+ printk(KERN_ALERT " mail drew@PoohSticks.ORG\n");
+ FATAL (host);
+ }
+ }
+
+ /*
+ * DSTAT_IID / DSTAT_OPC (same bit, same meaning, only the name
+ * is different) is generated whenever an illegal instruction is
+ * encountered.
+ *
+ * XXX - we may want to emulate INTFLY here, so we can use
+ * the same SCSI SCRIPT (tm) for NCR53c710 through NCR53c810
+ * chips.
+ */
+
+ if (dstat & DSTAT_OPC) {
+ /*
+ * Ascertain if this IID interrupts occurred before or after a STO
+ * interrupt. Since the interrupt handling code now leaves
+ * DSP unmodified until _after_ all stacked interrupts have been
+ * processed, reading the DSP returns the original DSP register.
+ * This means that if dsp lies between the select code, and
+ * message out following the selection code (where the IID interrupt
+ * would have to have occurred by due to the implicit wait for REQ),
+ * we have an IID interrupt resulting from a STO condition and
+ * can ignore it.
+ */
+
+ if (((dsp >= (hostdata->script + hostdata->E_select / sizeof(u32))) &&
+ (dsp <= (hostdata->script + hostdata->E_select_msgout /
+ sizeof(u32) + 8))) || (hostdata->test_running == 2)) {
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : ignoring DSTAT_IID for SSTAT_STO\n",
+ host->host_no);
+ if (hostdata->expecting_iid) {
+ hostdata->expecting_iid = 0;
+ hostdata->idle = 1;
+ if (hostdata->test_running == 2) {
+ hostdata->test_running = 0;
+ hostdata->test_completed = 3;
+ } else if (cmd)
+ abnormal_finished (cmd, DID_BAD_TARGET << 16);
+ } else {
+ hostdata->expecting_sto = 1;
+ }
+ /*
+ * We can't guarantee we'll be able to execute the WAIT DISCONNECT
+ * instruction within the 3.4us of bus free and arbitration delay
+ * that a target can RESELECT in and assert REQ after we've dropped
+ * ACK. If this happens, we'll get an illegal instruction interrupt.
+ * Doing away with the WAIT DISCONNECT instructions broke everything,
+ * so instead I'll settle for moving one WAIT DISCONNECT a few
+ * instructions closer to the CLEAR ACK before it to minimize the
+ * chances of this happening, and handle it if it occurs anyway.
+ *
+ * Simply continue with what we were doing, and control should
+ * be transfered to the schedule routine which will ultimately
+ * pass control onto the reselection or selection (not yet)
+ * code.
+ */
+ } else if (dbc_dcmd == 0x48000000 && (NCR53c7x0_read8 (SBCL_REG) &
+ SBCL_REQ)) {
+ if (!(hostdata->options & OPTION_NO_PRINT_RACE))
+ {
+ printk("scsi%d: REQ before WAIT DISCONNECT IID\n",
+ host->host_no);
+ hostdata->options |= OPTION_NO_PRINT_RACE;
+ }
+ } else {
+ printk(KERN_ALERT "scsi%d : illegal instruction\n", host->host_no);
+ print_lots (host);
+ printk(KERN_ALERT " mail drew@PoohSticks.ORG with ALL\n"
+ " boot messages and diagnostic output\n");
+ FATAL (host);
+ }
+ }
+
+ /*
+ * DSTAT_BF are bus fault errors
+ */
+
+ if (dstat & DSTAT_800_BF) {
+ intr_bf (host, cmd);
+ }
+
+
+ /*
+ * DSTAT_SIR interrupts are generated by the execution of
+ * the INT instruction. Since the exact values available
+ * are determined entirely by the SCSI script running,
+ * and are local to a particular script, a unique handler
+ * is called for each script.
+ */
+
+ if (dstat & DSTAT_SIR) {
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : DSTAT_SIR\n", host->host_no);
+ switch ((tmp = hostdata->dstat_sir_intr (host, cmd))) {
+ case SPECIFIC_INT_NOTHING:
+ case SPECIFIC_INT_RESTART:
+ break;
+ case SPECIFIC_INT_ABORT:
+ abort_connected(host);
+ break;
+ case SPECIFIC_INT_PANIC:
+ printk(KERN_ALERT "scsi%d : failure at ", host->host_no);
+ print_insn (host, dsp, KERN_ALERT "", 1);
+ printk(KERN_ALERT " dstat_sir_intr() returned SPECIFIC_INT_PANIC\n");
+ FATAL (host);
+ break;
+ case SPECIFIC_INT_BREAK:
+ intr_break (host, cmd);
+ break;
+ default:
+ printk(KERN_ALERT "scsi%d : failure at ", host->host_no);
+ print_insn (host, dsp, KERN_ALERT "", 1);
+ printk(KERN_ALERT" dstat_sir_intr() returned unknown value %d\n",
+ tmp);
+ FATAL (host);
+ }
+ }
+
+ if ((hostdata->chip / 100) == 8 && (dstat & DSTAT_800_MDPE)) {
+ printk(KERN_ALERT "scsi%d : Master Data Parity Error\n",
+ host->host_no);
+ FATAL (host);
+ }
+}
+
+/*
+ * Function : static int print_insn (struct Scsi_Host *host,
+ * u32 *insn, int kernel)
+ *
+ * Purpose : print numeric representation of the instruction pointed
+ * to by insn to the debugging or kernel message buffer
+ * as appropriate.
+ *
+ * If desired, a user level program can interpret this
+ * information.
+ *
+ * Inputs : host, insn - host, pointer to instruction, prefix -
+ * string to prepend, kernel - use printk instead of debugging buffer.
+ *
+ * Returns : size, in u32s, of instruction printed.
+ */
+
+/*
+ * FIXME: should change kernel parameter so that it takes an ENUM
+ * specifying severity - either KERN_ALERT or KERN_PANIC so
+ * all panic messages are output with the same severity.
+ */
+
+static int
+print_insn (struct Scsi_Host *host, const u32 *insn,
+ const char *prefix, int kernel) {
+ char buf[160], /* Temporary buffer and pointer. ICKY
+ arbitrary length. */
+
+
+ *tmp;
+ unsigned char dcmd; /* dcmd register for *insn */
+ int size;
+
+ /*
+ * Check to see if the instruction pointer is not bogus before
+ * indirecting through it; avoiding red-zone at start of
+ * memory.
+ *
+ * FIXME: icky magic needs to happen here on non-intel boxes which
+ * don't have kernel memory mapped in like this. Might be reasonable
+ * to use vverify()?
+ */
+
+ if (MAP_NR(insn) < 1 || MAP_NR(insn + 8) > MAP_NR(high_memory) ||
+ ((((dcmd = (insn[0] >> 24) & 0xff) & DCMD_TYPE_MMI) == DCMD_TYPE_MMI) &&
+ MAP_NR(insn + 12) > MAP_NR(high_memory))) {
+ size = 0;
+ sprintf (buf, "%s%p: address out of range\n",
+ prefix, insn);
+ } else {
+/*
+ * FIXME : (void *) cast in virt_to_bus should be unnecessary, because
+ * it should take const void * as argument.
+ */
+ sprintf(buf, "%s0x%lx (virt 0x%p) : 0x%08x 0x%08x (virt 0x%p)",
+ (prefix ? prefix : ""), virt_to_bus((void *) insn), insn,
+ insn[0], insn[1], bus_to_virt (insn[1]));
+ tmp = buf + strlen(buf);
+ if ((dcmd & DCMD_TYPE_MASK) == DCMD_TYPE_MMI) {
+ sprintf (tmp, " 0x%08x (virt 0x%p)\n", insn[2],
+ bus_to_virt(insn[2]));
+ size = 3;
+ } else {
+ sprintf (tmp, "\n");
+ size = 2;
+ }
+ }
+
+ if (kernel)
+ printk ("%s", buf);
+#ifdef NCR_DEBUG
+ else {
+ size_t len = strlen(buf);
+ debugger_kernel_write(host, buf, len);
+ }
+#endif
+ return size;
+}
+
+/*
+ * Function : static const char *ncr_state (int state)
+ *
+ * Purpose : convert state (probably from hostdata->state) to a string
+ *
+ * Inputs : state
+ *
+ * Returns : char * representation of state, "unknown" on error.
+ */
+
+static const char *
+ncr_state (int state) {
+ switch (state) {
+ case STATE_HALTED: return "halted";
+ case STATE_WAITING: return "waiting";
+ case STATE_RUNNING: return "running";
+ case STATE_ABORTING: return "aborting";
+ case STATE_DISABLED: return "disabled";
+ default: return "unknown";
+ }
+}
+
+/*
+ * Function : int NCR53c7xx_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : Abort an errant SCSI command, doing all necessary
+ * cleanup of the issue_queue, running_list, shared Linux/NCR
+ * dsa issue and reconnect queues.
+ *
+ * Inputs : cmd - command to abort, code - entire result field
+ *
+ * Returns : 0 on success, -1 on failure.
+ */
+
+int
+NCR53c7xx_abort (Scsi_Cmnd *cmd) {
+ NCR53c7x0_local_declare();
+ struct Scsi_Host *host = cmd->host;
+ struct NCR53c7x0_hostdata *hostdata = host ? (struct NCR53c7x0_hostdata *)
+ host->hostdata : NULL;
+ unsigned long flags;
+ struct NCR53c7x0_cmd *curr, **prev;
+ Scsi_Cmnd *me, **last;
+#if 0
+ static long cache_pid = -1;
+#endif
+
+
+ if (!host) {
+ printk ("Bogus SCSI command pid %ld; no host structure\n",
+ cmd->pid);
+ return SCSI_ABORT_ERROR;
+ } else if (!hostdata) {
+ printk ("Bogus SCSI host %d; no hostdata\n", host->host_no);
+ return SCSI_ABORT_ERROR;
+ }
+ NCR53c7x0_local_setup(host);
+
+/*
+ * CHECK : I don't think that reading ISTAT will unstack any interrupts,
+ * since we need to write the INTF bit to clear it, and SCSI/DMA
+ * interrupts don't clear until we read SSTAT/SIST and DSTAT registers.
+ *
+ * See that this is the case.
+ *
+ * I suspect that several of our failures may be coming from a new fatal
+ * interrupt (possibly due to a phase mismatch) happening after we've left
+ * the interrupt handler, but before the PIC has had the interrupt condition
+ * cleared.
+ */
+
+ if (NCR53c7x0_read8(hostdata->istat) &
+ (ISTAT_DIP|ISTAT_SIP|
+ (hostdata->chip / 100 == 8 ? ISTAT_800_INTF : 0))) {
+ printk ("scsi%d : dropped interrupt for command %ld\n", host->host_no,
+ cmd->pid);
+ NCR53c7x0_intr (host->irq, NULL, NULL);
+ return SCSI_ABORT_BUSY;
+ }
+
+ save_flags(flags);
+ cli();
+#if 0
+ if (cache_pid == cmd->pid)
+ panic ("scsi%d : bloody fetus %d\n", host->host_no, cmd->pid);
+ else
+ cache_pid = cmd->pid;
+#endif
+
+
+/*
+ * The command could be hiding in the issue_queue. This would be very
+ * nice, as commands can't be moved from the high level driver's issue queue
+ * into the shared queue until an interrupt routine is serviced, and this
+ * moving is atomic.
+ *
+ * If this is the case, we don't have to worry about anything - we simply
+ * pull the command out of the old queue, and call it aborted.
+ */
+
+ for (me = (Scsi_Cmnd *) hostdata->issue_queue,
+ last = (Scsi_Cmnd **) &(hostdata->issue_queue);
+ me && me != cmd; last = (Scsi_Cmnd **)&(me->SCp.ptr),
+ me = (Scsi_Cmnd *)me->SCp.ptr);
+
+ if (me) {
+ *last = (Scsi_Cmnd *) me->SCp.ptr;
+ if (me->host_scribble) {
+ ((struct NCR53c7x0_cmd *)me->host_scribble)->next = hostdata->free;
+ hostdata->free = (struct NCR53c7x0_cmd *) me->host_scribble;
+ me->host_scribble = NULL;
+ }
+ cmd->result = DID_ABORT << 16;
+ cmd->scsi_done(cmd);
+ printk ("scsi%d : found command %ld in Linux issue queue\n",
+ host->host_no, me->pid);
+ restore_flags(flags);
+ run_process_issue_queue();
+ return SCSI_ABORT_SUCCESS;
+ }
+
+/*
+ * That failing, the command could be in our list of already executing
+ * commands. If this is the case, drastic measures are called for.
+ */
+
+ for (curr = (struct NCR53c7x0_cmd *) hostdata->running_list,
+ prev = (struct NCR53c7x0_cmd **) &(hostdata->running_list);
+ curr && curr->cmd != cmd; prev = (struct NCR53c7x0_cmd **)
+ &(curr->next), curr = (struct NCR53c7x0_cmd *) curr->next);
+
+ if (curr) {
+ if ((cmd->result & 0xff) != 0xff && (cmd->result & 0xff00) != 0xff00) {
+ if (prev)
+ *prev = (struct NCR53c7x0_cmd *) curr->next;
+ curr->next = (struct NCR53c7x0_cmd *) hostdata->free;
+ cmd->host_scribble = NULL;
+ hostdata->free = curr;
+ cmd->scsi_done(cmd);
+ printk ("scsi%d : found finished command %ld in running list\n",
+ host->host_no, cmd->pid);
+ restore_flags(flags);
+ return SCSI_ABORT_NOT_RUNNING;
+ } else {
+ printk ("scsi%d : DANGER : command running, can not abort.\n",
+ cmd->host->host_no);
+ restore_flags(flags);
+ return SCSI_ABORT_BUSY;
+ }
+ }
+
+/*
+ * And if we couldn't find it in any of our queues, it must have been
+ * a dropped interrupt.
+ */
+
+ curr = (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ if (curr) {
+ curr->next = hostdata->free;
+ hostdata->free = curr;
+ cmd->host_scribble = NULL;
+ }
+
+ if (((cmd->result & 0xff00) == 0xff00) ||
+ ((cmd->result & 0xff) == 0xff)) {
+ printk ("scsi%d : did this command ever run?\n", host->host_no);
+ cmd->result = DID_ABORT << 16;
+ } else {
+ printk ("scsi%d : probably lost INTFLY, normal completion\n",
+ host->host_no);
+/*
+ * FIXME : We need to add an additional flag which indicates if a
+ * command was ever counted as BUSY, so if we end up here we can
+ * decrement the busy count if and only if it is necessary.
+ */
+ --hostdata->busy[cmd->target][cmd->lun];
+ }
+ restore_flags(flags);
+ cmd->scsi_done(cmd);
+
+/*
+ * We need to run process_issue_queue since termination of this command
+ * may allow another queued command to execute first?
+ */
+ return SCSI_ABORT_NOT_RUNNING;
+}
+
+/*
+ * Function : int NCR53c7xx_reset (Scsi_Cmnd *cmd)
+ *
+ * Purpose : perform a hard reset of the SCSI bus and NCR
+ * chip.
+ *
+ * Inputs : cmd - command which caused the SCSI RESET
+ *
+ * Returns : 0 on success.
+ */
+
+int
+NCR53c7xx_reset (Scsi_Cmnd *cmd, unsigned int reset_flags) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ int found = 0;
+ struct NCR53c7x0_cmd * c;
+ Scsi_Cmnd *tmp;
+ /*
+ * When we call scsi_done(), it's going to wake up anything sleeping on the
+ * resources which were in use by the aborted commands, and we'll start to
+ * get new commands.
+ *
+ * We can't let this happen until after we've re-initialized the driver
+ * structures, and can't reinitialize those structures until after we've
+ * dealt with their contents.
+ *
+ * So, we need to find all of the commands which were running, stick
+ * them on a linked list of completed commands (we'll use the host_scribble
+ * pointer), do our reinitialization, and then call the done function for
+ * each command.
+ */
+ Scsi_Cmnd *nuke_list = NULL;
+ struct Scsi_Host *host = cmd->host;
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata;
+
+ NCR53c7x0_local_setup(host);
+ save_flags(flags);
+ cli();
+ ncr_halt (host);
+ print_lots (host);
+ dump_events (host, 30);
+ ncr_scsi_reset (host);
+ for (tmp = nuke_list = return_outstanding_commands (host, 1 /* free */,
+ 0 /* issue */ ); tmp; tmp = (Scsi_Cmnd *) tmp->SCp.buffer)
+ if (tmp == cmd) {
+ found = 1;
+ break;
+ }
+
+ /*
+ * If we didn't find the command which caused this reset in our running
+ * list, then we've lost it. See that it terminates normally anyway.
+ */
+ if (!found) {
+ c = (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ if (c) {
+ cmd->host_scribble = NULL;
+ c->next = hostdata->free;
+ hostdata->free = c;
+ } else
+ printk ("scsi%d: lost command %ld\n", host->host_no, cmd->pid);
+ cmd->SCp.buffer = (struct scatterlist *) nuke_list;
+ nuke_list = cmd;
+ }
+
+ NCR53c7x0_driver_init (host);
+ hostdata->soft_reset (host);
+ if (hostdata->resets == 0)
+ disable(host);
+ else if (hostdata->resets != -1)
+ --hostdata->resets;
+ sti();
+ for (; nuke_list; nuke_list = tmp) {
+ tmp = (Scsi_Cmnd *) nuke_list->SCp.buffer;
+ nuke_list->result = DID_RESET << 16;
+ nuke_list->scsi_done (nuke_list);
+ }
+ restore_flags(flags);
+ return SCSI_RESET_SUCCESS;
+}
+
+/*
+ * The NCR SDMS bios follows Annex A of the SCSI-CAM draft, and
+ * therefore shares the scsicam_bios_param function.
+ */
+
+/*
+ * Function : int insn_to_offset (Scsi_Cmnd *cmd, u32 *insn)
+ *
+ * Purpose : convert instructions stored at NCR pointer into data
+ * pointer offset.
+ *
+ * Inputs : cmd - SCSI command; insn - pointer to instruction. Either current
+ * DSP, or saved data pointer.
+ *
+ * Returns : offset on success, -1 on failure.
+ */
+
+
+static int
+insn_to_offset (Scsi_Cmnd *cmd, u32 *insn) {
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) cmd->host->hostdata;
+ struct NCR53c7x0_cmd *ncmd =
+ (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ int offset = 0, buffers;
+ struct scatterlist *segment;
+ char *ptr;
+ int found = 0;
+
+/*
+ * With the current code implementation, if the insn is inside dynamically
+ * generated code, the data pointer will be the instruction preceding
+ * the next transfer segment.
+ */
+
+ if (!check_address ((unsigned long) ncmd, sizeof (struct NCR53c7x0_cmd)) &&
+ ((insn >= ncmd->data_transfer_start &&
+ insn < ncmd->data_transfer_end) ||
+ (insn >= ncmd->residual &&
+ insn < (ncmd->residual +
+ sizeof(ncmd->residual))))) {
+ ptr = bus_to_virt(insn[3]);
+
+ if ((buffers = cmd->use_sg)) {
+ for (offset = 0,
+ segment = (struct scatterlist *) cmd->buffer;
+ buffers && !((found = ((ptr >= segment->address) &&
+ (ptr < (segment->address + segment->length)))));
+ --buffers, offset += segment->length, ++segment)
+#if 0
+ printk("scsi%d: comparing 0x%p to 0x%p\n",
+ cmd->host->host_no, saved, segment->address);
+#else
+ ;
+#endif
+ offset += ptr - segment->address;
+ } else {
+ found = 1;
+ offset = ptr - (char *) (cmd->request_buffer);
+ }
+ } else if ((insn >= hostdata->script +
+ hostdata->E_data_transfer / sizeof(u32)) &&
+ (insn <= hostdata->script +
+ hostdata->E_end_data_transfer / sizeof(u32))) {
+ found = 1;
+ offset = 0;
+ }
+ return found ? offset : -1;
+}
+
+
+
+/*
+ * Function : void print_progress (Scsi_Cmnd *cmd)
+ *
+ * Purpose : print the current location of the saved data pointer
+ *
+ * Inputs : cmd - command we are interested in
+ *
+ */
+
+static void
+print_progress (Scsi_Cmnd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_cmd *ncmd =
+ (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ int offset, i;
+ char *where;
+ u32 *ptr;
+ NCR53c7x0_local_setup (cmd->host);
+ for (i = 0; i < 2; ++i) {
+ if (check_address ((unsigned long) ncmd,
+ sizeof (struct NCR53c7x0_cmd)) == -1)
+ continue;
+ if (!i) {
+ where = "saved";
+ ptr = bus_to_virt(ncmd->saved_data_pointer);
+ } else {
+ where = "active";
+ ptr = bus_to_virt (NCR53c7x0_read32 (DSP_REG) -
+ NCR53c7x0_insn_size (NCR53c7x0_read8 (DCMD_REG)) *
+ sizeof(u32));
+ }
+ offset = insn_to_offset (cmd, ptr);
+
+ if (offset != -1)
+ printk ("scsi%d : %s data pointer at offset %d\n",
+ cmd->host->host_no, where, offset);
+ else {
+ int size;
+ printk ("scsi%d : can't determine %s data pointer offset\n",
+ cmd->host->host_no, where);
+ if (ncmd) {
+ size = print_insn (cmd->host,
+ bus_to_virt(ncmd->saved_data_pointer), "", 1);
+ print_insn (cmd->host,
+ bus_to_virt(ncmd->saved_data_pointer) + size * sizeof(u32),
+ "", 1);
+ }
+ }
+ }
+}
+
+
+static void
+print_dsa (struct Scsi_Host *host, u32 *dsa, const char *prefix) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int i, len;
+ char *ptr;
+ Scsi_Cmnd *cmd;
+
+ if (check_address ((unsigned long) dsa, hostdata->dsa_end -
+ hostdata->dsa_start) == -1) {
+ printk("scsi%d : bad dsa virt 0x%p\n", host->host_no, dsa);
+ return;
+ }
+ printk("%sscsi%d : dsa at phys 0x%lx (virt 0x%p)\n"
+ " + %d : dsa_msgout length = %u, data = 0x%x (virt 0x%p)\n" ,
+ prefix ? prefix : "",
+ host->host_no, virt_to_bus (dsa), dsa, hostdata->dsa_msgout,
+ dsa[hostdata->dsa_msgout / sizeof(u32)],
+ dsa[hostdata->dsa_msgout / sizeof(u32) + 1],
+ bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1]));
+
+ /*
+ * Only print messages if they're sane in length so we don't
+ * blow the kernel printk buffer on something which won't buy us
+ * anything.
+ */
+
+ if (dsa[hostdata->dsa_msgout / sizeof(u32)] <
+ sizeof (hostdata->free->select))
+ for (i = dsa[hostdata->dsa_msgout / sizeof(u32)],
+ ptr = bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1]);
+ i > 0 && !check_address ((unsigned long) ptr, 1);
+ ptr += len, i -= len) {
+ printk(" ");
+ len = print_msg (ptr);
+ printk("\n");
+ if (!len)
+ break;
+ }
+
+ printk(" + %d : select_indirect = 0x%x\n",
+ hostdata->dsa_select, dsa[hostdata->dsa_select / sizeof(u32)]);
+ cmd = (Scsi_Cmnd *) bus_to_virt(dsa[hostdata->dsa_cmnd / sizeof(u32)]);
+ printk(" + %d : dsa_cmnd = 0x%x ", hostdata->dsa_cmnd,
+ (u32) virt_to_bus(cmd));
+ if (cmd) {
+ printk(" result = 0x%x, target = %d, lun = %d, cmd = ",
+ cmd->result, cmd->target, cmd->lun);
+ print_command(cmd->cmnd);
+ } else
+ printk("\n");
+ printk(" + %d : dsa_next = 0x%x\n", hostdata->dsa_next,
+ dsa[hostdata->dsa_next / sizeof(u32)]);
+ if (cmd) {
+ printk("scsi%d target %d : sxfer_sanity = 0x%x, scntl3_sanity = 0x%x\n"
+ " script : ",
+ host->host_no, cmd->target,
+ hostdata->sync[cmd->target].sxfer_sanity,
+ hostdata->sync[cmd->target].scntl3_sanity);
+ for (i = 0; i < (sizeof(hostdata->sync[cmd->target].script) / 4); ++i)
+ printk ("0x%x ", hostdata->sync[cmd->target].script[i]);
+ printk ("\n");
+ print_progress (cmd);
+ }
+}
+/*
+ * Function : void print_queues (Scsi_Host *host)
+ *
+ * Purpose : print the contents of the NCR issue and reconnect queues
+ *
+ * Inputs : host - SCSI host we are interested in
+ *
+ */
+
+static void
+print_queues (struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ u32 *dsa, *next_dsa;
+ volatile u32 *current;
+ int left;
+ Scsi_Cmnd *cmd, *next_cmd;
+ unsigned long flags;
+
+ printk ("scsi%d : issue queue\n", host->host_no);
+
+ for (left = host->can_queue, cmd = (Scsi_Cmnd *) hostdata->issue_queue;
+ left >= 0 && cmd;
+ cmd = next_cmd) {
+ next_cmd = (Scsi_Cmnd *) cmd->SCp.ptr;
+ save_flags(flags);
+ cli();
+ if (cmd->host_scribble) {
+ if (check_address ((unsigned long) (cmd->host_scribble),
+ sizeof (cmd->host_scribble)) == -1)
+ printk ("scsi%d: scsi pid %ld bad pointer to NCR53c7x0_cmd\n",
+ host->host_no, cmd->pid);
+ /* print_dsa does sanity check on address, no need to check */
+ else
+ print_dsa (host, ((struct NCR53c7x0_cmd *) cmd->host_scribble)
+ -> dsa, "");
+ } else
+ printk ("scsi%d : scsi pid %ld for target %d lun %d has no NCR53c7x0_cmd\n",
+ host->host_no, cmd->pid, cmd->target, cmd->lun);
+ restore_flags(flags);
+ }
+
+ if (left <= 0) {
+ printk ("scsi%d : loop detected in issue queue\n",
+ host->host_no);
+ }
+
+ /*
+ * Traverse the NCR reconnect and start DSA structures, printing out
+ * each element until we hit the end or detect a loop. Currently,
+ * the reconnect structure is a linked list; and the start structure
+ * is an array. Eventually, the reconnect structure will become a
+ * list as well, since this simplifies the code.
+ */
+
+ printk ("scsi%d : schedule dsa array :\n", host->host_no);
+ for (left = host->can_queue, current = hostdata->schedule;
+ left > 0; current += 2, --left)
+ if (current[0] != hostdata->NOP_insn)
+/* FIXME : convert pointer to dsa_begin to pointer to dsa. */
+ print_dsa (host, bus_to_virt (current[1] -
+ (hostdata->E_dsa_code_begin -
+ hostdata->E_dsa_code_template)), "");
+ printk ("scsi%d : end schedule dsa array\n", host->host_no);
+
+ printk ("scsi%d : reconnect_dsa_head :\n", host->host_no);
+
+ for (left = host->can_queue,
+ dsa = bus_to_virt (hostdata->reconnect_dsa_head);
+ left >= 0 && dsa;
+ dsa = next_dsa) {
+ save_flags (flags);
+ cli();
+ if (check_address ((unsigned long) dsa, sizeof(dsa)) == -1) {
+ printk ("scsi%d: bad DSA pointer 0x%p", host->host_no,
+ dsa);
+ next_dsa = NULL;
+ }
+ else
+ {
+ next_dsa = bus_to_virt(dsa[hostdata->dsa_next / sizeof(u32)]);
+ print_dsa (host, dsa, "");
+ }
+ restore_flags(flags);
+ }
+ printk ("scsi%d : end reconnect_dsa_head\n", host->host_no);
+ if (left < 0)
+ printk("scsi%d: possible loop in ncr reconnect list\n",
+ host->host_no);
+}
+
+static void
+print_lots (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata;
+ u32 *dsp_next, *dsp, *dsa, dbc_dcmd;
+ unsigned char dcmd, sbcl;
+ int i, size;
+ NCR53c7x0_local_setup(host);
+
+ if ((dsp_next = bus_to_virt(NCR53c7x0_read32 (DSP_REG)))) {
+ dbc_dcmd = NCR53c7x0_read32(DBC_REG);
+ dcmd = (dbc_dcmd & 0xff000000) >> 24;
+ dsp = dsp_next - NCR53c7x0_insn_size(dcmd);
+ dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+ sbcl = NCR53c7x0_read8 (SBCL_REG);
+
+
+ printk ("scsi%d : DCMD|DBC=0x%x, DNAD=0x%x (virt 0x%p)\n"
+ " DSA=0x%lx (virt 0x%p)\n"
+ " DSPS=0x%x, TEMP=0x%x (virt 0x%p), DMODE=0x%x\n"
+ " SXFER=0x%x, SCNTL3=0x%x\n"
+ " %s%s%sphase=%s, %d bytes in SCSI FIFO\n"
+ " STEST0=0x%x\n",
+ host->host_no, dbc_dcmd, NCR53c7x0_read32(DNAD_REG),
+ bus_to_virt(NCR53c7x0_read32(DNAD_REG)),
+ virt_to_bus(dsa), dsa,
+ NCR53c7x0_read32(DSPS_REG), NCR53c7x0_read32(TEMP_REG),
+ bus_to_virt (NCR53c7x0_read32(TEMP_REG)),
+ (int) NCR53c7x0_read8(hostdata->dmode),
+ (int) NCR53c7x0_read8(SXFER_REG),
+ (int) NCR53c7x0_read8(SCNTL3_REG_800),
+ (sbcl & SBCL_BSY) ? "BSY " : "",
+ (sbcl & SBCL_SEL) ? "SEL " : "",
+ (sbcl & SBCL_REQ) ? "REQ " : "",
+ sstat2_to_phase(NCR53c7x0_read8 (((hostdata->chip / 100) == 8) ?
+ SSTAT1_REG : SSTAT2_REG)),
+ (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ?
+ SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT,
+ NCR53c7x0_read8 (STEST0_REG_800));
+ printk ("scsi%d : DSP 0x%lx (virt 0x%p) ->\n", host->host_no,
+ virt_to_bus(dsp), dsp);
+ for (i = 6; i > 0; --i, dsp += size)
+ size = print_insn (host, dsp, "", 1);
+ if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) {
+ printk ("scsi%d : connected (SDID=0x%x, SSID=0x%x)\n",
+ host->host_no, NCR53c7x0_read8 (SDID_REG_800),
+ NCR53c7x0_read8 (SSID_REG_800));
+ print_dsa (host, dsa, "");
+ }
+
+#if 1
+ print_queues (host);
+#endif
+ }
+}
+
+/*
+ * Function : static int shutdown (struct Scsi_Host *host)
+ *
+ * Purpose : does a clean (we hope) shutdown of the NCR SCSI
+ * chip. Use prior to dumping core, unloading the NCR driver,
+ *
+ * Returns : 0 on success
+ */
+static int
+shutdown (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ NCR53c7x0_local_setup(host);
+ save_flags (flags);
+ cli();
+/* Get in a state where we can reset the SCSI bus */
+ ncr_halt (host);
+ ncr_scsi_reset (host);
+ hostdata->soft_reset(host);
+
+ disable (host);
+ restore_flags (flags);
+ return 0;
+}
+
+/*
+ * Function : void ncr_scsi_reset (struct Scsi_Host *host)
+ *
+ * Purpose : reset the SCSI bus.
+ */
+
+static void
+ncr_scsi_reset (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned long flags;
+ int sien = 0;
+ NCR53c7x0_local_setup(host);
+ save_flags (flags);
+ cli();
+ if ((hostdata->chip / 100) == 8) {
+ sien = NCR53c7x0_read8(SIEN0_REG_800);
+ NCR53c7x0_write8(SIEN0_REG_800, sien & ~SIEN_RST);
+ }
+ NCR53c7x0_write8(SCNTL1_REG, SCNTL1_RST);
+ udelay(25); /* Minimum amount of time to assert RST */
+ NCR53c7x0_write8(SCNTL1_REG, 0);
+ if ((hostdata->chip / 100) == 8) {
+ NCR53c7x0_write8(SIEN0_REG_800, sien);
+ }
+ restore_flags (flags);
+}
+
+/*
+ * Function : void hard_reset (struct Scsi_Host *host)
+ *
+ */
+
+static void
+hard_reset (struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned long flags;
+ save_flags (flags);
+ cli();
+ ncr_scsi_reset(host);
+ NCR53c7x0_driver_init (host);
+ if (hostdata->soft_reset)
+ hostdata->soft_reset (host);
+ restore_flags(flags);
+}
+
+
+/*
+ * Function : Scsi_Cmnd *return_outstanding_commands (struct Scsi_Host *host,
+ * int free, int issue)
+ *
+ * Purpose : return a linked list (using the SCp.buffer field as next,
+ * so we don't perturb hostdata. We don't use a field of the
+ * NCR53c7x0_cmd structure since we may not have allocated one
+ * for the command causing the reset.) of Scsi_Cmnd structures that
+ * had propagated below the Linux issue queue level. If free is set,
+ * free the NCR53c7x0_cmd structures which are associated with
+ * the Scsi_Cmnd structures, and clean up any internal
+ * NCR lists that the commands were on. If issue is set,
+ * also return commands in the issue queue.
+ *
+ * Returns : linked list of commands
+ *
+ * NOTE : the caller should insure that the NCR chip is halted
+ * if the free flag is set.
+ */
+
+static Scsi_Cmnd *
+return_outstanding_commands (struct Scsi_Host *host, int free, int issue) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ struct NCR53c7x0_cmd *c;
+ int i;
+ u32 *current;
+ Scsi_Cmnd *list = NULL, *tmp;
+ for (c = (struct NCR53c7x0_cmd *) hostdata->running_list; c;
+ c = (struct NCR53c7x0_cmd *) c->next) {
+ if (c->cmd->SCp.buffer) {
+ printk ("scsi%d : loop detected in running list!\n", host->host_no);
+ break;
+ } else {
+ printk ("The sti() implicit in a printk() prevents hangs\n");
+ break;
+ }
+
+ c->cmd->SCp.buffer = (struct scatterlist *) list;
+ list = c->cmd;
+ if (free) {
+ c->next = hostdata->free;
+ hostdata->free = c;
+ }
+ }
+
+ if (free) {
+ for (i = 0, current = (u32 *) hostdata->schedule;
+ i < host->can_queue; ++i, current += 2) {
+ current[0] = hostdata->NOP_insn;
+ current[1] = 0xdeadbeef;
+ }
+ hostdata->current = NULL;
+ }
+
+ if (issue) {
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; tmp = tmp->next) {
+ if (tmp->SCp.buffer) {
+ printk ("scsi%d : loop detected in issue queue!\n",
+ host->host_no);
+ break;
+ }
+ tmp->SCp.buffer = (struct scatterlist *) list;
+ list = tmp;
+ }
+ if (free)
+ hostdata->issue_queue = NULL;
+
+ }
+ return list;
+}
+
+/*
+ * Function : static int disable (struct Scsi_Host *host)
+ *
+ * Purpose : disables the given NCR host, causing all commands
+ * to return a driver error. Call this so we can unload the
+ * module during development and try again. Eventually,
+ * we should be able to find clean workarounds for these
+ * problems.
+ *
+ * Inputs : host - hostadapter to twiddle
+ *
+ * Returns : 0 on success.
+ */
+
+static int
+disable (struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned long flags;
+ Scsi_Cmnd *nuke_list, *tmp;
+ save_flags(flags);
+ cli();
+ if (hostdata->state != STATE_HALTED)
+ ncr_halt (host);
+ nuke_list = return_outstanding_commands (host, 1 /* free */, 1 /* issue */);
+ hard_reset (host);
+ hostdata->state = STATE_DISABLED;
+ restore_flags(flags);
+ printk ("scsi%d : nuking commands\n", host->host_no);
+ for (; nuke_list; nuke_list = tmp) {
+ tmp = (Scsi_Cmnd *) nuke_list->SCp.buffer;
+ nuke_list->result = DID_ERROR << 16;
+ nuke_list->scsi_done(nuke_list);
+ }
+ printk ("scsi%d : done. \n", host->host_no);
+ printk (KERN_ALERT "scsi%d : disabled. Unload and reload\n",
+ host->host_no);
+ return 0;
+}
+
+/*
+ * Function : static int ncr_halt (struct Scsi_Host *host)
+ *
+ * Purpose : halts the SCSI SCRIPTS(tm) processor on the NCR chip
+ *
+ * Inputs : host - SCSI chip to halt
+ *
+ * Returns : 0 on success
+ */
+
+static int
+ncr_halt (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ unsigned char istat, tmp;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int stage;
+ NCR53c7x0_local_setup(host);
+
+ save_flags(flags);
+ cli();
+ /* Stage 0 : eat all interrupts
+ Stage 1 : set ABORT
+ Stage 2 : eat all but abort interrupts
+ Stage 3 : eat all interrupts
+ */
+ for (stage = 0;;) {
+ if (stage == 1) {
+ NCR53c7x0_write8(hostdata->istat, ISTAT_ABRT);
+ ++stage;
+ }
+ istat = NCR53c7x0_read8 (hostdata->istat);
+ if (istat & ISTAT_SIP) {
+ if ((hostdata->chip / 100) == 8) {
+ tmp = NCR53c7x0_read8(SIST0_REG_800);
+ udelay(1);
+ tmp = NCR53c7x0_read8(SIST1_REG_800);
+ } else {
+ tmp = NCR53c7x0_read8(SSTAT0_REG);
+ }
+ } else if (istat & ISTAT_DIP) {
+ tmp = NCR53c7x0_read8(DSTAT_REG);
+ if (stage == 2) {
+ if (tmp & DSTAT_ABRT) {
+ NCR53c7x0_write8(hostdata->istat, 0);
+ ++stage;
+ } else {
+ printk(KERN_ALERT "scsi%d : could not halt NCR chip\n",
+ host->host_no);
+ disable (host);
+ }
+ }
+ }
+ if (!(istat & (ISTAT_SIP|ISTAT_DIP)))
+ if (stage == 0)
+ ++stage;
+ else if (stage == 3)
+ break;
+ }
+ hostdata->state = STATE_HALTED;
+ restore_flags(flags);
+#if 0
+ print_lots (host);
+#endif
+ return 0;
+}
+
+/*
+ * Function: event_name (int event)
+ *
+ * Purpose: map event enum into user-readable strings.
+ */
+
+static const char *
+event_name (int event) {
+ switch (event) {
+ case EVENT_NONE: return "none";
+ case EVENT_ISSUE_QUEUE: return "to issue queue";
+ case EVENT_START_QUEUE: return "to start queue";
+ case EVENT_SELECT: return "selected";
+ case EVENT_DISCONNECT: return "disconnected";
+ case EVENT_RESELECT: return "reselected";
+ case EVENT_COMPLETE: return "completed";
+ case EVENT_IDLE: return "idle";
+ case EVENT_SELECT_FAILED: return "select failed";
+ case EVENT_BEFORE_SELECT: return "before select";
+ case EVENT_RESELECT_FAILED: return "reselect failed";
+ default: return "unknown";
+ }
+}
+
+/*
+ * Function : void dump_events (struct Scsi_Host *host, count)
+ *
+ * Purpose : print last count events which have occurred.
+ */
+static void
+dump_events (struct Scsi_Host *host, int count) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ struct NCR53c7x0_event event;
+ int i;
+ unsigned long flags;
+ if (hostdata->events) {
+ if (count > hostdata->event_size)
+ count = hostdata->event_size;
+ for (i = hostdata->event_index; count > 0;
+ i = (i ? i - 1 : hostdata->event_size -1), --count) {
+ save_flags(flags);
+/*
+ * By copying the event we're currently examining with interrupts
+ * disabled, we can do multiple printk(), etc. operations and
+ * still be guaranteed that they're happening on the same
+ * event structure.
+ */
+ cli();
+#if 0
+ event = hostdata->events[i];
+#else
+ memcpy ((void *) &event, (void *) &(hostdata->events[i]),
+ sizeof(event));
+#endif
+
+ restore_flags(flags);
+ printk ("scsi%d : %s event %d at %ld secs %ld usecs target %d lun %d\n",
+ host->host_no, event_name (event.event), count,
+ (long) event.time.tv_sec, (long) event.time.tv_usec,
+ event.target, event.lun);
+ if (event.dsa)
+ printk (" event for dsa 0x%lx (virt 0x%p)\n",
+ virt_to_bus(event.dsa), event.dsa);
+ if (event.pid != -1) {
+ printk (" event for pid %ld ", event.pid);
+ print_command (event.cmnd);
+ }
+ }
+ }
+}
+
+/*
+ * Function: check_address
+ *
+ * Purpose: Check to see if a possibly corrupt pointer will fault the
+ * kernel.
+ *
+ * Inputs: addr - address; size - size of area
+ *
+ * Returns: 0 if area is OK, -1 on error.
+ *
+ * NOTES: should be implemented in terms of vverify on kernels
+ * that have it.
+ */
+
+static int
+check_address (unsigned long addr, int size) {
+ return (MAP_NR(addr) < 1 || MAP_NR(addr + size) > MAP_NR(high_memory) ?
+ -1 : 0);
+}
+
+#ifdef MODULE
+int
+NCR53c7x0_release(struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata;
+ struct NCR53c7x0_cmd *cmd, *tmp;
+ shutdown (host);
+ if (host->irq != IRQ_NONE)
+ {
+ int irq_count;
+ struct Scsi_Host *tmp;
+ for (irq_count = 0, tmp = first_host; tmp; tmp = tmp->next)
+ if (tmp->hostt == the_template && tmp->irq == host->irq)
+ ++irq_count;
+ if (irq_count == 1)
+ free_irq(host->irq, NULL);
+ }
+ if (host->dma_channel != DMA_NONE)
+ free_dma(host->dma_channel);
+ if (host->io_port)
+ release_region(host->io_port, host->n_io_port);
+
+ for (cmd = (struct NCR53c7x0_cmd *) hostdata->free; cmd; cmd = tmp,
+ --hostdata->num_cmds) {
+ tmp = (struct NCR53c7x0_cmd *) cmd->next;
+ /*
+ * If we're going to loop, try to stop it to get a more accurate
+ * count of the leaked commands.
+ */
+ cmd->next = NULL;
+ if (cmd->free)
+ cmd->free ((void *) cmd->real, cmd->size);
+ }
+ if (hostdata->num_cmds)
+ printk ("scsi%d : leaked %d NCR53c7x0_cmd structures\n",
+ host->host_no, hostdata->num_cmds);
+ if (hostdata->events)
+ vfree ((void *)hostdata->events);
+ return 1;
+}
+Scsi_Host_Template driver_template = NCR53c7xx;
+#include "scsi_module.c"
+#endif /* def MODULE */
diff --git a/linux/src/drivers/scsi/53c8xx_d.h b/linux/src/drivers/scsi/53c8xx_d.h
new file mode 100644
index 00000000..b5863406
--- /dev/null
+++ b/linux/src/drivers/scsi/53c8xx_d.h
@@ -0,0 +1,2677 @@
+u32 SCRIPT[] = {
+/*
+
+
+; NCR 53c810 driver, main script
+; Sponsored by
+; iX Multiuser Multitasking Magazine
+; hm@ix.de
+;
+; Copyright 1993, 1994, 1995 Drew Eckhardt
+; Visionary Computing
+; (Unix and Linux consulting and custom programming)
+; drew@PoohSticks.ORG
+; +1 (303) 786-7975
+;
+; TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+;
+; PRE-ALPHA
+;
+; For more information, please consult
+;
+; NCR 53C810
+; PCI-SCSI I/O Processor
+; Data Manual
+;
+; NCR 53C710
+; SCSI I/O Processor
+; Programmers Guide
+;
+; NCR Microelectronics
+; 1635 Aeroplaza Drive
+; Colorado Springs, CO 80916
+; 1+ (719) 578-3400
+;
+; Toll free literature number
+; +1 (800) 334-5454
+;
+; IMPORTANT : This code is self modifying due to the limitations of
+; the NCR53c7,8xx series chips. Persons debugging this code with
+; the remote debugger should take this into account, and NOT set
+; breakpoints in modified instructions.
+;
+; Design:
+; The NCR53c7,8xx family of SCSI chips are busmasters with an onboard
+; microcontroller using a simple instruction set.
+;
+; So, to minimize the effects of interrupt latency, and to maximize
+; throughput, this driver offloads the practical maximum amount
+; of processing to the SCSI chip while still maintaining a common
+; structure.
+;
+; Where tradeoffs were needed between efficiency on the older
+; chips and the newer NCR53c800 series, the NCR53c800 series
+; was chosen.
+;
+; While the NCR53c700 and NCR53c700-66 lacked the facilities to fully
+; automate SCSI transfers without host processor intervention, this
+; isn't the case with the NCR53c710 and newer chips which allow
+;
+; - reads and writes to the internal registers from within the SCSI
+; scripts, allowing the SCSI SCRIPTS(tm) code to save processor
+; state so that multiple threads of execution are possible, and also
+; provide an ALU for loop control, etc.
+;
+; - table indirect addressing for some instructions. This allows
+; pointers to be located relative to the DSA ((Data Structure
+; Address) register.
+;
+; These features make it possible to implement a mailbox style interface,
+; where the same piece of code is run to handle I/O for multiple threads
+; at once minimizing our need to relocate code. Since the NCR53c700/
+; NCR53c800 series have a unique combination of features, making a
+; a standard ingoing/outgoing mailbox system, costly, I've modified it.
+;
+; - Mailboxes are a mixture of code and data. This lets us greatly
+; simplify the NCR53c810 code and do things that would otherwise
+; not be possible.
+;
+; The saved data pointer is now implemented as follows :
+;
+; Control flow has been architected such that if control reaches
+; munge_save_data_pointer, on a restore pointers message or
+; reconnection, a jump to the address formerly in the TEMP register
+; will allow the SCSI command to resume execution.
+;
+
+;
+; Note : the DSA structures must be aligned on 32 bit boundaries,
+; since the source and destination of MOVE MEMORY instructions
+; must share the same alignment and this is the alignment of the
+; NCR registers.
+;
+
+ABSOLUTE dsa_temp_lun = 0 ; Patch to lun for current dsa
+ABSOLUTE dsa_temp_next = 0 ; Patch to dsa next for current dsa
+ABSOLUTE dsa_temp_addr_next = 0 ; Patch to address of dsa next address
+ ; for current dsa
+ABSOLUTE dsa_temp_sync = 0 ; Patch to address of per-target
+ ; sync routine
+ABSOLUTE dsa_temp_target = 0 ; Patch to id for current dsa
+ABSOLUTE dsa_temp_addr_saved_pointer = 0; Patch to address of per-command
+ ; saved data pointer
+ABSOLUTE dsa_temp_addr_residual = 0 ; Patch to address of per-command
+ ; current residual code
+ABSOLUTE dsa_temp_addr_saved_residual = 0; Patch to address of per-command
+ ; saved residual code
+ABSOLUTE dsa_temp_addr_new_value = 0 ; Address of value for JUMP operand
+ABSOLUTE dsa_temp_addr_array_value = 0 ; Address to copy to
+ABSOLUTE dsa_temp_addr_dsa_value = 0 ; Address of this DSA value
+
+;
+; Once a device has initiated reselection, we need to compare it
+; against the singly linked list of commands which have disconnected
+; and are pending reselection. These commands are maintained in
+; an unordered singly linked list of DSA structures, through the
+; DSA pointers at their 'centers' headed by the reconnect_dsa_head
+; pointer.
+;
+; To avoid complications in removing commands from the list,
+; I minimize the amount of expensive (at eight operations per
+; addition @ 500-600ns each) pointer operations which must
+; be done in the NCR driver by precomputing them on the
+; host processor during dsa structure generation.
+;
+; The fixed-up per DSA code knows how to recognize the nexus
+; associated with the corresponding SCSI command, and modifies
+; the source and destination pointers for the MOVE MEMORY
+; instruction which is executed when reselected_ok is called
+; to remove the command from the list. Similarly, DSA is
+; loaded with the address of the next DSA structure and
+; reselected_check_next is called if a failure occurs.
+;
+; Perhaps more concisely, the net effect of the mess is
+;
+; for (dsa = reconnect_dsa_head, dest = &reconnect_dsa_head,
+; src = NULL; dsa; dest = &dsa->next, dsa = dsa->next) {
+; src = &dsa->next;
+; if (target_id == dsa->id && target_lun == dsa->lun) {
+; *dest = *src;
+; break;
+; }
+; }
+;
+; if (!dsa)
+; error (int_err_unexpected_reselect);
+; else
+; longjmp (dsa->jump_resume, 0);
+;
+;
+
+
+; Define DSA structure used for mailboxes
+ENTRY dsa_code_template
+dsa_code_template:
+ENTRY dsa_code_begin
+dsa_code_begin:
+ MOVE dmode_memory_to_ncr TO DMODE
+
+at 0x00000000 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, dsa_temp_addr_dsa_value, addr_scratch
+
+at 0x00000002 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000005 : */ 0x78380000,0x00000000,
+/*
+ CALL scratch_to_dsa
+
+at 0x00000007 : */ 0x88080000,0x00000980,
+/*
+ CALL select
+
+at 0x00000009 : */ 0x88080000,0x000001fc,
+/*
+; Handle the phase mismatch which may have resulted from the
+; MOVE FROM dsa_msgout if we returned here. The CLEAR ATN
+; may or may not be necessary, and we should update script_asm.pl
+; to handle multiple pieces.
+ CLEAR ATN
+
+at 0x0000000b : */ 0x60000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000000d : */ 0x60000040,0x00000000,
+/*
+
+; Replace second operand with address of JUMP instruction dest operand
+; in schedule table for this DSA. Becomes dsa_jump_dest in 53c7,8xx.c.
+ENTRY dsa_code_fix_jump
+dsa_code_fix_jump:
+ MOVE MEMORY 4, NOP_insn, 0
+
+at 0x0000000f : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ JUMP select_done
+
+at 0x00000012 : */ 0x80080000,0x00000224,
+/*
+
+; wrong_dsa loads the DSA register with the value of the dsa_next
+; field.
+;
+wrong_dsa:
+; Patch the MOVE MEMORY INSTRUCTION such that
+; the destination address is the address of the OLD
+; next pointer.
+;
+ MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok + 8
+
+at 0x00000014 : */ 0xc0000004,0x00000000,0x00000758,
+/*
+ MOVE dmode_memory_to_ncr TO DMODE
+
+at 0x00000017 : */ 0x78380000,0x00000000,
+/*
+;
+; Move the _contents_ of the next pointer into the DSA register as
+; the next I_T_L or I_T_L_Q tupple to check against the established
+; nexus.
+;
+ MOVE MEMORY 4, dsa_temp_next, addr_scratch
+
+at 0x00000019 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x0000001c : */ 0x78380000,0x00000000,
+/*
+ CALL scratch_to_dsa
+
+at 0x0000001e : */ 0x88080000,0x00000980,
+/*
+ JUMP reselected_check_next
+
+at 0x00000020 : */ 0x80080000,0x000006a4,
+/*
+
+ABSOLUTE dsa_save_data_pointer = 0
+ENTRY dsa_code_save_data_pointer
+dsa_code_save_data_pointer:
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x00000022 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_temp, dsa_temp_addr_saved_pointer
+
+at 0x00000024 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000027 : */ 0x78380000,0x00000000,
+/*
+; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
+ MOVE MEMORY 24, dsa_temp_addr_residual, dsa_temp_addr_saved_residual
+
+at 0x00000029 : */ 0xc0000018,0x00000000,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000002c : */ 0x60000040,0x00000000,
+/*
+
+
+
+ RETURN
+
+at 0x0000002e : */ 0x90080000,0x00000000,
+/*
+ABSOLUTE dsa_restore_pointers = 0
+ENTRY dsa_code_restore_pointers
+dsa_code_restore_pointers:
+ MOVE dmode_memory_to_ncr TO DMODE
+
+at 0x00000030 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, dsa_temp_addr_saved_pointer, addr_temp
+
+at 0x00000032 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000035 : */ 0x78380000,0x00000000,
+/*
+; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
+ MOVE MEMORY 24, dsa_temp_addr_saved_residual, dsa_temp_addr_residual
+
+at 0x00000037 : */ 0xc0000018,0x00000000,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000003a : */ 0x60000040,0x00000000,
+/*
+
+
+
+ RETURN
+
+at 0x0000003c : */ 0x90080000,0x00000000,
+/*
+
+ABSOLUTE dsa_check_reselect = 0
+; dsa_check_reselect determines whether or not the current target and
+; lun match the current DSA
+ENTRY dsa_code_check_reselect
+dsa_code_check_reselect:
+ MOVE SSID TO SFBR ; SSID contains 3 bit target ID
+
+at 0x0000003e : */ 0x720a0000,0x00000000,
+/*
+; FIXME : we need to accommodate bit fielded and binary here for '7xx/'8xx chips
+ JUMP REL (wrong_dsa), IF NOT dsa_temp_target, AND MASK 0xf8
+
+at 0x00000040 : */ 0x8084f800,0x00ffff48,
+/*
+;
+; Hack - move to scratch first, since SFBR is not writeable
+; via the CPU and hence a MOVE MEMORY instruction.
+;
+ MOVE dmode_memory_to_ncr TO DMODE
+
+at 0x00000042 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 1, reselected_identify, addr_scratch
+
+at 0x00000044 : */ 0xc0000001,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000047 : */ 0x78380000,0x00000000,
+/*
+ MOVE SCRATCH0 TO SFBR
+
+at 0x00000049 : */ 0x72340000,0x00000000,
+/*
+; FIXME : we need to accommodate bit fielded and binary here for '7xx/'8xx chips
+ JUMP REL (wrong_dsa), IF NOT dsa_temp_lun, AND MASK 0xf8
+
+at 0x0000004b : */ 0x8084f800,0x00ffff1c,
+/*
+; Patch the MOVE MEMORY INSTRUCTION such that
+; the source address is the address of this dsa's
+; next pointer.
+ MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok + 4
+
+at 0x0000004d : */ 0xc0000004,0x00000000,0x00000754,
+/*
+ CALL reselected_ok
+
+at 0x00000050 : */ 0x88080000,0x00000750,
+/*
+ CALL dsa_temp_sync
+
+at 0x00000052 : */ 0x88080000,0x00000000,
+/*
+; Release ACK on the IDENTIFY message _after_ we've set the synchronous
+; transfer parameters!
+ CLEAR ACK
+
+at 0x00000054 : */ 0x60000040,0x00000000,
+/*
+; Implicitly restore pointers on reselection, so a RETURN
+; will transfer control back to the right spot.
+ CALL REL (dsa_code_restore_pointers)
+
+at 0x00000056 : */ 0x88880000,0x00ffff60,
+/*
+ RETURN
+
+at 0x00000058 : */ 0x90080000,0x00000000,
+/*
+ENTRY dsa_zero
+dsa_zero:
+ENTRY dsa_code_template_end
+dsa_code_template_end:
+
+; Perform sanity check for dsa_fields_start == dsa_code_template_end -
+; dsa_zero, puke.
+
+ABSOLUTE dsa_fields_start = 0 ; Sanity marker
+ ; pad 48 bytes (fix this RSN)
+ABSOLUTE dsa_next = 48 ; len 4 Next DSA
+ ; del 4 Previous DSA address
+ABSOLUTE dsa_cmnd = 56 ; len 4 Scsi_Cmnd * for this thread.
+ABSOLUTE dsa_select = 60 ; len 4 Device ID, Period, Offset for
+ ; table indirect select
+ABSOLUTE dsa_msgout = 64 ; len 8 table indirect move parameter for
+ ; select message
+ABSOLUTE dsa_cmdout = 72 ; len 8 table indirect move parameter for
+ ; command
+ABSOLUTE dsa_dataout = 80 ; len 4 code pointer for dataout
+ABSOLUTE dsa_datain = 84 ; len 4 code pointer for datain
+ABSOLUTE dsa_msgin = 88 ; len 8 table indirect move for msgin
+ABSOLUTE dsa_status = 96 ; len 8 table indirect move for status byte
+ABSOLUTE dsa_msgout_other = 104 ; len 8 table indirect for normal message out
+ ; (Synchronous transfer negotiation, etc).
+ABSOLUTE dsa_end = 112
+
+ABSOLUTE schedule = 0 ; Array of JUMP dsa_begin or JUMP (next),
+ ; terminated by a call to JUMP wait_reselect
+
+; Linked lists of DSA structures
+ABSOLUTE reconnect_dsa_head = 0 ; Link list of DSAs which can reconnect
+ABSOLUTE addr_reconnect_dsa_head = 0 ; Address of variable containing
+ ; address of reconnect_dsa_head
+
+; These select the source and destination of a MOVE MEMORY instruction
+ABSOLUTE dmode_memory_to_memory = 0x0
+ABSOLUTE dmode_memory_to_ncr = 0x0
+ABSOLUTE dmode_ncr_to_memory = 0x0
+
+ABSOLUTE addr_scratch = 0x0
+ABSOLUTE addr_temp = 0x0
+
+
+; Interrupts -
+; MSB indicates type
+; 0 handle error condition
+; 1 handle message
+; 2 handle normal condition
+; 3 debugging interrupt
+; 4 testing interrupt
+; Next byte indicates specific error
+
+; XXX not yet implemented, I'm not sure if I want to -
+; Next byte indicates the routine the error occurred in
+; The LSB indicates the specific place the error occurred
+
+ABSOLUTE int_err_unexpected_phase = 0x00000000 ; Unexpected phase encountered
+ABSOLUTE int_err_selected = 0x00010000 ; SELECTED (nee RESELECTED)
+ABSOLUTE int_err_unexpected_reselect = 0x00020000
+ABSOLUTE int_err_check_condition = 0x00030000
+ABSOLUTE int_err_no_phase = 0x00040000
+ABSOLUTE int_msg_wdtr = 0x01000000 ; WDTR message received
+ABSOLUTE int_msg_sdtr = 0x01010000 ; SDTR received
+ABSOLUTE int_msg_1 = 0x01020000 ; single byte special message
+ ; received
+
+ABSOLUTE int_norm_select_complete = 0x02000000 ; Select complete, reprogram
+ ; registers.
+ABSOLUTE int_norm_reselect_complete = 0x02010000 ; Nexus established
+ABSOLUTE int_norm_command_complete = 0x02020000 ; Command complete
+ABSOLUTE int_norm_disconnected = 0x02030000 ; Disconnected
+ABSOLUTE int_norm_aborted =0x02040000 ; Aborted *dsa
+ABSOLUTE int_norm_reset = 0x02050000 ; Generated BUS reset.
+ABSOLUTE int_debug_break = 0x03000000 ; Break point
+
+ABSOLUTE int_debug_panic = 0x030b0000 ; Panic driver
+
+
+ABSOLUTE int_test_1 = 0x04000000 ; Test 1 complete
+ABSOLUTE int_test_2 = 0x04010000 ; Test 2 complete
+ABSOLUTE int_test_3 = 0x04020000 ; Test 3 complete
+
+
+; These should start with 0x05000000, with low bits incrementing for
+; each one.
+
+
+
+ABSOLUTE NCR53c7xx_msg_abort = 0 ; Pointer to abort message
+ABSOLUTE NCR53c7xx_msg_reject = 0 ; Pointer to reject message
+ABSOLUTE NCR53c7xx_zero = 0 ; long with zero in it, use for source
+ABSOLUTE NCR53c7xx_sink = 0 ; long to dump worthless data in
+ABSOLUTE NOP_insn = 0 ; NOP instruction
+
+; Pointer to message, potentially multi-byte
+ABSOLUTE msg_buf = 0
+
+; Pointer to holding area for reselection information
+ABSOLUTE reselected_identify = 0
+ABSOLUTE reselected_tag = 0
+
+; Request sense command pointer, it's a 6 byte command, should
+; be constant for all commands since we always want 16 bytes of
+; sense and we don't need to change any fields as we did under
+; SCSI-I when we actually cared about the LUN field.
+;EXTERNAL NCR53c7xx_sense ; Request sense command
+
+
+; dsa_schedule
+; PURPOSE : after a DISCONNECT message has been received, and pointers
+; saved, insert the current DSA structure at the head of the
+; disconnected queue and fall through to the scheduler.
+;
+; CALLS : OK
+;
+; INPUTS : dsa - current DSA structure, reconnect_dsa_head - list
+; of disconnected commands
+;
+; MODIFIES : SCRATCH, reconnect_dsa_head
+;
+; EXITS : always passes control to schedule
+
+ENTRY dsa_schedule
+dsa_schedule:
+
+
+
+
+;
+; Calculate the address of the next pointer within the DSA
+; structure of the command that is currently disconnecting
+;
+ CALL dsa_to_scratch
+
+at 0x0000005a : */ 0x88080000,0x00000938,
+/*
+ MOVE SCRATCH0 + dsa_next TO SCRATCH0
+
+at 0x0000005c : */ 0x7e343000,0x00000000,
+/*
+ MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+
+at 0x0000005e : */ 0x7f350000,0x00000000,
+/*
+ MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+
+at 0x00000060 : */ 0x7f360000,0x00000000,
+/*
+ MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+at 0x00000062 : */ 0x7f370000,0x00000000,
+/*
+
+; Point the next field of this DSA structure at the current disconnected
+; list
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x00000064 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, dsa_schedule_insert + 8
+
+at 0x00000066 : */ 0xc0000004,0x00000000,0x000001b4,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000069 : */ 0x78380000,0x00000000,
+/*
+dsa_schedule_insert:
+ MOVE MEMORY 4, reconnect_dsa_head, 0
+
+at 0x0000006b : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+; And update the head pointer.
+ CALL dsa_to_scratch
+
+at 0x0000006e : */ 0x88080000,0x00000938,
+/*
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x00000070 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, reconnect_dsa_head
+
+at 0x00000072 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000075 : */ 0x78380000,0x00000000,
+/*
+
+
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+
+at 0x00000077 : */ 0x7c027f00,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x00000079 : */ 0x60000040,0x00000000,
+/*
+
+ WAIT DISCONNECT
+
+at 0x0000007b : */ 0x48000000,0x00000000,
+/*
+
+
+
+
+
+
+ JUMP schedule
+
+at 0x0000007d : */ 0x80080000,0x00000000,
+/*
+
+
+;
+; select
+;
+; PURPOSE : establish a nexus for the SCSI command referenced by DSA.
+; On success, the current DSA structure is removed from the issue
+; queue. Usually, this is entered as a fall-through from schedule,
+; although the contingent allegiance handling code will write
+; the select entry address to the DSP to restart a command as a
+; REQUEST SENSE. A message is sent (usually IDENTIFY, although
+; additional SDTR or WDTR messages may be sent). COMMAND OUT
+; is handled.
+;
+; INPUTS : DSA - SCSI command, issue_dsa_head
+;
+; CALLS : NOT OK
+;
+; MODIFIES : SCRATCH, issue_dsa_head
+;
+; EXITS : on reselection or selection, go to select_failed
+; otherwise, RETURN so control is passed back to
+; dsa_begin.
+;
+
+ENTRY select
+select:
+
+
+
+
+
+
+
+
+
+
+
+
+ CLEAR TARGET
+
+at 0x0000007f : */ 0x60000200,0x00000000,
+/*
+
+; XXX
+;
+; In effect, SELECTION operations are backgrounded, with execution
+; continuing until code which waits for REQ or a fatal interrupt is
+; encountered.
+;
+; So, for more performance, we could overlap the code which removes
+; the command from the NCRs issue queue with the selection, but
+; at this point I don't want to deal with the error recovery.
+;
+
+
+ SELECT ATN FROM dsa_select, select_failed
+
+at 0x00000081 : */ 0x4300003c,0x000007a4,
+/*
+ JUMP select_msgout, WHEN MSG_OUT
+
+at 0x00000083 : */ 0x860b0000,0x00000214,
+/*
+ENTRY select_msgout
+select_msgout:
+ MOVE FROM dsa_msgout, WHEN MSG_OUT
+
+at 0x00000085 : */ 0x1e000000,0x00000040,
+/*
+
+
+
+
+
+
+
+
+
+
+ RETURN
+
+at 0x00000087 : */ 0x90080000,0x00000000,
+/*
+
+;
+; select_done
+;
+; PURPOSE: continue on to normal data transfer; called as the exit
+; point from dsa_begin.
+;
+; INPUTS: dsa
+;
+; CALLS: OK
+;
+;
+
+select_done:
+
+
+
+
+
+
+
+; After a successful selection, we should get either a CMD phase or
+; some transfer request negotiation message.
+
+ JUMP cmdout, WHEN CMD
+
+at 0x00000089 : */ 0x820b0000,0x00000244,
+/*
+ INT int_err_unexpected_phase, WHEN NOT MSG_IN
+
+at 0x0000008b : */ 0x9f030000,0x00000000,
+/*
+
+select_msg_in:
+ CALL msg_in, WHEN MSG_IN
+
+at 0x0000008d : */ 0x8f0b0000,0x00000404,
+/*
+ JUMP select_msg_in, WHEN MSG_IN
+
+at 0x0000008f : */ 0x870b0000,0x00000234,
+/*
+
+cmdout:
+ INT int_err_unexpected_phase, WHEN NOT CMD
+
+at 0x00000091 : */ 0x9a030000,0x00000000,
+/*
+
+
+
+ENTRY cmdout_cmdout
+cmdout_cmdout:
+
+ MOVE FROM dsa_cmdout, WHEN CMD
+
+at 0x00000093 : */ 0x1a000000,0x00000048,
+/*
+
+
+
+
+;
+; data_transfer
+; other_out
+; other_in
+; other_transfer
+;
+; PURPOSE : handle the main data transfer for a SCSI command in
+; several parts. In the first part, data_transfer, DATA_IN
+; and DATA_OUT phases are allowed, with the user provided
+; code (usually dynamically generated based on the scatter/gather
+; list associated with a SCSI command) called to handle these
+; phases.
+;
+; After control has passed to one of the user provided
+; DATA_IN or DATA_OUT routines, back calls are made to
+; other_transfer_in or other_transfer_out to handle non-DATA IN
+; and DATA OUT phases respectively, with the state of the active
+; data pointer being preserved in TEMP.
+;
+; On completion, the user code passes control to other_transfer
+; which causes DATA_IN and DATA_OUT to result in unexpected_phase
+; interrupts so that data overruns may be trapped.
+;
+; INPUTS : DSA - SCSI command
+;
+; CALLS : OK in data_transfer_start, not ok in other_out and other_in, ok in
+; other_transfer
+;
+; MODIFIES : SCRATCH
+;
+; EXITS : if STATUS IN is detected, signifying command completion,
+; the NCR jumps to command_complete. If MSG IN occurs, a
+; CALL is made to msg_in. Otherwise, other_transfer runs in
+; an infinite loop.
+;
+
+ENTRY data_transfer
+data_transfer:
+ JUMP cmdout_cmdout, WHEN CMD
+
+at 0x00000095 : */ 0x820b0000,0x0000024c,
+/*
+ CALL msg_in, WHEN MSG_IN
+
+at 0x00000097 : */ 0x8f0b0000,0x00000404,
+/*
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x00000099 : */ 0x9e0b0000,0x00000000,
+/*
+ JUMP do_dataout, WHEN DATA_OUT
+
+at 0x0000009b : */ 0x800b0000,0x0000028c,
+/*
+ JUMP do_datain, WHEN DATA_IN
+
+at 0x0000009d : */ 0x810b0000,0x000002e4,
+/*
+ JUMP command_complete, WHEN STATUS
+
+at 0x0000009f : */ 0x830b0000,0x0000060c,
+/*
+ JUMP data_transfer
+
+at 0x000000a1 : */ 0x80080000,0x00000254,
+/*
+ENTRY end_data_transfer
+end_data_transfer:
+
+;
+; FIXME: On NCR53c700 and NCR53c700-66 chips, do_dataout/do_datain
+; should be fixed up whenever the nexus changes so it can point to the
+; correct routine for that command.
+;
+
+
+; Nasty jump to dsa->dataout
+do_dataout:
+ CALL dsa_to_scratch
+
+at 0x000000a3 : */ 0x88080000,0x00000938,
+/*
+ MOVE SCRATCH0 + dsa_dataout TO SCRATCH0
+
+at 0x000000a5 : */ 0x7e345000,0x00000000,
+/*
+ MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+
+at 0x000000a7 : */ 0x7f350000,0x00000000,
+/*
+ MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+
+at 0x000000a9 : */ 0x7f360000,0x00000000,
+/*
+ MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+at 0x000000ab : */ 0x7f370000,0x00000000,
+/*
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x000000ad : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, dataout_to_jump + 4
+
+at 0x000000af : */ 0xc0000004,0x00000000,0x000002d4,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x000000b2 : */ 0x78380000,0x00000000,
+/*
+dataout_to_jump:
+ MOVE MEMORY 4, 0, dataout_jump + 4
+
+at 0x000000b4 : */ 0xc0000004,0x00000000,0x000002e0,
+/*
+dataout_jump:
+ JUMP 0
+
+at 0x000000b7 : */ 0x80080000,0x00000000,
+/*
+
+; Nasty jump to dsa->dsain
+do_datain:
+ CALL dsa_to_scratch
+
+at 0x000000b9 : */ 0x88080000,0x00000938,
+/*
+ MOVE SCRATCH0 + dsa_datain TO SCRATCH0
+
+at 0x000000bb : */ 0x7e345400,0x00000000,
+/*
+ MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+
+at 0x000000bd : */ 0x7f350000,0x00000000,
+/*
+ MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+
+at 0x000000bf : */ 0x7f360000,0x00000000,
+/*
+ MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+at 0x000000c1 : */ 0x7f370000,0x00000000,
+/*
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x000000c3 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, datain_to_jump + 4
+
+at 0x000000c5 : */ 0xc0000004,0x00000000,0x0000032c,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x000000c8 : */ 0x78380000,0x00000000,
+/*
+ENTRY datain_to_jump
+datain_to_jump:
+ MOVE MEMORY 4, 0, datain_jump + 4
+
+at 0x000000ca : */ 0xc0000004,0x00000000,0x00000338,
+/*
+
+
+
+datain_jump:
+ JUMP 0
+
+at 0x000000cd : */ 0x80080000,0x00000000,
+/*
+
+
+
+; Note that other_out and other_in loop until a non-data phase
+; is discovered, so we only execute return statements when we
+; can go on to the next data phase block move statement.
+
+ENTRY other_out
+other_out:
+
+
+
+ INT int_err_unexpected_phase, WHEN CMD
+
+at 0x000000cf : */ 0x9a0b0000,0x00000000,
+/*
+ JUMP msg_in_restart, WHEN MSG_IN
+
+at 0x000000d1 : */ 0x870b0000,0x000003e4,
+/*
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x000000d3 : */ 0x9e0b0000,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN DATA_IN
+
+at 0x000000d5 : */ 0x990b0000,0x00000000,
+/*
+ JUMP command_complete, WHEN STATUS
+
+at 0x000000d7 : */ 0x830b0000,0x0000060c,
+/*
+ JUMP other_out, WHEN NOT DATA_OUT
+
+at 0x000000d9 : */ 0x80030000,0x0000033c,
+/*
+ RETURN
+
+at 0x000000db : */ 0x90080000,0x00000000,
+/*
+
+ENTRY other_in
+other_in:
+
+
+
+ INT int_err_unexpected_phase, WHEN CMD
+
+at 0x000000dd : */ 0x9a0b0000,0x00000000,
+/*
+ JUMP msg_in_restart, WHEN MSG_IN
+
+at 0x000000df : */ 0x870b0000,0x000003e4,
+/*
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x000000e1 : */ 0x9e0b0000,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN DATA_OUT
+
+at 0x000000e3 : */ 0x980b0000,0x00000000,
+/*
+ JUMP command_complete, WHEN STATUS
+
+at 0x000000e5 : */ 0x830b0000,0x0000060c,
+/*
+ JUMP other_in, WHEN NOT DATA_IN
+
+at 0x000000e7 : */ 0x81030000,0x00000374,
+/*
+ RETURN
+
+at 0x000000e9 : */ 0x90080000,0x00000000,
+/*
+
+
+ENTRY other_transfer
+other_transfer:
+ INT int_err_unexpected_phase, WHEN CMD
+
+at 0x000000eb : */ 0x9a0b0000,0x00000000,
+/*
+ CALL msg_in, WHEN MSG_IN
+
+at 0x000000ed : */ 0x8f0b0000,0x00000404,
+/*
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x000000ef : */ 0x9e0b0000,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN DATA_OUT
+
+at 0x000000f1 : */ 0x980b0000,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN DATA_IN
+
+at 0x000000f3 : */ 0x990b0000,0x00000000,
+/*
+ JUMP command_complete, WHEN STATUS
+
+at 0x000000f5 : */ 0x830b0000,0x0000060c,
+/*
+ JUMP other_transfer
+
+at 0x000000f7 : */ 0x80080000,0x000003ac,
+/*
+
+;
+; msg_in_restart
+; msg_in
+; munge_msg
+;
+; PURPOSE : process messages from a target. msg_in is called when the
+; caller hasn't read the first byte of the message. munge_message
+; is called when the caller has read the first byte of the message,
+; and left it in SFBR. msg_in_restart is called when the caller
+; hasn't read the first byte of the message, and wishes RETURN
+; to transfer control back to the address of the conditional
+; CALL instruction rather than to the instruction after it.
+;
+; Various int_* interrupts are generated when the host system
+; needs to intervene, as is the case with SDTR, WDTR, and
+; INITIATE RECOVERY messages.
+;
+; When the host system handles one of these interrupts,
+; it can respond by reentering at reject_message,
+; which rejects the message and returns control to
+; the caller of msg_in or munge_msg, accept_message
+; which clears ACK and returns control, or reply_message
+; which sends the message pointed to by the DSA
+; msgout_other table indirect field.
+;
+; DISCONNECT messages are handled by moving the command
+; to the reconnect_dsa_queue.
+;
+; INPUTS : DSA - SCSI COMMAND, SFBR - first byte of message (munge_msg
+; only)
+;
+; CALLS : NO. The TEMP register isn't backed up to allow nested calls.
+;
+; MODIFIES : SCRATCH, DSA on DISCONNECT
+;
+; EXITS : On receipt of SAVE DATA POINTER, RESTORE POINTERS,
+; and normal return from message handlers running under
+; Linux, control is returned to the caller. Receipt
+; of DISCONNECT messages pass control to dsa_schedule.
+;
+ENTRY msg_in_restart
+msg_in_restart:
+; XXX - hackish
+;
+; Since it's easier to debug changes to the statically
+; compiled code, rather than the dynamically generated
+; stuff, such as
+;
+; MOVE x, y, WHEN data_phase
+; CALL other_z, WHEN NOT data_phase
+; MOVE x, y, WHEN data_phase
+;
+; I'd like to have certain routines (notably the message handler)
+; restart on the conditional call rather than the next instruction.
+;
+; So, subtract 8 from the return address
+
+ MOVE TEMP0 + 0xf8 TO TEMP0
+
+at 0x000000f9 : */ 0x7e1cf800,0x00000000,
+/*
+ MOVE TEMP1 + 0xff TO TEMP1 WITH CARRY
+
+at 0x000000fb : */ 0x7f1dff00,0x00000000,
+/*
+ MOVE TEMP2 + 0xff TO TEMP2 WITH CARRY
+
+at 0x000000fd : */ 0x7f1eff00,0x00000000,
+/*
+ MOVE TEMP3 + 0xff TO TEMP3 WITH CARRY
+
+at 0x000000ff : */ 0x7f1fff00,0x00000000,
+/*
+
+ENTRY msg_in
+msg_in:
+ MOVE 1, msg_buf, WHEN MSG_IN
+
+at 0x00000101 : */ 0x0f000001,0x00000000,
+/*
+
+munge_msg:
+ JUMP munge_extended, IF 0x01 ; EXTENDED MESSAGE
+
+at 0x00000103 : */ 0x800c0001,0x00000524,
+/*
+ JUMP munge_2, IF 0x20, AND MASK 0xdf ; two byte message
+
+at 0x00000105 : */ 0x800cdf20,0x0000044c,
+/*
+;
+; XXX - I've seen a handful of broken SCSI devices which fail to issue
+; a SAVE POINTERS message before disconnecting in the middle of
+; a transfer, assuming that the DATA POINTER will be implicitly
+; restored.
+;
+; Historically, I've often done an implicit save when the DISCONNECT
+; message is processed. We may want to consider having the option of
+; doing that here.
+;
+ JUMP munge_save_data_pointer, IF 0x02 ; SAVE DATA POINTER
+
+at 0x00000107 : */ 0x800c0002,0x00000454,
+/*
+ JUMP munge_restore_pointers, IF 0x03 ; RESTORE POINTERS
+
+at 0x00000109 : */ 0x800c0003,0x000004b8,
+/*
+ JUMP munge_disconnect, IF 0x04 ; DISCONNECT
+
+at 0x0000010b : */ 0x800c0004,0x0000051c,
+/*
+ INT int_msg_1, IF 0x07 ; MESSAGE REJECT
+
+at 0x0000010d : */ 0x980c0007,0x01020000,
+/*
+ INT int_msg_1, IF 0x0f ; INITIATE RECOVERY
+
+at 0x0000010f : */ 0x980c000f,0x01020000,
+/*
+
+
+
+ JUMP reject_message
+
+at 0x00000111 : */ 0x80080000,0x000005b4,
+/*
+
+munge_2:
+ JUMP reject_message
+
+at 0x00000113 : */ 0x80080000,0x000005b4,
+/*
+;
+; The SCSI standard allows targets to recover from transient
+; error conditions by backing up the data pointer with a
+; RESTORE POINTERS message.
+;
+; So, we must save and restore the _residual_ code as well as
+; the current instruction pointer. Because of this messiness,
+; it is simpler to put dynamic code in the dsa for this and to
+; just do a simple jump down there.
+;
+
+munge_save_data_pointer:
+ MOVE DSA0 + dsa_save_data_pointer TO SFBR
+
+at 0x00000115 : */ 0x76100000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH0
+
+at 0x00000117 : */ 0x6a340000,0x00000000,
+/*
+ MOVE DSA1 + 0xff TO SFBR WITH CARRY
+
+at 0x00000119 : */ 0x7711ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH1
+
+at 0x0000011b : */ 0x6a350000,0x00000000,
+/*
+ MOVE DSA2 + 0xff TO SFBR WITH CARRY
+
+at 0x0000011d : */ 0x7712ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH2
+
+at 0x0000011f : */ 0x6a360000,0x00000000,
+/*
+ MOVE DSA3 + 0xff TO SFBR WITH CARRY
+
+at 0x00000121 : */ 0x7713ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH3
+
+at 0x00000123 : */ 0x6a370000,0x00000000,
+/*
+
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x00000125 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, jump_dsa_save + 4
+
+at 0x00000127 : */ 0xc0000004,0x00000000,0x000004b4,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x0000012a : */ 0x78380000,0x00000000,
+/*
+jump_dsa_save:
+ JUMP 0
+
+at 0x0000012c : */ 0x80080000,0x00000000,
+/*
+
+munge_restore_pointers:
+ MOVE DSA0 + dsa_restore_pointers TO SFBR
+
+at 0x0000012e : */ 0x76100000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH0
+
+at 0x00000130 : */ 0x6a340000,0x00000000,
+/*
+ MOVE DSA1 + 0xff TO SFBR WITH CARRY
+
+at 0x00000132 : */ 0x7711ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH1
+
+at 0x00000134 : */ 0x6a350000,0x00000000,
+/*
+ MOVE DSA2 + 0xff TO SFBR WITH CARRY
+
+at 0x00000136 : */ 0x7712ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH2
+
+at 0x00000138 : */ 0x6a360000,0x00000000,
+/*
+ MOVE DSA3 + 0xff TO SFBR WITH CARRY
+
+at 0x0000013a : */ 0x7713ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH3
+
+at 0x0000013c : */ 0x6a370000,0x00000000,
+/*
+
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x0000013e : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, jump_dsa_restore + 4
+
+at 0x00000140 : */ 0xc0000004,0x00000000,0x00000518,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000143 : */ 0x78380000,0x00000000,
+/*
+jump_dsa_restore:
+ JUMP 0
+
+at 0x00000145 : */ 0x80080000,0x00000000,
+/*
+
+
+munge_disconnect:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ JUMP dsa_schedule
+
+at 0x00000147 : */ 0x80080000,0x00000168,
+/*
+
+
+
+
+
+munge_extended:
+ CLEAR ACK
+
+at 0x00000149 : */ 0x60000040,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN NOT MSG_IN
+
+at 0x0000014b : */ 0x9f030000,0x00000000,
+/*
+ MOVE 1, msg_buf + 1, WHEN MSG_IN
+
+at 0x0000014d : */ 0x0f000001,0x00000001,
+/*
+ JUMP munge_extended_2, IF 0x02
+
+at 0x0000014f : */ 0x800c0002,0x00000554,
+/*
+ JUMP munge_extended_3, IF 0x03
+
+at 0x00000151 : */ 0x800c0003,0x00000584,
+/*
+ JUMP reject_message
+
+at 0x00000153 : */ 0x80080000,0x000005b4,
+/*
+
+munge_extended_2:
+ CLEAR ACK
+
+at 0x00000155 : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, msg_buf + 2, WHEN MSG_IN
+
+at 0x00000157 : */ 0x0f000001,0x00000002,
+/*
+ JUMP reject_message, IF NOT 0x02 ; Must be WDTR
+
+at 0x00000159 : */ 0x80040002,0x000005b4,
+/*
+ CLEAR ACK
+
+at 0x0000015b : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, msg_buf + 3, WHEN MSG_IN
+
+at 0x0000015d : */ 0x0f000001,0x00000003,
+/*
+ INT int_msg_wdtr
+
+at 0x0000015f : */ 0x98080000,0x01000000,
+/*
+
+munge_extended_3:
+ CLEAR ACK
+
+at 0x00000161 : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, msg_buf + 2, WHEN MSG_IN
+
+at 0x00000163 : */ 0x0f000001,0x00000002,
+/*
+ JUMP reject_message, IF NOT 0x01 ; Must be SDTR
+
+at 0x00000165 : */ 0x80040001,0x000005b4,
+/*
+ CLEAR ACK
+
+at 0x00000167 : */ 0x60000040,0x00000000,
+/*
+ MOVE 2, msg_buf + 3, WHEN MSG_IN
+
+at 0x00000169 : */ 0x0f000002,0x00000003,
+/*
+ INT int_msg_sdtr
+
+at 0x0000016b : */ 0x98080000,0x01010000,
+/*
+
+ENTRY reject_message
+reject_message:
+ SET ATN
+
+at 0x0000016d : */ 0x58000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000016f : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, NCR53c7xx_msg_reject, WHEN MSG_OUT
+
+at 0x00000171 : */ 0x0e000001,0x00000000,
+/*
+ RETURN
+
+at 0x00000173 : */ 0x90080000,0x00000000,
+/*
+
+ENTRY accept_message
+accept_message:
+ CLEAR ATN
+
+at 0x00000175 : */ 0x60000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x00000177 : */ 0x60000040,0x00000000,
+/*
+ RETURN
+
+at 0x00000179 : */ 0x90080000,0x00000000,
+/*
+
+ENTRY respond_message
+respond_message:
+ SET ATN
+
+at 0x0000017b : */ 0x58000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000017d : */ 0x60000040,0x00000000,
+/*
+ MOVE FROM dsa_msgout_other, WHEN MSG_OUT
+
+at 0x0000017f : */ 0x1e000000,0x00000068,
+/*
+ RETURN
+
+at 0x00000181 : */ 0x90080000,0x00000000,
+/*
+
+;
+; command_complete
+;
+; PURPOSE : handle command termination when STATUS IN is detected by reading
+; a status byte followed by a command termination message.
+;
+; Normal termination results in an INTFLY instruction, and
+; the host system can pick out which command terminated by
+; examining the MESSAGE and STATUS buffers of all currently
+; executing commands;
+;
+; Abnormal (CHECK_CONDITION) termination results in an
+; int_err_check_condition interrupt so that a REQUEST SENSE
+; command can be issued out-of-order so that no other command
+; clears the contingent allegiance condition.
+;
+;
+; INPUTS : DSA - command
+;
+; CALLS : OK
+;
+; EXITS : On successful termination, control is passed to schedule.
+; On abnormal termination, the user will usually modify the
+; DSA fields and corresponding buffers and return control
+; to select.
+;
+
+ENTRY command_complete
+command_complete:
+ MOVE FROM dsa_status, WHEN STATUS
+
+at 0x00000183 : */ 0x1b000000,0x00000060,
+/*
+
+ MOVE SFBR TO SCRATCH0 ; Save status
+
+at 0x00000185 : */ 0x6a340000,0x00000000,
+/*
+
+ENTRY command_complete_msgin
+command_complete_msgin:
+ MOVE FROM dsa_msgin, WHEN MSG_IN
+
+at 0x00000187 : */ 0x1f000000,0x00000058,
+/*
+; Indicate that we should be expecting a disconnect
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+
+at 0x00000189 : */ 0x7c027f00,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000018b : */ 0x60000040,0x00000000,
+/*
+
+ WAIT DISCONNECT
+
+at 0x0000018d : */ 0x48000000,0x00000000,
+/*
+
+;
+; The SCSI specification states that when a UNIT ATTENTION condition
+; is pending, as indicated by a CHECK CONDITION status message,
+; the target shall revert to asynchronous transfers. Since
+; synchronous transfers parameters are maintained on a per INITIATOR/TARGET
+; basis, and returning control to our scheduler could work on a command
+; running on another lun on that target using the old parameters, we must
+; interrupt the host processor to get them changed, or change them ourselves.
+;
+; Once SCSI-II tagged queueing is implemented, things will be even more
+; hairy, since contingent allegiance conditions exist on a per-target/lun
+; basis, and issuing a new command with a different tag would clear it.
+; In these cases, we must interrupt the host processor to get a request
+; added to the HEAD of the queue with the request sense command, or we
+; must automatically issue the request sense command.
+
+
+
+
+
+ INTFLY
+
+at 0x0000018f : */ 0x98180000,0x00000000,
+/*
+
+
+
+
+
+ JUMP schedule
+
+at 0x00000191 : */ 0x80080000,0x00000000,
+/*
+command_failed:
+ INT int_err_check_condition
+
+at 0x00000193 : */ 0x98080000,0x00030000,
+/*
+
+
+
+
+;
+; wait_reselect
+;
+; PURPOSE : This is essentially the idle routine, where control lands
+; when there are no new processes to schedule. wait_reselect
+; waits for reselection, selection, and new commands.
+;
+; When a successful reselection occurs, with the aid
+; of fixed up code in each DSA, wait_reselect walks the
+; reconnect_dsa_queue, asking each dsa if the target ID
+; and LUN match its.
+;
+; If a match is found, a call is made back to reselected_ok,
+; which through the miracles of self modifying code, extracts
+; the found DSA from the reconnect_dsa_queue and then
+; returns control to the DSAs thread of execution.
+;
+; INPUTS : NONE
+;
+; CALLS : OK
+;
+; MODIFIES : DSA,
+;
+; EXITS : On successful reselection, control is returned to the
+; DSA which called reselected_ok. If the WAIT RESELECT
+; was interrupted by a new commands arrival signaled by
+; SIG_P, control is passed to schedule. If the NCR is
+; selected, the host system is interrupted with an
+; int_err_selected which is usually responded to by
+; setting DSP to the target_abort address.
+
+ENTRY wait_reselect
+wait_reselect:
+
+
+
+
+
+
+ WAIT RESELECT wait_reselect_failed
+
+at 0x00000195 : */ 0x50000000,0x0000076c,
+/*
+
+reselected:
+
+
+
+ CLEAR TARGET
+
+at 0x00000197 : */ 0x60000200,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000199 : */ 0x78380000,0x00000000,
+/*
+ ; Read all data needed to reestablish the nexus -
+ MOVE 1, reselected_identify, WHEN MSG_IN
+
+at 0x0000019b : */ 0x0f000001,0x00000000,
+/*
+ ; We used to CLEAR ACK here.
+
+
+
+
+
+ ; Point DSA at the current head of the disconnected queue.
+ MOVE dmode_memory_to_ncr TO DMODE
+
+at 0x0000019d : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, reconnect_dsa_head, addr_scratch
+
+at 0x0000019f : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x000001a2 : */ 0x78380000,0x00000000,
+/*
+ CALL scratch_to_dsa
+
+at 0x000001a4 : */ 0x88080000,0x00000980,
+/*
+
+ ; Fix the update-next pointer so that the reconnect_dsa_head
+ ; pointer is the one that will be updated if this DSA is a hit
+ ; and we remove it from the queue.
+
+ MOVE MEMORY 4, addr_reconnect_dsa_head, reselected_ok + 8
+
+at 0x000001a6 : */ 0xc0000004,0x00000000,0x00000758,
+/*
+
+ENTRY reselected_check_next
+reselected_check_next:
+
+
+
+ ; Check for a NULL pointer.
+ MOVE DSA0 TO SFBR
+
+at 0x000001a9 : */ 0x72100000,0x00000000,
+/*
+ JUMP reselected_not_end, IF NOT 0
+
+at 0x000001ab : */ 0x80040000,0x000006ec,
+/*
+ MOVE DSA1 TO SFBR
+
+at 0x000001ad : */ 0x72110000,0x00000000,
+/*
+ JUMP reselected_not_end, IF NOT 0
+
+at 0x000001af : */ 0x80040000,0x000006ec,
+/*
+ MOVE DSA2 TO SFBR
+
+at 0x000001b1 : */ 0x72120000,0x00000000,
+/*
+ JUMP reselected_not_end, IF NOT 0
+
+at 0x000001b3 : */ 0x80040000,0x000006ec,
+/*
+ MOVE DSA3 TO SFBR
+
+at 0x000001b5 : */ 0x72130000,0x00000000,
+/*
+ JUMP reselected_not_end, IF NOT 0
+
+at 0x000001b7 : */ 0x80040000,0x000006ec,
+/*
+ INT int_err_unexpected_reselect
+
+at 0x000001b9 : */ 0x98080000,0x00020000,
+/*
+
+reselected_not_end:
+ ;
+ ; XXX the ALU is only eight bits wide, and the assembler
+ ; wont do the dirt work for us. As long as dsa_check_reselect
+ ; is negative, we need to sign extend with 1 bits to the full
+ ; 32 bit width of the address.
+ ;
+ ; A potential work around would be to have a known alignment
+ ; of the DSA structure such that the base address plus
+ ; dsa_check_reselect doesn't require carrying from bytes
+ ; higher than the LSB.
+ ;
+
+ MOVE DSA0 TO SFBR
+
+at 0x000001bb : */ 0x72100000,0x00000000,
+/*
+ MOVE SFBR + dsa_check_reselect TO SCRATCH0
+
+at 0x000001bd : */ 0x6e340000,0x00000000,
+/*
+ MOVE DSA1 TO SFBR
+
+at 0x000001bf : */ 0x72110000,0x00000000,
+/*
+ MOVE SFBR + 0xff TO SCRATCH1 WITH CARRY
+
+at 0x000001c1 : */ 0x6f35ff00,0x00000000,
+/*
+ MOVE DSA2 TO SFBR
+
+at 0x000001c3 : */ 0x72120000,0x00000000,
+/*
+ MOVE SFBR + 0xff TO SCRATCH2 WITH CARRY
+
+at 0x000001c5 : */ 0x6f36ff00,0x00000000,
+/*
+ MOVE DSA3 TO SFBR
+
+at 0x000001c7 : */ 0x72130000,0x00000000,
+/*
+ MOVE SFBR + 0xff TO SCRATCH3 WITH CARRY
+
+at 0x000001c9 : */ 0x6f37ff00,0x00000000,
+/*
+
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x000001cb : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, reselected_check + 4
+
+at 0x000001cd : */ 0xc0000004,0x00000000,0x0000074c,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x000001d0 : */ 0x78380000,0x00000000,
+/*
+reselected_check:
+ JUMP 0
+
+at 0x000001d2 : */ 0x80080000,0x00000000,
+/*
+
+
+;
+;
+ENTRY reselected_ok
+reselected_ok:
+ MOVE MEMORY 4, 0, 0 ; Patched : first word
+
+at 0x000001d4 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ ; is address of
+ ; successful dsa_next
+ ; Second word is last
+ ; unsuccessful dsa_next,
+ ; starting with
+ ; dsa_reconnect_head
+ ; We used to CLEAR ACK here.
+
+
+
+
+
+
+ RETURN ; Return control to where
+
+at 0x000001d7 : */ 0x90080000,0x00000000,
+/*
+
+
+
+
+selected:
+ INT int_err_selected;
+
+at 0x000001d9 : */ 0x98080000,0x00010000,
+/*
+
+;
+; A select or reselect failure can be caused by one of two conditions :
+; 1. SIG_P was set. This will be the case if the user has written
+; a new value to a previously NULL head of the issue queue.
+;
+; 2. The NCR53c810 was selected or reselected by another device.
+;
+; 3. The bus was already busy since we were selected or reselected
+; before starting the command.
+
+wait_reselect_failed:
+
+
+
+; Check selected bit.
+ MOVE SIST0 & 0x20 TO SFBR
+
+at 0x000001db : */ 0x74422000,0x00000000,
+/*
+ JUMP selected, IF 0x20
+
+at 0x000001dd : */ 0x800c0020,0x00000764,
+/*
+; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
+ MOVE CTEST2 & 0x40 TO SFBR
+
+at 0x000001df : */ 0x741a4000,0x00000000,
+/*
+ JUMP schedule, IF 0x40
+
+at 0x000001e1 : */ 0x800c0040,0x00000000,
+/*
+; Check connected bit.
+; FIXME: this needs to change if we support target mode
+ MOVE ISTAT & 0x08 TO SFBR
+
+at 0x000001e3 : */ 0x74140800,0x00000000,
+/*
+ JUMP reselected, IF 0x08
+
+at 0x000001e5 : */ 0x800c0008,0x0000065c,
+/*
+; FIXME : Something bogus happened, and we shouldn't fail silently.
+
+
+
+ INT int_debug_panic
+
+at 0x000001e7 : */ 0x98080000,0x030b0000,
+/*
+
+
+
+select_failed:
+
+
+
+; Otherwise, mask the selected and reselected bits off SIST0
+ MOVE SIST0 & 0x30 TO SFBR
+
+at 0x000001e9 : */ 0x74423000,0x00000000,
+/*
+ JUMP selected, IF 0x20
+
+at 0x000001eb : */ 0x800c0020,0x00000764,
+/*
+ JUMP reselected, IF 0x10
+
+at 0x000001ed : */ 0x800c0010,0x0000065c,
+/*
+; If SIGP is set, the user just gave us another command, and
+; we should restart or return to the scheduler.
+; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
+ MOVE CTEST2 & 0x40 TO SFBR
+
+at 0x000001ef : */ 0x741a4000,0x00000000,
+/*
+ JUMP select, IF 0x40
+
+at 0x000001f1 : */ 0x800c0040,0x000001fc,
+/*
+; Check connected bit.
+; FIXME: this needs to change if we support target mode
+; FIXME: is this really necessary?
+ MOVE ISTAT & 0x08 TO SFBR
+
+at 0x000001f3 : */ 0x74140800,0x00000000,
+/*
+ JUMP reselected, IF 0x08
+
+at 0x000001f5 : */ 0x800c0008,0x0000065c,
+/*
+; FIXME : Something bogus happened, and we shouldn't fail silently.
+
+
+
+ INT int_debug_panic
+
+at 0x000001f7 : */ 0x98080000,0x030b0000,
+/*
+
+
+;
+; test_1
+; test_2
+;
+; PURPOSE : run some verification tests on the NCR. test_1
+; copies test_src to test_dest and interrupts the host
+; processor, testing for cache coherency and interrupt
+; problems in the processes.
+;
+; test_2 runs a command with offsets relative to the
+; DSA on entry, and is useful for miscellaneous experimentation.
+;
+
+; Verify that interrupts are working correctly and that we don't
+; have a cache invalidation problem.
+
+ABSOLUTE test_src = 0, test_dest = 0
+ENTRY test_1
+test_1:
+ MOVE MEMORY 4, test_src, test_dest
+
+at 0x000001f9 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ INT int_test_1
+
+at 0x000001fc : */ 0x98080000,0x04000000,
+/*
+
+;
+; Run arbitrary commands, with test code establishing a DSA
+;
+
+ENTRY test_2
+test_2:
+ CLEAR TARGET
+
+at 0x000001fe : */ 0x60000200,0x00000000,
+/*
+ SELECT ATN FROM 0, test_2_fail
+
+at 0x00000200 : */ 0x43000000,0x00000850,
+/*
+ JUMP test_2_msgout, WHEN MSG_OUT
+
+at 0x00000202 : */ 0x860b0000,0x00000810,
+/*
+ENTRY test_2_msgout
+test_2_msgout:
+ MOVE FROM 8, WHEN MSG_OUT
+
+at 0x00000204 : */ 0x1e000000,0x00000008,
+/*
+ MOVE FROM 16, WHEN CMD
+
+at 0x00000206 : */ 0x1a000000,0x00000010,
+/*
+ MOVE FROM 24, WHEN DATA_IN
+
+at 0x00000208 : */ 0x19000000,0x00000018,
+/*
+ MOVE FROM 32, WHEN STATUS
+
+at 0x0000020a : */ 0x1b000000,0x00000020,
+/*
+ MOVE FROM 40, WHEN MSG_IN
+
+at 0x0000020c : */ 0x1f000000,0x00000028,
+/*
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+
+at 0x0000020e : */ 0x7c027f00,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x00000210 : */ 0x60000040,0x00000000,
+/*
+ WAIT DISCONNECT
+
+at 0x00000212 : */ 0x48000000,0x00000000,
+/*
+test_2_fail:
+ INT int_test_2
+
+at 0x00000214 : */ 0x98080000,0x04010000,
+/*
+
+ENTRY debug_break
+debug_break:
+ INT int_debug_break
+
+at 0x00000216 : */ 0x98080000,0x03000000,
+/*
+
+;
+; initiator_abort
+; target_abort
+;
+; PURPOSE : Abort the currently established nexus from with initiator
+; or target mode.
+;
+;
+
+ENTRY target_abort
+target_abort:
+ SET TARGET
+
+at 0x00000218 : */ 0x58000200,0x00000000,
+/*
+ DISCONNECT
+
+at 0x0000021a : */ 0x48000000,0x00000000,
+/*
+ CLEAR TARGET
+
+at 0x0000021c : */ 0x60000200,0x00000000,
+/*
+ JUMP schedule
+
+at 0x0000021e : */ 0x80080000,0x00000000,
+/*
+
+ENTRY initiator_abort
+initiator_abort:
+ SET ATN
+
+at 0x00000220 : */ 0x58000008,0x00000000,
+/*
+;
+; The SCSI-I specification says that targets may go into MSG out at
+; their leisure upon receipt of the ATN single. On all versions of the
+; specification, we can't change phases until REQ transitions true->false,
+; so we need to sink/source one byte of data to allow the transition.
+;
+; For the sake of safety, we'll only source one byte of data in all
+; cases, but to accommodate the SCSI-I dain bramage, we'll sink an
+; arbitrary number of bytes.
+ JUMP spew_cmd, WHEN CMD
+
+at 0x00000222 : */ 0x820b0000,0x000008b8,
+/*
+ JUMP eat_msgin, WHEN MSG_IN
+
+at 0x00000224 : */ 0x870b0000,0x000008c8,
+/*
+ JUMP eat_datain, WHEN DATA_IN
+
+at 0x00000226 : */ 0x810b0000,0x000008f8,
+/*
+ JUMP eat_status, WHEN STATUS
+
+at 0x00000228 : */ 0x830b0000,0x000008e0,
+/*
+ JUMP spew_dataout, WHEN DATA_OUT
+
+at 0x0000022a : */ 0x800b0000,0x00000910,
+/*
+ JUMP sated
+
+at 0x0000022c : */ 0x80080000,0x00000918,
+/*
+spew_cmd:
+ MOVE 1, NCR53c7xx_zero, WHEN CMD
+
+at 0x0000022e : */ 0x0a000001,0x00000000,
+/*
+ JUMP sated
+
+at 0x00000230 : */ 0x80080000,0x00000918,
+/*
+eat_msgin:
+ MOVE 1, NCR53c7xx_sink, WHEN MSG_IN
+
+at 0x00000232 : */ 0x0f000001,0x00000000,
+/*
+ JUMP eat_msgin, WHEN MSG_IN
+
+at 0x00000234 : */ 0x870b0000,0x000008c8,
+/*
+ JUMP sated
+
+at 0x00000236 : */ 0x80080000,0x00000918,
+/*
+eat_status:
+ MOVE 1, NCR53c7xx_sink, WHEN STATUS
+
+at 0x00000238 : */ 0x0b000001,0x00000000,
+/*
+ JUMP eat_status, WHEN STATUS
+
+at 0x0000023a : */ 0x830b0000,0x000008e0,
+/*
+ JUMP sated
+
+at 0x0000023c : */ 0x80080000,0x00000918,
+/*
+eat_datain:
+ MOVE 1, NCR53c7xx_sink, WHEN DATA_IN
+
+at 0x0000023e : */ 0x09000001,0x00000000,
+/*
+ JUMP eat_datain, WHEN DATA_IN
+
+at 0x00000240 : */ 0x810b0000,0x000008f8,
+/*
+ JUMP sated
+
+at 0x00000242 : */ 0x80080000,0x00000918,
+/*
+spew_dataout:
+ MOVE 1, NCR53c7xx_zero, WHEN DATA_OUT
+
+at 0x00000244 : */ 0x08000001,0x00000000,
+/*
+sated:
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+
+at 0x00000246 : */ 0x7c027f00,0x00000000,
+/*
+ MOVE 1, NCR53c7xx_msg_abort, WHEN MSG_OUT
+
+at 0x00000248 : */ 0x0e000001,0x00000000,
+/*
+ WAIT DISCONNECT
+
+at 0x0000024a : */ 0x48000000,0x00000000,
+/*
+ INT int_norm_aborted
+
+at 0x0000024c : */ 0x98080000,0x02040000,
+/*
+
+;
+; dsa_to_scratch
+; scratch_to_dsa
+;
+; PURPOSE :
+; The NCR chips cannot do a move memory instruction with the DSA register
+; as the source or destination. So, we provide a couple of subroutines
+; that let us switch between the DSA register and scratch register.
+;
+; Memory moves to/from the DSPS register also don't work, but we
+; don't use them.
+;
+;
+
+
+dsa_to_scratch:
+ MOVE DSA0 TO SFBR
+
+at 0x0000024e : */ 0x72100000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH0
+
+at 0x00000250 : */ 0x6a340000,0x00000000,
+/*
+ MOVE DSA1 TO SFBR
+
+at 0x00000252 : */ 0x72110000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH1
+
+at 0x00000254 : */ 0x6a350000,0x00000000,
+/*
+ MOVE DSA2 TO SFBR
+
+at 0x00000256 : */ 0x72120000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH2
+
+at 0x00000258 : */ 0x6a360000,0x00000000,
+/*
+ MOVE DSA3 TO SFBR
+
+at 0x0000025a : */ 0x72130000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH3
+
+at 0x0000025c : */ 0x6a370000,0x00000000,
+/*
+ RETURN
+
+at 0x0000025e : */ 0x90080000,0x00000000,
+/*
+
+scratch_to_dsa:
+ MOVE SCRATCH0 TO SFBR
+
+at 0x00000260 : */ 0x72340000,0x00000000,
+/*
+ MOVE SFBR TO DSA0
+
+at 0x00000262 : */ 0x6a100000,0x00000000,
+/*
+ MOVE SCRATCH1 TO SFBR
+
+at 0x00000264 : */ 0x72350000,0x00000000,
+/*
+ MOVE SFBR TO DSA1
+
+at 0x00000266 : */ 0x6a110000,0x00000000,
+/*
+ MOVE SCRATCH2 TO SFBR
+
+at 0x00000268 : */ 0x72360000,0x00000000,
+/*
+ MOVE SFBR TO DSA2
+
+at 0x0000026a : */ 0x6a120000,0x00000000,
+/*
+ MOVE SCRATCH3 TO SFBR
+
+at 0x0000026c : */ 0x72370000,0x00000000,
+/*
+ MOVE SFBR TO DSA3
+
+at 0x0000026e : */ 0x6a130000,0x00000000,
+/*
+ RETURN
+
+at 0x00000270 : */ 0x90080000,0x00000000,
+};
+
+#define A_NCR53c7xx_msg_abort 0x00000000
+u32 A_NCR53c7xx_msg_abort_used[] = {
+ 0x00000249,
+};
+
+#define A_NCR53c7xx_msg_reject 0x00000000
+u32 A_NCR53c7xx_msg_reject_used[] = {
+ 0x00000172,
+};
+
+#define A_NCR53c7xx_sink 0x00000000
+u32 A_NCR53c7xx_sink_used[] = {
+ 0x00000233,
+ 0x00000239,
+ 0x0000023f,
+};
+
+#define A_NCR53c7xx_zero 0x00000000
+u32 A_NCR53c7xx_zero_used[] = {
+ 0x0000022f,
+ 0x00000245,
+};
+
+#define A_NOP_insn 0x00000000
+u32 A_NOP_insn_used[] = {
+ 0x00000010,
+};
+
+#define A_addr_reconnect_dsa_head 0x00000000
+u32 A_addr_reconnect_dsa_head_used[] = {
+ 0x000001a7,
+};
+
+#define A_addr_scratch 0x00000000
+u32 A_addr_scratch_used[] = {
+ 0x00000004,
+ 0x0000001b,
+ 0x00000046,
+ 0x00000067,
+ 0x00000073,
+ 0x000000b0,
+ 0x000000c6,
+ 0x00000128,
+ 0x00000141,
+ 0x000001a1,
+ 0x000001ce,
+};
+
+#define A_addr_temp 0x00000000
+u32 A_addr_temp_used[] = {
+ 0x00000025,
+ 0x00000034,
+};
+
+#define A_dmode_memory_to_memory 0x00000000
+u32 A_dmode_memory_to_memory_used[] = {
+ 0x00000005,
+ 0x0000001c,
+ 0x00000027,
+ 0x00000035,
+ 0x00000047,
+ 0x00000069,
+ 0x00000075,
+ 0x000000b2,
+ 0x000000c8,
+ 0x0000012a,
+ 0x00000143,
+ 0x00000199,
+ 0x000001a2,
+ 0x000001d0,
+};
+
+#define A_dmode_memory_to_ncr 0x00000000
+u32 A_dmode_memory_to_ncr_used[] = {
+ 0x00000000,
+ 0x00000017,
+ 0x00000030,
+ 0x00000042,
+ 0x0000019d,
+};
+
+#define A_dmode_ncr_to_memory 0x00000000
+u32 A_dmode_ncr_to_memory_used[] = {
+ 0x00000022,
+ 0x00000064,
+ 0x00000070,
+ 0x000000ad,
+ 0x000000c3,
+ 0x00000125,
+ 0x0000013e,
+ 0x000001cb,
+};
+
+#define A_dsa_check_reselect 0x00000000
+u32 A_dsa_check_reselect_used[] = {
+ 0x000001bd,
+};
+
+#define A_dsa_cmdout 0x00000048
+u32 A_dsa_cmdout_used[] = {
+ 0x00000094,
+};
+
+#define A_dsa_cmnd 0x00000038
+u32 A_dsa_cmnd_used[] = {
+};
+
+#define A_dsa_datain 0x00000054
+u32 A_dsa_datain_used[] = {
+ 0x000000bb,
+};
+
+#define A_dsa_dataout 0x00000050
+u32 A_dsa_dataout_used[] = {
+ 0x000000a5,
+};
+
+#define A_dsa_end 0x00000070
+u32 A_dsa_end_used[] = {
+};
+
+#define A_dsa_fields_start 0x00000000
+u32 A_dsa_fields_start_used[] = {
+};
+
+#define A_dsa_msgin 0x00000058
+u32 A_dsa_msgin_used[] = {
+ 0x00000188,
+};
+
+#define A_dsa_msgout 0x00000040
+u32 A_dsa_msgout_used[] = {
+ 0x00000086,
+};
+
+#define A_dsa_msgout_other 0x00000068
+u32 A_dsa_msgout_other_used[] = {
+ 0x00000180,
+};
+
+#define A_dsa_next 0x00000030
+u32 A_dsa_next_used[] = {
+ 0x0000005c,
+};
+
+#define A_dsa_restore_pointers 0x00000000
+u32 A_dsa_restore_pointers_used[] = {
+ 0x0000012e,
+};
+
+#define A_dsa_save_data_pointer 0x00000000
+u32 A_dsa_save_data_pointer_used[] = {
+ 0x00000115,
+};
+
+#define A_dsa_select 0x0000003c
+u32 A_dsa_select_used[] = {
+ 0x00000081,
+};
+
+#define A_dsa_status 0x00000060
+u32 A_dsa_status_used[] = {
+ 0x00000184,
+};
+
+#define A_dsa_temp_addr_array_value 0x00000000
+u32 A_dsa_temp_addr_array_value_used[] = {
+};
+
+#define A_dsa_temp_addr_dsa_value 0x00000000
+u32 A_dsa_temp_addr_dsa_value_used[] = {
+ 0x00000003,
+};
+
+#define A_dsa_temp_addr_new_value 0x00000000
+u32 A_dsa_temp_addr_new_value_used[] = {
+};
+
+#define A_dsa_temp_addr_next 0x00000000
+u32 A_dsa_temp_addr_next_used[] = {
+ 0x00000015,
+ 0x0000004e,
+};
+
+#define A_dsa_temp_addr_residual 0x00000000
+u32 A_dsa_temp_addr_residual_used[] = {
+ 0x0000002a,
+ 0x00000039,
+};
+
+#define A_dsa_temp_addr_saved_pointer 0x00000000
+u32 A_dsa_temp_addr_saved_pointer_used[] = {
+ 0x00000026,
+ 0x00000033,
+};
+
+#define A_dsa_temp_addr_saved_residual 0x00000000
+u32 A_dsa_temp_addr_saved_residual_used[] = {
+ 0x0000002b,
+ 0x00000038,
+};
+
+#define A_dsa_temp_lun 0x00000000
+u32 A_dsa_temp_lun_used[] = {
+ 0x0000004b,
+};
+
+#define A_dsa_temp_next 0x00000000
+u32 A_dsa_temp_next_used[] = {
+ 0x0000001a,
+};
+
+#define A_dsa_temp_sync 0x00000000
+u32 A_dsa_temp_sync_used[] = {
+ 0x00000053,
+};
+
+#define A_dsa_temp_target 0x00000000
+u32 A_dsa_temp_target_used[] = {
+ 0x00000040,
+};
+
+#define A_int_debug_break 0x03000000
+u32 A_int_debug_break_used[] = {
+ 0x00000217,
+};
+
+#define A_int_debug_panic 0x030b0000
+u32 A_int_debug_panic_used[] = {
+ 0x000001e8,
+ 0x000001f8,
+};
+
+#define A_int_err_check_condition 0x00030000
+u32 A_int_err_check_condition_used[] = {
+ 0x00000194,
+};
+
+#define A_int_err_no_phase 0x00040000
+u32 A_int_err_no_phase_used[] = {
+};
+
+#define A_int_err_selected 0x00010000
+u32 A_int_err_selected_used[] = {
+ 0x000001da,
+};
+
+#define A_int_err_unexpected_phase 0x00000000
+u32 A_int_err_unexpected_phase_used[] = {
+ 0x0000008c,
+ 0x00000092,
+ 0x0000009a,
+ 0x000000d0,
+ 0x000000d4,
+ 0x000000d6,
+ 0x000000de,
+ 0x000000e2,
+ 0x000000e4,
+ 0x000000ec,
+ 0x000000f0,
+ 0x000000f2,
+ 0x000000f4,
+ 0x0000014c,
+};
+
+#define A_int_err_unexpected_reselect 0x00020000
+u32 A_int_err_unexpected_reselect_used[] = {
+ 0x000001ba,
+};
+
+#define A_int_msg_1 0x01020000
+u32 A_int_msg_1_used[] = {
+ 0x0000010e,
+ 0x00000110,
+};
+
+#define A_int_msg_sdtr 0x01010000
+u32 A_int_msg_sdtr_used[] = {
+ 0x0000016c,
+};
+
+#define A_int_msg_wdtr 0x01000000
+u32 A_int_msg_wdtr_used[] = {
+ 0x00000160,
+};
+
+#define A_int_norm_aborted 0x02040000
+u32 A_int_norm_aborted_used[] = {
+ 0x0000024d,
+};
+
+#define A_int_norm_command_complete 0x02020000
+u32 A_int_norm_command_complete_used[] = {
+};
+
+#define A_int_norm_disconnected 0x02030000
+u32 A_int_norm_disconnected_used[] = {
+};
+
+#define A_int_norm_reselect_complete 0x02010000
+u32 A_int_norm_reselect_complete_used[] = {
+};
+
+#define A_int_norm_reset 0x02050000
+u32 A_int_norm_reset_used[] = {
+};
+
+#define A_int_norm_select_complete 0x02000000
+u32 A_int_norm_select_complete_used[] = {
+};
+
+#define A_int_test_1 0x04000000
+u32 A_int_test_1_used[] = {
+ 0x000001fd,
+};
+
+#define A_int_test_2 0x04010000
+u32 A_int_test_2_used[] = {
+ 0x00000215,
+};
+
+#define A_int_test_3 0x04020000
+u32 A_int_test_3_used[] = {
+};
+
+#define A_msg_buf 0x00000000
+u32 A_msg_buf_used[] = {
+ 0x00000102,
+ 0x0000014e,
+ 0x00000158,
+ 0x0000015e,
+ 0x00000164,
+ 0x0000016a,
+};
+
+#define A_reconnect_dsa_head 0x00000000
+u32 A_reconnect_dsa_head_used[] = {
+ 0x0000006c,
+ 0x00000074,
+ 0x000001a0,
+};
+
+#define A_reselected_identify 0x00000000
+u32 A_reselected_identify_used[] = {
+ 0x00000045,
+ 0x0000019c,
+};
+
+#define A_reselected_tag 0x00000000
+u32 A_reselected_tag_used[] = {
+};
+
+#define A_schedule 0x00000000
+u32 A_schedule_used[] = {
+ 0x0000007e,
+ 0x00000192,
+ 0x000001e2,
+ 0x0000021f,
+};
+
+#define A_test_dest 0x00000000
+u32 A_test_dest_used[] = {
+ 0x000001fb,
+};
+
+#define A_test_src 0x00000000
+u32 A_test_src_used[] = {
+ 0x000001fa,
+};
+
+#define Ent_accept_message 0x000005d4
+#define Ent_cmdout_cmdout 0x0000024c
+#define Ent_command_complete 0x0000060c
+#define Ent_command_complete_msgin 0x0000061c
+#define Ent_data_transfer 0x00000254
+#define Ent_datain_to_jump 0x00000328
+#define Ent_debug_break 0x00000858
+#define Ent_dsa_code_begin 0x00000000
+#define Ent_dsa_code_check_reselect 0x000000f8
+#define Ent_dsa_code_fix_jump 0x0000003c
+#define Ent_dsa_code_restore_pointers 0x000000c0
+#define Ent_dsa_code_save_data_pointer 0x00000088
+#define Ent_dsa_code_template 0x00000000
+#define Ent_dsa_code_template_end 0x00000168
+#define Ent_dsa_schedule 0x00000168
+#define Ent_dsa_zero 0x00000168
+#define Ent_end_data_transfer 0x0000028c
+#define Ent_initiator_abort 0x00000880
+#define Ent_msg_in 0x00000404
+#define Ent_msg_in_restart 0x000003e4
+#define Ent_other_in 0x00000374
+#define Ent_other_out 0x0000033c
+#define Ent_other_transfer 0x000003ac
+#define Ent_reject_message 0x000005b4
+#define Ent_reselected_check_next 0x000006a4
+#define Ent_reselected_ok 0x00000750
+#define Ent_respond_message 0x000005ec
+#define Ent_select 0x000001fc
+#define Ent_select_msgout 0x00000214
+#define Ent_target_abort 0x00000860
+#define Ent_test_1 0x000007e4
+#define Ent_test_2 0x000007f8
+#define Ent_test_2_msgout 0x00000810
+#define Ent_wait_reselect 0x00000654
+u32 LABELPATCHES[] = {
+ 0x00000008,
+ 0x0000000a,
+ 0x00000013,
+ 0x00000016,
+ 0x0000001f,
+ 0x00000021,
+ 0x0000004f,
+ 0x00000051,
+ 0x0000005b,
+ 0x00000068,
+ 0x0000006f,
+ 0x00000082,
+ 0x00000084,
+ 0x0000008a,
+ 0x0000008e,
+ 0x00000090,
+ 0x00000096,
+ 0x00000098,
+ 0x0000009c,
+ 0x0000009e,
+ 0x000000a0,
+ 0x000000a2,
+ 0x000000a4,
+ 0x000000b1,
+ 0x000000b6,
+ 0x000000ba,
+ 0x000000c7,
+ 0x000000cc,
+ 0x000000d2,
+ 0x000000d8,
+ 0x000000da,
+ 0x000000e0,
+ 0x000000e6,
+ 0x000000e8,
+ 0x000000ee,
+ 0x000000f6,
+ 0x000000f8,
+ 0x00000104,
+ 0x00000106,
+ 0x00000108,
+ 0x0000010a,
+ 0x0000010c,
+ 0x00000112,
+ 0x00000114,
+ 0x00000129,
+ 0x00000142,
+ 0x00000148,
+ 0x00000150,
+ 0x00000152,
+ 0x00000154,
+ 0x0000015a,
+ 0x00000166,
+ 0x00000196,
+ 0x000001a5,
+ 0x000001a8,
+ 0x000001ac,
+ 0x000001b0,
+ 0x000001b4,
+ 0x000001b8,
+ 0x000001cf,
+ 0x000001de,
+ 0x000001e6,
+ 0x000001ec,
+ 0x000001ee,
+ 0x000001f2,
+ 0x000001f6,
+ 0x00000201,
+ 0x00000203,
+ 0x00000223,
+ 0x00000225,
+ 0x00000227,
+ 0x00000229,
+ 0x0000022b,
+ 0x0000022d,
+ 0x00000231,
+ 0x00000235,
+ 0x00000237,
+ 0x0000023b,
+ 0x0000023d,
+ 0x00000241,
+ 0x00000243,
+};
+
+struct {
+ u32 offset;
+ void *address;
+} EXTERNAL_PATCHES[] = {
+};
+
+u32 INSTRUCTIONS = 301;
+u32 PATCHES = 81;
+u32 EXTERNAL_PATCHES_LEN = 0;
diff --git a/linux/src/drivers/scsi/53c8xx_u.h b/linux/src/drivers/scsi/53c8xx_u.h
new file mode 100644
index 00000000..c3d486fe
--- /dev/null
+++ b/linux/src/drivers/scsi/53c8xx_u.h
@@ -0,0 +1,97 @@
+#undef A_NCR53c7xx_msg_abort
+#undef A_NCR53c7xx_msg_reject
+#undef A_NCR53c7xx_sink
+#undef A_NCR53c7xx_zero
+#undef A_NOP_insn
+#undef A_addr_reconnect_dsa_head
+#undef A_addr_scratch
+#undef A_addr_temp
+#undef A_dmode_memory_to_memory
+#undef A_dmode_memory_to_ncr
+#undef A_dmode_ncr_to_memory
+#undef A_dsa_check_reselect
+#undef A_dsa_cmdout
+#undef A_dsa_cmnd
+#undef A_dsa_datain
+#undef A_dsa_dataout
+#undef A_dsa_end
+#undef A_dsa_fields_start
+#undef A_dsa_msgin
+#undef A_dsa_msgout
+#undef A_dsa_msgout_other
+#undef A_dsa_next
+#undef A_dsa_restore_pointers
+#undef A_dsa_save_data_pointer
+#undef A_dsa_select
+#undef A_dsa_status
+#undef A_dsa_temp_addr_array_value
+#undef A_dsa_temp_addr_dsa_value
+#undef A_dsa_temp_addr_new_value
+#undef A_dsa_temp_addr_next
+#undef A_dsa_temp_addr_residual
+#undef A_dsa_temp_addr_saved_pointer
+#undef A_dsa_temp_addr_saved_residual
+#undef A_dsa_temp_lun
+#undef A_dsa_temp_next
+#undef A_dsa_temp_sync
+#undef A_dsa_temp_target
+#undef A_int_debug_break
+#undef A_int_debug_panic
+#undef A_int_err_check_condition
+#undef A_int_err_no_phase
+#undef A_int_err_selected
+#undef A_int_err_unexpected_phase
+#undef A_int_err_unexpected_reselect
+#undef A_int_msg_1
+#undef A_int_msg_sdtr
+#undef A_int_msg_wdtr
+#undef A_int_norm_aborted
+#undef A_int_norm_command_complete
+#undef A_int_norm_disconnected
+#undef A_int_norm_reselect_complete
+#undef A_int_norm_reset
+#undef A_int_norm_select_complete
+#undef A_int_test_1
+#undef A_int_test_2
+#undef A_int_test_3
+#undef A_msg_buf
+#undef A_reconnect_dsa_head
+#undef A_reselected_identify
+#undef A_reselected_tag
+#undef A_schedule
+#undef A_test_dest
+#undef A_test_src
+#undef Ent_accept_message
+#undef Ent_cmdout_cmdout
+#undef Ent_command_complete
+#undef Ent_command_complete_msgin
+#undef Ent_data_transfer
+#undef Ent_datain_to_jump
+#undef Ent_debug_break
+#undef Ent_dsa_code_begin
+#undef Ent_dsa_code_check_reselect
+#undef Ent_dsa_code_fix_jump
+#undef Ent_dsa_code_restore_pointers
+#undef Ent_dsa_code_save_data_pointer
+#undef Ent_dsa_code_template
+#undef Ent_dsa_code_template_end
+#undef Ent_dsa_schedule
+#undef Ent_dsa_zero
+#undef Ent_end_data_transfer
+#undef Ent_initiator_abort
+#undef Ent_msg_in
+#undef Ent_msg_in_restart
+#undef Ent_other_in
+#undef Ent_other_out
+#undef Ent_other_transfer
+#undef Ent_reject_message
+#undef Ent_reselected_check_next
+#undef Ent_reselected_ok
+#undef Ent_respond_message
+#undef Ent_select
+#undef Ent_select_msgout
+#undef Ent_target_abort
+#undef Ent_test_1
+#undef Ent_test_2
+#undef Ent_test_2_msgout
+#undef Ent_wait_reselect
diff --git a/linux/src/drivers/scsi/AM53C974.c b/linux/src/drivers/scsi/AM53C974.c
new file mode 100644
index 00000000..9ff5a2e4
--- /dev/null
+++ b/linux/src/drivers/scsi/AM53C974.c
@@ -0,0 +1,2274 @@
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "AM53C974.h"
+#include "constants.h"
+#include "sd.h"
+
+/* AM53/79C974 (PCscsi) driver release 0.5
+ *
+ * The architecture and much of the code of this device
+ * driver was originally developed by Drew Eckhardt for
+ * the NCR5380. The following copyrights apply:
+ * For the architecture and all pieces of code which can also be found
+ * in the NCR5380 device driver:
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * The AM53C974_nobios_detect code was originally developed by
+ * Robin Cutshaw (robin@xfree86.org) and is used here in a
+ * slightly modified form.
+ *
+ * For the remaining code:
+ * Copyright 1994, D. Frieauff
+ * EMail: fri@rsx42sun0.dofn.de
+ * Phone: x49-7545-8-2256 , x49-7541-42305
+ */
+
+/*
+ * $Log: AM53C974.c,v $
+ */
+
+#ifdef AM53C974_DEBUG
+ #define DEB(x) x
+ #ifdef AM53C974_DEBUG_KEYWAIT
+ #define KEYWAIT() AM53C974_keywait()
+ #else
+ #define KEYWAIT()
+ #endif
+ #ifdef AM53C974_DEBUG_INIT
+ #define DEB_INIT(x) x
+ #else
+ #define DEB_INIT(x)
+ #endif
+ #ifdef AM53C974_DEBUG_MSG
+ #define DEB_MSG(x) x
+ #else
+ #define DEB_MSG(x)
+ #endif
+ #ifdef AM53C974_DEB_RESEL
+ #define DEB_RESEL(x) x
+ #else
+ #define DEB_RESEL(x)
+ #endif
+ #ifdef AM53C974_DEBUG_QUEUE
+ #define DEB_QUEUE(x) x
+ #define LIST(x,y) {printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); }
+ #define REMOVE(w,x,y,z) {printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); }
+ #else
+ #define DEB_QUEUE(x)
+ #define LIST(x,y)
+ #define REMOVE(w,x,y,z)
+ #endif
+ #ifdef AM53C974_DEBUG_INFO
+ #define DEB_INFO(x) x
+ #else
+ #define DEB_INFO(x)
+ #endif
+ #ifdef AM53C974_DEBUG_LINKED
+ #define DEB_LINKED(x) x
+ #else
+ #define DEB_LINKED(x)
+ #endif
+ #ifdef AM53C974_DEBUG_INTR
+ #define DEB_INTR(x) x
+ #else
+ #define DEB_INTR(x)
+ #endif
+#else
+ #define DEB_INIT(x)
+ #define DEB(x)
+ #define DEB_QUEUE(x)
+ #define LIST(x,y)
+ #define REMOVE(w,x,y,z)
+ #define DEB_INFO(x)
+ #define DEB_LINKED(x)
+ #define DEB_INTR(x)
+ #define DEB_MSG(x)
+ #define DEB_RESEL(x)
+ #define KEYWAIT()
+#endif
+ #ifdef AM53C974_DEBUG_ABORT
+ #define DEB_ABORT(x) x
+ #else
+ #define DEB_ABORT(x)
+ #endif
+
+#ifdef VERBOSE_AM53C974_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define INSIDE(x,l,h) ( ((x) >= (l)) && ((x) <= (h)) )
+
+#ifdef AM53C974_DEBUG
+static void AM53C974_print_pci(struct Scsi_Host *instance);
+static void AM53C974_print_phase(struct Scsi_Host *instance);
+static void AM53C974_print_queues(struct Scsi_Host *instance);
+#endif /* AM53C974_DEBUG */
+static void AM53C974_print(struct Scsi_Host *instance);
+static void AM53C974_keywait(void);
+static int AM53C974_bios_detect(Scsi_Host_Template *tpnt);
+static int AM53C974_nobios_detect(Scsi_Host_Template *tpnt);
+static int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config);
+static void AM53C974_config_after_reset(struct Scsi_Host *instance);
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd);
+static __inline__ void run_main(void);
+static void AM53C974_main (void);
+static void AM53C974_intr(int irq, void *dev_id, struct pt_regs *regs);
+static void AM53C974_intr_disconnect(struct Scsi_Host *instance);
+static int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg);
+static __inline__ void AM53C974_set_async(struct Scsi_Host *instance, int target);
+static __inline__ void AM53C974_set_sync(struct Scsi_Host *instance, int target);
+static void AM53C974_information_transfer(struct Scsi_Host *instance,
+ unsigned char statreg, unsigned char isreg,
+ unsigned char instreg, unsigned char cfifo,
+ unsigned char dmastatus);
+static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd, unsigned char msg);
+static void AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag);
+static void AM53C974_intr_reselect(struct Scsi_Host *instance, unsigned char statreg);
+static __inline__ void AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
+ unsigned long length, char *data);
+static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
+ unsigned char statreg);
+static void AM53C974_intr_bus_reset(struct Scsi_Host *instance);
+
+static struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+static struct Scsi_Host *first_host = NULL; /* Head of list of AMD boards */
+static volatile int main_running = 0;
+static int commandline_current = 0;
+override_t overrides[7] = { {-1, 0, 0, 0}, }; /* LILO overrides */
+
+struct proc_dir_entry proc_scsi_am53c974 = {
+ PROC_SCSI_AM53C974, 8, "am53c974",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#ifdef AM53C974_DEBUG
+static int deb_stop = 1;
+
+/**************************************************************************
+ * Function : void AM53C974_print_pci(struct Scsi_Host *instance)
+ *
+ * Purpose : dump the PCI registers for debugging purposes
+ *
+ * Input : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print_pci(struct Scsi_Host *instance)
+{
+int i;
+unsigned short vendor_id, device_id, command, status, scratch[8];
+unsigned long class_revision, base;
+unsigned char irq, cache_line_size, latency_timer, header_type;
+
+AM53C974_PCIREG_OPEN();
+
+for (i = 0; i < 8; i++) *(scratch + i) = AM53C974_PCIREG_READ_WORD(instance, PCI_SCRATCH_REG_0 + 2*i);
+vendor_id = AM53C974_PCIREG_READ_WORD(instance, PCI_VENDOR_ID);
+device_id = AM53C974_PCIREG_READ_WORD(instance, PCI_DEVICE_ID);
+command = AM53C974_PCIREG_READ_WORD(instance, PCI_COMMAND);
+status = AM53C974_PCIREG_READ_WORD(instance, PCI_STATUS);
+class_revision = AM53C974_PCIREG_READ_DWORD(instance, PCI_CLASS_REVISION);
+cache_line_size = AM53C974_PCIREG_READ_BYTE(instance, PCI_CACHE_LINE_SIZE);
+latency_timer = AM53C974_PCIREG_READ_BYTE(instance, PCI_LATENCY_TIMER);
+header_type = AM53C974_PCIREG_READ_BYTE(instance, PCI_HEADER_TYPE);
+base = AM53C974_PCIREG_READ_DWORD(instance, PCI_BASE_ADDRESS_0);
+irq = AM53C974_PCIREG_READ_BYTE(instance, PCI_INTERRUPT_LINE);
+
+AM53C974_PCIREG_CLOSE();
+
+
+printk("------------- start of PCI register dump -------------\n");
+printk("PCI_VENDOR_ID: 0x%x\n", vendor_id);
+printk("PCI_DEVICE_ID: 0x%x\n", device_id);
+printk("PCI_COMMAND: 0x%x\n", command);
+printk("PCI_STATUS: 0x%x\n", status);
+printk("PCI_CLASS_REVISION: 0x%lx\n", class_revision);
+printk("PCI_CACHE_LINE_SIZE: 0x%x\n", cache_line_size);
+printk("PCI_LATENCY_TIMER: 0x%x\n", latency_timer);
+printk("PCI_HEADER_TYPE: 0x%x\n", header_type);
+printk("PCI_BASE_ADDRESS_0: 0x%lx\n", base);
+printk("PCI_INTERRUPT_LINE: %d\n", irq);
+for (i = 0; i < 8; i++) printk("PCI_SCRATCH_%d: 0x%x\n", i, scratch[i]);
+printk("------------- end of PCI register dump -------------\n\n");
+}
+
+static struct {
+ unsigned char value;
+ char *name;
+} phases[] = {
+{PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
+{PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
+{PHASE_RES_0, "RESERVED 0"}, {PHASE_RES_1, "RESERVED 1"}};
+
+/**************************************************************************
+ * Function : void AM53C974_print_phase(struct Scsi_Host *instance)
+ *
+ * Purpose : print the current SCSI phase for debugging purposes
+ *
+ * Input : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print_phase(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+unsigned char statreg, latched;
+int i;
+AM53C974_setio(instance);
+
+latched = (AM53C974_read_8(CNTLREG2)) & CNTLREG2_ENF;
+statreg = AM53C974_read_8(STATREG);
+for (i = 0; (phases[i].value != PHASE_RES_1) &&
+ (phases[i].value != (statreg & STATREG_PHASE)); ++i);
+if (latched)
+ printk("scsi%d : phase %s, latched at end of last command\n", instance->host_no, phases[i].name);
+ else
+ printk("scsi%d : phase %s, real time\n", instance->host_no, phases[i].name);
+}
+
+/**************************************************************************
+ * Function : void AM53C974_print_queues(struct Scsi_Host *instance)
+ *
+ * Purpose : print commands in the various queues
+ *
+ * Inputs : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print_queues(struct Scsi_Host *instance)
+{
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd *ptr;
+
+printk("AM53C974: coroutine is%s running.\n", main_running ? "" : "n't");
+
+cli();
+
+if (!hostdata->connected) {
+ printk ("scsi%d: no currently connected command\n", instance->host_no); }
+ else {
+ print_Scsi_Cmnd ((Scsi_Cmnd *)hostdata->connected); }
+if (!hostdata->sel_cmd) {
+ printk ("scsi%d: no currently arbitrating command\n", instance->host_no); }
+ else {
+ print_Scsi_Cmnd ((Scsi_Cmnd *)hostdata->sel_cmd); }
+
+printk ("scsi%d: issue_queue ", instance->host_no);
+if (!hostdata->issue_queue)
+ printk("empty\n");
+ else {
+ printk(":\n");
+ for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *)ptr->host_scribble)
+ print_Scsi_Cmnd (ptr); }
+
+printk ("scsi%d: disconnected_queue ", instance->host_no);
+if (!hostdata->disconnected_queue)
+ printk("empty\n");
+ else {
+ printk(":\n");
+ for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *)ptr->host_scribble)
+ print_Scsi_Cmnd (ptr); }
+
+sti();
+}
+
+#endif /* AM53C974_DEBUG */
+
+/**************************************************************************
+ * Function : void AM53C974_print(struct Scsi_Host *instance)
+ *
+ * Purpose : dump the chip registers for debugging purposes
+ *
+ * Input : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+unsigned long ctcreg, dmastc, dmaspa, dmawbc, dmawac;
+unsigned char cmdreg, statreg, isreg, cfireg, cntlreg[4], dmacmd, dmastatus;
+AM53C974_setio(instance);
+
+cli();
+ctcreg = AM53C974_read_8(CTCHREG) << 16;
+ctcreg |= AM53C974_read_8(CTCMREG) << 8;
+ctcreg |= AM53C974_read_8(CTCLREG);
+cmdreg = AM53C974_read_8(CMDREG);
+statreg = AM53C974_read_8(STATREG);
+isreg = AM53C974_read_8(ISREG);
+cfireg = AM53C974_read_8(CFIREG);
+cntlreg[0] = AM53C974_read_8(CNTLREG1);
+cntlreg[1] = AM53C974_read_8(CNTLREG2);
+cntlreg[2] = AM53C974_read_8(CNTLREG3);
+cntlreg[3] = AM53C974_read_8(CNTLREG4);
+dmacmd = AM53C974_read_8(DMACMD);
+dmastc = AM53C974_read_32(DMASTC);
+dmaspa = AM53C974_read_32(DMASPA);
+dmawbc = AM53C974_read_32(DMAWBC);
+dmawac = AM53C974_read_32(DMAWAC);
+dmastatus = AM53C974_read_8(DMASTATUS);
+sti();
+
+printk("AM53C974 register dump:\n");
+printk("IO base: 0x%04lx; CTCREG: 0x%04lx; CMDREG: 0x%02x; STATREG: 0x%02x; ISREG: 0x%02x\n",
+ io_port, ctcreg, cmdreg, statreg, isreg);
+printk("CFIREG: 0x%02x; CNTLREG1-4: 0x%02x; 0x%02x; 0x%02x; 0x%02x\n",
+ cfireg, cntlreg[0], cntlreg[1], cntlreg[2], cntlreg[3]);
+printk("DMACMD: 0x%02x; DMASTC: 0x%04lx; DMASPA: 0x%04lx\n", dmacmd, dmastc, dmaspa);
+printk("DMAWBC: 0x%04lx; DMAWAC: 0x%04lx; DMASTATUS: 0x%02x\n", dmawbc, dmawac, dmastatus);
+printk("---------------------------------------------------------\n");
+}
+
+/**************************************************************************
+* Function : void AM53C974_keywait(void)
+*
+* Purpose : wait until a key is pressed, if it was the 'r' key leave singlestep mode;
+* this function is used for debugging only
+*
+* Input : none
+**************************************************************************/
+static void AM53C974_keywait(void)
+{
+#ifdef AM53C974_DEBUG
+int key;
+
+if (!deb_stop) return;
+#endif
+
+cli();
+while ((inb_p(0x64) & 0x01) != 0x01) ;
+#ifdef AM53C974_DEBUG
+key = inb(0x60);
+if (key == 0x93) deb_stop = 0; /* don't stop if 'r' was pressed */
+#endif
+sti();
+}
+
+/**************************************************************************
+* Function : AM53C974_setup(char *str, int *ints)
+*
+* Purpose : LILO command line initialization of the overrides array,
+*
+* Inputs : str - unused, ints - array of integer parameters with ints[0]
+* equal to the number of ints.
+*
+* NOTE : this function needs to be declared as an external function
+* in init/main.c and included there in the bootsetups list
+***************************************************************************/
+void AM53C974_setup(char *str, int *ints)
+{
+if (ints[0] < 4)
+ printk("AM53C974_setup: wrong number of parameters;\n correct syntax is: AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset\n");
+ else {
+ if (commandline_current < (sizeof(overrides) / sizeof(override_t))) {
+ if ((ints[1] < 0) || (ints[1] > 7) ||
+ (ints[2] < 0) || (ints[2] > 7) ||
+ (ints[1] == ints[2]) ||
+ (ints[3] < (DEF_CLK / MAX_PERIOD)) || (ints[3] > (DEF_CLK / MIN_PERIOD)) ||
+ (ints[4] < 0) || (ints[4] > MAX_OFFSET))
+ printk("AM53C974_setup: illegal parameter\n");
+ else {
+ overrides[commandline_current].host_scsi_id = ints[1];
+ overrides[commandline_current].target_scsi_id = ints[2];
+ overrides[commandline_current].max_rate = ints[3];
+ overrides[commandline_current].max_offset = ints[4];
+ commandline_current++; }
+ }
+ else
+ printk("AM53C974_setup: too many overrides\n");
+ }
+}
+
+#if defined (CONFIG_PCI)
+/**************************************************************************
+* Function : int AM53C974_bios_detect(Scsi_Host_Template *tpnt)
+*
+* Purpose : detects and initializes AM53C974 SCSI chips with PCI Bios
+*
+* Inputs : tpnt - host template
+*
+* Returns : number of host adapters detected
+**************************************************************************/
+int AM53C974_bios_detect(Scsi_Host_Template *tpnt)
+{
+int count = 0; /* number of boards detected */
+int pci_index;
+pci_config_t pci_config;
+
+for (pci_index = 0; pci_index <= 16; ++pci_index) {
+ unsigned char pci_bus, pci_device_fn;
+ if (pcibios_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SCSI, pci_index, &pci_bus, &pci_device_fn) != 0)
+ break;
+
+ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &pci_config._vendor);
+ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &pci_config._device);
+ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_config._command);
+ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_STATUS, &pci_config._status);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_CLASS_REVISION, &pci_config._class_revision);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_CACHE_LINE_SIZE, &pci_config._cache_line_size);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_config._latency_timer);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_HEADER_TYPE, &pci_config._header_type);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_BIST, &pci_config._bist);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &pci_config._base0);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &pci_config._base1);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &pci_config._base2);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_3, &pci_config._base3);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_4, &pci_config._base4);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_5, &pci_config._base5);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_ROM_ADDRESS, &pci_config._baserom);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pci_config._int_line);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_PIN, &pci_config._int_pin);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_MIN_GNT, &pci_config._min_gnt);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_MAX_LAT, &pci_config._max_lat);
+ pci_config._pcibus = 0xFFFFFFFF;
+ pci_config._cardnum = 0xFFFFFFFF;
+
+ /* check whether device is I/O mapped -- should be */
+ if (!(pci_config._command & PCI_COMMAND_IO)) continue;
+
+ /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility
+ to set the PCI Master Enable Bit if needed.
+ (from Mark Stockton <marks@schooner.sys.hou.compaq.com>) */
+ if (!(pci_config._command & PCI_COMMAND_MASTER)) {
+ pci_config._command |= PCI_COMMAND_MASTER;
+ printk("PCI Master Bit has not been set. Setting...\n");
+ pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, pci_config._command); }
+
+ /* everything seems OK now, so initialize */
+ if (AM53C974_init(tpnt, pci_config)) count++ ;
+ }
+return (count);
+}
+#endif
+
+/**************************************************************************
+* Function : int AM53C974_nobios_detect(Scsi_Host_Template *tpnt)
+*
+* Purpose : detects and initializes AM53C974 SCSI chips using PCI config 2
+*
+* Inputs : tpnt - host template
+*
+* Returns : number of host adapters detected
+*
+* NOTE : This code assumes the controller on PCI bus 0.
+*
+* Origin: Robin Cutshaw (robin@xfree86.org)
+**************************************************************************/
+int AM53C974_nobios_detect(Scsi_Host_Template *tpnt)
+{
+int count = 0; /* number of boards detected */
+pci_config_t pci_config;
+
+/* first try PCI config method 1 */
+for (pci_config._pcibus = 0; pci_config._pcibus < 0x10; pci_config._pcibus++) {
+ for (pci_config._cardnum = 0; pci_config._cardnum < 0x20; pci_config._cardnum++) {
+ unsigned long config_cmd;
+ config_cmd = 0x80000000 | (pci_config._pcibus<<16) | (pci_config._cardnum<<11);
+
+ outl(config_cmd, 0xCF8); /* ioreg 0 */
+ pci_config._device_vendor = inl(0xCFC);
+
+ if ((pci_config._vendor == PCI_VENDOR_ID_AMD) && (pci_config._device == PCI_DEVICE_ID_AMD_SCSI)) {
+ outl(config_cmd | PCI_COMMAND, 0xCF8); pci_config._status_command = inl(0xCFC);
+ outl(config_cmd | PCI_CLASS_REVISION, 0xCF8); pci_config._class_revision = inl(0xCFC);
+ outl(config_cmd | PCI_CACHE_LINE_SIZE, 0xCF8); pci_config._bist_header_latency_cache = inl(0xCFC);
+ outl(config_cmd | PCI_BASE_ADDRESS_0, 0xCF8); pci_config._base0 = inl(0xCFC);
+ outl(config_cmd | PCI_BASE_ADDRESS_1, 0xCF8); pci_config._base1 = inl(0xCFC);
+ outl(config_cmd | PCI_BASE_ADDRESS_2, 0xCF8); pci_config._base2 = inl(0xCFC);
+ outl(config_cmd | PCI_BASE_ADDRESS_3, 0xCF8); pci_config._base3 = inl(0xCFC);
+ outl(config_cmd | PCI_BASE_ADDRESS_4, 0xCF8); pci_config._base4 = inl(0xCFC);
+ outl(config_cmd | PCI_BASE_ADDRESS_5, 0xCF8); pci_config._base5 = inl(0xCFC);
+ outl(config_cmd | PCI_ROM_ADDRESS, 0xCF8); pci_config._baserom = inl(0xCFC);
+ outl(config_cmd | PCI_INTERRUPT_LINE, 0xCF8); pci_config._max_min_ipin_iline = inl(0xCFC);
+
+ /* check whether device is I/O mapped -- should be */
+ if (!(pci_config._command & PCI_COMMAND_IO)) continue;
+
+ /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility
+ to set the PCI Master Enable Bit if needed.
+ From Mark Stockton <marks@schooner.sys.hou.compaq.com> */
+ if (!(pci_config._command & PCI_COMMAND_MASTER)) {
+ pci_config._command |= PCI_COMMAND_MASTER;
+ printk("Config 1; PCI Master Bit has not been set. Setting...\n");
+ outl(config_cmd | PCI_COMMAND, 0xCF8); outw(pci_config._command, 0xCFC); }
+
+ /* everything seems OK now, so initialize */
+ if (AM53C974_init(tpnt, pci_config)) count++ ;
+ }
+ }
+ }
+outb(0, 0xCF8); /* is this really necessary? */
+
+/* try PCI config method 2, if no device was detected by method 1 */
+if (!count) {
+ AM53C974_PCIREG_OPEN();
+
+ pci_config._pcibus = 0xFFFFFFFF;
+ pci_config._cardnum = 0xFFFFFFFF;
+
+ for (pci_config._ioaddr = 0xC000; pci_config._ioaddr < 0xD000; pci_config._ioaddr += 0x0100) {
+ pci_config._device_vendor = inl(pci_config._ioaddr);
+
+ if ((pci_config._vendor == PCI_VENDOR_ID_AMD) && (pci_config._device == PCI_DEVICE_ID_AMD_SCSI)) {
+ pci_config._status_command = inl(pci_config._ioaddr + PCI_COMMAND);
+ pci_config._class_revision = inl(pci_config._ioaddr + PCI_CLASS_REVISION);
+ pci_config._bist_header_latency_cache = inl(pci_config._ioaddr + PCI_CACHE_LINE_SIZE);
+ pci_config._base0 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_0);
+ pci_config._base1 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_1);
+ pci_config._base2 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_2);
+ pci_config._base3 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_3);
+ pci_config._base4 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_4);
+ pci_config._base5 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_5);
+ pci_config._baserom = inl(pci_config._ioaddr + PCI_ROM_ADDRESS);
+ pci_config._max_min_ipin_iline = inl(pci_config._ioaddr + PCI_INTERRUPT_LINE);
+
+ /* check whether device is I/O mapped -- should be */
+ if (!(pci_config._command & PCI_COMMAND_IO)) continue;
+
+ /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility
+ to set the PCI Master Enable Bit if needed.
+ From Mark Stockton <marks@schooner.sys.hou.compaq.com> */
+ if (!(pci_config._command & PCI_COMMAND_MASTER)) {
+ pci_config._command |= PCI_COMMAND_MASTER;
+ printk("Config 2; PCI Master Bit has not been set. Setting...\n");
+ outw(pci_config._command, pci_config._ioaddr + PCI_COMMAND); }
+
+ /* everything seems OK now, so initialize */
+ if (AM53C974_init(tpnt, pci_config)) count++ ;
+ }
+ }
+ AM53C974_PCIREG_CLOSE();
+ }
+
+return(count);
+}
+
+/**************************************************************************
+* Function : int AM53C974_detect(Scsi_Host_Template *tpnt)
+*
+* Purpose : detects and initializes AM53C974 SCSI chips
+*
+* Inputs : tpnt - host template
+*
+* Returns : number of host adapters detected
+**************************************************************************/
+int AM53C974_detect(Scsi_Host_Template *tpnt)
+{
+int count; /* number of boards detected */
+
+tpnt->proc_dir = &proc_scsi_am53c974;
+
+#if defined (CONFIG_PCI)
+if (pcibios_present())
+ count = AM53C974_bios_detect(tpnt);
+ else
+#endif
+count = AM53C974_nobios_detect(tpnt);
+return (count);
+}
+
+/**************************************************************************
+* Function : int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config)
+*
+* Purpose : initializes instance and corresponding AM53/79C974 chip,
+*
+* Inputs : tpnt - template, pci_config - PCI configuration,
+*
+* Returns : 1 on success, 0 on failure.
+*
+* NOTE: If no override for the controller's SCSI id is given and AM53C974_SCSI_ID
+* is not defined we assume that the SCSI address of this controller is correctly
+* set up by the BIOS (as reflected by contents of register CNTLREG1).
+* This is the only BIOS assistance we need.
+**************************************************************************/
+static int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config)
+{
+AM53C974_local_declare();
+int i, j;
+struct Scsi_Host *instance, *search;
+struct AM53C974_hostdata *hostdata;
+
+#ifdef AM53C974_OPTION_DEBUG_PROBE_ONLY
+ printk ("AM53C974: probe only enabled, aborting initialization\n");
+ return 0;
+#endif
+
+instance = scsi_register(tpnt, sizeof(struct AM53C974_hostdata));
+hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+instance->base = NULL;
+instance->io_port = pci_config._base0 & (pci_config._base0 & 0x1 ?
+ 0xFFFFFFFC : 0xFFFFFFF0);
+instance->irq = pci_config._int_line;
+instance->dma_channel = -1;
+AM53C974_setio(instance);
+
+#ifdef AM53C974_SCSI_ID
+instance->this_id = AM53C974_SCSI_ID;
+AM53C974_write_8(CNTLREG1, instance->this_id & CNTLREG1_SID);
+#else
+instance->this_id = AM53C974_read_8(CNTLREG1) & CNTLREG1_SID;
+if (instance->this_id != 7)
+ printk("scsi%d: WARNING: unusual hostadapter SCSI id %d; please verify!\n",
+ instance->host_no, instance->this_id);
+#endif
+
+for (i = 0; i < sizeof(hostdata->msgout); i++) {
+ hostdata->msgout[i] = NOP;
+ hostdata->last_message[i] = NOP; }
+for (i = 0; i < 8; i++) {
+ hostdata->busy[i] = 0;
+ hostdata->sync_per[i] = DEF_STP;
+ hostdata->sync_off[i] = 0;
+ hostdata->sync_neg[i] = 0;
+ hostdata->sync_en[i] = DEFAULT_SYNC_NEGOTIATION_ENABLED;
+ hostdata->max_rate[i] = DEFAULT_RATE;
+ hostdata->max_offset[i] = DEFAULT_SYNC_OFFSET; }
+
+/* overwrite defaults by LILO overrides */
+for (i = 0; i < commandline_current; i++) {
+ if (overrides[i].host_scsi_id == instance->this_id) {
+ j = overrides[i].target_scsi_id;
+ hostdata->sync_en[j] = 1;
+ hostdata->max_rate[j] = overrides[i].max_rate;
+ hostdata->max_offset[j] = overrides[i].max_offset;
+ }
+ }
+
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->issue_queue = NULL;
+hostdata->disconnected_queue = NULL;
+hostdata->in_reset = 0;
+hostdata->aborted = 0;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+
+/* Set up an interrupt handler if we aren't already sharing an IRQ with another board */
+for (search = first_host;
+ search && ( ((the_template != NULL) && (search->hostt != the_template)) ||
+ (search->irq != instance->irq) || (search == instance) );
+ search = search->next);
+if (!search) {
+ if (request_irq(instance->irq, AM53C974_intr, SA_INTERRUPT, "AM53C974", NULL)) {
+ printk("scsi%d: IRQ%d not free, detaching\n", instance->host_no, instance->irq);
+ scsi_unregister(instance);
+ return 0; }
+ }
+ else {
+ printk("scsi%d: using interrupt handler previously installed for scsi%d\n",
+ instance->host_no, search->host_no); }
+
+if (!the_template) {
+ the_template = instance->hostt;
+ first_instance = instance; }
+
+/* do hard reset */
+AM53C974_write_8(CMDREG, CMDREG_RDEV); /* reset device */
+udelay(5);
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id);
+AM53C974_write_8(CMDREG, CMDREG_RBUS); /* reset SCSI bus */
+udelay(10);
+AM53C974_config_after_reset(instance);
+udelay(500000);
+return(1);
+}
+
+/*********************************************************************
+* Function : AM53C974_config_after_reset(struct Scsi_Host *instance) *
+* *
+* Purpose : initializes chip registers after reset *
+* *
+* Inputs : instance - which AM53C974 *
+* *
+* Returns : nothing *
+**********************************************************************/
+static void AM53C974_config_after_reset(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+AM53C974_setio(instance);
+
+/* clear SCSI FIFO */
+AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+
+/* configure device */
+AM53C974_write_8(STIMREG, DEF_SCSI_TIMEOUT);
+AM53C974_write_8(STPREG, DEF_STP & STPREG_STP);
+AM53C974_write_8(SOFREG, (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
+AM53C974_write_8(CLKFREG, DEF_CLKF & CLKFREG_MASK);
+AM53C974_write_8(CNTLREG1, (DEF_ETM<<7) | CNTLREG1_DISR | (DEF_PERE<<4) | instance->this_id);
+AM53C974_write_8(CNTLREG2, (DEF_ENF<<6));
+AM53C974_write_8(CNTLREG3, (DEF_ADIDCHK<<7) | (DEF_FASTSCSI<<4) | (DEF_FASTCLK<<3));
+AM53C974_write_8(CNTLREG4, (DEF_GLITCH<<6) | (DEF_PWD<<5) | (DEF_RAE<<3) | (DEF_RADE<<2) | CNTLREG4_RES);
+}
+
+/***********************************************************************
+* Function : const char *AM53C974_info(struct Scsi_Host *instance) *
+* *
+* Purpose : return device driver information *
+* *
+* Inputs : instance - which AM53C974 *
+* *
+* Returns : info string *
+************************************************************************/
+const char *AM53C974_info(struct Scsi_Host *instance)
+{
+static char info[100];
+
+sprintf(info, "AM53/79C974 PCscsi driver rev. %d.%d; host I/O address: 0x%x; irq: %d\n",
+ AM53C974_DRIVER_REVISION_MAJOR, AM53C974_DRIVER_REVISION_MINOR,
+ instance->io_port, instance->irq);
+return (info);
+}
+
+/**************************************************************************
+* Function : int AM53C974_command (Scsi_Cmnd *SCpnt) *
+* *
+* Purpose : the unqueued SCSI command function, replaced by the *
+* AM53C974_queue_command function *
+* *
+* Inputs : SCpnt - pointer to command structure *
+* *
+* Returns :status, see hosts.h for details *
+***************************************************************************/
+int AM53C974_command(Scsi_Cmnd *SCpnt)
+{
+DEB(printk("AM53C974_command called\n"));
+return 0;
+}
+
+/**************************************************************************
+* Function : void initialize_SCp(Scsi_Cmnd *cmd) *
+* *
+* Purpose : initialize the saved data pointers for cmd to point to the *
+* start of the buffer. *
+* *
+* Inputs : cmd - Scsi_Cmnd structure to have pointers reset. *
+* *
+* Returns : nothing *
+**************************************************************************/
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd)
+{
+if (cmd->use_sg) {
+ cmd->SCp.buffer = (struct scatterlist *)cmd->buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.ptr = (char *)cmd->SCp.buffer->address;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length; }
+ else {
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *)cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen; }
+}
+
+/**************************************************************************
+* Function : run_main(void) *
+* *
+* Purpose : insure that the coroutine is running and will process our *
+* request. main_running is checked/set here (in an inline *
+* function rather than in AM53C974_main itself to reduce the *
+* chances of stack overflow. *
+* *
+* *
+* Inputs : none *
+* *
+* Returns : nothing *
+**************************************************************************/
+static __inline__ void run_main(void)
+{
+cli();
+if (!main_running) {
+ /* main_running is cleared in AM53C974_main once it can't do
+ more work, and AM53C974_main exits with interrupts disabled. */
+ main_running = 1;
+ AM53C974_main();
+ sti(); }
+ else
+ sti();
+}
+
+/**************************************************************************
+* Function : int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+*
+* Purpose : writes SCSI command into AM53C974 FIFO
+*
+* Inputs : cmd - SCSI command, done - function called on completion, with
+* a pointer to the command descriptor.
+*
+* Returns : status, see hosts.h for details
+*
+* Side effects :
+* cmd is added to the per instance issue_queue, with minor
+* twiddling done to the host specific fields of cmd. If the
+* main coroutine is not running, it is restarted.
+**************************************************************************/
+int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+struct Scsi_Host *instance = cmd->host;
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd *tmp;
+
+cli();
+DEB_QUEUE(printk(SEPARATOR_LINE));
+DEB_QUEUE(printk("scsi%d: AM53C974_queue_command called\n", instance->host_no));
+DEB_QUEUE(printk("cmd=%02x target=%02x lun=%02x bufflen=%d use_sg = %02x\n",
+ cmd->cmnd[0], cmd->target, cmd->lun, cmd->request_bufflen, cmd->use_sg));
+
+/* We use the host_scribble field as a pointer to the next command in a queue */
+cmd->host_scribble = NULL;
+cmd->scsi_done = done;
+cmd->result = 0;
+cmd->device->disconnect = 0;
+
+/* Insert the cmd into the issue queue. Note that REQUEST SENSE
+ * commands are added to the head of the queue since any command will
+ * clear the contingent allegiance condition that exists and the
+ * sense data is only guaranteed to be valid while the condition exists. */
+if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ LIST(cmd, hostdata->issue_queue);
+ cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+ hostdata->issue_queue = cmd; }
+ else {
+ for (tmp = (Scsi_Cmnd *)hostdata->issue_queue; tmp->host_scribble;
+ tmp = (Scsi_Cmnd *)tmp->host_scribble);
+ LIST(cmd, tmp);
+ tmp->host_scribble = (unsigned char *)cmd; }
+
+DEB_QUEUE(printk("scsi%d : command added to %s of queue\n", instance->host_no,
+ (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"));
+
+/* Run the coroutine if it isn't already running. */
+run_main();
+return 0;
+}
+
+/**************************************************************************
+ * Function : AM53C974_main (void)
+ *
+ * Purpose : AM53C974_main is a coroutine that runs as long as more work can
+ * be done on the AM53C974 host adapters in a system. Both
+ * AM53C974_queue_command() and AM53C974_intr() will try to start it
+ * in case it is not running.
+ *
+ * NOTE : AM53C974_main exits with interrupts *disabled*, the caller should
+ * reenable them. This prevents reentrancy and kernel stack overflow.
+ **************************************************************************/
+static void AM53C974_main(void)
+{
+AM53C974_local_declare();
+Scsi_Cmnd *tmp, *prev;
+struct Scsi_Host *instance;
+struct AM53C974_hostdata *hostdata;
+int done;
+
+/* We run (with interrupts disabled) until we're sure that none of
+ * the host adapters have anything that can be done, at which point
+ * we set main_running to 0 and exit. */
+
+do {
+ cli(); /* Freeze request queues */
+ done = 1;
+ for (instance = first_instance; instance && instance->hostt == the_template;
+ instance = instance->next) {
+ hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+ AM53C974_setio(instance);
+ /* start to select target if we are not connected and not in the
+ selection process */
+ if (!hostdata->connected && !hostdata->sel_cmd) {
+ /* Search through the issue_queue for a command destined for a target
+ that is not busy. */
+ for (tmp = (Scsi_Cmnd *)hostdata->issue_queue, prev = NULL; tmp;
+ prev = tmp, tmp = (Scsi_Cmnd *)tmp->host_scribble) {
+ /* When we find one, remove it from the issue queue. */
+ if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
+ if (prev) {
+ REMOVE(prev, (Scsi_Cmnd *)(prev->host_scribble), tmp,
+ (Scsi_Cmnd *)(tmp->host_scribble));
+ prev->host_scribble = tmp->host_scribble; }
+ else {
+ REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble);
+ hostdata->issue_queue = (Scsi_Cmnd *)tmp->host_scribble; }
+ tmp->host_scribble = NULL;
+
+ /* go into selection mode, disable reselection and wait for
+ SO interrupt which will continue with the selection process */
+ hostdata->selecting = 1;
+ hostdata->sel_cmd = tmp;
+ AM53C974_write_8(CMDREG, CMDREG_DSR);
+ break;
+ } /* if target/lun is not busy */
+
+ } /* for */
+ } /* if (!hostdata->connected) */
+ else {
+ DEB(printk("main: connected; cmd = 0x%lx, sel_cmd = 0x%lx\n",
+ (long)hostdata->connected, (long)hostdata->sel_cmd));
+ }
+ } /* for instance */
+ } while (!done);
+main_running = 0;
+}
+
+/************************************************************************
+* Function : AM53C974_intr(int irq, void *dev_id, struct pt_regs *regs) *
+* *
+* Purpose : interrupt handler *
+* *
+* Inputs : irq - interrupt line, regs - ? *
+* *
+* Returns : nothing *
+************************************************************************/
+static void AM53C974_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+AM53C974_local_declare();
+struct Scsi_Host *instance;
+struct AM53C974_hostdata *hostdata;
+unsigned char cmdreg, dmastatus, statreg, isreg, instreg, cfifo;
+
+/* find AM53C974 hostadapter responsible for this interrupt */
+for (instance = first_instance; instance; instance = instance->next)
+ if ((instance->irq == irq) && (instance->hostt == the_template)) goto FOUND;
+sti();
+return;
+
+/* found; now decode and process */
+FOUND:
+hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+dmastatus = AM53C974_read_8(DMASTATUS);
+
+DEB_INTR(printk(SEPARATOR_LINE));
+DEB_INTR(printk("AM53C974 interrupt; dmastatus=0x%02x\n", dmastatus));
+KEYWAIT();
+
+/*** DMA related interrupts ***/
+if (hostdata->connected && (dmastatus & (DMASTATUS_ERROR | DMASTATUS_PWDN |
+ DMASTATUS_ABORT))) {
+ /* DMA error or POWERDOWN */
+ printk("scsi%d: DMA error or powerdown; dmastatus: 0x%02x\n",
+ instance->host_no, dmastatus);
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ panic("scsi%d: cannot recover\n", instance->host_no); }
+
+if (hostdata->connected && (dmastatus & DMASTATUS_DONE)) {
+ /* DMA transfer done */
+ unsigned long residual;
+ cli();
+ if (!(AM53C974_read_8(DMACMD) & DMACMD_DIR)) {
+ do {
+ dmastatus = AM53C974_read_8(DMASTATUS);
+ residual = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+ (AM53C974_read_8(CTCHREG) << 16);
+ residual += AM53C974_read_8(CFIREG) & CFIREG_CF;
+ } while (!(dmastatus & DMASTATUS_SCSIINT) && residual);
+ residual = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+ (AM53C974_read_8(CTCHREG) << 16);
+ residual += AM53C974_read_8(CFIREG) & CFIREG_CF;
+ }
+ else
+ residual = 0;
+ hostdata->connected->SCp.ptr += hostdata->connected->SCp.this_residual - residual;
+ hostdata->connected->SCp.this_residual = residual;
+
+ AM53C974_write_8(DMACMD, DMACMD_IDLE);
+
+ /* if service request missed before, process it now (ugly) */
+ if (hostdata->dma_busy) {
+ hostdata->dma_busy = 0;
+ cmdreg = AM53C974_read_8(CMDREG);
+ statreg = AM53C974_read_8(STATREG);
+ isreg = AM53C974_read_8(ISREG);
+ instreg = AM53C974_read_8(INSTREG);
+ cfifo = AM53C974_cfifo();
+ AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo,
+ dmastatus); }
+ sti();
+ }
+
+if (!(dmastatus & DMASTATUS_SCSIINT)) {
+ sti();
+ return; }
+
+/*** SCSI related interrupts ***/
+cmdreg = AM53C974_read_8(CMDREG);
+statreg = AM53C974_read_8(STATREG);
+isreg = AM53C974_read_8(ISREG);
+instreg = AM53C974_read_8(INSTREG);
+cfifo = AM53C974_cfifo();
+
+DEB_INTR(printk("scsi%d: statreg: 0x%02x; isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
+ instance->host_no, statreg, isreg, instreg, cfifo));
+
+if (statreg & STATREG_PE) {
+ /* parity error */
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ printk("scsi%d : PARITY error\n", instance->host_no);
+ if (hostdata->connected) hostdata->sync_off[hostdata->connected->target] = 0; /* setup asynchronous transfer */
+ hostdata->aborted = 1; }
+
+if (statreg & STATREG_IOE) {
+ /* illegal operation error */
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ printk("scsi%d : ILLEGAL OPERATION error\n", instance->host_no);
+ printk("cmdreg: 0x%02x; dmacmd: 0x%02x; statreg: 0x%02x; \n"
+ "isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
+ cmdreg, AM53C974_read_8(DMACMD), statreg, isreg, instreg, cfifo); }
+if (hostdata->in_reset && (instreg & INSTREG_SRST)) {
+ /* RESET INTERRUPT */
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ DEB(printk("Bus reset interrupt received\n"));
+ AM53C974_intr_bus_reset(instance);
+ cli();
+ if (hostdata->connected) {
+ hostdata->connected->result = DID_RESET << 16;
+ hostdata->connected->scsi_done((Scsi_Cmnd *)hostdata->connected);
+ hostdata->connected = NULL; }
+ else {
+ if (hostdata->sel_cmd) {
+ hostdata->sel_cmd->result = DID_RESET << 16;
+ hostdata->sel_cmd->scsi_done((Scsi_Cmnd *)hostdata->sel_cmd);
+ hostdata->sel_cmd = NULL; }
+ }
+ sti();
+ if (hostdata->in_reset == 1) goto EXIT;
+ else return;
+ }
+
+if (instreg & INSTREG_ICMD) {
+ /* INVALID COMMAND INTERRUPT */
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ printk("scsi%d: Invalid command interrupt\n", instance->host_no);
+ printk("cmdreg: 0x%02x; dmacmd: 0x%02x; statreg: 0x%02x; dmastatus: 0x%02x; \n"
+ "isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
+ cmdreg, AM53C974_read_8(DMACMD), statreg, dmastatus, isreg, instreg, cfifo);
+ panic("scsi%d: cannot recover\n", instance->host_no); }
+
+if (instreg & INSTREG_DIS) {
+ /* DISCONNECT INTERRUPT */
+ DEB_INTR(printk("Disconnect interrupt received; "));
+ cli();
+ AM53C974_intr_disconnect(instance);
+ sti();
+ goto EXIT; }
+
+if (instreg & INSTREG_RESEL) {
+ /* RESELECTION INTERRUPT */
+ DEB_INTR(printk("Reselection interrupt received\n"));
+ cli();
+ AM53C974_intr_reselect(instance, statreg);
+ sti();
+ goto EXIT; }
+
+if (instreg & INSTREG_SO) {
+ DEB_INTR(printk("Successful operation interrupt received\n"));
+ if (hostdata->selecting) {
+ DEB_INTR(printk("DSR completed, starting select\n"));
+ cli();
+ AM53C974_select(instance, (Scsi_Cmnd *)hostdata->sel_cmd,
+ (hostdata->sel_cmd->cmnd[0] == REQUEST_SENSE) ?
+ TAG_NONE : TAG_NEXT);
+ hostdata->selecting = 0;
+ AM53C974_set_sync(instance, hostdata->sel_cmd->target);
+ sti();
+ return; }
+
+ if (hostdata->sel_cmd != NULL) {
+ if ( ((isreg & ISREG_IS) != ISREG_OK_NO_STOP) &&
+ ((isreg & ISREG_IS) != ISREG_OK_STOP) ) {
+ /* UNSUCCESSFUL SELECTION */
+ DEB_INTR(printk("unsuccessful selection\n"));
+ cli();
+ hostdata->dma_busy = 0;
+ LIST(hostdata->sel_cmd, hostdata->issue_queue);
+ hostdata->sel_cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+ hostdata->issue_queue = hostdata->sel_cmd;
+ hostdata->sel_cmd = NULL;
+ hostdata->selecting = 0;
+ sti();
+ goto EXIT; }
+ else {
+ /* SUCCESSFUL SELECTION */
+ DEB(printk("successful selection; cmd=0x%02lx\n", (long)hostdata->sel_cmd));
+ cli();
+ hostdata->dma_busy = 0;
+ hostdata->disconnecting = 0;
+ hostdata->connected = hostdata->sel_cmd;
+ hostdata->sel_cmd = NULL;
+ hostdata->selecting = 0;
+#ifdef SCSI2
+ if (!hostdata->connected->device->tagged_queue)
+#endif
+ hostdata->busy[hostdata->connected->target] |= (1 << hostdata->connected->lun);
+ /* very strange -- use_sg is sometimes nonzero for request sense commands !! */
+ if ((hostdata->connected->cmnd[0] == REQUEST_SENSE) && hostdata->connected->use_sg) {
+ DEB(printk("scsi%d: REQUEST_SENSE command with nonzero use_sg\n", instance->host_no));
+ KEYWAIT();
+ hostdata->connected->use_sg = 0; }
+ initialize_SCp((Scsi_Cmnd *)hostdata->connected);
+ hostdata->connected->SCp.phase = PHASE_CMDOUT;
+ AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+ sti();
+ return; }
+ }
+ else {
+ cli();
+ AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+ sti();
+ return; }
+ }
+
+if (instreg & INSTREG_SR) {
+ DEB_INTR(printk("Service request interrupt received, "));
+ if (hostdata->connected) {
+ DEB_INTR(printk("calling information_transfer\n"));
+ cli();
+ AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+ sti(); }
+ else {
+ printk("scsi%d: weird: service request when no command connected\n", instance->host_no);
+ AM53C974_write_8(CMDREG, CMDREG_CFIFO); } /* clear FIFO */
+ return;
+ }
+
+EXIT:
+ DEB_INTR(printk("intr: starting main\n"));
+ run_main();
+ DEB_INTR(printk("end of intr\n"));
+}
+
+/**************************************************************************
+* Function : AM53C974_intr_disconnect(struct Scsi_Host *instance)
+*
+* Purpose : manage target disconnection
+*
+* Inputs : instance -- which AM53C974
+*
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_intr_disconnect(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd *cmd;
+AM53C974_setio(instance);
+
+if (hostdata->sel_cmd != NULL) {
+ /* normal selection timeout, typical for nonexisting targets */
+ cmd = (Scsi_Cmnd *)hostdata->sel_cmd;
+ DEB_INTR(printk("bad target\n"));
+ cmd->result = DID_BAD_TARGET << 16;
+ goto EXIT_FINISHED; }
+
+if (!hostdata->connected) {
+ /* can happen if controller was reset, a device tried to reconnect,
+ failed and disconnects now */
+ AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+ return; }
+
+if (hostdata->disconnecting) {
+ /* target sent disconnect message, so we are prepared */
+ cmd = (Scsi_Cmnd *)hostdata->connected;
+ AM53C974_set_async(instance, cmd->target);
+ DEB_INTR(printk("scsi%d : disc. from cmnd %d for ta %d, lun %d\n",
+ instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+ if (cmd->device->disconnect) {
+ /* target wants to reselect later */
+ DEB_INTR(printk("ok, re-enabling selection\n"));
+ LIST(cmd,hostdata->disconnected_queue);
+ cmd->host_scribble = (unsigned char *)hostdata->disconnected_queue;
+ hostdata->disconnected_queue = cmd;
+ DEB_QUEUE(printk("scsi%d : command for target %d lun %d this %d was moved from connected to"
+ " the disconnected_queue\n", instance->host_no, cmd->target,
+ cmd->lun, hostdata->disconnected_queue->SCp.this_residual));
+ DEB_QUEUE(AM53C974_print_queues(instance));
+ goto EXIT_UNFINISHED; }
+ else {
+ /* target does not want to reselect later, we are really finished */
+#ifdef AM53C974_DEBUG
+ if (cmd->cmnd[0] == REQUEST_SENSE) {
+ int i;
+ printk("Request sense data dump:\n");
+ for (i = 0; i < cmd->request_bufflen; i++) {
+ printk("%02x ", *((char *)(cmd->request_buffer) + i));
+ if (i && !(i % 16)) printk("\n"); }
+ printk("\n"); }
+#endif
+ goto EXIT_FINISHED; } /* !cmd->device->disconnect */
+ } /* if (hostdata->disconnecting) */
+
+/* no disconnect message received; unexpected disconnection */
+cmd = (Scsi_Cmnd *)hostdata->connected;
+if (cmd) {
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ AM53C974_set_async(instance, cmd->target);
+ printk("scsi%d: Unexpected disconnect; phase: %d; target: %d; this_residual: %d; buffers_residual: %d; message: %d\n",
+ instance->host_no, cmd->SCp.phase, cmd->target, cmd->SCp.this_residual, cmd->SCp.buffers_residual,
+ cmd->SCp.Message);
+ printk("cmdreg: 0x%02x; statreg: 0x%02x; isreg: 0x%02x; cfifo: 0x%02x\n",
+ AM53C974_read_8(CMDREG), AM53C974_read_8(STATREG), AM53C974_read_8(ISREG),
+ AM53C974_read_8(CFIREG) & CFIREG_CF);
+
+ if ((hostdata->last_message[0] == EXTENDED_MESSAGE) &&
+ (hostdata->last_message[2] == EXTENDED_SDTR)) {
+ /* sync. negotiation was aborted, setup asynchronous transfer with target */
+ hostdata->sync_off[cmd->target] = 0; }
+ if (hostdata->aborted || hostdata->msgout[0] == ABORT)
+ cmd->result = DID_ABORT << 16;
+ else
+ cmd->result = DID_ERROR << 16;
+ goto EXIT_FINISHED; }
+
+EXIT_FINISHED:
+hostdata->aborted = 0;
+hostdata->msgout[0] = NOP;
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+DEB(printk("disconnect; issue_queue: 0x%lx, disconnected_queue: 0x%lx\n",
+ (long)hostdata->issue_queue, (long)hostdata->disconnected_queue));
+cmd->scsi_done(cmd);
+
+if (!hostdata->selecting) {
+ AM53C974_set_async(instance, cmd->target);
+ AM53C974_write_8(CMDREG, CMDREG_ESR); } /* allow reselect */
+return;
+
+EXIT_UNFINISHED:
+hostdata->msgout[0] = NOP;
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->aborted = 0;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+DEB(printk("disconnect; issue_queue: 0x%lx, disconnected_queue: 0x%lx\n",
+ (long)hostdata->issue_queue, (long)hostdata->disconnected_queue));
+if (!hostdata->selecting) {
+ AM53C974_set_async(instance, cmd->target);
+ AM53C974_write_8(CMDREG, CMDREG_ESR); } /* allow reselect */
+return;
+}
+
+/**************************************************************************
+* Function : int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg)
+*
+* Purpose : setup message string for sync. negotiation
+*
+* Inputs : instance -- which AM53C974
+* target -- which SCSI target to deal with
+* msg -- input message string
+*
+* Returns : 0 if parameters accepted or 1 if not accepted
+*
+* Side effects: hostdata is changed
+*
+* Note: we assume here that fastclk is enabled
+**************************************************************************/
+static int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+int period, offset, i, rate, rate_rem;
+AM53C974_setio(instance);
+
+period = (DEF_CLK * msg[3] * 8 + 1000) / 2000;
+if (period < MIN_PERIOD) {
+ period = MIN_PERIOD;
+ hostdata->msgout[3] = period / 4; }
+ else
+ if (period > MAX_PERIOD) {
+ period = MAX_PERIOD;
+ hostdata->msgout[3] = period / 4; }
+ else
+ hostdata->msgout[3] = msg[3];
+offset = msg[4];
+if (offset > MAX_OFFSET) offset = MAX_OFFSET;
+hostdata->msgout[4] = offset;
+hostdata->sync_per[target] = period;
+hostdata->sync_off[target] = offset;
+for (i = 0; i < 3; i++) hostdata->msgout[i] = msg[i];
+if ((hostdata->msgout[3] != msg[3]) || (msg[4] != offset)) return(1);
+
+rate = DEF_CLK / period;
+rate_rem = 10 * (DEF_CLK - period * rate) / period;
+
+if (offset)
+ printk("\ntarget %d: rate=%d.%d Mhz, synchronous, sync offset=%d bytes\n",
+ target, rate, rate_rem, offset);
+ else
+ printk("\ntarget %d: rate=%d.%d Mhz, asynchronous\n", target, rate, rate_rem);
+
+return(0);
+}
+
+/**************************************************************************
+* Function : AM53C974_set_async(struct Scsi_Host *instance, int target)
+*
+* Purpose : put controller into async. mode
+*
+* Inputs : instance -- which AM53C974
+* target -- which SCSI target to deal with
+*
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_set_async(struct Scsi_Host *instance, int target)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+AM53C974_write_8(STPREG, hostdata->sync_per[target]);
+AM53C974_write_8(SOFREG, (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
+}
+
+/**************************************************************************
+* Function : AM53C974_set_sync(struct Scsi_Host *instance, int target)
+*
+* Purpose : put controller into sync. mode
+*
+* Inputs : instance -- which AM53C974
+* target -- which SCSI target to deal with
+*
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_set_sync(struct Scsi_Host *instance, int target)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+AM53C974_write_8(STPREG, hostdata->sync_per[target]);
+AM53C974_write_8(SOFREG, (SOFREG_SO & hostdata->sync_off[target]) |
+ (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
+}
+
+/***********************************************************************
+* Function : AM53C974_information_transfer(struct Scsi_Host *instance, *
+* unsigned char statreg, unsigned char isreg, *
+* unsigned char instreg, unsigned char cfifo, *
+* unsigned char dmastatus) *
+* *
+* Purpose : handle phase changes *
+* *
+* Inputs : instance - which AM53C974 *
+* statreg - status register *
+* isreg - internal state register *
+* instreg - interrupt status register *
+* cfifo - number of bytes in FIFO *
+* dmastatus - dma status register *
+* *
+* Returns : nothing *
+************************************************************************/
+static void AM53C974_information_transfer(struct Scsi_Host *instance,
+ unsigned char statreg, unsigned char isreg,
+ unsigned char instreg, unsigned char cfifo,
+ unsigned char dmastatus)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd *cmd = (Scsi_Cmnd *)hostdata->connected;
+int ret, i, len, residual=-1;
+AM53C974_setio(instance);
+
+DEB_INFO(printk(SEPARATOR_LINE));
+switch (statreg & STATREG_PHASE) { /* scsi phase */
+ case PHASE_DATAOUT:
+ DEB_INFO(printk("Dataout phase; cmd=0x%lx, sel_cmd=0x%lx, this_residual=%d, buffers_residual=%d\n",
+ (long)hostdata->connected, (long)hostdata->sel_cmd, cmd->SCp.this_residual, cmd->SCp.buffers_residual));
+ cmd->SCp.phase = PHASE_DATAOUT;
+ goto PHASE_DATA_IO;
+
+ case PHASE_DATAIN:
+ DEB_INFO(printk("Datain phase; cmd=0x%lx, sel_cmd=0x%lx, this_residual=%d, buffers_residual=%d\n",
+ (long)hostdata->connected, (long)hostdata->sel_cmd, cmd->SCp.this_residual, cmd->SCp.buffers_residual));
+ cmd->SCp.phase = PHASE_DATAIN;
+ PHASE_DATA_IO:
+ if (hostdata->aborted) {
+ AM53C974_write_8(DMACMD, DMACMD_IDLE);
+ AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+ AM53C974_write_8(CMDREG, CMDREG_SATN);
+ return; }
+ if ((!cmd->SCp.this_residual) && cmd->SCp.buffers_residual) {
+ cmd->SCp.buffer++;
+ cmd->SCp.buffers_residual--;
+ cmd->SCp.ptr = (unsigned char *)cmd->SCp.buffer->address;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length; }
+ if (cmd->SCp.this_residual) {
+ if (!(AM53C974_read_8(DMACMD) & DMACMD_START)) {
+ hostdata->dma_busy = 0;
+ AM53C974_transfer_dma(instance, statreg & STATREG_IO,
+ (unsigned long)cmd->SCp.this_residual,
+ cmd->SCp.ptr); }
+ else
+ hostdata->dma_busy = 1;
+ }
+ return;
+
+ case PHASE_MSGIN:
+ DEB_INFO(printk("Message-In phase; cmd=0x%lx, sel_cmd=0x%lx\n",
+ (long)hostdata->connected, (long)hostdata->sel_cmd));
+ AM53C974_set_async(instance, cmd->target);
+ if (cmd->SCp.phase == PHASE_DATAIN)
+ AM53C974_dma_blast(instance, dmastatus, statreg);
+ if ((cmd->SCp.phase == PHASE_DATAOUT) && (AM53C974_read_8(DMACMD) & DMACMD_START)) {
+ AM53C974_write_8(DMACMD, DMACMD_IDLE);
+ residual = cfifo + (AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+ (AM53C974_read_8(CTCHREG) << 16));
+ cmd->SCp.ptr += cmd->SCp.this_residual - residual;
+ cmd->SCp.this_residual = residual;
+ if (cfifo) { AM53C974_write_8(CMDREG, CMDREG_CFIFO); cfifo = 0; }
+ }
+ if (cmd->SCp.phase == PHASE_STATIN) {
+ while ((AM53C974_read_8(CFIREG) & CFIREG_CF) < 2) ;
+ cmd->SCp.Status = AM53C974_read_8(FFREG);
+ cmd->SCp.Message = AM53C974_read_8(FFREG);
+ DEB_INFO(printk("Message-In phase; status=0x%02x, message=0x%02x\n",
+ cmd->SCp.Status, cmd->SCp.Message));
+ ret = AM53C974_message(instance, cmd, cmd->SCp.Message); }
+ else {
+ if (!cfifo) {
+ AM53C974_write_8(CMDREG, CMDREG_IT);
+ AM53C974_poll_int();
+ cmd->SCp.Message = AM53C974_read_8(FFREG);
+ }
+ ret = AM53C974_message(instance, cmd, cmd->SCp.Message);
+ }
+ cmd->SCp.phase = PHASE_MSGIN;
+ AM53C974_set_sync(instance, cmd->target);
+ break;
+ case PHASE_MSGOUT:
+ DEB_INFO(printk("Message-Out phase; cfifo=%d; msgout[0]=0x%02x\n",
+ AM53C974_read_8(CFIREG) & CFIREG_CF, hostdata->msgout[0]));
+ AM53C974_write_8(DMACMD, DMACMD_IDLE);
+ AM53C974_set_async(instance, cmd->target);
+ for (i = 0; i < sizeof(hostdata->last_message); i++)
+ hostdata->last_message[i] = hostdata->msgout[i];
+ if ((hostdata->msgout[0] == 0) || INSIDE(hostdata->msgout[0], 0x02, 0x1F) ||
+ INSIDE(hostdata->msgout[0], 0x80, 0xFF))
+ len = 1;
+ else {
+ if (hostdata->msgout[0] == EXTENDED_MESSAGE) {
+#ifdef AM53C974_DEBUG_INFO
+ printk("Extended message dump:\n");
+ for (i = 0; i < hostdata->msgout[1] + 2; i++) {
+ printk("%02x ", hostdata->msgout[i]);
+ if (i && !(i % 16)) printk("\n"); }
+ printk("\n");
+#endif
+ len = hostdata->msgout[1] + 2; }
+ else
+ len = 2;
+ }
+ for (i = 0; i < len; i++) AM53C974_write_8(FFREG, hostdata->msgout[i]);
+ AM53C974_write_8(CMDREG, CMDREG_IT);
+ cmd->SCp.phase = PHASE_MSGOUT;
+ hostdata->msgout[0] = NOP;
+ AM53C974_set_sync(instance, cmd->target);
+ break;
+
+ case PHASE_CMDOUT:
+ DEB_INFO(printk("Command-Out phase\n"));
+ AM53C974_set_async(instance, cmd->target);
+ for (i = 0; i < cmd->cmd_len; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]);
+ AM53C974_write_8(CMDREG, CMDREG_IT);
+ cmd->SCp.phase = PHASE_CMDOUT;
+ AM53C974_set_sync(instance, cmd->target);
+ break;
+
+ case PHASE_STATIN:
+ DEB_INFO(printk("Status phase\n"));
+ if (cmd->SCp.phase == PHASE_DATAIN)
+ AM53C974_dma_blast(instance, dmastatus, statreg);
+ AM53C974_set_async(instance, cmd->target);
+ if (cmd->SCp.phase == PHASE_DATAOUT) {
+ unsigned long residual;
+
+ if (AM53C974_read_8(DMACMD) & DMACMD_START) {
+ AM53C974_write_8(DMACMD, DMACMD_IDLE);
+ residual = cfifo + (AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+ (AM53C974_read_8(CTCHREG) << 16));
+ cmd->SCp.ptr += cmd->SCp.this_residual - residual;
+ cmd->SCp.this_residual = residual; }
+ if (cfifo) { AM53C974_write_8(CMDREG, CMDREG_CFIFO); cfifo = 0; }
+ }
+ cmd->SCp.phase = PHASE_STATIN;
+ AM53C974_write_8(CMDREG, CMDREG_ICCS); /* command complete */
+ break;
+
+ case PHASE_RES_0:
+ case PHASE_RES_1:
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ DEB_INFO(printk("Reserved phase\n"));
+ break;
+ }
+KEYWAIT();
+}
+
+/******************************************************************************
+* Function : int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+* unsigned char msg)
+*
+* Purpose : handle SCSI messages
+*
+* Inputs : instance -- which AM53C974
+* cmd -- SCSI command the message belongs to
+* msg -- message id byte
+*
+* Returns : 1 on success, 0 on failure.
+**************************************************************************/
+static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+ unsigned char msg)
+{
+AM53C974_local_declare();
+static unsigned char extended_msg[10];
+unsigned char statreg;
+int len, ret = 0;
+unsigned char *p;
+#ifdef AM53C974_DEBUG_MSG
+int j;
+#endif
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+DEB_MSG(printk(SEPARATOR_LINE));
+
+/* Linking lets us reduce the time required to get the
+ * next command out to the device, hopefully this will
+ * mean we don't waste another revolution due to the delays
+ * required by ARBITRATION and another SELECTION.
+ * In the current implementation proposal, low level drivers
+ * merely have to start the next command, pointed to by
+ * next_link, done() is called as with unlinked commands. */
+switch (msg) {
+#ifdef LINKED
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+ /* Accept message by releasing ACK */
+ DEB_LINKED(printk("scsi%d : target %d lun %d linked command complete.\n",
+ instance->host_no, cmd->target, cmd->lun));
+ /* Sanity check : A linked command should only terminate with
+ * one of these messages if there are more linked commands available. */
+ if (!cmd->next_link) {
+ printk("scsi%d : target %d lun %d linked command complete, no next_link\n"
+ instance->host_no, cmd->target, cmd->lun);
+ hostdata->aborted = 1;
+ AM53C974_write_8(CMDREG, CMDREG_SATN);
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ break; }
+ if (hostdata->aborted) {
+ DEB_ABORT(printk("ATN set for cmnd %d upon reception of LINKED_CMD_COMPLETE or"
+ "LINKED_FLG_CMD_COMPLETE message\n", cmd->cmnd[0]));
+ AM53C974_write_8(CMDREG, CMDREG_SATN); }
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+
+ initialize_SCp(cmd->next_link);
+ /* The next command is still part of this process */
+ cmd->next_link->tag = cmd->tag;
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ DEB_LINKED(printk("scsi%d : target %d lun %d linked request done, calling scsi_done().\n",
+ instance->host_no, cmd->target, cmd->lun));
+ cmd->scsi_done(cmd);
+ cmd = hostdata->connected;
+ break;
+
+#endif /* def LINKED */
+
+ case ABORT:
+ case COMMAND_COMPLETE:
+ DEB_MSG(printk("scsi%d: command complete message received; cmd %d for target %d, lun %d\n",
+ instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+ hostdata->disconnecting = 1;
+ cmd->device->disconnect = 0;
+
+ /* I'm not sure what the correct thing to do here is :
+ *
+ * If the command that just executed is NOT a request
+ * sense, the obvious thing to do is to set the result
+ * code to the values of the stored parameters.
+ * If it was a REQUEST SENSE command, we need some way
+ * to differentiate between the failure code of the original
+ * and the failure code of the REQUEST sense - the obvious
+ * case is success, where we fall through and leave the result
+ * code unchanged.
+ *
+ * The non-obvious place is where the REQUEST SENSE failed */
+ if (cmd->cmnd[0] != REQUEST_SENSE)
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ else if (cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ if (hostdata->aborted) {
+ AM53C974_write_8(CMDREG, CMDREG_SATN);
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ DEB_ABORT(printk("ATN set for cmnd %d upon reception of ABORT or"
+ "COMMAND_COMPLETE message\n", cmd->cmnd[0]));
+ break; }
+ if ((cmd->cmnd[0] != REQUEST_SENSE) && (cmd->SCp.Status == CHECK_CONDITION)) {
+ DEB_MSG(printk("scsi%d : performing request sense\n", instance->host_no));
+ cmd->cmnd[0] = REQUEST_SENSE;
+ cmd->cmnd[1] &= 0xe0;
+ cmd->cmnd[2] = 0;
+ cmd->cmnd[3] = 0;
+ cmd->cmnd[4] = sizeof(cmd->sense_buffer);
+ cmd->cmnd[5] = 0;
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *)cmd->sense_buffer;
+ cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
+ LIST(cmd,hostdata->issue_queue);
+ cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+ hostdata->issue_queue = (Scsi_Cmnd *)cmd;
+ DEB_MSG(printk("scsi%d : REQUEST SENSE added to head of issue queue\n",instance->host_no));
+ }
+
+ /* Accept message by clearing ACK */
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ break;
+
+ case MESSAGE_REJECT:
+ DEB_MSG(printk("scsi%d: reject message received; cmd %d for target %d, lun %d\n",
+ instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+ switch (hostdata->last_message[0]) {
+ case EXTENDED_MESSAGE:
+ if (hostdata->last_message[2] == EXTENDED_SDTR) {
+ /* sync. negotiation was rejected, setup asynchronous transfer with target */
+ printk("\ntarget %d: rate=%d Mhz, asynchronous (sync. negotiation rejected)\n",
+ cmd->target, DEF_CLK / DEF_STP);
+ hostdata->sync_off[cmd->target] = 0;
+ hostdata->sync_per[cmd->target] = DEF_STP; }
+ break;
+ case HEAD_OF_QUEUE_TAG:
+ case ORDERED_QUEUE_TAG:
+ case SIMPLE_QUEUE_TAG:
+ cmd->device->tagged_queue = 0;
+ hostdata->busy[cmd->target] |= (1 << cmd->lun);
+ break;
+ default:
+ break;
+ }
+ if (hostdata->aborted) AM53C974_write_8(CMDREG, CMDREG_SATN);
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ break;
+
+ case DISCONNECT:
+ DEB_MSG(printk("scsi%d: disconnect message received; cmd %d for target %d, lun %d\n",
+ instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+ cmd->device->disconnect = 1;
+ hostdata->disconnecting = 1;
+ AM53C974_write_8(CMDREG, CMDREG_MA); /* Accept message by clearing ACK */
+ break;
+
+ case SAVE_POINTERS:
+ case RESTORE_POINTERS:
+ DEB_MSG(printk("scsi%d: save/restore pointers message received; cmd %d for target %d, lun %d\n",
+ instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+ /* The SCSI data pointer is *IMPLICITLY* saved on a disconnect
+ * operation, in violation of the SCSI spec so we can safely
+ * ignore SAVE/RESTORE pointers calls.
+ *
+ * Unfortunately, some disks violate the SCSI spec and
+ * don't issue the required SAVE_POINTERS message before
+ * disconnecting, and we have to break spec to remain
+ * compatible. */
+ if (hostdata->aborted) {
+ DEB_ABORT(printk("ATN set for cmnd %d upon reception of SAVE/REST. POINTERS message\n",
+ cmd->cmnd[0]));
+ AM53C974_write_8(CMDREG, CMDREG_SATN); }
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ break;
+
+ case EXTENDED_MESSAGE:
+ DEB_MSG(printk("scsi%d: extended message received; cmd %d for target %d, lun %d\n",
+ instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+ /* Extended messages are sent in the following format :
+ * Byte
+ * 0 EXTENDED_MESSAGE == 1
+ * 1 length (includes one byte for code, doesn't include first two bytes)
+ * 2 code
+ * 3..length+1 arguments
+ */
+ /* BEWARE!! THIS CODE IS EXTREMELY UGLY */
+ extended_msg[0] = EXTENDED_MESSAGE;
+ AM53C974_read_8(INSTREG) ; /* clear int */
+ AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */
+ AM53C974_poll_int();
+ /* get length */
+ AM53C974_write_8(CMDREG, CMDREG_IT);
+ AM53C974_poll_int();
+ AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */
+ AM53C974_poll_int();
+ extended_msg[1] = len = AM53C974_read_8(FFREG); /* get length */
+ p = extended_msg+2;
+ /* read the remaining (len) bytes */
+ while (len) {
+ AM53C974_write_8(CMDREG, CMDREG_IT);
+ AM53C974_poll_int();
+ if (len > 1) {
+ AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */
+ AM53C974_poll_int(); }
+ *p = AM53C974_read_8(FFREG);
+ p++; len--; }
+
+#ifdef AM53C974_DEBUG_MSG
+ printk("scsi%d: received extended message: ", instance->host_no);
+ for (j = 0; j < extended_msg[1] + 2; j++) {
+ printk("0x%02x ", extended_msg[j]);
+ if (j && !(j % 16)) printk("\n"); }
+ printk("\n");
+#endif
+
+ /* check message */
+ if (extended_msg[2] == EXTENDED_SDTR)
+ ret = AM53C974_sync_neg(instance, cmd->target, extended_msg);
+ if (ret || hostdata->aborted) AM53C974_write_8(CMDREG, CMDREG_SATN);
+
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ break;
+
+ default:
+ printk("scsi%d: unknown message 0x%02x received\n",instance->host_no, msg);
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ /* reject message */
+ hostdata->msgout[0] = MESSAGE_REJECT;
+ AM53C974_write_8(CMDREG, CMDREG_SATN);
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ return(0);
+ break;
+
+ } /* switch (msg) */
+KEYWAIT();
+return(1);
+}
+
+/**************************************************************************
+* Function : AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+*
+* Purpose : try to establish nexus for the command;
+* start sync negotiation via start stop and transfer the command in
+* cmdout phase in case of an inquiry or req. sense command with no
+* sync. neg. performed yet
+*
+* Inputs : instance -- which AM53C974
+* cmd -- command which requires the selection
+* tag -- tagged queueing
+*
+* Returns : nothing
+*
+* Note: this function initializes the selection process, which is continued
+* in the interrupt handler
+**************************************************************************/
+static void AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+unsigned char cfifo, tmp[3];
+unsigned int i, len, cmd_size = COMMAND_SIZE(cmd->cmnd[0]);
+AM53C974_setio(instance);
+
+cfifo = AM53C974_cfifo();
+if (cfifo) {
+ printk("scsi%d: select error; %d residual bytes in FIFO\n", instance->host_no, cfifo);
+ AM53C974_write_8(CMDREG, CMDREG_CFIFO); /* clear FIFO */
+ }
+
+tmp[0] = IDENTIFY(1, cmd->lun);
+
+#ifdef SCSI2
+if (cmd->device->tagged_queue && (tag != TAG_NONE)) {
+ tmp[1] = SIMPLE_QUEUE_TAG;
+ if (tag == TAG_NEXT) {
+ /* 0 is TAG_NONE, used to imply no tag for this command */
+ if (cmd->device->current_tag == 0) cmd->device->current_tag = 1;
+ cmd->tag = cmd->device->current_tag;
+ cmd->device->current_tag++; }
+ else
+ cmd->tag = (unsigned char)tag;
+ tmp[2] = cmd->tag;
+ hostdata->last_message[0] = SIMPLE_QUEUE_TAG;
+ len = 3;
+ AM53C974_write_8(FFREG, tmp[0]);
+ AM53C974_write_8(FFREG, tmp[1]);
+ AM53C974_write_8(FFREG, tmp[2]);
+ }
+ else
+#endif /* def SCSI2 */
+ {
+ len = 1;
+ AM53C974_write_8(FFREG, tmp[0]);
+ cmd->tag = 0; }
+
+/* in case of an inquiry or req. sense command with no sync. neg performed yet, we start
+ sync negotiation via start stops and transfer the command in cmdout phase */
+if (((cmd->cmnd[0] == INQUIRY) || (cmd->cmnd[0] == REQUEST_SENSE)) &&
+ !(hostdata->sync_neg[cmd->target]) && hostdata->sync_en[cmd->target]) {
+ hostdata->sync_neg[cmd->target] = 1;
+ hostdata->msgout[0] = EXTENDED_MESSAGE;
+ hostdata->msgout[1] = 3;
+ hostdata->msgout[2] = EXTENDED_SDTR;
+ hostdata->msgout[3] = 250 / (int)hostdata->max_rate[cmd->target];
+ hostdata->msgout[4] = hostdata->max_offset[cmd->target];
+ len += 5; }
+
+AM53C974_write_8(SDIDREG, SDIREG_MASK & cmd->target); /* setup dest. id */
+AM53C974_write_8(STIMREG, DEF_SCSI_TIMEOUT); /* setup timeout reg */
+switch (len) {
+ case 1:
+ for (i = 0; i < cmd_size; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]);
+ AM53C974_write_8(CMDREG, CMDREG_SAS); /* select with ATN, 1 msg byte */
+ hostdata->msgout[0] = NOP;
+ break;
+ case 3:
+ for (i = 0; i < cmd_size; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]);
+ AM53C974_write_8(CMDREG, CMDREG_SA3S); /* select with ATN, 3 msg bytes */
+ hostdata->msgout[0] = NOP;
+ break;
+ default:
+ AM53C974_write_8(CMDREG, CMDREG_SASS); /* select with ATN, stop steps; continue in message out phase */
+ break;
+ }
+}
+
+/**************************************************************************
+* Function : AM53C974_intr_select(struct Scsi_Host *instance, unsigned char statreg)
+*
+* Purpose : handle reselection
+*
+* Inputs : instance -- which AM53C974
+* statreg -- status register
+*
+* Returns : nothing
+*
+* side effects: manipulates hostdata
+**************************************************************************/
+static void AM53C974_intr_reselect(struct Scsi_Host *instance, unsigned char statreg)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+unsigned char cfifo, msg[3], lun, t, target = 0;
+#ifdef SCSI2
+ unsigned char tag;
+#endif
+Scsi_Cmnd *tmp = NULL, *prev;
+AM53C974_setio(instance);
+
+cfifo = AM53C974_cfifo();
+
+if (hostdata->selecting) {
+ /* caught reselect interrupt in selection process;
+ put selecting command back into the issue queue and continue with the
+ reselecting command */
+ DEB_RESEL(printk("AM53C974_intr_reselect: in selection process\n"));
+ LIST(hostdata->sel_cmd, hostdata->issue_queue);
+ hostdata->sel_cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+ hostdata->issue_queue = hostdata->sel_cmd;
+ hostdata->sel_cmd = NULL;
+ hostdata->selecting = 0; }
+
+/* 2 bytes must be in the FIFO now */
+if (cfifo != 2) {
+ printk("scsi %d: error: %d bytes in fifo, 2 expected\n", instance->host_no, cfifo);
+ hostdata->aborted = 1;
+ goto EXIT_ABORT; }
+
+/* determine target which reselected */
+t = AM53C974_read_8(FFREG);
+if (!(t & (1 << instance->this_id))) {
+ printk("scsi %d: error: invalid host id\n", instance->host_no);
+ hostdata->aborted = 1;
+ goto EXIT_ABORT; }
+t ^= (1 << instance->this_id);
+target = 0; while (t != 1) { t >>= 1; target++; }
+DEB_RESEL(printk("scsi %d: reselect; target: %d\n", instance->host_no, target));
+
+if (hostdata->aborted) goto EXIT_ABORT;
+
+if ((statreg & STATREG_PHASE) != PHASE_MSGIN) {
+ printk("scsi %d: error: upon reselection interrupt not in MSGIN\n", instance->host_no);
+ hostdata->aborted = 1;
+ goto EXIT_ABORT; }
+
+msg[0] = AM53C974_read_8(FFREG);
+if (!msg[0] & 0x80) {
+ printk("scsi%d: error: expecting IDENTIFY message, got ", instance->host_no);
+ print_msg(msg);
+ hostdata->aborted = 1;
+ goto EXIT_ABORT; }
+
+lun = (msg[0] & 0x07);
+
+/* We need to add code for SCSI-II to track which devices have
+ * I_T_L_Q nexuses established, and which have simple I_T_L
+ * nexuses so we can chose to do additional data transfer. */
+#ifdef SCSI2
+#error "SCSI-II tagged queueing is not supported yet"
+#endif
+
+/* Find the command corresponding to the I_T_L or I_T_L_Q nexus we
+ * just reestablished, and remove it from the disconnected queue. */
+for (tmp = (Scsi_Cmnd *)hostdata->disconnected_queue, prev = NULL;
+ tmp; prev = tmp, tmp = (Scsi_Cmnd *)tmp->host_scribble)
+ if ((target == tmp->target) && (lun == tmp->lun)
+#ifdef SCSI2
+ && (tag == tmp->tag)
+#endif
+ ) {
+ if (prev) {
+ REMOVE(prev, (Scsi_Cmnd *)(prev->host_scribble), tmp,
+ (Scsi_Cmnd *)(tmp->host_scribble));
+ prev->host_scribble = tmp->host_scribble; }
+ else {
+ REMOVE(-1, hostdata->disconnected_queue, tmp, tmp->host_scribble);
+ hostdata->disconnected_queue = (Scsi_Cmnd *)tmp->host_scribble; }
+ tmp->host_scribble = NULL;
+ hostdata->connected = tmp;
+ break; }
+
+if (!tmp) {
+#ifdef SCSI2
+ printk("scsi%d: warning : target %d lun %d tag %d not in disconnect_queue.\n",
+ instance->host_no, target, lun, tag);
+#else
+ printk("scsi%d: warning : target %d lun %d not in disconnect_queue.\n",
+ instance->host_no, target, lun);
+#endif
+ /* Since we have an established nexus that we can't do anything with, we must abort it. */
+ hostdata->aborted = 1;
+ DEB(AM53C974_keywait());
+ goto EXIT_ABORT; }
+ else
+ goto EXIT_OK;
+
+EXIT_ABORT:
+AM53C974_write_8(CMDREG, CMDREG_SATN);
+AM53C974_write_8(CMDREG, CMDREG_MA);
+return;
+
+EXIT_OK:
+DEB_RESEL(printk("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
+ instance->host_no, target, tmp->lun, tmp->tag));
+AM53C974_set_sync(instance, target);
+AM53C974_write_8(SDIDREG, SDIREG_MASK & target); /* setup dest. id */
+AM53C974_write_8(CMDREG, CMDREG_MA);
+hostdata->dma_busy = 0;
+hostdata->connected->SCp.phase = PHASE_CMDOUT;
+}
+
+/**************************************************************************
+* Function : AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
+* unsigned long length, char *data)
+*
+* Purpose : setup DMA transfer
+*
+* Inputs : instance -- which AM53C974
+* dir -- direction flag, 0: write to device, read from memory;
+* 1: read from device, write to memory
+* length -- number of bytes to transfer to from buffer
+* data -- pointer to data buffer
+*
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
+ unsigned long length, char *data)
+{
+AM53C974_local_declare();
+AM53C974_setio(instance);
+
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D); /* idle command */
+AM53C974_write_8(STCLREG, (unsigned char)(length & 0xff));
+AM53C974_write_8(STCMREG, (unsigned char)((length & 0xff00) >> 8));
+AM53C974_write_8(STCHREG, (unsigned char)((length & 0xff0000) >> 16));
+AM53C974_write_32(DMASTC, length & 0xffffff);
+AM53C974_write_32(DMASPA, virt_to_bus(data));
+AM53C974_write_8(CMDREG, CMDREG_IT | CMDREG_DMA);
+AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D | DMACMD_START);
+}
+
+/**************************************************************************
+* Function : AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
+* unsigned char statreg)
+*
+* Purpose : cleanup DMA transfer
+*
+* Inputs : instance -- which AM53C974
+* dmastatus -- dma status register
+* statreg -- status register
+*
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
+ unsigned char statreg)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+unsigned long ctcreg;
+int dir = statreg & STATREG_IO;
+int cfifo, pio, i = 0;
+AM53C974_setio(instance);
+
+do {
+ cfifo = AM53C974_cfifo();
+ i++;
+ } while (cfifo && (i < 50000));
+pio = (i == 50000) ? 1: 0;
+
+if (statreg & STATREG_CTZ) { AM53C974_write_8(DMACMD, DMACMD_IDLE); return; }
+
+if (dmastatus & DMASTATUS_DONE) { AM53C974_write_8(DMACMD, DMACMD_IDLE); return; }
+
+AM53C974_write_8(DMACMD, ((dir << 7) & DMACMD_DIR) | DMACMD_BLAST);
+while(!(AM53C974_read_8(DMASTATUS) & DMASTATUS_BCMPLT)) ;
+AM53C974_write_8(DMACMD, DMACMD_IDLE);
+
+if (pio) {
+ /* transfer residual bytes via PIO */
+ unsigned char *wac = (unsigned char *)AM53C974_read_32(DMAWAC);
+ printk("pio mode, residual=%d\n", AM53C974_read_8(CFIREG) & CFIREG_CF);
+ while (AM53C974_read_8(CFIREG) & CFIREG_CF) *(wac++) = AM53C974_read_8(FFREG);
+ }
+
+ctcreg = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+ (AM53C974_read_8(CTCHREG) << 16);
+
+hostdata->connected->SCp.ptr += hostdata->connected->SCp.this_residual - ctcreg;
+hostdata->connected->SCp.this_residual = ctcreg;
+}
+
+/**************************************************************************
+* Function : AM53C974_intr_bus_reset(struct Scsi_Host *instance)
+*
+* Purpose : handle bus reset interrupt
+*
+* Inputs : instance -- which AM53C974
+*
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_intr_bus_reset(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+unsigned char cntlreg1;
+AM53C974_setio(instance);
+
+AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+
+cntlreg1 = AM53C974_read_8(CNTLREG1);
+AM53C974_write_8(CNTLREG1, cntlreg1 | CNTLREG1_DISR);
+}
+
+/**************************************************************************
+* Function : int AM53C974_abort(Scsi_Cmnd *cmd)
+*
+* Purpose : abort a command
+*
+* Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
+* host byte of the result field to, if zero DID_ABORTED is
+* used.
+*
+* Returns : 0 - success, -1 on failure.
+ **************************************************************************/
+int AM53C974_abort(Scsi_Cmnd *cmd)
+{
+AM53C974_local_declare();
+struct Scsi_Host *instance = cmd->host;
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd *tmp, **prev;
+
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+cli();
+AM53C974_setio(instance);
+
+DEB_ABORT(printk(SEPARATOR_LINE));
+DEB_ABORT(printk("scsi%d : AM53C974_abort called -- trouble starts!!\n", instance->host_no));
+DEB_ABORT(AM53C974_print(instance));
+DEB_ABORT(AM53C974_keywait());
+
+/* Case 1 : If the command is the currently executing command,
+ we'll set the aborted flag and return control so that the
+ information transfer routine can exit cleanly. */
+if ((hostdata->connected == cmd) || (hostdata->sel_cmd == cmd)) {
+ DEB_ABORT(printk("scsi%d: aborting connected command\n", instance->host_no));
+ hostdata->aborted = 1;
+ hostdata->msgout[0] = ABORT;
+ sti();
+ return(SCSI_ABORT_PENDING); }
+
+/* Case 2 : If the command hasn't been issued yet,
+ we simply remove it from the issue queue. */
+for (prev = (Scsi_Cmnd **)&(hostdata->issue_queue),
+ tmp = (Scsi_Cmnd *)hostdata->issue_queue; tmp;
+ prev = (Scsi_Cmnd **)&(tmp->host_scribble),
+ tmp = (Scsi_Cmnd *)tmp->host_scribble) {
+ if (cmd == tmp) {
+ DEB_ABORT(printk("scsi%d : abort removed command from issue queue.\n", instance->host_no));
+ REMOVE(5, *prev, tmp, tmp->host_scribble);
+ (*prev) = (Scsi_Cmnd *)tmp->host_scribble;
+ tmp->host_scribble = NULL;
+ tmp->result = DID_ABORT << 16;
+ sti();
+ tmp->done(tmp);
+ return(SCSI_ABORT_SUCCESS); }
+#ifdef AM53C974_DEBUG_ABORT
+ else {
+ if (prev == (Scsi_Cmnd **)tmp)
+ printk("scsi%d : LOOP\n", instance->host_no);
+ }
+#endif
+ }
+
+/* Case 3 : If any commands are connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or
+ * issue a reset.
+ *
+ * Timeouts, and therefore aborted commands, will be highly unlikely
+ * and handling them cleanly in this situation would make the common
+ * case of noresets less efficient, and would pollute our code. So,
+ * we fail. */
+if (hostdata->connected || hostdata->sel_cmd) {
+ DEB_ABORT(printk("scsi%d : abort failed, other command connected.\n", instance->host_no));
+ sti();
+ return(SCSI_ABORT_NOT_RUNNING); }
+
+/* Case 4: If the command is currently disconnected from the bus, and
+ * there are no connected commands, we reconnect the I_T_L or
+ * I_T_L_Q nexus associated with it, go into message out, and send
+ * an abort message. */
+for (tmp = (Scsi_Cmnd *)hostdata->disconnected_queue; tmp;
+ tmp = (Scsi_Cmnd *)tmp->host_scribble) {
+ if (cmd == tmp) {
+ DEB_ABORT(printk("scsi%d: aborting disconnected command\n", instance->host_no));
+ hostdata->aborted = 1;
+ hostdata->msgout[0] = ABORT;
+ hostdata->selecting = 1;
+ hostdata->sel_cmd = tmp;
+ AM53C974_write_8(CMDREG, CMDREG_DSR);
+ sti();
+ return(SCSI_ABORT_PENDING); }
+ }
+
+/* Case 5 : If we reached this point, the command was not found in any of
+ * the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke. */
+DEB_ABORT(printk("scsi%d : abort failed, command not found.\n", instance->host_no));
+sti();
+return(SCSI_ABORT_NOT_RUNNING);
+}
+
+/**************************************************************************
+* Function : int AM53C974_reset(Scsi_Cmnd *cmd)
+*
+* Purpose : reset the SCSI controller and bus
+*
+* Inputs : cmd -- which command within the command block was responsible for the reset
+*
+* Returns : status (SCSI_ABORT_SUCCESS)
+**************************************************************************/
+int AM53C974_reset(Scsi_Cmnd *cmd, unsigned int flags)
+{
+AM53C974_local_declare();
+int i;
+struct Scsi_Host *instance = cmd->host;
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+cli();
+DEB(printk("AM53C974_reset called; "));
+
+printk("AM53C974_reset called\n");
+AM53C974_print(instance);
+AM53C974_keywait();
+
+/* do hard reset */
+AM53C974_write_8(CMDREG, CMDREG_RDEV);
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+hostdata->msgout[0] = NOP;
+for (i = 0; i < 8; i++) {
+ hostdata->busy[i] = 0;
+ hostdata->sync_per[i] = DEF_STP;
+ hostdata->sync_off[i] = 0;
+ hostdata->sync_neg[i] = 0; }
+hostdata->last_message[0] = NOP;
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->issue_queue = NULL;
+hostdata->disconnected_queue = NULL;
+hostdata->in_reset = 0;
+hostdata->aborted = 0;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+
+/* reset bus */
+AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); /* disable interrupt upon SCSI RESET */
+AM53C974_write_8(CMDREG, CMDREG_RBUS); /* reset SCSI bus */
+udelay(40);
+AM53C974_config_after_reset(instance);
+
+sti();
+cmd->result = DID_RESET << 16;
+cmd->scsi_done(cmd);
+return SCSI_ABORT_SUCCESS;
+}
+
+
+/*
+ * AM53C974_release()
+ *
+ * Release resources allocated for a single AM53C974 adapter.
+ */
+int
+AM53C974_release(struct Scsi_Host *shp)
+{
+ free_irq(shp->irq, NULL);
+ scsi_unregister(shp);
+ return 0;
+}
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = AM53C974;
+
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/AM53C974.h b/linux/src/drivers/scsi/AM53C974.h
new file mode 100644
index 00000000..ab51d294
--- /dev/null
+++ b/linux/src/drivers/scsi/AM53C974.h
@@ -0,0 +1,413 @@
+/* AM53/79C974 (PCscsi) driver release 0.5
+ *
+ * The architecture and much of the code of this device
+ * driver was originally developed by Drew Eckhardt for
+ * the NCR5380. The following copyrights apply:
+ * For the architecture and all parts similar to the NCR5380:
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * The AM53C974_nobios_detect code was originally developed by
+ * Robin Cutshaw (robin@xfree86.org) and is used here in a
+ * modified form.
+ *
+ * For the other parts:
+ * Copyright 1994, D. Frieauff
+ * EMail: fri@rsx42sun0.dofn.de
+ * Phone: x49-7545-8-2256 , x49-7541-42305
+ */
+
+/*
+ * $Log: AM53C974.h,v $
+ */
+
+#ifndef AM53C974_H
+#define AM53C974_H
+
+#include <scsi/scsicam.h>
+
+/***************************************************************************************
+* Default setting of the controller's SCSI id. Edit and uncomment this only if your *
+* BIOS does not correctly initialize the controller's SCSI id. *
+* If you don't get a warning during boot, it is correctly initialized. *
+****************************************************************************************/
+/* #define AM53C974_SCSI_ID 7 */
+
+/***************************************************************************************
+* Default settings for sync. negotiation enable, transfer rate and sync. offset. *
+* These settings can be replaced by LILO overrides (append) with the following syntax: *
+* AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset *
+* Sync. negotiation is disabled by default and will be enabled for those targets which *
+* are specified in the LILO override *
+****************************************************************************************/
+#define DEFAULT_SYNC_NEGOTIATION_ENABLED 0 /* 0 or 1 */
+#define DEFAULT_RATE 5 /* MHz, min: 3; max: 10 */
+#define DEFAULT_SYNC_OFFSET 0 /* bytes, min: 0; max: 15; use 0 for async. mode */
+
+
+/* --------------------- don't edit below here --------------------- */
+
+#define AM53C974_DRIVER_REVISION_MAJOR 0
+#define AM53C974_DRIVER_REVISION_MINOR 5
+#define SEPARATOR_LINE \
+"--------------------------------------------------------------------------\n"
+
+/* debug control */
+/* #define AM53C974_DEBUG */
+/* #define AM53C974_DEBUG_MSG */
+/* #define AM53C974_DEBUG_KEYWAIT */
+/* #define AM53C974_DEBUG_INIT */
+/* #define AM53C974_DEBUG_QUEUE */
+/* #define AM53C974_DEBUG_INFO */
+/* #define AM53C974_DEBUG_LINKED */
+/* #define VERBOSE_AM53C974_DEBUG */
+/* #define AM53C974_DEBUG_INTR */
+/* #define AM53C974_DEB_RESEL */
+#define AM53C974_DEBUG_ABORT
+/* #define AM53C974_OPTION_DEBUG_PROBE_ONLY */
+
+/* special options/constants */
+#define DEF_CLK 40 /* chip clock freq. in MHz */
+#define MIN_PERIOD 4 /* for negotiation: min. number of clocks per cycle */
+#define MAX_PERIOD 13 /* for negotiation: max. number of clocks per cycle */
+#define MAX_OFFSET 15 /* for negotiation: max. offset (0=async) */
+
+#define DEF_SCSI_TIMEOUT 245 /* STIMREG value, 40 Mhz */
+#define DEF_STP 8 /* STPREG value assuming 5.0 MB/sec, FASTCLK, FASTSCSI */
+#define DEF_SOF_RAD 0 /* REQ/ACK deassertion delay */
+#define DEF_SOF_RAA 0 /* REQ/ACK assertion delay */
+#define DEF_ETM 0 /* CNTLREG1, ext. timing mode */
+#define DEF_PERE 1 /* CNTLREG1, parity error reporting */
+#define DEF_CLKF 0 /* CLKFREG, 0=40 Mhz */
+#define DEF_ENF 1 /* CNTLREG2, enable features */
+#define DEF_ADIDCHK 0 /* CNTLREG3, additional ID check */
+#define DEF_FASTSCSI 1 /* CNTLREG3, fast SCSI */
+#define DEF_FASTCLK 1 /* CNTLREG3, fast clocking, 5 MB/sec at 40MHz chip clk */
+#define DEF_GLITCH 1 /* CNTLREG4, glitch eater, 0=12ns, 1=35ns, 2=25ns, 3=off */
+#define DEF_PWD 0 /* CNTLREG4, reduced power feature */
+#define DEF_RAE 0 /* CNTLREG4, RAE active negation on REQ, ACK only */
+#define DEF_RADE 1 /* 1CNTLREG4, active negation on REQ, ACK and data */
+
+/*** PCI block ***/
+/* standard registers are defined in <linux/pci.h> */
+#ifndef PCI_VENDOR_ID_AMD
+#define PCI_VENDOR_ID_AMD 0x1022
+#define PCI_DEVICE_ID_AMD_SCSI 0x2020
+#endif
+#define PCI_BASE_MASK 0xFFFFFFE0
+#define PCI_COMMAND_PERREN 0x40
+#define PCI_SCRATCH_REG_0 0x40 /* 16 bits */
+#define PCI_SCRATCH_REG_1 0x42 /* 16 bits */
+#define PCI_SCRATCH_REG_2 0x44 /* 16 bits */
+#define PCI_SCRATCH_REG_3 0x46 /* 16 bits */
+#define PCI_SCRATCH_REG_4 0x48 /* 16 bits */
+#define PCI_SCRATCH_REG_5 0x4A /* 16 bits */
+#define PCI_SCRATCH_REG_6 0x4C /* 16 bits */
+#define PCI_SCRATCH_REG_7 0x4E /* 16 bits */
+
+/*** SCSI block ***/
+#define CTCLREG 0x00 /* r current transf. count, low byte */
+#define CTCMREG 0x04 /* r current transf. count, middle byte */
+#define CTCHREG 0x38 /* r current transf. count, high byte */
+#define STCLREG 0x00 /* w start transf. count, low byte */
+#define STCMREG 0x04 /* w start transf. count, middle byte */
+#define STCHREG 0x38 /* w start transf. count, high byte */
+#define FFREG 0x08 /* rw SCSI FIFO reg. */
+#define STIMREG 0x14 /* w SCSI timeout reg. */
+
+#define SDIDREG 0x10 /* w SCSI destination ID reg. */
+#define SDIREG_MASK 0x07 /* mask */
+
+#define STPREG 0x18 /* w synchronous transf. period reg. */
+#define STPREG_STP 0x1F /* synchr. transfer period */
+
+#define CLKFREG 0x24 /* w clock factor reg. */
+#define CLKFREG_MASK 0x07 /* mask */
+
+#define CMDREG 0x0C /* rw SCSI command reg. */
+#define CMDREG_DMA 0x80 /* set DMA mode (set together with opcodes below) */
+#define CMDREG_IT 0x10 /* information transfer */
+#define CMDREG_ICCS 0x11 /* initiator command complete steps */
+#define CMDREG_MA 0x12 /* message accepted */
+#define CMDREG_TPB 0x98 /* transfer pad bytes, DMA mode only */
+#define CMDREG_SATN 0x1A /* set ATN */
+#define CMDREG_RATN 0x1B /* reset ATN */
+#define CMDREG_SOAS 0x41 /* select without ATN steps */
+#define CMDREG_SAS 0x42 /* select with ATN steps (1 msg byte) */
+#define CMDREG_SASS 0x43 /* select with ATN and stop steps */
+#define CMDREG_ESR 0x44 /* enable selection/reselection */
+#define CMDREG_DSR 0x45 /* disable selection/reselection */
+#define CMDREG_SA3S 0x46 /* select with ATN 3 steps (3 msg bytes) */
+#define CMDREG_NOP 0x00 /* no operation */
+#define CMDREG_CFIFO 0x01 /* clear FIFO */
+#define CMDREG_RDEV 0x02 /* reset device */
+#define CMDREG_RBUS 0x03 /* reset SCSI bus */
+
+#define STATREG 0x10 /* r SCSI status reg. */
+#define STATREG_INT 0x80 /* SCSI interrupt condition detected */
+#define STATREG_IOE 0x40 /* SCSI illegal operation error detected */
+#define STATREG_PE 0x20 /* SCSI parity error detected */
+#define STATREG_CTZ 0x10 /* CTC reg decremented to zero */
+#define STATREG_MSG 0x04 /* SCSI MSG phase (latched?) */
+#define STATREG_CD 0x02 /* SCSI C/D phase (latched?) */
+#define STATREG_IO 0x01 /* SCSI I/O phase (latched?) */
+#define STATREG_PHASE 0x07 /* SCSI phase mask */
+
+#define INSTREG 0x14 /* r interrupt status reg. */
+#define INSTREG_SRST 0x80 /* SCSI reset detected */
+#define INSTREG_ICMD 0x40 /* SCSI invalid command detected */
+#define INSTREG_DIS 0x20 /* target disconnected or sel/resel timeout*/
+#define INSTREG_SR 0x10 /* device on bus has service request */
+#define INSTREG_SO 0x08 /* successful operation */
+#define INSTREG_RESEL 0x04 /* device reselected as initiator */
+
+#define ISREG 0x18 /* r internal state reg. */
+#define ISREG_SOF 0x08 /* synchronous offset flag (act. low) */
+#define ISREG_IS 0x07 /* status of intermediate op. */
+#define ISREG_OK_NO_STOP 0x04 /* selection successful */
+#define ISREG_OK_STOP 0x01 /* selection successful */
+
+#define CFIREG 0x1C /* r current FIFO/internal state reg. */
+#define CFIREG_IS 0xE0 /* status of intermediate op. */
+#define CFIREG_CF 0x1F /* number of bytes in SCSI FIFO */
+
+#define SOFREG 0x1C /* w synchr. offset reg. */
+#define SOFREG_RAD 0xC0 /* REQ/ACK deassertion delay (sync.) */
+#define SOFREG_RAA 0x30 /* REQ/ACK assertion delay (sync.) */
+#define SOFREG_SO 0x0F /* synch. offset (sync.) */
+
+#define CNTLREG1 0x20 /* rw control register one */
+#define CNTLREG1_ETM 0x80 /* set extended timing mode */
+#define CNTLREG1_DISR 0x40 /* disable interrupt on SCSI reset */
+#define CNTLREG1_PERE 0x10 /* enable parity error reporting */
+#define CNTLREG1_SID 0x07 /* host adapter SCSI ID */
+
+#define CNTLREG2 0x2C /* rw control register two */
+#define CNTLREG2_ENF 0x40 /* enable features */
+
+#define CNTLREG3 0x30 /* rw control register three */
+#define CNTLREG3_ADIDCHK 0x80 /* additional ID check */
+#define CNTLREG3_FASTSCSI 0x10 /* fast SCSI */
+#define CNTLREG3_FASTCLK 0x08 /* fast SCSI clocking */
+
+#define CNTLREG4 0x34 /* rw control register four */
+#define CNTLREG4_GLITCH 0xC0 /* glitch eater */
+#define CNTLREG4_PWD 0x20 /* reduced power feature */
+#define CNTLREG4_RAE 0x08 /* write only, active negot. ctrl. */
+#define CNTLREG4_RADE 0x04 /* active negot. ctrl. */
+#define CNTLREG4_RES 0x10 /* reserved bit, must be 1 */
+
+/*** DMA block ***/
+#define DMACMD 0x40 /* rw command */
+#define DMACMD_DIR 0x80 /* transfer direction (1=read from device) */
+#define DMACMD_INTE_D 0x40 /* DMA transfer interrupt enable */
+#define DMACMD_INTE_P 0x20 /* page transfer interrupt enable */
+#define DMACMD_MDL 0x10 /* map to memory descriptor list */
+#define DMACMD_DIAG 0x04 /* diagnostics, set to 0 */
+#define DMACMD_IDLE 0x00 /* idle cmd */
+#define DMACMD_BLAST 0x01 /* flush FIFO to memory */
+#define DMACMD_ABORT 0x02 /* terminate DMA */
+#define DMACMD_START 0x03 /* start DMA */
+
+#define DMASTATUS 0x54 /* r status register */
+#define DMASTATUS_BCMPLT 0x20 /* BLAST complete */
+#define DMASTATUS_SCSIINT 0x10 /* SCSI interrupt pending */
+#define DMASTATUS_DONE 0x08 /* DMA transfer terminated */
+#define DMASTATUS_ABORT 0x04 /* DMA transfer aborted */
+#define DMASTATUS_ERROR 0x02 /* DMA transfer error */
+#define DMASTATUS_PWDN 0x02 /* power down indicator */
+
+#define DMASTC 0x44 /* rw starting transfer count */
+#define DMASPA 0x48 /* rw starting physical address */
+#define DMAWBC 0x4C /* r working byte counter */
+#define DMAWAC 0x50 /* r working address counter */
+#define DMASMDLA 0x58 /* rw starting MDL address */
+#define DMAWMAC 0x5C /* r working MDL counter */
+
+/*** SCSI phases ***/
+#define PHASE_MSGIN 0x07
+#define PHASE_MSGOUT 0x06
+#define PHASE_RES_1 0x05
+#define PHASE_RES_0 0x04
+#define PHASE_STATIN 0x03
+#define PHASE_CMDOUT 0x02
+#define PHASE_DATAIN 0x01
+#define PHASE_DATAOUT 0x00
+
+struct AM53C974_hostdata {
+ volatile unsigned in_reset:1; /* flag, says bus reset pending */
+ volatile unsigned aborted:1; /* flag, says aborted */
+ volatile unsigned selecting:1; /* selection started, but not yet finished */
+ volatile unsigned disconnecting: 1; /* disconnection started, but not yet finished */
+ volatile unsigned dma_busy:1; /* dma busy when service request for info transfer received */
+ volatile unsigned char msgout[10]; /* message to output in MSGOUT_PHASE */
+ volatile unsigned char last_message[10]; /* last message OUT */
+ volatile Scsi_Cmnd *issue_queue; /* waiting to be issued */
+ volatile Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */
+ volatile Scsi_Cmnd *sel_cmd; /* command for selection */
+ volatile Scsi_Cmnd *connected; /* currently connected command */
+ volatile unsigned char busy[8]; /* index = target, bit = lun */
+ unsigned char sync_per[8]; /* synchronous transfer period (in effect) */
+ unsigned char sync_off[8]; /* synchronous offset (in effect) */
+ unsigned char sync_neg[8]; /* sync. negotiation performed (in effect) */
+ unsigned char sync_en[8]; /* sync. negotiation performed (in effect) */
+ unsigned char max_rate[8]; /* max. transfer rate (setup) */
+ unsigned char max_offset[8]; /* max. sync. offset (setup), only valid if corresponding sync_en is nonzero */
+ };
+
+#define AM53C974 { \
+ NULL, /* pointer to next in list */ \
+ NULL, /* long * usage_count */ \
+ NULL, /* struct proc_dir_entry *proc_dir */ \
+ NULL, /* int (*proc_info)(char *, char **, off_t, int, int, int); */ \
+ "AM53C974", /* name */ \
+ AM53C974_detect, /* int (* detect)(struct SHT *) */ \
+ NULL, /* int (*release)(struct Scsi_Host *) */ \
+ AM53C974_info, /* const char *(* info)(struct Scsi_Host *) */ \
+ AM53C974_command, /* int (* command)(Scsi_Cmnd *) */ \
+ AM53C974_queue_command, /* int (* queuecommand)(Scsi_Cmnd *, \
+ void (*done)(Scsi_Cmnd *)) */ \
+ AM53C974_abort, /* int (* abort)(Scsi_Cmnd *) */ \
+ AM53C974_reset, /* int (* reset)(Scsi_Cmnd *) */ \
+ NULL, /* int (* slave_attach)(int, int) */ \
+ scsicam_bios_param, /* int (* bios_param)(Disk *, int, int[]) */ \
+ 12, /* can_queue */ \
+ -1, /* this_id */ \
+ SG_ALL, /* sg_tablesize */ \
+ 1, /* cmd_per_lun */ \
+ 0, /* present, i.e. how many adapters of this kind */ \
+ 0, /* unchecked_isa_dma */ \
+ DISABLE_CLUSTERING /* use_clustering */ \
+ }
+
+void AM53C974_setup(char *str, int *ints);
+int AM53C974_detect(Scsi_Host_Template *tpnt);
+int AM53C974_biosparm(Disk *disk, int dev, int *info_array);
+const char *AM53C974_info(struct Scsi_Host *);
+int AM53C974_command(Scsi_Cmnd *SCpnt);
+int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+int AM53C974_abort(Scsi_Cmnd *cmd);
+int AM53C974_reset (Scsi_Cmnd *cmd, unsigned int flags);
+
+#define AM53C974_local_declare() unsigned long io_port
+#define AM53C974_setio(instance) io_port = instance->io_port
+#define AM53C974_read_8(addr) inb(io_port + (addr))
+#define AM53C974_write_8(addr,x) outb((x), io_port + (addr))
+#define AM53C974_read_16(addr) inw(io_port + (addr))
+#define AM53C974_write_16(addr,x) outw((x), io_port + (addr))
+#define AM53C974_read_32(addr) inl(io_port + (addr))
+#define AM53C974_write_32(addr,x) outl((x), io_port + (addr))
+
+#define AM53C974_poll_int() { do { statreg = AM53C974_read_8(STATREG); } \
+ while (!(statreg & STATREG_INT)) ; \
+ AM53C974_read_8(INSTREG) ; } /* clear int */
+#define AM53C974_cfifo() (AM53C974_read_8(CFIREG) & CFIREG_CF)
+
+/* These are "special" values for the tag parameter passed to AM53C974_select. */
+#define TAG_NEXT -1 /* Use next free tag */
+#define TAG_NONE -2 /* Establish I_T_L nexus instead of I_T_L_Q
+ * even on SCSI-II devices */
+
+/************ LILO overrides *************/
+typedef struct _override_t {
+ int host_scsi_id; /* SCSI id of the bus controller */
+ int target_scsi_id; /* SCSI id of target */
+ int max_rate; /* max. transfer rate */
+ int max_offset; /* max. sync. offset, 0 = asynchronous */
+ } override_t;
+
+/************ PCI stuff *************/
+#define AM53C974_PCIREG_OPEN() outb(0xF1, 0xCF8); outb(0, 0xCFA)
+#define AM53C974_PCIREG_CLOSE() outb(0, 0xCF8)
+#define AM53C974_PCIREG_READ_BYTE(instance,a) ( inb((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_READ_WORD(instance,a) ( inw((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_READ_DWORD(instance,a) ( inl((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_BYTE(instance,x,a) ( outb((x), (a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_WORD(instance,x,a) ( outw((x), (a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_DWORD(instance,x,a) ( outl((x), (a) + (instance)->io_port) )
+
+typedef struct _pci_config_t {
+ /* start of official PCI config space header */
+ union {
+ unsigned int device_vendor;
+ struct {
+ unsigned short vendor;
+ unsigned short device;
+ } dv;
+ } dv_id;
+#define _device_vendor dv_id.device_vendor
+#define _vendor dv_id.dv.vendor
+#define _device dv_id.dv.device
+ union {
+ unsigned int status_command;
+ struct {
+ unsigned short command;
+ unsigned short status;
+ } sc;
+ } stat_cmd;
+#define _status_command stat_cmd.status_command
+#define _command stat_cmd.sc.command
+#define _status stat_cmd.sc.status
+ union {
+ unsigned int class_revision;
+ struct {
+ unsigned char rev_id;
+ unsigned char prog_if;
+ unsigned char sub_class;
+ unsigned char base_class;
+ } cr;
+ } class_rev;
+#define _class_revision class_rev.class_revision
+#define _rev_id class_rev.cr.rev_id
+#define _prog_if class_rev.cr.prog_if
+#define _sub_class class_rev.cr.sub_class
+#define _base_class class_rev.cr.base_class
+ union {
+ unsigned int bist_header_latency_cache;
+ struct {
+ unsigned char cache_line_size;
+ unsigned char latency_timer;
+ unsigned char header_type;
+ unsigned char bist;
+ } bhlc;
+ } bhlc;
+#define _bist_header_latency_cache bhlc.bist_header_latency_cache
+#define _cache_line_size bhlc.bhlc.cache_line_size
+#define _latency_timer bhlc.bhlc.latency_timer
+#define _header_type bhlc.bhlc.header_type
+#define _bist bhlc.bhlc.bist
+ unsigned int _base0;
+ unsigned int _base1;
+ unsigned int _base2;
+ unsigned int _base3;
+ unsigned int _base4;
+ unsigned int _base5;
+ unsigned int rsvd1;
+ unsigned int rsvd2;
+ unsigned int _baserom;
+ unsigned int rsvd3;
+ unsigned int rsvd4;
+ union {
+ unsigned int max_min_ipin_iline;
+ struct {
+ unsigned char int_line;
+ unsigned char int_pin;
+ unsigned char min_gnt;
+ unsigned char max_lat;
+ } mmii;
+ } mmii;
+#define _max_min_ipin_iline mmii.max_min_ipin_iline
+#define _int_line mmii.mmii.int_line
+#define _int_pin mmii.mmii.int_pin
+#define _min_gnt mmii.mmii.min_gnt
+#define _max_lat mmii.mmii.max_lat
+ /* end of official PCI config space header */
+ unsigned short _ioaddr; /* config type 1 - private I/O addr */
+ unsigned int _pcibus; /* config type 2 - private bus id */
+ unsigned int _cardnum; /* config type 2 - private card number */
+} pci_config_t;
+
+#endif /* AM53C974_H */
diff --git a/linux/src/drivers/scsi/BusLogic.c b/linux/src/drivers/scsi/BusLogic.c
new file mode 100644
index 00000000..3c52e154
--- /dev/null
+++ b/linux/src/drivers/scsi/BusLogic.c
@@ -0,0 +1,5003 @@
+/*
+
+ Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters
+
+ Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+ This program is free software; you may redistribute and/or modify it under
+ the terms of the GNU General Public License Version 2 as published by the
+ Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for complete details.
+
+ The author respectfully requests that any modifications to this software be
+ sent directly to him for evaluation and testing.
+
+ Special thanks to Wayne Yen, Jin-Lon Hon, and Alex Win of BusLogic, whose
+ advice has been invaluable, to David Gentzel, for writing the original Linux
+ BusLogic driver, and to Paul Gortmaker, for being such a dedicated test site.
+
+ Finally, special thanks to Mylex/BusLogic for making the FlashPoint SCCB
+ Manager available as freely redistributable source code.
+
+*/
+
+
+#define BusLogic_DriverVersion "2.0.15"
+#define BusLogic_DriverDate "17 August 1998"
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "BusLogic.h"
+#include "FlashPoint.c"
+
+
+/*
+ BusLogic_DriverOptionsCount is a count of the number of BusLogic Driver
+ Options specifications provided via the Linux Kernel Command Line or via
+ the Loadable Kernel Module Installation Facility.
+*/
+
+static int
+ BusLogic_DriverOptionsCount = 0;
+
+
+/*
+ BusLogic_DriverOptions is an array of Driver Options structures representing
+ BusLogic Driver Options specifications provided via the Linux Kernel Command
+ Line or via the Loadable Kernel Module Installation Facility.
+*/
+
+static BusLogic_DriverOptions_T
+ BusLogic_DriverOptions[BusLogic_MaxHostAdapters];
+
+
+/*
+ BusLogic_Options can be assigned a string by the Loadable Kernel Module
+ Installation Facility to be parsed for BusLogic Driver Options
+ specifications.
+*/
+
+static char
+ *BusLogic_Options = NULL;
+
+
+/*
+ BusLogic_ProbeOptions is a set of Probe Options to be applied across
+ all BusLogic Host Adapters.
+*/
+
+static BusLogic_ProbeOptions_T
+ BusLogic_ProbeOptions = { 0 };
+
+
+/*
+ BusLogic_GlobalOptions is a set of Global Options to be applied across
+ all BusLogic Host Adapters.
+*/
+
+static BusLogic_GlobalOptions_T
+ BusLogic_GlobalOptions = { 0 };
+
+
+/*
+ BusLogic_FirstRegisteredHostAdapter and BusLogic_LastRegisteredHostAdapter
+ are pointers to the first and last registered BusLogic Host Adapters.
+*/
+
+static BusLogic_HostAdapter_T
+ *BusLogic_FirstRegisteredHostAdapter = NULL,
+ *BusLogic_LastRegisteredHostAdapter = NULL;
+
+
+/*
+ BusLogic_ProbeInfoCount is the number of entries in BusLogic_ProbeInfoList.
+*/
+
+static int
+ BusLogic_ProbeInfoCount = 0;
+
+
+/*
+ BusLogic_ProbeInfoList is the list of I/O Addresses and Bus Probe Information
+ to be checked for potential BusLogic Host Adapters. It is initialized by
+ interrogating the PCI Configuration Space on PCI machines as well as from the
+ list of standard BusLogic I/O Addresses.
+*/
+
+static BusLogic_ProbeInfo_T
+ *BusLogic_ProbeInfoList = NULL;
+
+
+/*
+ BusLogic_CommandFailureReason holds a string identifying the reason why a
+ call to BusLogic_Command failed. It is only non-NULL when BusLogic_Command
+ returns a failure code.
+*/
+
+static char
+ *BusLogic_CommandFailureReason;
+
+
+/*
+ BusLogic_ProcDirectoryEntry is the BusLogic /proc/scsi directory entry.
+*/
+
+PROC_DirectoryEntry_T
+ BusLogic_ProcDirectoryEntry =
+ { PROC_SCSI_BUSLOGIC, 8, "BusLogic", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+
+/*
+ BusLogic_AnnounceDriver announces the Driver Version and Date, Author's
+ Name, Copyright Notice, and Electronic Mail Address.
+*/
+
+static void BusLogic_AnnounceDriver(BusLogic_HostAdapter_T *HostAdapter)
+{
+ BusLogic_Announce("***** BusLogic SCSI Driver Version "
+ BusLogic_DriverVersion " of "
+ BusLogic_DriverDate " *****\n", HostAdapter);
+ BusLogic_Announce("Copyright 1995-1998 by Leonard N. Zubkoff "
+ "<lnz@dandelion.com>\n", HostAdapter);
+}
+
+
+/*
+ BusLogic_DriverInfo returns the Host Adapter Name to identify this SCSI
+ Driver and Host Adapter.
+*/
+
+const char *BusLogic_DriverInfo(SCSI_Host_T *Host)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Host->hostdata;
+ return HostAdapter->FullModelName;
+}
+
+
+/*
+ BusLogic_RegisterHostAdapter adds Host Adapter to the list of registered
+ BusLogic Host Adapters.
+*/
+
+static void BusLogic_RegisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+ HostAdapter->Next = NULL;
+ if (BusLogic_FirstRegisteredHostAdapter == NULL)
+ {
+ BusLogic_FirstRegisteredHostAdapter = HostAdapter;
+ BusLogic_LastRegisteredHostAdapter = HostAdapter;
+ }
+ else
+ {
+ BusLogic_LastRegisteredHostAdapter->Next = HostAdapter;
+ BusLogic_LastRegisteredHostAdapter = HostAdapter;
+ }
+}
+
+
+/*
+ BusLogic_UnregisterHostAdapter removes Host Adapter from the list of
+ registered BusLogic Host Adapters.
+*/
+
+static void BusLogic_UnregisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+ if (HostAdapter == BusLogic_FirstRegisteredHostAdapter)
+ {
+ BusLogic_FirstRegisteredHostAdapter =
+ BusLogic_FirstRegisteredHostAdapter->Next;
+ if (HostAdapter == BusLogic_LastRegisteredHostAdapter)
+ BusLogic_LastRegisteredHostAdapter = NULL;
+ }
+ else
+ {
+ BusLogic_HostAdapter_T *PreviousHostAdapter =
+ BusLogic_FirstRegisteredHostAdapter;
+ while (PreviousHostAdapter != NULL &&
+ PreviousHostAdapter->Next != HostAdapter)
+ PreviousHostAdapter = PreviousHostAdapter->Next;
+ if (PreviousHostAdapter != NULL)
+ PreviousHostAdapter->Next = HostAdapter->Next;
+ }
+ HostAdapter->Next = NULL;
+}
+
+
+/*
+ BusLogic_InitializeCCBs initializes a group of Command Control Blocks (CCBs)
+ for Host Adapter from the BlockSize bytes located at BlockPointer. The newly
+ created CCBs are added to Host Adapter's free list.
+*/
+
+static void BusLogic_InitializeCCBs(BusLogic_HostAdapter_T *HostAdapter,
+ void *BlockPointer, int BlockSize)
+{
+ BusLogic_CCB_T *CCB = (BusLogic_CCB_T *) BlockPointer;
+ memset(BlockPointer, 0, BlockSize);
+ CCB->AllocationGroupHead = true;
+ while ((BlockSize -= sizeof(BusLogic_CCB_T)) >= 0)
+ {
+ CCB->Status = BusLogic_CCB_Free;
+ CCB->HostAdapter = HostAdapter;
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ {
+ CCB->CallbackFunction = BusLogic_QueueCompletedCCB;
+ CCB->BaseAddress = HostAdapter->FlashPointInfo.BaseAddress;
+ }
+ CCB->Next = HostAdapter->Free_CCBs;
+ CCB->NextAll = HostAdapter->All_CCBs;
+ HostAdapter->Free_CCBs = CCB;
+ HostAdapter->All_CCBs = CCB;
+ HostAdapter->AllocatedCCBs++;
+ CCB++;
+ }
+}
+
+
+/*
+ BusLogic_CreateInitialCCBs allocates the initial CCBs for Host Adapter.
+*/
+
+static boolean BusLogic_CreateInitialCCBs(BusLogic_HostAdapter_T *HostAdapter)
+{
+ int BlockSize = BusLogic_CCB_AllocationGroupSize * sizeof(BusLogic_CCB_T);
+ while (HostAdapter->AllocatedCCBs < HostAdapter->InitialCCBs)
+ {
+ void *BlockPointer = kmalloc(BlockSize,
+ (HostAdapter->BounceBuffersRequired
+ ? GFP_ATOMIC | GFP_DMA
+ : GFP_ATOMIC));
+ if (BlockPointer == NULL)
+ {
+ BusLogic_Error("UNABLE TO ALLOCATE CCB GROUP - DETACHING\n",
+ HostAdapter);
+ return false;
+ }
+ BusLogic_InitializeCCBs(HostAdapter, BlockPointer, BlockSize);
+ }
+ return true;
+}
+
+
+/*
+ BusLogic_DestroyCCBs deallocates the CCBs for Host Adapter.
+*/
+
+static void BusLogic_DestroyCCBs(BusLogic_HostAdapter_T *HostAdapter)
+{
+ BusLogic_CCB_T *NextCCB = HostAdapter->All_CCBs, *CCB;
+ HostAdapter->All_CCBs = NULL;
+ HostAdapter->Free_CCBs = NULL;
+ while ((CCB = NextCCB) != NULL)
+ {
+ NextCCB = CCB->NextAll;
+ if (CCB->AllocationGroupHead)
+ kfree(CCB);
+ }
+}
+
+
+/*
+ BusLogic_CreateAdditionalCCBs allocates Additional CCBs for Host Adapter. If
+ allocation fails and there are no remaining CCBs available, the Driver Queue
+ Depth is decreased to a known safe value to avoid potential deadlocks when
+ multiple host adapters share the same IRQ Channel.
+*/
+
+static void BusLogic_CreateAdditionalCCBs(BusLogic_HostAdapter_T *HostAdapter,
+ int AdditionalCCBs,
+ boolean SuccessMessageP)
+{
+ int BlockSize = BusLogic_CCB_AllocationGroupSize * sizeof(BusLogic_CCB_T);
+ int PreviouslyAllocated = HostAdapter->AllocatedCCBs;
+ if (AdditionalCCBs <= 0) return;
+ while (HostAdapter->AllocatedCCBs - PreviouslyAllocated < AdditionalCCBs)
+ {
+ void *BlockPointer = kmalloc(BlockSize,
+ (HostAdapter->BounceBuffersRequired
+ ? GFP_ATOMIC | GFP_DMA
+ : GFP_ATOMIC));
+ if (BlockPointer == NULL) break;
+ BusLogic_InitializeCCBs(HostAdapter, BlockPointer, BlockSize);
+ }
+ if (HostAdapter->AllocatedCCBs > PreviouslyAllocated)
+ {
+ if (SuccessMessageP)
+ BusLogic_Notice("Allocated %d additional CCBs (total now %d)\n",
+ HostAdapter,
+ HostAdapter->AllocatedCCBs - PreviouslyAllocated,
+ HostAdapter->AllocatedCCBs);
+ return;
+ }
+ BusLogic_Notice("Failed to allocate additional CCBs\n", HostAdapter);
+ if (HostAdapter->DriverQueueDepth >
+ HostAdapter->AllocatedCCBs - HostAdapter->TargetDeviceCount)
+ {
+ HostAdapter->DriverQueueDepth =
+ HostAdapter->AllocatedCCBs - HostAdapter->TargetDeviceCount;
+ HostAdapter->SCSI_Host->can_queue = HostAdapter->DriverQueueDepth;
+ }
+}
+
+
+/*
+ BusLogic_AllocateCCB allocates a CCB from Host Adapter's free list,
+ allocating more memory from the Kernel if necessary. The Host Adapter's
+ Lock should already have been acquired by the caller.
+*/
+
+static BusLogic_CCB_T *BusLogic_AllocateCCB(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ static unsigned long SerialNumber = 0;
+ BusLogic_CCB_T *CCB;
+ CCB = HostAdapter->Free_CCBs;
+ if (CCB != NULL)
+ {
+ CCB->SerialNumber = ++SerialNumber;
+ HostAdapter->Free_CCBs = CCB->Next;
+ CCB->Next = NULL;
+ if (HostAdapter->Free_CCBs == NULL)
+ BusLogic_CreateAdditionalCCBs(HostAdapter,
+ HostAdapter->IncrementalCCBs,
+ true);
+ return CCB;
+ }
+ BusLogic_CreateAdditionalCCBs(HostAdapter,
+ HostAdapter->IncrementalCCBs,
+ true);
+ CCB = HostAdapter->Free_CCBs;
+ if (CCB == NULL) return NULL;
+ CCB->SerialNumber = ++SerialNumber;
+ HostAdapter->Free_CCBs = CCB->Next;
+ CCB->Next = NULL;
+ return CCB;
+}
+
+
+/*
+ BusLogic_DeallocateCCB deallocates a CCB, returning it to the Host Adapter's
+ free list. The Host Adapter's Lock should already have been acquired by the
+ caller.
+*/
+
+static void BusLogic_DeallocateCCB(BusLogic_CCB_T *CCB)
+{
+ BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter;
+ CCB->Command = NULL;
+ CCB->Status = BusLogic_CCB_Free;
+ CCB->Next = HostAdapter->Free_CCBs;
+ HostAdapter->Free_CCBs = CCB;
+}
+
+
+/*
+ BusLogic_Command sends the command OperationCode to HostAdapter, optionally
+ providing ParameterLength bytes of ParameterData and receiving at most
+ ReplyLength bytes of ReplyData; any excess reply data is received but
+ discarded.
+
+ On success, this function returns the number of reply bytes read from
+ the Host Adapter (including any discarded data); on failure, it returns
+ -1 if the command was invalid, or -2 if a timeout occurred.
+
+ BusLogic_Command is called exclusively during host adapter detection and
+ initialization, so performance and latency are not critical, and exclusive
+ access to the Host Adapter hardware is assumed. Once the host adapter and
+ driver are initialized, the only Host Adapter command that is issued is the
+ single byte Execute Mailbox Command operation code, which does not require
+ waiting for the Host Adapter Ready bit to be set in the Status Register.
+*/
+
+static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
+ BusLogic_OperationCode_T OperationCode,
+ void *ParameterData,
+ int ParameterLength,
+ void *ReplyData,
+ int ReplyLength)
+{
+ unsigned char *ParameterPointer = (unsigned char *) ParameterData;
+ unsigned char *ReplyPointer = (unsigned char *) ReplyData;
+ BusLogic_StatusRegister_T StatusRegister;
+ BusLogic_InterruptRegister_T InterruptRegister;
+ ProcessorFlags_T ProcessorFlags = 0;
+ int ReplyBytes = 0, Result;
+ long TimeoutCounter;
+ /*
+ Clear out the Reply Data if provided.
+ */
+ if (ReplyLength > 0)
+ memset(ReplyData, 0, ReplyLength);
+ /*
+ If the IRQ Channel has not yet been acquired, then interrupts must be
+ disabled while issuing host adapter commands since a Command Complete
+ interrupt could occur if the IRQ Channel was previously enabled by another
+ BusLogic Host Adapter or another driver sharing the same IRQ Channel.
+ */
+ if (!HostAdapter->IRQ_ChannelAcquired)
+ {
+ save_flags(ProcessorFlags);
+ cli();
+ }
+ /*
+ Wait for the Host Adapter Ready bit to be set and the Command/Parameter
+ Register Busy bit to be reset in the Status Register.
+ */
+ TimeoutCounter = 10000;
+ while (--TimeoutCounter >= 0)
+ {
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.Bits.HostAdapterReady &&
+ !StatusRegister.Bits.CommandParameterRegisterBusy)
+ break;
+ udelay(100);
+ }
+ if (TimeoutCounter < 0)
+ {
+ BusLogic_CommandFailureReason = "Timeout waiting for Host Adapter Ready";
+ Result = -2;
+ goto Done;
+ }
+ /*
+ Write the OperationCode to the Command/Parameter Register.
+ */
+ HostAdapter->HostAdapterCommandCompleted = false;
+ BusLogic_WriteCommandParameterRegister(HostAdapter, OperationCode);
+ /*
+ Write any additional Parameter Bytes.
+ */
+ TimeoutCounter = 10000;
+ while (ParameterLength > 0 && --TimeoutCounter >= 0)
+ {
+ /*
+ Wait 100 microseconds to give the Host Adapter enough time to determine
+ whether the last value written to the Command/Parameter Register was
+ valid or not. If the Command Complete bit is set in the Interrupt
+ Register, then the Command Invalid bit in the Status Register will be
+ reset if the Operation Code or Parameter was valid and the command
+ has completed, or set if the Operation Code or Parameter was invalid.
+ If the Data In Register Ready bit is set in the Status Register, then
+ the Operation Code was valid, and data is waiting to be read back
+ from the Host Adapter. Otherwise, wait for the Command/Parameter
+ Register Busy bit in the Status Register to be reset.
+ */
+ udelay(100);
+ InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (InterruptRegister.Bits.CommandComplete) break;
+ if (HostAdapter->HostAdapterCommandCompleted) break;
+ if (StatusRegister.Bits.DataInRegisterReady) break;
+ if (StatusRegister.Bits.CommandParameterRegisterBusy) continue;
+ BusLogic_WriteCommandParameterRegister(HostAdapter, *ParameterPointer++);
+ ParameterLength--;
+ }
+ if (TimeoutCounter < 0)
+ {
+ BusLogic_CommandFailureReason =
+ "Timeout waiting for Parameter Acceptance";
+ Result = -2;
+ goto Done;
+ }
+ /*
+ The Modify I/O Address command does not cause a Command Complete Interrupt.
+ */
+ if (OperationCode == BusLogic_ModifyIOAddress)
+ {
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.Bits.CommandInvalid)
+ {
+ BusLogic_CommandFailureReason = "Modify I/O Address Invalid";
+ Result = -1;
+ goto Done;
+ }
+ if (BusLogic_GlobalOptions.TraceConfiguration)
+ BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: "
+ "(Modify I/O Address)\n", HostAdapter,
+ OperationCode, StatusRegister.All);
+ Result = 0;
+ goto Done;
+ }
+ /*
+ Select an appropriate timeout value for awaiting command completion.
+ */
+ switch (OperationCode)
+ {
+ case BusLogic_InquireInstalledDevicesID0to7:
+ case BusLogic_InquireInstalledDevicesID8to15:
+ case BusLogic_InquireTargetDevices:
+ /* Approximately 60 seconds. */
+ TimeoutCounter = 60*10000;
+ break;
+ default:
+ /* Approximately 1 second. */
+ TimeoutCounter = 10000;
+ break;
+ }
+ /*
+ Receive any Reply Bytes, waiting for either the Command Complete bit to
+ be set in the Interrupt Register, or for the Interrupt Handler to set the
+ Host Adapter Command Completed bit in the Host Adapter structure.
+ */
+ while (--TimeoutCounter >= 0)
+ {
+ InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (InterruptRegister.Bits.CommandComplete) break;
+ if (HostAdapter->HostAdapterCommandCompleted) break;
+ if (StatusRegister.Bits.DataInRegisterReady)
+ {
+ if (++ReplyBytes <= ReplyLength)
+ *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter);
+ else BusLogic_ReadDataInRegister(HostAdapter);
+ }
+ if (OperationCode == BusLogic_FetchHostAdapterLocalRAM &&
+ StatusRegister.Bits.HostAdapterReady) break;
+ udelay(100);
+ }
+ if (TimeoutCounter < 0)
+ {
+ BusLogic_CommandFailureReason = "Timeout waiting for Command Complete";
+ Result = -2;
+ goto Done;
+ }
+ /*
+ Clear any pending Command Complete Interrupt.
+ */
+ BusLogic_InterruptReset(HostAdapter);
+ /*
+ Provide tracing information if requested.
+ */
+ if (BusLogic_GlobalOptions.TraceConfiguration)
+ {
+ int i;
+ BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:",
+ HostAdapter, OperationCode,
+ StatusRegister.All, ReplyLength, ReplyBytes);
+ if (ReplyLength > ReplyBytes) ReplyLength = ReplyBytes;
+ for (i = 0; i < ReplyLength; i++)
+ BusLogic_Notice(" %02X", HostAdapter,
+ ((unsigned char *) ReplyData)[i]);
+ BusLogic_Notice("\n", HostAdapter);
+ }
+ /*
+ Process Command Invalid conditions.
+ */
+ if (StatusRegister.Bits.CommandInvalid)
+ {
+ /*
+ Some early BusLogic Host Adapters may not recover properly from
+ a Command Invalid condition, so if this appears to be the case,
+ a Soft Reset is issued to the Host Adapter. Potentially invalid
+ commands are never attempted after Mailbox Initialization is
+ performed, so there should be no Host Adapter state lost by a
+ Soft Reset in response to a Command Invalid condition.
+ */
+ udelay(1000);
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.Bits.CommandInvalid ||
+ StatusRegister.Bits.Reserved ||
+ StatusRegister.Bits.DataInRegisterReady ||
+ StatusRegister.Bits.CommandParameterRegisterBusy ||
+ !StatusRegister.Bits.HostAdapterReady ||
+ !StatusRegister.Bits.InitializationRequired ||
+ StatusRegister.Bits.DiagnosticActive ||
+ StatusRegister.Bits.DiagnosticFailure)
+ {
+ BusLogic_SoftReset(HostAdapter);
+ udelay(1000);
+ }
+ BusLogic_CommandFailureReason = "Command Invalid";
+ Result = -1;
+ goto Done;
+ }
+ /*
+ Handle Excess Parameters Supplied conditions.
+ */
+ if (ParameterLength > 0)
+ {
+ BusLogic_CommandFailureReason = "Excess Parameters Supplied";
+ Result = -1;
+ goto Done;
+ }
+ /*
+ Indicate the command completed successfully.
+ */
+ BusLogic_CommandFailureReason = NULL;
+ Result = ReplyBytes;
+ /*
+ Restore the interrupt status if necessary and return.
+ */
+Done:
+ if (!HostAdapter->IRQ_ChannelAcquired)
+ restore_flags(ProcessorFlags);
+ return Result;
+}
+
+
+/*
+ BusLogic_AppendProbeAddressISA appends a single ISA I/O Address to the list
+ of I/O Address and Bus Probe Information to be checked for potential BusLogic
+ Host Adapters.
+*/
+
+static void BusLogic_AppendProbeAddressISA(BusLogic_IO_Address_T IO_Address)
+{
+ BusLogic_ProbeInfo_T *ProbeInfo;
+ if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters) return;
+ ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+ ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+ ProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
+ ProbeInfo->IO_Address = IO_Address;
+}
+
+
+/*
+ BusLogic_InitializeProbeInfoListISA initializes the list of I/O Address and
+ Bus Probe Information to be checked for potential BusLogic SCSI Host Adapters
+ only from the list of standard BusLogic MultiMaster ISA I/O Addresses.
+*/
+
+static void BusLogic_InitializeProbeInfoListISA(BusLogic_HostAdapter_T
+ *PrototypeHostAdapter)
+{
+ /*
+ If BusLogic Driver Options specifications requested that ISA Bus Probes
+ be inhibited, do not proceed further.
+ */
+ if (BusLogic_ProbeOptions.NoProbeISA) return;
+ /*
+ Append the list of standard BusLogic MultiMaster ISA I/O Addresses.
+ */
+ if (BusLogic_ProbeOptions.LimitedProbeISA
+ ? BusLogic_ProbeOptions.Probe330
+ : check_region(0x330, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x330);
+ if (BusLogic_ProbeOptions.LimitedProbeISA
+ ? BusLogic_ProbeOptions.Probe334
+ : check_region(0x334, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x334);
+ if (BusLogic_ProbeOptions.LimitedProbeISA
+ ? BusLogic_ProbeOptions.Probe230
+ : check_region(0x230, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x230);
+ if (BusLogic_ProbeOptions.LimitedProbeISA
+ ? BusLogic_ProbeOptions.Probe234
+ : check_region(0x234, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x234);
+ if (BusLogic_ProbeOptions.LimitedProbeISA
+ ? BusLogic_ProbeOptions.Probe130
+ : check_region(0x130, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x130);
+ if (BusLogic_ProbeOptions.LimitedProbeISA
+ ? BusLogic_ProbeOptions.Probe134
+ : check_region(0x134, BusLogic_MultiMasterAddressCount) == 0)
+ BusLogic_AppendProbeAddressISA(0x134);
+}
+
+
+#ifdef CONFIG_PCI
+
+
+/*
+ BusLogic_SortProbeInfo sorts a section of BusLogic_ProbeInfoList in order
+ of increasing PCI Bus and Device Number.
+*/
+
+static void BusLogic_SortProbeInfo(BusLogic_ProbeInfo_T *ProbeInfoList,
+ int ProbeInfoCount)
+{
+ int LastInterchange = ProbeInfoCount-1, Bound, j;
+ while (LastInterchange > 0)
+ {
+ Bound = LastInterchange;
+ LastInterchange = 0;
+ for (j = 0; j < Bound; j++)
+ {
+ BusLogic_ProbeInfo_T *ProbeInfo1 = &ProbeInfoList[j];
+ BusLogic_ProbeInfo_T *ProbeInfo2 = &ProbeInfoList[j+1];
+ if (ProbeInfo1->Bus > ProbeInfo2->Bus ||
+ (ProbeInfo1->Bus == ProbeInfo2->Bus &&
+ (ProbeInfo1->Device > ProbeInfo2->Device)))
+ {
+ BusLogic_ProbeInfo_T TempProbeInfo;
+ memcpy(&TempProbeInfo, ProbeInfo1, sizeof(BusLogic_ProbeInfo_T));
+ memcpy(ProbeInfo1, ProbeInfo2, sizeof(BusLogic_ProbeInfo_T));
+ memcpy(ProbeInfo2, &TempProbeInfo, sizeof(BusLogic_ProbeInfo_T));
+ LastInterchange = j;
+ }
+ }
+ }
+}
+
+
+/*
+ BusLogic_InitializeMultiMasterProbeInfo initializes the list of I/O Address
+ and Bus Probe Information to be checked for potential BusLogic MultiMaster
+ SCSI Host Adapters by interrogating the PCI Configuration Space on PCI
+ machines as well as from the list of standard BusLogic MultiMaster ISA
+ I/O Addresses. It returns the number of PCI MultiMaster Host Adapters found.
+*/
+
+static int BusLogic_InitializeMultiMasterProbeInfo(BusLogic_HostAdapter_T
+ *PrototypeHostAdapter)
+{
+ BusLogic_ProbeInfo_T *PrimaryProbeInfo =
+ &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount];
+ int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount + 1;
+ int NonPrimaryPCIMultiMasterCount = 0, PCIMultiMasterCount = 0;
+ boolean ForceBusDeviceScanningOrder = false;
+ boolean ForceBusDeviceScanningOrderChecked = false;
+ boolean StandardAddressSeen[6];
+ unsigned char Bus, DeviceFunction;
+ unsigned int BaseAddress0, BaseAddress1;
+ unsigned char IRQ_Channel;
+ BusLogic_IO_Address_T IO_Address;
+ BusLogic_PCI_Address_T PCI_Address;
+ unsigned short Index = 0;
+ int i;
+ if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters) return 0;
+ BusLogic_ProbeInfoCount++;
+ for (i = 0; i < 6; i++)
+ StandardAddressSeen[i] = false;
+ /*
+ Iterate over the MultiMaster PCI Host Adapters. For each enumerated host
+ adapter, determine whether its ISA Compatible I/O Port is enabled and if
+ so, whether it is assigned the Primary I/O Address. A host adapter that is
+ assigned the Primary I/O Address will always be the preferred boot device.
+ The MultiMaster BIOS will first recognize a host adapter at the Primary I/O
+ Address, then any other PCI host adapters, and finally any host adapters
+ located at the remaining standard ISA I/O Addresses. When a PCI host
+ adapter is found with its ISA Compatible I/O Port enabled, a command is
+ issued to disable the ISA Compatible I/O Port, and it is noted that the
+ particular standard ISA I/O Address need not be probed.
+ */
+ PrimaryProbeInfo->IO_Address = 0;
+ while (pcibios_find_device(PCI_VENDOR_ID_BUSLOGIC,
+ PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER,
+ Index++, &Bus, &DeviceFunction) == 0)
+ if (pcibios_read_config_dword(Bus, DeviceFunction,
+ PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
+ pcibios_read_config_dword(Bus, DeviceFunction,
+ PCI_BASE_ADDRESS_1, &BaseAddress1) == 0 &&
+ pcibios_read_config_byte(Bus, DeviceFunction,
+ PCI_INTERRUPT_LINE, &IRQ_Channel) == 0)
+ {
+ BusLogic_HostAdapter_T *HostAdapter = PrototypeHostAdapter;
+ BusLogic_PCIHostAdapterInformation_T PCIHostAdapterInformation;
+ BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest;
+ unsigned char Device = DeviceFunction >> 3;
+ IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+ PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
+ if ((BaseAddress0 & PCI_BASE_ADDRESS_SPACE)
+ != PCI_BASE_ADDRESS_SPACE_IO)
+ {
+ BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for "
+ "MultiMaster Host Adapter\n", NULL, BaseAddress0);
+ BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
+ NULL, Bus, Device, IO_Address);
+ continue;
+ }
+ if ((BaseAddress1 & PCI_BASE_ADDRESS_SPACE)
+ != PCI_BASE_ADDRESS_SPACE_MEMORY)
+ {
+ BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for "
+ "MultiMaster Host Adapter\n", NULL, BaseAddress1);
+ BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n",
+ NULL, Bus, Device, PCI_Address);
+ continue;
+ }
+ if (IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS)
+ {
+ BusLogic_Error("BusLogic: IRQ Channel %d illegal for "
+ "MultiMaster Host Adapter\n", NULL, IRQ_Channel);
+ BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
+ NULL, Bus, Device, IO_Address);
+ continue;
+ }
+ if (BusLogic_GlobalOptions.TraceProbe)
+ {
+ BusLogic_Notice("BusLogic: PCI MultiMaster Host Adapter "
+ "detected at\n", NULL);
+ BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address "
+ "0x%X PCI Address 0x%X\n", NULL,
+ Bus, Device, IO_Address, PCI_Address);
+ }
+ /*
+ Issue the Inquire PCI Host Adapter Information command to determine
+ the ISA Compatible I/O Port. If the ISA Compatible I/O Port is
+ known and enabled, note that the particular Standard ISA I/O
+ Address should not be probed.
+ */
+ HostAdapter->IO_Address = IO_Address;
+ BusLogic_InterruptReset(HostAdapter);
+ if (BusLogic_Command(HostAdapter,
+ BusLogic_InquirePCIHostAdapterInformation,
+ NULL, 0, &PCIHostAdapterInformation,
+ sizeof(PCIHostAdapterInformation))
+ == sizeof(PCIHostAdapterInformation))
+ {
+ if (PCIHostAdapterInformation.ISACompatibleIOPort < 6)
+ StandardAddressSeen[PCIHostAdapterInformation
+ .ISACompatibleIOPort] = true;
+ }
+ else PCIHostAdapterInformation.ISACompatibleIOPort =
+ BusLogic_IO_Disable;
+ /*
+ Issue the Modify I/O Address command to disable the ISA Compatible
+ I/O Port.
+ */
+ ModifyIOAddressRequest = BusLogic_IO_Disable;
+ BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress,
+ &ModifyIOAddressRequest,
+ sizeof(ModifyIOAddressRequest), NULL, 0);
+ /*
+ For the first MultiMaster Host Adapter enumerated, issue the Fetch
+ Host Adapter Local RAM command to read byte 45 of the AutoSCSI area,
+ for the setting of the "Use Bus And Device # For PCI Scanning Seq."
+ option. Issue the Inquire Board ID command since this option is
+ only valid for the BT-948/958/958D.
+ */
+ if (!ForceBusDeviceScanningOrderChecked)
+ {
+ BusLogic_FetchHostAdapterLocalRAMRequest_T
+ FetchHostAdapterLocalRAMRequest;
+ BusLogic_AutoSCSIByte45_T AutoSCSIByte45;
+ BusLogic_BoardID_T BoardID;
+ FetchHostAdapterLocalRAMRequest.ByteOffset =
+ BusLogic_AutoSCSI_BaseOffset + 45;
+ FetchHostAdapterLocalRAMRequest.ByteCount =
+ sizeof(AutoSCSIByte45);
+ BusLogic_Command(HostAdapter,
+ BusLogic_FetchHostAdapterLocalRAM,
+ &FetchHostAdapterLocalRAMRequest,
+ sizeof(FetchHostAdapterLocalRAMRequest),
+ &AutoSCSIByte45, sizeof(AutoSCSIByte45));
+ BusLogic_Command(HostAdapter, BusLogic_InquireBoardID,
+ NULL, 0, &BoardID, sizeof(BoardID));
+ if (BoardID.FirmwareVersion1stDigit == '5')
+ ForceBusDeviceScanningOrder =
+ AutoSCSIByte45.ForceBusDeviceScanningOrder;
+ ForceBusDeviceScanningOrderChecked = true;
+ }
+ /*
+ Determine whether this MultiMaster Host Adapter has its ISA
+ Compatible I/O Port enabled and is assigned the Primary I/O Address.
+ If it does, then it is the Primary MultiMaster Host Adapter and must
+ be recognized first. If it does not, then it is added to the list
+ for probing after any Primary MultiMaster Host Adapter is probed.
+ */
+ if (PCIHostAdapterInformation.ISACompatibleIOPort == BusLogic_IO_330)
+ {
+ PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+ PrimaryProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+ PrimaryProbeInfo->IO_Address = IO_Address;
+ PrimaryProbeInfo->PCI_Address = PCI_Address;
+ PrimaryProbeInfo->Bus = Bus;
+ PrimaryProbeInfo->Device = Device;
+ PrimaryProbeInfo->IRQ_Channel = IRQ_Channel;
+ PCIMultiMasterCount++;
+ }
+ else if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters)
+ {
+ BusLogic_ProbeInfo_T *ProbeInfo =
+ &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+ ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+ ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+ ProbeInfo->IO_Address = IO_Address;
+ ProbeInfo->PCI_Address = PCI_Address;
+ ProbeInfo->Bus = Bus;
+ ProbeInfo->Device = Device;
+ ProbeInfo->IRQ_Channel = IRQ_Channel;
+ NonPrimaryPCIMultiMasterCount++;
+ PCIMultiMasterCount++;
+ }
+ else BusLogic_Warning("BusLogic: Too many Host Adapters "
+ "detected\n", NULL);
+ }
+ /*
+ If the AutoSCSI "Use Bus And Device # For PCI Scanning Seq." option is ON
+ for the first enumerated MultiMaster Host Adapter, and if that host adapter
+ is a BT-948/958/958D, then the MultiMaster BIOS will recognize MultiMaster
+ Host Adapters in the order of increasing PCI Bus and Device Number. In
+ that case, sort the probe information into the same order the BIOS uses.
+ If this option is OFF, then the MultiMaster BIOS will recognize MultiMaster
+ Host Adapters in the order they are enumerated by the PCI BIOS, and hence
+ no sorting is necessary.
+ */
+ if (ForceBusDeviceScanningOrder)
+ BusLogic_SortProbeInfo(&BusLogic_ProbeInfoList[
+ NonPrimaryPCIMultiMasterIndex],
+ NonPrimaryPCIMultiMasterCount);
+ /*
+ If no PCI MultiMaster Host Adapter is assigned the Primary I/O Address,
+ then the Primary I/O Address must be probed explicitly before any PCI
+ host adapters are probed.
+ */
+ if (!BusLogic_ProbeOptions.NoProbeISA)
+ if (PrimaryProbeInfo->IO_Address == 0 &&
+ (BusLogic_ProbeOptions.LimitedProbeISA
+ ? BusLogic_ProbeOptions.Probe330
+ : check_region(0x330, BusLogic_MultiMasterAddressCount) == 0))
+ {
+ PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+ PrimaryProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
+ PrimaryProbeInfo->IO_Address = 0x330;
+ }
+ /*
+ Append the list of standard BusLogic MultiMaster ISA I/O Addresses,
+ omitting the Primary I/O Address which has already been handled.
+ */
+ if (!BusLogic_ProbeOptions.NoProbeISA)
+ {
+ if (!StandardAddressSeen[1] &&
+ (BusLogic_ProbeOptions.LimitedProbeISA
+ ? BusLogic_ProbeOptions.Probe334
+ : check_region(0x334, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x334);
+ if (!StandardAddressSeen[2] &&
+ (BusLogic_ProbeOptions.LimitedProbeISA
+ ? BusLogic_ProbeOptions.Probe230
+ : check_region(0x230, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x230);
+ if (!StandardAddressSeen[3] &&
+ (BusLogic_ProbeOptions.LimitedProbeISA
+ ? BusLogic_ProbeOptions.Probe234
+ : check_region(0x234, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x234);
+ if (!StandardAddressSeen[4] &&
+ (BusLogic_ProbeOptions.LimitedProbeISA
+ ? BusLogic_ProbeOptions.Probe130
+ : check_region(0x130, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x130);
+ if (!StandardAddressSeen[5] &&
+ (BusLogic_ProbeOptions.LimitedProbeISA
+ ? BusLogic_ProbeOptions.Probe134
+ : check_region(0x134, BusLogic_MultiMasterAddressCount) == 0))
+ BusLogic_AppendProbeAddressISA(0x134);
+ }
+ /*
+ Iterate over the older non-compliant MultiMaster PCI Host Adapters,
+ noting the PCI bus location and assigned IRQ Channel.
+ */
+ Index = 0;
+ while (pcibios_find_device(PCI_VENDOR_ID_BUSLOGIC,
+ PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC,
+ Index++, &Bus, &DeviceFunction) == 0)
+ if (pcibios_read_config_dword(Bus, DeviceFunction,
+ PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
+ pcibios_read_config_byte(Bus, DeviceFunction,
+ PCI_INTERRUPT_LINE, &IRQ_Channel) == 0)
+ {
+ unsigned char Device = DeviceFunction >> 3;
+ IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+ if (IO_Address == 0 || IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS)
+ continue;
+ for (i = 0; i < BusLogic_ProbeInfoCount; i++)
+ {
+ BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[i];
+ if (ProbeInfo->IO_Address == IO_Address &&
+ ProbeInfo->HostAdapterType == BusLogic_MultiMaster)
+ {
+ ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+ ProbeInfo->PCI_Address = 0;
+ ProbeInfo->Bus = Bus;
+ ProbeInfo->Device = Device;
+ ProbeInfo->IRQ_Channel = IRQ_Channel;
+ break;
+ }
+ }
+ }
+ return PCIMultiMasterCount;
+}
+
+
+/*
+ BusLogic_InitializeFlashPointProbeInfo initializes the list of I/O Address
+ and Bus Probe Information to be checked for potential BusLogic FlashPoint
+ Host Adapters by interrogating the PCI Configuration Space. It returns the
+ number of FlashPoint Host Adapters found.
+*/
+
+static int BusLogic_InitializeFlashPointProbeInfo(BusLogic_HostAdapter_T
+ *PrototypeHostAdapter)
+{
+ int FlashPointIndex = BusLogic_ProbeInfoCount, FlashPointCount = 0;
+ unsigned char Bus, DeviceFunction;
+ unsigned int BaseAddress0, BaseAddress1;
+ unsigned char IRQ_Channel;
+ BusLogic_IO_Address_T IO_Address;
+ BusLogic_PCI_Address_T PCI_Address;
+ unsigned short Index = 0;
+ /*
+ Interrogate PCI Configuration Space for any FlashPoint Host Adapters.
+ */
+ while (pcibios_find_device(PCI_VENDOR_ID_BUSLOGIC,
+ PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT,
+ Index++, &Bus, &DeviceFunction) == 0)
+ if (pcibios_read_config_dword(Bus, DeviceFunction,
+ PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
+ pcibios_read_config_dword(Bus, DeviceFunction,
+ PCI_BASE_ADDRESS_1, &BaseAddress1) == 0 &&
+ pcibios_read_config_byte(Bus, DeviceFunction,
+ PCI_INTERRUPT_LINE, &IRQ_Channel) == 0)
+ {
+ unsigned char Device = DeviceFunction >> 3;
+ IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+ PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+ if ((BaseAddress0 & PCI_BASE_ADDRESS_SPACE)
+ != PCI_BASE_ADDRESS_SPACE_IO)
+ {
+ BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for "
+ "FlashPoint Host Adapter\n", NULL, BaseAddress0);
+ BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
+ NULL, Bus, Device, IO_Address);
+ continue;
+ }
+ if ((BaseAddress1 & PCI_BASE_ADDRESS_SPACE)
+ != PCI_BASE_ADDRESS_SPACE_MEMORY)
+ {
+ BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for "
+ "FlashPoint Host Adapter\n", NULL, BaseAddress1);
+ BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n",
+ NULL, Bus, Device, PCI_Address);
+ continue;
+ }
+ if (IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS)
+ {
+ BusLogic_Error("BusLogic: IRQ Channel %d illegal for "
+ "FlashPoint Host Adapter\n", NULL, IRQ_Channel);
+ BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
+ NULL, Bus, Device, IO_Address);
+ continue;
+ }
+ if (BusLogic_GlobalOptions.TraceProbe)
+ {
+ BusLogic_Notice("BusLogic: FlashPoint Host Adapter "
+ "detected at\n", NULL);
+ BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address "
+ "0x%X PCI Address 0x%X\n", NULL,
+ Bus, Device, IO_Address, PCI_Address);
+ }
+ if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters)
+ {
+ BusLogic_ProbeInfo_T *ProbeInfo =
+ &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+ ProbeInfo->HostAdapterType = BusLogic_FlashPoint;
+ ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+ ProbeInfo->IO_Address = IO_Address;
+ ProbeInfo->PCI_Address = PCI_Address;
+ ProbeInfo->Bus = Bus;
+ ProbeInfo->Device = Device;
+ ProbeInfo->IRQ_Channel = IRQ_Channel;
+ FlashPointCount++;
+ }
+ else BusLogic_Warning("BusLogic: Too many Host Adapters "
+ "detected\n", NULL);
+#else
+ BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at "
+ "PCI Bus %d Device %d\n", NULL, Bus, Device);
+ BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, "
+ "but FlashPoint\n", NULL, IO_Address, PCI_Address);
+ BusLogic_Error("BusLogic: support was omitted in this kernel "
+ "configuration.\n", NULL);
+#endif
+ }
+ /*
+ The FlashPoint BIOS will scan for FlashPoint Host Adapters in the order of
+ increasing PCI Bus and Device Number, so sort the probe information into
+ the same order the BIOS uses.
+ */
+ BusLogic_SortProbeInfo(&BusLogic_ProbeInfoList[FlashPointIndex],
+ FlashPointCount);
+ return FlashPointCount;
+}
+
+
+/*
+ BusLogic_InitializeProbeInfoList initializes the list of I/O Address and Bus
+ Probe Information to be checked for potential BusLogic SCSI Host Adapters by
+ interrogating the PCI Configuration Space on PCI machines as well as from the
+ list of standard BusLogic MultiMaster ISA I/O Addresses. By default, if both
+ FlashPoint and PCI MultiMaster Host Adapters are present, this driver will
+ probe for FlashPoint Host Adapters first unless the BIOS primary disk is
+ controlled by the first PCI MultiMaster Host Adapter, in which case
+ MultiMaster Host Adapters will be probed first. The BusLogic Driver Options
+ specifications "MultiMasterFirst" and "FlashPointFirst" can be used to force
+ a particular probe order.
+*/
+
+static void BusLogic_InitializeProbeInfoList(BusLogic_HostAdapter_T
+ *PrototypeHostAdapter)
+{
+ /*
+ If a PCI BIOS is present, interrogate it for MultiMaster and FlashPoint
+ Host Adapters; otherwise, default to the standard ISA MultiMaster probe.
+ */
+ if (!BusLogic_ProbeOptions.NoProbePCI && pcibios_present())
+ {
+ if (BusLogic_ProbeOptions.MultiMasterFirst)
+ {
+ BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter);
+ BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter);
+ }
+ else if (BusLogic_ProbeOptions.FlashPointFirst)
+ {
+ BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter);
+ BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter);
+ }
+ else
+ {
+ int FlashPointCount =
+ BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter);
+ int PCIMultiMasterCount =
+ BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter);
+ if (FlashPointCount > 0 && PCIMultiMasterCount > 0)
+ {
+ BusLogic_ProbeInfo_T *ProbeInfo =
+ &BusLogic_ProbeInfoList[FlashPointCount];
+ BusLogic_HostAdapter_T *HostAdapter = PrototypeHostAdapter;
+ BusLogic_FetchHostAdapterLocalRAMRequest_T
+ FetchHostAdapterLocalRAMRequest;
+ BusLogic_BIOSDriveMapByte_T Drive0MapByte;
+ while (ProbeInfo->HostAdapterBusType != BusLogic_PCI_Bus)
+ ProbeInfo++;
+ HostAdapter->IO_Address = ProbeInfo->IO_Address;
+ FetchHostAdapterLocalRAMRequest.ByteOffset =
+ BusLogic_BIOS_BaseOffset + BusLogic_BIOS_DriveMapOffset + 0;
+ FetchHostAdapterLocalRAMRequest.ByteCount =
+ sizeof(Drive0MapByte);
+ BusLogic_Command(HostAdapter,
+ BusLogic_FetchHostAdapterLocalRAM,
+ &FetchHostAdapterLocalRAMRequest,
+ sizeof(FetchHostAdapterLocalRAMRequest),
+ &Drive0MapByte, sizeof(Drive0MapByte));
+ /*
+ If the Map Byte for BIOS Drive 0 indicates that BIOS Drive 0
+ is controlled by this PCI MultiMaster Host Adapter, then
+ reverse the probe order so that MultiMaster Host Adapters are
+ probed before FlashPoint Host Adapters.
+ */
+ if (Drive0MapByte.DiskGeometry !=
+ BusLogic_BIOS_Disk_Not_Installed)
+ {
+ BusLogic_ProbeInfo_T
+ SavedProbeInfo[BusLogic_MaxHostAdapters];
+ int MultiMasterCount =
+ BusLogic_ProbeInfoCount - FlashPointCount;
+ memcpy(SavedProbeInfo,
+ BusLogic_ProbeInfoList,
+ BusLogic_ProbeInfoCount
+ * sizeof(BusLogic_ProbeInfo_T));
+ memcpy(&BusLogic_ProbeInfoList[0],
+ &SavedProbeInfo[FlashPointCount],
+ MultiMasterCount * sizeof(BusLogic_ProbeInfo_T));
+ memcpy(&BusLogic_ProbeInfoList[MultiMasterCount],
+ &SavedProbeInfo[0],
+ FlashPointCount * sizeof(BusLogic_ProbeInfo_T));
+ }
+ }
+ }
+ }
+ else BusLogic_InitializeProbeInfoListISA(PrototypeHostAdapter);
+}
+
+
+#endif /* CONFIG_PCI */
+
+
+/*
+ BusLogic_Failure prints a standardized error message, and then returns false.
+*/
+
+static boolean BusLogic_Failure(BusLogic_HostAdapter_T *HostAdapter,
+ char *ErrorMessage)
+{
+ BusLogic_AnnounceDriver(HostAdapter);
+ if (HostAdapter->HostAdapterBusType == BusLogic_PCI_Bus)
+ {
+ BusLogic_Error("While configuring BusLogic PCI Host Adapter at\n",
+ HostAdapter);
+ BusLogic_Error("Bus %d Device %d I/O Address 0x%X PCI Address 0x%X:\n",
+ HostAdapter, HostAdapter->Bus, HostAdapter->Device,
+ HostAdapter->IO_Address, HostAdapter->PCI_Address);
+ }
+ else BusLogic_Error("While configuring BusLogic Host Adapter at "
+ "I/O Address 0x%X:\n", HostAdapter,
+ HostAdapter->IO_Address);
+ BusLogic_Error("%s FAILED - DETACHING\n", HostAdapter, ErrorMessage);
+ if (BusLogic_CommandFailureReason != NULL)
+ BusLogic_Error("ADDITIONAL FAILURE INFO - %s\n", HostAdapter,
+ BusLogic_CommandFailureReason);
+ return false;
+}
+
+
+/*
+ BusLogic_ProbeHostAdapter probes for a BusLogic Host Adapter.
+*/
+
+static boolean BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+ BusLogic_StatusRegister_T StatusRegister;
+ BusLogic_InterruptRegister_T InterruptRegister;
+ BusLogic_GeometryRegister_T GeometryRegister;
+ /*
+ FlashPoint Host Adapters are Probed by the FlashPoint SCCB Manager.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ {
+ FlashPoint_Info_T *FlashPointInfo = &HostAdapter->FlashPointInfo;
+ FlashPointInfo->BaseAddress =
+ (BusLogic_Base_Address_T) HostAdapter->IO_Address;
+ FlashPointInfo->IRQ_Channel = HostAdapter->IRQ_Channel;
+ FlashPointInfo->Present = false;
+ if (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 &&
+ FlashPointInfo->Present))
+ {
+ BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at "
+ "PCI Bus %d Device %d\n", HostAdapter,
+ HostAdapter->Bus, HostAdapter->Device);
+ BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, "
+ "but FlashPoint\n", HostAdapter,
+ HostAdapter->IO_Address, HostAdapter->PCI_Address);
+ BusLogic_Error("BusLogic: Probe Function failed to validate it.\n",
+ HostAdapter);
+ return false;
+ }
+ if (BusLogic_GlobalOptions.TraceProbe)
+ BusLogic_Notice("BusLogic_Probe(0x%X): FlashPoint Found\n",
+ HostAdapter, HostAdapter->IO_Address);
+ /*
+ Indicate the Host Adapter Probe completed successfully.
+ */
+ return true;
+ }
+ /*
+ Read the Status, Interrupt, and Geometry Registers to test if there are I/O
+ ports that respond, and to check the values to determine if they are from a
+ BusLogic Host Adapter. A nonexistent I/O port will return 0xFF, in which
+ case there is definitely no BusLogic Host Adapter at this base I/O Address.
+ The test here is a subset of that used by the BusLogic Host Adapter BIOS.
+ */
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+ GeometryRegister.All = BusLogic_ReadGeometryRegister(HostAdapter);
+ if (BusLogic_GlobalOptions.TraceProbe)
+ BusLogic_Notice("BusLogic_Probe(0x%X): Status 0x%02X, Interrupt 0x%02X, "
+ "Geometry 0x%02X\n", HostAdapter,
+ HostAdapter->IO_Address, StatusRegister.All,
+ InterruptRegister.All, GeometryRegister.All);
+ if (StatusRegister.All == 0 ||
+ StatusRegister.Bits.DiagnosticActive ||
+ StatusRegister.Bits.CommandParameterRegisterBusy ||
+ StatusRegister.Bits.Reserved ||
+ StatusRegister.Bits.CommandInvalid ||
+ InterruptRegister.Bits.Reserved != 0)
+ return false;
+ /*
+ Check the undocumented Geometry Register to test if there is an I/O port
+ that responded. Adaptec Host Adapters do not implement the Geometry
+ Register, so this test helps serve to avoid incorrectly recognizing an
+ Adaptec 1542A or 1542B as a BusLogic. Unfortunately, the Adaptec 1542C
+ series does respond to the Geometry Register I/O port, but it will be
+ rejected later when the Inquire Extended Setup Information command is
+ issued in BusLogic_CheckHostAdapter. The AMI FastDisk Host Adapter is a
+ BusLogic clone that implements the same interface as earlier BusLogic
+ Host Adapters, including the undocumented commands, and is therefore
+ supported by this driver. However, the AMI FastDisk always returns 0x00
+ upon reading the Geometry Register, so the extended translation option
+ should always be left disabled on the AMI FastDisk.
+ */
+ if (GeometryRegister.All == 0xFF) return false;
+ /*
+ Indicate the Host Adapter Probe completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_HardwareResetHostAdapter issues a Hardware Reset to the Host Adapter
+ and waits for Host Adapter Diagnostics to complete. If HardReset is true, a
+ Hard Reset is performed which also initiates a SCSI Bus Reset. Otherwise, a
+ Soft Reset is performed which only resets the Host Adapter without forcing a
+ SCSI Bus Reset.
+*/
+
+static boolean BusLogic_HardwareResetHostAdapter(BusLogic_HostAdapter_T
+ *HostAdapter,
+ boolean HardReset)
+{
+ BusLogic_StatusRegister_T StatusRegister;
+ int TimeoutCounter;
+ /*
+ FlashPoint Host Adapters are Hard Reset by the FlashPoint SCCB Manager.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ {
+ FlashPoint_Info_T *FlashPointInfo = &HostAdapter->FlashPointInfo;
+ FlashPointInfo->HostSoftReset = !HardReset;
+ FlashPointInfo->ReportDataUnderrun = true;
+ HostAdapter->CardHandle =
+ FlashPoint_HardwareResetHostAdapter(FlashPointInfo);
+ if (HostAdapter->CardHandle == FlashPoint_BadCardHandle) return false;
+ /*
+ Indicate the Host Adapter Hard Reset completed successfully.
+ */
+ return true;
+ }
+ /*
+ Issue a Hard Reset or Soft Reset Command to the Host Adapter. The Host
+ Adapter should respond by setting Diagnostic Active in the Status Register.
+ */
+ if (HardReset)
+ BusLogic_HardReset(HostAdapter);
+ else BusLogic_SoftReset(HostAdapter);
+ /*
+ Wait until Diagnostic Active is set in the Status Register.
+ */
+ TimeoutCounter = 5*10000;
+ while (--TimeoutCounter >= 0)
+ {
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.Bits.DiagnosticActive) break;
+ udelay(100);
+ }
+ if (BusLogic_GlobalOptions.TraceHardwareReset)
+ BusLogic_Notice("BusLogic_HardwareReset(0x%X): Diagnostic Active, "
+ "Status 0x%02X\n", HostAdapter,
+ HostAdapter->IO_Address, StatusRegister.All);
+ if (TimeoutCounter < 0) return false;
+ /*
+ Wait 100 microseconds to allow completion of any initial diagnostic
+ activity which might leave the contents of the Status Register
+ unpredictable.
+ */
+ udelay(100);
+ /*
+ Wait until Diagnostic Active is reset in the Status Register.
+ */
+ TimeoutCounter = 10*10000;
+ while (--TimeoutCounter >= 0)
+ {
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (!StatusRegister.Bits.DiagnosticActive) break;
+ udelay(100);
+ }
+ if (BusLogic_GlobalOptions.TraceHardwareReset)
+ BusLogic_Notice("BusLogic_HardwareReset(0x%X): Diagnostic Completed, "
+ "Status 0x%02X\n", HostAdapter,
+ HostAdapter->IO_Address, StatusRegister.All);
+ if (TimeoutCounter < 0) return false;
+ /*
+ Wait until at least one of the Diagnostic Failure, Host Adapter Ready,
+ or Data In Register Ready bits is set in the Status Register.
+ */
+ TimeoutCounter = 10000;
+ while (--TimeoutCounter >= 0)
+ {
+ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister.Bits.DiagnosticFailure ||
+ StatusRegister.Bits.HostAdapterReady ||
+ StatusRegister.Bits.DataInRegisterReady)
+ break;
+ udelay(100);
+ }
+ if (BusLogic_GlobalOptions.TraceHardwareReset)
+ BusLogic_Notice("BusLogic_HardwareReset(0x%X): Host Adapter Ready, "
+ "Status 0x%02X\n", HostAdapter,
+ HostAdapter->IO_Address, StatusRegister.All);
+ if (TimeoutCounter < 0) return false;
+ /*
+ If Diagnostic Failure is set or Host Adapter Ready is reset, then an
+ error occurred during the Host Adapter diagnostics. If Data In Register
+ Ready is set, then there is an Error Code available.
+ */
+ if (StatusRegister.Bits.DiagnosticFailure ||
+ !StatusRegister.Bits.HostAdapterReady)
+ {
+ BusLogic_CommandFailureReason = NULL;
+ BusLogic_Failure(HostAdapter, "HARD RESET DIAGNOSTICS");
+ BusLogic_Error("HOST ADAPTER STATUS REGISTER = %02X\n",
+ HostAdapter, StatusRegister.All);
+ if (StatusRegister.Bits.DataInRegisterReady)
+ {
+ unsigned char ErrorCode = BusLogic_ReadDataInRegister(HostAdapter);
+ BusLogic_Error("HOST ADAPTER ERROR CODE = %d\n",
+ HostAdapter, ErrorCode);
+ }
+ return false;
+ }
+ /*
+ Indicate the Host Adapter Hard Reset completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_CheckHostAdapter checks to be sure this really is a BusLogic
+ Host Adapter.
+*/
+
+static boolean BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+ BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
+ BusLogic_RequestedReplyLength_T RequestedReplyLength;
+ boolean Result = true;
+ /*
+ FlashPoint Host Adapters do not require this protection.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) return true;
+ /*
+ Issue the Inquire Extended Setup Information command. Only genuine
+ BusLogic Host Adapters and true clones support this command. Adaptec 1542C
+ series Host Adapters that respond to the Geometry Register I/O port will
+ fail this command.
+ */
+ RequestedReplyLength = sizeof(ExtendedSetupInformation);
+ if (BusLogic_Command(HostAdapter,
+ BusLogic_InquireExtendedSetupInformation,
+ &RequestedReplyLength,
+ sizeof(RequestedReplyLength),
+ &ExtendedSetupInformation,
+ sizeof(ExtendedSetupInformation))
+ != sizeof(ExtendedSetupInformation))
+ Result = false;
+ /*
+ Provide tracing information if requested and return.
+ */
+ if (BusLogic_GlobalOptions.TraceProbe)
+ BusLogic_Notice("BusLogic_Check(0x%X): MultiMaster %s\n", HostAdapter,
+ HostAdapter->IO_Address, (Result ? "Found" : "Not Found"));
+ return Result;
+}
+
+
+/*
+ BusLogic_ReadHostAdapterConfiguration reads the Configuration Information
+ from Host Adapter and initializes the Host Adapter structure.
+*/
+
+static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ BusLogic_BoardID_T BoardID;
+ BusLogic_Configuration_T Configuration;
+ BusLogic_SetupInformation_T SetupInformation;
+ BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
+ BusLogic_HostAdapterModelNumber_T HostAdapterModelNumber;
+ BusLogic_FirmwareVersion3rdDigit_T FirmwareVersion3rdDigit;
+ BusLogic_FirmwareVersionLetter_T FirmwareVersionLetter;
+ BusLogic_PCIHostAdapterInformation_T PCIHostAdapterInformation;
+ BusLogic_FetchHostAdapterLocalRAMRequest_T FetchHostAdapterLocalRAMRequest;
+ BusLogic_AutoSCSIData_T AutoSCSIData;
+ BusLogic_GeometryRegister_T GeometryRegister;
+ BusLogic_RequestedReplyLength_T RequestedReplyLength;
+ unsigned char *TargetPointer, Character;
+ int TargetID, i;
+ /*
+ Configuration Information for FlashPoint Host Adapters is provided in the
+ FlashPoint_Info structure by the FlashPoint SCCB Manager's Probe Function.
+ Initialize fields in the Host Adapter structure from the FlashPoint_Info
+ structure.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ {
+ FlashPoint_Info_T *FlashPointInfo = &HostAdapter->FlashPointInfo;
+ TargetPointer = HostAdapter->ModelName;
+ *TargetPointer++ = 'B';
+ *TargetPointer++ = 'T';
+ *TargetPointer++ = '-';
+ for (i = 0; i < sizeof(FlashPointInfo->ModelNumber); i++)
+ *TargetPointer++ = FlashPointInfo->ModelNumber[i];
+ *TargetPointer++ = '\0';
+ strcpy(HostAdapter->FirmwareVersion, FlashPoint_FirmwareVersion);
+ HostAdapter->SCSI_ID = FlashPointInfo->SCSI_ID;
+ HostAdapter->ExtendedTranslationEnabled =
+ FlashPointInfo->ExtendedTranslationEnabled;
+ HostAdapter->ParityCheckingEnabled =
+ FlashPointInfo->ParityCheckingEnabled;
+ HostAdapter->BusResetEnabled = !FlashPointInfo->HostSoftReset;
+ HostAdapter->LevelSensitiveInterrupt = true;
+ HostAdapter->HostWideSCSI = FlashPointInfo->HostWideSCSI;
+ HostAdapter->HostDifferentialSCSI = false;
+ HostAdapter->HostSupportsSCAM = true;
+ HostAdapter->HostUltraSCSI = true;
+ HostAdapter->ExtendedLUNSupport = true;
+ HostAdapter->TerminationInfoValid = true;
+ HostAdapter->LowByteTerminated = FlashPointInfo->LowByteTerminated;
+ HostAdapter->HighByteTerminated = FlashPointInfo->HighByteTerminated;
+ HostAdapter->SCAM_Enabled = FlashPointInfo->SCAM_Enabled;
+ HostAdapter->SCAM_Level2 = FlashPointInfo->SCAM_Level2;
+ HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit;
+ HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8);
+ HostAdapter->MaxLogicalUnits = 32;
+ HostAdapter->InitialCCBs = 4 * BusLogic_CCB_AllocationGroupSize;
+ HostAdapter->IncrementalCCBs = BusLogic_CCB_AllocationGroupSize;
+ HostAdapter->DriverQueueDepth = 255;
+ HostAdapter->HostAdapterQueueDepth = HostAdapter->DriverQueueDepth;
+ HostAdapter->SynchronousPermitted = FlashPointInfo->SynchronousPermitted;
+ HostAdapter->FastPermitted = FlashPointInfo->FastPermitted;
+ HostAdapter->UltraPermitted = FlashPointInfo->UltraPermitted;
+ HostAdapter->WidePermitted = FlashPointInfo->WidePermitted;
+ HostAdapter->DisconnectPermitted = FlashPointInfo->DisconnectPermitted;
+ HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+ goto Common;
+ }
+ /*
+ Issue the Inquire Board ID command.
+ */
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, NULL, 0,
+ &BoardID, sizeof(BoardID)) != sizeof(BoardID))
+ return BusLogic_Failure(HostAdapter, "INQUIRE BOARD ID");
+ /*
+ Issue the Inquire Configuration command.
+ */
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireConfiguration, NULL, 0,
+ &Configuration, sizeof(Configuration))
+ != sizeof(Configuration))
+ return BusLogic_Failure(HostAdapter, "INQUIRE CONFIGURATION");
+ /*
+ Issue the Inquire Setup Information command.
+ */
+ RequestedReplyLength = sizeof(SetupInformation);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation,
+ &RequestedReplyLength, sizeof(RequestedReplyLength),
+ &SetupInformation, sizeof(SetupInformation))
+ != sizeof(SetupInformation))
+ return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION");
+ /*
+ Issue the Inquire Extended Setup Information command.
+ */
+ RequestedReplyLength = sizeof(ExtendedSetupInformation);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireExtendedSetupInformation,
+ &RequestedReplyLength, sizeof(RequestedReplyLength),
+ &ExtendedSetupInformation,
+ sizeof(ExtendedSetupInformation))
+ != sizeof(ExtendedSetupInformation))
+ return BusLogic_Failure(HostAdapter, "INQUIRE EXTENDED SETUP INFORMATION");
+ /*
+ Issue the Inquire Firmware Version 3rd Digit command.
+ */
+ FirmwareVersion3rdDigit = '\0';
+ if (BoardID.FirmwareVersion1stDigit > '0')
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersion3rdDigit,
+ NULL, 0, &FirmwareVersion3rdDigit,
+ sizeof(FirmwareVersion3rdDigit))
+ != sizeof(FirmwareVersion3rdDigit))
+ return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT");
+ /*
+ Issue the Inquire Host Adapter Model Number command.
+ */
+ if (ExtendedSetupInformation.BusType == 'A' &&
+ BoardID.FirmwareVersion1stDigit == '2')
+ /* BusLogic BT-542B ISA 2.xx */
+ strcpy(HostAdapterModelNumber, "542B");
+ else if (ExtendedSetupInformation.BusType == 'E' &&
+ BoardID.FirmwareVersion1stDigit == '2' &&
+ (BoardID.FirmwareVersion2ndDigit <= '1' ||
+ (BoardID.FirmwareVersion2ndDigit == '2' &&
+ FirmwareVersion3rdDigit == '0')))
+ /* BusLogic BT-742A EISA 2.1x or 2.20 */
+ strcpy(HostAdapterModelNumber, "742A");
+ else if (ExtendedSetupInformation.BusType == 'E' &&
+ BoardID.FirmwareVersion1stDigit == '0')
+ /* AMI FastDisk EISA Series 441 0.x */
+ strcpy(HostAdapterModelNumber, "747A");
+ else
+ {
+ RequestedReplyLength = sizeof(HostAdapterModelNumber);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireHostAdapterModelNumber,
+ &RequestedReplyLength, sizeof(RequestedReplyLength),
+ &HostAdapterModelNumber,
+ sizeof(HostAdapterModelNumber))
+ != sizeof(HostAdapterModelNumber))
+ return BusLogic_Failure(HostAdapter,
+ "INQUIRE HOST ADAPTER MODEL NUMBER");
+ }
+ /*
+ BusLogic MultiMaster Host Adapters can be identified by their model number
+ and the major version number of their firmware as follows:
+
+ 5.xx BusLogic "W" Series Host Adapters:
+ BT-948/958/958D
+ 4.xx BusLogic "C" Series Host Adapters:
+ BT-946C/956C/956CD/747C/757C/757CD/445C/545C/540CF
+ 3.xx BusLogic "S" Series Host Adapters:
+ BT-747S/747D/757S/757D/445S/545S/542D
+ BT-542B/742A (revision H)
+ 2.xx BusLogic "A" Series Host Adapters:
+ BT-542B/742A (revision G and below)
+ 0.xx AMI FastDisk VLB/EISA BusLogic Clone Host Adapter
+ */
+ /*
+ Save the Model Name and Host Adapter Name in the Host Adapter structure.
+ */
+ TargetPointer = HostAdapter->ModelName;
+ *TargetPointer++ = 'B';
+ *TargetPointer++ = 'T';
+ *TargetPointer++ = '-';
+ for (i = 0; i < sizeof(HostAdapterModelNumber); i++)
+ {
+ Character = HostAdapterModelNumber[i];
+ if (Character == ' ' || Character == '\0') break;
+ *TargetPointer++ = Character;
+ }
+ *TargetPointer++ = '\0';
+ /*
+ Save the Firmware Version in the Host Adapter structure.
+ */
+ TargetPointer = HostAdapter->FirmwareVersion;
+ *TargetPointer++ = BoardID.FirmwareVersion1stDigit;
+ *TargetPointer++ = '.';
+ *TargetPointer++ = BoardID.FirmwareVersion2ndDigit;
+ if (FirmwareVersion3rdDigit != ' ' && FirmwareVersion3rdDigit != '\0')
+ *TargetPointer++ = FirmwareVersion3rdDigit;
+ *TargetPointer = '\0';
+ /*
+ Issue the Inquire Firmware Version Letter command.
+ */
+ if (strcmp(HostAdapter->FirmwareVersion, "3.3") >= 0)
+ {
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter,
+ NULL, 0, &FirmwareVersionLetter,
+ sizeof(FirmwareVersionLetter))
+ != sizeof(FirmwareVersionLetter))
+ return BusLogic_Failure(HostAdapter,
+ "INQUIRE FIRMWARE VERSION LETTER");
+ if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0')
+ *TargetPointer++ = FirmwareVersionLetter;
+ *TargetPointer = '\0';
+ }
+ /*
+ Save the Host Adapter SCSI ID in the Host Adapter structure.
+ */
+ HostAdapter->SCSI_ID = Configuration.HostAdapterID;
+ /*
+ Determine the Bus Type and save it in the Host Adapter structure, determine
+ and save the IRQ Channel if necessary, and determine and save the DMA
+ Channel for ISA Host Adapters.
+ */
+ HostAdapter->HostAdapterBusType =
+ BusLogic_HostAdapterBusTypes[HostAdapter->ModelName[3] - '4'];
+ if (HostAdapter->IRQ_Channel == 0)
+ {
+ if (Configuration.IRQ_Channel9)
+ HostAdapter->IRQ_Channel = 9;
+ else if (Configuration.IRQ_Channel10)
+ HostAdapter->IRQ_Channel = 10;
+ else if (Configuration.IRQ_Channel11)
+ HostAdapter->IRQ_Channel = 11;
+ else if (Configuration.IRQ_Channel12)
+ HostAdapter->IRQ_Channel = 12;
+ else if (Configuration.IRQ_Channel14)
+ HostAdapter->IRQ_Channel = 14;
+ else if (Configuration.IRQ_Channel15)
+ HostAdapter->IRQ_Channel = 15;
+ }
+ if (HostAdapter->HostAdapterBusType == BusLogic_ISA_Bus)
+ {
+ if (Configuration.DMA_Channel5)
+ HostAdapter->DMA_Channel = 5;
+ else if (Configuration.DMA_Channel6)
+ HostAdapter->DMA_Channel = 6;
+ else if (Configuration.DMA_Channel7)
+ HostAdapter->DMA_Channel = 7;
+ }
+ /*
+ Determine whether Extended Translation is enabled and save it in
+ the Host Adapter structure.
+ */
+ GeometryRegister.All = BusLogic_ReadGeometryRegister(HostAdapter);
+ HostAdapter->ExtendedTranslationEnabled =
+ GeometryRegister.Bits.ExtendedTranslationEnabled;
+ /*
+ Save the Scatter Gather Limits, Level Sensitive Interrupt flag, Wide
+ SCSI flag, Differential SCSI flag, SCAM Supported flag, and
+ Ultra SCSI flag in the Host Adapter structure.
+ */
+ HostAdapter->HostAdapterScatterGatherLimit =
+ ExtendedSetupInformation.ScatterGatherLimit;
+ HostAdapter->DriverScatterGatherLimit =
+ HostAdapter->HostAdapterScatterGatherLimit;
+ if (HostAdapter->HostAdapterScatterGatherLimit > BusLogic_ScatterGatherLimit)
+ HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit;
+ if (ExtendedSetupInformation.Misc.LevelSensitiveInterrupt)
+ HostAdapter->LevelSensitiveInterrupt = true;
+ HostAdapter->HostWideSCSI = ExtendedSetupInformation.HostWideSCSI;
+ HostAdapter->HostDifferentialSCSI =
+ ExtendedSetupInformation.HostDifferentialSCSI;
+ HostAdapter->HostSupportsSCAM = ExtendedSetupInformation.HostSupportsSCAM;
+ HostAdapter->HostUltraSCSI = ExtendedSetupInformation.HostUltraSCSI;
+ /*
+ Determine whether Extended LUN Format CCBs are supported and save the
+ information in the Host Adapter structure.
+ */
+ if (HostAdapter->FirmwareVersion[0] == '5' ||
+ (HostAdapter->FirmwareVersion[0] == '4' && HostAdapter->HostWideSCSI))
+ HostAdapter->ExtendedLUNSupport = true;
+ /*
+ Issue the Inquire PCI Host Adapter Information command to read the
+ Termination Information from "W" series MultiMaster Host Adapters.
+ */
+ if (HostAdapter->FirmwareVersion[0] == '5')
+ {
+ if (BusLogic_Command(HostAdapter,
+ BusLogic_InquirePCIHostAdapterInformation,
+ NULL, 0, &PCIHostAdapterInformation,
+ sizeof(PCIHostAdapterInformation))
+ != sizeof(PCIHostAdapterInformation))
+ return BusLogic_Failure(HostAdapter,
+ "INQUIRE PCI HOST ADAPTER INFORMATION");
+ /*
+ Save the Termination Information in the Host Adapter structure.
+ */
+ if (PCIHostAdapterInformation.GenericInfoValid)
+ {
+ HostAdapter->TerminationInfoValid = true;
+ HostAdapter->LowByteTerminated =
+ PCIHostAdapterInformation.LowByteTerminated;
+ HostAdapter->HighByteTerminated =
+ PCIHostAdapterInformation.HighByteTerminated;
+ }
+ }
+ /*
+ Issue the Fetch Host Adapter Local RAM command to read the AutoSCSI data
+ from "W" and "C" series MultiMaster Host Adapters.
+ */
+ if (HostAdapter->FirmwareVersion[0] >= '4')
+ {
+ FetchHostAdapterLocalRAMRequest.ByteOffset =
+ BusLogic_AutoSCSI_BaseOffset;
+ FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(AutoSCSIData);
+ if (BusLogic_Command(HostAdapter,
+ BusLogic_FetchHostAdapterLocalRAM,
+ &FetchHostAdapterLocalRAMRequest,
+ sizeof(FetchHostAdapterLocalRAMRequest),
+ &AutoSCSIData, sizeof(AutoSCSIData))
+ != sizeof(AutoSCSIData))
+ return BusLogic_Failure(HostAdapter, "FETCH HOST ADAPTER LOCAL RAM");
+ /*
+ Save the Parity Checking Enabled, Bus Reset Enabled, and Termination
+ Information in the Host Adapter structure.
+ */
+ HostAdapter->ParityCheckingEnabled = AutoSCSIData.ParityCheckingEnabled;
+ HostAdapter->BusResetEnabled = AutoSCSIData.BusResetEnabled;
+ if (HostAdapter->FirmwareVersion[0] == '4')
+ {
+ HostAdapter->TerminationInfoValid = true;
+ HostAdapter->LowByteTerminated = AutoSCSIData.LowByteTerminated;
+ HostAdapter->HighByteTerminated = AutoSCSIData.HighByteTerminated;
+ }
+ /*
+ Save the Wide Permitted, Fast Permitted, Synchronous Permitted,
+ Disconnect Permitted, Ultra Permitted, and SCAM Information in the
+ Host Adapter structure.
+ */
+ HostAdapter->WidePermitted = AutoSCSIData.WidePermitted;
+ HostAdapter->FastPermitted = AutoSCSIData.FastPermitted;
+ HostAdapter->SynchronousPermitted =
+ AutoSCSIData.SynchronousPermitted;
+ HostAdapter->DisconnectPermitted =
+ AutoSCSIData.DisconnectPermitted;
+ if (HostAdapter->HostUltraSCSI)
+ HostAdapter->UltraPermitted = AutoSCSIData.UltraPermitted;
+ if (HostAdapter->HostSupportsSCAM)
+ {
+ HostAdapter->SCAM_Enabled = AutoSCSIData.SCAM_Enabled;
+ HostAdapter->SCAM_Level2 = AutoSCSIData.SCAM_Level2;
+ }
+ }
+ /*
+ Initialize fields in the Host Adapter structure for "S" and "A" series
+ MultiMaster Host Adapters.
+ */
+ if (HostAdapter->FirmwareVersion[0] < '4')
+ {
+ if (SetupInformation.SynchronousInitiationEnabled)
+ {
+ HostAdapter->SynchronousPermitted = 0xFF;
+ if (HostAdapter->HostAdapterBusType == BusLogic_EISA_Bus)
+ {
+ if (ExtendedSetupInformation.Misc.FastOnEISA)
+ HostAdapter->FastPermitted = 0xFF;
+ if (strcmp(HostAdapter->ModelName, "BT-757") == 0)
+ HostAdapter->WidePermitted = 0xFF;
+ }
+ }
+ HostAdapter->DisconnectPermitted = 0xFF;
+ HostAdapter->ParityCheckingEnabled =
+ SetupInformation.ParityCheckingEnabled;
+ HostAdapter->BusResetEnabled = true;
+ }
+ /*
+ Determine the maximum number of Target IDs and Logical Units supported by
+ this driver for Wide and Narrow Host Adapters.
+ */
+ HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8);
+ HostAdapter->MaxLogicalUnits = (HostAdapter->ExtendedLUNSupport ? 32 : 8);
+ /*
+ Select appropriate values for the Mailbox Count, Driver Queue Depth,
+ Initial CCBs, and Incremental CCBs variables based on whether or not Strict
+ Round Robin Mode is supported. If Strict Round Robin Mode is supported,
+ then there is no performance degradation in using the maximum possible
+ number of Outgoing and Incoming Mailboxes and allowing the Tagged and
+ Untagged Queue Depths to determine the actual utilization. If Strict Round
+ Robin Mode is not supported, then the Host Adapter must scan all the
+ Outgoing Mailboxes whenever an Outgoing Mailbox entry is made, which can
+ cause a substantial performance penalty. The host adapters actually have
+ room to store the following number of CCBs internally; that is, they can
+ internally queue and manage this many active commands on the SCSI bus
+ simultaneously. Performance measurements demonstrate that the Driver Queue
+ Depth should be set to the Mailbox Count, rather than the Host Adapter
+ Queue Depth (internal CCB capacity), as it is more efficient to have the
+ queued commands waiting in Outgoing Mailboxes if necessary than to block
+ the process in the higher levels of the SCSI Subsystem.
+
+ 192 BT-948/958/958D
+ 100 BT-946C/956C/956CD/747C/757C/757CD/445C
+ 50 BT-545C/540CF
+ 30 BT-747S/747D/757S/757D/445S/545S/542D/542B/742A
+ */
+ if (HostAdapter->FirmwareVersion[0] == '5')
+ HostAdapter->HostAdapterQueueDepth = 192;
+ else if (HostAdapter->FirmwareVersion[0] == '4')
+ HostAdapter->HostAdapterQueueDepth =
+ (HostAdapter->HostAdapterBusType != BusLogic_ISA_Bus ? 100 : 50);
+ else HostAdapter->HostAdapterQueueDepth = 30;
+ if (strcmp(HostAdapter->FirmwareVersion, "3.31") >= 0)
+ {
+ HostAdapter->StrictRoundRobinModeSupport = true;
+ HostAdapter->MailboxCount = BusLogic_MaxMailboxes;
+ }
+ else
+ {
+ HostAdapter->StrictRoundRobinModeSupport = false;
+ HostAdapter->MailboxCount = 32;
+ }
+ HostAdapter->DriverQueueDepth = HostAdapter->MailboxCount;
+ HostAdapter->InitialCCBs = 4 * BusLogic_CCB_AllocationGroupSize;
+ HostAdapter->IncrementalCCBs = BusLogic_CCB_AllocationGroupSize;
+ /*
+ Tagged Queuing support is available and operates properly on all "W" series
+ MultiMaster Host Adapters, on "C" series MultiMaster Host Adapters with
+ firmware version 4.22 and above, and on "S" series MultiMaster Host
+ Adapters with firmware version 3.35 and above.
+ */
+ HostAdapter->TaggedQueuingPermitted = 0;
+ switch (HostAdapter->FirmwareVersion[0])
+ {
+ case '5':
+ HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+ break;
+ case '4':
+ if (strcmp(HostAdapter->FirmwareVersion, "4.22") >= 0)
+ HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+ break;
+ case '3':
+ if (strcmp(HostAdapter->FirmwareVersion, "3.35") >= 0)
+ HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+ break;
+ }
+ /*
+ Determine the Host Adapter BIOS Address if the BIOS is enabled and
+ save it in the Host Adapter structure. The BIOS is disabled if the
+ BIOS_Address is 0.
+ */
+ HostAdapter->BIOS_Address = ExtendedSetupInformation.BIOS_Address << 12;
+ /*
+ ISA Host Adapters require Bounce Buffers if there is more than 16MB memory.
+ */
+ if (HostAdapter->HostAdapterBusType == BusLogic_ISA_Bus &&
+ (void *) high_memory > (void *) MAX_DMA_ADDRESS)
+ HostAdapter->BounceBuffersRequired = true;
+ /*
+ BusLogic BT-445S Host Adapters prior to board revision E have a hardware
+ bug whereby when the BIOS is enabled, transfers to/from the same address
+ range the BIOS occupies modulo 16MB are handled incorrectly. Only properly
+ functioning BT-445S Host Adapters have firmware version 3.37, so require
+ that ISA Bounce Buffers be used for the buggy BT-445S models if there is
+ more than 16MB memory.
+ */
+ if (HostAdapter->BIOS_Address > 0 &&
+ strcmp(HostAdapter->ModelName, "BT-445S") == 0 &&
+ strcmp(HostAdapter->FirmwareVersion, "3.37") < 0 &&
+ (void *) high_memory > (void *) MAX_DMA_ADDRESS)
+ HostAdapter->BounceBuffersRequired = true;
+ /*
+ Initialize parameters common to MultiMaster and FlashPoint Host Adapters.
+ */
+Common:
+ /*
+ Initialize the Host Adapter Full Model Name from the Model Name.
+ */
+ strcpy(HostAdapter->FullModelName, "BusLogic ");
+ strcat(HostAdapter->FullModelName, HostAdapter->ModelName);
+ /*
+ Select an appropriate value for the Tagged Queue Depth either from a
+ BusLogic Driver Options specification, or based on whether this Host
+ Adapter requires that ISA Bounce Buffers be used. The Tagged Queue Depth
+ is left at 0 for automatic determination in BusLogic_SelectQueueDepths.
+ Initialize the Untagged Queue Depth.
+ */
+ for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++)
+ {
+ unsigned char QueueDepth = 0;
+ if (HostAdapter->DriverOptions != NULL &&
+ HostAdapter->DriverOptions->QueueDepth[TargetID] > 0)
+ QueueDepth = HostAdapter->DriverOptions->QueueDepth[TargetID];
+ else if (HostAdapter->BounceBuffersRequired)
+ QueueDepth = BusLogic_TaggedQueueDepthBB;
+ HostAdapter->QueueDepth[TargetID] = QueueDepth;
+ }
+ if (HostAdapter->BounceBuffersRequired)
+ HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepthBB;
+ else HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepth;
+ if (HostAdapter->DriverOptions != NULL)
+ HostAdapter->CommonQueueDepth =
+ HostAdapter->DriverOptions->CommonQueueDepth;
+ if (HostAdapter->CommonQueueDepth > 0 &&
+ HostAdapter->CommonQueueDepth < HostAdapter->UntaggedQueueDepth)
+ HostAdapter->UntaggedQueueDepth = HostAdapter->CommonQueueDepth;
+ /*
+ Tagged Queuing is only allowed if Disconnect/Reconnect is permitted.
+ Therefore, mask the Tagged Queuing Permitted Default bits with the
+ Disconnect/Reconnect Permitted bits.
+ */
+ HostAdapter->TaggedQueuingPermitted &= HostAdapter->DisconnectPermitted;
+ /*
+ Combine the default Tagged Queuing Permitted bits with any BusLogic Driver
+ Options Tagged Queuing specification.
+ */
+ if (HostAdapter->DriverOptions != NULL)
+ HostAdapter->TaggedQueuingPermitted =
+ (HostAdapter->DriverOptions->TaggedQueuingPermitted &
+ HostAdapter->DriverOptions->TaggedQueuingPermittedMask) |
+ (HostAdapter->TaggedQueuingPermitted &
+ ~HostAdapter->DriverOptions->TaggedQueuingPermittedMask);
+ /*
+ Select appropriate values for the Error Recovery Strategy array
+ either from a BusLogic Driver Options specification, or using
+ BusLogic_ErrorRecovery_Default.
+ */
+ for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++)
+ if (HostAdapter->DriverOptions != NULL)
+ HostAdapter->ErrorRecoveryStrategy[TargetID] =
+ HostAdapter->DriverOptions->ErrorRecoveryStrategy[TargetID];
+ else HostAdapter->ErrorRecoveryStrategy[TargetID] =
+ BusLogic_ErrorRecovery_Default;
+ /*
+ Select an appropriate value for Bus Settle Time either from a BusLogic
+ Driver Options specification, or from BusLogic_DefaultBusSettleTime.
+ */
+ if (HostAdapter->DriverOptions != NULL &&
+ HostAdapter->DriverOptions->BusSettleTime > 0)
+ HostAdapter->BusSettleTime = HostAdapter->DriverOptions->BusSettleTime;
+ else HostAdapter->BusSettleTime = BusLogic_DefaultBusSettleTime;
+ /*
+ Indicate reading the Host Adapter Configuration completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_ReportHostAdapterConfiguration reports the configuration of
+ Host Adapter.
+*/
+
+static boolean BusLogic_ReportHostAdapterConfiguration(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ unsigned short AllTargetsMask = (1 << HostAdapter->MaxTargetDevices) - 1;
+ unsigned short SynchronousPermitted, FastPermitted;
+ unsigned short UltraPermitted, WidePermitted;
+ unsigned short DisconnectPermitted, TaggedQueuingPermitted;
+ boolean CommonSynchronousNegotiation, CommonTaggedQueueDepth;
+ boolean CommonErrorRecovery;
+ char SynchronousString[BusLogic_MaxTargetDevices+1];
+ char WideString[BusLogic_MaxTargetDevices+1];
+ char DisconnectString[BusLogic_MaxTargetDevices+1];
+ char TaggedQueuingString[BusLogic_MaxTargetDevices+1];
+ char ErrorRecoveryString[BusLogic_MaxTargetDevices+1];
+ char *SynchronousMessage = SynchronousString;
+ char *WideMessage = WideString;
+ char *DisconnectMessage = DisconnectString;
+ char *TaggedQueuingMessage = TaggedQueuingString;
+ char *ErrorRecoveryMessage = ErrorRecoveryString;
+ int TargetID;
+ BusLogic_Info("Configuring BusLogic Model %s %s%s%s%s SCSI Host Adapter\n",
+ HostAdapter, HostAdapter->ModelName,
+ BusLogic_HostAdapterBusNames[HostAdapter->HostAdapterBusType],
+ (HostAdapter->HostWideSCSI ? " Wide" : ""),
+ (HostAdapter->HostDifferentialSCSI ? " Differential" : ""),
+ (HostAdapter->HostUltraSCSI ? " Ultra" : ""));
+ BusLogic_Info(" Firmware Version: %s, I/O Address: 0x%X, "
+ "IRQ Channel: %d/%s\n", HostAdapter,
+ HostAdapter->FirmwareVersion,
+ HostAdapter->IO_Address, HostAdapter->IRQ_Channel,
+ (HostAdapter->LevelSensitiveInterrupt ? "Level" : "Edge"));
+ if (HostAdapter->HostAdapterBusType != BusLogic_PCI_Bus)
+ {
+ BusLogic_Info(" DMA Channel: ", HostAdapter);
+ if (HostAdapter->DMA_Channel > 0)
+ BusLogic_Info("%d, ", HostAdapter, HostAdapter->DMA_Channel);
+ else BusLogic_Info("None, ", HostAdapter);
+ if (HostAdapter->BIOS_Address > 0)
+ BusLogic_Info("BIOS Address: 0x%X, ", HostAdapter,
+ HostAdapter->BIOS_Address);
+ else BusLogic_Info("BIOS Address: None, ", HostAdapter);
+ }
+ else
+ {
+ BusLogic_Info(" PCI Bus: %d, Device: %d, Address: ",
+ HostAdapter, HostAdapter->Bus, HostAdapter->Device);
+ if (HostAdapter->PCI_Address > 0)
+ BusLogic_Info("0x%X, ", HostAdapter, HostAdapter->PCI_Address);
+ else BusLogic_Info("Unassigned, ", HostAdapter);
+ }
+ BusLogic_Info("Host Adapter SCSI ID: %d\n", HostAdapter,
+ HostAdapter->SCSI_ID);
+ BusLogic_Info(" Parity Checking: %s, Extended Translation: %s\n",
+ HostAdapter,
+ (HostAdapter->ParityCheckingEnabled
+ ? "Enabled" : "Disabled"),
+ (HostAdapter->ExtendedTranslationEnabled
+ ? "Enabled" : "Disabled"));
+ AllTargetsMask &= ~(1 << HostAdapter->SCSI_ID);
+ SynchronousPermitted = HostAdapter->SynchronousPermitted & AllTargetsMask;
+ FastPermitted = HostAdapter->FastPermitted & AllTargetsMask;
+ UltraPermitted = HostAdapter->UltraPermitted & AllTargetsMask;
+ if ((BusLogic_MultiMasterHostAdapterP(HostAdapter) &&
+ (HostAdapter->FirmwareVersion[0] >= '4' ||
+ HostAdapter->HostAdapterBusType == BusLogic_EISA_Bus)) ||
+ BusLogic_FlashPointHostAdapterP(HostAdapter))
+ {
+ CommonSynchronousNegotiation = false;
+ if (SynchronousPermitted == 0)
+ {
+ SynchronousMessage = "Disabled";
+ CommonSynchronousNegotiation = true;
+ }
+ else if (SynchronousPermitted == AllTargetsMask)
+ {
+ if (FastPermitted == 0)
+ {
+ SynchronousMessage = "Slow";
+ CommonSynchronousNegotiation = true;
+ }
+ else if (FastPermitted == AllTargetsMask)
+ {
+ if (UltraPermitted == 0)
+ {
+ SynchronousMessage = "Fast";
+ CommonSynchronousNegotiation = true;
+ }
+ else if (UltraPermitted == AllTargetsMask)
+ {
+ SynchronousMessage = "Ultra";
+ CommonSynchronousNegotiation = true;
+ }
+ }
+ }
+ if (!CommonSynchronousNegotiation)
+ {
+ for (TargetID = 0;
+ TargetID < HostAdapter->MaxTargetDevices;
+ TargetID++)
+ SynchronousString[TargetID] =
+ ((!(SynchronousPermitted & (1 << TargetID))) ? 'N' :
+ (!(FastPermitted & (1 << TargetID)) ? 'S' :
+ (!(UltraPermitted & (1 << TargetID)) ? 'F' : 'U')));
+ SynchronousString[HostAdapter->SCSI_ID] = '#';
+ SynchronousString[HostAdapter->MaxTargetDevices] = '\0';
+ }
+ }
+ else SynchronousMessage =
+ (SynchronousPermitted == 0 ? "Disabled" : "Enabled");
+ WidePermitted = HostAdapter->WidePermitted & AllTargetsMask;
+ if (WidePermitted == 0)
+ WideMessage = "Disabled";
+ else if (WidePermitted == AllTargetsMask)
+ WideMessage = "Enabled";
+ else
+ {
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ WideString[TargetID] =
+ ((WidePermitted & (1 << TargetID)) ? 'Y' : 'N');
+ WideString[HostAdapter->SCSI_ID] = '#';
+ WideString[HostAdapter->MaxTargetDevices] = '\0';
+ }
+ DisconnectPermitted = HostAdapter->DisconnectPermitted & AllTargetsMask;
+ if (DisconnectPermitted == 0)
+ DisconnectMessage = "Disabled";
+ else if (DisconnectPermitted == AllTargetsMask)
+ DisconnectMessage = "Enabled";
+ else
+ {
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ DisconnectString[TargetID] =
+ ((DisconnectPermitted & (1 << TargetID)) ? 'Y' : 'N');
+ DisconnectString[HostAdapter->SCSI_ID] = '#';
+ DisconnectString[HostAdapter->MaxTargetDevices] = '\0';
+ }
+ TaggedQueuingPermitted =
+ HostAdapter->TaggedQueuingPermitted & AllTargetsMask;
+ if (TaggedQueuingPermitted == 0)
+ TaggedQueuingMessage = "Disabled";
+ else if (TaggedQueuingPermitted == AllTargetsMask)
+ TaggedQueuingMessage = "Enabled";
+ else
+ {
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ TaggedQueuingString[TargetID] =
+ ((TaggedQueuingPermitted & (1 << TargetID)) ? 'Y' : 'N');
+ TaggedQueuingString[HostAdapter->SCSI_ID] = '#';
+ TaggedQueuingString[HostAdapter->MaxTargetDevices] = '\0';
+ }
+ BusLogic_Info(" Synchronous Negotiation: %s, Wide Negotiation: %s\n",
+ HostAdapter, SynchronousMessage, WideMessage);
+ BusLogic_Info(" Disconnect/Reconnect: %s, Tagged Queuing: %s\n",
+ HostAdapter, DisconnectMessage, TaggedQueuingMessage);
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+ {
+ BusLogic_Info(" Scatter/Gather Limit: %d of %d segments, "
+ "Mailboxes: %d\n", HostAdapter,
+ HostAdapter->DriverScatterGatherLimit,
+ HostAdapter->HostAdapterScatterGatherLimit,
+ HostAdapter->MailboxCount);
+ BusLogic_Info(" Driver Queue Depth: %d, "
+ "Host Adapter Queue Depth: %d\n",
+ HostAdapter, HostAdapter->DriverQueueDepth,
+ HostAdapter->HostAdapterQueueDepth);
+ }
+ else BusLogic_Info(" Driver Queue Depth: %d, "
+ "Scatter/Gather Limit: %d segments\n",
+ HostAdapter, HostAdapter->DriverQueueDepth,
+ HostAdapter->DriverScatterGatherLimit);
+ BusLogic_Info(" Tagged Queue Depth: ", HostAdapter);
+ CommonTaggedQueueDepth = true;
+ for (TargetID = 1; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ if (HostAdapter->QueueDepth[TargetID] != HostAdapter->QueueDepth[0])
+ {
+ CommonTaggedQueueDepth = false;
+ break;
+ }
+ if (CommonTaggedQueueDepth)
+ {
+ if (HostAdapter->QueueDepth[0] > 0)
+ BusLogic_Info("%d", HostAdapter, HostAdapter->QueueDepth[0]);
+ else BusLogic_Info("Automatic", HostAdapter);
+ }
+ else BusLogic_Info("Individual", HostAdapter);
+ BusLogic_Info(", Untagged Queue Depth: %d\n", HostAdapter,
+ HostAdapter->UntaggedQueueDepth);
+ CommonErrorRecovery = true;
+ for (TargetID = 1; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ if (HostAdapter->ErrorRecoveryStrategy[TargetID] !=
+ HostAdapter->ErrorRecoveryStrategy[0])
+ {
+ CommonErrorRecovery = false;
+ break;
+ }
+ if (CommonErrorRecovery)
+ ErrorRecoveryMessage =
+ BusLogic_ErrorRecoveryStrategyNames[
+ HostAdapter->ErrorRecoveryStrategy[0]];
+ else
+ {
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ ErrorRecoveryString[TargetID] =
+ BusLogic_ErrorRecoveryStrategyLetters[
+ HostAdapter->ErrorRecoveryStrategy[TargetID]];
+ ErrorRecoveryString[HostAdapter->SCSI_ID] = '#';
+ ErrorRecoveryString[HostAdapter->MaxTargetDevices] = '\0';
+ }
+ BusLogic_Info(" Error Recovery Strategy: %s, SCSI Bus Reset: %s\n",
+ HostAdapter, ErrorRecoveryMessage,
+ (HostAdapter->BusResetEnabled ? "Enabled" : "Disabled"));
+ if (HostAdapter->TerminationInfoValid)
+ {
+ if (HostAdapter->HostWideSCSI)
+ BusLogic_Info(" SCSI Bus Termination: %s", HostAdapter,
+ (HostAdapter->LowByteTerminated
+ ? (HostAdapter->HighByteTerminated
+ ? "Both Enabled" : "Low Enabled")
+ : (HostAdapter->HighByteTerminated
+ ? "High Enabled" : "Both Disabled")));
+ else BusLogic_Info(" SCSI Bus Termination: %s", HostAdapter,
+ (HostAdapter->LowByteTerminated ?
+ "Enabled" : "Disabled"));
+ if (HostAdapter->HostSupportsSCAM)
+ BusLogic_Info(", SCAM: %s", HostAdapter,
+ (HostAdapter->SCAM_Enabled
+ ? (HostAdapter->SCAM_Level2
+ ? "Enabled, Level 2" : "Enabled, Level 1")
+ : "Disabled"));
+ BusLogic_Info("\n", HostAdapter);
+ }
+ /*
+ Indicate reporting the Host Adapter configuration completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_AcquireResources acquires the system resources necessary to use
+ Host Adapter.
+*/
+
+static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter)
+{
+ if (HostAdapter->IRQ_Channel == 0)
+ {
+ BusLogic_Error("NO LEGAL INTERRUPT CHANNEL ASSIGNED - DETACHING\n",
+ HostAdapter);
+ return false;
+ }
+ /*
+ Acquire shared access to the IRQ Channel.
+ */
+ if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler,
+ SA_INTERRUPT | SA_SHIRQ,
+ HostAdapter->FullModelName, HostAdapter) < 0)
+ {
+ BusLogic_Error("UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n",
+ HostAdapter, HostAdapter->IRQ_Channel);
+ return false;
+ }
+ HostAdapter->IRQ_ChannelAcquired = true;
+ /*
+ Acquire exclusive access to the DMA Channel.
+ */
+ if (HostAdapter->DMA_Channel > 0)
+ {
+ if (request_dma(HostAdapter->DMA_Channel,
+ HostAdapter->FullModelName) < 0)
+ {
+ BusLogic_Error("UNABLE TO ACQUIRE DMA CHANNEL %d - DETACHING\n",
+ HostAdapter, HostAdapter->DMA_Channel);
+ return false;
+ }
+ set_dma_mode(HostAdapter->DMA_Channel, DMA_MODE_CASCADE);
+ enable_dma(HostAdapter->DMA_Channel);
+ HostAdapter->DMA_ChannelAcquired = true;
+ }
+ /*
+ Indicate the System Resource Acquisition completed successfully,
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_ReleaseResources releases any system resources previously acquired
+ by BusLogic_AcquireResources.
+*/
+
+static void BusLogic_ReleaseResources(BusLogic_HostAdapter_T *HostAdapter)
+{
+ /*
+ Release shared access to the IRQ Channel.
+ */
+ if (HostAdapter->IRQ_ChannelAcquired)
+ free_irq(HostAdapter->IRQ_Channel, HostAdapter);
+ /*
+ Release exclusive access to the DMA Channel.
+ */
+ if (HostAdapter->DMA_ChannelAcquired)
+ free_dma(HostAdapter->DMA_Channel);
+}
+
+
+/*
+ BusLogic_InitializeHostAdapter initializes Host Adapter. This is the only
+ function called during SCSI Host Adapter detection which modifies the state
+ of the Host Adapter from its initial power on or hard reset state.
+*/
+
+static boolean BusLogic_InitializeHostAdapter(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ BusLogic_ExtendedMailboxRequest_T ExtendedMailboxRequest;
+ BusLogic_RoundRobinModeRequest_T RoundRobinModeRequest;
+ BusLogic_SetCCBFormatRequest_T SetCCBFormatRequest;
+ int TargetID;
+ /*
+ Initialize the pointers to the first and last CCBs that are queued for
+ completion processing.
+ */
+ HostAdapter->FirstCompletedCCB = NULL;
+ HostAdapter->LastCompletedCCB = NULL;
+ /*
+ Initialize the Bus Device Reset Pending CCB, Tagged Queuing Active,
+ Command Successful Flag, Active Commands, and Commands Since Reset
+ for each Target Device.
+ */
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ {
+ HostAdapter->BusDeviceResetPendingCCB[TargetID] = NULL;
+ HostAdapter->TargetFlags[TargetID].TaggedQueuingActive = false;
+ HostAdapter->TargetFlags[TargetID].CommandSuccessfulFlag = false;
+ HostAdapter->ActiveCommands[TargetID] = 0;
+ HostAdapter->CommandsSinceReset[TargetID] = 0;
+ }
+ /*
+ FlashPoint Host Adapters do not use Outgoing and Incoming Mailboxes.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) goto Done;
+ /*
+ Initialize the Outgoing and Incoming Mailbox pointers.
+ */
+ HostAdapter->FirstOutgoingMailbox =
+ (BusLogic_OutgoingMailbox_T *) HostAdapter->MailboxSpace;
+ HostAdapter->LastOutgoingMailbox =
+ HostAdapter->FirstOutgoingMailbox + HostAdapter->MailboxCount - 1;
+ HostAdapter->NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
+ HostAdapter->FirstIncomingMailbox =
+ (BusLogic_IncomingMailbox_T *) (HostAdapter->LastOutgoingMailbox + 1);
+ HostAdapter->LastIncomingMailbox =
+ HostAdapter->FirstIncomingMailbox + HostAdapter->MailboxCount - 1;
+ HostAdapter->NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+ /*
+ Initialize the Outgoing and Incoming Mailbox structures.
+ */
+ memset(HostAdapter->FirstOutgoingMailbox, 0,
+ HostAdapter->MailboxCount * sizeof(BusLogic_OutgoingMailbox_T));
+ memset(HostAdapter->FirstIncomingMailbox, 0,
+ HostAdapter->MailboxCount * sizeof(BusLogic_IncomingMailbox_T));
+ /*
+ Initialize the Host Adapter's Pointer to the Outgoing/Incoming Mailboxes.
+ */
+ ExtendedMailboxRequest.MailboxCount = HostAdapter->MailboxCount;
+ ExtendedMailboxRequest.BaseMailboxAddress =
+ Virtual_to_Bus(HostAdapter->FirstOutgoingMailbox);
+ if (BusLogic_Command(HostAdapter, BusLogic_InitializeExtendedMailbox,
+ &ExtendedMailboxRequest,
+ sizeof(ExtendedMailboxRequest), NULL, 0) < 0)
+ return BusLogic_Failure(HostAdapter, "MAILBOX INITIALIZATION");
+ /*
+ Enable Strict Round Robin Mode if supported by the Host Adapter. In
+ Strict Round Robin Mode, the Host Adapter only looks at the next Outgoing
+ Mailbox for each new command, rather than scanning through all the
+ Outgoing Mailboxes to find any that have new commands in them. Strict
+ Round Robin Mode is significantly more efficient.
+ */
+ if (HostAdapter->StrictRoundRobinModeSupport)
+ {
+ RoundRobinModeRequest = BusLogic_StrictRoundRobinMode;
+ if (BusLogic_Command(HostAdapter, BusLogic_EnableStrictRoundRobinMode,
+ &RoundRobinModeRequest,
+ sizeof(RoundRobinModeRequest), NULL, 0) < 0)
+ return BusLogic_Failure(HostAdapter, "ENABLE STRICT ROUND ROBIN MODE");
+ }
+ /*
+ For Host Adapters that support Extended LUN Format CCBs, issue the Set CCB
+ Format command to allow 32 Logical Units per Target Device.
+ */
+ if (HostAdapter->ExtendedLUNSupport)
+ {
+ SetCCBFormatRequest = BusLogic_ExtendedLUNFormatCCB;
+ if (BusLogic_Command(HostAdapter, BusLogic_SetCCBFormat,
+ &SetCCBFormatRequest, sizeof(SetCCBFormatRequest),
+ NULL, 0) < 0)
+ return BusLogic_Failure(HostAdapter, "SET CCB FORMAT");
+ }
+ /*
+ Announce Successful Initialization.
+ */
+Done:
+ if (!HostAdapter->HostAdapterInitialized)
+ {
+ BusLogic_Info("*** %s Initialized Successfully ***\n",
+ HostAdapter, HostAdapter->FullModelName);
+ BusLogic_Info("\n", HostAdapter);
+ }
+ else BusLogic_Warning("*** %s Initialized Successfully ***\n",
+ HostAdapter, HostAdapter->FullModelName);
+ HostAdapter->HostAdapterInitialized = true;
+ /*
+ Indicate the Host Adapter Initialization completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_TargetDeviceInquiry inquires about the Target Devices accessible
+ through Host Adapter.
+*/
+
+static boolean BusLogic_TargetDeviceInquiry(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ BusLogic_InstalledDevices_T InstalledDevices;
+ BusLogic_InstalledDevices8_T InstalledDevicesID0to7;
+ BusLogic_SetupInformation_T SetupInformation;
+ BusLogic_SynchronousPeriod_T SynchronousPeriod;
+ BusLogic_RequestedReplyLength_T RequestedReplyLength;
+ int TargetID;
+ /*
+ Wait a few seconds between the Host Adapter Hard Reset which initiates
+ a SCSI Bus Reset and issuing any SCSI Commands. Some SCSI devices get
+ confused if they receive SCSI Commands too soon after a SCSI Bus Reset.
+ */
+ BusLogic_Delay(HostAdapter->BusSettleTime);
+ /*
+ FlashPoint Host Adapters do not provide for Target Device Inquiry.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) return true;
+ /*
+ Inhibit the Target Device Inquiry if requested.
+ */
+ if (HostAdapter->DriverOptions != NULL &&
+ HostAdapter->DriverOptions->LocalOptions.InhibitTargetInquiry)
+ return true;
+ /*
+ Issue the Inquire Target Devices command for host adapters with firmware
+ version 4.25 or later, or the Inquire Installed Devices ID 0 to 7 command
+ for older host adapters. This is necessary to force Synchronous Transfer
+ Negotiation so that the Inquire Setup Information and Inquire Synchronous
+ Period commands will return valid data. The Inquire Target Devices command
+ is preferable to Inquire Installed Devices ID 0 to 7 since it only probes
+ Logical Unit 0 of each Target Device.
+ */
+ if (strcmp(HostAdapter->FirmwareVersion, "4.25") >= 0)
+ {
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireTargetDevices, NULL, 0,
+ &InstalledDevices, sizeof(InstalledDevices))
+ != sizeof(InstalledDevices))
+ return BusLogic_Failure(HostAdapter, "INQUIRE TARGET DEVICES");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ HostAdapter->TargetFlags[TargetID].TargetExists =
+ (InstalledDevices & (1 << TargetID) ? true : false);
+ }
+ else
+ {
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID0to7,
+ NULL, 0, &InstalledDevicesID0to7,
+ sizeof(InstalledDevicesID0to7))
+ != sizeof(InstalledDevicesID0to7))
+ return BusLogic_Failure(HostAdapter,
+ "INQUIRE INSTALLED DEVICES ID 0 TO 7");
+ for (TargetID = 0; TargetID < 8; TargetID++)
+ HostAdapter->TargetFlags[TargetID].TargetExists =
+ (InstalledDevicesID0to7[TargetID] != 0 ? true : false);
+ }
+ /*
+ Issue the Inquire Setup Information command.
+ */
+ RequestedReplyLength = sizeof(SetupInformation);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation,
+ &RequestedReplyLength, sizeof(RequestedReplyLength),
+ &SetupInformation, sizeof(SetupInformation))
+ != sizeof(SetupInformation))
+ return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ HostAdapter->SynchronousOffset[TargetID] =
+ (TargetID < 8
+ ? SetupInformation.SynchronousValuesID0to7[TargetID].Offset
+ : SetupInformation.SynchronousValuesID8to15[TargetID-8].Offset);
+ if (strcmp(HostAdapter->FirmwareVersion, "5.06L") >= 0)
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ HostAdapter->TargetFlags[TargetID].WideTransfersActive =
+ (TargetID < 8
+ ? (SetupInformation.WideTransfersActiveID0to7 & (1 << TargetID)
+ ? true : false)
+ : (SetupInformation.WideTransfersActiveID8to15 & (1 << (TargetID-8))
+ ? true : false));
+ /*
+ Issue the Inquire Synchronous Period command.
+ */
+ if (HostAdapter->FirmwareVersion[0] >= '3')
+ {
+ RequestedReplyLength = sizeof(SynchronousPeriod);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireSynchronousPeriod,
+ &RequestedReplyLength, sizeof(RequestedReplyLength),
+ &SynchronousPeriod, sizeof(SynchronousPeriod))
+ != sizeof(SynchronousPeriod))
+ return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ HostAdapter->SynchronousPeriod[TargetID] = SynchronousPeriod[TargetID];
+ }
+ else
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ if (SetupInformation.SynchronousValuesID0to7[TargetID].Offset > 0)
+ HostAdapter->SynchronousPeriod[TargetID] =
+ 20 + 5 * SetupInformation.SynchronousValuesID0to7[TargetID]
+ .TransferPeriod;
+ /*
+ Indicate the Target Device Inquiry completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_ReportTargetDeviceInfo reports about the Target Devices accessible
+ through Host Adapter.
+*/
+
+static void BusLogic_ReportTargetDeviceInfo(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ int TargetID;
+ /*
+ Inhibit the Target Device Inquiry and Reporting if requested.
+ */
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter) &&
+ HostAdapter->DriverOptions != NULL &&
+ HostAdapter->DriverOptions->LocalOptions.InhibitTargetInquiry)
+ return;
+ /*
+ Report on the Target Devices found.
+ */
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ {
+ BusLogic_TargetFlags_T *TargetFlags = &HostAdapter->TargetFlags[TargetID];
+ if (TargetFlags->TargetExists && !TargetFlags->TargetInfoReported)
+ {
+ int SynchronousTransferRate = 0;
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ {
+ boolean WideTransfersActive;
+ FlashPoint_InquireTargetInfo(
+ HostAdapter->CardHandle, TargetID,
+ &HostAdapter->SynchronousPeriod[TargetID],
+ &HostAdapter->SynchronousOffset[TargetID],
+ &WideTransfersActive);
+ TargetFlags->WideTransfersActive = WideTransfersActive;
+ }
+ else if (TargetFlags->WideTransfersSupported &&
+ (HostAdapter->WidePermitted & (1 << TargetID)) &&
+ strcmp(HostAdapter->FirmwareVersion, "5.06L") < 0)
+ TargetFlags->WideTransfersActive = true;
+ if (HostAdapter->SynchronousPeriod[TargetID] > 0)
+ SynchronousTransferRate =
+ 100000 / HostAdapter->SynchronousPeriod[TargetID];
+ if (TargetFlags->WideTransfersActive)
+ SynchronousTransferRate <<= 1;
+ if (SynchronousTransferRate >= 9950)
+ {
+ SynchronousTransferRate = (SynchronousTransferRate + 50) / 100;
+ BusLogic_Info("Target %d: Queue Depth %d, %sSynchronous at "
+ "%d.%01d MB/sec, offset %d\n",
+ HostAdapter, TargetID,
+ HostAdapter->QueueDepth[TargetID],
+ (TargetFlags->WideTransfersActive ? "Wide " : ""),
+ SynchronousTransferRate / 10,
+ SynchronousTransferRate % 10,
+ HostAdapter->SynchronousOffset[TargetID]);
+ }
+ else if (SynchronousTransferRate > 0)
+ {
+ SynchronousTransferRate = (SynchronousTransferRate + 5) / 10;
+ BusLogic_Info("Target %d: Queue Depth %d, %sSynchronous at "
+ "%d.%02d MB/sec, offset %d\n",
+ HostAdapter, TargetID,
+ HostAdapter->QueueDepth[TargetID],
+ (TargetFlags->WideTransfersActive ? "Wide " : ""),
+ SynchronousTransferRate / 100,
+ SynchronousTransferRate % 100,
+ HostAdapter->SynchronousOffset[TargetID]);
+ }
+ else BusLogic_Info("Target %d: Queue Depth %d, Asynchronous\n",
+ HostAdapter, TargetID,
+ HostAdapter->QueueDepth[TargetID]);
+ TargetFlags->TargetInfoReported = true;
+ }
+ }
+}
+
+
+/*
+ BusLogic_InitializeHostStructure initializes the fields in the SCSI Host
+ structure. The base, io_port, n_io_ports, irq, and dma_channel fields in the
+ SCSI Host structure are intentionally left uninitialized, as this driver
+ handles acquisition and release of these resources explicitly, as well as
+ ensuring exclusive access to the Host Adapter hardware and data structures
+ through explicit acquisition and release of the Host Adapter's Lock.
+*/
+
+static void BusLogic_InitializeHostStructure(BusLogic_HostAdapter_T
+ *HostAdapter,
+ SCSI_Host_T *Host)
+{
+ Host->max_id = HostAdapter->MaxTargetDevices;
+ Host->max_lun = HostAdapter->MaxLogicalUnits;
+ Host->max_channel = 0;
+ Host->unique_id = HostAdapter->IO_Address;
+ Host->this_id = HostAdapter->SCSI_ID;
+ Host->can_queue = HostAdapter->DriverQueueDepth;
+ Host->sg_tablesize = HostAdapter->DriverScatterGatherLimit;
+ Host->unchecked_isa_dma = HostAdapter->BounceBuffersRequired;
+ Host->cmd_per_lun = HostAdapter->UntaggedQueueDepth;
+}
+
+
+/*
+ BusLogic_SelectQueueDepths selects Queue Depths for each Target Device based
+ on the Host Adapter's Total Queue Depth and the number, type, speed, and
+ capabilities of the Target Devices. When called for the last Host Adapter,
+ it reports on the Target Device Information for all BusLogic Host Adapters
+ since all the Target Devices have now been probed.
+*/
+
+static void BusLogic_SelectQueueDepths(SCSI_Host_T *Host,
+ SCSI_Device_T *DeviceList)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Host->hostdata;
+ int TaggedDeviceCount = 0, AutomaticTaggedDeviceCount = 0;
+ int UntaggedDeviceCount = 0, AutomaticTaggedQueueDepth = 0;
+ int AllocatedQueueDepth = 0;
+ SCSI_Device_T *Device;
+ int TargetID;
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ if (HostAdapter->TargetFlags[TargetID].TargetExists)
+ {
+ int QueueDepth = HostAdapter->QueueDepth[TargetID];
+ if (HostAdapter->TargetFlags[TargetID].TaggedQueuingSupported &&
+ (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)))
+ {
+ TaggedDeviceCount++;
+ if (QueueDepth == 0) AutomaticTaggedDeviceCount++;
+ }
+ else
+ {
+ UntaggedDeviceCount++;
+ if (QueueDepth == 0 ||
+ QueueDepth > HostAdapter->UntaggedQueueDepth)
+ {
+ QueueDepth = HostAdapter->UntaggedQueueDepth;
+ HostAdapter->QueueDepth[TargetID] = QueueDepth;
+ }
+ }
+ AllocatedQueueDepth += QueueDepth;
+ if (QueueDepth == 1)
+ HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID);
+ }
+ HostAdapter->TargetDeviceCount = TaggedDeviceCount + UntaggedDeviceCount;
+ if (AutomaticTaggedDeviceCount > 0)
+ {
+ AutomaticTaggedQueueDepth =
+ (HostAdapter->HostAdapterQueueDepth - AllocatedQueueDepth)
+ / AutomaticTaggedDeviceCount;
+ if (AutomaticTaggedQueueDepth > BusLogic_MaxAutomaticTaggedQueueDepth)
+ AutomaticTaggedQueueDepth = BusLogic_MaxAutomaticTaggedQueueDepth;
+ if (AutomaticTaggedQueueDepth < BusLogic_MinAutomaticTaggedQueueDepth)
+ AutomaticTaggedQueueDepth = BusLogic_MinAutomaticTaggedQueueDepth;
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ if (HostAdapter->TargetFlags[TargetID].TargetExists &&
+ HostAdapter->QueueDepth[TargetID] == 0)
+ {
+ AllocatedQueueDepth += AutomaticTaggedQueueDepth;
+ HostAdapter->QueueDepth[TargetID] = AutomaticTaggedQueueDepth;
+ }
+ }
+ for (Device = DeviceList; Device != NULL; Device = Device->next)
+ if (Device->host == Host)
+ Device->queue_depth = HostAdapter->QueueDepth[Device->id];
+ /* Allocate an extra CCB for each Target Device for a Bus Device Reset. */
+ AllocatedQueueDepth += HostAdapter->TargetDeviceCount;
+ if (AllocatedQueueDepth > HostAdapter->DriverQueueDepth)
+ AllocatedQueueDepth = HostAdapter->DriverQueueDepth;
+ BusLogic_CreateAdditionalCCBs(HostAdapter,
+ AllocatedQueueDepth
+ - HostAdapter->AllocatedCCBs,
+ false);
+ if (HostAdapter == BusLogic_LastRegisteredHostAdapter)
+ for (HostAdapter = BusLogic_FirstRegisteredHostAdapter;
+ HostAdapter != NULL;
+ HostAdapter = HostAdapter->Next)
+ BusLogic_ReportTargetDeviceInfo(HostAdapter);
+}
+
+
+/*
+ BusLogic_DetectHostAdapter probes for BusLogic Host Adapters at the standard
+ I/O Addresses where they may be located, initializing, registering, and
+ reporting the configuration of each BusLogic Host Adapter it finds. It
+ returns the number of BusLogic Host Adapters successfully initialized and
+ registered.
+*/
+
+int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
+{
+ int BusLogicHostAdapterCount = 0, DriverOptionsIndex = 0, ProbeIndex;
+ BusLogic_HostAdapter_T *PrototypeHostAdapter;
+ if (BusLogic_ProbeOptions.NoProbe) return 0;
+ BusLogic_ProbeInfoList = (BusLogic_ProbeInfo_T *)
+ kmalloc(BusLogic_MaxHostAdapters * sizeof(BusLogic_ProbeInfo_T),
+ GFP_ATOMIC);
+ if (BusLogic_ProbeInfoList == NULL)
+ {
+ BusLogic_Error("BusLogic: Unable to allocate Probe Info List\n", NULL);
+ return 0;
+ }
+ memset(BusLogic_ProbeInfoList, 0,
+ BusLogic_MaxHostAdapters * sizeof(BusLogic_ProbeInfo_T));
+ PrototypeHostAdapter = (BusLogic_HostAdapter_T *)
+ kmalloc(sizeof(BusLogic_HostAdapter_T), GFP_ATOMIC);
+ if (PrototypeHostAdapter == NULL)
+ {
+ kfree(BusLogic_ProbeInfoList);
+ BusLogic_Error("BusLogic: Unable to allocate Prototype "
+ "Host Adapter\n", NULL);
+ return 0;
+ }
+ memset(PrototypeHostAdapter, 0, sizeof(BusLogic_HostAdapter_T));
+ if (BusLogic_Options != NULL)
+ BusLogic_ParseDriverOptions(BusLogic_Options);
+ BusLogic_InitializeProbeInfoList(PrototypeHostAdapter);
+ for (ProbeIndex = 0; ProbeIndex < BusLogic_ProbeInfoCount; ProbeIndex++)
+ {
+ BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[ProbeIndex];
+ BusLogic_HostAdapter_T *HostAdapter = PrototypeHostAdapter;
+ SCSI_Host_T *Host;
+ if (ProbeInfo->IO_Address == 0) continue;
+ memset(HostAdapter, 0, sizeof(BusLogic_HostAdapter_T));
+ HostAdapter->HostAdapterType = ProbeInfo->HostAdapterType;
+ HostAdapter->HostAdapterBusType = ProbeInfo->HostAdapterBusType;
+ HostAdapter->IO_Address = ProbeInfo->IO_Address;
+ HostAdapter->PCI_Address = ProbeInfo->PCI_Address;
+ HostAdapter->Bus = ProbeInfo->Bus;
+ HostAdapter->Device = ProbeInfo->Device;
+ HostAdapter->IRQ_Channel = ProbeInfo->IRQ_Channel;
+ HostAdapter->AddressCount =
+ BusLogic_HostAdapterAddressCount[HostAdapter->HostAdapterType];
+ /*
+ Probe the Host Adapter. If unsuccessful, abort further initialization.
+ */
+ if (!BusLogic_ProbeHostAdapter(HostAdapter)) continue;
+ /*
+ Hard Reset the Host Adapter. If unsuccessful, abort further
+ initialization.
+ */
+ if (!BusLogic_HardwareResetHostAdapter(HostAdapter, true)) continue;
+ /*
+ Check the Host Adapter. If unsuccessful, abort further initialization.
+ */
+ if (!BusLogic_CheckHostAdapter(HostAdapter)) continue;
+ /*
+ Initialize the Driver Options field if provided.
+ */
+ if (DriverOptionsIndex < BusLogic_DriverOptionsCount)
+ HostAdapter->DriverOptions =
+ &BusLogic_DriverOptions[DriverOptionsIndex++];
+ /*
+ Announce the Driver Version and Date, Author's Name, Copyright Notice,
+ and Electronic Mail Address.
+ */
+ BusLogic_AnnounceDriver(HostAdapter);
+ /*
+ Register usage of the I/O Address range. From this point onward, any
+ failure will be assumed to be due to a problem with the Host Adapter,
+ rather than due to having mistakenly identified this port as belonging
+ to a BusLogic Host Adapter. The I/O Address range will not be
+ released, thereby preventing it from being incorrectly identified as
+ any other type of Host Adapter.
+ */
+ request_region(HostAdapter->IO_Address, HostAdapter->AddressCount,
+ "BusLogic");
+ /*
+ Register the SCSI Host structure.
+ */
+ Host = scsi_register(HostTemplate, sizeof(BusLogic_HostAdapter_T));
+ HostAdapter = (BusLogic_HostAdapter_T *) Host->hostdata;
+ memcpy(HostAdapter, PrototypeHostAdapter, sizeof(BusLogic_HostAdapter_T));
+ HostAdapter->SCSI_Host = Host;
+ HostAdapter->HostNumber = Host->host_no;
+ Host->select_queue_depths = BusLogic_SelectQueueDepths;
+ /*
+ Add Host Adapter to the end of the list of registered BusLogic
+ Host Adapters.
+ */
+ BusLogic_RegisterHostAdapter(HostAdapter);
+ /*
+ Read the Host Adapter Configuration, Configure the Host Adapter,
+ Acquire the System Resources necessary to use the Host Adapter, then
+ Create the Initial CCBs, Initialize the Host Adapter, and finally
+ perform Target Device Inquiry.
+ */
+ if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) &&
+ BusLogic_ReportHostAdapterConfiguration(HostAdapter) &&
+ BusLogic_AcquireResources(HostAdapter) &&
+ BusLogic_CreateInitialCCBs(HostAdapter) &&
+ BusLogic_InitializeHostAdapter(HostAdapter) &&
+ BusLogic_TargetDeviceInquiry(HostAdapter))
+ {
+ /*
+ Initialization has been completed successfully. Release and
+ re-register usage of the I/O Address range so that the Model
+ Name of the Host Adapter will appear, and initialize the SCSI
+ Host structure.
+ */
+ release_region(HostAdapter->IO_Address,
+ HostAdapter->AddressCount);
+ request_region(HostAdapter->IO_Address,
+ HostAdapter->AddressCount,
+ HostAdapter->FullModelName);
+ BusLogic_InitializeHostStructure(HostAdapter, Host);
+ BusLogicHostAdapterCount++;
+ }
+ else
+ {
+ /*
+ An error occurred during Host Adapter Configuration Querying, Host
+ Adapter Configuration, Resource Acquisition, CCB Creation, Host
+ Adapter Initialization, or Target Device Inquiry, so remove Host
+ Adapter from the list of registered BusLogic Host Adapters, destroy
+ the CCBs, Release the System Resources, and Unregister the SCSI
+ Host.
+ */
+ BusLogic_DestroyCCBs(HostAdapter);
+ BusLogic_ReleaseResources(HostAdapter);
+ BusLogic_UnregisterHostAdapter(HostAdapter);
+ scsi_unregister(Host);
+ }
+ }
+ kfree(PrototypeHostAdapter);
+ kfree(BusLogic_ProbeInfoList);
+ BusLogic_ProbeInfoList = NULL;
+ return BusLogicHostAdapterCount;
+}
+
+
+/*
+ BusLogic_ReleaseHostAdapter releases all resources previously acquired to
+ support a specific Host Adapter, including the I/O Address range, and
+ unregisters the BusLogic Host Adapter.
+*/
+
+int BusLogic_ReleaseHostAdapter(SCSI_Host_T *Host)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Host->hostdata;
+ /*
+ FlashPoint Host Adapters must first be released by the FlashPoint
+ SCCB Manager.
+ */
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ FlashPoint_ReleaseHostAdapter(HostAdapter->CardHandle);
+ /*
+ Destroy the CCBs and release any system resources acquired to
+ support Host Adapter.
+ */
+ BusLogic_DestroyCCBs(HostAdapter);
+ BusLogic_ReleaseResources(HostAdapter);
+ /*
+ Release usage of the I/O Address range.
+ */
+ release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
+ /*
+ Remove Host Adapter from the list of registered BusLogic Host Adapters.
+ */
+ BusLogic_UnregisterHostAdapter(HostAdapter);
+ return 0;
+}
+
+
+/*
+ BusLogic_QueueCompletedCCB queues CCB for completion processing.
+*/
+
+static void BusLogic_QueueCompletedCCB(BusLogic_CCB_T *CCB)
+{
+ BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter;
+ CCB->Status = BusLogic_CCB_Completed;
+ CCB->Next = NULL;
+ if (HostAdapter->FirstCompletedCCB == NULL)
+ {
+ HostAdapter->FirstCompletedCCB = CCB;
+ HostAdapter->LastCompletedCCB = CCB;
+ }
+ else
+ {
+ HostAdapter->LastCompletedCCB->Next = CCB;
+ HostAdapter->LastCompletedCCB = CCB;
+ }
+ HostAdapter->ActiveCommands[CCB->TargetID]--;
+}
+
+
+/*
+ BusLogic_ComputeResultCode computes a SCSI Subsystem Result Code from
+ the Host Adapter Status and Target Device Status.
+*/
+
+static int BusLogic_ComputeResultCode(BusLogic_HostAdapter_T *HostAdapter,
+ BusLogic_HostAdapterStatus_T
+ HostAdapterStatus,
+ BusLogic_TargetDeviceStatus_T
+ TargetDeviceStatus)
+{
+ int HostStatus;
+ switch (HostAdapterStatus)
+ {
+ case BusLogic_CommandCompletedNormally:
+ case BusLogic_LinkedCommandCompleted:
+ case BusLogic_LinkedCommandCompletedWithFlag:
+ HostStatus = DID_OK;
+ break;
+ case BusLogic_SCSISelectionTimeout:
+ HostStatus = DID_TIME_OUT;
+ break;
+ case BusLogic_InvalidOutgoingMailboxActionCode:
+ case BusLogic_InvalidCommandOperationCode:
+ case BusLogic_InvalidCommandParameter:
+ BusLogic_Warning("BusLogic Driver Protocol Error 0x%02X\n",
+ HostAdapter, HostAdapterStatus);
+ case BusLogic_DataUnderRun:
+ case BusLogic_DataOverRun:
+ case BusLogic_UnexpectedBusFree:
+ case BusLogic_LinkedCCBhasInvalidLUN:
+ case BusLogic_AutoRequestSenseFailed:
+ case BusLogic_TaggedQueuingMessageRejected:
+ case BusLogic_UnsupportedMessageReceived:
+ case BusLogic_HostAdapterHardwareFailed:
+ case BusLogic_TargetDeviceReconnectedImproperly:
+ case BusLogic_AbortQueueGenerated:
+ case BusLogic_HostAdapterSoftwareError:
+ case BusLogic_HostAdapterHardwareTimeoutError:
+ case BusLogic_SCSIParityErrorDetected:
+ HostStatus = DID_ERROR;
+ break;
+ case BusLogic_InvalidBusPhaseRequested:
+ case BusLogic_TargetFailedResponseToATN:
+ case BusLogic_HostAdapterAssertedRST:
+ case BusLogic_OtherDeviceAssertedRST:
+ case BusLogic_HostAdapterAssertedBusDeviceReset:
+ HostStatus = DID_RESET;
+ break;
+ default:
+ BusLogic_Warning("Unknown Host Adapter Status 0x%02X\n",
+ HostAdapter, HostAdapterStatus);
+ HostStatus = DID_ERROR;
+ break;
+ }
+ return (HostStatus << 16) | TargetDeviceStatus;
+}
+
+
+/*
+ BusLogic_ScanIncomingMailboxes scans the Incoming Mailboxes saving any
+ Incoming Mailbox entries for completion processing.
+*/
+
+static void BusLogic_ScanIncomingMailboxes(BusLogic_HostAdapter_T *HostAdapter)
+{
+ /*
+ Scan through the Incoming Mailboxes in Strict Round Robin fashion, saving
+ any completed CCBs for further processing. It is essential that for each
+ CCB and SCSI Command issued, command completion processing is performed
+ exactly once. Therefore, only Incoming Mailboxes with completion code
+ Command Completed Without Error, Command Completed With Error, or Command
+ Aborted At Host Request are saved for completion processing. When an
+ Incoming Mailbox has a completion code of Aborted Command Not Found, the
+ CCB had already completed or been aborted before the current Abort request
+ was processed, and so completion processing has already occurred and no
+ further action should be taken.
+ */
+ BusLogic_IncomingMailbox_T *NextIncomingMailbox =
+ HostAdapter->NextIncomingMailbox;
+ BusLogic_CompletionCode_T CompletionCode;
+ while ((CompletionCode = NextIncomingMailbox->CompletionCode) !=
+ BusLogic_IncomingMailboxFree)
+ {
+ BusLogic_CCB_T *CCB = (BusLogic_CCB_T *)
+ Bus_to_Virtual(NextIncomingMailbox->CCB);
+ if (CompletionCode != BusLogic_AbortedCommandNotFound)
+ {
+ if (CCB->Status == BusLogic_CCB_Active ||
+ CCB->Status == BusLogic_CCB_Reset)
+ {
+ /*
+ Save the Completion Code for this CCB and queue the CCB
+ for completion processing.
+ */
+ CCB->CompletionCode = CompletionCode;
+ BusLogic_QueueCompletedCCB(CCB);
+ }
+ else
+ {
+ /*
+ If a CCB ever appears in an Incoming Mailbox and is not marked
+ as status Active or Reset, then there is most likely a bug in
+ the Host Adapter firmware.
+ */
+ BusLogic_Warning("Illegal CCB #%ld status %d in "
+ "Incoming Mailbox\n", HostAdapter,
+ CCB->SerialNumber, CCB->Status);
+ }
+ }
+ NextIncomingMailbox->CompletionCode = BusLogic_IncomingMailboxFree;
+ if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
+ NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+ }
+ HostAdapter->NextIncomingMailbox = NextIncomingMailbox;
+}
+
+
+/*
+ BusLogic_ProcessCompletedCCBs iterates over the completed CCBs for Host
+ Adapter setting the SCSI Command Result Codes, deallocating the CCBs, and
+ calling the SCSI Subsystem Completion Routines. The Host Adapter's Lock
+ should already have been acquired by the caller.
+*/
+
+static void BusLogic_ProcessCompletedCCBs(BusLogic_HostAdapter_T *HostAdapter)
+{
+ if (HostAdapter->ProcessCompletedCCBsActive) return;
+ HostAdapter->ProcessCompletedCCBsActive = true;
+ while (HostAdapter->FirstCompletedCCB != NULL)
+ {
+ BusLogic_CCB_T *CCB = HostAdapter->FirstCompletedCCB;
+ SCSI_Command_T *Command = CCB->Command;
+ HostAdapter->FirstCompletedCCB = CCB->Next;
+ if (HostAdapter->FirstCompletedCCB == NULL)
+ HostAdapter->LastCompletedCCB = NULL;
+ /*
+ Process the Completed CCB.
+ */
+ if (CCB->Opcode == BusLogic_BusDeviceReset)
+ {
+ int TargetID = CCB->TargetID;
+ BusLogic_Warning("Bus Device Reset CCB #%ld to Target "
+ "%d Completed\n", HostAdapter,
+ CCB->SerialNumber, TargetID);
+ BusLogic_IncrementErrorCounter(
+ &HostAdapter->TargetStatistics[TargetID].BusDeviceResetsCompleted);
+ HostAdapter->TargetFlags[TargetID].TaggedQueuingActive = false;
+ HostAdapter->CommandsSinceReset[TargetID] = 0;
+ HostAdapter->LastResetCompleted[TargetID] = jiffies;
+ /*
+ Place CCB back on the Host Adapter's free list.
+ */
+ BusLogic_DeallocateCCB(CCB);
+ /*
+ Bus Device Reset CCBs have the Command field non-NULL only when a
+ Bus Device Reset was requested for a Command that did not have a
+ currently active CCB in the Host Adapter (i.e., a Synchronous
+ Bus Device Reset), and hence would not have its Completion Routine
+ called otherwise.
+ */
+ while (Command != NULL)
+ {
+ SCSI_Command_T *NextCommand = Command->reset_chain;
+ Command->reset_chain = NULL;
+ Command->result = DID_RESET << 16;
+ Command->scsi_done(Command);
+ Command = NextCommand;
+ }
+ /*
+ Iterate over the CCBs for this Host Adapter performing completion
+ processing for any CCBs marked as Reset for this Target.
+ */
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Status == BusLogic_CCB_Reset && CCB->TargetID == TargetID)
+ {
+ Command = CCB->Command;
+ BusLogic_DeallocateCCB(CCB);
+ HostAdapter->ActiveCommands[TargetID]--;
+ Command->result = DID_RESET << 16;
+ Command->scsi_done(Command);
+ }
+ HostAdapter->BusDeviceResetPendingCCB[TargetID] = NULL;
+ }
+ else
+ {
+ /*
+ Translate the Completion Code, Host Adapter Status, and Target
+ Device Status into a SCSI Subsystem Result Code.
+ */
+ switch (CCB->CompletionCode)
+ {
+ case BusLogic_IncomingMailboxFree:
+ case BusLogic_AbortedCommandNotFound:
+ case BusLogic_InvalidCCB:
+ BusLogic_Warning("CCB #%ld to Target %d Impossible State\n",
+ HostAdapter, CCB->SerialNumber, CCB->TargetID);
+ break;
+ case BusLogic_CommandCompletedWithoutError:
+ HostAdapter->TargetStatistics[CCB->TargetID]
+ .CommandsCompleted++;
+ HostAdapter->TargetFlags[CCB->TargetID]
+ .CommandSuccessfulFlag = true;
+ Command->result = DID_OK << 16;
+ break;
+ case BusLogic_CommandAbortedAtHostRequest:
+ BusLogic_Warning("CCB #%ld to Target %d Aborted\n",
+ HostAdapter, CCB->SerialNumber, CCB->TargetID);
+ BusLogic_IncrementErrorCounter(
+ &HostAdapter->TargetStatistics[CCB->TargetID]
+ .CommandAbortsCompleted);
+ Command->result = DID_ABORT << 16;
+ break;
+ case BusLogic_CommandCompletedWithError:
+ Command->result =
+ BusLogic_ComputeResultCode(HostAdapter,
+ CCB->HostAdapterStatus,
+ CCB->TargetDeviceStatus);
+ if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout)
+ {
+ HostAdapter->TargetStatistics[CCB->TargetID]
+ .CommandsCompleted++;
+ if (BusLogic_GlobalOptions.TraceErrors)
+ {
+ int i;
+ BusLogic_Notice("CCB #%ld Target %d: Result %X Host "
+ "Adapter Status %02X "
+ "Target Status %02X\n",
+ HostAdapter, CCB->SerialNumber,
+ CCB->TargetID, Command->result,
+ CCB->HostAdapterStatus,
+ CCB->TargetDeviceStatus);
+ BusLogic_Notice("CDB ", HostAdapter);
+ for (i = 0; i < CCB->CDB_Length; i++)
+ BusLogic_Notice(" %02X", HostAdapter, CCB->CDB[i]);
+ BusLogic_Notice("\n", HostAdapter);
+ BusLogic_Notice("Sense ", HostAdapter);
+ for (i = 0; i < CCB->SenseDataLength; i++)
+ BusLogic_Notice(" %02X", HostAdapter,
+ Command->sense_buffer[i]);
+ BusLogic_Notice("\n", HostAdapter);
+ }
+ }
+ break;
+ }
+ /*
+ When an INQUIRY command completes normally, save the
+ CmdQue (Tagged Queuing Supported) and WBus16 (16 Bit
+ Wide Data Transfers Supported) bits.
+ */
+ if (CCB->CDB[0] == INQUIRY && CCB->CDB[1] == 0 &&
+ CCB->HostAdapterStatus == BusLogic_CommandCompletedNormally)
+ {
+ BusLogic_TargetFlags_T *TargetFlags =
+ &HostAdapter->TargetFlags[CCB->TargetID];
+ SCSI_Inquiry_T *InquiryResult =
+ (SCSI_Inquiry_T *) Command->request_buffer;
+ TargetFlags->TargetExists = true;
+ TargetFlags->TaggedQueuingSupported = InquiryResult->CmdQue;
+ TargetFlags->WideTransfersSupported = InquiryResult->WBus16;
+ }
+ /*
+ Place CCB back on the Host Adapter's free list.
+ */
+ BusLogic_DeallocateCCB(CCB);
+ /*
+ Call the SCSI Command Completion Routine.
+ */
+ Command->scsi_done(Command);
+ }
+ }
+ HostAdapter->ProcessCompletedCCBsActive = false;
+}
+
+
+/*
+ BusLogic_InterruptHandler handles hardware interrupts from BusLogic Host
+ Adapters.
+*/
+
+static void BusLogic_InterruptHandler(int IRQ_Channel,
+ void *DeviceIdentifier,
+ Registers_T *InterruptRegisters)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) DeviceIdentifier;
+ ProcessorFlags_T ProcessorFlags;
+ /*
+ Acquire exclusive access to Host Adapter.
+ */
+ BusLogic_AcquireHostAdapterLockIH(HostAdapter, &ProcessorFlags);
+ /*
+ Handle Interrupts appropriately for each Host Adapter type.
+ */
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+ {
+ BusLogic_InterruptRegister_T InterruptRegister;
+ /*
+ Read the Host Adapter Interrupt Register.
+ */
+ InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+ if (InterruptRegister.Bits.InterruptValid)
+ {
+ /*
+ Acknowledge the interrupt and reset the Host Adapter
+ Interrupt Register.
+ */
+ BusLogic_InterruptReset(HostAdapter);
+ /*
+ Process valid External SCSI Bus Reset and Incoming Mailbox
+ Loaded Interrupts. Command Complete Interrupts are noted,
+ and Outgoing Mailbox Available Interrupts are ignored, as
+ they are never enabled.
+ */
+ if (InterruptRegister.Bits.ExternalBusReset)
+ HostAdapter->HostAdapterExternalReset = true;
+ else if (InterruptRegister.Bits.IncomingMailboxLoaded)
+ BusLogic_ScanIncomingMailboxes(HostAdapter);
+ else if (InterruptRegister.Bits.CommandComplete)
+ HostAdapter->HostAdapterCommandCompleted = true;
+ }
+ }
+ else
+ {
+ /*
+ Check if there is a pending interrupt for this Host Adapter.
+ */
+ if (FlashPoint_InterruptPending(HostAdapter->CardHandle))
+ switch (FlashPoint_HandleInterrupt(HostAdapter->CardHandle))
+ {
+ case FlashPoint_NormalInterrupt:
+ break;
+ case FlashPoint_ExternalBusReset:
+ HostAdapter->HostAdapterExternalReset = true;
+ break;
+ case FlashPoint_InternalError:
+ BusLogic_Warning("Internal FlashPoint Error detected"
+ " - Resetting Host Adapter\n", HostAdapter);
+ HostAdapter->HostAdapterInternalError = true;
+ break;
+ }
+ }
+ /*
+ Process any completed CCBs.
+ */
+ if (HostAdapter->FirstCompletedCCB != NULL)
+ BusLogic_ProcessCompletedCCBs(HostAdapter);
+ /*
+ Reset the Host Adapter if requested.
+ */
+ if (HostAdapter->HostAdapterExternalReset ||
+ HostAdapter->HostAdapterInternalError)
+ {
+ BusLogic_ResetHostAdapter(HostAdapter, NULL, 0);
+ HostAdapter->HostAdapterExternalReset = false;
+ HostAdapter->HostAdapterInternalError = false;
+ scsi_mark_host_reset(HostAdapter->SCSI_Host);
+ }
+ /*
+ Release exclusive access to Host Adapter.
+ */
+ BusLogic_ReleaseHostAdapterLockIH(HostAdapter, &ProcessorFlags);
+}
+
+
+/*
+ BusLogic_WriteOutgoingMailbox places CCB and Action Code into an Outgoing
+ Mailbox for execution by Host Adapter. The Host Adapter's Lock should
+ already have been acquired by the caller.
+*/
+
+static boolean BusLogic_WriteOutgoingMailbox(BusLogic_HostAdapter_T
+ *HostAdapter,
+ BusLogic_ActionCode_T ActionCode,
+ BusLogic_CCB_T *CCB)
+{
+ BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
+ NextOutgoingMailbox = HostAdapter->NextOutgoingMailbox;
+ if (NextOutgoingMailbox->ActionCode == BusLogic_OutgoingMailboxFree)
+ {
+ CCB->Status = BusLogic_CCB_Active;
+ /*
+ The CCB field must be written before the Action Code field since
+ the Host Adapter is operating asynchronously and the locking code
+ does not protect against simultaneous access by the Host Adapter.
+ */
+ NextOutgoingMailbox->CCB = Virtual_to_Bus(CCB);
+ NextOutgoingMailbox->ActionCode = ActionCode;
+ BusLogic_StartMailboxCommand(HostAdapter);
+ if (++NextOutgoingMailbox > HostAdapter->LastOutgoingMailbox)
+ NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
+ HostAdapter->NextOutgoingMailbox = NextOutgoingMailbox;
+ if (ActionCode == BusLogic_MailboxStartCommand)
+ {
+ HostAdapter->ActiveCommands[CCB->TargetID]++;
+ if (CCB->Opcode != BusLogic_BusDeviceReset)
+ HostAdapter->TargetStatistics[CCB->TargetID].CommandsAttempted++;
+ }
+ return true;
+ }
+ return false;
+}
+
+
+/*
+ BusLogic_QueueCommand creates a CCB for Command and places it into an
+ Outgoing Mailbox for execution by the associated Host Adapter.
+*/
+
+int BusLogic_QueueCommand(SCSI_Command_T *Command,
+ void (*CompletionRoutine)(SCSI_Command_T *))
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Command->host->hostdata;
+ BusLogic_TargetFlags_T *TargetFlags =
+ &HostAdapter->TargetFlags[Command->target];
+ BusLogic_TargetStatistics_T *TargetStatistics =
+ HostAdapter->TargetStatistics;
+ unsigned char *CDB = Command->cmnd;
+ int CDB_Length = Command->cmd_len;
+ int TargetID = Command->target;
+ int LogicalUnit = Command->lun;
+ void *BufferPointer = Command->request_buffer;
+ int BufferLength = Command->request_bufflen;
+ int SegmentCount = Command->use_sg;
+ ProcessorFlags_T ProcessorFlags;
+ BusLogic_CCB_T *CCB;
+ /*
+ SCSI REQUEST_SENSE commands will be executed automatically by the Host
+ Adapter for any errors, so they should not be executed explicitly unless
+ the Sense Data is zero indicating that no error occurred.
+ */
+ if (CDB[0] == REQUEST_SENSE && Command->sense_buffer[0] != 0)
+ {
+ Command->result = DID_OK << 16;
+ CompletionRoutine(Command);
+ return 0;
+ }
+ /*
+ Acquire exclusive access to Host Adapter.
+ */
+ BusLogic_AcquireHostAdapterLock(HostAdapter, &ProcessorFlags);
+ /*
+ Allocate a CCB from the Host Adapter's free list. In the unlikely event
+ that there are none available and memory allocation fails, wait 1 second
+ and try again. If that fails, the Host Adapter is probably hung so signal
+ an error as a Host Adapter Hard Reset should be initiated soon.
+ */
+ CCB = BusLogic_AllocateCCB(HostAdapter);
+ if (CCB == NULL)
+ {
+ BusLogic_Delay(1);
+ CCB = BusLogic_AllocateCCB(HostAdapter);
+ if (CCB == NULL)
+ {
+ Command->result = DID_ERROR << 16;
+ CompletionRoutine(Command);
+ goto Done;
+ }
+ }
+ /*
+ Initialize the fields in the BusLogic Command Control Block (CCB).
+ */
+ if (SegmentCount == 0)
+ {
+ CCB->Opcode = BusLogic_InitiatorCCB;
+ CCB->DataLength = BufferLength;
+ CCB->DataPointer = Virtual_to_Bus(BufferPointer);
+ }
+ else
+ {
+ SCSI_ScatterList_T *ScatterList = (SCSI_ScatterList_T *) BufferPointer;
+ int Segment;
+ CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather;
+ CCB->DataLength = SegmentCount * sizeof(BusLogic_ScatterGatherSegment_T);
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+ CCB->DataPointer = Virtual_to_Bus(CCB->ScatterGatherList);
+ else CCB->DataPointer = Virtual_to_32Bit_Virtual(CCB->ScatterGatherList);
+ for (Segment = 0; Segment < SegmentCount; Segment++)
+ {
+ CCB->ScatterGatherList[Segment].SegmentByteCount =
+ ScatterList[Segment].length;
+ CCB->ScatterGatherList[Segment].SegmentDataPointer =
+ Virtual_to_Bus(ScatterList[Segment].address);
+ }
+ }
+ switch (CDB[0])
+ {
+ case READ_6:
+ case READ_10:
+ CCB->DataDirection = BusLogic_DataInLengthChecked;
+ TargetStatistics[TargetID].ReadCommands++;
+ BusLogic_IncrementByteCounter(
+ &TargetStatistics[TargetID].TotalBytesRead, BufferLength);
+ BusLogic_IncrementSizeBucket(
+ TargetStatistics[TargetID].ReadCommandSizeBuckets, BufferLength);
+ break;
+ case WRITE_6:
+ case WRITE_10:
+ CCB->DataDirection = BusLogic_DataOutLengthChecked;
+ TargetStatistics[TargetID].WriteCommands++;
+ BusLogic_IncrementByteCounter(
+ &TargetStatistics[TargetID].TotalBytesWritten, BufferLength);
+ BusLogic_IncrementSizeBucket(
+ TargetStatistics[TargetID].WriteCommandSizeBuckets, BufferLength);
+ break;
+ default:
+ CCB->DataDirection = BusLogic_UncheckedDataTransfer;
+ break;
+ }
+ CCB->CDB_Length = CDB_Length;
+ CCB->SenseDataLength = sizeof(Command->sense_buffer);
+ CCB->HostAdapterStatus = 0;
+ CCB->TargetDeviceStatus = 0;
+ CCB->TargetID = TargetID;
+ CCB->LogicalUnit = LogicalUnit;
+ CCB->TagEnable = false;
+ CCB->LegacyTagEnable = false;
+ /*
+ BusLogic recommends that after a Reset the first couple of commands that
+ are sent to a Target Device be sent in a non Tagged Queue fashion so that
+ the Host Adapter and Target Device can establish Synchronous and Wide
+ Transfer before Queue Tag messages can interfere with the Synchronous and
+ Wide Negotiation messages. By waiting to enable Tagged Queuing until after
+ the first BusLogic_MaxTaggedQueueDepth commands have been queued, it is
+ assured that after a Reset any pending commands are requeued before Tagged
+ Queuing is enabled and that the Tagged Queuing message will not occur while
+ the partition table is being printed. In addition, some devices do not
+ properly handle the transition from non-tagged to tagged commands, so it is
+ necessary to wait until there are no pending commands for a target device
+ before queuing tagged commands.
+ */
+ if (HostAdapter->CommandsSinceReset[TargetID]++ >=
+ BusLogic_MaxTaggedQueueDepth &&
+ !TargetFlags->TaggedQueuingActive &&
+ HostAdapter->ActiveCommands[TargetID] == 0 &&
+ TargetFlags->TaggedQueuingSupported &&
+ (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)))
+ {
+ TargetFlags->TaggedQueuingActive = true;
+ BusLogic_Notice("Tagged Queuing now active for Target %d\n",
+ HostAdapter, TargetID);
+ }
+ if (TargetFlags->TaggedQueuingActive)
+ {
+ BusLogic_QueueTag_T QueueTag = BusLogic_SimpleQueueTag;
+ /*
+ When using Tagged Queuing with Simple Queue Tags, it appears that disk
+ drive controllers do not guarantee that a queued command will not
+ remain in a disconnected state indefinitely if commands that read or
+ write nearer the head position continue to arrive without interruption.
+ Therefore, for each Target Device this driver keeps track of the last
+ time either the queue was empty or an Ordered Queue Tag was issued. If
+ more than 4 seconds (one fifth of the 20 second disk timeout) have
+ elapsed since this last sequence point, this command will be issued
+ with an Ordered Queue Tag rather than a Simple Queue Tag, which forces
+ the Target Device to complete all previously queued commands before
+ this command may be executed.
+ */
+ if (HostAdapter->ActiveCommands[TargetID] == 0)
+ HostAdapter->LastSequencePoint[TargetID] = jiffies;
+ else if (jiffies - HostAdapter->LastSequencePoint[TargetID] > 4*HZ)
+ {
+ HostAdapter->LastSequencePoint[TargetID] = jiffies;
+ QueueTag = BusLogic_OrderedQueueTag;
+ }
+ if (HostAdapter->ExtendedLUNSupport)
+ {
+ CCB->TagEnable = true;
+ CCB->QueueTag = QueueTag;
+ }
+ else
+ {
+ CCB->LegacyTagEnable = true;
+ CCB->LegacyQueueTag = QueueTag;
+ }
+ }
+ memcpy(CCB->CDB, CDB, CDB_Length);
+ CCB->SenseDataPointer = Virtual_to_Bus(&Command->sense_buffer);
+ CCB->Command = Command;
+ Command->scsi_done = CompletionRoutine;
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+ {
+ /*
+ Place the CCB in an Outgoing Mailbox. The higher levels of the SCSI
+ Subsystem should not attempt to queue more commands than can be placed
+ in Outgoing Mailboxes, so there should always be one free. In the
+ unlikely event that there are none available, wait 1 second and try
+ again. If that fails, the Host Adapter is probably hung so signal an
+ error as a Host Adapter Hard Reset should be initiated soon.
+ */
+ if (!BusLogic_WriteOutgoingMailbox(
+ HostAdapter, BusLogic_MailboxStartCommand, CCB))
+ {
+ BusLogic_Warning("Unable to write Outgoing Mailbox - "
+ "Pausing for 1 second\n", HostAdapter);
+ BusLogic_Delay(1);
+ if (!BusLogic_WriteOutgoingMailbox(
+ HostAdapter, BusLogic_MailboxStartCommand, CCB))
+ {
+ BusLogic_Warning("Still unable to write Outgoing Mailbox - "
+ "Host Adapter Dead?\n", HostAdapter);
+ BusLogic_DeallocateCCB(CCB);
+ Command->result = DID_ERROR << 16;
+ Command->scsi_done(Command);
+ }
+ }
+ }
+ else
+ {
+ /*
+ Call the FlashPoint SCCB Manager to start execution of the CCB.
+ */
+ CCB->Status = BusLogic_CCB_Active;
+ HostAdapter->ActiveCommands[TargetID]++;
+ TargetStatistics[TargetID].CommandsAttempted++;
+ FlashPoint_StartCCB(HostAdapter->CardHandle, CCB);
+ /*
+ The Command may have already completed and BusLogic_QueueCompletedCCB
+ been called, or it may still be pending.
+ */
+ if (CCB->Status == BusLogic_CCB_Completed)
+ BusLogic_ProcessCompletedCCBs(HostAdapter);
+ }
+ /*
+ Release exclusive access to Host Adapter.
+ */
+Done:
+ BusLogic_ReleaseHostAdapterLock(HostAdapter, &ProcessorFlags);
+ return 0;
+}
+
+
+/*
+ BusLogic_AbortCommand aborts Command if possible.
+*/
+
+int BusLogic_AbortCommand(SCSI_Command_T *Command)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Command->host->hostdata;
+ int TargetID = Command->target;
+ ProcessorFlags_T ProcessorFlags;
+ BusLogic_CCB_T *CCB;
+ int Result;
+ BusLogic_IncrementErrorCounter(
+ &HostAdapter->TargetStatistics[TargetID].CommandAbortsRequested);
+ /*
+ Acquire exclusive access to Host Adapter.
+ */
+ BusLogic_AcquireHostAdapterLock(HostAdapter, &ProcessorFlags);
+ /*
+ If this Command has already completed, then no Abort is necessary.
+ */
+ if (Command->serial_number != Command->serial_number_at_timeout)
+ {
+ BusLogic_Warning("Unable to Abort Command to Target %d - "
+ "Already Completed\n", HostAdapter, TargetID);
+ Result = SCSI_ABORT_NOT_RUNNING;
+ goto Done;
+ }
+ /*
+ Attempt to find an Active CCB for this Command. If no Active CCB for this
+ Command is found, then no Abort is necessary.
+ */
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Command == Command) break;
+ if (CCB == NULL)
+ {
+ BusLogic_Warning("Unable to Abort Command to Target %d - "
+ "No CCB Found\n", HostAdapter, TargetID);
+ Result = SCSI_ABORT_NOT_RUNNING;
+ goto Done;
+ }
+ else if (CCB->Status == BusLogic_CCB_Completed)
+ {
+ BusLogic_Warning("Unable to Abort Command to Target %d - "
+ "CCB Completed\n", HostAdapter, TargetID);
+ Result = SCSI_ABORT_NOT_RUNNING;
+ goto Done;
+ }
+ else if (CCB->Status == BusLogic_CCB_Reset)
+ {
+ BusLogic_Warning("Unable to Abort Command to Target %d - "
+ "CCB Reset\n", HostAdapter, TargetID);
+ Result = SCSI_ABORT_PENDING;
+ goto Done;
+ }
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+ {
+ /*
+ Attempt to Abort this CCB. MultiMaster Firmware versions prior to 5.xx
+ do not generate Abort Tag messages, but only generate the non-tagged
+ Abort message. Since non-tagged commands are not sent by the Host
+ Adapter until the queue of outstanding tagged commands has completed,
+ and the Abort message is treated as a non-tagged command, it is
+ effectively impossible to abort commands when Tagged Queuing is active.
+ Firmware version 5.xx does generate Abort Tag messages, so it is
+ possible to abort commands when Tagged Queuing is active.
+ */
+ if (HostAdapter->TargetFlags[TargetID].TaggedQueuingActive &&
+ HostAdapter->FirmwareVersion[0] < '5')
+ {
+ BusLogic_Warning("Unable to Abort CCB #%ld to Target %d - "
+ "Abort Tag Not Supported\n",
+ HostAdapter, CCB->SerialNumber, TargetID);
+ Result = SCSI_ABORT_SNOOZE;
+ }
+ else if (BusLogic_WriteOutgoingMailbox(
+ HostAdapter, BusLogic_MailboxAbortCommand, CCB))
+ {
+ BusLogic_Warning("Aborting CCB #%ld to Target %d\n",
+ HostAdapter, CCB->SerialNumber, TargetID);
+ BusLogic_IncrementErrorCounter(
+ &HostAdapter->TargetStatistics[TargetID].CommandAbortsAttempted);
+ Result = SCSI_ABORT_PENDING;
+ }
+ else
+ {
+ BusLogic_Warning("Unable to Abort CCB #%ld to Target %d - "
+ "No Outgoing Mailboxes\n",
+ HostAdapter, CCB->SerialNumber, TargetID);
+ Result = SCSI_ABORT_BUSY;
+ }
+ }
+ else
+ {
+ /*
+ Call the FlashPoint SCCB Manager to abort execution of the CCB.
+ */
+ BusLogic_Warning("Aborting CCB #%ld to Target %d\n",
+ HostAdapter, CCB->SerialNumber, TargetID);
+ BusLogic_IncrementErrorCounter(
+ &HostAdapter->TargetStatistics[TargetID].CommandAbortsAttempted);
+ FlashPoint_AbortCCB(HostAdapter->CardHandle, CCB);
+ /*
+ The Abort may have already been completed and
+ BusLogic_QueueCompletedCCB been called, or it
+ may still be pending.
+ */
+ Result = SCSI_ABORT_PENDING;
+ if (CCB->Status == BusLogic_CCB_Completed)
+ {
+ BusLogic_ProcessCompletedCCBs(HostAdapter);
+ Result = SCSI_ABORT_SUCCESS;
+ }
+ }
+ /*
+ Release exclusive access to Host Adapter.
+ */
+Done:
+ BusLogic_ReleaseHostAdapterLock(HostAdapter, &ProcessorFlags);
+ return Result;
+}
+
+
+/*
+ BusLogic_ResetHostAdapter resets Host Adapter if possible, marking all
+ currently executing SCSI Commands as having been Reset.
+*/
+
+static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *HostAdapter,
+ SCSI_Command_T *Command,
+ unsigned int ResetFlags)
+{
+ ProcessorFlags_T ProcessorFlags;
+ BusLogic_CCB_T *CCB;
+ int TargetID, Result;
+ boolean HardReset;
+ if (HostAdapter->HostAdapterExternalReset)
+ {
+ BusLogic_IncrementErrorCounter(&HostAdapter->ExternalHostAdapterResets);
+ HardReset = false;
+ }
+ else if (HostAdapter->HostAdapterInternalError)
+ {
+ BusLogic_IncrementErrorCounter(&HostAdapter->HostAdapterInternalErrors);
+ HardReset = true;
+ }
+ else
+ {
+ BusLogic_IncrementErrorCounter(
+ &HostAdapter->TargetStatistics[Command->target]
+ .HostAdapterResetsRequested);
+ HardReset = true;
+ }
+ /*
+ Acquire exclusive access to Host Adapter.
+ */
+ BusLogic_AcquireHostAdapterLock(HostAdapter, &ProcessorFlags);
+ /*
+ If this is an Asynchronous Reset and this Command has already completed,
+ then no Reset is necessary.
+ */
+ if (ResetFlags & SCSI_RESET_ASYNCHRONOUS)
+ {
+ TargetID = Command->target;
+ if (Command->serial_number != Command->serial_number_at_timeout)
+ {
+ BusLogic_Warning("Unable to Reset Command to Target %d - "
+ "Already Completed or Reset\n",
+ HostAdapter, TargetID);
+ Result = SCSI_RESET_NOT_RUNNING;
+ goto Done;
+ }
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Command == Command) break;
+ if (CCB == NULL)
+ {
+ BusLogic_Warning("Unable to Reset Command to Target %d - "
+ "No CCB Found\n", HostAdapter, TargetID);
+ Result = SCSI_RESET_NOT_RUNNING;
+ goto Done;
+ }
+ else if (CCB->Status == BusLogic_CCB_Completed)
+ {
+ BusLogic_Warning("Unable to Reset Command to Target %d - "
+ "CCB Completed\n", HostAdapter, TargetID);
+ Result = SCSI_RESET_NOT_RUNNING;
+ goto Done;
+ }
+ else if (CCB->Status == BusLogic_CCB_Reset &&
+ HostAdapter->BusDeviceResetPendingCCB[TargetID] == NULL)
+ {
+ BusLogic_Warning("Unable to Reset Command to Target %d - "
+ "Reset Pending\n", HostAdapter, TargetID);
+ Result = SCSI_RESET_PENDING;
+ goto Done;
+ }
+ }
+ if (Command == NULL)
+ {
+ if (HostAdapter->HostAdapterInternalError)
+ BusLogic_Warning("Resetting %s due to Host Adapter Internal Error\n",
+ HostAdapter, HostAdapter->FullModelName);
+ else BusLogic_Warning("Resetting %s due to External SCSI Bus Reset\n",
+ HostAdapter, HostAdapter->FullModelName);
+ }
+ else
+ {
+ BusLogic_Warning("Resetting %s due to Target %d\n", HostAdapter,
+ HostAdapter->FullModelName, Command->target);
+ BusLogic_IncrementErrorCounter(
+ &HostAdapter->TargetStatistics[Command->target]
+ .HostAdapterResetsAttempted);
+ }
+ /*
+ Attempt to Reset and Reinitialize the Host Adapter.
+ */
+ if (!(BusLogic_HardwareResetHostAdapter(HostAdapter, HardReset) &&
+ BusLogic_InitializeHostAdapter(HostAdapter)))
+ {
+ BusLogic_Error("Resetting %s Failed\n", HostAdapter,
+ HostAdapter->FullModelName);
+ Result = SCSI_RESET_ERROR;
+ goto Done;
+ }
+ if (Command != NULL)
+ BusLogic_IncrementErrorCounter(
+ &HostAdapter->TargetStatistics[Command->target]
+ .HostAdapterResetsCompleted);
+ /*
+ Mark all currently executing CCBs as having been Reset.
+ */
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Status == BusLogic_CCB_Active)
+ CCB->Status = BusLogic_CCB_Reset;
+ /*
+ Wait a few seconds between the Host Adapter Hard Reset which initiates
+ a SCSI Bus Reset and issuing any SCSI Commands. Some SCSI devices get
+ confused if they receive SCSI Commands too soon after a SCSI Bus Reset.
+ Note that a timer interrupt may occur here, but all active CCBs have
+ already been marked Reset and so a reentrant call will return Pending.
+ */
+ if (HardReset)
+ BusLogic_Delay(HostAdapter->BusSettleTime);
+ /*
+ If this is a Synchronous Reset, perform completion processing for
+ the Command being Reset.
+ */
+ if (ResetFlags & SCSI_RESET_SYNCHRONOUS)
+ {
+ Command->result = DID_RESET << 16;
+ Command->scsi_done(Command);
+ }
+ /*
+ Perform completion processing for all CCBs marked as Reset.
+ */
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Status == BusLogic_CCB_Reset)
+ {
+ Command = CCB->Command;
+ BusLogic_DeallocateCCB(CCB);
+ while (Command != NULL)
+ {
+ SCSI_Command_T *NextCommand = Command->reset_chain;
+ Command->reset_chain = NULL;
+ Command->result = DID_RESET << 16;
+ Command->scsi_done(Command);
+ Command = NextCommand;
+ }
+ }
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ {
+ HostAdapter->LastResetAttempted[TargetID] = jiffies;
+ HostAdapter->LastResetCompleted[TargetID] = jiffies;
+ }
+ Result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
+ /*
+ Release exclusive access to Host Adapter.
+ */
+Done:
+ BusLogic_ReleaseHostAdapterLock(HostAdapter, &ProcessorFlags);
+ return Result;
+}
+
+
+/*
+ BusLogic_SendBusDeviceReset sends a Bus Device Reset to the Target
+ Device associated with Command.
+*/
+
+static int BusLogic_SendBusDeviceReset(BusLogic_HostAdapter_T *HostAdapter,
+ SCSI_Command_T *Command,
+ unsigned int ResetFlags)
+{
+ int TargetID = Command->target;
+ BusLogic_CCB_T *CCB, *XCCB;
+ ProcessorFlags_T ProcessorFlags;
+ int Result = -1;
+ BusLogic_IncrementErrorCounter(
+ &HostAdapter->TargetStatistics[TargetID].BusDeviceResetsRequested);
+ /*
+ Acquire exclusive access to Host Adapter.
+ */
+ BusLogic_AcquireHostAdapterLock(HostAdapter, &ProcessorFlags);
+ /*
+ If this is an Asynchronous Reset and this Command has already completed,
+ then no Reset is necessary.
+ */
+ if (ResetFlags & SCSI_RESET_ASYNCHRONOUS)
+ {
+ if (Command->serial_number != Command->serial_number_at_timeout)
+ {
+ BusLogic_Warning("Unable to Reset Command to Target %d - "
+ "Already Completed\n", HostAdapter, TargetID);
+ Result = SCSI_RESET_NOT_RUNNING;
+ goto Done;
+ }
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Command == Command) break;
+ if (CCB == NULL)
+ {
+ BusLogic_Warning("Unable to Reset Command to Target %d - "
+ "No CCB Found\n", HostAdapter, TargetID);
+ Result = SCSI_RESET_NOT_RUNNING;
+ goto Done;
+ }
+ else if (CCB->Status == BusLogic_CCB_Completed)
+ {
+ BusLogic_Warning("Unable to Reset Command to Target %d - "
+ "CCB Completed\n", HostAdapter, TargetID);
+ Result = SCSI_RESET_NOT_RUNNING;
+ goto Done;
+ }
+ else if (CCB->Status == BusLogic_CCB_Reset)
+ {
+ BusLogic_Warning("Unable to Reset Command to Target %d - "
+ "Reset Pending\n", HostAdapter, TargetID);
+ Result = SCSI_RESET_PENDING;
+ goto Done;
+ }
+ else if (HostAdapter->BusDeviceResetPendingCCB[TargetID] != NULL)
+ {
+ BusLogic_Warning("Bus Device Reset already pending to Target %d\n",
+ HostAdapter, TargetID);
+ goto Done;
+ }
+ }
+ /*
+ If this is a Synchronous Reset and a Bus Device Reset is already pending
+ for this Target Device, do not send a second one. Add this Command to
+ the list of Commands for which completion processing must be performed
+ when the Bus Device Reset CCB completes.
+ */
+ if (ResetFlags & SCSI_RESET_SYNCHRONOUS)
+ if ((CCB = HostAdapter->BusDeviceResetPendingCCB[TargetID]) != NULL)
+ {
+ Command->reset_chain = CCB->Command;
+ CCB->Command = Command;
+ BusLogic_Warning("Unable to Reset Command to Target %d - "
+ "Reset Pending\n", HostAdapter, TargetID);
+ Result = SCSI_RESET_PENDING;
+ goto Done;
+ }
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+ {
+ /*
+ MultiMaster Firmware versions prior to 5.xx treat a Bus Device Reset as
+ a non-tagged command. Since non-tagged commands are not sent by the
+ Host Adapter until the queue of outstanding tagged commands has
+ completed, it is effectively impossible to send a Bus Device Reset
+ while there are tagged commands outstanding. Therefore, in that case a
+ full Host Adapter Hard Reset and SCSI Bus Reset must be done.
+ */
+ if (HostAdapter->TargetFlags[TargetID].TaggedQueuingActive &&
+ HostAdapter->ActiveCommands[TargetID] > 0 &&
+ HostAdapter->FirmwareVersion[0] < '5')
+ goto Done;
+ }
+ /*
+ Allocate a CCB from the Host Adapter's free list. In the unlikely event
+ that there are none available and memory allocation fails, attempt a full
+ Host Adapter Hard Reset and SCSI Bus Reset.
+ */
+ CCB = BusLogic_AllocateCCB(HostAdapter);
+ if (CCB == NULL) goto Done;
+ BusLogic_Warning("Sending Bus Device Reset CCB #%ld to Target %d\n",
+ HostAdapter, CCB->SerialNumber, TargetID);
+ CCB->Opcode = BusLogic_BusDeviceReset;
+ CCB->TargetID = TargetID;
+ /*
+ For Synchronous Resets, arrange for the interrupt handler to perform
+ completion processing for the Command being Reset.
+ */
+ if (ResetFlags & SCSI_RESET_SYNCHRONOUS)
+ {
+ Command->reset_chain = NULL;
+ CCB->Command = Command;
+ }
+ if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+ {
+ /*
+ Attempt to write an Outgoing Mailbox with the Bus Device Reset CCB.
+ If sending a Bus Device Reset is impossible, attempt a full Host
+ Adapter Hard Reset and SCSI Bus Reset.
+ */
+ if (!(BusLogic_WriteOutgoingMailbox(
+ HostAdapter, BusLogic_MailboxStartCommand, CCB)))
+ {
+ BusLogic_Warning("Unable to write Outgoing Mailbox for "
+ "Bus Device Reset\n", HostAdapter);
+ BusLogic_DeallocateCCB(CCB);
+ goto Done;
+ }
+ }
+ else
+ {
+ /*
+ Call the FlashPoint SCCB Manager to start execution of the CCB.
+ */
+ CCB->Status = BusLogic_CCB_Active;
+ HostAdapter->ActiveCommands[TargetID]++;
+ FlashPoint_StartCCB(HostAdapter->CardHandle, CCB);
+ }
+ /*
+ If there is a currently executing CCB in the Host Adapter for this Command
+ (i.e. this is an Asynchronous Reset), then an Incoming Mailbox entry may be
+ made with a completion code of BusLogic_HostAdapterAssertedBusDeviceReset.
+ If there is no active CCB for this Command (i.e. this is a Synchronous
+ Reset), then the Bus Device Reset CCB's Command field will have been set
+ to the Command so that the interrupt for the completion of the Bus Device
+ Reset can call the Completion Routine for the Command. On successful
+ execution of a Bus Device Reset, older firmware versions did return the
+ pending CCBs with the appropriate completion code, but more recent firmware
+ versions only return the Bus Device Reset CCB itself. This driver handles
+ both cases by marking all the currently executing CCBs to this Target
+ Device as Reset. When the Bus Device Reset CCB is processed by the
+ interrupt handler, any remaining CCBs marked as Reset will have completion
+ processing performed.
+ */
+ BusLogic_IncrementErrorCounter(
+ &HostAdapter->TargetStatistics[TargetID].BusDeviceResetsAttempted);
+ HostAdapter->BusDeviceResetPendingCCB[TargetID] = CCB;
+ HostAdapter->LastResetAttempted[TargetID] = jiffies;
+ for (XCCB = HostAdapter->All_CCBs; XCCB != NULL; XCCB = XCCB->NextAll)
+ if (XCCB->Status == BusLogic_CCB_Active && XCCB->TargetID == TargetID)
+ XCCB->Status = BusLogic_CCB_Reset;
+ /*
+ FlashPoint Host Adapters may have already completed the Bus Device
+ Reset and BusLogic_QueueCompletedCCB been called, or it may still be
+ pending.
+ */
+ Result = SCSI_RESET_PENDING;
+ if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+ if (CCB->Status == BusLogic_CCB_Completed)
+ {
+ BusLogic_ProcessCompletedCCBs(HostAdapter);
+ Result = SCSI_RESET_SUCCESS;
+ }
+ /*
+ If a Bus Device Reset was not possible for some reason, force a full
+ Host Adapter Hard Reset and SCSI Bus Reset.
+ */
+Done:
+ if (Result < 0)
+ Result = BusLogic_ResetHostAdapter(HostAdapter, Command, ResetFlags);
+ /*
+ Release exclusive access to Host Adapter.
+ */
+ BusLogic_ReleaseHostAdapterLock(HostAdapter, &ProcessorFlags);
+ return Result;
+}
+
+
+/*
+ BusLogic_ResetCommand takes appropriate action to reset Command.
+*/
+
+int BusLogic_ResetCommand(SCSI_Command_T *Command, unsigned int ResetFlags)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Command->host->hostdata;
+ int TargetID = Command->target;
+ BusLogic_ErrorRecoveryStrategy_T
+ ErrorRecoveryStrategy = HostAdapter->ErrorRecoveryStrategy[TargetID];
+ /*
+ Disable Tagged Queuing if it is active for this Target Device and if
+ it has been less than 10 minutes since the last reset occurred, or since
+ the system was initialized if no prior resets have occurred.
+ */
+ if (HostAdapter->TargetFlags[TargetID].TaggedQueuingActive &&
+ jiffies - HostAdapter->LastResetCompleted[TargetID] < 10*60*HZ)
+ {
+ HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID);
+ HostAdapter->TargetFlags[TargetID].TaggedQueuingActive = false;
+ BusLogic_Warning("Tagged Queuing now disabled for Target %d\n",
+ HostAdapter, TargetID);
+ }
+ switch (ErrorRecoveryStrategy)
+ {
+ case BusLogic_ErrorRecovery_Default:
+ if (ResetFlags & SCSI_RESET_SUGGEST_HOST_RESET)
+ return BusLogic_ResetHostAdapter(HostAdapter, Command, ResetFlags);
+ else if (ResetFlags & SCSI_RESET_SUGGEST_BUS_RESET)
+ return BusLogic_ResetHostAdapter(HostAdapter, Command, ResetFlags);
+ /* Fall through to Bus Device Reset case. */
+ case BusLogic_ErrorRecovery_BusDeviceReset:
+ /*
+ The Bus Device Reset Error Recovery Strategy only graduates to a Hard
+ Reset when no commands have completed successfully since the last Bus
+ Device Reset and it has been at least 100 milliseconds. This prevents
+ a sequence of commands that all timeout together from immediately
+ forcing a Hard Reset before the Bus Device Reset has had a chance to
+ clear the error condition.
+ */
+ if (HostAdapter->TargetFlags[TargetID].CommandSuccessfulFlag ||
+ jiffies - HostAdapter->LastResetAttempted[TargetID] < HZ/10)
+ {
+ HostAdapter->TargetFlags[TargetID].CommandSuccessfulFlag = false;
+ return BusLogic_SendBusDeviceReset(HostAdapter, Command, ResetFlags);
+ }
+ /* Fall through to Hard Reset case. */
+ case BusLogic_ErrorRecovery_HardReset:
+ return BusLogic_ResetHostAdapter(HostAdapter, Command, ResetFlags);
+ case BusLogic_ErrorRecovery_None:
+ BusLogic_Warning("Error Recovery for Target %d Suppressed\n",
+ HostAdapter, TargetID);
+ break;
+ }
+ return SCSI_RESET_PUNT;
+}
+
+
+/*
+ BusLogic_BIOSDiskParameters returns the Heads/Sectors/Cylinders BIOS Disk
+ Parameters for Disk. The default disk geometry is 64 heads, 32 sectors, and
+ the appropriate number of cylinders so as not to exceed drive capacity. In
+ order for disks equal to or larger than 1 GB to be addressable by the BIOS
+ without exceeding the BIOS limitation of 1024 cylinders, Extended Translation
+ may be enabled in AutoSCSI on FlashPoint Host Adapters and on "W" and "C"
+ series MultiMaster Host Adapters, or by a dip switch setting on "S" and "A"
+ series MultiMaster Host Adapters. With Extended Translation enabled, drives
+ between 1 GB inclusive and 2 GB exclusive are given a disk geometry of 128
+ heads and 32 sectors, and drives above 2 GB inclusive are given a disk
+ geometry of 255 heads and 63 sectors. However, if the BIOS detects that the
+ Extended Translation setting does not match the geometry in the partition
+ table, then the translation inferred from the partition table will be used by
+ the BIOS, and a warning may be displayed.
+*/
+
+int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, KernelDevice_T Device,
+ int *Parameters)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Disk->device->host->hostdata;
+ BIOS_DiskParameters_T *DiskParameters = (BIOS_DiskParameters_T *) Parameters;
+ struct buffer_head *BufferHead;
+ if (HostAdapter->ExtendedTranslationEnabled &&
+ Disk->capacity >= 2*1024*1024 /* 1 GB in 512 byte sectors */)
+ {
+ if (Disk->capacity >= 4*1024*1024 /* 2 GB in 512 byte sectors */)
+ {
+ DiskParameters->Heads = 255;
+ DiskParameters->Sectors = 63;
+ }
+ else
+ {
+ DiskParameters->Heads = 128;
+ DiskParameters->Sectors = 32;
+ }
+ }
+ else
+ {
+ DiskParameters->Heads = 64;
+ DiskParameters->Sectors = 32;
+ }
+ DiskParameters->Cylinders =
+ Disk->capacity / (DiskParameters->Heads * DiskParameters->Sectors);
+ /*
+ Attempt to read the first 1024 bytes from the disk device.
+ */
+ BufferHead = bread(MKDEV(MAJOR(Device), MINOR(Device) & ~0x0F), 0, 1024);
+ if (BufferHead == NULL) return 0;
+ /*
+ If the boot sector partition table flag is valid, search for a partition
+ table entry whose end_head matches one of the standard BusLogic geometry
+ translations (64/32, 128/32, or 255/63).
+ */
+ if (*(unsigned short *) (BufferHead->b_data + 0x1FE) == 0xAA55)
+ {
+ PartitionTable_T *FirstPartitionEntry =
+ (PartitionTable_T *) (BufferHead->b_data + 0x1BE);
+ PartitionTable_T *PartitionEntry = FirstPartitionEntry;
+ int SavedCylinders = DiskParameters->Cylinders, PartitionNumber;
+ unsigned char PartitionEntryEndHead, PartitionEntryEndSector;
+ for (PartitionNumber = 0; PartitionNumber < 4; PartitionNumber++)
+ {
+ PartitionEntryEndHead = PartitionEntry->end_head;
+ PartitionEntryEndSector = PartitionEntry->end_sector & 0x3F;
+ if (PartitionEntryEndHead == 64-1)
+ {
+ DiskParameters->Heads = 64;
+ DiskParameters->Sectors = 32;
+ break;
+ }
+ else if (PartitionEntryEndHead == 128-1)
+ {
+ DiskParameters->Heads = 128;
+ DiskParameters->Sectors = 32;
+ break;
+ }
+ else if (PartitionEntryEndHead == 255-1)
+ {
+ DiskParameters->Heads = 255;
+ DiskParameters->Sectors = 63;
+ break;
+ }
+ PartitionEntry++;
+ }
+ if (PartitionNumber == 4)
+ {
+ PartitionEntryEndHead = FirstPartitionEntry->end_head;
+ PartitionEntryEndSector = FirstPartitionEntry->end_sector & 0x3F;
+ }
+ DiskParameters->Cylinders =
+ Disk->capacity / (DiskParameters->Heads * DiskParameters->Sectors);
+ if (PartitionNumber < 4 &&
+ PartitionEntryEndSector == DiskParameters->Sectors)
+ {
+ if (DiskParameters->Cylinders != SavedCylinders)
+ BusLogic_Warning("Adopting Geometry %d/%d from Partition Table\n",
+ HostAdapter,
+ DiskParameters->Heads, DiskParameters->Sectors);
+ }
+ else if (PartitionEntryEndHead > 0 || PartitionEntryEndSector > 0)
+ {
+ BusLogic_Warning("Warning: Partition Table appears to "
+ "have Geometry %d/%d which is\n", HostAdapter,
+ PartitionEntryEndHead + 1,
+ PartitionEntryEndSector);
+ BusLogic_Warning("not compatible with current BusLogic "
+ "Host Adapter Geometry %d/%d\n", HostAdapter,
+ DiskParameters->Heads, DiskParameters->Sectors);
+ }
+ }
+ brelse(BufferHead);
+ return 0;
+}
+
+
+/*
+ BugLogic_ProcDirectoryInfo implements /proc/scsi/BusLogic/<N>.
+*/
+
+int BusLogic_ProcDirectoryInfo(char *ProcBuffer, char **StartPointer,
+ off_t Offset, int BytesAvailable,
+ int HostNumber, int WriteFlag)
+{
+ BusLogic_HostAdapter_T *HostAdapter;
+ BusLogic_TargetStatistics_T *TargetStatistics;
+ int TargetID, Length;
+ char *Buffer;
+ for (HostAdapter = BusLogic_FirstRegisteredHostAdapter;
+ HostAdapter != NULL;
+ HostAdapter = HostAdapter->Next)
+ if (HostAdapter->HostNumber == HostNumber) break;
+ if (HostAdapter == NULL)
+ {
+ BusLogic_Error("Cannot find Host Adapter for SCSI Host %d\n",
+ NULL, HostNumber);
+ return 0;
+ }
+ TargetStatistics = HostAdapter->TargetStatistics;
+ if (WriteFlag)
+ {
+ HostAdapter->ExternalHostAdapterResets = 0;
+ HostAdapter->HostAdapterInternalErrors = 0;
+ memset(TargetStatistics, 0,
+ BusLogic_MaxTargetDevices * sizeof(BusLogic_TargetStatistics_T));
+ return 0;
+ }
+ Buffer = HostAdapter->MessageBuffer;
+ Length = HostAdapter->MessageBufferLength;
+ Length += sprintf(&Buffer[Length], "\n\
+Current Driver Queue Depth: %d\n\
+Currently Allocated CCBs: %d\n",
+ HostAdapter->DriverQueueDepth,
+ HostAdapter->AllocatedCCBs);
+ Length += sprintf(&Buffer[Length], "\n\n\
+ DATA TRANSFER STATISTICS\n\
+\n\
+Target Tagged Queuing Queue Depth Active Attempted Completed\n\
+====== ============== =========== ====== ========= =========\n");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ {
+ BusLogic_TargetFlags_T *TargetFlags = &HostAdapter->TargetFlags[TargetID];
+ if (!TargetFlags->TargetExists) continue;
+ Length +=
+ sprintf(&Buffer[Length], " %2d %s", TargetID,
+ (TargetFlags->TaggedQueuingSupported
+ ? (TargetFlags->TaggedQueuingActive
+ ? " Active"
+ : (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)
+ ? " Permitted" : " Disabled"))
+ : "Not Supported"));
+ Length += sprintf(&Buffer[Length],
+ " %3d %3u %9u %9u\n",
+ HostAdapter->QueueDepth[TargetID],
+ HostAdapter->ActiveCommands[TargetID],
+ TargetStatistics[TargetID].CommandsAttempted,
+ TargetStatistics[TargetID].CommandsCompleted);
+ }
+ Length += sprintf(&Buffer[Length], "\n\
+Target Read Commands Write Commands Total Bytes Read Total Bytes Written\n\
+====== ============= ============== =================== ===================\n");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ {
+ BusLogic_TargetFlags_T *TargetFlags = &HostAdapter->TargetFlags[TargetID];
+ if (!TargetFlags->TargetExists) continue;
+ Length +=
+ sprintf(&Buffer[Length], " %2d %9u %9u", TargetID,
+ TargetStatistics[TargetID].ReadCommands,
+ TargetStatistics[TargetID].WriteCommands);
+ if (TargetStatistics[TargetID].TotalBytesRead.Billions > 0)
+ Length +=
+ sprintf(&Buffer[Length], " %9u%09u",
+ TargetStatistics[TargetID].TotalBytesRead.Billions,
+ TargetStatistics[TargetID].TotalBytesRead.Units);
+ else
+ Length +=
+ sprintf(&Buffer[Length], " %9u",
+ TargetStatistics[TargetID].TotalBytesRead.Units);
+ if (TargetStatistics[TargetID].TotalBytesWritten.Billions > 0)
+ Length +=
+ sprintf(&Buffer[Length], " %9u%09u\n",
+ TargetStatistics[TargetID].TotalBytesWritten.Billions,
+ TargetStatistics[TargetID].TotalBytesWritten.Units);
+ else
+ Length +=
+ sprintf(&Buffer[Length], " %9u\n",
+ TargetStatistics[TargetID].TotalBytesWritten.Units);
+ }
+ Length += sprintf(&Buffer[Length], "\n\
+Target Command 0-1KB 1-2KB 2-4KB 4-8KB 8-16KB\n\
+====== ======= ========= ========= ========= ========= =========\n");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ {
+ BusLogic_TargetFlags_T *TargetFlags = &HostAdapter->TargetFlags[TargetID];
+ if (!TargetFlags->TargetExists) continue;
+ Length +=
+ sprintf(&Buffer[Length],
+ " %2d Read %9u %9u %9u %9u %9u\n", TargetID,
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[0],
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[1],
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[2],
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[3],
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[4]);
+ Length +=
+ sprintf(&Buffer[Length],
+ " %2d Write %9u %9u %9u %9u %9u\n", TargetID,
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[0],
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[1],
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[2],
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[3],
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[4]);
+ }
+ Length += sprintf(&Buffer[Length], "\n\
+Target Command 16-32KB 32-64KB 64-128KB 128-256KB 256KB+\n\
+====== ======= ========= ========= ========= ========= =========\n");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ {
+ BusLogic_TargetFlags_T *TargetFlags = &HostAdapter->TargetFlags[TargetID];
+ if (!TargetFlags->TargetExists) continue;
+ Length +=
+ sprintf(&Buffer[Length],
+ " %2d Read %9u %9u %9u %9u %9u\n", TargetID,
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[5],
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[6],
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[7],
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[8],
+ TargetStatistics[TargetID].ReadCommandSizeBuckets[9]);
+ Length +=
+ sprintf(&Buffer[Length],
+ " %2d Write %9u %9u %9u %9u %9u\n", TargetID,
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[5],
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[6],
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[7],
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[8],
+ TargetStatistics[TargetID].WriteCommandSizeBuckets[9]);
+ }
+ Length += sprintf(&Buffer[Length], "\n\n\
+ ERROR RECOVERY STATISTICS\n\
+\n\
+ Command Aborts Bus Device Resets Host Adapter Resets\n\
+Target Requested Completed Requested Completed Requested Completed\n\
+ ID \\\\\\\\ Attempted //// \\\\\\\\ Attempted //// \\\\\\\\ Attempted ////\n\
+====== ===== ===== ===== ===== ===== ===== ===== ===== =====\n");
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+ {
+ BusLogic_TargetFlags_T *TargetFlags = &HostAdapter->TargetFlags[TargetID];
+ if (!TargetFlags->TargetExists) continue;
+ Length +=
+ sprintf(&Buffer[Length], "\
+ %2d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n", TargetID,
+ TargetStatistics[TargetID].CommandAbortsRequested,
+ TargetStatistics[TargetID].CommandAbortsAttempted,
+ TargetStatistics[TargetID].CommandAbortsCompleted,
+ TargetStatistics[TargetID].BusDeviceResetsRequested,
+ TargetStatistics[TargetID].BusDeviceResetsAttempted,
+ TargetStatistics[TargetID].BusDeviceResetsCompleted,
+ TargetStatistics[TargetID].HostAdapterResetsRequested,
+ TargetStatistics[TargetID].HostAdapterResetsAttempted,
+ TargetStatistics[TargetID].HostAdapterResetsCompleted);
+ }
+ Length += sprintf(&Buffer[Length], "\nExternal Host Adapter Resets: %d\n",
+ HostAdapter->ExternalHostAdapterResets);
+ Length += sprintf(&Buffer[Length], "Host Adapter Internal Errors: %d\n",
+ HostAdapter->HostAdapterInternalErrors);
+ if (Length >= BusLogic_MessageBufferSize)
+ BusLogic_Error("Message Buffer length %d exceeds size %d\n",
+ HostAdapter, Length, BusLogic_MessageBufferSize);
+ if ((Length -= Offset) <= 0) return 0;
+ if (Length >= BytesAvailable) Length = BytesAvailable;
+ *StartPointer = &HostAdapter->MessageBuffer[Offset];
+ return Length;
+}
+
+
+/*
+ BusLogic_Message prints Driver Messages.
+*/
+
+static void BusLogic_Message(BusLogic_MessageLevel_T MessageLevel,
+ char *Format,
+ BusLogic_HostAdapter_T *HostAdapter,
+ ...)
+{
+ static char Buffer[BusLogic_LineBufferSize];
+ static boolean BeginningOfLine = true;
+ va_list Arguments;
+ int Length = 0;
+ va_start(Arguments, HostAdapter);
+ Length = vsprintf(Buffer, Format, Arguments);
+ va_end(Arguments);
+ if (MessageLevel == BusLogic_AnnounceLevel)
+ {
+ static int AnnouncementLines = 0;
+ strcpy(&HostAdapter->MessageBuffer[HostAdapter->MessageBufferLength],
+ Buffer);
+ HostAdapter->MessageBufferLength += Length;
+ if (++AnnouncementLines <= 2)
+ printk("%sscsi: %s", BusLogic_MessageLevelMap[MessageLevel], Buffer);
+ }
+ else if (MessageLevel == BusLogic_InfoLevel)
+ {
+ strcpy(&HostAdapter->MessageBuffer[HostAdapter->MessageBufferLength],
+ Buffer);
+ HostAdapter->MessageBufferLength += Length;
+ if (BeginningOfLine)
+ {
+ if (Buffer[0] != '\n' || Length > 1)
+ printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel],
+ HostAdapter->HostNumber, Buffer);
+ }
+ else printk("%s", Buffer);
+ }
+ else
+ {
+ if (BeginningOfLine)
+ {
+ if (HostAdapter != NULL && HostAdapter->HostAdapterInitialized)
+ printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel],
+ HostAdapter->HostNumber, Buffer);
+ else printk("%s%s", BusLogic_MessageLevelMap[MessageLevel], Buffer);
+ }
+ else printk("%s", Buffer);
+ }
+ BeginningOfLine = (Buffer[Length-1] == '\n');
+}
+
+
+/*
+ BusLogic_ParseKeyword parses an individual option keyword. It returns true
+ and updates the pointer if the keyword is recognized and false otherwise.
+*/
+
+static boolean BusLogic_ParseKeyword(char **StringPointer, char *Keyword)
+{
+ char *Pointer = *StringPointer;
+ while (*Keyword != '\0')
+ {
+ char StringChar = *Pointer++;
+ char KeywordChar = *Keyword++;
+ if (StringChar >= 'A' && StringChar <= 'Z')
+ StringChar += 'a' - 'Z';
+ if (KeywordChar >= 'A' && KeywordChar <= 'Z')
+ KeywordChar += 'a' - 'Z';
+ if (StringChar != KeywordChar) return false;
+ }
+ *StringPointer = Pointer;
+ return true;
+}
+
+
+/*
+ BusLogic_ParseDriverOptions handles processing of BusLogic Driver Options
+ specifications.
+
+ BusLogic Driver Options may be specified either via the Linux Kernel Command
+ Line or via the Loadable Kernel Module Installation Facility. Driver Options
+ for multiple host adapters may be specified either by separating the option
+ strings by a semicolon, or by specifying multiple "BusLogic=" strings on the
+ command line. Individual option specifications for a single host adapter are
+ separated by commas. The Probing and Debugging Options apply to all host
+ adapters whereas the remaining options apply individually only to the
+ selected host adapter.
+
+ The BusLogic Driver Probing Options comprise the following:
+
+ IO:<integer>
+
+ The "IO:" option specifies an ISA I/O Address to be probed for a non-PCI
+ MultiMaster Host Adapter. If neither "IO:" nor "NoProbeISA" options are
+ specified, then the standard list of BusLogic MultiMaster ISA I/O Addresses
+ will be probed (0x330, 0x334, 0x230, 0x234, 0x130, and 0x134). Multiple
+ "IO:" options may be specified to precisely determine the I/O Addresses to
+ be probed, but the probe order will always follow the standard list.
+
+ NoProbe
+
+ The "NoProbe" option disables all probing and therefore no BusLogic Host
+ Adapters will be detected.
+
+ NoProbeISA
+
+ The "NoProbeISA" option disables probing of the standard BusLogic ISA I/O
+ Addresses and therefore only PCI MultiMaster and FlashPoint Host Adapters
+ will be detected.
+
+ NoProbePCI
+
+ The "NoProbePCI" options disables the interrogation of PCI Configuration
+ Space and therefore only ISA Multimaster Host Adapters will be detected, as
+ well as PCI Multimaster Host Adapters that have their ISA Compatible I/O
+ Port set to "Primary" or "Alternate".
+
+ NoSortPCI
+
+ The "NoSortPCI" option forces PCI MultiMaster Host Adapters to be
+ enumerated in the order provided by the PCI BIOS, ignoring any setting of
+ the AutoSCSI "Use Bus And Device # For PCI Scanning Seq." option.
+
+ MultiMasterFirst
+
+ The "MultiMasterFirst" option forces MultiMaster Host Adapters to be probed
+ before FlashPoint Host Adapters. By default, if both FlashPoint and PCI
+ MultiMaster Host Adapters are present, this driver will probe for
+ FlashPoint Host Adapters first unless the BIOS primary disk is controlled
+ by the first PCI MultiMaster Host Adapter, in which case MultiMaster Host
+ Adapters will be probed first.
+
+ FlashPointFirst
+
+ The "FlashPointFirst" option forces FlashPoint Host Adapters to be probed
+ before MultiMaster Host Adapters.
+
+ The BusLogic Driver Tagged Queuing Options allow for explicitly specifying
+ the Queue Depth and whether Tagged Queuing is permitted for each Target
+ Device (assuming that the Target Device supports Tagged Queuing). The Queue
+ Depth is the number of SCSI Commands that are allowed to be concurrently
+ presented for execution (either to the Host Adapter or Target Device). Note
+ that explicitly enabling Tagged Queuing may lead to problems; the option to
+ enable or disable Tagged Queuing is provided primarily to allow disabling
+ Tagged Queuing on Target Devices that do not implement it correctly. The
+ following options are available:
+
+ QueueDepth:<integer>
+
+ The "QueueDepth:" or QD:" option specifies the Queue Depth to use for all
+ Target Devices that support Tagged Queuing, as well as the maximum Queue
+ Depth for devices that do not support Tagged Queuing. If no Queue Depth
+ option is provided, the Queue Depth will be determined automatically based
+ on the Host Adapter's Total Queue Depth and the number, type, speed, and
+ capabilities of the detected Target Devices. For Host Adapters that
+ require ISA Bounce Buffers, the Queue Depth is automatically set by default
+ to BusLogic_TaggedQueueDepthBB or BusLogic_UntaggedQueueDepthBB to avoid
+ excessive preallocation of DMA Bounce Buffer memory. Target Devices that
+ do not support Tagged Queuing always have their Queue Depth set to
+ BusLogic_UntaggedQueueDepth or BusLogic_UntaggedQueueDepthBB, unless a
+ lower Queue Depth option is provided. A Queue Depth of 1 automatically
+ disables Tagged Queuing.
+
+ QueueDepth:[<integer>,<integer>...]
+
+ The "QueueDepth:[...]" or "QD:[...]" option specifies the Queue Depth
+ individually for each Target Device. If an <integer> is omitted, the
+ associated Target Device will have its Queue Depth selected automatically.
+
+ TaggedQueuing:Default
+
+ The "TaggedQueuing:Default" or "TQ:Default" option permits Tagged Queuing
+ based on the firmware version of the BusLogic Host Adapter and based on
+ whether the Queue Depth allows queuing multiple commands.
+
+ TaggedQueuing:Enable
+
+ The "TaggedQueuing:Enable" or "TQ:Enable" option enables Tagged Queuing for
+ all Target Devices on this Host Adapter, overriding any limitation that
+ would otherwise be imposed based on the Host Adapter firmware version.
+
+ TaggedQueuing:Disable
+
+ The "TaggedQueuing:Disable" or "TQ:Disable" option disables Tagged Queuing
+ for all Target Devices on this Host Adapter.
+
+ TaggedQueuing:<Target-Spec>
+
+ The "TaggedQueuing:<Target-Spec>" or "TQ:<Target-Spec>" option controls
+ Tagged Queuing individually for each Target Device. <Target-Spec> is a
+ sequence of "Y", "N", and "X" characters. "Y" enables Tagged Queuing, "N"
+ disables Tagged Queuing, and "X" accepts the default based on the firmware
+ version. The first character refers to Target Device 0, the second to
+ Target Device 1, and so on; if the sequence of "Y", "N", and "X" characters
+ does not cover all the Target Devices, unspecified characters are assumed
+ to be "X".
+
+ The BusLogic Driver Error Recovery Option allows for explicitly specifying
+ the Error Recovery action to be performed when BusLogic_ResetCommand is
+ called due to a SCSI Command failing to complete successfully. The following
+ options are available:
+
+ ErrorRecovery:Default
+
+ The "ErrorRecovery:Default" or "ER:Default" option selects between the Hard
+ Reset and Bus Device Reset options based on the recommendation of the SCSI
+ Subsystem.
+
+ ErrorRecovery:HardReset
+
+ The "ErrorRecovery:HardReset" or "ER:HardReset" option will initiate a Host
+ Adapter Hard Reset which also causes a SCSI Bus Reset.
+
+ ErrorRecovery:BusDeviceReset
+
+ The "ErrorRecovery:BusDeviceReset" or "ER:BusDeviceReset" option will send
+ a Bus Device Reset message to the individual Target Device causing the
+ error. If Error Recovery is again initiated for this Target Device and no
+ SCSI Command to this Target Device has completed successfully since the Bus
+ Device Reset message was sent, then a Hard Reset will be attempted.
+
+ ErrorRecovery:None
+
+ The "ErrorRecovery:None" or "ER:None" option suppresses Error Recovery.
+ This option should only be selected if a SCSI Bus Reset or Bus Device Reset
+ will cause the Target Device or a critical operation to suffer a complete
+ and unrecoverable failure.
+
+ ErrorRecovery:<Target-Spec>
+
+ The "ErrorRecovery:<Target-Spec>" or "ER:<Target-Spec>" option controls
+ Error Recovery individually for each Target Device. <Target-Spec> is a
+ sequence of "D", "H", "B", and "N" characters. "D" selects Default, "H"
+ selects Hard Reset, "B" selects Bus Device Reset, and "N" selects None.
+ The first character refers to Target Device 0, the second to Target Device
+ 1, and so on; if the sequence of "D", "H", "B", and "N" characters does not
+ cover all the possible Target Devices, unspecified characters are assumed
+ to be "D".
+
+ The BusLogic Driver Miscellaneous Options comprise the following:
+
+ BusSettleTime:<seconds>
+
+ The "BusSettleTime:" or "BST:" option specifies the Bus Settle Time in
+ seconds. The Bus Settle Time is the amount of time to wait between a Host
+ Adapter Hard Reset which initiates a SCSI Bus Reset and issuing any SCSI
+ Commands. If unspecified, it defaults to BusLogic_DefaultBusSettleTime.
+
+ InhibitTargetInquiry
+
+ The "InhibitTargetInquiry" option inhibits the execution of an Inquire
+ Target Devices or Inquire Installed Devices command on MultiMaster Host
+ Adapters. This may be necessary with some older Target Devices that do not
+ respond correctly when Logical Units above 0 are addressed.
+
+ The BusLogic Driver Debugging Options comprise the following:
+
+ TraceProbe
+
+ The "TraceProbe" option enables tracing of Host Adapter Probing.
+
+ TraceHardwareReset
+
+ The "TraceHardwareReset" option enables tracing of Host Adapter Hardware
+ Reset.
+
+ TraceConfiguration
+
+ The "TraceConfiguration" option enables tracing of Host Adapter
+ Configuration.
+
+ TraceErrors
+
+ The "TraceErrors" option enables tracing of SCSI Commands that return an
+ error from the Target Device. The CDB and Sense Data will be printed for
+ each SCSI Command that fails.
+
+ Debug
+
+ The "Debug" option enables all debugging options.
+
+ The following examples demonstrate setting the Queue Depth for Target Devices
+ 1 and 2 on the first host adapter to 7 and 15, the Queue Depth for all Target
+ Devices on the second host adapter to 31, and the Bus Settle Time on the
+ second host adapter to 30 seconds.
+
+ Linux Kernel Command Line:
+
+ linux BusLogic=QueueDepth:[,7,15];QueueDepth:31,BusSettleTime:30
+
+ LILO Linux Boot Loader (in /etc/lilo.conf):
+
+ append = "BusLogic=QueueDepth:[,7,15];QueueDepth:31,BusSettleTime:30"
+
+ INSMOD Loadable Kernel Module Installation Facility:
+
+ insmod BusLogic.o \
+ 'BusLogic_Options="QueueDepth:[,7,15];QueueDepth:31,BusSettleTime:30"'
+
+ NOTE: Module Utilities 2.1.71 or later is required for correct parsing
+ of driver options containing commas.
+
+*/
+
+static void BusLogic_ParseDriverOptions(char *OptionsString)
+{
+ while (true)
+ {
+ BusLogic_DriverOptions_T *DriverOptions =
+ &BusLogic_DriverOptions[BusLogic_DriverOptionsCount++];
+ int TargetID;
+ memset(DriverOptions, 0, sizeof(BusLogic_DriverOptions_T));
+ for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++)
+ DriverOptions->ErrorRecoveryStrategy[TargetID] =
+ BusLogic_ErrorRecovery_Default;
+ while (*OptionsString != '\0' && *OptionsString != ';')
+ {
+ /* Probing Options. */
+ if (BusLogic_ParseKeyword(&OptionsString, "IO:"))
+ {
+ BusLogic_IO_Address_T IO_Address =
+ simple_strtoul(OptionsString, &OptionsString, 0);
+ BusLogic_ProbeOptions.LimitedProbeISA = true;
+ switch (IO_Address)
+ {
+ case 0x330:
+ BusLogic_ProbeOptions.Probe330 = true;
+ break;
+ case 0x334:
+ BusLogic_ProbeOptions.Probe334 = true;
+ break;
+ case 0x230:
+ BusLogic_ProbeOptions.Probe230 = true;
+ break;
+ case 0x234:
+ BusLogic_ProbeOptions.Probe234 = true;
+ break;
+ case 0x130:
+ BusLogic_ProbeOptions.Probe130 = true;
+ break;
+ case 0x134:
+ BusLogic_ProbeOptions.Probe134 = true;
+ break;
+ default:
+ BusLogic_Error("BusLogic: Invalid Driver Options "
+ "(illegal I/O Address 0x%X)\n",
+ NULL, IO_Address);
+ return;
+ }
+ }
+ else if (BusLogic_ParseKeyword(&OptionsString, "NoProbeISA"))
+ BusLogic_ProbeOptions.NoProbeISA = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "NoProbePCI"))
+ BusLogic_ProbeOptions.NoProbePCI = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "NoProbe"))
+ BusLogic_ProbeOptions.NoProbe = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "NoSortPCI"))
+ BusLogic_ProbeOptions.NoSortPCI = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "MultiMasterFirst"))
+ BusLogic_ProbeOptions.MultiMasterFirst = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "FlashPointFirst"))
+ BusLogic_ProbeOptions.FlashPointFirst = true;
+ /* Tagged Queuing Options. */
+ else if (BusLogic_ParseKeyword(&OptionsString, "QueueDepth:[") ||
+ BusLogic_ParseKeyword(&OptionsString, "QD:["))
+ {
+ for (TargetID = 0;
+ TargetID < BusLogic_MaxTargetDevices;
+ TargetID++)
+ {
+ unsigned short QueueDepth =
+ simple_strtoul(OptionsString, &OptionsString, 0);
+ if (QueueDepth > BusLogic_MaxTaggedQueueDepth)
+ {
+ BusLogic_Error("BusLogic: Invalid Driver Options "
+ "(illegal Queue Depth %d)\n",
+ NULL, QueueDepth);
+ return;
+ }
+ DriverOptions->QueueDepth[TargetID] = QueueDepth;
+ if (*OptionsString == ',')
+ OptionsString++;
+ else if (*OptionsString == ']')
+ break;
+ else
+ {
+ BusLogic_Error("BusLogic: Invalid Driver Options "
+ "(',' or ']' expected at '%s')\n",
+ NULL, OptionsString);
+ return;
+ }
+ }
+ if (*OptionsString != ']')
+ {
+ BusLogic_Error("BusLogic: Invalid Driver Options "
+ "(']' expected at '%s')\n",
+ NULL, OptionsString);
+ return;
+ }
+ else OptionsString++;
+ }
+ else if (BusLogic_ParseKeyword(&OptionsString, "QueueDepth:") ||
+ BusLogic_ParseKeyword(&OptionsString, "QD:"))
+ {
+ unsigned short QueueDepth =
+ simple_strtoul(OptionsString, &OptionsString, 0);
+ if (QueueDepth == 0 || QueueDepth > BusLogic_MaxTaggedQueueDepth)
+ {
+ BusLogic_Error("BusLogic: Invalid Driver Options "
+ "(illegal Queue Depth %d)\n",
+ NULL, QueueDepth);
+ return;
+ }
+ DriverOptions->CommonQueueDepth = QueueDepth;
+ for (TargetID = 0;
+ TargetID < BusLogic_MaxTargetDevices;
+ TargetID++)
+ DriverOptions->QueueDepth[TargetID] = QueueDepth;
+ }
+ else if (BusLogic_ParseKeyword(&OptionsString, "TaggedQueuing:") ||
+ BusLogic_ParseKeyword(&OptionsString, "TQ:"))
+ {
+ if (BusLogic_ParseKeyword(&OptionsString, "Default"))
+ {
+ DriverOptions->TaggedQueuingPermitted = 0x0000;
+ DriverOptions->TaggedQueuingPermittedMask = 0x0000;
+ }
+ else if (BusLogic_ParseKeyword(&OptionsString, "Enable"))
+ {
+ DriverOptions->TaggedQueuingPermitted = 0xFFFF;
+ DriverOptions->TaggedQueuingPermittedMask = 0xFFFF;
+ }
+ else if (BusLogic_ParseKeyword(&OptionsString, "Disable"))
+ {
+ DriverOptions->TaggedQueuingPermitted = 0x0000;
+ DriverOptions->TaggedQueuingPermittedMask = 0xFFFF;
+ }
+ else
+ {
+ unsigned short TargetBit;
+ for (TargetID = 0, TargetBit = 1;
+ TargetID < BusLogic_MaxTargetDevices;
+ TargetID++, TargetBit <<= 1)
+ switch (*OptionsString++)
+ {
+ case 'Y':
+ DriverOptions->TaggedQueuingPermitted |= TargetBit;
+ DriverOptions->TaggedQueuingPermittedMask |= TargetBit;
+ break;
+ case 'N':
+ DriverOptions->TaggedQueuingPermitted &= ~TargetBit;
+ DriverOptions->TaggedQueuingPermittedMask |= TargetBit;
+ break;
+ case 'X':
+ break;
+ default:
+ OptionsString--;
+ TargetID = BusLogic_MaxTargetDevices;
+ break;
+ }
+ }
+ }
+ /* Error Recovery Option. */
+ else if (BusLogic_ParseKeyword(&OptionsString, "ErrorRecovery:") ||
+ BusLogic_ParseKeyword(&OptionsString, "ER:"))
+ {
+ if (BusLogic_ParseKeyword(&OptionsString, "Default"))
+ for (TargetID = 0;
+ TargetID < BusLogic_MaxTargetDevices;
+ TargetID++)
+ DriverOptions->ErrorRecoveryStrategy[TargetID] =
+ BusLogic_ErrorRecovery_Default;
+ else if (BusLogic_ParseKeyword(&OptionsString, "HardReset"))
+ for (TargetID = 0;
+ TargetID < BusLogic_MaxTargetDevices;
+ TargetID++)
+ DriverOptions->ErrorRecoveryStrategy[TargetID] =
+ BusLogic_ErrorRecovery_HardReset;
+ else if (BusLogic_ParseKeyword(&OptionsString, "BusDeviceReset"))
+ for (TargetID = 0;
+ TargetID < BusLogic_MaxTargetDevices;
+ TargetID++)
+ DriverOptions->ErrorRecoveryStrategy[TargetID] =
+ BusLogic_ErrorRecovery_BusDeviceReset;
+ else if (BusLogic_ParseKeyword(&OptionsString, "None"))
+ for (TargetID = 0;
+ TargetID < BusLogic_MaxTargetDevices;
+ TargetID++)
+ DriverOptions->ErrorRecoveryStrategy[TargetID] =
+ BusLogic_ErrorRecovery_None;
+ else
+ for (TargetID = 0;
+ TargetID < BusLogic_MaxTargetDevices;
+ TargetID++)
+ switch (*OptionsString++)
+ {
+ case 'D':
+ DriverOptions->ErrorRecoveryStrategy[TargetID] =
+ BusLogic_ErrorRecovery_Default;
+ break;
+ case 'H':
+ DriverOptions->ErrorRecoveryStrategy[TargetID] =
+ BusLogic_ErrorRecovery_HardReset;
+ break;
+ case 'B':
+ DriverOptions->ErrorRecoveryStrategy[TargetID] =
+ BusLogic_ErrorRecovery_BusDeviceReset;
+ break;
+ case 'N':
+ DriverOptions->ErrorRecoveryStrategy[TargetID] =
+ BusLogic_ErrorRecovery_None;
+ break;
+ default:
+ OptionsString--;
+ TargetID = BusLogic_MaxTargetDevices;
+ break;
+ }
+ }
+ /* Miscellaneous Options. */
+ else if (BusLogic_ParseKeyword(&OptionsString, "BusSettleTime:") ||
+ BusLogic_ParseKeyword(&OptionsString, "BST:"))
+ {
+ unsigned short BusSettleTime =
+ simple_strtoul(OptionsString, &OptionsString, 0);
+ if (BusSettleTime > 5 * 60)
+ {
+ BusLogic_Error("BusLogic: Invalid Driver Options "
+ "(illegal Bus Settle Time %d)\n",
+ NULL, BusSettleTime);
+ return;
+ }
+ DriverOptions->BusSettleTime = BusSettleTime;
+ }
+ else if (BusLogic_ParseKeyword(&OptionsString,
+ "InhibitTargetInquiry"))
+ DriverOptions->LocalOptions.InhibitTargetInquiry = true;
+ /* Debugging Options. */
+ else if (BusLogic_ParseKeyword(&OptionsString, "TraceProbe"))
+ BusLogic_GlobalOptions.TraceProbe = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "TraceHardwareReset"))
+ BusLogic_GlobalOptions.TraceHardwareReset = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "TraceConfiguration"))
+ BusLogic_GlobalOptions.TraceConfiguration = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "TraceErrors"))
+ BusLogic_GlobalOptions.TraceErrors = true;
+ else if (BusLogic_ParseKeyword(&OptionsString, "Debug"))
+ {
+ BusLogic_GlobalOptions.TraceProbe = true;
+ BusLogic_GlobalOptions.TraceHardwareReset = true;
+ BusLogic_GlobalOptions.TraceConfiguration = true;
+ BusLogic_GlobalOptions.TraceErrors = true;
+ }
+ if (*OptionsString == ',')
+ OptionsString++;
+ else if (*OptionsString != ';' && *OptionsString != '\0')
+ {
+ BusLogic_Error("BusLogic: Unexpected Driver Option '%s' "
+ "ignored\n", NULL, OptionsString);
+ *OptionsString = '\0';
+ }
+ }
+ if (!(BusLogic_DriverOptionsCount == 0 ||
+ BusLogic_ProbeInfoCount == 0 ||
+ BusLogic_DriverOptionsCount == BusLogic_ProbeInfoCount))
+ {
+ BusLogic_Error("BusLogic: Invalid Driver Options "
+ "(all or no I/O Addresses must be specified)\n", NULL);
+ return;
+ }
+ /*
+ Tagged Queuing is disabled when the Queue Depth is 1 since queuing
+ multiple commands is not possible.
+ */
+ for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++)
+ if (DriverOptions->QueueDepth[TargetID] == 1)
+ {
+ unsigned short TargetBit = 1 << TargetID;
+ DriverOptions->TaggedQueuingPermitted &= ~TargetBit;
+ DriverOptions->TaggedQueuingPermittedMask |= TargetBit;
+ }
+ if (*OptionsString == ';') OptionsString++;
+ if (*OptionsString == '\0') return;
+ }
+}
+
+
+/*
+ BusLogic_Setup handles processing of Kernel Command Line Arguments.
+*/
+
+void BusLogic_Setup(char *CommandLineString, int *CommandLineIntegers)
+{
+ if (CommandLineIntegers[0] != 0)
+ {
+ BusLogic_Error("BusLogic: Obsolete Command Line Entry "
+ "Format Ignored\n", NULL);
+ return;
+ }
+ if (CommandLineString == NULL || *CommandLineString == '\0') return;
+ BusLogic_ParseDriverOptions(CommandLineString);
+}
+
+
+/*
+ Include Module support if requested.
+*/
+
+#ifdef MODULE
+
+SCSI_Host_Template_T driver_template = BUSLOGIC;
+
+#include "scsi_module.c"
+
+#endif
diff --git a/linux/src/drivers/scsi/BusLogic.h b/linux/src/drivers/scsi/BusLogic.h
new file mode 100644
index 00000000..f60ee071
--- /dev/null
+++ b/linux/src/drivers/scsi/BusLogic.h
@@ -0,0 +1,1775 @@
+/*
+
+ Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters
+
+ Copyright 1995-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+ This program is free software; you may redistribute and/or modify it under
+ the terms of the GNU General Public License Version 2 as published by the
+ Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for complete details.
+
+ The author respectfully requests that any modifications to this software be
+ sent directly to him for evaluation and testing.
+
+ Special thanks to Wayne Yen, Jin-Lon Hon, and Alex Win of BusLogic, whose
+ advice has been invaluable, to David Gentzel, for writing the original Linux
+ BusLogic driver, and to Paul Gortmaker, for being such a dedicated test site.
+
+ Finally, special thanks to Mylex/BusLogic for making the FlashPoint SCCB
+ Manager available as freely redistributable source code.
+
+*/
+
+
+#include <linux/config.h>
+
+
+/*
+ Define types for some of the structures that interface with the rest
+ of the Linux Kernel and SCSI Subsystem.
+*/
+
+typedef kdev_t KernelDevice_T;
+typedef struct proc_dir_entry PROC_DirectoryEntry_T;
+typedef unsigned long ProcessorFlags_T;
+typedef struct pt_regs Registers_T;
+typedef struct partition PartitionTable_T;
+typedef Scsi_Host_Template SCSI_Host_Template_T;
+typedef struct Scsi_Host SCSI_Host_T;
+typedef struct scsi_device SCSI_Device_T;
+typedef struct scsi_disk SCSI_Disk_T;
+typedef struct scsi_cmnd SCSI_Command_T;
+typedef struct scatterlist SCSI_ScatterList_T;
+
+
+/*
+ Define prototypes for the BusLogic Driver Interface Functions.
+*/
+
+extern PROC_DirectoryEntry_T BusLogic_ProcDirectoryEntry;
+extern const char *BusLogic_DriverInfo(SCSI_Host_T *);
+extern int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *);
+extern int BusLogic_ReleaseHostAdapter(SCSI_Host_T *);
+extern int BusLogic_QueueCommand(SCSI_Command_T *,
+ void (*CompletionRoutine)(SCSI_Command_T *));
+extern int BusLogic_AbortCommand(SCSI_Command_T *);
+extern int BusLogic_ResetCommand(SCSI_Command_T *, unsigned int);
+extern int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *);
+extern int BusLogic_ProcDirectoryInfo(char *, char **, off_t, int, int, int);
+
+
+/*
+ Define the BusLogic SCSI Host Template structure.
+*/
+
+#define BUSLOGIC \
+ { proc_dir: &BusLogic_ProcDirectoryEntry, /* ProcFS Directory Entry */ \
+ proc_info: BusLogic_ProcDirectoryInfo, /* ProcFS Info Function */ \
+ name: "BusLogic", /* Driver Name */ \
+ detect: BusLogic_DetectHostAdapter, /* Detect Host Adapter */ \
+ release: BusLogic_ReleaseHostAdapter, /* Release Host Adapter */ \
+ info: BusLogic_DriverInfo, /* Driver Info Function */ \
+ queuecommand: BusLogic_QueueCommand, /* Queue Command Function */ \
+ abort: BusLogic_AbortCommand, /* Abort Command Function */ \
+ reset: BusLogic_ResetCommand, /* Reset Command Function */ \
+ bios_param: BusLogic_BIOSDiskParameters, /* BIOS Disk Parameters */ \
+ unchecked_isa_dma: 1, /* Default Initial Value */ \
+ use_clustering: ENABLE_CLUSTERING } /* Enable Clustering */
+
+
+/*
+ BusLogic_DriverVersion protects the private portion of this file.
+*/
+
+#ifdef BusLogic_DriverVersion
+
+
+/*
+ FlashPoint support is only available for the Intel x86 Architecture with
+ CONFIG_PCI set.
+*/
+
+#ifndef __i386__
+#undef CONFIG_SCSI_OMIT_FLASHPOINT
+#define CONFIG_SCSI_OMIT_FLASHPOINT
+#endif
+
+#ifndef CONFIG_PCI
+#undef CONFIG_SCSI_OMIT_FLASHPOINT
+#define CONFIG_SCSI_OMIT_FLASHPOINT
+#define BusLogic_InitializeProbeInfoListISA \
+ BusLogic_InitializeProbeInfoList
+#endif
+
+
+/*
+ Define the maximum number of BusLogic Host Adapters supported by this driver.
+*/
+
+#define BusLogic_MaxHostAdapters 16
+
+
+/*
+ Define the maximum number of Target Devices supported by this driver.
+*/
+
+#define BusLogic_MaxTargetDevices 16
+
+
+/*
+ Define the maximum number of Scatter/Gather Segments used by this driver.
+ For optimal performance, it is important that this limit be at least as
+ large as the largest single request generated by the I/O Subsystem.
+*/
+
+#define BusLogic_ScatterGatherLimit 128
+
+
+/*
+ Define the maximum, maximum automatic, minimum automatic, and default Queue
+ Depth to allow for Target Devices depending on whether or not they support
+ Tagged Queuing and whether or not ISA Bounce Buffers are required.
+*/
+
+#define BusLogic_MaxTaggedQueueDepth 64
+#define BusLogic_MaxAutomaticTaggedQueueDepth 28
+#define BusLogic_MinAutomaticTaggedQueueDepth 7
+#define BusLogic_TaggedQueueDepthBB 3
+#define BusLogic_UntaggedQueueDepth 3
+#define BusLogic_UntaggedQueueDepthBB 2
+
+
+/*
+ Define the default amount of time in seconds to wait between a Host Adapter
+ Hard Reset which initiates a SCSI Bus Reset and issuing any SCSI commands.
+ Some SCSI devices get confused if they receive SCSI commands too soon after
+ a SCSI Bus Reset.
+*/
+
+#define BusLogic_DefaultBusSettleTime 2
+
+
+/*
+ Define the maximum number of Mailboxes that should be used for MultiMaster
+ Host Adapters. This number is chosen to be larger than the maximum Host
+ Adapter Queue Depth and small enough so that the Host Adapter structure
+ does not cross an allocation block size boundary.
+*/
+
+#define BusLogic_MaxMailboxes 211
+
+
+/*
+ Define the number of CCBs that should be allocated as a group to optimize
+ Kernel memory allocation.
+*/
+
+#define BusLogic_CCB_AllocationGroupSize 7
+
+
+/*
+ Define the Host Adapter Line and Message Buffer Sizes.
+*/
+
+#define BusLogic_LineBufferSize 100
+#define BusLogic_MessageBufferSize 9700
+
+
+/*
+ Define the Driver Message Levels.
+*/
+
+typedef enum BusLogic_MessageLevel
+{
+ BusLogic_AnnounceLevel = 0,
+ BusLogic_InfoLevel = 1,
+ BusLogic_NoticeLevel = 2,
+ BusLogic_WarningLevel = 3,
+ BusLogic_ErrorLevel = 4
+}
+BusLogic_MessageLevel_T;
+
+static char
+ *BusLogic_MessageLevelMap[] =
+ { KERN_NOTICE, KERN_NOTICE, KERN_NOTICE, KERN_WARNING, KERN_ERR };
+
+
+/*
+ Define Driver Message macros.
+*/
+
+#define BusLogic_Announce(Format, Arguments...) \
+ BusLogic_Message(BusLogic_AnnounceLevel, Format, ##Arguments)
+
+#define BusLogic_Info(Format, Arguments...) \
+ BusLogic_Message(BusLogic_InfoLevel, Format, ##Arguments)
+
+#define BusLogic_Notice(Format, Arguments...) \
+ BusLogic_Message(BusLogic_NoticeLevel, Format, ##Arguments)
+
+#define BusLogic_Warning(Format, Arguments...) \
+ BusLogic_Message(BusLogic_WarningLevel, Format, ##Arguments)
+
+#define BusLogic_Error(Format, Arguments...) \
+ BusLogic_Message(BusLogic_ErrorLevel, Format, ##Arguments)
+
+
+/*
+ Define the types of BusLogic Host Adapters that are supported and the number
+ of I/O Addresses required by each type.
+*/
+
+typedef enum
+{
+ BusLogic_MultiMaster = 1,
+ BusLogic_FlashPoint = 2
+}
+__attribute__ ((packed))
+BusLogic_HostAdapterType_T;
+
+#define BusLogic_MultiMasterAddressCount 4
+#define BusLogic_FlashPointAddressCount 256
+
+static int
+ BusLogic_HostAdapterAddressCount[3] =
+ { 0, BusLogic_MultiMasterAddressCount, BusLogic_FlashPointAddressCount };
+
+
+/*
+ Define macros for testing the Host Adapter Type.
+*/
+
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+
+#define BusLogic_MultiMasterHostAdapterP(HostAdapter) \
+ (HostAdapter->HostAdapterType == BusLogic_MultiMaster)
+
+#define BusLogic_FlashPointHostAdapterP(HostAdapter) \
+ (HostAdapter->HostAdapterType == BusLogic_FlashPoint)
+
+#else
+
+#define BusLogic_MultiMasterHostAdapterP(HostAdapter) \
+ (true)
+
+#define BusLogic_FlashPointHostAdapterP(HostAdapter) \
+ (false)
+
+#endif
+
+
+/*
+ Define the possible Host Adapter Bus Types.
+*/
+
+typedef enum
+{
+ BusLogic_Unknown_Bus = 0,
+ BusLogic_ISA_Bus = 1,
+ BusLogic_EISA_Bus = 2,
+ BusLogic_PCI_Bus = 3,
+ BusLogic_VESA_Bus = 4,
+ BusLogic_MCA_Bus = 5
+}
+__attribute__ ((packed))
+BusLogic_HostAdapterBusType_T;
+
+static char
+ *BusLogic_HostAdapterBusNames[] =
+ { "Unknown", "ISA", "EISA", "PCI", "VESA", "MCA" };
+
+static BusLogic_HostAdapterBusType_T
+ BusLogic_HostAdapterBusTypes[] =
+ { BusLogic_VESA_Bus, /* BT-4xx */
+ BusLogic_ISA_Bus, /* BT-5xx */
+ BusLogic_MCA_Bus, /* BT-6xx */
+ BusLogic_EISA_Bus, /* BT-7xx */
+ BusLogic_Unknown_Bus, /* BT-8xx */
+ BusLogic_PCI_Bus }; /* BT-9xx */
+
+
+/*
+ Define the possible Host Adapter BIOS Disk Geometry Translations.
+*/
+
+typedef enum BusLogic_BIOS_DiskGeometryTranslation
+{
+ BusLogic_BIOS_Disk_Not_Installed = 0,
+ BusLogic_BIOS_Disk_Installed_64x32 = 1,
+ BusLogic_BIOS_Disk_Installed_128x32 = 2,
+ BusLogic_BIOS_Disk_Installed_255x63 = 3
+}
+__attribute__ ((packed))
+BusLogic_BIOS_DiskGeometryTranslation_T;
+
+
+/*
+ Define a Boolean data type.
+*/
+
+typedef enum { false, true } __attribute__ ((packed)) boolean;
+
+
+/*
+ Define a 32 bit I/O Address data type.
+*/
+
+typedef unsigned int BusLogic_IO_Address_T;
+
+
+/*
+ Define a 32 bit PCI Bus Address data type.
+*/
+
+typedef unsigned int BusLogic_PCI_Address_T;
+
+
+/*
+ Define a 32 bit Base Address data type.
+*/
+
+typedef unsigned int BusLogic_Base_Address_T;
+
+
+/*
+ Define a 32 bit Bus Address data type.
+*/
+
+typedef unsigned int BusLogic_BusAddress_T;
+
+
+/*
+ Define a 32 bit Byte Count data type.
+*/
+
+typedef unsigned int BusLogic_ByteCount_T;
+
+
+/*
+ Define a 10^18 Statistics Byte Counter data type.
+*/
+
+typedef struct BusLogic_ByteCounter
+{
+ unsigned int Units;
+ unsigned int Billions;
+}
+BusLogic_ByteCounter_T;
+
+
+/*
+ Define the structure for I/O Address and Bus Probing Information.
+*/
+
+typedef struct BusLogic_ProbeInfo
+{
+ BusLogic_HostAdapterType_T HostAdapterType;
+ BusLogic_HostAdapterBusType_T HostAdapterBusType;
+ BusLogic_IO_Address_T IO_Address;
+ BusLogic_PCI_Address_T PCI_Address;
+ unsigned char Bus;
+ unsigned char Device;
+ unsigned char IRQ_Channel;
+}
+BusLogic_ProbeInfo_T;
+
+
+/*
+ Define the Probe Options.
+*/
+
+typedef struct BusLogic_ProbeOptions
+{
+ boolean NoProbe:1; /* Bit 0 */
+ boolean NoProbeISA:1; /* Bit 1 */
+ boolean NoProbePCI:1; /* Bit 2 */
+ boolean NoSortPCI:1; /* Bit 3 */
+ boolean MultiMasterFirst:1; /* Bit 4 */
+ boolean FlashPointFirst:1; /* Bit 5 */
+ boolean LimitedProbeISA:1; /* Bit 6 */
+ boolean Probe330:1; /* Bit 7 */
+ boolean Probe334:1; /* Bit 8 */
+ boolean Probe230:1; /* Bit 9 */
+ boolean Probe234:1; /* Bit 10 */
+ boolean Probe130:1; /* Bit 11 */
+ boolean Probe134:1; /* Bit 12 */
+}
+BusLogic_ProbeOptions_T;
+
+
+/*
+ Define the Global Options.
+*/
+
+typedef struct BusLogic_GlobalOptions
+{
+ boolean TraceProbe:1; /* Bit 0 */
+ boolean TraceHardwareReset:1; /* Bit 1 */
+ boolean TraceConfiguration:1; /* Bit 2 */
+ boolean TraceErrors:1; /* Bit 3 */
+}
+BusLogic_GlobalOptions_T;
+
+
+/*
+ Define the Local Options.
+*/
+
+typedef struct BusLogic_LocalOptions
+{
+ boolean InhibitTargetInquiry:1; /* Bit 0 */
+}
+BusLogic_LocalOptions_T;
+
+
+/*
+ Define the Error Recovery Strategy Options.
+*/
+
+typedef enum
+{
+ BusLogic_ErrorRecovery_Default = 0,
+ BusLogic_ErrorRecovery_BusDeviceReset = 1,
+ BusLogic_ErrorRecovery_HardReset = 2,
+ BusLogic_ErrorRecovery_None = 3
+}
+__attribute__ ((packed))
+BusLogic_ErrorRecoveryStrategy_T;
+
+static char
+ *BusLogic_ErrorRecoveryStrategyNames[] =
+ { "Default", "Bus Device Reset", "Hard Reset", "None" },
+ BusLogic_ErrorRecoveryStrategyLetters[] =
+ { 'D', 'B', 'H', 'N' };
+
+
+/*
+ Define the BusLogic SCSI Host Adapter I/O Register Offsets.
+*/
+
+#define BusLogic_ControlRegisterOffset 0 /* WO register */
+#define BusLogic_StatusRegisterOffset 0 /* RO register */
+#define BusLogic_CommandParameterRegisterOffset 1 /* WO register */
+#define BusLogic_DataInRegisterOffset 1 /* RO register */
+#define BusLogic_InterruptRegisterOffset 2 /* RO register */
+#define BusLogic_GeometryRegisterOffset 3 /* RO register */
+
+
+/*
+ Define the structure of the write-only Control Register.
+*/
+
+typedef union BusLogic_ControlRegister
+{
+ unsigned char All;
+ struct {
+ unsigned char :4; /* Bits 0-3 */
+ boolean SCSIBusReset:1; /* Bit 4 */
+ boolean InterruptReset:1; /* Bit 5 */
+ boolean SoftReset:1; /* Bit 6 */
+ boolean HardReset:1; /* Bit 7 */
+ } Bits;
+}
+BusLogic_ControlRegister_T;
+
+
+/*
+ Define the structure of the read-only Status Register.
+*/
+
+typedef union BusLogic_StatusRegister
+{
+ unsigned char All;
+ struct {
+ boolean CommandInvalid:1; /* Bit 0 */
+ boolean Reserved:1; /* Bit 1 */
+ boolean DataInRegisterReady:1; /* Bit 2 */
+ boolean CommandParameterRegisterBusy:1; /* Bit 3 */
+ boolean HostAdapterReady:1; /* Bit 4 */
+ boolean InitializationRequired:1; /* Bit 5 */
+ boolean DiagnosticFailure:1; /* Bit 6 */
+ boolean DiagnosticActive:1; /* Bit 7 */
+ } Bits;
+}
+BusLogic_StatusRegister_T;
+
+
+/*
+ Define the structure of the read-only Interrupt Register.
+*/
+
+typedef union BusLogic_InterruptRegister
+{
+ unsigned char All;
+ struct {
+ boolean IncomingMailboxLoaded:1; /* Bit 0 */
+ boolean OutgoingMailboxAvailable:1; /* Bit 1 */
+ boolean CommandComplete:1; /* Bit 2 */
+ boolean ExternalBusReset:1; /* Bit 3 */
+ unsigned char Reserved:3; /* Bits 4-6 */
+ boolean InterruptValid:1; /* Bit 7 */
+ } Bits;
+}
+BusLogic_InterruptRegister_T;
+
+
+/*
+ Define the structure of the read-only Geometry Register.
+*/
+
+typedef union BusLogic_GeometryRegister
+{
+ unsigned char All;
+ struct {
+ BusLogic_BIOS_DiskGeometryTranslation_T Drive0Geometry:2; /* Bits 0-1 */
+ BusLogic_BIOS_DiskGeometryTranslation_T Drive1Geometry:2; /* Bits 2-3 */
+ unsigned char :3; /* Bits 4-6 */
+ boolean ExtendedTranslationEnabled:1; /* Bit 7 */
+ } Bits;
+}
+BusLogic_GeometryRegister_T;
+
+
+/*
+ Define the BusLogic SCSI Host Adapter Command Register Operation Codes.
+*/
+
+typedef enum
+{
+ BusLogic_TestCommandCompleteInterrupt = 0x00,
+ BusLogic_InitializeMailbox = 0x01,
+ BusLogic_ExecuteMailboxCommand = 0x02,
+ BusLogic_ExecuteBIOSCommand = 0x03,
+ BusLogic_InquireBoardID = 0x04,
+ BusLogic_EnableOutgoingMailboxAvailableInt = 0x05,
+ BusLogic_SetSCSISelectionTimeout = 0x06,
+ BusLogic_SetPreemptTimeOnBus = 0x07,
+ BusLogic_SetTimeOffBus = 0x08,
+ BusLogic_SetBusTransferRate = 0x09,
+ BusLogic_InquireInstalledDevicesID0to7 = 0x0A,
+ BusLogic_InquireConfiguration = 0x0B,
+ BusLogic_EnableTargetMode = 0x0C,
+ BusLogic_InquireSetupInformation = 0x0D,
+ BusLogic_WriteAdapterLocalRAM = 0x1A,
+ BusLogic_ReadAdapterLocalRAM = 0x1B,
+ BusLogic_WriteBusMasterChipFIFO = 0x1C,
+ BusLogic_ReadBusMasterChipFIFO = 0x1D,
+ BusLogic_EchoCommandData = 0x1F,
+ BusLogic_HostAdapterDiagnostic = 0x20,
+ BusLogic_SetAdapterOptions = 0x21,
+ BusLogic_InquireInstalledDevicesID8to15 = 0x23,
+ BusLogic_InquireTargetDevices = 0x24,
+ BusLogic_DisableHostAdapterInterrupt = 0x25,
+ BusLogic_InitializeExtendedMailbox = 0x81,
+ BusLogic_ExecuteSCSICommand = 0x83,
+ BusLogic_InquireFirmwareVersion3rdDigit = 0x84,
+ BusLogic_InquireFirmwareVersionLetter = 0x85,
+ BusLogic_InquirePCIHostAdapterInformation = 0x86,
+ BusLogic_InquireHostAdapterModelNumber = 0x8B,
+ BusLogic_InquireSynchronousPeriod = 0x8C,
+ BusLogic_InquireExtendedSetupInformation = 0x8D,
+ BusLogic_EnableStrictRoundRobinMode = 0x8F,
+ BusLogic_StoreHostAdapterLocalRAM = 0x90,
+ BusLogic_FetchHostAdapterLocalRAM = 0x91,
+ BusLogic_StoreLocalDataInEEPROM = 0x92,
+ BusLogic_UploadAutoSCSICode = 0x94,
+ BusLogic_ModifyIOAddress = 0x95,
+ BusLogic_SetCCBFormat = 0x96,
+ BusLogic_WriteInquiryBuffer = 0x9A,
+ BusLogic_ReadInquiryBuffer = 0x9B,
+ BusLogic_FlashROMUploadDownload = 0xA7,
+ BusLogic_ReadSCAMData = 0xA8,
+ BusLogic_WriteSCAMData = 0xA9
+}
+BusLogic_OperationCode_T;
+
+
+/*
+ Define the Inquire Board ID reply structure.
+*/
+
+typedef struct BusLogic_BoardID
+{
+ unsigned char BoardType; /* Byte 0 */
+ unsigned char CustomFeatures; /* Byte 1 */
+ unsigned char FirmwareVersion1stDigit; /* Byte 2 */
+ unsigned char FirmwareVersion2ndDigit; /* Byte 3 */
+}
+BusLogic_BoardID_T;
+
+
+/*
+ Define the Inquire Installed Devices ID 0 to 7 and Inquire Installed
+ Devices ID 8 to 15 reply type. For each Target Device, a byte is returned
+ where bit 0 set indicates that Logical Unit 0 exists, bit 1 set indicates
+ that Logical Unit 1 exists, and so on.
+*/
+
+typedef unsigned char BusLogic_InstalledDevices8_T[8];
+
+
+/*
+ Define the Inquire Target Devices reply type. Inquire Target Devices only
+ tests Logical Unit 0 of each Target Device unlike the Inquire Installed
+ Devices commands which test Logical Units 0 - 7. Two bytes are returned,
+ where byte 0 bit 0 set indicates that Target Device 0 exists, and so on.
+*/
+
+typedef unsigned short BusLogic_InstalledDevices_T;
+
+
+/*
+ Define the Inquire Configuration reply structure.
+*/
+
+typedef struct BusLogic_Configuration
+{
+ unsigned char :5; /* Byte 0 Bits 0-4 */
+ boolean DMA_Channel5:1; /* Byte 0 Bit 5 */
+ boolean DMA_Channel6:1; /* Byte 0 Bit 6 */
+ boolean DMA_Channel7:1; /* Byte 0 Bit 7 */
+ boolean IRQ_Channel9:1; /* Byte 1 Bit 0 */
+ boolean IRQ_Channel10:1; /* Byte 1 Bit 1 */
+ boolean IRQ_Channel11:1; /* Byte 1 Bit 2 */
+ boolean IRQ_Channel12:1; /* Byte 1 Bit 3 */
+ unsigned char :1; /* Byte 1 Bit 4 */
+ boolean IRQ_Channel14:1; /* Byte 1 Bit 5 */
+ boolean IRQ_Channel15:1; /* Byte 1 Bit 6 */
+ unsigned char :1; /* Byte 1 Bit 7 */
+ unsigned char HostAdapterID:4; /* Byte 2 Bits 0-3 */
+ unsigned char :4; /* Byte 2 Bits 4-7 */
+}
+BusLogic_Configuration_T;
+
+
+/*
+ Define the Inquire Setup Information reply structure.
+*/
+
+typedef struct BusLogic_SynchronousValue
+{
+ unsigned char Offset:4; /* Bits 0-3 */
+ unsigned char TransferPeriod:3; /* Bits 4-6 */
+ boolean Synchronous:1; /* Bit 7 */
+}
+BusLogic_SynchronousValue_T;
+
+typedef BusLogic_SynchronousValue_T
+ BusLogic_SynchronousValues8_T[8];
+
+typedef BusLogic_SynchronousValue_T
+ BusLogic_SynchronousValues_T[BusLogic_MaxTargetDevices];
+
+typedef struct BusLogic_SetupInformation
+{
+ boolean SynchronousInitiationEnabled:1; /* Byte 0 Bit 0 */
+ boolean ParityCheckingEnabled:1; /* Byte 0 Bit 1 */
+ unsigned char :6; /* Byte 0 Bits 2-7 */
+ unsigned char BusTransferRate; /* Byte 1 */
+ unsigned char PreemptTimeOnBus; /* Byte 2 */
+ unsigned char TimeOffBus; /* Byte 3 */
+ unsigned char MailboxCount; /* Byte 4 */
+ unsigned char MailboxAddress[3]; /* Bytes 5-7 */
+ BusLogic_SynchronousValues8_T SynchronousValuesID0to7; /* Bytes 8-15 */
+ unsigned char DisconnectPermittedID0to7; /* Byte 16 */
+ unsigned char Signature; /* Byte 17 */
+ unsigned char CharacterD; /* Byte 18 */
+ unsigned char HostBusType; /* Byte 19 */
+ unsigned char WideTransfersPermittedID0to7; /* Byte 20 */
+ unsigned char WideTransfersActiveID0to7; /* Byte 21 */
+ BusLogic_SynchronousValues8_T SynchronousValuesID8to15; /* Bytes 22-29 */
+ unsigned char DisconnectPermittedID8to15; /* Byte 30 */
+ unsigned char :8; /* Byte 31 */
+ unsigned char WideTransfersPermittedID8to15; /* Byte 32 */
+ unsigned char WideTransfersActiveID8to15; /* Byte 33 */
+}
+BusLogic_SetupInformation_T;
+
+
+/*
+ Define the Initialize Extended Mailbox request structure.
+*/
+
+typedef struct BusLogic_ExtendedMailboxRequest
+{
+ unsigned char MailboxCount; /* Byte 0 */
+ BusLogic_BusAddress_T BaseMailboxAddress; /* Bytes 1-4 */
+}
+__attribute__ ((packed))
+BusLogic_ExtendedMailboxRequest_T;
+
+
+/*
+ Define the Inquire Firmware Version 3rd Digit reply type.
+*/
+
+typedef unsigned char BusLogic_FirmwareVersion3rdDigit_T;
+
+
+/*
+ Define the Inquire Firmware Version Letter reply type.
+*/
+
+typedef unsigned char BusLogic_FirmwareVersionLetter_T;
+
+
+/*
+ Define the Inquire PCI Host Adapter Information reply type. The ISA
+ Compatible I/O Port values are defined here and are also used with
+ the Modify I/O Address command.
+*/
+
+typedef enum BusLogic_ISACompatibleIOPort
+{
+ BusLogic_IO_330 = 0,
+ BusLogic_IO_334 = 1,
+ BusLogic_IO_230 = 2,
+ BusLogic_IO_234 = 3,
+ BusLogic_IO_130 = 4,
+ BusLogic_IO_134 = 5,
+ BusLogic_IO_Disable = 6,
+ BusLogic_IO_Disable2 = 7
+}
+__attribute__ ((packed))
+BusLogic_ISACompatibleIOPort_T;
+
+typedef struct BusLogic_PCIHostAdapterInformation
+{
+ BusLogic_ISACompatibleIOPort_T ISACompatibleIOPort; /* Byte 0 */
+ unsigned char PCIAssignedIRQChannel; /* Byte 1 */
+ boolean LowByteTerminated:1; /* Byte 2 Bit 0 */
+ boolean HighByteTerminated:1; /* Byte 2 Bit 1 */
+ unsigned char :2; /* Byte 2 Bits 2-3 */
+ boolean JP1:1; /* Byte 2 Bit 4 */
+ boolean JP2:1; /* Byte 2 Bit 5 */
+ boolean JP3:1; /* Byte 2 Bit 6 */
+ boolean GenericInfoValid:1; /* Byte 2 Bit 7 */
+ unsigned char :8; /* Byte 3 */
+}
+BusLogic_PCIHostAdapterInformation_T;
+
+
+/*
+ Define the Inquire Host Adapter Model Number reply type.
+*/
+
+typedef unsigned char BusLogic_HostAdapterModelNumber_T[5];
+
+
+/*
+ Define the Inquire Synchronous Period reply type. For each Target Device,
+ a byte is returned which represents the Synchronous Transfer Period in units
+ of 10 nanoseconds.
+*/
+
+typedef unsigned char BusLogic_SynchronousPeriod_T[BusLogic_MaxTargetDevices];
+
+
+/*
+ Define the Inquire Extended Setup Information reply structure.
+*/
+
+typedef struct BusLogic_ExtendedSetupInformation
+{
+ unsigned char BusType; /* Byte 0 */
+ unsigned char BIOS_Address; /* Byte 1 */
+ unsigned short ScatterGatherLimit; /* Bytes 2-3 */
+ unsigned char MailboxCount; /* Byte 4 */
+ BusLogic_BusAddress_T BaseMailboxAddress; /* Bytes 5-8 */
+ struct { unsigned char :2; /* Byte 9 Bits 0-1 */
+ boolean FastOnEISA:1; /* Byte 9 Bit 2 */
+ unsigned char :3; /* Byte 9 Bits 3-5 */
+ boolean LevelSensitiveInterrupt:1; /* Byte 9 Bit 6 */
+ unsigned char :1; } Misc; /* Byte 9 Bit 7 */
+ unsigned char FirmwareRevision[3]; /* Bytes 10-12 */
+ boolean HostWideSCSI:1; /* Byte 13 Bit 0 */
+ boolean HostDifferentialSCSI:1; /* Byte 13 Bit 1 */
+ boolean HostSupportsSCAM:1; /* Byte 13 Bit 2 */
+ boolean HostUltraSCSI:1; /* Byte 13 Bit 3 */
+ boolean HostSmartTermination:1; /* Byte 13 Bit 4 */
+ unsigned char :3; /* Byte 13 Bits 5-7 */
+}
+__attribute__ ((packed))
+BusLogic_ExtendedSetupInformation_T;
+
+
+/*
+ Define the Enable Strict Round Robin Mode request type.
+*/
+
+typedef enum BusLogic_RoundRobinModeRequest
+{
+ BusLogic_AggressiveRoundRobinMode = 0,
+ BusLogic_StrictRoundRobinMode = 1
+}
+__attribute__ ((packed))
+BusLogic_RoundRobinModeRequest_T;
+
+
+/*
+ Define the Fetch Host Adapter Local RAM request type.
+*/
+
+#define BusLogic_BIOS_BaseOffset 0
+#define BusLogic_AutoSCSI_BaseOffset 64
+
+typedef struct BusLogic_FetchHostAdapterLocalRAMRequest
+{
+ unsigned char ByteOffset; /* Byte 0 */
+ unsigned char ByteCount; /* Byte 1 */
+}
+BusLogic_FetchHostAdapterLocalRAMRequest_T;
+
+
+/*
+ Define the Host Adapter Local RAM AutoSCSI structure.
+*/
+
+typedef struct BusLogic_AutoSCSIData
+{
+ unsigned char InternalFactorySignature[2]; /* Bytes 0-1 */
+ unsigned char InformationByteCount; /* Byte 2 */
+ unsigned char HostAdapterType[6]; /* Bytes 3-8 */
+ unsigned char :8; /* Byte 9 */
+ boolean FloppyEnabled:1; /* Byte 10 Bit 0 */
+ boolean FloppySecondary:1; /* Byte 10 Bit 1 */
+ boolean LevelSensitiveInterrupt:1; /* Byte 10 Bit 2 */
+ unsigned char :2; /* Byte 10 Bits 3-4 */
+ unsigned char SystemRAMAreaForBIOS:3; /* Byte 10 Bits 5-7 */
+ unsigned char DMA_Channel:7; /* Byte 11 Bits 0-6 */
+ boolean DMA_AutoConfiguration:1; /* Byte 11 Bit 7 */
+ unsigned char IRQ_Channel:7; /* Byte 12 Bits 0-6 */
+ boolean IRQ_AutoConfiguration:1; /* Byte 12 Bit 7 */
+ unsigned char DMA_TransferRate; /* Byte 13 */
+ unsigned char SCSI_ID; /* Byte 14 */
+ boolean LowByteTerminated:1; /* Byte 15 Bit 0 */
+ boolean ParityCheckingEnabled:1; /* Byte 15 Bit 1 */
+ boolean HighByteTerminated:1; /* Byte 15 Bit 2 */
+ boolean NoisyCablingEnvironment:1; /* Byte 15 Bit 3 */
+ boolean FastSynchronousNegotiation:1; /* Byte 15 Bit 4 */
+ boolean BusResetEnabled:1; /* Byte 15 Bit 5 */
+ boolean :1; /* Byte 15 Bit 6 */
+ boolean ActiveNegationEnabled:1; /* Byte 15 Bit 7 */
+ unsigned char BusOnDelay; /* Byte 16 */
+ unsigned char BusOffDelay; /* Byte 17 */
+ boolean HostAdapterBIOSEnabled:1; /* Byte 18 Bit 0 */
+ boolean BIOSRedirectionOfINT19Enabled:1; /* Byte 18 Bit 1 */
+ boolean ExtendedTranslationEnabled:1; /* Byte 18 Bit 2 */
+ boolean MapRemovableAsFixedEnabled:1; /* Byte 18 Bit 3 */
+ boolean :1; /* Byte 18 Bit 4 */
+ boolean BIOSSupportsMoreThan2DrivesEnabled:1; /* Byte 18 Bit 5 */
+ boolean BIOSInterruptModeEnabled:1; /* Byte 18 Bit 6 */
+ boolean FlopticalSupportEnabled:1; /* Byte 19 Bit 7 */
+ unsigned short DeviceEnabled; /* Bytes 19-20 */
+ unsigned short WidePermitted; /* Bytes 21-22 */
+ unsigned short FastPermitted; /* Bytes 23-24 */
+ unsigned short SynchronousPermitted; /* Bytes 25-26 */
+ unsigned short DisconnectPermitted; /* Bytes 27-28 */
+ unsigned short SendStartUnitCommand; /* Bytes 29-30 */
+ unsigned short IgnoreInBIOSScan; /* Bytes 31-32 */
+ unsigned char PCIInterruptPin:2; /* Byte 33 Bits 0-1 */
+ unsigned char HostAdapterIOPortAddress:2; /* Byte 33 Bits 2-3 */
+ boolean StrictRoundRobinModeEnabled:1; /* Byte 33 Bit 4 */
+ boolean VESABusSpeedGreaterThan33MHz:1; /* Byte 33 Bit 5 */
+ boolean VESABurstWriteEnabled:1; /* Byte 33 Bit 6 */
+ boolean VESABurstReadEnabled:1; /* Byte 33 Bit 7 */
+ unsigned short UltraPermitted; /* Bytes 34-35 */
+ unsigned int :32; /* Bytes 36-39 */
+ unsigned char :8; /* Byte 40 */
+ unsigned char AutoSCSIMaximumLUN; /* Byte 41 */
+ boolean :1; /* Byte 42 Bit 0 */
+ boolean SCAM_Dominant:1; /* Byte 42 Bit 1 */
+ boolean SCAM_Enabled:1; /* Byte 42 Bit 2 */
+ boolean SCAM_Level2:1; /* Byte 42 Bit 3 */
+ unsigned char :4; /* Byte 42 Bits 4-7 */
+ boolean INT13ExtensionEnabled:1; /* Byte 43 Bit 0 */
+ boolean :1; /* Byte 43 Bit 1 */
+ boolean CDROMBootEnabled:1; /* Byte 43 Bit 2 */
+ unsigned char :5; /* Byte 43 Bits 3-7 */
+ unsigned char BootTargetID:4; /* Byte 44 Bits 0-3 */
+ unsigned char BootChannel:4; /* Byte 44 Bits 4-7 */
+ unsigned char ForceBusDeviceScanningOrder:1; /* Byte 45 Bit 0 */
+ unsigned char :7; /* Byte 45 Bits 1-7 */
+ unsigned short NonTaggedToAlternateLUNPermitted; /* Bytes 46-47 */
+ unsigned short RenegotiateSyncAfterCheckCondition; /* Bytes 48-49 */
+ unsigned char Reserved[10]; /* Bytes 50-59 */
+ unsigned char ManufacturingDiagnostic[2]; /* Bytes 60-61 */
+ unsigned short Checksum; /* Bytes 62-63 */
+}
+__attribute__ ((packed))
+BusLogic_AutoSCSIData_T;
+
+
+/*
+ Define the Host Adapter Local RAM Auto SCSI Byte 45 structure.
+*/
+
+typedef struct BusLogic_AutoSCSIByte45
+{
+ unsigned char ForceBusDeviceScanningOrder:1; /* Bit 0 */
+ unsigned char :7; /* Bits 1-7 */
+}
+BusLogic_AutoSCSIByte45_T;
+
+
+/*
+ Define the Host Adapter Local RAM BIOS Drive Map Byte structure.
+*/
+
+#define BusLogic_BIOS_DriveMapOffset 17
+
+typedef struct BusLogic_BIOSDriveMapByte
+{
+ unsigned char TargetIDBit3:1; /* Bit 0 */
+ unsigned char :2; /* Bits 1-2 */
+ BusLogic_BIOS_DiskGeometryTranslation_T DiskGeometry:2; /* Bits 3-4 */
+ unsigned char TargetID:3; /* Bits 5-7 */
+}
+BusLogic_BIOSDriveMapByte_T;
+
+
+/*
+ Define the Modify I/O Address request type. On PCI Host Adapters, the
+ Modify I/O Address command allows modification of the ISA compatible I/O
+ Address that the Host Adapter responds to; it does not affect the PCI
+ compliant I/O Address assigned at system initialization.
+*/
+
+typedef BusLogic_ISACompatibleIOPort_T BusLogic_ModifyIOAddressRequest_T;
+
+
+/*
+ Define the Set CCB Format request type. Extended LUN Format CCBs are
+ necessary to support more than 8 Logical Units per Target Device.
+*/
+
+typedef enum BusLogic_SetCCBFormatRequest
+{
+ BusLogic_LegacyLUNFormatCCB = 0,
+ BusLogic_ExtendedLUNFormatCCB = 1
+}
+__attribute__ ((packed))
+BusLogic_SetCCBFormatRequest_T;
+
+
+/*
+ Define the Requested Reply Length type used by the Inquire Setup Information,
+ Inquire Host Adapter Model Number, Inquire Synchronous Period, and Inquire
+ Extended Setup Information commands.
+*/
+
+typedef unsigned char BusLogic_RequestedReplyLength_T;
+
+
+/*
+ Define the Outgoing Mailbox Action Codes.
+*/
+
+typedef enum
+{
+ BusLogic_OutgoingMailboxFree = 0x00,
+ BusLogic_MailboxStartCommand = 0x01,
+ BusLogic_MailboxAbortCommand = 0x02
+}
+__attribute__ ((packed))
+BusLogic_ActionCode_T;
+
+
+/*
+ Define the Incoming Mailbox Completion Codes. The MultiMaster Firmware
+ only uses codes 0 - 4. The FlashPoint SCCB Manager has no mailboxes, so
+ completion codes are stored in the CCB; it only uses codes 1, 2, 4, and 5.
+*/
+
+typedef enum
+{
+ BusLogic_IncomingMailboxFree = 0x00,
+ BusLogic_CommandCompletedWithoutError = 0x01,
+ BusLogic_CommandAbortedAtHostRequest = 0x02,
+ BusLogic_AbortedCommandNotFound = 0x03,
+ BusLogic_CommandCompletedWithError = 0x04,
+ BusLogic_InvalidCCB = 0x05
+}
+__attribute__ ((packed))
+BusLogic_CompletionCode_T;
+
+
+/*
+ Define the Command Control Block (CCB) Opcodes.
+*/
+
+typedef enum
+{
+ BusLogic_InitiatorCCB = 0x00,
+ BusLogic_TargetCCB = 0x01,
+ BusLogic_InitiatorCCB_ScatterGather = 0x02,
+ BusLogic_InitiatorCCB_ResidualDataLength = 0x03,
+ BusLogic_InitiatorCCB_ScatterGatherResidual = 0x04,
+ BusLogic_BusDeviceReset = 0x81
+}
+__attribute__ ((packed))
+BusLogic_CCB_Opcode_T;
+
+
+/*
+ Define the CCB Data Direction Codes.
+*/
+
+typedef enum
+{
+ BusLogic_UncheckedDataTransfer = 0,
+ BusLogic_DataInLengthChecked = 1,
+ BusLogic_DataOutLengthChecked = 2,
+ BusLogic_NoDataTransfer = 3
+}
+BusLogic_DataDirection_T;
+
+
+/*
+ Define the Host Adapter Status Codes. The MultiMaster Firmware does not
+ return status code 0x0C; it uses 0x12 for both overruns and underruns.
+*/
+
+typedef enum
+{
+ BusLogic_CommandCompletedNormally = 0x00,
+ BusLogic_LinkedCommandCompleted = 0x0A,
+ BusLogic_LinkedCommandCompletedWithFlag = 0x0B,
+ BusLogic_DataUnderRun = 0x0C,
+ BusLogic_SCSISelectionTimeout = 0x11,
+ BusLogic_DataOverRun = 0x12,
+ BusLogic_UnexpectedBusFree = 0x13,
+ BusLogic_InvalidBusPhaseRequested = 0x14,
+ BusLogic_InvalidOutgoingMailboxActionCode = 0x15,
+ BusLogic_InvalidCommandOperationCode = 0x16,
+ BusLogic_LinkedCCBhasInvalidLUN = 0x17,
+ BusLogic_InvalidCommandParameter = 0x1A,
+ BusLogic_AutoRequestSenseFailed = 0x1B,
+ BusLogic_TaggedQueuingMessageRejected = 0x1C,
+ BusLogic_UnsupportedMessageReceived = 0x1D,
+ BusLogic_HostAdapterHardwareFailed = 0x20,
+ BusLogic_TargetFailedResponseToATN = 0x21,
+ BusLogic_HostAdapterAssertedRST = 0x22,
+ BusLogic_OtherDeviceAssertedRST = 0x23,
+ BusLogic_TargetDeviceReconnectedImproperly = 0x24,
+ BusLogic_HostAdapterAssertedBusDeviceReset = 0x25,
+ BusLogic_AbortQueueGenerated = 0x26,
+ BusLogic_HostAdapterSoftwareError = 0x27,
+ BusLogic_HostAdapterHardwareTimeoutError = 0x30,
+ BusLogic_SCSIParityErrorDetected = 0x34
+}
+__attribute__ ((packed))
+BusLogic_HostAdapterStatus_T;
+
+
+/*
+ Define the SCSI Target Device Status Codes.
+*/
+
+typedef enum
+{
+ BusLogic_OperationGood = 0x00,
+ BusLogic_CheckCondition = 0x02,
+ BusLogic_DeviceBusy = 0x08
+}
+__attribute__ ((packed))
+BusLogic_TargetDeviceStatus_T;
+
+
+/*
+ Define the Queue Tag Codes.
+*/
+
+typedef enum
+{
+ BusLogic_SimpleQueueTag = 0,
+ BusLogic_HeadOfQueueTag = 1,
+ BusLogic_OrderedQueueTag = 2,
+ BusLogic_ReservedQT = 3
+}
+BusLogic_QueueTag_T;
+
+
+/*
+ Define the SCSI Command Descriptor Block (CDB).
+*/
+
+#define BusLogic_CDB_MaxLength 12
+
+typedef unsigned char SCSI_CDB_T[BusLogic_CDB_MaxLength];
+
+
+/*
+ Define the Scatter/Gather Segment structure required by the MultiMaster
+ Firmware Interface and the FlashPoint SCCB Manager.
+*/
+
+typedef struct BusLogic_ScatterGatherSegment
+{
+ BusLogic_ByteCount_T SegmentByteCount; /* Bytes 0-3 */
+ BusLogic_BusAddress_T SegmentDataPointer; /* Bytes 4-7 */
+}
+BusLogic_ScatterGatherSegment_T;
+
+
+/*
+ Define the Driver CCB Status Codes.
+*/
+
+typedef enum
+{
+ BusLogic_CCB_Free = 0,
+ BusLogic_CCB_Active = 1,
+ BusLogic_CCB_Completed = 2,
+ BusLogic_CCB_Reset = 3
+}
+__attribute__ ((packed))
+BusLogic_CCB_Status_T;
+
+
+/*
+ Define the 32 Bit Mode Command Control Block (CCB) structure. The first 40
+ bytes are defined by and common to both the MultiMaster Firmware and the
+ FlashPoint SCCB Manager. The next 60 bytes are defined by the FlashPoint
+ SCCB Manager. The remaining components are defined by the Linux BusLogic
+ Driver. Extended LUN Format CCBs differ from Legacy LUN Format 32 Bit Mode
+ CCBs only in having the TagEnable and QueueTag fields moved from byte 17 to
+ byte 1, and the Logical Unit field in byte 17 expanded to 6 bits. In theory,
+ Extended LUN Format CCBs can support up to 64 Logical Units, but in practice
+ many devices will respond improperly to Logical Units between 32 and 63, and
+ the SCSI-2 specification defines Bit 5 as LUNTAR. Extended LUN Format CCBs
+ are used by recent versions of the MultiMaster Firmware, as well as by the
+ FlashPoint SCCB Manager; the FlashPoint SCCB Manager only supports 32 Logical
+ Units. Since 64 Logical Units are unlikely to be needed in practice, and
+ since they are problematic for the above reasons, and since limiting them to
+ 5 bits simplifies the CCB structure definition, this driver only supports
+ 32 Logical Units per Target Device.
+*/
+
+typedef struct BusLogic_CCB
+{
+ /*
+ MultiMaster Firmware and FlashPoint SCCB Manager Common Portion.
+ */
+ BusLogic_CCB_Opcode_T Opcode; /* Byte 0 */
+ unsigned char :3; /* Byte 1 Bits 0-2 */
+ BusLogic_DataDirection_T DataDirection:2; /* Byte 1 Bits 3-4 */
+ boolean TagEnable:1; /* Byte 1 Bit 5 */
+ BusLogic_QueueTag_T QueueTag:2; /* Byte 1 Bits 6-7 */
+ unsigned char CDB_Length; /* Byte 2 */
+ unsigned char SenseDataLength; /* Byte 3 */
+ BusLogic_ByteCount_T DataLength; /* Bytes 4-7 */
+ BusLogic_BusAddress_T DataPointer; /* Bytes 8-11 */
+ unsigned char :8; /* Byte 12 */
+ unsigned char :8; /* Byte 13 */
+ BusLogic_HostAdapterStatus_T HostAdapterStatus; /* Byte 14 */
+ BusLogic_TargetDeviceStatus_T TargetDeviceStatus; /* Byte 15 */
+ unsigned char TargetID; /* Byte 16 */
+ unsigned char LogicalUnit:5; /* Byte 17 Bits 0-4 */
+ boolean LegacyTagEnable:1; /* Byte 17 Bit 5 */
+ BusLogic_QueueTag_T LegacyQueueTag:2; /* Byte 17 Bits 6-7 */
+ SCSI_CDB_T CDB; /* Bytes 18-29 */
+ unsigned char :8; /* Byte 30 */
+ unsigned char :8; /* Byte 31 */
+ unsigned int :32; /* Bytes 32-35 */
+ BusLogic_BusAddress_T SenseDataPointer; /* Bytes 36-39 */
+ /*
+ FlashPoint SCCB Manager Defined Portion.
+ */
+ void (*CallbackFunction)(struct BusLogic_CCB *); /* Bytes 40-43 */
+ BusLogic_Base_Address_T BaseAddress; /* Bytes 44-47 */
+ BusLogic_CompletionCode_T CompletionCode; /* Byte 48 */
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+ unsigned char :8; /* Byte 49 */
+ unsigned short OS_Flags; /* Bytes 50-51 */
+ unsigned char Private[48]; /* Bytes 52-99 */
+#endif
+ /*
+ BusLogic Linux Driver Defined Portion.
+ */
+ boolean AllocationGroupHead;
+ BusLogic_CCB_Status_T Status;
+ unsigned long SerialNumber;
+ SCSI_Command_T *Command;
+ struct BusLogic_HostAdapter *HostAdapter;
+ struct BusLogic_CCB *Next;
+ struct BusLogic_CCB *NextAll;
+ BusLogic_ScatterGatherSegment_T
+ ScatterGatherList[BusLogic_ScatterGatherLimit];
+}
+BusLogic_CCB_T;
+
+
+/*
+ Define the 32 Bit Mode Outgoing Mailbox structure.
+*/
+
+typedef struct BusLogic_OutgoingMailbox
+{
+ BusLogic_BusAddress_T CCB; /* Bytes 0-3 */
+ unsigned int :24; /* Bytes 4-6 */
+ BusLogic_ActionCode_T ActionCode; /* Byte 7 */
+}
+BusLogic_OutgoingMailbox_T;
+
+
+/*
+ Define the 32 Bit Mode Incoming Mailbox structure.
+*/
+
+typedef struct BusLogic_IncomingMailbox
+{
+ BusLogic_BusAddress_T CCB; /* Bytes 0-3 */
+ BusLogic_HostAdapterStatus_T HostAdapterStatus; /* Byte 4 */
+ BusLogic_TargetDeviceStatus_T TargetDeviceStatus; /* Byte 5 */
+ unsigned char :8; /* Byte 6 */
+ BusLogic_CompletionCode_T CompletionCode; /* Byte 7 */
+}
+BusLogic_IncomingMailbox_T;
+
+
+/*
+ Define the BusLogic Driver Options structure.
+*/
+
+typedef struct BusLogic_DriverOptions
+{
+ unsigned short TaggedQueuingPermitted;
+ unsigned short TaggedQueuingPermittedMask;
+ unsigned short BusSettleTime;
+ BusLogic_LocalOptions_T LocalOptions;
+ unsigned char CommonQueueDepth;
+ unsigned char QueueDepth[BusLogic_MaxTargetDevices];
+ BusLogic_ErrorRecoveryStrategy_T
+ ErrorRecoveryStrategy[BusLogic_MaxTargetDevices];
+}
+BusLogic_DriverOptions_T;
+
+
+/*
+ Define the Host Adapter Target Flags structure.
+*/
+
+typedef struct BusLogic_TargetFlags
+{
+ boolean TargetExists:1;
+ boolean TaggedQueuingSupported:1;
+ boolean WideTransfersSupported:1;
+ boolean TaggedQueuingActive:1;
+ boolean WideTransfersActive:1;
+ boolean CommandSuccessfulFlag:1;
+ boolean TargetInfoReported:1;
+}
+BusLogic_TargetFlags_T;
+
+
+/*
+ Define the Host Adapter Target Statistics structure.
+*/
+
+#define BusLogic_SizeBuckets 10
+
+typedef unsigned int BusLogic_CommandSizeBuckets_T[BusLogic_SizeBuckets];
+
+typedef struct BusLogic_TargetStatistics
+{
+ unsigned int CommandsAttempted;
+ unsigned int CommandsCompleted;
+ unsigned int ReadCommands;
+ unsigned int WriteCommands;
+ BusLogic_ByteCounter_T TotalBytesRead;
+ BusLogic_ByteCounter_T TotalBytesWritten;
+ BusLogic_CommandSizeBuckets_T ReadCommandSizeBuckets;
+ BusLogic_CommandSizeBuckets_T WriteCommandSizeBuckets;
+ unsigned short CommandAbortsRequested;
+ unsigned short CommandAbortsAttempted;
+ unsigned short CommandAbortsCompleted;
+ unsigned short BusDeviceResetsRequested;
+ unsigned short BusDeviceResetsAttempted;
+ unsigned short BusDeviceResetsCompleted;
+ unsigned short HostAdapterResetsRequested;
+ unsigned short HostAdapterResetsAttempted;
+ unsigned short HostAdapterResetsCompleted;
+}
+BusLogic_TargetStatistics_T;
+
+
+/*
+ Define the FlashPoint Card Handle data type.
+*/
+
+#define FlashPoint_BadCardHandle 0xFFFFFFFF
+
+typedef unsigned int FlashPoint_CardHandle_T;
+
+
+/*
+ Define the FlashPoint Information structure. This structure is defined
+ by the FlashPoint SCCB Manager.
+*/
+
+typedef struct FlashPoint_Info
+{
+ BusLogic_Base_Address_T BaseAddress; /* Bytes 0-3 */
+ boolean Present; /* Byte 4 */
+ unsigned char IRQ_Channel; /* Byte 5 */
+ unsigned char SCSI_ID; /* Byte 6 */
+ unsigned char SCSI_LUN; /* Byte 7 */
+ unsigned short FirmwareRevision; /* Bytes 8-9 */
+ unsigned short SynchronousPermitted; /* Bytes 10-11 */
+ unsigned short FastPermitted; /* Bytes 12-13 */
+ unsigned short UltraPermitted; /* Bytes 14-15 */
+ unsigned short DisconnectPermitted; /* Bytes 16-17 */
+ unsigned short WidePermitted; /* Bytes 18-19 */
+ boolean ParityCheckingEnabled:1; /* Byte 20 Bit 0 */
+ boolean HostWideSCSI:1; /* Byte 20 Bit 1 */
+ boolean HostSoftReset:1; /* Byte 20 Bit 2 */
+ boolean ExtendedTranslationEnabled:1; /* Byte 20 Bit 3 */
+ boolean LowByteTerminated:1; /* Byte 20 Bit 4 */
+ boolean HighByteTerminated:1; /* Byte 20 Bit 5 */
+ boolean ReportDataUnderrun:1; /* Byte 20 Bit 6 */
+ boolean SCAM_Enabled:1; /* Byte 20 Bit 7 */
+ boolean SCAM_Level2:1; /* Byte 21 Bit 0 */
+ unsigned char :7; /* Byte 21 Bits 1-7 */
+ unsigned char Family; /* Byte 22 */
+ unsigned char BusType; /* Byte 23 */
+ unsigned char ModelNumber[3]; /* Bytes 24-26 */
+ unsigned char RelativeCardNumber; /* Byte 27 */
+ unsigned char Reserved[4]; /* Bytes 28-31 */
+ unsigned int OS_Reserved; /* Bytes 32-35 */
+ unsigned char TranslationInfo[4]; /* Bytes 36-39 */
+ unsigned int Reserved2[5]; /* Bytes 40-59 */
+ unsigned int SecondaryRange; /* Bytes 60-63 */
+}
+FlashPoint_Info_T;
+
+
+/*
+ Define the BusLogic Driver Host Adapter structure.
+*/
+
+typedef struct BusLogic_HostAdapter
+{
+ SCSI_Host_T *SCSI_Host;
+ BusLogic_HostAdapterType_T HostAdapterType;
+ BusLogic_HostAdapterBusType_T HostAdapterBusType;
+ BusLogic_IO_Address_T IO_Address;
+ BusLogic_PCI_Address_T PCI_Address;
+ unsigned short AddressCount;
+ unsigned char HostNumber;
+ unsigned char ModelName[9];
+ unsigned char FirmwareVersion[6];
+ unsigned char FullModelName[18];
+ unsigned char Bus;
+ unsigned char Device;
+ unsigned char IRQ_Channel;
+ unsigned char DMA_Channel;
+ unsigned char SCSI_ID;
+ boolean IRQ_ChannelAcquired:1;
+ boolean DMA_ChannelAcquired:1;
+ boolean ExtendedTranslationEnabled:1;
+ boolean ParityCheckingEnabled:1;
+ boolean BusResetEnabled:1;
+ boolean LevelSensitiveInterrupt:1;
+ boolean HostWideSCSI:1;
+ boolean HostDifferentialSCSI:1;
+ boolean HostSupportsSCAM:1;
+ boolean HostUltraSCSI:1;
+ boolean ExtendedLUNSupport:1;
+ boolean TerminationInfoValid:1;
+ boolean LowByteTerminated:1;
+ boolean HighByteTerminated:1;
+ boolean BounceBuffersRequired:1;
+ boolean StrictRoundRobinModeSupport:1;
+ boolean SCAM_Enabled:1;
+ boolean SCAM_Level2:1;
+ boolean HostAdapterInitialized:1;
+ boolean HostAdapterExternalReset:1;
+ boolean HostAdapterInternalError:1;
+ boolean ProcessCompletedCCBsActive;
+ volatile boolean HostAdapterCommandCompleted;
+ unsigned short HostAdapterScatterGatherLimit;
+ unsigned short DriverScatterGatherLimit;
+ unsigned short MaxTargetDevices;
+ unsigned short MaxLogicalUnits;
+ unsigned short MailboxCount;
+ unsigned short InitialCCBs;
+ unsigned short IncrementalCCBs;
+ unsigned short AllocatedCCBs;
+ unsigned short DriverQueueDepth;
+ unsigned short HostAdapterQueueDepth;
+ unsigned short UntaggedQueueDepth;
+ unsigned short CommonQueueDepth;
+ unsigned short BusSettleTime;
+ unsigned short SynchronousPermitted;
+ unsigned short FastPermitted;
+ unsigned short UltraPermitted;
+ unsigned short WidePermitted;
+ unsigned short DisconnectPermitted;
+ unsigned short TaggedQueuingPermitted;
+ unsigned short ExternalHostAdapterResets;
+ unsigned short HostAdapterInternalErrors;
+ unsigned short TargetDeviceCount;
+ unsigned short MessageBufferLength;
+ BusLogic_BusAddress_T BIOS_Address;
+ BusLogic_DriverOptions_T *DriverOptions;
+ FlashPoint_Info_T FlashPointInfo;
+ FlashPoint_CardHandle_T CardHandle;
+ struct BusLogic_HostAdapter *Next;
+ BusLogic_CCB_T *All_CCBs;
+ BusLogic_CCB_T *Free_CCBs;
+ BusLogic_CCB_T *FirstCompletedCCB;
+ BusLogic_CCB_T *LastCompletedCCB;
+ BusLogic_CCB_T *BusDeviceResetPendingCCB[BusLogic_MaxTargetDevices];
+ BusLogic_ErrorRecoveryStrategy_T
+ ErrorRecoveryStrategy[BusLogic_MaxTargetDevices];
+ BusLogic_TargetFlags_T TargetFlags[BusLogic_MaxTargetDevices];
+ unsigned char QueueDepth[BusLogic_MaxTargetDevices];
+ unsigned char SynchronousPeriod[BusLogic_MaxTargetDevices];
+ unsigned char SynchronousOffset[BusLogic_MaxTargetDevices];
+ unsigned char ActiveCommands[BusLogic_MaxTargetDevices];
+ unsigned int CommandsSinceReset[BusLogic_MaxTargetDevices];
+ unsigned long LastSequencePoint[BusLogic_MaxTargetDevices];
+ unsigned long LastResetAttempted[BusLogic_MaxTargetDevices];
+ unsigned long LastResetCompleted[BusLogic_MaxTargetDevices];
+ BusLogic_OutgoingMailbox_T *FirstOutgoingMailbox;
+ BusLogic_OutgoingMailbox_T *LastOutgoingMailbox;
+ BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
+ BusLogic_IncomingMailbox_T *FirstIncomingMailbox;
+ BusLogic_IncomingMailbox_T *LastIncomingMailbox;
+ BusLogic_IncomingMailbox_T *NextIncomingMailbox;
+ BusLogic_TargetStatistics_T TargetStatistics[BusLogic_MaxTargetDevices];
+ unsigned char MailboxSpace[BusLogic_MaxMailboxes
+ * (sizeof(BusLogic_OutgoingMailbox_T)
+ + sizeof(BusLogic_IncomingMailbox_T))];
+ char MessageBuffer[BusLogic_MessageBufferSize];
+}
+BusLogic_HostAdapter_T;
+
+
+/*
+ Define a structure for the BIOS Disk Parameters.
+*/
+
+typedef struct BIOS_DiskParameters
+{
+ int Heads;
+ int Sectors;
+ int Cylinders;
+}
+BIOS_DiskParameters_T;
+
+
+/*
+ Define a structure for the SCSI Inquiry command results.
+*/
+
+typedef struct SCSI_Inquiry
+{
+ unsigned char PeripheralDeviceType:5; /* Byte 0 Bits 0-4 */
+ unsigned char PeripheralQualifier:3; /* Byte 0 Bits 5-7 */
+ unsigned char DeviceTypeModifier:7; /* Byte 1 Bits 0-6 */
+ boolean RMB:1; /* Byte 1 Bit 7 */
+ unsigned char ANSI_ApprovedVersion:3; /* Byte 2 Bits 0-2 */
+ unsigned char ECMA_Version:3; /* Byte 2 Bits 3-5 */
+ unsigned char ISO_Version:2; /* Byte 2 Bits 6-7 */
+ unsigned char ResponseDataFormat:4; /* Byte 3 Bits 0-3 */
+ unsigned char :2; /* Byte 3 Bits 4-5 */
+ boolean TrmIOP:1; /* Byte 3 Bit 6 */
+ boolean AENC:1; /* Byte 3 Bit 7 */
+ unsigned char AdditionalLength; /* Byte 4 */
+ unsigned char :8; /* Byte 5 */
+ unsigned char :8; /* Byte 6 */
+ boolean SftRe:1; /* Byte 7 Bit 0 */
+ boolean CmdQue:1; /* Byte 7 Bit 1 */
+ boolean :1; /* Byte 7 Bit 2 */
+ boolean Linked:1; /* Byte 7 Bit 3 */
+ boolean Sync:1; /* Byte 7 Bit 4 */
+ boolean WBus16:1; /* Byte 7 Bit 5 */
+ boolean WBus32:1; /* Byte 7 Bit 6 */
+ boolean RelAdr:1; /* Byte 7 Bit 7 */
+ unsigned char VendorIdentification[8]; /* Bytes 8-15 */
+ unsigned char ProductIdentification[16]; /* Bytes 16-31 */
+ unsigned char ProductRevisionLevel[4]; /* Bytes 32-35 */
+}
+SCSI_Inquiry_T;
+
+
+/*
+ BusLogic_AcquireHostAdapterLock acquires exclusive access to Host Adapter.
+*/
+
+static inline
+void BusLogic_AcquireHostAdapterLock(BusLogic_HostAdapter_T *HostAdapter,
+ ProcessorFlags_T *ProcessorFlags)
+{
+ save_flags(*ProcessorFlags);
+ cli();
+}
+
+
+/*
+ BusLogic_ReleaseHostAdapterLock releases exclusive access to Host Adapter.
+*/
+
+static inline
+void BusLogic_ReleaseHostAdapterLock(BusLogic_HostAdapter_T *HostAdapter,
+ ProcessorFlags_T *ProcessorFlags)
+{
+ restore_flags(*ProcessorFlags);
+}
+
+
+/*
+ BusLogic_AcquireHostAdapterLockIH acquires exclusive access to Host Adapter,
+ but is only called from the interrupt handler when interrupts are disabled.
+*/
+
+static inline
+void BusLogic_AcquireHostAdapterLockIH(BusLogic_HostAdapter_T *HostAdapter,
+ ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+ BusLogic_ReleaseHostAdapterLockIH releases exclusive access to Host Adapter,
+ but is only called from the interrupt handler when interrupts are disabled.
+*/
+
+static inline
+void BusLogic_ReleaseHostAdapterLockIH(BusLogic_HostAdapter_T *HostAdapter,
+ ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+ Define functions to provide an abstraction for reading and writing the
+ Host Adapter I/O Registers.
+*/
+
+static inline
+void BusLogic_SCSIBusReset(BusLogic_HostAdapter_T *HostAdapter)
+{
+ BusLogic_ControlRegister_T ControlRegister;
+ ControlRegister.All = 0;
+ ControlRegister.Bits.SCSIBusReset = true;
+ outb(ControlRegister.All,
+ HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+}
+
+static inline
+void BusLogic_InterruptReset(BusLogic_HostAdapter_T *HostAdapter)
+{
+ BusLogic_ControlRegister_T ControlRegister;
+ ControlRegister.All = 0;
+ ControlRegister.Bits.InterruptReset = true;
+ outb(ControlRegister.All,
+ HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+}
+
+static inline
+void BusLogic_SoftReset(BusLogic_HostAdapter_T *HostAdapter)
+{
+ BusLogic_ControlRegister_T ControlRegister;
+ ControlRegister.All = 0;
+ ControlRegister.Bits.SoftReset = true;
+ outb(ControlRegister.All,
+ HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+}
+
+static inline
+void BusLogic_HardReset(BusLogic_HostAdapter_T *HostAdapter)
+{
+ BusLogic_ControlRegister_T ControlRegister;
+ ControlRegister.All = 0;
+ ControlRegister.Bits.HardReset = true;
+ outb(ControlRegister.All,
+ HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+}
+
+static inline
+unsigned char BusLogic_ReadStatusRegister(BusLogic_HostAdapter_T *HostAdapter)
+{
+ return inb(HostAdapter->IO_Address + BusLogic_StatusRegisterOffset);
+}
+
+static inline
+void BusLogic_WriteCommandParameterRegister(BusLogic_HostAdapter_T
+ *HostAdapter,
+ unsigned char Value)
+{
+ outb(Value,
+ HostAdapter->IO_Address + BusLogic_CommandParameterRegisterOffset);
+}
+
+static inline
+unsigned char BusLogic_ReadDataInRegister(BusLogic_HostAdapter_T *HostAdapter)
+{
+ return inb(HostAdapter->IO_Address + BusLogic_DataInRegisterOffset);
+}
+
+static inline
+unsigned char BusLogic_ReadInterruptRegister(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ return inb(HostAdapter->IO_Address + BusLogic_InterruptRegisterOffset);
+}
+
+static inline
+unsigned char BusLogic_ReadGeometryRegister(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ return inb(HostAdapter->IO_Address + BusLogic_GeometryRegisterOffset);
+}
+
+
+/*
+ BusLogic_StartMailboxCommand issues an Execute Mailbox Command, which
+ notifies the Host Adapter that an entry has been made in an Outgoing
+ Mailbox.
+*/
+
+static inline
+void BusLogic_StartMailboxCommand(BusLogic_HostAdapter_T *HostAdapter)
+{
+ BusLogic_WriteCommandParameterRegister(HostAdapter,
+ BusLogic_ExecuteMailboxCommand);
+}
+
+
+/*
+ BusLogic_Delay waits for Seconds to elapse.
+*/
+
+static inline void BusLogic_Delay(int Seconds)
+{
+ int Milliseconds = 1000 * Seconds;
+ unsigned long ProcessorFlags;
+ save_flags(ProcessorFlags);
+ sti();
+ while (--Milliseconds >= 0) udelay(1000);
+ restore_flags(ProcessorFlags);
+}
+
+
+/*
+ Virtual_to_Bus and Bus_to_Virtual map between Kernel Virtual Addresses
+ and PCI/VLB/EISA/ISA Bus Addresses.
+*/
+
+static inline BusLogic_BusAddress_T Virtual_to_Bus(void *VirtualAddress)
+{
+ return (BusLogic_BusAddress_T) virt_to_bus(VirtualAddress);
+}
+
+static inline void *Bus_to_Virtual(BusLogic_BusAddress_T BusAddress)
+{
+ return (void *) bus_to_virt(BusAddress);
+}
+
+
+/*
+ Virtual_to_32Bit_Virtual maps between Kernel Virtual Addresses and
+ 32 bit Kernel Virtual Addresses. This avoids compilation warnings
+ on 64 bit architectures.
+*/
+
+static inline
+BusLogic_BusAddress_T Virtual_to_32Bit_Virtual(void *VirtualAddress)
+{
+ return (BusLogic_BusAddress_T) (unsigned long) VirtualAddress;
+}
+
+
+/*
+ BusLogic_IncrementErrorCounter increments Error Counter by 1, stopping at
+ 65535 rather than wrapping around to 0.
+*/
+
+static inline void BusLogic_IncrementErrorCounter(unsigned short *ErrorCounter)
+{
+ if (*ErrorCounter < 65535) (*ErrorCounter)++;
+}
+
+
+/*
+ BusLogic_IncrementByteCounter increments Byte Counter by Amount.
+*/
+
+static inline void BusLogic_IncrementByteCounter(BusLogic_ByteCounter_T
+ *ByteCounter,
+ unsigned int Amount)
+{
+ ByteCounter->Units += Amount;
+ if (ByteCounter->Units > 999999999)
+ {
+ ByteCounter->Units -= 1000000000;
+ ByteCounter->Billions++;
+ }
+}
+
+
+/*
+ BusLogic_IncrementSizeBucket increments the Bucket for Amount.
+*/
+
+static inline void BusLogic_IncrementSizeBucket(BusLogic_CommandSizeBuckets_T
+ CommandSizeBuckets,
+ unsigned int Amount)
+{
+ int Index = 0;
+ if (Amount < 8*1024)
+ {
+ if (Amount < 2*1024)
+ Index = (Amount < 1*1024 ? 0 : 1);
+ else Index = (Amount < 4*1024 ? 2 : 3);
+ }
+ else if (Amount < 128*1024)
+ {
+ if (Amount < 32*1024)
+ Index = (Amount < 16*1024 ? 4 : 5);
+ else Index = (Amount < 64*1024 ? 6 : 7);
+ }
+ else Index = (Amount < 256*1024 ? 8 : 9);
+ CommandSizeBuckets[Index]++;
+}
+
+
+/*
+ Define the version number of the FlashPoint Firmware (SCCB Manager).
+*/
+
+#define FlashPoint_FirmwareVersion "5.02"
+
+
+/*
+ Define the possible return values from FlashPoint_HandleInterrupt.
+*/
+
+#define FlashPoint_NormalInterrupt 0x00
+#define FlashPoint_InternalError 0xFE
+#define FlashPoint_ExternalBusReset 0xFF
+
+
+/*
+ Define prototypes for the forward referenced BusLogic Driver
+ Internal Functions.
+*/
+
+static void BusLogic_QueueCompletedCCB(BusLogic_CCB_T *);
+static void BusLogic_InterruptHandler(int, void *, Registers_T *);
+static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *,
+ SCSI_Command_T *, unsigned int);
+static void BusLogic_Message(BusLogic_MessageLevel_T, char *,
+ BusLogic_HostAdapter_T *, ...);
+static void BusLogic_ParseDriverOptions(char *);
+
+
+#endif /* BusLogic_DriverVersion */
diff --git a/linux/src/drivers/scsi/FlashPoint.c b/linux/src/drivers/scsi/FlashPoint.c
new file mode 100644
index 00000000..a74c3c5f
--- /dev/null
+++ b/linux/src/drivers/scsi/FlashPoint.c
@@ -0,0 +1,12159 @@
+/*
+
+ FlashPoint.c -- FlashPoint SCCB Manager for Linux
+
+ This file contains the FlashPoint SCCB Manager from BusLogic's FlashPoint
+ Driver Developer's Kit, with minor modifications by Leonard N. Zubkoff for
+ Linux compatibility. It was provided by BusLogic in the form of 16 separate
+ source files, which would have unnecessarily cluttered the scsi directory, so
+ the individual files have been combined into this single file.
+
+ Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+
+ This file is available under both the GNU General Public License
+ and a BSD-style copyright; see LICENSE.FlashPoint for details.
+
+*/
+
+
+#include <linux/config.h>
+
+
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+
+
+#define UNIX
+#define FW_TYPE _SCCB_MGR_
+#define MAX_CARDS 8
+#undef BUSTYPE_PCI
+
+
+#define OS_InPortByte(port) inb(port)
+#define OS_InPortWord(port) inw(port)
+#define OS_InPortLong(port) inl(port)
+#define OS_OutPortByte(port, value) outb(value, port)
+#define OS_OutPortWord(port, value) outw(value, port)
+#define OS_OutPortLong(port, value) outl(value, port)
+#define OS_Lock(x)
+#define OS_UnLock(x)
+
+
+/*
+ Define name replacements for compatibility with the Linux BusLogic Driver.
+*/
+
+#define SccbMgr_sense_adapter FlashPoint_ProbeHostAdapter
+#define SccbMgr_config_adapter FlashPoint_HardwareResetHostAdapter
+#define SccbMgr_unload_card FlashPoint_ReleaseHostAdapter
+#define SccbMgr_start_sccb FlashPoint_StartCCB
+#define SccbMgr_abort_sccb FlashPoint_AbortCCB
+#define SccbMgr_my_int FlashPoint_InterruptPending
+#define SccbMgr_isr FlashPoint_HandleInterrupt
+
+
+/*
+ Define name replacements to avoid kernel namespace pollution.
+*/
+
+#define BL_Card FPT_BL_Card
+#define BusMasterInit FPT_BusMasterInit
+#define CalcCrc16 FPT_CalcCrc16
+#define CalcLrc FPT_CalcLrc
+#define ChkIfChipInitialized FPT_ChkIfChipInitialized
+#define DiagBusMaster FPT_DiagBusMaster
+#define DiagEEPROM FPT_DiagEEPROM
+#define DiagXbow FPT_DiagXbow
+#define GetTarLun FPT_GetTarLun
+#define RNVRamData FPT_RNVRamData
+#define RdStack FPT_RdStack
+#define SccbMgrTableInitAll FPT_SccbMgrTableInitAll
+#define SccbMgrTableInitCard FPT_SccbMgrTableInitCard
+#define SccbMgrTableInitTarget FPT_SccbMgrTableInitTarget
+#define SccbMgr_bad_isr FPT_SccbMgr_bad_isr
+#define SccbMgr_scsi_reset FPT_SccbMgr_scsi_reset
+#define SccbMgr_timer_expired FPT_SccbMgr_timer_expired
+#define SendMsg FPT_SendMsg
+#define Wait FPT_Wait
+#define Wait1Second FPT_Wait1Second
+#define WrStack FPT_WrStack
+#define XbowInit FPT_XbowInit
+#define autoCmdCmplt FPT_autoCmdCmplt
+#define autoLoadDefaultMap FPT_autoLoadDefaultMap
+#define busMstrDataXferStart FPT_busMstrDataXferStart
+#define busMstrSGDataXferStart FPT_busMstrSGDataXferStart
+#define busMstrTimeOut FPT_busMstrTimeOut
+#define dataXferProcessor FPT_dataXferProcessor
+#define default_intena FPT_default_intena
+#define hostDataXferAbort FPT_hostDataXferAbort
+#define hostDataXferRestart FPT_hostDataXferRestart
+#define inisci FPT_inisci
+#define mbCards FPT_mbCards
+#define nvRamInfo FPT_nvRamInfo
+#define phaseBusFree FPT_phaseBusFree
+#define phaseChkFifo FPT_phaseChkFifo
+#define phaseCommand FPT_phaseCommand
+#define phaseDataIn FPT_phaseDataIn
+#define phaseDataOut FPT_phaseDataOut
+#define phaseDecode FPT_phaseDecode
+#define phaseIllegal FPT_phaseIllegal
+#define phaseMsgIn FPT_phaseMsgIn
+#define phaseMsgOut FPT_phaseMsgOut
+#define phaseStatus FPT_phaseStatus
+#define queueAddSccb FPT_queueAddSccb
+#define queueCmdComplete FPT_queueCmdComplete
+#define queueDisconnect FPT_queueDisconnect
+#define queueFindSccb FPT_queueFindSccb
+#define queueFlushSccb FPT_queueFlushSccb
+#define queueFlushTargSccb FPT_queueFlushTargSccb
+#define queueSearchSelect FPT_queueSearchSelect
+#define queueSelectFail FPT_queueSelectFail
+#define s_PhaseTbl FPT_s_PhaseTbl
+#define scamHAString FPT_scamHAString
+#define scamInfo FPT_scamInfo
+#define scarb FPT_scarb
+#define scasid FPT_scasid
+#define scbusf FPT_scbusf
+#define sccbMgrTbl FPT_sccbMgrTbl
+#define schkdd FPT_schkdd
+#define scini FPT_scini
+#define sciso FPT_sciso
+#define scmachid FPT_scmachid
+#define scsavdi FPT_scsavdi
+#define scsel FPT_scsel
+#define scsell FPT_scsell
+#define scsendi FPT_scsendi
+#define scvalq FPT_scvalq
+#define scwirod FPT_scwirod
+#define scwiros FPT_scwiros
+#define scwtsel FPT_scwtsel
+#define scxferc FPT_scxferc
+#define sdecm FPT_sdecm
+#define sfm FPT_sfm
+#define shandem FPT_shandem
+#define sinits FPT_sinits
+#define sisyncn FPT_sisyncn
+#define sisyncr FPT_sisyncr
+#define siwidn FPT_siwidn
+#define siwidr FPT_siwidr
+#define sres FPT_sres
+#define sresb FPT_sresb
+#define ssel FPT_ssel
+#define ssenss FPT_ssenss
+#define sssyncv FPT_sssyncv
+#define stsyncn FPT_stsyncn
+#define stwidn FPT_stwidn
+#define sxfrp FPT_sxfrp
+#define utilEERead FPT_utilEERead
+#define utilEEReadOrg FPT_utilEEReadOrg
+#define utilEESendCmdAddr FPT_utilEESendCmdAddr
+#define utilEEWrite FPT_utilEEWrite
+#define utilEEWriteOnOff FPT_utilEEWriteOnOff
+#define utilUpdateResidual FPT_utilUpdateResidual
+
+
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: globals.h $
+ *
+ * Description: Common shared global defines.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+#ifndef __GLOBALS_H__
+#define __GLOBALS_H__
+
+#define _UCB_MGR_ 1
+#define _SCCB_MGR_ 2
+
+/*#include <osflags.h>*/
+
+#define MAX_CDBLEN 12
+
+#define SCAM_LEV_2 1
+
+#define CRCMASK 0xA001
+
+/* In your osflags.h file, please ENSURE that only ONE OS FLAG
+ is on at a time !!! Also, please make sure you turn set the
+ variable FW_TYPE to either _UCB_MGR_ or _SCCB_MGR_ !!! */
+
+#if defined(DOS) || defined(WIN95_16) || defined(OS2) || defined(OTHER_16)
+ #define COMPILER_16_BIT 1
+#elif defined(NETWARE) || defined(NT) || defined(WIN95_32) || defined(UNIX) || defined(OTHER_32) || defined(SOLARIS_REAL_MODE)
+ #define COMPILER_32_BIT 1
+#endif
+
+
+#define BL_VENDOR_ID 0x104B
+#define FP_DEVICE_ID 0x8130
+#define MM_DEVICE_ID 0x1040
+
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!(FALSE))
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define FAILURE 0xFFFFFFFFL
+
+
+typedef unsigned char UCHAR;
+typedef unsigned short USHORT;
+typedef unsigned int UINT;
+typedef unsigned long ULONG;
+typedef unsigned char * PUCHAR;
+typedef unsigned short* PUSHORT;
+typedef unsigned long * PULONG;
+typedef void * PVOID;
+
+
+#if defined(COMPILER_16_BIT)
+typedef unsigned char far * uchar_ptr;
+typedef unsigned short far * ushort_ptr;
+typedef unsigned long far * ulong_ptr;
+#endif /* 16_BIT_COMPILER */
+
+#if defined(COMPILER_32_BIT)
+typedef unsigned char * uchar_ptr;
+typedef unsigned short * ushort_ptr;
+typedef unsigned long * ulong_ptr;
+#endif /* 32_BIT_COMPILER */
+
+
+/* NEW TYPE DEFINITIONS (shared with Mylex North)
+
+** Use following type defines to avoid confusion in 16 and 32-bit
+** environments. Avoid using 'int' as it denotes 16 bits in 16-bit
+** environment and 32 in 32-bit environments.
+
+*/
+
+#define s08bits char
+#define s16bits short
+#define s32bits long
+
+#define u08bits unsigned s08bits
+#define u16bits unsigned s16bits
+#define u32bits unsigned s32bits
+
+#if defined(COMPILER_16_BIT)
+
+typedef u08bits far * pu08bits;
+typedef u16bits far * pu16bits;
+typedef u32bits far * pu32bits;
+
+#endif /* COMPILER_16_BIT */
+
+#if defined(COMPILER_32_BIT)
+
+typedef u08bits * pu08bits;
+typedef u16bits * pu16bits;
+typedef u32bits * pu32bits;
+
+#endif /* COMPILER_32_BIT */
+
+
+#define BIT(x) ((UCHAR)(1<<(x))) /* single-bit mask in bit position x */
+#define BITW(x) ((USHORT)(1<<(x))) /* single-bit mask in bit position x */
+
+
+
+#if defined(DOS)
+/*#include <dos.h>*/
+ #undef inportb /* undefine for Borland Lib */
+ #undef inport /* they may have define I/O function in LIB */
+ #undef outportb
+ #undef outport
+
+ #define OS_InPortByte(ioport) inportb(ioport)
+ #define OS_InPortWord(ioport) inport(ioport)
+ #define OS_InPortLong(ioport) inportq(ioport, val)
+ #define OS_OutPortByte(ioport, val) outportb(ioport, val)
+ #define OS_OutPortWord(ioport, val) outport(ioport, val)
+ #define OS_OutPortLong(ioport) outportq(ioport, val)
+#endif /* DOS */
+
+#if defined(NETWARE) || defined(OTHER_32) || defined(OTHER_16)
+ extern u08bits OS_InPortByte(u32bits ioport);
+ extern u16bits OS_InPortWord(u32bits ioport);
+ extern u32bits OS_InPortLong(u32bits ioport);
+
+ extern OS_InPortByteBuffer(u32bits ioport, pu08bits buffer, u32bits count);
+ extern OS_InPortWordBuffer(u32bits ioport, pu16bits buffer, u32bits count);
+ extern OS_OutPortByte(u32bits ioport, u08bits val);
+ extern OS_OutPortWord(u32bits ioport, u16bits val);
+ extern OS_OutPortLong(u32bits ioport, u32bits val);
+ extern OS_OutPortByteBuffer(u32bits ioport, pu08bits buffer, u32bits count);
+ extern OS_OutPortWordBuffer(u32bits ioport, pu16bits buffer, u32bits count);
+#endif /* NETWARE || OTHER_32 || OTHER_16 */
+
+#if defined (NT) || defined(WIN95_32) || defined(WIN95_16)
+ #if defined(NT)
+
+ extern __declspec(dllimport) u08bits ScsiPortReadPortUchar(pu08bits ioport);
+ extern __declspec(dllimport) u16bits ScsiPortReadPortUshort(pu16bits ioport);
+ extern __declspec(dllimport) u32bits ScsiPortReadPortUlong(pu32bits ioport);
+ extern __declspec(dllimport) void ScsiPortWritePortUchar(pu08bits ioport, u08bits val);
+ extern __declspec(dllimport) void ScsiPortWritePortUshort(pu16bits port, u16bits val);
+ extern __declspec(dllimport) void ScsiPortWritePortUlong(pu32bits port, u32bits val);
+
+ #else
+
+ extern u08bits ScsiPortReadPortUchar(pu08bits ioport);
+ extern u16bits ScsiPortReadPortUshort(pu16bits ioport);
+ extern u32bits ScsiPortReadPortUlong(pu32bits ioport);
+ extern void ScsiPortWritePortUchar(pu08bits ioport, u08bits val);
+ extern void ScsiPortWritePortUshort(pu16bits port, u16bits val);
+ extern void ScsiPortWritePortUlong(pu32bits port, u32bits val);
+ #endif
+
+
+ #define OS_InPortByte(ioport) ScsiPortReadPortUchar((pu08bits) ioport)
+ #define OS_InPortWord(ioport) ScsiPortReadPortUshort((pu16bits) ioport)
+ #define OS_InPortLong(ioport) ScsiPortReadPortUlong((pu32bits) ioport)
+
+ #define OS_OutPortByte(ioport, val) ScsiPortWritePortUchar((pu08bits) ioport, (u08bits) val)
+ #define OS_OutPortWord(ioport, val) ScsiPortWritePortUshort((pu16bits) ioport, (u16bits) val)
+ #define OS_OutPortLong(ioport, val) ScsiPortWritePortUlong((pu32bits) ioport, (u32bits) val)
+ #define OS_OutPortByteBuffer(ioport, buffer, count) \
+ ScsiPortWritePortBufferUchar((pu08bits)&port, (pu08bits) buffer, (u32bits) count)
+ #define OS_OutPortWordBuffer(ioport, buffer, count) \
+ ScsiPortWritePortBufferUshort((pu16bits)&port, (pu16bits) buffer, (u32bits) count)
+
+ #define OS_Lock(x)
+ #define OS_UnLock(x)
+#endif /* NT || WIN95_32 || WIN95_16 */
+
+#if defined (UNIX) && !defined(OS_InPortByte)
+ #define OS_InPortByte(ioport) inb((u16bits)ioport)
+ #define OS_InPortWord(ioport) inw((u16bits)ioport)
+ #define OS_InPortLong(ioport) inl((u16bits)ioport)
+ #define OS_OutPortByte(ioport,val) outb((u16bits)ioport, (u08bits)val)
+ #define OS_OutPortWord(ioport,val) outw((u16bits)ioport, (u16bits)val)
+ #define OS_OutPortLong(ioport,val) outl((u16bits)ioport, (u32bits)val)
+
+ #define OS_Lock(x)
+ #define OS_UnLock(x)
+#endif /* UNIX */
+
+
+#if defined(OS2)
+ extern u08bits inb(u32bits ioport);
+ extern u16bits inw(u32bits ioport);
+ extern void outb(u32bits ioport, u08bits val);
+ extern void outw(u32bits ioport, u16bits val);
+
+ #define OS_InPortByte(ioport) inb(ioport)
+ #define OS_InPortWord(ioport) inw(ioport)
+ #define OS_OutPortByte(ioport, val) outb(ioport, val)
+ #define OS_OutPortWord(ioport, val) outw(ioport, val)
+ extern u32bits OS_InPortLong(u32bits ioport);
+ extern void OS_OutPortLong(u32bits ioport, u32bits val);
+
+ #define OS_Lock(x)
+ #define OS_UnLock(x)
+#endif /* OS2 */
+
+#if defined(SOLARIS_REAL_MODE)
+
+extern unsigned char inb(unsigned long ioport);
+extern unsigned short inw(unsigned long ioport);
+
+#define OS_InPortByte(ioport) inb(ioport)
+#define OS_InPortWord(ioport) inw(ioport)
+
+extern void OS_OutPortByte(unsigned long ioport, unsigned char val);
+extern void OS_OutPortWord(unsigned long ioport, unsigned short val);
+extern unsigned long OS_InPortLong(unsigned long ioport);
+extern void OS_OutPortLong(unsigned long ioport, unsigned long val);
+
+#define OS_Lock(x)
+#define OS_UnLock(x)
+
+#endif /* SOLARIS_REAL_MODE */
+
+#endif /* __GLOBALS_H__ */
+
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: sccbmgr.h $
+ *
+ * Description: Common shared SCCB Interface defines and SCCB
+ * Manager specifics defines.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+#ifndef __SCCB_H__
+#define __SCCB_H__
+
+/*#include <osflags.h>*/
+/*#include <globals.h>*/
+
+#if defined(BUGBUG)
+#define debug_size 32
+#endif
+
+#if defined(DOS)
+
+ typedef struct _SCCB near *PSCCB;
+ #if (FW_TYPE == _SCCB_MGR_)
+ typedef void (*CALL_BK_FN)(PSCCB);
+ #endif
+
+#elif defined(OS2)
+
+ typedef struct _SCCB far *PSCCB;
+ #if (FW_TYPE == _SCCB_MGR_)
+ typedef void (far *CALL_BK_FN)(PSCCB);
+ #endif
+
+#else
+
+ typedef struct _SCCB *PSCCB;
+ #if (FW_TYPE == _SCCB_MGR_)
+ typedef void (*CALL_BK_FN)(PSCCB);
+ #endif
+
+#endif
+
+
+typedef struct SCCBMgr_info {
+ ULONG si_baseaddr;
+ UCHAR si_present;
+ UCHAR si_intvect;
+ UCHAR si_id;
+ UCHAR si_lun;
+ USHORT si_fw_revision;
+ USHORT si_per_targ_init_sync;
+ USHORT si_per_targ_fast_nego;
+ USHORT si_per_targ_ultra_nego;
+ USHORT si_per_targ_no_disc;
+ USHORT si_per_targ_wide_nego;
+ USHORT si_flags;
+ UCHAR si_card_family;
+ UCHAR si_bustype;
+ UCHAR si_card_model[3];
+ UCHAR si_relative_cardnum;
+ UCHAR si_reserved[4];
+ ULONG si_OS_reserved;
+ UCHAR si_XlatInfo[4];
+ ULONG si_reserved2[5];
+ ULONG si_secondary_range;
+} SCCBMGR_INFO;
+
+#if defined(DOS)
+ typedef SCCBMGR_INFO * PSCCBMGR_INFO;
+#else
+ #if defined (COMPILER_16_BIT)
+ typedef SCCBMGR_INFO far * PSCCBMGR_INFO;
+ #else
+ typedef SCCBMGR_INFO * PSCCBMGR_INFO;
+ #endif
+#endif // defined(DOS)
+
+
+
+
+#if (FW_TYPE==_SCCB_MGR_)
+ #define SCSI_PARITY_ENA 0x0001
+ #define LOW_BYTE_TERM 0x0010
+ #define HIGH_BYTE_TERM 0x0020
+ #define BUSTYPE_PCI 0x3
+#endif
+
+#define SUPPORT_16TAR_32LUN 0x0002
+#define SOFT_RESET 0x0004
+#define EXTENDED_TRANSLATION 0x0008
+#define POST_ALL_UNDERRRUNS 0x0040
+#define FLAG_SCAM_ENABLED 0x0080
+#define FLAG_SCAM_LEVEL2 0x0100
+
+
+
+
+#define HARPOON_FAMILY 0x02
+
+
+#define ISA_BUS_CARD 0x01
+#define EISA_BUS_CARD 0x02
+#define PCI_BUS_CARD 0x03
+#define VESA_BUS_CARD 0x04
+
+/* SCCB struc used for both SCCB and UCB manager compiles!
+ * The UCB Manager treats the SCCB as it's 'native hardware structure'
+ */
+
+
+#pragma pack(1)
+typedef struct _SCCB {
+ UCHAR OperationCode;
+ UCHAR ControlByte;
+ UCHAR CdbLength;
+ UCHAR RequestSenseLength;
+ ULONG DataLength;
+ ULONG DataPointer;
+ UCHAR CcbRes[2];
+ UCHAR HostStatus;
+ UCHAR TargetStatus;
+ UCHAR TargID;
+ UCHAR Lun;
+ UCHAR Cdb[12];
+ UCHAR CcbRes1;
+ UCHAR Reserved1;
+ ULONG Reserved2;
+ ULONG SensePointer;
+
+
+ CALL_BK_FN SccbCallback; /* VOID (*SccbCallback)(); */
+ ULONG SccbIOPort; /* Identifies board base port */
+ UCHAR SccbStatus;
+ UCHAR SCCBRes2;
+ USHORT SccbOSFlags;
+
+
+ ULONG Sccb_XferCnt; /* actual transfer count */
+ ULONG Sccb_ATC;
+ ULONG SccbVirtDataPtr; /* virtual addr for OS/2 */
+ ULONG Sccb_res1;
+ USHORT Sccb_MGRFlags;
+ USHORT Sccb_sgseg;
+ UCHAR Sccb_scsimsg; /* identify msg for selection */
+ UCHAR Sccb_tag;
+ UCHAR Sccb_scsistat;
+ UCHAR Sccb_idmsg; /* image of last msg in */
+ PSCCB Sccb_forwardlink;
+ PSCCB Sccb_backlink;
+ ULONG Sccb_savedATC;
+ UCHAR Save_Cdb[6];
+ UCHAR Save_CdbLen;
+ UCHAR Sccb_XferState;
+ ULONG Sccb_SGoffset;
+#if (FW_TYPE == _UCB_MGR_)
+ PUCB Sccb_ucb_ptr;
+#endif
+ } SCCB;
+
+#define SCCB_SIZE sizeof(SCCB)
+
+#pragma pack()
+
+
+
+#define SCSI_INITIATOR_COMMAND 0x00
+#define TARGET_MODE_COMMAND 0x01
+#define SCATTER_GATHER_COMMAND 0x02
+#define RESIDUAL_COMMAND 0x03
+#define RESIDUAL_SG_COMMAND 0x04
+#define RESET_COMMAND 0x81
+
+
+#define F_USE_CMD_Q 0x20 /*Inidcates TAGGED command. */
+#define TAG_TYPE_MASK 0xC0 /*Type of tag msg to send. */
+#define TAG_Q_MASK 0xE0
+#define SCCB_DATA_XFER_OUT 0x10 /* Write */
+#define SCCB_DATA_XFER_IN 0x08 /* Read */
+
+
+#define FOURTEEN_BYTES 0x00 /* Request Sense Buffer size */
+#define NO_AUTO_REQUEST_SENSE 0x01 /* No Request Sense Buffer */
+
+
+#define BUS_FREE_ST 0
+#define SELECT_ST 1
+#define SELECT_BDR_ST 2 /* Select w\ Bus Device Reset */
+#define SELECT_SN_ST 3 /* Select w\ Sync Nego */
+#define SELECT_WN_ST 4 /* Select w\ Wide Data Nego */
+#define SELECT_Q_ST 5 /* Select w\ Tagged Q'ing */
+#define COMMAND_ST 6
+#define DATA_OUT_ST 7
+#define DATA_IN_ST 8
+#define DISCONNECT_ST 9
+#define STATUS_ST 10
+#define ABORT_ST 11
+#define MESSAGE_ST 12
+
+
+#define F_HOST_XFER_DIR 0x01
+#define F_ALL_XFERRED 0x02
+#define F_SG_XFER 0x04
+#define F_AUTO_SENSE 0x08
+#define F_ODD_BALL_CNT 0x10
+#define F_NO_DATA_YET 0x80
+
+
+#define F_STATUSLOADED 0x01
+#define F_MSGLOADED 0x02
+#define F_DEV_SELECTED 0x04
+
+
+#define SCCB_COMPLETE 0x00 /* SCCB completed without error */
+#define SCCB_DATA_UNDER_RUN 0x0C
+#define SCCB_SELECTION_TIMEOUT 0x11 /* Set SCSI selection timed out */
+#define SCCB_DATA_OVER_RUN 0x12
+#define SCCB_UNEXPECTED_BUS_FREE 0x13 /* Target dropped SCSI BSY */
+#define SCCB_PHASE_SEQUENCE_FAIL 0x14 /* Target bus phase sequence failure */
+
+#define SCCB_INVALID_OP_CODE 0x16 /* SCCB invalid operation code */
+#define SCCB_INVALID_SCCB 0x1A /* Invalid SCCB - bad parameter */
+#define SCCB_GROSS_FW_ERR 0x27 /* Major problem! */
+#define SCCB_BM_ERR 0x30 /* BusMaster error. */
+#define SCCB_PARITY_ERR 0x34 /* SCSI parity error */
+
+
+
+#if (FW_TYPE==_UCB_MGR_)
+ #define HBA_AUTO_SENSE_FAIL 0x1B
+ #define HBA_TQ_REJECTED 0x1C
+ #define HBA_UNSUPORTED_MSG 0x1D
+ #define HBA_HW_ERROR 0x20
+ #define HBA_ATN_NOT_RESPONDED 0x21
+ #define HBA_SCSI_RESET_BY_ADAPTER 0x22
+ #define HBA_SCSI_RESET_BY_TARGET 0x23
+ #define HBA_WRONG_CONNECTION 0x24
+ #define HBA_BUS_DEVICE_RESET 0x25
+ #define HBA_ABORT_QUEUE 0x26
+
+#else // these are not defined in BUDI/UCB
+
+ #define SCCB_INVALID_DIRECTION 0x18 /* Invalid target direction */
+ #define SCCB_DUPLICATE_SCCB 0x19 /* Duplicate SCCB */
+ #define SCCB_SCSI_RST 0x35 /* SCSI RESET detected. */
+
+#endif // (FW_TYPE==_UCB_MGR_)
+
+
+#define SCCB_IN_PROCESS 0x00
+#define SCCB_SUCCESS 0x01
+#define SCCB_ABORT 0x02
+#define SCCB_NOT_FOUND 0x03
+#define SCCB_ERROR 0x04
+#define SCCB_INVALID 0x05
+
+#define SCCB_SIZE sizeof(SCCB)
+
+
+
+
+#if (FW_TYPE == _UCB_MGR_)
+ void SccbMgr_start_sccb(CARD_HANDLE pCurrCard, PUCB p_ucb);
+ s32bits SccbMgr_abort_sccb(CARD_HANDLE pCurrCard, PUCB p_ucb);
+ u08bits SccbMgr_my_int(CARD_HANDLE pCurrCard);
+ s32bits SccbMgr_isr(CARD_HANDLE pCurrCard);
+ void SccbMgr_scsi_reset(CARD_HANDLE pCurrCard);
+ void SccbMgr_timer_expired(CARD_HANDLE pCurrCard);
+ void SccbMgr_unload_card(CARD_HANDLE pCurrCard);
+ void SccbMgr_restore_foreign_state(CARD_HANDLE pCurrCard);
+ void SccbMgr_restore_native_state(CARD_HANDLE pCurrCard);
+ void SccbMgr_save_foreign_state(PADAPTER_INFO pAdapterInfo);
+
+#endif
+
+
+#if (FW_TYPE == _SCCB_MGR_)
+
+ #if defined (DOS)
+ int SccbMgr_sense_adapter(PSCCBMGR_INFO pCardInfo);
+ USHORT SccbMgr_config_adapter(PSCCBMGR_INFO pCardInfo);
+ void SccbMgr_start_sccb(USHORT pCurrCard, PSCCB p_SCCB);
+ int SccbMgr_abort_sccb(USHORT pCurrCard, PSCCB p_SCCB);
+ UCHAR SccbMgr_my_int(USHORT pCurrCard);
+ int SccbMgr_isr(USHORT pCurrCard);
+ void SccbMgr_scsi_reset(USHORT pCurrCard);
+ void SccbMgr_timer_expired(USHORT pCurrCard);
+ USHORT SccbMgr_status(USHORT pCurrCard);
+ void SccbMgr_unload_card(USHORT pCurrCard);
+
+ #else //non-DOS
+
+ int SccbMgr_sense_adapter(PSCCBMGR_INFO pCardInfo);
+ ULONG SccbMgr_config_adapter(PSCCBMGR_INFO pCardInfo);
+ void SccbMgr_start_sccb(ULONG pCurrCard, PSCCB p_SCCB);
+ int SccbMgr_abort_sccb(ULONG pCurrCard, PSCCB p_SCCB);
+ UCHAR SccbMgr_my_int(ULONG pCurrCard);
+ int SccbMgr_isr(ULONG pCurrCard);
+ void SccbMgr_scsi_reset(ULONG pCurrCard);
+ void SccbMgr_enable_int(ULONG pCurrCard);
+ void SccbMgr_disable_int(ULONG pCurrCard);
+ void SccbMgr_timer_expired(ULONG pCurrCard);
+ void SccbMgr_unload_card(ULONG pCurrCard);
+
+ #endif
+#endif // (FW_TYPE == _SCCB_MGR_)
+
+#endif /* __SCCB_H__ */
+
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: blx30.h $
+ *
+ * Description: This module contains SCCB/UCB Manager implementation
+ * specific stuff.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+
+#ifndef __blx30_H__
+#define __blx30_H__
+
+/*#include <globals.h>*/
+
+#define ORION_FW_REV 3110
+
+
+
+
+#define HARP_REVD 1
+
+
+#if defined(DOS)
+#define QUEUE_DEPTH 8+1 /*1 for Normal disconnect 0 for Q'ing. */
+#else
+#define QUEUE_DEPTH 254+1 /*1 for Normal disconnect 32 for Q'ing. */
+#endif // defined(DOS)
+
+#define MAX_MB_CARDS 4 /* Max. no of cards suppoerted on Mother Board */
+
+#define WIDE_SCSI 1
+
+#if defined(WIDE_SCSI)
+ #if defined(DOS)
+ #define MAX_SCSI_TAR 16
+ #define MAX_LUN 8
+ #define LUN_MASK 0x07
+ #else
+ #define MAX_SCSI_TAR 16
+ #define MAX_LUN 32
+ #define LUN_MASK 0x1f
+
+ #endif
+#else
+ #define MAX_SCSI_TAR 8
+ #define MAX_LUN 8
+ #define LUN_MASK 0x07
+#endif
+
+#if defined(HARP_REVA)
+#define SG_BUF_CNT 15 /*Number of prefetched elements. */
+#else
+#define SG_BUF_CNT 16 /*Number of prefetched elements. */
+#endif
+
+#define SG_ELEMENT_SIZE 8 /*Eight byte per element. */
+#define SG_LOCAL_MASK 0x00000000L
+#define SG_ELEMENT_MASK 0xFFFFFFFFL
+
+
+#if (FW_TYPE == _UCB_MGR_)
+ #define OPC_DECODE_NORMAL 0x0f7f
+#endif // _UCB_MGR_
+
+
+
+#if defined(DOS)
+
+/*#include <dos.h>*/
+ #define RD_HARPOON(ioport) (OS_InPortByte(ioport))
+ #define RDW_HARPOON(ioport) (OS_InPortWord(ioport))
+ #define WR_HARPOON(ioport,val) (OS_OutPortByte(ioport,val))
+ #define WRW_HARPOON(ioport,val) (OS_OutPortWord(ioport,val))
+
+ #define RD_HARP32(port,offset,data) asm{db 66h; \
+ push ax; \
+ mov dx,port; \
+ add dx, offset; \
+ db 66h; \
+ in ax,dx; \
+ db 66h; \
+ mov word ptr data,ax;\
+ db 66h; \
+ pop ax}
+
+ #define WR_HARP32(port,offset,data) asm{db 66h; \
+ push ax; \
+ mov dx,port; \
+ add dx, offset; \
+ db 66h; \
+ mov ax,word ptr data;\
+ db 66h; \
+ out dx,ax; \
+ db 66h; \
+ pop ax}
+#endif /* DOS */
+
+#if defined(NETWARE) || defined(OTHER_32) || defined(OTHER_16)
+ #define RD_HARPOON(ioport) OS_InPortByte((unsigned long)ioport)
+ #define RDW_HARPOON(ioport) OS_InPortWord((unsigned long)ioport)
+ #define RD_HARP32(ioport,offset,data) (data = OS_InPortLong(ioport + offset))
+ #define WR_HARPOON(ioport,val) OS_OutPortByte((ULONG)ioport,(UCHAR) val)
+ #define WRW_HARPOON(ioport,val) OS_OutPortWord((ULONG)ioport,(USHORT)val)
+ #define WR_HARP32(ioport,offset,data) OS_OutPortLong((ioport + offset), data)
+#endif /* NETWARE || OTHER_32 || OTHER_16 */
+
+#if defined(NT) || defined(WIN95_32) || defined(WIN95_16)
+ #define RD_HARPOON(ioport) OS_InPortByte((ULONG)ioport)
+ #define RDW_HARPOON(ioport) OS_InPortWord((ULONG)ioport)
+ #define RD_HARP32(ioport,offset,data) (data = OS_InPortLong((ULONG)(ioport + offset)))
+ #define WR_HARPOON(ioport,val) OS_OutPortByte((ULONG)ioport,(UCHAR) val)
+ #define WRW_HARPOON(ioport,val) OS_OutPortWord((ULONG)ioport,(USHORT)val)
+ #define WR_HARP32(ioport,offset,data) OS_OutPortLong((ULONG)(ioport + offset), data)
+#endif /* NT || WIN95_32 || WIN95_16 */
+
+#if defined (UNIX)
+ #define RD_HARPOON(ioport) OS_InPortByte((u32bits)ioport)
+ #define RDW_HARPOON(ioport) OS_InPortWord((u32bits)ioport)
+ #define RD_HARP32(ioport,offset,data) (data = OS_InPortLong((u32bits)(ioport + offset)))
+ #define WR_HARPOON(ioport,val) OS_OutPortByte((u32bits)ioport,(u08bits) val)
+ #define WRW_HARPOON(ioport,val) OS_OutPortWord((u32bits)ioport,(u16bits)val)
+ #define WR_HARP32(ioport,offset,data) OS_OutPortLong((u32bits)(ioport + offset), data)
+#endif /* UNIX */
+
+#if defined(OS2)
+ #define RD_HARPOON(ioport) OS_InPortByte((unsigned long)ioport)
+ #define RDW_HARPOON(ioport) OS_InPortWord((unsigned long)ioport)
+ #define RD_HARP32(ioport,offset,data) (data = OS_InPortLong((ULONG)(ioport + offset)))
+ #define WR_HARPOON(ioport,val) OS_OutPortByte((ULONG)ioport,(UCHAR) val)
+ #define WRW_HARPOON(ioport,val) OS_OutPortWord((ULONG)ioport,(USHORT)val)
+ #define WR_HARP32(ioport,offset,data) OS_OutPortLong(((ULONG)(ioport + offset)), data)
+#endif /* OS2 */
+
+#if defined(SOLARIS_REAL_MODE)
+
+ #define RD_HARPOON(ioport) OS_InPortByte((unsigned long)ioport)
+ #define RDW_HARPOON(ioport) OS_InPortWord((unsigned long)ioport)
+ #define RD_HARP32(ioport,offset,data) (data = OS_InPortLong((ULONG)(ioport + offset)))
+ #define WR_HARPOON(ioport,val) OS_OutPortByte((ULONG)ioport,(UCHAR) val)
+ #define WRW_HARPOON(ioport,val) OS_OutPortWord((ULONG)ioport,(USHORT)val)
+ #define WR_HARP32(ioport,offset,data) OS_OutPortLong((ULONG)(ioport + offset), (ULONG)data)
+
+#endif /* SOLARIS_REAL_MODE */
+
+#endif /* __BLX30_H__ */
+
+
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: target.h $
+ *
+ * Description: Definitions for Target related structures
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+#ifndef __TARGET__
+#define __TARGET__
+
+/*#include <globals.h>*/
+/*#include <blx30.h>*/
+
+
+#define TAR_SYNC_MASK (BIT(7)+BIT(6))
+#define SYNC_UNKNOWN 0x00
+#define SYNC_TRYING BIT(6)
+#define SYNC_SUPPORTED (BIT(7)+BIT(6))
+
+#define TAR_WIDE_MASK (BIT(5)+BIT(4))
+#define WIDE_DISABLED 0x00
+#define WIDE_ENABLED BIT(4)
+#define WIDE_NEGOCIATED BIT(5)
+
+#define TAR_TAG_Q_MASK (BIT(3)+BIT(2))
+#define TAG_Q_UNKNOWN 0x00
+#define TAG_Q_TRYING BIT(2)
+#define TAG_Q_REJECT BIT(3)
+#define TAG_Q_SUPPORTED (BIT(3)+BIT(2))
+
+#define TAR_ALLOW_DISC BIT(0)
+
+
+#define EE_SYNC_MASK (BIT(0)+BIT(1))
+#define EE_SYNC_ASYNC 0x00
+#define EE_SYNC_5MB BIT(0)
+#define EE_SYNC_10MB BIT(1)
+#define EE_SYNC_20MB (BIT(0)+BIT(1))
+
+#define EE_ALLOW_DISC BIT(6)
+#define EE_WIDE_SCSI BIT(7)
+
+
+#if defined(DOS)
+ typedef struct SCCBMgr_tar_info near *PSCCBMgr_tar_info;
+
+#elif defined(OS2)
+ typedef struct SCCBMgr_tar_info far *PSCCBMgr_tar_info;
+
+#else
+ typedef struct SCCBMgr_tar_info *PSCCBMgr_tar_info;
+
+#endif
+
+
+typedef struct SCCBMgr_tar_info {
+
+ PSCCB TarSelQ_Head;
+ PSCCB TarSelQ_Tail;
+ UCHAR TarLUN_CA; /*Contingent Allgiance */
+ UCHAR TarTagQ_Cnt;
+ UCHAR TarSelQ_Cnt;
+ UCHAR TarStatus;
+ UCHAR TarEEValue;
+ UCHAR TarSyncCtrl;
+ UCHAR TarReserved[2]; /* for alignment */
+ UCHAR LunDiscQ_Idx[MAX_LUN];
+ UCHAR TarLUNBusy[MAX_LUN];
+} SCCBMGR_TAR_INFO;
+
+typedef struct NVRAMInfo {
+ UCHAR niModel; /* Model No. of card */
+ UCHAR niCardNo; /* Card no. */
+#if defined(DOS)
+ USHORT niBaseAddr; /* Port Address of card */
+#else
+ ULONG niBaseAddr; /* Port Address of card */
+#endif
+ UCHAR niSysConf; /* Adapter Configuration byte - Byte 16 of eeprom map */
+ UCHAR niScsiConf; /* SCSI Configuration byte - Byte 17 of eeprom map */
+ UCHAR niScamConf; /* SCAM Configuration byte - Byte 20 of eeprom map */
+ UCHAR niAdapId; /* Host Adapter ID - Byte 24 of eerpom map */
+ UCHAR niSyncTbl[MAX_SCSI_TAR / 2]; /* Sync/Wide byte of targets */
+ UCHAR niScamTbl[MAX_SCSI_TAR][4]; /* Compressed Scam name string of Targets */
+}NVRAMINFO;
+
+#if defined(DOS)
+typedef NVRAMINFO near *PNVRamInfo;
+#elif defined (OS2)
+typedef NVRAMINFO far *PNVRamInfo;
+#else
+typedef NVRAMINFO *PNVRamInfo;
+#endif
+
+#define MODEL_LT 1
+#define MODEL_DL 2
+#define MODEL_LW 3
+#define MODEL_DW 4
+
+
+typedef struct SCCBcard {
+ PSCCB currentSCCB;
+#if (FW_TYPE==_SCCB_MGR_)
+ PSCCBMGR_INFO cardInfo;
+#else
+ PADAPTER_INFO cardInfo;
+#endif
+
+#if defined(DOS)
+ USHORT ioPort;
+#else
+ ULONG ioPort;
+#endif
+
+ USHORT cmdCounter;
+ UCHAR discQCount;
+ UCHAR tagQ_Lst;
+ UCHAR cardIndex;
+ UCHAR scanIndex;
+ UCHAR globalFlags;
+ UCHAR ourId;
+ PNVRamInfo pNvRamInfo;
+ PSCCB discQ_Tbl[QUEUE_DEPTH];
+
+}SCCBCARD;
+
+#if defined(DOS)
+typedef struct SCCBcard near *PSCCBcard;
+#elif defined (OS2)
+typedef struct SCCBcard far *PSCCBcard;
+#else
+typedef struct SCCBcard *PSCCBcard;
+#endif
+
+
+#define F_TAG_STARTED 0x01
+#define F_CONLUN_IO 0x02
+#define F_DO_RENEGO 0x04
+#define F_NO_FILTER 0x08
+#define F_GREEN_PC 0x10
+#define F_HOST_XFER_ACT 0x20
+#define F_NEW_SCCB_CMD 0x40
+#define F_UPDATE_EEPROM 0x80
+
+
+#define ID_STRING_LENGTH 32
+#define TYPE_CODE0 0x63 /*Level2 Mstr (bits 7-6), */
+
+#define TYPE_CODE1 00 /*No ID yet */
+
+#define SLV_TYPE_CODE0 0xA3 /*Priority Bit set (bits 7-6), */
+
+#define ASSIGN_ID 0x00
+#define SET_P_FLAG 0x01
+#define CFG_CMPLT 0x03
+#define DOM_MSTR 0x0F
+#define SYNC_PTRN 0x1F
+
+#define ID_0_7 0x18
+#define ID_8_F 0x11
+#define ID_10_17 0x12
+#define ID_18_1F 0x0B
+#define MISC_CODE 0x14
+#define CLR_P_FLAG 0x18
+#define LOCATE_ON 0x12
+#define LOCATE_OFF 0x0B
+
+#define LVL_1_MST 0x00
+#define LVL_2_MST 0x40
+#define DOM_LVL_2 0xC0
+
+
+#define INIT_SELTD 0x01
+#define LEVEL2_TAR 0x02
+
+
+enum scam_id_st { ID0,ID1,ID2,ID3,ID4,ID5,ID6,ID7,ID8,ID9,ID10,ID11,ID12,
+ ID13,ID14,ID15,ID_UNUSED,ID_UNASSIGNED,ID_ASSIGNED,LEGACY,
+ CLR_PRIORITY,NO_ID_AVAIL };
+
+typedef struct SCCBscam_info {
+
+ UCHAR id_string[ID_STRING_LENGTH];
+ enum scam_id_st state;
+
+} SCCBSCAM_INFO, *PSCCBSCAM_INFO;
+
+#endif
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: scsi2.h $
+ *
+ * Description: Register definitions for HARPOON ASIC.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+#ifndef __SCSI_H__
+#define __SCSI_H__
+
+
+
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_REZERO_UNIT 0x01
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_FORMAT_UNIT 0x04
+#define SCSI_REASSIGN 0x07
+#define SCSI_READ 0x08
+#define SCSI_WRITE 0x0A
+#define SCSI_SEEK 0x0B
+#define SCSI_INQUIRY 0x12
+#define SCSI_MODE_SELECT 0x15
+#define SCSI_RESERVE_UNIT 0x16
+#define SCSI_RELEASE_UNIT 0x17
+#define SCSI_MODE_SENSE 0x1A
+#define SCSI_START_STOP_UNIT 0x1B
+#define SCSI_SEND_DIAGNOSTIC 0x1D
+#define SCSI_READ_CAPACITY 0x25
+#define SCSI_READ_EXTENDED 0x28
+#define SCSI_WRITE_EXTENDED 0x2A
+#define SCSI_SEEK_EXTENDED 0x2B
+#define SCSI_WRITE_AND_VERIFY 0x2E
+#define SCSI_VERIFY 0x2F
+#define SCSI_READ_DEFECT_DATA 0x37
+#define SCSI_WRITE_BUFFER 0x3B
+#define SCSI_READ_BUFFER 0x3C
+#define SCSI_RECV_DIAGNOSTIC 0x1C
+#define SCSI_READ_LONG 0x3E
+#define SCSI_WRITE_LONG 0x3F
+#define SCSI_LAST_SCSI_CMND SCSI_WRITE_LONG
+#define SCSI_INVALID_CMND 0xFF
+
+
+
+#define SSGOOD 0x00
+#define SSCHECK 0x02
+#define SSCOND_MET 0x04
+#define SSBUSY 0x08
+#define SSRESERVATION_CONFLICT 0x18
+#define SSCMD_TERM 0x22
+#define SSQ_FULL 0x28
+
+
+#define SKNO_SEN 0x00
+#define SKRECOV_ERR 0x01
+#define SKNOT_RDY 0x02
+#define SKMED_ERR 0x03
+#define SKHW_ERR 0x04
+#define SKILL_REQ 0x05
+#define SKUNIT_ATTN 0x06
+#define SKDATA_PROTECT 0x07
+#define SKBLNK_CHK 0x08
+#define SKCPY_ABORT 0x0A
+#define SKABORT_CMD 0x0B
+#define SKEQUAL 0x0C
+#define SKVOL_OVF 0x0D
+#define SKMIS_CMP 0x0E
+
+
+#define SMCMD_COMP 0x00
+#define SMEXT 0x01
+#define SMSAVE_DATA_PTR 0x02
+#define SMREST_DATA_PTR 0x03
+#define SMDISC 0x04
+#define SMINIT_DETEC_ERR 0x05
+#define SMABORT 0x06
+#define SMREJECT 0x07
+#define SMNO_OP 0x08
+#define SMPARITY 0x09
+#define SMDEV_RESET 0x0C
+#define SMABORT_TAG 0x0D
+#define SMINIT_RECOVERY 0x0F
+#define SMREL_RECOVERY 0x10
+
+#define SMIDENT 0x80
+#define DISC_PRIV 0x40
+
+
+#define SMSYNC 0x01
+#define SM10MBS 0x19 /* 100ns */
+#define SM5MBS 0x32 /* 200ns */
+#define SMOFFSET 0x0F /* Maxoffset value */
+#define SMWDTR 0x03
+#define SM8BIT 0x00
+#define SM16BIT 0x01
+#define SM32BIT 0x02
+#define SMIGNORWR 0x23 /* Ignore Wide Residue */
+
+
+#define ARBITRATION_DELAY 0x01 /* 2.4us using a 40Mhz clock */
+#define BUS_SETTLE_DELAY 0x01 /* 400ns */
+#define BUS_CLEAR_DELAY 0x01 /* 800ns */
+
+
+
+#define SPHASE_TO 0x0A /* 10 second timeout waiting for */
+#define SCMD_TO 0x0F /* Overall command timeout */
+
+
+
+#define SIX_BYTE_CMD 0x06
+#define TEN_BYTE_CMD 0x0A
+#define TWELVE_BYTE_CMD 0x0C
+
+#define ASYNC 0x00
+#define PERI25NS 0x06 /* 25/4ns to next clock for xbow. */
+#define SYNC10MBS 0x19
+#define SYNC5MBS 0x32
+#define MAX_OFFSET 0x0F /* Maxbyteoffset for Sync Xfers */
+
+#endif
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: eeprom.h $
+ *
+ * Description: Definitions for EEPROM related structures
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+#ifndef __EEPROM__
+#define __EEPROM__
+
+/*#include <globals.h>*/
+
+#define EEPROM_WD_CNT 256
+
+#define EEPROM_CHECK_SUM 0
+#define FW_SIGNATURE 2
+#define MODEL_NUMB_0 4
+#define MODEL_NUMB_1 5
+#define MODEL_NUMB_2 6
+#define MODEL_NUMB_3 7
+#define MODEL_NUMB_4 8
+#define MODEL_NUMB_5 9
+#define IO_BASE_ADDR 10
+#define IRQ_NUMBER 12
+#define PCI_INT_PIN 13
+#define BUS_DELAY 14 /*On time in byte 14 off delay in 15 */
+#define SYSTEM_CONFIG 16
+#define SCSI_CONFIG 17
+#define BIOS_CONFIG 18
+#define SPIN_UP_DELAY 19
+#define SCAM_CONFIG 20
+#define ADAPTER_SCSI_ID 24
+
+
+#define IGNORE_B_SCAN 32
+#define SEND_START_ENA 34
+#define DEVICE_ENABLE 36
+
+#define SYNC_RATE_TBL 38
+#define SYNC_RATE_TBL01 38
+#define SYNC_RATE_TBL23 40
+#define SYNC_RATE_TBL45 42
+#define SYNC_RATE_TBL67 44
+#define SYNC_RATE_TBL89 46
+#define SYNC_RATE_TBLab 48
+#define SYNC_RATE_TBLcd 50
+#define SYNC_RATE_TBLef 52
+
+
+
+#define EE_SCAMBASE 256
+
+
+
+ #define DOM_MASTER (BIT(0) + BIT(1))
+ #define SCAM_ENABLED BIT(2)
+ #define SCAM_LEVEL2 BIT(3)
+
+
+ #define RENEGO_ENA BITW(10)
+ #define CONNIO_ENA BITW(11)
+ #define GREEN_PC_ENA BITW(12)
+
+
+ #define AUTO_RATE_00 00
+ #define AUTO_RATE_05 01
+ #define AUTO_RATE_10 02
+ #define AUTO_RATE_20 03
+
+ #define WIDE_NEGO_BIT BIT(7)
+ #define DISC_ENABLE_BIT BIT(6)
+
+
+#endif
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: harpoon.h $
+ *
+ * Description: Register definitions for HARPOON ASIC.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+
+/*#include <globals.h>*/
+
+#ifndef __HARPOON__
+#define __HARPOON__
+
+
+ #define hp_vendor_id_0 0x00 /* LSB */
+ #define ORION_VEND_0 0x4B
+
+ #define hp_vendor_id_1 0x01 /* MSB */
+ #define ORION_VEND_1 0x10
+
+ #define hp_device_id_0 0x02 /* LSB */
+ #define ORION_DEV_0 0x30
+
+ #define hp_device_id_1 0x03 /* MSB */
+ #define ORION_DEV_1 0x81
+
+ /* Sub Vendor ID and Sub Device ID only available in
+ Harpoon Version 2 and higher */
+
+ #define hp_sub_vendor_id_0 0x04 /* LSB */
+ #define hp_sub_vendor_id_1 0x05 /* MSB */
+ #define hp_sub_device_id_0 0x06 /* LSB */
+ #define hp_sub_device_id_1 0x07 /* MSB */
+
+
+ #define hp_dual_addr_lo 0x08
+ #define hp_dual_addr_lmi 0x09
+ #define hp_dual_addr_hmi 0x0A
+ #define hp_dual_addr_hi 0x0B
+
+ #define hp_semaphore 0x0C
+ #define SCCB_MGR_ACTIVE BIT(0)
+ #define TICKLE_ME BIT(1)
+ #define SCCB_MGR_PRESENT BIT(3)
+ #define BIOS_IN_USE BIT(4)
+
+ #define hp_user_defined_D 0x0D
+
+ #define hp_reserved_E 0x0E
+
+ #define hp_sys_ctrl 0x0F
+
+ #define STOP_CLK BIT(0) /*Turn off BusMaster Clock */
+ #define DRVR_RST BIT(1) /*Firmware Reset to 80C15 chip */
+ #define HALT_MACH BIT(3) /*Halt State Machine */
+ #define HARD_ABORT BIT(4) /*Hard Abort */
+ #define DIAG_MODE BIT(5) /*Diagnostic Mode */
+
+ #define BM_ABORT_TMOUT 0x50 /*Halt State machine time out */
+
+ #define hp_sys_cfg 0x10
+
+ #define DONT_RST_FIFO BIT(7) /*Don't reset FIFO */
+
+
+ #define hp_host_ctrl0 0x11
+
+ #define DUAL_ADDR_MODE BIT(0) /*Enable 64-bit addresses */
+ #define IO_MEM_SPACE BIT(1) /*I/O Memory Space */
+ #define RESOURCE_LOCK BIT(2) /*Enable Resource Lock */
+ #define IGNOR_ACCESS_ERR BIT(3) /*Ignore Access Error */
+ #define HOST_INT_EDGE BIT(4) /*Host interrupt level/edge mode sel */
+ #define SIX_CLOCKS BIT(5) /*6 Clocks between Strobe */
+ #define DMA_EVEN_PARITY BIT(6) /*Enable DMA Enen Parity */
+
+/*
+ #define BURST_MODE BIT(0)
+*/
+
+ #define hp_reserved_12 0x12
+
+ #define hp_host_blk_cnt 0x13
+
+ #define XFER_BLK1 0x00 /* 0 0 0 1 byte per block*/
+ #define XFER_BLK2 0x01 /* 0 0 1 2 byte per block*/
+ #define XFER_BLK4 0x02 /* 0 1 0 4 byte per block*/
+ #define XFER_BLK8 0x03 /* 0 1 1 8 byte per block*/
+ #define XFER_BLK16 0x04 /* 1 0 0 16 byte per block*/
+ #define XFER_BLK32 0x05 /* 1 0 1 32 byte per block*/
+ #define XFER_BLK64 0x06 /* 1 1 0 64 byte per block*/
+
+ #define BM_THRESHOLD 0x40 /* PCI mode can only xfer 16 bytes*/
+
+
+ #define hp_reserved_14 0x14
+ #define hp_reserved_15 0x15
+ #define hp_reserved_16 0x16
+
+ #define hp_int_mask 0x17
+
+ #define INT_CMD_COMPL BIT(0) /* DMA command complete */
+ #define INT_EXT_STATUS BIT(1) /* Extended Status Set */
+ #define INT_SCSI BIT(2) /* Scsi block interrupt */
+ #define INT_FIFO_RDY BIT(4) /* FIFO data ready */
+
+
+ #define hp_xfer_cnt_lo 0x18
+ #define hp_xfer_cnt_mi 0x19
+ #define hp_xfer_cnt_hi 0x1A
+ #define hp_xfer_cmd 0x1B
+
+ #define XFER_HOST_DMA 0x00 /* 0 0 0 Transfer Host -> DMA */
+ #define XFER_DMA_HOST 0x01 /* 0 0 1 Transfer DMA -> Host */
+ #define XFER_HOST_MPU 0x02 /* 0 1 0 Transfer Host -> MPU */
+ #define XFER_MPU_HOST 0x03 /* 0 1 1 Transfer MPU -> Host */
+ #define XFER_DMA_MPU 0x04 /* 1 0 0 Transfer DMA -> MPU */
+ #define XFER_MPU_DMA 0x05 /* 1 0 1 Transfer MPU -> DMA */
+ #define SET_SEMAPHORE 0x06 /* 1 1 0 Set Semaphore */
+ #define XFER_NOP 0x07 /* 1 1 1 Transfer NOP */
+ #define XFER_MB_MPU 0x06 /* 1 1 0 Transfer MB -> MPU */
+ #define XFER_MB_DMA 0x07 /* 1 1 1 Transfer MB -> DMA */
+
+
+ #define XFER_HOST_AUTO 0x00 /* 0 0 Auto Transfer Size */
+ #define XFER_HOST_8BIT 0x08 /* 0 1 8 BIT Transfer Size */
+ #define XFER_HOST_16BIT 0x10 /* 1 0 16 BIT Transfer Size */
+ #define XFER_HOST_32BIT 0x18 /* 1 1 32 BIT Transfer Size */
+
+ #define XFER_DMA_8BIT 0x20 /* 0 1 8 BIT Transfer Size */
+ #define XFER_DMA_16BIT 0x40 /* 1 0 16 BIT Transfer Size */
+
+ #define DISABLE_INT BIT(7) /*Do not interrupt at end of cmd. */
+
+ #define HOST_WRT_CMD ((DISABLE_INT + XFER_HOST_DMA + XFER_HOST_AUTO + XFER_DMA_8BIT))
+ #define HOST_RD_CMD ((DISABLE_INT + XFER_DMA_HOST + XFER_HOST_AUTO + XFER_DMA_8BIT))
+ #define WIDE_HOST_WRT_CMD ((DISABLE_INT + XFER_HOST_DMA + XFER_HOST_AUTO + XFER_DMA_16BIT))
+ #define WIDE_HOST_RD_CMD ((DISABLE_INT + XFER_DMA_HOST + XFER_HOST_AUTO + XFER_DMA_16BIT))
+
+ #define hp_host_addr_lo 0x1C
+ #define hp_host_addr_lmi 0x1D
+ #define hp_host_addr_hmi 0x1E
+ #define hp_host_addr_hi 0x1F
+
+ #define hp_pio_data 0x20
+ #define hp_reserved_21 0x21
+ #define hp_ee_ctrl 0x22
+
+ #define EXT_ARB_ACK BIT(7)
+ #define SCSI_TERM_ENA_H BIT(6) /* SCSI high byte terminator */
+ #define SEE_MS BIT(5)
+ #define SEE_CS BIT(3)
+ #define SEE_CLK BIT(2)
+ #define SEE_DO BIT(1)
+ #define SEE_DI BIT(0)
+
+ #define EE_READ 0x06
+ #define EE_WRITE 0x05
+ #define EWEN 0x04
+ #define EWEN_ADDR 0x03C0
+ #define EWDS 0x04
+ #define EWDS_ADDR 0x0000
+
+ #define hp_brdctl 0x23
+
+ #define DAT_7 BIT(7)
+ #define DAT_6 BIT(6)
+ #define DAT_5 BIT(5)
+ #define BRD_STB BIT(4)
+ #define BRD_CS BIT(3)
+ #define BRD_WR BIT(2)
+
+ #define hp_reserved_24 0x24
+ #define hp_reserved_25 0x25
+
+
+
+
+ #define hp_bm_ctrl 0x26
+
+ #define SCSI_TERM_ENA_L BIT(0) /*Enable/Disable external terminators */
+ #define FLUSH_XFER_CNTR BIT(1) /*Flush transfer counter */
+ #define BM_XFER_MIN_8 BIT(2) /*Enable bus master transfer of 9 */
+ #define BIOS_ENA BIT(3) /*Enable BIOS/FLASH Enable */
+ #define FORCE1_XFER BIT(5) /*Always xfer one byte in byte mode */
+ #define FAST_SINGLE BIT(6) /*?? */
+
+ #define BMCTRL_DEFAULT (FORCE1_XFER|FAST_SINGLE|SCSI_TERM_ENA_L)
+
+ #define hp_reserved_27 0x27
+
+ #define hp_sg_addr 0x28
+ #define hp_page_ctrl 0x29
+
+ #define SCATTER_EN BIT(0)
+ #define SGRAM_ARAM BIT(1)
+ #define BIOS_SHADOW BIT(2)
+ #define G_INT_DISABLE BIT(3) /* Enable/Disable all Interrupts */
+ #define NARROW_SCSI_CARD BIT(4) /* NARROW/WIDE SCSI config pin */
+
+ #define hp_reserved_2A 0x2A
+ #define hp_pci_cmd_cfg 0x2B
+
+ #define IO_SPACE_ENA BIT(0) /*enable I/O space */
+ #define MEM_SPACE_ENA BIT(1) /*enable memory space */
+ #define BUS_MSTR_ENA BIT(2) /*enable bus master operation */
+ #define MEM_WI_ENA BIT(4) /*enable Write and Invalidate */
+ #define PAR_ERR_RESP BIT(6) /*enable parity error responce. */
+
+ #define hp_reserved_2C 0x2C
+
+ #define hp_pci_stat_cfg 0x2D
+
+ #define DATA_PARITY_ERR BIT(0)
+ #define REC_TARGET_ABORT BIT(4) /*received Target abort */
+ #define REC_MASTER_ABORT BIT(5) /*received Master abort */
+ #define SIG_SYSTEM_ERR BIT(6)
+ #define DETECTED_PAR_ERR BIT(7)
+
+ #define hp_reserved_2E 0x2E
+
+ #define hp_sys_status 0x2F
+
+ #define SLV_DATA_RDY BIT(0) /*Slave data ready */
+ #define XFER_CNT_ZERO BIT(1) /*Transfer counter = 0 */
+ #define BM_FIFO_EMPTY BIT(2) /*FIFO empty */
+ #define BM_FIFO_FULL BIT(3) /*FIFO full */
+ #define HOST_OP_DONE BIT(4) /*host operation done */
+ #define DMA_OP_DONE BIT(5) /*DMA operation done */
+ #define SLV_OP_DONE BIT(6) /*Slave operation done */
+ #define PWR_ON_FLAG BIT(7) /*Power on flag */
+
+ #define hp_reserved_30 0x30
+
+ #define hp_host_status0 0x31
+
+ #define HOST_TERM BIT(5) /*Host Terminal Count */
+ #define HOST_TRSHLD BIT(6) /*Host Threshold */
+ #define CONNECTED_2_HOST BIT(7) /*Connected to Host */
+
+ #define hp_reserved_32 0x32
+
+ #define hp_rev_num 0x33
+
+ #define REV_A_CONST 0x0E
+ #define REV_B_CONST 0x0E
+
+ #define hp_stack_data 0x34
+ #define hp_stack_addr 0x35
+
+ #define hp_ext_status 0x36
+
+ #define BM_FORCE_OFF BIT(0) /*Bus Master is forced to get off */
+ #define PCI_TGT_ABORT BIT(0) /*PCI bus master transaction aborted */
+ #define PCI_DEV_TMOUT BIT(1) /*PCI Device Time out */
+ #define FIFO_TC_NOT_ZERO BIT(2) /*FIFO or transfer counter not zero */
+ #define CHIP_RST_OCCUR BIT(3) /*Chip reset occurs */
+ #define CMD_ABORTED BIT(4) /*Command aborted */
+ #define BM_PARITY_ERR BIT(5) /*parity error on data received */
+ #define PIO_OVERRUN BIT(6) /*Slave data overrun */
+ #define BM_CMD_BUSY BIT(7) /*Bus master transfer command busy */
+ #define BAD_EXT_STATUS (BM_FORCE_OFF | PCI_DEV_TMOUT | CMD_ABORTED | \
+ BM_PARITY_ERR | PIO_OVERRUN)
+
+ #define hp_int_status 0x37
+
+ #define BM_CMD_CMPL BIT(0) /*Bus Master command complete */
+ #define EXT_STATUS_ON BIT(1) /*Extended status is valid */
+ #define SCSI_INTERRUPT BIT(2) /*Global indication of a SCSI int. */
+ #define BM_FIFO_RDY BIT(4)
+ #define INT_ASSERTED BIT(5) /* */
+ #define SRAM_BUSY BIT(6) /*Scatter/Gather RAM busy */
+ #define CMD_REG_BUSY BIT(7)
+
+
+ #define hp_fifo_cnt 0x38
+ #define hp_curr_host_cnt 0x39
+ #define hp_reserved_3A 0x3A
+ #define hp_fifo_in_addr 0x3B
+
+ #define hp_fifo_out_addr 0x3C
+ #define hp_reserved_3D 0x3D
+ #define hp_reserved_3E 0x3E
+ #define hp_reserved_3F 0x3F
+
+
+
+ extern USHORT default_intena;
+
+ #define hp_intena 0x40
+
+ #define RESET BITW(7)
+ #define PROG_HLT BITW(6)
+ #define PARITY BITW(5)
+ #define FIFO BITW(4)
+ #define SEL BITW(3)
+ #define SCAM_SEL BITW(2)
+ #define RSEL BITW(1)
+ #define TIMEOUT BITW(0)
+ #define BUS_FREE BITW(15)
+ #define XFER_CNT_0 BITW(14)
+ #define PHASE BITW(13)
+ #define IUNKWN BITW(12)
+ #define ICMD_COMP BITW(11)
+ #define ITICKLE BITW(10)
+ #define IDO_STRT BITW(9)
+ #define ITAR_DISC BITW(8)
+ #define AUTO_INT (BITW(12)+BITW(11)+BITW(10)+BITW(9)+BITW(8))
+ #define CLR_ALL_INT 0xFFFF
+ #define CLR_ALL_INT_1 0xFF00
+
+ #define hp_intstat 0x42
+
+ #define hp_scsisig 0x44
+
+ #define SCSI_SEL BIT(7)
+ #define SCSI_BSY BIT(6)
+ #define SCSI_REQ BIT(5)
+ #define SCSI_ACK BIT(4)
+ #define SCSI_ATN BIT(3)
+ #define SCSI_CD BIT(2)
+ #define SCSI_MSG BIT(1)
+ #define SCSI_IOBIT BIT(0)
+
+ #define S_SCSI_PHZ (BIT(2)+BIT(1)+BIT(0))
+ #define S_CMD_PH (BIT(2) )
+ #define S_MSGO_PH (BIT(2)+BIT(1) )
+ #define S_STAT_PH (BIT(2) +BIT(0))
+ #define S_MSGI_PH (BIT(2)+BIT(1)+BIT(0))
+ #define S_DATAI_PH ( BIT(0))
+ #define S_DATAO_PH 0x00
+ #define S_ILL_PH ( BIT(1) )
+
+ #define hp_scsictrl_0 0x45
+
+ #define NO_ARB BIT(7)
+ #define SEL_TAR BIT(6)
+ #define ENA_ATN BIT(4)
+ #define ENA_RESEL BIT(2)
+ #define SCSI_RST BIT(1)
+ #define ENA_SCAM_SEL BIT(0)
+
+
+
+ #define hp_portctrl_0 0x46
+
+ #define SCSI_PORT BIT(7)
+ #define SCSI_INBIT BIT(6)
+ #define DMA_PORT BIT(5)
+ #define DMA_RD BIT(4)
+ #define HOST_PORT BIT(3)
+ #define HOST_WRT BIT(2)
+ #define SCSI_BUS_EN BIT(1)
+ #define START_TO BIT(0)
+
+ #define hp_scsireset 0x47
+
+ #define SCSI_TAR BIT(7)
+ #define SCSI_INI BIT(6)
+ #define SCAM_EN BIT(5)
+ #define ACK_HOLD BIT(4)
+ #define DMA_RESET BIT(3)
+ #define HPSCSI_RESET BIT(2)
+ #define PROG_RESET BIT(1)
+ #define FIFO_CLR BIT(0)
+
+ #define hp_xfercnt_0 0x48
+ #define hp_xfercnt_1 0x49
+ #define hp_xfercnt_2 0x4A
+ #define hp_xfercnt_3 0x4B
+
+ #define hp_fifodata_0 0x4C
+ #define hp_fifodata_1 0x4D
+ #define hp_addstat 0x4E
+
+ #define SCAM_TIMER BIT(7)
+ #define AUTO_RUNNING BIT(6)
+ #define FAST_SYNC BIT(5)
+ #define SCSI_MODE8 BIT(3)
+ #define SCSI_PAR_ERR BIT(0)
+
+ #define hp_prgmcnt_0 0x4F
+
+ #define AUTO_PC_MASK 0x3F
+
+ #define hp_selfid_0 0x50
+ #define hp_selfid_1 0x51
+ #define hp_arb_id 0x52
+
+ #define ARB_ID (BIT(3) + BIT(2) + BIT(1) + BIT(0))
+
+ #define hp_select_id 0x53
+
+ #define RESEL_ID (BIT(7) + BIT(6) + BIT(5) + BIT(4))
+ #define SELECT_ID (BIT(3) + BIT(2) + BIT(1) + BIT(0))
+
+ #define hp_synctarg_base 0x54
+ #define hp_synctarg_12 0x54
+ #define hp_synctarg_13 0x55
+ #define hp_synctarg_14 0x56
+ #define hp_synctarg_15 0x57
+
+ #define hp_synctarg_8 0x58
+ #define hp_synctarg_9 0x59
+ #define hp_synctarg_10 0x5A
+ #define hp_synctarg_11 0x5B
+
+ #define hp_synctarg_4 0x5C
+ #define hp_synctarg_5 0x5D
+ #define hp_synctarg_6 0x5E
+ #define hp_synctarg_7 0x5F
+
+ #define hp_synctarg_0 0x60
+ #define hp_synctarg_1 0x61
+ #define hp_synctarg_2 0x62
+ #define hp_synctarg_3 0x63
+
+ #define RATE_20MB 0x00
+ #define RATE_10MB ( BIT(5))
+ #define RATE_6_6MB ( BIT(6) )
+ #define RATE_5MB ( BIT(6)+BIT(5))
+ #define RATE_4MB (BIT(7) )
+ #define RATE_3_33MB (BIT(7) +BIT(5))
+ #define RATE_2_85MB (BIT(7)+BIT(6) )
+ #define RATE_2_5MB (BIT(7)+BIT(5)+BIT(6))
+ #define NEXT_CLK BIT(5)
+ #define SLOWEST_SYNC (BIT(7)+BIT(6)+BIT(5))
+ #define NARROW_SCSI BIT(4)
+ #define SYNC_OFFSET (BIT(3) + BIT(2) + BIT(1) + BIT(0))
+ #define DEFAULT_ASYNC 0x00
+ #define DEFAULT_OFFSET 0x0F
+
+ #define hp_autostart_0 0x64
+ #define hp_autostart_1 0x65
+ #define hp_autostart_2 0x66
+ #define hp_autostart_3 0x67
+
+
+
+ #define DISABLE 0x00
+ #define AUTO_IMMED BIT(5)
+ #define SELECT BIT(6)
+ #define RESELECT (BIT(6)+BIT(5))
+ #define BUSFREE BIT(7)
+ #define XFER_0 (BIT(7)+BIT(5))
+ #define END_DATA (BIT(7)+BIT(6))
+ #define MSG_PHZ (BIT(7)+BIT(6)+BIT(5))
+
+ #define hp_gp_reg_0 0x68
+ #define hp_gp_reg_1 0x69
+ #define hp_gp_reg_2 0x6A
+ #define hp_gp_reg_3 0x6B
+
+ #define hp_seltimeout 0x6C
+
+
+ #define TO_2ms 0x54 /* 2.0503ms */
+ #define TO_4ms 0x67 /* 3.9959ms */
+
+ #define TO_5ms 0x03 /* 4.9152ms */
+ #define TO_10ms 0x07 /* 11.xxxms */
+ #define TO_250ms 0x99 /* 250.68ms */
+ #define TO_290ms 0xB1 /* 289.99ms */
+ #define TO_350ms 0xD6 /* 350.62ms */
+ #define TO_417ms 0xFF /* 417.79ms */
+
+ #define hp_clkctrl_0 0x6D
+
+ #define PWR_DWN BIT(6)
+ #define ACTdeassert BIT(4)
+ #define ATNonErr BIT(3)
+ #define CLK_30MHZ BIT(1)
+ #define CLK_40MHZ (BIT(1) + BIT(0))
+ #define CLK_50MHZ BIT(2)
+
+ #define CLKCTRL_DEFAULT (ACTdeassert | CLK_40MHZ)
+
+ #define hp_fiforead 0x6E
+ #define hp_fifowrite 0x6F
+
+ #define hp_offsetctr 0x70
+ #define hp_xferstat 0x71
+
+ #define FIFO_FULL BIT(7)
+ #define FIFO_EMPTY BIT(6)
+ #define FIFO_MASK 0x3F /* Mask for the FIFO count value. */
+ #define FIFO_LEN 0x20
+
+ #define hp_portctrl_1 0x72
+
+ #define EVEN_HOST_P BIT(5)
+ #define INVT_SCSI BIT(4)
+ #define CHK_SCSI_P BIT(3)
+ #define HOST_MODE8 BIT(0)
+ #define HOST_MODE16 0x00
+
+ #define hp_xfer_pad 0x73
+
+ #define ID_UNLOCK BIT(3)
+ #define XFER_PAD BIT(2)
+
+ #define hp_scsidata_0 0x74
+ #define hp_scsidata_1 0x75
+ #define hp_timer_0 0x76
+ #define hp_timer_1 0x77
+
+ #define hp_reserved_78 0x78
+ #define hp_reserved_79 0x79
+ #define hp_reserved_7A 0x7A
+ #define hp_reserved_7B 0x7B
+
+ #define hp_reserved_7C 0x7C
+ #define hp_reserved_7D 0x7D
+ #define hp_reserved_7E 0x7E
+ #define hp_reserved_7F 0x7F
+
+ #define hp_aramBase 0x80
+ #define BIOS_DATA_OFFSET 0x60
+ #define BIOS_RELATIVE_CARD 0x64
+
+
+
+
+ #define AUTO_LEN 0x80
+ #define AR0 0x00
+ #define AR1 BITW(8)
+ #define AR2 BITW(9)
+ #define AR3 (BITW(9) + BITW(8))
+ #define SDATA BITW(10)
+
+ #define NOP_OP 0x00 /* Nop command */
+
+ #define CRD_OP BITW(11) /* Cmp Reg. w/ Data */
+
+ #define CRR_OP BITW(12) /* Cmp Reg. w. Reg. */
+
+ #define CBE_OP (BITW(14)+BITW(12)+BITW(11)) /* Cmp SCSI cmd class & Branch EQ */
+
+ #define CBN_OP (BITW(14)+BITW(13)) /* Cmp SCSI cmd class & Branch NOT EQ */
+
+ #define CPE_OP (BITW(14)+BITW(11)) /* Cmp SCSI phs & Branch EQ */
+
+ #define CPN_OP (BITW(14)+BITW(12)) /* Cmp SCSI phs & Branch NOT EQ */
+
+
+ #define ADATA_OUT 0x00
+ #define ADATA_IN BITW(8)
+ #define ACOMMAND BITW(10)
+ #define ASTATUS (BITW(10)+BITW(8))
+ #define AMSG_OUT (BITW(10)+BITW(9))
+ #define AMSG_IN (BITW(10)+BITW(9)+BITW(8))
+ #define AILLEGAL (BITW(9)+BITW(8))
+
+
+ #define BRH_OP BITW(13) /* Branch */
+
+
+ #define ALWAYS 0x00
+ #define EQUAL BITW(8)
+ #define NOT_EQ BITW(9)
+
+ #define TCB_OP (BITW(13)+BITW(11)) /* Test condition & branch */
+
+
+ #define ATN_SET BITW(8)
+ #define ATN_RESET BITW(9)
+ #define XFER_CNT (BITW(9)+BITW(8))
+ #define FIFO_0 BITW(10)
+ #define FIFO_NOT0 (BITW(10)+BITW(8))
+ #define T_USE_SYNC0 (BITW(10)+BITW(9))
+
+
+ #define MPM_OP BITW(15) /* Match phase and move data */
+
+ #define MDR_OP (BITW(12)+BITW(11)) /* Move data to Reg. */
+
+ #define MRR_OP BITW(14) /* Move DReg. to Reg. */
+
+
+ #define S_IDREG (BIT(2)+BIT(1)+BIT(0))
+
+
+ #define D_AR0 0x00
+ #define D_AR1 BIT(0)
+ #define D_AR2 BIT(1)
+ #define D_AR3 (BIT(1) + BIT(0))
+ #define D_SDATA BIT(2)
+ #define D_BUCKET (BIT(2) + BIT(1) + BIT(0))
+
+
+ #define ADR_OP (BITW(13)+BITW(12)) /* Logical AND Reg. w. Data */
+
+ #define ADS_OP (BITW(14)+BITW(13)+BITW(12))
+
+ #define ODR_OP (BITW(13)+BITW(12)+BITW(11))
+
+ #define ODS_OP (BITW(14)+BITW(13)+BITW(12)+BITW(11))
+
+ #define STR_OP (BITW(15)+BITW(14)) /* Store to A_Reg. */
+
+ #define AINT_ENA1 0x00
+ #define AINT_STAT1 BITW(8)
+ #define ASCSI_SIG BITW(9)
+ #define ASCSI_CNTL (BITW(9)+BITW(8))
+ #define APORT_CNTL BITW(10)
+ #define ARST_CNTL (BITW(10)+BITW(8))
+ #define AXFERCNT0 (BITW(10)+BITW(9))
+ #define AXFERCNT1 (BITW(10)+BITW(9)+BITW(8))
+ #define AXFERCNT2 BITW(11)
+ #define AFIFO_DATA (BITW(11)+BITW(8))
+ #define ASCSISELID (BITW(11)+BITW(9))
+ #define ASCSISYNC0 (BITW(11)+BITW(9)+BITW(8))
+
+
+ #define RAT_OP (BITW(14)+BITW(13)+BITW(11))
+
+ #define SSI_OP (BITW(15)+BITW(11))
+
+
+ #define SSI_ITAR_DISC (ITAR_DISC >> 8)
+ #define SSI_IDO_STRT (IDO_STRT >> 8)
+ #define SSI_IDI_STRT (IDO_STRT >> 8)
+
+ #define SSI_ICMD_COMP (ICMD_COMP >> 8)
+ #define SSI_ITICKLE (ITICKLE >> 8)
+
+ #define SSI_IUNKWN (IUNKWN >> 8)
+ #define SSI_INO_CC (IUNKWN >> 8)
+ #define SSI_IRFAIL (IUNKWN >> 8)
+
+
+ #define NP 0x10 /*Next Phase */
+ #define NTCMD 0x02 /*Non- Tagged Command start */
+ #define CMDPZ 0x04 /*Command phase */
+ #define DINT 0x12 /*Data Out/In interrupt */
+ #define DI 0x13 /*Data Out */
+ #define MI 0x14 /*Message In */
+ #define DC 0x19 /*Disconnect Message */
+ #define ST 0x1D /*Status Phase */
+ #define UNKNWN 0x24 /*Unknown bus action */
+ #define CC 0x25 /*Command Completion failure */
+ #define TICK 0x26 /*New target reselected us. */
+ #define RFAIL 0x27 /*Reselection failed */
+ #define SELCHK 0x28 /*Select & Check SCSI ID latch reg */
+
+
+ #define ID_MSG_STRT hp_aramBase + 0x00
+ #define NON_TAG_ID_MSG hp_aramBase + 0x06
+ #define CMD_STRT hp_aramBase + 0x08
+ #define SYNC_MSGS hp_aramBase + 0x08
+
+
+
+
+
+ #define TAG_STRT 0x00
+ #define SELECTION_START 0x00
+ #define DISCONNECT_START 0x10/2
+ #define END_DATA_START 0x14/2
+ #define NONTAG_STRT 0x02/2
+ #define CMD_ONLY_STRT CMDPZ/2
+ #define TICKLE_STRT TICK/2
+ #define SELCHK_STRT SELCHK/2
+
+
+
+
+#define mEEPROM_CLK_DELAY(port) (RD_HARPOON(port+hp_intstat_1))
+
+#define mWAIT_10MS(port) (RD_HARPOON(port+hp_intstat_1))
+
+
+#define CLR_XFER_CNT(port) (WR_HARPOON(port+hp_xfercnt_0, 0x00))
+
+#define SET_XFER_CNT(port, data) (WR_HARP32(port,hp_xfercnt_0,data))
+
+#define GET_XFER_CNT(port, xfercnt) {RD_HARP32(port,hp_xfercnt_0,xfercnt); xfercnt &= 0xFFFFFF;}
+/* #define GET_XFER_CNT(port, xfercnt) (xfercnt = RD_HARPOON(port+hp_xfercnt_2), \
+ xfercnt <<= 16,\
+ xfercnt |= RDW_HARPOON((USHORT)(port+hp_xfercnt_0)))
+ */
+#if defined(DOS)
+#define HP_SETUP_ADDR_CNT(port,addr,count) (WRW_HARPOON((USHORT)(port+hp_host_addr_lo), (USHORT)(addr & 0x0000FFFFL)),\
+ addr >>= 16,\
+ WRW_HARPOON((USHORT)(port+hp_host_addr_hmi), (USHORT)(addr & 0x0000FFFFL)),\
+ WR_HARP32(port,hp_xfercnt_0,count),\
+ WRW_HARPOON((USHORT)(port+hp_xfer_cnt_lo), (USHORT)(count & 0x0000FFFFL)),\
+ count >>= 16,\
+ WR_HARPOON(port+hp_xfer_cnt_hi, (count & 0xFF)))
+#else
+#define HP_SETUP_ADDR_CNT(port,addr,count) (WRW_HARPOON((port+hp_host_addr_lo), (USHORT)(addr & 0x0000FFFFL)),\
+ addr >>= 16,\
+ WRW_HARPOON((port+hp_host_addr_hmi), (USHORT)(addr & 0x0000FFFFL)),\
+ WR_HARP32(port,hp_xfercnt_0,count),\
+ WRW_HARPOON((port+hp_xfer_cnt_lo), (USHORT)(count & 0x0000FFFFL)),\
+ count >>= 16,\
+ WR_HARPOON(port+hp_xfer_cnt_hi, (count & 0xFF)))
+#endif
+
+#define ACCEPT_MSG(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
+ WR_HARPOON(port+hp_scsisig, S_ILL_PH);}
+
+
+#define ACCEPT_MSG_ATN(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
+ WR_HARPOON(port+hp_scsisig, (S_ILL_PH|SCSI_ATN));}
+
+#define ACCEPT_STAT(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
+ WR_HARPOON(port+hp_scsisig, S_ILL_PH);}
+
+#define ACCEPT_STAT_ATN(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
+ WR_HARPOON(port+hp_scsisig, (S_ILL_PH|SCSI_ATN));}
+
+#define DISABLE_AUTO(port) (WR_HARPOON(port+hp_scsireset, PROG_RESET),\
+ WR_HARPOON(port+hp_scsireset, 0x00))
+
+#define ARAM_ACCESS(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
+ (RD_HARPOON(p_port+hp_page_ctrl) | SGRAM_ARAM)))
+
+#define SGRAM_ACCESS(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~SGRAM_ARAM)))
+
+#define MDISABLE_INT(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
+ (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE)))
+
+#define MENABLE_INT(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE)))
+
+
+
+#endif
+
+
+#if (FW_TYPE==_UCB_MGR_)
+void ReadNVRam(PSCCBcard pCurrCard,PUCB p_ucb);
+void WriteNVRam(PSCCBcard pCurrCard,PUCB p_ucb);
+void UpdateCheckSum(u32bits baseport);
+#endif // (FW_TYPE==_UCB_MGR_)
+
+#if defined(DOS)
+UCHAR sfm(USHORT port, PSCCB pcurrSCCB);
+void scsiStartAuto(USHORT port);
+UCHAR sisyncn(USHORT port, UCHAR p_card, UCHAR syncFlag);
+void ssel(USHORT port, UCHAR p_card);
+void sres(USHORT port, UCHAR p_card, PSCCBcard pCurrCard);
+void sdecm(UCHAR message, USHORT port, UCHAR p_card);
+void shandem(USHORT port, UCHAR p_card,PSCCB pCurrSCCB);
+void stsyncn(USHORT port, UCHAR p_card);
+void sisyncr(USHORT port,UCHAR sync_pulse, UCHAR offset);
+void sssyncv(USHORT p_port, UCHAR p_id, UCHAR p_sync_value, PSCCBMgr_tar_info currTar_Info);
+void sresb(USHORT port, UCHAR p_card);
+void sxfrp(USHORT p_port, UCHAR p_card);
+void schkdd(USHORT port, UCHAR p_card);
+UCHAR RdStack(USHORT port, UCHAR index);
+void WrStack(USHORT portBase, UCHAR index, UCHAR data);
+UCHAR ChkIfChipInitialized(USHORT ioPort);
+
+#if defined(V302)
+UCHAR GetTarLun(USHORT port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tag, PUCHAR lun);
+#endif
+
+void SendMsg(USHORT port, UCHAR message);
+void queueFlushTargSccb(UCHAR p_card, UCHAR thisTarg, UCHAR error_code);
+UCHAR scsellDOS(USHORT p_port, UCHAR targ_id);
+#else
+UCHAR sfm(ULONG port, PSCCB pcurrSCCB);
+void scsiStartAuto(ULONG port);
+UCHAR sisyncn(ULONG port, UCHAR p_card, UCHAR syncFlag);
+void ssel(ULONG port, UCHAR p_card);
+void sres(ULONG port, UCHAR p_card, PSCCBcard pCurrCard);
+void sdecm(UCHAR message, ULONG port, UCHAR p_card);
+void shandem(ULONG port, UCHAR p_card,PSCCB pCurrSCCB);
+void stsyncn(ULONG port, UCHAR p_card);
+void sisyncr(ULONG port,UCHAR sync_pulse, UCHAR offset);
+void sssyncv(ULONG p_port, UCHAR p_id, UCHAR p_sync_value, PSCCBMgr_tar_info currTar_Info);
+void sresb(ULONG port, UCHAR p_card);
+void sxfrp(ULONG p_port, UCHAR p_card);
+void schkdd(ULONG port, UCHAR p_card);
+UCHAR RdStack(ULONG port, UCHAR index);
+void WrStack(ULONG portBase, UCHAR index, UCHAR data);
+UCHAR ChkIfChipInitialized(ULONG ioPort);
+
+#if defined(V302)
+UCHAR GetTarLun(ULONG port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tar, PUCHAR lun);
+#endif
+
+void SendMsg(ULONG port, UCHAR message);
+void queueFlushTargSccb(UCHAR p_card, UCHAR thisTarg, UCHAR error_code);
+#endif
+
+void ssenss(PSCCBcard pCurrCard);
+void sinits(PSCCB p_sccb, UCHAR p_card);
+void RNVRamData(PNVRamInfo pNvRamInfo);
+
+#if defined(WIDE_SCSI)
+ #if defined(DOS)
+ UCHAR siwidn(USHORT port, UCHAR p_card);
+ void stwidn(USHORT port, UCHAR p_card);
+ void siwidr(USHORT port, UCHAR width);
+ #else
+ UCHAR siwidn(ULONG port, UCHAR p_card);
+ void stwidn(ULONG port, UCHAR p_card);
+ void siwidr(ULONG port, UCHAR width);
+ #endif
+#endif
+
+
+void queueSelectFail(PSCCBcard pCurrCard, UCHAR p_card);
+void queueDisconnect(PSCCB p_SCCB, UCHAR p_card);
+void queueCmdComplete(PSCCBcard pCurrCard, PSCCB p_SCCB, UCHAR p_card);
+void queueSearchSelect(PSCCBcard pCurrCard, UCHAR p_card);
+void queueFlushSccb(UCHAR p_card, UCHAR error_code);
+void queueAddSccb(PSCCB p_SCCB, UCHAR card);
+UCHAR queueFindSccb(PSCCB p_SCCB, UCHAR p_card);
+void utilUpdateResidual(PSCCB p_SCCB);
+USHORT CalcCrc16(UCHAR buffer[]);
+UCHAR CalcLrc(UCHAR buffer[]);
+
+
+#if defined(DOS)
+void Wait1Second(USHORT p_port);
+void Wait(USHORT p_port, UCHAR p_delay);
+void utilEEWriteOnOff(USHORT p_port,UCHAR p_mode);
+void utilEEWrite(USHORT p_port, USHORT ee_data, USHORT ee_addr);
+USHORT utilEERead(USHORT p_port, USHORT ee_addr);
+USHORT utilEEReadOrg(USHORT p_port, USHORT ee_addr);
+void utilEESendCmdAddr(USHORT p_port, UCHAR ee_cmd, USHORT ee_addr);
+#else
+void Wait1Second(ULONG p_port);
+void Wait(ULONG p_port, UCHAR p_delay);
+void utilEEWriteOnOff(ULONG p_port,UCHAR p_mode);
+void utilEEWrite(ULONG p_port, USHORT ee_data, USHORT ee_addr);
+USHORT utilEERead(ULONG p_port, USHORT ee_addr);
+USHORT utilEEReadOrg(ULONG p_port, USHORT ee_addr);
+void utilEESendCmdAddr(ULONG p_port, UCHAR ee_cmd, USHORT ee_addr);
+#endif
+
+
+
+#if defined(OS2)
+ void far phaseDataOut(ULONG port, UCHAR p_card);
+ void far phaseDataIn(ULONG port, UCHAR p_card);
+ void far phaseCommand(ULONG port, UCHAR p_card);
+ void far phaseStatus(ULONG port, UCHAR p_card);
+ void far phaseMsgOut(ULONG port, UCHAR p_card);
+ void far phaseMsgIn(ULONG port, UCHAR p_card);
+ void far phaseIllegal(ULONG port, UCHAR p_card);
+#else
+ #if defined(DOS)
+ void phaseDataOut(USHORT port, UCHAR p_card);
+ void phaseDataIn(USHORT port, UCHAR p_card);
+ void phaseCommand(USHORT port, UCHAR p_card);
+ void phaseStatus(USHORT port, UCHAR p_card);
+ void phaseMsgOut(USHORT port, UCHAR p_card);
+ void phaseMsgIn(USHORT port, UCHAR p_card);
+ void phaseIllegal(USHORT port, UCHAR p_card);
+ #else
+ void phaseDataOut(ULONG port, UCHAR p_card);
+ void phaseDataIn(ULONG port, UCHAR p_card);
+ void phaseCommand(ULONG port, UCHAR p_card);
+ void phaseStatus(ULONG port, UCHAR p_card);
+ void phaseMsgOut(ULONG port, UCHAR p_card);
+ void phaseMsgIn(ULONG port, UCHAR p_card);
+ void phaseIllegal(ULONG port, UCHAR p_card);
+ #endif
+#endif
+
+#if defined(DOS)
+void phaseDecode(USHORT port, UCHAR p_card);
+void phaseChkFifo(USHORT port, UCHAR p_card);
+void phaseBusFree(USHORT p_port, UCHAR p_card);
+#else
+void phaseDecode(ULONG port, UCHAR p_card);
+void phaseChkFifo(ULONG port, UCHAR p_card);
+void phaseBusFree(ULONG p_port, UCHAR p_card);
+#endif
+
+
+
+
+#if defined(DOS)
+void XbowInit(USHORT port, UCHAR scamFlg);
+void BusMasterInit(USHORT p_port);
+int DiagXbow(USHORT port);
+int DiagBusMaster(USHORT port);
+void DiagEEPROM(USHORT p_port);
+#else
+void XbowInit(ULONG port, UCHAR scamFlg);
+void BusMasterInit(ULONG p_port);
+int DiagXbow(ULONG port);
+int DiagBusMaster(ULONG port);
+void DiagEEPROM(ULONG p_port);
+#endif
+
+
+
+
+#if defined(DOS)
+void busMstrAbort(USHORT port);
+UCHAR busMstrTimeOut(USHORT port);
+void dataXferProcessor(USHORT port, PSCCBcard pCurrCard);
+void busMstrSGDataXferStart(USHORT port, PSCCB pCurrSCCB);
+void busMstrDataXferStart(USHORT port, PSCCB pCurrSCCB);
+void hostDataXferAbort(USHORT port, UCHAR p_card, PSCCB pCurrSCCB);
+#else
+void busMstrAbort(ULONG port);
+UCHAR busMstrTimeOut(ULONG port);
+void dataXferProcessor(ULONG port, PSCCBcard pCurrCard);
+void busMstrSGDataXferStart(ULONG port, PSCCB pCurrSCCB);
+void busMstrDataXferStart(ULONG port, PSCCB pCurrSCCB);
+void hostDataXferAbort(ULONG port, UCHAR p_card, PSCCB pCurrSCCB);
+#endif
+void hostDataXferRestart(PSCCB currSCCB);
+
+
+#if defined (DOS)
+UCHAR SccbMgr_bad_isr(USHORT p_port, UCHAR p_card, PSCCBcard pCurrCard, USHORT p_int);
+#else
+UCHAR SccbMgr_bad_isr(ULONG p_port, UCHAR p_card, PSCCBcard pCurrCard, USHORT p_int);
+
+#endif
+
+void SccbMgrTableInitAll(void);
+void SccbMgrTableInitCard(PSCCBcard pCurrCard, UCHAR p_card);
+void SccbMgrTableInitTarget(UCHAR p_card, UCHAR target);
+
+
+
+void scini(UCHAR p_card, UCHAR p_our_id, UCHAR p_power_up);
+
+#if defined(DOS)
+int scarb(USHORT p_port, UCHAR p_sel_type);
+void scbusf(USHORT p_port);
+void scsel(USHORT p_port);
+void scasid(UCHAR p_card, USHORT p_port);
+UCHAR scxferc(USHORT p_port, UCHAR p_data);
+UCHAR scsendi(USHORT p_port, UCHAR p_id_string[]);
+UCHAR sciso(USHORT p_port, UCHAR p_id_string[]);
+void scwirod(USHORT p_port, UCHAR p_data_bit);
+void scwiros(USHORT p_port, UCHAR p_data_bit);
+UCHAR scvalq(UCHAR p_quintet);
+UCHAR scsell(USHORT p_port, UCHAR targ_id);
+void scwtsel(USHORT p_port);
+void inisci(UCHAR p_card, USHORT p_port, UCHAR p_our_id);
+void scsavdi(UCHAR p_card, USHORT p_port);
+#else
+int scarb(ULONG p_port, UCHAR p_sel_type);
+void scbusf(ULONG p_port);
+void scsel(ULONG p_port);
+void scasid(UCHAR p_card, ULONG p_port);
+UCHAR scxferc(ULONG p_port, UCHAR p_data);
+UCHAR scsendi(ULONG p_port, UCHAR p_id_string[]);
+UCHAR sciso(ULONG p_port, UCHAR p_id_string[]);
+void scwirod(ULONG p_port, UCHAR p_data_bit);
+void scwiros(ULONG p_port, UCHAR p_data_bit);
+UCHAR scvalq(UCHAR p_quintet);
+UCHAR scsell(ULONG p_port, UCHAR targ_id);
+void scwtsel(ULONG p_port);
+void inisci(UCHAR p_card, ULONG p_port, UCHAR p_our_id);
+void scsavdi(UCHAR p_card, ULONG p_port);
+#endif
+UCHAR scmachid(UCHAR p_card, UCHAR p_id_string[]);
+
+
+#if defined(DOS)
+void autoCmdCmplt(USHORT p_port, UCHAR p_card);
+void autoLoadDefaultMap(USHORT p_port);
+#else
+void autoCmdCmplt(ULONG p_port, UCHAR p_card);
+void autoLoadDefaultMap(ULONG p_port);
+#endif
+
+
+
+#if (FW_TYPE==_SCCB_MGR_)
+ void OS_start_timer(unsigned long ioport, unsigned long timeout);
+ void OS_stop_timer(unsigned long ioport, unsigned long timeout);
+ void OS_disable_int(unsigned char intvec);
+ void OS_enable_int(unsigned char intvec);
+ void OS_delay(unsigned long count);
+ int OS_VirtToPhys(u32bits CardHandle, u32bits *physaddr, u32bits *virtaddr);
+ #if !(defined(UNIX) || defined(OS2) || defined(SOLARIS_REAL_MODE))
+ void OS_Lock(PSCCBMGR_INFO pCardInfo);
+ void OS_UnLock(PSCCBMGR_INFO pCardInfo);
+#endif // if FW_TYPE == ...
+
+#endif
+
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+
+
+#if defined(OS2)
+ extern void (far *s_PhaseTbl[8]) (ULONG, UCHAR);
+#else
+ #if defined(DOS)
+ extern void (*s_PhaseTbl[8]) (USHORT, UCHAR);
+ #else
+ extern void (*s_PhaseTbl[8]) (ULONG, UCHAR);
+ #endif
+#endif
+
+extern SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR];
+extern NVRAMINFO nvRamInfo[MAX_MB_CARDS];
+#if defined(DOS) || defined(OS2)
+extern UCHAR temp_id_string[ID_STRING_LENGTH];
+#endif
+extern UCHAR scamHAString[];
+
+
+extern UCHAR mbCards;
+#if defined(BUGBUG)
+extern UCHAR debug_int[MAX_CARDS][debug_size];
+extern UCHAR debug_index[MAX_CARDS];
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+
+#if (FW_TYPE==_SCCB_MGR_)
+#if defined(DOS)
+ extern UCHAR first_time;
+#endif
+#endif /* (FW_TYPE==_SCCB_MGR_) */
+
+#if (FW_TYPE==_UCB_MGR_)
+#if defined(DOS)
+ extern u08bits first_time;
+#endif
+#endif /* (FW_TYPE==_UCB_MGR_) */
+
+#if defined(BUGBUG)
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+
+extern unsigned int SccbGlobalFlags;
+
+
+#ident "$Id: FlashPoint.c,v 1.1 1999/04/26 05:53:56 tb Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: sccb.c $
+ *
+ * Description: Functions relating to handling of the SCCB interface
+ * between the device driver and the HARPOON.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+ /*#include <budioctl.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <eeprom.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+
+
+#if (FW_TYPE==_SCCB_MGR_)
+#define mOS_Lock(card) OS_Lock((PSCCBMGR_INFO)(((PSCCBcard)card)->cardInfo))
+#define mOS_UnLock(card) OS_UnLock((PSCCBMGR_INFO)(((PSCCBcard)card)->cardInfo))
+#else /* FW_TYPE==_UCB_MGR_ */
+#define mOS_Lock(card) OS_Lock((u32bits)(((PSCCBcard)card)->ioPort))
+#define mOS_UnLock(card) OS_UnLock((u32bits)(((PSCCBcard)card)->ioPort))
+#endif
+
+
+/*
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+extern SCCBCARD BL_Card[MAX_CARDS];
+
+extern NVRAMINFO nvRamInfo[MAX_MB_CARDS];
+extern UCHAR mbCards;
+
+#if defined (OS2)
+ extern void (far *s_PhaseTbl[8]) (ULONG, UCHAR);
+#else
+ #if defined(DOS)
+ extern void (*s_PhaseTbl[8]) (USHORT, UCHAR);
+ #else
+ extern void (*s_PhaseTbl[8]) (ULONG, UCHAR);
+ #endif
+#endif
+
+
+#if defined(BUGBUG)
+extern UCHAR debug_int[MAX_CARDS][debug_size];
+extern UCHAR debug_index[MAX_CARDS];
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+*/
+
+#if (FW_TYPE==_SCCB_MGR_)
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_sense_adapter
+ *
+ * Description: Setup and/or Search for cards and return info to caller.
+ *
+ *---------------------------------------------------------------------*/
+
+int SccbMgr_sense_adapter(PSCCBMGR_INFO pCardInfo)
+{
+#if defined(DOS)
+#else
+ static UCHAR first_time = 1;
+#endif
+
+ UCHAR i,j,id,ScamFlg;
+ USHORT temp,temp2,temp3,temp4,temp5,temp6;
+#if defined(DOS)
+ USHORT ioport;
+#else
+ ULONG ioport;
+#endif
+ PNVRamInfo pCurrNvRam;
+
+#if defined(DOS)
+ ioport = (USHORT)pCardInfo->si_baseaddr;
+#else
+ ioport = pCardInfo->si_baseaddr;
+#endif
+
+
+ if (RD_HARPOON(ioport+hp_vendor_id_0) != ORION_VEND_0)
+ return((int)FAILURE);
+
+ if ((RD_HARPOON(ioport+hp_vendor_id_1) != ORION_VEND_1))
+ return((int)FAILURE);
+
+ if ((RD_HARPOON(ioport+hp_device_id_0) != ORION_DEV_0))
+ return((int)FAILURE);
+
+ if ((RD_HARPOON(ioport+hp_device_id_1) != ORION_DEV_1))
+ return((int)FAILURE);
+
+
+ if (RD_HARPOON(ioport+hp_rev_num) != 0x0f){
+
+/* For new Harpoon then check for sub_device ID LSB
+ the bits(0-3) must be all ZERO for compatible with
+ current version of SCCBMgr, else skip this Harpoon
+ device. */
+
+ if (RD_HARPOON(ioport+hp_sub_device_id_0) & 0x0f)
+ return((int)FAILURE);
+ }
+
+ if (first_time)
+ {
+ SccbMgrTableInitAll();
+ first_time = 0;
+ mbCards = 0;
+ }
+
+ if(RdStack(ioport, 0) != 0x00) {
+ if(ChkIfChipInitialized(ioport) == FALSE)
+ {
+ pCurrNvRam = NULL;
+ WR_HARPOON(ioport+hp_semaphore, 0x00);
+ XbowInit(ioport, 0); /*Must Init the SCSI before attempting */
+ DiagEEPROM(ioport);
+ }
+ else
+ {
+ if(mbCards < MAX_MB_CARDS) {
+ pCurrNvRam = &nvRamInfo[mbCards];
+ mbCards++;
+ pCurrNvRam->niBaseAddr = ioport;
+ RNVRamData(pCurrNvRam);
+ }else
+ return((int) FAILURE);
+ }
+ }else
+ pCurrNvRam = NULL;
+#if defined (NO_BIOS_OPTION)
+ pCurrNvRam = NULL;
+ XbowInit(ioport, 0); /*Must Init the SCSI before attempting */
+ DiagEEPROM(ioport);
+#endif /* No BIOS Option */
+
+ WR_HARPOON(ioport+hp_clkctrl_0, CLKCTRL_DEFAULT);
+ WR_HARPOON(ioport+hp_sys_ctrl, 0x00);
+
+ if(pCurrNvRam)
+ pCardInfo->si_id = pCurrNvRam->niAdapId;
+ else
+ pCardInfo->si_id = (UCHAR)(utilEERead(ioport, (ADAPTER_SCSI_ID/2)) &
+ (UCHAR)0x0FF);
+
+ pCardInfo->si_lun = 0x00;
+ pCardInfo->si_fw_revision = ORION_FW_REV;
+ temp2 = 0x0000;
+ temp3 = 0x0000;
+ temp4 = 0x0000;
+ temp5 = 0x0000;
+ temp6 = 0x0000;
+
+ for (id = 0; id < (16/2); id++) {
+
+ if(pCurrNvRam){
+ temp = (USHORT) pCurrNvRam->niSyncTbl[id];
+ temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
+ (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
+ }else
+ temp = utilEERead(ioport, (USHORT)((SYNC_RATE_TBL/2)+id));
+
+ for (i = 0; i < 2; temp >>=8,i++) {
+
+ temp2 >>= 1;
+ temp3 >>= 1;
+ temp4 >>= 1;
+ temp5 >>= 1;
+ temp6 >>= 1;
+ switch (temp & 0x3)
+ {
+ case AUTO_RATE_20: /* Synchronous, 20 mega-transfers/second */
+ temp6 |= 0x8000; /* Fall through */
+ case AUTO_RATE_10: /* Synchronous, 10 mega-transfers/second */
+ temp5 |= 0x8000; /* Fall through */
+ case AUTO_RATE_05: /* Synchronous, 5 mega-transfers/second */
+ temp2 |= 0x8000; /* Fall through */
+ case AUTO_RATE_00: /* Asynchronous */
+ break;
+ }
+
+ if (temp & DISC_ENABLE_BIT)
+ temp3 |= 0x8000;
+
+ if (temp & WIDE_NEGO_BIT)
+ temp4 |= 0x8000;
+
+ }
+ }
+
+ pCardInfo->si_per_targ_init_sync = temp2;
+ pCardInfo->si_per_targ_no_disc = temp3;
+ pCardInfo->si_per_targ_wide_nego = temp4;
+ pCardInfo->si_per_targ_fast_nego = temp5;
+ pCardInfo->si_per_targ_ultra_nego = temp6;
+
+ if(pCurrNvRam)
+ i = pCurrNvRam->niSysConf;
+ else
+ i = (UCHAR)(utilEERead(ioport, (SYSTEM_CONFIG/2)));
+
+ if(pCurrNvRam)
+ ScamFlg = pCurrNvRam->niScamConf;
+ else
+ ScamFlg = (UCHAR) utilEERead(ioport, SCAM_CONFIG/2);
+
+ pCardInfo->si_flags = 0x0000;
+
+ if (i & 0x01)
+ pCardInfo->si_flags |= SCSI_PARITY_ENA;
+
+ if (!(i & 0x02))
+ pCardInfo->si_flags |= SOFT_RESET;
+
+ if (i & 0x10)
+ pCardInfo->si_flags |= EXTENDED_TRANSLATION;
+
+ if (ScamFlg & SCAM_ENABLED)
+ pCardInfo->si_flags |= FLAG_SCAM_ENABLED;
+
+ if (ScamFlg & SCAM_LEVEL2)
+ pCardInfo->si_flags |= FLAG_SCAM_LEVEL2;
+
+ j = (RD_HARPOON(ioport+hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
+ if (i & 0x04) {
+ j |= SCSI_TERM_ENA_L;
+ }
+ WR_HARPOON(ioport+hp_bm_ctrl, j );
+
+ j = (RD_HARPOON(ioport+hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
+ if (i & 0x08) {
+ j |= SCSI_TERM_ENA_H;
+ }
+ WR_HARPOON(ioport+hp_ee_ctrl, j );
+
+ if (!(RD_HARPOON(ioport+hp_page_ctrl) & NARROW_SCSI_CARD))
+
+ pCardInfo->si_flags |= SUPPORT_16TAR_32LUN;
+
+ pCardInfo->si_card_family = HARPOON_FAMILY;
+ pCardInfo->si_bustype = BUSTYPE_PCI;
+
+ if(pCurrNvRam){
+ pCardInfo->si_card_model[0] = '9';
+ switch(pCurrNvRam->niModel & 0x0f){
+ case MODEL_LT:
+ pCardInfo->si_card_model[1] = '3';
+ pCardInfo->si_card_model[2] = '0';
+ break;
+ case MODEL_LW:
+ pCardInfo->si_card_model[1] = '5';
+ pCardInfo->si_card_model[2] = '0';
+ break;
+ case MODEL_DL:
+ pCardInfo->si_card_model[1] = '3';
+ pCardInfo->si_card_model[2] = '2';
+ break;
+ case MODEL_DW:
+ pCardInfo->si_card_model[1] = '5';
+ pCardInfo->si_card_model[2] = '2';
+ break;
+ }
+ }else{
+ temp = utilEERead(ioport, (MODEL_NUMB_0/2));
+ pCardInfo->si_card_model[0] = (UCHAR)(temp >> 8);
+ temp = utilEERead(ioport, (MODEL_NUMB_2/2));
+
+ pCardInfo->si_card_model[1] = (UCHAR)(temp & 0x00FF);
+ pCardInfo->si_card_model[2] = (UCHAR)(temp >> 8);
+ }
+
+ if (pCardInfo->si_card_model[1] == '3')
+ {
+ if (RD_HARPOON(ioport+hp_ee_ctrl) & BIT(7))
+ pCardInfo->si_flags |= LOW_BYTE_TERM;
+ }
+ else if (pCardInfo->si_card_model[2] == '0')
+ {
+ temp = RD_HARPOON(ioport+hp_xfer_pad);
+ WR_HARPOON(ioport+hp_xfer_pad, (temp & ~BIT(4)));
+ if (RD_HARPOON(ioport+hp_ee_ctrl) & BIT(7))
+ pCardInfo->si_flags |= LOW_BYTE_TERM;
+ WR_HARPOON(ioport+hp_xfer_pad, (temp | BIT(4)));
+ if (RD_HARPOON(ioport+hp_ee_ctrl) & BIT(7))
+ pCardInfo->si_flags |= HIGH_BYTE_TERM;
+ WR_HARPOON(ioport+hp_xfer_pad, temp);
+ }
+ else
+ {
+ temp = RD_HARPOON(ioport+hp_ee_ctrl);
+ temp2 = RD_HARPOON(ioport+hp_xfer_pad);
+ WR_HARPOON(ioport+hp_ee_ctrl, (temp | SEE_CS));
+ WR_HARPOON(ioport+hp_xfer_pad, (temp2 | BIT(4)));
+ temp3 = 0;
+ for (i = 0; i < 8; i++)
+ {
+ temp3 <<= 1;
+ if (!(RD_HARPOON(ioport+hp_ee_ctrl) & BIT(7)))
+ temp3 |= 1;
+ WR_HARPOON(ioport+hp_xfer_pad, (temp2 & ~BIT(4)));
+ WR_HARPOON(ioport+hp_xfer_pad, (temp2 | BIT(4)));
+ }
+ WR_HARPOON(ioport+hp_ee_ctrl, temp);
+ WR_HARPOON(ioport+hp_xfer_pad, temp2);
+ if (!(temp3 & BIT(7)))
+ pCardInfo->si_flags |= LOW_BYTE_TERM;
+ if (!(temp3 & BIT(6)))
+ pCardInfo->si_flags |= HIGH_BYTE_TERM;
+ }
+
+
+ ARAM_ACCESS(ioport);
+
+ for ( i = 0; i < 4; i++ ) {
+
+ pCardInfo->si_XlatInfo[i] =
+ RD_HARPOON(ioport+hp_aramBase+BIOS_DATA_OFFSET+i);
+ }
+
+ /* return with -1 if no sort, else return with
+ logical card number sorted by BIOS (zero-based) */
+
+ pCardInfo->si_relative_cardnum =
+ (UCHAR)(RD_HARPOON(ioport+hp_aramBase+BIOS_RELATIVE_CARD)-1);
+
+ SGRAM_ACCESS(ioport);
+
+ s_PhaseTbl[0] = phaseDataOut;
+ s_PhaseTbl[1] = phaseDataIn;
+ s_PhaseTbl[2] = phaseIllegal;
+ s_PhaseTbl[3] = phaseIllegal;
+ s_PhaseTbl[4] = phaseCommand;
+ s_PhaseTbl[5] = phaseStatus;
+ s_PhaseTbl[6] = phaseMsgOut;
+ s_PhaseTbl[7] = phaseMsgIn;
+
+ pCardInfo->si_present = 0x01;
+
+#if defined(BUGBUG)
+
+
+ for (i = 0; i < MAX_CARDS; i++) {
+
+ for (id=0; id<debug_size; id++)
+ debug_int[i][id] = (UCHAR)0x00;
+ debug_index[i] = 0;
+ }
+
+#endif
+
+ return(0);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_config_adapter
+ *
+ * Description: Setup adapter for normal operation (hard reset).
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+USHORT SccbMgr_config_adapter(PSCCBMGR_INFO pCardInfo)
+#else
+ULONG SccbMgr_config_adapter(PSCCBMGR_INFO pCardInfo)
+#endif
+{
+ PSCCBcard CurrCard = NULL;
+ PNVRamInfo pCurrNvRam;
+ UCHAR i,j,thisCard, ScamFlg;
+ USHORT temp,sync_bit_map,id;
+#if defined(DOS)
+ USHORT ioport;
+#else
+ ULONG ioport;
+#endif
+
+#if defined(DOS)
+ ioport = (USHORT)pCardInfo->si_baseaddr;
+#else
+ ioport = pCardInfo->si_baseaddr;
+#endif
+
+ for(thisCard =0; thisCard <= MAX_CARDS; thisCard++) {
+
+ if (thisCard == MAX_CARDS) {
+
+ return(FAILURE);
+ }
+
+ if (BL_Card[thisCard].ioPort == ioport) {
+
+ CurrCard = &BL_Card[thisCard];
+ SccbMgrTableInitCard(CurrCard,thisCard);
+ break;
+ }
+
+ else if (BL_Card[thisCard].ioPort == 0x00) {
+
+ BL_Card[thisCard].ioPort = ioport;
+ CurrCard = &BL_Card[thisCard];
+
+ if(mbCards)
+ for(i = 0; i < mbCards; i++){
+ if(CurrCard->ioPort == nvRamInfo[i].niBaseAddr)
+ CurrCard->pNvRamInfo = &nvRamInfo[i];
+ }
+ SccbMgrTableInitCard(CurrCard,thisCard);
+ CurrCard->cardIndex = thisCard;
+ CurrCard->cardInfo = pCardInfo;
+
+ break;
+ }
+ }
+
+ pCurrNvRam = CurrCard->pNvRamInfo;
+
+ if(pCurrNvRam){
+ ScamFlg = pCurrNvRam->niScamConf;
+ }
+ else{
+ ScamFlg = (UCHAR) utilEERead(ioport, SCAM_CONFIG/2);
+ }
+
+
+ BusMasterInit(ioport);
+ XbowInit(ioport, ScamFlg);
+
+#if defined (NO_BIOS_OPTION)
+
+
+ if (DiagXbow(ioport)) return(FAILURE);
+ if (DiagBusMaster(ioport)) return(FAILURE);
+
+#endif /* No BIOS Option */
+
+ autoLoadDefaultMap(ioport);
+
+
+ for (i = 0,id = 0x01; i != pCardInfo->si_id; i++,id <<= 1){}
+
+ WR_HARPOON(ioport+hp_selfid_0, id);
+ WR_HARPOON(ioport+hp_selfid_1, 0x00);
+ WR_HARPOON(ioport+hp_arb_id, pCardInfo->si_id);
+ CurrCard->ourId = pCardInfo->si_id;
+
+ i = (UCHAR) pCardInfo->si_flags;
+ if (i & SCSI_PARITY_ENA)
+ WR_HARPOON(ioport+hp_portctrl_1,(HOST_MODE8 | CHK_SCSI_P));
+
+ j = (RD_HARPOON(ioport+hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
+ if (i & LOW_BYTE_TERM)
+ j |= SCSI_TERM_ENA_L;
+ WR_HARPOON(ioport+hp_bm_ctrl, j);
+
+ j = (RD_HARPOON(ioport+hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
+ if (i & HIGH_BYTE_TERM)
+ j |= SCSI_TERM_ENA_H;
+ WR_HARPOON(ioport+hp_ee_ctrl, j );
+
+
+ if (!(pCardInfo->si_flags & SOFT_RESET)) {
+
+ sresb(ioport,thisCard);
+
+ scini(thisCard, pCardInfo->si_id, 0);
+ }
+
+
+
+ if (pCardInfo->si_flags & POST_ALL_UNDERRRUNS)
+ CurrCard->globalFlags |= F_NO_FILTER;
+
+ if(pCurrNvRam){
+ if(pCurrNvRam->niSysConf & 0x10)
+ CurrCard->globalFlags |= F_GREEN_PC;
+ }
+ else{
+ if (utilEERead(ioport, (SYSTEM_CONFIG/2)) & GREEN_PC_ENA)
+ CurrCard->globalFlags |= F_GREEN_PC;
+ }
+
+ /* Set global flag to indicate Re-Negotiation to be done on all
+ ckeck condition */
+ if(pCurrNvRam){
+ if(pCurrNvRam->niScsiConf & 0x04)
+ CurrCard->globalFlags |= F_DO_RENEGO;
+ }
+ else{
+ if (utilEERead(ioport, (SCSI_CONFIG/2)) & RENEGO_ENA)
+ CurrCard->globalFlags |= F_DO_RENEGO;
+ }
+
+ if(pCurrNvRam){
+ if(pCurrNvRam->niScsiConf & 0x08)
+ CurrCard->globalFlags |= F_CONLUN_IO;
+ }
+ else{
+ if (utilEERead(ioport, (SCSI_CONFIG/2)) & CONNIO_ENA)
+ CurrCard->globalFlags |= F_CONLUN_IO;
+ }
+
+
+ temp = pCardInfo->si_per_targ_no_disc;
+
+ for (i = 0,id = 1; i < MAX_SCSI_TAR; i++, id <<= 1) {
+
+ if (temp & id)
+ sccbMgrTbl[thisCard][i].TarStatus |= TAR_ALLOW_DISC;
+ }
+
+ sync_bit_map = 0x0001;
+
+ for (id = 0; id < (MAX_SCSI_TAR/2); id++) {
+
+ if(pCurrNvRam){
+ temp = (USHORT) pCurrNvRam->niSyncTbl[id];
+ temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
+ (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
+ }else
+ temp = utilEERead(ioport, (USHORT)((SYNC_RATE_TBL/2)+id));
+
+ for (i = 0; i < 2; temp >>=8,i++) {
+
+ if (pCardInfo->si_per_targ_init_sync & sync_bit_map) {
+
+ sccbMgrTbl[thisCard][id*2+i].TarEEValue = (UCHAR)temp;
+ }
+
+ else {
+ sccbMgrTbl[thisCard][id*2+i].TarStatus |= SYNC_SUPPORTED;
+ sccbMgrTbl[thisCard][id*2+i].TarEEValue =
+ (UCHAR)(temp & ~EE_SYNC_MASK);
+ }
+
+#if defined(WIDE_SCSI)
+/* if ((pCardInfo->si_per_targ_wide_nego & sync_bit_map) ||
+ (id*2+i >= 8)){
+*/
+ if (pCardInfo->si_per_targ_wide_nego & sync_bit_map){
+
+ sccbMgrTbl[thisCard][id*2+i].TarEEValue |= EE_WIDE_SCSI;
+
+ }
+
+ else { /* NARROW SCSI */
+ sccbMgrTbl[thisCard][id*2+i].TarStatus |= WIDE_NEGOCIATED;
+ }
+
+#else
+ sccbMgrTbl[thisCard][id*2+i].TarStatus |= WIDE_NEGOCIATED;
+#endif
+
+
+ sync_bit_map <<= 1;
+
+
+
+ }
+ }
+
+ WR_HARPOON((ioport+hp_semaphore),
+ (UCHAR)(RD_HARPOON((ioport+hp_semaphore)) | SCCB_MGR_PRESENT));
+
+#if defined(DOS)
+ return((USHORT)CurrCard);
+#else
+ return((ULONG)CurrCard);
+#endif
+}
+
+#else /* end (FW_TYPE==_SCCB_MGR_) */
+
+
+
+STATIC s16bits FP_PresenceCheck(PMGR_INFO pMgrInfo)
+{
+ PMGR_ENTRYPNTS pMgr_EntryPnts = &pMgrInfo->mi_Functions;
+
+ pMgr_EntryPnts->UCBMgr_probe_adapter = probe_adapter;
+ pMgr_EntryPnts->UCBMgr_init_adapter = init_adapter;
+ pMgr_EntryPnts->UCBMgr_start_UCB = SccbMgr_start_sccb;
+ pMgr_EntryPnts->UCBMgr_build_UCB = build_UCB;
+ pMgr_EntryPnts->UCBMgr_abort_UCB = SccbMgr_abort_sccb;
+ pMgr_EntryPnts->UCBMgr_my_int = SccbMgr_my_int;
+ pMgr_EntryPnts->UCBMgr_isr = SccbMgr_isr;
+ pMgr_EntryPnts->UCBMgr_scsi_reset = SccbMgr_scsi_reset;
+ pMgr_EntryPnts->UCBMgr_timer_expired = SccbMgr_timer_expired;
+#ifndef NO_IOCTLS
+ pMgr_EntryPnts->UCBMgr_unload_card = SccbMgr_unload_card;
+ pMgr_EntryPnts->UCBMgr_save_foreign_state =
+ SccbMgr_save_foreign_state;
+ pMgr_EntryPnts->UCBMgr_restore_foreign_state =
+ SccbMgr_restore_foreign_state;
+ pMgr_EntryPnts->UCBMgr_restore_native_state =
+ SccbMgr_restore_native_state;
+#endif /*NO_IOCTLS*/
+
+ pMgrInfo->mi_SGListFormat=0x01;
+ pMgrInfo->mi_DataPtrFormat=0x01;
+ pMgrInfo->mi_MaxSGElements= (u16bits) 0xffffffff;
+ pMgrInfo->mi_MgrPrivateLen=sizeof(SCCB);
+ pMgrInfo->mi_PCIVendorID=BL_VENDOR_ID;
+ pMgrInfo->mi_PCIDeviceID=FP_DEVICE_ID;
+ pMgrInfo->mi_MgrAttributes= ATTR_IO_MAPPED +
+ ATTR_PHYSICAL_ADDRESS +
+ ATTR_VIRTUAL_ADDRESS +
+ ATTR_OVERLAPPED_IO_IOCTLS_OK;
+ pMgrInfo->mi_IoRangeLen = 256;
+ return(0);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: probe_adapter
+ *
+ * Description: Setup and/or Search for cards and return info to caller.
+ *
+ *---------------------------------------------------------------------*/
+STATIC s32bits probe_adapter(PADAPTER_INFO pAdapterInfo)
+{
+ u16bits temp,temp2,temp3,temp4;
+ u08bits i,j,id;
+
+#if defined(DOS)
+#else
+ static u08bits first_time = 1;
+#endif
+ BASE_PORT ioport;
+ PNVRamInfo pCurrNvRam;
+
+ ioport = (BASE_PORT)pAdapterInfo->ai_baseaddr;
+
+
+
+ if (RD_HARPOON(ioport+hp_vendor_id_0) != ORION_VEND_0)
+ return(1);
+
+ if ((RD_HARPOON(ioport+hp_vendor_id_1) != ORION_VEND_1))
+ return(2);
+
+ if ((RD_HARPOON(ioport+hp_device_id_0) != ORION_DEV_0))
+ return(3);
+
+ if ((RD_HARPOON(ioport+hp_device_id_1) != ORION_DEV_1))
+ return(4);
+
+
+ if (RD_HARPOON(ioport+hp_rev_num) != 0x0f){
+
+
+/* For new Harpoon then check for sub_device ID LSB
+ the bits(0-3) must be all ZERO for compatible with
+ current version of SCCBMgr, else skip this Harpoon
+ device. */
+
+ if (RD_HARPOON(ioport+hp_sub_device_id_0) & 0x0f)
+ return(5);
+ }
+
+ if (first_time) {
+
+ SccbMgrTableInitAll();
+ first_time = 0;
+ mbCards = 0;
+ }
+
+ if(RdStack(ioport, 0) != 0x00) {
+ if(ChkIfChipInitialized(ioport) == FALSE)
+ {
+ pCurrNvRam = NULL;
+ WR_HARPOON(ioport+hp_semaphore, 0x00);
+ XbowInit(ioport, 0); /*Must Init the SCSI before attempting */
+ DiagEEPROM(ioport);
+ }
+ else
+ {
+ if(mbCards < MAX_MB_CARDS) {
+ pCurrNvRam = &nvRamInfo[mbCards];
+ mbCards++;
+ pCurrNvRam->niBaseAddr = ioport;
+ RNVRamData(pCurrNvRam);
+ }else
+ return((int) FAILURE);
+ }
+ }else
+ pCurrNvRam = NULL;
+
+#if defined (NO_BIOS_OPTION)
+ pCurrNvRam = NULL;
+ XbowInit(ioport, 0); /*Must Init the SCSI before attempting */
+ DiagEEPROM(ioport);
+#endif /* No BIOS Option */
+
+ WR_HARPOON(ioport+hp_clkctrl_0, CLKCTRL_DEFAULT);
+ WR_HARPOON(ioport+hp_sys_ctrl, 0x00);
+
+ if(pCurrNvRam)
+ pAdapterInfo->ai_id = pCurrNvRam->niAdapId;
+ else
+ pAdapterInfo->ai_id = (u08bits)(utilEERead(ioport, (ADAPTER_SCSI_ID/2)) &
+ (u08bits)0x0FF);
+
+ pAdapterInfo->ai_lun = 0x00;
+ pAdapterInfo->ai_fw_revision[0] = '3';
+ pAdapterInfo->ai_fw_revision[1] = '1';
+ pAdapterInfo->ai_fw_revision[2] = '1';
+ pAdapterInfo->ai_fw_revision[3] = ' ';
+ pAdapterInfo->ai_NumChannels = 1;
+
+ temp2 = 0x0000;
+ temp3 = 0x0000;
+ temp4 = 0x0000;
+
+ for (id = 0; id < (16/2); id++) {
+
+ if(pCurrNvRam){
+ temp = (USHORT) pCurrNvRam->niSyncTbl[id];
+ temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
+ (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
+ }else
+ temp = utilEERead(ioport, (u16bits)((SYNC_RATE_TBL/2)+id));
+
+ for (i = 0; i < 2; temp >>=8,i++) {
+
+ if ((temp & 0x03) != AUTO_RATE_00) {
+
+ temp2 >>= 0x01;
+ temp2 |= 0x8000;
+ }
+
+ else {
+ temp2 >>= 0x01;
+ }
+
+ if (temp & DISC_ENABLE_BIT) {
+
+ temp3 >>= 0x01;
+ temp3 |= 0x8000;
+ }
+
+ else {
+ temp3 >>= 0x01;
+ }
+
+ if (temp & WIDE_NEGO_BIT) {
+
+ temp4 >>= 0x01;
+ temp4 |= 0x8000;
+ }
+
+ else {
+ temp4 >>= 0x01;
+ }
+
+ }
+ }
+
+ pAdapterInfo->ai_per_targ_init_sync = temp2;
+ pAdapterInfo->ai_per_targ_no_disc = temp3;
+ pAdapterInfo->ai_per_targ_wide_nego = temp4;
+ if(pCurrNvRam)
+ i = pCurrNvRam->niSysConf;
+ else
+ i = (u08bits)(utilEERead(ioport, (SYSTEM_CONFIG/2)));
+
+ /*
+ ** interrupts always level-triggered for FlashPoint
+ */
+ pAdapterInfo->ai_stateinfo |= LEVEL_TRIG;
+
+ if (i & 0x01)
+ pAdapterInfo->ai_stateinfo |= SCSI_PARITY_ENA;
+
+ if (i & 0x02) /* SCSI Bus reset in AutoSCSI Set ? */
+ {
+ if(pCurrNvRam)
+ {
+ j = pCurrNvRam->niScamConf;
+ }
+ else
+ {
+ j = (u08bits) utilEERead(ioport, SCAM_CONFIG/2);
+ }
+ if(j & SCAM_ENABLED)
+ {
+ if(j & SCAM_LEVEL2)
+ {
+ pAdapterInfo->ai_stateinfo |= SCAM2_ENA;
+ }
+ else
+ {
+ pAdapterInfo->ai_stateinfo |= SCAM1_ENA;
+ }
+ }
+ }
+ j = (RD_HARPOON(ioport+hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
+ if (i & 0x04) {
+ j |= SCSI_TERM_ENA_L;
+ pAdapterInfo->ai_stateinfo |= LOW_BYTE_TERM_ENA;
+ }
+ WR_HARPOON(ioport+hp_bm_ctrl, j );
+
+ j = (RD_HARPOON(ioport+hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
+ if (i & 0x08) {
+ j |= SCSI_TERM_ENA_H;
+ pAdapterInfo->ai_stateinfo |= HIGH_BYTE_TERM_ENA;
+ }
+ WR_HARPOON(ioport+hp_ee_ctrl, j );
+
+ if(RD_HARPOON(ioport + hp_page_ctrl) & BIOS_SHADOW)
+ {
+ pAdapterInfo->ai_FlashRomSize = 64 * 1024; /* 64k ROM */
+ }
+ else
+ {
+ pAdapterInfo->ai_FlashRomSize = 32 * 1024; /* 32k ROM */
+ }
+
+ pAdapterInfo->ai_stateinfo |= (FAST20_ENA | TAG_QUEUE_ENA);
+ if (!(RD_HARPOON(ioport+hp_page_ctrl) & NARROW_SCSI_CARD))
+ {
+ pAdapterInfo->ai_attributes |= (WIDE_CAPABLE | FAST20_CAPABLE
+ | SCAM2_CAPABLE
+ | TAG_QUEUE_CAPABLE
+ | SUPRESS_UNDERRRUNS_CAPABLE
+ | SCSI_PARITY_CAPABLE);
+ pAdapterInfo->ai_MaxTarg = 16;
+ pAdapterInfo->ai_MaxLun = 32;
+ }
+ else
+ {
+ pAdapterInfo->ai_attributes |= (FAST20_CAPABLE | SCAM2_CAPABLE
+ | TAG_QUEUE_CAPABLE
+ | SUPRESS_UNDERRRUNS_CAPABLE
+ | SCSI_PARITY_CAPABLE);
+ pAdapterInfo->ai_MaxTarg = 8;
+ pAdapterInfo->ai_MaxLun = 8;
+ }
+
+ pAdapterInfo->ai_product_family = HARPOON_FAMILY;
+ pAdapterInfo->ai_HBAbustype = BUSTYPE_PCI;
+
+ for (i=0;i<CARD_MODEL_NAMELEN;i++)
+ {
+ pAdapterInfo->ai_card_model[i]=' '; /* initialize the ai_card_model */
+ }
+
+ if(pCurrNvRam){
+ pAdapterInfo->ai_card_model[0] = '9';
+ switch(pCurrNvRam->niModel & 0x0f){
+ case MODEL_LT:
+ pAdapterInfo->ai_card_model[1] = '3';
+ pAdapterInfo->ai_card_model[2] = '0';
+ break;
+ case MODEL_LW:
+ pAdapterInfo->ai_card_model[1] = '5';
+ pAdapterInfo->ai_card_model[2] = '0';
+ break;
+ case MODEL_DL:
+ pAdapterInfo->ai_card_model[1] = '3';
+ pAdapterInfo->ai_card_model[2] = '2';
+ break;
+ case MODEL_DW:
+ pAdapterInfo->ai_card_model[1] = '5';
+ pAdapterInfo->ai_card_model[2] = '2';
+ break;
+ }
+ }else{
+ temp = utilEERead(ioport, (MODEL_NUMB_0/2));
+ pAdapterInfo->ai_card_model[0] = (u08bits)(temp >> 8);
+ temp = utilEERead(ioport, (MODEL_NUMB_2/2));
+
+ pAdapterInfo->ai_card_model[1] = (u08bits)(temp & 0x00FF);
+ pAdapterInfo->ai_card_model[2] = (u08bits)(temp >> 8);
+ }
+
+
+
+ pAdapterInfo->ai_FiberProductType = 0;
+
+ pAdapterInfo->ai_secondary_range = 0;
+
+ for (i=0;i<WORLD_WIDE_NAMELEN;i++)
+ {
+ pAdapterInfo->ai_worldwidename[i]='\0';
+ }
+
+ for (i=0;i<VENDOR_NAMELEN;i++)
+ {
+ pAdapterInfo->ai_vendorstring[i]='\0';
+ }
+ pAdapterInfo->ai_vendorstring[0]='B';
+ pAdapterInfo->ai_vendorstring[1]='U';
+ pAdapterInfo->ai_vendorstring[2]='S';
+ pAdapterInfo->ai_vendorstring[3]='L';
+ pAdapterInfo->ai_vendorstring[4]='O';
+ pAdapterInfo->ai_vendorstring[5]='G';
+ pAdapterInfo->ai_vendorstring[6]='I';
+ pAdapterInfo->ai_vendorstring[7]='C';
+
+ for (i=0;i<FAMILY_NAMELEN;i++)
+ {
+ pAdapterInfo->ai_AdapterFamilyString[i]='\0';
+ }
+ pAdapterInfo->ai_AdapterFamilyString[0]='F';
+ pAdapterInfo->ai_AdapterFamilyString[1]='L';
+ pAdapterInfo->ai_AdapterFamilyString[2]='A';
+ pAdapterInfo->ai_AdapterFamilyString[3]='S';
+ pAdapterInfo->ai_AdapterFamilyString[4]='H';
+ pAdapterInfo->ai_AdapterFamilyString[5]='P';
+ pAdapterInfo->ai_AdapterFamilyString[6]='O';
+ pAdapterInfo->ai_AdapterFamilyString[7]='I';
+ pAdapterInfo->ai_AdapterFamilyString[8]='N';
+ pAdapterInfo->ai_AdapterFamilyString[9]='T';
+
+ ARAM_ACCESS(ioport);
+
+ for ( i = 0; i < 4; i++ ) {
+
+ pAdapterInfo->ai_XlatInfo[i] =
+ RD_HARPOON(ioport+hp_aramBase+BIOS_DATA_OFFSET+i);
+ }
+
+ /* return with -1 if no sort, else return with
+ logical card number sorted by BIOS (zero-based) */
+
+
+ pAdapterInfo->ai_relative_cardnum =
+ (u08bits)(RD_HARPOON(ioport+hp_aramBase+BIOS_RELATIVE_CARD)-1);
+
+ SGRAM_ACCESS(ioport);
+
+ s_PhaseTbl[0] = phaseDataOut;
+ s_PhaseTbl[1] = phaseDataIn;
+ s_PhaseTbl[2] = phaseIllegal;
+ s_PhaseTbl[3] = phaseIllegal;
+ s_PhaseTbl[4] = phaseCommand;
+ s_PhaseTbl[5] = phaseStatus;
+ s_PhaseTbl[6] = phaseMsgOut;
+ s_PhaseTbl[7] = phaseMsgIn;
+
+ pAdapterInfo->ai_present = 0x01;
+
+#if defined(BUGBUG)
+
+
+ for (i = 0; i < MAX_CARDS; i++) {
+
+ for (id=0; id<debug_size; id++)
+ debug_int[i][id] = (u08bits)0x00;
+ debug_index[i] = 0;
+ }
+
+#endif
+
+ return(0);
+}
+
+
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: init_adapter, exported to BUDI via UCBMgr_init_adapter entry
+ *
+ *
+ * Description: Setup adapter for normal operation (hard reset).
+ *
+ *---------------------------------------------------------------------*/
+STATIC CARD_HANDLE init_adapter(PADAPTER_INFO pCardInfo)
+{
+ PSCCBcard CurrCard;
+ PNVRamInfo pCurrNvRam;
+ u08bits i,j,thisCard, ScamFlg;
+ u16bits temp,sync_bit_map,id;
+ BASE_PORT ioport;
+
+ ioport = (BASE_PORT)pCardInfo->ai_baseaddr;
+
+ for(thisCard =0; thisCard <= MAX_CARDS; thisCard++) {
+
+ if (thisCard == MAX_CARDS) {
+
+ return(FAILURE);
+ }
+
+ if (BL_Card[thisCard].ioPort == ioport) {
+
+ CurrCard = &BL_Card[thisCard];
+ SccbMgrTableInitCard(CurrCard,thisCard);
+ break;
+ }
+
+ else if (BL_Card[thisCard].ioPort == 0x00) {
+
+ BL_Card[thisCard].ioPort = ioport;
+ CurrCard = &BL_Card[thisCard];
+
+ if(mbCards)
+ for(i = 0; i < mbCards; i++){
+ if(CurrCard->ioPort == nvRamInfo[i].niBaseAddr)
+ CurrCard->pNvRamInfo = &nvRamInfo[i];
+ }
+ SccbMgrTableInitCard(CurrCard,thisCard);
+ CurrCard->cardIndex = thisCard;
+ CurrCard->cardInfo = pCardInfo;
+
+ break;
+ }
+ }
+
+ pCurrNvRam = CurrCard->pNvRamInfo;
+
+
+ if(pCurrNvRam){
+ ScamFlg = pCurrNvRam->niScamConf;
+ }
+ else{
+ ScamFlg = (UCHAR) utilEERead(ioport, SCAM_CONFIG/2);
+ }
+
+
+ BusMasterInit(ioport);
+ XbowInit(ioport, ScamFlg);
+
+#if defined (NO_BIOS_OPTION)
+
+
+ if (DiagXbow(ioport)) return(FAILURE);
+ if (DiagBusMaster(ioport)) return(FAILURE);
+
+#endif /* No BIOS Option */
+
+ autoLoadDefaultMap(ioport);
+
+
+ for (i = 0,id = 0x01; i != pCardInfo->ai_id; i++,id <<= 1){}
+
+ WR_HARPOON(ioport+hp_selfid_0, id);
+ WR_HARPOON(ioport+hp_selfid_1, 0x00);
+ WR_HARPOON(ioport+hp_arb_id, pCardInfo->ai_id);
+ CurrCard->ourId = (unsigned char) pCardInfo->ai_id;
+
+ i = (u08bits) pCardInfo->ai_stateinfo;
+ if (i & SCSI_PARITY_ENA)
+ WR_HARPOON(ioport+hp_portctrl_1,(HOST_MODE8 | CHK_SCSI_P));
+
+ j = (RD_HARPOON(ioport+hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
+ if (i & LOW_BYTE_TERM_ENA)
+ j |= SCSI_TERM_ENA_L;
+ WR_HARPOON(ioport+hp_bm_ctrl, j);
+
+ j = (RD_HARPOON(ioport+hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
+ if (i & HIGH_BYTE_TERM_ENA)
+ j |= SCSI_TERM_ENA_H;
+ WR_HARPOON(ioport+hp_ee_ctrl, j );
+
+
+ if (!(pCardInfo->ai_stateinfo & NO_RESET_IN_INIT)) {
+
+ sresb(ioport,thisCard);
+
+ scini(thisCard, (u08bits) pCardInfo->ai_id, 0);
+ }
+
+
+
+ if (pCardInfo->ai_stateinfo & SUPRESS_UNDERRRUNS_ENA)
+ CurrCard->globalFlags |= F_NO_FILTER;
+
+ if(pCurrNvRam){
+ if(pCurrNvRam->niSysConf & 0x10)
+ CurrCard->globalFlags |= F_GREEN_PC;
+ }
+ else{
+ if (utilEERead(ioport, (SYSTEM_CONFIG/2)) & GREEN_PC_ENA)
+ CurrCard->globalFlags |= F_GREEN_PC;
+ }
+
+ /* Set global flag to indicate Re-Negotiation to be done on all
+ ckeck condition */
+ if(pCurrNvRam){
+ if(pCurrNvRam->niScsiConf & 0x04)
+ CurrCard->globalFlags |= F_DO_RENEGO;
+ }
+ else{
+ if (utilEERead(ioport, (SCSI_CONFIG/2)) & RENEGO_ENA)
+ CurrCard->globalFlags |= F_DO_RENEGO;
+ }
+
+ if(pCurrNvRam){
+ if(pCurrNvRam->niScsiConf & 0x08)
+ CurrCard->globalFlags |= F_CONLUN_IO;
+ }
+ else{
+ if (utilEERead(ioport, (SCSI_CONFIG/2)) & CONNIO_ENA)
+ CurrCard->globalFlags |= F_CONLUN_IO;
+ }
+
+ temp = pCardInfo->ai_per_targ_no_disc;
+
+ for (i = 0,id = 1; i < MAX_SCSI_TAR; i++, id <<= 1) {
+
+ if (temp & id)
+ sccbMgrTbl[thisCard][i].TarStatus |= TAR_ALLOW_DISC;
+ }
+
+ sync_bit_map = 0x0001;
+
+ for (id = 0; id < (MAX_SCSI_TAR/2); id++){
+
+ if(pCurrNvRam){
+ temp = (USHORT) pCurrNvRam->niSyncTbl[id];
+ temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
+ (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
+ }else
+ temp = utilEERead(ioport, (u16bits)((SYNC_RATE_TBL/2)+id));
+
+ for (i = 0; i < 2; temp >>=8,i++){
+
+ if (pCardInfo->ai_per_targ_init_sync & sync_bit_map){
+
+ sccbMgrTbl[thisCard][id*2+i].TarEEValue = (u08bits)temp;
+ }
+
+ else {
+ sccbMgrTbl[thisCard][id*2+i].TarStatus |= SYNC_SUPPORTED;
+ sccbMgrTbl[thisCard][id*2+i].TarEEValue =
+ (u08bits)(temp & ~EE_SYNC_MASK);
+ }
+
+#if defined(WIDE_SCSI)
+/* if ((pCardInfo->ai_per_targ_wide_nego & sync_bit_map) ||
+ (id*2+i >= 8)){
+*/
+ if (pCardInfo->ai_per_targ_wide_nego & sync_bit_map){
+
+ sccbMgrTbl[thisCard][id*2+i].TarEEValue |= EE_WIDE_SCSI;
+
+ }
+
+ else { /* NARROW SCSI */
+ sccbMgrTbl[thisCard][id*2+i].TarStatus |= WIDE_NEGOCIATED;
+ }
+
+#else
+ sccbMgrTbl[thisCard][id*2+i].TarStatus |= WIDE_NEGOCIATED;
+#endif
+
+
+ sync_bit_map <<= 1;
+ }
+ }
+
+
+ pCardInfo->ai_SGListFormat=0x01;
+ pCardInfo->ai_DataPtrFormat=0x01;
+ pCardInfo->ai_AEN_mask &= SCSI_RESET_COMPLETE;
+
+ WR_HARPOON((ioport+hp_semaphore),
+ (u08bits)(RD_HARPOON((ioport+hp_semaphore)) | SCCB_MGR_PRESENT));
+
+ return((u32bits)CurrCard);
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: build_ucb, exported to BUDI via UCBMgr_build_ucb entry
+ *
+ * Description: prepare fw portion of ucb. do not start, resource not guaranteed
+ * so don't manipulate anything that's derived from states which
+ * may change
+ *
+ *---------------------------------------------------------------------*/
+void build_UCB(CARD_HANDLE pCurrCard, PUCB p_ucb)
+{
+
+ u08bits thisCard;
+ u08bits i,j;
+
+ PSCCB p_sccb;
+
+
+ thisCard = ((PSCCBcard) pCurrCard)->cardIndex;
+
+
+ p_sccb=(PSCCB)p_ucb->UCB_MgrPrivatePtr;
+
+
+ p_sccb->Sccb_ucb_ptr=p_ucb;
+
+ switch (p_ucb->UCB_opcode & (OPC_DEVICE_RESET+OPC_XFER_SG+OPC_CHK_RESIDUAL))
+ {
+ case OPC_DEVICE_RESET:
+ p_sccb->OperationCode=RESET_COMMAND;
+ break;
+ case OPC_XFER_SG:
+ p_sccb->OperationCode=SCATTER_GATHER_COMMAND;
+ break;
+ case OPC_XFER_SG+OPC_CHK_RESIDUAL:
+ p_sccb->OperationCode=RESIDUAL_SG_COMMAND;
+ break;
+ case OPC_CHK_RESIDUAL:
+
+ p_sccb->OperationCode=RESIDUAL_COMMAND;
+ break;
+ default:
+ p_sccb->OperationCode=SCSI_INITIATOR_COMMAND;
+ break;
+ }
+
+ if (p_ucb->UCB_opcode & OPC_TQ_ENABLE)
+ {
+ p_sccb->ControlByte = (u08bits)((p_ucb->UCB_opcode & OPC_TQ_MASK)>>2) | F_USE_CMD_Q;
+ }
+ else
+ {
+ p_sccb->ControlByte = 0;
+ }
+
+
+ p_sccb->CdbLength = (u08bits)p_ucb->UCB_cdblen;
+
+ if (p_ucb->UCB_opcode & OPC_NO_AUTO_SENSE)
+ {
+ p_sccb->RequestSenseLength = 0;
+ }
+ else
+ {
+ p_sccb->RequestSenseLength = (unsigned char) p_ucb->UCB_senselen;
+ }
+
+
+ if (p_ucb->UCB_opcode & OPC_XFER_SG)
+ {
+ p_sccb->DataPointer=p_ucb->UCB_virt_dataptr;
+ p_sccb->DataLength = (((u32bits)p_ucb->UCB_NumSgElements)<<3);
+ }
+ else
+ {
+ p_sccb->DataPointer=p_ucb->UCB_phys_dataptr;
+ p_sccb->DataLength=p_ucb->UCB_datalen;
+ };
+
+ p_sccb->HostStatus=0;
+ p_sccb->TargetStatus=0;
+ p_sccb->TargID=(unsigned char)p_ucb->UCB_targid;
+ p_sccb->Lun=(unsigned char) p_ucb->UCB_lun;
+ p_sccb->SccbIOPort=((PSCCBcard)pCurrCard)->ioPort;
+
+ j=p_ucb->UCB_cdblen;
+ for (i=0;i<j;i++)
+ {
+ p_sccb->Cdb[i] = p_ucb->UCB_cdb[i];
+ }
+
+ p_sccb->SensePointer=p_ucb->UCB_phys_senseptr;
+
+ sinits(p_sccb,thisCard);
+
+}
+#ifndef NO_IOCTLS
+
+/*---------------------------------------------------------------------
+ *
+ * Function: GetDevSyncRate
+ *
+ *---------------------------------------------------------------------*/
+STATIC int GetDevSyncRate(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+ struct _SYNC_RATE_INFO * pSyncStr;
+ PSCCBMgr_tar_info currTar_Info;
+ BASE_PORT ioport;
+ u08bits scsiID, j;
+
+#if (FW_TYPE != _SCCB_MGR_)
+ if( p_ucb->UCB_targid >= pCurrCard->cardInfo->ai_MaxTarg )
+ {
+ return(1);
+ }
+#endif
+
+ ioport = pCurrCard->ioPort;
+ pSyncStr = (struct _SYNC_RATE_INFO *) p_ucb->UCB_virt_dataptr;
+ scsiID = (u08bits) p_ucb->UCB_targid;
+ currTar_Info = &sccbMgrTbl[pCurrCard->cardIndex][scsiID];
+ j = currTar_Info->TarSyncCtrl;
+
+ switch (currTar_Info->TarEEValue & EE_SYNC_MASK)
+ {
+ case EE_SYNC_ASYNC:
+ pSyncStr->RequestMegaXferRate = 0x00;
+ break;
+ case EE_SYNC_5MB:
+ pSyncStr->RequestMegaXferRate = (j & NARROW_SCSI) ? 50 : 100;
+ break;
+ case EE_SYNC_10MB:
+ pSyncStr->RequestMegaXferRate = (j & NARROW_SCSI) ? 100 : 200;
+ break;
+ case EE_SYNC_20MB:
+ pSyncStr->RequestMegaXferRate = (j & NARROW_SCSI) ? 200 : 400;
+ break;
+ }
+
+ switch ((j >> 5) & 0x07)
+ {
+ case 0x00:
+ if((j & 0x07) == 0x00)
+ {
+ pSyncStr->ActualMegaXferRate = 0x00; /* Async Mode */
+ }
+ else
+ {
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 200 : 400;
+ }
+ break;
+ case 0x01:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 100 : 200;
+ break;
+ case 0x02:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 66 : 122;
+ break;
+ case 0x03:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 50 : 100;
+ break;
+ case 0x04:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 40 : 80;
+ break;
+ case 0x05:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 33 : 66;
+ break;
+ case 0x06:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 28 : 56;
+ break;
+ case 0x07:
+ pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 25 : 50;
+ break;
+ }
+ pSyncStr->NegotiatedOffset = j & 0x0f;
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SetDevSyncRate
+ *
+ *---------------------------------------------------------------------*/
+STATIC int SetDevSyncRate(PSCCBcard pCurrCard, PUCB p_ucb)
+{
+ struct _SYNC_RATE_INFO * pSyncStr;
+ PSCCBMgr_tar_info currTar_Info;
+ BASE_PORT ioPort;
+ u08bits scsiID, i, j, syncVal;
+ u16bits syncOffset, actualXferRate;
+ union {
+ u08bits tempb[2];
+ u16bits tempw;
+ }temp2;
+
+#if (FW_TYPE != _SCCB_MGR_)
+ if( p_ucb->UCB_targid >= pCurrCard->cardInfo->ai_MaxTarg )
+ {
+ return(1);
+ }
+#endif
+
+ ioPort = pCurrCard->ioPort;
+ pSyncStr = (struct _SYNC_RATE_INFO *) p_ucb->UCB_virt_dataptr;
+ scsiID = (u08bits) p_ucb->UCB_targid;
+ currTar_Info = &sccbMgrTbl[pCurrCard->cardIndex][scsiID];
+ i = RD_HARPOON(ioPort+hp_xfer_pad); /* Save current value */
+ WR_HARPOON(ioPort+hp_xfer_pad, (i | ID_UNLOCK));
+ WR_HARPOON(ioPort+hp_select_id, ((scsiID << 4) | scsiID));
+ j = RD_HARPOON(ioPort+hp_synctarg_0);
+ WR_HARPOON(ioPort+hp_xfer_pad, i); /* restore value */
+
+ actualXferRate = pSyncStr->ActualMegaXferRate;
+ if(!(j & NARROW_SCSI))
+ {
+ actualXferRate <<= 1;
+ }
+ if(actualXferRate == 0x00)
+ {
+ syncVal = EE_SYNC_ASYNC; /* Async Mode */
+ }
+ if(actualXferRate == 0x0200)
+ {
+ syncVal = EE_SYNC_20MB; /* 20/40 MB Mode */
+ }
+ if(actualXferRate > 0x0050 && actualXferRate < 0x0200 )
+ {
+ syncVal = EE_SYNC_10MB; /* 10/20 MB Mode */
+ }
+ else
+ {
+ syncVal = EE_SYNC_5MB; /* 5/10 MB Mode */
+ }
+ if(currTar_Info->TarEEValue && EE_SYNC_MASK == syncVal)
+ return(0);
+ currTar_Info->TarEEValue = (currTar_Info->TarEEValue & !EE_SYNC_MASK)
+ | syncVal;
+ syncOffset = (SYNC_RATE_TBL + scsiID) / 2;
+ temp2.tempw = utilEERead(ioPort, syncOffset);
+ if(scsiID & 0x01)
+ {
+ temp2.tempb[0] = (temp2.tempb[0] & !EE_SYNC_MASK) | syncVal;
+ }
+ else
+ {
+ temp2.tempb[1] = (temp2.tempb[1] & !EE_SYNC_MASK) | syncVal;
+ }
+ utilEEWriteOnOff(ioPort, 1);
+ utilEEWrite(ioPort, temp2.tempw, syncOffset);
+ utilEEWriteOnOff(ioPort, 0);
+ UpdateCheckSum(ioPort);
+
+ return(0);
+}
+/*---------------------------------------------------------------------
+ *
+ * Function: GetDevWideMode
+ *
+ *---------------------------------------------------------------------*/
+int GetDevWideMode(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+ u08bits *pData;
+
+ pData = (u08bits *)p_ucb->UCB_virt_dataptr;
+ if(sccbMgrTbl[pCurrCard->cardIndex][p_ucb->UCB_targid].TarEEValue
+ & EE_WIDE_SCSI)
+ {
+ *pData = 1;
+ }
+ else
+ {
+ *pData = 0;
+ }
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SetDevWideMode
+ *
+ *---------------------------------------------------------------------*/
+int SetDevWideMode(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+ u08bits *pData;
+ PSCCBMgr_tar_info currTar_Info;
+ BASE_PORT ioPort;
+ u08bits scsiID, scsiWideMode;
+ u16bits syncOffset;
+ union {
+ u08bits tempb[2];
+ u16bits tempw;
+ }temp2;
+
+#if (FW_TYPE != _SCCB_MGR_)
+ if( !(pCurrCard->cardInfo->ai_attributes & WIDE_CAPABLE) )
+ {
+ return(1);
+ }
+
+ if( p_ucb->UCB_targid >= pCurrCard->cardInfo->ai_MaxTarg )
+ {
+ return(1);
+ }
+#endif
+
+ ioPort = pCurrCard->ioPort;
+ pData = (u08bits *)p_ucb->UCB_virt_dataptr;
+ scsiID = (u08bits) p_ucb->UCB_targid;
+ currTar_Info = &sccbMgrTbl[pCurrCard->cardIndex][scsiID];
+
+ if(*pData)
+ {
+ if(currTar_Info->TarEEValue & EE_WIDE_SCSI)
+ {
+ return(0);
+ }
+ else
+ {
+ scsiWideMode = EE_WIDE_SCSI;
+ }
+ }
+ else
+ {
+ if(!currTar_Info->TarEEValue & EE_WIDE_SCSI)
+ {
+ return(0);
+ }
+ else
+ {
+ scsiWideMode = 0;
+ }
+ }
+ currTar_Info->TarEEValue = (currTar_Info->TarEEValue & !EE_WIDE_SCSI)
+ | scsiWideMode;
+
+ syncOffset = (SYNC_RATE_TBL + scsiID) / 2;
+ temp2.tempw = utilEERead(ioPort, syncOffset);
+ if(scsiID & 0x01)
+ {
+ temp2.tempb[0] = (temp2.tempb[0] & !EE_WIDE_SCSI) | scsiWideMode;
+ }
+ else
+ {
+ temp2.tempb[1] = (temp2.tempb[1] & !EE_WIDE_SCSI) | scsiWideMode;
+ }
+ utilEEWriteOnOff(ioPort, 1);
+ utilEEWrite(ioPort, temp2.tempw, syncOffset);
+ utilEEWriteOnOff(ioPort, 0);
+ UpdateCheckSum(ioPort);
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: ReadNVRam
+ *
+ *---------------------------------------------------------------------*/
+void ReadNVRam(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+ u08bits *pdata;
+ u16bits i,numwrds,numbytes,offset,temp;
+ u08bits OneMore = FALSE;
+#if defined(DOS)
+ u16bits ioport;
+#else
+ u32bits ioport;
+#endif
+
+ numbytes = (u16bits) p_ucb->UCB_datalen;
+ ioport = pCurrCard->ioPort;
+ pdata = (u08bits *) p_ucb->UCB_virt_dataptr;
+ offset = (u16bits) (p_ucb->UCB_IOCTLParams[0]);
+
+
+
+ if (offset & 0x1)
+ {
+ *((u16bits*) pdata) = utilEERead(ioport,(u16bits)((offset - 1) / 2)); /* 16 bit read */
+ *pdata = *(pdata + 1);
+ ++offset;
+ ++pdata;
+ --numbytes;
+ }
+
+ numwrds = numbytes / 2;
+ if (numbytes & 1)
+ OneMore = TRUE;
+
+ for (i = 0; i < numwrds; i++)
+ {
+ *((u16bits*) pdata) = utilEERead(ioport,(u16bits)(offset / 2));
+ pdata += 2;
+ offset += 2;
+ }
+ if (OneMore)
+ {
+ --pdata;
+ -- offset;
+ temp = utilEERead(ioport,(u16bits)(offset / 2));
+ *pdata = (u08bits) (temp);
+ }
+
+} /* end proc ReadNVRam */
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: WriteNVRam
+ *
+ *---------------------------------------------------------------------*/
+void WriteNVRam(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+ u08bits *pdata;
+ u16bits i,numwrds,numbytes,offset, eeprom_end;
+ u08bits OneMore = FALSE;
+ union {
+ u08bits tempb[2];
+ u16bits tempw;
+ } temp2;
+
+#if defined(DOS)
+ u16bits ioport;
+#else
+ u32bits ioport;
+#endif
+
+ numbytes = (u16bits) p_ucb->UCB_datalen;
+ ioport = pCurrCard->ioPort;
+ pdata = (u08bits *) p_ucb->UCB_virt_dataptr;
+ offset = (u16bits) (p_ucb->UCB_IOCTLParams[0]);
+
+ if (RD_HARPOON(ioport+hp_page_ctrl) & NARROW_SCSI_CARD)
+ eeprom_end = 512;
+ else
+ eeprom_end = 768;
+
+ if(offset > eeprom_end)
+ return;
+
+ if((offset + numbytes) > eeprom_end)
+ numbytes = eeprom_end - offset;
+
+ utilEEWriteOnOff(ioport,1); /* Enable write access to the EEPROM */
+
+
+
+ if (offset & 0x1)
+ {
+ temp2.tempw = utilEERead(ioport,(u16bits)((offset - 1) / 2)); /* 16 bit read */
+ temp2.tempb[1] = *pdata;
+ utilEEWrite(ioport, temp2.tempw, (u16bits)((offset -1) / 2));
+ *pdata = *(pdata + 1);
+ ++offset;
+ ++pdata;
+ --numbytes;
+ }
+
+ numwrds = numbytes / 2;
+ if (numbytes & 1)
+ OneMore = TRUE;
+
+ for (i = 0; i < numwrds; i++)
+ {
+ utilEEWrite(ioport, *((pu16bits)pdata),(u16bits)(offset / 2));
+ pdata += 2;
+ offset += 2;
+ }
+ if (OneMore)
+ {
+
+ temp2.tempw = utilEERead(ioport,(u16bits)(offset / 2));
+ temp2.tempb[0] = *pdata;
+ utilEEWrite(ioport, temp2.tempw, (u16bits)(offset / 2));
+ }
+ utilEEWriteOnOff(ioport,0); /* Turn off write access */
+ UpdateCheckSum((u32bits)ioport);
+
+} /* end proc WriteNVRam */
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: UpdateCheckSum
+ *
+ * Description: Update Check Sum in EEPROM
+ *
+ *---------------------------------------------------------------------*/
+
+
+void UpdateCheckSum(u32bits baseport)
+{
+ USHORT i,sum_data, eeprom_end;
+
+ sum_data = 0x0000;
+
+
+ if (RD_HARPOON(baseport+hp_page_ctrl) & NARROW_SCSI_CARD)
+ eeprom_end = 512;
+ else
+ eeprom_end = 768;
+
+ for (i = 1; i < eeprom_end/2; i++)
+ {
+ sum_data += utilEERead(baseport, i);
+ }
+
+ utilEEWriteOnOff(baseport,1); /* Enable write access to the EEPROM */
+
+ utilEEWrite(baseport, sum_data, EEPROM_CHECK_SUM/2);
+ utilEEWriteOnOff(baseport,0); /* Turn off write access */
+}
+
+void SccbMgr_save_foreign_state(PADAPTER_INFO pAdapterInfo)
+{
+}
+
+
+void SccbMgr_restore_foreign_state(CARD_HANDLE pCurrCard)
+{
+}
+
+void SccbMgr_restore_native_state(CARD_HANDLE pCurrCard)
+{
+}
+
+#endif /* NO_IOCTLS */
+
+#endif /* (FW_TYPE==_UCB_MGR_) */
+
+#ifndef NO_IOCTLS
+#if (FW_TYPE==_UCB_MGR_)
+void SccbMgr_unload_card(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+void SccbMgr_unload_card(USHORT pCurrCard)
+#else
+void SccbMgr_unload_card(ULONG pCurrCard)
+#endif
+#endif
+{
+ UCHAR i;
+#if defined(DOS)
+ USHORT portBase;
+ USHORT regOffset;
+#else
+ ULONG portBase;
+ ULONG regOffset;
+#endif
+ ULONG scamData;
+#if defined(OS2)
+ ULONG far *pScamTbl;
+#else
+ ULONG *pScamTbl;
+#endif
+ PNVRamInfo pCurrNvRam;
+
+ pCurrNvRam = ((PSCCBcard)pCurrCard)->pNvRamInfo;
+
+ if(pCurrNvRam){
+ WrStack(pCurrNvRam->niBaseAddr, 0, pCurrNvRam->niModel);
+ WrStack(pCurrNvRam->niBaseAddr, 1, pCurrNvRam->niSysConf);
+ WrStack(pCurrNvRam->niBaseAddr, 2, pCurrNvRam->niScsiConf);
+ WrStack(pCurrNvRam->niBaseAddr, 3, pCurrNvRam->niScamConf);
+ WrStack(pCurrNvRam->niBaseAddr, 4, pCurrNvRam->niAdapId);
+
+ for(i = 0; i < MAX_SCSI_TAR / 2; i++)
+ WrStack(pCurrNvRam->niBaseAddr, (UCHAR)(i+5), pCurrNvRam->niSyncTbl[i]);
+
+ portBase = pCurrNvRam->niBaseAddr;
+
+ for(i = 0; i < MAX_SCSI_TAR; i++){
+ regOffset = hp_aramBase + 64 + i*4;
+#if defined(OS2)
+ pScamTbl = (ULONG far *) &pCurrNvRam->niScamTbl[i];
+#else
+ pScamTbl = (ULONG *) &pCurrNvRam->niScamTbl[i];
+#endif
+ scamData = *pScamTbl;
+ WR_HARP32(portBase, regOffset, scamData);
+ }
+
+ }else{
+ WrStack(((PSCCBcard)pCurrCard)->ioPort, 0, 0);
+ }
+}
+#endif /* NO_IOCTLS */
+
+
+void RNVRamData(PNVRamInfo pNvRamInfo)
+{
+ UCHAR i;
+#if defined(DOS)
+ USHORT portBase;
+ USHORT regOffset;
+#else
+ ULONG portBase;
+ ULONG regOffset;
+#endif
+ ULONG scamData;
+#if defined (OS2)
+ ULONG far *pScamTbl;
+#else
+ ULONG *pScamTbl;
+#endif
+
+ pNvRamInfo->niModel = RdStack(pNvRamInfo->niBaseAddr, 0);
+ pNvRamInfo->niSysConf = RdStack(pNvRamInfo->niBaseAddr, 1);
+ pNvRamInfo->niScsiConf = RdStack(pNvRamInfo->niBaseAddr, 2);
+ pNvRamInfo->niScamConf = RdStack(pNvRamInfo->niBaseAddr, 3);
+ pNvRamInfo->niAdapId = RdStack(pNvRamInfo->niBaseAddr, 4);
+
+ for(i = 0; i < MAX_SCSI_TAR / 2; i++)
+ pNvRamInfo->niSyncTbl[i] = RdStack(pNvRamInfo->niBaseAddr, (UCHAR)(i+5));
+
+ portBase = pNvRamInfo->niBaseAddr;
+
+ for(i = 0; i < MAX_SCSI_TAR; i++){
+ regOffset = hp_aramBase + 64 + i*4;
+ RD_HARP32(portBase, regOffset, scamData);
+#if defined(OS2)
+ pScamTbl = (ULONG far *) &pNvRamInfo->niScamTbl[i];
+#else
+ pScamTbl = (ULONG *) &pNvRamInfo->niScamTbl[i];
+#endif
+ *pScamTbl = scamData;
+ }
+
+}
+
+#if defined(DOS)
+UCHAR RdStack(USHORT portBase, UCHAR index)
+#else
+UCHAR RdStack(ULONG portBase, UCHAR index)
+#endif
+{
+ WR_HARPOON(portBase + hp_stack_addr, index);
+ return(RD_HARPOON(portBase + hp_stack_data));
+}
+
+#if defined(DOS)
+void WrStack(USHORT portBase, UCHAR index, UCHAR data)
+#else
+void WrStack(ULONG portBase, UCHAR index, UCHAR data)
+#endif
+{
+ WR_HARPOON(portBase + hp_stack_addr, index);
+ WR_HARPOON(portBase + hp_stack_data, data);
+}
+
+
+#if (FW_TYPE==_UCB_MGR_)
+u08bits ChkIfChipInitialized(BASE_PORT ioPort)
+#else
+#if defined(DOS)
+UCHAR ChkIfChipInitialized(USHORT ioPort)
+#else
+UCHAR ChkIfChipInitialized(ULONG ioPort)
+#endif
+#endif
+{
+ if((RD_HARPOON(ioPort + hp_arb_id) & 0x0f) != RdStack(ioPort, 4))
+ return(FALSE);
+ if((RD_HARPOON(ioPort + hp_clkctrl_0) & CLKCTRL_DEFAULT)
+ != CLKCTRL_DEFAULT)
+ return(FALSE);
+ if((RD_HARPOON(ioPort + hp_seltimeout) == TO_250ms) ||
+ (RD_HARPOON(ioPort + hp_seltimeout) == TO_290ms))
+ return(TRUE);
+ return(FALSE);
+
+}
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_start_sccb
+ *
+ * Description: Start a command pointed to by p_Sccb. When the
+ * command is completed it will be returned via the
+ * callback function.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+void SccbMgr_start_sccb(CARD_HANDLE pCurrCard, PUCB p_ucb)
+#else
+#if defined(DOS)
+void SccbMgr_start_sccb(USHORT pCurrCard, PSCCB p_Sccb)
+#else
+void SccbMgr_start_sccb(ULONG pCurrCard, PSCCB p_Sccb)
+#endif
+#endif
+{
+#if defined(DOS)
+ USHORT ioport;
+#else
+ ULONG ioport;
+#endif
+ UCHAR thisCard, lun;
+ PSCCB pSaveSccb;
+ CALL_BK_FN callback;
+
+#if (FW_TYPE==_UCB_MGR_)
+ PSCCB p_Sccb;
+#endif
+
+ mOS_Lock((PSCCBcard)pCurrCard);
+ thisCard = ((PSCCBcard) pCurrCard)->cardIndex;
+ ioport = ((PSCCBcard) pCurrCard)->ioPort;
+
+#if (FW_TYPE==_UCB_MGR_)
+ p_Sccb = (PSCCB)p_ucb->UCB_MgrPrivatePtr;
+#endif
+
+ if((p_Sccb->TargID > MAX_SCSI_TAR) || (p_Sccb->Lun > MAX_LUN))
+ {
+
+#if (FW_TYPE==_UCB_MGR_)
+ p_ucb->UCB_hbastat = SCCB_COMPLETE;
+ p_ucb->UCB_status=SCCB_ERROR;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+#endif
+
+#if (FW_TYPE==_SCCB_MGR_)
+ p_Sccb->HostStatus = SCCB_COMPLETE;
+ p_Sccb->SccbStatus = SCCB_ERROR;
+ callback = (CALL_BK_FN)p_Sccb->SccbCallback;
+ if (callback)
+ callback(p_Sccb);
+#endif
+
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ }
+
+#if (FW_TYPE==_SCCB_MGR_)
+ sinits(p_Sccb,thisCard);
+#endif
+
+
+#if (FW_TYPE==_UCB_MGR_)
+#ifndef NO_IOCTLS
+
+ if (p_ucb->UCB_opcode & OPC_IOCTL)
+ {
+
+ switch (p_ucb->UCB_IOCTLCommand)
+ {
+ case READ_NVRAM:
+ ReadNVRam((PSCCBcard)pCurrCard,p_ucb);
+ p_ucb->UCB_status=UCB_SUCCESS;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+
+ case WRITE_NVRAM:
+ WriteNVRam((PSCCBcard)pCurrCard,p_ucb);
+ p_ucb->UCB_status=UCB_SUCCESS;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+
+ case SEND_SCSI_PASSTHRU:
+#if (FW_TYPE != _SCCB_MGR_)
+ if( p_ucb->UCB_targid >=
+ ((PSCCBcard)pCurrCard)->cardInfo->ai_MaxTarg )
+ {
+ p_ucb->UCB_status = UCB_ERROR;
+ p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ }
+#endif
+ break;
+
+ case HARD_RESET:
+ p_ucb->UCB_status = UCB_INVALID;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ case GET_DEVICE_SYNCRATE:
+ if( !GetDevSyncRate((PSCCBcard)pCurrCard,p_ucb) )
+ {
+ p_ucb->UCB_status = UCB_SUCCESS;
+ }
+ else
+ {
+ p_ucb->UCB_status = UCB_ERROR;
+ p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+ }
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ case SET_DEVICE_SYNCRATE:
+ if( !SetDevSyncRate((PSCCBcard)pCurrCard,p_ucb) )
+ {
+ p_ucb->UCB_status = UCB_SUCCESS;
+ }
+ else
+ {
+ p_ucb->UCB_status = UCB_ERROR;
+ p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+ }
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ case GET_WIDE_MODE:
+ if( !GetDevWideMode((PSCCBcard)pCurrCard,p_ucb) )
+ {
+ p_ucb->UCB_status = UCB_SUCCESS;
+ }
+ else
+ {
+ p_ucb->UCB_status = UCB_ERROR;
+ p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+ }
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ case SET_WIDE_MODE:
+ if( !SetDevWideMode((PSCCBcard)pCurrCard,p_ucb) )
+ {
+ p_ucb->UCB_status = UCB_SUCCESS;
+ }
+ else
+ {
+ p_ucb->UCB_status = UCB_ERROR;
+ p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+ }
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ default:
+ p_ucb->UCB_status=UCB_INVALID;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ if (callback)
+ callback(p_ucb);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return;
+ }
+ }
+#endif /* NO_IOCTLS */
+#endif /* (FW_TYPE==_UCB_MGR_) */
+
+
+ if (!((PSCCBcard) pCurrCard)->cmdCounter)
+ {
+ WR_HARPOON(ioport+hp_semaphore, (RD_HARPOON(ioport+hp_semaphore)
+ | SCCB_MGR_ACTIVE));
+
+ if (((PSCCBcard) pCurrCard)->globalFlags & F_GREEN_PC)
+ {
+ WR_HARPOON(ioport+hp_clkctrl_0, CLKCTRL_DEFAULT);
+ WR_HARPOON(ioport+hp_sys_ctrl, 0x00);
+ }
+ }
+
+ ((PSCCBcard)pCurrCard)->cmdCounter++;
+
+ if (RD_HARPOON(ioport+hp_semaphore) & BIOS_IN_USE) {
+
+ WR_HARPOON(ioport+hp_semaphore, (RD_HARPOON(ioport+hp_semaphore)
+ | TICKLE_ME));
+ if(p_Sccb->OperationCode == RESET_COMMAND)
+ {
+ pSaveSccb = ((PSCCBcard) pCurrCard)->currentSCCB;
+ ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+ queueSelectFail(&BL_Card[thisCard], thisCard);
+ ((PSCCBcard) pCurrCard)->currentSCCB = pSaveSccb;
+ }
+ else
+ {
+ queueAddSccb(p_Sccb,thisCard);
+ }
+ }
+
+ else if ((RD_HARPOON(ioport+hp_page_ctrl) & G_INT_DISABLE)) {
+
+ if(p_Sccb->OperationCode == RESET_COMMAND)
+ {
+ pSaveSccb = ((PSCCBcard) pCurrCard)->currentSCCB;
+ ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+ queueSelectFail(&BL_Card[thisCard], thisCard);
+ ((PSCCBcard) pCurrCard)->currentSCCB = pSaveSccb;
+ }
+ else
+ {
+ queueAddSccb(p_Sccb,thisCard);
+ }
+ }
+
+ else {
+
+ MDISABLE_INT(ioport);
+
+ if((((PSCCBcard) pCurrCard)->globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[thisCard][p_Sccb->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ lun = p_Sccb->Lun;
+ else
+ lun = 0;
+ if ((((PSCCBcard) pCurrCard)->currentSCCB == NULL) &&
+ (sccbMgrTbl[thisCard][p_Sccb->TargID].TarSelQ_Cnt == 0) &&
+ (sccbMgrTbl[thisCard][p_Sccb->TargID].TarLUNBusy[lun]
+ == FALSE)) {
+
+ ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+ mOS_UnLock((PSCCBcard)pCurrCard);
+#if defined(DOS)
+ ssel((USHORT)p_Sccb->SccbIOPort,thisCard);
+#else
+ ssel(p_Sccb->SccbIOPort,thisCard);
+#endif
+ mOS_Lock((PSCCBcard)pCurrCard);
+ }
+
+ else {
+
+ if(p_Sccb->OperationCode == RESET_COMMAND)
+ {
+ pSaveSccb = ((PSCCBcard) pCurrCard)->currentSCCB;
+ ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+ queueSelectFail(&BL_Card[thisCard], thisCard);
+ ((PSCCBcard) pCurrCard)->currentSCCB = pSaveSccb;
+ }
+ else
+ {
+ queueAddSccb(p_Sccb,thisCard);
+ }
+ }
+
+
+ MENABLE_INT(ioport);
+ }
+
+ mOS_UnLock((PSCCBcard)pCurrCard);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_abort_sccb
+ *
+ * Description: Abort the command pointed to by p_Sccb. When the
+ * command is completed it will be returned via the
+ * callback function.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+s32bits SccbMgr_abort_sccb(CARD_HANDLE pCurrCard, PUCB p_ucb)
+#else
+#if defined(DOS)
+int SccbMgr_abort_sccb(USHORT pCurrCard, PSCCB p_Sccb)
+#else
+int SccbMgr_abort_sccb(ULONG pCurrCard, PSCCB p_Sccb)
+#endif
+#endif
+
+{
+#if defined(DOS)
+ USHORT ioport;
+#else
+ ULONG ioport;
+#endif
+
+ UCHAR thisCard;
+ CALL_BK_FN callback;
+ UCHAR TID;
+ PSCCB pSaveSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+
+#if (FW_TYPE==_UCB_MGR_)
+ PSCCB p_Sccb;
+ p_Sccb=(PSCCB)p_ucb->UCB_MgrPrivatePtr;
+#endif
+
+ ioport = ((PSCCBcard) pCurrCard)->ioPort;
+
+ thisCard = ((PSCCBcard)pCurrCard)->cardIndex;
+
+ mOS_Lock((PSCCBcard)pCurrCard);
+
+ if (RD_HARPOON(ioport+hp_page_ctrl) & G_INT_DISABLE)
+ {
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ }
+
+ else
+ {
+
+ if (queueFindSccb(p_Sccb,thisCard))
+ {
+
+ mOS_UnLock((PSCCBcard)pCurrCard);
+
+ ((PSCCBcard)pCurrCard)->cmdCounter--;
+
+ if (!((PSCCBcard)pCurrCard)->cmdCounter)
+ WR_HARPOON(ioport+hp_semaphore,(RD_HARPOON(ioport+hp_semaphore)
+ & (UCHAR)(~(SCCB_MGR_ACTIVE | TICKLE_ME)) ));
+
+#if (FW_TYPE==_SCCB_MGR_)
+ p_Sccb->SccbStatus = SCCB_ABORT;
+ callback = p_Sccb->SccbCallback;
+ callback(p_Sccb);
+#else
+ p_ucb->UCB_status=SCCB_ABORT;
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ callback(p_ucb);
+#endif
+
+ return(0);
+ }
+
+ else
+ {
+ mOS_UnLock((PSCCBcard)pCurrCard);
+
+ if (((PSCCBcard)pCurrCard)->currentSCCB == p_Sccb)
+ {
+ p_Sccb->SccbStatus = SCCB_ABORT;
+ return(0);
+
+ }
+
+ else
+ {
+
+ TID = p_Sccb->TargID;
+
+
+ if(p_Sccb->Sccb_tag)
+ {
+ MDISABLE_INT(ioport);
+ if (((PSCCBcard) pCurrCard)->discQ_Tbl[p_Sccb->Sccb_tag]==p_Sccb)
+ {
+ p_Sccb->SccbStatus = SCCB_ABORT;
+ p_Sccb->Sccb_scsistat = ABORT_ST;
+#if (FW_TYPE==_UCB_MGR_)
+ p_ucb->UCB_status=SCCB_ABORT;
+#endif
+ p_Sccb->Sccb_scsimsg = SMABORT_TAG;
+
+ if(((PSCCBcard) pCurrCard)->currentSCCB == NULL)
+ {
+ ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+ ssel(ioport, thisCard);
+ }
+ else
+ {
+ pSaveSCCB = ((PSCCBcard) pCurrCard)->currentSCCB;
+ ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+ queueSelectFail((PSCCBcard) pCurrCard, thisCard);
+ ((PSCCBcard) pCurrCard)->currentSCCB = pSaveSCCB;
+ }
+ }
+ MENABLE_INT(ioport);
+ return(0);
+ }
+ else
+ {
+ currTar_Info = &sccbMgrTbl[thisCard][p_Sccb->TargID];
+
+ if(BL_Card[thisCard].discQ_Tbl[currTar_Info->LunDiscQ_Idx[p_Sccb->Lun]]
+ == p_Sccb)
+ {
+ p_Sccb->SccbStatus = SCCB_ABORT;
+ return(0);
+ }
+ }
+ }
+ }
+ }
+ return(-1);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_my_int
+ *
+ * Description: Do a quick check to determine if there is a pending
+ * interrupt for this card and disable the IRQ Pin if so.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+u08bits SccbMgr_my_int(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+UCHAR SccbMgr_my_int(USHORT pCurrCard)
+#else
+UCHAR SccbMgr_my_int(ULONG pCurrCard)
+#endif
+#endif
+{
+#if defined(DOS)
+ USHORT ioport;
+#else
+ ULONG ioport;
+#endif
+
+ ioport = ((PSCCBcard)pCurrCard)->ioPort;
+
+ if (RD_HARPOON(ioport+hp_int_status) & INT_ASSERTED)
+ {
+
+#if defined(DOS)
+ MDISABLE_INT(ioport);
+#endif
+
+ return(TRUE);
+ }
+
+ else
+
+ return(FALSE);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_isr
+ *
+ * Description: This is our entry point when an interrupt is generated
+ * by the card and the upper level driver passes it on to
+ * us.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+s32bits SccbMgr_isr(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+int SccbMgr_isr(USHORT pCurrCard)
+#else
+int SccbMgr_isr(ULONG pCurrCard)
+#endif
+#endif
+{
+ PSCCB currSCCB;
+ UCHAR thisCard,result,bm_status, bm_int_st;
+ USHORT hp_int;
+ UCHAR i, target;
+#if defined(DOS)
+ USHORT ioport;
+#else
+ ULONG ioport;
+#endif
+
+ mOS_Lock((PSCCBcard)pCurrCard);
+
+ thisCard = ((PSCCBcard)pCurrCard)->cardIndex;
+ ioport = ((PSCCBcard)pCurrCard)->ioPort;
+
+ MDISABLE_INT(ioport);
+
+#if defined(BUGBUG)
+ WR_HARPOON(ioport+hp_user_defined_D, RD_HARPOON(ioport+hp_int_status));
+#endif
+
+ if ((bm_int_st=RD_HARPOON(ioport+hp_int_status)) & EXT_STATUS_ON)
+ bm_status = RD_HARPOON(ioport+hp_ext_status) & (UCHAR)BAD_EXT_STATUS;
+ else
+ bm_status = 0;
+
+ WR_HARPOON(ioport+hp_int_mask, (INT_CMD_COMPL | SCSI_INTERRUPT));
+
+ mOS_UnLock((PSCCBcard)pCurrCard);
+
+ while ((hp_int = RDW_HARPOON((ioport+hp_intstat)) & default_intena) |
+ bm_status)
+ {
+
+ currSCCB = ((PSCCBcard)pCurrCard)->currentSCCB;
+
+#if defined(BUGBUG)
+ Debug_Load(thisCard,(UCHAR) 0XFF);
+ Debug_Load(thisCard,bm_int_st);
+
+ Debug_Load(thisCard,hp_int_0);
+ Debug_Load(thisCard,hp_int_1);
+#endif
+
+
+ if (hp_int & (FIFO | TIMEOUT | RESET | SCAM_SEL) || bm_status) {
+ result = SccbMgr_bad_isr(ioport,thisCard,((PSCCBcard)pCurrCard),hp_int);
+ WRW_HARPOON((ioport+hp_intstat), (FIFO | TIMEOUT | RESET | SCAM_SEL));
+ bm_status = 0;
+
+ if (result) {
+
+ mOS_Lock((PSCCBcard)pCurrCard);
+ MENABLE_INT(ioport);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return(result);
+ }
+ }
+
+
+ else if (hp_int & ICMD_COMP) {
+
+ if ( !(hp_int & BUS_FREE) ) {
+ /* Wait for the BusFree before starting a new command. We
+ must also check for being reselected since the BusFree
+ may not show up if another device reselects us in 1.5us or
+ less. SRR Wednesday, 3/8/1995.
+ */
+ while (!(RDW_HARPOON((ioport+hp_intstat)) & (BUS_FREE | RSEL))) ;
+ }
+
+ if (((PSCCBcard)pCurrCard)->globalFlags & F_HOST_XFER_ACT)
+
+ phaseChkFifo(ioport, thisCard);
+
+/* WRW_HARPOON((ioport+hp_intstat),
+ (BUS_FREE | ICMD_COMP | ITAR_DISC | XFER_CNT_0));
+ */
+
+ WRW_HARPOON((ioport+hp_intstat), CLR_ALL_INT_1);
+
+ autoCmdCmplt(ioport,thisCard);
+
+ }
+
+
+ else if (hp_int & ITAR_DISC)
+ {
+
+ if (((PSCCBcard)pCurrCard)->globalFlags & F_HOST_XFER_ACT) {
+
+ phaseChkFifo(ioport, thisCard);
+
+ }
+
+ if (RD_HARPOON(ioport+hp_gp_reg_1) == SMSAVE_DATA_PTR) {
+
+ WR_HARPOON(ioport+hp_gp_reg_1, 0x00);
+ currSCCB->Sccb_XferState |= F_NO_DATA_YET;
+
+ currSCCB->Sccb_savedATC = currSCCB->Sccb_ATC;
+ }
+
+ currSCCB->Sccb_scsistat = DISCONNECT_ST;
+ queueDisconnect(currSCCB,thisCard);
+
+ /* Wait for the BusFree before starting a new command. We
+ must also check for being reselected since the BusFree
+ may not show up if another device reselects us in 1.5us or
+ less. SRR Wednesday, 3/8/1995.
+ */
+ while (!(RDW_HARPOON((ioport+hp_intstat)) & (BUS_FREE | RSEL)) &&
+ !((RDW_HARPOON((ioport+hp_intstat)) & PHASE) &&
+ RD_HARPOON((ioport+hp_scsisig)) ==
+ (SCSI_BSY | SCSI_REQ | SCSI_CD | SCSI_MSG | SCSI_IOBIT))) ;
+
+ /*
+ The additional loop exit condition above detects a timing problem
+ with the revision D/E harpoon chips. The caller should reset the
+ host adapter to recover when 0xFE is returned.
+ */
+ if (!(RDW_HARPOON((ioport+hp_intstat)) & (BUS_FREE | RSEL)))
+ {
+ mOS_Lock((PSCCBcard)pCurrCard);
+ MENABLE_INT(ioport);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+ return 0xFE;
+ }
+
+ WRW_HARPOON((ioport+hp_intstat), (BUS_FREE | ITAR_DISC));
+
+
+ ((PSCCBcard)pCurrCard)->globalFlags |= F_NEW_SCCB_CMD;
+
+ }
+
+
+ else if (hp_int & RSEL) {
+
+ WRW_HARPOON((ioport+hp_intstat), (PROG_HLT | RSEL | PHASE | BUS_FREE));
+
+ if (RDW_HARPOON((ioport+hp_intstat)) & ITAR_DISC)
+ {
+ if (((PSCCBcard)pCurrCard)->globalFlags & F_HOST_XFER_ACT)
+ {
+ phaseChkFifo(ioport, thisCard);
+ }
+
+ if (RD_HARPOON(ioport+hp_gp_reg_1) == SMSAVE_DATA_PTR)
+ {
+ WR_HARPOON(ioport+hp_gp_reg_1, 0x00);
+ currSCCB->Sccb_XferState |= F_NO_DATA_YET;
+ currSCCB->Sccb_savedATC = currSCCB->Sccb_ATC;
+ }
+
+ WRW_HARPOON((ioport+hp_intstat), (BUS_FREE | ITAR_DISC));
+ currSCCB->Sccb_scsistat = DISCONNECT_ST;
+ queueDisconnect(currSCCB,thisCard);
+ }
+
+ sres(ioport,thisCard,((PSCCBcard)pCurrCard));
+ phaseDecode(ioport,thisCard);
+
+ }
+
+
+ else if ((hp_int & IDO_STRT) && (!(hp_int & BUS_FREE)))
+ {
+
+ WRW_HARPOON((ioport+hp_intstat), (IDO_STRT | XFER_CNT_0));
+ phaseDecode(ioport,thisCard);
+
+ }
+
+
+ else if ( (hp_int & IUNKWN) || (hp_int & PROG_HLT) )
+ {
+ WRW_HARPOON((ioport+hp_intstat), (PHASE | IUNKWN | PROG_HLT));
+ if ((RD_HARPOON(ioport+hp_prgmcnt_0) & (UCHAR)0x3f)< (UCHAR)SELCHK)
+ {
+ phaseDecode(ioport,thisCard);
+ }
+ else
+ {
+ /* Harpoon problem some SCSI target device respond to selection
+ with short BUSY pulse (<400ns) this will make the Harpoon is not able
+ to latch the correct Target ID into reg. x53.
+ The work around require to correct this reg. But when write to this
+ reg. (0x53) also increment the FIFO write addr reg (0x6f), thus we
+ need to read this reg first then restore it later. After update to 0x53 */
+
+ i = (UCHAR)(RD_HARPOON(ioport+hp_fifowrite));
+ target = (UCHAR)(RD_HARPOON(ioport+hp_gp_reg_3));
+ WR_HARPOON(ioport+hp_xfer_pad, (UCHAR) ID_UNLOCK);
+ WR_HARPOON(ioport+hp_select_id, (UCHAR)(target | target<<4));
+ WR_HARPOON(ioport+hp_xfer_pad, (UCHAR) 0x00);
+ WR_HARPOON(ioport+hp_fifowrite, i);
+ WR_HARPOON(ioport+hp_autostart_3, (AUTO_IMMED+TAG_STRT));
+ }
+ }
+
+ else if (hp_int & XFER_CNT_0) {
+
+ WRW_HARPOON((ioport+hp_intstat), XFER_CNT_0);
+
+ schkdd(ioport,thisCard);
+
+ }
+
+
+ else if (hp_int & BUS_FREE) {
+
+ WRW_HARPOON((ioport+hp_intstat), BUS_FREE);
+
+ if (((PSCCBcard)pCurrCard)->globalFlags & F_HOST_XFER_ACT) {
+
+ hostDataXferAbort(ioport,thisCard,currSCCB);
+ }
+
+ phaseBusFree(ioport,thisCard);
+ }
+
+
+ else if (hp_int & ITICKLE) {
+
+ WRW_HARPOON((ioport+hp_intstat), ITICKLE);
+ ((PSCCBcard)pCurrCard)->globalFlags |= F_NEW_SCCB_CMD;
+ }
+
+
+
+ if (((PSCCBcard)pCurrCard)->globalFlags & F_NEW_SCCB_CMD) {
+
+
+ ((PSCCBcard)pCurrCard)->globalFlags &= ~F_NEW_SCCB_CMD;
+
+
+ if (((PSCCBcard)pCurrCard)->currentSCCB == NULL) {
+
+ queueSearchSelect(((PSCCBcard)pCurrCard),thisCard);
+ }
+
+ if (((PSCCBcard)pCurrCard)->currentSCCB != NULL) {
+ ((PSCCBcard)pCurrCard)->globalFlags &= ~F_NEW_SCCB_CMD;
+ ssel(ioport,thisCard);
+ }
+
+ break;
+
+ }
+
+ } /*end while */
+
+ mOS_Lock((PSCCBcard)pCurrCard);
+ MENABLE_INT(ioport);
+ mOS_UnLock((PSCCBcard)pCurrCard);
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Sccb_bad_isr
+ *
+ * Description: Some type of interrupt has occurred which is slightly
+ * out of the ordinary. We will now decode it fully, in
+ * this routine. This is broken up in an attempt to save
+ * processing time.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+UCHAR SccbMgr_bad_isr(USHORT p_port, UCHAR p_card, PSCCBcard pCurrCard, USHORT p_int)
+#else
+UCHAR SccbMgr_bad_isr(ULONG p_port, UCHAR p_card, PSCCBcard pCurrCard, USHORT p_int)
+#endif
+{
+#if defined(HARP_REVX)
+ ULONG timer;
+#endif
+UCHAR temp, ScamFlg;
+PSCCBMgr_tar_info currTar_Info;
+PNVRamInfo pCurrNvRam;
+
+
+ if (RD_HARPOON(p_port+hp_ext_status) &
+ (BM_FORCE_OFF | PCI_DEV_TMOUT | BM_PARITY_ERR | PIO_OVERRUN) )
+ {
+
+ if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
+ {
+
+ hostDataXferAbort(p_port,p_card, pCurrCard->currentSCCB);
+ }
+
+ if (RD_HARPOON(p_port+hp_pci_stat_cfg) & REC_MASTER_ABORT)
+
+ {
+ WR_HARPOON(p_port+hp_pci_stat_cfg,
+ (RD_HARPOON(p_port+hp_pci_stat_cfg) & ~REC_MASTER_ABORT));
+
+ WR_HARPOON(p_port+hp_host_blk_cnt, 0x00);
+
+ }
+
+ if (pCurrCard->currentSCCB != NULL)
+ {
+
+ if (!pCurrCard->currentSCCB->HostStatus)
+ pCurrCard->currentSCCB->HostStatus = SCCB_BM_ERR;
+
+ sxfrp(p_port,p_card);
+
+ temp = (UCHAR)(RD_HARPOON(p_port+hp_ee_ctrl) &
+ (EXT_ARB_ACK | SCSI_TERM_ENA_H));
+ WR_HARPOON(p_port+hp_ee_ctrl, ((UCHAR)temp | SEE_MS | SEE_CS));
+ WR_HARPOON(p_port+hp_ee_ctrl, temp);
+
+ if (!(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RESET)))
+ {
+ phaseDecode(p_port,p_card);
+ }
+ }
+ }
+
+
+ else if (p_int & RESET)
+ {
+
+ WR_HARPOON(p_port+hp_clkctrl_0, CLKCTRL_DEFAULT);
+ WR_HARPOON(p_port+hp_sys_ctrl, 0x00);
+ if (pCurrCard->currentSCCB != NULL) {
+
+ if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
+
+ hostDataXferAbort(p_port,p_card, pCurrCard->currentSCCB);
+ }
+
+
+ DISABLE_AUTO(p_port);
+
+ sresb(p_port,p_card);
+
+ while(RD_HARPOON(p_port+hp_scsictrl_0) & SCSI_RST) {}
+
+ pCurrNvRam = pCurrCard->pNvRamInfo;
+ if(pCurrNvRam){
+ ScamFlg = pCurrNvRam->niScamConf;
+ }
+ else{
+ ScamFlg = (UCHAR) utilEERead(p_port, SCAM_CONFIG/2);
+ }
+
+ XbowInit(p_port, ScamFlg);
+
+ scini(p_card, pCurrCard->ourId, 0);
+
+ return(0xFF);
+ }
+
+
+ else if (p_int & FIFO) {
+
+ WRW_HARPOON((p_port+hp_intstat), FIFO);
+
+#if defined(HARP_REVX)
+
+ for (timer=0x00FFFFFFL; timer != 0x00000000L; timer--) {
+
+ if (RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY)
+ break;
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & BUS_FREE)
+ break;
+ }
+
+
+ if ( (RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY) &&
+ (RD_HARPOON(p_port+hp_fiforead) !=
+ RD_HARPOON(p_port+hp_fifowrite)) &&
+ (RD_HARPOON(p_port+hp_xfercnt_0))
+ )
+
+ WR_HARPOON((p_port+hp_xferstat), 0x01);
+
+/* else
+ */
+/* sxfrp(p_port,p_card);
+ */
+#else
+ if (pCurrCard->currentSCCB != NULL)
+ sxfrp(p_port,p_card);
+#endif
+ }
+
+ else if (p_int & TIMEOUT)
+ {
+
+ DISABLE_AUTO(p_port);
+
+ WRW_HARPOON((p_port+hp_intstat),
+ (PROG_HLT | TIMEOUT | SEL |BUS_FREE | PHASE | IUNKWN));
+
+ pCurrCard->currentSCCB->HostStatus = SCCB_SELECTION_TIMEOUT;
+
+
+ currTar_Info = &sccbMgrTbl[p_card][pCurrCard->currentSCCB->TargID];
+ if((pCurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ currTar_Info->TarLUNBusy[pCurrCard->currentSCCB->Lun] = FALSE;
+ else
+ currTar_Info->TarLUNBusy[0] = FALSE;
+
+
+ if (currTar_Info->TarEEValue & EE_SYNC_MASK)
+ {
+ currTar_Info->TarSyncCtrl = 0;
+ currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
+ }
+
+ if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
+ {
+ currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
+ }
+
+ sssyncv(p_port, pCurrCard->currentSCCB->TargID, NARROW_SCSI,currTar_Info);
+
+ queueCmdComplete(pCurrCard, pCurrCard->currentSCCB, p_card);
+
+ }
+
+#if defined(SCAM_LEV_2)
+
+ else if (p_int & SCAM_SEL)
+ {
+
+ scarb(p_port,LEVEL2_TAR);
+ scsel(p_port);
+ scasid(p_card, p_port);
+
+ scbusf(p_port);
+
+ WRW_HARPOON((p_port+hp_intstat), SCAM_SEL);
+ }
+#endif
+
+ return(0x00);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_scsi_reset
+ *
+ * Description: A SCSI bus reset will be generated and all outstanding
+ * Sccbs will be returned via the callback.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+void SccbMgr_scsi_reset(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+void SccbMgr_scsi_reset(USHORT pCurrCard)
+#else
+void SccbMgr_scsi_reset(ULONG pCurrCard)
+#endif
+#endif
+{
+ UCHAR thisCard;
+
+ thisCard = ((PSCCBcard)pCurrCard)->cardIndex;
+
+ mOS_Lock((PSCCBcard)pCurrCard);
+
+ if (((PSCCBcard) pCurrCard)->globalFlags & F_GREEN_PC)
+ {
+ WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_clkctrl_0, CLKCTRL_DEFAULT);
+ WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_sys_ctrl, 0x00);
+ }
+
+ sresb(((PSCCBcard)pCurrCard)->ioPort,thisCard);
+
+ if (RD_HARPOON(((PSCCBcard)pCurrCard)->ioPort+hp_ext_status) & BM_CMD_BUSY)
+ {
+ WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_page_ctrl,
+ (RD_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_page_ctrl)
+ & ~SCATTER_EN));
+
+ WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_sg_addr,0x00);
+
+ ((PSCCBcard) pCurrCard)->globalFlags &= ~F_HOST_XFER_ACT;
+ busMstrTimeOut(((PSCCBcard) pCurrCard)->ioPort);
+
+ WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_int_mask,
+ (INT_CMD_COMPL | SCSI_INTERRUPT));
+ }
+
+/*
+ if (utilEERead(((PSCCBcard)pCurrCard)->ioPort, (SCAM_CONFIG/2))
+ & SCAM_ENABLED)
+*/
+ scini(thisCard, ((PSCCBcard)pCurrCard)->ourId, 0);
+
+#if (FW_TYPE==_UCB_MGR_)
+ ((PSCCBcard)pCurrCard)->cardInfo->ai_AEN_routine(0x01,pCurrCard,0,0,0,0);
+#endif
+
+ mOS_UnLock((PSCCBcard)pCurrCard);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_timer_expired
+ *
+ * Description: This function allow me to kill my own job that has not
+ * yet completed, and has cause a timeout to occur. This
+ * timeout has caused the upper level driver to call this
+ * function.
+ *
+ *---------------------------------------------------------------------*/
+
+#if (FW_TYPE==_UCB_MGR_)
+void SccbMgr_timer_expired(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+void SccbMgr_timer_expired(USHORT pCurrCard)
+#else
+void SccbMgr_timer_expired(ULONG pCurrCard)
+#endif
+#endif
+{
+}
+
+#if defined(DOS)
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_status
+ *
+ * Description: This function returns the number of outstanding SCCB's.
+ * This is specific to the DOS enviroment, which needs this
+ * to help them keep protected and real mode commands staight.
+ *
+ *---------------------------------------------------------------------*/
+
+USHORT SccbMgr_status(USHORT pCurrCard)
+{
+ return(BL_Card[pCurrCard].cmdCounter);
+}
+#endif
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgrTableInit
+ *
+ * Description: Initialize all Sccb manager data structures.
+ *
+ *---------------------------------------------------------------------*/
+
+void SccbMgrTableInitAll()
+{
+ UCHAR thisCard;
+
+ for (thisCard = 0; thisCard < MAX_CARDS; thisCard++)
+ {
+ SccbMgrTableInitCard(&BL_Card[thisCard],thisCard);
+
+ BL_Card[thisCard].ioPort = 0x00;
+ BL_Card[thisCard].cardInfo = NULL;
+ BL_Card[thisCard].cardIndex = 0xFF;
+ BL_Card[thisCard].ourId = 0x00;
+ BL_Card[thisCard].pNvRamInfo = NULL;
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgrTableInit
+ *
+ * Description: Initialize all Sccb manager data structures.
+ *
+ *---------------------------------------------------------------------*/
+
+void SccbMgrTableInitCard(PSCCBcard pCurrCard, UCHAR p_card)
+{
+ UCHAR scsiID, qtag;
+
+ for (qtag = 0; qtag < QUEUE_DEPTH; qtag++)
+ {
+ BL_Card[p_card].discQ_Tbl[qtag] = NULL;
+ }
+
+ for (scsiID = 0; scsiID < MAX_SCSI_TAR; scsiID++)
+ {
+ sccbMgrTbl[p_card][scsiID].TarStatus = 0;
+ sccbMgrTbl[p_card][scsiID].TarEEValue = 0;
+ SccbMgrTableInitTarget(p_card, scsiID);
+ }
+
+ pCurrCard->scanIndex = 0x00;
+ pCurrCard->currentSCCB = NULL;
+ pCurrCard->globalFlags = 0x00;
+ pCurrCard->cmdCounter = 0x00;
+ pCurrCard->tagQ_Lst = 0x01;
+ pCurrCard->discQCount = 0;
+
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgrTableInit
+ *
+ * Description: Initialize all Sccb manager data structures.
+ *
+ *---------------------------------------------------------------------*/
+
+void SccbMgrTableInitTarget(UCHAR p_card, UCHAR target)
+{
+
+ UCHAR lun, qtag;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currTar_Info = &sccbMgrTbl[p_card][target];
+
+ currTar_Info->TarSelQ_Cnt = 0;
+ currTar_Info->TarSyncCtrl = 0;
+
+ currTar_Info->TarSelQ_Head = NULL;
+ currTar_Info->TarSelQ_Tail = NULL;
+ currTar_Info->TarTagQ_Cnt = 0;
+ currTar_Info->TarLUN_CA = FALSE;
+
+
+ for (lun = 0; lun < MAX_LUN; lun++)
+ {
+ currTar_Info->TarLUNBusy[lun] = FALSE;
+ currTar_Info->LunDiscQ_Idx[lun] = 0;
+ }
+
+ for (qtag = 0; qtag < QUEUE_DEPTH; qtag++)
+ {
+ if(BL_Card[p_card].discQ_Tbl[qtag] != NULL)
+ {
+ if(BL_Card[p_card].discQ_Tbl[qtag]->TargID == target)
+ {
+ BL_Card[p_card].discQ_Tbl[qtag] = NULL;
+ BL_Card[p_card].discQCount--;
+ }
+ }
+ }
+}
+
+#if defined(BUGBUG)
+
+/*****************************************************************
+ * Save the current byte in the debug array
+ *****************************************************************/
+
+
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data)
+{
+ debug_int[p_card][debug_index[p_card]] = p_bug_data;
+ debug_index[p_card]++;
+
+ if (debug_index[p_card] == debug_size)
+
+ debug_index[p_card] = 0;
+}
+
+#endif
+#ident "$Id: FlashPoint.c,v 1.1 1999/04/26 05:53:56 tb Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: sccb_dat.c $
+ *
+ * Description: Functions relating to handling of the SCCB interface
+ * between the device driver and the HARPOON.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <harpoon.h>*/
+
+/*
+** IMPORTANT NOTE!!!
+**
+** You MUST preassign all data to a valid value or zero. This is
+** required due to the MS compiler bug under OS/2 and Solaris Real-Mode
+** driver environment.
+*/
+
+
+SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR] = { { { 0 } } };
+SCCBCARD BL_Card[MAX_CARDS] = { { 0 } };
+SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR] = { { { 0 } } };
+NVRAMINFO nvRamInfo[MAX_MB_CARDS] = { { 0 } };
+
+
+#if defined(OS2)
+void (far *s_PhaseTbl[8]) (ULONG, UCHAR) = { 0 };
+UCHAR temp_id_string[ID_STRING_LENGTH] = { 0 };
+#elif defined(SOLARIS_REAL_MODE) || defined(__STDC__)
+void (*s_PhaseTbl[8]) (ULONG, UCHAR) = { 0 };
+#else
+void (*s_PhaseTbl[8]) ();
+#endif
+
+#if defined(DOS)
+UCHAR first_time = 0;
+#endif
+
+UCHAR mbCards = 0;
+UCHAR scamHAString[] = {0x63, 0x07, 'B', 'U', 'S', 'L', 'O', 'G', 'I', 'C', \
+ ' ', 'B', 'T', '-', '9', '3', '0', \
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, \
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
+
+USHORT default_intena = 0;
+
+#if defined(BUGBUG)
+UCHAR debug_int[MAX_CARDS][debug_size] = { 0 };
+UCHAR debug_index[MAX_CARDS] = { 0 };
+UCHAR reserved_1[3] = { 0 };
+#endif
+#ident "$Id: FlashPoint.c,v 1.1 1999/04/26 05:53:56 tb Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: scsi.c $
+ *
+ * Description: Functions for handling SCSI bus functions such as
+ * selection/reselection, sync negotiation, message-in
+ * decoding.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <eeprom.h>*/
+/*#include <harpoon.h>*/
+
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+#if defined(BUGBUG)
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sfetm
+ *
+ * Description: Read in a message byte from the SCSI bus, and check
+ * for a parity error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR sfm(USHORT port, PSCCB pCurrSCCB)
+#else
+UCHAR sfm(ULONG port, PSCCB pCurrSCCB)
+#endif
+{
+ UCHAR message;
+ USHORT TimeOutLoop;
+
+ TimeOutLoop = 0;
+ while( (!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) &&
+ (TimeOutLoop++ < 20000) ){}
+
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+ message = RD_HARPOON(port+hp_scsidata_0);
+
+ WR_HARPOON(port+hp_scsisig, SCSI_ACK + S_MSGI_PH);
+
+
+ if (TimeOutLoop > 20000)
+ message = 0x00; /* force message byte = 0 if Time Out on Req */
+
+ if ((RDW_HARPOON((port+hp_intstat)) & PARITY) &&
+ (RD_HARPOON(port+hp_addstat) & SCSI_PAR_ERR))
+ {
+ WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+ WR_HARPOON(port+hp_xferstat, 0);
+ WR_HARPOON(port+hp_fiforead, 0);
+ WR_HARPOON(port+hp_fifowrite, 0);
+ if (pCurrSCCB != NULL)
+ {
+ pCurrSCCB->Sccb_scsimsg = SMPARITY;
+ }
+ message = 0x00;
+ do
+ {
+ ACCEPT_MSG_ATN(port);
+ TimeOutLoop = 0;
+ while( (!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) &&
+ (TimeOutLoop++ < 20000) ){}
+ if (TimeOutLoop > 20000)
+ {
+ WRW_HARPOON((port+hp_intstat), PARITY);
+ return(message);
+ }
+ if ((RD_HARPOON(port+hp_scsisig) & S_SCSI_PHZ) != S_MSGI_PH)
+ {
+ WRW_HARPOON((port+hp_intstat), PARITY);
+ return(message);
+ }
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+ RD_HARPOON(port+hp_scsidata_0);
+
+ WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+
+ }while(1);
+
+ }
+ WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+ WR_HARPOON(port+hp_xferstat, 0);
+ WR_HARPOON(port+hp_fiforead, 0);
+ WR_HARPOON(port+hp_fifowrite, 0);
+ return(message);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: ssel
+ *
+ * Description: Load up automation and select target device.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void ssel(USHORT port, UCHAR p_card)
+#else
+void ssel(ULONG port, UCHAR p_card)
+#endif
+{
+
+#if defined(DOS)
+ UCHAR auto_loaded, i, target, *theCCB;
+#elif defined(OS2)
+ UCHAR auto_loaded, i, target;
+ UCHAR far *theCCB;
+#else
+ UCHAR auto_loaded, i, target, *theCCB;
+#endif
+
+#if defined(DOS)
+ USHORT cdb_reg;
+#else
+ ULONG cdb_reg;
+#endif
+ PSCCBcard CurrCard;
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+ UCHAR lastTag, lun;
+
+ CurrCard = &BL_Card[p_card];
+ currSCCB = CurrCard->currentSCCB;
+ target = currSCCB->TargID;
+ currTar_Info = &sccbMgrTbl[p_card][target];
+ lastTag = CurrCard->tagQ_Lst;
+
+ ARAM_ACCESS(port);
+
+
+ if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_REJECT)
+ currSCCB->ControlByte &= ~F_USE_CMD_Q;
+
+ if(((CurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+
+ lun = currSCCB->Lun;
+ else
+ lun = 0;
+
+
+#if defined(DOS)
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+
+#else
+
+ if (CurrCard->globalFlags & F_TAG_STARTED)
+ {
+ if (!(currSCCB->ControlByte & F_USE_CMD_Q))
+ {
+ if ((currTar_Info->TarLUN_CA == FALSE)
+ && ((currTar_Info->TarStatus & TAR_TAG_Q_MASK)
+ == TAG_Q_TRYING))
+ {
+
+ if (currTar_Info->TarTagQ_Cnt !=0)
+ {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ queueSelectFail(CurrCard,p_card);
+ SGRAM_ACCESS(port);
+ return;
+ }
+
+ else {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ }
+
+ } /*End non-tagged */
+
+ else {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ }
+
+ } /*!Use cmd Q Tagged */
+
+ else {
+ if (currTar_Info->TarLUN_CA == TRUE)
+ {
+ queueSelectFail(CurrCard,p_card);
+ SGRAM_ACCESS(port);
+ return;
+ }
+
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+
+ } /*else use cmd Q tagged */
+
+ } /*if glob tagged started */
+
+ else {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ }
+
+#endif /* DOS */
+
+
+
+ if((((CurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ || (!(currSCCB->ControlByte & F_USE_CMD_Q))))
+ {
+ if(CurrCard->discQCount >= QUEUE_DEPTH)
+ {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ queueSelectFail(CurrCard,p_card);
+ SGRAM_ACCESS(port);
+ return;
+ }
+ for (i = 1; i < QUEUE_DEPTH; i++)
+ {
+ if (++lastTag >= QUEUE_DEPTH) lastTag = 1;
+ if (CurrCard->discQ_Tbl[lastTag] == NULL)
+ {
+ CurrCard->tagQ_Lst = lastTag;
+ currTar_Info->LunDiscQ_Idx[lun] = lastTag;
+ CurrCard->discQ_Tbl[lastTag] = currSCCB;
+ CurrCard->discQCount++;
+ break;
+ }
+ }
+ if(i == QUEUE_DEPTH)
+ {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ queueSelectFail(CurrCard,p_card);
+ SGRAM_ACCESS(port);
+ return;
+ }
+ }
+
+
+
+ auto_loaded = FALSE;
+
+ WR_HARPOON(port+hp_select_id, target);
+ WR_HARPOON(port+hp_gp_reg_3, target); /* Use by new automation logic */
+
+ if (currSCCB->OperationCode == RESET_COMMAND) {
+ WRW_HARPOON((port+ID_MSG_STRT), (MPM_OP+AMSG_OUT+
+ (currSCCB->Sccb_idmsg & ~DISC_PRIV)));
+
+ WRW_HARPOON((port+ID_MSG_STRT+2),BRH_OP+ALWAYS+NP);
+
+ currSCCB->Sccb_scsimsg = SMDEV_RESET;
+
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+ auto_loaded = TRUE;
+ currSCCB->Sccb_scsistat = SELECT_BDR_ST;
+
+ if (currTar_Info->TarEEValue & EE_SYNC_MASK)
+ {
+ currTar_Info->TarSyncCtrl = 0;
+ currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
+ }
+
+#if defined(WIDE_SCSI)
+
+ if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
+ {
+ currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
+ }
+#endif
+
+ sssyncv(port, target, NARROW_SCSI,currTar_Info);
+ SccbMgrTableInitTarget(p_card, target);
+
+ }
+
+ else if(currSCCB->Sccb_scsistat == ABORT_ST)
+ {
+ WRW_HARPOON((port+ID_MSG_STRT), (MPM_OP+AMSG_OUT+
+ (currSCCB->Sccb_idmsg & ~DISC_PRIV)));
+
+ WRW_HARPOON((port+ID_MSG_STRT+2),BRH_OP+ALWAYS+CMDPZ);
+
+ WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+
+ (((UCHAR)(currSCCB->ControlByte & TAG_TYPE_MASK)
+ >> 6) | (UCHAR)0x20)));
+ WRW_HARPOON((port+SYNC_MSGS+2),
+ (MPM_OP+AMSG_OUT+currSCCB->Sccb_tag));
+ WRW_HARPOON((port+SYNC_MSGS+4), (BRH_OP+ALWAYS+NP ));
+
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+ auto_loaded = TRUE;
+
+ }
+
+#if defined(WIDE_SCSI)
+
+
+ else if (!(currTar_Info->TarStatus & WIDE_NEGOCIATED)) {
+ auto_loaded = siwidn(port,p_card);
+ currSCCB->Sccb_scsistat = SELECT_WN_ST;
+ }
+
+#endif
+
+
+ else if (!((currTar_Info->TarStatus & TAR_SYNC_MASK)
+ == SYNC_SUPPORTED)) {
+ auto_loaded = sisyncn(port,p_card, FALSE);
+ currSCCB->Sccb_scsistat = SELECT_SN_ST;
+ }
+
+
+ if (!auto_loaded)
+ {
+
+#if !defined(DOS)
+ if (currSCCB->ControlByte & F_USE_CMD_Q)
+ {
+
+ CurrCard->globalFlags |= F_TAG_STARTED;
+
+ if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK)
+ == TAG_Q_REJECT)
+ {
+ currSCCB->ControlByte &= ~F_USE_CMD_Q;
+
+ /* Fix up the start instruction with a jump to
+ Non-Tag-CMD handling */
+ WRW_HARPOON((port+ID_MSG_STRT),BRH_OP+ALWAYS+NTCMD);
+
+ WRW_HARPOON((port+NON_TAG_ID_MSG),
+ (MPM_OP+AMSG_OUT+currSCCB->Sccb_idmsg));
+
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+
+ /* Setup our STATE so we know what happend when
+ the wheels fall off. */
+ currSCCB->Sccb_scsistat = SELECT_ST;
+
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ }
+
+ else
+ {
+ WRW_HARPOON((port+ID_MSG_STRT), (MPM_OP+AMSG_OUT+currSCCB->Sccb_idmsg));
+
+ WRW_HARPOON((port+ID_MSG_STRT+2), (MPM_OP+AMSG_OUT+
+ (((UCHAR)(currSCCB->ControlByte & TAG_TYPE_MASK)
+ >> 6) | (UCHAR)0x20)));
+
+ for (i = 1; i < QUEUE_DEPTH; i++)
+ {
+ if (++lastTag >= QUEUE_DEPTH) lastTag = 1;
+ if (CurrCard->discQ_Tbl[lastTag] == NULL)
+ {
+ WRW_HARPOON((port+ID_MSG_STRT+6),
+ (MPM_OP+AMSG_OUT+lastTag));
+ CurrCard->tagQ_Lst = lastTag;
+ currSCCB->Sccb_tag = lastTag;
+ CurrCard->discQ_Tbl[lastTag] = currSCCB;
+ CurrCard->discQCount++;
+ break;
+ }
+ }
+
+
+ if ( i == QUEUE_DEPTH )
+ {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ queueSelectFail(CurrCard,p_card);
+ SGRAM_ACCESS(port);
+ return;
+ }
+
+ currSCCB->Sccb_scsistat = SELECT_Q_ST;
+
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+ }
+ }
+
+ else
+ {
+#endif /* !DOS */
+
+ WRW_HARPOON((port+ID_MSG_STRT),BRH_OP+ALWAYS+NTCMD);
+
+ WRW_HARPOON((port+NON_TAG_ID_MSG),
+ (MPM_OP+AMSG_OUT+currSCCB->Sccb_idmsg));
+
+ currSCCB->Sccb_scsistat = SELECT_ST;
+
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+#if !defined(DOS)
+ }
+#endif
+
+
+#if defined(OS2)
+ theCCB = (UCHAR far *)&currSCCB->Cdb[0];
+#else
+ theCCB = (UCHAR *)&currSCCB->Cdb[0];
+#endif
+
+ cdb_reg = port + CMD_STRT;
+
+ for (i=0; i < currSCCB->CdbLength; i++)
+ {
+ WRW_HARPOON(cdb_reg, (MPM_OP + ACOMMAND + *theCCB));
+ cdb_reg +=2;
+ theCCB++;
+ }
+
+ if (currSCCB->CdbLength != TWELVE_BYTE_CMD)
+ WRW_HARPOON(cdb_reg, (BRH_OP+ALWAYS+ NP));
+
+ } /* auto_loaded */
+
+#if defined(WIDE_SCSI)
+ WRW_HARPOON((port+hp_fiforead), (USHORT) 0x00);
+ WR_HARPOON(port+hp_xferstat, 0x00);
+#endif
+
+ WRW_HARPOON((port+hp_intstat), (PROG_HLT | TIMEOUT | SEL | BUS_FREE));
+
+ WR_HARPOON(port+hp_portctrl_0,(SCSI_PORT));
+
+
+ if (!(currSCCB->Sccb_MGRFlags & F_DEV_SELECTED))
+ {
+ WR_HARPOON(port+hp_scsictrl_0, (SEL_TAR | ENA_ATN | ENA_RESEL | ENA_SCAM_SEL));
+ }
+ else
+ {
+
+/* auto_loaded = (RD_HARPOON(port+hp_autostart_3) & (UCHAR)0x1F);
+ auto_loaded |= AUTO_IMMED; */
+ auto_loaded = AUTO_IMMED;
+
+ DISABLE_AUTO(port);
+
+ WR_HARPOON(port+hp_autostart_3, auto_loaded);
+ }
+
+ SGRAM_ACCESS(port);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sres
+ *
+ * Description: Hookup the correct CCB and handle the incoming messages.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void sres(USHORT port, UCHAR p_card, PSCCBcard pCurrCard)
+#else
+void sres(ULONG port, UCHAR p_card, PSCCBcard pCurrCard)
+#endif
+{
+
+#if defined(V302)
+#ifdef DOS
+ UCHAR our_target,message, msgRetryCount;
+ extern UCHAR lun, tag;
+#else
+ UCHAR our_target,message,lun,tag, msgRetryCount;
+#endif
+
+#else /* V302 */
+ UCHAR our_target, message, lun = 0, tag, msgRetryCount;
+#endif /* V302 */
+
+
+ PSCCBMgr_tar_info currTar_Info;
+ PSCCB currSCCB;
+
+
+
+
+ if(pCurrCard->currentSCCB != NULL)
+ {
+ currTar_Info = &sccbMgrTbl[p_card][pCurrCard->currentSCCB->TargID];
+ DISABLE_AUTO(port);
+
+
+ WR_HARPOON((port+hp_scsictrl_0),(ENA_RESEL | ENA_SCAM_SEL));
+
+
+ currSCCB = pCurrCard->currentSCCB;
+ if(currSCCB->Sccb_scsistat == SELECT_WN_ST)
+ {
+ currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
+ currSCCB->Sccb_scsistat = BUS_FREE_ST;
+ }
+ if(currSCCB->Sccb_scsistat == SELECT_SN_ST)
+ {
+ currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
+ currSCCB->Sccb_scsistat = BUS_FREE_ST;
+ }
+ if(((pCurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ currTar_Info->TarLUNBusy[currSCCB->Lun] = FALSE;
+ if(currSCCB->Sccb_scsistat != ABORT_ST)
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[currSCCB->Lun]]
+ = NULL;
+ }
+ }
+ else
+ {
+ currTar_Info->TarLUNBusy[0] = FALSE;
+ if(currSCCB->Sccb_tag)
+ {
+ if(currSCCB->Sccb_scsistat != ABORT_ST)
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ }
+ }else
+ {
+ if(currSCCB->Sccb_scsistat != ABORT_ST)
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+ }
+
+ queueSelectFail(&BL_Card[p_card],p_card);
+ }
+
+#if defined(WIDE_SCSI)
+ WRW_HARPOON((port+hp_fiforead), (USHORT) 0x00);
+#endif
+
+
+ our_target = (UCHAR)(RD_HARPOON(port+hp_select_id) >> 4);
+ currTar_Info = &sccbMgrTbl[p_card][our_target];
+
+
+ msgRetryCount = 0;
+ do
+ {
+
+#if defined(V302)
+
+ message = GetTarLun(port, p_card, our_target, pCurrCard, &tag, &lun);
+
+#else /* V302 */
+
+ currTar_Info = &sccbMgrTbl[p_card][our_target];
+ tag = 0;
+
+
+ while(!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ))
+ {
+ if (! (RD_HARPOON(port+hp_scsisig) & SCSI_BSY))
+ {
+
+ WRW_HARPOON((port+hp_intstat), PHASE);
+ return;
+ }
+ }
+
+ WRW_HARPOON((port+hp_intstat), PHASE);
+ if ((RD_HARPOON(port+hp_scsisig) & S_SCSI_PHZ) == S_MSGI_PH)
+ {
+
+ message = sfm(port,pCurrCard->currentSCCB);
+ if (message)
+ {
+
+ if (message <= (0x80 | LUN_MASK))
+ {
+ lun = message & (UCHAR)LUN_MASK;
+
+#if !defined(DOS)
+ if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_TRYING)
+ {
+ if (currTar_Info->TarTagQ_Cnt != 0)
+ {
+
+ if (!(currTar_Info->TarLUN_CA))
+ {
+ ACCEPT_MSG(port); /*Release the ACK for ID msg. */
+
+
+ message = sfm(port,pCurrCard->currentSCCB);
+ if (message)
+ {
+ ACCEPT_MSG(port);
+ }
+
+ else
+ message = FALSE;
+
+ if(message != FALSE)
+ {
+ tag = sfm(port,pCurrCard->currentSCCB);
+
+ if (!(tag))
+ message = FALSE;
+ }
+
+ } /*C.A. exists! */
+
+ } /*End Q cnt != 0 */
+
+ } /*End Tag cmds supported! */
+#endif /* !DOS */
+
+ } /*End valid ID message. */
+
+ else
+ {
+
+ ACCEPT_MSG_ATN(port);
+ }
+
+ } /* End good id message. */
+
+ else
+ {
+
+ message = FALSE;
+ }
+ }
+ else
+ {
+ ACCEPT_MSG_ATN(port);
+
+ while (!(RDW_HARPOON((port+hp_intstat)) & (PHASE | RESET)) &&
+ !(RD_HARPOON(port+hp_scsisig) & SCSI_REQ) &&
+ (RD_HARPOON(port+hp_scsisig) & SCSI_BSY)) ;
+
+ return;
+ }
+
+#endif /* V302 */
+
+ if(message == FALSE)
+ {
+ msgRetryCount++;
+ if(msgRetryCount == 1)
+ {
+ SendMsg(port, SMPARITY);
+ }
+ else
+ {
+ SendMsg(port, SMDEV_RESET);
+
+ sssyncv(port, our_target, NARROW_SCSI,currTar_Info);
+
+ if (sccbMgrTbl[p_card][our_target].TarEEValue & EE_SYNC_MASK)
+ {
+
+ sccbMgrTbl[p_card][our_target].TarStatus &= ~TAR_SYNC_MASK;
+
+ }
+
+ if (sccbMgrTbl[p_card][our_target].TarEEValue & EE_WIDE_SCSI)
+ {
+
+ sccbMgrTbl[p_card][our_target].TarStatus &= ~TAR_WIDE_MASK;
+ }
+
+
+ queueFlushTargSccb(p_card, our_target, SCCB_COMPLETE);
+ SccbMgrTableInitTarget(p_card,our_target);
+ return;
+ }
+ }
+ }while(message == FALSE);
+
+
+
+ if(((pCurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ currTar_Info->TarLUNBusy[lun] = TRUE;
+ pCurrCard->currentSCCB = pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[lun]];
+ if(pCurrCard->currentSCCB != NULL)
+ {
+ ACCEPT_MSG(port);
+ }
+ else
+ {
+ ACCEPT_MSG_ATN(port);
+ }
+ }
+ else
+ {
+ currTar_Info->TarLUNBusy[0] = TRUE;
+
+
+ if (tag)
+ {
+ if (pCurrCard->discQ_Tbl[tag] != NULL)
+ {
+ pCurrCard->currentSCCB = pCurrCard->discQ_Tbl[tag];
+ currTar_Info->TarTagQ_Cnt--;
+ ACCEPT_MSG(port);
+ }
+ else
+ {
+ ACCEPT_MSG_ATN(port);
+ }
+ }else
+ {
+ pCurrCard->currentSCCB = pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]];
+ if(pCurrCard->currentSCCB != NULL)
+ {
+ ACCEPT_MSG(port);
+ }
+ else
+ {
+ ACCEPT_MSG_ATN(port);
+ }
+ }
+ }
+
+ if(pCurrCard->currentSCCB != NULL)
+ {
+ if(pCurrCard->currentSCCB->Sccb_scsistat == ABORT_ST)
+ {
+ /* During Abort Tag command, the target could have got re-selected
+ and completed the command. Check the select Q and remove the CCB
+ if it is in the Select Q */
+ queueFindSccb(pCurrCard->currentSCCB, p_card);
+ }
+ }
+
+
+ while (!(RDW_HARPOON((port+hp_intstat)) & (PHASE | RESET)) &&
+ !(RD_HARPOON(port+hp_scsisig) & SCSI_REQ) &&
+ (RD_HARPOON(port+hp_scsisig) & SCSI_BSY)) ;
+}
+
+#if defined(V302)
+
+#if defined(DOS)
+UCHAR GetTarLun(USHORT port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tag, PUCHAR lun)
+#else
+UCHAR GetTarLun(ULONG port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tag, PUCHAR lun)
+#endif
+{
+ UCHAR message;
+ PSCCBMgr_tar_info currTar_Info;
+
+
+ currTar_Info = &sccbMgrTbl[p_card][our_target];
+ *tag = 0;
+
+
+ while(!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ))
+ {
+ if (! (RD_HARPOON(port+hp_scsisig) & SCSI_BSY))
+ {
+
+ WRW_HARPOON((port+hp_intstat), PHASE);
+ return(TRUE);
+ }
+ }
+
+ WRW_HARPOON((port+hp_intstat), PHASE);
+ if ((RD_HARPOON(port+hp_scsisig) & S_SCSI_PHZ) == S_MSGI_PH)
+ {
+
+ message = sfm(port,pCurrCard->currentSCCB);
+ if (message)
+ {
+
+ if (message <= (0x80 | LUN_MASK))
+ {
+ *lun = message & (UCHAR)LUN_MASK;
+
+#if !defined(DOS)
+ if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_TRYING)
+ {
+ if (currTar_Info->TarTagQ_Cnt != 0)
+ {
+
+ if (!(currTar_Info->TarLUN_CA))
+ {
+ ACCEPT_MSG(port); /*Release the ACK for ID msg. */
+
+
+ message = sfm(port,pCurrCard->currentSCCB);
+ if (message)
+ {
+ ACCEPT_MSG(port);
+ }
+
+ else
+ return(FALSE);
+
+ *tag = sfm(port,pCurrCard->currentSCCB);
+
+ if (!(*tag)) return(FALSE);
+
+ } /*C.A. exists! */
+
+ } /*End Q cnt != 0 */
+
+ } /*End Tag cmds supported! */
+#endif /* !DOS */
+
+ } /*End valid ID message. */
+
+ else
+ {
+
+ ACCEPT_MSG_ATN(port);
+ }
+
+ } /* End good id message. */
+
+ else
+ {
+
+ return(FALSE);
+ }
+ }
+ else
+ {
+ ACCEPT_MSG_ATN(port);
+ return(TRUE);
+ }
+ return(TRUE);
+}
+
+#endif /* V302 */
+
+#if defined(DOS)
+void SendMsg(USHORT port, UCHAR message)
+#else
+void SendMsg(ULONG port, UCHAR message)
+#endif
+{
+ while(!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ))
+ {
+ if (! (RD_HARPOON(port+hp_scsisig) & SCSI_BSY))
+ {
+
+ WRW_HARPOON((port+hp_intstat), PHASE);
+ return;
+ }
+ }
+
+ WRW_HARPOON((port+hp_intstat), PHASE);
+ if ((RD_HARPOON(port+hp_scsisig) & S_SCSI_PHZ) == S_MSGO_PH)
+ {
+ WRW_HARPOON((port+hp_intstat), (BUS_FREE | PHASE | XFER_CNT_0));
+
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_BUS_EN);
+
+ WR_HARPOON(port+hp_scsidata_0,message);
+
+ WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+
+ ACCEPT_MSG(port);
+
+ WR_HARPOON(port+hp_portctrl_0, 0x00);
+
+ if ((message == SMABORT) || (message == SMDEV_RESET) ||
+ (message == SMABORT_TAG) )
+ {
+ while(!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | PHASE))) {}
+
+ if (RDW_HARPOON((port+hp_intstat)) & BUS_FREE)
+ {
+ WRW_HARPOON((port+hp_intstat), BUS_FREE);
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sdecm
+ *
+ * Description: Determine the proper responce to the message from the
+ * target device.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void sdecm(UCHAR message, USHORT port, UCHAR p_card)
+#else
+void sdecm(UCHAR message, ULONG port, UCHAR p_card)
+#endif
+{
+ PSCCB currSCCB;
+ PSCCBcard CurrCard;
+ PSCCBMgr_tar_info currTar_Info;
+
+ CurrCard = &BL_Card[p_card];
+ currSCCB = CurrCard->currentSCCB;
+
+ currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+ if (message == SMREST_DATA_PTR)
+ {
+ if (!(currSCCB->Sccb_XferState & F_NO_DATA_YET))
+ {
+ currSCCB->Sccb_ATC = currSCCB->Sccb_savedATC;
+
+ hostDataXferRestart(currSCCB);
+ }
+
+ ACCEPT_MSG(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+
+ else if (message == SMCMD_COMP)
+ {
+
+
+ if (currSCCB->Sccb_scsistat == SELECT_Q_ST)
+ {
+ currTar_Info->TarStatus &= ~(UCHAR)TAR_TAG_Q_MASK;
+ currTar_Info->TarStatus |= (UCHAR)TAG_Q_REJECT;
+ }
+
+ ACCEPT_MSG(port);
+
+ }
+
+ else if ((message == SMNO_OP) || (message >= SMIDENT)
+ || (message == SMINIT_RECOVERY) || (message == SMREL_RECOVERY))
+ {
+
+ ACCEPT_MSG(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+
+ else if (message == SMREJECT)
+ {
+
+ if ((currSCCB->Sccb_scsistat == SELECT_SN_ST) ||
+ (currSCCB->Sccb_scsistat == SELECT_WN_ST) ||
+ ((currTar_Info->TarStatus & TAR_SYNC_MASK) == SYNC_TRYING ) ||
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_TRYING ) )
+
+ {
+ WRW_HARPOON((port+hp_intstat), BUS_FREE);
+
+ ACCEPT_MSG(port);
+
+
+ while ((!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) &&
+ (!(RDW_HARPOON((port+hp_intstat)) & BUS_FREE))) {}
+
+ if(currSCCB->Lun == 0x00)
+ {
+ if ((currSCCB->Sccb_scsistat == SELECT_SN_ST))
+ {
+
+ currTar_Info->TarStatus |= (UCHAR)SYNC_SUPPORTED;
+
+ currTar_Info->TarEEValue &= ~EE_SYNC_MASK;
+ }
+
+#if defined(WIDE_SCSI)
+ else if ((currSCCB->Sccb_scsistat == SELECT_WN_ST))
+ {
+
+
+ currTar_Info->TarStatus = (currTar_Info->TarStatus &
+ ~WIDE_ENABLED) | WIDE_NEGOCIATED;
+
+ currTar_Info->TarEEValue &= ~EE_WIDE_SCSI;
+
+ }
+#endif
+
+ else if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_TRYING )
+ {
+ currTar_Info->TarStatus = (currTar_Info->TarStatus &
+ ~(UCHAR)TAR_TAG_Q_MASK) | TAG_Q_REJECT;
+
+
+ currSCCB->ControlByte &= ~F_USE_CMD_Q;
+ CurrCard->discQCount--;
+ CurrCard->discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ currSCCB->Sccb_tag = 0x00;
+
+ }
+ }
+
+ if (RDW_HARPOON((port+hp_intstat)) & BUS_FREE)
+ {
+
+
+ if(currSCCB->Lun == 0x00)
+ {
+ WRW_HARPOON((port+hp_intstat), BUS_FREE);
+ CurrCard->globalFlags |= F_NEW_SCCB_CMD;
+ }
+ }
+
+ else
+ {
+
+ if((CurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ currTar_Info->TarLUNBusy[currSCCB->Lun] = TRUE;
+ else
+ currTar_Info->TarLUNBusy[0] = TRUE;
+
+
+ currSCCB->ControlByte &= ~(UCHAR)F_USE_CMD_Q;
+
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+
+ }
+ }
+
+ else
+ {
+ ACCEPT_MSG(port);
+
+ while ((!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) &&
+ (!(RDW_HARPOON((port+hp_intstat)) & BUS_FREE))) {}
+
+ if (!(RDW_HARPOON((port+hp_intstat)) & BUS_FREE))
+ {
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ }
+ }
+
+ else if (message == SMEXT)
+ {
+
+ ACCEPT_MSG(port);
+ shandem(port,p_card,currSCCB);
+ }
+
+ else if (message == SMIGNORWR)
+ {
+
+ ACCEPT_MSG(port); /* ACK the RESIDUE MSG */
+
+ message = sfm(port,currSCCB);
+
+ if(currSCCB->Sccb_scsimsg != SMPARITY)
+ ACCEPT_MSG(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+
+
+ else
+ {
+
+ currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
+ currSCCB->Sccb_scsimsg = SMREJECT;
+
+ ACCEPT_MSG_ATN(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: shandem
+ *
+ * Description: Decide what to do with the extended message.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void shandem(USHORT port, UCHAR p_card, PSCCB pCurrSCCB)
+#else
+void shandem(ULONG port, UCHAR p_card, PSCCB pCurrSCCB)
+#endif
+{
+ UCHAR length,message;
+
+ length = sfm(port,pCurrSCCB);
+ if (length)
+ {
+
+ ACCEPT_MSG(port);
+ message = sfm(port,pCurrSCCB);
+ if (message)
+ {
+
+ if (message == SMSYNC)
+ {
+
+ if (length == 0x03)
+ {
+
+ ACCEPT_MSG(port);
+ stsyncn(port,p_card);
+ }
+ else
+ {
+
+ pCurrSCCB->Sccb_scsimsg = SMREJECT;
+ ACCEPT_MSG_ATN(port);
+ }
+ }
+#if defined(WIDE_SCSI)
+ else if (message == SMWDTR)
+ {
+
+ if (length == 0x02)
+ {
+
+ ACCEPT_MSG(port);
+ stwidn(port,p_card);
+ }
+ else
+ {
+
+ pCurrSCCB->Sccb_scsimsg = SMREJECT;
+ ACCEPT_MSG_ATN(port);
+
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ }
+#endif
+ else
+ {
+
+ pCurrSCCB->Sccb_scsimsg = SMREJECT;
+ ACCEPT_MSG_ATN(port);
+
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ }
+ else
+ {
+ if(pCurrSCCB->Sccb_scsimsg != SMPARITY)
+ ACCEPT_MSG(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ }else
+ {
+ if(pCurrSCCB->Sccb_scsimsg == SMPARITY)
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sisyncn
+ *
+ * Description: Read in a message byte from the SCSI bus, and check
+ * for a parity error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR sisyncn(USHORT port, UCHAR p_card, UCHAR syncFlag)
+#else
+UCHAR sisyncn(ULONG port, UCHAR p_card, UCHAR syncFlag)
+#endif
+{
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+ currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+ if (!((currTar_Info->TarStatus & TAR_SYNC_MASK) == SYNC_TRYING)) {
+
+
+ WRW_HARPOON((port+ID_MSG_STRT),
+ (MPM_OP+AMSG_OUT+(currSCCB->Sccb_idmsg & ~(UCHAR)DISC_PRIV)));
+
+ WRW_HARPOON((port+ID_MSG_STRT+2),BRH_OP+ALWAYS+CMDPZ);
+
+ WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+SMEXT ));
+ WRW_HARPOON((port+SYNC_MSGS+2), (MPM_OP+AMSG_OUT+0x03 ));
+ WRW_HARPOON((port+SYNC_MSGS+4), (MPM_OP+AMSG_OUT+SMSYNC));
+
+
+ if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_20MB)
+
+ WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+ 12));
+
+ else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_10MB)
+
+ WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+ 25));
+
+ else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_5MB)
+
+ WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+ 50));
+
+ else
+ WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+ 00));
+
+
+ WRW_HARPOON((port+SYNC_MSGS+8), (RAT_OP ));
+ WRW_HARPOON((port+SYNC_MSGS+10),(MPM_OP+AMSG_OUT+DEFAULT_OFFSET));
+ WRW_HARPOON((port+SYNC_MSGS+12),(BRH_OP+ALWAYS+NP ));
+
+
+ if(syncFlag == FALSE)
+ {
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+ currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+ ~(UCHAR)TAR_SYNC_MASK) | (UCHAR)SYNC_TRYING);
+ }
+ else
+ {
+ WR_HARPOON(port+hp_autostart_3, (AUTO_IMMED + CMD_ONLY_STRT));
+ }
+
+
+ return(TRUE);
+ }
+
+ else {
+
+ currTar_Info->TarStatus |= (UCHAR)SYNC_SUPPORTED;
+ currTar_Info->TarEEValue &= ~EE_SYNC_MASK;
+ return(FALSE);
+ }
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: stsyncn
+ *
+ * Description: The has sent us a Sync Nego message so handle it as
+ * necessary.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void stsyncn(USHORT port, UCHAR p_card)
+#else
+void stsyncn(ULONG port, UCHAR p_card)
+#endif
+{
+ UCHAR sync_msg,offset,sync_reg,our_sync_msg;
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+ currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+ sync_msg = sfm(port,currSCCB);
+
+ if((sync_msg == 0x00) && (currSCCB->Sccb_scsimsg == SMPARITY))
+ {
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ return;
+ }
+
+ ACCEPT_MSG(port);
+
+
+ offset = sfm(port,currSCCB);
+
+ if((offset == 0x00) && (currSCCB->Sccb_scsimsg == SMPARITY))
+ {
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ return;
+ }
+
+ if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_20MB)
+
+ our_sync_msg = 12; /* Setup our Message to 20mb/s */
+
+ else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_10MB)
+
+ our_sync_msg = 25; /* Setup our Message to 10mb/s */
+
+ else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_5MB)
+
+ our_sync_msg = 50; /* Setup our Message to 5mb/s */
+ else
+
+ our_sync_msg = 0; /* Message = Async */
+
+ if (sync_msg < our_sync_msg) {
+ sync_msg = our_sync_msg; /*if faster, then set to max. */
+ }
+
+ if (offset == ASYNC)
+ sync_msg = ASYNC;
+
+ if (offset > MAX_OFFSET)
+ offset = MAX_OFFSET;
+
+ sync_reg = 0x00;
+
+ if (sync_msg > 12)
+
+ sync_reg = 0x20; /* Use 10MB/s */
+
+ if (sync_msg > 25)
+
+ sync_reg = 0x40; /* Use 6.6MB/s */
+
+ if (sync_msg > 38)
+
+ sync_reg = 0x60; /* Use 5MB/s */
+
+ if (sync_msg > 50)
+
+ sync_reg = 0x80; /* Use 4MB/s */
+
+ if (sync_msg > 62)
+
+ sync_reg = 0xA0; /* Use 3.33MB/s */
+
+ if (sync_msg > 75)
+
+ sync_reg = 0xC0; /* Use 2.85MB/s */
+
+ if (sync_msg > 87)
+
+ sync_reg = 0xE0; /* Use 2.5MB/s */
+
+ if (sync_msg > 100) {
+
+ sync_reg = 0x00; /* Use ASYNC */
+ offset = 0x00;
+ }
+
+
+#if defined(WIDE_SCSI)
+ if (currTar_Info->TarStatus & WIDE_ENABLED)
+
+ sync_reg |= offset;
+
+ else
+
+ sync_reg |= (offset | NARROW_SCSI);
+
+#else
+ sync_reg |= (offset | NARROW_SCSI);
+#endif
+
+ sssyncv(port,currSCCB->TargID,sync_reg,currTar_Info);
+
+
+ if (currSCCB->Sccb_scsistat == SELECT_SN_ST) {
+
+
+ ACCEPT_MSG(port);
+
+ currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+ ~(UCHAR)TAR_SYNC_MASK) | (UCHAR)SYNC_SUPPORTED);
+
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+
+ else {
+
+
+ ACCEPT_MSG_ATN(port);
+
+ sisyncr(port,sync_msg,offset);
+
+ currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+ ~(UCHAR)TAR_SYNC_MASK) | (UCHAR)SYNC_SUPPORTED);
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sisyncr
+ *
+ * Description: Answer the targets sync message.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void sisyncr(USHORT port,UCHAR sync_pulse, UCHAR offset)
+#else
+void sisyncr(ULONG port,UCHAR sync_pulse, UCHAR offset)
+#endif
+{
+ ARAM_ACCESS(port);
+ WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+SMEXT ));
+ WRW_HARPOON((port+SYNC_MSGS+2), (MPM_OP+AMSG_OUT+0x03 ));
+ WRW_HARPOON((port+SYNC_MSGS+4), (MPM_OP+AMSG_OUT+SMSYNC));
+ WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+sync_pulse));
+ WRW_HARPOON((port+SYNC_MSGS+8), (RAT_OP ));
+ WRW_HARPOON((port+SYNC_MSGS+10),(MPM_OP+AMSG_OUT+offset));
+ WRW_HARPOON((port+SYNC_MSGS+12),(BRH_OP+ALWAYS+NP ));
+ SGRAM_ACCESS(port);
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT_1);
+
+ WR_HARPOON(port+hp_autostart_3, (AUTO_IMMED+CMD_ONLY_STRT));
+
+ while (!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | AUTO_INT))) {}
+}
+
+
+
+#if defined(WIDE_SCSI)
+
+/*---------------------------------------------------------------------
+ *
+ * Function: siwidn
+ *
+ * Description: Read in a message byte from the SCSI bus, and check
+ * for a parity error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR siwidn(USHORT port, UCHAR p_card)
+#else
+UCHAR siwidn(ULONG port, UCHAR p_card)
+#endif
+{
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+ currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+ if (!((currTar_Info->TarStatus & TAR_WIDE_MASK) == WIDE_NEGOCIATED)) {
+
+
+ WRW_HARPOON((port+ID_MSG_STRT),
+ (MPM_OP+AMSG_OUT+(currSCCB->Sccb_idmsg & ~(UCHAR)DISC_PRIV)));
+
+ WRW_HARPOON((port+ID_MSG_STRT+2),BRH_OP+ALWAYS+CMDPZ);
+
+ WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+SMEXT ));
+ WRW_HARPOON((port+SYNC_MSGS+2), (MPM_OP+AMSG_OUT+0x02 ));
+ WRW_HARPOON((port+SYNC_MSGS+4), (MPM_OP+AMSG_OUT+SMWDTR));
+ WRW_HARPOON((port+SYNC_MSGS+6), (RAT_OP ));
+ WRW_HARPOON((port+SYNC_MSGS+8), (MPM_OP+AMSG_OUT+ SM16BIT));
+ WRW_HARPOON((port+SYNC_MSGS+10),(BRH_OP+ALWAYS+NP ));
+
+ WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+
+
+ currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+ ~(UCHAR)TAR_WIDE_MASK) | (UCHAR)WIDE_ENABLED);
+
+ return(TRUE);
+ }
+
+ else {
+
+ currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+ ~(UCHAR)TAR_WIDE_MASK) | WIDE_NEGOCIATED);
+
+ currTar_Info->TarEEValue &= ~EE_WIDE_SCSI;
+ return(FALSE);
+ }
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: stwidn
+ *
+ * Description: The has sent us a Wide Nego message so handle it as
+ * necessary.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void stwidn(USHORT port, UCHAR p_card)
+#else
+void stwidn(ULONG port, UCHAR p_card)
+#endif
+{
+ UCHAR width;
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+ currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+ width = sfm(port,currSCCB);
+
+ if((width == 0x00) && (currSCCB->Sccb_scsimsg == SMPARITY))
+ {
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ return;
+ }
+
+
+ if (!(currTar_Info->TarEEValue & EE_WIDE_SCSI))
+ width = 0;
+
+ if (width) {
+ currTar_Info->TarStatus |= WIDE_ENABLED;
+ width = 0;
+ }
+ else {
+ width = NARROW_SCSI;
+ currTar_Info->TarStatus &= ~WIDE_ENABLED;
+ }
+
+
+ sssyncv(port,currSCCB->TargID,width,currTar_Info);
+
+
+ if (currSCCB->Sccb_scsistat == SELECT_WN_ST)
+ {
+
+
+
+ currTar_Info->TarStatus |= WIDE_NEGOCIATED;
+
+ if (!((currTar_Info->TarStatus & TAR_SYNC_MASK) == SYNC_SUPPORTED))
+ {
+ ACCEPT_MSG_ATN(port);
+ ARAM_ACCESS(port);
+ sisyncn(port,p_card, TRUE);
+ currSCCB->Sccb_scsistat = SELECT_SN_ST;
+ SGRAM_ACCESS(port);
+ }
+ else
+ {
+ ACCEPT_MSG(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ }
+
+ else {
+
+
+ ACCEPT_MSG_ATN(port);
+
+ if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
+ width = SM16BIT;
+ else
+ width = SM8BIT;
+
+ siwidr(port,width);
+
+ currTar_Info->TarStatus |= (WIDE_NEGOCIATED | WIDE_ENABLED);
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: siwidr
+ *
+ * Description: Answer the targets Wide nego message.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void siwidr(USHORT port, UCHAR width)
+#else
+void siwidr(ULONG port, UCHAR width)
+#endif
+{
+ ARAM_ACCESS(port);
+ WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+SMEXT ));
+ WRW_HARPOON((port+SYNC_MSGS+2), (MPM_OP+AMSG_OUT+0x02 ));
+ WRW_HARPOON((port+SYNC_MSGS+4), (MPM_OP+AMSG_OUT+SMWDTR));
+ WRW_HARPOON((port+SYNC_MSGS+6), (RAT_OP ));
+ WRW_HARPOON((port+SYNC_MSGS+8),(MPM_OP+AMSG_OUT+width));
+ WRW_HARPOON((port+SYNC_MSGS+10),(BRH_OP+ALWAYS+NP ));
+ SGRAM_ACCESS(port);
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT_1);
+
+ WR_HARPOON(port+hp_autostart_3, (AUTO_IMMED+CMD_ONLY_STRT));
+
+ while (!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | AUTO_INT))) {}
+}
+
+#endif
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sssyncv
+ *
+ * Description: Write the desired value to the Sync Register for the
+ * ID specified.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void sssyncv(USHORT p_port, UCHAR p_id, UCHAR p_sync_value,PSCCBMgr_tar_info currTar_Info)
+#else
+void sssyncv(ULONG p_port, UCHAR p_id, UCHAR p_sync_value,PSCCBMgr_tar_info currTar_Info)
+#endif
+{
+ UCHAR index;
+
+ index = p_id;
+
+ switch (index) {
+
+ case 0:
+ index = 12; /* hp_synctarg_0 */
+ break;
+ case 1:
+ index = 13; /* hp_synctarg_1 */
+ break;
+ case 2:
+ index = 14; /* hp_synctarg_2 */
+ break;
+ case 3:
+ index = 15; /* hp_synctarg_3 */
+ break;
+ case 4:
+ index = 8; /* hp_synctarg_4 */
+ break;
+ case 5:
+ index = 9; /* hp_synctarg_5 */
+ break;
+ case 6:
+ index = 10; /* hp_synctarg_6 */
+ break;
+ case 7:
+ index = 11; /* hp_synctarg_7 */
+ break;
+ case 8:
+ index = 4; /* hp_synctarg_8 */
+ break;
+ case 9:
+ index = 5; /* hp_synctarg_9 */
+ break;
+ case 10:
+ index = 6; /* hp_synctarg_10 */
+ break;
+ case 11:
+ index = 7; /* hp_synctarg_11 */
+ break;
+ case 12:
+ index = 0; /* hp_synctarg_12 */
+ break;
+ case 13:
+ index = 1; /* hp_synctarg_13 */
+ break;
+ case 14:
+ index = 2; /* hp_synctarg_14 */
+ break;
+ case 15:
+ index = 3; /* hp_synctarg_15 */
+
+ }
+
+ WR_HARPOON(p_port+hp_synctarg_base+index, p_sync_value);
+
+ currTar_Info->TarSyncCtrl = p_sync_value;
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sresb
+ *
+ * Description: Reset the desired card's SCSI bus.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void sresb(USHORT port, UCHAR p_card)
+#else
+void sresb(ULONG port, UCHAR p_card)
+#endif
+{
+ UCHAR scsiID, i;
+
+ PSCCBMgr_tar_info currTar_Info;
+
+ WR_HARPOON(port+hp_page_ctrl,
+ (RD_HARPOON(port+hp_page_ctrl) | G_INT_DISABLE));
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+
+ WR_HARPOON(port+hp_scsictrl_0, SCSI_RST);
+
+ scsiID = RD_HARPOON(port+hp_seltimeout);
+ WR_HARPOON(port+hp_seltimeout,TO_5ms);
+ WRW_HARPOON((port+hp_intstat), TIMEOUT);
+
+ WR_HARPOON(port+hp_portctrl_0,(SCSI_PORT | START_TO));
+
+ while (!(RDW_HARPOON((port+hp_intstat)) & TIMEOUT)) {}
+
+ WR_HARPOON(port+hp_seltimeout,scsiID);
+
+ WR_HARPOON(port+hp_scsictrl_0, ENA_SCAM_SEL);
+
+ Wait(port, TO_5ms);
+
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+
+ WR_HARPOON(port+hp_int_mask, (RD_HARPOON(port+hp_int_mask) | 0x00));
+
+ for (scsiID = 0; scsiID < MAX_SCSI_TAR; scsiID++)
+ {
+ currTar_Info = &sccbMgrTbl[p_card][scsiID];
+
+ if (currTar_Info->TarEEValue & EE_SYNC_MASK)
+ {
+ currTar_Info->TarSyncCtrl = 0;
+ currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
+ }
+
+ if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
+ {
+ currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
+ }
+
+ sssyncv(port, scsiID, NARROW_SCSI,currTar_Info);
+
+ SccbMgrTableInitTarget(p_card, scsiID);
+ }
+
+ BL_Card[p_card].scanIndex = 0x00;
+ BL_Card[p_card].currentSCCB = NULL;
+ BL_Card[p_card].globalFlags &= ~(F_TAG_STARTED | F_HOST_XFER_ACT
+ | F_NEW_SCCB_CMD);
+ BL_Card[p_card].cmdCounter = 0x00;
+ BL_Card[p_card].discQCount = 0x00;
+ BL_Card[p_card].tagQ_Lst = 0x01;
+
+ for(i = 0; i < QUEUE_DEPTH; i++)
+ BL_Card[p_card].discQ_Tbl[i] = NULL;
+
+ WR_HARPOON(port+hp_page_ctrl,
+ (RD_HARPOON(port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: ssenss
+ *
+ * Description: Setup for the Auto Sense command.
+ *
+ *---------------------------------------------------------------------*/
+void ssenss(PSCCBcard pCurrCard)
+{
+ UCHAR i;
+ PSCCB currSCCB;
+
+ currSCCB = pCurrCard->currentSCCB;
+
+
+ currSCCB->Save_CdbLen = currSCCB->CdbLength;
+
+ for (i = 0; i < 6; i++) {
+
+ currSCCB->Save_Cdb[i] = currSCCB->Cdb[i];
+ }
+
+ currSCCB->CdbLength = SIX_BYTE_CMD;
+ currSCCB->Cdb[0] = SCSI_REQUEST_SENSE;
+ currSCCB->Cdb[1] = currSCCB->Cdb[1] & (UCHAR)0xE0; /*Keep LUN. */
+ currSCCB->Cdb[2] = 0x00;
+ currSCCB->Cdb[3] = 0x00;
+ currSCCB->Cdb[4] = currSCCB->RequestSenseLength;
+ currSCCB->Cdb[5] = 0x00;
+
+ currSCCB->Sccb_XferCnt = (unsigned long)currSCCB->RequestSenseLength;
+
+ currSCCB->Sccb_ATC = 0x00;
+
+ currSCCB->Sccb_XferState |= F_AUTO_SENSE;
+
+ currSCCB->Sccb_XferState &= ~F_SG_XFER;
+
+ currSCCB->Sccb_idmsg = currSCCB->Sccb_idmsg & ~(UCHAR)DISC_PRIV;
+
+ currSCCB->ControlByte = 0x00;
+
+ currSCCB->Sccb_MGRFlags &= F_STATUSLOADED;
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sxfrp
+ *
+ * Description: Transfer data into the bit bucket until the device
+ * decides to switch phase.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void sxfrp(USHORT p_port, UCHAR p_card)
+#else
+void sxfrp(ULONG p_port, UCHAR p_card)
+#endif
+{
+ UCHAR curr_phz;
+
+
+ DISABLE_AUTO(p_port);
+
+ if (BL_Card[p_card].globalFlags & F_HOST_XFER_ACT) {
+
+ hostDataXferAbort(p_port,p_card,BL_Card[p_card].currentSCCB);
+
+ }
+
+ /* If the Automation handled the end of the transfer then do not
+ match the phase or we will get out of sync with the ISR. */
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | XFER_CNT_0 | AUTO_INT))
+ return;
+
+ WR_HARPOON(p_port+hp_xfercnt_0, 0x00);
+
+ curr_phz = RD_HARPOON(p_port+hp_scsisig) & (UCHAR)S_SCSI_PHZ;
+
+ WRW_HARPOON((p_port+hp_intstat), XFER_CNT_0);
+
+
+ WR_HARPOON(p_port+hp_scsisig, curr_phz);
+
+ while ( !(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RESET)) &&
+ (curr_phz == (RD_HARPOON(p_port+hp_scsisig) & (UCHAR)S_SCSI_PHZ)) )
+ {
+ if (curr_phz & (UCHAR)SCSI_IOBIT)
+ {
+ WR_HARPOON(p_port+hp_portctrl_0, (SCSI_PORT | HOST_PORT | SCSI_INBIT));
+
+ if (!(RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY))
+ {
+ RD_HARPOON(p_port+hp_fifodata_0);
+ }
+ }
+ else
+ {
+ WR_HARPOON(p_port+hp_portctrl_0, (SCSI_PORT | HOST_PORT | HOST_WRT));
+ if (RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY)
+ {
+ WR_HARPOON(p_port+hp_fifodata_0,0xFA);
+ }
+ }
+ } /* End of While loop for padding data I/O phase */
+
+ while ( !(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RESET)))
+ {
+ if (RD_HARPOON(p_port+hp_scsisig) & SCSI_REQ)
+ break;
+ }
+
+ WR_HARPOON(p_port+hp_portctrl_0, (SCSI_PORT | HOST_PORT | SCSI_INBIT));
+ while (!(RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY))
+ {
+ RD_HARPOON(p_port+hp_fifodata_0);
+ }
+
+ if ( !(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RESET)))
+ {
+ WR_HARPOON(p_port+hp_autostart_0, (AUTO_IMMED+DISCONNECT_START));
+ while (!(RDW_HARPOON((p_port+hp_intstat)) & AUTO_INT)) {}
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & (ICMD_COMP | ITAR_DISC))
+ while (!(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RSEL))) ;
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: schkdd
+ *
+ * Description: Make sure data has been flushed from both FIFOs and abort
+ * the operations if necessary.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void schkdd(USHORT port, UCHAR p_card)
+#else
+void schkdd(ULONG port, UCHAR p_card)
+#endif
+{
+ USHORT TimeOutLoop;
+ UCHAR sPhase;
+
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+
+ if ((currSCCB->Sccb_scsistat != DATA_OUT_ST) &&
+ (currSCCB->Sccb_scsistat != DATA_IN_ST)) {
+ return;
+ }
+
+
+
+ if (currSCCB->Sccb_XferState & F_ODD_BALL_CNT)
+ {
+
+ currSCCB->Sccb_ATC += (currSCCB->Sccb_XferCnt-1);
+
+ currSCCB->Sccb_XferCnt = 1;
+
+ currSCCB->Sccb_XferState &= ~F_ODD_BALL_CNT;
+ WRW_HARPOON((port+hp_fiforead), (USHORT) 0x00);
+ WR_HARPOON(port+hp_xferstat, 0x00);
+ }
+
+ else
+ {
+
+ currSCCB->Sccb_ATC += currSCCB->Sccb_XferCnt;
+
+ currSCCB->Sccb_XferCnt = 0;
+ }
+
+ if ((RDW_HARPOON((port+hp_intstat)) & PARITY) &&
+ (currSCCB->HostStatus == SCCB_COMPLETE)) {
+
+ currSCCB->HostStatus = SCCB_PARITY_ERR;
+ WRW_HARPOON((port+hp_intstat), PARITY);
+ }
+
+
+ hostDataXferAbort(port,p_card,currSCCB);
+
+
+ while (RD_HARPOON(port+hp_scsisig) & SCSI_ACK) {}
+
+ TimeOutLoop = 0;
+
+ while(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY)
+ {
+ if (RDW_HARPOON((port+hp_intstat)) & BUS_FREE) {
+ return;
+ }
+ if (RD_HARPOON(port+hp_offsetctr) & (UCHAR)0x1F) {
+ break;
+ }
+ if (RDW_HARPOON((port+hp_intstat)) & RESET) {
+ return;
+ }
+ if ((RD_HARPOON(port+hp_scsisig) & SCSI_REQ) || (TimeOutLoop++>0x3000) )
+ break;
+ }
+
+ sPhase = RD_HARPOON(port+hp_scsisig) & (SCSI_BSY | S_SCSI_PHZ);
+ if ((!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY)) ||
+ (RD_HARPOON(port+hp_offsetctr) & (UCHAR)0x1F) ||
+ (sPhase == (SCSI_BSY | S_DATAO_PH)) ||
+ (sPhase == (SCSI_BSY | S_DATAI_PH)))
+ {
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+ if (!(currSCCB->Sccb_XferState & F_ALL_XFERRED))
+ {
+ if (currSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
+ phaseDataIn(port,p_card);
+ }
+
+ else {
+ phaseDataOut(port,p_card);
+ }
+ }
+ else
+ {
+ sxfrp(port,p_card);
+ if (!(RDW_HARPOON((port+hp_intstat)) &
+ (BUS_FREE | ICMD_COMP | ITAR_DISC | RESET)))
+ {
+ WRW_HARPOON((port+hp_intstat), AUTO_INT);
+ phaseDecode(port,p_card);
+ }
+ }
+
+ }
+
+ else {
+ WR_HARPOON(port+hp_portctrl_0, 0x00);
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sinits
+ *
+ * Description: Setup SCCB manager fields in this SCCB.
+ *
+ *---------------------------------------------------------------------*/
+
+void sinits(PSCCB p_sccb, UCHAR p_card)
+{
+ PSCCBMgr_tar_info currTar_Info;
+
+ if((p_sccb->TargID > MAX_SCSI_TAR) || (p_sccb->Lun > MAX_LUN))
+ {
+ return;
+ }
+ currTar_Info = &sccbMgrTbl[p_card][p_sccb->TargID];
+
+ p_sccb->Sccb_XferState = 0x00;
+ p_sccb->Sccb_XferCnt = p_sccb->DataLength;
+
+ if ((p_sccb->OperationCode == SCATTER_GATHER_COMMAND) ||
+ (p_sccb->OperationCode == RESIDUAL_SG_COMMAND)) {
+
+ p_sccb->Sccb_SGoffset = 0;
+ p_sccb->Sccb_XferState = F_SG_XFER;
+ p_sccb->Sccb_XferCnt = 0x00;
+ }
+
+ if (p_sccb->DataLength == 0x00)
+
+ p_sccb->Sccb_XferState |= F_ALL_XFERRED;
+
+ if (p_sccb->ControlByte & F_USE_CMD_Q)
+ {
+ if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_REJECT)
+ p_sccb->ControlByte &= ~F_USE_CMD_Q;
+
+ else
+ currTar_Info->TarStatus |= TAG_Q_TRYING;
+ }
+
+/* For !single SCSI device in system & device allow Disconnect
+ or command is tag_q type then send Cmd with Disconnect Enable
+ else send Cmd with Disconnect Disable */
+
+/*
+ if (((!(BL_Card[p_card].globalFlags & F_SINGLE_DEVICE)) &&
+ (currTar_Info->TarStatus & TAR_ALLOW_DISC)) ||
+ (currTar_Info->TarStatus & TAG_Q_TRYING)) {
+*/
+ if ((currTar_Info->TarStatus & TAR_ALLOW_DISC) ||
+ (currTar_Info->TarStatus & TAG_Q_TRYING)) {
+ p_sccb->Sccb_idmsg = (UCHAR)(SMIDENT | DISC_PRIV) | p_sccb->Lun;
+ }
+
+ else {
+
+ p_sccb->Sccb_idmsg = (UCHAR)SMIDENT | p_sccb->Lun;
+ }
+
+ p_sccb->HostStatus = 0x00;
+ p_sccb->TargetStatus = 0x00;
+ p_sccb->Sccb_tag = 0x00;
+ p_sccb->Sccb_MGRFlags = 0x00;
+ p_sccb->Sccb_sgseg = 0x00;
+ p_sccb->Sccb_ATC = 0x00;
+ p_sccb->Sccb_savedATC = 0x00;
+/*
+ p_sccb->SccbVirtDataPtr = 0x00;
+ p_sccb->Sccb_forwardlink = NULL;
+ p_sccb->Sccb_backlink = NULL;
+ */
+ p_sccb->Sccb_scsistat = BUS_FREE_ST;
+ p_sccb->SccbStatus = SCCB_IN_PROCESS;
+ p_sccb->Sccb_scsimsg = SMNO_OP;
+
+}
+
+
+#ident "$Id: FlashPoint.c,v 1.1 1999/04/26 05:53:56 tb Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: phase.c $
+ *
+ * Description: Functions to intially handle the SCSI bus phase when
+ * the target asserts request (and the automation is not
+ * enabled to handle the situation).
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+
+#if defined(OS2)
+ extern void (far *s_PhaseTbl[8]) (ULONG, UCHAR);
+#else
+ #if defined(DOS)
+ extern void (*s_PhaseTbl[8]) (USHORT, UCHAR);
+ #else
+ extern void (*s_PhaseTbl[8]) (ULONG, UCHAR);
+ #endif
+#endif
+*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Phase Decode
+ *
+ * Description: Determine the phase and call the appropriate function.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void phaseDecode(USHORT p_port, UCHAR p_card)
+#else
+void phaseDecode(ULONG p_port, UCHAR p_card)
+#endif
+{
+ unsigned char phase_ref;
+#if defined(OS2)
+ void (far *phase) (ULONG, UCHAR);
+#else
+ #if defined(DOS)
+ void (*phase) (USHORT, UCHAR);
+ #else
+ void (*phase) (ULONG, UCHAR);
+ #endif
+#endif
+
+
+ DISABLE_AUTO(p_port);
+
+ phase_ref = (UCHAR) (RD_HARPOON(p_port+hp_scsisig) & S_SCSI_PHZ);
+
+ phase = s_PhaseTbl[phase_ref];
+
+ (*phase)(p_port, p_card); /* Call the correct phase func */
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Data Out Phase
+ *
+ * Description: Start up both the BusMaster and Xbow.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseDataOut(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseDataOut(USHORT port, UCHAR p_card)
+#else
+void phaseDataOut(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+ if (currSCCB == NULL)
+ {
+ return; /* Exit if No SCCB record */
+ }
+
+ currSCCB->Sccb_scsistat = DATA_OUT_ST;
+ currSCCB->Sccb_XferState &= ~(F_HOST_XFER_DIR | F_NO_DATA_YET);
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+ WRW_HARPOON((port+hp_intstat), XFER_CNT_0);
+
+ WR_HARPOON(port+hp_autostart_0, (END_DATA+END_DATA_START));
+
+ dataXferProcessor(port, &BL_Card[p_card]);
+
+#if defined(NOBUGBUG)
+ if (RDW_HARPOON((port+hp_intstat)) & XFER_CNT_0)
+ WRW_HARPOON((port+hp_intstat), XFER_CNT_0);
+
+#endif
+
+
+ if (currSCCB->Sccb_XferCnt == 0) {
+
+
+ if ((currSCCB->ControlByte & SCCB_DATA_XFER_OUT) &&
+ (currSCCB->HostStatus == SCCB_COMPLETE))
+ currSCCB->HostStatus = SCCB_DATA_OVER_RUN;
+
+ sxfrp(port,p_card);
+ if (!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | RESET)))
+ phaseDecode(port,p_card);
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Data In Phase
+ *
+ * Description: Startup the BusMaster and the XBOW.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseDataIn(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseDataIn(USHORT port, UCHAR p_card)
+#else
+void phaseDataIn(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ if (currSCCB == NULL)
+ {
+ return; /* Exit if No SCCB record */
+ }
+
+
+ currSCCB->Sccb_scsistat = DATA_IN_ST;
+ currSCCB->Sccb_XferState |= F_HOST_XFER_DIR;
+ currSCCB->Sccb_XferState &= ~F_NO_DATA_YET;
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+ WRW_HARPOON((port+hp_intstat), XFER_CNT_0);
+
+ WR_HARPOON(port+hp_autostart_0, (END_DATA+END_DATA_START));
+
+ dataXferProcessor(port, &BL_Card[p_card]);
+
+ if (currSCCB->Sccb_XferCnt == 0) {
+
+
+ if ((currSCCB->ControlByte & SCCB_DATA_XFER_IN) &&
+ (currSCCB->HostStatus == SCCB_COMPLETE))
+ currSCCB->HostStatus = SCCB_DATA_OVER_RUN;
+
+ sxfrp(port,p_card);
+ if (!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | RESET)))
+ phaseDecode(port,p_card);
+
+ }
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Command Phase
+ *
+ * Description: Load the CDB into the automation and start it up.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseCommand(ULONG p_port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseCommand(USHORT p_port, UCHAR p_card)
+#else
+void phaseCommand(ULONG p_port, UCHAR p_card)
+#endif
+#endif
+{
+ PSCCB currSCCB;
+#if defined(DOS)
+ USHORT cdb_reg;
+#else
+ ULONG cdb_reg;
+#endif
+ UCHAR i;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ if (currSCCB->OperationCode == RESET_COMMAND) {
+
+ currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
+ currSCCB->CdbLength = SIX_BYTE_CMD;
+ }
+
+ WR_HARPOON(p_port+hp_scsisig, 0x00);
+
+ ARAM_ACCESS(p_port);
+
+
+ cdb_reg = p_port + CMD_STRT;
+
+ for (i=0; i < currSCCB->CdbLength; i++) {
+
+ if (currSCCB->OperationCode == RESET_COMMAND)
+
+ WRW_HARPOON(cdb_reg, (MPM_OP + ACOMMAND + 0x00));
+
+ else
+ WRW_HARPOON(cdb_reg, (MPM_OP + ACOMMAND + currSCCB->Cdb[i]));
+ cdb_reg +=2;
+ }
+
+ if (currSCCB->CdbLength != TWELVE_BYTE_CMD)
+ WRW_HARPOON(cdb_reg, (BRH_OP+ALWAYS+ NP));
+
+ WR_HARPOON(p_port+hp_portctrl_0,(SCSI_PORT));
+
+ currSCCB->Sccb_scsistat = COMMAND_ST;
+
+ WR_HARPOON(p_port+hp_autostart_3, (AUTO_IMMED | CMD_ONLY_STRT));
+ SGRAM_ACCESS(p_port);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Status phase
+ *
+ * Description: Bring in the status and command complete message bytes
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseStatus(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseStatus(USHORT port, UCHAR p_card)
+#else
+void phaseStatus(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+ /* Start-up the automation to finish off this command and let the
+ isr handle the interrupt for command complete when it comes in.
+ We could wait here for the interrupt to be generated?
+ */
+
+ WR_HARPOON(port+hp_scsisig, 0x00);
+
+ WR_HARPOON(port+hp_autostart_0, (AUTO_IMMED+END_DATA_START));
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Phase Message Out
+ *
+ * Description: Send out our message (if we have one) and handle whatever
+ * else is involed.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseMsgOut(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseMsgOut(USHORT port, UCHAR p_card)
+#else
+void phaseMsgOut(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+ UCHAR message,scsiID;
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ if (currSCCB != NULL) {
+
+ message = currSCCB->Sccb_scsimsg;
+ scsiID = currSCCB->TargID;
+
+ if (message == SMDEV_RESET)
+ {
+
+
+ currTar_Info = &sccbMgrTbl[p_card][scsiID];
+ currTar_Info->TarSyncCtrl = 0;
+ sssyncv(port, scsiID, NARROW_SCSI,currTar_Info);
+
+ if (sccbMgrTbl[p_card][scsiID].TarEEValue & EE_SYNC_MASK)
+ {
+
+ sccbMgrTbl[p_card][scsiID].TarStatus &= ~TAR_SYNC_MASK;
+
+ }
+
+ if (sccbMgrTbl[p_card][scsiID].TarEEValue & EE_WIDE_SCSI)
+ {
+
+ sccbMgrTbl[p_card][scsiID].TarStatus &= ~TAR_WIDE_MASK;
+ }
+
+
+ queueFlushSccb(p_card,SCCB_COMPLETE);
+ SccbMgrTableInitTarget(p_card,scsiID);
+ }
+ else if (currSCCB->Sccb_scsistat == ABORT_ST)
+ {
+ currSCCB->HostStatus = SCCB_COMPLETE;
+ if(BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] != NULL)
+ {
+ BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ sccbMgrTbl[p_card][scsiID].TarTagQ_Cnt--;
+ }
+
+ }
+
+ else if (currSCCB->Sccb_scsistat < COMMAND_ST)
+ {
+
+
+ if(message == SMNO_OP)
+ {
+ currSCCB->Sccb_MGRFlags |= F_DEV_SELECTED;
+
+ ssel(port,p_card);
+ return;
+ }
+ }
+ else
+ {
+
+
+ if (message == SMABORT)
+
+ queueFlushSccb(p_card,SCCB_COMPLETE);
+ }
+
+ }
+ else
+ {
+ message = SMABORT;
+ }
+
+ WRW_HARPOON((port+hp_intstat), (BUS_FREE | PHASE | XFER_CNT_0));
+
+
+ WR_HARPOON(port+hp_portctrl_0, SCSI_BUS_EN);
+
+ WR_HARPOON(port+hp_scsidata_0,message);
+
+ WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+
+ ACCEPT_MSG(port);
+
+ WR_HARPOON(port+hp_portctrl_0, 0x00);
+
+ if ((message == SMABORT) || (message == SMDEV_RESET) ||
+ (message == SMABORT_TAG) )
+ {
+
+ while(!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | PHASE))) {}
+
+ if (RDW_HARPOON((port+hp_intstat)) & BUS_FREE)
+ {
+ WRW_HARPOON((port+hp_intstat), BUS_FREE);
+
+ if (currSCCB != NULL)
+ {
+
+ if((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = FALSE;
+ else
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = FALSE;
+
+ queueCmdComplete(&BL_Card[p_card],currSCCB, p_card);
+ }
+
+ else
+ {
+ BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+ }
+ }
+
+ else
+ {
+
+ sxfrp(port,p_card);
+ }
+ }
+
+ else
+ {
+
+ if(message == SMPARITY)
+ {
+ currSCCB->Sccb_scsimsg = SMNO_OP;
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ else
+ {
+ sxfrp(port,p_card);
+ }
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Message In phase
+ *
+ * Description: Bring in the message and determine what to do with it.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseMsgIn(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseMsgIn(USHORT port, UCHAR p_card)
+#else
+void phaseMsgIn(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+ UCHAR message;
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ if (BL_Card[p_card].globalFlags & F_HOST_XFER_ACT)
+ {
+
+ phaseChkFifo(port, p_card);
+ }
+
+ message = RD_HARPOON(port+hp_scsidata_0);
+ if ((message == SMDISC) || (message == SMSAVE_DATA_PTR))
+ {
+
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+END_DATA_START));
+
+ }
+
+ else
+ {
+
+ message = sfm(port,currSCCB);
+ if (message)
+ {
+
+
+ sdecm(message,port,p_card);
+
+ }
+ else
+ {
+ if(currSCCB->Sccb_scsimsg != SMPARITY)
+ ACCEPT_MSG(port);
+ WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+ }
+ }
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Illegal phase
+ *
+ * Description: Target switched to some illegal phase, so all we can do
+ * is report an error back to the host (if that is possible)
+ * and send an ABORT message to the misbehaving target.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseIllegal(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseIllegal(USHORT port, UCHAR p_card)
+#else
+void phaseIllegal(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ WR_HARPOON(port+hp_scsisig, RD_HARPOON(port+hp_scsisig));
+ if (currSCCB != NULL) {
+
+ currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
+ currSCCB->Sccb_scsistat = ABORT_ST;
+ currSCCB->Sccb_scsimsg = SMABORT;
+ }
+
+ ACCEPT_MSG_ATN(port);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Phase Check FIFO
+ *
+ * Description: Make sure data has been flushed from both FIFOs and abort
+ * the operations if necessary.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void phaseChkFifo(USHORT port, UCHAR p_card)
+#else
+void phaseChkFifo(ULONG port, UCHAR p_card)
+#endif
+{
+ ULONG xfercnt;
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ if (currSCCB->Sccb_scsistat == DATA_IN_ST)
+ {
+
+ while((!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY)) &&
+ (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY)) {}
+
+
+ if (!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY))
+ {
+ currSCCB->Sccb_ATC += currSCCB->Sccb_XferCnt;
+
+ currSCCB->Sccb_XferCnt = 0;
+
+ if ((RDW_HARPOON((port+hp_intstat)) & PARITY) &&
+ (currSCCB->HostStatus == SCCB_COMPLETE))
+ {
+ currSCCB->HostStatus = SCCB_PARITY_ERR;
+ WRW_HARPOON((port+hp_intstat), PARITY);
+ }
+
+ hostDataXferAbort(port,p_card,currSCCB);
+
+ dataXferProcessor(port, &BL_Card[p_card]);
+
+ while((!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY)) &&
+ (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY)) {}
+
+ }
+ } /*End Data In specific code. */
+
+
+
+#if defined(DOS)
+ asm { mov dx,port;
+ add dx,hp_xfercnt_2;
+ in al,dx;
+ dec dx;
+ xor ah,ah;
+ mov word ptr xfercnt+2,ax;
+ in al,dx;
+ dec dx;
+ mov ah,al;
+ in al,dx;
+ mov word ptr xfercnt,ax;
+ }
+#else
+ GET_XFER_CNT(port,xfercnt);
+#endif
+
+
+ WR_HARPOON(port+hp_xfercnt_0, 0x00);
+
+
+ WR_HARPOON(port+hp_portctrl_0, 0x00);
+
+ currSCCB->Sccb_ATC += (currSCCB->Sccb_XferCnt - xfercnt);
+
+ currSCCB->Sccb_XferCnt = xfercnt;
+
+ if ((RDW_HARPOON((port+hp_intstat)) & PARITY) &&
+ (currSCCB->HostStatus == SCCB_COMPLETE)) {
+
+ currSCCB->HostStatus = SCCB_PARITY_ERR;
+ WRW_HARPOON((port+hp_intstat), PARITY);
+ }
+
+
+ hostDataXferAbort(port,p_card,currSCCB);
+
+
+ WR_HARPOON(port+hp_fifowrite, 0x00);
+ WR_HARPOON(port+hp_fiforead, 0x00);
+ WR_HARPOON(port+hp_xferstat, 0x00);
+
+ WRW_HARPOON((port+hp_intstat), XFER_CNT_0);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Phase Bus Free
+ *
+ * Description: We just went bus free so figure out if it was
+ * because of command complete or from a disconnect.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void phaseBusFree(USHORT port, UCHAR p_card)
+#else
+void phaseBusFree(ULONG port, UCHAR p_card)
+#endif
+{
+ PSCCB currSCCB;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ if (currSCCB != NULL)
+ {
+
+ DISABLE_AUTO(port);
+
+
+ if (currSCCB->OperationCode == RESET_COMMAND)
+ {
+
+ if((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = FALSE;
+ else
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = FALSE;
+
+ queueCmdComplete(&BL_Card[p_card], currSCCB, p_card);
+
+ queueSearchSelect(&BL_Card[p_card],p_card);
+
+ }
+
+ else if(currSCCB->Sccb_scsistat == SELECT_SN_ST)
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus |=
+ (UCHAR)SYNC_SUPPORTED;
+ sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &= ~EE_SYNC_MASK;
+ }
+
+ else if(currSCCB->Sccb_scsistat == SELECT_WN_ST)
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus =
+ (sccbMgrTbl[p_card][currSCCB->TargID].
+ TarStatus & ~WIDE_ENABLED) | WIDE_NEGOCIATED;
+
+ sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &= ~EE_WIDE_SCSI;
+ }
+
+#if !defined(DOS)
+ else if(currSCCB->Sccb_scsistat == SELECT_Q_ST)
+ {
+ /* Make sure this is not a phony BUS_FREE. If we were
+ reselected or if BUSY is NOT on then this is a
+ valid BUS FREE. SRR Wednesday, 5/10/1995. */
+
+ if ((!(RD_HARPOON(port+hp_scsisig) & SCSI_BSY)) ||
+ (RDW_HARPOON((port+hp_intstat)) & RSEL))
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus &= ~TAR_TAG_Q_MASK;
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus |= TAG_Q_REJECT;
+ }
+
+ else
+ {
+ return;
+ }
+ }
+#endif
+
+ else
+ {
+
+ currSCCB->Sccb_scsistat = BUS_FREE_ST;
+
+ if (!currSCCB->HostStatus)
+ {
+ currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
+ }
+
+ if((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = FALSE;
+ else
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = FALSE;
+
+ queueCmdComplete(&BL_Card[p_card], currSCCB, p_card);
+ return;
+ }
+
+
+ BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+ } /*end if !=null */
+}
+
+
+
+
+#ident "$Id: FlashPoint.c,v 1.1 1999/04/26 05:53:56 tb Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: automate.c $
+ *
+ * Description: Functions relating to programming the automation of
+ * the HARPOON.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+extern SCCBCARD BL_Card[MAX_CARDS];
+*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Auto Load Default Map
+ *
+ * Description: Load the Automation RAM with the defualt map values.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void autoLoadDefaultMap(USHORT p_port)
+#else
+void autoLoadDefaultMap(ULONG p_port)
+#endif
+{
+#if defined(DOS)
+ USHORT map_addr;
+#else
+ ULONG map_addr;
+#endif
+
+ ARAM_ACCESS(p_port);
+ map_addr = p_port + hp_aramBase;
+
+ WRW_HARPOON(map_addr, (MPM_OP+AMSG_OUT+ 0xC0)); /*ID MESSAGE */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+AMSG_OUT+ 0x20)); /*SIMPLE TAG QUEUEING MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, RAT_OP); /*RESET ATTENTION */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+AMSG_OUT+ 0x00)); /*TAG ID MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 0 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 1 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 2 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 3 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 4 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 5 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 6 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 7 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 8 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 9 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 10 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00)); /*CDB BYTE 11 */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CPE_OP+ADATA_OUT+ DINT)); /*JUMP IF DATA OUT */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (TCB_OP+FIFO_0+ DI)); /*JUMP IF NO DATA IN FIFO */
+ map_addr +=2; /*This means AYNC DATA IN */
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_IDO_STRT)); /*STOP AND INTERRUPT */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CPE_OP+ADATA_IN+DINT)); /*JUMP IF NOT DATA IN PHZ */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CPN_OP+AMSG_IN+ ST)); /*IF NOT MSG IN CHECK 4 DATA IN */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CRD_OP+SDATA+ 0x02)); /*SAVE DATA PTR MSG? */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (BRH_OP+NOT_EQ+ DC)); /*GO CHECK FOR DISCONNECT MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MRR_OP+SDATA+ D_AR1)); /*SAVE DATA PTRS MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CPN_OP+AMSG_IN+ ST)); /*IF NOT MSG IN CHECK DATA IN */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CRD_OP+SDATA+ 0x04)); /*DISCONNECT MSG? */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (BRH_OP+NOT_EQ+ UNKNWN));/*UKNKNOWN MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MRR_OP+SDATA+ D_BUCKET));/*XFER DISCONNECT MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_ITAR_DISC));/*STOP AND INTERRUPT */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CPN_OP+ASTATUS+ UNKNWN));/*JUMP IF NOT STATUS PHZ. */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MRR_OP+SDATA+ D_AR0)); /*GET STATUS BYTE */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CPN_OP+AMSG_IN+ CC)); /*ERROR IF NOT MSG IN PHZ */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (CRD_OP+SDATA+ 0x00)); /*CHECK FOR CMD COMPLETE MSG. */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (BRH_OP+NOT_EQ+ CC)); /*ERROR IF NOT CMD COMPLETE MSG. */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (MRR_OP+SDATA+ D_BUCKET));/*GET CMD COMPLETE MSG */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_ICMD_COMP));/*END OF COMMAND */
+ map_addr +=2;
+
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_IUNKWN)); /*RECEIVED UNKNOWN MSG BYTE */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_INO_CC)); /*NO COMMAND COMPLETE AFTER STATUS */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_ITICKLE)); /*BIOS Tickled the Mgr */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_IRFAIL)); /*EXPECTED ID/TAG MESSAGES AND */
+ map_addr +=2; /* DIDN'T GET ONE */
+ WRW_HARPOON(map_addr, (CRR_OP+AR3+ S_IDREG)); /* comp SCSI SEL ID & AR3*/
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (BRH_OP+EQUAL+ 0x00)); /*SEL ID OK then Conti. */
+ map_addr +=2;
+ WRW_HARPOON(map_addr, (SSI_OP+ SSI_INO_CC)); /*NO COMMAND COMPLETE AFTER STATUS */
+
+
+
+ SGRAM_ACCESS(p_port);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Auto Command Complete
+ *
+ * Description: Post command back to host and find another command
+ * to execute.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void autoCmdCmplt(USHORT p_port, UCHAR p_card)
+#else
+void autoCmdCmplt(ULONG p_port, UCHAR p_card)
+#endif
+{
+ PSCCB currSCCB;
+ UCHAR status_byte;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+
+ status_byte = RD_HARPOON(p_port+hp_gp_reg_0);
+
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUN_CA = FALSE;
+
+ if (status_byte != SSGOOD) {
+
+ if (status_byte == SSQ_FULL) {
+
+
+ if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+ }
+ else
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+ if(currSCCB->Sccb_tag)
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ }else
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+
+ currSCCB->Sccb_MGRFlags |= F_STATUSLOADED;
+
+ queueSelectFail(&BL_Card[p_card],p_card);
+
+ return;
+ }
+
+ if(currSCCB->Sccb_scsistat == SELECT_SN_ST)
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus |=
+ (UCHAR)SYNC_SUPPORTED;
+
+ sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &= ~EE_SYNC_MASK;
+ BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+ if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+ }
+ else
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+ if(currSCCB->Sccb_tag)
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ }else
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+ return;
+
+ }
+
+ if(currSCCB->Sccb_scsistat == SELECT_WN_ST)
+ {
+
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus =
+ (sccbMgrTbl[p_card][currSCCB->TargID].
+ TarStatus & ~WIDE_ENABLED) | WIDE_NEGOCIATED;
+
+ sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &= ~EE_WIDE_SCSI;
+ BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+ if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+ }
+ else
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+ if(currSCCB->Sccb_tag)
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ }else
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+ return;
+
+ }
+
+ if (status_byte == SSCHECK)
+ {
+ if(BL_Card[p_card].globalFlags & F_DO_RENEGO)
+ {
+ if (sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue & EE_SYNC_MASK)
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus &= ~TAR_SYNC_MASK;
+ }
+ if (sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue & EE_WIDE_SCSI)
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarStatus &= ~TAR_WIDE_MASK;
+ }
+ }
+ }
+
+ if (!(currSCCB->Sccb_XferState & F_AUTO_SENSE)) {
+
+ currSCCB->SccbStatus = SCCB_ERROR;
+ currSCCB->TargetStatus = status_byte;
+
+ if (status_byte == SSCHECK) {
+
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUN_CA
+ = TRUE;
+
+
+#if (FW_TYPE==_SCCB_MGR_)
+ if (currSCCB->RequestSenseLength != NO_AUTO_REQUEST_SENSE) {
+
+ if (currSCCB->RequestSenseLength == 0)
+ currSCCB->RequestSenseLength = 14;
+
+ ssenss(&BL_Card[p_card]);
+ BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+ if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+ }
+ else
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+ if(currSCCB->Sccb_tag)
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ }else
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+ return;
+ }
+#else
+ if ((!(currSCCB->Sccb_ucb_ptr->UCB_opcode & OPC_NO_AUTO_SENSE)) &&
+ (currSCCB->RequestSenseLength))
+ {
+ ssenss(&BL_Card[p_card]);
+ BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+ if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+ }
+ else
+ {
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+ if(currSCCB->Sccb_tag)
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+ }else
+ {
+ if(BL_Card[p_card].discQCount != 0)
+ BL_Card[p_card].discQCount--;
+ BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+ return;
+ }
+
+#endif
+ }
+ }
+ }
+
+
+ if((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = FALSE;
+ else
+ sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = FALSE;
+
+
+ queueCmdComplete(&BL_Card[p_card], currSCCB, p_card);
+}
+#ident "$Id: FlashPoint.c,v 1.1 1999/04/26 05:53:56 tb Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: busmstr.c $
+ *
+ * Description: Functions to start, stop, and abort BusMaster operations.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+*/
+
+#define SHORT_WAIT 0x0000000F
+#define LONG_WAIT 0x0000FFFFL
+
+#if defined(BUGBUG)
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Data Transfer Processor
+ *
+ * Description: This routine performs two tasks.
+ * (1) Start data transfer by calling HOST_DATA_XFER_START
+ * function. Once data transfer is started, (2) Depends
+ * on the type of data transfer mode Scatter/Gather mode
+ * or NON Scatter/Gather mode. In NON Scatter/Gather mode,
+ * this routine checks Sccb_MGRFlag (F_HOST_XFER_ACT bit) for
+ * data transfer done. In Scatter/Gather mode, this routine
+ * checks bus master command complete and dual rank busy
+ * bit to keep chaining SC transfer command. Similarly,
+ * in Scatter/Gather mode, it checks Sccb_MGRFlag
+ * (F_HOST_XFER_ACT bit) for data transfer done.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void dataXferProcessor(USHORT port, PSCCBcard pCurrCard)
+#else
+void dataXferProcessor(ULONG port, PSCCBcard pCurrCard)
+#endif
+{
+ PSCCB currSCCB;
+
+ currSCCB = pCurrCard->currentSCCB;
+
+ if (currSCCB->Sccb_XferState & F_SG_XFER)
+ {
+ if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
+
+ {
+ currSCCB->Sccb_sgseg += (UCHAR)SG_BUF_CNT;
+ currSCCB->Sccb_SGoffset = 0x00;
+ }
+ pCurrCard->globalFlags |= F_HOST_XFER_ACT;
+
+ busMstrSGDataXferStart(port, currSCCB);
+ }
+
+ else
+ {
+ if (!(pCurrCard->globalFlags & F_HOST_XFER_ACT))
+ {
+ pCurrCard->globalFlags |= F_HOST_XFER_ACT;
+
+ busMstrDataXferStart(port, currSCCB);
+ }
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: BusMaster Scatter Gather Data Transfer Start
+ *
+ * Description:
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void busMstrSGDataXferStart(USHORT p_port, PSCCB pcurrSCCB)
+#else
+void busMstrSGDataXferStart(ULONG p_port, PSCCB pcurrSCCB)
+#endif
+{
+ ULONG count,addr,tmpSGCnt;
+ UINT sg_index;
+ UCHAR sg_count, i;
+#if defined(DOS)
+ USHORT reg_offset;
+#else
+ ULONG reg_offset;
+#endif
+
+
+ if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
+
+ count = ((ULONG) HOST_RD_CMD)<<24;
+ }
+
+ else {
+ count = ((ULONG) HOST_WRT_CMD)<<24;
+ }
+
+ sg_count = 0;
+ tmpSGCnt = 0;
+ sg_index = pcurrSCCB->Sccb_sgseg;
+ reg_offset = hp_aramBase;
+
+
+ i = (UCHAR) (RD_HARPOON(p_port+hp_page_ctrl) & ~(SGRAM_ARAM|SCATTER_EN));
+
+
+ WR_HARPOON(p_port+hp_page_ctrl, i);
+
+ while ((sg_count < (UCHAR)SG_BUF_CNT) &&
+ ((ULONG)(sg_index * (UINT)SG_ELEMENT_SIZE) < pcurrSCCB->DataLength) ) {
+
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+ tmpSGCnt += *(((ULONG far *)pcurrSCCB->DataPointer)+
+ (sg_index * 2));
+
+ count |= *(((ULONG far *)pcurrSCCB->DataPointer)+
+ (sg_index * 2));
+
+ addr = *(((ULONG far *)pcurrSCCB->DataPointer)+
+ ((sg_index * 2) + 1));
+
+#else
+ tmpSGCnt += *(((ULONG *)pcurrSCCB->DataPointer)+
+ (sg_index * 2));
+
+ count |= *(((ULONG *)pcurrSCCB->DataPointer)+
+ (sg_index * 2));
+
+ addr = *(((ULONG *)pcurrSCCB->DataPointer)+
+ ((sg_index * 2) + 1));
+#endif
+
+
+ if ((!sg_count) && (pcurrSCCB->Sccb_SGoffset)) {
+
+ addr += ((count & 0x00FFFFFFL) - pcurrSCCB->Sccb_SGoffset);
+ count = (count & 0xFF000000L) | pcurrSCCB->Sccb_SGoffset;
+
+ tmpSGCnt = count & 0x00FFFFFFL;
+ }
+
+ WR_HARP32(p_port,reg_offset,addr);
+ reg_offset +=4;
+
+ WR_HARP32(p_port,reg_offset,count);
+ reg_offset +=4;
+
+ count &= 0xFF000000L;
+ sg_index++;
+ sg_count++;
+
+ } /*End While */
+
+ pcurrSCCB->Sccb_XferCnt = tmpSGCnt;
+
+ WR_HARPOON(p_port+hp_sg_addr,(sg_count<<4));
+
+ if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
+
+ WR_HARP32(p_port,hp_xfercnt_0,tmpSGCnt);
+
+
+ WR_HARPOON(p_port+hp_portctrl_0,(DMA_PORT | SCSI_PORT | SCSI_INBIT));
+ WR_HARPOON(p_port+hp_scsisig, S_DATAI_PH);
+ }
+
+ else {
+
+
+ if ((!(RD_HARPOON(p_port+hp_synctarg_0) & NARROW_SCSI)) &&
+ (tmpSGCnt & 0x000000001))
+ {
+
+ pcurrSCCB->Sccb_XferState |= F_ODD_BALL_CNT;
+ tmpSGCnt--;
+ }
+
+
+ WR_HARP32(p_port,hp_xfercnt_0,tmpSGCnt);
+
+ WR_HARPOON(p_port+hp_portctrl_0,(SCSI_PORT | DMA_PORT | DMA_RD));
+ WR_HARPOON(p_port+hp_scsisig, S_DATAO_PH);
+ }
+
+
+ WR_HARPOON(p_port+hp_page_ctrl, (UCHAR) (i | SCATTER_EN));
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: BusMaster Data Transfer Start
+ *
+ * Description:
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void busMstrDataXferStart(USHORT p_port, PSCCB pcurrSCCB)
+#else
+void busMstrDataXferStart(ULONG p_port, PSCCB pcurrSCCB)
+#endif
+{
+ ULONG addr,count;
+
+ if (!(pcurrSCCB->Sccb_XferState & F_AUTO_SENSE)) {
+
+ count = pcurrSCCB->Sccb_XferCnt;
+
+ addr = (ULONG) pcurrSCCB->DataPointer + pcurrSCCB->Sccb_ATC;
+ }
+
+ else {
+ addr = pcurrSCCB->SensePointer;
+ count = pcurrSCCB->RequestSenseLength;
+
+ }
+
+#if defined(DOS)
+ asm { mov dx,p_port;
+ mov ax,word ptr count;
+ add dx,hp_xfer_cnt_lo;
+ out dx,al;
+ inc dx;
+ xchg ah,al
+ out dx,al;
+ inc dx;
+ mov ax,word ptr count+2;
+ out dx,al;
+ inc dx;
+ inc dx;
+ mov ax,word ptr addr;
+ out dx,al;
+ inc dx;
+ xchg ah,al
+ out dx,al;
+ inc dx;
+ mov ax,word ptr addr+2;
+ out dx,al;
+ inc dx;
+ xchg ah,al
+ out dx,al;
+ }
+
+ WR_HARP32(p_port,hp_xfercnt_0,count);
+
+#else
+ HP_SETUP_ADDR_CNT(p_port,addr,count);
+#endif
+
+
+ if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
+
+ WR_HARPOON(p_port+hp_portctrl_0,(DMA_PORT | SCSI_PORT | SCSI_INBIT));
+ WR_HARPOON(p_port+hp_scsisig, S_DATAI_PH);
+
+ WR_HARPOON(p_port+hp_xfer_cmd,
+ (XFER_DMA_HOST | XFER_HOST_AUTO | XFER_DMA_8BIT));
+ }
+
+ else {
+
+ WR_HARPOON(p_port+hp_portctrl_0,(SCSI_PORT | DMA_PORT | DMA_RD));
+ WR_HARPOON(p_port+hp_scsisig, S_DATAO_PH);
+
+ WR_HARPOON(p_port+hp_xfer_cmd,
+ (XFER_HOST_DMA | XFER_HOST_AUTO | XFER_DMA_8BIT));
+
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: BusMaster Timeout Handler
+ *
+ * Description: This function is called after a bus master command busy time
+ * out is detected. This routines issue halt state machine
+ * with a software time out for command busy. If command busy
+ * is still asserted at the end of the time out, it issues
+ * hard abort with another software time out. It hard abort
+ * command busy is also time out, it'll just give up.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+UCHAR busMstrTimeOut(USHORT p_port)
+#else
+UCHAR busMstrTimeOut(ULONG p_port)
+#endif
+{
+ ULONG timeout;
+
+ timeout = LONG_WAIT;
+
+ WR_HARPOON(p_port+hp_sys_ctrl, HALT_MACH);
+
+ while ((!(RD_HARPOON(p_port+hp_ext_status) & CMD_ABORTED)) && timeout--) {}
+
+
+
+ if (RD_HARPOON(p_port+hp_ext_status) & BM_CMD_BUSY) {
+ WR_HARPOON(p_port+hp_sys_ctrl, HARD_ABORT);
+
+ timeout = LONG_WAIT;
+ while ((RD_HARPOON(p_port+hp_ext_status) & BM_CMD_BUSY) && timeout--) {}
+ }
+
+ RD_HARPOON(p_port+hp_int_status); /*Clear command complete */
+
+ if (RD_HARPOON(p_port+hp_ext_status) & BM_CMD_BUSY) {
+ return(TRUE);
+ }
+
+ else {
+ return(FALSE);
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Host Data Transfer Abort
+ *
+ * Description: Abort any in progress transfer.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void hostDataXferAbort(USHORT port, UCHAR p_card, PSCCB pCurrSCCB)
+#else
+void hostDataXferAbort(ULONG port, UCHAR p_card, PSCCB pCurrSCCB)
+#endif
+{
+
+ ULONG timeout;
+ ULONG remain_cnt;
+ UINT sg_ptr;
+
+ BL_Card[p_card].globalFlags &= ~F_HOST_XFER_ACT;
+
+ if (pCurrSCCB->Sccb_XferState & F_AUTO_SENSE) {
+
+
+ if (!(RD_HARPOON(port+hp_int_status) & INT_CMD_COMPL)) {
+
+ WR_HARPOON(port+hp_bm_ctrl, (RD_HARPOON(port+hp_bm_ctrl) | FLUSH_XFER_CNTR));
+ timeout = LONG_WAIT;
+
+ while ((RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) && timeout--) {}
+
+ WR_HARPOON(port+hp_bm_ctrl, (RD_HARPOON(port+hp_bm_ctrl) & ~FLUSH_XFER_CNTR));
+
+ if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+ if (busMstrTimeOut(port)) {
+
+ if (pCurrSCCB->HostStatus == 0x00)
+
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+
+ }
+
+ if (RD_HARPOON(port+hp_int_status) & INT_EXT_STATUS)
+
+ if (RD_HARPOON(port+hp_ext_status) & BAD_EXT_STATUS)
+
+ if (pCurrSCCB->HostStatus == 0x00)
+
+ {
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+#if defined(BUGBUG)
+ WR_HARPOON(port+hp_dual_addr_lo,
+ RD_HARPOON(port+hp_ext_status));
+#endif
+ }
+ }
+ }
+ }
+
+ else if (pCurrSCCB->Sccb_XferCnt) {
+
+ if (pCurrSCCB->Sccb_XferState & F_SG_XFER) {
+
+
+ WR_HARPOON(port+hp_page_ctrl, (RD_HARPOON(port+hp_page_ctrl) &
+ ~SCATTER_EN));
+
+ WR_HARPOON(port+hp_sg_addr,0x00);
+
+ sg_ptr = pCurrSCCB->Sccb_sgseg + SG_BUF_CNT;
+
+ if (sg_ptr > (UINT)(pCurrSCCB->DataLength / SG_ELEMENT_SIZE)) {
+
+ sg_ptr = (UINT)(pCurrSCCB->DataLength / SG_ELEMENT_SIZE);
+ }
+
+ remain_cnt = pCurrSCCB->Sccb_XferCnt;
+
+ while (remain_cnt < 0x01000000L) {
+
+ sg_ptr--;
+
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+ if (remain_cnt > (ULONG)(*(((ULONG far *)pCurrSCCB->
+ DataPointer) + (sg_ptr * 2)))) {
+
+ remain_cnt -= (ULONG)(*(((ULONG far *)pCurrSCCB->
+ DataPointer) + (sg_ptr * 2)));
+ }
+
+#else
+ if (remain_cnt > (ULONG)(*(((ULONG *)pCurrSCCB->
+ DataPointer) + (sg_ptr * 2)))) {
+
+ remain_cnt -= (ULONG)(*(((ULONG *)pCurrSCCB->
+ DataPointer) + (sg_ptr * 2)));
+ }
+#endif
+
+ else {
+
+ break;
+ }
+ }
+
+
+
+ if (remain_cnt < 0x01000000L) {
+
+
+ pCurrSCCB->Sccb_SGoffset = remain_cnt;
+
+ pCurrSCCB->Sccb_sgseg = (USHORT)sg_ptr;
+
+
+ if ((ULONG)(sg_ptr * SG_ELEMENT_SIZE) == pCurrSCCB->DataLength
+ && (remain_cnt == 0))
+
+ pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
+ }
+
+ else {
+
+
+ if (pCurrSCCB->HostStatus == 0x00) {
+
+ pCurrSCCB->HostStatus = SCCB_GROSS_FW_ERR;
+ }
+ }
+ }
+
+
+ if (!(pCurrSCCB->Sccb_XferState & F_HOST_XFER_DIR)) {
+
+
+ if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+ busMstrTimeOut(port);
+ }
+
+ else {
+
+ if (RD_HARPOON(port+hp_int_status) & INT_EXT_STATUS) {
+
+ if (RD_HARPOON(port+hp_ext_status) & BAD_EXT_STATUS) {
+
+ if (pCurrSCCB->HostStatus == 0x00) {
+
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+#if defined(BUGBUG)
+ WR_HARPOON(port+hp_dual_addr_lo,
+ RD_HARPOON(port+hp_ext_status));
+#endif
+ }
+ }
+ }
+
+ }
+ }
+
+ else {
+
+
+ if ((RD_HARPOON(port+hp_fifo_cnt)) >= BM_THRESHOLD) {
+
+ timeout = SHORT_WAIT;
+
+ while ((RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) &&
+ ((RD_HARPOON(port+hp_fifo_cnt)) >= BM_THRESHOLD) &&
+ timeout--) {}
+ }
+
+ if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+ WR_HARPOON(port+hp_bm_ctrl, (RD_HARPOON(port+hp_bm_ctrl) |
+ FLUSH_XFER_CNTR));
+
+ timeout = LONG_WAIT;
+
+ while ((RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) &&
+ timeout--) {}
+
+ WR_HARPOON(port+hp_bm_ctrl, (RD_HARPOON(port+hp_bm_ctrl) &
+ ~FLUSH_XFER_CNTR));
+
+
+ if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+ if (pCurrSCCB->HostStatus == 0x00) {
+
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+ }
+
+ busMstrTimeOut(port);
+ }
+ }
+
+ if (RD_HARPOON(port+hp_int_status) & INT_EXT_STATUS) {
+
+ if (RD_HARPOON(port+hp_ext_status) & BAD_EXT_STATUS) {
+
+ if (pCurrSCCB->HostStatus == 0x00) {
+
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+#if defined(BUGBUG)
+ WR_HARPOON(port+hp_dual_addr_lo,
+ RD_HARPOON(port+hp_ext_status));
+#endif
+ }
+ }
+ }
+ }
+
+ }
+
+ else {
+
+
+ if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+ timeout = LONG_WAIT;
+
+ while ((RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) && timeout--) {}
+
+ if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+ if (pCurrSCCB->HostStatus == 0x00) {
+
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+ }
+
+ busMstrTimeOut(port);
+ }
+ }
+
+
+ if (RD_HARPOON(port+hp_int_status) & INT_EXT_STATUS) {
+
+ if (RD_HARPOON(port+hp_ext_status) & BAD_EXT_STATUS) {
+
+ if (pCurrSCCB->HostStatus == 0x00) {
+
+ pCurrSCCB->HostStatus = SCCB_BM_ERR;
+#if defined(BUGBUG)
+ WR_HARPOON(port+hp_dual_addr_lo,
+ RD_HARPOON(port+hp_ext_status));
+#endif
+ }
+ }
+
+ }
+
+ if (pCurrSCCB->Sccb_XferState & F_SG_XFER) {
+
+ WR_HARPOON(port+hp_page_ctrl, (RD_HARPOON(port+hp_page_ctrl) &
+ ~SCATTER_EN));
+
+ WR_HARPOON(port+hp_sg_addr,0x00);
+
+ pCurrSCCB->Sccb_sgseg += SG_BUF_CNT;
+
+ pCurrSCCB->Sccb_SGoffset = 0x00;
+
+
+ if ((ULONG)(pCurrSCCB->Sccb_sgseg * SG_ELEMENT_SIZE) >=
+ pCurrSCCB->DataLength) {
+
+ pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
+
+ pCurrSCCB->Sccb_sgseg = (USHORT)(pCurrSCCB->DataLength / SG_ELEMENT_SIZE);
+
+ }
+ }
+
+ else {
+
+ if (!(pCurrSCCB->Sccb_XferState & F_AUTO_SENSE))
+
+ pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
+ }
+ }
+
+ WR_HARPOON(port+hp_int_mask,(INT_CMD_COMPL | SCSI_INTERRUPT));
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Host Data Transfer Restart
+ *
+ * Description: Reset the available count due to a restore data
+ * pointers message.
+ *
+ *---------------------------------------------------------------------*/
+void hostDataXferRestart(PSCCB currSCCB)
+{
+ ULONG data_count;
+ UINT sg_index;
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+ ULONG far *sg_ptr;
+#else
+ ULONG *sg_ptr;
+#endif
+
+ if (currSCCB->Sccb_XferState & F_SG_XFER) {
+
+ currSCCB->Sccb_XferCnt = 0;
+
+ sg_index = 0xffff; /*Index by long words into sg list. */
+ data_count = 0; /*Running count of SG xfer counts. */
+
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+ sg_ptr = (ULONG far *)currSCCB->DataPointer;
+#else
+ sg_ptr = (ULONG *)currSCCB->DataPointer;
+#endif
+
+ while (data_count < currSCCB->Sccb_ATC) {
+
+ sg_index++;
+ data_count += *(sg_ptr+(sg_index * 2));
+ }
+
+ if (data_count == currSCCB->Sccb_ATC) {
+
+ currSCCB->Sccb_SGoffset = 0;
+ sg_index++;
+ }
+
+ else {
+ currSCCB->Sccb_SGoffset = data_count - currSCCB->Sccb_ATC;
+ }
+
+ currSCCB->Sccb_sgseg = (USHORT)sg_index;
+ }
+
+ else {
+ currSCCB->Sccb_XferCnt = currSCCB->DataLength - currSCCB->Sccb_ATC;
+ }
+}
+#ident "$Id: FlashPoint.c,v 1.1 1999/04/26 05:53:56 tb Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: scam.c $
+ *
+ * Description: Functions relating to handling of the SCAM selection
+ * and the determination of the SCSI IDs to be assigned
+ * to all perspective SCSI targets.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <eeprom.h>*/
+/*#include <harpoon.h>*/
+
+
+
+/*
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR];
+extern NVRAMINFO nvRamInfo[MAX_MB_CARDS];
+#if defined(DOS) || defined(OS2)
+extern UCHAR temp_id_string[ID_STRING_LENGTH];
+#endif
+extern UCHAR scamHAString[];
+*/
+/*---------------------------------------------------------------------
+ *
+ * Function: scini
+ *
+ * Description: Setup all data structures necessary for SCAM selection.
+ *
+ *---------------------------------------------------------------------*/
+
+void scini(UCHAR p_card, UCHAR p_our_id, UCHAR p_power_up)
+{
+
+#if defined(SCAM_LEV_2)
+ UCHAR loser,assigned_id;
+#endif
+#if defined(DOS)
+
+ USHORT p_port;
+#else
+ ULONG p_port;
+#endif
+
+ UCHAR i,k,ScamFlg ;
+ PSCCBcard currCard;
+ PNVRamInfo pCurrNvRam;
+
+ currCard = &BL_Card[p_card];
+ p_port = currCard->ioPort;
+ pCurrNvRam = currCard->pNvRamInfo;
+
+
+ if(pCurrNvRam){
+ ScamFlg = pCurrNvRam->niScamConf;
+ i = pCurrNvRam->niSysConf;
+ }
+ else{
+ ScamFlg = (UCHAR) utilEERead(p_port, SCAM_CONFIG/2);
+ i = (UCHAR)(utilEERead(p_port, (SYSTEM_CONFIG/2)));
+ }
+ if(!(i & 0x02)) /* check if reset bus in AutoSCSI parameter set */
+ return;
+
+ inisci(p_card,p_port, p_our_id);
+
+ /* Force to wait 1 sec after SCSI bus reset. Some SCAM device FW
+ too slow to return to SCAM selection */
+
+ /* if (p_power_up)
+ Wait1Second(p_port);
+ else
+ Wait(p_port, TO_250ms); */
+
+ Wait1Second(p_port);
+
+#if defined(SCAM_LEV_2)
+
+ if ((ScamFlg & SCAM_ENABLED) && (ScamFlg & SCAM_LEVEL2))
+ {
+ while (!(scarb(p_port,INIT_SELTD))) {}
+
+ scsel(p_port);
+
+ do {
+ scxferc(p_port,SYNC_PTRN);
+ scxferc(p_port,DOM_MSTR);
+ loser = scsendi(p_port,&scamInfo[p_our_id].id_string[0]);
+ } while ( loser == 0xFF );
+
+ scbusf(p_port);
+
+ if ((p_power_up) && (!loser))
+ {
+ sresb(p_port,p_card);
+ Wait(p_port, TO_250ms);
+
+ while (!(scarb(p_port,INIT_SELTD))) {}
+
+ scsel(p_port);
+
+ do {
+ scxferc(p_port, SYNC_PTRN);
+ scxferc(p_port, DOM_MSTR);
+ loser = scsendi(p_port,&scamInfo[p_our_id].
+ id_string[0]);
+ } while ( loser == 0xFF );
+
+ scbusf(p_port);
+ }
+ }
+
+ else
+ {
+ loser = FALSE;
+ }
+
+
+ if (!loser)
+ {
+
+#endif /* SCAM_LEV_2 */
+
+ scamInfo[p_our_id].state = ID_ASSIGNED;
+
+
+ if (ScamFlg & SCAM_ENABLED)
+ {
+
+ for (i=0; i < MAX_SCSI_TAR; i++)
+ {
+ if ((scamInfo[i].state == ID_UNASSIGNED) ||
+ (scamInfo[i].state == ID_UNUSED))
+ {
+ if (scsell(p_port,i))
+ {
+ scamInfo[i].state = LEGACY;
+ if ((scamInfo[i].id_string[0] != 0xFF) ||
+ (scamInfo[i].id_string[1] != 0xFA))
+ {
+
+ scamInfo[i].id_string[0] = 0xFF;
+ scamInfo[i].id_string[1] = 0xFA;
+ if(pCurrNvRam == NULL)
+ currCard->globalFlags |= F_UPDATE_EEPROM;
+ }
+ }
+ }
+ }
+
+ sresb(p_port,p_card);
+ Wait1Second(p_port);
+ while (!(scarb(p_port,INIT_SELTD))) {}
+ scsel(p_port);
+ scasid(p_card, p_port);
+ }
+
+#if defined(SCAM_LEV_2)
+
+ }
+
+ else if ((loser) && (ScamFlg & SCAM_ENABLED))
+ {
+ scamInfo[p_our_id].id_string[0] = SLV_TYPE_CODE0;
+ assigned_id = FALSE;
+ scwtsel(p_port);
+
+ do {
+ while (scxferc(p_port,0x00) != SYNC_PTRN) {}
+
+ i = scxferc(p_port,0x00);
+ if (i == ASSIGN_ID)
+ {
+ if (!(scsendi(p_port,&scamInfo[p_our_id].id_string[0])))
+ {
+ i = scxferc(p_port,0x00);
+ if (scvalq(i))
+ {
+ k = scxferc(p_port,0x00);
+
+ if (scvalq(k))
+ {
+ currCard->ourId =
+ ((UCHAR)(i<<3)+(k & (UCHAR)7)) & (UCHAR) 0x3F;
+ inisci(p_card, p_port, p_our_id);
+ scamInfo[currCard->ourId].state = ID_ASSIGNED;
+ scamInfo[currCard->ourId].id_string[0]
+ = SLV_TYPE_CODE0;
+ assigned_id = TRUE;
+ }
+ }
+ }
+ }
+
+ else if (i == SET_P_FLAG)
+ {
+ if (!(scsendi(p_port,
+ &scamInfo[p_our_id].id_string[0])))
+ scamInfo[p_our_id].id_string[0] |= 0x80;
+ }
+ }while (!assigned_id);
+
+ while (scxferc(p_port,0x00) != CFG_CMPLT) {}
+ }
+
+#endif /* SCAM_LEV_2 */
+ if (ScamFlg & SCAM_ENABLED)
+ {
+ scbusf(p_port);
+ if (currCard->globalFlags & F_UPDATE_EEPROM)
+ {
+ scsavdi(p_card, p_port);
+ currCard->globalFlags &= ~F_UPDATE_EEPROM;
+ }
+ }
+
+
+#if defined(DOS)
+ for (i=0; i < MAX_SCSI_TAR; i++)
+ {
+ if (((ScamFlg & SCAM_ENABLED) && (scamInfo[i].state == LEGACY))
+ || (i != p_our_id))
+ {
+ scsellDOS(p_port,i);
+ }
+ }
+#endif
+
+/*
+ for (i=0,k=0; i < MAX_SCSI_TAR; i++)
+ {
+ if ((scamInfo[i].state == ID_ASSIGNED) ||
+ (scamInfo[i].state == LEGACY))
+ k++;
+ }
+
+ if (k==2)
+ currCard->globalFlags |= F_SINGLE_DEVICE;
+ else
+ currCard->globalFlags &= ~F_SINGLE_DEVICE;
+*/
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scarb
+ *
+ * Description: Gain control of the bus and wait SCAM select time (250ms)
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+int scarb(USHORT p_port, UCHAR p_sel_type)
+#else
+int scarb(ULONG p_port, UCHAR p_sel_type)
+#endif
+{
+ if (p_sel_type == INIT_SELTD)
+ {
+
+ while (RD_HARPOON(p_port+hp_scsisig) & (SCSI_SEL | SCSI_BSY)) {}
+
+
+ if (RD_HARPOON(p_port+hp_scsisig) & SCSI_SEL)
+ return(FALSE);
+
+ if (RD_HARPOON(p_port+hp_scsidata_0) != 00)
+ return(FALSE);
+
+ WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) | SCSI_BSY));
+
+ if (RD_HARPOON(p_port+hp_scsisig) & SCSI_SEL) {
+
+ WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) &
+ ~SCSI_BSY));
+ return(FALSE);
+ }
+
+
+ WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) | SCSI_SEL));
+
+ if (RD_HARPOON(p_port+hp_scsidata_0) != 00) {
+
+ WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) &
+ ~(SCSI_BSY | SCSI_SEL)));
+ return(FALSE);
+ }
+ }
+
+
+ WR_HARPOON(p_port+hp_clkctrl_0, (RD_HARPOON(p_port+hp_clkctrl_0)
+ & ~ACTdeassert));
+ WR_HARPOON(p_port+hp_scsireset, SCAM_EN);
+ WR_HARPOON(p_port+hp_scsidata_0, 0x00);
+#if defined(WIDE_SCSI)
+ WR_HARPOON(p_port+hp_scsidata_1, 0x00);
+#endif
+ WR_HARPOON(p_port+hp_portctrl_0, SCSI_BUS_EN);
+
+ WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) | SCSI_MSG));
+
+ WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig)
+ & ~SCSI_BSY));
+
+ Wait(p_port,TO_250ms);
+
+ return(TRUE);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scbusf
+ *
+ * Description: Release the SCSI bus and disable SCAM selection.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scbusf(USHORT p_port)
+#else
+void scbusf(ULONG p_port)
+#endif
+{
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE));
+
+
+ WR_HARPOON(p_port+hp_scsidata_0, 0x00);
+
+ WR_HARPOON(p_port+hp_portctrl_0, (RD_HARPOON(p_port+hp_portctrl_0)
+ & ~SCSI_BUS_EN));
+
+ WR_HARPOON(p_port+hp_scsisig, 0x00);
+
+
+ WR_HARPOON(p_port+hp_scsireset, (RD_HARPOON(p_port+hp_scsireset)
+ & ~SCAM_EN));
+
+ WR_HARPOON(p_port+hp_clkctrl_0, (RD_HARPOON(p_port+hp_clkctrl_0)
+ | ACTdeassert));
+
+#if defined(SCAM_LEV_2)
+ WRW_HARPOON((p_port+hp_intstat), (BUS_FREE | AUTO_INT | SCAM_SEL));
+#else
+ WRW_HARPOON((p_port+hp_intstat), (BUS_FREE | AUTO_INT));
+#endif
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scasid
+ *
+ * Description: Assign an ID to all the SCAM devices.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scasid(UCHAR p_card, USHORT p_port)
+#else
+void scasid(UCHAR p_card, ULONG p_port)
+#endif
+{
+#if defined(DOS) || defined(OS2)
+ /* Use external defined in global space area, instead of Stack
+ space. WIN/95 DOS doesnot work TINY mode. The OS doesnot intialize
+ SS equal to DS. Thus the array allocated on stack doesnot get
+ access correctly */
+#else
+ UCHAR temp_id_string[ID_STRING_LENGTH];
+#endif
+
+ UCHAR i,k,scam_id;
+ UCHAR crcBytes[3];
+ PNVRamInfo pCurrNvRam;
+ ushort_ptr pCrcBytes;
+
+ pCurrNvRam = BL_Card[p_card].pNvRamInfo;
+
+ i=FALSE;
+
+ while (!i)
+ {
+
+ for (k=0; k < ID_STRING_LENGTH; k++)
+ {
+ temp_id_string[k] = (UCHAR) 0x00;
+ }
+
+ scxferc(p_port,SYNC_PTRN);
+ scxferc(p_port,ASSIGN_ID);
+
+ if (!(sciso(p_port,&temp_id_string[0])))
+ {
+ if(pCurrNvRam){
+ pCrcBytes = (ushort_ptr)&crcBytes[0];
+ *pCrcBytes = CalcCrc16(&temp_id_string[0]);
+ crcBytes[2] = CalcLrc(&temp_id_string[0]);
+ temp_id_string[1] = crcBytes[2];
+ temp_id_string[2] = crcBytes[0];
+ temp_id_string[3] = crcBytes[1];
+ for(k = 4; k < ID_STRING_LENGTH; k++)
+ temp_id_string[k] = (UCHAR) 0x00;
+ }
+ i = scmachid(p_card,temp_id_string);
+
+ if (i == CLR_PRIORITY)
+ {
+ scxferc(p_port,MISC_CODE);
+ scxferc(p_port,CLR_P_FLAG);
+ i = FALSE; /*Not the last ID yet. */
+ }
+
+ else if (i != NO_ID_AVAIL)
+ {
+ if (i < 8 )
+ scxferc(p_port,ID_0_7);
+ else
+ scxferc(p_port,ID_8_F);
+
+ scam_id = (i & (UCHAR) 0x07);
+
+
+ for (k=1; k < 0x08; k <<= 1)
+ if (!( k & i ))
+ scam_id += 0x08; /*Count number of zeros in DB0-3. */
+
+ scxferc(p_port,scam_id);
+
+ i = FALSE; /*Not the last ID yet. */
+ }
+ }
+
+ else
+ {
+ i = TRUE;
+ }
+
+ } /*End while */
+
+ scxferc(p_port,SYNC_PTRN);
+ scxferc(p_port,CFG_CMPLT);
+}
+
+
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scsel
+ *
+ * Description: Select all the SCAM devices.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scsel(USHORT p_port)
+#else
+void scsel(ULONG p_port)
+#endif
+{
+
+ WR_HARPOON(p_port+hp_scsisig, SCSI_SEL);
+ scwiros(p_port, SCSI_MSG);
+
+ WR_HARPOON(p_port+hp_scsisig, (SCSI_SEL | SCSI_BSY));
+
+
+ WR_HARPOON(p_port+hp_scsisig, (SCSI_SEL | SCSI_BSY | SCSI_IOBIT | SCSI_CD));
+ WR_HARPOON(p_port+hp_scsidata_0, (UCHAR)(RD_HARPOON(p_port+hp_scsidata_0) |
+ (UCHAR)(BIT(7)+BIT(6))));
+
+
+ WR_HARPOON(p_port+hp_scsisig, (SCSI_BSY | SCSI_IOBIT | SCSI_CD));
+ scwiros(p_port, SCSI_SEL);
+
+ WR_HARPOON(p_port+hp_scsidata_0, (UCHAR)(RD_HARPOON(p_port+hp_scsidata_0) &
+ ~(UCHAR)BIT(6)));
+ scwirod(p_port, BIT(6));
+
+ WR_HARPOON(p_port+hp_scsisig, (SCSI_SEL | SCSI_BSY | SCSI_IOBIT | SCSI_CD));
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scxferc
+ *
+ * Description: Handshake the p_data (DB4-0) across the bus.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR scxferc(USHORT p_port, UCHAR p_data)
+#else
+UCHAR scxferc(ULONG p_port, UCHAR p_data)
+#endif
+{
+ UCHAR curr_data, ret_data;
+
+ curr_data = p_data | BIT(7) | BIT(5); /*Start with DB7 & DB5 asserted. */
+
+ WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+ curr_data &= ~BIT(7);
+
+ WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+ scwirod(p_port,BIT(7)); /*Wait for DB7 to be released. */
+ while (!(RD_HARPOON(p_port+hp_scsidata_0) & BIT(5)));
+
+ ret_data = (RD_HARPOON(p_port+hp_scsidata_0) & (UCHAR) 0x1F);
+
+ curr_data |= BIT(6);
+
+ WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+ curr_data &= ~BIT(5);
+
+ WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+ scwirod(p_port,BIT(5)); /*Wait for DB5 to be released. */
+
+ curr_data &= ~(BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0)); /*Release data bits */
+ curr_data |= BIT(7);
+
+ WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+ curr_data &= ~BIT(6);
+
+ WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+ scwirod(p_port,BIT(6)); /*Wait for DB6 to be released. */
+
+ return(ret_data);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scsendi
+ *
+ * Description: Transfer our Identification string to determine if we
+ * will be the dominant master.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR scsendi(USHORT p_port, UCHAR p_id_string[])
+#else
+UCHAR scsendi(ULONG p_port, UCHAR p_id_string[])
+#endif
+{
+ UCHAR ret_data,byte_cnt,bit_cnt,defer;
+
+ defer = FALSE;
+
+ for (byte_cnt = 0; byte_cnt < ID_STRING_LENGTH; byte_cnt++) {
+
+ for (bit_cnt = 0x80; bit_cnt != 0 ; bit_cnt >>= 1) {
+
+ if (defer)
+ ret_data = scxferc(p_port,00);
+
+ else if (p_id_string[byte_cnt] & bit_cnt)
+
+ ret_data = scxferc(p_port,02);
+
+ else {
+
+ ret_data = scxferc(p_port,01);
+ if (ret_data & 02)
+ defer = TRUE;
+ }
+
+ if ((ret_data & 0x1C) == 0x10)
+ return(0x00); /*End of isolation stage, we won! */
+
+ if (ret_data & 0x1C)
+ return(0xFF);
+
+ if ((defer) && (!(ret_data & 0x1F)))
+ return(0x01); /*End of isolation stage, we lost. */
+
+ } /*bit loop */
+
+ } /*byte loop */
+
+ if (defer)
+ return(0x01); /*We lost */
+ else
+ return(0); /*We WON! Yeeessss! */
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sciso
+ *
+ * Description: Transfer the Identification string.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR sciso(USHORT p_port, UCHAR p_id_string[])
+#else
+UCHAR sciso(ULONG p_port, UCHAR p_id_string[])
+#endif
+{
+ UCHAR ret_data,the_data,byte_cnt,bit_cnt;
+
+ the_data = 0;
+
+ for (byte_cnt = 0; byte_cnt < ID_STRING_LENGTH; byte_cnt++) {
+
+ for (bit_cnt = 0; bit_cnt < 8; bit_cnt++) {
+
+ ret_data = scxferc(p_port,0);
+
+ if (ret_data & 0xFC)
+ return(0xFF);
+
+ else {
+
+ the_data <<= 1;
+ if (ret_data & BIT(1)) {
+ the_data |= 1;
+ }
+ }
+
+ if ((ret_data & 0x1F) == 0)
+ {
+/*
+ if(bit_cnt != 0 || bit_cnt != 8)
+ {
+ byte_cnt = 0;
+ bit_cnt = 0;
+ scxferc(p_port, SYNC_PTRN);
+ scxferc(p_port, ASSIGN_ID);
+ continue;
+ }
+*/
+ if (byte_cnt)
+ return(0x00);
+ else
+ return(0xFF);
+ }
+
+ } /*bit loop */
+
+ p_id_string[byte_cnt] = the_data;
+
+ } /*byte loop */
+
+ return(0);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scwirod
+ *
+ * Description: Sample the SCSI data bus making sure the signal has been
+ * deasserted for the correct number of consecutive samples.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scwirod(USHORT p_port, UCHAR p_data_bit)
+#else
+void scwirod(ULONG p_port, UCHAR p_data_bit)
+#endif
+{
+ UCHAR i;
+
+ i = 0;
+ while ( i < MAX_SCSI_TAR ) {
+
+ if (RD_HARPOON(p_port+hp_scsidata_0) & p_data_bit)
+
+ i = 0;
+
+ else
+
+ i++;
+
+ }
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scwiros
+ *
+ * Description: Sample the SCSI Signal lines making sure the signal has been
+ * deasserted for the correct number of consecutive samples.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scwiros(USHORT p_port, UCHAR p_data_bit)
+#else
+void scwiros(ULONG p_port, UCHAR p_data_bit)
+#endif
+{
+ UCHAR i;
+
+ i = 0;
+ while ( i < MAX_SCSI_TAR ) {
+
+ if (RD_HARPOON(p_port+hp_scsisig) & p_data_bit)
+
+ i = 0;
+
+ else
+
+ i++;
+
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scvalq
+ *
+ * Description: Make sure we received a valid data byte.
+ *
+ *---------------------------------------------------------------------*/
+
+UCHAR scvalq(UCHAR p_quintet)
+{
+ UCHAR count;
+
+ for (count=1; count < 0x08; count<<=1) {
+ if (!(p_quintet & count))
+ p_quintet -= 0x80;
+ }
+
+ if (p_quintet & 0x18)
+ return(FALSE);
+
+ else
+ return(TRUE);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scsell
+ *
+ * Description: Select the specified device ID using a selection timeout
+ * less than 4ms. If somebody responds then it is a legacy
+ * drive and this ID must be marked as such.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR scsell(USHORT p_port, UCHAR targ_id)
+#else
+UCHAR scsell(ULONG p_port, UCHAR targ_id)
+#endif
+{
+#if defined(DOS)
+ USHORT i;
+#else
+ ULONG i;
+#endif
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE));
+
+ ARAM_ACCESS(p_port);
+
+ WR_HARPOON(p_port+hp_addstat,(RD_HARPOON(p_port+hp_addstat) | SCAM_TIMER));
+ WR_HARPOON(p_port+hp_seltimeout,TO_4ms);
+
+
+ for (i = p_port+CMD_STRT; i < p_port+CMD_STRT+12; i+=2) {
+ WRW_HARPOON(i, (MPM_OP+ACOMMAND));
+ }
+ WRW_HARPOON(i, (BRH_OP+ALWAYS+ NP));
+
+ WRW_HARPOON((p_port+hp_intstat),
+ (RESET | TIMEOUT | SEL | BUS_FREE | AUTO_INT));
+
+ WR_HARPOON(p_port+hp_select_id, targ_id);
+
+ WR_HARPOON(p_port+hp_portctrl_0, SCSI_PORT);
+ WR_HARPOON(p_port+hp_autostart_3, (SELECT | CMD_ONLY_STRT));
+ WR_HARPOON(p_port+hp_scsictrl_0, (SEL_TAR | ENA_RESEL));
+
+
+ while (!(RDW_HARPOON((p_port+hp_intstat)) &
+ (RESET | PROG_HLT | TIMEOUT | AUTO_INT))) {}
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & RESET)
+ Wait(p_port, TO_250ms);
+
+ DISABLE_AUTO(p_port);
+
+ WR_HARPOON(p_port+hp_addstat,(RD_HARPOON(p_port+hp_addstat) & ~SCAM_TIMER));
+ WR_HARPOON(p_port+hp_seltimeout,TO_290ms);
+
+ SGRAM_ACCESS(p_port);
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & (RESET | TIMEOUT) ) {
+
+ WRW_HARPOON((p_port+hp_intstat),
+ (RESET | TIMEOUT | SEL | BUS_FREE | PHASE));
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+ return(FALSE); /*No legacy device */
+ }
+
+ else {
+
+ while(!(RDW_HARPOON((p_port+hp_intstat)) & BUS_FREE)) {
+ if (RD_HARPOON(p_port+hp_scsisig) & SCSI_REQ)
+ {
+ WR_HARPOON(p_port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+ ACCEPT_MSG(p_port);
+ }
+ }
+
+ WRW_HARPOON((p_port+hp_intstat), CLR_ALL_INT_1);
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+ return(TRUE); /*Found one of them oldies! */
+ }
+}
+
+#if defined(DOS)
+/*---------------------------------------------------------------------
+ *
+ * Function: scsell for DOS
+ *
+ * Description: Select the specified device ID using a selection timeout
+ * less than 2ms. This was specially required to solve
+ * the problem with Plextor 12X CD-ROM drive. This drive
+ * was responding the Selection at the end of 4ms and
+ * hanging the system.
+ *
+ *---------------------------------------------------------------------*/
+
+UCHAR scsellDOS(USHORT p_port, UCHAR targ_id)
+{
+ USHORT i;
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE));
+
+ ARAM_ACCESS(p_port);
+
+ WR_HARPOON(p_port+hp_addstat,(RD_HARPOON(p_port+hp_addstat) | SCAM_TIMER));
+ WR_HARPOON(p_port+hp_seltimeout,TO_2ms);
+
+
+ for (i = p_port+CMD_STRT; i < p_port+CMD_STRT+12; i+=2) {
+ WRW_HARPOON(i, (MPM_OP+ACOMMAND));
+ }
+ WRW_HARPOON(i, (BRH_OP+ALWAYS+ NP));
+
+ WRW_HARPOON((p_port+hp_intstat),
+ (RESET | TIMEOUT | SEL | BUS_FREE | AUTO_INT));
+
+ WR_HARPOON(p_port+hp_select_id, targ_id);
+
+ WR_HARPOON(p_port+hp_portctrl_0, SCSI_PORT);
+ WR_HARPOON(p_port+hp_autostart_3, (SELECT | CMD_ONLY_STRT));
+ WR_HARPOON(p_port+hp_scsictrl_0, (SEL_TAR | ENA_RESEL));
+
+
+ while (!(RDW_HARPOON((p_port+hp_intstat)) &
+ (RESET | PROG_HLT | TIMEOUT | AUTO_INT))) {}
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & RESET)
+ Wait(p_port, TO_250ms);
+
+ DISABLE_AUTO(p_port);
+
+ WR_HARPOON(p_port+hp_addstat,(RD_HARPOON(p_port+hp_addstat) & ~SCAM_TIMER));
+ WR_HARPOON(p_port+hp_seltimeout,TO_290ms);
+
+ SGRAM_ACCESS(p_port);
+
+ if (RDW_HARPOON((p_port+hp_intstat)) & (RESET | TIMEOUT) ) {
+
+ WRW_HARPOON((p_port+hp_intstat),
+ (RESET | TIMEOUT | SEL | BUS_FREE | PHASE));
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+ return(FALSE); /*No legacy device */
+ }
+
+ else {
+
+ while(!(RDW_HARPOON((p_port+hp_intstat)) & BUS_FREE)) {
+ if (RD_HARPOON(p_port+hp_scsisig) & SCSI_REQ)
+ {
+ WR_HARPOON(p_port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+ ACCEPT_MSG(p_port);
+ }
+ }
+
+ WRW_HARPOON((p_port+hp_intstat), CLR_ALL_INT_1);
+
+ WR_HARPOON(p_port+hp_page_ctrl,
+ (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+ return(TRUE); /*Found one of them oldies! */
+ }
+}
+#endif /* DOS */
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scwtsel
+ *
+ * Description: Wait to be selected by another SCAM initiator.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scwtsel(USHORT p_port)
+#else
+void scwtsel(ULONG p_port)
+#endif
+{
+ while(!(RDW_HARPOON((p_port+hp_intstat)) & SCAM_SEL)) {}
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: inisci
+ *
+ * Description: Setup the data Structure with the info from the EEPROM.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void inisci(UCHAR p_card, USHORT p_port, UCHAR p_our_id)
+#else
+void inisci(UCHAR p_card, ULONG p_port, UCHAR p_our_id)
+#endif
+{
+ UCHAR i,k,max_id;
+ USHORT ee_data;
+ PNVRamInfo pCurrNvRam;
+
+ pCurrNvRam = BL_Card[p_card].pNvRamInfo;
+
+ if (RD_HARPOON(p_port+hp_page_ctrl) & NARROW_SCSI_CARD)
+ max_id = 0x08;
+
+ else
+ max_id = 0x10;
+
+ if(pCurrNvRam){
+ for(i = 0; i < max_id; i++){
+
+ for(k = 0; k < 4; k++)
+ scamInfo[i].id_string[k] = pCurrNvRam->niScamTbl[i][k];
+ for(k = 4; k < ID_STRING_LENGTH; k++)
+ scamInfo[i].id_string[k] = (UCHAR) 0x00;
+
+ if(scamInfo[i].id_string[0] == 0x00)
+ scamInfo[i].state = ID_UNUSED; /*Default to unused ID. */
+ else
+ scamInfo[i].state = ID_UNASSIGNED; /*Default to unassigned ID. */
+
+ }
+ }else {
+ for (i=0; i < max_id; i++)
+ {
+ for (k=0; k < ID_STRING_LENGTH; k+=2)
+ {
+ ee_data = utilEERead(p_port, (USHORT)((EE_SCAMBASE/2) +
+ (USHORT) (i*((USHORT)ID_STRING_LENGTH/2)) + (USHORT)(k/2)));
+ scamInfo[i].id_string[k] = (UCHAR) ee_data;
+ ee_data >>= 8;
+ scamInfo[i].id_string[k+1] = (UCHAR) ee_data;
+ }
+
+ if ((scamInfo[i].id_string[0] == 0x00) ||
+ (scamInfo[i].id_string[0] == 0xFF))
+
+ scamInfo[i].state = ID_UNUSED; /*Default to unused ID. */
+
+ else
+ scamInfo[i].state = ID_UNASSIGNED; /*Default to unassigned ID. */
+
+ }
+ }
+ for(k = 0; k < ID_STRING_LENGTH; k++)
+ scamInfo[p_our_id].id_string[k] = scamHAString[k];
+
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scmachid
+ *
+ * Description: Match the Device ID string with our values stored in
+ * the EEPROM.
+ *
+ *---------------------------------------------------------------------*/
+
+UCHAR scmachid(UCHAR p_card, UCHAR p_id_string[])
+{
+
+ UCHAR i,k,match;
+
+
+ for (i=0; i < MAX_SCSI_TAR; i++) {
+
+#if !defined(SCAM_LEV_2)
+ if (scamInfo[i].state == ID_UNASSIGNED)
+ {
+#endif
+ match = TRUE;
+
+ for (k=0; k < ID_STRING_LENGTH; k++)
+ {
+ if (p_id_string[k] != scamInfo[i].id_string[k])
+ match = FALSE;
+ }
+
+ if (match)
+ {
+ scamInfo[i].state = ID_ASSIGNED;
+ return(i);
+ }
+
+#if !defined(SCAM_LEV_2)
+ }
+#endif
+
+ }
+
+
+
+ if (p_id_string[0] & BIT(5))
+ i = 8;
+ else
+ i = MAX_SCSI_TAR;
+
+ if (((p_id_string[0] & 0x06) == 0x02) || ((p_id_string[0] & 0x06) == 0x04))
+ match = p_id_string[1] & (UCHAR) 0x1F;
+ else
+ match = 7;
+
+ while (i > 0)
+ {
+ i--;
+
+ if (scamInfo[match].state == ID_UNUSED)
+ {
+ for (k=0; k < ID_STRING_LENGTH; k++)
+ {
+ scamInfo[match].id_string[k] = p_id_string[k];
+ }
+
+ scamInfo[match].state = ID_ASSIGNED;
+
+ if(BL_Card[p_card].pNvRamInfo == NULL)
+ BL_Card[p_card].globalFlags |= F_UPDATE_EEPROM;
+ return(match);
+
+ }
+
+
+ match--;
+
+ if (match == 0xFF)
+ {
+ if (p_id_string[0] & BIT(5))
+ match = 7;
+ else
+ match = MAX_SCSI_TAR-1;
+ }
+ }
+
+
+
+ if (p_id_string[0] & BIT(7))
+ {
+ return(CLR_PRIORITY);
+ }
+
+
+ if (p_id_string[0] & BIT(5))
+ i = 8;
+ else
+ i = MAX_SCSI_TAR;
+
+ if (((p_id_string[0] & 0x06) == 0x02) || ((p_id_string[0] & 0x06) == 0x04))
+ match = p_id_string[1] & (UCHAR) 0x1F;
+ else
+ match = 7;
+
+ while (i > 0)
+ {
+
+ i--;
+
+ if (scamInfo[match].state == ID_UNASSIGNED)
+ {
+ for (k=0; k < ID_STRING_LENGTH; k++)
+ {
+ scamInfo[match].id_string[k] = p_id_string[k];
+ }
+
+ scamInfo[match].id_string[0] |= BIT(7);
+ scamInfo[match].state = ID_ASSIGNED;
+ if(BL_Card[p_card].pNvRamInfo == NULL)
+ BL_Card[p_card].globalFlags |= F_UPDATE_EEPROM;
+ return(match);
+
+ }
+
+
+ match--;
+
+ if (match == 0xFF)
+ {
+ if (p_id_string[0] & BIT(5))
+ match = 7;
+ else
+ match = MAX_SCSI_TAR-1;
+ }
+ }
+
+ return(NO_ID_AVAIL);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scsavdi
+ *
+ * Description: Save off the device SCAM ID strings.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scsavdi(UCHAR p_card, USHORT p_port)
+#else
+void scsavdi(UCHAR p_card, ULONG p_port)
+#endif
+{
+ UCHAR i,k,max_id;
+ USHORT ee_data,sum_data;
+
+
+ sum_data = 0x0000;
+
+ for (i = 1; i < EE_SCAMBASE/2; i++)
+ {
+ sum_data += utilEERead(p_port, i);
+ }
+
+
+ utilEEWriteOnOff(p_port,1); /* Enable write access to the EEPROM */
+
+ if (RD_HARPOON(p_port+hp_page_ctrl) & NARROW_SCSI_CARD)
+ max_id = 0x08;
+
+ else
+ max_id = 0x10;
+
+ for (i=0; i < max_id; i++)
+ {
+
+ for (k=0; k < ID_STRING_LENGTH; k+=2)
+ {
+ ee_data = scamInfo[i].id_string[k+1];
+ ee_data <<= 8;
+ ee_data |= scamInfo[i].id_string[k];
+ sum_data += ee_data;
+ utilEEWrite(p_port, ee_data, (USHORT)((EE_SCAMBASE/2) +
+ (USHORT)(i*((USHORT)ID_STRING_LENGTH/2)) + (USHORT)(k/2)));
+ }
+ }
+
+
+ utilEEWrite(p_port, sum_data, EEPROM_CHECK_SUM/2);
+ utilEEWriteOnOff(p_port,0); /* Turn off write access */
+}
+#ident "$Id: FlashPoint.c,v 1.1 1999/04/26 05:53:56 tb Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: diagnose.c $
+ *
+ * Description: Diagnostic funtions for testing the integrity of
+ * the HARPOON.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <eeprom.h>*/
+/*#include <harpoon.h>*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: XbowInit
+ *
+ * Description: Setup the Xbow for normal operation.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void XbowInit(USHORT port, UCHAR ScamFlg)
+#else
+void XbowInit(ULONG port, UCHAR ScamFlg)
+#endif
+{
+UCHAR i;
+
+ i = RD_HARPOON(port+hp_page_ctrl);
+ WR_HARPOON(port+hp_page_ctrl, (UCHAR) (i | G_INT_DISABLE));
+
+ WR_HARPOON(port+hp_scsireset,0x00);
+ WR_HARPOON(port+hp_portctrl_1,HOST_MODE8);
+
+ WR_HARPOON(port+hp_scsireset,(DMA_RESET | HPSCSI_RESET | PROG_RESET | \
+ FIFO_CLR));
+
+ WR_HARPOON(port+hp_scsireset,SCSI_INI);
+
+ WR_HARPOON(port+hp_clkctrl_0,CLKCTRL_DEFAULT);
+
+ WR_HARPOON(port+hp_scsisig,0x00); /* Clear any signals we might */
+ WR_HARPOON(port+hp_scsictrl_0,ENA_SCAM_SEL);
+
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+
+#if defined(SCAM_LEV_2)
+ default_intena = RESET | RSEL | PROG_HLT | TIMEOUT |
+ BUS_FREE | XFER_CNT_0 | AUTO_INT;
+
+ if ((ScamFlg & SCAM_ENABLED) && (ScamFlg & SCAM_LEVEL2))
+ default_intena |= SCAM_SEL;
+
+#else
+ default_intena = RESET | RSEL | PROG_HLT | TIMEOUT |
+ BUS_FREE | XFER_CNT_0 | AUTO_INT;
+#endif
+ WRW_HARPOON((port+hp_intena), default_intena);
+
+ WR_HARPOON(port+hp_seltimeout,TO_290ms);
+
+ /* Turn on SCSI_MODE8 for narrow cards to fix the
+ strapping issue with the DUAL CHANNEL card */
+ if (RD_HARPOON(port+hp_page_ctrl) & NARROW_SCSI_CARD)
+ WR_HARPOON(port+hp_addstat,SCSI_MODE8);
+
+#if defined(NO_BIOS_OPTION)
+
+ WR_HARPOON(port+hp_synctarg_0,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_1,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_2,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_3,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_4,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_5,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_6,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_7,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_8,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_9,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_10,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_11,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_12,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_13,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_14,NARROW_SCSI);
+ WR_HARPOON(port+hp_synctarg_15,NARROW_SCSI);
+
+#endif
+ WR_HARPOON(port+hp_page_ctrl, i);
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: BusMasterInit
+ *
+ * Description: Initialize the BusMaster for normal operations.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void BusMasterInit(USHORT p_port)
+#else
+void BusMasterInit(ULONG p_port)
+#endif
+{
+
+
+ WR_HARPOON(p_port+hp_sys_ctrl, DRVR_RST);
+ WR_HARPOON(p_port+hp_sys_ctrl, 0x00);
+
+ WR_HARPOON(p_port+hp_host_blk_cnt, XFER_BLK64);
+
+
+ WR_HARPOON(p_port+hp_bm_ctrl, (BMCTRL_DEFAULT));
+
+ WR_HARPOON(p_port+hp_ee_ctrl, (SCSI_TERM_ENA_H));
+
+
+#if defined(NT)
+
+ WR_HARPOON(p_port+hp_pci_cmd_cfg, (RD_HARPOON(p_port+hp_pci_cmd_cfg)
+ & ~MEM_SPACE_ENA));
+
+#endif
+
+ RD_HARPOON(p_port+hp_int_status); /*Clear interrupts. */
+ WR_HARPOON(p_port+hp_int_mask, (INT_CMD_COMPL | SCSI_INTERRUPT));
+ WR_HARPOON(p_port+hp_page_ctrl, (RD_HARPOON(p_port+hp_page_ctrl) &
+ ~SCATTER_EN));
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: DiagXbow
+ *
+ * Description: Test Xbow integrity. Non-zero return indicates an error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+int DiagXbow(USHORT port)
+#else
+int DiagXbow(ULONG port)
+#endif
+{
+ unsigned char fifo_cnt,loop_cnt;
+
+ unsigned char fifodata[5];
+ fifodata[0] = 0x00;
+ fifodata[1] = 0xFF;
+ fifodata[2] = 0x55;
+ fifodata[3] = 0xAA;
+ fifodata[4] = 0x00;
+
+
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+ WRW_HARPOON((port+hp_intena), 0x0000);
+
+ WR_HARPOON(port+hp_seltimeout,TO_5ms);
+
+ WR_HARPOON(port+hp_portctrl_0,START_TO);
+
+
+ for(fifodata[4] = 0x01; fifodata[4] != (UCHAR) 0; fifodata[4] = fifodata[4] << 1) {
+
+ WR_HARPOON(port+hp_selfid_0,fifodata[4]);
+ WR_HARPOON(port+hp_selfid_1,fifodata[4]);
+
+ if ((RD_HARPOON(port+hp_selfid_0) != fifodata[4]) ||
+ (RD_HARPOON(port+hp_selfid_1) != fifodata[4]))
+ return(1);
+ }
+
+
+ for(loop_cnt = 0; loop_cnt < 4; loop_cnt++) {
+
+ WR_HARPOON(port+hp_portctrl_0,(HOST_PORT | HOST_WRT | START_TO));
+
+
+ for (fifo_cnt = 0; fifo_cnt < FIFO_LEN; fifo_cnt++) {
+
+ WR_HARPOON(port+hp_fifodata_0, fifodata[loop_cnt]);
+ }
+
+
+ if (!(RD_HARPOON(port+hp_xferstat) & FIFO_FULL))
+ return(1);
+
+
+ WR_HARPOON(port+hp_portctrl_0,(HOST_PORT | START_TO));
+
+ for (fifo_cnt = 0; fifo_cnt < FIFO_LEN; fifo_cnt++) {
+
+ if (RD_HARPOON(port+hp_fifodata_0) != fifodata[loop_cnt])
+ return(1);
+ }
+
+
+ if (!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY))
+ return(1);
+ }
+
+
+ while(!(RDW_HARPOON((port+hp_intstat)) & TIMEOUT)) {}
+
+
+ WR_HARPOON(port+hp_seltimeout,TO_290ms);
+
+ WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+
+ WRW_HARPOON((port+hp_intena), default_intena);
+
+ return(0);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: DiagBusMaster
+ *
+ * Description: Test BusMaster integrity. Non-zero return indicates an
+ * error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+int DiagBusMaster(USHORT port)
+#else
+int DiagBusMaster(ULONG port)
+#endif
+{
+ UCHAR testdata;
+
+ for(testdata = (UCHAR) 1; testdata != (UCHAR)0; testdata = testdata << 1) {
+
+ WR_HARPOON(port+hp_xfer_cnt_lo,testdata);
+ WR_HARPOON(port+hp_xfer_cnt_mi,testdata);
+ WR_HARPOON(port+hp_xfer_cnt_hi,testdata);
+ WR_HARPOON(port+hp_host_addr_lo,testdata);
+ WR_HARPOON(port+hp_host_addr_lmi,testdata);
+ WR_HARPOON(port+hp_host_addr_hmi,testdata);
+ WR_HARPOON(port+hp_host_addr_hi,testdata);
+
+ if ((RD_HARPOON(port+hp_xfer_cnt_lo) != testdata) ||
+ (RD_HARPOON(port+hp_xfer_cnt_mi) != testdata) ||
+ (RD_HARPOON(port+hp_xfer_cnt_hi) != testdata) ||
+ (RD_HARPOON(port+hp_host_addr_lo) != testdata) ||
+ (RD_HARPOON(port+hp_host_addr_lmi) != testdata) ||
+ (RD_HARPOON(port+hp_host_addr_hmi) != testdata) ||
+ (RD_HARPOON(port+hp_host_addr_hi) != testdata))
+
+ return(1);
+ }
+ RD_HARPOON(port+hp_int_status); /*Clear interrupts. */
+ return(0);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: DiagEEPROM
+ *
+ * Description: Verfiy checksum and 'Key' and initialize the EEPROM if
+ * neccessary.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void DiagEEPROM(USHORT p_port)
+#else
+void DiagEEPROM(ULONG p_port)
+#endif
+
+{
+ USHORT index,temp,max_wd_cnt;
+
+ if (RD_HARPOON(p_port+hp_page_ctrl) & NARROW_SCSI_CARD)
+ max_wd_cnt = EEPROM_WD_CNT;
+ else
+ max_wd_cnt = EEPROM_WD_CNT * 2;
+
+ temp = utilEERead(p_port, FW_SIGNATURE/2);
+
+ if (temp == 0x4641) {
+
+ for (index = 2; index < max_wd_cnt; index++) {
+
+ temp += utilEERead(p_port, index);
+
+ }
+
+ if (temp == utilEERead(p_port, EEPROM_CHECK_SUM/2)) {
+
+ return; /*EEPROM is Okay so return now! */
+ }
+ }
+
+
+ utilEEWriteOnOff(p_port,(UCHAR)1);
+
+ for (index = 0; index < max_wd_cnt; index++) {
+
+ utilEEWrite(p_port, 0x0000, index);
+ }
+
+ temp = 0;
+
+ utilEEWrite(p_port, 0x4641, FW_SIGNATURE/2);
+ temp += 0x4641;
+ utilEEWrite(p_port, 0x3920, MODEL_NUMB_0/2);
+ temp += 0x3920;
+ utilEEWrite(p_port, 0x3033, MODEL_NUMB_2/2);
+ temp += 0x3033;
+ utilEEWrite(p_port, 0x2020, MODEL_NUMB_4/2);
+ temp += 0x2020;
+ utilEEWrite(p_port, 0x70D3, SYSTEM_CONFIG/2);
+ temp += 0x70D3;
+ utilEEWrite(p_port, 0x0010, BIOS_CONFIG/2);
+ temp += 0x0010;
+ utilEEWrite(p_port, 0x0003, SCAM_CONFIG/2);
+ temp += 0x0003;
+ utilEEWrite(p_port, 0x0007, ADAPTER_SCSI_ID/2);
+ temp += 0x0007;
+
+ utilEEWrite(p_port, 0x0000, IGNORE_B_SCAN/2);
+ temp += 0x0000;
+ utilEEWrite(p_port, 0x0000, SEND_START_ENA/2);
+ temp += 0x0000;
+ utilEEWrite(p_port, 0x0000, DEVICE_ENABLE/2);
+ temp += 0x0000;
+
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL01/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL23/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL45/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL67/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL89/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBLab/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBLcd/2);
+ temp += 0x4242;
+ utilEEWrite(p_port, 0x4242, SYNC_RATE_TBLef/2);
+ temp += 0x4242;
+
+
+ utilEEWrite(p_port, 0x6C46, 64/2); /*PRODUCT ID */
+ temp += 0x6C46;
+ utilEEWrite(p_port, 0x7361, 66/2); /* FlashPoint LT */
+ temp += 0x7361;
+ utilEEWrite(p_port, 0x5068, 68/2);
+ temp += 0x5068;
+ utilEEWrite(p_port, 0x696F, 70/2);
+ temp += 0x696F;
+ utilEEWrite(p_port, 0x746E, 72/2);
+ temp += 0x746E;
+ utilEEWrite(p_port, 0x4C20, 74/2);
+ temp += 0x4C20;
+ utilEEWrite(p_port, 0x2054, 76/2);
+ temp += 0x2054;
+ utilEEWrite(p_port, 0x2020, 78/2);
+ temp += 0x2020;
+
+ index = ((EE_SCAMBASE/2)+(7*16));
+ utilEEWrite(p_port, (0x0700+TYPE_CODE0), index);
+ temp += (0x0700+TYPE_CODE0);
+ index++;
+ utilEEWrite(p_port, 0x5542, index); /*Vendor ID code */
+ temp += 0x5542; /* BUSLOGIC */
+ index++;
+ utilEEWrite(p_port, 0x4C53, index);
+ temp += 0x4C53;
+ index++;
+ utilEEWrite(p_port, 0x474F, index);
+ temp += 0x474F;
+ index++;
+ utilEEWrite(p_port, 0x4349, index);
+ temp += 0x4349;
+ index++;
+ utilEEWrite(p_port, 0x5442, index); /*Vendor unique code */
+ temp += 0x5442; /* BT- 930 */
+ index++;
+ utilEEWrite(p_port, 0x202D, index);
+ temp += 0x202D;
+ index++;
+ utilEEWrite(p_port, 0x3339, index);
+ temp += 0x3339;
+ index++; /*Serial # */
+ utilEEWrite(p_port, 0x2030, index); /* 01234567 */
+ temp += 0x2030;
+ index++;
+ utilEEWrite(p_port, 0x5453, index);
+ temp += 0x5453;
+ index++;
+ utilEEWrite(p_port, 0x5645, index);
+ temp += 0x5645;
+ index++;
+ utilEEWrite(p_port, 0x2045, index);
+ temp += 0x2045;
+ index++;
+ utilEEWrite(p_port, 0x202F, index);
+ temp += 0x202F;
+ index++;
+ utilEEWrite(p_port, 0x4F4A, index);
+ temp += 0x4F4A;
+ index++;
+ utilEEWrite(p_port, 0x204E, index);
+ temp += 0x204E;
+ index++;
+ utilEEWrite(p_port, 0x3539, index);
+ temp += 0x3539;
+
+
+
+ utilEEWrite(p_port, temp, EEPROM_CHECK_SUM/2);
+
+ utilEEWriteOnOff(p_port,(UCHAR)0);
+
+}
+
+#ident "$Id: FlashPoint.c,v 1.1 1999/04/26 05:53:56 tb Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ * Copyright 1995-1996 by Mylex Corporation. All Rights Reserved
+ *
+ * This file is available under both the GNU General Public License
+ * and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ * $Workfile: utility.c $
+ *
+ * Description: Utility functions relating to queueing and EEPROM
+ * manipulation and any other garbage functions.
+ *
+ * $Date: 1999/04/26 05:53:56 $
+ *
+ * $Revision: 1.1 $
+ *
+ *----------------------------------------------------------------------*/
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+ /*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+extern unsigned int SccbGlobalFlags;
+*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Search Select
+ *
+ * Description: Try to find a new command to execute.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueSearchSelect(PSCCBcard pCurrCard, UCHAR p_card)
+{
+ UCHAR scan_ptr, lun;
+ PSCCBMgr_tar_info currTar_Info;
+ PSCCB pOldSccb;
+
+ scan_ptr = pCurrCard->scanIndex;
+ do
+ {
+ currTar_Info = &sccbMgrTbl[p_card][scan_ptr];
+ if((pCurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+ {
+ if (currTar_Info->TarSelQ_Cnt != 0)
+ {
+
+ scan_ptr++;
+ if (scan_ptr == MAX_SCSI_TAR)
+ scan_ptr = 0;
+
+ for(lun=0; lun < MAX_LUN; lun++)
+ {
+ if(currTar_Info->TarLUNBusy[lun] == FALSE)
+ {
+
+ pCurrCard->currentSCCB = currTar_Info->TarSelQ_Head;
+ pOldSccb = NULL;
+
+ while((pCurrCard->currentSCCB != NULL) &&
+ (lun != pCurrCard->currentSCCB->Lun))
+ {
+ pOldSccb = pCurrCard->currentSCCB;
+ pCurrCard->currentSCCB = (PSCCB)(pCurrCard->currentSCCB)->
+ Sccb_forwardlink;
+ }
+ if(pCurrCard->currentSCCB == NULL)
+ continue;
+ if(pOldSccb != NULL)
+ {
+ pOldSccb->Sccb_forwardlink = (PSCCB)(pCurrCard->currentSCCB)->
+ Sccb_forwardlink;
+ pOldSccb->Sccb_backlink = (PSCCB)(pCurrCard->currentSCCB)->
+ Sccb_backlink;
+ currTar_Info->TarSelQ_Cnt--;
+ }
+ else
+ {
+ currTar_Info->TarSelQ_Head = (PSCCB)(pCurrCard->currentSCCB)->Sccb_forwardlink;
+
+ if (currTar_Info->TarSelQ_Head == NULL)
+ {
+ currTar_Info->TarSelQ_Tail = NULL;
+ currTar_Info->TarSelQ_Cnt = 0;
+ }
+ else
+ {
+ currTar_Info->TarSelQ_Cnt--;
+ currTar_Info->TarSelQ_Head->Sccb_backlink = (PSCCB)NULL;
+ }
+ }
+ pCurrCard->scanIndex = scan_ptr;
+
+ pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
+
+ break;
+ }
+ }
+ }
+
+ else
+ {
+ scan_ptr++;
+ if (scan_ptr == MAX_SCSI_TAR) {
+ scan_ptr = 0;
+ }
+ }
+
+ }
+ else
+ {
+ if ((currTar_Info->TarSelQ_Cnt != 0) &&
+ (currTar_Info->TarLUNBusy[0] == FALSE))
+ {
+
+ pCurrCard->currentSCCB = currTar_Info->TarSelQ_Head;
+
+ currTar_Info->TarSelQ_Head = (PSCCB)(pCurrCard->currentSCCB)->Sccb_forwardlink;
+
+ if (currTar_Info->TarSelQ_Head == NULL)
+ {
+ currTar_Info->TarSelQ_Tail = NULL;
+ currTar_Info->TarSelQ_Cnt = 0;
+ }
+ else
+ {
+ currTar_Info->TarSelQ_Cnt--;
+ currTar_Info->TarSelQ_Head->Sccb_backlink = (PSCCB)NULL;
+ }
+
+ scan_ptr++;
+ if (scan_ptr == MAX_SCSI_TAR)
+ scan_ptr = 0;
+
+ pCurrCard->scanIndex = scan_ptr;
+
+ pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
+
+ break;
+ }
+
+ else
+ {
+ scan_ptr++;
+ if (scan_ptr == MAX_SCSI_TAR)
+ {
+ scan_ptr = 0;
+ }
+ }
+ }
+ } while (scan_ptr != pCurrCard->scanIndex);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Select Fail
+ *
+ * Description: Add the current SCCB to the head of the Queue.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueSelectFail(PSCCBcard pCurrCard, UCHAR p_card)
+{
+ UCHAR thisTarg;
+ PSCCBMgr_tar_info currTar_Info;
+
+ if (pCurrCard->currentSCCB != NULL)
+ {
+ thisTarg = (UCHAR)(((PSCCB)(pCurrCard->currentSCCB))->TargID);
+ currTar_Info = &sccbMgrTbl[p_card][thisTarg];
+
+ pCurrCard->currentSCCB->Sccb_backlink = (PSCCB)NULL;
+
+ pCurrCard->currentSCCB->Sccb_forwardlink = currTar_Info->TarSelQ_Head;
+
+ if (currTar_Info->TarSelQ_Cnt == 0)
+ {
+ currTar_Info->TarSelQ_Tail = pCurrCard->currentSCCB;
+ }
+
+ else
+ {
+ currTar_Info->TarSelQ_Head->Sccb_backlink = pCurrCard->currentSCCB;
+ }
+
+
+ currTar_Info->TarSelQ_Head = pCurrCard->currentSCCB;
+
+ pCurrCard->currentSCCB = NULL;
+ currTar_Info->TarSelQ_Cnt++;
+ }
+}
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Command Complete
+ *
+ * Description: Call the callback function with the current SCCB.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueCmdComplete(PSCCBcard pCurrCard, PSCCB p_sccb, UCHAR p_card)
+{
+
+#if (FW_TYPE==_UCB_MGR_)
+
+ u08bits SCSIcmd;
+ CALL_BK_FN callback;
+ PSCCBMgr_tar_info currTar_Info;
+
+ PUCB p_ucb;
+ p_ucb=p_sccb->Sccb_ucb_ptr;
+
+ SCSIcmd = p_sccb->Cdb[0];
+
+
+ if (!(p_sccb->Sccb_XferState & F_ALL_XFERRED))
+ {
+
+ if ((p_ucb->UCB_opcode & OPC_CHK_UNDER_OVER_RUN) &&
+ (p_sccb->HostStatus == SCCB_COMPLETE) &&
+ (p_sccb->TargetStatus != SSCHECK))
+
+ if ((SCSIcmd == SCSI_READ) ||
+ (SCSIcmd == SCSI_WRITE) ||
+ (SCSIcmd == SCSI_READ_EXTENDED) ||
+ (SCSIcmd == SCSI_WRITE_EXTENDED) ||
+ (SCSIcmd == SCSI_WRITE_AND_VERIFY) ||
+ (SCSIcmd == SCSI_START_STOP_UNIT) ||
+ (pCurrCard->globalFlags & F_NO_FILTER)
+ )
+ p_sccb->HostStatus = SCCB_DATA_UNDER_RUN;
+ }
+
+ p_ucb->UCB_status=SCCB_SUCCESS;
+
+ if ((p_ucb->UCB_hbastat=p_sccb->HostStatus) || (p_ucb->UCB_scsistat=p_sccb->TargetStatus))
+ {
+ p_ucb->UCB_status=SCCB_ERROR;
+ }
+
+ if ((p_sccb->OperationCode == RESIDUAL_SG_COMMAND) ||
+ (p_sccb->OperationCode == RESIDUAL_COMMAND))
+ {
+
+ utilUpdateResidual(p_sccb);
+
+ p_ucb->UCB_datalen=p_sccb->DataLength;
+ }
+
+ pCurrCard->cmdCounter--;
+ if (!pCurrCard->cmdCounter)
+ {
+
+ if (pCurrCard->globalFlags & F_GREEN_PC)
+ {
+ WR_HARPOON(pCurrCard->ioPort+hp_clkctrl_0,(PWR_DWN | CLKCTRL_DEFAULT));
+ WR_HARPOON(pCurrCard->ioPort+hp_sys_ctrl, STOP_CLK);
+ }
+
+ WR_HARPOON(pCurrCard->ioPort+hp_semaphore,
+ (RD_HARPOON(pCurrCard->ioPort+hp_semaphore) & ~SCCB_MGR_ACTIVE));
+ }
+
+ if(pCurrCard->discQCount != 0)
+ {
+ currTar_Info = &sccbMgrTbl[p_card][p_sccb->TargID];
+ if(((pCurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[p_sccb->Lun]] = NULL;
+ }
+ else
+ {
+ if(p_sccb->Sccb_tag)
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[p_sccb->Sccb_tag] = NULL;
+ }else
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+
+ }
+ callback = (CALL_BK_FN)p_ucb->UCB_callback;
+ callback(p_ucb);
+ pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
+ pCurrCard->currentSCCB = NULL;
+}
+
+
+
+
+#else
+
+ UCHAR i, SCSIcmd;
+ CALL_BK_FN callback;
+ PSCCBMgr_tar_info currTar_Info;
+
+ SCSIcmd = p_sccb->Cdb[0];
+
+
+ if (!(p_sccb->Sccb_XferState & F_ALL_XFERRED)) {
+
+ if ((p_sccb->ControlByte & (SCCB_DATA_XFER_OUT | SCCB_DATA_XFER_IN)) &&
+ (p_sccb->HostStatus == SCCB_COMPLETE) &&
+ (p_sccb->TargetStatus != SSCHECK))
+
+ if ((SCSIcmd == SCSI_READ) ||
+ (SCSIcmd == SCSI_WRITE) ||
+ (SCSIcmd == SCSI_READ_EXTENDED) ||
+ (SCSIcmd == SCSI_WRITE_EXTENDED) ||
+ (SCSIcmd == SCSI_WRITE_AND_VERIFY) ||
+ (SCSIcmd == SCSI_START_STOP_UNIT) ||
+ (pCurrCard->globalFlags & F_NO_FILTER)
+ )
+ p_sccb->HostStatus = SCCB_DATA_UNDER_RUN;
+ }
+
+
+ if(p_sccb->SccbStatus == SCCB_IN_PROCESS)
+ {
+ if (p_sccb->HostStatus || p_sccb->TargetStatus)
+ p_sccb->SccbStatus = SCCB_ERROR;
+ else
+ p_sccb->SccbStatus = SCCB_SUCCESS;
+ }
+
+ if (p_sccb->Sccb_XferState & F_AUTO_SENSE) {
+
+ p_sccb->CdbLength = p_sccb->Save_CdbLen;
+ for (i=0; i < 6; i++) {
+ p_sccb->Cdb[i] = p_sccb->Save_Cdb[i];
+ }
+ }
+
+ if ((p_sccb->OperationCode == RESIDUAL_SG_COMMAND) ||
+ (p_sccb->OperationCode == RESIDUAL_COMMAND)) {
+
+ utilUpdateResidual(p_sccb);
+ }
+
+ pCurrCard->cmdCounter--;
+ if (!pCurrCard->cmdCounter) {
+
+ if (pCurrCard->globalFlags & F_GREEN_PC) {
+ WR_HARPOON(pCurrCard->ioPort+hp_clkctrl_0,(PWR_DWN | CLKCTRL_DEFAULT));
+ WR_HARPOON(pCurrCard->ioPort+hp_sys_ctrl, STOP_CLK);
+ }
+
+ WR_HARPOON(pCurrCard->ioPort+hp_semaphore,
+ (RD_HARPOON(pCurrCard->ioPort+hp_semaphore) & ~SCCB_MGR_ACTIVE));
+
+ }
+
+ if(pCurrCard->discQCount != 0)
+ {
+ currTar_Info = &sccbMgrTbl[p_card][p_sccb->TargID];
+ if(((pCurrCard->globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[p_sccb->Lun]] = NULL;
+ }
+ else
+ {
+ if(p_sccb->Sccb_tag)
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[p_sccb->Sccb_tag] = NULL;
+ }else
+ {
+ pCurrCard->discQCount--;
+ pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]] = NULL;
+ }
+ }
+
+ }
+
+ callback = (CALL_BK_FN)p_sccb->SccbCallback;
+ callback(p_sccb);
+ pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
+ pCurrCard->currentSCCB = NULL;
+}
+#endif /* ( if FW_TYPE==...) */
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Disconnect
+ *
+ * Description: Add SCCB to our disconnect array.
+ *
+ *---------------------------------------------------------------------*/
+void queueDisconnect(PSCCB p_sccb, UCHAR p_card)
+{
+ PSCCBMgr_tar_info currTar_Info;
+
+ currTar_Info = &sccbMgrTbl[p_card][p_sccb->TargID];
+
+ if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+ ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+ {
+ BL_Card[p_card].discQ_Tbl[currTar_Info->LunDiscQ_Idx[p_sccb->Lun]] = p_sccb;
+ }
+ else
+ {
+ if (p_sccb->Sccb_tag)
+ {
+ BL_Card[p_card].discQ_Tbl[p_sccb->Sccb_tag] = p_sccb;
+ sccbMgrTbl[p_card][p_sccb->TargID].TarLUNBusy[0] = FALSE;
+ sccbMgrTbl[p_card][p_sccb->TargID].TarTagQ_Cnt++;
+ }else
+ {
+ BL_Card[p_card].discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]] = p_sccb;
+ }
+ }
+ BL_Card[p_card].currentSCCB = NULL;
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Flush SCCB
+ *
+ * Description: Flush all SCCB's back to the host driver for this target.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueFlushSccb(UCHAR p_card, UCHAR error_code)
+{
+ UCHAR qtag,thisTarg;
+ PSCCB currSCCB;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currSCCB = BL_Card[p_card].currentSCCB;
+ if(currSCCB != NULL)
+ {
+ thisTarg = (UCHAR)currSCCB->TargID;
+ currTar_Info = &sccbMgrTbl[p_card][thisTarg];
+
+ for (qtag=0; qtag<QUEUE_DEPTH; qtag++) {
+
+ if (BL_Card[p_card].discQ_Tbl[qtag] &&
+ (BL_Card[p_card].discQ_Tbl[qtag]->TargID == thisTarg))
+ {
+
+ BL_Card[p_card].discQ_Tbl[qtag]->HostStatus = (UCHAR)error_code;
+
+ queueCmdComplete(&BL_Card[p_card],BL_Card[p_card].discQ_Tbl[qtag], p_card);
+
+ BL_Card[p_card].discQ_Tbl[qtag] = NULL;
+ currTar_Info->TarTagQ_Cnt--;
+
+ }
+ }
+ }
+
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Flush Target SCCB
+ *
+ * Description: Flush all SCCB's back to the host driver for this target.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueFlushTargSccb(UCHAR p_card, UCHAR thisTarg, UCHAR error_code)
+{
+ UCHAR qtag;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currTar_Info = &sccbMgrTbl[p_card][thisTarg];
+
+ for (qtag=0; qtag<QUEUE_DEPTH; qtag++) {
+
+ if (BL_Card[p_card].discQ_Tbl[qtag] &&
+ (BL_Card[p_card].discQ_Tbl[qtag]->TargID == thisTarg))
+ {
+
+ BL_Card[p_card].discQ_Tbl[qtag]->HostStatus = (UCHAR)error_code;
+
+ queueCmdComplete(&BL_Card[p_card],BL_Card[p_card].discQ_Tbl[qtag], p_card);
+
+ BL_Card[p_card].discQ_Tbl[qtag] = NULL;
+ currTar_Info->TarTagQ_Cnt--;
+
+ }
+ }
+
+}
+
+
+
+
+
+void queueAddSccb(PSCCB p_SCCB, UCHAR p_card)
+{
+ PSCCBMgr_tar_info currTar_Info;
+ currTar_Info = &sccbMgrTbl[p_card][p_SCCB->TargID];
+
+ p_SCCB->Sccb_forwardlink = NULL;
+
+ p_SCCB->Sccb_backlink = currTar_Info->TarSelQ_Tail;
+
+ if (currTar_Info->TarSelQ_Cnt == 0) {
+
+ currTar_Info->TarSelQ_Head = p_SCCB;
+ }
+
+ else {
+
+ currTar_Info->TarSelQ_Tail->Sccb_forwardlink = p_SCCB;
+ }
+
+
+ currTar_Info->TarSelQ_Tail = p_SCCB;
+ currTar_Info->TarSelQ_Cnt++;
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Find SCCB
+ *
+ * Description: Search the target select Queue for this SCCB, and
+ * remove it if found.
+ *
+ *---------------------------------------------------------------------*/
+
+UCHAR queueFindSccb(PSCCB p_SCCB, UCHAR p_card)
+{
+ PSCCB q_ptr;
+ PSCCBMgr_tar_info currTar_Info;
+
+ currTar_Info = &sccbMgrTbl[p_card][p_SCCB->TargID];
+
+ q_ptr = currTar_Info->TarSelQ_Head;
+
+ while(q_ptr != NULL) {
+
+ if (q_ptr == p_SCCB) {
+
+
+ if (currTar_Info->TarSelQ_Head == q_ptr) {
+
+ currTar_Info->TarSelQ_Head = q_ptr->Sccb_forwardlink;
+ }
+
+ if (currTar_Info->TarSelQ_Tail == q_ptr) {
+
+ currTar_Info->TarSelQ_Tail = q_ptr->Sccb_backlink;
+ }
+
+ if (q_ptr->Sccb_forwardlink != NULL) {
+ q_ptr->Sccb_forwardlink->Sccb_backlink = q_ptr->Sccb_backlink;
+ }
+
+ if (q_ptr->Sccb_backlink != NULL) {
+ q_ptr->Sccb_backlink->Sccb_forwardlink = q_ptr->Sccb_forwardlink;
+ }
+
+ currTar_Info->TarSelQ_Cnt--;
+
+ return(TRUE);
+ }
+
+ else {
+ q_ptr = q_ptr->Sccb_forwardlink;
+ }
+ }
+
+
+ return(FALSE);
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Utility Update Residual Count
+ *
+ * Description: Update the XferCnt to the remaining byte count.
+ * If we transferred all the data then just write zero.
+ * If Non-SG transfer then report Total Cnt - Actual Transfer
+ * Cnt. For SG transfers add the count fields of all
+ * remaining SG elements, as well as any partial remaining
+ * element.
+ *
+ *---------------------------------------------------------------------*/
+
+void utilUpdateResidual(PSCCB p_SCCB)
+{
+ ULONG partial_cnt;
+ UINT sg_index;
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+ ULONG far *sg_ptr;
+#else
+ ULONG *sg_ptr;
+#endif
+
+ if (p_SCCB->Sccb_XferState & F_ALL_XFERRED) {
+
+ p_SCCB->DataLength = 0x0000;
+ }
+
+ else if (p_SCCB->Sccb_XferState & F_SG_XFER) {
+
+ partial_cnt = 0x0000;
+
+ sg_index = p_SCCB->Sccb_sgseg;
+
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+ sg_ptr = (ULONG far *)p_SCCB->DataPointer;
+#else
+ sg_ptr = (ULONG *)p_SCCB->DataPointer;
+#endif
+
+ if (p_SCCB->Sccb_SGoffset) {
+
+ partial_cnt = p_SCCB->Sccb_SGoffset;
+ sg_index++;
+ }
+
+ while ( ((ULONG)sg_index * (ULONG)SG_ELEMENT_SIZE) <
+ p_SCCB->DataLength ) {
+
+ partial_cnt += *(sg_ptr+(sg_index * 2));
+ sg_index++;
+ }
+
+ p_SCCB->DataLength = partial_cnt;
+ }
+
+ else {
+
+ p_SCCB->DataLength -= p_SCCB->Sccb_ATC;
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Wait 1 Second
+ *
+ * Description: Wait for 1 second.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void Wait1Second(USHORT p_port)
+#else
+void Wait1Second(ULONG p_port)
+#endif
+{
+ UCHAR i;
+
+ for(i=0; i < 4; i++) {
+
+ Wait(p_port, TO_250ms);
+
+ if ((RD_HARPOON(p_port+hp_scsictrl_0) & SCSI_RST))
+ break;
+
+ if((RDW_HARPOON((p_port+hp_intstat)) & SCAM_SEL))
+ break;
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Wait
+ *
+ * Description: Wait the desired delay.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void Wait(USHORT p_port, UCHAR p_delay)
+#else
+void Wait(ULONG p_port, UCHAR p_delay)
+#endif
+{
+ UCHAR old_timer;
+ UCHAR green_flag;
+
+ old_timer = RD_HARPOON(p_port+hp_seltimeout);
+
+ green_flag=RD_HARPOON(p_port+hp_clkctrl_0);
+ WR_HARPOON(p_port+hp_clkctrl_0, CLKCTRL_DEFAULT);
+
+ WR_HARPOON(p_port+hp_seltimeout,p_delay);
+ WRW_HARPOON((p_port+hp_intstat), TIMEOUT);
+ WRW_HARPOON((p_port+hp_intena), (default_intena & ~TIMEOUT));
+
+
+ WR_HARPOON(p_port+hp_portctrl_0,
+ (RD_HARPOON(p_port+hp_portctrl_0) | START_TO));
+
+ while (!(RDW_HARPOON((p_port+hp_intstat)) & TIMEOUT)) {
+
+ if ((RD_HARPOON(p_port+hp_scsictrl_0) & SCSI_RST))
+ break;
+
+ if ((RDW_HARPOON((p_port+hp_intstat)) & SCAM_SEL))
+ break;
+ }
+
+ WR_HARPOON(p_port+hp_portctrl_0,
+ (RD_HARPOON(p_port+hp_portctrl_0) & ~START_TO));
+
+ WRW_HARPOON((p_port+hp_intstat), TIMEOUT);
+ WRW_HARPOON((p_port+hp_intena), default_intena);
+
+ WR_HARPOON(p_port+hp_clkctrl_0,green_flag);
+
+ WR_HARPOON(p_port+hp_seltimeout,old_timer);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Enable/Disable Write to EEPROM
+ *
+ * Description: The EEPROM must first be enabled for writes
+ * A total of 9 clocks are needed.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void utilEEWriteOnOff(USHORT p_port,UCHAR p_mode)
+#else
+void utilEEWriteOnOff(ULONG p_port,UCHAR p_mode)
+#endif
+{
+ UCHAR ee_value;
+
+ ee_value = (UCHAR)(RD_HARPOON(p_port+hp_ee_ctrl) & (EXT_ARB_ACK | SCSI_TERM_ENA_H));
+
+ if (p_mode)
+
+ utilEESendCmdAddr(p_port, EWEN, EWEN_ADDR);
+
+ else
+
+
+ utilEESendCmdAddr(p_port, EWDS, EWDS_ADDR);
+
+ WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS)); /*Turn off CS */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value); /*Turn off Master Select */
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Write EEPROM
+ *
+ * Description: Write a word to the EEPROM at the specified
+ * address.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void utilEEWrite(USHORT p_port, USHORT ee_data, USHORT ee_addr)
+#else
+void utilEEWrite(ULONG p_port, USHORT ee_data, USHORT ee_addr)
+#endif
+{
+
+ UCHAR ee_value;
+ USHORT i;
+
+ ee_value = (UCHAR)((RD_HARPOON(p_port+hp_ee_ctrl) & (EXT_ARB_ACK | SCSI_TERM_ENA_H))|
+ (SEE_MS | SEE_CS));
+
+
+
+ utilEESendCmdAddr(p_port, EE_WRITE, ee_addr);
+
+
+ ee_value |= (SEE_MS + SEE_CS);
+
+ for(i = 0x8000; i != 0; i>>=1) {
+
+ if (i & ee_data)
+ ee_value |= SEE_DO;
+ else
+ ee_value &= ~SEE_DO;
+
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value |= SEE_CLK; /* Clock data! */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value &= ~SEE_CLK;
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ }
+ ee_value &= (EXT_ARB_ACK | SCSI_TERM_ENA_H);
+ WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS));
+
+ Wait(p_port, TO_10ms);
+
+ WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS | SEE_CS)); /* Set CS to EEPROM */
+ WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS)); /* Turn off CS */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value); /* Turn off Master Select */
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Read EEPROM
+ *
+ * Description: Read a word from the EEPROM at the desired
+ * address.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+USHORT utilEERead(USHORT p_port, USHORT ee_addr)
+#else
+USHORT utilEERead(ULONG p_port, USHORT ee_addr)
+#endif
+{
+ USHORT i, ee_data1, ee_data2;
+
+ i = 0;
+ ee_data1 = utilEEReadOrg(p_port, ee_addr);
+ do
+ {
+ ee_data2 = utilEEReadOrg(p_port, ee_addr);
+
+ if(ee_data1 == ee_data2)
+ return(ee_data1);
+
+ ee_data1 = ee_data2;
+ i++;
+
+ }while(i < 4);
+
+ return(ee_data1);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Read EEPROM Original
+ *
+ * Description: Read a word from the EEPROM at the desired
+ * address.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+USHORT utilEEReadOrg(USHORT p_port, USHORT ee_addr)
+#else
+USHORT utilEEReadOrg(ULONG p_port, USHORT ee_addr)
+#endif
+{
+
+ UCHAR ee_value;
+ USHORT i, ee_data;
+
+ ee_value = (UCHAR)((RD_HARPOON(p_port+hp_ee_ctrl) & (EXT_ARB_ACK | SCSI_TERM_ENA_H))|
+ (SEE_MS | SEE_CS));
+
+
+ utilEESendCmdAddr(p_port, EE_READ, ee_addr);
+
+
+ ee_value |= (SEE_MS + SEE_CS);
+ ee_data = 0;
+
+ for(i = 1; i <= 16; i++) {
+
+ ee_value |= SEE_CLK; /* Clock data! */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value &= ~SEE_CLK;
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+
+ ee_data <<= 1;
+
+ if (RD_HARPOON(p_port+hp_ee_ctrl) & SEE_DI)
+ ee_data |= 1;
+ }
+
+ ee_value &= ~(SEE_MS + SEE_CS);
+ WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS)); /*Turn off CS */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value); /*Turn off Master Select */
+
+ return(ee_data);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Send EE command and Address to the EEPROM
+ *
+ * Description: Transfers the correct command and sends the address
+ * to the eeprom.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void utilEESendCmdAddr(USHORT p_port, UCHAR ee_cmd, USHORT ee_addr)
+#else
+void utilEESendCmdAddr(ULONG p_port, UCHAR ee_cmd, USHORT ee_addr)
+#endif
+{
+ UCHAR ee_value;
+ UCHAR narrow_flg;
+
+ USHORT i;
+
+
+ narrow_flg= (UCHAR)(RD_HARPOON(p_port+hp_page_ctrl) & NARROW_SCSI_CARD);
+
+
+ ee_value = SEE_MS;
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+
+ ee_value |= SEE_CS; /* Set CS to EEPROM */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+
+
+ for(i = 0x04; i != 0; i>>=1) {
+
+ if (i & ee_cmd)
+ ee_value |= SEE_DO;
+ else
+ ee_value &= ~SEE_DO;
+
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value |= SEE_CLK; /* Clock data! */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value &= ~SEE_CLK;
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ }
+
+
+ if (narrow_flg)
+ i = 0x0080;
+
+ else
+ i = 0x0200;
+
+
+ while (i != 0) {
+
+ if (i & ee_addr)
+ ee_value |= SEE_DO;
+ else
+ ee_value &= ~SEE_DO;
+
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value |= SEE_CLK; /* Clock data! */
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ ee_value &= ~SEE_CLK;
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+ WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+
+ i >>= 1;
+ }
+}
+
+USHORT CalcCrc16(UCHAR buffer[])
+{
+ USHORT crc=0;
+ int i,j;
+ USHORT ch;
+ for (i=0; i < ID_STRING_LENGTH; i++)
+ {
+ ch = (USHORT) buffer[i];
+ for(j=0; j < 8; j++)
+ {
+ if ((crc ^ ch) & 1)
+ crc = (crc >> 1) ^ CRCMASK;
+ else
+ crc >>= 1;
+ ch >>= 1;
+ }
+ }
+ return(crc);
+}
+
+UCHAR CalcLrc(UCHAR buffer[])
+{
+ int i;
+ UCHAR lrc;
+ lrc = 0;
+ for(i = 0; i < ID_STRING_LENGTH; i++)
+ lrc ^= buffer[i];
+ return(lrc);
+}
+
+
+
+/*
+ The following inline definitions avoid type conflicts.
+*/
+
+static inline unsigned char
+FlashPoint__ProbeHostAdapter(FlashPoint_Info_T *FlashPointInfo)
+{
+ return FlashPoint_ProbeHostAdapter((PSCCBMGR_INFO) FlashPointInfo);
+}
+
+
+static inline FlashPoint_CardHandle_T
+FlashPoint__HardwareResetHostAdapter(FlashPoint_Info_T *FlashPointInfo)
+{
+ return FlashPoint_HardwareResetHostAdapter((PSCCBMGR_INFO) FlashPointInfo);
+}
+
+static inline void
+FlashPoint__ReleaseHostAdapter(FlashPoint_CardHandle_T CardHandle)
+{
+ FlashPoint_ReleaseHostAdapter(CardHandle);
+}
+
+
+static inline void
+FlashPoint__StartCCB(FlashPoint_CardHandle_T CardHandle, BusLogic_CCB_T *CCB)
+{
+ FlashPoint_StartCCB(CardHandle, (PSCCB) CCB);
+}
+
+
+static inline void
+FlashPoint__AbortCCB(FlashPoint_CardHandle_T CardHandle, BusLogic_CCB_T *CCB)
+{
+ FlashPoint_AbortCCB(CardHandle, (PSCCB) CCB);
+}
+
+
+static inline boolean
+FlashPoint__InterruptPending(FlashPoint_CardHandle_T CardHandle)
+{
+ return FlashPoint_InterruptPending(CardHandle);
+}
+
+
+static inline int
+FlashPoint__HandleInterrupt(FlashPoint_CardHandle_T CardHandle)
+{
+ return FlashPoint_HandleInterrupt(CardHandle);
+}
+
+
+#define FlashPoint_ProbeHostAdapter FlashPoint__ProbeHostAdapter
+#define FlashPoint_HardwareResetHostAdapter FlashPoint__HardwareResetHostAdapter
+#define FlashPoint_ReleaseHostAdapter FlashPoint__ReleaseHostAdapter
+#define FlashPoint_StartCCB FlashPoint__StartCCB
+#define FlashPoint_AbortCCB FlashPoint__AbortCCB
+#define FlashPoint_InterruptPending FlashPoint__InterruptPending
+#define FlashPoint_HandleInterrupt FlashPoint__HandleInterrupt
+
+
+/*
+ FlashPoint_InquireTargetInfo returns the Synchronous Period, Synchronous
+ Offset, and Wide Transfers Active information for TargetID on CardHandle.
+*/
+
+void FlashPoint_InquireTargetInfo(FlashPoint_CardHandle_T CardHandle,
+ int TargetID,
+ unsigned char *SynchronousPeriod,
+ unsigned char *SynchronousOffset,
+ unsigned char *WideTransfersActive)
+{
+ SCCBMGR_TAR_INFO *TargetInfo =
+ &sccbMgrTbl[((SCCBCARD *)CardHandle)->cardIndex][TargetID];
+ if ((TargetInfo->TarSyncCtrl & SYNC_OFFSET) > 0)
+ {
+ *SynchronousPeriod = 5 * ((TargetInfo->TarSyncCtrl >> 5) + 1);
+ *SynchronousOffset = TargetInfo->TarSyncCtrl & SYNC_OFFSET;
+ }
+ else
+ {
+ *SynchronousPeriod = 0;
+ *SynchronousOffset = 0;
+ }
+ *WideTransfersActive = (TargetInfo->TarSyncCtrl & NARROW_SCSI ? 0 : 1);
+}
+
+
+#else /* CONFIG_SCSI_OMIT_FLASHPOINT */
+
+
+/*
+ Define prototypes for the FlashPoint SCCB Manager Functions.
+*/
+
+extern unsigned char FlashPoint_ProbeHostAdapter(FlashPoint_Info_T *);
+extern FlashPoint_CardHandle_T
+ FlashPoint_HardwareResetHostAdapter(FlashPoint_Info_T *);
+extern void FlashPoint_StartCCB(FlashPoint_CardHandle_T, BusLogic_CCB_T *);
+extern int FlashPoint_AbortCCB(FlashPoint_CardHandle_T, BusLogic_CCB_T *);
+extern boolean FlashPoint_InterruptPending(FlashPoint_CardHandle_T);
+extern int FlashPoint_HandleInterrupt(FlashPoint_CardHandle_T);
+extern void FlashPoint_ReleaseHostAdapter(FlashPoint_CardHandle_T);
+extern void FlashPoint_InquireTargetInfo(FlashPoint_CardHandle_T,
+ int, unsigned char *,
+ unsigned char *, unsigned char *);
+
+
+#endif /* CONFIG_SCSI_OMIT_FLASHPOINT */
diff --git a/linux/src/drivers/scsi/NCR5380.c b/linux/src/drivers/scsi/NCR5380.c
new file mode 100644
index 00000000..e6fc75c9
--- /dev/null
+++ b/linux/src/drivers/scsi/NCR5380.c
@@ -0,0 +1,3247 @@
+#ifndef NDEBUG
+#define NDEBUG (NDEBUG_RESTART_SELECT | NDEBUG_ABORT)
+#endif
+/*
+ * NCR 5380 generic driver routines. These should make it *trivial*
+ * to implement 5380 SCSI drivers under Linux with a non-trantor
+ * architecture.
+ *
+ * Note that these routines also work with NR53c400 family chips.
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * DISTRIBUTION RELEASE 6.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: NCR5380.c,v $
+ * Revision 1.7 1996/3/2 Ray Van Tassle (rayvt@comm.mot.com)
+ * added proc_info
+ * added support needed for DTC 3180/3280
+ * fixed a couple of bugs
+ *
+
+ * Revision 1.5 1994/01/19 09:14:57 drew
+ * Fixed udelay() hack that was being used on DATAOUT phases
+ * instead of a proper wait for the final handshake.
+ *
+ * Revision 1.4 1994/01/19 06:44:25 drew
+ * *** empty log message ***
+ *
+ * Revision 1.3 1994/01/19 05:24:40 drew
+ * Added support for TCR LAST_BYTE_SENT bit.
+ *
+ * Revision 1.2 1994/01/15 06:14:11 drew
+ * REAL DMA support, bug fixes.
+ *
+ * Revision 1.1 1994/01/15 06:00:54 drew
+ * Initial revision
+ *
+ */
+
+/*
+ * Further development / testing that should be done :
+ * 1. Cleanup the NCR5380_transfer_dma function and DMA operation complete
+ * code so that everything does the same thing that's done at the
+ * end of a pseudo-DMA read operation.
+ *
+ * 2. Fix REAL_DMA (interrupt driven, polled works fine) -
+ * basically, transfer size needs to be reduced by one
+ * and the last byte read as is done with PSEUDO_DMA.
+ *
+ * 3. Test USLEEP code
+ *
+ * 4. Test SCSI-II tagged queueing (I have no devices which support
+ * tagged queueing)
+ *
+ * 5. Test linked command handling code after Eric is ready with
+ * the high level code.
+ */
+
+#if (NDEBUG & NDEBUG_LISTS)
+#define LIST(x,y) {printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); }
+#define REMOVE(w,x,y,z) {printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); }
+#else
+#define LIST(x,y)
+#define REMOVE(w,x,y,z)
+#endif
+
+#ifndef notyet
+#undef LINKED
+#undef USLEEP
+#undef REAL_DMA
+#endif
+
+#ifdef REAL_DMA_POLL
+#undef READ_OVERRUNS
+#define READ_OVERRUNS
+#endif
+
+/*
+ * Design
+ * Issues :
+ *
+ * The other Linux SCSI drivers were written when Linux was Intel PC-only,
+ * and specifically for each board rather than each chip. This makes their
+ * adaptation to platforms like the Mac (Some of which use NCR5380's)
+ * more difficult than it has to be.
+ *
+ * Also, many of the SCSI drivers were written before the command queuing
+ * routines were implemented, meaning their implementations of queued
+ * commands were hacked on rather than designed in from the start.
+ *
+ * When I designed the Linux SCSI drivers I figured that
+ * while having two different SCSI boards in a system might be useful
+ * for debugging things, two of the same type wouldn't be used.
+ * Well, I was wrong and a number of users have mailed me about running
+ * multiple high-performance SCSI boards in a server.
+ *
+ * Finally, when I get questions from users, I have no idea what
+ * revision of my driver they are running.
+ *
+ * This driver attempts to address these problems :
+ * This is a generic 5380 driver. To use it on a different platform,
+ * one simply writes appropriate system specific macros (ie, data
+ * transfer - some PC's will use the I/O bus, 68K's must use
+ * memory mapped) and drops this file in their 'C' wrapper.
+ *
+ * As far as command queueing, two queues are maintained for
+ * each 5380 in the system - commands that haven't been issued yet,
+ * and commands that are currently executing. This means that an
+ * unlimited number of commands may be queued, letting
+ * more commands propagate from the higher driver levels giving higher
+ * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported,
+ * allowing multiple commands to propagate all the way to a SCSI-II device
+ * while a command is already executing.
+ *
+ * To solve the multiple-boards-in-the-same-system problem,
+ * there is a separate instance structure for each instance
+ * of a 5380 in the system. So, multiple NCR5380 drivers will
+ * be able to coexist with appropriate changes to the high level
+ * SCSI code.
+ *
+ * A NCR5380_PUBLIC_REVISION macro is provided, with the release
+ * number (updated for each public release) printed by the
+ * NCR5380_print_options command, which should be called from the
+ * wrapper detect function, so that I know what release of the driver
+ * users are using.
+ *
+ * Issues specific to the NCR5380 :
+ *
+ * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
+ * piece of hardware that requires you to sit in a loop polling for
+ * the REQ signal as long as you are connected. Some devices are
+ * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
+ * while doing long seek operations.
+ *
+ * The workaround for this is to keep track of devices that have
+ * disconnected. If the device hasn't disconnected, for commands that
+ * should disconnect, we do something like
+ *
+ * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
+ *
+ * Some tweaking of N and M needs to be done. An algorithm based
+ * on "time to data" would give the best results as long as short time
+ * to datas (ie, on the same track) were considered, however these
+ * broken devices are the exception rather than the rule and I'd rather
+ * spend my time optimizing for the normal case.
+ *
+ * Architecture :
+ *
+ * At the heart of the design is a coroutine, NCR5380_main,
+ * which is started when not running by the interrupt handler,
+ * timer, and queue command function. It attempts to establish
+ * I_T_L or I_T_L_Q nexuses by removing the commands from the
+ * issue queue and calling NCR5380_select() if a nexus
+ * is not established.
+ *
+ * Once a nexus is established, the NCR5380_information_transfer()
+ * phase goes through the various phases as instructed by the target.
+ * if the target goes into MSG IN and sends a DISCONNECT message,
+ * the command structure is placed into the per instance disconnected
+ * queue, and NCR5380_main tries to find more work. If USLEEP
+ * was defined, and the target is idle for too long, the system
+ * will try to sleep.
+ *
+ * If a command has disconnected, eventually an interrupt will trigger,
+ * calling NCR5380_intr() which will in turn call NCR5380_reselect
+ * to reestablish a nexus. This will run main if necessary.
+ *
+ * On command termination, the done function will be called as
+ * appropriate.
+ *
+ * SCSI pointers are maintained in the SCp field of SCSI command
+ * structures, being initialized after the command is connected
+ * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
+ * Note that in violation of the standard, an implicit SAVE POINTERS operation
+ * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
+ */
+
+/*
+ * Using this file :
+ * This file a skeleton Linux SCSI driver for the NCR 5380 series
+ * of chips. To use it, you write a architecture specific functions
+ * and macros and include this file in your driver.
+ *
+ * These macros control options :
+ * AUTOPROBE_IRQ - if defined, the NCR5380_probe_irq() function will be
+ * defined.
+ *
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * DIFFERENTIAL - if defined, NCR53c81 chips will use external differential
+ * transceivers.
+ *
+ * DONT_USE_INTR - if defined, never use interrupts, even if we probe or
+ * override-configure an IRQ.
+ *
+ * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512
+ * bytes at a time. Since interrupts are disabled by default during
+ * these transfers, we might need this to give reasonable interrupt
+ * service time if the transfer size gets too large.
+ *
+ * LINKED - if defined, linked commands are supported.
+ *
+ * PSEUDO_DMA - if defined, PSEUDO DMA is used during the data transfer phases.
+ *
+ * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
+ *
+ * REAL_DMA_POLL - if defined, REAL DMA is used but the driver doesn't
+ * rely on phase mismatch and EOP interrupts to determine end
+ * of phase.
+ *
+ * SCSI2 - if defined, SCSI-2 tagged queuing is used where possible
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You
+ * only really want to use this if you're having a problem with
+ * dropped characters during high speed communications, and even
+ * then, you're going to be better off twiddling with transfersize
+ * in the high level code.
+ *
+ * USLEEP - if defined, on devices that aren't disconnecting from the
+ * bus, we will go to sleep so that the CPU can get real work done
+ * when we run a command that won't complete immediately.
+ *
+ * Note that if USLEEP is defined, NCR5380_TIMER *must* also be
+ * defined.
+ *
+ * Defaults for these will be provided if USLEEP is defined, although
+ * the user may want to adjust these to allocate CPU resources to
+ * the SCSI driver or "real" code.
+ *
+ * USLEEP_SLEEP - amount of time, in jiffies, to sleep
+ *
+ * USLEEP_POLL - amount of time, in jiffies, to poll
+ *
+ * These macros MUST be defined :
+ * NCR5380_local_declare() - declare any local variables needed for your
+ * transfer routines.
+ *
+ * NCR5380_setup(instance) - initialize any local variables needed from a given
+ * instance of the host adapter for NCR5380_{read,write,pread,pwrite}
+ *
+ * NCR5380_read(register) - read from the specified register
+ *
+ * NCR5380_write(register, value) - write to the specific register
+ *
+ * NCR5380_implementation_fields - additional fields needed for this
+ * specific implementation of the NCR5380
+ *
+ * Either real DMA *or* pseudo DMA may be implemented
+ * REAL functions :
+ * NCR5380_REAL_DMA should be defined if real DMA is to be used.
+ * Note that the DMA setup functions should return the number of bytes
+ * that they were able to program the controller for.
+ *
+ * Also note that generic i386/PC versions of these macros are
+ * available as NCR5380_i386_dma_write_setup,
+ * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
+ *
+ * NCR5380_dma_write_setup(instance, src, count) - initialize
+ * NCR5380_dma_read_setup(instance, dst, count) - initialize
+ * NCR5380_dma_residual(instance); - residual count
+ *
+ * PSEUDO functions :
+ * NCR5380_pwrite(instance, src, count)
+ * NCR5380_pread(instance, dst, count);
+ *
+ * If nothing specific to this implementation needs doing (ie, with external
+ * hardware), you must also define
+ *
+ * NCR5380_queue_command
+ * NCR5380_reset
+ * NCR5380_abort
+ * NCR5380_proc_info
+ *
+ * to be the global entry points into the specific driver, ie
+ * #define NCR5380_queue_command t128_queue_command.
+ *
+ * If this is not done, the routines will be defined as static functions
+ * with the NCR5380* names and the user must provide a globally
+ * accessible wrapper function.
+ *
+ * The generic driver is initialized by calling NCR5380_init(instance),
+ * after setting the appropriate host specific fields and ID. If the
+ * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
+ * possible) function may be used. Before the specific driver initialization
+ * code finishes, NCR5380_print_options should be called.
+ */
+
+static int do_abort (struct Scsi_Host *host);
+static void do_reset (struct Scsi_Host *host);
+static struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+
+/*
+ * Function : void initialize_SCp(Scsi_Cmnd *cmd)
+ *
+ * Purpose : initialize the saved data pointers for cmd to point to the
+ * start of the buffer.
+ *
+ * Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
+ */
+
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd) {
+ /*
+ * Initialize the Scsi Pointer field so that all of the commands in the
+ * various queues are valid.
+ */
+
+ if (cmd->use_sg) {
+ cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ } else {
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ }
+}
+
+#include <linux/delay.h>
+
+#ifdef NDEBUG
+static struct {
+ unsigned char mask;
+ const char * name;}
+signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" },
+ { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" },
+ { SR_SEL, "SEL" }, {0, NULL}},
+basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}},
+icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
+ {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"},
+ {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"},
+ {0, NULL}},
+mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"},
+ {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR,
+ "MODE PARITY INTR"}, {MR_MONITOR_BSY, "MODE MONITOR BSY"},
+ {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"},
+ {0, NULL}};
+
+/*
+ * Function : void NCR5380_print(struct Scsi_Host *instance)
+ *
+ * Purpose : print the SCSI bus signals for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print(struct Scsi_Host *instance) {
+ NCR5380_local_declare();
+ unsigned char status, data, basr, mr, icr, i;
+ NCR5380_setup(instance);
+ cli();
+ data = NCR5380_read(CURRENT_SCSI_DATA_REG);
+ status = NCR5380_read(STATUS_REG);
+ mr = NCR5380_read(MODE_REG);
+ icr = NCR5380_read(INITIATOR_COMMAND_REG);
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+ sti();
+ printk("STATUS_REG: %02x ", status);
+ for (i = 0; signals[i].mask ; ++i)
+ if (status & signals[i].mask)
+ printk(",%s", signals[i].name);
+ printk("\nBASR: %02x ", basr);
+ for (i = 0; basrs[i].mask ; ++i)
+ if (basr & basrs[i].mask)
+ printk(",%s", basrs[i].name);
+ printk("\nICR: %02x ", icr);
+ for (i = 0; icrs[i].mask; ++i)
+ if (icr & icrs[i].mask)
+ printk(",%s", icrs[i].name);
+ printk("\nMODE: %02x ", mr);
+ for (i = 0; mrs[i].mask; ++i)
+ if (mr & mrs[i].mask)
+ printk(",%s", mrs[i].name);
+ printk("\n");
+}
+
+static struct {
+ unsigned char value;
+ const char *name;
+} phases[] = {
+{PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
+{PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
+{PHASE_UNKNOWN, "UNKNOWN"}};
+
+/*
+ * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
+ *
+ * Purpose : print the current SCSI phase for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print_phase(struct Scsi_Host *instance) {
+ NCR5380_local_declare();
+ unsigned char status;
+ int i;
+ NCR5380_setup(instance);
+
+ status = NCR5380_read(STATUS_REG);
+ if (!(status & SR_REQ))
+ printk("scsi%d : REQ not asserted, phase unknown.\n",
+ instance->host_no);
+ else {
+ for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
+ (phases[i].value != (status & PHASE_MASK)); ++i);
+ printk("scsi%d : phase %s\n", instance->host_no, phases[i].name);
+ }
+}
+#endif
+
+/*
+ * We need to have our coroutine active given these constraints :
+ * 1. The mutex flag, main_running, can only be set when the main
+ * routine can actually process data, otherwise SCSI commands
+ * will never get issued.
+ *
+ * 2. NCR5380_main() shouldn't be called before it has exited, because
+ * other drivers have had kernel stack overflows in similar
+ * situations.
+ *
+ * 3. We don't want to inline NCR5380_main() because of space concerns,
+ * even though it is only called in two places.
+ *
+ * So, the solution is to set the mutex in an inline wrapper for the
+ * main coroutine, and have the main coroutine exit with interrupts
+ * disabled after the final search through the queues so that no race
+ * conditions are possible.
+ */
+
+static volatile int main_running = 0;
+
+/*
+ * Function : run_main(void)
+ *
+ * Purpose : insure that the coroutine is running and will process our
+ * request. main_running is checked/set here (in an inline function)
+ * rather than in NCR5380_main itself to reduce the chances of stack
+ * overflow.
+ *
+ */
+
+static __inline__ void run_main(void) {
+ cli();
+ if (!main_running) {
+ main_running = 1;
+ NCR5380_main();
+ /*
+ * main_running is cleared in NCR5380_main once it can't do
+ * more work, and NCR5380_main exits with interrupts disabled.
+ */
+ sti();
+ } else
+ sti();
+}
+
+#ifdef USLEEP
+#ifndef NCR5380_TIMER
+#error "NCR5380_TIMER must be defined so that this type of NCR5380 driver gets a unique timer."
+#endif
+
+/*
+ * These need tweaking, and would probably work best as per-device
+ * flags initialized differently for disk, tape, cd, etc devices.
+ * People with broken devices are free to experiment as to what gives
+ * the best results for them.
+ *
+ * USLEEP_SLEEP should be a minimum seek time.
+ *
+ * USLEEP_POLL should be a maximum rotational latency.
+ */
+#ifndef USLEEP_SLEEP
+/* 20 ms (reasonable hard disk speed) */
+#define USLEEP_SLEEP (20*HZ/1000)
+#endif
+/* 300 RPM (floppy speed) */
+#ifndef USLEEP_POLL
+#define USLEEP_POLL (200*HZ/1000)
+#endif
+
+static struct Scsi_Host * expires_first = NULL;
+
+/*
+ * Function : int should_disconnect (unsigned char cmd)
+ *
+ * Purpose : decide weather a command would normally disconnect or
+ * not, since if it won't disconnect we should go to sleep.
+ *
+ * Input : cmd - opcode of SCSI command
+ *
+ * Returns : DISCONNECT_LONG if we should disconnect for a really long
+ * time (ie always, sleep, look for REQ active, sleep),
+ * DISCONNECT_TIME_TO_DATA if we would only disconnect for a normal
+ * time-to-data delay, DISCONNECT_NONE if this command would return
+ * immediately.
+ *
+ * Future sleep algorithms based on time to data can exploit
+ * something like this so they can differentiate between "normal"
+ * (ie, read, write, seek) and unusual commands (ie, * format).
+ *
+ * Note : We don't deal with commands that handle an immediate disconnect,
+ *
+ */
+
+static int should_disconnect (unsigned char cmd) {
+ switch (cmd) {
+ case READ_6:
+ case WRITE_6:
+ case SEEK_6:
+ case READ_10:
+ case WRITE_10:
+ case SEEK_10:
+ return DISCONNECT_TIME_TO_DATA;
+ case FORMAT_UNIT:
+ case SEARCH_HIGH:
+ case SEARCH_LOW:
+ case SEARCH_EQUAL:
+ return DISCONNECT_LONG;
+ default:
+ return DISCONNECT_NONE;
+ }
+}
+
+/*
+ * Assumes instance->time_expires has been set in higher level code.
+ */
+
+static int NCR5380_set_timer (struct Scsi_Host *instance) {
+ struct Scsi_Host *tmp, **prev;
+
+ cli();
+ if (((struct NCR5380_hostdata *) (instance->host_data))->next_timer) {
+ sti();
+ return -1;
+ }
+
+ for (prev = &expires_first, tmp = expires_first; tmp;
+ prev = &(((struct NCR5380_hostdata *) tmp->host_data)->next_timer),
+ tmp = ((struct NCR5380_hostdata *) tmp->host_data)->next_timer)
+ if (instance->time_expires < tmp->time_expires)
+ break;
+
+ instance->next_timer = tmp;
+ *prev = instance;
+ timer_table[NCR5380_TIMER].expires = expires_first->time_expires;
+ timer_active |= 1 << NCR5380_TIMER;
+ sti();
+ return 0;
+}
+
+/* Doing something about unwanted reentrancy here might be useful */
+void NCR5380_timer_fn(void) {
+ struct Scsi_Host *instance;
+ cli();
+ for (; expires_first && expires_first->time_expires >= jiffies; ) {
+ instance = ((NCR5380_hostdata *) expires_first->host_data)->
+ expires_next;
+ ((NCR5380_hostdata *) expires_first->host_data)->expires_next =
+ NULL;
+ ((NCR5380_hostdata *) expires_first->host_data)->time_expires =
+ 0;
+ expires_first = instance;
+ }
+
+ if (expires_first) {
+ timer_table[NCR5380_TIMER].expires = ((NCR5380_hostdata *)
+ expires_first->host_data)->time_expires;
+ timer_active |= (1 << NCR5380_TIMER);
+ } else {
+ timer_table[NCR5380_TIMER].expires = 0;
+ timer_active &= ~(1 << MCR5380_TIMER);
+ }
+ sti();
+
+ run_main();
+}
+#endif /* def USLEEP */
+
+static void NCR5380_all_init (void) {
+ static int done = 0;
+ if (!done) {
+#if (NDEBUG & NDEBUG_INIT)
+ printk("scsi : NCR5380_all_init()\n");
+#endif
+ done = 1;
+#ifdef USLEEP
+ timer_table[NCR5380_TIMER].expires = 0;
+ timer_table[NCR5380_TIMER].fn = NCR5380_timer_fn;
+#endif
+ }
+}
+
+#ifdef AUTOPROBE_IRQ
+/*
+ * Function : int NCR5380_probe_irq (struct Scsi_Host *instance, int possible)
+ *
+ * Purpose : autoprobe for the IRQ line used by the NCR5380.
+ *
+ * Inputs : instance - pointer to this instance of the NCR5380 driver,
+ * possible - bitmask of permissible interrupts.
+ *
+ * Returns : number of the IRQ selected, IRQ_NONE if no interrupt fired.
+ *
+ * XXX no effort is made to deal with spurious interrupts.
+ */
+
+
+static int probe_irq;
+static void probe_intr (int irq, void *dev_id, struct pt_regs * regs) {
+ probe_irq = irq;
+};
+
+static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+ unsigned long timeout;
+ int trying_irqs, i, mask;
+ NCR5380_setup(instance);
+
+ for (trying_irqs = i = 0, mask = 1; i < 16; ++i, mask <<= 1)
+ if ((mask & possible) && (request_irq(i, &probe_intr, SA_INTERRUPT, "NCR-probe", NULL)
+ == 0))
+ trying_irqs |= mask;
+
+ timeout = jiffies + 250*HZ/1000;
+ probe_irq = IRQ_NONE;
+
+/*
+ * A interrupt is triggered whenever BSY = false, SEL = true
+ * and a bit set in the SELECT_ENABLE_REG is asserted on the
+ * SCSI bus.
+ *
+ * Note that the bus is only driven when the phase control signals
+ * (I/O, C/D, and MSG) match those in the TCR, so we must reset that
+ * to zero.
+ */
+
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA |
+ ICR_ASSERT_SEL);
+
+ while (probe_irq == IRQ_NONE && jiffies < timeout)
+ barrier();
+
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ for (i = 0, mask = 1; i < 16; ++i, mask <<= 1)
+ if (trying_irqs & mask)
+ free_irq(i, NULL);
+
+ return probe_irq;
+}
+#endif /* AUTOPROBE_IRQ */
+
+/*
+ * Function : void NCR58380_print_options (struct Scsi_Host *instance)
+ *
+ * Purpose : called by probe code indicating the NCR5380 driver
+ * options that were selected.
+ *
+ * Inputs : instance, pointer to this instance. Unused.
+ */
+
+static void NCR5380_print_options (struct Scsi_Host *instance) {
+ printk(" generic options"
+#ifdef AUTOPROBE_IRQ
+ " AUTOPROBE_IRQ"
+#endif
+#ifdef AUTOSENSE
+ " AUTOSENSE"
+#endif
+#ifdef DIFFERENTIAL
+ " DIFFERENTIAL"
+#endif
+#ifdef REAL_DMA
+ " REAL DMA"
+#endif
+#ifdef REAL_DMA_POLL
+ " REAL DMA POLL"
+#endif
+#ifdef PARITY
+ " PARITY"
+#endif
+#ifdef PSEUDO_DMA
+ " PSEUDO DMA"
+#endif
+#ifdef SCSI2
+ " SCSI-2"
+#endif
+#ifdef UNSAFE
+ " UNSAFE "
+#endif
+ );
+#ifdef USLEEP
+ printk(" USLEEP, USLEEP_POLL=%d USLEEP_SLEEP=%d", USLEEP_POLL, USLEEP_SLEEP);
+#endif
+ printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
+ if (((struct NCR5380_hostdata *)instance->hostdata)->flags & FLAG_NCR53C400) {
+ printk(" ncr53c400 release=%d", NCR53C400_PUBLIC_RELEASE);
+ }
+}
+
+/*
+ * Function : void NCR5380_print_status (struct Scsi_Host *instance)
+ *
+ * Purpose : print commands in the various queues, called from
+ * NCR5380_abort and NCR5380_debug to aid debugging.
+ *
+ * Inputs : instance, pointer to this instance.
+ */
+
+static void NCR5380_print_status (struct Scsi_Host *instance) {
+ static char pr_bfr[512];
+ char *start;
+ int len;
+
+ printk("NCR5380 : coroutine is%s running.\n",
+ main_running ? "" : "n't");
+
+#ifdef NDEBUG
+ NCR5380_print (instance);
+ NCR5380_print_phase (instance);
+#endif
+
+ len = NCR5380_proc_info(pr_bfr, &start, 0, sizeof(pr_bfr),
+ instance->host_no, 0);
+ pr_bfr[len] = 0;
+ printk("\n%s\n", pr_bfr);
+ }
+
+/******************************************/
+/*
+ * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED]
+ *
+ * *buffer: I/O buffer
+ * **start: if inout == FALSE pointer into buffer where user read should start
+ * offset: current offset
+ * length: length of buffer
+ * hostno: Scsi_Host host_no
+ * inout: TRUE - user is writing; FALSE - user is reading
+ *
+ * Return the number of bytes read from or written
+*/
+
+#undef SPRINTF
+#define SPRINTF(args...) do { if(pos < buffer + length-80) pos += sprintf(pos, ## args); } while(0)
+static
+char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length);
+static
+char *lprint_command (unsigned char *cmd, char *pos, char *buffer, int len);
+static
+char *lprint_opcode(int opcode, char *pos, char *buffer, int length);
+
+#ifndef NCR5380_proc_info
+static
+#endif
+int NCR5380_proc_info (
+ char *buffer, char **start,off_t offset,
+ int length,int hostno,int inout)
+{
+ char *pos = buffer;
+ struct Scsi_Host *instance;
+ struct NCR5380_hostdata *hostdata;
+ Scsi_Cmnd *ptr;
+
+ for (instance = first_instance; instance &&
+ instance->host_no != hostno; instance=instance->next)
+ ;
+ if (!instance)
+ return(-ESRCH);
+ hostdata = (struct NCR5380_hostdata *)instance->hostdata;
+
+ if (inout) { /* Has data been written to the file ? */
+#ifdef DTC_PUBLIC_RELEASE
+ dtc_wmaxi = dtc_maxi = 0;
+#endif
+#ifdef PAS16_PUBLIC_RELEASE
+ pas_wmaxi = pas_maxi = 0;
+#endif
+ return(-ENOSYS); /* Currently this is a no-op */
+ }
+ SPRINTF("NCR5380 core release=%d. ", NCR5380_PUBLIC_RELEASE);
+ if (((struct NCR5380_hostdata *)instance->hostdata)->flags & FLAG_NCR53C400)
+ SPRINTF("ncr53c400 release=%d. ", NCR53C400_PUBLIC_RELEASE);
+#ifdef DTC_PUBLIC_RELEASE
+ SPRINTF("DTC 3180/3280 release %d", DTC_PUBLIC_RELEASE);
+#endif
+#ifdef T128_PUBLIC_RELEASE
+ SPRINTF("T128 release %d", T128_PUBLIC_RELEASE);
+#endif
+#ifdef GENERIC_NCR5380_PUBLIC_RELEASE
+ SPRINTF("Generic5380 release %d", GENERIC_NCR5380_PUBLIC_RELEASE);
+#endif
+#ifdef PAS16_PUBLIC_RELEASE
+SPRINTF("PAS16 release=%d", PAS16_PUBLIC_RELEASE);
+#endif
+
+ SPRINTF("\nBase Addr: 0x%05lX ", (long)instance->base);
+ SPRINTF("io_port: %04x ", (int)instance->io_port);
+ if (instance->irq == IRQ_NONE)
+ SPRINTF("IRQ: None.\n");
+ else
+ SPRINTF("IRQ: %d.\n", instance->irq);
+
+#ifdef DTC_PUBLIC_RELEASE
+ SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n",
+ dtc_wmaxi, dtc_maxi);
+#endif
+#ifdef PAS16_PUBLIC_RELEASE
+ SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n",
+ pas_wmaxi, pas_maxi);
+#endif
+ cli();
+ SPRINTF("NCR5380 : coroutine is%s running.\n", main_running ? "" : "n't");
+ if (!hostdata->connected)
+ SPRINTF("scsi%d: no currently connected command\n", instance->host_no);
+ else
+ pos = lprint_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected,
+ pos, buffer, length);
+ SPRINTF("scsi%d: issue_queue\n", instance->host_no);
+ for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr;
+ ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
+
+ SPRINTF("scsi%d: disconnected_queue\n", instance->host_no);
+ for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
+ ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
+
+ sti();
+ *start=buffer;
+ if (pos - buffer < offset)
+ return 0;
+ else if (pos - buffer - offset < length)
+ return pos - buffer - offset;
+ return length;
+}
+
+static
+char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length) {
+ SPRINTF("scsi%d : destination target %d, lun %d\n",
+ cmd->host->host_no, cmd->target, cmd->lun);
+ SPRINTF(" command = ");
+ pos = lprint_command (cmd->cmnd, pos, buffer, length);
+ return (pos);
+}
+
+static
+char *lprint_command (unsigned char *command,
+ char *pos, char *buffer, int length) {
+ int i, s;
+ pos = lprint_opcode(command[0], pos, buffer, length);
+ for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+ SPRINTF("%02x ", command[i]);
+ SPRINTF("\n");
+ return(pos);
+}
+
+static
+char *lprint_opcode(int opcode, char *pos, char *buffer, int length) {
+ SPRINTF("%2d (0x%02x)", opcode, opcode);
+ return(pos);
+}
+
+
+/*
+ * Function : void NCR5380_init (struct Scsi_Host *instance, flags)
+ *
+ * Purpose : initializes *instance and corresponding 5380 chip,
+ * with flags OR'd into the initial flags value.
+ *
+ * Inputs : instance - instantiation of the 5380 driver.
+ *
+ * Notes : I assume that the host, hostno, and id bits have been
+ * set correctly. I don't care about the irq and other fields.
+ *
+ */
+
+static void NCR5380_init (struct Scsi_Host *instance, int flags) {
+ NCR5380_local_declare();
+ int i, pass;
+ unsigned long timeout;
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+
+ /*
+ * On NCR53C400 boards, NCR5380 registers are mapped 8 past
+ * the base address.
+ */
+
+#ifdef NCR53C400
+ if (flags & FLAG_NCR53C400)
+ instance->NCR5380_instance_name += NCR53C400_address_adjust;
+#endif
+
+ NCR5380_setup(instance);
+
+ NCR5380_all_init();
+
+ hostdata->aborted = 0;
+ hostdata->id_mask = 1 << instance->this_id;
+ for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
+ if (i > hostdata->id_mask)
+ hostdata->id_higher_mask |= i;
+ for (i = 0; i < 8; ++i)
+ hostdata->busy[i] = 0;
+#ifdef REAL_DMA
+ hostdata->dmalen = 0;
+#endif
+ hostdata->targets_present = 0;
+ hostdata->connected = NULL;
+ hostdata->issue_queue = NULL;
+ hostdata->disconnected_queue = NULL;
+#ifdef NCR5380_STATS
+ for (i = 0; i < 8; ++i) {
+ hostdata->time_read[i] = 0;
+ hostdata->time_write[i] = 0;
+ hostdata->bytes_read[i] = 0;
+ hostdata->bytes_write[i] = 0;
+ }
+ hostdata->timebase = 0;
+ hostdata->pendingw = 0;
+ hostdata->pendingr = 0;
+#endif
+
+ /* The CHECK code seems to break the 53C400. Will check it later maybe */
+ if (flags & FLAG_NCR53C400)
+ hostdata->flags = FLAG_HAS_LAST_BYTE_SENT | flags;
+ else
+ hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT | flags;
+
+ if (!the_template) {
+ the_template = instance->hostt;
+ first_instance = instance;
+ }
+
+
+#ifdef USLEEP
+ hostdata->time_expires = 0;
+ hostdata->next_timer = NULL;
+#endif
+
+#ifndef AUTOSENSE
+ if ((instance->cmd_per_lun > 1) || instance->can_queue > 1))
+ printk("scsi%d : WARNING : support for multiple outstanding commands enabled\n"
+ " without AUTOSENSE option, contingent allegiance conditions may\n"
+ " be incorrectly cleared.\n", instance->host_no);
+#endif /* def AUTOSENSE */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+
+#ifdef NCR53C400
+ if (hostdata->flags & FLAG_NCR53C400) {
+ NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
+ }
+#endif
+
+ /*
+ * Detect and correct bus wedge problems.
+ *
+ * If the system crashed, it may have crashed in a state
+ * where a SCSI command was still executing, and the
+ * SCSI bus is not in a BUS FREE STATE.
+ *
+ * If this is the case, we'll try to abort the currently
+ * established nexus which we know nothing about, and that
+ * failing, do a hard reset of the SCSI bus
+ */
+
+ for (pass = 1; (NCR5380_read(STATUS_REG) & SR_BSY) &&
+ pass <= 6 ; ++pass) {
+ switch (pass) {
+ case 1:
+ case 3:
+ case 5:
+ printk("scsi%d: SCSI bus busy, waiting up to five seconds\n",
+ instance->host_no);
+ timeout = jiffies + 5*HZ;
+ while (jiffies < timeout && (NCR5380_read(STATUS_REG) & SR_BSY));
+ break;
+ case 2:
+ printk("scsi%d: bus busy, attempting abort\n",
+ instance->host_no);
+ do_abort (instance);
+ break;
+ case 4:
+ printk("scsi%d: bus busy, attempting reset\n",
+ instance->host_no);
+ do_reset (instance);
+ break;
+ case 6:
+ printk("scsi%d: bus locked solid or invalid override\n",
+ instance->host_no);
+ }
+ }
+}
+
+/*
+ * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd,
+ * void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ * a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ * Side effects :
+ * cmd is added to the per instance issue_queue, with minor
+ * twiddling done to the host specific fields of cmd. If the
+ * main coroutine is not running, it is restarted.
+ *
+ */
+
+/* Only make static if a wrapper function is used */
+#ifndef NCR5380_queue_command
+static
+#endif
+int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) {
+ struct Scsi_Host *instance = cmd->host;
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+ Scsi_Cmnd *tmp;
+
+#if (NDEBUG & NDEBUG_NO_WRITE)
+ switch (cmd->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n",
+ instance->host_no);
+ cmd->result = (DID_ERROR << 16);
+ done(cmd);
+ return 0;
+ }
+#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
+
+#ifdef NCR5380_STATS
+# if 0
+ if (!hostdata->connected && !hostdata->issue_queue &&
+ !hostdata->disconnected_queue) {
+ hostdata->timebase = jiffies;
+ }
+# endif
+# ifdef NCR5380_STAT_LIMIT
+ if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
+# endif
+ switch (cmd->cmnd[0])
+ {
+ case WRITE:
+ case WRITE_6:
+ case WRITE_10:
+ hostdata->time_write[cmd->target] -= (jiffies - hostdata->timebase);
+ hostdata->bytes_write[cmd->target] += cmd->request_bufflen;
+ hostdata->pendingw++;
+ break;
+ case READ:
+ case READ_6:
+ case READ_10:
+ hostdata->time_read[cmd->target] -= (jiffies - hostdata->timebase);
+ hostdata->bytes_read[cmd->target] += cmd->request_bufflen;
+ hostdata->pendingr++;
+ break;
+ }
+#endif
+
+ /*
+ * We use the host_scribble field as a pointer to the next command
+ * in a queue
+ */
+
+ cmd->host_scribble = NULL;
+ cmd->scsi_done = done;
+
+ cmd->result = 0;
+
+
+ /*
+ * Insert the cmd into the issue queue. Note that REQUEST SENSE
+ * commands are added to the head of the queue since any command will
+ * clear the contingent allegiance condition that exists and the
+ * sense data is only guaranteed to be valid while the condition exists.
+ */
+
+ cli();
+ if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ LIST(cmd, hostdata->issue_queue);
+ cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
+ hostdata->issue_queue = cmd;
+ } else {
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble;
+ tmp = (Scsi_Cmnd *) tmp->host_scribble);
+ LIST(cmd, tmp);
+ tmp->host_scribble = (unsigned char *) cmd;
+ }
+#if (NDEBUG & NDEBUG_QUEUES)
+ printk("scsi%d : command added to %s of queue\n", instance->host_no,
+ (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
+#endif
+
+/* Run the coroutine if it isn't already running. */
+ run_main();
+ return 0;
+}
+
+/*
+ * Function : NCR5380_main (void)
+ *
+ * Purpose : NCR5380_main is a coroutine that runs as long as more work can
+ * be done on the NCR5380 host adapters in a system. Both
+ * NCR5380_queue_command() and NCR5380_intr() will try to start it
+ * in case it is not running.
+ *
+ * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should
+ * reenable them. This prevents reentrancy and kernel stack overflow.
+ */
+
+static void NCR5380_main (void) {
+ Scsi_Cmnd *tmp, *prev;
+ struct Scsi_Host *instance;
+ struct NCR5380_hostdata *hostdata;
+ int done;
+
+ /*
+ * We run (with interrupts disabled) until we're sure that none of
+ * the host adapters have anything that can be done, at which point
+ * we set main_running to 0 and exit.
+ *
+ * Interrupts are enabled before doing various other internal
+ * instructions, after we've decided that we need to run through
+ * the loop again.
+ *
+ * this should prevent any race conditions.
+ */
+
+ do {
+ cli(); /* Freeze request queues */
+ done = 1;
+ for (instance = first_instance; instance &&
+ instance->hostt == the_template; instance=instance->next) {
+ hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+ cli();
+ if (!hostdata->connected) {
+#if (NDEBUG & NDEBUG_MAIN)
+ printk("scsi%d : not connected\n", instance->host_no);
+#endif
+ /*
+ * Search through the issue_queue for a command destined
+ * for a target that's not busy.
+ */
+#if (NDEBUG & NDEBUG_LISTS)
+ for (tmp= (Scsi_Cmnd *) hostdata->issue_queue, prev=NULL; tmp && (tmp != prev); prev=tmp, tmp=(Scsi_Cmnd*)tmp->host_scribble)
+ ;
+ /*printk("%p ", tmp);*/
+ if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/
+#endif
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
+ prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *)
+ tmp->host_scribble) {
+
+#if (NDEBUG & NDEBUG_LISTS)
+ if (prev != tmp)
+ printk("MAIN tmp=%p target=%d busy=%d lun=%d\n", tmp, tmp->target, hostdata->busy[tmp->target], tmp->lun);
+#endif
+ /* When we find one, remove it from the issue queue. */
+ if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
+ if (prev) {
+ REMOVE(prev,prev->host_scribble,tmp,tmp->host_scribble);
+ prev->host_scribble = tmp->host_scribble;
+ } else {
+ REMOVE(-1,hostdata->issue_queue,tmp,tmp->host_scribble);
+ hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
+ }
+ tmp->host_scribble = NULL;
+
+ /* reenable interrupts after finding one */
+ sti();
+
+ /*
+ * Attempt to establish an I_T_L nexus here.
+ * On success, instance->hostdata->connected is set.
+ * On failure, we must add the command back to the
+ * issue queue so we can keep trying.
+ */
+#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
+ printk("scsi%d : main() : command for target %d lun %d removed from issue_queue\n",
+ instance->host_no, tmp->target, tmp->lun);
+#endif
+
+ /*
+ * A successful selection is defined as one that
+ * leaves us with the command connected and
+ * in hostdata->connected, OR has terminated the
+ * command.
+ *
+ * With successful commands, we fall through
+ * and see if we can do an information transfer,
+ * with failures we will restart.
+ */
+
+ if (!NCR5380_select(instance, tmp,
+ /*
+ * REQUEST SENSE commands are issued without tagged
+ * queueing, even on SCSI-II devices because the
+ * contingent allegiance condition exists for the
+ * entire unit.
+ */
+ (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE :
+ TAG_NEXT)) {
+ break;
+ } else {
+ cli();
+ LIST(tmp, hostdata->issue_queue);
+ tmp->host_scribble = (unsigned char *)
+ hostdata->issue_queue;
+ hostdata->issue_queue = tmp;
+ done = 0;
+ sti();
+#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
+ printk("scsi%d : main(): select() failed, returned to issue_queue\n",
+ instance->host_no);
+#endif
+ }
+ } /* if target/lun is not busy */
+ } /* for */
+ } /* if (!hostdata->connected) */
+
+ if (hostdata->connected
+#ifdef REAL_DMA
+ && !hostdata->dmalen
+#endif
+#ifdef USLEEP
+ && (!hostdata->time_expires || hostdata->time_expires >= jiffies)
+#endif
+ ) {
+ sti();
+#if (NDEBUG & NDEBUG_MAIN)
+ printk("scsi%d : main() : performing information transfer\n",
+ instance->host_no);
+#endif
+ NCR5380_information_transfer(instance);
+#if (NDEBUG & NDEBUG_MAIN)
+ printk("scsi%d : main() : done set false\n", instance->host_no);
+#endif
+ done = 0;
+ } else
+ break;
+ } /* for instance */
+ } while (!done);
+ main_running = 0;
+}
+
+#ifndef DONT_USE_INTR
+/*
+ * Function : void NCR5380_intr (int irq)
+ *
+ * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
+ * from the disconnected queue, and restarting NCR5380_main()
+ * as required.
+ *
+ * Inputs : int irq, irq that caused this interrupt.
+ *
+ */
+
+static void NCR5380_intr (int irq, void *dev_id, struct pt_regs * regs) {
+ NCR5380_local_declare();
+ struct Scsi_Host *instance;
+ int done;
+ unsigned char basr;
+#if (NDEBUG & NDEBUG_INTR)
+ printk("scsi : NCR5380 irq %d triggered\n", irq);
+#endif
+ do {
+ done = 1;
+ for (instance = first_instance; instance && (instance->hostt ==
+ the_template); instance = instance->next)
+ if (instance->irq == irq) {
+
+ /* Look for pending interrupts */
+ NCR5380_setup(instance);
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+ /* XXX dispatch to appropriate routine if found and done=0 */
+ if (basr & BASR_IRQ) {
+#if (NDEBUG & NDEBUG_INTR)
+ NCR5380_print(instance);
+#endif
+ if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
+ (SR_SEL | SR_IO)) {
+ done = 0;
+ sti();
+#if (NDEBUG & NDEBUG_INTR)
+ printk("scsi%d : SEL interrupt\n", instance->host_no);
+#endif
+ NCR5380_reselect(instance);
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ } else if (basr & BASR_PARITY_ERROR) {
+#if (NDEBUG & NDEBUG_INTR)
+ printk("scsi%d : PARITY interrupt\n", instance->host_no);
+#endif
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ } else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
+#if (NDEBUG & NDEBUG_INTR)
+ printk("scsi%d : RESET interrupt\n", instance->host_no);
+#endif
+ (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ } else {
+/*
+ * XXX the rest of the interrupt conditions should *only* occur during a
+ * DMA transfer, which I haven't gotten around to fixing yet.
+ */
+
+#if defined(REAL_DMA)
+ /*
+ * We should only get PHASE MISMATCH and EOP interrupts
+ * if we have DMA enabled, so do a sanity check based on
+ * the current setting of the MODE register.
+ */
+
+ if ((NCR5380_read(MODE_REG) & MR_DMA) && ((basr &
+ BASR_END_DMA_TRANSFER) ||
+ !(basr & BASR_PHASE_MATCH))) {
+ int transfered;
+
+ if (!hostdata->connected)
+ panic("scsi%d : received end of DMA interrupt with no connected cmd\n",
+ instance->hostno);
+
+ transfered = (hostdata->dmalen - NCR5380_dma_residual(instance));
+ hostdata->connected->SCp.this_residual -= transferred;
+ hostdata->connected->SCp.ptr += transferred;
+ hostdata->dmalen = 0;
+
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#if NCR_TIMEOUT
+ {
+ unsigned long timeout = jiffies + NCR_TIMEOUT;
+
+ while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK
+ && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ printk("scsi%d: timeout at NCR5380.c:%d\n",
+ host->host_no, __LINE__);
+ }
+#else /* NCR_TIMEOUT */
+ while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
+#endif
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ }
+#else
+#if (NDEBUG & NDEBUG_INTR)
+ printk("scsi : unknown interrupt, BASR 0x%X, MR 0x%X, SR 0x%x\n", basr, NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
+#endif
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#endif
+ }
+ } /* if BASR_IRQ */
+ if (!done)
+ run_main();
+ } /* if (instance->irq == irq) */
+ } while (!done);
+}
+#endif
+
+#ifdef NCR5380_STATS
+static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd* cmd)
+{
+# ifdef NCR5380_STAT_LIMIT
+ if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
+# endif
+ switch (cmd->cmnd[0])
+ {
+ case WRITE:
+ case WRITE_6:
+ case WRITE_10:
+ hostdata->time_write[cmd->target] += (jiffies - hostdata->timebase);
+ /*hostdata->bytes_write[cmd->target] += cmd->request_bufflen;*/
+ hostdata->pendingw--;
+ break;
+ case READ:
+ case READ_6:
+ case READ_10:
+ hostdata->time_read[cmd->target] += (jiffies - hostdata->timebase);
+ /*hostdata->bytes_read[cmd->target] += cmd->request_bufflen;*/
+ hostdata->pendingr--;
+ break;
+ }
+}
+#endif
+
+/*
+ * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+ * int tag);
+ *
+ * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
+ * including ARBITRATION, SELECTION, and initial message out for
+ * IDENTIFY and queue messages.
+ *
+ * Inputs : instance - instantiation of the 5380 driver on which this
+ * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
+ * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
+ * the command that is presently connected.
+ *
+ * Returns : -1 if selection could not execute for some reason,
+ * 0 if selection succeeded or failed because the target
+ * did not respond.
+ *
+ * Side effects :
+ * If bus busy, arbitration failed, etc, NCR5380_select() will exit
+ * with registers as they should have been on entry - ie
+ * SELECT_ENABLE will be set appropriately, the NCR5380
+ * will cease to drive any SCSI bus signals.
+ *
+ * If successful : I_T_L or I_T_L_Q nexus will be established,
+ * instance->connected will be set to cmd.
+ * SELECT interrupt will be disabled.
+ *
+ * If failed (no target) : cmd->scsi_done() will be called, and the
+ * cmd->result host byte set to DID_BAD_TARGET.
+ */
+
+static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+ int tag) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata*)
+ instance->hostdata;
+ unsigned char tmp[3], phase;
+ unsigned char *data;
+ int len;
+ unsigned long timeout;
+ NCR5380_setup(instance);
+
+ hostdata->restart_select = 0;
+#if defined (NDEBUG) && (NDEBUG & NDEBUG_ARBITRATION)
+ NCR5380_print(instance);
+ printk("scsi%d : starting arbitration, id = %d\n", instance->host_no,
+ instance->this_id);
+#endif
+ cli();
+
+ /*
+ * Set the phase bits to 0, otherwise the NCR5380 won't drive the
+ * data bus during SELECTION.
+ */
+
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+
+ /*
+ * Start arbitration.
+ */
+
+ NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+ NCR5380_write(MODE_REG, MR_ARBITRATE);
+
+ sti();
+
+ /* Wait for arbitration logic to complete */
+#if NCR_TIMEOUT
+ {
+ unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
+
+ while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
+ && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ {
+ printk("scsi: arbitration timeout at %d\n", __LINE__);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ }
+#else /* NCR_TIMEOUT */
+ while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS));
+#endif
+
+#if (NDEBUG & NDEBUG_ARBITRATION)
+ printk("scsi%d : arbitration complete\n", instance->host_no);
+/* Avoid GCC 2.4.5 asm needs to many reloads error */
+ __asm__("nop");
+#endif
+
+ /*
+ * The arbitration delay is 2.2us, but this is a minimum and there is
+ * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
+ * the integral nature of udelay().
+ *
+ */
+
+ udelay(3);
+
+ /* Check for lost arbitration */
+ if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+ (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
+ (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) {
+ NCR5380_write(MODE_REG, MR_BASE);
+#if (NDEBUG & NDEBUG_ARBITRATION)
+ printk("scsi%d : lost arbitration, deasserting MR_ARBITRATE\n",
+ instance->host_no);
+#endif
+ return -1;
+ }
+
+
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL);
+
+ if (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+#if (NDEBUG & NDEBUG_ARBITRATION)
+ printk("scsi%d : lost arbitration, deasserting ICR_ASSERT_SEL\n",
+ instance->host_no);
+#endif
+ return -1;
+ }
+
+ /*
+ * Again, bus clear + bus settle time is 1.2us, however, this is
+ * a minimum so we'll udelay ceil(1.2)
+ */
+
+ udelay(2);
+
+#if (NDEBUG & NDEBUG_ARBITRATION)
+ printk("scsi%d : won arbitration\n", instance->host_no);
+#endif
+
+
+ /*
+ * Now that we have won arbitration, start Selection process, asserting
+ * the host and target ID's on the SCSI bus.
+ */
+
+ NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->target)));
+
+ /*
+ * Raise ATN while SEL is true before BSY goes false from arbitration,
+ * since this is the only way to guarantee that we'll get a MESSAGE OUT
+ * phase immediately after selection.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
+ NCR5380_write(MODE_REG, MR_BASE);
+
+ /*
+ * Reselect interrupts must be turned off prior to the dropping of BSY,
+ * otherwise we will trigger an interrupt.
+ */
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+
+ /*
+ * The initiator shall then wait at least two deskew delays and release
+ * the BSY signal.
+ */
+ udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */
+
+ /* Reset BSY */
+ NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA |
+ ICR_ASSERT_ATN | ICR_ASSERT_SEL));
+
+ /*
+ * Something weird happens when we cease to drive BSY - looks
+ * like the board/chip is letting us do another read before the
+ * appropriate propagation delay has expired, and we're confusing
+ * a BSY signal from ourselves as the target's response to SELECTION.
+ *
+ * A small delay (the 'C++' frontend breaks the pipeline with an
+ * unnecessary jump, making it work on my 386-33/Trantor T128, the
+ * tighter 'C' code breaks and requires this) solves the problem -
+ * the 1 us delay is arbitrary, and only used because this delay will
+ * be the same on other platforms and since it works here, it should
+ * work there.
+ *
+ * wingel suggests that this could be due to failing to wait
+ * one deskew delay.
+ */
+
+ udelay(1);
+
+#if (NDEBUG & NDEBUG_SELECTION)
+ printk("scsi%d : selecting target %d\n", instance->host_no, cmd->target);
+#endif
+
+ /*
+ * The SCSI specification calls for a 250 ms timeout for the actual
+ * selection.
+ */
+
+ timeout = jiffies + 250*HZ/1000;
+
+ /*
+ * XXX very interesting - we're seeing a bounce where the BSY we
+ * asserted is being reflected / still asserted (propagation delay?)
+ * and it's detecting as true. Sigh.
+ */
+
+ while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) &
+ (SR_BSY | SR_IO)));
+
+ if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
+ (SR_SEL | SR_IO)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_reselect(instance);
+ printk ("scsi%d : reselection after won arbitration?\n",
+ instance->host_no);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+
+ /*
+ * No less than two deskew delays after the initiator detects the
+ * BSY signal is true, it shall release the SEL signal and may
+ * change the DATA BUS. -wingel
+ */
+
+ udelay(1);
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+ if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ if (hostdata->targets_present & (1 << cmd->target)) {
+ printk("scsi%d : weirdness\n", instance->host_no);
+ if (hostdata->restart_select)
+ printk("\trestart select\n");
+#ifdef NDEBUG
+ NCR5380_print (instance);
+#endif
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ cmd->result = DID_BAD_TARGET << 16;
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+#if (NDEBUG & NDEBUG_SELECTION)
+ printk("scsi%d : target did not respond within 250ms\n",
+ instance->host_no);
+#endif
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return 0;
+ }
+
+ hostdata->targets_present |= (1 << cmd->target);
+
+ /*
+ * Since we followed the SCSI spec, and raised ATN while SEL
+ * was true but before BSY was false during selection, the information
+ * transfer phase should be a MESSAGE OUT phase so that we can send the
+ * IDENTIFY message.
+ *
+ * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
+ * message (2 bytes) with a tag ID that we increment with every command
+ * until it wraps back to 0.
+ *
+ * XXX - it turns out that there are some broken SCSI-II devices,
+ * which claim to support tagged queuing but fail when more than
+ * some number of commands are issued at once.
+ */
+
+ /* Wait for start of REQ/ACK handshake */
+#ifdef NCR_TIMEOUT
+ {
+ unsigned long timeout = jiffies + NCR_TIMEOUT;
+
+ while (!(NCR5380_read(STATUS_REG) & SR_REQ) && jiffies < timeout);
+
+ if (jiffies >= timeout) {
+ printk("scsi%d: timeout at NCR5380.c:%d\n", __LINE__);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ }
+#else /* NCR_TIMEOUT */
+ while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+#endif /* def NCR_TIMEOUT */
+
+#if (NDEBUG & NDEBUG_SELECTION)
+ printk("scsi%d : target %d selected, going into MESSAGE OUT phase.\n",
+ instance->host_no, cmd->target);
+#endif
+ tmp[0] = IDENTIFY(((instance->irq == IRQ_NONE) ? 0 : 1), cmd->lun);
+#ifdef SCSI2
+ if (cmd->device->tagged_queue && (tag != TAG_NONE)) {
+ tmp[1] = SIMPLE_QUEUE_TAG;
+ if (tag == TAG_NEXT) {
+ /* 0 is TAG_NONE, used to imply no tag for this command */
+ if (cmd->device->current_tag == 0)
+ cmd->device->current_tag = 1;
+
+ cmd->tag = cmd->device->current_tag;
+ cmd->device->current_tag++;
+ } else
+ cmd->tag = (unsigned char) tag;
+
+ tmp[2] = cmd->tag;
+ hostdata->last_message = SIMPLE_QUEUE_TAG;
+ len = 3;
+ } else
+#endif /* def SCSI2 */
+ {
+ len = 1;
+ cmd->tag=0;
+ }
+
+ /* Send message(s) */
+ data = tmp;
+ phase = PHASE_MSGOUT;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+#if (NDEBUG & NDEBUG_SELECTION)
+ printk("scsi%d : nexus established.\n", instance->host_no);
+#endif
+ /* XXX need to handle errors here */
+ hostdata->connected = cmd;
+#ifdef SCSI2
+ if (!cmd->device->tagged_queue)
+#endif
+ hostdata->busy[cmd->target] |= (1 << cmd->lun);
+
+ initialize_SCp(cmd);
+
+
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using polled I/O
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
+ *
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes or transfered or exit
+ * is in same phase.
+ *
+ * Also, *phase, *count, *data are modified in place.
+ *
+ * XXX Note : handling for bus free may be useful.
+ */
+
+/*
+ * Note : this code is not as quick as it could be, however it
+ * IS 100% reliable, and for the actual data transfer where speed
+ * counts, we will always do a pseudo DMA or DMA transfer.
+ */
+
+static int NCR5380_transfer_pio (struct Scsi_Host *instance,
+ unsigned char *phase, int *count, unsigned char **data) {
+ NCR5380_local_declare();
+ register unsigned char p = *phase, tmp;
+ register int c = *count;
+ register unsigned char *d = *data;
+ NCR5380_setup(instance);
+
+#if (NDEBUG & NDEBUG_PIO)
+ if (!(p & SR_IO))
+ printk("scsi%d : pio write %d bytes\n", instance->host_no, c);
+ else
+ printk("scsi%d : pio read %d bytes\n", instance->host_no, c);
+#endif
+
+ /*
+ * The NCR5380 chip will only drive the SCSI bus when the
+ * phase specified in the appropriate bits of the TARGET COMMAND
+ * REGISTER match the STATUS REGISTER
+ */
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+ do {
+ /*
+ * Wait for assertion of REQ, after which the phase bits will be
+ * valid
+ */
+ while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
+
+#if (NDEBUG & NDEBUG_HANDSHAKE)
+ printk("scsi%d : REQ detected\n", instance->host_no);
+#endif
+
+ /* Check for phase mismatch */
+ if ((tmp & PHASE_MASK) != p) {
+#if (NDEBUG & NDEBUG_PIO)
+ printk("scsi%d : phase mismatch\n", instance->host_no);
+ NCR5380_print_phase(instance);
+#endif
+ break;
+ }
+
+ /* Do actual transfer from SCSI bus to / from memory */
+ if (!(p & SR_IO))
+ NCR5380_write(OUTPUT_DATA_REG, *d);
+ else
+ *d = NCR5380_read(CURRENT_SCSI_DATA_REG);
+
+ ++d;
+
+ /*
+ * The SCSI standard suggests that in MSGOUT phase, the initiator
+ * should drop ATN on the last byte of the message phase
+ * after REQ has been asserted for the handshake but before
+ * the initiator raises ACK.
+ */
+
+ if (!(p & SR_IO)) {
+ if (!((p & SR_MSG) && c > 1)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA);
+#if (NDEBUG & NDEBUG_PIO)
+ NCR5380_print(instance);
+#endif
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ACK);
+ } else {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN);
+#if (NDEBUG & NDEBUG_PIO)
+ NCR5380_print(instance);
+#endif
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
+ }
+ } else {
+#if (NDEBUG & NDEBUG_PIO)
+ NCR5380_print(instance);
+#endif
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+ }
+
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+
+#if (NDEBUG & NDEBUG_HANDSHAKE)
+ printk("scsi%d : req false, handshake complete\n", instance->host_no);
+#endif
+
+/*
+ * We have several special cases to consider during REQ/ACK handshaking :
+ * 1. We were in MSGOUT phase, and we are on the last byte of the
+ * message. ATN must be dropped as ACK is dropped.
+ *
+ * 2. We are in a MSGIN phase, and we are on the last byte of the
+ * message. We must exit with ACK asserted, so that the calling
+ * code may raise ATN before dropping ACK to reject the message.
+ *
+ * 3. ACK and ATN are clear and the target may proceed as normal.
+ */
+ if (!(p == PHASE_MSGIN && c == 1)) {
+ if (p == PHASE_MSGOUT && c > 1)
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ else
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ }
+ } while (--c);
+
+#if (NDEBUG & NDEBUG_PIO)
+ printk("scsi%d : residual %d\n", instance->host_no, c);
+#endif
+
+ *count = c;
+ *data = d;
+ tmp = NCR5380_read(STATUS_REG);
+ if (tmp & SR_REQ)
+ *phase = tmp & PHASE_MASK;
+ else
+ *phase = PHASE_UNKNOWN;
+
+ if (!c || (*phase == p))
+ return 0;
+ else
+ return -1;
+}
+
+static void do_reset (struct Scsi_Host *host) {
+ NCR5380_local_declare();
+ NCR5380_setup(host);
+
+ cli();
+ NCR5380_write(TARGET_COMMAND_REG,
+ PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG) & PHASE_MASK));
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
+ udelay(25);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ sti();
+}
+
+/*
+ * Function : do_abort (Scsi_Host *host)
+ *
+ * Purpose : abort the currently established nexus. Should only be
+ * called from a routine which can drop into a
+ *
+ * Returns : 0 on success, -1 on failure.
+ */
+
+static int do_abort (struct Scsi_Host *host) {
+ NCR5380_local_declare();
+ unsigned char tmp, *msgptr, phase;
+ int len;
+ NCR5380_setup(host);
+
+
+ /* Request message out phase */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+ /*
+ * Wait for the target to indicate a valid phase by asserting
+ * REQ. Once this happens, we'll have either a MSGOUT phase
+ * and can immediately send the ABORT message, or we'll have some
+ * other phase and will have to source/sink data.
+ *
+ * We really don't care what value was on the bus or what value
+ * the target sees, so we just handshake.
+ */
+
+ while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ);
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+ if ((tmp & PHASE_MASK) != PHASE_MSGOUT) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
+ ICR_ASSERT_ACK);
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ }
+
+ tmp = ABORT;
+ msgptr = &tmp;
+ len = 1;
+ phase = PHASE_MSGOUT;
+ NCR5380_transfer_pio (host, &phase, &len, &msgptr);
+
+ /*
+ * If we got here, and the command completed successfully,
+ * we're about to go into bus free state.
+ */
+
+ return len ? -1 : 0;
+}
+
+#if defined(REAL_DMA) || defined(PSEUDO_DMA) || defined (REAL_DMA_POLL)
+/*
+ * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using either real
+ * or pseudo DMA.
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
+ *
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes or transfered or exit
+ * is in same phase.
+ *
+ * Also, *phase, *count, *data are modified in place.
+ *
+ */
+
+
+static int NCR5380_transfer_dma (struct Scsi_Host *instance,
+ unsigned char *phase, int *count, unsigned char **data) {
+ NCR5380_local_declare();
+ register int c = *count;
+ register unsigned char p = *phase;
+ register unsigned char *d = *data;
+ unsigned char tmp;
+ int foo;
+#if defined(REAL_DMA_POLL)
+ int cnt, toPIO;
+ unsigned char saved_data = 0, overrun = 0, residue;
+#endif
+
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+
+ NCR5380_setup(instance);
+
+ if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) {
+ *phase = tmp;
+ return -1;
+ }
+#if defined(REAL_DMA) || defined(REAL_DMA_POLL)
+#ifdef READ_OVERRUNS
+ if (p & SR_IO) {
+ c -= 2;
+ }
+#endif
+#if (NDEBUG & NDEBUG_DMA)
+ printk("scsi%d : initializing DMA channel %d for %s, %d bytes %s %0x\n",
+ instance->host_no, instance->dma_channel, (p & SR_IO) ? "reading" :
+ "writing", c, (p & SR_IO) ? "to" : "from", (unsigned) d);
+#endif
+ hostdata->dma_len = (p & SR_IO) ?
+ NCR5380_dma_read_setup(instance, d, c) :
+ NCR5380_dma_write_setup(instance, d, c);
+#endif
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+#ifdef REAL_DMA
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
+#elif defined(REAL_DMA_POLL)
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
+#else
+ /*
+ * Note : on my sample board, watch-dog timeouts occurred when interrupts
+ * were not disabled for the duration of a single DMA transfer, from
+ * before the setting of DMA mode to after transfer of the last byte.
+ */
+
+#if defined(PSEUDO_DMA) && !defined(UNSAFE)
+ cli();
+#endif
+ /* KLL May need eop and parity in 53c400 */
+ if (hostdata->flags & FLAG_NCR53C400)
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_PAR_CHECK
+ | MR_ENABLE_PAR_INTR | MR_ENABLE_EOP_INTR | MR_DMA_MODE
+ | MR_MONITOR_BSY);
+ else
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
+#endif /* def REAL_DMA */
+
+#if (NDEBUG & NDEBUG_DMA) & 0
+ printk("scsi%d : mode reg = 0x%X\n", instance->host_no, NCR5380_read(MODE_REG));
+#endif
+
+/*
+ * FOO stuff. For some UNAPPARENT reason, I'm getting
+ * watchdog timers fired on bootup for NO APPARENT REASON, meaning it's
+ * probably a timing problem.
+ *
+ * Since this is the only place I have back-to-back writes, perhaps this
+ * is the problem?
+ */
+
+ if (p & SR_IO) {
+#ifndef FOO
+ udelay(1);
+#endif
+ NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
+ } else {
+#ifndef FOO
+ udelay(1);
+#endif
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
+#ifndef FOO
+ udelay(1);
+#endif
+ NCR5380_write(START_DMA_SEND_REG, 0);
+#ifndef FOO
+ udelay(1);
+#endif
+ }
+
+#if defined(REAL_DMA_POLL)
+ do {
+ tmp = NCR5380_read(BUS_AND_STATUS_REG);
+ } while ((tmp & BASR_PHASE_MATCH) && !(tmp & (BASR_BUSY_ERROR |
+ BASR_END_DMA_TRANSFER)));
+
+/*
+ At this point, either we've completed DMA, or we have a phase mismatch,
+ or we've unexpectedly lost BUSY (which is a real error).
+
+ For write DMAs, we want to wait until the last byte has been
+ transferred out over the bus before we turn off DMA mode. Alas, there
+ seems to be no terribly good way of doing this on a 5380 under all
+ conditions. For non-scatter-gather operations, we can wait until REQ
+ and ACK both go false, or until a phase mismatch occurs. Gather-writes
+ are nastier, since the device will be expecting more data than we
+ are prepared to send it, and REQ will remain asserted. On a 53C8[01] we
+ could test LAST BIT SENT to assure transfer (I imagine this is precisely
+ why this signal was added to the newer chips) but on the older 538[01]
+ this signal does not exist. The workaround for this lack is a watchdog;
+ we bail out of the wait-loop after a modest amount of wait-time if
+ the usual exit conditions are not met. Not a terribly clean or
+ correct solution :-%
+
+ Reads are equally tricky due to a nasty characteristic of the NCR5380.
+ If the chip is in DMA mode for an READ, it will respond to a target's
+ REQ by latching the SCSI data into the INPUT DATA register and asserting
+ ACK, even if it has _already_ been notified by the DMA controller that
+ the current DMA transfer has completed! If the NCR5380 is then taken
+ out of DMA mode, this already-acknowledged byte is lost.
+
+ This is not a problem for "one DMA transfer per command" reads, because
+ the situation will never arise... either all of the data is DMA'ed
+ properly, or the target switches to MESSAGE IN phase to signal a
+ disconnection (either operation bringing the DMA to a clean halt).
+ However, in order to handle scatter-reads, we must work around the
+ problem. The chosen fix is to DMA N-2 bytes, then check for the
+ condition before taking the NCR5380 out of DMA mode. One or two extra
+ bytes are transferred via PIO as necessary to fill out the original
+ request.
+*/
+
+ if (p & SR_IO) {
+#ifdef READ_OVERRUNS
+ udelay(10);
+ if (((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH|BASR_ACK)) ==
+ (BASR_PHASE_MATCH | BASR_ACK))) {
+ saved_data = NCR5380_read(INPUT_DATA_REGISTER);
+ overrun = 1;
+ }
+#endif
+ } else {
+ int limit = 100;
+ while (((tmp = NCR5380_read(BUS_AND_STATUS_REG)) & BASR_ACK) ||
+ (NCR5380_read(STATUS_REG) & SR_REQ)) {
+ if (!(tmp & BASR_PHASE_MATCH)) break;
+ if (--limit < 0) break;
+ }
+ }
+
+
+#if (NDEBUG & NDEBUG_DMA)
+ printk("scsi%d : polled DMA transfer complete, basr 0x%X, sr 0x%X\n",
+ instance->host_no, tmp, NCR5380_read(STATUS_REG));
+#endif
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ residue = NCR5380_dma_residual(instance);
+ c -= residue;
+ *count -= c;
+ *data += c;
+ *phase = NCR5380_read(STATUS_REG) & PHASE_MASK;
+
+#ifdef READ_OVERRUNS
+ if (*phase == p && (p & SR_IO) && residue == 0) {
+ if (overrun) {
+#if (NDEBUG & NDEBUG_DMA)
+ printk("Got an input overrun, using saved byte\n");
+#endif
+ **data = saved_data;
+ *data += 1;
+ *count -= 1;
+ cnt = toPIO = 1;
+ } else {
+ printk("No overrun??\n");
+ cnt = toPIO = 2;
+ }
+#if (NDEBUG & NDEBUG_DMA)
+ printk("Doing %d-byte PIO to 0x%X\n", cnt, *data);
+#endif
+ NCR5380_transfer_pio(instance, phase, &cnt, data);
+ *count -= toPIO - cnt;
+ }
+#endif
+
+#if (NDEBUG & NDEBUG_DMA)
+ printk("Return with data ptr = 0x%X, count %d, last 0x%X, next 0x%X\n",
+ *data, *count, *(*data+*count-1), *(*data+*count));
+#endif
+ return 0;
+
+#elif defined(REAL_DMA)
+ return 0;
+#else /* defined(REAL_DMA_POLL) */
+ if (p & SR_IO) {
+#ifdef DMA_WORKS_RIGHT
+ foo = NCR5380_pread(instance, d, c);
+#else
+ int diff = 1;
+ if (hostdata->flags & FLAG_NCR53C400) {
+ diff=0;
+ }
+
+ if (!(foo = NCR5380_pread(instance, d, c - diff))) {
+ /*
+ * We can't disable DMA mode after successfully transferring
+ * what we plan to be the last byte, since that would open up
+ * a race condition where if the target asserted REQ before
+ * we got the DMA mode reset, the NCR5380 would have latched
+ * an additional byte into the INPUT DATA register and we'd
+ * have dropped it.
+ *
+ * The workaround was to transfer one fewer bytes than we
+ * intended to with the pseudo-DMA read function, wait for
+ * the chip to latch the last byte, read it, and then disable
+ * pseudo-DMA mode.
+ *
+ * After REQ is asserted, the NCR5380 asserts DRQ and ACK.
+ * REQ is deasserted when ACK is asserted, and not reasserted
+ * until ACK goes false. Since the NCR5380 won't lower ACK
+ * until DACK is asserted, which won't happen unless we twiddle
+ * the DMA port or we take the NCR5380 out of DMA mode, we
+ * can guarantee that we won't handshake another extra
+ * byte.
+ */
+
+ if (!(hostdata->flags & FLAG_NCR53C400)) {
+ while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
+ /* Wait for clean handshake */
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ d[c - 1] = NCR5380_read(INPUT_DATA_REG);
+ }
+ }
+#endif
+ } else {
+#ifdef DMA_WORKS_RIGHT
+ foo = NCR5380_pwrite(instance, d, c);
+#else
+ int timeout;
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("About to pwrite %d bytes\n", c);
+#endif
+ if (!(foo = NCR5380_pwrite(instance, d, c))) {
+ /*
+ * Wait for the last byte to be sent. If REQ is being asserted for
+ * the byte we're interested, we'll ACK it and it will go false.
+ */
+ if (!(hostdata->flags & FLAG_HAS_LAST_BYTE_SENT)) {
+ timeout = 20000;
+#if 1
+#if 1
+ while (!(NCR5380_read(BUS_AND_STATUS_REG) &
+ BASR_DRQ) && (NCR5380_read(BUS_AND_STATUS_REG) &
+ BASR_PHASE_MATCH));
+#else
+ if (NCR5380_read(STATUS_REG) & SR_REQ) {
+ for (; timeout &&
+ !(NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
+ --timeout);
+ for (; timeout && (NCR5380_read(STATUS_REG) & SR_REQ);
+ --timeout);
+ }
+#endif
+
+
+#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
+ if (!timeout)
+ printk("scsi%d : timed out on last byte\n",
+ instance->host_no);
+#endif
+
+
+ if (hostdata->flags & FLAG_CHECK_LAST_BYTE_SENT) {
+ hostdata->flags &= ~FLAG_CHECK_LAST_BYTE_SENT;
+ if (NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT) {
+ hostdata->flags |= FLAG_HAS_LAST_BYTE_SENT;
+#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
+ printk("scsi%d : last bit sent works\n",
+ instance->host_no);
+#endif
+ }
+ }
+ } else {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("Waiting for LASTBYTE\n");
+#endif
+ while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT));
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("Got LASTBYTE\n");
+#endif
+ }
+#else
+ udelay (5);
+#endif
+ }
+#endif
+ }
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ if ((!(p & SR_IO)) && (hostdata->flags & FLAG_NCR53C400)) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: Checking for IRQ\n");
+#endif
+ if (NCR5380_read(BUS_AND_STATUS_REG) & BASR_IRQ) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: got it, reading reset interrupt reg\n");
+#endif
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ } else {
+ printk("53C400w: IRQ NOT THERE!\n");
+ }
+ }
+
+ *data = d + c;
+ *count = 0;
+ *phase = NCR5380_read(STATUS_REG) & PHASE_MASK;
+#if 0
+ NCR5380_print_phase(instance);
+#endif
+#if defined(PSEUDO_DMA) && !defined(UNSAFE)
+ sti();
+#endif /* defined(REAL_DMA_POLL) */
+ return foo;
+#endif /* def REAL_DMA */
+}
+#endif /* defined(REAL_DMA) | defined(PSEUDO_DMA) */
+
+/*
+ * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
+ *
+ * Purpose : run through the various SCSI phases and do as the target
+ * directs us to. Operates on the currently connected command,
+ * instance->connected.
+ *
+ * Inputs : instance, instance for which we are doing commands
+ *
+ * Side effects : SCSI things happen, the disconnected queue will be
+ * modified if a command disconnects, *instance->connected will
+ * change.
+ *
+ * XXX Note : we need to watch for bus free or a reset condition here
+ * to recover from an unexpected bus free condition.
+ */
+
+static void NCR5380_information_transfer (struct Scsi_Host *instance) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+ unsigned char msgout = NOP;
+ int sink = 0;
+ int len;
+#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
+ int transfersize;
+#endif
+ unsigned char *data;
+ unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
+ Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
+ NCR5380_setup(instance);
+
+ while (1) {
+ tmp = NCR5380_read(STATUS_REG);
+ /* We only have a valid SCSI phase when REQ is asserted */
+ if (tmp & SR_REQ) {
+ phase = (tmp & PHASE_MASK);
+ if (phase != old_phase) {
+ old_phase = phase;
+#if (NDEBUG & NDEBUG_INFORMATION)
+ NCR5380_print_phase(instance);
+#endif
+ }
+
+ if (sink && (phase != PHASE_MSGOUT)) {
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
+ ICR_ASSERT_ACK);
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ sink = 0;
+ continue;
+ }
+
+ switch (phase) {
+ case PHASE_DATAIN:
+ case PHASE_DATAOUT:
+#if (NDEBUG & NDEBUG_NO_DATAOUT)
+ printk("scsi%d : NDEBUG_NO_DATAOUT set, attempted DATAOUT aborted\n",
+ instance->host_no);
+ sink = 1;
+ do_abort(instance);
+ cmd->result = DID_ERROR << 16;
+ cmd->done(cmd);
+ return;
+#endif
+ /*
+ * If there is no room left in the current buffer in the
+ * scatter-gather list, move onto the next one.
+ */
+
+ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+ ++cmd->SCp.buffer;
+ --cmd->SCp.buffers_residual;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = cmd->SCp.buffer->address;
+#if (NDEBUG & NDEBUG_INFORMATION)
+ printk("scsi%d : %d bytes and %d buffers left\n",
+ instance->host_no, cmd->SCp.this_residual,
+ cmd->SCp.buffers_residual);
+#endif
+ }
+
+ /*
+ * The preferred transfer method is going to be
+ * PSEUDO-DMA for systems that are strictly PIO,
+ * since we can let the hardware do the handshaking.
+ *
+ * For this to work, we need to know the transfersize
+ * ahead of time, since the pseudo-DMA code will sit
+ * in an unconditional loop.
+ */
+
+#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
+ /* KLL
+ * PSEUDO_DMA is defined here. If this is the g_NCR5380
+ * driver then it will always be defined, so the
+ * FLAG_NO_PSEUDO_DMA is used to inhibit PDMA in the base
+ * NCR5380 case. I think this is a fairly clean solution.
+ * We supplement these 2 if's with the flag.
+ */
+#ifdef NCR5380_dma_xfer_len
+ if (!cmd->device->borken &&
+ !(hostdata->flags & FLAG_NO_PSEUDO_DMA) &&
+ (transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) {
+#else
+ transfersize = cmd->transfersize;
+
+#ifdef LIMIT_TRANSFERSIZE /* If we have problems with interrupt service */
+ if( transfersize > 512 )
+ transfersize = 512;
+#endif /* LIMIT_TRANSFERSIZE */
+
+ if (!cmd->device->borken && transfersize &&
+ !(hostdata->flags & FLAG_NO_PSEUDO_DMA) &&
+ cmd->SCp.this_residual && !(cmd->SCp.this_residual %
+ transfersize)) {
+ /* Limit transfers to 32K, for xx400 & xx406
+ * pseudoDMA that transfers in 128 bytes blocks. */
+ if (transfersize > 32*1024)
+ transfersize = 32*1024;
+#endif
+ len = transfersize;
+ if (NCR5380_transfer_dma(instance, &phase,
+ &len, (unsigned char **) &cmd->SCp.ptr)) {
+ /*
+ * If the watchdog timer fires, all future accesses to this
+ * device will use the polled-IO.
+ */
+ printk("scsi%d : switching target %d lun %d to slow handshake\n",
+ instance->host_no, cmd->target, cmd->lun);
+ cmd->device->borken = 1;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ sink = 1;
+ do_abort(instance);
+ cmd->result = DID_ERROR << 16;
+ cmd->done(cmd);
+ /* XXX - need to source or sink data here, as appropriate */
+ } else
+ cmd->SCp.this_residual -= transfersize - len;
+ } else
+#endif /* defined(PSEUDO_DMA) || defined(REAL_DMA_POLL) */
+ NCR5380_transfer_pio(instance, &phase,
+ (int *) &cmd->SCp.this_residual, (unsigned char **)
+ &cmd->SCp.ptr);
+ break;
+ case PHASE_MSGIN:
+ len = 1;
+ data = &tmp;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ cmd->SCp.Message = tmp;
+
+ switch (tmp) {
+ /*
+ * Linking lets us reduce the time required to get the
+ * next command out to the device, hopefully this will
+ * mean we don't waste another revolution due to the delays
+ * required by ARBITRATION and another SELECTION.
+ *
+ * In the current implementation proposal, low level drivers
+ * merely have to start the next command, pointed to by
+ * next_link, done() is called as with unlinked commands.
+ */
+#ifdef LINKED
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+#if (NDEBUG & NDEBUG_LINKED)
+ printk("scsi%d : target %d lun %d linked command complete.\n",
+ instance->host_no, cmd->target, cmd->lun);
+#endif
+ /*
+ * Sanity check : A linked command should only terminate with
+ * one of these messages if there are more linked commands
+ * available.
+ */
+
+ if (!cmd->next_link) {
+ printk("scsi%d : target %d lun %d linked command complete, no next_link\n"
+ instance->host_no, cmd->target, cmd->lun);
+ sink = 1;
+ do_abort (instance);
+ return;
+ }
+
+ initialize_SCp(cmd->next_link);
+ /* The next command is still part of this process */
+ cmd->next_link->tag = cmd->tag;
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+#if (NDEBUG & NDEBUG_LINKED)
+ printk("scsi%d : target %d lun %d linked request done, calling scsi_done().\n",
+ instance->host_no, cmd->target, cmd->lun);
+#endif
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ cmd = hostdata->connected;
+ break;
+#endif /* def LINKED */
+ case ABORT:
+ case COMMAND_COMPLETE:
+ /* Accept message by clearing ACK */
+ sink = 1;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ hostdata->connected = NULL;
+#if (NDEBUG & NDEBUG_QUEUES)
+ printk("scsi%d : command for target %d, lun %d completed\n",
+ instance->host_no, cmd->target, cmd->lun);
+#endif
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+
+ /*
+ * I'm not sure what the correct thing to do here is :
+ *
+ * If the command that just executed is NOT a request
+ * sense, the obvious thing to do is to set the result
+ * code to the values of the stored parameters.
+ *
+ * If it was a REQUEST SENSE command, we need some way
+ * to differentiate between the failure code of the original
+ * and the failure code of the REQUEST sense - the obvious
+ * case is success, where we fall through and leave the result
+ * code unchanged.
+ *
+ * The non-obvious place is where the REQUEST SENSE failed
+ */
+
+ if (cmd->cmnd[0] != REQUEST_SENSE)
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ else if (cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+
+#ifdef AUTOSENSE
+ if ((cmd->cmnd[0] != REQUEST_SENSE) &&
+ (cmd->SCp.Status == CHECK_CONDITION)) {
+#if (NDEBUG & NDEBUG_AUTOSENSE)
+ printk("scsi%d : performing request sense\n",
+ instance->host_no);
+#endif
+ cmd->cmnd[0] = REQUEST_SENSE;
+ cmd->cmnd[1] &= 0xe0;
+ cmd->cmnd[2] = 0;
+ cmd->cmnd[3] = 0;
+ cmd->cmnd[4] = sizeof(cmd->sense_buffer);
+ cmd->cmnd[5] = 0;
+
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *) cmd->sense_buffer;
+ cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
+
+ cli();
+ LIST(cmd,hostdata->issue_queue);
+ cmd->host_scribble = (unsigned char *)
+ hostdata->issue_queue;
+ hostdata->issue_queue = (Scsi_Cmnd *) cmd;
+ sti();
+#if (NDEBUG & NDEBUG_QUEUES)
+ printk("scsi%d : REQUEST SENSE added to head of issue queue\n",instance->host_no);
+#endif
+ } else {
+#endif /* def AUTOSENSE */
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ }
+
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
+ return;
+ case MESSAGE_REJECT:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ switch (hostdata->last_message) {
+ case HEAD_OF_QUEUE_TAG:
+ case ORDERED_QUEUE_TAG:
+ case SIMPLE_QUEUE_TAG:
+ cmd->device->tagged_queue = 0;
+ hostdata->busy[cmd->target] |= (1 << cmd->lun);
+ break;
+ default:
+ break;
+ }
+ case DISCONNECT:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ cmd->device->disconnect = 1;
+ cli();
+ LIST(cmd,hostdata->disconnected_queue);
+ cmd->host_scribble = (unsigned char *)
+ hostdata->disconnected_queue;
+ hostdata->connected = NULL;
+ hostdata->disconnected_queue = cmd;
+ sti();
+#if (NDEBUG & NDEBUG_QUEUES)
+ printk("scsi%d : command for target %d lun %d was moved from connected to"
+ " the disconnected_queue\n", instance->host_no,
+ cmd->target, cmd->lun);
+#endif
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /* Wait for bus free to avoid nasty timeouts */
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
+#if 0
+ NCR5380_print_status(instance);
+#endif
+ return;
+ /*
+ * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
+ * operation, in violation of the SCSI spec so we can safely
+ * ignore SAVE/RESTORE pointers calls.
+ *
+ * Unfortunately, some disks violate the SCSI spec and
+ * don't issue the required SAVE_POINTERS message before
+ * disconnecting, and we have to break spec to remain
+ * compatible.
+ */
+ case SAVE_POINTERS:
+ case RESTORE_POINTERS:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ break;
+ case EXTENDED_MESSAGE:
+/*
+ * Extended messages are sent in the following format :
+ * Byte
+ * 0 EXTENDED_MESSAGE == 1
+ * 1 length (includes one byte for code, doesn't
+ * include first two bytes)
+ * 2 code
+ * 3..length+1 arguments
+ *
+ * Start the extended message buffer with the EXTENDED_MESSAGE
+ * byte, since print_msg() wants the whole thing.
+ */
+ extended_msg[0] = EXTENDED_MESSAGE;
+ /* Accept first byte by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+#if (NDEBUG & NDEBUG_EXTENDED)
+ printk("scsi%d : receiving extended message\n",
+ instance->host_no);
+#endif
+
+ len = 2;
+ data = extended_msg + 1;
+ phase = PHASE_MSGIN;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+
+#if (NDEBUG & NDEBUG_EXTENDED)
+ printk("scsi%d : length=%d, code=0x%02x\n",
+ instance->host_no, (int) extended_msg[1],
+ (int) extended_msg[2]);
+#endif
+
+ if (!len && extended_msg[1] <=
+ (sizeof (extended_msg) - 1)) {
+ /* Accept third byte by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ len = extended_msg[1] - 1;
+ data = extended_msg + 3;
+ phase = PHASE_MSGIN;
+
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+
+#if (NDEBUG & NDEBUG_EXTENDED)
+ printk("scsi%d : message received, residual %d\n",
+ instance->host_no, len);
+#endif
+
+ switch (extended_msg[2]) {
+ case EXTENDED_SDTR:
+ case EXTENDED_WDTR:
+ case EXTENDED_MODIFY_DATA_POINTER:
+ case EXTENDED_EXTENDED_IDENTIFY:
+ tmp = 0;
+ }
+ } else if (len) {
+ printk("scsi%d: error receiving extended message\n",
+ instance->host_no);
+ tmp = 0;
+ } else {
+ printk("scsi%d: extended message code %02x length %d is too long\n",
+ instance->host_no, extended_msg[2], extended_msg[1]);
+ tmp = 0;
+ }
+ /* Fall through to reject message */
+
+ /*
+ * If we get something weird that we aren't expecting,
+ * reject it.
+ */
+ default:
+ if (!tmp) {
+ printk("scsi%d: rejecting message ", instance->host_no);
+ print_msg (extended_msg);
+ printk("\n");
+ } else if (tmp != EXTENDED_MESSAGE)
+ printk("scsi%d: rejecting unknown message %02x from target %d, lun %d\n",
+ instance->host_no, tmp, cmd->target, cmd->lun);
+ else
+ printk("scsi%d: rejecting unknown extended message code %02x, length %d from target %d, lun %d\n",
+ instance->host_no, extended_msg[1], extended_msg[0], cmd->target, cmd->lun);
+
+ msgout = MESSAGE_REJECT;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ break;
+ } /* switch (tmp) */
+ break;
+ case PHASE_MSGOUT:
+ len = 1;
+ data = &msgout;
+ hostdata->last_message = msgout;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ if (msgout == ABORT) {
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+ hostdata->connected = NULL;
+ cmd->result = DID_ERROR << 16;
+#ifdef NCR5380_STATS
+ collect_stats(hostdata, cmd);
+#endif
+ cmd->scsi_done(cmd);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return;
+ }
+ msgout = NOP;
+ break;
+ case PHASE_CMDOUT:
+ len = cmd->cmd_len;
+ data = cmd->cmnd;
+ /*
+ * XXX for performance reasons, on machines with a
+ * PSEUDO-DMA architecture we should probably
+ * use the dma transfer function.
+ */
+ NCR5380_transfer_pio(instance, &phase, &len,
+ &data);
+#ifdef USLEEP
+ if (!disconnect && should_disconnect(cmd->cmnd[0])) {
+ hostdata->time_expires = jiffies + USLEEP_SLEEP;
+#if (NDEBUG & NDEBUG_USLEEP)
+ printk("scsi%d : issued command, sleeping until %ul\n", instance->host_no,
+ hostdata->time_expires);
+#endif
+ NCR5380_set_timer (instance);
+ return;
+ }
+#endif /* def USLEEP */
+ break;
+ case PHASE_STATIN:
+ len = 1;
+ data = &tmp;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ cmd->SCp.Status = tmp;
+ break;
+ default:
+ printk("scsi%d : unknown phase\n", instance->host_no);
+#ifdef NDEBUG
+ NCR5380_print(instance);
+#endif
+ } /* switch(phase) */
+ } /* if (tmp * SR_REQ) */
+#ifdef USLEEP
+ else {
+ if (!disconnect && hostdata->time_expires && jiffies >
+ hostdata->time_expires) {
+ hostdata->time_expires = jiffies + USLEEP_SLEEP;
+#if (NDEBUG & NDEBUG_USLEEP)
+ printk("scsi%d : poll timed out, sleeping until %ul\n", instance->host_no,
+ hostdata->time_expires);
+#endif
+ NCR5380_set_timer (instance);
+ return;
+ }
+ }
+#endif
+ } /* while (1) */
+}
+
+/*
+ * Function : void NCR5380_reselect (struct Scsi_Host *instance)
+ *
+ * Purpose : does reselection, initializing the instance->connected
+ * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q
+ * nexus has been reestablished,
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ */
+
+
+static void NCR5380_reselect (struct Scsi_Host *instance) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+ unsigned char target_mask;
+ unsigned char lun, phase;
+ int len;
+#ifdef SCSI2
+ unsigned char tag;
+#endif
+ unsigned char msg[3];
+ unsigned char *data;
+ Scsi_Cmnd *tmp = NULL, *prev;
+ int abort = 0;
+ NCR5380_setup(instance);
+
+ /*
+ * Disable arbitration, etc. since the host adapter obviously
+ * lost, and tell an interrupted NCR5380_select() to restart.
+ */
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ hostdata->restart_select = 1;
+
+ target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
+
+#if (NDEBUG & NDEBUG_RESELECTION)
+ printk("scsi%d : reselect\n", instance->host_no);
+#endif
+
+ /*
+ * At this point, we have detected that our SCSI ID is on the bus,
+ * SEL is true and BSY was false for at least one bus settle delay
+ * (400 ns).
+ *
+ * We must assert BSY ourselves, until the target drops the SEL
+ * signal.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
+
+ while (NCR5380_read(STATUS_REG) & SR_SEL);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ /*
+ * Wait for target to go into MSGIN.
+ */
+
+ while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+
+ len = 1;
+ data = msg;
+ phase = PHASE_MSGIN;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+
+
+ if (!msg[0] & 0x80) {
+ printk("scsi%d : expecting IDENTIFY message, got ",
+ instance->host_no);
+ print_msg(msg);
+ abort = 1;
+ } else {
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ lun = (msg[0] & 0x07);
+
+ /*
+ * We need to add code for SCSI-II to track which devices have
+ * I_T_L_Q nexuses established, and which have simple I_T_L
+ * nexuses so we can chose to do additional data transfer.
+ */
+
+#ifdef SCSI2
+#error "SCSI-II tagged queueing is not supported yet"
+#endif
+
+ /*
+ * Find the command corresponding to the I_T_L or I_T_L_Q nexus we
+ * just reestablished, and remove it from the disconnected queue.
+ */
+
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL;
+ tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ if ((target_mask == (1 << tmp->target)) && (lun == tmp->lun)
+#ifdef SCSI2
+ && (tag == tmp->tag)
+#endif
+) {
+ if (prev) {
+ REMOVE(prev,prev->host_scribble,tmp,tmp->host_scribble);
+ prev->host_scribble = tmp->host_scribble;
+ } else {
+ REMOVE(-1,hostdata->disconnected_queue,tmp,tmp->host_scribble);
+ hostdata->disconnected_queue = (Scsi_Cmnd *) tmp->host_scribble;
+ }
+ tmp->host_scribble = NULL;
+ break;
+ }
+
+ if (!tmp) {
+#ifdef SCSI2
+ printk("scsi%d : warning : target bitmask %02x lun %d tag %d not in disconnect_queue.\n",
+ instance->host_no, target_mask, lun, tag);
+#else
+ printk("scsi%d : warning : target bitmask %02x lun %d not in disconnect_queue.\n",
+ instance->host_no, target_mask, lun);
+#endif
+ /*
+ * Since we have an established nexus that we can't do anything with,
+ * we must abort it.
+ */
+ abort = 1;
+ }
+ }
+
+ if (abort) {
+ do_abort (instance);
+ } else {
+ hostdata->connected = tmp;
+#if (NDEBUG & NDEBUG_RESELECTION)
+ printk("scsi%d : nexus established, target = %d, lun = %d, tag = %d\n",
+ instance->host_no, tmp->target, tmp->lun, tmp->tag);
+#endif
+ }
+}
+
+/*
+ * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
+ *
+ * Purpose : called by interrupt handler when DMA finishes or a phase
+ * mismatch occurs (which would finish the DMA transfer).
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ * Returns : pointer to the Scsi_Cmnd structure for which the I_T_L
+ * nexus has been reestablished, on failure NULL is returned.
+ */
+
+#ifdef REAL_DMA
+static void NCR5380_dma_complete (NCR5380_instance *instance) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *
+ instance->hostdata);
+ int transferred;
+ NCR5380_setup(instance);
+
+ /*
+ * XXX this might not be right.
+ *
+ * Wait for final byte to transfer, ie wait for ACK to go false.
+ *
+ * We should use the Last Byte Sent bit, unfortunately this is
+ * not available on the 5380/5381 (only the various CMOS chips)
+ */
+
+ while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ /*
+ * The only places we should see a phase mismatch and have to send
+ * data from the same set of pointers will be the data transfer
+ * phases. So, residual, requested length are only important here.
+ */
+
+ if (!(hostdata->connected->SCp.phase & SR_CD)) {
+ transferred = instance->dmalen - NCR5380_dma_residual();
+ hostdata->connected->SCp.this_residual -= transferred;
+ hostdata->connected->SCp.ptr += transferred;
+ }
+}
+#endif /* def REAL_DMA */
+
+/*
+ * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : abort a command
+ *
+ * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
+ * host byte of the result field to, if zero DID_ABORTED is
+ * used.
+ *
+ * Returns : 0 - success, -1 on failure.
+ *
+ * XXX - there is no way to abort the command that is currently
+ * connected, you have to wait for it to complete. If this is
+ * a problem, we could implement longjmp() / setjmp(), setjmp()
+ * called where the loop started in NCR5380_main().
+ */
+
+#ifndef NCR5380_abort
+static
+#endif
+int NCR5380_abort (Scsi_Cmnd *cmd) {
+ NCR5380_local_declare();
+ struct Scsi_Host *instance = cmd->host;
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+ Scsi_Cmnd *tmp, **prev;
+
+ printk("scsi%d : aborting command\n", instance->host_no);
+ print_Scsi_Cmnd (cmd);
+
+ NCR5380_print_status (instance);
+
+ printk("scsi%d : aborting command\n", instance->host_no);
+ print_Scsi_Cmnd (cmd);
+
+ NCR5380_print_status (instance);
+
+ cli();
+ NCR5380_setup(instance);
+
+#if (NDEBUG & NDEBUG_ABORT)
+ printk("scsi%d : abort called\n", instance->host_no);
+ printk(" basr 0x%X, sr 0x%X\n",
+ NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG));
+#endif
+
+#if 0
+/*
+ * Case 1 : If the command is the currently executing command,
+ * we'll set the aborted flag and return control so that
+ * information transfer routine can exit cleanly.
+ */
+
+ if (hostdata->connected == cmd) {
+#if (NDEBUG & NDEBUG_ABORT)
+ printk("scsi%d : aborting connected command\n", instance->host_no);
+#endif
+ hostdata->aborted = 1;
+/*
+ * We should perform BSY checking, and make sure we haven't slipped
+ * into BUS FREE.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN);
+/*
+ * Since we can't change phases until we've completed the current
+ * handshake, we have to source or sink a byte of data if the current
+ * phase is not MSGOUT.
+ */
+
+/*
+ * Return control to the executing NCR drive so we can clear the
+ * aborted flag and get back into our main loop.
+ */
+
+ return 0;
+ }
+#endif
+
+/*
+ * Case 2 : If the command hasn't been issued yet, we simply remove it
+ * from the issue queue.
+ */
+#if (NDEBUG & NDEBUG_ABORT)
+ /* KLL */
+ printk("scsi%d : abort going into loop.\n", instance->host_no);
+#endif
+ for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue),
+ tmp = (Scsi_Cmnd *) hostdata->issue_queue;
+ tmp; prev = (Scsi_Cmnd **) &(tmp->host_scribble), tmp =
+ (Scsi_Cmnd *) tmp->host_scribble)
+ if (cmd == tmp) {
+ REMOVE(5,*prev,tmp,tmp->host_scribble);
+ (*prev) = (Scsi_Cmnd *) tmp->host_scribble;
+ tmp->host_scribble = NULL;
+ tmp->result = DID_ABORT << 16;
+ sti();
+#if (NDEBUG & NDEBUG_ABORT)
+ printk("scsi%d : abort removed command from issue queue.\n",
+ instance->host_no);
+#endif
+ tmp->done(tmp);
+ return SCSI_ABORT_SUCCESS;
+ }
+#if (NDEBUG & NDEBUG_ABORT)
+ /* KLL */
+ else if (prev == tmp) printk("scsi%d : LOOP\n", instance->host_no);
+#endif
+
+/*
+ * Case 3 : If any commands are connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or
+ * issue a reset.
+ *
+ * Timeouts, and therefore aborted commands, will be highly unlikely
+ * and handling them cleanly in this situation would make the common
+ * case of noresets less efficient, and would pollute our code. So,
+ * we fail.
+ */
+
+ if (hostdata->connected) {
+ sti();
+#if (NDEBUG & NDEBUG_ABORT)
+ printk("scsi%d : abort failed, command connected.\n", instance->host_no);
+#endif
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+/*
+ * Case 4: If the command is currently disconnected from the bus, and
+ * there are no connected commands, we reconnect the I_T_L or
+ * I_T_L_Q nexus associated with it, go into message out, and send
+ * an abort message.
+ *
+ * This case is especially ugly. In order to reestablish the nexus, we
+ * need to call NCR5380_select(). The easiest way to implement this
+ * function was to abort if the bus was busy, and let the interrupt
+ * handler triggered on the SEL for reselect take care of lost arbitrations
+ * where necessary, meaning interrupts need to be enabled.
+ *
+ * When interrupts are enabled, the queues may change - so we
+ * can't remove it from the disconnected queue before selecting it
+ * because that could cause a failure in hashing the nexus if that
+ * device reselected.
+ *
+ * Since the queues may change, we can't use the pointers from when we
+ * first locate it.
+ *
+ * So, we must first locate the command, and if NCR5380_select()
+ * succeeds, then issue the abort, relocate the command and remove
+ * it from the disconnected queue.
+ */
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp;
+ tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ if (cmd == tmp) {
+ sti();
+#if (NDEBUG & NDEBUG_ABORT)
+ printk("scsi%d : aborting disconnected command.\n", instance->host_no);
+#endif
+
+ if (NCR5380_select (instance, cmd, (int) cmd->tag))
+ return SCSI_ABORT_BUSY;
+
+#if (NDEBUG & NDEBUG_ABORT)
+ printk("scsi%d : nexus reestablished.\n", instance->host_no);
+#endif
+
+ do_abort (instance);
+
+ cli();
+ for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue),
+ tmp = (Scsi_Cmnd *) hostdata->disconnected_queue;
+ tmp; prev = (Scsi_Cmnd **) &(tmp->host_scribble), tmp =
+ (Scsi_Cmnd *) tmp->host_scribble)
+ if (cmd == tmp) {
+ REMOVE(5,*prev,tmp,tmp->host_scribble);
+ *prev = (Scsi_Cmnd *) tmp->host_scribble;
+ tmp->host_scribble = NULL;
+ tmp->result = DID_ABORT << 16;
+ sti();
+ tmp->done(tmp);
+ return SCSI_ABORT_SUCCESS;
+ }
+ }
+
+/*
+ * Case 5 : If we reached this point, the command was not found in any of
+ * the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke.
+ */
+
+ sti();
+ printk("scsi%d : warning : SCSI command probably completed successfully\n"
+ " before abortion\n", instance->host_no);
+ return SCSI_ABORT_NOT_RUNNING;
+}
+
+
+/*
+ * Function : int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int reset_flags)
+ *
+ * Purpose : reset the SCSI bus.
+ *
+ * Returns : SCSI_RESET_WAKEUP
+ *
+ */
+
+#ifndef NCR5380_reset
+static
+#endif
+int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int dummy) {
+ NCR5380_local_declare();
+ NCR5380_setup(cmd->host);
+
+ NCR5380_print_status (cmd->host);
+ do_reset (cmd->host);
+
+ return SCSI_RESET_WAKEUP;
+}
+
diff --git a/linux/src/drivers/scsi/NCR5380.h b/linux/src/drivers/scsi/NCR5380.h
new file mode 100644
index 00000000..08e2897a
--- /dev/null
+++ b/linux/src/drivers/scsi/NCR5380.h
@@ -0,0 +1,373 @@
+/*
+ * NCR 5380 defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * DISTRIBUTION RELEASE 7
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: NCR5380.h,v $
+ */
+
+#ifndef NCR5380_H
+#define NCR5380_H
+
+#define NCR5380_PUBLIC_RELEASE 7
+#define NCR53C400_PUBLIC_RELEASE 2
+
+#define NDEBUG_ARBITRATION 0x1
+#define NDEBUG_AUTOSENSE 0x2
+#define NDEBUG_DMA 0x4
+#define NDEBUG_HANDSHAKE 0x8
+#define NDEBUG_INFORMATION 0x10
+#define NDEBUG_INIT 0x20
+#define NDEBUG_INTR 0x40
+#define NDEBUG_LINKED 0x80
+#define NDEBUG_MAIN 0x100
+#define NDEBUG_NO_DATAOUT 0x200
+#define NDEBUG_NO_WRITE 0x400
+#define NDEBUG_PIO 0x800
+#define NDEBUG_PSEUDO_DMA 0x1000
+#define NDEBUG_QUEUES 0x2000
+#define NDEBUG_RESELECTION 0x4000
+#define NDEBUG_SELECTION 0x8000
+#define NDEBUG_USLEEP 0x10000
+#define NDEBUG_LAST_BYTE_SENT 0x20000
+#define NDEBUG_RESTART_SELECT 0x40000
+#define NDEBUG_EXTENDED 0x80000
+#define NDEBUG_C400_PREAD 0x100000
+#define NDEBUG_C400_PWRITE 0x200000
+#define NDEBUG_LISTS 0x400000
+
+/*
+ * The contents of the OUTPUT DATA register are asserted on the bus when
+ * either arbitration is occurring or the phase-indicating signals (
+ * IO, CD, MSG) in the TARGET COMMAND register and the ASSERT DATA
+ * bit in the INITIATOR COMMAND register is set.
+ */
+
+#define OUTPUT_DATA_REG 0 /* wo DATA lines on SCSI bus */
+#define CURRENT_SCSI_DATA_REG 0 /* ro same */
+
+#define INITIATOR_COMMAND_REG 1 /* rw */
+#define ICR_ASSERT_RST 0x80 /* rw Set to assert RST */
+#define ICR_ARBITRATION_PROGRESS 0x40 /* ro Indicates arbitration complete */
+#define ICR_TRI_STATE 0x40 /* wo Set to tri-state drivers */
+#define ICR_ARBITRATION_LOST 0x20 /* ro Indicates arbitration lost */
+#define ICR_DIFF_ENABLE 0x20 /* wo Set to enable diff. drivers */
+#define ICR_ASSERT_ACK 0x10 /* rw ini Set to assert ACK */
+#define ICR_ASSERT_BSY 0x08 /* rw Set to assert BSY */
+#define ICR_ASSERT_SEL 0x04 /* rw Set to assert SEL */
+#define ICR_ASSERT_ATN 0x02 /* rw Set to assert ATN */
+#define ICR_ASSERT_DATA 0x01 /* rw SCSI_DATA_REG is asserted */
+
+#ifdef DIFFERENTIAL
+#define ICR_BASE ICR_DIFF_ENABLE
+#else
+#define ICR_BASE 0
+#endif
+
+#define MODE_REG 2
+/*
+ * Note : BLOCK_DMA code will keep DRQ asserted for the duration of the
+ * transfer, causing the chip to hog the bus. You probably don't want
+ * this.
+ */
+#define MR_BLOCK_DMA_MODE 0x80 /* rw block mode DMA */
+#define MR_TARGET 0x40 /* rw target mode */
+#define MR_ENABLE_PAR_CHECK 0x20 /* rw enable parity checking */
+#define MR_ENABLE_PAR_INTR 0x10 /* rw enable bad parity interrupt */
+#define MR_ENABLE_EOP_INTR 0x08 /* rw enable eop interrupt */
+#define MR_MONITOR_BSY 0x04 /* rw enable int on unexpected bsy fail */
+#define MR_DMA_MODE 0x02 /* rw DMA / pseudo DMA mode */
+#define MR_ARBITRATE 0x01 /* rw start arbitration */
+
+#ifdef PARITY
+#define MR_BASE MR_ENABLE_PAR_CHECK
+#else
+#define MR_BASE 0
+#endif
+
+#define TARGET_COMMAND_REG 3
+#define TCR_LAST_BYTE_SENT 0x80 /* ro DMA done */
+#define TCR_ASSERT_REQ 0x08 /* tgt rw assert REQ */
+#define TCR_ASSERT_MSG 0x04 /* tgt rw assert MSG */
+#define TCR_ASSERT_CD 0x02 /* tgt rw assert CD */
+#define TCR_ASSERT_IO 0x01 /* tgt rw assert IO */
+
+#define STATUS_REG 4 /* ro */
+/*
+ * Note : a set bit indicates an active signal, driven by us or another
+ * device.
+ */
+#define SR_RST 0x80
+#define SR_BSY 0x40
+#define SR_REQ 0x20
+#define SR_MSG 0x10
+#define SR_CD 0x08
+#define SR_IO 0x04
+#define SR_SEL 0x02
+#define SR_DBP 0x01
+
+/*
+ * Setting a bit in this register will cause an interrupt to be generated when
+ * BSY is false and SEL true and this bit is asserted on the bus.
+ */
+#define SELECT_ENABLE_REG 4 /* wo */
+
+#define BUS_AND_STATUS_REG 5 /* ro */
+#define BASR_END_DMA_TRANSFER 0x80 /* ro set on end of transfer */
+#define BASR_DRQ 0x40 /* ro mirror of DRQ pin */
+#define BASR_PARITY_ERROR 0x20 /* ro parity error detected */
+#define BASR_IRQ 0x10 /* ro mirror of IRQ pin */
+#define BASR_PHASE_MATCH 0x08 /* ro Set when MSG CD IO match TCR */
+#define BASR_BUSY_ERROR 0x04 /* ro Unexpected change to inactive state */
+#define BASR_ATN 0x02 /* ro BUS status */
+#define BASR_ACK 0x01 /* ro BUS status */
+
+/* Write any value to this register to start a DMA send */
+#define START_DMA_SEND_REG 5 /* wo */
+
+/*
+ * Used in DMA transfer mode, data is latched from the SCSI bus on
+ * the falling edge of REQ (ini) or ACK (tgt)
+ */
+#define INPUT_DATA_REG 6 /* ro */
+
+/* Write any value to this register to start a DMA receive */
+#define START_DMA_TARGET_RECEIVE_REG 6 /* wo */
+
+/* Read this register to clear interrupt conditions */
+#define RESET_PARITY_INTERRUPT_REG 7 /* ro */
+
+/* Write any value to this register to start an ini mode DMA receive */
+#define START_DMA_INITIATOR_RECEIVE_REG 7 /* wo */
+
+#define C400_CONTROL_STATUS_REG NCR53C400_register_offset-8 /* rw */
+
+#define CSR_RESET 0x80 /* wo Resets 53c400 */
+#define CSR_53C80_REG 0x80 /* ro 5380 registers busy */
+#define CSR_TRANS_DIR 0x40 /* rw Data transfer direction */
+#define CSR_SCSI_BUFF_INTR 0x20 /* rw Enable int on transfer ready */
+#define CSR_53C80_INTR 0x10 /* rw Enable 53c80 interrupts */
+#define CSR_SHARED_INTR 0x08 /* rw Interrupt sharing */
+#define CSR_HOST_BUF_NOT_RDY 0x04 /* ro Is Host buffer ready */
+#define CSR_SCSI_BUF_RDY 0x02 /* ro SCSI buffer read */
+#define CSR_GATED_53C80_IRQ 0x01 /* ro Last block xferred */
+
+#if 0
+#define CSR_BASE CSR_SCSI_BUFF_INTR | CSR_53C80_INTR
+#else
+#define CSR_BASE CSR_53C80_INTR
+#endif
+
+/* Number of 128-byte blocks to be transferred */
+#define C400_BLOCK_COUNTER_REG NCR53C400_register_offset-7 /* rw */
+
+/* Resume transfer after disconnect */
+#define C400_RESUME_TRANSFER_REG NCR53C400_register_offset-6 /* wo */
+
+/* Access to host buffer stack */
+#define C400_HOST_BUFFER NCR53C400_register_offset-4 /* rw */
+
+
+/* Note : PHASE_* macros are based on the values of the STATUS register */
+#define PHASE_MASK (SR_MSG | SR_CD | SR_IO)
+
+#define PHASE_DATAOUT 0
+#define PHASE_DATAIN SR_IO
+#define PHASE_CMDOUT SR_CD
+#define PHASE_STATIN (SR_CD | SR_IO)
+#define PHASE_MSGOUT (SR_MSG | SR_CD)
+#define PHASE_MSGIN (SR_MSG | SR_CD | SR_IO)
+#define PHASE_UNKNOWN 0xff
+
+/*
+ * Convert status register phase to something we can use to set phase in
+ * the target register so we can get phase mismatch interrupts on DMA
+ * transfers.
+ */
+
+#define PHASE_SR_TO_TCR(phase) ((phase) >> 2)
+
+/*
+ * The internal should_disconnect() function returns these based on the
+ * expected length of a disconnect if a device supports disconnect/
+ * reconnect.
+ */
+
+#define DISCONNECT_NONE 0
+#define DISCONNECT_TIME_TO_DATA 1
+#define DISCONNECT_LONG 2
+
+/*
+ * These are "special" values for the tag parameter passed to NCR5380_select.
+ */
+
+#define TAG_NEXT -1 /* Use next free tag */
+#define TAG_NONE -2 /*
+ * Establish I_T_L nexus instead of I_T_L_Q
+ * even on SCSI-II devices.
+ */
+
+/*
+ * These are "special" values for the irq and dma_channel fields of the
+ * Scsi_Host structure
+ */
+
+#define IRQ_NONE 255
+#define DMA_NONE 255
+#define IRQ_AUTO 254
+#define DMA_AUTO 254
+
+#define FLAG_HAS_LAST_BYTE_SENT 1 /* NCR53c81 or better */
+#define FLAG_CHECK_LAST_BYTE_SENT 2 /* Only test once */
+#define FLAG_NCR53C400 4 /* NCR53c400 */
+#define FLAG_NO_PSEUDO_DMA 8 /* Inhibit DMA */
+
+#ifndef ASM
+struct NCR5380_hostdata {
+ NCR5380_implementation_fields; /* implementation specific */
+ unsigned char id_mask, id_higher_mask; /* 1 << id, all bits greater */
+ unsigned char targets_present; /* targets we have connected
+ to, so we can call a select
+ failure a retryable condition */
+ volatile unsigned char busy[8]; /* index = target, bit = lun */
+#if defined(REAL_DMA) || defined(REAL_DMA_POLL)
+ volatile int dma_len; /* requested length of DMA */
+#endif
+ volatile unsigned char last_message; /* last message OUT */
+ volatile Scsi_Cmnd *connected; /* currently connected command */
+ volatile Scsi_Cmnd *issue_queue; /* waiting to be issued */
+ volatile Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */
+ volatile int restart_select; /* we have disconnected,
+ used to restart
+ NCR5380_select() */
+ volatile unsigned aborted:1; /* flag, says aborted */
+ int flags;
+#ifdef USLEEP
+ unsigned long time_expires; /* in jiffies, set prior to sleeping */
+ struct Scsi_Host *next_timer;
+#endif
+#ifdef NCR5380_STATS
+ unsigned timebase; /* Base for time calcs */
+ long time_read[8]; /* time to do reads */
+ long time_write[8]; /* time to do writes */
+ unsigned long bytes_read[8]; /* bytes read */
+ unsigned long bytes_write[8]; /* bytes written */
+ unsigned pendingr;
+ unsigned pendingw;
+#endif
+};
+
+#ifdef __KERNEL__
+static struct Scsi_Host *first_instance; /* linked list of 5380's */
+
+#if defined(AUTOPROBE_IRQ)
+static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible);
+#endif
+static void NCR5380_init (struct Scsi_Host *instance, int flags);
+static void NCR5380_information_transfer (struct Scsi_Host *instance);
+#ifndef DONT_USE_INTR
+static void NCR5380_intr (int irq, void *dev_id, struct pt_regs * regs);
+#endif
+static void NCR5380_main (void);
+static void NCR5380_print_options (struct Scsi_Host *instance);
+static void NCR5380_print_phase (struct Scsi_Host *instance);
+static void NCR5380_print (struct Scsi_Host *instance);
+#ifndef NCR5380_abort
+static
+#endif
+int NCR5380_abort (Scsi_Cmnd *cmd);
+#ifndef NCR5380_reset
+static
+#endif
+int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int reset_flags);
+#ifndef NCR5380_queue_command
+static
+#endif
+int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+
+
+static void NCR5380_reselect (struct Scsi_Host *instance);
+static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag);
+#if defined(PSEUDO_DMA) || defined(REAL_DMA) || defined(REAL_DMA_POLL)
+static int NCR5380_transfer_dma (struct Scsi_Host *instance,
+ unsigned char *phase, int *count, unsigned char **data);
+#endif
+static int NCR5380_transfer_pio (struct Scsi_Host *instance,
+ unsigned char *phase, int *count, unsigned char **data);
+
+#if (defined(REAL_DMA) || defined(REAL_DMA_POLL))
+
+#if defined(i386) || defined(__alpha__)
+
+static __inline__ int NCR5380_pc_dma_setup (struct Scsi_Host *instance,
+ unsigned char *ptr, unsigned int count, unsigned char mode) {
+ unsigned limit;
+ unsigned long bus_addr = virt_to_bus(ptr);
+
+ if (instance->dma_channel <=3) {
+ if (count > 65536)
+ count = 65536;
+ limit = 65536 - (bus_addr & 0xFFFF);
+ } else {
+ if (count > 65536 * 2)
+ count = 65536 * 2;
+ limit = 65536* 2 - (bus_addr & 0x1FFFF);
+ }
+
+ if (count > limit) count = limit;
+
+ if ((count & 1) || (bus_addr & 1))
+ panic ("scsi%d : attempted unaligned DMA transfer\n", instance->host_no);
+ cli();
+ disable_dma(instance->dma_channel);
+ clear_dma_ff(instance->dma_channel);
+ set_dma_addr(instance->dma_channel, bus_addr);
+ set_dma_count(instance->dma_channel, count);
+ set_dma_mode(instance->dma_channel, mode);
+ enable_dma(instance->dma_channel);
+ sti();
+ return count;
+}
+
+static __inline__ int NCR5380_pc_dma_write_setup (struct Scsi_Host *instance,
+ unsigned char *src, unsigned int count) {
+ return NCR5380_pc_dma_setup (instance, src, count, DMA_MODE_WRITE);
+}
+
+static __inline__ int NCR5380_pc_dma_read_setup (struct Scsi_Host *instance,
+ unsigned char *src, unsigned int count) {
+ return NCR5380_pc_dma_setup (instance, src, count, DMA_MODE_READ);
+}
+
+static __inline__ int NCR5380_pc_dma_residual (struct Scsi_Host *instance) {
+ register int tmp;
+ cli();
+ clear_dma_ff(instance->dma_channel);
+ tmp = get_dma_residue(instance->dma_channel);
+ sti();
+ return tmp;
+}
+#endif /* defined(i386) || defined(__alpha__) */
+#endif /* defined(REAL_DMA) */
+#endif __KERNEL_
+#endif /* ndef ASM */
+#endif /* NCR5380_H */
diff --git a/linux/src/drivers/scsi/NCR53c406a.c b/linux/src/drivers/scsi/NCR53c406a.c
new file mode 100644
index 00000000..9f2de9a0
--- /dev/null
+++ b/linux/src/drivers/scsi/NCR53c406a.c
@@ -0,0 +1,1079 @@
+/*
+ * NCR53c406.c
+ * Low-level SCSI driver for NCR53c406a chip.
+ * Copyright (C) 1994, 1995, 1996 Normunds Saumanis (normunds@fi.ibm.com)
+ *
+ * LILO command line usage: ncr53c406a=<PORTBASE>[,<IRQ>[,<FASTPIO>]]
+ * Specify IRQ = 0 for non-interrupt driven mode.
+ * FASTPIO = 1 for fast pio mode, 0 for slow mode.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#define NCR53C406A_DEBUG 0
+#define VERBOSE_NCR53C406A_DEBUG 0
+
+/* Set this to 1 for PIO mode (recommended) or to 0 for DMA mode */
+#define USE_PIO 1
+
+#define USE_BIOS 0
+/* #define BIOS_ADDR 0xD8000 */ /* define this if autoprobe fails */
+/* #define PORT_BASE 0x330 */ /* define this if autoprobe fails */
+/* #define IRQ_LEV 0 */ /* define this if autoprobe fails */
+#define DMA_CHAN 5 /* this is ignored if DMA is disabled */
+
+/* Set this to 0 if you encounter kernel lockups while transferring
+ * data in PIO mode */
+#define USE_FAST_PIO 1
+
+/* ============= End of user configurable parameters ============= */
+
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#include "NCR53c406a.h"
+
+/* ============================================================= */
+
+#define WATCHDOG 5000000
+
+#define SYNC_MODE 0 /* Synchronous transfer mode */
+
+#if DEBUG
+#undef NCR53C406A_DEBUG
+#define NCR53C406A_DEBUG 1
+#endif
+
+#if USE_PIO
+#define USE_DMA 0
+#else
+#define USE_DMA 1
+#endif
+
+/* Default configuration */
+#define C1_IMG 0x07 /* ID=7 */
+#define C2_IMG 0x48 /* FE SCSI2 */
+#if USE_DMA
+#define C3_IMG 0x21 /* CDB TE */
+#else
+#define C3_IMG 0x20 /* CDB */
+#endif
+#define C4_IMG 0x04 /* ANE */
+#define C5_IMG 0xb6 /* AA PI SIE POL */
+
+#define REG0 (outb(C4_IMG, CONFIG4))
+#define REG1 (outb(C5_IMG, CONFIG5))
+
+#if NCR53C406A_DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#if VERBOSE_NCR53C406A_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define LOAD_DMA_COUNT(count) \
+ outb(count & 0xff, TC_LSB); \
+ outb((count >> 8) & 0xff, TC_MSB); \
+ outb((count >> 16) & 0xff, TC_HIGH);
+
+/* Chip commands */
+#define DMA_OP 0x80
+
+#define SCSI_NOP 0x00
+#define FLUSH_FIFO 0x01
+#define CHIP_RESET 0x02
+#define SCSI_RESET 0x03
+#define RESELECT 0x40
+#define SELECT_NO_ATN 0x41
+#define SELECT_ATN 0x42
+#define SELECT_ATN_STOP 0x43
+#define ENABLE_SEL 0x44
+#define DISABLE_SEL 0x45
+#define SELECT_ATN3 0x46
+#define RESELECT3 0x47
+#define TRANSFER_INFO 0x10
+#define INIT_CMD_COMPLETE 0x11
+#define MSG_ACCEPT 0x12
+#define TRANSFER_PAD 0x18
+#define SET_ATN 0x1a
+#define RESET_ATN 0x1b
+#define SEND_MSG 0x20
+#define SEND_STATUS 0x21
+#define SEND_DATA 0x22
+#define DISCONN_SEQ 0x23
+#define TERMINATE_SEQ 0x24
+#define TARG_CMD_COMPLETE 0x25
+#define DISCONN 0x27
+#define RECV_MSG 0x28
+#define RECV_CMD 0x29
+#define RECV_DATA 0x2a
+#define RECV_CMD_SEQ 0x2b
+#define TARGET_ABORT_DMA 0x04
+
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at */
+/* 1 = blue
+ 2 = green
+ 3 = cyan
+ 4 = red
+ 5 = magenta
+ 6 = yellow
+ 7 = white
+*/
+
+#if NCR53C406A_DEBUG
+#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+/*----------------------------------------------------------------*/
+
+enum Phase {
+ idle,
+ data_out,
+ data_in,
+ command_ph,
+ status_ph,
+ message_out,
+ message_in
+};
+
+/* Static function prototypes */
+static void NCR53c406a_intr(int, void *, struct pt_regs *);
+static void internal_done(Scsi_Cmnd *);
+static void wait_intr(void);
+static void chip_init(void);
+static void calc_port_addr(void);
+#ifndef IRQ_LEV
+static int irq_probe(void);
+#endif
+
+/* ================================================================= */
+
+#if USE_BIOS
+static void *bios_base = (void *)0;
+#endif
+
+#if PORT_BASE
+static int port_base = PORT_BASE;
+#else
+static int port_base = 0;
+#endif
+
+#if IRQ_LEV
+static int irq_level = IRQ_LEV;
+#else
+static int irq_level = -1; /* 0 is 'no irq', so use -1 for 'uninitialized'*/
+#endif
+
+#if USE_DMA
+static int dma_chan = 0;
+#endif
+
+#if USE_PIO
+static int fast_pio = USE_FAST_PIO;
+#endif
+
+static Scsi_Cmnd *current_SC = NULL;
+static volatile int internal_done_flag = 0;
+static volatile int internal_done_errcode = 0;
+static char info_msg[256];
+
+struct proc_dir_entry proc_scsi_NCR53c406a = {
+ PROC_SCSI_NCR53C406A, 7, "NCR53c406a",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+/* ================================================================= */
+
+/* possible BIOS locations */
+#if USE_BIOS
+static void *addresses[] = {
+ (void *)0xd8000,
+ (void *)0xc8000
+};
+#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned ))
+#endif USE_BIOS
+
+/* possible i/o port addresses */
+static unsigned short ports[] = { 0x230, 0x330 };
+#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))
+
+/* possible interrupt channels */
+static unsigned short intrs[] = { 10, 11, 12, 15 };
+#define INTR_COUNT (sizeof( intrs ) / sizeof( unsigned short ))
+
+/* signatures for NCR 53c406a based controllers */
+#if USE_BIOS
+struct signature {
+ char *signature;
+ int sig_offset;
+ int sig_length;
+} signatures[] = {
+ /* 1 2 3 4 5 6 */
+ /* 123456789012345678901234567890123456789012345678901234567890 */
+ { "Copyright (C) Acculogic, Inc.\r\n2.8M Diskette Extension Bios ver 4.04.03 03/01/1993", 61, 82 },
+};
+#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))
+#endif USE_BIOS
+
+/* ============================================================ */
+
+/* Control Register Set 0 */
+static int TC_LSB; /* transfer counter lsb */
+static int TC_MSB; /* transfer counter msb */
+static int SCSI_FIFO; /* scsi fifo register */
+static int CMD_REG; /* command register */
+static int STAT_REG; /* status register */
+static int DEST_ID; /* selection/reselection bus id */
+static int INT_REG; /* interrupt status register */
+static int SRTIMOUT; /* select/reselect timeout reg */
+static int SEQ_REG; /* sequence step register */
+static int SYNCPRD; /* synchronous transfer period */
+static int FIFO_FLAGS; /* indicates # of bytes in fifo */
+static int SYNCOFF; /* synchronous offset register */
+static int CONFIG1; /* configuration register */
+static int CLKCONV; /* clock conversion reg */
+/*static int TESTREG;*/ /* test mode register */
+static int CONFIG2; /* Configuration 2 Register */
+static int CONFIG3; /* Configuration 3 Register */
+static int CONFIG4; /* Configuration 4 Register */
+static int TC_HIGH; /* Transfer Counter High */
+/*static int FIFO_BOTTOM;*/ /* Reserve FIFO byte register */
+
+/* Control Register Set 1 */
+/*static int JUMPER_SENSE;*/ /* Jumper sense port reg (r/w) */
+/*static int SRAM_PTR;*/ /* SRAM address pointer reg (r/w) */
+/*static int SRAM_DATA;*/ /* SRAM data register (r/w) */
+static int PIO_FIFO; /* PIO FIFO registers (r/w) */
+/*static int PIO_FIFO1;*/ /* */
+/*static int PIO_FIFO2;*/ /* */
+/*static int PIO_FIFO3;*/ /* */
+static int PIO_STATUS; /* PIO status (r/w) */
+/*static int ATA_CMD;*/ /* ATA command/status reg (r/w) */
+/*static int ATA_ERR;*/ /* ATA features/error register (r/w)*/
+static int PIO_FLAG; /* PIO flag interrupt enable (r/w) */
+static int CONFIG5; /* Configuration 5 register (r/w) */
+/*static int SIGNATURE;*/ /* Signature Register (r) */
+/*static int CONFIG6;*/ /* Configuration 6 register (r) */
+
+/* ============================================================== */
+
+#if USE_DMA
+static __inline__ int
+NCR53c406a_dma_setup (unsigned char *ptr,
+ unsigned int count,
+ unsigned char mode) {
+ unsigned limit;
+ unsigned long flags = 0;
+
+ VDEB(printk("dma: before count=%d ", count));
+ if (dma_chan <=3) {
+ if (count > 65536)
+ count = 65536;
+ limit = 65536 - (((unsigned) ptr) & 0xFFFF);
+ } else {
+ if (count > (65536<<1))
+ count = (65536<<1);
+ limit = (65536<<1) - (((unsigned) ptr) & 0x1FFFF);
+ }
+
+ if (count > limit) count = limit;
+
+ VDEB(printk("after count=%d\n", count));
+ if ((count & 1) || (((unsigned) ptr) & 1))
+ panic ("NCR53c406a: attempted unaligned DMA transfer\n");
+
+ save_flags(flags);
+ cli();
+ disable_dma(dma_chan);
+ clear_dma_ff(dma_chan);
+ set_dma_addr(dma_chan, (long) ptr);
+ set_dma_count(dma_chan, count);
+ set_dma_mode(dma_chan, mode);
+ enable_dma(dma_chan);
+ restore_flags(flags);
+
+ return count;
+}
+
+static __inline__ int
+NCR53c406a_dma_write(unsigned char *src, unsigned int count) {
+ return NCR53c406a_dma_setup (src, count, DMA_MODE_WRITE);
+}
+
+static __inline__ int
+NCR53c406a_dma_read(unsigned char *src, unsigned int count) {
+ return NCR53c406a_dma_setup (src, count, DMA_MODE_READ);
+}
+
+static __inline__ int
+NCR53c406a_dma_residual (void) {
+ register int tmp;
+ unsigned long flags = 0;
+ save_flags(flags);
+ cli();
+ clear_dma_ff(dma_chan);
+ tmp = get_dma_residue(dma_chan);
+ restore_flags(flags);
+
+ return tmp;
+}
+#endif USE_DMA
+
+#if USE_PIO
+static __inline__ int NCR53c406a_pio_read(unsigned char *request,
+ unsigned int reqlen)
+{
+ int i;
+ int len; /* current scsi fifo size */
+ unsigned long flags = 0;
+
+ REG1;
+ while (reqlen) {
+ i = inb(PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80)
+ return 0;
+
+ switch( i & 0x1e ) {
+ default:
+ case 0x10:
+ len=0; break;
+ case 0x0:
+ len=1; break;
+ case 0x8:
+ len=42; break;
+ case 0xc:
+ len=84; break;
+ case 0xe:
+ len=128; break;
+ }
+
+ if ((i & 0x40) && len == 0) { /* fifo empty and interrupt occurred */
+ return 0;
+ }
+
+ if (len) {
+ if( len > reqlen )
+ len = reqlen;
+
+ save_flags(flags);
+ cli();
+ if( fast_pio && len > 3 ) {
+ insl(PIO_FIFO,request,len>>2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ }
+ else {
+ while(len--) {
+ *request++ = inb(PIO_FIFO);
+ reqlen--;
+ }
+ }
+ restore_flags(flags);
+ }
+ }
+ return 0;
+}
+
+static __inline__ int NCR53c406a_pio_write(unsigned char *request,
+ unsigned int reqlen)
+{
+ int i = 0;
+ int len; /* current scsi fifo size */
+ unsigned long flags = 0;
+
+ REG1;
+ while (reqlen && !(i&0x40)) {
+ i = inb(PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80) /* error */
+ return 0;
+
+ switch( i & 0x1e ) {
+ case 0x10:
+ len=128; break;
+ case 0x0:
+ len=84; break;
+ case 0x8:
+ len=42; break;
+ case 0xc:
+ len=1; break;
+ default:
+ case 0xe:
+ len=0; break;
+ }
+
+ if (len) {
+ if( len > reqlen )
+ len = reqlen;
+
+ save_flags(flags);
+ cli();
+ if( fast_pio && len > 3 ) {
+ outsl(PIO_FIFO,request,len>>2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ }
+ else {
+ while(len--) {
+ outb(*request++, PIO_FIFO);
+ reqlen--;
+ }
+ }
+ restore_flags(flags);
+ }
+ }
+ return 0;
+}
+#endif USE_PIO
+
+int
+NCR53c406a_detect(Scsi_Host_Template * tpnt){
+ struct Scsi_Host *shpnt;
+#ifndef PORT_BASE
+ int i;
+#endif
+
+#if USE_BIOS
+ int ii, jj;
+ bios_base = 0;
+ /* look for a valid signature */
+ for( ii=0; ii < ADDRESS_COUNT && !bios_base; ii++)
+ for( jj=0; (jj < SIGNATURE_COUNT) && !bios_base; jj++)
+ if(!memcmp((void *) addresses[ii]+signatures[jj].sig_offset,
+ (void *) signatures[jj].signature,
+ (int) signatures[jj].sig_length))
+ bios_base=addresses[ii];
+
+ if(!bios_base){
+ printk("NCR53c406a: BIOS signature not found\n");
+ return 0;
+ }
+
+ DEB(printk("NCR53c406a BIOS found at %X\n", (unsigned int) bios_base););
+#endif USE_BIOS
+
+#ifdef PORT_BASE
+ if (check_region(port_base, 0x10)) /* ports already snatched */
+ port_base = 0;
+
+#else /* autodetect */
+ if (port_base) { /* LILO override */
+ if (check_region(port_base, 0x10))
+ port_base = 0;
+ }
+ else {
+ for(i=0; i<PORT_COUNT && !port_base; i++){
+ if(check_region(ports[i], 0x10)){
+ DEB(printk("NCR53c406a: port %x in use\n", ports[i]));
+ }
+ else {
+ VDEB(printk("NCR53c406a: port %x available\n", ports[i]));
+ outb(C5_IMG, ports[i] + 0x0d); /* reg set 1 */
+ if( (inb(ports[i] + 0x0e) ^ inb(ports[i] + 0x0e)) == 7
+ && (inb(ports[i] + 0x0e) ^ inb(ports[i] + 0x0e)) == 7
+ && (inb(ports[i] + 0x0e) & 0xf8) == 0x58 ) {
+ VDEB(printk("NCR53c406a: Sig register valid\n"));
+ VDEB(printk("port_base=%x\n", port_base));
+ port_base = ports[i];
+ }
+ }
+ }
+ }
+#endif PORT_BASE
+
+ if(!port_base){ /* no ports found */
+ printk("NCR53c406a: no available ports found\n");
+ return 0;
+ }
+
+ DEB(printk("NCR53c406a detected\n"));
+
+ calc_port_addr();
+ chip_init();
+
+#ifndef IRQ_LEV
+ if (irq_level < 0) { /* LILO override if >= 0*/
+ irq_level=irq_probe();
+ if (irq_level < 0) { /* Trouble */
+ printk("NCR53c406a: IRQ problem, irq_level=%d, giving up\n", irq_level);
+ return 0;
+ }
+ }
+#endif
+
+ DEB(printk("NCR53c406a: using port_base %x\n", port_base));
+ request_region(port_base, 0x10, "NCR53c406a");
+
+ if(irq_level > 0) {
+ if(request_irq(irq_level, NCR53c406a_intr, 0, "NCR53c406a", NULL)){
+ printk("NCR53c406a: unable to allocate IRQ %d\n", irq_level);
+ return 0;
+ }
+ tpnt->can_queue = 1;
+ DEB(printk("NCR53c406a: allocated IRQ %d\n", irq_level));
+ }
+ else if (irq_level == 0) {
+ tpnt->can_queue = 0;
+ DEB(printk("NCR53c406a: No interrupts detected\n"));
+#if USE_DMA
+ printk("NCR53c406a: No interrupts found and DMA mode defined. Giving up.\n");
+ return 0;
+#endif USE_DMA
+ }
+ else {
+ DEB(printk("NCR53c406a: Shouldn't get here!\n"));
+ return 0;
+ }
+
+#if USE_DMA
+ dma_chan = DMA_CHAN;
+ if(request_dma(dma_chan, "NCR53c406a") != 0){
+ printk("NCR53c406a: unable to allocate DMA channel %d\n", dma_chan);
+ return 0;
+ }
+
+ DEB(printk("Allocated DMA channel %d\n", dma_chan));
+#endif USE_DMA
+
+ tpnt->present = 1;
+ tpnt->proc_dir = &proc_scsi_NCR53c406a;
+
+ shpnt = scsi_register(tpnt, 0);
+ shpnt->irq = irq_level;
+ shpnt->io_port = port_base;
+ shpnt->n_io_port = 0x10;
+#if USE_DMA
+ shpnt->dma = dma_chan;
+#endif
+
+#if USE_DMA
+ sprintf(info_msg, "NCR53c406a at 0x%x, IRQ %d, DMA channel %d.",
+ port_base, irq_level, dma_chan);
+#else
+ sprintf(info_msg, "NCR53c406a at 0x%x, IRQ %d, %s PIO mode.",
+ port_base, irq_level, fast_pio ? "fast" : "slow");
+#endif
+
+ return (tpnt->present);
+}
+
+/* called from init/main.c */
+void NCR53c406a_setup(char *str, int *ints)
+{
+ static size_t setup_idx = 0;
+ size_t i;
+
+ DEB(printk("NCR53c406a: Setup called\n"););
+
+ if (setup_idx >= PORT_COUNT - 1) {
+ printk("NCR53c406a: Setup called too many times. Bad LILO params?\n");
+ return;
+ }
+ if (ints[0] < 1 || ints[0] > 3) {
+ printk("NCR53c406a: Malformed command line\n");
+ printk("NCR53c406a: Usage: ncr53c406a=<PORTBASE>[,<IRQ>[,<FASTPIO>]]\n");
+ return;
+ }
+ for (i = 0; i < PORT_COUNT && !port_base; i++)
+ if (ports[i] == ints[1]) {
+ port_base = ints[1];
+ DEB(printk("NCR53c406a: Specified port_base 0x%X\n", port_base);)
+ }
+ if (!port_base) {
+ printk("NCR53c406a: Invalid PORTBASE 0x%X specified\n", ints[1]);
+ return;
+ }
+
+ if (ints[0] > 1) {
+ if (ints[2] == 0) {
+ irq_level = 0;
+ DEB(printk("NCR53c406a: Specified irq %d\n", irq_level);)
+ }
+ else
+ for (i = 0; i < INTR_COUNT && irq_level < 0; i++)
+ if (intrs[i] == ints[2]) {
+ irq_level = ints[2];
+ DEB(printk("NCR53c406a: Specified irq %d\n", port_base);)
+ }
+ if (irq_level < 0)
+ printk("NCR53c406a: Invalid IRQ %d specified\n", ints[2]);
+ }
+
+ if (ints[0] > 2)
+ fast_pio = ints[3];
+
+ DEB(printk("NCR53c406a: port_base=0x%X, irq=%d, fast_pio=%d\n",
+ port_base, irq_level, fast_pio);)
+}
+
+const char*
+NCR53c406a_info(struct Scsi_Host *SChost){
+ DEB(printk("NCR53c406a_info called\n"));
+ return (info_msg);
+}
+
+static void internal_done(Scsi_Cmnd *SCpnt) {
+ internal_done_errcode = SCpnt->result;
+ ++internal_done_flag;
+}
+
+
+static void wait_intr() {
+ int i = jiffies + WATCHDOG;
+
+ while(i>jiffies && !(inb(STAT_REG)&0xe0)) /* wait for a pseudo-interrupt */
+ barrier();
+
+ if (i <= jiffies) { /* Timed out */
+ rtrc(0);
+ current_SC->result = DID_TIME_OUT << 16;
+ current_SC->SCp.phase = idle;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ NCR53c406a_intr(0, NULL, NULL);
+}
+
+int NCR53c406a_command(Scsi_Cmnd *SCpnt){
+ DEB(printk("NCR53c406a_command called\n"));
+ NCR53c406a_queue(SCpnt, internal_done);
+ if(irq_level)
+ while (!internal_done_flag);
+ else /* interrupts not supported */
+ while (!internal_done_flag)
+ wait_intr();
+
+ internal_done_flag = 0;
+ return internal_done_errcode;
+}
+
+
+int
+NCR53c406a_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)){
+ int i;
+ unsigned long flags = 0;
+
+ VDEB(printk("NCR53c406a_queue called\n"));
+ DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n",
+ SCpnt->cmnd[0],
+ SCpnt->cmd_len,
+ SCpnt->target,
+ SCpnt->lun,
+ SCpnt->request_bufflen));
+
+#if 0
+ VDEB(for(i=0; i<SCpnt->cmd_len; i++)
+ printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i]));
+ VDEB(printk("\n"));
+#endif
+
+ current_SC = SCpnt;
+ current_SC->scsi_done = done;
+ current_SC->SCp.phase = command_ph;
+ current_SC->SCp.Status = 0;
+ current_SC->SCp.Message = 0;
+
+ save_flags(flags);
+ cli();
+ REG0;
+ outb(SCpnt->target, DEST_ID); /* set destination */
+ outb(FLUSH_FIFO, CMD_REG); /* reset the fifos */
+
+ for(i=0; i<SCpnt->cmd_len; i++){
+ outb(SCpnt->cmnd[i], SCSI_FIFO);
+ }
+ outb(SELECT_NO_ATN, CMD_REG);
+ restore_flags(flags);
+
+ rtrc(1);
+ return 0;
+}
+
+int
+NCR53c406a_abort(Scsi_Cmnd *SCpnt){
+ DEB(printk("NCR53c406a_abort called\n"));
+ return SCSI_ABORT_SNOOZE; /* Don't know how to abort */
+}
+
+int
+NCR53c406a_reset(Scsi_Cmnd *SCpnt, unsigned int flags){
+ DEB(printk("NCR53c406a_reset called\n"));
+ outb(C4_IMG, CONFIG4); /* Select reg set 0 */
+ outb(CHIP_RESET, CMD_REG);
+ outb(SCSI_NOP, CMD_REG); /* required after reset */
+ outb(SCSI_RESET, CMD_REG);
+ chip_init();
+
+ rtrc(2);
+ if (irq_level)
+ return SCSI_RESET_PENDING; /* should get an interrupt */
+ else
+ return SCSI_RESET_WAKEUP; /* won't get any interrupts */
+}
+
+int
+NCR53c406a_biosparm(Scsi_Disk *disk, kdev_t dev, int* info_array){
+ int size;
+
+ DEB(printk("NCR53c406a_biosparm called\n"));
+
+ size = disk->capacity;
+ info_array[0] = 64; /* heads */
+ info_array[1] = 32; /* sectors */
+ info_array[2] = size>>11; /* cylinders */
+ if (info_array[2] > 1024) { /* big disk */
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = size / (255*63);
+ }
+ return 0;
+ }
+
+ static void
+NCR53c406a_intr(int unused, void *dev_id, struct pt_regs *regs){
+ DEB(unsigned char fifo_size;)
+ DEB(unsigned char seq_reg;)
+ unsigned char status, int_reg;
+ unsigned long flags = 0;
+#if USE_PIO
+ unsigned char pio_status;
+ struct scatterlist *sglist;
+ unsigned int sgcount;
+#endif
+
+ VDEB(printk("NCR53c406a_intr called\n"));
+
+ save_flags(flags);
+ cli();
+#if USE_PIO
+ REG1;
+ pio_status = inb(PIO_STATUS);
+#endif
+ REG0;
+ status = inb(STAT_REG);
+ DEB(seq_reg = inb(SEQ_REG));
+ int_reg = inb(INT_REG);
+ DEB(fifo_size = inb(FIFO_FLAGS) & 0x1f);
+ restore_flags(flags);
+
+#if NCR53C406A_DEBUG
+ printk("status=%02x, seq_reg=%02x, int_reg=%02x, fifo_size=%02x",
+ status, seq_reg, int_reg, fifo_size);
+#if (USE_DMA)
+ printk("\n");
+#else
+ printk(", pio=%02x\n", pio_status);
+#endif USE_DMA
+#endif NCR53C406A_DEBUG
+
+ if(int_reg & 0x80){ /* SCSI reset intr */
+ rtrc(3);
+ DEB(printk("NCR53c406a: reset intr received\n"));
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_RESET << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+#if USE_PIO
+ if(pio_status & 0x80) {
+ printk("NCR53C406A: Warning: PIO error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_ERROR << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+#endif USE_PIO
+
+ if(status & 0x20) { /* Parity error */
+ printk("NCR53c406a: Warning: parity error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_PARITY << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if(status & 0x40) { /* Gross error */
+ printk("NCR53c406a: Warning: gross error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_ERROR << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if(int_reg & 0x20){ /* Disconnect */
+ DEB(printk("NCR53c406a: disconnect intr received\n"));
+ if(current_SC->SCp.phase != message_in){ /* Unexpected disconnect */
+ current_SC->result = DID_NO_CONNECT << 16;
+ }
+ else{ /* Command complete, return status and message */
+ current_SC->result = (current_SC->SCp.Status & 0xff)
+ | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16);
+ }
+
+ rtrc(0);
+ current_SC->SCp.phase = idle;
+ current_SC->scsi_done( current_SC );
+ return;
+ }
+
+ switch(status & 0x07){ /* scsi phase */
+ case 0x00: /* DATA-OUT */
+ if(int_reg & 0x10){ /* Target requesting info transfer */
+ rtrc(5);
+ current_SC->SCp.phase = data_out;
+ VDEB(printk("NCR53c406a: Data-Out phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ LOAD_DMA_COUNT(current_SC->request_bufflen); /* Max transfer size */
+#if USE_DMA /* No s/g support for DMA */
+ NCR53c406a_dma_write(current_SC->request_buffer,
+ current_SC->request_bufflen);
+#endif USE_DMA
+ outb(TRANSFER_INFO | DMA_OP, CMD_REG);
+#if USE_PIO
+ if (!current_SC->use_sg) /* Don't use scatter-gather */
+ NCR53c406a_pio_write(current_SC->request_buffer,
+ current_SC->request_bufflen);
+ else { /* use scatter-gather */
+ sgcount = current_SC->use_sg;
+ sglist = current_SC->request_buffer;
+ while( sgcount-- ) {
+ NCR53c406a_pio_write(sglist->address, sglist->length);
+ sglist++;
+ }
+ }
+ REG0;
+#endif USE_PIO
+ }
+ break;
+
+ case 0x01: /* DATA-IN */
+ if(int_reg & 0x10){ /* Target requesting info transfer */
+ rtrc(6);
+ current_SC->SCp.phase = data_in;
+ VDEB(printk("NCR53c406a: Data-In phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ LOAD_DMA_COUNT(current_SC->request_bufflen); /* Max transfer size */
+#if USE_DMA /* No s/g support for DMA */
+ NCR53c406a_dma_read(current_SC->request_buffer,
+ current_SC->request_bufflen);
+#endif USE_DMA
+ outb(TRANSFER_INFO | DMA_OP, CMD_REG);
+#if USE_PIO
+ if (!current_SC->use_sg) /* Don't use scatter-gather */
+ NCR53c406a_pio_read(current_SC->request_buffer,
+ current_SC->request_bufflen);
+ else { /* Use scatter-gather */
+ sgcount = current_SC->use_sg;
+ sglist = current_SC->request_buffer;
+ while( sgcount-- ) {
+ NCR53c406a_pio_read(sglist->address, sglist->length);
+ sglist++;
+ }
+ }
+ REG0;
+#endif USE_PIO
+ }
+ break;
+
+ case 0x02: /* COMMAND */
+ current_SC->SCp.phase = command_ph;
+ printk("NCR53c406a: Warning: Unknown interrupt occurred in command phase!\n");
+ break;
+
+ case 0x03: /* STATUS */
+ rtrc(7);
+ current_SC->SCp.phase = status_ph;
+ VDEB(printk("NCR53c406a: Status phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ outb(INIT_CMD_COMPLETE, CMD_REG);
+ break;
+
+ case 0x04: /* Reserved */
+ case 0x05: /* Reserved */
+ printk("NCR53c406a: WARNING: Reserved phase!!!\n");
+ break;
+
+ case 0x06: /* MESSAGE-OUT */
+ DEB(printk("NCR53c406a: Message-Out phase\n"));
+ current_SC->SCp.phase = message_out;
+ outb(SET_ATN, CMD_REG); /* Reject the message */
+ outb(MSG_ACCEPT, CMD_REG);
+ break;
+
+ case 0x07: /* MESSAGE-IN */
+ rtrc(4);
+ VDEB(printk("NCR53c406a: Message-In phase\n"));
+ current_SC->SCp.phase = message_in;
+
+ current_SC->SCp.Status = inb(SCSI_FIFO);
+ current_SC->SCp.Message = inb(SCSI_FIFO);
+
+ VDEB(printk("SCSI FIFO size=%d\n", inb(FIFO_FLAGS) & 0x1f));
+ DEB(printk("Status = %02x Message = %02x\n",
+ current_SC->SCp.Status, current_SC->SCp.Message));
+
+ if(current_SC->SCp.Message == SAVE_POINTERS ||
+ current_SC->SCp.Message == DISCONNECT) {
+ outb(SET_ATN, CMD_REG); /* Reject message */
+ DEB(printk("Discarding SAVE_POINTERS message\n"));
+ }
+ outb(MSG_ACCEPT, CMD_REG);
+ break;
+ }
+}
+
+#ifndef IRQ_LEV
+static int irq_probe()
+{
+ int irqs, irq;
+ int i;
+
+ inb(INT_REG); /* clear the interrupt register */
+ sti();
+ irqs = probe_irq_on();
+
+ /* Invalid command will cause an interrupt */
+ REG0;
+ outb(0xff, CMD_REG);
+
+ /* Wait for the interrupt to occur */
+ i = jiffies + WATCHDOG;
+ while(i > jiffies && !(inb(STAT_REG) & 0x80))
+ barrier();
+ if (i <= jiffies) { /* Timed out, must be hardware trouble */
+ probe_irq_off(irqs);
+ return -1;
+ }
+
+ irq = probe_irq_off(irqs);
+
+ /* Kick the chip */
+ outb(CHIP_RESET, CMD_REG);
+ outb(SCSI_NOP, CMD_REG);
+ chip_init();
+
+ return irq;
+}
+#endif IRQ_LEV
+
+static void chip_init()
+{
+ REG1;
+#if USE_DMA
+ outb(0x00, PIO_STATUS);
+#else /* USE_PIO */
+ outb(0x01, PIO_STATUS);
+#endif
+ outb(0x00, PIO_FLAG);
+
+ outb(C4_IMG, CONFIG4); /* REG0; */
+ outb(C3_IMG, CONFIG3);
+ outb(C2_IMG, CONFIG2);
+ outb(C1_IMG, CONFIG1);
+
+ outb(0x05, CLKCONV); /* clock conversion factor */
+ outb(0x9C, SRTIMOUT); /* Selection timeout */
+ outb(0x05, SYNCPRD); /* Synchronous transfer period */
+ outb(SYNC_MODE, SYNCOFF); /* synchronous mode */
+}
+
+void calc_port_addr()
+{
+ /* Control Register Set 0 */
+ TC_LSB = (port_base+0x00);
+ TC_MSB = (port_base+0x01);
+ SCSI_FIFO = (port_base+0x02);
+ CMD_REG = (port_base+0x03);
+ STAT_REG = (port_base+0x04);
+ DEST_ID = (port_base+0x04);
+ INT_REG = (port_base+0x05);
+ SRTIMOUT = (port_base+0x05);
+ SEQ_REG = (port_base+0x06);
+ SYNCPRD = (port_base+0x06);
+ FIFO_FLAGS = (port_base+0x07);
+ SYNCOFF = (port_base+0x07);
+ CONFIG1 = (port_base+0x08);
+ CLKCONV = (port_base+0x09);
+ /* TESTREG = (port_base+0x0A); */
+ CONFIG2 = (port_base+0x0B);
+ CONFIG3 = (port_base+0x0C);
+ CONFIG4 = (port_base+0x0D);
+ TC_HIGH = (port_base+0x0E);
+ /* FIFO_BOTTOM = (port_base+0x0F); */
+
+ /* Control Register Set 1 */
+ /* JUMPER_SENSE = (port_base+0x00);*/
+ /* SRAM_PTR = (port_base+0x01);*/
+ /* SRAM_DATA = (port_base+0x02);*/
+ PIO_FIFO = (port_base+0x04);
+ /* PIO_FIFO1 = (port_base+0x05);*/
+ /* PIO_FIFO2 = (port_base+0x06);*/
+ /* PIO_FIFO3 = (port_base+0x07);*/
+ PIO_STATUS = (port_base+0x08);
+ /* ATA_CMD = (port_base+0x09);*/
+ /* ATA_ERR = (port_base+0x0A);*/
+ PIO_FLAG = (port_base+0x0B);
+ CONFIG5 = (port_base+0x0D);
+ /* SIGNATURE = (port_base+0x0E);*/
+ /* CONFIG6 = (port_base+0x0F);*/
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = NCR53c406a;
+
+#include "scsi_module.c"
+#endif
+
+/*
+ * Overrides for Emacs so that we get a uniform tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/NCR53c406a.h b/linux/src/drivers/scsi/NCR53c406a.h
new file mode 100644
index 00000000..88e45e5e
--- /dev/null
+++ b/linux/src/drivers/scsi/NCR53c406a.h
@@ -0,0 +1,83 @@
+#ifndef _NCR53C406A_H
+#define _NCR53C406A_H
+
+/*
+ * NCR53c406a.h
+ *
+ * Copyright (C) 1994 Normunds Saumanis (normunds@rx.tech.swh.lv)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* NOTE: scatter-gather support only works in PIO mode.
+ * Use SG_NONE if DMA mode is enabled!
+ */
+#define NCR53c406a { \
+ NULL /* next */, \
+ NULL /* usage count */, \
+ &proc_scsi_NCR53c406a /* proc_dir */, \
+ NULL /* proc_info */, \
+ "NCR53c406a" /* name */, \
+ NCR53c406a_detect /* detect */, \
+ NULL /* release */, \
+ NCR53c406a_info /* info */, \
+ NCR53c406a_command /* command */, \
+ NCR53c406a_queue /* queuecommand */, \
+ NCR53c406a_abort /* abort */, \
+ NCR53c406a_reset /* reset */, \
+ NULL /* slave_attach */, \
+ NCR53c406a_biosparm /* biosparm */, \
+ 1 /* can_queue */, \
+ 7 /* SCSI ID of the chip */, \
+ 32 /*SG_ALL*/ /*SG_NONE*/, \
+ 1 /* commands per lun */, \
+ 0 /* number of boards in system */, \
+ 1 /* unchecked_isa_dma */, \
+ ENABLE_CLUSTERING \
+}
+
+extern struct proc_dir_entry proc_scsi_NCR53c406a;
+
+int NCR53c406a_detect(Scsi_Host_Template *);
+const char* NCR53c406a_info(struct Scsi_Host *);
+
+int NCR53c406a_command(Scsi_Cmnd *);
+int NCR53c406a_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int NCR53c406a_abort(Scsi_Cmnd *);
+int NCR53c406a_reset(Scsi_Cmnd *, unsigned int);
+int NCR53c406a_biosparm(Disk *, kdev_t, int []);
+
+#endif /* _NCR53C406A_H */
+
+/*
+ * Overrides for Emacs so that we get a uniform tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
+
diff --git a/linux/src/drivers/scsi/advansys.c b/linux/src/drivers/scsi/advansys.c
new file mode 100644
index 00000000..5b7b4e07
--- /dev/null
+++ b/linux/src/drivers/scsi/advansys.c
@@ -0,0 +1,15547 @@
+/* $Id: advansys.c,v 1.1 1999/04/26 05:54:04 tb Exp $ */
+#define ASC_VERSION "3.1E" /* AdvanSys Driver Version */
+
+/*
+ * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
+ *
+ * Copyright (c) 1995-1998 Advanced System Products, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * There is an AdvanSys Linux WWW page at:
+ * http://www.advansys.com/linux.html
+ *
+ * The latest version of the AdvanSys driver is available at:
+ * ftp://ftp.advansys.com/pub/linux/linux.tgz
+ *
+ * Please send questions, comments, bug reports to:
+ * bobf@advansys.com (Bob Frey)
+ */
+
+/*
+
+ Documentation for the AdvanSys Driver
+
+ A. Linux Kernel Testing
+ B. Adapters Supported by this Driver
+ C. Linux v1.2.X - Directions for Adding the AdvanSys Driver
+ D. Linux v1.3.1 - v1.3.57 - Directions for Adding the AdvanSys Driver
+ E. Linux v1.3.58 and Newer - Upgrading the AdvanSys Driver
+ F. Source Comments
+ G. Driver Compile Time Options and Debugging
+ H. Driver LILO Option
+ I. Release History
+ J. Known Problems or Issues
+ K. Credits
+ L. AdvanSys Contact Information
+
+ A. Linux Kernel Testing
+
+ This driver has been tested in the following Linux kernels: v1.2.13,
+ v1.3.57, v2.0.33, v2.1.77. These kernel versions are major releases
+ of Linux or the latest Linux kernel versions available when this version
+ of the driver was released. The driver should also work in earlier
+ versions of the Linux kernel. Beginning with v1.3.58 the AdvanSys driver
+ is included with all Linux kernels. Please refer to sections C, D, and
+ E for instructions on adding or upgrading the AdvanSys driver.
+
+ B. Adapters Supported by this Driver
+
+ AdvanSys (Advanced System Products, Inc.) manufactures the following
+ RISC-based, Bus-Mastering, Fast (10 Mhz) and Ultra (20 Mhz) Narrow
+ (8-bit transfer) SCSI Host Adapters for the ISA, EISA, VL, and PCI
+ buses and RISC-based, Bus-Mastering, Ultra (20 Mhz) Wide (16-bit
+ transfer) SCSI Host Adapters for the PCI bus.
+
+ The CDB counts below indicate the number of SCSI CDB (Command
+ Descriptor Block) requests that can be stored in the RISC chip
+ cache and board LRAM. A CDB is a single SCSI command. The driver
+ detect routine will display the number of CDBs available for each
+ adapter detected. The number of CDBs used by the driver can be
+ lowered in the BIOS by changing the 'Host Queue Size' adapter setting.
+
+ Connectivity Products:
+ ABP510/5150 - Bus-Master ISA (240 CDB) (Footnote 1)
+ ABP5140 - Bus-Master ISA PnP (16 CDB) (Footnote 1, 3)
+ ABP5142 - Bus-Master ISA PnP with floppy (16 CDB) (Footnote 4)
+ ABP920 - Bus-Master PCI (16 CDB)
+ ABP930 - Bus-Master PCI (16 CDB) (Footnote 5)
+ ABP930U - Bus-Master PCI Ultra (16 CDB)
+ ABP930UA - Bus-Master PCI Ultra (16 CDB)
+ ABP960 - Bus-Master PCI MAC/PC (16 CDB) (Footnote 2)
+ ABP960U - Bus-Master PCI MAC/PC Ultra (16 CDB) (Footnote 2)
+
+ Single Channel Products:
+ ABP542 - Bus-Master ISA with floppy (240 CDB)
+ ABP742 - Bus-Master EISA (240 CDB)
+ ABP842 - Bus-Master VL (240 CDB)
+ ABP940 - Bus-Master PCI (240 CDB)
+ ABP940U - Bus-Master PCI Ultra (240 CDB)
+ ABP970 - Bus-Master PCI MAC/PC (240 CDB)
+ ABP970U - Bus-Master PCI MAC/PC Ultra (240 CDB)
+ ABP940UW - Bus-Master PCI Ultra-Wide (240 CDB)
+
+ Multi Channel Products:
+ ABP752 - Dual Channel Bus-Master EISA (240 CDB Per Channel)
+ ABP852 - Dual Channel Bus-Master VL (240 CDB Per Channel)
+ ABP950 - Dual Channel Bus-Master PCI (240 CDB Per Channel)
+ ABP980 - Four Channel Bus-Master PCI (240 CDB Per Channel)
+ ABP980U - Four Channel Bus-Master PCI Ultra (240 CDB Per Channel)
+
+ Footnotes:
+ 1. This board has been shipped by HP with the 4020i CD-R drive.
+ The board has no BIOS so it cannot control a boot device, but
+ it can control any secondary SCSI device.
+ 2. This board has been sold by Iomega as a Jaz Jet PCI adapter.
+ 3. This board has been sold by SIIG as the i540 SpeedMaster.
+ 4. This board has been sold by SIIG as the i542 SpeedMaster.
+ 5. This board has been sold by SIIG as the Fast SCSI Pro PCI.
+
+ C. Linux v1.2.X - Directions for Adding the AdvanSys Driver
+
+ These directions apply to v1.2.13. For versions that follow v1.2.13.
+ but precede v1.3.57 some of the changes for Linux v1.3.X listed
+ below may need to be modified or included. A patch is available
+ for v1.2.13 from the AdvanSys WWW and FTP sites.
+
+ There are two source files: advansys.h and advansys.c. Copy
+ both of these files to the directory /usr/src/linux/drivers/scsi.
+
+ 1. Add the following line to /usr/src/linux/arch/i386/config.in
+ after "comment 'SCSI low-level drivers'":
+
+ bool 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS y
+
+ 2. Add the following lines to /usr/src/linux/drivers/scsi/hosts.c
+ after "#include "hosts.h"":
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ #include "advansys.h"
+ #endif
+
+ and after "static Scsi_Host_Template builtin_scsi_hosts[] =":
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ ADVANSYS,
+ #endif
+
+ 3. Add the following lines to /usr/src/linux/drivers/scsi/Makefile:
+
+ ifdef CONFIG_SCSI_ADVANSYS
+ SCSI_SRCS := $(SCSI_SRCS) advansys.c
+ SCSI_OBJS := $(SCSI_OBJS) advansys.o
+ else
+ SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) advansys.o
+ endif
+
+ 4. (Optional) If you would like to enable the LILO command line
+ and /etc/lilo.conf 'advansys' option, make the following changes.
+ This option can be used to disable I/O port scanning or to limit
+ I/O port scanning to specific addresses. Refer to the 'Driver
+ LILO Option' section below. Add the following lines to
+ /usr/src/linux/init/main.c in the prototype section:
+
+ extern void advansys_setup(char *str, int *ints);
+
+ and add the following lines to the bootsetups[] array.
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ { "advansys=", advansys_setup },
+ #endif
+
+ 5. If you have the HP 4020i CD-R driver and Linux v1.2.X you should
+ add a fix to the CD-ROM target driver. This fix will allow
+ you to mount CDs with the iso9660 file system. Linux v1.3.X
+ already has this fix. In the file /usr/src/linux/drivers/scsi/sr.c
+ and function get_sectorsize() after the line:
+
+ if(scsi_CDs[i].sector_size == 0) scsi_CDs[i].sector_size = 2048;
+
+ add the following line:
+
+ if(scsi_CDs[i].sector_size == 2340) scsi_CDs[i].sector_size = 2048;
+
+ 6. In the directory /usr/src/linux run 'make config' to configure
+ the AdvanSys driver, then run 'make vmlinux' or 'make zlilo' to
+ make the kernel. If the AdvanSys driver is not configured, then
+ a loadable module can be built by running 'make modules' and
+ 'make modules_install'. Use 'insmod' and 'rmmod' to install
+ and remove advansys.o.
+
+ D. Linux v1.3.1 - v1.3.57 - Directions for Adding the AdvanSys Driver
+
+ These directions apply to v1.3.57. For versions that precede v1.3.57
+ some of these changes may need to be modified or eliminated. A patch
+ is available for v1.3.57 from the AdvanSys WWW and FTP sites.
+ Beginning with v1.3.58 this driver is included with the Linux
+ distribution eliminating the need for making any changes.
+
+ There are two source files: advansys.h and advansys.c. Copy
+ both of these files to the directory /usr/src/linux/drivers/scsi.
+
+ 1. Add the following line to /usr/src/linux/drivers/scsi/Config.in
+ after "comment 'SCSI low-level drivers'":
+
+ dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
+
+ 2. Add the following lines to /usr/src/linux/drivers/scsi/hosts.c
+ after "#include "hosts.h"":
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ #include "advansys.h"
+ #endif
+
+ and after "static Scsi_Host_Template builtin_scsi_hosts[] =":
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ ADVANSYS,
+ #endif
+
+ 3. Add the following lines to /usr/src/linux/drivers/scsi/Makefile:
+
+ ifeq ($(CONFIG_SCSI_ADVANSYS),y)
+ L_OBJS += advansys.o
+ else
+ ifeq ($(CONFIG_SCSI_ADVANSYS),m)
+ M_OBJS += advansys.o
+ endif
+ endif
+
+ 4. Add the following line to /usr/src/linux/include/linux/proc_fs.h
+ in the enum scsi_directory_inos array:
+
+ PROC_SCSI_ADVANSYS,
+
+ 5. (Optional) If you would like to enable the LILO command line
+ and /etc/lilo.conf 'advansys' option, make the following changes.
+ This option can be used to disable I/O port scanning or to limit
+ I/O port scanning to specific addresses. Refer to the 'Driver
+ LILO Option' section below. Add the following lines to
+ /usr/src/linux/init/main.c in the prototype section:
+
+ extern void advansys_setup(char *str, int *ints);
+
+ and add the following lines to the bootsetups[] array.
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ { "advansys=", advansys_setup },
+ #endif
+
+ 6. In the directory /usr/src/linux run 'make config' to configure
+ the AdvanSys driver, then run 'make vmlinux' or 'make zlilo' to
+ make the kernel. If the AdvanSys driver is not configured, then
+ a loadable module can be built by running 'make modules' and
+ 'make modules_install'. Use 'insmod' and 'rmmod' to install
+ and remove advansys.o.
+
+ E. Linux v1.3.58 and Newer - Upgrading the AdvanSys Driver
+
+ To upgrade the AdvanSys driver in a Linux v1.3.58 and newer
+ kernel, first check the version of the current driver. The
+ version is defined by the manifest constant ASC_VERSION at
+ the beginning of advansys.c. The new driver should have a
+ ASC_VERSION value greater than the current version. To install
+ the new driver rename advansys.c and advansys.h in the Linux
+ kernel source tree drivers/scsi directory to different names
+ or save them to a different directory in case you want to revert
+ to the old version of the driver. After the old driver is saved
+ copy the new advansys.c and advansys.h to drivers/scsi, rebuild
+ the kernel, and install the new kernel. No other changes are needed.
+
+ F. Source Comments
+
+ 1. Use tab stops set to 4 for the source files. For vi use 'se tabstops=4'.
+
+ 2. This driver should be maintained in multiple files. But to make
+ it easier to include with Linux and to follow Linux conventions,
+ the whole driver is maintained in the source files advansys.h and
+ advansys.c. In this file logical sections of the driver begin with
+ a comment that contains '---'. The following are the logical sections
+ of the driver below.
+
+ --- Linux Version
+ --- Linux Include Files
+ --- Driver Options
+ --- Debugging Header
+ --- Asc Library Constants and Macros
+ --- Adv Library Constants and Macros
+ --- Driver Constants and Macros
+ --- Driver Structures
+ --- Driver Data
+ --- Driver Function Prototypes
+ --- Linux 'Scsi_Host_Template' and advansys_setup() Functions
+ --- Loadable Driver Support
+ --- Miscellaneous Driver Functions
+ --- Functions Required by the Asc Library
+ --- Functions Required by the Adv Library
+ --- Tracing and Debugging Functions
+ --- Asc Library Functions
+ --- Adv Library Functions
+
+ 3. The string 'XXX' is used to flag code that needs to be re-written
+ or that contains a problem that needs to be addressed.
+
+ 4. I have stripped comments from and reformatted the source for the
+ Asc Library and Adv Library to reduce the size of this file. This
+ source can be found under the following headings. The Asc Library
+ is used to support Narrow Boards. The Adv Library is used to
+ support Wide Boards.
+
+ --- Asc Library Constants and Macros
+ --- Adv Library Constants and Macros
+ --- Asc Library Functions
+ --- Adv Library Functions
+
+ G. Driver Compile Time Options and Debugging
+
+ In this source file the following constants can be defined. They are
+ defined in the source below. Both of these options are enabled by
+ default.
+
+ 1. ADVANSYS_ASSERT - Enable driver assertions (Def: Enabled)
+
+ Enabling this option adds assertion logic statements to the
+ driver. If an assertion fails a message will be displayed to
+ the console, but the system will continue to operate. Any
+ assertions encountered should be reported to the person
+ responsible for the driver. Assertion statements may proactively
+ detect problems with the driver and facilitate fixing these
+ problems. Enabling assertions will add a small overhead to the
+ execution of the driver.
+
+ 2. ADVANSYS_DEBUG - Enable driver debugging (Def: Disabled)
+
+ Enabling this option adds tracing functions to the driver and
+ the ability to set a driver tracing level at boot time. This
+ option will also export symbols not required outside the driver to
+ the kernel name space. This option is very useful for debugging
+ the driver, but it will add to the size of the driver execution
+ image and add overhead to the execution of the driver.
+
+ The amount of debugging output can be controlled with the global
+ variable 'asc_dbglvl'. The higher the number the more output. By
+ default the debug level is 0.
+
+ If the driver is loaded at boot time and the LILO Driver Option
+ is included in the system, the debug level can be changed by
+ specifying a 5th (ASC_NUM_IOPORT_PROBE + 1) I/O Port. The
+ first three hex digits of the pseudo I/O Port must be set to
+ 'deb' and the fourth hex digit specifies the debug level: 0 - F.
+ The following command line will look for an adapter at 0x330
+ and set the debug level to 2.
+
+ linux advansys=0x330,0,0,0,0xdeb2
+
+ If the driver is built as a loadable module this variable can be
+ defined when the driver is loaded. The following insmod command
+ will set the debug level to one.
+
+ insmod advansys.o asc_dbglvl=1
+
+ Debugging Message Levels:
+ 0: Errors Only
+ 1: High-Level Tracing
+ 2-N: Verbose Tracing
+
+ I don't know the approved way for turning on printk()s to the
+ console. Here's a program I use to do this. Debug output is
+ logged in /var/adm/messages.
+
+ main()
+ {
+ syscall(103, 7, 0, 0);
+ }
+
+ I found that increasing LOG_BUF_LEN to 40960 in kernel/printk.c
+ prevents most level 1 debug messages from being lost.
+
+ 3. ADVANSYS_STATS - Enable statistics (Def: Enabled >= v1.3.0)
+
+ Enabling this option adds statistics collection and display
+ through /proc to the driver. The information is useful for
+ monitoring driver and device performance. It will add to the
+ size of the driver execution image and add minor overhead to
+ the execution of the driver.
+
+ Statistics are maintained on a per adapter basis. Driver entry
+ point call counts and transfer size counts are maintained.
+ Statistics are only available for kernels greater than or equal
+ to v1.3.0 with the CONFIG_PROC_FS (/proc) file system configured.
+
+ AdvanSys SCSI adapter files have the following path name format:
+
+ /proc/scsi/advansys/[0-(ASC_NUM_BOARD_SUPPORTED-1)]
+
+ This information can be displayed with cat. For example:
+
+ cat /proc/scsi/advansys/0
+
+ When ADVANSYS_STATS is not defined the AdvanSys /proc files only
+ contain adapter and device configuration information.
+
+ H. Driver LILO Option
+
+ If init/main.c is modified as described in the 'Directions for Adding
+ the AdvanSys Driver to Linux' section (B.4.) above, the driver will
+ recognize the 'advansys' LILO command line and /etc/lilo.conf option.
+ This option can be used to either disable I/O port scanning or to limit
+ scanning to 1 - 4 I/O ports. Regardless of the option setting EISA and
+ PCI boards will still be searched for and detected. This option only
+ affects searching for ISA and VL boards.
+
+ Examples:
+ 1. Eliminate I/O port scanning:
+ boot: linux advansys=
+ or
+ boot: linux advansys=0x0
+ 2. Limit I/O port scanning to one I/O port:
+ boot: linux advansys=0x110
+ 3. Limit I/O port scanning to four I/O ports:
+ boot: linux advansys=0x110,0x210,0x230,0x330
+
+ For a loadable module the same effect can be achieved by setting
+ the 'asc_iopflag' variable and 'asc_ioport' array when loading
+ the driver, e.g.
+
+ insmod advansys.o asc_iopflag=1 asc_ioport=0x110,0x330
+
+ If ADVANSYS_DEBUG is defined a 5th (ASC_NUM_IOPORT_PROBE + 1)
+ I/O Port may be added to specify the driver debug level. Refer to
+ the 'Driver Compile Time Options and Debugging' section above for
+ more information.
+
+ I. Release History
+
+ BETA-1.0 (12/23/95):
+ First Release
+
+ BETA-1.1 (12/28/95):
+ 1. Prevent advansys_detect() from being called twice.
+ 2. Add LILO 0xdeb[0-f] option to set 'asc_dbglvl'.
+
+ 1.2 (1/12/96):
+ 1. Prevent re-entrancy in the interrupt handler which
+ resulted in the driver hanging Linux.
+ 2. Fix problem that prevented ABP-940 cards from being
+ recognized on some PCI motherboards.
+ 3. Add support for the ABP-5140 PnP ISA card.
+ 4. Fix check condition return status.
+ 5. Add conditionally compiled code for Linux v1.3.X.
+
+ 1.3 (2/23/96):
+ 1. Fix problem in advansys_biosparam() that resulted in the
+ wrong drive geometry being returned for drives > 1GB with
+ extended translation enabled.
+ 2. Add additional tracing during device initialization.
+ 3. Change code that only applies to ISA PnP adapter.
+ 4. Eliminate 'make dep' warning.
+ 5. Try to fix problem with handling resets by increasing their
+ timeout value.
+
+ 1.4 (5/8/96):
+ 1. Change definitions to eliminate conflicts with other subsystems.
+ 2. Add versioning code for the shared interrupt changes.
+ 3. Eliminate problem in asc_rmqueue() with iterating after removing
+ a request.
+ 4. Remove reset request loop problem from the "Known Problems or
+ Issues" section. This problem was isolated and fixed in the
+ mid-level SCSI driver.
+
+ 1.5 (8/8/96):
+ 1. Add support for ABP-940U (PCI Ultra) adapter.
+ 2. Add support for IRQ sharing by setting the SA_SHIRQ flag for
+ request_irq and supplying a dev_id pointer to both request_irq()
+ and free_irq().
+ 3. In AscSearchIOPortAddr11() restore a call to check_region() which
+ should be used before I/O port probing.
+ 4. Fix bug in asc_prt_hex() which resulted in the displaying
+ the wrong data.
+ 5. Incorporate miscellaneous Asc Library bug fixes and new microcode.
+ 6. Change driver versioning to be specific to each Linux sub-level.
+ 7. Change statistics gathering to be per adapter instead of global
+ to the driver.
+ 8. Add more information and statistics to the adapter /proc file:
+ /proc/scsi/advansys[0...].
+ 9. Remove 'cmd_per_lun' from the "Known Problems or Issues" list.
+ This problem has been addressed with the SCSI mid-level changes
+ made in v1.3.89. The advansys_select_queue_depths() function
+ was added for the v1.3.89 changes.
+
+ 1.6 (9/10/96):
+ 1. Incorporate miscellaneous Asc Library bug fixes and new microcode.
+
+ 1.7 (9/25/96):
+ 1. Enable clustering and optimize the setting of the maximum number
+ of scatter gather elements for any particular board. Clustering
+ increases CPU utilization, but results in a relatively larger
+ increase in I/O throughput.
+ 2. Improve the performance of the request queuing functions by
+ adding a last pointer to the queue structure.
+ 3. Correct problems with reset and abort request handling that
+ could have hung or crashed Linux.
+ 4. Add more information to the adapter /proc file:
+ /proc/scsi/advansys[0...].
+ 5. Remove the request timeout issue form the driver issues list.
+ 6. Miscellaneous documentation additions and changes.
+
+ 1.8 (10/4/96):
+ 1. Make changes to handle the new v2.1.0 kernel memory mapping
+ in which a kernel virtual address may not be equivalent to its
+ bus or DMA memory address.
+ 2. Change abort and reset request handling to make it yet even
+ more robust.
+ 3. Try to mitigate request starvation by sending ordered requests
+ to heavily loaded, tag queuing enabled devices.
+ 4. Maintain statistics on request response time.
+ 5. Add request response time statistics and other information to
+ the adapter /proc file: /proc/scsi/advansys[0...].
+
+ 1.9 (10/21/96):
+ 1. Add conditionally compiled code (ASC_QUEUE_FLOW_CONTROL) to
+ make use of mid-level SCSI driver device queue depth flow
+ control mechanism. This will eliminate aborts caused by a
+ device being unable to keep up with requests and eliminate
+ repeat busy or QUEUE FULL status returned by a device.
+ 2. Incorporate miscellaneous Asc Library bug fixes.
+ 3. To allow the driver to work in kernels with broken module
+ support set 'cmd_per_lun' if the driver is compiled as a
+ module. This change affects kernels v1.3.89 to present.
+ 4. Remove PCI BIOS address from the driver banner. The PCI BIOS
+ is relocated by the motherboard BIOS and its new address can
+ not be determined by the driver.
+ 5. Add mid-level SCSI queue depth information to the adapter
+ /proc file: /proc/scsi/advansys[0...].
+
+ 2.0 (11/14/96):
+ 1. Change allocation of global structures used for device
+ initialization to guarantee they are in DMA-able memory.
+ Previously when the driver was loaded as a module these
+ structures might not have been in DMA-able memory, causing
+ device initialization to fail.
+
+ 2.1 (12/30/96):
+ 1. In advansys_reset(), if the request is a synchronous reset
+ request, even if the request serial number has changed, then
+ complete the request.
+ 2. Add Asc Library bug fixes including new microcode.
+ 3. Clear inquiry buffer before using it.
+ 4. Correct ifdef typo.
+
+ 2.2 (1/15/97):
+ 1. Add Asc Library bug fixes including new microcode.
+ 2. Add synchronous data transfer rate information to the
+ adapter /proc file: /proc/scsi/advansys[0...].
+ 3. Change ADVANSYS_DEBUG to be disabled by default. This
+ will reduce the size of the driver image, eliminate execution
+ overhead, and remove unneeded symbols from the kernel symbol
+ space that were previously added by the driver.
+ 4. Add new compile-time option ADVANSYS_ASSERT for assertion
+ code that used to be defined within ADVANSYS_DEBUG. This
+ option is enabled by default.
+
+ 2.8 (5/26/97):
+ 1. Change version number to 2.8 to synchronize the Linux driver
+ version numbering with other AdvanSys drivers.
+ 2. Reformat source files without tabs to present the same view
+ of the file to everyone regardless of the editor tab setting
+ being used.
+ 3. Add Asc Library bug fixes.
+
+ 3.1A (1/8/98):
+ 1. Change version number to 3.1 to indicate that support for
+ Ultra-Wide adapters (ABP-940UW) is included in this release.
+ 2. Add Asc Library (Narrow Board) bug fixes.
+ 3. Report an underrun condition with the host status byte set
+ to DID_UNDERRUN. Currently DID_UNDERRUN is defined to 0 which
+ causes the underrun condition to be ignored. When Linux defines
+ its own DID_UNDERRUN the constant defined in this file can be
+ removed.
+ 4. Add patch to AscWaitTixISRDone().
+ 5. Add support for up to 16 different AdvanSys host adapter SCSI
+ channels in one system. This allows four cards with four channels
+ to be used in one system.
+
+ 3.1B (1/9/98):
+ 1. Handle that PCI register base addresses are not always page
+ aligned even though ioremap() requires that the address argument
+ be page aligned.
+
+ 3.1C (1/10/98):
+ 1. Update latest BIOS version checked for from the /proc file.
+ 2. Don't set microcode SDTR variable at initialization. Instead
+ wait until device capabilities have been detected from an Inquiry
+ command.
+
+ 3.1D (1/21/98):
+ 1. Improve performance when the driver is compiled as module by
+ allowing up to 64 scatter-gather elements instead of 8.
+
+ 3.1E (5/1/98):
+ 1. Set time delay in AscWaitTixISRDone() to 1000 ms.
+ 2. Include SMP locking changes.
+ 3. For v2.1.93 and newer kernels use CONFIG_PCI and new PCI BIOS
+ access functions.
+ 4. Update board serial number printing.
+ 5. Try allocating an IRQ both with and without the SA_INTERRUPT
+ flag set to allow IRQ sharing with drivers that do not set
+ the SA_INTERRUPT flag. Also display a more descriptive error
+ message if request_irq() fails.
+ 5. Update to latest Asc and Adv Libraries.
+
+ J. Known Problems or Issues
+
+ 1. Remove conditional constants (ASC_QUEUE_FLOW_CONTROL) around
+ the queue depth flow control code when mid-level SCSI changes
+ are included in Linux.
+
+ K. Credits
+
+ Nathan Hartwell <mage@cdc3.cdc.net> provided the directions and
+ basis for the Linux v1.3.X changes which were included in the
+ 1.2 release.
+
+ Thomas E Zerucha <zerucha@shell.portal.com> pointed out a bug
+ in advansys_biosparam() which was fixed in the 1.3 release.
+
+ Erik Ratcliffe <erik@caldera.com> has done testing of the
+ AdvanSys driver in the Caldera releases.
+
+ Rik van Riel <H.H.vanRiel@fys.ruu.nl> provided a patch to
+ AscWaitTixISRDone() which he found necessary to make the
+ driver work with a SCSI-1 disk.
+
+ Mark Moran <mmoran@mmoran.com> has helped test Ultra-Wide
+ support in the 3.1A driver.
+
+ L. AdvanSys Contact Information
+
+ Mail: Advanced System Products, Inc.
+ 1150 Ringwood Court
+ San Jose, CA 95131
+ Operator: 1-408-383-9400
+ FAX: 1-408-383-9612
+ Tech Support: 1-800-525-7440/1-408-467-2930
+ BBS: 1-408-383-9540 (14400,N,8,1)
+ Interactive FAX: 1-408-383-9753
+ Customer Direct Sales: 1-800-525-7443/1-408-383-5777
+ Tech Support E-Mail: support@advansys.com
+ FTP Site: ftp.advansys.com (login: anonymous)
+ Web Site: http://www.advansys.com
+
+*/
+
+
+/*
+ * --- Linux Version
+ */
+
+/* Convert Linux Version, Patch-level, Sub-level to LINUX_VERSION_CODE. */
+#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif /* LINUX_VERSION_CODE */
+
+
+/*
+ * --- Linux Include Files
+ */
+
+#include <linux/config.h>
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+#ifdef MODULE
+#include <linux/module.h>
+#endif /* MODULE */
+#endif /* version >= v1.3.0 */
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+#include <linux/proc_fs.h>
+#endif /* version >= v1.3.0 */
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,23)
+#include <linux/init.h>
+#endif /* version >= v2.1.23 */
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0)
+#include "../block/blk.h"
+#else /* version >= v1.3.0 */
+#include <linux/blk.h>
+#include <linux/stat.h>
+#endif /* version >= v1.3.0 */
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,95)
+#include <asm/spinlock.h>
+#endif /* version >= 2.1.95 */
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "advansys.h"
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,93)
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif /* CONFIG_PCI */
+#else /* version < v2.1.93 */
+/*
+ * For earlier than v2.1.93 the driver has its own PCI configuration.
+ * If PCI is not needed in a kernel before v2.1.93 this define can be
+ * turned-off to make the driver object smaller.
+ */
+#define ASC_CONFIG_PCI
+#endif /* version < v2.1.93 */
+
+/*
+ * If Linux eventually defines a DID_UNDERRUN, the constant here can be
+ * removed. The current value of zero for DID_UNDERRUN results in underrun
+ * conditions being ignored.
+ */
+#define DID_UNDERRUN 0
+
+
+/*
+ * --- Driver Options
+ */
+
+/* Enable driver assertions. */
+#define ADVANSYS_ASSERT
+
+/* Enable driver tracing. */
+/* #define ADVANSYS_DEBUG */
+
+/*
+ * Because of no /proc to display them, statistics are disabled
+ * for versions prior to v1.3.0.
+ */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0)
+#undef ADVANSYS_STATS /* Disable statistics */
+#else /* version >= v1.3.0 */
+#define ADVANSYS_STATS /* Enable statistics. */
+#endif /* version >= v1.3.0 */
+
+
+/*
+ * --- Debugging Header
+ */
+
+#ifdef ADVANSYS_DEBUG
+#define STATIC
+#else /* ADVANSYS_DEBUG */
+#define STATIC static
+#endif /* ADVANSYS_DEBUG */
+
+
+/*
+ * --- Asc Library Constants and Macros
+ */
+
+#define ASC_LIB_VERSION_MAJOR 1
+#define ASC_LIB_VERSION_MINOR 22
+#define ASC_LIB_SERIAL_NUMBER 113
+
+typedef unsigned char uchar;
+
+#ifndef NULL
+#define NULL (0)
+#endif
+#ifndef TRUE
+#define TRUE (1)
+#endif
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#define REG register
+#define rchar REG __s8
+#define rshort REG __s16
+#define rint REG __s32
+#define rlong REG __s32
+#define ruchar REG __u8
+#define rushort REG __u16
+#define ruint REG __u32
+#define rulong REG __u32
+#define NULLPTR (void *)0
+#define FNULLPTR (void *)0UL
+#define EOF (-1)
+#define EOS '\0'
+#define ERR (-1)
+#define UB_ERR (uchar)(0xFF)
+#define UW_ERR (uint)(0xFFFF)
+#define UL_ERR (ulong)(0xFFFFFFFFUL)
+#define iseven_word(val) ((((uint)val) & (uint)0x0001) == 0)
+#define isodd_word(val) ((((uint)val) & (uint)0x0001) != 0)
+#define toeven_word(val) (((uint)val) & (uint)0xFFFE)
+#define biton(val, bits) (((uint)(val >> bits) & (uint)0x0001) != 0)
+#define bitoff(val, bits) (((uint)(val >> bits) & (uint)0x0001) == 0)
+#define lbiton(val, bits) (((ulong)(val >> bits) & (ulong)0x00000001UL) != 0)
+#define lbitoff(val, bits) (((ulong)(val >> bits) & (ulong)0x00000001UL) == 0)
+#define absh(val) ((val) < 0 ? -(val) : (val))
+#define swapbyte(ch) ((((ch) << 4) | ((ch) >> 4)))
+#ifndef GBYTE
+#define GBYTE (0x40000000UL)
+#endif
+#ifndef MBYTE
+#define MBYTE (0x100000UL)
+#endif
+#ifndef KBYTE
+#define KBYTE (0x400)
+#endif
+#define HI_BYTE(x) (*((__u8 *)(&x)+1))
+#define LO_BYTE(x) (*((__u8 *)&x))
+#define HI_WORD(x) (*((__u16 *)(&x)+1))
+#define LO_WORD(x) (*((__u16 *)&x))
+#ifndef MAKEWORD
+#define MAKEWORD(lo, hi) ((__u16) (((__u16) lo) | ((__u16) hi << 8)))
+#endif
+#ifndef MAKELONG
+#define MAKELONG(lo, hi) ((__u32) (((__u32) lo) | ((__u32) hi << 16)))
+#endif
+#define SwapWords(dWord) ((__u32) ((dWord >> 16) | (dWord << 16)))
+#define SwapBytes(word) ((__u16) ((word >> 8) | (word << 8)))
+#define BigToLittle(dWord) ((__u32) (SwapWords(MAKELONG(SwapBytes(LO_WORD(dWord)), SwapBytes(HI_WORD(dWord))))))
+#define LittleToBig(dWord) BigToLittle(dWord)
+#define AscPCIConfigVendorIDRegister 0x0000
+#define AscPCIConfigDeviceIDRegister 0x0002
+#define AscPCIConfigCommandRegister 0x0004
+#define AscPCIConfigStatusRegister 0x0006
+#define AscPCIConfigRevisionIDRegister 0x0008
+#define AscPCIConfigCacheSize 0x000C
+#define AscPCIConfigLatencyTimer 0x000D
+#define AscPCIIOBaseRegister 0x0010
+#define AscPCICmdRegBits_IOMemBusMaster 0x0007
+#define ASC_PCI_ID2BUS(id) ((id) & 0xFF)
+#define ASC_PCI_ID2DEV(id) (((id) >> 11) & 0x1F)
+#define ASC_PCI_ID2FUNC(id) (((id) >> 8) & 0x7)
+#define ASC_PCI_MKID(bus, dev, func) ((((dev) & 0x1F) << 11) | (((func) & 0x7) << 8) | ((bus) & 0xFF))
+#define ASC_PCI_VENDORID 0x10CD
+#define ASC_PCI_DEVICEID_1200A 0x1100
+#define ASC_PCI_DEVICEID_1200B 0x1200
+#define ASC_PCI_DEVICEID_ULTRA 0x1300
+#define ASC_PCI_REVISION_3150 0x02
+#define ASC_PCI_REVISION_3050 0x03
+
+#define ASC_DVCLIB_CALL_DONE (1)
+#define ASC_DVCLIB_CALL_FAILED (0)
+#define ASC_DVCLIB_CALL_ERROR (-1)
+
+#define PortAddr unsigned short /* port address size */
+#define Ptr2Func ulong
+#define inp(port) inb(port)
+#define inpw(port) inw(port)
+#define inpl(port) inl(port)
+#define outp(port, byte) outb((byte), (port))
+#define outpw(port, word) outw((word), (port))
+#define outpl(port, long) outl((long), (port))
+#define ASC_MAX_SG_QUEUE 7
+#define ASC_MAX_SG_LIST SG_ALL
+
+#define ASC_CS_TYPE unsigned short
+#ifndef asc_ptr_type
+#define asc_ptr_type
+#endif
+
+#ifndef ASC_GET_PTR2FUNC
+#define ASC_GET_PTR2FUNC(fun) (Ptr2Func)(fun)
+#endif
+#define FLIP_BYTE_NIBBLE(x) (((x<<4)& 0xFF) | (x>>4))
+#define ASC_IS_ISA (0x0001)
+#define ASC_IS_ISAPNP (0x0081)
+#define ASC_IS_EISA (0x0002)
+#define ASC_IS_PCI (0x0004)
+#define ASC_IS_PCI_ULTRA (0x0104)
+#define ASC_IS_PCMCIA (0x0008)
+#define ASC_IS_MCA (0x0020)
+#define ASC_IS_VL (0x0040)
+#define ASC_ISA_PNP_PORT_ADDR (0x279)
+#define ASC_ISA_PNP_PORT_WRITE (ASC_ISA_PNP_PORT_ADDR+0x800)
+#define ASC_IS_WIDESCSI_16 (0x0100)
+#define ASC_IS_WIDESCSI_32 (0x0200)
+#define ASC_IS_BIG_ENDIAN (0x8000)
+#define ASC_CHIP_MIN_VER_VL (0x01)
+#define ASC_CHIP_MAX_VER_VL (0x07)
+#define ASC_CHIP_MIN_VER_PCI (0x09)
+#define ASC_CHIP_MAX_VER_PCI (0x0F)
+#define ASC_CHIP_VER_PCI_BIT (0x08)
+#define ASC_CHIP_MIN_VER_ISA (0x11)
+#define ASC_CHIP_MIN_VER_ISA_PNP (0x21)
+#define ASC_CHIP_MAX_VER_ISA (0x27)
+#define ASC_CHIP_VER_ISA_BIT (0x30)
+#define ASC_CHIP_VER_ISAPNP_BIT (0x20)
+#define ASC_CHIP_VER_ASYN_BUG (0x21)
+#define ASC_CHIP_VER_PCI 0x08
+#define ASC_CHIP_VER_PCI_ULTRA_3150 (ASC_CHIP_VER_PCI | 0x02)
+#define ASC_CHIP_VER_PCI_ULTRA_3050 (ASC_CHIP_VER_PCI | 0x03)
+#define ASC_CHIP_MIN_VER_EISA (0x41)
+#define ASC_CHIP_MAX_VER_EISA (0x47)
+#define ASC_CHIP_VER_EISA_BIT (0x40)
+#define ASC_CHIP_LATEST_VER_EISA ((ASC_CHIP_MIN_VER_EISA - 1) + 3)
+#define ASC_MAX_LIB_SUPPORTED_ISA_CHIP_VER 0x21
+#define ASC_MAX_LIB_SUPPORTED_PCI_CHIP_VER 0x0A
+#define ASC_MAX_VL_DMA_ADDR (0x07FFFFFFL)
+#define ASC_MAX_VL_DMA_COUNT (0x07FFFFFFL)
+#define ASC_MAX_PCI_DMA_ADDR (0xFFFFFFFFL)
+#define ASC_MAX_PCI_DMA_COUNT (0xFFFFFFFFL)
+#define ASC_MAX_ISA_DMA_ADDR (0x00FFFFFFL)
+#define ASC_MAX_ISA_DMA_COUNT (0x00FFFFFFL)
+#define ASC_MAX_EISA_DMA_ADDR (0x07FFFFFFL)
+#define ASC_MAX_EISA_DMA_COUNT (0x07FFFFFFL)
+#ifndef inpw_noswap
+#define inpw_noswap(port) inpw(port)
+#endif
+#ifndef outpw_noswap
+#define outpw_noswap(port, data) outpw(port, data)
+#endif
+#define ASC_SCSI_ID_BITS 3
+#define ASC_SCSI_TIX_TYPE uchar
+#define ASC_ALL_DEVICE_BIT_SET 0xFF
+#ifdef ASC_WIDESCSI_16
+#undef ASC_SCSI_ID_BITS
+#define ASC_SCSI_ID_BITS 4
+#define ASC_ALL_DEVICE_BIT_SET 0xFFFF
+#endif
+#ifdef ASC_WIDESCSI_32
+#undef ASC_SCSI_ID_BITS
+#define ASC_SCSI_ID_BITS 5
+#define ASC_ALL_DEVICE_BIT_SET 0xFFFFFFFFL
+#endif
+#if ASC_SCSI_ID_BITS == 3
+#define ASC_SCSI_BIT_ID_TYPE uchar
+#define ASC_MAX_TID 7
+#define ASC_MAX_LUN 7
+#define ASC_SCSI_WIDTH_BIT_SET 0xFF
+#elif ASC_SCSI_ID_BITS == 4
+#define ASC_SCSI_BIT_ID_TYPE ushort
+#define ASC_MAX_TID 15
+#define ASC_MAX_LUN 7
+#define ASC_SCSI_WIDTH_BIT_SET 0xFFFF
+#elif ASC_SCSI_ID_BITS == 5
+#define ASC_SCSI_BIT_ID_TYPE ulong
+#define ASC_MAX_TID 31
+#define ASC_MAX_LUN 7
+#define ASC_SCSI_WIDTH_BIT_SET 0xFFFFFFFF
+#else
+#error ASC_SCSI_ID_BITS definition is wrong
+#endif
+#define ASC_MAX_SENSE_LEN 32
+#define ASC_MIN_SENSE_LEN 14
+#define ASC_MAX_CDB_LEN 12
+#define ASC_SCSI_RESET_HOLD_TIME_US 60
+#define SCSICMD_TestUnitReady 0x00
+#define SCSICMD_Rewind 0x01
+#define SCSICMD_Rezero 0x01
+#define SCSICMD_RequestSense 0x03
+#define SCSICMD_Format 0x04
+#define SCSICMD_FormatUnit 0x04
+#define SCSICMD_Read6 0x08
+#define SCSICMD_Write6 0x0A
+#define SCSICMD_Seek6 0x0B
+#define SCSICMD_Inquiry 0x12
+#define SCSICMD_Verify6 0x13
+#define SCSICMD_ModeSelect6 0x15
+#define SCSICMD_ModeSense6 0x1A
+#define SCSICMD_StartStopUnit 0x1B
+#define SCSICMD_LoadUnloadTape 0x1B
+#define SCSICMD_ReadCapacity 0x25
+#define SCSICMD_Read10 0x28
+#define SCSICMD_Write10 0x2A
+#define SCSICMD_Seek10 0x2B
+#define SCSICMD_Erase10 0x2C
+#define SCSICMD_WriteAndVerify10 0x2E
+#define SCSICMD_Verify10 0x2F
+#define SCSICMD_WriteBuffer 0x3B
+#define SCSICMD_ReadBuffer 0x3C
+#define SCSICMD_ReadLong 0x3E
+#define SCSICMD_WriteLong 0x3F
+#define SCSICMD_ReadTOC 0x43
+#define SCSICMD_ReadHeader 0x44
+#define SCSICMD_ModeSelect10 0x55
+#define SCSICMD_ModeSense10 0x5A
+#define SCSI_TYPE_DASD 0x00
+#define SCSI_TYPE_SASD 0x01
+#define SCSI_TYPE_PRN 0x02
+#define SCSI_TYPE_PROC 0x03
+#define SCSI_TYPE_WORM 0x04
+#define SCSI_TYPE_CDROM 0x05
+#define SCSI_TYPE_SCANNER 0x06
+#define SCSI_TYPE_OPTMEM 0x07
+#define SCSI_TYPE_MED_CHG 0x08
+#define SCSI_TYPE_COMM 0x09
+#define SCSI_TYPE_UNKNOWN 0x1F
+#define SCSI_TYPE_NO_DVC 0xFF
+#define ASC_SCSIDIR_NOCHK 0x00
+#define ASC_SCSIDIR_T2H 0x08
+#define ASC_SCSIDIR_H2T 0x10
+#define ASC_SCSIDIR_NODATA 0x18
+#define SCSI_SENKEY_NO_SENSE 0x00
+#define SCSI_SENKEY_UNDEFINED 0x01
+#define SCSI_SENKEY_NOT_READY 0x02
+#define SCSI_SENKEY_MEDIUM_ERR 0x03
+#define SCSI_SENKEY_HW_ERR 0x04
+#define SCSI_SENKEY_ILLEGAL 0x05
+#define SCSI_SENKEY_ATTENTION 0x06
+#define SCSI_SENKEY_PROTECTED 0x07
+#define SCSI_SENKEY_BLANK 0x08
+#define SCSI_SENKEY_V_UNIQUE 0x09
+#define SCSI_SENKEY_CPY_ABORT 0x0A
+#define SCSI_SENKEY_ABORT 0x0B
+#define SCSI_SENKEY_EQUAL 0x0C
+#define SCSI_SENKEY_VOL_OVERFLOW 0x0D
+#define SCSI_SENKEY_MISCOMP 0x0E
+#define SCSI_SENKEY_RESERVED 0x0F
+#define SCSI_ASC_NOMEDIA 0x3A
+#define ASC_SRB_HOST(x) ((uchar)((uchar)(x) >> 4))
+#define ASC_SRB_TID(x) ((uchar)((uchar)(x) & (uchar)0x0F))
+#define ASC_SRB_LUN(x) ((uchar)((uint)(x) >> 13))
+#define PUT_CDB1(x) ((uchar)((uint)(x) >> 8))
+#define SS_GOOD 0x00
+#define SS_CHK_CONDITION 0x02
+#define SS_CONDITION_MET 0x04
+#define SS_TARGET_BUSY 0x08
+#define SS_INTERMID 0x10
+#define SS_INTERMID_COND_MET 0x14
+#define SS_RSERV_CONFLICT 0x18
+#define SS_CMD_TERMINATED 0x22
+#define SS_QUEUE_FULL 0x28
+#define MS_CMD_DONE 0x00
+#define MS_EXTEND 0x01
+#define MS_SDTR_LEN 0x03
+#define MS_SDTR_CODE 0x01
+#define MS_WDTR_LEN 0x02
+#define MS_WDTR_CODE 0x03
+#define MS_MDP_LEN 0x05
+#define MS_MDP_CODE 0x00
+#define M1_SAVE_DATA_PTR 0x02
+#define M1_RESTORE_PTRS 0x03
+#define M1_DISCONNECT 0x04
+#define M1_INIT_DETECTED_ERR 0x05
+#define M1_ABORT 0x06
+#define M1_MSG_REJECT 0x07
+#define M1_NO_OP 0x08
+#define M1_MSG_PARITY_ERR 0x09
+#define M1_LINK_CMD_DONE 0x0A
+#define M1_LINK_CMD_DONE_WFLAG 0x0B
+#define M1_BUS_DVC_RESET 0x0C
+#define M1_ABORT_TAG 0x0D
+#define M1_CLR_QUEUE 0x0E
+#define M1_INIT_RECOVERY 0x0F
+#define M1_RELEASE_RECOVERY 0x10
+#define M1_KILL_IO_PROC 0x11
+#define M2_QTAG_MSG_SIMPLE 0x20
+#define M2_QTAG_MSG_HEAD 0x21
+#define M2_QTAG_MSG_ORDERED 0x22
+#define M2_IGNORE_WIDE_RESIDUE 0x23
+
+typedef struct {
+ uchar peri_dvc_type:5;
+ uchar peri_qualifier:3;
+} ASC_SCSI_INQ0;
+
+typedef struct {
+ uchar dvc_type_modifier:7;
+ uchar rmb:1;
+} ASC_SCSI_INQ1;
+
+typedef struct {
+ uchar ansi_apr_ver:3;
+ uchar ecma_ver:3;
+ uchar iso_ver:2;
+} ASC_SCSI_INQ2;
+
+typedef struct {
+ uchar rsp_data_fmt:4;
+ uchar res:2;
+ uchar TemIOP:1;
+ uchar aenc:1;
+} ASC_SCSI_INQ3;
+
+typedef struct {
+ uchar StfRe:1;
+ uchar CmdQue:1;
+ uchar Reserved:1;
+ uchar Linked:1;
+ uchar Sync:1;
+ uchar WBus16:1;
+ uchar WBus32:1;
+ uchar RelAdr:1;
+} ASC_SCSI_INQ7;
+
+typedef struct {
+ ASC_SCSI_INQ0 byte0;
+ ASC_SCSI_INQ1 byte1;
+ ASC_SCSI_INQ2 byte2;
+ ASC_SCSI_INQ3 byte3;
+ uchar add_len;
+ uchar res1;
+ uchar res2;
+ ASC_SCSI_INQ7 byte7;
+ uchar vendor_id[8];
+ uchar product_id[16];
+ uchar product_rev_level[4];
+} ASC_SCSI_INQUIRY;
+
+typedef struct asc_req_sense {
+ uchar err_code:7;
+ uchar info_valid:1;
+ uchar segment_no;
+ uchar sense_key:4;
+ uchar reserved_bit:1;
+ uchar sense_ILI:1;
+ uchar sense_EOM:1;
+ uchar file_mark:1;
+ uchar info1[4];
+ uchar add_sense_len;
+ uchar cmd_sp_info[4];
+ uchar asc;
+ uchar ascq;
+ uchar fruc;
+ uchar sks_byte0:7;
+ uchar sks_valid:1;
+ uchar sks_bytes[2];
+ uchar notused[2];
+ uchar ex_sense_code;
+ uchar info2[4];
+} ASC_REQ_SENSE;
+
+#define ASC_SG_LIST_PER_Q 7
+#define QS_FREE 0x00
+#define QS_READY 0x01
+#define QS_DISC1 0x02
+#define QS_DISC2 0x04
+#define QS_BUSY 0x08
+#define QS_ABORTED 0x40
+#define QS_DONE 0x80
+#define QC_NO_CALLBACK 0x01
+#define QC_SG_SWAP_QUEUE 0x02
+#define QC_SG_HEAD 0x04
+#define QC_DATA_IN 0x08
+#define QC_DATA_OUT 0x10
+#define QC_URGENT 0x20
+#define QC_MSG_OUT 0x40
+#define QC_REQ_SENSE 0x80
+#define QCSG_SG_XFER_LIST 0x02
+#define QCSG_SG_XFER_MORE 0x04
+#define QCSG_SG_XFER_END 0x08
+#define QD_IN_PROGRESS 0x00
+#define QD_NO_ERROR 0x01
+#define QD_ABORTED_BY_HOST 0x02
+#define QD_WITH_ERROR 0x04
+#define QD_INVALID_REQUEST 0x80
+#define QD_INVALID_HOST_NUM 0x81
+#define QD_INVALID_DEVICE 0x82
+#define QD_ERR_INTERNAL 0xFF
+#define QHSTA_NO_ERROR 0x00
+#define QHSTA_M_SEL_TIMEOUT 0x11
+#define QHSTA_M_DATA_OVER_RUN 0x12
+#define QHSTA_M_DATA_UNDER_RUN 0x12
+#define QHSTA_M_UNEXPECTED_BUS_FREE 0x13
+#define QHSTA_M_BAD_BUS_PHASE_SEQ 0x14
+#define QHSTA_D_QDONE_SG_LIST_CORRUPTED 0x21
+#define QHSTA_D_ASC_DVC_ERROR_CODE_SET 0x22
+#define QHSTA_D_HOST_ABORT_FAILED 0x23
+#define QHSTA_D_EXE_SCSI_Q_FAILED 0x24
+#define QHSTA_D_EXE_SCSI_Q_BUSY_TIMEOUT 0x25
+#define QHSTA_D_ASPI_NO_BUF_POOL 0x26
+#define QHSTA_M_WTM_TIMEOUT 0x41
+#define QHSTA_M_BAD_CMPL_STATUS_IN 0x42
+#define QHSTA_M_NO_AUTO_REQ_SENSE 0x43
+#define QHSTA_M_AUTO_REQ_SENSE_FAIL 0x44
+#define QHSTA_M_TARGET_STATUS_BUSY 0x45
+#define QHSTA_M_BAD_TAG_CODE 0x46
+#define QHSTA_M_BAD_QUEUE_FULL_OR_BUSY 0x47
+#define QHSTA_M_HUNG_REQ_SCSI_BUS_RESET 0x48
+#define QHSTA_D_LRAM_CMP_ERROR 0x81
+#define QHSTA_M_MICRO_CODE_ERROR_HALT 0xA1
+#define ASC_FLAG_SCSIQ_REQ 0x01
+#define ASC_FLAG_BIOS_SCSIQ_REQ 0x02
+#define ASC_FLAG_BIOS_ASYNC_IO 0x04
+#define ASC_FLAG_SRB_LINEAR_ADDR 0x08
+#define ASC_FLAG_WIN16 0x10
+#define ASC_FLAG_WIN32 0x20
+#define ASC_FLAG_ISA_OVER_16MB 0x40
+#define ASC_FLAG_DOS_VM_CALLBACK 0x80
+#define ASC_TAG_FLAG_EXTRA_BYTES 0x10
+#define ASC_TAG_FLAG_DISABLE_DISCONNECT 0x04
+#define ASC_TAG_FLAG_DISABLE_ASYN_USE_SYN_FIX 0x08
+#define ASC_TAG_FLAG_DISABLE_CHK_COND_INT_HOST 0x40
+#define ASC_SCSIQ_CPY_BEG 4
+#define ASC_SCSIQ_SGHD_CPY_BEG 2
+#define ASC_SCSIQ_B_FWD 0
+#define ASC_SCSIQ_B_BWD 1
+#define ASC_SCSIQ_B_STATUS 2
+#define ASC_SCSIQ_B_QNO 3
+#define ASC_SCSIQ_B_CNTL 4
+#define ASC_SCSIQ_B_SG_QUEUE_CNT 5
+#define ASC_SCSIQ_D_DATA_ADDR 8
+#define ASC_SCSIQ_D_DATA_CNT 12
+#define ASC_SCSIQ_B_SENSE_LEN 20
+#define ASC_SCSIQ_DONE_INFO_BEG 22
+#define ASC_SCSIQ_D_SRBPTR 22
+#define ASC_SCSIQ_B_TARGET_IX 26
+#define ASC_SCSIQ_B_CDB_LEN 28
+#define ASC_SCSIQ_B_TAG_CODE 29
+#define ASC_SCSIQ_W_VM_ID 30
+#define ASC_SCSIQ_DONE_STATUS 32
+#define ASC_SCSIQ_HOST_STATUS 33
+#define ASC_SCSIQ_SCSI_STATUS 34
+#define ASC_SCSIQ_CDB_BEG 36
+#define ASC_SCSIQ_DW_REMAIN_XFER_ADDR 56
+#define ASC_SCSIQ_DW_REMAIN_XFER_CNT 60
+#define ASC_SCSIQ_B_SG_WK_QP 49
+#define ASC_SCSIQ_B_SG_WK_IX 50
+#define ASC_SCSIQ_W_REQ_COUNT 52
+#define ASC_SCSIQ_B_LIST_CNT 6
+#define ASC_SCSIQ_B_CUR_LIST_CNT 7
+#define ASC_SGQ_B_SG_CNTL 4
+#define ASC_SGQ_B_SG_HEAD_QP 5
+#define ASC_SGQ_B_SG_LIST_CNT 6
+#define ASC_SGQ_B_SG_CUR_LIST_CNT 7
+#define ASC_SGQ_LIST_BEG 8
+#define ASC_DEF_SCSI1_QNG 4
+#define ASC_MAX_SCSI1_QNG 4
+#define ASC_DEF_SCSI2_QNG 16
+#define ASC_MAX_SCSI2_QNG 32
+#define ASC_TAG_CODE_MASK 0x23
+#define ASC_STOP_REQ_RISC_STOP 0x01
+#define ASC_STOP_ACK_RISC_STOP 0x03
+#define ASC_STOP_CLEAN_UP_BUSY_Q 0x10
+#define ASC_STOP_CLEAN_UP_DISC_Q 0x20
+#define ASC_STOP_HOST_REQ_RISC_HALT 0x40
+#define ASC_TIDLUN_TO_IX(tid, lun) (ASC_SCSI_TIX_TYPE)((tid) + ((lun)<<ASC_SCSI_ID_BITS))
+#define ASC_TID_TO_TARGET_ID(tid) (ASC_SCSI_BIT_ID_TYPE)(0x01 << (tid))
+#define ASC_TIX_TO_TARGET_ID(tix) (0x01 << ((tix) & ASC_MAX_TID))
+#define ASC_TIX_TO_TID(tix) ((tix) & ASC_MAX_TID)
+#define ASC_TID_TO_TIX(tid) ((tid) & ASC_MAX_TID)
+#define ASC_TIX_TO_LUN(tix) (((tix) >> ASC_SCSI_ID_BITS) & ASC_MAX_LUN)
+#define ASC_QNO_TO_QADDR(q_no) ((ASC_QADR_BEG)+((int)(q_no) << 6))
+
+typedef struct asc_scisq_1 {
+ uchar status;
+ uchar q_no;
+ uchar cntl;
+ uchar sg_queue_cnt;
+ uchar target_id;
+ uchar target_lun;
+ ulong data_addr;
+ ulong data_cnt;
+ ulong sense_addr;
+ uchar sense_len;
+ uchar extra_bytes;
+} ASC_SCSIQ_1;
+
+typedef struct asc_scisq_2 {
+ ulong srb_ptr;
+ uchar target_ix;
+ uchar flag;
+ uchar cdb_len;
+ uchar tag_code;
+ ushort vm_id;
+} ASC_SCSIQ_2;
+
+typedef struct asc_scsiq_3 {
+ uchar done_stat;
+ uchar host_stat;
+ uchar scsi_stat;
+ uchar scsi_msg;
+} ASC_SCSIQ_3;
+
+typedef struct asc_scsiq_4 {
+ uchar cdb[ASC_MAX_CDB_LEN];
+ uchar y_first_sg_list_qp;
+ uchar y_working_sg_qp;
+ uchar y_working_sg_ix;
+ uchar y_res;
+ ushort x_req_count;
+ ushort x_reconnect_rtn;
+ ulong x_saved_data_addr;
+ ulong x_saved_data_cnt;
+} ASC_SCSIQ_4;
+
+typedef struct asc_q_done_info {
+ ASC_SCSIQ_2 d2;
+ ASC_SCSIQ_3 d3;
+ uchar q_status;
+ uchar q_no;
+ uchar cntl;
+ uchar sense_len;
+ uchar extra_bytes;
+ uchar res;
+ ulong remain_bytes;
+} ASC_QDONE_INFO;
+
+typedef struct asc_sg_list {
+ ulong addr;
+ ulong bytes;
+} ASC_SG_LIST;
+
+typedef struct asc_sg_head {
+ ushort entry_cnt;
+ ushort queue_cnt;
+ ushort entry_to_copy;
+ ushort res;
+ ASC_SG_LIST sg_list[ASC_MAX_SG_LIST];
+} ASC_SG_HEAD;
+
+#define ASC_MIN_SG_LIST 2
+
+typedef struct asc_min_sg_head {
+ ushort entry_cnt;
+ ushort queue_cnt;
+ ushort entry_to_copy;
+ ushort res;
+ ASC_SG_LIST sg_list[ASC_MIN_SG_LIST];
+} ASC_MIN_SG_HEAD;
+
+#define QCX_SORT (0x0001)
+#define QCX_COALEASE (0x0002)
+
+typedef struct asc_scsi_q {
+ ASC_SCSIQ_1 q1;
+ ASC_SCSIQ_2 q2;
+ uchar *cdbptr;
+ ASC_SG_HEAD *sg_head;
+} ASC_SCSI_Q;
+
+typedef struct asc_scsi_req_q {
+ ASC_SCSIQ_1 r1;
+ ASC_SCSIQ_2 r2;
+ uchar *cdbptr;
+ ASC_SG_HEAD *sg_head;
+ uchar *sense_ptr;
+ ASC_SCSIQ_3 r3;
+ uchar cdb[ASC_MAX_CDB_LEN];
+ uchar sense[ASC_MIN_SENSE_LEN];
+} ASC_SCSI_REQ_Q;
+
+typedef struct asc_scsi_bios_req_q {
+ ASC_SCSIQ_1 r1;
+ ASC_SCSIQ_2 r2;
+ uchar *cdbptr;
+ ASC_SG_HEAD *sg_head;
+ uchar *sense_ptr;
+ ASC_SCSIQ_3 r3;
+ uchar cdb[ASC_MAX_CDB_LEN];
+ uchar sense[ASC_MIN_SENSE_LEN];
+} ASC_SCSI_BIOS_REQ_Q;
+
+typedef struct asc_risc_q {
+ uchar fwd;
+ uchar bwd;
+ ASC_SCSIQ_1 i1;
+ ASC_SCSIQ_2 i2;
+ ASC_SCSIQ_3 i3;
+ ASC_SCSIQ_4 i4;
+} ASC_RISC_Q;
+
+typedef struct asc_sg_list_q {
+ uchar seq_no;
+ uchar q_no;
+ uchar cntl;
+ uchar sg_head_qp;
+ uchar sg_list_cnt;
+ uchar sg_cur_list_cnt;
+} ASC_SG_LIST_Q;
+
+typedef struct asc_risc_sg_list_q {
+ uchar fwd;
+ uchar bwd;
+ ASC_SG_LIST_Q sg;
+ ASC_SG_LIST sg_list[7];
+} ASC_RISC_SG_LIST_Q;
+
+#define ASC_EXE_SCSI_IO_MAX_IDLE_LOOP 0x1000000UL
+#define ASC_EXE_SCSI_IO_MAX_WAIT_LOOP 1024
+#define ASCQ_ERR_NO_ERROR 0
+#define ASCQ_ERR_IO_NOT_FOUND 1
+#define ASCQ_ERR_LOCAL_MEM 2
+#define ASCQ_ERR_CHKSUM 3
+#define ASCQ_ERR_START_CHIP 4
+#define ASCQ_ERR_INT_TARGET_ID 5
+#define ASCQ_ERR_INT_LOCAL_MEM 6
+#define ASCQ_ERR_HALT_RISC 7
+#define ASCQ_ERR_GET_ASPI_ENTRY 8
+#define ASCQ_ERR_CLOSE_ASPI 9
+#define ASCQ_ERR_HOST_INQUIRY 0x0A
+#define ASCQ_ERR_SAVED_SRB_BAD 0x0B
+#define ASCQ_ERR_QCNTL_SG_LIST 0x0C
+#define ASCQ_ERR_Q_STATUS 0x0D
+#define ASCQ_ERR_WR_SCSIQ 0x0E
+#define ASCQ_ERR_PC_ADDR 0x0F
+#define ASCQ_ERR_SYN_OFFSET 0x10
+#define ASCQ_ERR_SYN_XFER_TIME 0x11
+#define ASCQ_ERR_LOCK_DMA 0x12
+#define ASCQ_ERR_UNLOCK_DMA 0x13
+#define ASCQ_ERR_VDS_CHK_INSTALL 0x14
+#define ASCQ_ERR_MICRO_CODE_HALT 0x15
+#define ASCQ_ERR_SET_LRAM_ADDR 0x16
+#define ASCQ_ERR_CUR_QNG 0x17
+#define ASCQ_ERR_SG_Q_LINKS 0x18
+#define ASCQ_ERR_SCSIQ_PTR 0x19
+#define ASCQ_ERR_ISR_RE_ENTRY 0x1A
+#define ASCQ_ERR_CRITICAL_RE_ENTRY 0x1B
+#define ASCQ_ERR_ISR_ON_CRITICAL 0x1C
+#define ASCQ_ERR_SG_LIST_ODD_ADDRESS 0x1D
+#define ASCQ_ERR_XFER_ADDRESS_TOO_BIG 0x1E
+#define ASCQ_ERR_SCSIQ_NULL_PTR 0x1F
+#define ASCQ_ERR_SCSIQ_BAD_NEXT_PTR 0x20
+#define ASCQ_ERR_GET_NUM_OF_FREE_Q 0x21
+#define ASCQ_ERR_SEND_SCSI_Q 0x22
+#define ASCQ_ERR_HOST_REQ_RISC_HALT 0x23
+#define ASCQ_ERR_RESET_SDTR 0x24
+#define ASC_WARN_NO_ERROR 0x0000
+#define ASC_WARN_IO_PORT_ROTATE 0x0001
+#define ASC_WARN_EEPROM_CHKSUM 0x0002
+#define ASC_WARN_IRQ_MODIFIED 0x0004
+#define ASC_WARN_AUTO_CONFIG 0x0008
+#define ASC_WARN_CMD_QNG_CONFLICT 0x0010
+#define ASC_WARN_EEPROM_RECOVER 0x0020
+#define ASC_WARN_CFG_MSW_RECOVER 0x0040
+#define ASC_WARN_SET_PCI_CONFIG_SPACE 0x0080
+#define ASC_IERR_WRITE_EEPROM 0x0001
+#define ASC_IERR_MCODE_CHKSUM 0x0002
+#define ASC_IERR_SET_PC_ADDR 0x0004
+#define ASC_IERR_START_STOP_CHIP 0x0008
+#define ASC_IERR_IRQ_NO 0x0010
+#define ASC_IERR_SET_IRQ_NO 0x0020
+#define ASC_IERR_CHIP_VERSION 0x0040
+#define ASC_IERR_SET_SCSI_ID 0x0080
+#define ASC_IERR_GET_PHY_ADDR 0x0100
+#define ASC_IERR_BAD_SIGNATURE 0x0200
+#define ASC_IERR_NO_BUS_TYPE 0x0400
+#define ASC_IERR_SCAM 0x0800
+#define ASC_IERR_SET_SDTR 0x1000
+#define ASC_IERR_RW_LRAM 0x8000
+#define ASC_DEF_IRQ_NO 10
+#define ASC_MAX_IRQ_NO 15
+#define ASC_MIN_IRQ_NO 10
+#define ASC_MIN_REMAIN_Q (0x02)
+#define ASC_DEF_MAX_TOTAL_QNG (0xF0)
+#define ASC_MIN_TAG_Q_PER_DVC (0x04)
+#define ASC_DEF_TAG_Q_PER_DVC (0x04)
+#define ASC_MIN_FREE_Q ASC_MIN_REMAIN_Q
+#define ASC_MIN_TOTAL_QNG ((ASC_MAX_SG_QUEUE)+(ASC_MIN_FREE_Q))
+#define ASC_MAX_TOTAL_QNG 240
+#define ASC_MAX_PCI_ULTRA_INRAM_TOTAL_QNG 16
+#define ASC_MAX_PCI_ULTRA_INRAM_TAG_QNG 8
+#define ASC_MAX_PCI_INRAM_TOTAL_QNG 20
+#define ASC_MAX_INRAM_TAG_QNG 16
+#define ASC_IOADR_TABLE_MAX_IX 11
+#define ASC_IOADR_GAP 0x10
+#define ASC_SEARCH_IOP_GAP 0x10
+#define ASC_MIN_IOP_ADDR (PortAddr)0x0100
+#define ASC_MAX_IOP_ADDR (PortAddr)0x3F0
+#define ASC_IOADR_1 (PortAddr)0x0110
+#define ASC_IOADR_2 (PortAddr)0x0130
+#define ASC_IOADR_3 (PortAddr)0x0150
+#define ASC_IOADR_4 (PortAddr)0x0190
+#define ASC_IOADR_5 (PortAddr)0x0210
+#define ASC_IOADR_6 (PortAddr)0x0230
+#define ASC_IOADR_7 (PortAddr)0x0250
+#define ASC_IOADR_8 (PortAddr)0x0330
+#define ASC_IOADR_DEF ASC_IOADR_8
+#define ASC_LIB_SCSIQ_WK_SP 256
+#define ASC_MAX_SYN_XFER_NO 16
+#define ASC_SYN_MAX_OFFSET 0x0F
+#define ASC_DEF_SDTR_OFFSET 0x0F
+#define ASC_DEF_SDTR_INDEX 0x00
+#define ASC_SDTR_ULTRA_PCI_10MB_INDEX 0x02
+#define SYN_XFER_NS_0 25
+#define SYN_XFER_NS_1 30
+#define SYN_XFER_NS_2 35
+#define SYN_XFER_NS_3 40
+#define SYN_XFER_NS_4 50
+#define SYN_XFER_NS_5 60
+#define SYN_XFER_NS_6 70
+#define SYN_XFER_NS_7 85
+#define SYN_ULTRA_XFER_NS_0 12
+#define SYN_ULTRA_XFER_NS_1 19
+#define SYN_ULTRA_XFER_NS_2 25
+#define SYN_ULTRA_XFER_NS_3 32
+#define SYN_ULTRA_XFER_NS_4 38
+#define SYN_ULTRA_XFER_NS_5 44
+#define SYN_ULTRA_XFER_NS_6 50
+#define SYN_ULTRA_XFER_NS_7 57
+#define SYN_ULTRA_XFER_NS_8 63
+#define SYN_ULTRA_XFER_NS_9 69
+#define SYN_ULTRA_XFER_NS_10 75
+#define SYN_ULTRA_XFER_NS_11 82
+#define SYN_ULTRA_XFER_NS_12 88
+#define SYN_ULTRA_XFER_NS_13 94
+#define SYN_ULTRA_XFER_NS_14 100
+#define SYN_ULTRA_XFER_NS_15 107
+
+typedef struct ext_msg {
+ uchar msg_type;
+ uchar msg_len;
+ uchar msg_req;
+ union {
+ struct {
+ uchar sdtr_xfer_period;
+ uchar sdtr_req_ack_offset;
+ } sdtr;
+ struct {
+ uchar wdtr_width;
+ } wdtr;
+ struct {
+ uchar mdp_b3;
+ uchar mdp_b2;
+ uchar mdp_b1;
+ uchar mdp_b0;
+ } mdp;
+ } u_ext_msg;
+ uchar res;
+} EXT_MSG;
+
+#define xfer_period u_ext_msg.sdtr.sdtr_xfer_period
+#define req_ack_offset u_ext_msg.sdtr.sdtr_req_ack_offset
+#define wdtr_width u_ext_msg.wdtr.wdtr_width
+#define mdp_b3 u_ext_msg.mdp_b3
+#define mdp_b2 u_ext_msg.mdp_b2
+#define mdp_b1 u_ext_msg.mdp_b1
+#define mdp_b0 u_ext_msg.mdp_b0
+
+typedef struct asc_dvc_cfg {
+ ASC_SCSI_BIT_ID_TYPE can_tagged_qng;
+ ASC_SCSI_BIT_ID_TYPE cmd_qng_enabled;
+ ASC_SCSI_BIT_ID_TYPE disc_enable;
+ ASC_SCSI_BIT_ID_TYPE sdtr_enable;
+ uchar chip_scsi_id:4;
+ uchar isa_dma_speed:4;
+ uchar isa_dma_channel;
+ uchar chip_version;
+ ushort pci_device_id;
+ ushort lib_serial_no;
+ ushort lib_version;
+ ushort mcode_date;
+ ushort mcode_version;
+ uchar max_tag_qng[ASC_MAX_TID + 1];
+ uchar *overrun_buf;
+ uchar sdtr_period_offset[ASC_MAX_TID + 1];
+ ushort pci_slot_info;
+ uchar adapter_info[6];
+} ASC_DVC_CFG;
+
+#define ASC_DEF_DVC_CNTL 0xFFFF
+#define ASC_DEF_CHIP_SCSI_ID 7
+#define ASC_DEF_ISA_DMA_SPEED 4
+#define ASC_INIT_STATE_NULL 0x0000
+#define ASC_INIT_STATE_BEG_GET_CFG 0x0001
+#define ASC_INIT_STATE_END_GET_CFG 0x0002
+#define ASC_INIT_STATE_BEG_SET_CFG 0x0004
+#define ASC_INIT_STATE_END_SET_CFG 0x0008
+#define ASC_INIT_STATE_BEG_LOAD_MC 0x0010
+#define ASC_INIT_STATE_END_LOAD_MC 0x0020
+#define ASC_INIT_STATE_BEG_INQUIRY 0x0040
+#define ASC_INIT_STATE_END_INQUIRY 0x0080
+#define ASC_INIT_RESET_SCSI_DONE 0x0100
+#define ASC_INIT_STATE_WITHOUT_EEP 0x8000
+#define ASC_PCI_DEVICE_ID_REV_A 0x1100
+#define ASC_PCI_DEVICE_ID_REV_B 0x1200
+#define ASC_BUG_FIX_IF_NOT_DWB 0x0001
+#define ASC_BUG_FIX_ASYN_USE_SYN 0x0002
+#define ASYN_SDTR_DATA_FIX_PCI_REV_AB 0x41
+#define ASC_MIN_TAGGED_CMD 7
+#define ASC_MAX_SCSI_RESET_WAIT 30
+
+typedef struct asc_dvc_var {
+ PortAddr iop_base;
+ ushort err_code;
+ ushort dvc_cntl;
+ ushort bug_fix_cntl;
+ ushort bus_type;
+ Ptr2Func isr_callback;
+ Ptr2Func exe_callback;
+ ASC_SCSI_BIT_ID_TYPE init_sdtr;
+ ASC_SCSI_BIT_ID_TYPE sdtr_done;
+ ASC_SCSI_BIT_ID_TYPE use_tagged_qng;
+ ASC_SCSI_BIT_ID_TYPE unit_not_ready;
+ ASC_SCSI_BIT_ID_TYPE queue_full_or_busy;
+ ASC_SCSI_BIT_ID_TYPE start_motor;
+ uchar scsi_reset_wait;
+ uchar chip_no;
+ char is_in_int;
+ uchar max_total_qng;
+ uchar cur_total_qng;
+ uchar in_critical_cnt;
+ uchar irq_no;
+ uchar last_q_shortage;
+ ushort init_state;
+ uchar cur_dvc_qng[ASC_MAX_TID + 1];
+ uchar max_dvc_qng[ASC_MAX_TID + 1];
+ ASC_SCSI_Q *scsiq_busy_head[ASC_MAX_TID + 1];
+ ASC_SCSI_Q *scsiq_busy_tail[ASC_MAX_TID + 1];
+ uchar sdtr_period_tbl[ASC_MAX_SYN_XFER_NO];
+ ASC_DVC_CFG *cfg;
+ Ptr2Func saved_ptr2func;
+ ASC_SCSI_BIT_ID_TYPE pci_fix_asyn_xfer_always;
+ char redo_scam;
+ ushort res2;
+ uchar dos_int13_table[ASC_MAX_TID + 1];
+ ulong max_dma_count;
+ ASC_SCSI_BIT_ID_TYPE no_scam;
+ ASC_SCSI_BIT_ID_TYPE pci_fix_asyn_xfer;
+ uchar max_sdtr_index;
+ uchar host_init_sdtr_index;
+ ulong drv_ptr;
+ ulong uc_break;
+ ulong res7;
+ ulong res8;
+} ASC_DVC_VAR;
+
+typedef int (* ASC_ISR_CALLBACK) (ASC_DVC_VAR asc_ptr_type *, ASC_QDONE_INFO *);
+typedef int (* ASC_EXE_CALLBACK) (ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_Q *);
+
+typedef struct asc_dvc_inq_info {
+ uchar type[ASC_MAX_TID + 1][ASC_MAX_LUN + 1];
+} ASC_DVC_INQ_INFO;
+
+typedef struct asc_cap_info {
+ ulong lba;
+ ulong blk_size;
+} ASC_CAP_INFO;
+
+typedef struct asc_cap_info_array {
+ ASC_CAP_INFO cap_info[ASC_MAX_TID + 1][ASC_MAX_LUN + 1];
+} ASC_CAP_INFO_ARRAY;
+
+#define ASC_MCNTL_NO_SEL_TIMEOUT (ushort)0x0001
+#define ASC_MCNTL_NULL_TARGET (ushort)0x0002
+#define ASC_CNTL_INITIATOR (ushort)0x0001
+#define ASC_CNTL_BIOS_GT_1GB (ushort)0x0002
+#define ASC_CNTL_BIOS_GT_2_DISK (ushort)0x0004
+#define ASC_CNTL_BIOS_REMOVABLE (ushort)0x0008
+#define ASC_CNTL_NO_SCAM (ushort)0x0010
+#define ASC_CNTL_INT_MULTI_Q (ushort)0x0080
+#define ASC_CNTL_NO_LUN_SUPPORT (ushort)0x0040
+#define ASC_CNTL_NO_VERIFY_COPY (ushort)0x0100
+#define ASC_CNTL_RESET_SCSI (ushort)0x0200
+#define ASC_CNTL_INIT_INQUIRY (ushort)0x0400
+#define ASC_CNTL_INIT_VERBOSE (ushort)0x0800
+#define ASC_CNTL_SCSI_PARITY (ushort)0x1000
+#define ASC_CNTL_BURST_MODE (ushort)0x2000
+#define ASC_CNTL_SDTR_ENABLE_ULTRA (ushort)0x4000
+#define ASC_EEP_DVC_CFG_BEG_VL 2
+#define ASC_EEP_MAX_DVC_ADDR_VL 15
+#define ASC_EEP_DVC_CFG_BEG 32
+#define ASC_EEP_MAX_DVC_ADDR 45
+#define ASC_EEP_DEFINED_WORDS 10
+#define ASC_EEP_MAX_ADDR 63
+#define ASC_EEP_RES_WORDS 0
+#define ASC_EEP_MAX_RETRY 20
+#define ASC_MAX_INIT_BUSY_RETRY 8
+#define ASC_EEP_ISA_PNP_WSIZE 16
+
+typedef struct asceep_config {
+ ushort cfg_lsw;
+ ushort cfg_msw;
+ uchar init_sdtr;
+ uchar disc_enable;
+ uchar use_cmd_qng;
+ uchar start_motor;
+ uchar max_total_qng;
+ uchar max_tag_qng;
+ uchar bios_scan;
+ uchar power_up_wait;
+ uchar no_scam;
+ uchar chip_scsi_id:4;
+ uchar isa_dma_speed:4;
+ uchar dos_int13_table[ASC_MAX_TID + 1];
+ uchar adapter_info[6];
+ ushort cntl;
+ ushort chksum;
+} ASCEEP_CONFIG;
+
+#define ASC_PCI_CFG_LSW_SCSI_PARITY 0x0800
+#define ASC_PCI_CFG_LSW_BURST_MODE 0x0080
+#define ASC_PCI_CFG_LSW_INTR_ABLE 0x0020
+
+#define ASC_EEP_CMD_READ 0x80
+#define ASC_EEP_CMD_WRITE 0x40
+#define ASC_EEP_CMD_WRITE_ABLE 0x30
+#define ASC_EEP_CMD_WRITE_DISABLE 0x00
+#define ASC_OVERRUN_BSIZE 0x00000048UL
+#define ASC_CTRL_BREAK_ONCE 0x0001
+#define ASC_CTRL_BREAK_STAY_IDLE 0x0002
+#define ASCV_MSGOUT_BEG 0x0000
+#define ASCV_MSGOUT_SDTR_PERIOD (ASCV_MSGOUT_BEG+3)
+#define ASCV_MSGOUT_SDTR_OFFSET (ASCV_MSGOUT_BEG+4)
+#define ASCV_BREAK_SAVED_CODE (ushort)0x0006
+#define ASCV_MSGIN_BEG (ASCV_MSGOUT_BEG+8)
+#define ASCV_MSGIN_SDTR_PERIOD (ASCV_MSGIN_BEG+3)
+#define ASCV_MSGIN_SDTR_OFFSET (ASCV_MSGIN_BEG+4)
+#define ASCV_SDTR_DATA_BEG (ASCV_MSGIN_BEG+8)
+#define ASCV_SDTR_DONE_BEG (ASCV_SDTR_DATA_BEG+8)
+#define ASCV_MAX_DVC_QNG_BEG (ushort)0x0020
+#define ASCV_BREAK_ADDR (ushort)0x0028
+#define ASCV_BREAK_NOTIFY_COUNT (ushort)0x002A
+#define ASCV_BREAK_CONTROL (ushort)0x002C
+#define ASCV_BREAK_HIT_COUNT (ushort)0x002E
+
+#define ASCV_ASCDVC_ERR_CODE_W (ushort)0x0030
+#define ASCV_MCODE_CHKSUM_W (ushort)0x0032
+#define ASCV_MCODE_SIZE_W (ushort)0x0034
+#define ASCV_STOP_CODE_B (ushort)0x0036
+#define ASCV_DVC_ERR_CODE_B (ushort)0x0037
+#define ASCV_OVERRUN_PADDR_D (ushort)0x0038
+#define ASCV_OVERRUN_BSIZE_D (ushort)0x003C
+#define ASCV_HALTCODE_W (ushort)0x0040
+#define ASCV_CHKSUM_W (ushort)0x0042
+#define ASCV_MC_DATE_W (ushort)0x0044
+#define ASCV_MC_VER_W (ushort)0x0046
+#define ASCV_NEXTRDY_B (ushort)0x0048
+#define ASCV_DONENEXT_B (ushort)0x0049
+#define ASCV_USE_TAGGED_QNG_B (ushort)0x004A
+#define ASCV_SCSIBUSY_B (ushort)0x004B
+#define ASCV_Q_DONE_IN_PROGRESS_B (ushort)0x004C
+#define ASCV_CURCDB_B (ushort)0x004D
+#define ASCV_RCLUN_B (ushort)0x004E
+#define ASCV_BUSY_QHEAD_B (ushort)0x004F
+#define ASCV_DISC1_QHEAD_B (ushort)0x0050
+#define ASCV_DISC_ENABLE_B (ushort)0x0052
+#define ASCV_CAN_TAGGED_QNG_B (ushort)0x0053
+#define ASCV_HOSTSCSI_ID_B (ushort)0x0055
+#define ASCV_MCODE_CNTL_B (ushort)0x0056
+#define ASCV_NULL_TARGET_B (ushort)0x0057
+#define ASCV_FREE_Q_HEAD_W (ushort)0x0058
+#define ASCV_DONE_Q_TAIL_W (ushort)0x005A
+#define ASCV_FREE_Q_HEAD_B (ushort)(ASCV_FREE_Q_HEAD_W+1)
+#define ASCV_DONE_Q_TAIL_B (ushort)(ASCV_DONE_Q_TAIL_W+1)
+#define ASCV_HOST_FLAG_B (ushort)0x005D
+#define ASCV_TOTAL_READY_Q_B (ushort)0x0064
+#define ASCV_VER_SERIAL_B (ushort)0x0065
+#define ASCV_HALTCODE_SAVED_W (ushort)0x0066
+#define ASCV_WTM_FLAG_B (ushort)0x0068
+#define ASCV_RISC_FLAG_B (ushort)0x006A
+#define ASCV_REQ_SG_LIST_QP (ushort)0x006B
+#define ASC_HOST_FLAG_IN_ISR 0x01
+#define ASC_HOST_FLAG_ACK_INT 0x02
+#define ASC_RISC_FLAG_GEN_INT 0x01
+#define ASC_RISC_FLAG_REQ_SG_LIST 0x02
+#define IOP_CTRL (0x0F)
+#define IOP_STATUS (0x0E)
+#define IOP_INT_ACK IOP_STATUS
+#define IOP_REG_IFC (0x0D)
+#define IOP_SYN_OFFSET (0x0B)
+#define IOP_EXTRA_CONTROL (0x0D)
+#define IOP_REG_PC (0x0C)
+#define IOP_RAM_ADDR (0x0A)
+#define IOP_RAM_DATA (0x08)
+#define IOP_EEP_DATA (0x06)
+#define IOP_EEP_CMD (0x07)
+#define IOP_VERSION (0x03)
+#define IOP_CONFIG_HIGH (0x04)
+#define IOP_CONFIG_LOW (0x02)
+#define IOP_SIG_BYTE (0x01)
+#define IOP_SIG_WORD (0x00)
+#define IOP_REG_DC1 (0x0E)
+#define IOP_REG_DC0 (0x0C)
+#define IOP_REG_SB (0x0B)
+#define IOP_REG_DA1 (0x0A)
+#define IOP_REG_DA0 (0x08)
+#define IOP_REG_SC (0x09)
+#define IOP_DMA_SPEED (0x07)
+#define IOP_REG_FLAG (0x07)
+#define IOP_FIFO_H (0x06)
+#define IOP_FIFO_L (0x04)
+#define IOP_REG_ID (0x05)
+#define IOP_REG_QP (0x03)
+#define IOP_REG_IH (0x02)
+#define IOP_REG_IX (0x01)
+#define IOP_REG_AX (0x00)
+#define IFC_REG_LOCK (0x00)
+#define IFC_REG_UNLOCK (0x09)
+#define IFC_WR_EN_FILTER (0x10)
+#define IFC_RD_NO_EEPROM (0x10)
+#define IFC_SLEW_RATE (0x20)
+#define IFC_ACT_NEG (0x40)
+#define IFC_INP_FILTER (0x80)
+#define IFC_INIT_DEFAULT (IFC_ACT_NEG | IFC_REG_UNLOCK)
+#define SC_SEL (uchar)(0x80)
+#define SC_BSY (uchar)(0x40)
+#define SC_ACK (uchar)(0x20)
+#define SC_REQ (uchar)(0x10)
+#define SC_ATN (uchar)(0x08)
+#define SC_IO (uchar)(0x04)
+#define SC_CD (uchar)(0x02)
+#define SC_MSG (uchar)(0x01)
+#define SEC_SCSI_CTL (uchar)(0x80)
+#define SEC_ACTIVE_NEGATE (uchar)(0x40)
+#define SEC_SLEW_RATE (uchar)(0x20)
+#define SEC_ENABLE_FILTER (uchar)(0x10)
+#define ASC_HALT_EXTMSG_IN (ushort)0x8000
+#define ASC_HALT_CHK_CONDITION (ushort)0x8100
+#define ASC_HALT_SS_QUEUE_FULL (ushort)0x8200
+#define ASC_HALT_DISABLE_ASYN_USE_SYN_FIX (ushort)0x8300
+#define ASC_HALT_ENABLE_ASYN_USE_SYN_FIX (ushort)0x8400
+#define ASC_HALT_SDTR_REJECTED (ushort)0x4000
+#define ASC_MAX_QNO 0xF8
+#define ASC_DATA_SEC_BEG (ushort)0x0080
+#define ASC_DATA_SEC_END (ushort)0x0080
+#define ASC_CODE_SEC_BEG (ushort)0x0080
+#define ASC_CODE_SEC_END (ushort)0x0080
+#define ASC_QADR_BEG (0x4000)
+#define ASC_QADR_USED (ushort)(ASC_MAX_QNO * 64)
+#define ASC_QADR_END (ushort)0x7FFF
+#define ASC_QLAST_ADR (ushort)0x7FC0
+#define ASC_QBLK_SIZE 0x40
+#define ASC_BIOS_DATA_QBEG 0xF8
+#define ASC_MIN_ACTIVE_QNO 0x01
+#define ASC_QLINK_END 0xFF
+#define ASC_EEPROM_WORDS 0x10
+#define ASC_MAX_MGS_LEN 0x10
+#define ASC_BIOS_ADDR_DEF 0xDC00
+#define ASC_BIOS_SIZE 0x3800
+#define ASC_BIOS_RAM_OFF 0x3800
+#define ASC_BIOS_RAM_SIZE 0x800
+#define ASC_BIOS_MIN_ADDR 0xC000
+#define ASC_BIOS_MAX_ADDR 0xEC00
+#define ASC_BIOS_BANK_SIZE 0x0400
+#define ASC_MCODE_START_ADDR 0x0080
+#define ASC_CFG0_HOST_INT_ON 0x0020
+#define ASC_CFG0_BIOS_ON 0x0040
+#define ASC_CFG0_VERA_BURST_ON 0x0080
+#define ASC_CFG0_SCSI_PARITY_ON 0x0800
+#define ASC_CFG1_SCSI_TARGET_ON 0x0080
+#define ASC_CFG1_LRAM_8BITS_ON 0x0800
+#define ASC_CFG_MSW_CLR_MASK 0x3080
+#define CSW_TEST1 (ASC_CS_TYPE)0x8000
+#define CSW_AUTO_CONFIG (ASC_CS_TYPE)0x4000
+#define CSW_RESERVED1 (ASC_CS_TYPE)0x2000
+#define CSW_IRQ_WRITTEN (ASC_CS_TYPE)0x1000
+#define CSW_33MHZ_SELECTED (ASC_CS_TYPE)0x0800
+#define CSW_TEST2 (ASC_CS_TYPE)0x0400
+#define CSW_TEST3 (ASC_CS_TYPE)0x0200
+#define CSW_RESERVED2 (ASC_CS_TYPE)0x0100
+#define CSW_DMA_DONE (ASC_CS_TYPE)0x0080
+#define CSW_FIFO_RDY (ASC_CS_TYPE)0x0040
+#define CSW_EEP_READ_DONE (ASC_CS_TYPE)0x0020
+#define CSW_HALTED (ASC_CS_TYPE)0x0010
+#define CSW_SCSI_RESET_ACTIVE (ASC_CS_TYPE)0x0008
+#define CSW_PARITY_ERR (ASC_CS_TYPE)0x0004
+#define CSW_SCSI_RESET_LATCH (ASC_CS_TYPE)0x0002
+#define CSW_INT_PENDING (ASC_CS_TYPE)0x0001
+#define CIW_CLR_SCSI_RESET_INT (ASC_CS_TYPE)0x1000
+#define CIW_INT_ACK (ASC_CS_TYPE)0x0100
+#define CIW_TEST1 (ASC_CS_TYPE)0x0200
+#define CIW_TEST2 (ASC_CS_TYPE)0x0400
+#define CIW_SEL_33MHZ (ASC_CS_TYPE)0x0800
+#define CIW_IRQ_ACT (ASC_CS_TYPE)0x1000
+#define CC_CHIP_RESET (uchar)0x80
+#define CC_SCSI_RESET (uchar)0x40
+#define CC_HALT (uchar)0x20
+#define CC_SINGLE_STEP (uchar)0x10
+#define CC_DMA_ABLE (uchar)0x08
+#define CC_TEST (uchar)0x04
+#define CC_BANK_ONE (uchar)0x02
+#define CC_DIAG (uchar)0x01
+#define ASC_1000_ID0W 0x04C1
+#define ASC_1000_ID0W_FIX 0x00C1
+#define ASC_1000_ID1B 0x25
+#define ASC_EISA_BIG_IOP_GAP (0x1C30-0x0C50)
+#define ASC_EISA_SMALL_IOP_GAP (0x0020)
+#define ASC_EISA_MIN_IOP_ADDR (0x0C30)
+#define ASC_EISA_MAX_IOP_ADDR (0xFC50)
+#define ASC_EISA_REV_IOP_MASK (0x0C83)
+#define ASC_EISA_PID_IOP_MASK (0x0C80)
+#define ASC_EISA_CFG_IOP_MASK (0x0C86)
+#define ASC_GET_EISA_SLOT(iop) (PortAddr)((iop) & 0xF000)
+#define ASC_EISA_ID_740 0x01745004UL
+#define ASC_EISA_ID_750 0x01755004UL
+#define INS_HALTINT (ushort)0x6281
+#define INS_HALT (ushort)0x6280
+#define INS_SINT (ushort)0x6200
+#define INS_RFLAG_WTM (ushort)0x7380
+#define ASC_MC_SAVE_CODE_WSIZE 0x500
+#define ASC_MC_SAVE_DATA_WSIZE 0x40
+
+typedef struct asc_mc_saved {
+ ushort data[ASC_MC_SAVE_DATA_WSIZE];
+ ushort code[ASC_MC_SAVE_CODE_WSIZE];
+} ASC_MC_SAVED;
+
+#define AscGetQDoneInProgress(port) AscReadLramByte((port), ASCV_Q_DONE_IN_PROGRESS_B)
+#define AscPutQDoneInProgress(port, val) AscWriteLramByte((port), ASCV_Q_DONE_IN_PROGRESS_B, val)
+#define AscGetVarFreeQHead(port) AscReadLramWord((port), ASCV_FREE_Q_HEAD_W)
+#define AscGetVarDoneQTail(port) AscReadLramWord((port), ASCV_DONE_Q_TAIL_W)
+#define AscPutVarFreeQHead(port, val) AscWriteLramWord((port), ASCV_FREE_Q_HEAD_W, val)
+#define AscPutVarDoneQTail(port, val) AscWriteLramWord((port), ASCV_DONE_Q_TAIL_W, val)
+#define AscGetRiscVarFreeQHead(port) AscReadLramByte((port), ASCV_NEXTRDY_B)
+#define AscGetRiscVarDoneQTail(port) AscReadLramByte((port), ASCV_DONENEXT_B)
+#define AscPutRiscVarFreeQHead(port, val) AscWriteLramByte((port), ASCV_NEXTRDY_B, val)
+#define AscPutRiscVarDoneQTail(port, val) AscWriteLramByte((port), ASCV_DONENEXT_B, val)
+#define AscPutMCodeSDTRDoneAtID(port, id, data) AscWriteLramByte((port), (ushort)((ushort)ASCV_SDTR_DONE_BEG+(ushort)id), (data)) ;
+#define AscGetMCodeSDTRDoneAtID(port, id) AscReadLramByte((port), (ushort)((ushort)ASCV_SDTR_DONE_BEG+(ushort)id)) ;
+#define AscPutMCodeInitSDTRAtID(port, id, data) AscWriteLramByte((port), (ushort)((ushort)ASCV_SDTR_DATA_BEG+(ushort)id), data) ;
+#define AscGetMCodeInitSDTRAtID(port, id) AscReadLramByte((port), (ushort)((ushort)ASCV_SDTR_DATA_BEG+(ushort)id)) ;
+#define AscSynIndexToPeriod(index) (uchar)(asc_dvc->sdtr_period_tbl[ (index) ])
+#define AscGetChipSignatureByte(port) (uchar)inp((port)+IOP_SIG_BYTE)
+#define AscGetChipSignatureWord(port) (ushort)inpw((port)+IOP_SIG_WORD)
+#define AscGetChipVerNo(port) (uchar)inp((port)+IOP_VERSION)
+#define AscGetChipCfgLsw(port) (ushort)inpw((port)+IOP_CONFIG_LOW)
+#define AscGetChipCfgMsw(port) (ushort)inpw((port)+IOP_CONFIG_HIGH)
+#define AscSetChipCfgLsw(port, data) outpw((port)+IOP_CONFIG_LOW, data)
+#define AscSetChipCfgMsw(port, data) outpw((port)+IOP_CONFIG_HIGH, data)
+#define AscGetChipEEPCmd(port) (uchar)inp((port)+IOP_EEP_CMD)
+#define AscSetChipEEPCmd(port, data) outp((port)+IOP_EEP_CMD, data)
+#define AscGetChipEEPData(port) (ushort)inpw((port)+IOP_EEP_DATA)
+#define AscSetChipEEPData(port, data) outpw((port)+IOP_EEP_DATA, data)
+#define AscGetChipLramAddr(port) (ushort)inpw((PortAddr)((port)+IOP_RAM_ADDR))
+#define AscSetChipLramAddr(port, addr) outpw((PortAddr)((port)+IOP_RAM_ADDR), addr)
+#define AscGetChipLramData(port) (ushort)inpw((port)+IOP_RAM_DATA)
+#define AscSetChipLramData(port, data) outpw((port)+IOP_RAM_DATA, data)
+#define AscGetChipLramDataNoSwap(port) (ushort)inpw_noswap((port)+IOP_RAM_DATA)
+#define AscSetChipLramDataNoSwap(port, data) outpw_noswap((port)+IOP_RAM_DATA, data)
+#define AscGetChipIFC(port) (uchar)inp((port)+IOP_REG_IFC)
+#define AscSetChipIFC(port, data) outp((port)+IOP_REG_IFC, data)
+#define AscGetChipStatus(port) (ASC_CS_TYPE)inpw((port)+IOP_STATUS)
+#define AscSetChipStatus(port, cs_val) outpw((port)+IOP_STATUS, cs_val)
+#define AscGetChipControl(port) (uchar)inp((port)+IOP_CTRL)
+#define AscSetChipControl(port, cc_val) outp((port)+IOP_CTRL, cc_val)
+#define AscGetChipSyn(port) (uchar)inp((port)+IOP_SYN_OFFSET)
+#define AscSetChipSyn(port, data) outp((port)+IOP_SYN_OFFSET, data)
+#define AscSetPCAddr(port, data) outpw((port)+IOP_REG_PC, data)
+#define AscGetPCAddr(port) (ushort)inpw((port)+IOP_REG_PC)
+#define AscIsIntPending(port) (AscGetChipStatus(port) & (CSW_INT_PENDING | CSW_SCSI_RESET_LATCH))
+#define AscGetChipScsiID(port) ((AscGetChipCfgLsw(port) >> 8) & ASC_MAX_TID)
+#define AscGetExtraControl(port) (uchar)inp((port)+IOP_EXTRA_CONTROL)
+#define AscSetExtraControl(port, data) outp((port)+IOP_EXTRA_CONTROL, data)
+#define AscReadChipAX(port) (ushort)inpw((port)+IOP_REG_AX)
+#define AscWriteChipAX(port, data) outpw((port)+IOP_REG_AX, data)
+#define AscReadChipIX(port) (uchar)inp((port)+IOP_REG_IX)
+#define AscWriteChipIX(port, data) outp((port)+IOP_REG_IX, data)
+#define AscReadChipIH(port) (ushort)inpw((port)+IOP_REG_IH)
+#define AscWriteChipIH(port, data) outpw((port)+IOP_REG_IH, data)
+#define AscReadChipQP(port) (uchar)inp((port)+IOP_REG_QP)
+#define AscWriteChipQP(port, data) outp((port)+IOP_REG_QP, data)
+#define AscReadChipFIFO_L(port) (ushort)inpw((port)+IOP_REG_FIFO_L)
+#define AscWriteChipFIFO_L(port, data) outpw((port)+IOP_REG_FIFO_L, data)
+#define AscReadChipFIFO_H(port) (ushort)inpw((port)+IOP_REG_FIFO_H)
+#define AscWriteChipFIFO_H(port, data) outpw((port)+IOP_REG_FIFO_H, data)
+#define AscReadChipDmaSpeed(port) (uchar)inp((port)+IOP_DMA_SPEED)
+#define AscWriteChipDmaSpeed(port, data) outp((port)+IOP_DMA_SPEED, data)
+#define AscReadChipDA0(port) (ushort)inpw((port)+IOP_REG_DA0)
+#define AscWriteChipDA0(port) outpw((port)+IOP_REG_DA0, data)
+#define AscReadChipDA1(port) (ushort)inpw((port)+IOP_REG_DA1)
+#define AscWriteChipDA1(port) outpw((port)+IOP_REG_DA1, data)
+#define AscReadChipDC0(port) (ushort)inpw((port)+IOP_REG_DC0)
+#define AscWriteChipDC0(port) outpw((port)+IOP_REG_DC0, data)
+#define AscReadChipDC1(port) (ushort)inpw((port)+IOP_REG_DC1)
+#define AscWriteChipDC1(port) outpw((port)+IOP_REG_DC1, data)
+#define AscReadChipDvcID(port) (uchar)inp((port)+IOP_REG_ID)
+#define AscWriteChipDvcID(port, data) outp((port)+IOP_REG_ID, data)
+
+STATIC int AscWriteEEPCmdReg(PortAddr iop_base, uchar cmd_reg);
+STATIC int AscWriteEEPDataReg(PortAddr iop_base, ushort data_reg);
+STATIC void AscWaitEEPRead(void);
+STATIC void AscWaitEEPWrite(void);
+STATIC ushort AscReadEEPWord(PortAddr, uchar);
+STATIC ushort AscWriteEEPWord(PortAddr, uchar, ushort);
+STATIC ushort AscGetEEPConfig(PortAddr, ASCEEP_CONFIG *, ushort);
+STATIC int AscSetEEPConfigOnce(PortAddr, ASCEEP_CONFIG *, ushort);
+STATIC int AscSetEEPConfig(PortAddr, ASCEEP_CONFIG *, ushort);
+STATIC int AscStartChip(PortAddr);
+STATIC int AscStopChip(PortAddr);
+STATIC void AscSetChipIH(PortAddr, ushort);
+STATIC int AscIsChipHalted(PortAddr);
+STATIC void AscAckInterrupt(PortAddr);
+STATIC void AscDisableInterrupt(PortAddr);
+STATIC void AscEnableInterrupt(PortAddr);
+STATIC void AscSetBank(PortAddr, uchar);
+STATIC int AscResetChipAndScsiBus(ASC_DVC_VAR *);
+STATIC ushort AscGetIsaDmaChannel(PortAddr);
+STATIC ushort AscSetIsaDmaChannel(PortAddr, ushort);
+STATIC uchar AscSetIsaDmaSpeed(PortAddr, uchar);
+STATIC uchar AscGetIsaDmaSpeed(PortAddr);
+STATIC uchar AscReadLramByte(PortAddr, ushort);
+STATIC ushort AscReadLramWord(PortAddr, ushort);
+STATIC ulong AscReadLramDWord(PortAddr, ushort);
+STATIC void AscWriteLramWord(PortAddr, ushort, ushort);
+STATIC void AscWriteLramDWord(PortAddr, ushort, ulong);
+STATIC void AscWriteLramByte(PortAddr, ushort, uchar);
+STATIC ulong AscMemSumLramWord(PortAddr, ushort, rint);
+STATIC void AscMemWordSetLram(PortAddr, ushort, ushort, rint);
+STATIC void AscMemWordCopyToLram(PortAddr, ushort, ushort *, int);
+STATIC void AscMemDWordCopyToLram(PortAddr, ushort, ulong *, int);
+STATIC void AscMemWordCopyFromLram(PortAddr, ushort, ushort *, int);
+STATIC ushort AscInitAscDvcVar(ASC_DVC_VAR asc_ptr_type *);
+STATIC ushort AscInitFromEEP(ASC_DVC_VAR asc_ptr_type *);
+STATIC ushort AscInitFromAscDvcVar(ASC_DVC_VAR asc_ptr_type *);
+STATIC ushort AscInitMicroCodeVar(ASC_DVC_VAR asc_ptr_type * asc_dvc);
+STATIC int AscTestExternalLram(ASC_DVC_VAR asc_ptr_type *);
+STATIC uchar AscMsgOutSDTR(ASC_DVC_VAR asc_ptr_type *, uchar, uchar);
+STATIC uchar AscCalSDTRData(ASC_DVC_VAR asc_ptr_type *, uchar, uchar);
+STATIC void AscSetChipSDTR(PortAddr, uchar, uchar);
+STATIC uchar AscGetSynPeriodIndex(ASC_DVC_VAR asc_ptr_type *, ruchar);
+STATIC uchar AscAllocFreeQueue(PortAddr, uchar);
+STATIC uchar AscAllocMultipleFreeQueue(PortAddr, uchar, uchar);
+STATIC int AscRiscHaltedAbortSRB(ASC_DVC_VAR asc_ptr_type *, ulong);
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+STATIC int AscRiscHaltedAbortTIX(ASC_DVC_VAR asc_ptr_type *, uchar);
+#endif /* version >= v1.3.89 */
+STATIC int AscHostReqRiscHalt(PortAddr);
+STATIC int AscStopQueueExe(PortAddr);
+STATIC int AscStartQueueExe(PortAddr);
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+STATIC int AscCleanUpDiscQueue(PortAddr);
+#endif /* version >= v1.3.89 */
+STATIC int AscCleanUpBusyQueue(PortAddr);
+STATIC int AscWaitTixISRDone(ASC_DVC_VAR asc_ptr_type *, uchar);
+STATIC int AscWaitISRDone(ASC_DVC_VAR asc_ptr_type *);
+STATIC ulong AscGetOnePhyAddr(ASC_DVC_VAR asc_ptr_type *, uchar *,
+ ulong);
+STATIC int AscSendScsiQueue(ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_Q * scsiq,
+ uchar n_q_required);
+STATIC int AscPutReadyQueue(ASC_DVC_VAR asc_ptr_type *,
+ ASC_SCSI_Q *, uchar);
+STATIC int AscPutReadySgListQueue(ASC_DVC_VAR asc_ptr_type *,
+ ASC_SCSI_Q *, uchar);
+STATIC int AscSetChipSynRegAtID(PortAddr, uchar, uchar);
+STATIC int AscSetRunChipSynRegAtID(PortAddr, uchar, uchar);
+STATIC ushort AscInitLram(ASC_DVC_VAR asc_ptr_type *);
+STATIC int AscReInitLram(ASC_DVC_VAR asc_ptr_type *);
+STATIC ushort AscInitQLinkVar(ASC_DVC_VAR asc_ptr_type *);
+STATIC int AscSetLibErrorCode(ASC_DVC_VAR asc_ptr_type *, ushort);
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+STATIC int _AscWaitQDone(PortAddr, ASC_SCSI_Q *);
+#endif /* version >= v1.3.89 */
+STATIC int AscIsrChipHalted(ASC_DVC_VAR asc_ptr_type *);
+STATIC uchar _AscCopyLramScsiDoneQ(PortAddr, ushort,
+ ASC_QDONE_INFO *, ulong);
+STATIC int AscIsrQDone(ASC_DVC_VAR asc_ptr_type *);
+STATIC int AscCompareString(uchar *, uchar *, int);
+STATIC ushort AscGetEisaChipCfg(PortAddr);
+STATIC ulong AscGetEisaProductID(PortAddr);
+STATIC PortAddr AscSearchIOPortAddrEISA(PortAddr);
+STATIC uchar AscGetChipScsiCtrl(PortAddr);
+STATIC uchar AscSetChipScsiID(PortAddr, uchar);
+STATIC uchar AscGetChipVersion(PortAddr, ushort);
+STATIC ushort AscGetChipBusType(PortAddr);
+STATIC ulong AscLoadMicroCode(PortAddr, ushort, ushort *, ushort);
+STATIC int AscFindSignature(PortAddr);
+STATIC PortAddr AscSearchIOPortAddr11(PortAddr);
+STATIC void AscToggleIRQAct(PortAddr);
+STATIC void AscSetISAPNPWaitForKey(void);
+STATIC uchar AscGetChipIRQ(PortAddr, ushort);
+STATIC uchar AscSetChipIRQ(PortAddr, uchar, ushort);
+STATIC ushort AscGetChipBiosAddress(PortAddr, ushort);
+STATIC int DvcEnterCritical(void);
+STATIC void DvcLeaveCritical(int);
+STATIC void DvcInPortWords(PortAddr, ushort *, int);
+STATIC void DvcOutPortWords(PortAddr, ushort *, int);
+STATIC void DvcOutPortDWords(PortAddr, ulong *, int);
+STATIC uchar DvcReadPCIConfigByte(ASC_DVC_VAR asc_ptr_type *, ushort);
+STATIC void DvcWritePCIConfigByte(ASC_DVC_VAR asc_ptr_type *,
+ ushort, uchar);
+STATIC ushort AscGetChipBiosAddress(PortAddr, ushort);
+STATIC void DvcSleepMilliSecond(ulong);
+STATIC void DvcDelayNanoSecond(ASC_DVC_VAR asc_ptr_type *, ulong);
+STATIC ulong DvcGetSGList(ASC_DVC_VAR asc_ptr_type *, uchar *,
+ ulong, ASC_SG_HEAD *);
+STATIC void DvcPutScsiQ(PortAddr, ushort, ushort *, int);
+STATIC void DvcGetQinfo(PortAddr, ushort, ushort *, int);
+STATIC PortAddr AscSearchIOPortAddr(PortAddr, ushort);
+STATIC ushort AscInitGetConfig(ASC_DVC_VAR asc_ptr_type *);
+STATIC ushort AscInitSetConfig(ASC_DVC_VAR asc_ptr_type *);
+STATIC ushort AscInitAsc1000Driver(ASC_DVC_VAR asc_ptr_type *);
+STATIC void AscAsyncFix(ASC_DVC_VAR asc_ptr_type *, uchar,
+ ASC_SCSI_INQUIRY *);
+STATIC int AscTagQueuingSafe(ASC_SCSI_INQUIRY *);
+STATIC void AscInquiryHandling(ASC_DVC_VAR asc_ptr_type *,
+ uchar, ASC_SCSI_INQUIRY *);
+STATIC int AscExeScsiQueue(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_Q *);
+STATIC int AscISR(ASC_DVC_VAR asc_ptr_type *);
+STATIC uint AscGetNumOfFreeQueue(ASC_DVC_VAR asc_ptr_type *, uchar,
+ uchar);
+STATIC int AscSgListToQueue(int);
+STATIC int AscAbortSRB(ASC_DVC_VAR asc_ptr_type *, ulong);
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+STATIC int AscResetDevice(ASC_DVC_VAR asc_ptr_type *, uchar);
+#endif /* version >= v1.3.89 */
+STATIC int AscResetSB(ASC_DVC_VAR asc_ptr_type *);
+STATIC void AscEnableIsaDma(uchar);
+STATIC ulong AscGetMaxDmaCount(ushort);
+
+
+/*
+ * --- Adv Library Constants and Macros
+ */
+
+#define ADV_LIB_VERSION_MAJOR 3
+#define ADV_LIB_VERSION_MINOR 45
+
+/* d_os_dep.h */
+#define ADV_OS_LINUX
+
+/*
+ * Define Adv Library required special types.
+ */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0)
+#define AdvPortAddr unsigned short /* I/O Port address size */
+#else /* version >= v1,3,0 */
+#define AdvPortAddr unsigned long /* Virtual memory address size */
+#endif /* version >= v1,3,0 */
+
+/*
+ * Define Adv Library required memory access macros.
+ */
+#define ADV_MEM_READB(addr) readb(addr)
+#define ADV_MEM_READW(addr) readw(addr)
+#define ADV_MEM_WRITEB(addr, byte) writeb(byte, addr)
+#define ADV_MEM_WRITEW(addr, word) writew(word, addr)
+
+/*
+ * The I/O memory mapping function names changed in 2.1.X.
+ */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,0)
+#define ioremap vremap
+#define iounmap vfree
+#endif /* version < v2.1.0 */
+
+/*
+ * Define total number of simultaneous maximum element scatter-gather
+ * requests, i.e. ADV_TOT_SG_LIST * ADV_MAX_SG_LIST is the total number
+ * of simultaneous scatter-gather elements supported per wide adapter.
+ */
+#define ADV_TOT_SG_LIST 64
+
+/*
+ * Define Adv Library required per request scatter-gather element limit.
+ */
+#define ADV_MAX_SG_LIST 64
+
+/*
+ * Scatter-Gather Definitions per request.
+ *
+ * Because SG block memory is allocated in virtual memory but is
+ * referenced by the microcode as physical memory, we need to do
+ * calculations to insure there will be enough physically contiguous
+ * memory to support ADV_MAX_SG_LIST SG entries.
+ */
+
+/* Number of SG blocks needed. */
+#define ADV_NUM_SG_BLOCK \
+ ((ADV_MAX_SG_LIST + (NO_OF_SG_PER_BLOCK - 1))/NO_OF_SG_PER_BLOCK)
+
+/* Total contiguous memory needed for SG blocks. */
+#define ADV_SG_TOTAL_MEM_SIZE \
+ (sizeof(ADV_SG_BLOCK) * ADV_NUM_SG_BLOCK)
+
+#define ASC_PAGE_SIZE PAGE_SIZE
+
+/*
+ * Number of page crossings possible for the total contiguous virtual memory
+ * needed for SG blocks.
+ *
+ * We need to allocate this many additional SG blocks in virtual memory to
+ * insure there will be space for ADV_NUM_SG_BLOCK physically contiguous
+ * scatter-gather blocks.
+ */
+#define ADV_NUM_PAGE_CROSSING \
+ ((ADV_SG_TOTAL_MEM_SIZE + (ASC_PAGE_SIZE - 1))/ASC_PAGE_SIZE)
+
+/*
+ * Define Adv Library Assertion Macro.
+ */
+
+#define ADV_ASSERT(a) ASC_ASSERT(a)
+
+/* a_condor.h */
+#define ADV_PCI_VENDOR_ID 0x10CD
+#define ADV_PCI_DEVICE_ID_REV_A 0x2300
+
+#define ASC_EEP_DVC_CFG_BEGIN (0x00)
+#define ASC_EEP_DVC_CFG_END (0x15)
+#define ASC_EEP_DVC_CTL_BEGIN (0x16) /* location of OEM name */
+#define ASC_EEP_MAX_WORD_ADDR (0x1E)
+
+#define ASC_EEP_DELAY_MS 100
+
+/*
+ * EEPROM bits reference by the RISC after initialization.
+ */
+#define ADV_EEPROM_BIG_ENDIAN 0x8000 /* EEPROM Bit 15 */
+#define ADV_EEPROM_BIOS_ENABLE 0x4000 /* EEPROM Bit 14 */
+#define ADV_EEPROM_TERM_POL 0x2000 /* EEPROM Bit 13 */
+
+/*
+ * EEPROM configuration format
+ *
+ * Field naming convention:
+ *
+ * *_enable indicates the field enables or disables the feature. The
+ * value is never reset.
+ *
+ * *_able indicates both whether a feature should be enabled or disabled
+ * and whether a device isi capable of the feature. At initialization
+ * this field may be set, but later if a device is found to be incapable
+ * of the feature, the field is cleared.
+ *
+ * Default values are maintained in a_init.c in the structure
+ * Default_EEPROM_Config.
+ */
+typedef struct adveep_config
+{
+ /* Word Offset, Description */
+
+ ushort cfg_lsw; /* 00 power up initialization */
+ /* bit 13 set - Term Polarity Control */
+ /* bit 14 set - BIOS Enable */
+ /* bit 15 set - Big Endian Mode */
+ ushort cfg_msw; /* 01 unused */
+ ushort disc_enable; /* 02 disconnect enable */
+ ushort wdtr_able; /* 03 Wide DTR able */
+ ushort sdtr_able; /* 04 Synchronous DTR able */
+ ushort start_motor; /* 05 send start up motor */
+ ushort tagqng_able; /* 06 tag queuing able */
+ ushort bios_scan; /* 07 BIOS device control */
+ ushort scam_tolerant; /* 08 no scam */
+
+ uchar adapter_scsi_id; /* 09 Host Adapter ID */
+ uchar bios_boot_delay; /* power up wait */
+
+ uchar scsi_reset_delay; /* 10 reset delay */
+ uchar bios_id_lun; /* first boot device scsi id & lun */
+ /* high nibble is lun */
+ /* low nibble is scsi id */
+
+ uchar termination; /* 11 0 - automatic */
+ /* 1 - low off / high off */
+ /* 2 - low off / high on */
+ /* 3 - low on / high on */
+ /* There is no low on / high off */
+
+ uchar reserved1; /* reserved byte (not used) */
+
+ ushort bios_ctrl; /* 12 BIOS control bits */
+ /* bit 0 set: BIOS don't act as initiator. */
+ /* bit 1 set: BIOS > 1 GB support */
+ /* bit 2 set: BIOS > 2 Disk Support */
+ /* bit 3 set: BIOS don't support removables */
+ /* bit 4 set: BIOS support bootable CD */
+ /* bit 5 set: */
+ /* bit 6 set: BIOS support multiple LUNs */
+ /* bit 7 set: BIOS display of message */
+ /* bit 8 set: */
+ /* bit 9 set: Reset SCSI bus during init. */
+ /* bit 10 set: */
+ /* bit 11 set: No verbose initialization. */
+ /* bit 12 set: SCSI parity enabled */
+ /* bit 13 set: */
+ /* bit 14 set: */
+ /* bit 15 set: */
+ ushort ultra_able; /* 13 ULTRA speed able */
+ ushort reserved2; /* 14 reserved */
+ uchar max_host_qng; /* 15 maximum host queuing */
+ uchar max_dvc_qng; /* maximum per device queuing */
+ ushort dvc_cntl; /* 16 control bit for driver */
+ ushort bug_fix; /* 17 control bit for bug fix */
+ ushort serial_number_word1; /* 18 Board serial number word 1 */
+ ushort serial_number_word2; /* 19 Board serial number word 2 */
+ ushort serial_number_word3; /* 20 Board serial number word 3 */
+ ushort check_sum; /* 21 EEP check sum */
+ uchar oem_name[16]; /* 22 OEM name */
+ ushort dvc_err_code; /* 30 last device driver error code */
+ ushort adv_err_code; /* 31 last uc and Adv Lib error code */
+ ushort adv_err_addr; /* 32 last uc error address */
+ ushort saved_dvc_err_code; /* 33 saved last dev. driver error code */
+ ushort saved_adv_err_code; /* 34 saved last uc and Adv Lib error code */
+ ushort saved_adv_err_addr; /* 35 saved last uc error address */
+ ushort num_of_err; /* 36 number of error */
+} ADVEEP_CONFIG;
+
+/*
+ * EEPROM Commands
+ */
+#define ASC_EEP_CMD_DONE 0x0200
+#define ASC_EEP_CMD_DONE_ERR 0x0001
+
+/* cfg_word */
+#define EEP_CFG_WORD_BIG_ENDIAN 0x8000
+
+/* bios_ctrl */
+#define BIOS_CTRL_BIOS 0x0001
+#define BIOS_CTRL_EXTENDED_XLAT 0x0002
+#define BIOS_CTRL_GT_2_DISK 0x0004
+#define BIOS_CTRL_BIOS_REMOVABLE 0x0008
+#define BIOS_CTRL_BOOTABLE_CD 0x0010
+#define BIOS_CTRL_MULTIPLE_LUN 0x0040
+#define BIOS_CTRL_DISPLAY_MSG 0x0080
+#define BIOS_CTRL_NO_SCAM 0x0100
+#define BIOS_CTRL_RESET_SCSI_BUS 0x0200
+#define BIOS_CTRL_INIT_VERBOSE 0x0800
+#define BIOS_CTRL_SCSI_PARITY 0x1000
+
+/*
+ * ASC 3550 Internal Memory Size - 8KB
+ */
+#define ADV_CONDOR_MEMSIZE 0x2000 /* 8 KB Internal Memory */
+
+/*
+ * ASC 3550 I/O Length - 64 bytes
+ */
+#define ADV_CONDOR_IOLEN 0x40 /* I/O Port Range in bytes */
+
+/*
+ * Byte I/O register address from base of 'iop_base'.
+ */
+#define IOPB_INTR_STATUS_REG 0x00
+#define IOPB_CHIP_ID_1 0x01
+#define IOPB_INTR_ENABLES 0x02
+#define IOPB_CHIP_TYPE_REV 0x03
+#define IOPB_RES_ADDR_4 0x04
+#define IOPB_RES_ADDR_5 0x05
+#define IOPB_RAM_DATA 0x06
+#define IOPB_RES_ADDR_7 0x07
+#define IOPB_FLAG_REG 0x08
+#define IOPB_RES_ADDR_9 0x09
+#define IOPB_RISC_CSR 0x0A
+#define IOPB_RES_ADDR_B 0x0B
+#define IOPB_RES_ADDR_C 0x0C
+#define IOPB_RES_ADDR_D 0x0D
+#define IOPB_RES_ADDR_E 0x0E
+#define IOPB_RES_ADDR_F 0x0F
+#define IOPB_MEM_CFG 0x10
+#define IOPB_RES_ADDR_11 0x11
+#define IOPB_RES_ADDR_12 0x12
+#define IOPB_RES_ADDR_13 0x13
+#define IOPB_FLASH_PAGE 0x14
+#define IOPB_RES_ADDR_15 0x15
+#define IOPB_RES_ADDR_16 0x16
+#define IOPB_RES_ADDR_17 0x17
+#define IOPB_FLASH_DATA 0x18
+#define IOPB_RES_ADDR_19 0x19
+#define IOPB_RES_ADDR_1A 0x1A
+#define IOPB_RES_ADDR_1B 0x1B
+#define IOPB_RES_ADDR_1C 0x1C
+#define IOPB_RES_ADDR_1D 0x1D
+#define IOPB_RES_ADDR_1E 0x1E
+#define IOPB_RES_ADDR_1F 0x1F
+#define IOPB_DMA_CFG0 0x20
+#define IOPB_DMA_CFG1 0x21
+#define IOPB_TICKLE 0x22
+#define IOPB_DMA_REG_WR 0x23
+#define IOPB_SDMA_STATUS 0x24
+#define IOPB_SCSI_BYTE_CNT 0x25
+#define IOPB_HOST_BYTE_CNT 0x26
+#define IOPB_BYTE_LEFT_TO_XFER 0x27
+#define IOPB_BYTE_TO_XFER_0 0x28
+#define IOPB_BYTE_TO_XFER_1 0x29
+#define IOPB_BYTE_TO_XFER_2 0x2A
+#define IOPB_BYTE_TO_XFER_3 0x2B
+#define IOPB_ACC_GRP 0x2C
+#define IOPB_RES_ADDR_2D 0x2D
+#define IOPB_DEV_ID 0x2E
+#define IOPB_RES_ADDR_2F 0x2F
+#define IOPB_SCSI_DATA 0x30
+#define IOPB_RES_ADDR_31 0x31
+#define IOPB_RES_ADDR_32 0x32
+#define IOPB_SCSI_DATA_HSHK 0x33
+#define IOPB_SCSI_CTRL 0x34
+#define IOPB_RES_ADDR_35 0x35
+#define IOPB_RES_ADDR_36 0x36
+#define IOPB_RES_ADDR_37 0x37
+#define IOPB_RES_ADDR_38 0x38
+#define IOPB_RES_ADDR_39 0x39
+#define IOPB_RES_ADDR_3A 0x3A
+#define IOPB_RES_ADDR_3B 0x3B
+#define IOPB_RFIFO_CNT 0x3C
+#define IOPB_RES_ADDR_3D 0x3D
+#define IOPB_RES_ADDR_3E 0x3E
+#define IOPB_RES_ADDR_3F 0x3F
+
+/*
+ * Word I/O register address from base of 'iop_base'.
+ */
+#define IOPW_CHIP_ID_0 0x00 /* CID0 */
+#define IOPW_CTRL_REG 0x02 /* CC */
+#define IOPW_RAM_ADDR 0x04 /* LA */
+#define IOPW_RAM_DATA 0x06 /* LD */
+#define IOPW_RES_ADDR_08 0x08
+#define IOPW_RISC_CSR 0x0A /* CSR */
+#define IOPW_SCSI_CFG0 0x0C /* CFG0 */
+#define IOPW_SCSI_CFG1 0x0E /* CFG1 */
+#define IOPW_RES_ADDR_10 0x10
+#define IOPW_SEL_MASK 0x12 /* SM */
+#define IOPW_RES_ADDR_14 0x14
+#define IOPW_FLASH_ADDR 0x16 /* FA */
+#define IOPW_RES_ADDR_18 0x18
+#define IOPW_EE_CMD 0x1A /* EC */
+#define IOPW_EE_DATA 0x1C /* ED */
+#define IOPW_SFIFO_CNT 0x1E /* SFC */
+#define IOPW_RES_ADDR_20 0x20
+#define IOPW_Q_BASE 0x22 /* QB */
+#define IOPW_QP 0x24 /* QP */
+#define IOPW_IX 0x26 /* IX */
+#define IOPW_SP 0x28 /* SP */
+#define IOPW_PC 0x2A /* PC */
+#define IOPW_RES_ADDR_2C 0x2C
+#define IOPW_RES_ADDR_2E 0x2E
+#define IOPW_SCSI_DATA 0x30 /* SD */
+#define IOPW_SCSI_DATA_HSHK 0x32 /* SDH */
+#define IOPW_SCSI_CTRL 0x34 /* SC */
+#define IOPW_HSHK_CFG 0x36 /* HCFG */
+#define IOPW_SXFR_STATUS 0x36 /* SXS */
+#define IOPW_SXFR_CNTL 0x38 /* SXL */
+#define IOPW_SXFR_CNTH 0x3A /* SXH */
+#define IOPW_RES_ADDR_3C 0x3C
+#define IOPW_RFIFO_DATA 0x3E /* RFD */
+
+/*
+ * Doubleword I/O register address from base of 'iop_base'.
+ */
+#define IOPDW_RES_ADDR_0 0x00
+#define IOPDW_RAM_DATA 0x04
+#define IOPDW_RES_ADDR_8 0x08
+#define IOPDW_RES_ADDR_C 0x0C
+#define IOPDW_RES_ADDR_10 0x10
+#define IOPDW_RES_ADDR_14 0x14
+#define IOPDW_RES_ADDR_18 0x18
+#define IOPDW_RES_ADDR_1C 0x1C
+#define IOPDW_SDMA_ADDR0 0x20
+#define IOPDW_SDMA_ADDR1 0x24
+#define IOPDW_SDMA_COUNT 0x28
+#define IOPDW_SDMA_ERROR 0x2C
+#define IOPDW_RDMA_ADDR0 0x30
+#define IOPDW_RDMA_ADDR1 0x34
+#define IOPDW_RDMA_COUNT 0x38
+#define IOPDW_RDMA_ERROR 0x3C
+
+#define ADV_CHIP_ID_BYTE 0x25
+#define ADV_CHIP_ID_WORD 0x04C1
+
+#define ADV_SC_SCSI_BUS_RESET 0x2000
+
+#define ADV_INTR_ENABLE_HOST_INTR 0x01
+#define ADV_INTR_ENABLE_SEL_INTR 0x02
+#define ADV_INTR_ENABLE_DPR_INTR 0x04
+#define ADV_INTR_ENABLE_RTA_INTR 0x08
+#define ADV_INTR_ENABLE_RMA_INTR 0x10
+#define ADV_INTR_ENABLE_RST_INTR 0x20
+#define ADV_INTR_ENABLE_DPE_INTR 0x40
+#define ADV_INTR_ENABLE_GLOBAL_INTR 0x80
+
+#define ADV_INTR_STATUS_INTRA 0x01
+#define ADV_INTR_STATUS_INTRB 0x02
+#define ADV_INTR_STATUS_INTRC 0x04
+
+#define ADV_RISC_CSR_STOP (0x0000)
+#define ADV_RISC_TEST_COND (0x2000)
+#define ADV_RISC_CSR_RUN (0x4000)
+#define ADV_RISC_CSR_SINGLE_STEP (0x8000)
+
+#define ADV_CTRL_REG_HOST_INTR 0x0100
+#define ADV_CTRL_REG_SEL_INTR 0x0200
+#define ADV_CTRL_REG_DPR_INTR 0x0400
+#define ADV_CTRL_REG_RTA_INTR 0x0800
+#define ADV_CTRL_REG_RMA_INTR 0x1000
+#define ADV_CTRL_REG_RES_BIT14 0x2000
+#define ADV_CTRL_REG_DPE_INTR 0x4000
+#define ADV_CTRL_REG_POWER_DONE 0x8000
+#define ADV_CTRL_REG_ANY_INTR 0xFF00
+
+#define ADV_CTRL_REG_CMD_RESET 0x00C6
+#define ADV_CTRL_REG_CMD_WR_IO_REG 0x00C5
+#define ADV_CTRL_REG_CMD_RD_IO_REG 0x00C4
+#define ADV_CTRL_REG_CMD_WR_PCI_CFG_SPACE 0x00C3
+#define ADV_CTRL_REG_CMD_RD_PCI_CFG_SPACE 0x00C2
+
+#define ADV_SCSI_CTRL_RSTOUT 0x2000
+
+#define AdvIsIntPending(port) \
+ (AdvReadWordRegister(port, IOPW_CTRL_REG) & ADV_CTRL_REG_HOST_INTR)
+
+/*
+ * SCSI_CFG0 Register bit definitions
+ */
+#define TIMER_MODEAB 0xC000 /* Watchdog, Second, and Select. Timer Ctrl. */
+#define PARITY_EN 0x2000 /* Enable SCSI Parity Error detection */
+#define EVEN_PARITY 0x1000 /* Select Even Parity */
+#define WD_LONG 0x0800 /* Watchdog Interval, 1: 57 min, 0: 13 sec */
+#define QUEUE_128 0x0400 /* Queue Size, 1: 128 byte, 0: 64 byte */
+#define PRIM_MODE 0x0100 /* Primitive SCSI mode */
+#define SCAM_EN 0x0080 /* Enable SCAM selection */
+#define SEL_TMO_LONG 0x0040 /* Sel/Resel Timeout, 1: 400 ms, 0: 1.6 ms */
+#define CFRM_ID 0x0020 /* SCAM id sel. confirm., 1: fast, 0: 6.4 ms */
+#define OUR_ID_EN 0x0010 /* Enable OUR_ID bits */
+#define OUR_ID 0x000F /* SCSI ID */
+
+/*
+ * SCSI_CFG1 Register bit definitions
+ */
+#define BIG_ENDIAN 0x8000 /* Enable Big Endian Mode MIO:15, EEP:15 */
+#define TERM_POL 0x2000 /* Terminator Polarity Ctrl. MIO:13, EEP:13 */
+#define SLEW_RATE 0x1000 /* SCSI output buffer slew rate */
+#define FILTER_SEL 0x0C00 /* Filter Period Selection */
+#define FLTR_DISABLE 0x0000 /* Input Filtering Disabled */
+#define FLTR_11_TO_20NS 0x0800 /* Input Filtering 11ns to 20ns */
+#define FLTR_21_TO_39NS 0x0C00 /* Input Filtering 21ns to 39ns */
+#define ACTIVE_DBL 0x0200 /* Disable Active Negation */
+#define DIFF_MODE 0x0100 /* SCSI differential Mode (Read-Only) */
+#define DIFF_SENSE 0x0080 /* 1: No SE cables, 0: SE cable (Read-Only) */
+#define TERM_CTL_SEL 0x0040 /* Enable TERM_CTL_H and TERM_CTL_L */
+#define TERM_CTL 0x0030 /* External SCSI Termination Bits */
+#define TERM_CTL_H 0x0020 /* Enable External SCSI Upper Termination */
+#define TERM_CTL_L 0x0010 /* Enable External SCSI Lower Termination */
+#define CABLE_DETECT 0x000F /* External SCSI Cable Connection Status */
+
+#define CABLE_ILLEGAL_A 0x7
+ /* x 0 0 0 | on on | Illegal (all 3 connectors are used) */
+
+#define CABLE_ILLEGAL_B 0xB
+ /* 0 x 0 0 | on on | Illegal (all 3 connectors are used) */
+
+/*
+ The following table details the SCSI_CFG1 Termination Polarity,
+ Termination Control and Cable Detect bits.
+
+ Cable Detect | Termination
+ Bit 3 2 1 0 | 5 4 | Notes
+ _____________|________|____________________
+ 1 1 1 0 | on on | Internal wide only
+ 1 1 0 1 | on on | Internal narrow only
+ 1 0 1 1 | on on | External narrow only
+ 0 x 1 1 | on on | External wide only
+ 1 1 0 0 | on off| Internal wide and internal narrow
+ 1 0 1 0 | on off| Internal wide and external narrow
+ 0 x 1 0 | off off| Internal wide and external wide
+ 1 0 0 1 | on off| Internal narrow and external narrow
+ 0 x 0 1 | on off| Internal narrow and external wide
+ 1 1 1 1 | on on | No devices are attached
+ x 0 0 0 | on on | Illegal (all 3 connectors are used)
+ 0 x 0 0 | on on | Illegal (all 3 connectors are used)
+
+ x means don't-care (either '0' or '1')
+
+ If term_pol (bit 13) is '0' (active-low terminator enable), then:
+ 'on' is '0' and 'off' is '1'.
+
+ If term_pol bit is '1' (meaning active-hi terminator enable), then:
+ 'on' is '1' and 'off' is '0'.
+ */
+
+/*
+ * MEM_CFG Register bit definitions
+ */
+#define BIOS_EN 0x40 /* BIOS Enable MIO:14,EEP:14 */
+#define FAST_EE_CLK 0x20 /* Diagnostic Bit */
+#define RAM_SZ 0x1C /* Specify size of RAM to RISC */
+#define RAM_SZ_2KB 0x00 /* 2 KB */
+#define RAM_SZ_4KB 0x04 /* 4 KB */
+#define RAM_SZ_8KB 0x08 /* 8 KB */
+#define RAM_SZ_16KB 0x0C /* 16 KB */
+#define RAM_SZ_32KB 0x10 /* 32 KB */
+#define RAM_SZ_64KB 0x14 /* 64 KB */
+
+/*
+ * DMA_CFG0 Register bit definitions
+ *
+ * This register is only accessible to the host.
+ */
+#define BC_THRESH_ENB 0x80 /* PCI DMA Start Conditions */
+#define FIFO_THRESH 0x70 /* PCI DMA FIFO Threshold */
+#define FIFO_THRESH_16B 0x00 /* 16 bytes */
+#define FIFO_THRESH_32B 0x20 /* 32 bytes */
+#define FIFO_THRESH_48B 0x30 /* 48 bytes */
+#define FIFO_THRESH_64B 0x40 /* 64 bytes */
+#define FIFO_THRESH_80B 0x50 /* 80 bytes (default) */
+#define FIFO_THRESH_96B 0x60 /* 96 bytes */
+#define FIFO_THRESH_112B 0x70 /* 112 bytes */
+#define START_CTL 0x0C /* DMA start conditions */
+#define START_CTL_TH 0x00 /* Wait threshold level (default) */
+#define START_CTL_ID 0x04 /* Wait SDMA/SBUS idle */
+#define START_CTL_THID 0x08 /* Wait threshold and SDMA/SBUS idle */
+#define START_CTL_EMFU 0x0C /* Wait SDMA FIFO empty/full */
+#define READ_CMD 0x03 /* Memory Read Method */
+#define READ_CMD_MR 0x00 /* Memory Read */
+#define READ_CMD_MRL 0x02 /* Memory Read Long */
+#define READ_CMD_MRM 0x03 /* Memory Read Multiple (default) */
+
+/* a_advlib.h */
+
+/*
+ * Adv Library Status Definitions
+ */
+#define ADV_TRUE 1
+#define ADV_FALSE 0
+#define ADV_NOERROR 1
+#define ADV_SUCCESS 1
+#define ADV_BUSY 0
+#define ADV_ERROR (-1)
+
+
+/*
+ * ASC_DVC_VAR 'warn_code' values
+ */
+#define ASC_WARN_EEPROM_CHKSUM 0x0002 /* EEP check sum error */
+#define ASC_WARN_EEPROM_TERMINATION 0x0004 /* EEP termination bad field */
+#define ASC_WARN_SET_PCI_CONFIG_SPACE 0x0080 /* PCI config space set error */
+#define ASC_WARN_ERROR 0xFFFF /* ADV_ERROR return */
+
+#define ADV_MAX_TID 15 /* max. target identifier */
+#define ADV_MAX_LUN 7 /* max. logical unit number */
+
+
+/*
+ * AscInitGetConfig() and AscInitAsc1000Driver() Definitions
+ *
+ * Error code values are set in ASC_DVC_VAR 'err_code'.
+ */
+#define ASC_IERR_WRITE_EEPROM 0x0001 /* write EEPROM error */
+#define ASC_IERR_MCODE_CHKSUM 0x0002 /* micro code check sum error */
+#define ASC_IERR_START_STOP_CHIP 0x0008 /* start/stop chip failed */
+#define ASC_IERR_CHIP_VERSION 0x0040 /* wrong chip version */
+#define ASC_IERR_SET_SCSI_ID 0x0080 /* set SCSI ID failed */
+#define ASC_IERR_BAD_SIGNATURE 0x0200 /* signature not found */
+#define ASC_IERR_ILLEGAL_CONNECTION 0x0400 /* Illegal cable connection */
+#define ASC_IERR_SINGLE_END_DEVICE 0x0800 /* Single-end used w/differential */
+#define ASC_IERR_REVERSED_CABLE 0x1000 /* Narrow flat cable reversed */
+#define ASC_IERR_RW_LRAM 0x8000 /* read/write local RAM error */
+
+/*
+ * Fixed locations of microcode operating variables.
+ */
+#define ASC_MC_CODE_BEGIN_ADDR 0x0028 /* microcode start address */
+#define ASC_MC_CODE_END_ADDR 0x002A /* microcode end address */
+#define ASC_MC_CODE_CHK_SUM 0x002C /* microcode code checksum */
+#define ASC_MC_STACK_BEGIN 0x002E /* microcode stack begin */
+#define ASC_MC_STACK_END 0x0030 /* microcode stack end */
+#define ASC_MC_VERSION_DATE 0x0038 /* microcode version */
+#define ASC_MC_VERSION_NUM 0x003A /* microcode number */
+#define ASCV_VER_SERIAL_W 0x003C /* used in dos_init */
+#define ASC_MC_BIOSMEM 0x0040 /* BIOS RISC Memory Start */
+#define ASC_MC_BIOSLEN 0x0050 /* BIOS RISC Memory Length */
+#define ASC_MC_HALTCODE 0x0094 /* microcode halt code */
+#define ASC_MC_CALLERPC 0x0096 /* microcode halt caller PC */
+#define ASC_MC_ADAPTER_SCSI_ID 0x0098 /* one ID byte + reserved */
+#define ASC_MC_ULTRA_ABLE 0x009C
+#define ASC_MC_SDTR_ABLE 0x009E
+#define ASC_MC_TAGQNG_ABLE 0x00A0
+#define ASC_MC_DISC_ENABLE 0x00A2
+#define ASC_MC_IDLE_CMD 0x00A6
+#define ASC_MC_IDLE_PARA_STAT 0x00A8
+#define ASC_MC_DEFAULT_SCSI_CFG0 0x00AC
+#define ASC_MC_DEFAULT_SCSI_CFG1 0x00AE
+#define ASC_MC_DEFAULT_MEM_CFG 0x00B0
+#define ASC_MC_DEFAULT_SEL_MASK 0x00B2
+#define ASC_MC_RISC_NEXT_READY 0x00B4
+#define ASC_MC_RISC_NEXT_DONE 0x00B5
+#define ASC_MC_SDTR_DONE 0x00B6
+#define ASC_MC_NUMBER_OF_QUEUED_CMD 0x00C0
+#define ASC_MC_NUMBER_OF_MAX_CMD 0x00D0
+#define ASC_MC_DEVICE_HSHK_CFG_TABLE 0x0100
+#define ASC_MC_WDTR_ABLE 0x0120 /* Wide Transfer TID bitmask. */
+#define ASC_MC_CONTROL_FLAG 0x0122 /* Microcode control flag. */
+#define ASC_MC_WDTR_DONE 0x0124
+#define ASC_MC_HOST_NEXT_READY 0x0128 /* Host Next Ready RQL Entry. */
+#define ASC_MC_HOST_NEXT_DONE 0x0129 /* Host Next Done RQL Entry. */
+
+/*
+ * BIOS LRAM variable absolute offsets.
+ */
+#define BIOS_CODESEG 0x54
+#define BIOS_CODELEN 0x56
+#define BIOS_SIGNATURE 0x58
+#define BIOS_VERSION 0x5A
+#define BIOS_SIGNATURE 0x58
+
+/*
+ * Microcode Control Flags
+ *
+ * Flags set by the Adv Library in RISC variable 'control_flag' (0x122)
+ * and handled by the microcode.
+ */
+#define CONTROL_FLAG_IGNORE_PERR 0x0001 /* Ignore DMA Parity Errors */
+
+/*
+ * ASC_MC_DEVICE_HSHK_CFG_TABLE microcode table or HSHK_CFG register format
+ */
+#define HSHK_CFG_WIDE_XFR 0x8000
+#define HSHK_CFG_RATE 0x0F00
+#define HSHK_CFG_OFFSET 0x001F
+
+/*
+ * LRAM RISC Queue Lists (LRAM addresses 0x1200 - 0x19FF)
+ *
+ * Each of the 255 Adv Library/Microcode RISC queue lists or mailboxes
+ * starting at LRAM address 0x1200 is 8 bytes and has the following
+ * structure. Only 253 of these are actually used for command queues.
+ */
+
+#define ASC_MC_RISC_Q_LIST_BASE 0x1200
+#define ASC_MC_RISC_Q_LIST_SIZE 0x0008
+#define ASC_MC_RISC_Q_TOTAL_CNT 0x00FF /* Num. queue slots in LRAM. */
+#define ASC_MC_RISC_Q_FIRST 0x0001
+#define ASC_MC_RISC_Q_LAST 0x00FF
+
+#define ASC_DEF_MAX_HOST_QNG 0xFD /* Max. number of host commands (253) */
+#define ASC_DEF_MIN_HOST_QNG 0x10 /* Min. number of host commands (16) */
+#define ASC_DEF_MAX_DVC_QNG 0x3F /* Max. number commands per device (63) */
+#define ASC_DEF_MIN_DVC_QNG 0x04 /* Min. number commands per device (4) */
+
+/* RISC Queue List structure - 8 bytes */
+#define RQL_FWD 0 /* forward pointer (1 byte) */
+#define RQL_BWD 1 /* backward pointer (1 byte) */
+#define RQL_STATE 2 /* state byte - free, ready, done, aborted (1 byte) */
+#define RQL_TID 3 /* request target id (1 byte) */
+#define RQL_PHYADDR 4 /* request physical pointer (4 bytes) */
+
+/* RISC Queue List state values */
+#define ASC_MC_QS_FREE 0x00
+#define ASC_MC_QS_READY 0x01
+#define ASC_MC_QS_DONE 0x40
+#define ASC_MC_QS_ABORTED 0x80
+
+/* RISC Queue List pointer values */
+#define ASC_MC_NULL_Q 0x00 /* NULL_Q == 0 */
+#define ASC_MC_BIOS_Q 0xFF /* BIOS_Q = 255 */
+
+/* ASC_SCSI_REQ_Q 'cntl' field values */
+#define ASC_MC_QC_START_MOTOR 0x02 /* Issue start motor. */
+#define ASC_MC_QC_NO_OVERRUN 0x04 /* Don't report overrun. */
+#define ASC_MC_QC_FIRST_DMA 0x08 /* Internal microcode flag. */
+#define ASC_MC_QC_ABORTED 0x10 /* Request aborted by host. */
+#define ASC_MC_QC_REQ_SENSE 0x20 /* Auto-Request Sense. */
+#define ASC_MC_QC_DOS_REQ 0x80 /* Request issued by DOS. */
+
+
+/*
+ * ASC_SCSI_REQ_Q 'a_flag' definitions
+ *
+ * The Adv Library should limit use to the lower nibble (4 bits) of
+ * a_flag. Drivers are free to use the upper nibble (4 bits) of a_flag.
+ */
+#define ADV_POLL_REQUEST 0x01 /* poll for request completion */
+#define ADV_SCSIQ_DONE 0x02 /* request done */
+
+/*
+ * Adapter temporary configuration structure
+ *
+ * This structure can be discarded after initialization. Don't add
+ * fields here needed after initialization.
+ *
+ * Field naming convention:
+ *
+ * *_enable indicates the field enables or disables a feature. The
+ * value of the field is never reset.
+ */
+typedef struct adv_dvc_cfg {
+ ushort disc_enable; /* enable disconnection */
+ uchar chip_version; /* chip version */
+ uchar termination; /* Term. Ctrl. bits 6-5 of SCSI_CFG1 register */
+ ushort pci_device_id; /* PCI device code number */
+ ushort lib_version; /* Adv Library version number */
+ ushort control_flag; /* Microcode Control Flag */
+ ushort mcode_date; /* Microcode date */
+ ushort mcode_version; /* Microcode version */
+ ushort pci_slot_info; /* high byte device/function number */
+ /* bits 7-3 device num., bits 2-0 function num. */
+ /* low byte bus num. */
+ ushort bios_boot_wait; /* BIOS boot time delay */
+ ushort serial1; /* EEPROM serial number word 1 */
+ ushort serial2; /* EEPROM serial number word 2 */
+ ushort serial3; /* EEPROM serial number word 3 */
+} ADV_DVC_CFG;
+
+/*
+ * Adapter operation variable structure.
+ *
+ * One structure is required per host adapter.
+ *
+ * Field naming convention:
+ *
+ * *_able indicates both whether a feature should be enabled or disabled
+ * and whether a device isi capable of the feature. At initialization
+ * this field may be set, but later if a device is found to be incapable
+ * of the feature, the field is cleared.
+ */
+typedef struct adv_dvc_var {
+ AdvPortAddr iop_base; /* I/O port address */
+ ushort err_code; /* fatal error code */
+ ushort bios_ctrl; /* BIOS control word, EEPROM word 12 */
+ Ptr2Func isr_callback; /* pointer to function, called in AdvISR() */
+ Ptr2Func sbreset_callback; /* pointer to function, called in AdvISR() */
+ ushort wdtr_able; /* try WDTR for a device */
+ ushort sdtr_able; /* try SDTR for a device */
+ ushort ultra_able; /* try SDTR Ultra speed for a device */
+ ushort tagqng_able; /* try tagged queuing with a device */
+ uchar max_dvc_qng; /* maximum number of tagged commands per device */
+ ushort start_motor; /* start motor command allowed */
+ uchar scsi_reset_wait; /* delay in seconds after scsi bus reset */
+ uchar chip_no; /* should be assigned by caller */
+ uchar max_host_qng; /* maximum number of Q'ed command allowed */
+ uchar cur_host_qng; /* total number of queue command */
+ uchar irq_no; /* IRQ number */
+ ushort no_scam; /* scam_tolerant of EEPROM */
+ ushort idle_cmd_done; /* microcode idle command done set by AdvISR() */
+ ulong drv_ptr; /* driver pointer to private structure */
+ uchar chip_scsi_id; /* chip SCSI target ID */
+ /*
+ * Note: The following fields will not be used after initialization. The
+ * driver may discard the buffer after initialization is done.
+ */
+ ADV_DVC_CFG *cfg; /* temporary configuration structure */
+} ADV_DVC_VAR;
+
+#define NO_OF_SG_PER_BLOCK 15
+
+typedef struct asc_sg_block {
+ uchar reserved1;
+ uchar reserved2;
+ uchar first_entry_no; /* starting entry number */
+ uchar last_entry_no; /* last entry number */
+ struct asc_sg_block *sg_ptr; /* links to the next sg block */
+ struct {
+ ulong sg_addr; /* SG element address */
+ ulong sg_count; /* SG element count */
+ } sg_list[NO_OF_SG_PER_BLOCK];
+} ADV_SG_BLOCK;
+
+/*
+ * ASC_SCSI_REQ_Q - microcode request structure
+ *
+ * All fields in this structure up to byte 60 are used by the microcode.
+ * The microcode makes assumptions about the size and ordering of fields
+ * in this structure. Do not change the structure definition here without
+ * coordinating the change with the microcode.
+ */
+typedef struct adv_scsi_req_q {
+ uchar cntl; /* Ucode flags and state (ASC_MC_QC_*). */
+ uchar sg_entry_cnt; /* SG element count. Zero for no SG. */
+ uchar target_id; /* Device target identifier. */
+ uchar target_lun; /* Device target logical unit number. */
+ ulong data_addr; /* Data buffer physical address. */
+ ulong data_cnt; /* Data count. Ucode sets to residual. */
+ ulong sense_addr; /* Sense buffer physical address. */
+ ulong srb_ptr; /* Driver request pointer. */
+ uchar a_flag; /* Adv Library flag field. */
+ uchar sense_len; /* Auto-sense length. Ucode sets to residual. */
+ uchar cdb_len; /* SCSI CDB length. */
+ uchar tag_code; /* SCSI-2 Tag Queue Code: 00, 20-22. */
+ uchar done_status; /* Completion status. */
+ uchar scsi_status; /* SCSI status byte. */
+ uchar host_status; /* Ucode host status. */
+ uchar ux_sg_ix; /* Ucode working SG variable. */
+ uchar cdb[12]; /* SCSI command block. */
+ ulong sg_real_addr; /* SG list physical address. */
+ struct adv_scsi_req_q *free_scsiq_link;
+ ulong ux_wk_data_cnt; /* Saved data count at disconnection. */
+ struct adv_scsi_req_q *scsiq_ptr;
+ ADV_SG_BLOCK *sg_list_ptr; /* SG list virtual address. */
+ /*
+ * End of microcode structure - 60 bytes. The rest of the structure
+ * is used by the Adv Library and ignored by the microcode.
+ */
+ ulong vsense_addr; /* Sense buffer virtual address. */
+ ulong vdata_addr; /* Data buffer virtual address. */
+ uchar orig_sense_len; /* Original length of sense buffer. */
+} ADV_SCSI_REQ_Q; /* BIOS - 70 bytes, DOS - 76 bytes, W95, WNT - 69 bytes */
+
+/*
+ * Microcode idle loop commands
+ */
+#define IDLE_CMD_COMPLETED 0
+#define IDLE_CMD_STOP_CHIP 0x0001
+#define IDLE_CMD_STOP_CHIP_SEND_INT 0x0002
+#define IDLE_CMD_SEND_INT 0x0004
+#define IDLE_CMD_ABORT 0x0008
+#define IDLE_CMD_DEVICE_RESET 0x0010
+#define IDLE_CMD_SCSI_RESET 0x0020
+
+/*
+ * AdvSendIdleCmd() flag definitions.
+ */
+#define ADV_NOWAIT 0x01
+
+/*
+ * Wait loop time out values.
+ */
+#define SCSI_WAIT_10_SEC 10 /* 10 seconds */
+#define SCSI_MS_PER_SEC 1000 /* milliseconds per second */
+
+/*
+ * Device drivers must define the following functions.
+ */
+STATIC int DvcEnterCritical(void);
+STATIC void DvcLeaveCritical(int);
+STATIC void DvcSleepMilliSecond(ulong);
+STATIC uchar DvcAdvReadPCIConfigByte(ADV_DVC_VAR *, ushort);
+STATIC void DvcAdvWritePCIConfigByte(ADV_DVC_VAR *, ushort, uchar);
+STATIC ulong DvcGetPhyAddr(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *,
+ uchar *, long *, int);
+STATIC void DvcDelayMicroSecond(ADV_DVC_VAR *, ushort);
+
+/*
+ * Adv Library functions available to drivers.
+ */
+STATIC int AdvExeScsiQueue(ADV_DVC_VAR *,
+ ADV_SCSI_REQ_Q *);
+STATIC int AdvISR(ADV_DVC_VAR *);
+STATIC int AdvInitGetConfig(ADV_DVC_VAR *);
+STATIC int AdvInitAsc3550Driver(ADV_DVC_VAR *);
+STATIC int AdvResetSB(ADV_DVC_VAR *);
+
+/*
+ * Internal Adv Library functions.
+ */
+STATIC int AdvSendIdleCmd(ADV_DVC_VAR *, ushort, ulong, int);
+STATIC void AdvResetChip(ADV_DVC_VAR *);
+STATIC int AdvSendScsiCmd(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *);
+STATIC void AdvInquiryHandling(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *);
+STATIC int AdvInitFromEEP(ADV_DVC_VAR *);
+STATIC ushort AdvGetEEPConfig(AdvPortAddr, ADVEEP_CONFIG *);
+STATIC void AdvSetEEPConfig(AdvPortAddr, ADVEEP_CONFIG *);
+STATIC void AdvWaitEEPCmd(AdvPortAddr);
+STATIC ushort AdvReadEEPWord(AdvPortAddr, int);
+STATIC void AdvResetSCSIBus(ADV_DVC_VAR *);
+
+/*
+ * PCI Bus Definitions
+ */
+#define AscPCICmdRegBits_BusMastering 0x0007
+#define AscPCICmdRegBits_ParErrRespCtrl 0x0040
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0)
+
+/* Read byte from a register. */
+#define AdvReadByteRegister(iop_base, reg_off) \
+ (inp((iop_base) + (reg_off)))
+
+/* Write byte to a register. */
+#define AdvWriteByteRegister(iop_base, reg_off, byte) \
+ (outp((iop_base) + (reg_off), (byte)))
+
+/* Read word (2 bytes) from a register. */
+#define AdvReadWordRegister(iop_base, reg_off) \
+ (inpw((iop_base) + (reg_off)))
+
+/* Write word (2 bytes) to a register. */
+#define AdvWriteWordRegister(iop_base, reg_off, word) \
+ (outpw((iop_base) + (reg_off), (word)))
+
+/* Read byte from LRAM. */
+#define AdvReadByteLram(iop_base, addr, byte) \
+do { \
+ outpw((iop_base) + IOPW_RAM_ADDR, (addr)); \
+ (byte) = inp((iop_base) + IOPB_RAM_DATA); \
+} while (0)
+
+/* Write byte to LRAM. */
+#define AdvWriteByteLram(iop_base, addr, byte) \
+ (outpw((iop_base) + IOPW_RAM_ADDR, (addr)), \
+ outp((iop_base) + IOPB_RAM_DATA, (byte)))
+
+/* Read word (2 bytes) from LRAM. */
+#define AdvReadWordLram(iop_base, addr, word) \
+do { \
+ outpw((iop_base) + IOPW_RAM_ADDR, (addr)); \
+ (word) = inpw((iop_base) + IOPW_RAM_DATA); \
+} while (0)
+
+/* Write word (2 bytes) to LRAM. */
+#define AdvWriteWordLram(iop_base, addr, word) \
+ (outpw((iop_base) + IOPW_RAM_ADDR, (addr)), \
+ outpw((iop_base) + IOPW_RAM_DATA, (word)))
+
+/* Write double word (4 bytes) to LRAM */
+/* Because of unspecified C language ordering don't use auto-increment. */
+#define AdvWriteDWordLram(iop_base, addr, dword) \
+ ((outpw((iop_base) + IOPW_RAM_ADDR, (addr)), \
+ outpw((iop_base) + IOPW_RAM_DATA, (ushort) ((dword) & 0xFFFF))), \
+ (outpw((iop_base) + IOPW_RAM_ADDR, (addr) + 2), \
+ outpw((iop_base) + IOPW_RAM_DATA, (ushort) ((dword >> 16) & 0xFFFF))))
+
+/* Read word (2 bytes) from LRAM assuming that the address is already set. */
+#define AdvReadWordAutoIncLram(iop_base) \
+ (inpw((iop_base) + IOPW_RAM_DATA))
+
+/* Write word (2 bytes) to LRAM assuming that the address is already set. */
+#define AdvWriteWordAutoIncLram(iop_base, word) \
+ (outpw((iop_base) + IOPW_RAM_DATA, (word)))
+
+#else /* version >= v1,3,0 */
+
+/* Read byte from a register. */
+#define AdvReadByteRegister(iop_base, reg_off) \
+ (ADV_MEM_READB((iop_base) + (reg_off)))
+
+/* Write byte to a register. */
+#define AdvWriteByteRegister(iop_base, reg_off, byte) \
+ (ADV_MEM_WRITEB((iop_base) + (reg_off), (byte)))
+
+/* Read word (2 bytes) from a register. */
+#define AdvReadWordRegister(iop_base, reg_off) \
+ (ADV_MEM_READW((iop_base) + (reg_off)))
+
+/* Write word (2 bytes) to a register. */
+#define AdvWriteWordRegister(iop_base, reg_off, word) \
+ (ADV_MEM_WRITEW((iop_base) + (reg_off), (word)))
+
+/* Read byte from LRAM. */
+#define AdvReadByteLram(iop_base, addr, byte) \
+do { \
+ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)); \
+ (byte) = ADV_MEM_READB((iop_base) + IOPB_RAM_DATA); \
+} while (0)
+
+/* Write byte to LRAM. */
+#define AdvWriteByteLram(iop_base, addr, byte) \
+ (ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)), \
+ ADV_MEM_WRITEB((iop_base) + IOPB_RAM_DATA, (byte)))
+
+/* Read word (2 bytes) from LRAM. */
+#define AdvReadWordLram(iop_base, addr, word) \
+do { \
+ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)); \
+ (word) = ADV_MEM_READW((iop_base) + IOPW_RAM_DATA); \
+} while (0)
+
+/* Write word (2 bytes) to LRAM. */
+#define AdvWriteWordLram(iop_base, addr, word) \
+ (ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)), \
+ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, (word)))
+
+/* Write double word (4 bytes) to LRAM */
+/* Because of unspecified C language ordering don't use auto-increment. */
+#define AdvWriteDWordLram(iop_base, addr, dword) \
+ ((ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr)), \
+ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, \
+ (ushort) ((dword) & 0xFFFF))), \
+ (ADV_MEM_WRITEW((iop_base) + IOPW_RAM_ADDR, (addr) + 2), \
+ ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, \
+ (ushort) ((dword >> 16) & 0xFFFF))))
+
+/* Read word (2 bytes) from LRAM assuming that the address is already set. */
+#define AdvReadWordAutoIncLram(iop_base) \
+ (ADV_MEM_READW((iop_base) + IOPW_RAM_DATA))
+
+/* Write word (2 bytes) to LRAM assuming that the address is already set. */
+#define AdvWriteWordAutoIncLram(iop_base, word) \
+ (ADV_MEM_WRITEW((iop_base) + IOPW_RAM_DATA, (word)))
+
+#endif /* version >= v1,3,0 */
+
+/*
+ * Define macro to check for Condor signature.
+ *
+ * Evaluate to ADV_TRUE if a Condor chip is found the specified port
+ * address 'iop_base'. Otherwise evalue to ADV_FALSE.
+ */
+#define AdvFindSignature(iop_base) \
+ (((AdvReadByteRegister((iop_base), IOPB_CHIP_ID_1) == \
+ ADV_CHIP_ID_BYTE) && \
+ (AdvReadWordRegister((iop_base), IOPW_CHIP_ID_0) == \
+ ADV_CHIP_ID_WORD)) ? ADV_TRUE : ADV_FALSE)
+
+/*
+ * Define macro to Return the version number of the chip at 'iop_base'.
+ *
+ * The second parameter 'bus_type' is currently unused.
+ */
+#define AdvGetChipVersion(iop_base, bus_type) \
+ AdvReadByteRegister((iop_base), IOPB_CHIP_TYPE_REV)
+
+/*
+ * Abort an SRB in the chip's RISC Memory. The 'srb_ptr' argument must
+ * match the ASC_SCSI_REQ_Q 'srb_ptr' field.
+ *
+ * If the request has not yet been sent to the device it will simply be
+ * aborted from RISC memory. If the request is disconnected it will be
+ * aborted on reselection by sending an Abort Message to the target ID.
+ *
+ * Return value:
+ * ADV_TRUE(1) - Queue was successfully aborted.
+ * ADV_FALSE(0) - Queue was not found on the active queue list.
+ */
+#define AdvAbortSRB(asc_dvc, srb_ptr) \
+ AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_ABORT, \
+ (ulong) (srb_ptr), 0)
+
+/*
+ * Send a Bus Device Reset Message to the specified target ID.
+ *
+ * All outstanding commands will be purged if sending the
+ * Bus Device Reset Message is successful.
+ *
+ * Return Value:
+ * ADV_TRUE(1) - All requests on the target are purged.
+ * ADV_FALSE(0) - Couldn't issue Bus Device Reset Message; Requests
+ * are not purged.
+ */
+#define AdvResetDevice(asc_dvc, target_id) \
+ AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_DEVICE_RESET, \
+ (ulong) (target_id), 0)
+
+/*
+ * SCSI Wide Type definition.
+ */
+#define ADV_SCSI_BIT_ID_TYPE ushort
+
+/*
+ * AdvInitScsiTarget() 'cntl_flag' options.
+ */
+#define ADV_SCAN_LUN 0x01
+#define ADV_CAPINFO_NOLUN 0x02
+
+/*
+ * Convert target id to target id bit mask.
+ */
+#define ADV_TID_TO_TIDMASK(tid) (0x01 << ((tid) & ADV_MAX_TID))
+
+/*
+ * ASC_SCSI_REQ_Q 'done_status' and 'host_status' return values.
+ */
+
+#define QD_NO_STATUS 0x00 /* Request not completed yet. */
+#define QD_NO_ERROR 0x01
+#define QD_ABORTED_BY_HOST 0x02
+#define QD_WITH_ERROR 0x04
+
+#define QHSTA_NO_ERROR 0x00
+#define QHSTA_M_SEL_TIMEOUT 0x11
+#define QHSTA_M_DATA_OVER_RUN 0x12
+#define QHSTA_M_UNEXPECTED_BUS_FREE 0x13
+#define QHSTA_M_QUEUE_ABORTED 0x15
+#define QHSTA_M_SXFR_SDMA_ERR 0x16 /* SXFR_STATUS SCSI DMA Error */
+#define QHSTA_M_SXFR_SXFR_PERR 0x17 /* SXFR_STATUS SCSI Bus Parity Error */
+#define QHSTA_M_RDMA_PERR 0x18 /* RISC PCI DMA parity error */
+#define QHSTA_M_SXFR_OFF_UFLW 0x19 /* SXFR_STATUS Offset Underflow */
+#define QHSTA_M_SXFR_OFF_OFLW 0x20 /* SXFR_STATUS Offset Overflow */
+#define QHSTA_M_SXFR_WD_TMO 0x21 /* SXFR_STATUS Watchdog Timeout */
+#define QHSTA_M_SXFR_DESELECTED 0x22 /* SXFR_STATUS Deselected */
+/* Note: QHSTA_M_SXFR_XFR_OFLW is identical to QHSTA_M_DATA_OVER_RUN. */
+#define QHSTA_M_SXFR_XFR_OFLW 0x12 /* SXFR_STATUS Transfer Overflow */
+#define QHSTA_M_SXFR_XFR_PH_ERR 0x24 /* SXFR_STATUS Transfer Phase Error */
+#define QHSTA_M_SXFR_UNKNOWN_ERROR 0x25 /* SXFR_STATUS Unknown Error */
+#define QHSTA_M_WTM_TIMEOUT 0x41
+#define QHSTA_M_BAD_CMPL_STATUS_IN 0x42
+#define QHSTA_M_NO_AUTO_REQ_SENSE 0x43
+#define QHSTA_M_AUTO_REQ_SENSE_FAIL 0x44
+#define QHSTA_M_INVALID_DEVICE 0x45 /* Bad target ID */
+
+typedef int (* ADV_ISR_CALLBACK)
+ (ADV_DVC_VAR *, ADV_SCSI_REQ_Q *);
+
+typedef int (* ADV_SBRESET_CALLBACK)
+ (ADV_DVC_VAR *);
+
+/*
+ * Default EEPROM Configuration structure defined in a_init.c.
+ */
+extern ADVEEP_CONFIG Default_EEPROM_Config;
+
+/*
+ * DvcGetPhyAddr() flag arguments
+ */
+#define ADV_IS_SCSIQ_FLAG 0x01 /* 'addr' is ASC_SCSI_REQ_Q pointer */
+#define ADV_ASCGETSGLIST_VADDR 0x02 /* 'addr' is AscGetSGList() virtual addr */
+#define ADV_IS_SENSE_FLAG 0x04 /* 'addr' is sense virtual pointer */
+#define ADV_IS_DATA_FLAG 0x08 /* 'addr' is data virtual pointer */
+#define ADV_IS_SGLIST_FLAG 0x10 /* 'addr' is sglist virtual pointer */
+
+/* Return the address that is aligned at the next doubleword >= to 'addr'. */
+#define ADV_DWALIGN(addr) (((ulong) (addr) + 0x3) & ~0x3)
+
+/*
+ * Total contiguous memory needed for driver SG blocks.
+ *
+ * ADV_MAX_SG_LIST must be defined by a driver. It is the maximum
+ * number of scatter-gather elements the driver supports in a
+ * single request.
+ */
+
+#ifndef ADV_MAX_SG_LIST
+Forced Error: Driver must define ADV_MAX_SG_LIST.
+#endif /* ADV_MAX_SG_LIST */
+
+#define ADV_SG_LIST_MAX_BYTE_SIZE \
+ (sizeof(ADV_SG_BLOCK) * \
+ ((ADV_MAX_SG_LIST + (NO_OF_SG_PER_BLOCK - 1))/NO_OF_SG_PER_BLOCK))
+
+/*
+ * A driver may optionally define the assertion macro ADV_ASSERT() in
+ * its d_os_dep.h file. If the macro has not already been defined,
+ * then define the macro to a no-op.
+ */
+#ifndef ADV_ASSERT
+#define ADV_ASSERT(a)
+#endif /* ADV_ASSERT */
+
+
+/*
+ * --- Driver Constants and Macros
+ */
+
+#define ASC_NUM_BOARD_SUPPORTED 16
+#define ASC_NUM_IOPORT_PROBE 4
+#define ASC_NUM_BUS 4
+
+/* Reference Scsi_Host hostdata */
+#define ASC_BOARDP(host) ((asc_board_t *) &((host)->hostdata))
+
+/* asc_board_t flags */
+#define ASC_HOST_IN_RESET 0x01
+#define ASC_HOST_IN_ABORT 0x02
+#define ASC_IS_WIDE_BOARD 0x04 /* AdvanSys Wide Board */
+#define ASC_SELECT_QUEUE_DEPTHS 0x08
+
+#define ASC_NARROW_BOARD(boardp) (((boardp)->flags & ASC_IS_WIDE_BOARD) == 0)
+#define ASC_WIDE_BOARD(boardp) ((boardp)->flags & ASC_IS_WIDE_BOARD)
+
+#define NO_ISA_DMA 0xff /* No ISA DMA Channel Used */
+
+/*
+ * If the Linux kernel version supports freeing initialization code
+ * and data after loading, define macros for this purpose. These macros
+ * are not used when the driver is built as a module, cf. linux/init.h.
+ */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,23)
+#define ASC_INITFUNC(func) func
+#define ASC_INITDATA
+#define ASC_INIT
+#else /* version >= v2.1.23 */
+#define ASC_INITFUNC(func) __initfunc(func)
+#define ASC_INITDATA __initdata
+#define ASC_INIT __init
+#endif /* version >= v2.1.23 */
+
+#define ASC_INFO_SIZE 128 /* advansys_info() line size */
+
+/* /proc/scsi/advansys/[0...] related definitions */
+#define ASC_PRTBUF_SIZE 2048
+#define ASC_PRTLINE_SIZE 160
+
+#define ASC_PRT_NEXT() \
+ if (cp) { \
+ totlen += len; \
+ leftlen -= len; \
+ if (leftlen == 0) { \
+ return totlen; \
+ } \
+ cp += len; \
+ }
+
+#define ASC_MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+/* Asc Library return codes */
+#define ASC_TRUE 1
+#define ASC_FALSE 0
+#define ASC_NOERROR 1
+#define ASC_BUSY 0
+#define ASC_ERROR (-1)
+
+/* Scsi_Cmnd function return codes */
+#define STATUS_BYTE(byte) (byte)
+#define MSG_BYTE(byte) ((byte) << 8)
+#define HOST_BYTE(byte) ((byte) << 16)
+#define DRIVER_BYTE(byte) ((byte) << 24)
+
+/*
+ * The following definitions and macros are OS independent interfaces to
+ * the queue functions:
+ * REQ - SCSI request structure
+ * REQP - pointer to SCSI request structure
+ * REQPTID(reqp) - reqp's target id
+ * REQPNEXT(reqp) - reqp's next pointer
+ * REQPNEXTP(reqp) - pointer to reqp's next pointer
+ * REQPTIME(reqp) - reqp's time stamp value
+ * REQTIMESTAMP() - system time stamp value
+ */
+typedef Scsi_Cmnd REQ, *REQP;
+#define REQPNEXT(reqp) ((REQP) ((reqp)->host_scribble))
+#define REQPNEXTP(reqp) ((REQP *) &((reqp)->host_scribble))
+#define REQPTID(reqp) ((reqp)->target)
+#define REQPTIME(reqp) ((reqp)->SCp.this_residual)
+#define REQTIMESTAMP() (jiffies)
+
+#define REQTIMESTAT(function, ascq, reqp, tid) \
+{ \
+ /*
+ * If the request time stamp is less than the system time stamp, then \
+ * maybe the system time stamp wrapped. Set the request time to zero.\
+ */ \
+ if (REQPTIME(reqp) <= REQTIMESTAMP()) { \
+ REQPTIME(reqp) = REQTIMESTAMP() - REQPTIME(reqp); \
+ } else { \
+ /* Indicate an error occurred with the assertion. */ \
+ ASC_ASSERT(REQPTIME(reqp) <= REQTIMESTAMP()); \
+ REQPTIME(reqp) = 0; \
+ } \
+ /* Handle first minimum time case without external initialization. */ \
+ if (((ascq)->q_tot_cnt[tid] == 1) || \
+ (REQPTIME(reqp) < (ascq)->q_min_tim[tid])) { \
+ (ascq)->q_min_tim[tid] = REQPTIME(reqp); \
+ ASC_DBG3(1, "%s: new q_min_tim[%d] %u\n", \
+ (function), (tid), (ascq)->q_min_tim[tid]); \
+ } \
+ if (REQPTIME(reqp) > (ascq)->q_max_tim[tid]) { \
+ (ascq)->q_max_tim[tid] = REQPTIME(reqp); \
+ ASC_DBG3(1, "%s: new q_max_tim[%d] %u\n", \
+ (function), tid, (ascq)->q_max_tim[tid]); \
+ } \
+ (ascq)->q_tot_tim[tid] += REQPTIME(reqp); \
+ /* Reset the time stamp field. */ \
+ REQPTIME(reqp) = 0; \
+}
+
+/* asc_enqueue() flags */
+#define ASC_FRONT 1
+#define ASC_BACK 2
+
+/* asc_dequeue_list() argument */
+#define ASC_TID_ALL (-1)
+
+/* Return non-zero, if the queue is empty. */
+#define ASC_QUEUE_EMPTY(ascq) ((ascq)->q_tidmask == 0)
+
+/* PCI configuration declarations */
+
+#define PCI_BASE_CLASS_PREDEFINED 0x00
+#define PCI_BASE_CLASS_MASS_STORAGE 0x01
+#define PCI_BASE_CLASS_NETWORK 0x02
+#define PCI_BASE_CLASS_DISPLAY 0x03
+#define PCI_BASE_CLASS_MULTIMEDIA 0x04
+#define PCI_BASE_CLASS_MEMORY_CONTROLLER 0x05
+#define PCI_BASE_CLASS_BRIDGE_DEVICE 0x06
+
+/* MASS STORAGE */
+#define PCI_SUB_CLASS_SCSI_CONTROLLER 0x00
+#define PCI_SUB_CLASS_IDE_CONTROLLER 0x01
+#define PCI_SUB_CLASS_FLOPPY_DISK_CONTROLLER 0x02
+#define PCI_SUB_CLASS_IPI_BUS_CONTROLLER 0x03
+#define PCI_SUB_CLASS_OTHER_MASS_CONTROLLER 0x80
+
+/* NETWORK CONTROLLER */
+#define PCI_SUB_CLASS_ETHERNET_CONTROLLER 0x00
+#define PCI_SUB_CLASS_TOKEN_RING_CONTROLLER 0x01
+#define PCI_SUB_CLASS_FDDI_CONTROLLER 0x02
+#define PCI_SUB_CLASS_OTHER_NETWORK_CONTROLLER 0x80
+
+/* DISPLAY CONTROLLER */
+#define PCI_SUB_CLASS_VGA_CONTROLLER 0x00
+#define PCI_SUB_CLASS_XGA_CONTROLLER 0x01
+#define PCI_SUB_CLASS_OTHER_DISPLAY_CONTROLLER 0x80
+
+/* MULTIMEDIA CONTROLLER */
+#define PCI_SUB_CLASS_VIDEO_DEVICE 0x00
+#define PCI_SUB_CLASS_AUDIO_DEVICE 0x01
+#define PCI_SUB_CLASS_OTHER_MULTIMEDIA_DEVICE 0x80
+
+/* MEMORY CONTROLLER */
+#define PCI_SUB_CLASS_RAM_CONTROLLER 0x00
+#define PCI_SUB_CLASS_FLASH_CONTROLLER 0x01
+#define PCI_SUB_CLASS_OTHER_MEMORY_CONTROLLER 0x80
+
+/* BRIDGE CONTROLLER */
+#define PCI_SUB_CLASS_HOST_BRIDGE_CONTROLLER 0x00
+#define PCI_SUB_CLASS_ISA_BRIDGE_CONTROLLER 0x01
+#define PCI_SUB_CLASS_EISA_BRIDGE_CONTROLLER 0x02
+#define PCI_SUB_CLASS_MC_BRIDGE_CONTROLLER 0x03
+#define PCI_SUB_CLASS_PCI_TO_PCI_BRIDGE_CONTROLLER 0x04
+#define PCI_SUB_CLASS_PCMCIA_BRIDGE_CONTROLLER 0x05
+#define PCI_SUB_CLASS_OTHER_BRIDGE_CONTROLLER 0x80
+
+#define PCI_MAX_SLOT 0x1F
+#define PCI_MAX_BUS 0xFF
+#define PCI_IOADDRESS_MASK 0xFFFE
+#define ASC_PCI_VENDORID 0x10CD
+#define ASC_PCI_DEVICE_ID_CNT 4 /* PCI Device ID count. */
+#define ASC_PCI_DEVICE_ID_1100 0x1100
+#define ASC_PCI_DEVICE_ID_1200 0x1200
+#define ASC_PCI_DEVICE_ID_1300 0x1300
+#define ASC_PCI_DEVICE_ID_2300 0x2300
+
+/* PCI IO Port Addresses to generate special cycle */
+
+#define PCI_CONFIG_ADDRESS_MECH1 0x0CF8
+#define PCI_CONFIG_DATA_MECH1 0x0CFC
+
+#define PCI_CONFIG_FORWARD_REGISTER 0x0CFA /* 0=type 0; 1=type 1; */
+
+#define PCI_CONFIG_BUS_NUMBER_MASK 0x00FF0000
+#define PCI_CONFIG_DEVICE_FUNCTION_MASK 0x0000FF00
+#define PCI_CONFIG_REGISTER_NUMBER_MASK 0x000000F8
+
+#define PCI_DEVICE_FOUND 0x0000
+#define PCI_DEVICE_NOT_FOUND 0xffff
+
+#define SUBCLASS_OFFSET 0x0A
+#define CLASSCODE_OFFSET 0x0B
+#define VENDORID_OFFSET 0x00
+#define DEVICEID_OFFSET 0x02
+
+#ifndef ADVANSYS_STATS
+#define ASC_STATS(shp, counter)
+#define ASC_STATS_ADD(shp, counter, count)
+#else /* ADVANSYS_STATS */
+#define ASC_STATS(shp, counter) \
+ (ASC_BOARDP(shp)->asc_stats.counter++)
+
+#define ASC_STATS_ADD(shp, counter, count) \
+ (ASC_BOARDP(shp)->asc_stats.counter += (count))
+#endif /* ADVANSYS_STATS */
+
+#define ASC_CEILING(val, unit) (((val) + ((unit) - 1))/(unit))
+
+/* If the result wraps when calculating tenths, return 0. */
+#define ASC_TENTHS(num, den) \
+ (((10 * ((num)/(den))) > (((num) * 10)/(den))) ? \
+ 0 : ((((num) * 10)/(den)) - (10 * ((num)/(den)))))
+
+/*
+ * Display a message to the console.
+ */
+#define ASC_PRINT(s) \
+ { \
+ printk("advansys: "); \
+ printk(s); \
+ }
+
+#define ASC_PRINT1(s, a1) \
+ { \
+ printk("advansys: "); \
+ printk((s), (a1)); \
+ }
+
+#define ASC_PRINT2(s, a1, a2) \
+ { \
+ printk("advansys: "); \
+ printk((s), (a1), (a2)); \
+ }
+
+#define ASC_PRINT3(s, a1, a2, a3) \
+ { \
+ printk("advansys: "); \
+ printk((s), (a1), (a2), (a3)); \
+ }
+
+#define ASC_PRINT4(s, a1, a2, a3, a4) \
+ { \
+ printk("advansys: "); \
+ printk((s), (a1), (a2), (a3), (a4)); \
+ }
+
+
+#ifndef ADVANSYS_DEBUG
+
+#define ASC_DBG(lvl, s)
+#define ASC_DBG1(lvl, s, a1)
+#define ASC_DBG2(lvl, s, a1, a2)
+#define ASC_DBG3(lvl, s, a1, a2, a3)
+#define ASC_DBG4(lvl, s, a1, a2, a3, a4)
+#define ASC_DBG_PRT_SCSI_HOST(lvl, s)
+#define ASC_DBG_PRT_SCSI_CMND(lvl, s)
+#define ASC_DBG_PRT_ASC_SCSI_Q(lvl, scsiqp)
+#define ASC_DBG_PRT_ADV_SCSI_REQ_Q(lvl, scsiqp)
+#define ASC_DBG_PRT_ASC_QDONE_INFO(lvl, qdone)
+#define ADV_DBG_PRT_ADV_SCSI_REQ_Q(lvl, scsiqp)
+#define ASC_DBG_PRT_HEX(lvl, name, start, length)
+#define ASC_DBG_PRT_CDB(lvl, cdb, len)
+#define ASC_DBG_PRT_SENSE(lvl, sense, len)
+#define ASC_DBG_PRT_INQUIRY(lvl, inq, len)
+
+#else /* ADVANSYS_DEBUG */
+
+/*
+ * Debugging Message Levels:
+ * 0: Errors Only
+ * 1: High-Level Tracing
+ * 2-N: Verbose Tracing
+ */
+
+#define ASC_DBG(lvl, s) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk(s); \
+ } \
+ }
+
+#define ASC_DBG1(lvl, s, a1) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk((s), (a1)); \
+ } \
+ }
+
+#define ASC_DBG2(lvl, s, a1, a2) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk((s), (a1), (a2)); \
+ } \
+ }
+
+#define ASC_DBG3(lvl, s, a1, a2, a3) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk((s), (a1), (a2), (a3)); \
+ } \
+ }
+
+#define ASC_DBG4(lvl, s, a1, a2, a3, a4) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk((s), (a1), (a2), (a3), (a4)); \
+ } \
+ }
+
+#define ASC_DBG_PRT_SCSI_HOST(lvl, s) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_scsi_host(s); \
+ } \
+ }
+
+#define ASC_DBG_PRT_SCSI_CMND(lvl, s) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_scsi_cmnd(s); \
+ } \
+ }
+
+#define ASC_DBG_PRT_ASC_SCSI_Q(lvl, scsiqp) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_asc_scsi_q(scsiqp); \
+ } \
+ }
+
+#define ASC_DBG_PRT_ASC_QDONE_INFO(lvl, qdone) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_asc_qdone_info(qdone); \
+ } \
+ }
+
+#define ASC_DBG_PRT_ADV_SCSI_REQ_Q(lvl, scsiqp) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_adv_scsi_req_q(scsiqp); \
+ } \
+ }
+
+#define ASC_DBG_PRT_HEX(lvl, name, start, length) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_hex((name), (start), (length)); \
+ } \
+ }
+
+#define ASC_DBG_PRT_CDB(lvl, cdb, len) \
+ ASC_DBG_PRT_HEX((lvl), "CDB", (uchar *) (cdb), (len));
+
+#define ASC_DBG_PRT_SENSE(lvl, sense, len) \
+ ASC_DBG_PRT_HEX((lvl), "SENSE", (uchar *) (sense), (len));
+
+#define ASC_DBG_PRT_INQUIRY(lvl, inq, len) \
+ ASC_DBG_PRT_HEX((lvl), "INQUIRY", (uchar *) (inq), (len));
+#endif /* ADVANSYS_DEBUG */
+
+#ifndef ADVANSYS_ASSERT
+#define ASC_ASSERT(a)
+#else /* ADVANSYS_ASSERT */
+
+#define ASC_ASSERT(a) \
+ { \
+ if (!(a)) { \
+ printk("ASC_ASSERT() Failure: file %s, line %d\n", \
+ __FILE__, __LINE__); \
+ } \
+ }
+
+#endif /* ADVANSYS_ASSERT */
+
+
+/*
+ * --- Driver Structures
+ */
+
+#ifdef ADVANSYS_STATS
+
+/* Per board statistics structure */
+struct asc_stats {
+ /* Driver Entrypoint Statistics */
+ ulong command; /* # calls to advansys_command() */
+ ulong queuecommand; /* # calls to advansys_queuecommand() */
+ ulong abort; /* # calls to advansys_abort() */
+ ulong reset; /* # calls to advansys_reset() */
+ ulong biosparam; /* # calls to advansys_biosparam() */
+ ulong interrupt; /* # advansys_interrupt() calls */
+ ulong callback; /* # calls to asc/adv_isr_callback() */
+ ulong done; /* # calls to request's scsi_done function */
+ ulong build_error; /* # asc/adv_build_req() ASC_ERROR returns. */
+ ulong adv_build_noreq; /* # adv_build_req() adv_req_t alloc. fail. */
+ ulong adv_build_nosg; /* # adv_build_req() adv_sgblk_t alloc. fail. */
+ /* AscExeScsiQueue()/AdvExeScsiQueue() Statistics */
+ ulong exe_noerror; /* # ASC_NOERROR returns. */
+ ulong exe_busy; /* # ASC_BUSY returns. */
+ ulong exe_error; /* # ASC_ERROR returns. */
+ ulong exe_unknown; /* # unknown returns. */
+ /* Data Transfer Statistics */
+ ulong cont_cnt; /* # non-scatter-gather I/O requests received */
+ ulong cont_xfer; /* # contiguous transfer 512-bytes */
+ ulong sg_cnt; /* # scatter-gather I/O requests received */
+ ulong sg_elem; /* # scatter-gather elements */
+ ulong sg_xfer; /* # scatter-gather transfer 512-bytes */
+};
+#endif /* ADVANSYS_STATS */
+
+/*
+ * Request queuing structure
+ */
+typedef struct asc_queue {
+ ADV_SCSI_BIT_ID_TYPE q_tidmask; /* queue mask */
+ REQP q_first[ADV_MAX_TID+1]; /* first queued request */
+ REQP q_last[ADV_MAX_TID+1]; /* last queued request */
+#ifdef ADVANSYS_STATS
+ short q_cur_cnt[ADV_MAX_TID+1]; /* current queue count */
+ short q_max_cnt[ADV_MAX_TID+1]; /* maximum queue count */
+ ulong q_tot_cnt[ADV_MAX_TID+1]; /* total enqueue count */
+ ulong q_tot_tim[ADV_MAX_TID+1]; /* total time queued */
+ ushort q_max_tim[ADV_MAX_TID+1]; /* maximum time queued */
+ ushort q_min_tim[ADV_MAX_TID+1]; /* minimum time queued */
+#endif /* ADVANSYS_STATS */
+} asc_queue_t;
+
+/*
+ * Adv Library Request Structures
+ *
+ * The following two se structures are used to process Wide Board requests.
+ * One structure is needed for each command received from the Mid-Level SCSI
+ * driver.
+ *
+ * The ADV_SCSI_REQ_Q structure in adv_req_t is passed to the Adv Library
+ * and microcode with the ADV_SCSI_REQ_Q field 'srb_ptr' pointing to the
+ * adv_req_t. The adv_req_t structure 'cmndp' field in turn points to the
+ * Mid-Level SCSI request structure.
+ *
+ * The adv_sgblk_t structure is used to handle requests that include
+ * scatter-gather elements.
+ */
+typedef struct adv_sgblk {
+ ADV_SG_BLOCK sg_block[ADV_NUM_SG_BLOCK + ADV_NUM_PAGE_CROSSING];
+ uchar align2[4]; /* Sgblock structure padding. */
+ struct adv_sgblk *next_sgblkp; /* Next scatter-gather structure. */
+} adv_sgblk_t;
+
+typedef struct adv_req {
+ ADV_SCSI_REQ_Q scsi_req_q; /* Adv Library request structure. */
+ uchar align1[4]; /* Request structure padding. */
+ Scsi_Cmnd *cmndp; /* Mid-Level SCSI command pointer. */
+ adv_sgblk_t *sgblkp; /* Adv Library scatter-gather pointer. */
+ struct adv_req *next_reqp; /* Next Request Structure. */
+} adv_req_t;
+
+/*
+ * Structure allocated for each board.
+ *
+ * This structure is allocated by scsi_register() at the end
+ * of the 'Scsi_Host' structure starting at the 'hostdata'
+ * field. It is guaranteed to be allocated from DMA-able memory.
+ */
+typedef struct asc_board {
+ int id; /* Board Id */
+ uint flags; /* Board flags */
+ union {
+ ASC_DVC_VAR asc_dvc_var; /* Narrow board */
+ ADV_DVC_VAR adv_dvc_var; /* Wide board */
+ } dvc_var;
+ union {
+ ASC_DVC_CFG asc_dvc_cfg; /* Narrow board */
+ ADV_DVC_CFG adv_dvc_cfg; /* Wide board */
+ } dvc_cfg;
+ asc_queue_t active; /* Active command queue */
+ asc_queue_t waiting; /* Waiting command queue */
+ asc_queue_t done; /* Done command queue */
+ ADV_SCSI_BIT_ID_TYPE init_tidmask; /* Target init./valid mask */
+ Scsi_Device *device[ADV_MAX_TID+1]; /* Mid-Level Scsi Device */
+ ushort reqcnt[ADV_MAX_TID+1]; /* Starvation request count */
+#if ASC_QUEUE_FLOW_CONTROL
+ ushort nerrcnt[ADV_MAX_TID+1]; /* No error request count */
+#endif /* ASC_QUEUE_FLOW_CONTROL */
+ ADV_SCSI_BIT_ID_TYPE queue_full; /* Queue full mask */
+ ushort queue_full_cnt[ADV_MAX_TID+1]; /* Queue full count */
+ union {
+ ASCEEP_CONFIG asc_eep; /* Narrow EEPROM config. */
+ ADVEEP_CONFIG adv_eep; /* Wide EEPROM config. */
+ } eep_config;
+ ulong last_reset; /* Saved last reset time */
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ /* /proc/scsi/advansys/[0...] */
+ char *prtbuf; /* Statistics Print Buffer */
+#endif /* version >= v1.3.0 */
+#ifdef ADVANSYS_STATS
+ struct asc_stats asc_stats; /* Board statistics */
+#endif /* ADVANSYS_STATS */
+ /*
+ * The following fields are used only for Narrow Boards.
+ */
+ /* The following three structures must be in DMA-able memory. */
+ ASC_SCSI_REQ_Q scsireqq;
+ ASC_CAP_INFO cap_info;
+ ASC_SCSI_INQUIRY inquiry;
+ uchar sdtr_data[ASC_MAX_TID+1]; /* SDTR information */
+ /*
+ * The following fields are used only for Wide Boards.
+ */
+ void *ioremap_addr; /* I/O Memory remap address. */
+ ushort ioport; /* I/O Port address. */
+ adv_req_t *orig_reqp; /* adv_req_t memory block. */
+ adv_req_t *adv_reqp; /* Request structures. */
+ adv_sgblk_t *orig_sgblkp; /* adv_sgblk_t memory block. */
+ adv_sgblk_t *adv_sgblkp; /* Scatter-gather structures. */
+ ushort bios_signature; /* BIOS Signature. */
+ ushort bios_version; /* BIOS Version. */
+ ushort bios_codeseg; /* BIOS Code Segment. */
+ ushort bios_codelen; /* BIOS Code Segment Length. */
+} asc_board_t;
+
+/*
+ * PCI configuration structures
+ */
+typedef struct _PCI_DATA_
+{
+ uchar type;
+ uchar bus;
+ uchar slot;
+ uchar func;
+ uchar offset;
+} PCI_DATA;
+
+typedef struct _PCI_DEVICE_
+{
+ ushort vendorID;
+ ushort deviceID;
+ ushort slotNumber;
+ ushort slotFound;
+ uchar busNumber;
+ uchar maxBusNumber;
+ uchar devFunc;
+ ushort startSlot;
+ ushort endSlot;
+ uchar bridge;
+ uchar type;
+} PCI_DEVICE;
+
+typedef struct _PCI_CONFIG_SPACE_
+{
+ ushort vendorID;
+ ushort deviceID;
+ ushort command;
+ ushort status;
+ uchar revision;
+ uchar classCode[3];
+ uchar cacheSize;
+ uchar latencyTimer;
+ uchar headerType;
+ uchar bist;
+ ulong baseAddress[6];
+ ushort reserved[4];
+ ulong optionRomAddr;
+ ushort reserved2[4];
+ uchar irqLine;
+ uchar irqPin;
+ uchar minGnt;
+ uchar maxLatency;
+} PCI_CONFIG_SPACE;
+
+
+/*
+ * --- Driver Data
+ */
+
+/* Note: All driver global data should be initialized. */
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+struct proc_dir_entry proc_scsi_advansys =
+{
+ PROC_SCSI_ADVANSYS, /* unsigned short low_ino */
+ 8, /* unsigned short namelen */
+ "advansys", /* const char *name */
+ S_IFDIR | S_IRUGO | S_IXUGO, /* mode_t mode */
+ 2 /* nlink_t nlink */
+};
+#endif /* version >= v1.3.0 */
+
+/* Number of boards detected in system. */
+STATIC int asc_board_count = 0;
+STATIC struct Scsi_Host *asc_host[ASC_NUM_BOARD_SUPPORTED] = { 0 };
+
+/* Overrun buffer shared between all boards. */
+STATIC uchar overrun_buf[ASC_OVERRUN_BSIZE] = { 0 };
+
+/*
+ * Global structures required to issue a command.
+ */
+STATIC ASC_SCSI_Q asc_scsi_q = { { 0 } };
+STATIC ASC_SG_HEAD asc_sg_head = { 0 };
+
+/* List of supported bus types. */
+STATIC ushort asc_bus[ASC_NUM_BUS] ASC_INITDATA = {
+ ASC_IS_ISA,
+ ASC_IS_VL,
+ ASC_IS_EISA,
+ ASC_IS_PCI,
+};
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+STATIC int pci_scan_method ASC_INITDATA = -1;
+#endif /* ASC_CONFIG_PCI */
+#endif /* version < v2.1.93 */
+
+/*
+ * Used with the LILO 'advansys' option to eliminate or
+ * limit I/O port probing at boot time, cf. advansys_setup().
+ */
+STATIC int asc_iopflag = ASC_FALSE;
+STATIC int asc_ioport[ASC_NUM_IOPORT_PROBE] = { 0, 0, 0, 0 };
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0)
+/*
+ * In kernels earlier than v1.3.0, kmalloc() does not work
+ * during driver initialization. Therefore statically declare
+ * 16 elements of each structure. v1.3.0 kernels will probably
+ * not need any more than this number.
+ */
+uchar adv_req_buf[16 * sizeof(adv_req_t)] = { 0 };
+uchar adv_sgblk_buf[16 * sizeof(adv_sgblk_t)] = { 0 };
+#endif /* version >= v1,3,0 */
+
+#ifdef ADVANSYS_DEBUG
+STATIC char *
+asc_bus_name[ASC_NUM_BUS] = {
+ "ASC_IS_ISA",
+ "ASC_IS_VL",
+ "ASC_IS_EISA",
+ "ASC_IS_PCI",
+};
+
+STATIC int asc_dbglvl = 0;
+#endif /* ADVANSYS_DEBUG */
+
+/* Declaration for Asc Library internal data referenced by driver. */
+STATIC PortAddr _asc_def_iop_base[];
+
+
+/*
+ * --- Driver Function Prototypes
+ *
+ * advansys.h contains function prototypes for functions global to Linux.
+ */
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+STATIC int asc_proc_copy(off_t, off_t, char *, int , char *, int);
+#endif /* version >= v1.3.0 */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,70)
+STATIC void advansys_interrupt(int, struct pt_regs *);
+#else /* version >= v1.3.70 */
+STATIC void advansys_interrupt(int, void *, struct pt_regs *);
+#endif /* version >= v1.3.70 */
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+STATIC void advansys_select_queue_depths(struct Scsi_Host *,
+ Scsi_Device *);
+#endif /* version >= v1.3.89 */
+STATIC void advansys_command_done(Scsi_Cmnd *);
+STATIC void asc_scsi_done_list(Scsi_Cmnd *);
+STATIC int asc_execute_scsi_cmnd(Scsi_Cmnd *);
+STATIC int asc_build_req(asc_board_t *, Scsi_Cmnd *);
+STATIC int adv_build_req(asc_board_t *, Scsi_Cmnd *, ADV_SCSI_REQ_Q **);
+STATIC int adv_get_sglist(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *, Scsi_Cmnd *);
+STATIC void asc_isr_callback(ASC_DVC_VAR *, ASC_QDONE_INFO *);
+STATIC void adv_isr_callback(ADV_DVC_VAR *, ADV_SCSI_REQ_Q *);
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+STATIC int asc_srch_pci_dev(PCI_DEVICE *);
+STATIC uchar asc_scan_method(void);
+STATIC int asc_pci_find_dev(PCI_DEVICE *);
+STATIC void asc_get_pci_cfg(PCI_DEVICE *, PCI_CONFIG_SPACE *);
+STATIC ushort asc_get_cfg_word(PCI_DATA *);
+STATIC uchar asc_get_cfg_byte(PCI_DATA *);
+STATIC void asc_put_cfg_byte(PCI_DATA *, uchar);
+#endif /* ASC_CONFIG_PCI */
+#endif /* version < v2.1.93 */
+STATIC void asc_enqueue(asc_queue_t *, REQP, int);
+STATIC REQP asc_dequeue(asc_queue_t *, int);
+STATIC REQP asc_dequeue_list(asc_queue_t *, REQP *, int);
+STATIC int asc_rmqueue(asc_queue_t *, REQP);
+STATIC int asc_isqueued(asc_queue_t *, REQP);
+STATIC void asc_execute_queue(asc_queue_t *);
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+STATIC int asc_prt_board_devices(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_adv_bios(struct Scsi_Host *, char *, int);
+STATIC int asc_get_eeprom_string(ushort *serialnum, uchar *cp);
+STATIC int asc_prt_asc_board_eeprom(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_adv_board_eeprom(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_driver_conf(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_asc_board_info(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_adv_board_info(struct Scsi_Host *, char *, int);
+STATIC int asc_prt_line(char *, int, char *fmt, ...);
+#endif /* version >= v1.3.0 */
+
+/* Declaration for Asc Library internal functions reference by driver. */
+STATIC int AscFindSignature(PortAddr);
+STATIC ushort AscGetEEPConfig(PortAddr, ASCEEP_CONFIG *, ushort);
+
+#ifdef ADVANSYS_STATS
+STATIC int asc_prt_board_stats(struct Scsi_Host *, char *, int);
+#endif /* ADVANSYS_STATS */
+
+#ifdef ADVANSYS_DEBUG
+STATIC void asc_prt_scsi_host(struct Scsi_Host *);
+STATIC void asc_prt_scsi_cmnd(Scsi_Cmnd *);
+STATIC void asc_prt_asc_dvc_cfg(ASC_DVC_CFG *);
+STATIC void asc_prt_asc_dvc_var(ASC_DVC_VAR *);
+STATIC void asc_prt_asc_scsi_q(ASC_SCSI_Q *);
+STATIC void asc_prt_asc_qdone_info(ASC_QDONE_INFO *);
+STATIC void asc_prt_adv_dvc_cfg(ADV_DVC_CFG *);
+STATIC void asc_prt_adv_dvc_var(ADV_DVC_VAR *);
+STATIC void asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *);
+STATIC void asc_prt_adv_sgblock(int, ADV_SG_BLOCK *);
+STATIC void asc_prt_hex(char *f, uchar *, int);
+#endif /* ADVANSYS_DEBUG */
+
+#ifdef ADVANSYS_ASSERT
+STATIC int interrupts_enabled(void);
+#endif /* ADVANSYS_ASSERT */
+
+
+/*
+ * --- Linux 'Scsi_Host_Template' and advansys_setup() Functions
+ */
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+/*
+ * advansys_proc_info() - /proc/scsi/advansys/[0-(ASC_NUM_BOARD_SUPPORTED-1)]
+ *
+ * *buffer: I/O buffer
+ * **start: if inout == FALSE pointer into buffer where user read should start
+ * offset: current offset into a /proc/scsi/advansys/[0...] file
+ * length: length of buffer
+ * hostno: Scsi_Host host_no
+ * inout: TRUE - user is writing; FALSE - user is reading
+ *
+ * Return the number of bytes read from or written to a
+ * /proc/scsi/advansys/[0...] file.
+ *
+ * Note: This function uses the per board buffer 'prtbuf' which is
+ * allocated when the board is initialized in advansys_detect(). The
+ * buffer is ASC_PRTBUF_SIZE bytes. The function asc_proc_copy() is
+ * used to write to the buffer. The way asc_proc_copy() is written
+ * if 'prtbuf' is too small it will not be overwritten. Instead the
+ * user just won't get all the available statistics.
+ */
+int
+advansys_proc_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+ struct Scsi_Host *shp;
+ asc_board_t *boardp;
+ int i;
+ char *cp;
+ int cplen;
+ int cnt;
+ int totcnt;
+ int leftlen;
+ char *curbuf;
+ off_t advoffset;
+ Scsi_Device *scd;
+
+ ASC_DBG(1, "advansys_proc_info: begin\n");
+
+ /*
+ * User write not supported.
+ */
+ if (inout == TRUE) {
+ return(-ENOSYS);
+ }
+
+ /*
+ * User read of /proc/scsi/advansys/[0...] file.
+ */
+
+ /* Find the specified board. */
+ for (i = 0; i < asc_board_count; i++) {
+ if (asc_host[i]->host_no == hostno) {
+ break;
+ }
+ }
+ if (i == asc_board_count) {
+ return(-ENOENT);
+ }
+
+ shp = asc_host[i];
+ boardp = ASC_BOARDP(shp);
+
+ /* Copy read data starting at the beginning of the buffer. */
+ *start = buffer;
+ curbuf = buffer;
+ advoffset = 0;
+ totcnt = 0;
+ leftlen = length;
+
+ /*
+ * Get board configuration information.
+ *
+ * advansys_info() returns the board string from its own static buffer.
+ */
+ cp = (char *) advansys_info(shp);
+ strcat(cp, "\n");
+ cplen = strlen(cp);
+ /* Copy board information. */
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+ /*
+ * Display Wide Board BIOS Information.
+ */
+ if (ASC_WIDE_BOARD(boardp)) {
+ cp = boardp->prtbuf;
+ cplen = asc_prt_adv_bios(shp, cp, ASC_PRTBUF_SIZE);
+ ASC_ASSERT(cplen < ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+ }
+
+ /*
+ * Display driver information for each device attached to the board.
+ */
+ cp = boardp->prtbuf;
+ cplen = asc_prt_board_devices(shp, cp, ASC_PRTBUF_SIZE);
+ ASC_ASSERT(cplen < ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+ /*
+ * Display target driver information for each device attached
+ * to the board.
+ */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,75)
+ for (scd = scsi_devices; scd; scd = scd->next)
+#else /* version >= v2.1.75 */
+ for (scd = shp->host_queue; scd; scd = scd->next)
+#endif /* version >= v2.1.75 */
+ {
+ if (scd->host == shp) {
+ cp = boardp->prtbuf;
+ /*
+ * Note: If proc_print_scsidevice() writes more than
+ * ASC_PRTBUF_SIZE bytes, it will overrun 'prtbuf'.
+ */
+ proc_print_scsidevice(scd, cp, &cplen, 0);
+ ASC_ASSERT(cplen < ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+ }
+ }
+
+ /*
+ * Display EEPROM configuration for the board.
+ */
+ cp = boardp->prtbuf;
+ if (ASC_NARROW_BOARD(boardp)) {
+ cplen = asc_prt_asc_board_eeprom(shp, cp, ASC_PRTBUF_SIZE);
+ } else {
+ cplen = asc_prt_adv_board_eeprom(shp, cp, ASC_PRTBUF_SIZE);
+ }
+ ASC_ASSERT(cplen < ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+ /*
+ * Display driver configuration and information for the board.
+ */
+ cp = boardp->prtbuf;
+ cplen = asc_prt_driver_conf(shp, cp, ASC_PRTBUF_SIZE);
+ ASC_ASSERT(cplen < ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+#ifdef ADVANSYS_STATS
+ /*
+ * Display driver statistics for the board.
+ */
+ cp = boardp->prtbuf;
+ cplen = asc_prt_board_stats(shp, cp, ASC_PRTBUF_SIZE);
+ ASC_ASSERT(cplen < ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+#endif /* ADVANSYS_STATS */
+
+ /*
+ * Display Asc Library dynamic configuration information
+ * for the board.
+ */
+ cp = boardp->prtbuf;
+ if (ASC_NARROW_BOARD(boardp)) {
+ cplen = asc_prt_asc_board_info(shp, cp, ASC_PRTBUF_SIZE);
+ } else {
+ cplen = asc_prt_adv_board_info(shp, cp, ASC_PRTBUF_SIZE);
+ }
+ ASC_ASSERT(cplen < ASC_PRTBUF_SIZE);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+
+ return totcnt;
+}
+#endif /* version >= v1.3.0 */
+
+/*
+ * advansys_detect()
+ *
+ * Detect function for AdvanSys adapters.
+ *
+ * Argument is a pointer to the host driver's scsi_hosts entry.
+ *
+ * Return number of adapters found.
+ *
+ * Note: Because this function is called during system initialization
+ * it must not call SCSI mid-level functions including scsi_malloc()
+ * and scsi_free().
+ */
+ASC_INITFUNC(
+int
+advansys_detect(Scsi_Host_Template *tpnt)
+)
+{
+ static int detect_called = ASC_FALSE;
+ int iop;
+ int bus;
+ struct Scsi_Host *shp;
+ asc_board_t *boardp;
+ ASC_DVC_VAR *asc_dvc_varp = NULL;
+ ADV_DVC_VAR *adv_dvc_varp = NULL;
+ int ioport = 0;
+ int share_irq = FALSE;
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+ PCI_DEVICE pciDevice;
+ PCI_CONFIG_SPACE pciConfig;
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ unsigned long pci_memory_address;
+#endif /* version >= v1,3,0 */
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */
+#ifdef CONFIG_PCI
+ struct pci_dev *pci_devp = NULL;
+ int pci_device_id_cnt = 0;
+ unsigned int pci_device_id[ASC_PCI_DEVICE_ID_CNT] = {
+ ASC_PCI_DEVICE_ID_1100,
+ ASC_PCI_DEVICE_ID_1200,
+ ASC_PCI_DEVICE_ID_1300,
+ ASC_PCI_DEVICE_ID_2300
+ };
+ unsigned long pci_memory_address;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */
+ int warn_code, err_code;
+ int ret;
+
+ if (detect_called == ASC_FALSE) {
+ detect_called = ASC_TRUE;
+ } else {
+ printk("AdvanSys SCSI: advansys_detect() multiple calls ignored\n");
+ return 0;
+ }
+
+ ASC_DBG(1, "advansys_detect: begin\n");
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ tpnt->proc_dir = &proc_scsi_advansys;
+#endif /* version >= v1.3.0 */
+
+ asc_board_count = 0;
+
+ /*
+ * If I/O port probing has been modified, then verify and
+ * clean-up the 'asc_ioport' list.
+ */
+ if (asc_iopflag == ASC_TRUE) {
+ for (ioport = 0; ioport < ASC_NUM_IOPORT_PROBE; ioport++) {
+ ASC_DBG2(1, "advansys_detect: asc_ioport[%d] %x\n",
+ ioport, asc_ioport[ioport]);
+ if (asc_ioport[ioport] != 0) {
+ for (iop = 0; iop < ASC_IOADR_TABLE_MAX_IX; iop++) {
+ if (_asc_def_iop_base[iop] == asc_ioport[ioport]) {
+ break;
+ }
+ }
+ if (iop == ASC_IOADR_TABLE_MAX_IX) {
+ printk(
+"AdvanSys SCSI: specified I/O Port 0x%X is invalid\n",
+ asc_ioport[ioport]);
+ asc_ioport[ioport] = 0;
+ }
+ }
+ }
+ ioport = 0;
+ }
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+ memset(&pciDevice, 0, sizeof(PCI_DEVICE));
+ memset(&pciConfig, 0, sizeof(PCI_CONFIG_SPACE));
+ pciDevice.maxBusNumber = PCI_MAX_BUS;
+ pciDevice.endSlot = PCI_MAX_SLOT;
+#endif /* ASC_CONFIG_PCI */
+#endif /* version < v2.1.93 */
+
+ for (bus = 0; bus < ASC_NUM_BUS; bus++) {
+
+ ASC_DBG2(1, "advansys_detect: bus search type %d (%s)\n",
+ bus, asc_bus_name[bus]);
+ iop = 0;
+
+ while (asc_board_count < ASC_NUM_BOARD_SUPPORTED) {
+
+ ASC_DBG1(2, "advansys_detect: asc_board_count %d\n",
+ asc_board_count);
+
+ switch (asc_bus[bus]) {
+ case ASC_IS_ISA:
+ case ASC_IS_VL:
+ if (asc_iopflag == ASC_FALSE) {
+ iop = AscSearchIOPortAddr(iop, asc_bus[bus]);
+ } else {
+ /*
+ * ISA and VL I/O port scanning has either been
+ * eliminated or limited to selected ports on
+ * the LILO command line, /etc/lilo.conf, or
+ * by setting variables when the module was loaded.
+ */
+ ASC_DBG(1, "advansys_detect: I/O port scanning modified\n");
+ ioport_try_again:
+ iop = 0;
+ for (; ioport < ASC_NUM_IOPORT_PROBE; ioport++) {
+ if ((iop = asc_ioport[ioport]) != 0) {
+ break;
+ }
+ }
+ if (iop) {
+ ASC_DBG1(1, "advansys_detect: probing I/O port %x...\n",
+ iop);
+ if (check_region(iop, ASC_IOADR_GAP) != 0) {
+ printk(
+"AdvanSys SCSI: specified I/O Port 0x%X is busy\n", iop);
+ /* Don't try this I/O port twice. */
+ asc_ioport[ioport] = 0;
+ goto ioport_try_again;
+ } else if (AscFindSignature(iop) == ASC_FALSE) {
+ printk(
+"AdvanSys SCSI: specified I/O Port 0x%X has no adapter\n", iop);
+ /* Don't try this I/O port twice. */
+ asc_ioport[ioport] = 0;
+ goto ioport_try_again;
+ } else {
+ /*
+ * If this isn't an ISA board, then it must be
+ * a VL board. If currently looking an ISA
+ * board is being looked for then try for
+ * another ISA board in 'asc_ioport'.
+ */
+ if (asc_bus[bus] == ASC_IS_ISA &&
+ (AscGetChipVersion(iop, ASC_IS_ISA) &
+ ASC_CHIP_VER_ISA_BIT) == 0) {
+ /*
+ * Don't clear 'asc_ioport[ioport]'. Try
+ * this board again for VL. Increment
+ * 'ioport' past this board.
+ */
+ ioport++;
+ goto ioport_try_again;
+ }
+ }
+ /*
+ * This board appears good, don't try the I/O port
+ * again by clearing its value. Increment 'ioport'
+ * for the next iteration.
+ */
+ asc_ioport[ioport++] = 0;
+ }
+ }
+ break;
+
+ case ASC_IS_EISA:
+ iop = AscSearchIOPortAddr(iop, asc_bus[bus]);
+ break;
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+ case ASC_IS_PCI:
+ if (asc_srch_pci_dev(&pciDevice) != PCI_DEVICE_FOUND) {
+ iop = 0;
+ } else {
+ ASC_DBG2(2,
+ "advansys_detect: slotFound %d, busNumber %d\n",
+ pciDevice.slotFound, pciDevice.busNumber);
+ asc_get_pci_cfg(&pciDevice, &pciConfig);
+ iop = pciConfig.baseAddress[0] & PCI_IOADDRESS_MASK;
+ ASC_DBG2(1,
+ "advansys_detect: vendorID %X, deviceID %X\n",
+ pciConfig.vendorID, pciConfig.deviceID);
+ ASC_DBG2(2, "advansys_detect: iop %X, irqLine %d\n",
+ iop, pciConfig.irqLine);
+ }
+ break;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */
+#ifdef CONFIG_PCI
+ case ASC_IS_PCI:
+ while (pci_device_id_cnt < ASC_PCI_DEVICE_ID_CNT) {
+ if ((pci_devp = pci_find_device(ASC_PCI_VENDORID,
+ pci_device_id[pci_device_id_cnt], pci_devp)) == NULL) {
+ pci_device_id_cnt++;
+ } else {
+ break;
+ }
+ }
+ if (pci_devp == NULL) {
+ iop = 0;
+ } else {
+ ASC_DBG2(2,
+ "advansys_detect: devfn %d, bus number %d\n",
+ pci_devp->devfn, pci_devp->bus->number);
+ iop = pci_devp->base_address[0] & PCI_IOADDRESS_MASK;
+ ASC_DBG2(1,
+ "advansys_detect: vendorID %X, deviceID %X\n",
+ pci_devp->vendor, pci_devp->device);
+ ASC_DBG2(2, "advansys_detect: iop %X, irqLine %d\n",
+ iop, pci_devp->irq);
+ }
+ break;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */
+
+ default:
+ ASC_PRINT1("advansys_detect: unknown bus type: %d\n",
+ asc_bus[bus]);
+ break;
+ }
+ ASC_DBG1(1, "advansys_detect: iop %x\n", iop);
+
+ /*
+ * Adapter not found, try next bus type.
+ */
+ if (iop == 0) {
+ break;
+ }
+
+ /*
+ * Adapter found.
+ *
+ * Register the adapter, get its configuration, and
+ * initialize it.
+ */
+ ASC_DBG(2, "advansys_detect: scsi_register()\n");
+ shp = scsi_register(tpnt, sizeof(asc_board_t));
+
+ /* Save a pointer to the Scsi_host of each board found. */
+ asc_host[asc_board_count++] = shp;
+
+ /* Initialize private per board data */
+ boardp = ASC_BOARDP(shp);
+ memset(boardp, 0, sizeof(asc_board_t));
+ boardp->id = asc_board_count - 1;
+
+ /*
+ * Handle both narrow and wide boards.
+ *
+ * If a Wide board was detected, set the board structure
+ * wide board flag. Set-up the board structure based on
+ * the board type.
+ */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+ if (asc_bus[bus] == ASC_IS_PCI &&
+ pciConfig.deviceID == ASC_PCI_DEVICE_ID_2300) {
+ boardp->flags |= ASC_IS_WIDE_BOARD;
+ }
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */
+#ifdef CONFIG_PCI
+ if (asc_bus[bus] == ASC_IS_PCI &&
+ pci_devp->device == ASC_PCI_DEVICE_ID_2300) {
+ boardp->flags |= ASC_IS_WIDE_BOARD;
+ }
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ ASC_DBG(1, "advansys_detect: narrow board\n");
+ asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
+ asc_dvc_varp->bus_type = asc_bus[bus];
+ asc_dvc_varp->drv_ptr = (ulong) boardp;
+ asc_dvc_varp->cfg = &boardp->dvc_cfg.asc_dvc_cfg;
+ asc_dvc_varp->cfg->overrun_buf = &overrun_buf[0];
+ asc_dvc_varp->iop_base = iop;
+ asc_dvc_varp->isr_callback = (Ptr2Func) asc_isr_callback;
+ } else {
+ ASC_DBG(1, "advansys_detect: wide board\n");
+ adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
+ adv_dvc_varp->drv_ptr = (ulong) boardp;
+ adv_dvc_varp->cfg = &boardp->dvc_cfg.adv_dvc_cfg;
+ adv_dvc_varp->isr_callback = (Ptr2Func) adv_isr_callback;
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0)
+ adv_dvc_varp->iop_base = iop;
+#else /* version >= v1,3,0 */
+ /*
+ * Map the board's registers into virtual memory for
+ * PCI slave access. Only memory accesses are used to
+ * access the board's registers.
+ *
+ * Note: The PCI register base address is not always
+ * page aligned, but the address passed to ioremap()
+ * must be page aligned. It is guaranteed that the
+ * PCI register base address will not cross a page
+ * boundary.
+ */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+ pci_memory_address = pciConfig.baseAddress[1];
+ if ((boardp->ioremap_addr =
+ ioremap(pci_memory_address & PAGE_MASK,
+ PAGE_SIZE)) == 0) {
+ ASC_PRINT3(
+"advansys_detect: board %d: ioremap(%lx, %d) returned NULL\n",
+ boardp->id, pci_memory_address, ADV_CONDOR_IOLEN);
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+ adv_dvc_varp->iop_base = (AdvPortAddr)
+ (boardp->ioremap_addr +
+ (pci_memory_address - (pci_memory_address & PAGE_MASK)));
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */
+#ifdef CONFIG_PCI
+ pci_memory_address = pci_devp->base_address[1];
+ if ((boardp->ioremap_addr =
+ ioremap(pci_memory_address & PAGE_MASK,
+ PAGE_SIZE)) == 0) {
+ ASC_PRINT3(
+"advansys_detect: board %d: ioremap(%lx, %d) returned NULL\n",
+ boardp->id, pci_memory_address, ADV_CONDOR_IOLEN);
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+ adv_dvc_varp->iop_base = (AdvPortAddr)
+ (boardp->ioremap_addr +
+ (pci_memory_address - (pci_memory_address & PAGE_MASK)));
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */
+#endif /* version >= v1,3,0 */
+
+ /*
+ * Even though it isn't used to access the board in
+ * kernels greater than or equal to v1.3.0, save
+ * the I/O Port address so that it can be reported and
+ * displayed.
+ */
+ boardp->ioport = iop;
+ }
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ /*
+ * Allocate buffer for printing information from
+ * /proc/scsi/advansys/[0...].
+ */
+ if ((boardp->prtbuf =
+ kmalloc(ASC_PRTBUF_SIZE, GFP_ATOMIC)) == NULL) {
+ ASC_PRINT3(
+"advansys_detect: board %d: kmalloc(%d, %d) returned NULL\n",
+ boardp->id, ASC_PRTBUF_SIZE, GFP_ATOMIC);
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+#endif /* version >= v1.3.0 */
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ /*
+ * Set the board bus type and PCI IRQ before
+ * calling AscInitGetConfig().
+ */
+ switch (asc_dvc_varp->bus_type) {
+ case ASC_IS_ISA:
+ shp->unchecked_isa_dma = TRUE;
+ share_irq = FALSE;
+ break;
+ case ASC_IS_VL:
+ shp->unchecked_isa_dma = FALSE;
+ share_irq = FALSE;
+ break;
+ case ASC_IS_EISA:
+ shp->unchecked_isa_dma = FALSE;
+ share_irq = TRUE;
+ break;
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+ case ASC_IS_PCI:
+ shp->irq = asc_dvc_varp->irq_no = pciConfig.irqLine;
+ asc_dvc_varp->cfg->pci_device_id = pciConfig.deviceID;
+ asc_dvc_varp->cfg->pci_slot_info =
+ ASC_PCI_MKID(pciDevice.busNumber,
+ pciDevice.slotFound,
+ pciDevice.devFunc);
+ shp->unchecked_isa_dma = FALSE;
+ share_irq = TRUE;
+ break;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */
+#ifdef CONFIG_PCI
+ case ASC_IS_PCI:
+ shp->irq = asc_dvc_varp->irq_no = pci_devp->irq;
+ asc_dvc_varp->cfg->pci_device_id = pci_devp->device;
+ asc_dvc_varp->cfg->pci_slot_info =
+ ASC_PCI_MKID(pci_devp->bus->number,
+ PCI_SLOT(pci_devp->devfn),
+ PCI_FUNC(pci_devp->devfn));
+ shp->unchecked_isa_dma = FALSE;
+ share_irq = TRUE;
+ break;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */
+ default:
+ ASC_PRINT2(
+"advansys_detect: board %d: unknown adapter type: %d\n",
+ boardp->id, asc_dvc_varp->bus_type);
+ shp->unchecked_isa_dma = TRUE;
+ share_irq = FALSE;
+ break;
+ }
+ } else {
+ /*
+ * For Wide boards set PCI information before calling
+ * AdvInitGetConfig().
+ */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+ shp->irq = adv_dvc_varp->irq_no = pciConfig.irqLine;
+ adv_dvc_varp->cfg->pci_device_id = pciConfig.deviceID;
+ adv_dvc_varp->cfg->pci_slot_info =
+ ASC_PCI_MKID(pciDevice.busNumber,
+ pciDevice.slotFound,
+ pciDevice.devFunc);
+ shp->unchecked_isa_dma = FALSE;
+ share_irq = TRUE;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */
+#ifdef CONFIG_PCI
+ shp->irq = adv_dvc_varp->irq_no = pci_devp->irq;
+ adv_dvc_varp->cfg->pci_device_id = pci_devp->device;
+ adv_dvc_varp->cfg->pci_slot_info =
+ ASC_PCI_MKID(pci_devp->bus->number,
+ PCI_SLOT(pci_devp->devfn),
+ PCI_FUNC(pci_devp->devfn));
+ shp->unchecked_isa_dma = FALSE;
+ share_irq = TRUE;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */
+ }
+
+ /*
+ * Read the board configuration.
+ */
+ if (ASC_NARROW_BOARD(boardp)) {
+ /*
+ * NOTE: AscInitGetConfig() may change the board's
+ * bus_type value. The asc_bus[bus] value should no
+ * longer be used. If the bus_type field must be
+ * referenced only use the bit-wise AND operator "&".
+ */
+ ASC_DBG(2, "advansys_detect: AscInitGetConfig()\n");
+ switch(ret = AscInitGetConfig(asc_dvc_varp)) {
+ case 0: /* No error */
+ break;
+ case ASC_WARN_IO_PORT_ROTATE:
+ ASC_PRINT1(
+"AscInitGetConfig: board %d: I/O port address modified\n",
+ boardp->id);
+ break;
+ case ASC_WARN_AUTO_CONFIG:
+ ASC_PRINT1(
+"AscInitGetConfig: board %d: I/O port increment switch enabled\n",
+ boardp->id);
+ break;
+ case ASC_WARN_EEPROM_CHKSUM:
+ ASC_PRINT1(
+"AscInitGetConfig: board %d: EEPROM checksum error\n",
+ boardp->id);
+ break;
+ case ASC_WARN_IRQ_MODIFIED:
+ ASC_PRINT1(
+"AscInitGetConfig: board %d: IRQ modified\n",
+ boardp->id);
+ break;
+ case ASC_WARN_CMD_QNG_CONFLICT:
+ ASC_PRINT1(
+"AscInitGetConfig: board %d: tag queuing enabled w/o disconnects\n",
+ boardp->id);
+ break;
+ default:
+ ASC_PRINT2(
+"AscInitGetConfig: board %d: unknown warning: %x\n",
+ boardp->id, ret);
+ break;
+ }
+ if ((err_code = asc_dvc_varp->err_code) != 0) {
+ ASC_PRINT3(
+"AscInitGetConfig: board %d error: init_state %x, err_code %x\n",
+ boardp->id, asc_dvc_varp->init_state,
+ asc_dvc_varp->err_code);
+ }
+ } else {
+ ASC_DBG(2, "advansys_detect: AdvInitGetConfig()\n");
+ if ((ret = AdvInitGetConfig(adv_dvc_varp)) != 0) {
+ ASC_PRINT2("AdvInitGetConfig: board %d: warning: %x\n",
+ boardp->id, ret);
+ }
+ if ((err_code = adv_dvc_varp->err_code) != 0) {
+ ASC_PRINT2(
+"AdvInitGetConfig: board %d error: err_code %x\n",
+ boardp->id, adv_dvc_varp->err_code);
+ }
+ }
+
+ if (err_code != 0) {
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ kfree(boardp->prtbuf);
+#endif /* version >= v1.3.0 */
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+
+ /*
+ * Save the EEPROM configuration so that it can be displayed
+ * from /proc/scsi/advansys/[0...].
+ */
+ if (ASC_NARROW_BOARD(boardp)) {
+
+ ASCEEP_CONFIG *ep;
+
+ /*
+ * Set the adapter's target id bit in the 'init_tidmask' field.
+ */
+ boardp->init_tidmask |=
+ ADV_TID_TO_TIDMASK(asc_dvc_varp->cfg->chip_scsi_id);
+
+ /*
+ * Save EEPROM settings for the board.
+ */
+ ep = &boardp->eep_config.asc_eep;
+
+ ep->init_sdtr = asc_dvc_varp->cfg->sdtr_enable;
+ ep->disc_enable = asc_dvc_varp->cfg->disc_enable;
+ ep->use_cmd_qng = asc_dvc_varp->cfg->cmd_qng_enabled;
+ ep->isa_dma_speed = asc_dvc_varp->cfg->isa_dma_speed;
+ ep->start_motor = asc_dvc_varp->start_motor;
+ ep->cntl = asc_dvc_varp->dvc_cntl;
+ ep->no_scam = asc_dvc_varp->no_scam;
+ ep->max_total_qng = asc_dvc_varp->max_total_qng;
+ ep->chip_scsi_id = asc_dvc_varp->cfg->chip_scsi_id;
+ /* 'max_tag_qng' is set to the same value for every device. */
+ ep->max_tag_qng = asc_dvc_varp->cfg->max_tag_qng[0];
+ ep->adapter_info[0] = asc_dvc_varp->cfg->adapter_info[0];
+ ep->adapter_info[1] = asc_dvc_varp->cfg->adapter_info[1];
+ ep->adapter_info[2] = asc_dvc_varp->cfg->adapter_info[2];
+ ep->adapter_info[3] = asc_dvc_varp->cfg->adapter_info[3];
+ ep->adapter_info[4] = asc_dvc_varp->cfg->adapter_info[4];
+ ep->adapter_info[5] = asc_dvc_varp->cfg->adapter_info[5];
+ ep->adapter_info[6] = asc_dvc_varp->cfg->adapter_info[6];
+
+ /*
+ * Modify board configuration.
+ */
+ ASC_DBG(2, "advansys_detect: AscInitSetConfig()\n");
+ switch (ret = AscInitSetConfig(asc_dvc_varp)) {
+ case 0: /* No error. */
+ break;
+ case ASC_WARN_IO_PORT_ROTATE:
+ ASC_PRINT1(
+"AscInitSetConfig: board %d: I/O port address modified\n",
+ boardp->id);
+ break;
+ case ASC_WARN_AUTO_CONFIG:
+ ASC_PRINT1(
+"AscInitSetConfig: board %d: I/O port increment switch enabled\n",
+ boardp->id);
+ break;
+ case ASC_WARN_EEPROM_CHKSUM:
+ ASC_PRINT1(
+"AscInitSetConfig: board %d: EEPROM checksum error\n",
+ boardp->id);
+ break;
+ case ASC_WARN_IRQ_MODIFIED:
+ ASC_PRINT1(
+"AscInitSetConfig: board %d: IRQ modified\n",
+ boardp->id);
+ break;
+ case ASC_WARN_CMD_QNG_CONFLICT:
+ ASC_PRINT1(
+"AscInitSetConfig: board %d: tag queuing w/o disconnects\n",
+ boardp->id);
+ break;
+ default:
+ ASC_PRINT2(
+"AscInitSetConfig: board %d: unknown warning: %x\n",
+ boardp->id, ret);
+ break;
+ }
+ if (asc_dvc_varp->err_code != 0) {
+ ASC_PRINT3(
+"AscInitSetConfig: board %d error: init_state %x, err_code %x\n",
+ boardp->id, asc_dvc_varp->init_state,
+ asc_dvc_varp->err_code);
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ kfree(boardp->prtbuf);
+#endif /* version >= v1.3.0 */
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+
+ /*
+ * Finish initializing the 'Scsi_Host' structure.
+ */
+ /* AscInitSetConfig() will set the IRQ for non-PCI boards. */
+ if ((asc_dvc_varp->bus_type & ASC_IS_PCI) == 0) {
+ shp->irq = asc_dvc_varp->irq_no;
+ }
+ } else {
+
+ ADVEEP_CONFIG *ep;
+
+ /*
+ * Save Wide EEP Configuration Information.
+ */
+ ep = &boardp->eep_config.adv_eep;
+
+ ep->adapter_scsi_id = adv_dvc_varp->chip_scsi_id;
+ ep->max_host_qng = adv_dvc_varp->max_host_qng;
+ ep->max_dvc_qng = adv_dvc_varp->max_dvc_qng;
+ ep->termination = adv_dvc_varp->cfg->termination;
+ ep->disc_enable = adv_dvc_varp->cfg->disc_enable;
+ ep->bios_ctrl = adv_dvc_varp->bios_ctrl;
+ ep->wdtr_able = adv_dvc_varp->wdtr_able;
+ ep->sdtr_able = adv_dvc_varp->sdtr_able;
+ ep->ultra_able = adv_dvc_varp->ultra_able;
+ ep->tagqng_able = adv_dvc_varp->tagqng_able;
+ ep->start_motor = adv_dvc_varp->start_motor;
+ ep->scsi_reset_delay = adv_dvc_varp->scsi_reset_wait;
+ ep->bios_boot_delay = adv_dvc_varp->cfg->bios_boot_wait;
+ ep->serial_number_word1 = adv_dvc_varp->cfg->serial1;
+ ep->serial_number_word2 = adv_dvc_varp->cfg->serial2;
+ ep->serial_number_word3 = adv_dvc_varp->cfg->serial3;
+
+ /*
+ * Set the adapter's target id bit in the 'init_tidmask' field.
+ */
+ boardp->init_tidmask |=
+ ADV_TID_TO_TIDMASK(adv_dvc_varp->chip_scsi_id);
+
+ /*
+ * Finish initializing the 'Scsi_Host' structure.
+ */
+ shp->irq = adv_dvc_varp->irq_no;
+ }
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+ /*
+ * Channels are numbered beginning with 0. For AdvanSys One host
+ * structure supports one channel. Multi-channel boards have a
+ * separate host structure for each channel.
+ */
+ shp->max_channel = 0;
+#endif /* version >= v1.3.89 */
+ if (ASC_NARROW_BOARD(boardp)) {
+ shp->max_id = ASC_MAX_TID + 1;
+ shp->max_lun = ASC_MAX_LUN + 1;
+
+ shp->io_port = asc_dvc_varp->iop_base;
+ shp->n_io_port = ASC_IOADR_GAP;
+ shp->this_id = asc_dvc_varp->cfg->chip_scsi_id;
+
+ /* Set maximum number of queues the adapter can handle. */
+ shp->can_queue = asc_dvc_varp->max_total_qng;
+ } else {
+ shp->max_id = ADV_MAX_TID + 1;
+ shp->max_lun = ADV_MAX_LUN + 1;
+
+ /*
+ * Save the I/O Port address and length even though the
+ * in v1.3.0 and greater kernels the region is not used
+ * by a Wide board. Instead the board is accessed with
+ * Memory Mapped I/O.
+ */
+ shp->io_port = iop;
+ shp->n_io_port = ADV_CONDOR_IOLEN;
+
+ shp->this_id = adv_dvc_varp->chip_scsi_id;
+
+ /* Set maximum number of queues the adapter can handle. */
+ shp->can_queue = adv_dvc_varp->max_host_qng;
+ }
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,89)
+ /*
+ * In old kernels without tag queuing support and with memory
+ * allocation problems set a conservative 'cmd_per_lun' value.
+ */
+#ifdef MODULE
+ shp->cmd_per_lun = 1;
+#else /* MODULE */
+ shp->cmd_per_lun = 4;
+#endif /* MODULE */
+ ASC_DBG1(1, "advansys_detect: cmd_per_lun: %d\n", shp->cmd_per_lun);
+#else /* version >= v1.3.89 */
+ /*
+ * Following v1.3.89, 'cmd_per_lun' is no longer needed
+ * and should be set to zero.
+ *
+ * But because of a bug introduced in v1.3.89 if the driver is
+ * compiled as a module and 'cmd_per_lun' is zero, the Mid-Level
+ * SCSI function 'allocate_device' will panic. To allow the driver
+ * to work as a module in these kernels set 'cmd_per_lun' to 1.
+ */
+#ifdef MODULE
+ shp->cmd_per_lun = 1;
+#else /* MODULE */
+ shp->cmd_per_lun = 0;
+#endif /* MODULE */
+ /*
+ * Use the host 'select_queue_depths' function to determine
+ * the number of commands to queue per device.
+ */
+ shp->select_queue_depths = advansys_select_queue_depths;
+#endif /* version >= v1.3.89 */
+
+ /*
+ * Set the maximum number of scatter-gather elements the
+ * adapter can handle.
+ */
+ if (ASC_NARROW_BOARD(boardp)) {
+ /*
+ * Allow two commands with 'sg_tablesize' scatter-gather
+ * elements to be executed simultaneously. This value is
+ * the theoretical hardware limit. It may be decreased
+ * below.
+ */
+ shp->sg_tablesize =
+ (((asc_dvc_varp->max_total_qng - 2) / 2) *
+ ASC_SG_LIST_PER_Q) + 1;
+ } else {
+ shp->sg_tablesize = ADV_MAX_SG_LIST;
+ }
+
+#ifdef MODULE
+ /*
+ * If the driver is compiled as a module, set a limit on the
+ * 'sg_tablesize' value to prevent memory allocation failures.
+ * Memory allocation errors are more likely to occur at module
+ * load time, then at driver initialization time.
+ */
+ if (shp->sg_tablesize > 64) {
+ shp->sg_tablesize = 64;
+ }
+#endif /* MODULE */
+
+ /*
+ * The value of 'sg_tablesize' can not exceed the SCSI
+ * mid-level driver definition of SG_ALL. SG_ALL also
+ * must not be exceeded, because it is used to define the
+ * size of the scatter-gather table in 'struct asc_sg_head'.
+ */
+ if (shp->sg_tablesize > SG_ALL) {
+ shp->sg_tablesize = SG_ALL;
+ }
+
+ ASC_DBG1(1, "advansys_detect: sg_tablesize: %d\n",
+ shp->sg_tablesize);
+
+ /* BIOS start address. */
+ if (ASC_NARROW_BOARD(boardp)) {
+ shp->base = (char *) ((ulong) AscGetChipBiosAddress(
+ asc_dvc_varp->iop_base,
+ asc_dvc_varp->bus_type));
+ } else {
+ /*
+ * Fill-in BIOS board variables. The Wide BIOS saves
+ * information in LRAM that is used by the driver.
+ */
+ AdvReadWordLram(adv_dvc_varp->iop_base, BIOS_SIGNATURE,
+ boardp->bios_signature);
+ AdvReadWordLram(adv_dvc_varp->iop_base, BIOS_VERSION,
+ boardp->bios_version);
+ AdvReadWordLram(adv_dvc_varp->iop_base, BIOS_CODESEG,
+ boardp->bios_codeseg);
+ AdvReadWordLram(adv_dvc_varp->iop_base, BIOS_CODELEN,
+ boardp->bios_codelen);
+
+ ASC_DBG2(1,
+ "advansys_detect: bios_signature %x, bios_version %x\n",
+ boardp->bios_signature, boardp->bios_version);
+
+ ASC_DBG2(1,
+ "advansys_detect: bios_codeseg %x, bios_codelen %x\n",
+ boardp->bios_codeseg, boardp->bios_codelen);
+
+ /*
+ * If the BIOS saved a valid signature, then fill in
+ * the BIOS code segment base address.
+ */
+ if (boardp->bios_signature == 0x55AA) {
+ /*
+ * Convert x86 realmode code segment to a linear
+ * address by shifting left 4.
+ */
+ shp->base = (uchar *) (boardp->bios_codeseg << 4);
+ } else {
+ shp->base = 0;
+ }
+ }
+
+ /*
+ * Register Board Resources - I/O Port, DMA, IRQ
+ */
+
+ /* Register I/O port range. */
+ ASC_DBG(2, "advansys_detect: request_region()\n");
+ request_region(shp->io_port, shp->n_io_port, "advansys");
+
+ /* Register DMA Channel for Narrow boards. */
+ shp->dma_channel = NO_ISA_DMA; /* Default to no ISA DMA. */
+ if (ASC_NARROW_BOARD(boardp)) {
+ /* Register DMA channel for ISA bus. */
+ if (asc_dvc_varp->bus_type & ASC_IS_ISA) {
+ shp->dma_channel = asc_dvc_varp->cfg->isa_dma_channel;
+ if ((ret =
+ request_dma(shp->dma_channel, "advansys")) != 0) {
+ ASC_PRINT3(
+"advansys_detect: board %d: request_dma() %d failed %d\n",
+ boardp->id, shp->dma_channel, ret);
+ release_region(shp->io_port, shp->n_io_port);
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ kfree(boardp->prtbuf);
+#endif /* version >= v1.3.0 */
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+ AscEnableIsaDma(shp->dma_channel);
+ }
+ }
+
+ /* Register IRQ Number. */
+ ASC_DBG1(2, "advansys_detect: request_irq() %d\n", shp->irq);
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,70)
+ if ((ret = request_irq(shp->irq, advansys_interrupt,
+ SA_INTERRUPT, "advansys")) != 0)
+#else /* version >= v1.3.70 */
+ /*
+ * If request_irq() fails with the SA_INTERRUPT flag set,
+ * then try again without the SA_INTERRUPT flag set. This
+ * allows IRQ sharing to work even with other drivers that
+ * do not set the SA_INTERRUPT flag.
+ *
+ * If SA_INTERRUPT is not set, then interrupts are enabled
+ * before the driver interrupt function is called.
+ */
+ if (((ret = request_irq(shp->irq, advansys_interrupt,
+ SA_INTERRUPT | (share_irq == TRUE ? SA_SHIRQ : 0),
+ "advansys", boardp)) != 0) &&
+ ((ret = request_irq(shp->irq, advansys_interrupt,
+ (share_irq == TRUE ? SA_SHIRQ : 0),
+ "advansys", boardp)) != 0))
+#endif /* version >= v1.3.70 */
+ {
+ if (ret == -EBUSY) {
+ ASC_PRINT2(
+"advansys_detect: board %d: request_irq(): IRQ %d already in use.\n",
+ boardp->id, shp->irq);
+ } else if (ret == -EINVAL) {
+ ASC_PRINT2(
+"advansys_detect: board %d: request_irq(): IRQ %d not valid.\n",
+ boardp->id, shp->irq);
+ } else {
+ ASC_PRINT3(
+"advansys_detect: board %d: request_irq(): IRQ %d failed with %d\n",
+ boardp->id, shp->irq, ret);
+ }
+ release_region(shp->io_port, shp->n_io_port);
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ iounmap(boardp->ioremap_addr);
+#endif /* version >= v1,3,0 */
+ if (shp->dma_channel != NO_ISA_DMA) {
+ free_dma(shp->dma_channel);
+ }
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ kfree(boardp->prtbuf);
+#endif /* version >= v1.3.0 */
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+
+ /*
+ * Initialize board RISC chip and enable interrupts.
+ */
+ if (ASC_NARROW_BOARD(boardp)) {
+ ASC_DBG(2, "advansys_detect: AscInitAsc1000Driver()\n");
+ warn_code = AscInitAsc1000Driver(asc_dvc_varp);
+ err_code = asc_dvc_varp->err_code;
+
+ if (warn_code || err_code) {
+ ASC_PRINT4(
+"AscInitAsc1000Driver: board %d: error: init_state %x, warn %x error %x\n",
+ boardp->id, asc_dvc_varp->init_state,
+ warn_code, err_code);
+ }
+ } else {
+ int req_cnt;
+ adv_req_t *reqp = NULL;
+ int sg_cnt;
+ adv_sgblk_t *sgp = NULL;
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0)
+ req_cnt = sizeof(adv_req_buf)/sizeof(adv_req_t);
+ sg_cnt = sizeof(adv_sgblk_buf)/sizeof(adv_sgblk_t);
+ reqp = (adv_req_t *) &adv_req_buf[0];
+ sgp = (adv_sgblk_t *) &adv_sgblk_buf[0];
+#else /* version >= v1.3.0 */
+ /*
+ * Allocate up to 'max_host_qng' request structures for
+ * the Wide board.
+ */
+ for (req_cnt = adv_dvc_varp->max_host_qng;
+ req_cnt > 0; req_cnt--) {
+
+ reqp = (adv_req_t *)
+ kmalloc(sizeof(adv_req_t) * req_cnt, GFP_ATOMIC);
+
+ ASC_DBG3(1,
+ "advansys_detect: reqp %x, req_cnt %d, bytes %d\n",
+ (unsigned) reqp, req_cnt, sizeof(adv_req_t) * req_cnt);
+
+ if (reqp != NULL) {
+ break;
+ }
+ }
+
+ /*
+ * Allocate up to ADV_TOT_SG_LIST request structures for
+ * the Wide board.
+ */
+ for (sg_cnt = ADV_TOT_SG_LIST; sg_cnt > 0; sg_cnt--) {
+
+ sgp = (adv_sgblk_t *)
+ kmalloc(sizeof(adv_sgblk_t) * sg_cnt, GFP_ATOMIC);
+
+ ASC_DBG3(1,
+ "advansys_detect: sgp %x, sg_cnt %d, bytes %d\n",
+ (unsigned) sgp, sg_cnt, sizeof(adv_sgblk_t) * sg_cnt);
+
+ if (sgp != NULL) {
+ break;
+ }
+ }
+#endif /* version >= v1.3.0 */
+
+ /*
+ * If no request structures or scatter-gather structures could
+ * be allocated, then return an error. Otherwise continue with
+ * initialization.
+ */
+ if (reqp == NULL) {
+ ASC_PRINT1(
+"advansys_detect: board %d: error: failed to kmalloc() adv_req_t buffer.\n",
+ boardp->id);
+ err_code = ADV_ERROR;
+ } else if (sgp == NULL) {
+ kfree(reqp);
+ ASC_PRINT1(
+"advansys_detect: board %d: error: failed to kmalloc() adv_sgblk_t buffer.\n",
+ boardp->id);
+ err_code = ADV_ERROR;
+ } else {
+
+ /*
+ * Save original pointer for kfree() in case the
+ * driver is built as a module and can be unloaded.
+ */
+ boardp->orig_reqp = reqp;
+
+ /*
+ * Point 'adv_reqp' to the request structures and
+ * link them together.
+ */
+ req_cnt--;
+ reqp[req_cnt].next_reqp = NULL;
+ for (; req_cnt > 0; req_cnt--) {
+ reqp[req_cnt - 1].next_reqp = &reqp[req_cnt];
+ }
+ boardp->adv_reqp = &reqp[0];
+
+ /*
+ * Save original pointer for kfree() in case the
+ * driver is built as a module and can be unloaded.
+ */
+ boardp->orig_sgblkp = sgp;
+
+ /*
+ * Point 'adv_sgblkp' to the request structures and
+ * link them together.
+ */
+ sg_cnt--;
+ sgp[sg_cnt].next_sgblkp = NULL;
+ for (; sg_cnt > 0; sg_cnt--) {
+ sgp[sg_cnt - 1].next_sgblkp = &sgp[sg_cnt];
+ }
+ boardp->adv_sgblkp = &sgp[0];
+
+ ASC_DBG(2, "advansys_detect: AdvInitAsc3550Driver()\n");
+ warn_code = AdvInitAsc3550Driver(adv_dvc_varp);
+ err_code = adv_dvc_varp->err_code;
+
+ if (warn_code || err_code) {
+ ASC_PRINT3(
+"AdvInitAsc3550Driver: board %d: error: warn %x, error %x\n",
+ boardp->id, warn_code, adv_dvc_varp->err_code);
+ }
+ }
+ }
+
+ if (err_code != 0) {
+ release_region(shp->io_port, shp->n_io_port);
+ if (ASC_WIDE_BOARD(boardp)) {
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ iounmap(boardp->ioremap_addr);
+#endif /* version >= v1,3,0 */
+ if (boardp->orig_reqp) {
+ kfree(boardp->orig_reqp);
+ boardp->orig_reqp = boardp->adv_reqp = NULL;
+ }
+ if (boardp->orig_sgblkp) {
+ kfree(boardp->orig_sgblkp);
+ boardp->orig_sgblkp = boardp->adv_sgblkp = NULL;
+ }
+ }
+ if (shp->dma_channel != NO_ISA_DMA) {
+ free_dma(shp->dma_channel);
+ }
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ kfree(boardp->prtbuf);
+#endif /* version >= v1.3.0 */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,70)
+ free_irq(shp->irq);
+#else /* version >= v1.3.70 */
+ free_irq(shp->irq, boardp);
+#endif /* version >= v1.3.70 */
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+ ASC_DBG_PRT_SCSI_HOST(2, shp);
+ }
+ }
+ ASC_DBG1(1, "advansys_detect: done: asc_board_count %d\n", asc_board_count);
+ return asc_board_count;
+}
+
+/*
+ * advansys_release()
+ *
+ * Release resources allocated for a single AdvanSys adapter.
+ */
+int
+advansys_release(struct Scsi_Host *shp)
+{
+ asc_board_t *boardp;
+
+ ASC_DBG(1, "advansys_release: begin\n");
+ boardp = ASC_BOARDP(shp);
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,70)
+ free_irq(shp->irq);
+#else /* version >= v1.3.70 */
+ free_irq(shp->irq, boardp);
+#endif /* version >= v1.3.70 */
+ if (shp->dma_channel != NO_ISA_DMA) {
+ ASC_DBG(1, "advansys_release: free_dma()\n");
+ free_dma(shp->dma_channel);
+ }
+ release_region(shp->io_port, shp->n_io_port);
+ if (ASC_WIDE_BOARD(boardp)) {
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ iounmap(boardp->ioremap_addr);
+#endif /* version >= v1,3,0 */
+ if (boardp->orig_reqp) {
+ kfree(boardp->orig_reqp);
+ boardp->orig_reqp = boardp->adv_reqp = NULL;
+ }
+ if (boardp->orig_sgblkp) {
+ kfree(boardp->orig_sgblkp);
+ boardp->orig_sgblkp = boardp->adv_sgblkp = NULL;
+ }
+ }
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+ ASC_ASSERT(boardp->prtbuf != NULL);
+ kfree(boardp->prtbuf);
+#endif /* version >= v1.3.0 */
+ scsi_unregister(shp);
+ ASC_DBG(1, "advansys_release: end\n");
+ return 0;
+}
+
+/*
+ * advansys_info()
+ *
+ * Return suitable for printing on the console with the argument
+ * adapter's configuration information.
+ *
+ * Note: The information line should not exceed ASC_INFO_SIZE bytes,
+ * otherwise the static 'info' array will be overrun.
+ */
+const char *
+advansys_info(struct Scsi_Host *shp)
+{
+ static char info[ASC_INFO_SIZE];
+ asc_board_t *boardp;
+ ASC_DVC_VAR *asc_dvc_varp;
+ ADV_DVC_VAR *adv_dvc_varp;
+ char *busname;
+
+ boardp = ASC_BOARDP(shp);
+ if (ASC_NARROW_BOARD(boardp)) {
+ asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
+ ASC_DBG(1, "advansys_info: begin\n");
+ if (asc_dvc_varp->bus_type & ASC_IS_ISA) {
+ if ((asc_dvc_varp->bus_type & ASC_IS_ISAPNP) == ASC_IS_ISAPNP) {
+ busname = "ISA PnP";
+ } else {
+ busname = "ISA";
+ }
+ sprintf(info,
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92)
+"AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %X/%X, IRQ %u, DMA %u",
+#else /* version >= v2.1.92 */
+"AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %lX/%X, IRQ %u, DMA %u",
+#endif /* version >= v2.1.92 */
+ ASC_VERSION, busname, asc_dvc_varp->max_total_qng,
+ (unsigned) shp->base,
+ shp->io_port, shp->n_io_port - 1,
+ shp->irq, shp->dma_channel);
+ } else if (asc_dvc_varp->bus_type & ASC_IS_PCI) {
+ if ((asc_dvc_varp->bus_type & ASC_IS_PCI_ULTRA)
+ == ASC_IS_PCI_ULTRA) {
+ busname = "PCI Ultra";
+ } else {
+ busname = "PCI";
+ }
+ sprintf(info,
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92)
+ "AdvanSys SCSI %s: %s %u CDB: IO %X/%X, IRQ %u",
+#else /* version >= v2.1.92 */
+ "AdvanSys SCSI %s: %s %u CDB: IO %lX/%X, IRQ %u",
+#endif /* version >= v2.1.92 */
+ ASC_VERSION, busname, asc_dvc_varp->max_total_qng,
+ shp->io_port, shp->n_io_port - 1, shp->irq);
+ } else {
+ if (asc_dvc_varp->bus_type & ASC_IS_VL) {
+ busname = "VL";
+ } else if (asc_dvc_varp->bus_type & ASC_IS_EISA) {
+ busname = "EISA";
+ } else {
+ busname = "?";
+ ASC_PRINT2(
+ "advansys_info: board %d: unknown bus type %d\n",
+ boardp->id, asc_dvc_varp->bus_type);
+ }
+ sprintf(info,
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,92)
+ "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %X/%X, IRQ %u",
+#else /* version >= v2.1.92 */
+ "AdvanSys SCSI %s: %s %u CDB: BIOS %X, IO %lX/%X, IRQ %u",
+#endif /* version >= v2.1.92 */
+ ASC_VERSION, busname, asc_dvc_varp->max_total_qng,
+ (unsigned) shp->base, shp->io_port - 1,
+ shp->n_io_port, shp->irq);
+ }
+ } else {
+ /*
+ * Wide Adapter Information
+ *
+ * Memory-mapped I/O is used instead of I/O space to access
+ * the adapter, but display the I/O Port range. The Memory
+ * I/O address is displayed through the driver /proc file.
+ */
+ adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
+ if (boardp->bios_signature == 0x55AA) {
+ sprintf(info,
+"AdvanSys SCSI %s: PCI Ultra-Wide: BIOS %X/%X, IO %X/%X, IRQ %u",
+ ASC_VERSION,
+ boardp->bios_codeseg << 4,
+ boardp->bios_codelen > 0 ?
+ (boardp->bios_codelen << 9) - 1 : 0,
+ (unsigned) boardp->ioport, ADV_CONDOR_IOLEN - 1,
+ shp->irq);
+ } else {
+ sprintf(info,
+"AdvanSys SCSI %s: PCI Ultra-Wide: IO %X/%X, IRQ %u",
+ ASC_VERSION,
+ (unsigned) boardp->ioport,
+ (ADV_CONDOR_IOLEN - 1),
+ shp->irq);
+ }
+ }
+ ASC_ASSERT(strlen(info) < ASC_INFO_SIZE);
+ ASC_DBG(1, "advansys_info: end\n");
+ return info;
+}
+
+/*
+ * advansys_command() - polled I/O entrypoint.
+ *
+ * Apparently host drivers shouldn't return until the command
+ * is finished.
+ *
+ * Note: This is an old interface that is no longer used by the SCSI
+ * mid-level driver. The new interface, advansys_queuecommand(),
+ * currently handles all requests.
+ */
+int
+advansys_command(Scsi_Cmnd *scp)
+{
+ ASC_DBG1(1, "advansys_command: scp %x\n", (unsigned) scp);
+ ASC_STATS(scp->host, command);
+ scp->SCp.Status = 0; /* Set to a known state */
+ advansys_queuecommand(scp, advansys_command_done);
+ while (scp->SCp.Status == 0) {
+ continue;
+ }
+ ASC_DBG1(1, "advansys_command: result %x\n", scp->result);
+ return scp->result;
+}
+
+/*
+ * advansys_queuecommand() - interrupt-driven I/O entrypoint.
+ *
+ * This function always returns 0. Command return status is saved
+ * in the 'scp' result field.
+ */
+int
+advansys_queuecommand(Scsi_Cmnd *scp, void (*done)(Scsi_Cmnd *))
+{
+ struct Scsi_Host *shp;
+ asc_board_t *boardp;
+ int flags;
+ Scsi_Cmnd *done_scp;
+
+ shp = scp->host;
+ boardp = ASC_BOARDP(shp);
+ ASC_STATS(shp, queuecommand);
+
+ /*
+ * Disable interrupts to preserve request ordering and provide
+ * mutually exclusive access to global structures used to initiate
+ * a request.
+ */
+ save_flags(flags);
+ cli();
+
+ /*
+ * Block new commands while handling a reset or abort request.
+ */
+ if (boardp->flags & (ASC_HOST_IN_RESET | ASC_HOST_IN_ABORT)) {
+ if (boardp->flags & ASC_HOST_IN_RESET) {
+ ASC_DBG1(1,
+ "advansys_queuecommand: scp %x blocked for reset request\n",
+ (unsigned) scp);
+ scp->result = HOST_BYTE(DID_RESET);
+ } else {
+ ASC_DBG1(1,
+ "advansys_queuecommand: scp %x blocked for abort request\n",
+ (unsigned) scp);
+ scp->result = HOST_BYTE(DID_ABORT);
+ }
+
+ /*
+ * Add blocked requests to the board's 'done' queue. The queued
+ * requests will be completed at the end of the abort or reset
+ * handling.
+ */
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ restore_flags(flags);
+ return 0;
+ }
+
+ /*
+ * Attempt to execute any waiting commands for the board.
+ */
+ if (!ASC_QUEUE_EMPTY(&boardp->waiting)) {
+ ASC_DBG(1,
+ "advansys_queuecommand: before asc_execute_queue() waiting\n");
+ asc_execute_queue(&boardp->waiting);
+ }
+
+ /*
+ * Save the function pointer to Linux mid-level 'done' function
+ * and attempt to execute the command.
+ *
+ * If ASC_ERROR is returned the request has been added to the
+ * board's 'active' queue and will be completed by the interrupt
+ * handler.
+ *
+ * If ASC_BUSY is returned add the request to the board's per
+ * target waiting list.
+ *
+ * If an error occurred, the request will have been placed on the
+ * board's 'done' queue and must be completed before returning.
+ */
+ scp->scsi_done = done;
+ switch (asc_execute_scsi_cmnd(scp)) {
+ case ASC_NOERROR:
+ break;
+ case ASC_BUSY:
+ asc_enqueue(&boardp->waiting, scp, ASC_BACK);
+ break;
+ case ASC_ERROR:
+ default:
+ done_scp = asc_dequeue_list(&boardp->done, NULL, ASC_TID_ALL);
+ /* Interrupts could be enabled here. */
+ asc_scsi_done_list(done_scp);
+ break;
+ }
+
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * advansys_abort()
+ *
+ * Abort the command specified by 'scp'.
+ */
+int
+advansys_abort(Scsi_Cmnd *scp)
+{
+ struct Scsi_Host *shp;
+ asc_board_t *boardp;
+ ASC_DVC_VAR *asc_dvc_varp;
+ ADV_DVC_VAR *adv_dvc_varp;
+ int flags;
+ int do_scsi_done;
+ int scp_found;
+ Scsi_Cmnd *done_scp = NULL;
+ int ret;
+
+ /* Save current flags and disable interrupts. */
+ save_flags(flags);
+ cli();
+
+ ASC_DBG1(1, "advansys_abort: scp %x\n", (unsigned) scp);
+
+#ifdef ADVANSYS_STATS
+ if (scp->host != NULL) {
+ ASC_STATS(scp->host, abort);
+ }
+#endif /* ADVANSYS_STATS */
+
+#ifdef ADVANSYS_ASSERT
+ do_scsi_done = ASC_ERROR;
+ scp_found = ASC_ERROR;
+ ret = ASC_ERROR;
+#endif /* ADVANSYS_ASSERT */
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+ if (scp->serial_number != scp->serial_number_at_timeout) {
+ ASC_PRINT1(
+"advansys_abort: timeout serial number changed for request %x\n",
+ (unsigned) scp);
+ do_scsi_done = ASC_FALSE;
+ scp_found = ASC_FALSE;
+ ret = SCSI_ABORT_NOT_RUNNING;
+ } else
+#endif /* version >= v1.3.89 */
+ if ((shp = scp->host) == NULL) {
+ scp->result = HOST_BYTE(DID_ERROR);
+ do_scsi_done = ASC_TRUE;
+ scp_found = ASC_FALSE;
+ ret = SCSI_ABORT_ERROR;
+ } else if ((boardp = ASC_BOARDP(shp))->flags &
+ (ASC_HOST_IN_RESET | ASC_HOST_IN_ABORT)) {
+ ASC_PRINT2(
+"advansys_abort: board %d: Nested host reset or abort, flags 0x%x\n",
+ boardp->id, boardp->flags);
+ do_scsi_done = ASC_TRUE;
+ if ((asc_rmqueue(&boardp->active, scp) == ASC_TRUE) ||
+ (asc_rmqueue(&boardp->waiting, scp) == ASC_TRUE)) {
+ scp_found = ASC_TRUE;
+ } else {
+ scp_found = ASC_FALSE;
+ }
+ scp->result = HOST_BYTE(DID_ERROR);
+ ret = SCSI_ABORT_ERROR;
+ } else {
+ /* Set abort flag to avoid nested reset or abort requests. */
+ boardp->flags |= ASC_HOST_IN_ABORT;
+
+ do_scsi_done = ASC_TRUE;
+ if (asc_rmqueue(&boardp->waiting, scp) == ASC_TRUE) {
+ /*
+ * If asc_rmqueue() found the command on the waiting
+ * queue, it had not been sent to the device. After
+ * the queue is removed, no other handling is required.
+ */
+ ASC_DBG1(1, "advansys_abort: scp %x found on waiting queue\n",
+ (unsigned) scp);
+ scp_found = ASC_TRUE;
+ scp->result = HOST_BYTE(DID_ABORT);
+ ret = SCSI_ABORT_SUCCESS;
+ } else if (asc_isqueued(&boardp->active, scp) == ASC_TRUE) {
+ /*
+ * If asc_isqueued() found the command on the active
+ * queue, it has been sent to the device. The command
+ * will be returned through the interrupt handler after
+ * it has been aborted.
+ */
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ /*
+ * Narrow Board
+ */
+ asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
+ scp->result = HOST_BYTE(DID_ABORT);
+
+ sti(); /* Enable interrupts for AscAbortSRB(). */
+ ASC_DBG1(1, "advansys_abort: before AscAbortSRB(), scp %x\n",
+ (unsigned) scp);
+ switch (AscAbortSRB(asc_dvc_varp, (ulong) scp)) {
+ case ASC_TRUE:
+ /* asc_isr_callback() will be called */
+ ASC_DBG(1, "advansys_abort: AscAbortSRB() TRUE\n");
+ ret = SCSI_ABORT_PENDING;
+ break;
+ case ASC_FALSE:
+ /* Request has apparently already completed. */
+ ASC_DBG(1, "advansys_abort: AscAbortSRB() FALSE\n");
+ ret = SCSI_ABORT_NOT_RUNNING;
+ break;
+ case ASC_ERROR:
+ default:
+ ASC_DBG(1, "advansys_abort: AscAbortSRB() ERROR\n");
+ ret = SCSI_ABORT_ERROR;
+ break;
+ }
+ cli();
+ } else {
+ /*
+ * Wide Board
+ */
+ adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
+ scp->result = HOST_BYTE(DID_ABORT);
+
+ ASC_DBG1(1, "advansys_abort: before AdvAbortSRB(), scp %x\n",
+ (unsigned) scp);
+ switch (AdvAbortSRB(adv_dvc_varp, (ulong) scp)) {
+ case ASC_TRUE:
+ /* asc_isr_callback() will be called */
+ ASC_DBG(1, "advansys_abort: AdvAbortSRB() TRUE\n");
+ ret = SCSI_ABORT_PENDING;
+ break;
+ case ASC_FALSE:
+ /* Request has apparently already completed. */
+ ASC_DBG(1, "advansys_abort: AdvAbortSRB() FALSE\n");
+ ret = SCSI_ABORT_NOT_RUNNING;
+ break;
+ case ASC_ERROR:
+ default:
+ ASC_DBG(1, "advansys_abort: AdvAbortSRB() ERROR\n");
+ ret = SCSI_ABORT_ERROR;
+ break;
+ }
+ /*
+ * Ensure all requests completed by the microcode have
+ * been processed by calling AdvISR().
+ */
+ (void) AdvISR(adv_dvc_varp);
+ }
+
+ /*
+ * The request will either still be on the active queue
+ * or have been added to the board's done queue.
+ */
+ if (asc_rmqueue(&boardp->active, scp) == ASC_TRUE) {
+ scp->result = HOST_BYTE(DID_ABORT);
+ scp_found = ASC_TRUE;
+ } else {
+ scp_found = asc_rmqueue(&boardp->done, scp);
+ ASC_ASSERT(scp_found == ASC_TRUE);
+ }
+
+ } else {
+ /*
+ * The command was not found on the active or waiting queues.
+ */
+ do_scsi_done = ASC_TRUE;
+ scp_found = ASC_FALSE;
+ ret = SCSI_ABORT_NOT_RUNNING;
+ }
+
+ /* Clear abort flag. */
+ boardp->flags &= ~ASC_HOST_IN_ABORT;
+
+ /*
+ * Because the ASC_HOST_IN_ABORT flag causes both
+ * 'advansys_interrupt' and 'asc_isr_callback' to
+ * queue requests to the board's 'done' queue and
+ * prevents waiting commands from being executed,
+ * these queued requests must be handled here.
+ */
+ done_scp = asc_dequeue_list(&boardp->done, NULL, ASC_TID_ALL);
+
+ /*
+ * Start any waiting commands for the board.
+ */
+ if (!ASC_QUEUE_EMPTY(&boardp->waiting)) {
+ ASC_DBG(1, "advansys_interrupt: before asc_execute_queue()\n");
+ asc_execute_queue(&boardp->waiting);
+ }
+ }
+
+ /* Interrupts could be enabled here. */
+
+ /*
+ * Complete the request to be aborted, unless it has been
+ * restarted as detected above, even if it was not found on
+ * the device active or waiting queues.
+ */
+ ASC_ASSERT(do_scsi_done != ASC_ERROR);
+ ASC_ASSERT(scp_found != ASC_ERROR);
+ if (do_scsi_done == ASC_TRUE) {
+ if (scp->scsi_done == NULL) {
+ ASC_PRINT1(
+"advansys_abort: aborted request scsi_done() is NULL, %x\n",
+ (unsigned) scp);
+ } else {
+ if (scp_found == ASC_FALSE) {
+ ASC_PRINT1(
+"advansys_abort: abort request not active or waiting, completing anyway %x\n",
+ (unsigned) scp);
+ }
+ ASC_STATS(scp->host, done);
+ scp->scsi_done(scp);
+ }
+ }
+
+ /*
+ * It is possible for the request done function to re-enable
+ * interrupts without confusing the driver. But here interrupts
+ * aren't enabled until all requests have been completed.
+ */
+ if (done_scp != NULL) {
+ asc_scsi_done_list(done_scp);
+ }
+
+ ASC_DBG1(1, "advansys_abort: ret %d\n", ret);
+
+ /* Re-enable interrupts, if they were enabled on entry. */
+ restore_flags(flags);
+
+ ASC_ASSERT(ret != ASC_ERROR);
+ return ret;
+}
+
+/*
+ * advansys_reset()
+ *
+ * Reset the device associated with the command 'scp'.
+ */
+int
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,89)
+advansys_reset(Scsi_Cmnd *scp)
+#else /* version >= v1.3.89 */
+advansys_reset(Scsi_Cmnd *scp, unsigned int reset_flags)
+#endif /* version >= v1.3.89 */
+{
+ struct Scsi_Host *shp;
+ asc_board_t *boardp;
+ ASC_DVC_VAR *asc_dvc_varp;
+ ADV_DVC_VAR *adv_dvc_varp;
+ int flags;
+ Scsi_Cmnd *done_scp = NULL, *last_scp = NULL;
+ Scsi_Cmnd *tscp, *new_last_scp;
+ int do_scsi_done;
+ int scp_found;
+ int status;
+ int target;
+ int ret;
+ int device_reset = ASC_FALSE;
+
+ /* Save current flags and disable interrupts. */
+ save_flags(flags);
+ cli();
+
+ ASC_DBG1(1, "advansys_reset: %x\n", (unsigned) scp);
+
+#ifdef ADVANSYS_STATS
+ if (scp->host != NULL) {
+ ASC_STATS(scp->host, reset);
+ }
+#endif /* ADVANSYS_STATS */
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+ if ((reset_flags & SCSI_RESET_ASYNCHRONOUS) &&
+ (scp->serial_number != scp->serial_number_at_timeout)) {
+ ASC_PRINT1(
+"advansys_reset: timeout serial number changed for request %x\n",
+ (unsigned) scp);
+ do_scsi_done = ASC_FALSE;
+ scp_found = ASC_FALSE;
+ ret = SCSI_RESET_NOT_RUNNING;
+ } else
+#endif /* version >= v1.3.89 */
+ if ((shp = scp->host) == NULL) {
+ scp->result = HOST_BYTE(DID_ERROR);
+ do_scsi_done = ASC_TRUE;
+ scp_found = ASC_FALSE;
+ ret = SCSI_RESET_ERROR;
+ } else if ((boardp = ASC_BOARDP(shp))->flags &
+ (ASC_HOST_IN_RESET | ASC_HOST_IN_ABORT)) {
+ ASC_PRINT2(
+"advansys_reset: board %d: Nested host reset or abort, flags 0x%x\n",
+ boardp->id, boardp->flags);
+ do_scsi_done = ASC_TRUE;
+ if ((asc_rmqueue(&boardp->active, scp) == ASC_TRUE) ||
+ (asc_rmqueue(&boardp->waiting, scp) == ASC_TRUE)) {
+ scp_found = ASC_TRUE;
+ } else {
+ scp_found = ASC_FALSE;
+ }
+ scp->result = HOST_BYTE(DID_ERROR);
+ ret = SCSI_RESET_ERROR;
+ } else if (jiffies >= boardp->last_reset &&
+ jiffies < (boardp->last_reset + (10 * HZ))) {
+ /*
+ * Don't allow a reset to be attempted within 10 seconds
+ * of the last reset.
+ *
+ * If 'jiffies' wrapping occurs, the reset request will go
+ * through, because a wrapped 'jiffies' would not pass the
+ * test above.
+ */
+ ASC_DBG(1,
+ "advansys_reset: reset within 10 sec of last reset ignored\n");
+ do_scsi_done = ASC_TRUE;
+ if ((asc_rmqueue(&boardp->active, scp) == ASC_TRUE) ||
+ (asc_rmqueue(&boardp->waiting, scp) == ASC_TRUE)) {
+ scp_found = ASC_TRUE;
+ } else {
+ scp_found = ASC_FALSE;
+ }
+ scp->result = HOST_BYTE(DID_ERROR);
+ ret = SCSI_RESET_ERROR;
+ } else {
+ do_scsi_done = ASC_TRUE;
+
+ /* Set reset flag to avoid nested reset or abort requests. */
+ boardp->flags |= ASC_HOST_IN_RESET;
+
+ /*
+ * If the request is on the target waiting or active queue
+ * or the board done queue, then remove it and note that it
+ * was found.
+ */
+ if (asc_rmqueue(&boardp->active, scp) == ASC_TRUE) {
+ ASC_DBG(1, "advansys_reset: active scp_found = TRUE\n");
+ scp_found = ASC_TRUE;
+ } else if (asc_rmqueue(&boardp->waiting, scp) == ASC_TRUE) {
+ ASC_DBG(1, "advansys_reset: waiting scp_found = TRUE\n");
+ scp_found = ASC_TRUE;
+ } else if (asc_rmqueue(&boardp->done, scp) == ASC_TRUE) {
+ scp_found = ASC_TRUE;
+ } else {
+ scp_found = ASC_FALSE;
+ }
+
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ /*
+ * Narrow Board
+ *
+ * If the suggest reset bus flags are set, then reset the bus.
+ * Otherwise only reset the device.
+ */
+ asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+ if (reset_flags &
+ (SCSI_RESET_SUGGEST_BUS_RESET |
+ SCSI_RESET_SUGGEST_HOST_RESET)) {
+#endif /* version >= v1.3.89 */
+
+ /*
+ * Reset the target's SCSI bus.
+ */
+ ASC_DBG(1, "advansys_reset: before AscResetSB()\n");
+ sti(); /* Enable interrupts for AscResetSB(). */
+ status = AscResetSB(asc_dvc_varp);
+ cli();
+ switch (status) {
+ case ASC_TRUE:
+ ASC_DBG(1, "advansys_reset: AscResetSB() success\n");
+ ret = SCSI_RESET_SUCCESS;
+ break;
+ case ASC_ERROR:
+ default:
+ ASC_DBG(1, "advansys_reset: AscResetSB() failed\n");
+ ret = SCSI_RESET_ERROR;
+ break;
+ }
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+ } else {
+ /*
+ * Reset the specified device. If the device reset fails,
+ * then reset the SCSI bus.
+ */
+
+ ASC_DBG1(1,
+ "advansys_reset: before AscResetDevice(), target %d\n",
+ scp->target);
+ sti(); /* Enable interrupts for AscResetDevice(). */
+ status = AscResetDevice(asc_dvc_varp, scp->target);
+ cli();
+
+ switch (status) {
+ case ASC_TRUE:
+ ASC_DBG(1, "advansys_reset: AscResetDevice() success\n");
+ device_reset = ASC_TRUE;
+ ret = SCSI_RESET_SUCCESS;
+ break;
+ case ASC_ERROR:
+ default:
+ ASC_DBG(1,
+"advansys_reset: AscResetDevice() failed; Calling AscResetSB()\n");
+ sti(); /* Enable interrupts for AscResetSB(). */
+ status = AscResetSB(asc_dvc_varp);
+ cli();
+ switch (status) {
+ case ASC_TRUE:
+ ASC_DBG(1, "advansys_reset: AscResetSB() TRUE\n");
+ ret = SCSI_RESET_SUCCESS;
+ break;
+ case ASC_ERROR:
+ default:
+ ASC_DBG(1, "advansys_reset: AscResetSB() ERROR\n");
+ ret = SCSI_RESET_ERROR;
+ break;
+ }
+ break;
+ }
+ }
+#endif /* version >= v1.3.89 */
+ } else {
+ /*
+ * Wide Board
+ *
+ * If the suggest reset bus flags are set, then reset the bus.
+ * Otherwise only reset the device.
+ */
+ adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+ if (reset_flags &
+ (SCSI_RESET_SUGGEST_BUS_RESET |
+ SCSI_RESET_SUGGEST_HOST_RESET)) {
+#endif /* version >= v1.3.89 */
+
+ /*
+ * Reset the target's SCSI bus.
+ */
+ ASC_DBG(1, "advansys_reset: before AdvResetSB()\n");
+ switch (AdvResetSB(adv_dvc_varp)) {
+ case ASC_TRUE:
+ ASC_DBG(1, "advansys_reset: AdvResetSB() success\n");
+ ret = SCSI_RESET_SUCCESS;
+ break;
+ case ASC_FALSE:
+ default:
+ ASC_DBG(1, "advansys_reset: AdvResetSB() failed\n");
+ ret = SCSI_RESET_ERROR;
+ break;
+ }
+ /*
+ * Ensure all requests completed by the microcode have
+ * been processed by calling AdvISR().
+ */
+ (void) AdvISR(adv_dvc_varp);
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+ } else {
+ /*
+ * Reset the specified device. If the device reset fails,
+ * then reset the SCSI bus.
+ */
+
+ ASC_DBG1(1,
+ "advansys_reset: before AdvResetDevice(), target %d\n",
+ scp->target);
+
+ switch (AdvResetDevice(adv_dvc_varp, scp->target)) {
+ case ASC_TRUE:
+ ASC_DBG(1, "advansys_reset: AdvResetDevice() success\n");
+ device_reset = ASC_TRUE;
+ ret = SCSI_RESET_SUCCESS;
+ break;
+ case ASC_FALSE:
+ default:
+ ASC_DBG(1,
+"advansys_reset: AdvResetDevice() failed; Calling AdvResetSB()\n");
+
+ switch (AdvResetSB(adv_dvc_varp)) {
+ case ASC_TRUE:
+ ASC_DBG(1, "advansys_reset: AdvResetSB() TRUE\n");
+ ret = SCSI_RESET_SUCCESS;
+ break;
+ case ASC_FALSE:
+ default:
+ ASC_DBG(1, "advansys_reset: AdvResetSB() ERROR\n");
+ ret = SCSI_RESET_ERROR;
+ break;
+ }
+ break;
+ }
+ /*
+ * Ensure all requests completed by the microcode have
+ * been processed by calling AdvISR().
+ */
+ (void) AdvISR(adv_dvc_varp);
+ }
+#endif /* version >= v1.3.89 */
+ }
+
+ /*
+ * Because the ASC_HOST_IN_RESET flag causes both
+ * 'advansys_interrupt' and 'asc_isr_callback' to
+ * queue requests to the board's 'done' queue and
+ * prevents waiting commands from being executed,
+ * these queued requests must be handled here.
+ */
+ done_scp = asc_dequeue_list(&boardp->done, &last_scp,
+ ASC_TID_ALL);
+
+ /*
+ * If a device reset was performed dequeue all waiting
+ * and active requests for the device and set the request
+ * status to DID_RESET.
+ *
+ * If a SCSI bus reset was performed dequeue all waiting
+ * and active requests for all devices and set the request
+ * status to DID_RESET.
+ */
+ if (device_reset == ASC_TRUE) {
+ target = scp->target;
+ } else {
+ target = ASC_TID_ALL;
+ }
+
+ /*
+ * Add active requests to 'done_scp' and set the request status
+ * to DID_RESET.
+ */
+ if (done_scp == NULL) {
+ done_scp = asc_dequeue_list(&boardp->active, &last_scp, target);
+ for (tscp = done_scp; tscp; tscp = REQPNEXT(tscp)) {
+ tscp->result = HOST_BYTE(DID_RESET);
+ }
+ } else {
+ ASC_ASSERT(last_scp != NULL);
+ REQPNEXT(last_scp) = asc_dequeue_list(&boardp->active,
+ &new_last_scp, target);
+ if (new_last_scp != NULL) {
+ ASC_ASSERT(REQPNEXT(last_scp) != NULL);
+ for (tscp = REQPNEXT(last_scp); tscp; tscp = REQPNEXT(tscp)) {
+ tscp->result = HOST_BYTE(DID_RESET);
+ }
+ last_scp = new_last_scp;
+ }
+ }
+
+ /*
+ * Add waiting requests to 'done_scp' and set the request status
+ * to DID_RESET.
+ */
+ if (done_scp == NULL) {
+ done_scp = asc_dequeue_list(&boardp->waiting, &last_scp, target);
+ for (tscp = done_scp; tscp; tscp = REQPNEXT(tscp)) {
+ tscp->result = HOST_BYTE(DID_RESET);
+ }
+ } else {
+ ASC_ASSERT(last_scp != NULL);
+ REQPNEXT(last_scp) = asc_dequeue_list(&boardp->waiting,
+ &new_last_scp, target);
+ if (new_last_scp != NULL) {
+ ASC_ASSERT(REQPNEXT(last_scp) != NULL);
+ for (tscp = REQPNEXT(last_scp); tscp; tscp = REQPNEXT(tscp)) {
+ tscp->result = HOST_BYTE(DID_RESET);
+ }
+ last_scp = new_last_scp;
+ }
+ }
+
+ /* Save the time of the most recently completed reset. */
+ boardp->last_reset = jiffies;
+
+ /* Clear reset flag. */
+ boardp->flags &= ~ASC_HOST_IN_RESET;
+
+ /*
+ * Start any waiting commands for the board.
+ */
+ if (!ASC_QUEUE_EMPTY(&boardp->waiting)) {
+ ASC_DBG(1, "advansys_interrupt: before asc_execute_queue()\n");
+ asc_execute_queue(&boardp->waiting);
+ }
+ ret = SCSI_RESET_SUCCESS;
+ }
+
+ /* Interrupts could be enabled here. */
+
+ ASC_ASSERT(do_scsi_done != ASC_ERROR);
+ ASC_ASSERT(scp_found != ASC_ERROR);
+ if (do_scsi_done == ASC_TRUE) {
+ if (scp->scsi_done == NULL) {
+ ASC_PRINT1(
+"advansys_reset: reset request scsi_done() is NULL, %x\n",
+ (unsigned) scp);
+ } else {
+ if (scp_found == ASC_FALSE) {
+ ASC_PRINT1(
+"advansys_reset: reset request not active or waiting, completing anyway %x\n",
+ (unsigned) scp);
+ }
+ ASC_STATS(scp->host, done);
+ scp->scsi_done(scp);
+ }
+ }
+
+ /*
+ * It is possible for the request done function to re-enable
+ * interrupts without confusing the driver. But here interrupts
+ * aren't enabled until requests have been completed.
+ */
+ if (done_scp != NULL) {
+ asc_scsi_done_list(done_scp);
+ }
+
+ ASC_DBG1(1, "advansys_reset: ret %d\n", ret);
+
+ /* Re-enable interrupts, if they were enabled on entry. */
+ restore_flags(flags);
+
+ ASC_ASSERT(ret != ASC_ERROR);
+ return ret;
+}
+
+/*
+ * advansys_biosparam()
+ *
+ * Translate disk drive geometry if the "BIOS greater than 1 GB"
+ * support is enabled for a drive.
+ *
+ * ip (information pointer) is an int array with the following definition:
+ * ip[0]: heads
+ * ip[1]: sectors
+ * ip[2]: cylinders
+ */
+int
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0)
+advansys_biosparam(Disk *dp, int dep, int ip[])
+#else /* version >= v1.3.0 */
+advansys_biosparam(Disk *dp, kdev_t dep, int ip[])
+#endif /* version >= v1.3.0 */
+{
+ asc_board_t *boardp;
+
+ ASC_DBG(1, "advansys_biosparam: begin\n");
+ ASC_STATS(dp->device->host, biosparam);
+ boardp = ASC_BOARDP(dp->device->host);
+ if (ASC_NARROW_BOARD(boardp)) {
+ if ((boardp->dvc_var.asc_dvc_var.dvc_cntl &
+ ASC_CNTL_BIOS_GT_1GB) && dp->capacity > 0x200000) {
+ ip[0] = 255;
+ ip[1] = 63;
+ } else {
+ ip[0] = 64;
+ ip[1] = 32;
+ }
+ } else {
+ if ((boardp->dvc_var.adv_dvc_var.bios_ctrl &
+ BIOS_CTRL_EXTENDED_XLAT) && dp->capacity > 0x200000) {
+ ip[0] = 255;
+ ip[1] = 63;
+ } else {
+ ip[0] = 64;
+ ip[1] = 32;
+ }
+ }
+ ip[2] = dp->capacity / (ip[0] * ip[1]);
+ ASC_DBG(1, "advansys_biosparam: end\n");
+ return 0;
+}
+
+/*
+ * advansys_setup()
+ *
+ * This function is called from init/main.c at boot time.
+ * It it passed LILO parameters that can be set from the
+ * LILO command line or in /etc/lilo.conf.
+ *
+ * It is used by the AdvanSys driver to either disable I/O
+ * port scanning or to limit scanning to 1 - 4 I/O ports.
+ * Regardless of the option setting EISA and PCI boards
+ * will still be searched for and detected. This option
+ * only affects searching for ISA and VL boards.
+ *
+ * If ADVANSYS_DEBUG is defined the driver debug level may
+ * be set using the 5th (ASC_NUM_IOPORT_PROBE + 1) I/O Port.
+ *
+ * Examples:
+ * 1. Eliminate I/O port scanning:
+ * boot: linux advansys=
+ * or
+ * boot: linux advansys=0x0
+ * 2. Limit I/O port scanning to one I/O port:
+ * boot: linux advansys=0x110
+ * 3. Limit I/O port scanning to four I/O ports:
+ * boot: linux advansys=0x110,0x210,0x230,0x330
+ * 4. If ADVANSYS_DEBUG, limit I/O port scanning to four I/O ports and
+ * set the driver debug level to 2.
+ * boot: linux advansys=0x110,0x210,0x230,0x330,0xdeb2
+ *
+ * ints[0] - number of arguments
+ * ints[1] - first argument
+ * ints[2] - second argument
+ * ...
+ */
+ASC_INITFUNC(
+void
+advansys_setup(char *str, int *ints)
+)
+{
+ int i;
+
+ if (asc_iopflag == ASC_TRUE) {
+ printk("AdvanSys SCSI: 'advansys' LILO option may appear only once\n");
+ return;
+ }
+
+ asc_iopflag = ASC_TRUE;
+
+ if (ints[0] > ASC_NUM_IOPORT_PROBE) {
+#ifdef ADVANSYS_DEBUG
+ if ((ints[0] == ASC_NUM_IOPORT_PROBE + 1) &&
+ (ints[ASC_NUM_IOPORT_PROBE + 1] >> 4 == 0xdeb)) {
+ asc_dbglvl = ints[ASC_NUM_IOPORT_PROBE + 1] & 0xf;
+ } else {
+#endif /* ADVANSYS_DEBUG */
+ printk("AdvanSys SCSI: only %d I/O ports accepted\n",
+ ASC_NUM_IOPORT_PROBE);
+#ifdef ADVANSYS_DEBUG
+ }
+#endif /* ADVANSYS_DEBUG */
+ }
+
+#ifdef ADVANSYS_DEBUG
+ ASC_DBG1(1, "advansys_setup: ints[0] %d\n", ints[0]);
+ for (i = 1; i < ints[0]; i++) {
+ ASC_DBG2(1, " ints[%d] %x", i, ints[i]);
+ }
+ ASC_DBG(1, "\n");
+#endif /* ADVANSYS_DEBUG */
+
+ for (i = 1; i <= ints[0] && i <= ASC_NUM_IOPORT_PROBE; i++) {
+ asc_ioport[i-1] = ints[i];
+ ASC_DBG2(1, "advansys_setup: asc_ioport[%d] %x\n",
+ i - 1, asc_ioport[i-1]);
+ }
+}
+
+
+/*
+ * --- Loadable Driver Support
+ */
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = ADVANSYS;
+# include "scsi_module.c"
+#endif /* MODULE */
+
+
+/*
+ * --- Miscellaneous Driver Functions
+ */
+
+/*
+ * First-level interrupt handler.
+ *
+ * For versions > v1.3.70, 'dev_id' is a pointer to the interrupting
+ * adapter's asc_board_t. Because all boards are currently checked
+ * for interrupts on each interrupt, 'dev_id' is not referenced. 'dev_id'
+ * could be used to identify an interrupt passed to the AdvanSys driver,
+ * which is for a device sharing an interrupt with an AdvanSys adapter.
+ */
+STATIC void
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,70)
+advansys_interrupt(int irq, struct pt_regs *regs)
+#else /* version >= v1.3.70 */
+advansys_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+#endif /* version >= v1.3.70 */
+{
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,95)
+ int flags;
+#else /* version >= v2.1.95 */
+ unsigned long flags;
+#endif /* version >= v2.1.95 */
+ int i;
+ asc_board_t *boardp;
+ Scsi_Cmnd *done_scp = NULL, *last_scp = NULL;
+ Scsi_Cmnd *new_last_scp;
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,95)
+ /* Disable interrupts, if they aren't already disabled. */
+ save_flags(flags);
+ cli();
+#else /* version >= v2.1.95 */
+ /*
+ * Disable interrupts, if they aren't already disabled and acquire
+ * the I/O spinlock.
+ */
+ spin_lock_irqsave(&io_request_lock, flags);
+#endif /* version >= v2.1.95 */
+
+ ASC_DBG(1, "advansys_interrupt: begin\n");
+
+ /*
+ * Check for interrupts on all boards.
+ * AscISR() will call asc_isr_callback().
+ */
+ for (i = 0; i < asc_board_count; i++) {
+ boardp = ASC_BOARDP(asc_host[i]);
+ ASC_DBG2(2, "advansys_interrupt: i %d, boardp %lx\n",
+ i, (ulong) boardp)
+ if (ASC_NARROW_BOARD(boardp)) {
+ /*
+ * Narrow Board
+ */
+ if (AscIsIntPending(asc_host[i]->io_port)) {
+ ASC_STATS(asc_host[i], interrupt);
+ ASC_DBG(1, "advansys_interrupt: before AscISR()\n");
+ AscISR(&boardp->dvc_var.asc_dvc_var);
+ }
+ } else {
+ /*
+ * Wide Board
+ */
+ ASC_DBG(1, "advansys_interrupt: before AdvISR()\n");
+ if (AdvISR(&boardp->dvc_var.adv_dvc_var)) {
+ ASC_STATS(asc_host[i], interrupt);
+ }
+ }
+
+ /*
+ * Start waiting requests and create a list of completed requests.
+ *
+ * If a reset or abort request is being performed for the board,
+ * the reset or abort handler will complete pending requests after
+ * it has completed.
+ */
+ if ((boardp->flags & (ASC_HOST_IN_RESET | ASC_HOST_IN_ABORT)) == 0) {
+ ASC_DBG2(1, "advansys_interrupt: done_scp %lx, last_scp %lx\n",
+ (ulong) done_scp, (ulong) last_scp);
+
+ /* Start any waiting commands for the board. */
+ if (!ASC_QUEUE_EMPTY(&boardp->waiting)) {
+ ASC_DBG(1, "advansys_interrupt: before asc_execute_queue()\n");
+ asc_execute_queue(&boardp->waiting);
+ }
+
+ /*
+ * Add to the list of requests that must be completed.
+ *
+ * 'done_scp' will always be NULL on the first iteration
+ * of this loop. 'last_scp' is set at the same time as
+ * 'done_scp'.
+ */
+ if (done_scp == NULL) {
+ done_scp = asc_dequeue_list(&boardp->done, &last_scp,
+ ASC_TID_ALL);
+ } else {
+ ASC_ASSERT(last_scp != NULL);
+ REQPNEXT(last_scp) = asc_dequeue_list(&boardp->done,
+ &new_last_scp, ASC_TID_ALL);
+ if (new_last_scp != NULL) {
+ ASC_ASSERT(REQPNEXT(last_scp) != NULL);
+ last_scp = new_last_scp;
+ }
+ }
+ }
+ }
+
+ /* Interrupts could be enabled here. */
+
+ /*
+ * It is possible for the request done function to re-enable
+ * interrupts without confusing the driver. But here the
+ * original flags aren't restored until all requests have been
+ * completed.
+ */
+ asc_scsi_done_list(done_scp);
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,95)
+ /*
+ * Restore the original flags which will enable interrupts
+ * if and only if they were enabled on entry.
+ */
+ restore_flags(flags);
+#else /* version >= v2.1.95 */
+ /*
+ * Release the I/O spinlock and restore the original flags
+ * which will enable interrupts if and only if they were
+ * enabled on entry.
+ */
+ spin_unlock_irqrestore(&io_request_lock, flags);
+#endif /* version >= v2.1.95 */
+
+ ASC_DBG(1, "advansys_interrupt: end\n");
+ return;
+}
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+/*
+ * Set the number of commands to queue per device for the
+ * specified host adapter.
+ */
+STATIC void
+advansys_select_queue_depths(struct Scsi_Host *shp, Scsi_Device *devicelist)
+{
+ Scsi_Device *device;
+ asc_board_t *boardp;
+
+ boardp = ASC_BOARDP(shp);
+ boardp->flags |= ASC_SELECT_QUEUE_DEPTHS;
+ for (device = devicelist; device != NULL; device = device->next) {
+ if (device->host != shp) {
+ continue;
+ }
+ /*
+ * Save a pointer to the device and set its initial/maximum
+ * queue depth.
+ */
+ boardp->device[device->id] = device;
+ if (ASC_NARROW_BOARD(boardp)) {
+ device->queue_depth =
+ boardp->dvc_var.asc_dvc_var.max_dvc_qng[device->id];
+ } else {
+ device->queue_depth =
+ boardp->dvc_var.adv_dvc_var.max_dvc_qng;
+ }
+ ASC_DBG3(1, "advansys_select_queue_depths: shp %x, id %d, depth %d\n",
+ (unsigned) shp, device->id, device->queue_depth);
+ }
+}
+#endif /* version >= v1.3.89 */
+
+/*
+ * Function used only with polled I/O requests that are initiated by
+ * advansys_command().
+ */
+STATIC void
+advansys_command_done(Scsi_Cmnd *scp)
+{
+ ASC_DBG1(1, "advansys_command_done: scp %x\n", (unsigned) scp);
+ scp->SCp.Status = 1;
+}
+
+/*
+ * Complete all requests on the singly linked list pointed
+ * to by 'scp'.
+ *
+ * Interrupts can be enabled on entry.
+ */
+STATIC void
+asc_scsi_done_list(Scsi_Cmnd *scp)
+{
+ Scsi_Cmnd *tscp;
+
+ ASC_DBG(2, "asc_scsi_done_list: begin\n");
+ while (scp != NULL) {
+ ASC_DBG1(3, "asc_scsi_done_list: scp %x\n", (unsigned) scp);
+ tscp = REQPNEXT(scp);
+ REQPNEXT(scp) = NULL;
+ ASC_STATS(scp->host, done);
+ ASC_ASSERT(scp->scsi_done != NULL);
+ scp->scsi_done(scp);
+ scp = tscp;
+ }
+ ASC_DBG(2, "asc_scsi_done_list: done\n");
+ return;
+}
+
+/*
+ * Execute a single 'Scsi_Cmnd'.
+ *
+ * The function 'done' is called when the request has been completed.
+ *
+ * Scsi_Cmnd:
+ *
+ * host - board controlling device
+ * device - device to send command
+ * target - target of device
+ * lun - lun of device
+ * cmd_len - length of SCSI CDB
+ * cmnd - buffer for SCSI 8, 10, or 12 byte CDB
+ * use_sg - if non-zero indicates scatter-gather request with use_sg elements
+ *
+ * if (use_sg == 0) {
+ * request_buffer - buffer address for request
+ * request_bufflen - length of request buffer
+ * } else {
+ * request_buffer - pointer to scatterlist structure
+ * }
+ *
+ * sense_buffer - sense command buffer
+ *
+ * result (4 bytes of an int):
+ * Byte Meaning
+ * 0 SCSI Status Byte Code
+ * 1 SCSI One Byte Message Code
+ * 2 Host Error Code
+ * 3 Mid-Level Error Code
+ *
+ * host driver fields:
+ * SCp - Scsi_Pointer used for command processing status
+ * scsi_done - used to save caller's done function
+ * host_scribble - used for pointer to another Scsi_Cmnd
+ *
+ * If this function returns ASC_NOERROR or ASC_ERROR the request
+ * has been enqueued on the board's 'done' queue and must be
+ * completed by the caller.
+ *
+ * If ASC_BUSY is returned the request must be enqueued by the
+ * caller and re-tried later.
+ */
+STATIC int
+asc_execute_scsi_cmnd(Scsi_Cmnd *scp)
+{
+ asc_board_t *boardp;
+ ASC_DVC_VAR *asc_dvc_varp;
+ ADV_DVC_VAR *adv_dvc_varp;
+ ADV_SCSI_REQ_Q *adv_scsiqp;
+ Scsi_Device *device;
+ int ret;
+
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ ASC_DBG2(1, "asc_execute_scsi_cmnd: scp %x, done %x\n",
+ (unsigned) scp, (unsigned) scp->scsi_done);
+
+ boardp = ASC_BOARDP(scp->host);
+ device = boardp->device[scp->target];
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ /*
+ * Build and execute Narrow Board request.
+ */
+
+ asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
+
+ /*
+ * Build Asc Library request structure using the
+ * global structures 'asc_scsi_req' and 'asc_sg_head'.
+ *
+ * asc_build_req() can not return ASC_BUSY.
+ */
+ if (asc_build_req(boardp, scp) == ASC_ERROR) {
+ ASC_STATS(scp->host, build_error);
+ return ASC_ERROR;
+ }
+
+ /*
+ * Execute the command. If there is no error, add the command
+ * to the active queue.
+ */
+ switch (ret = AscExeScsiQueue(asc_dvc_varp, &asc_scsi_q)) {
+ case ASC_NOERROR:
+ ASC_STATS(scp->host, exe_noerror);
+ /*
+ * Increment monotonically increasing per device successful
+ * request counter. Wrapping doesn't matter.
+ */
+ boardp->reqcnt[scp->target]++;
+
+#if ASC_QUEUE_FLOW_CONTROL
+ /*
+ * Conditionally increment the device queue depth.
+ *
+ * If no error occurred and there have been 100 consecutive
+ * successful requests and the current queue depth is less
+ * than the maximum queue depth, then increment the current
+ * queue depth.
+ */
+ if (boardp->nerrcnt[scp->target]++ > 100) {
+ boardp->nerrcnt[scp->target] = 0;
+ if (device != NULL &&
+ (device->queue_curr_depth < device->queue_depth) &&
+ (!(boardp->queue_full &
+ ADV_TID_TO_TIDMASK(scp->target)) ||
+ (boardp->queue_full_cnt[scp->target] >
+ device->queue_curr_depth))) {
+ device->queue_curr_depth++;
+ }
+ }
+#endif /* ASC_QUEUE_FLOW_CONTROL */
+ asc_enqueue(&boardp->active, scp, ASC_BACK);
+ ASC_DBG(1,
+ "asc_execute_scsi_cmnd: AscExeScsiQueue(), ASC_NOERROR\n");
+ break;
+ case ASC_BUSY:
+ /* Caller must enqueue request and retry later. */
+ ASC_STATS(scp->host, exe_busy);
+#if ASC_QUEUE_FLOW_CONTROL
+ /*
+ * Clear consecutive no error counter and if possible decrement
+ * queue depth.
+ */
+ boardp->nerrcnt[scp->target] = 0;
+ if (device != NULL && device->queue_curr_depth > 1) {
+ device->queue_curr_depth--;
+ }
+#endif /* ASC_QUEUE_FLOW_CONTROL */
+ break;
+ case ASC_ERROR:
+ ASC_PRINT2(
+"asc_execute_scsi_cmnd: board %d: AscExeScsiQueue() ASC_ERROR, err_code %x\n",
+ boardp->id, asc_dvc_varp->err_code);
+ ASC_STATS(scp->host, exe_error);
+#if ASC_QUEUE_FLOW_CONTROL
+ /* Clear consecutive no error counter. */
+ boardp->nerrcnt[scp->target] = 0;
+#endif /* ASC_QUEUE_FLOW_CONTROL */
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ break;
+ default:
+ ASC_PRINT2(
+"asc_execute_scsi_cmnd: board %d: AscExeScsiQueue() unknown, err_code %x\n",
+ boardp->id, asc_dvc_varp->err_code);
+ ASC_STATS(scp->host, exe_unknown);
+#if ASC_QUEUE_FLOW_CONTROL
+ /* Clear consecutive no error counter. */
+ boardp->nerrcnt[scp->target] = 0;
+#endif /* ASC_QUEUE_FLOW_CONTROL */
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ break;
+ }
+ } else {
+ /*
+ * Build and execute Wide Board request.
+ */
+ adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
+
+ /*
+ * Build and get a pointer to an Adv Library request structure.
+ *
+ * If the request is successfully built then send it below,
+ * otherwise return with an error.
+ */
+ switch (adv_build_req(boardp, scp, &adv_scsiqp)) {
+ case ASC_NOERROR:
+ ASC_DBG(3, "asc_execute_scsi_cmnd: adv_build_req ASC_NOERROR\n");
+ break;
+ case ASC_BUSY:
+ ASC_DBG(1, "asc_execute_scsi_cmnd: adv_build_req ASC_BUSY\n");
+ return ASC_BUSY;
+ case ASC_ERROR:
+ default:
+ ASC_DBG(1, "asc_execute_scsi_cmnd: adv_build_req ASC_ERROR\n");
+ ASC_STATS(scp->host, build_error);
+ return ASC_ERROR;
+ }
+
+ /*
+ * Execute the command. If there is no error, add the command
+ * to the active queue.
+ */
+ switch (ret = AdvExeScsiQueue(adv_dvc_varp, adv_scsiqp)) {
+ case ASC_NOERROR:
+ ASC_STATS(scp->host, exe_noerror);
+ /*
+ * Increment monotonically increasing per device successful
+ * request counter. Wrapping doesn't matter.
+ */
+ boardp->reqcnt[scp->target]++;
+ asc_enqueue(&boardp->active, scp, ASC_BACK);
+ ASC_DBG(1,
+ "asc_execute_scsi_cmnd: AdvExeScsiQueue(), ASC_NOERROR\n");
+ break;
+ case ASC_BUSY:
+ /* Caller must enqueue request and retry later. */
+ ASC_STATS(scp->host, exe_busy);
+ break;
+ case ASC_ERROR:
+ ASC_PRINT2(
+"asc_execute_scsi_cmnd: board %d: AdvExeScsiQueue() ASC_ERROR, err_code %x\n",
+ boardp->id, adv_dvc_varp->err_code);
+ ASC_STATS(scp->host, exe_error);
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ break;
+ default:
+ ASC_PRINT2(
+"asc_execute_scsi_cmnd: board %d: AdvExeScsiQueue() unknown, err_code %x\n",
+ boardp->id, adv_dvc_varp->err_code);
+ ASC_STATS(scp->host, exe_unknown);
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ break;
+ }
+ }
+
+ ASC_DBG(1, "asc_execute_scsi_cmnd: end\n");
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ return ret;
+}
+
+/*
+ * Build a request structure for the Asc Library (Narrow Board).
+ *
+ * The global structures 'asc_scsi_q' and 'asc_sg_head' are
+ * used to build the request.
+ *
+ * If an error occurs, then return ASC_ERROR.
+ */
+STATIC int
+asc_build_req(asc_board_t *boardp, Scsi_Cmnd *scp)
+{
+ /*
+ * Mutually exclusive access is required to 'asc_scsi_q' and
+ * 'asc_sg_head' until after the request is started.
+ */
+ memset(&asc_scsi_q, 0, sizeof(ASC_SCSI_Q));
+
+ /*
+ * Point the ASC_SCSI_Q to the 'Scsi_Cmnd'.
+ */
+ asc_scsi_q.q2.srb_ptr = (ulong) scp;
+
+ /*
+ * Build the ASC_SCSI_Q request.
+ */
+ ASC_ASSERT(scp->cmd_len <= ASC_MAX_CDB_LEN);
+ if (scp->cmd_len > ASC_MAX_CDB_LEN) {
+ scp->cmd_len = ASC_MAX_CDB_LEN;
+ }
+ asc_scsi_q.cdbptr = &scp->cmnd[0];
+ asc_scsi_q.q2.cdb_len = scp->cmd_len;
+ asc_scsi_q.q1.target_id = ASC_TID_TO_TARGET_ID(scp->target);
+ asc_scsi_q.q1.target_lun = scp->lun;
+ asc_scsi_q.q2.target_ix = ASC_TIDLUN_TO_IX(scp->target, scp->lun);
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0)
+ asc_scsi_q.q1.sense_addr = (ulong) &scp->sense_buffer[0];
+#else /* version >= v2.0.0 */
+ asc_scsi_q.q1.sense_addr = virt_to_bus(&scp->sense_buffer[0]);
+#endif /* version >= v2.0.0 */
+ asc_scsi_q.q1.sense_len = sizeof(scp->sense_buffer);
+
+ /*
+ * If there are any outstanding requests for the current target,
+ * then every 255th request send an ORDERED request. This heuristic
+ * tries to retain the benefit of request sorting while preventing
+ * request starvation. 255 is the max number of tags or pending commands
+ * a device may have outstanding.
+ *
+ * The request count is incremented below for every successfully
+ * started request.
+ *
+ */
+ if ((boardp->dvc_var.asc_dvc_var.cur_dvc_qng[scp->target] > 0) &&
+ (boardp->reqcnt[scp->target] % 255) == 0) {
+ asc_scsi_q.q2.tag_code = M2_QTAG_MSG_ORDERED;
+ } else {
+ asc_scsi_q.q2.tag_code = M2_QTAG_MSG_SIMPLE;
+ }
+
+ /*
+ * Build ASC_SCSI_Q for a contiguous buffer or a scatter-gather
+ * buffer command.
+ */
+ if (scp->use_sg == 0) {
+ /*
+ * CDB request of single contiguous buffer.
+ */
+ ASC_STATS(scp->host, cont_cnt);
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0)
+ asc_scsi_q.q1.data_addr = (ulong) scp->request_buffer;
+#else /* version >= v2.0.0 */
+ asc_scsi_q.q1.data_addr = virt_to_bus(scp->request_buffer);
+#endif /* version >= v2.0.0 */
+ asc_scsi_q.q1.data_cnt = scp->request_bufflen;
+ ASC_STATS_ADD(scp->host, cont_xfer,
+ ASC_CEILING(scp->request_bufflen, 512));
+ asc_scsi_q.q1.sg_queue_cnt = 0;
+ asc_scsi_q.sg_head = NULL;
+ } else {
+ /*
+ * CDB scatter-gather request list.
+ */
+ int sgcnt;
+ struct scatterlist *slp;
+
+ if (scp->use_sg > scp->host->sg_tablesize) {
+ ASC_PRINT3(
+"asc_build_req: board %d: use_sg %d > sg_tablesize %d\n",
+ boardp->id, scp->use_sg, scp->host->sg_tablesize);
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+ return ASC_ERROR;
+ }
+
+ ASC_STATS(scp->host, sg_cnt);
+
+ /*
+ * Use global ASC_SG_HEAD structure and set the ASC_SCSI_Q
+ * structure to point to it.
+ */
+ memset(&asc_sg_head, 0, sizeof(ASC_SG_HEAD));
+
+ asc_scsi_q.q1.cntl |= QC_SG_HEAD;
+ asc_scsi_q.sg_head = &asc_sg_head;
+ asc_scsi_q.q1.data_cnt = 0;
+ asc_scsi_q.q1.data_addr = 0;
+ asc_sg_head.entry_cnt = asc_scsi_q.q1.sg_queue_cnt = scp->use_sg;
+ ASC_STATS_ADD(scp->host, sg_elem, asc_sg_head.entry_cnt);
+
+ /*
+ * Convert scatter-gather list into ASC_SG_HEAD list.
+ */
+ slp = (struct scatterlist *) scp->request_buffer;
+ for (sgcnt = 0; sgcnt < scp->use_sg; sgcnt++, slp++) {
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0)
+ asc_sg_head.sg_list[sgcnt].addr = (ulong) slp->address;
+#else /* version >= v2.0.0 */
+ asc_sg_head.sg_list[sgcnt].addr = virt_to_bus(slp->address);
+#endif /* version >= v2.0.0 */
+ asc_sg_head.sg_list[sgcnt].bytes = slp->length;
+ ASC_STATS_ADD(scp->host, sg_xfer, ASC_CEILING(slp->length, 512));
+ }
+ }
+
+ ASC_DBG_PRT_ASC_SCSI_Q(2, &asc_scsi_q);
+ ASC_DBG_PRT_CDB(1, scp->cmnd, scp->cmd_len);
+
+ return ASC_NOERROR;
+}
+
+/*
+ * Build a request structure for the Adv Library (Wide Board).
+ *
+ * If an adv_req_t can not be allocated to issue the request,
+ * then return ASC_BUSY. If an error occurs, then return ASC_ERROR.
+ */
+STATIC int
+adv_build_req(asc_board_t *boardp, Scsi_Cmnd *scp,
+ ADV_SCSI_REQ_Q **adv_scsiqpp)
+{
+ adv_req_t *reqp;
+ ADV_SCSI_REQ_Q *scsiqp;
+ int i;
+
+ /*
+ * Allocate an adv_req_t structure from the board to execute
+ * the command.
+ */
+ if (boardp->adv_reqp == NULL) {
+ ASC_DBG(1, "adv_build_req: no free adv_req_t\n");
+ ASC_STATS(scp->host, adv_build_noreq);
+ return ASC_BUSY;
+ } else {
+ reqp = boardp->adv_reqp;
+ boardp->adv_reqp = reqp->next_reqp;
+ reqp->next_reqp = NULL;
+ }
+
+ /*
+ * Get 4-byte aligned ADV_SCSI_REQ_Q and ADV_SG_BLOCK pointers.
+ */
+ scsiqp = (ADV_SCSI_REQ_Q *) ADV_DWALIGN(&reqp->scsi_req_q);
+ memset(scsiqp, 0, sizeof(ADV_SCSI_REQ_Q));
+
+ /*
+ * Set the ADV_SCSI_REQ_Q 'srb_ptr' to point to the adv_req_t structure.
+ */
+ scsiqp->srb_ptr = (ulong) reqp;
+
+ /*
+ * Set the adv_req_t 'cmndp' to point to the Scsi_Cmnd structure.
+ */
+ reqp->cmndp = scp;
+
+ /*
+ * Build the ADV_SCSI_REQ_Q request.
+ */
+
+ /*
+ * Set CDB length and copy it to the request structure.
+ */
+ ASC_ASSERT(scp->cmd_len <= ASC_MAX_CDB_LEN);
+ if (scp->cmd_len > ASC_MAX_CDB_LEN) {
+ scp->cmd_len = ASC_MAX_CDB_LEN;
+ }
+ scsiqp->cdb_len = scp->cmd_len;
+ for (i = 0; i < scp->cmd_len; i++) {
+ scsiqp->cdb[i] = scp->cmnd[i];
+ }
+
+ scsiqp->target_id = scp->target;
+ scsiqp->target_lun = scp->lun;
+
+ scsiqp->vsense_addr = (ulong) &scp->sense_buffer[0];
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0)
+ scsiqp->sense_addr = (ulong) &scp->sense_buffer[0];
+#else /* version >= v2.0.0 */
+ scsiqp->sense_addr = virt_to_bus(&scp->sense_buffer[0]);
+#endif /* version >= v2.0.0 */
+ scsiqp->sense_len = sizeof(scp->sense_buffer);
+
+ /*
+ * Build ADV_SCSI_REQ_Q for a contiguous buffer or a scatter-gather
+ * buffer command.
+ */
+ scsiqp->data_cnt = scp->request_bufflen;
+ scsiqp->vdata_addr = (ulong) scp->request_buffer;
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0)
+ scsiqp->data_addr = (ulong) scp->request_buffer;
+#else /* version >= v2.0.0 */
+ scsiqp->data_addr = virt_to_bus(scp->request_buffer);
+#endif /* version >= v2.0.0 */
+
+ if (scp->use_sg == 0) {
+ /*
+ * CDB request of single contiguous buffer.
+ */
+ reqp->sgblkp = NULL;
+ scsiqp->sg_list_ptr = NULL;
+ ASC_STATS(scp->host, cont_cnt);
+ ASC_STATS_ADD(scp->host, cont_xfer,
+ ASC_CEILING(scp->request_bufflen, 512));
+ } else {
+ /*
+ * CDB scatter-gather request list.
+ */
+ if (scp->use_sg > ADV_MAX_SG_LIST) {
+ ASC_PRINT3(
+"adv_build_req: board %d: use_sg %d > ADV_MAX_SG_LIST %d\n",
+ boardp->id, scp->use_sg, scp->host->sg_tablesize);
+ scp->result = HOST_BYTE(DID_ERROR);
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+
+ /*
+ * Free the 'adv_req_t' structure by adding it back to the
+ * board free list.
+ */
+ reqp->next_reqp = boardp->adv_reqp;
+ boardp->adv_reqp = reqp;
+
+ return ASC_ERROR;
+ }
+
+ /*
+ * Allocate an 'adv_sgblk_t' structure from the board to
+ * execute the command.
+ */
+ if (boardp->adv_sgblkp == NULL) {
+ ASC_DBG(1, "adv_build_req: no free adv_sgblk_t\n");
+ ASC_STATS(scp->host, adv_build_nosg);
+ /*
+ * Free the 'adv_req_t' structure by adding it back to the
+ * board free list.
+ */
+ reqp->next_reqp = boardp->adv_reqp;
+ boardp->adv_reqp = reqp;
+ return ASC_BUSY;
+ } else {
+ reqp->sgblkp = boardp->adv_sgblkp;
+ boardp->adv_sgblkp = reqp->sgblkp->next_sgblkp;
+ reqp->sgblkp->next_sgblkp = NULL;
+ }
+
+ /*
+ * Build scatter-gather list.
+ */
+ scsiqp->sg_list_ptr = (ADV_SG_BLOCK *)
+ ADV_DWALIGN(&reqp->sgblkp->sg_block[0]);
+
+ memset(scsiqp->sg_list_ptr, 0, sizeof(ADV_SG_BLOCK) *
+ (ADV_NUM_SG_BLOCK + ADV_NUM_PAGE_CROSSING));
+
+ if (adv_get_sglist(&boardp->dvc_var.adv_dvc_var, scsiqp, scp) ==
+ ADV_ERROR) {
+
+ /*
+ * Free the adv_sgblk_t structure, if any, by adding it back
+ * to the board free list.
+ */
+ ASC_ASSERT(reqp->sgblkp != NULL);
+ reqp->sgblkp->next_sgblkp = boardp->adv_sgblkp;
+ boardp->adv_sgblkp = reqp->sgblkp;
+
+ /*
+ * Free the adv_req_t structure by adding it back to the
+ * board free list.
+ */
+ reqp->next_reqp = boardp->adv_reqp;
+ boardp->adv_reqp = reqp;
+
+ return ADV_ERROR;
+ }
+
+ ASC_STATS(scp->host, sg_cnt);
+ ASC_STATS_ADD(scp->host, sg_elem, scp->use_sg);
+ }
+
+ ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp);
+ ASC_DBG_PRT_CDB(1, scp->cmnd, scp->cmd_len);
+
+ *adv_scsiqpp = scsiqp;
+
+ return ASC_NOERROR;
+}
+
+/*
+ * Build scatter-gather list for Adv Library (Wide Board).
+ *
+ * Return:
+ * ADV_SUCCESS(1) - SG List successfully created
+ * ADV_ERROR(-1) - SG List creation failed
+ */
+STATIC int
+adv_get_sglist(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp,
+ Scsi_Cmnd *scp)
+{
+ ADV_SG_BLOCK *sg_block; /* virtual address of a SG */
+ ulong sg_block_next_addr; /* block and its next */
+ ulong sg_block_physical_addr;
+ int sg_block_index, i; /* how many SG entries */
+ struct scatterlist *slp;
+ int sg_elem_cnt;
+
+ slp = (struct scatterlist *) scp->request_buffer;
+ sg_elem_cnt = scp->use_sg;
+
+ sg_block = scsiqp->sg_list_ptr;
+ sg_block_next_addr = (ulong) sg_block; /* allow math operation */
+ sg_block_physical_addr =
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0)
+ (ulong) scsiqp->sg_list_ptr;
+#else /* version >= v2.0.0 */
+ virt_to_bus(scsiqp->sg_list_ptr);
+#endif /* version >= v2.0.0 */
+ ADV_ASSERT(ADV_DWALIGN(sg_block_physical_addr) ==
+ sg_block_physical_addr);
+ scsiqp->sg_real_addr = sg_block_physical_addr;
+
+ sg_block_index = 0;
+ do
+ {
+ sg_block->first_entry_no = sg_block_index;
+ for (i = 0; i < NO_OF_SG_PER_BLOCK; i++)
+ {
+ sg_block->sg_list[i].sg_addr =
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0)
+ (ulong) slp->address;
+#else /* version >= v2.0.0 */
+ virt_to_bus(slp->address);
+#endif /* version >= v2.0.0 */
+ sg_block->sg_list[i].sg_count = slp->length;
+ ASC_STATS_ADD(scp->host, sg_xfer, ASC_CEILING(slp->length, 512));
+
+ if (--sg_elem_cnt == 0)
+ { /* last entry, get out */
+ scsiqp->sg_entry_cnt = sg_block_index + i + 1;
+ sg_block->last_entry_no = sg_block_index + i;
+ sg_block->sg_ptr = 0L; /* next link = NULL */
+ return ADV_SUCCESS;
+ }
+ slp++;
+ }
+ sg_block_next_addr += sizeof(ADV_SG_BLOCK);
+ sg_block_physical_addr += sizeof(ADV_SG_BLOCK);
+ ADV_ASSERT(ADV_DWALIGN(sg_block_physical_addr) ==
+ sg_block_physical_addr);
+
+ sg_block_index += NO_OF_SG_PER_BLOCK;
+ sg_block->sg_ptr = (ADV_SG_BLOCK *) sg_block_physical_addr;
+ sg_block->last_entry_no = sg_block_index - 1;
+ sg_block = (ADV_SG_BLOCK *) sg_block_next_addr; /* virtual addr */
+ }
+ while (1);
+ /* NOTREACHED */
+}
+
+/*
+ * asc_isr_callback() - Second Level Interrupt Handler called by AscISR().
+ *
+ * Interrupt callback function for the Narrow SCSI Asc Library.
+ */
+STATIC void
+asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep)
+{
+ asc_board_t *boardp;
+ Scsi_Cmnd *scp;
+ struct Scsi_Host *shp;
+ int underrun = ASC_FALSE;
+ int i;
+
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ ASC_DBG2(1, "asc_isr_callback: asc_dvc_varp %x, qdonep %x\n",
+ (unsigned) asc_dvc_varp, (unsigned) qdonep);
+ ASC_DBG_PRT_ASC_QDONE_INFO(2, qdonep);
+
+ /*
+ * Get the Scsi_Cmnd structure and Scsi_Host structure for the
+ * command that has been completed.
+ */
+ scp = (Scsi_Cmnd *) qdonep->d2.srb_ptr;
+ ASC_DBG1(1, "asc_isr_callback: scp %x\n", (unsigned) scp);
+
+ if (scp == NULL) {
+ ASC_PRINT("asc_isr_callback: scp is NULL\n");
+ return;
+ }
+ ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len);
+
+ /*
+ * If the request's host pointer is not valid, display a
+ * message and return.
+ */
+ shp = scp->host;
+ for (i = 0; i < asc_board_count; i++) {
+ if (asc_host[i] == shp) {
+ break;
+ }
+ }
+ if (i == asc_board_count) {
+ ASC_PRINT2("asc_isr_callback: scp %x has bad host pointer, host %x\n",
+ (unsigned) scp, (unsigned) shp);
+ return;
+ }
+
+ ASC_STATS(shp, callback);
+ ASC_DBG1(1, "asc_isr_callback: shp %x\n", (unsigned) shp);
+
+ /*
+ * If the request isn't found on the active queue, it may
+ * have been removed to handle a reset or abort request.
+ * Display a message and return.
+ */
+ boardp = ASC_BOARDP(shp);
+ ASC_ASSERT(asc_dvc_varp == &boardp->dvc_var.asc_dvc_var);
+ if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) {
+ ASC_PRINT2("asc_isr_callback: board %d: scp %x not on active queue\n",
+ boardp->id, (unsigned) scp);
+ return;
+ }
+
+ /*
+ * Check for an underrun condition.
+ */
+ if (scp->request_bufflen != 0 && qdonep->remain_bytes != 0 &&
+ qdonep->remain_bytes <= scp->request_bufflen != 0) {
+ ASC_DBG1(1, "asc_isr_callback: underrun condition %u bytes\n",
+ (unsigned) qdonep->remain_bytes);
+ underrun = ASC_TRUE;
+ }
+
+ /*
+ * 'qdonep' contains the command's ending status.
+ */
+ switch (qdonep->d3.done_stat) {
+ case QD_NO_ERROR:
+ ASC_DBG(2, "asc_isr_callback: QD_NO_ERROR\n");
+ switch (qdonep->d3.host_stat) {
+ case QHSTA_NO_ERROR:
+ scp->result = 0;
+ break;
+ default:
+ /* QHSTA error occurred */
+ scp->result = HOST_BYTE(DID_ERROR);
+ break;
+ }
+
+ /*
+ * If an INQUIRY command completed successfully, then call
+ * the AscInquiryHandling() function to set-up the device.
+ */
+ if (scp->cmnd[0] == SCSICMD_Inquiry && scp->lun == 0 &&
+ (scp->request_bufflen - qdonep->remain_bytes) >= 8)
+ {
+ AscInquiryHandling(asc_dvc_varp, scp->target & 0x7,
+ (ASC_SCSI_INQUIRY *) scp->request_buffer);
+ }
+
+ /*
+ * If there was an underrun without any other error,
+ * set DID_ERROR to indicate the underrun error.
+ *
+ * Note: There is no way yet to indicate the number
+ * of underrun bytes.
+ */
+ if (scp->result == 0 && underrun == ASC_TRUE) {
+ scp->result = HOST_BYTE(DID_UNDERRUN);
+ }
+ break;
+
+ case QD_WITH_ERROR:
+ ASC_DBG(2, "asc_isr_callback: QD_WITH_ERROR\n");
+ switch (qdonep->d3.host_stat) {
+ case QHSTA_NO_ERROR:
+ if (qdonep->d3.scsi_stat == SS_CHK_CONDITION) {
+ ASC_DBG(2, "asc_isr_callback: SS_CHK_CONDITION\n");
+ ASC_DBG_PRT_SENSE(2, scp->sense_buffer,
+ sizeof(scp->sense_buffer));
+ /*
+ * Note: The 'status_byte()' macro used by target drivers
+ * defined in scsi.h shifts the status byte returned by
+ * host drivers right by 1 bit. This is why target drivers
+ * also use right shifted status byte definitions. For
+ * instance target drivers use CHECK_CONDITION, defined to
+ * 0x1, instead of the SCSI defined check condition value
+ * of 0x2. Host drivers are supposed to return the status
+ * byte as it is defined by SCSI.
+ */
+ scp->result = DRIVER_BYTE(DRIVER_SENSE) |
+ STATUS_BYTE(qdonep->d3.scsi_stat);
+ } else {
+ scp->result = STATUS_BYTE(qdonep->d3.scsi_stat);
+ }
+ break;
+
+ default:
+ /* QHSTA error occurred */
+ ASC_DBG1(1, "asc_isr_callback: host_stat %x\n",
+ qdonep->d3.host_stat);
+ scp->result = HOST_BYTE(DID_BAD_TARGET);
+ break;
+ }
+ break;
+
+ case QD_ABORTED_BY_HOST:
+ ASC_DBG(1, "asc_isr_callback: QD_ABORTED_BY_HOST\n");
+ scp->result = HOST_BYTE(DID_ABORT) | MSG_BYTE(qdonep->d3.scsi_msg) |
+ STATUS_BYTE(qdonep->d3.scsi_stat);
+ break;
+
+ default:
+ ASC_DBG1(1, "asc_isr_callback: done_stat %x\n", qdonep->d3.done_stat);
+ scp->result = HOST_BYTE(DID_ERROR) | MSG_BYTE(qdonep->d3.scsi_msg) |
+ STATUS_BYTE(qdonep->d3.scsi_stat);
+ break;
+ }
+
+ /*
+ * If the 'init_tidmask' bit isn't already set for the target and the
+ * current request finished normally, then set the bit for the target
+ * to indicate that a device is present.
+ */
+ if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->target)) == 0 &&
+ qdonep->d3.done_stat == QD_NO_ERROR &&
+ qdonep->d3.host_stat == QHSTA_NO_ERROR) {
+ boardp->init_tidmask |= ADV_TID_TO_TIDMASK(scp->target);
+ }
+
+ /*
+ * Because interrupts may be enabled by the 'Scsi_Cmnd' done
+ * function, add the command to the end of the board's done queue.
+ * The done function for the command will be called from
+ * advansys_interrupt().
+ */
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+
+ return;
+}
+
+/*
+ * adv_isr_callback() - Second Level Interrupt Handler called by AdvISR().
+ *
+ * Callback function for the Wide SCSI Adv Library.
+ */
+STATIC void
+adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
+{
+ asc_board_t *boardp;
+ adv_req_t *reqp;
+ Scsi_Cmnd *scp;
+ struct Scsi_Host *shp;
+ int underrun = ASC_FALSE;
+ int i;
+
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ ASC_DBG2(1, "adv_isr_callback: adv_dvc_varp %x, scsiqp %x\n",
+ (unsigned) adv_dvc_varp, (unsigned) scsiqp);
+ ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp);
+
+ /*
+ * Get the adv_req_t structure for the command that has been
+ * completed. The adv_req_t structure actually contains the
+ * completed ADV_SCSI_REQ_Q structure.
+ */
+ reqp = (adv_req_t *) scsiqp->srb_ptr;
+ ASC_DBG1(1, "adv_isr_callback: reqp %x\n", (unsigned) reqp);
+ if (reqp == NULL) {
+ ASC_PRINT("adv_isr_callback: reqp is NULL\n");
+ return;
+ }
+
+ /*
+ * Get the Scsi_Cmnd structure and Scsi_Host structure for the
+ * command that has been completed.
+ *
+ * Note: The adv_req_t request structure and adv_sgblk_t structure,
+ * if any, * dropped, because a board structure pointer can not be
+ * determined.
+ */
+ scp = reqp->cmndp;
+ ASC_DBG1(1, "adv_isr_callback: scp %x\n", (unsigned) scp);
+ if (scp == NULL) {
+ ASC_PRINT("adv_isr_callback: scp is NULL; adv_req_t dropped.\n");
+ return;
+ }
+ ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len);
+
+ /*
+ * If the request's host pointer is not valid, display a message
+ * and return.
+ */
+ shp = scp->host;
+ for (i = 0; i < asc_board_count; i++) {
+ if (asc_host[i] == shp) {
+ break;
+ }
+ }
+ /*
+ * Note: If the host structure is not found, the adv_req_t request
+ * structure and adv_sgblk_t structure, if any, is dropped.
+ */
+ if (i == asc_board_count) {
+ ASC_PRINT2("adv_isr_callback: scp %x has bad host pointer, host %x\n",
+ (unsigned) scp, (unsigned) shp);
+ return;
+ }
+
+ ASC_STATS(shp, callback);
+ ASC_DBG1(1, "adv_isr_callback: shp %x\n", (unsigned) shp);
+
+ /*
+ * If the request isn't found on the active queue, it may have been
+ * removed to handle a reset or abort request. Display a message and
+ * return.
+ *
+ * Note: Because the structure may still be in use don't attempt
+ * to free the adv_req_t and adv_sgblk_t, if any, structures.
+ */
+ boardp = ASC_BOARDP(shp);
+ ASC_ASSERT(adv_dvc_varp == &boardp->dvc_var.adv_dvc_var);
+ if (asc_rmqueue(&boardp->active, scp) == ASC_FALSE) {
+ ASC_PRINT2("adv_isr_callback: board %d: scp %x not on active queue\n",
+ boardp->id, (unsigned) scp);
+ return;
+ }
+
+ /*
+ * Check for an underrun condition.
+ */
+ if (scp->request_bufflen != 0 && scsiqp->data_cnt != 0) {
+ ASC_DBG1(1, "adv_isr_callback: underrun condition %lu bytes\n",
+ scsiqp->data_cnt);
+ underrun = ASC_TRUE;
+ }
+
+ /*
+ * 'done_status' contains the command's ending status.
+ */
+ switch (scsiqp->done_status) {
+ case QD_NO_ERROR:
+ ASC_DBG(2, "adv_isr_callback: QD_NO_ERROR\n");
+ switch (scsiqp->host_status) {
+ case QHSTA_NO_ERROR:
+ scp->result = 0;
+ break;
+ default:
+ /* QHSTA error occurred. */
+ ASC_DBG1(2, "adv_isr_callback: host_status %x\n",
+ scsiqp->host_status);
+ scp->result = HOST_BYTE(DID_ERROR);
+ break;
+ }
+ /*
+ * If there was an underrun without any other error,
+ * set DID_ERROR to indicate the underrun error.
+ *
+ * Note: There is no way yet to indicate the number
+ * of underrun bytes.
+ */
+ if (scp->result == 0 && underrun == ASC_TRUE) {
+ scp->result = HOST_BYTE(DID_UNDERRUN);
+ }
+ break;
+
+ case QD_WITH_ERROR:
+ ASC_DBG(2, "adv_isr_callback: QD_WITH_ERROR\n");
+ switch (scsiqp->host_status) {
+ case QHSTA_NO_ERROR:
+ if (scsiqp->scsi_status == SS_CHK_CONDITION) {
+ ASC_DBG(2, "adv_isr_callback: SS_CHK_CONDITION\n");
+ ASC_DBG_PRT_SENSE(2, scp->sense_buffer,
+ sizeof(scp->sense_buffer));
+ /*
+ * Note: The 'status_byte()' macro used by target drivers
+ * defined in scsi.h shifts the status byte returned by
+ * host drivers right by 1 bit. This is why target drivers
+ * also use right shifted status byte definitions. For
+ * instance target drivers use CHECK_CONDITION, defined to
+ * 0x1, instead of the SCSI defined check condition value
+ * of 0x2. Host drivers are supposed to return the status
+ * byte as it is defined by SCSI.
+ */
+ scp->result = DRIVER_BYTE(DRIVER_SENSE) |
+ STATUS_BYTE(scsiqp->scsi_status);
+ } else {
+ scp->result = STATUS_BYTE(scsiqp->scsi_status);
+ }
+ break;
+
+ default:
+ /* Some other QHSTA error occurred. */
+ ASC_DBG1(1, "adv_isr_callback: host_status %x\n",
+ scsiqp->host_status);
+ scp->result = HOST_BYTE(DID_BAD_TARGET);
+ break;
+ }
+ break;
+
+ case QD_ABORTED_BY_HOST:
+ ASC_DBG(1, "adv_isr_callback: QD_ABORTED_BY_HOST\n");
+ scp->result = HOST_BYTE(DID_ABORT) | STATUS_BYTE(scsiqp->scsi_status);
+ break;
+
+ default:
+ ASC_DBG1(1, "adv_isr_callback: done_status %x\n", scsiqp->done_status);
+ scp->result = HOST_BYTE(DID_ERROR) | STATUS_BYTE(scsiqp->scsi_status);
+ break;
+ }
+
+ /*
+ * If the 'init_tidmask' bit isn't already set for the target and the
+ * current request finished normally, then set the bit for the target
+ * to indicate that a device is present.
+ */
+ if ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(scp->target)) == 0 &&
+ scsiqp->done_status == QD_NO_ERROR &&
+ scsiqp->host_status == QHSTA_NO_ERROR) {
+ boardp->init_tidmask |= ADV_TID_TO_TIDMASK(scp->target);
+ }
+
+ /*
+ * Because interrupts may be enabled by the 'Scsi_Cmnd' done
+ * function, add the command to the end of the board's done queue.
+ * The done function for the command will be called from
+ * advansys_interrupt().
+ */
+ asc_enqueue(&boardp->done, scp, ASC_BACK);
+
+ /*
+ * Free the adv_sgblk_t structure, if any, by adding it back
+ * to the board free list.
+ */
+ if (reqp->sgblkp != NULL) {
+ reqp->sgblkp->next_sgblkp = boardp->adv_sgblkp;
+ boardp->adv_sgblkp = reqp->sgblkp;
+ }
+
+ /*
+ * Free the adv_req_t structure used with the command by adding
+ * it back to the board free list.
+ */
+ reqp->next_reqp = boardp->adv_reqp;
+ boardp->adv_reqp = reqp;
+
+ ASC_DBG(1, "adv_isr_callback: done\n");
+
+ return;
+}
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+/*
+ * Search for an AdvanSys PCI device in the PCI configuration space.
+ */
+ASC_INITFUNC(
+STATIC int
+asc_srch_pci_dev(PCI_DEVICE *pciDevice)
+)
+{
+ int ret = PCI_DEVICE_NOT_FOUND;
+
+ ASC_DBG(2, "asc_srch_pci_dev: begin\n");
+
+ if (pci_scan_method == -1) {
+ pci_scan_method = asc_scan_method();
+ }
+ pciDevice->type = pci_scan_method;
+ ASC_DBG1(2, "asc_srch_pci_dev: type %d\n", pciDevice->type);
+
+ ret = asc_pci_find_dev(pciDevice);
+ ASC_DBG1(2, "asc_srch_pci_dev: asc_pci_find_dev() return %d\n", ret);
+ if (ret == PCI_DEVICE_FOUND) {
+ pciDevice->slotNumber = pciDevice->slotFound + 1;
+ pciDevice->startSlot = pciDevice->slotFound + 1;
+ } else {
+ if (pciDevice->bridge > pciDevice->busNumber) {
+ ASC_DBG2(2, "asc_srch_pci_dev: bridge %x, busNumber %x\n",
+ pciDevice->bridge, pciDevice->busNumber);
+ pciDevice->busNumber++;
+ pciDevice->slotNumber = 0;
+ pciDevice->startSlot = 0;
+ pciDevice->endSlot = 0x0f;
+ ret = asc_srch_pci_dev(pciDevice);
+ ASC_DBG1(2, "asc_srch_pci_dev: recursive call return %d\n", ret);
+ }
+ }
+
+ ASC_DBG1(2, "asc_srch_pci_dev: return %d\n", ret);
+ return ret;
+}
+
+/*
+ * Determine the access method to be used for 'pciDevice'.
+ */
+ASC_INITFUNC(
+STATIC uchar
+asc_scan_method(void)
+)
+{
+ ushort data;
+ PCI_DATA pciData;
+ uchar type;
+ uchar slot;
+
+ ASC_DBG(2, "asc_scan_method: begin\n");
+ memset(&pciData, 0, sizeof(pciData));
+ for (type = 1; type < 3; type++) {
+ pciData.type = type;
+ for (slot = 0; slot < PCI_MAX_SLOT; slot++) {
+ pciData.slot = slot;
+ data = asc_get_cfg_word(&pciData);
+ if ((data != 0xFFFF) && (data != 0x0000)) {
+ ASC_DBG2(4, "asc_scan_method: data %x, type %d\n", data, type);
+ return (type);
+ }
+ }
+ }
+ ASC_DBG1(4, "asc_scan_method: type %d\n", type);
+ return (type);
+}
+
+/*
+ * Check for an AdvanSys PCI device in 'pciDevice'.
+ *
+ * Return PCI_DEVICE_FOUND if found, otherwise return PCI_DEVICE_NOT_FOUND.
+ */
+ASC_INITFUNC(
+STATIC int
+asc_pci_find_dev(PCI_DEVICE *pciDevice)
+)
+{
+ PCI_DATA pciData;
+ ushort vendorid, deviceid;
+ uchar classcode, subclass;
+ uchar lslot;
+
+ ASC_DBG(3, "asc_pci_find_dev: begin\n");
+ pciData.type = pciDevice->type;
+ pciData.bus = pciDevice->busNumber;
+ pciData.func = pciDevice->devFunc;
+ lslot = pciDevice->startSlot;
+ for (; lslot < pciDevice->endSlot; lslot++) {
+ pciData.slot = lslot;
+ pciData.offset = VENDORID_OFFSET;
+ vendorid = asc_get_cfg_word(&pciData);
+ ASC_DBG1(3, "asc_pci_find_dev: vendorid %x\n", vendorid);
+ if (vendorid != 0xffff) {
+ pciData.offset = DEVICEID_OFFSET;
+ deviceid = asc_get_cfg_word(&pciData);
+ ASC_DBG1(3, "asc_pci_find_dev: deviceid %x\n", deviceid);
+ if ((vendorid == ASC_PCI_VENDORID) &&
+ ((deviceid == ASC_PCI_DEVICE_ID_1100) ||
+ (deviceid == ASC_PCI_DEVICE_ID_1200) ||
+ (deviceid == ASC_PCI_DEVICE_ID_1300) ||
+ (deviceid == ASC_PCI_DEVICE_ID_2300))) {
+ pciDevice->slotFound = lslot;
+ ASC_DBG(3, "asc_pci_find_dev: PCI_DEVICE_FOUND\n");
+ return PCI_DEVICE_FOUND;
+ } else {
+ pciData.offset = SUBCLASS_OFFSET;
+ subclass = asc_get_cfg_byte(&pciData);
+ pciData.offset = CLASSCODE_OFFSET;
+ classcode = asc_get_cfg_byte(&pciData);
+ if ((classcode & PCI_BASE_CLASS_BRIDGE_DEVICE) &&
+ (subclass & PCI_SUB_CLASS_PCI_TO_PCI_BRIDGE_CONTROLLER)) {
+ pciDevice->bridge++;
+ }
+ ASC_DBG2(3, "asc_pci_find_dev: subclass %x, classcode %x\n",
+ subclass, classcode);
+ }
+ }
+ }
+ return PCI_DEVICE_NOT_FOUND;
+}
+
+/*
+ * Read PCI configuration data into 'pciConfig'.
+ */
+ASC_INITFUNC(
+STATIC void
+asc_get_pci_cfg(PCI_DEVICE *pciDevice, PCI_CONFIG_SPACE *pciConfig)
+)
+{
+ PCI_DATA pciData;
+ uchar counter;
+ uchar *localConfig;
+
+ ASC_DBG1(4, "asc_get_pci_cfg: slotFound %d\n ",
+ pciDevice->slotFound);
+
+ pciData.type = pciDevice->type;
+ pciData.bus = pciDevice->busNumber;
+ pciData.slot = pciDevice->slotFound;
+ pciData.func = pciDevice->devFunc;
+ localConfig = (uchar *) pciConfig;
+
+ for (counter = 0; counter < sizeof(PCI_CONFIG_SPACE); counter++) {
+ pciData.offset = counter;
+ *localConfig = asc_get_cfg_byte(&pciData);
+ ASC_DBG1(4, "asc_get_pci_cfg: byte %x\n", *localConfig);
+ localConfig++;
+ }
+ ASC_DBG1(4, "asc_get_pci_cfg: counter %d\n", counter);
+}
+
+/*
+ * Read a word (16 bits) from the PCI configuration space.
+ *
+ * The configuration mechanism is checked for the correct access method.
+ */
+ASC_INITFUNC(
+STATIC ushort
+asc_get_cfg_word(PCI_DATA *pciData)
+)
+{
+ ushort tmp;
+ ulong address;
+ ulong lbus = pciData->bus;
+ ulong lslot = pciData->slot;
+ ulong lfunc = pciData->func;
+ uchar t2CFA, t2CF8;
+ ulong t1CF8, t1CFC;
+
+ ASC_DBG4(4, "asc_get_cfg_word: type %d, bus %lu, slot %lu, func %lu\n",
+ pciData->type, lbus, lslot, lfunc);
+
+ /*
+ * Check type of configuration mechanism.
+ */
+ if (pciData->type == 2) {
+ /*
+ * Save registers to be restored later.
+ */
+ t2CFA = inp(0xCFA); /* save PCI bus register */
+ t2CF8 = inp(0xCF8); /* save config space enable register */
+
+ /*
+ * Write the bus and enable registers.
+ */
+ /* set for type 1 cycle, if needed */
+ outp(0xCFA, pciData->bus);
+ /* set the function number */
+ outp(0xCF8, 0x10 | (pciData->func << 1)) ;
+
+ /*
+ * Read the configuration space type 2 locations.
+ */
+ tmp = (ushort) inpw(0xC000 | ((pciData->slot << 8) + pciData->offset));
+
+ outp(0xCFA, t2CFA); /* save PCI bus register */
+ outp(0xCF8, t2CF8); /* save config space enable register */
+ } else {
+ /*
+ * Type 1 or 3 configuration mechanism.
+ *
+ * Save the CONFIG_ADDRESS and CONFIG_DATA register values.
+ */
+ t1CF8 = inpl(0xCF8);
+ t1CFC = inpl(0xCFC);
+
+ /*
+ * enable <31>, bus = <23:16>, slot = <15:11>,
+ * func = <10:8>, reg = <7:2>
+ */
+ address = (ulong) ((lbus << 16) | (lslot << 11) |
+ (lfunc << 8) | (pciData->offset & 0xFC) | 0x80000000L);
+
+ /*
+ * Write out the address to CONFIG_ADDRESS.
+ */
+ outpl(0xCF8, address);
+
+ /*
+ * Read in word from CONFIG_DATA.
+ */
+ tmp = (ushort) ((inpl(0xCFC) >>
+ ((pciData->offset & 2) * 8)) & 0xFFFF);
+
+ /*
+ * Restore registers.
+ */
+ outpl(0xCF8, t1CF8);
+ outpl(0xCFC, t1CFC);
+ }
+ ASC_DBG1(4, "asc_get_cfg_word: config data: %x\n", tmp);
+ return tmp;
+}
+
+/*
+ * Reads a byte from the PCI configuration space.
+ *
+ * The configuration mechanism is checked for the correct access method.
+ */
+ASC_INITFUNC(
+STATIC uchar
+asc_get_cfg_byte(PCI_DATA *pciData)
+)
+{
+ uchar tmp;
+ ulong address;
+ ulong lbus = pciData->bus, lslot = pciData->slot, lfunc = pciData->func;
+ uchar t2CFA, t2CF8;
+ ulong t1CF8, t1CFC;
+
+ ASC_DBG1(4, "asc_get_cfg_byte: type: %d\n", pciData->type);
+
+ /*
+ * Check type of configuration mechanism.
+ */
+ if (pciData->type == 2) {
+ /*
+ * Save registers to be restored later.
+ */
+ t2CFA = inp(0xCFA); /* save PCI bus register */
+ t2CF8 = inp(0xCF8); /* save config space enable register */
+
+ /*
+ * Write the bus and enable registers.
+ */
+ /* set for type 1 cycle, if needed */
+ outp(0xCFA, pciData->bus);
+ /* set the function number */
+ outp(0xCF8, 0x10 | (pciData->func << 1));
+
+ /*
+ * Read configuration space type 2 locations.
+ */
+ tmp = inp(0xC000 | ((pciData->slot << 8) + pciData->offset));
+
+ /*
+ * Restore registers.
+ */
+ outp(0xCF8, t2CF8); /* restore the enable register */
+ outp(0xCFA, t2CFA); /* restore PCI bus register */
+ } else {
+ /*
+ * Type 1 or 3 configuration mechanism.
+ *
+ * Save CONFIG_ADDRESS and CONFIG_DATA register values.
+ */
+ t1CF8 = inpl(0xCF8);
+ t1CFC = inpl(0xCFC);
+
+ /*
+ * enable <31>, bus = <23:16>, slot = <15:11>, func = <10:8>,
+ * reg = <7:2>
+ */
+ address = (ulong) ((lbus << 16) | (lslot << 11) |
+ (lfunc << 8) | (pciData->offset & 0xFC) | 0x80000000L);
+
+ /*
+ * Write out address to CONFIG_ADDRESS.
+ */
+ outpl(0xCF8, address);
+
+ /*
+ * Read in word from CONFIG_DATA.
+ */
+ tmp = (uchar) ((inpl(0xCFC) >> ((pciData->offset & 3) * 8)) & 0xFF);
+
+ /*
+ * Restore registers.
+ */
+ outpl(0xCF8, t1CF8);
+ outpl(0xCFC, t1CFC);
+ }
+ ASC_DBG1(4, "asc_get_cfg_byte: config data: %x\n", tmp);
+ return tmp;
+}
+
+/*
+ * Write a byte to the PCI configuration space.
+ */
+ASC_INITFUNC(
+STATIC void
+asc_put_cfg_byte(PCI_DATA *pciData, uchar byte_data)
+)
+{
+ ulong tmpl;
+ ulong address;
+ ulong lbus = pciData->bus, lslot = pciData->slot, lfunc = pciData->func;
+ uchar t2CFA, t2CF8;
+ ulong t1CF8, t1CFC;
+
+ ASC_DBG2(4, "asc_put_cfg_byte: type: %d, byte_data %x\n",
+ pciData->type, byte_data);
+
+ /*
+ * Check type of configuration mechanism.
+ */
+ if (pciData->type == 2) {
+
+ /*
+ * Save registers to be restored later.
+ */
+ t2CFA = inp(0xCFA); /* save PCI bus register */
+ t2CF8 = inp(0xCF8); /* save config space enable register */
+
+ /*
+ * Write bus and enable registers.
+ */
+ outp(0xCFA, pciData->bus);
+
+ /*
+ * Set the function number.
+ */
+ outp(0xCF8, 0x10 | (pciData->func << 1));
+
+ /*
+ * Write the configuration space type 2 locations.
+ */
+ outp(0xC000 | ((pciData->slot << 8) + pciData->offset), byte_data);
+
+ /*
+ * Restore registers.
+ */
+ outp(0xCF8, t2CF8); /* restore the enable register */
+ outp(0xCFA, t2CFA); /* restore PCI bus register */
+ } else {
+
+ /*
+ * Type 1 or 3 configuration mechanism.
+ *
+ * Save the CONFIG_ADDRESS and CONFIG_DATA register values.
+ */
+ t1CF8 = inpl(0xCF8);
+ t1CFC = inpl(0xCFC);
+
+ /*
+ * enable <31>, bus = <23:16>, slot = <15:11>, func = <10:8>,
+ * reg = <7:2>
+ */
+ address = (ulong) ((lbus << 16) | (lslot << 11) | (lfunc << 8) |
+ (pciData->offset & 0xFC) | 0x80000000L);
+ /*
+ * Write out address to CONFIG_ADDRESS.
+ */
+ outpl(0xCF8, address);
+
+ /*
+ * Write double word to CONFIG_DATA preserving the bytes
+ * in the double not written.
+ */
+ tmpl = inpl(0xCFC) & ~(0xFF << ((pciData->offset & 3) * 8));
+ outpl(0xCFC, tmpl | (byte_data << ((pciData->offset & 3) * 8)));
+
+ /*
+ * Restore registers.
+ */
+ outpl(0xCF8, t1CF8);
+ outpl(0xCFC, t1CFC);
+ }
+ ASC_DBG(4, "asc_put_cfg_byte: end\n");
+}
+#endif /* ASC_CONFIG_PCI */
+#endif /* version < v2.1.93 */
+
+/*
+ * Add a 'REQP' to the end of specified queue. Set 'tidmask'
+ * to indicate a command is queued for the device.
+ *
+ * 'flag' may be either ASC_FRONT or ASC_BACK.
+ *
+ * 'REQPNEXT(reqp)' returns reqp's next pointer.
+ */
+STATIC void
+asc_enqueue(asc_queue_t *ascq, REQP reqp, int flag)
+{
+ int tid;
+
+ ASC_DBG3(3, "asc_enqueue: ascq %x, reqp %x, flag %d\n",
+ (unsigned) ascq, (unsigned) reqp, flag);
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ ASC_ASSERT(reqp != NULL);
+ ASC_ASSERT(flag == ASC_FRONT || flag == ASC_BACK);
+ tid = REQPTID(reqp);
+ ASC_ASSERT(tid >= 0 && tid <= ADV_MAX_TID);
+ if (flag == ASC_FRONT) {
+ REQPNEXT(reqp) = ascq->q_first[tid];
+ ascq->q_first[tid] = reqp;
+ /* If the queue was empty, set the last pointer. */
+ if (ascq->q_last[tid] == NULL) {
+ ascq->q_last[tid] = reqp;
+ }
+ } else { /* ASC_BACK */
+ if (ascq->q_last[tid] != NULL) {
+ REQPNEXT(ascq->q_last[tid]) = reqp;
+ }
+ ascq->q_last[tid] = reqp;
+ REQPNEXT(reqp) = NULL;
+ /* If the queue was empty, set the first pointer. */
+ if (ascq->q_first[tid] == NULL) {
+ ascq->q_first[tid] = reqp;
+ }
+ }
+ /* The queue has at least one entry, set its bit. */
+ ascq->q_tidmask |= ADV_TID_TO_TIDMASK(tid);
+#ifdef ADVANSYS_STATS
+ /* Maintain request queue statistics. */
+ ascq->q_tot_cnt[tid]++;
+ ascq->q_cur_cnt[tid]++;
+ if (ascq->q_cur_cnt[tid] > ascq->q_max_cnt[tid]) {
+ ascq->q_max_cnt[tid] = ascq->q_cur_cnt[tid];
+ ASC_DBG2(2, "asc_enqueue: new q_max_cnt[%d] %d\n",
+ tid, ascq->q_max_cnt[tid]);
+ }
+ REQPTIME(reqp) = REQTIMESTAMP();
+#endif /* ADVANSYS_STATS */
+ ASC_DBG1(3, "asc_enqueue: reqp %x\n", (unsigned) reqp);
+ return;
+}
+
+/*
+ * Return first queued 'REQP' on the specified queue for
+ * the specified target device. Clear the 'tidmask' bit for
+ * the device if no more commands are left queued for it.
+ *
+ * 'REQPNEXT(reqp)' returns reqp's next pointer.
+ */
+STATIC REQP
+asc_dequeue(asc_queue_t *ascq, int tid)
+{
+ REQP reqp;
+
+ ASC_DBG2(3, "asc_dequeue: ascq %x, tid %d\n", (unsigned) ascq, tid);
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ ASC_ASSERT(tid >= 0 && tid <= ADV_MAX_TID);
+ if ((reqp = ascq->q_first[tid]) != NULL) {
+ ASC_ASSERT(ascq->q_tidmask & ADV_TID_TO_TIDMASK(tid));
+ ascq->q_first[tid] = REQPNEXT(reqp);
+ /* If the queue is empty, clear its bit and the last pointer. */
+ if (ascq->q_first[tid] == NULL) {
+ ascq->q_tidmask &= ~ADV_TID_TO_TIDMASK(tid);
+ ASC_ASSERT(ascq->q_last[tid] == reqp);
+ ascq->q_last[tid] = NULL;
+ }
+#ifdef ADVANSYS_STATS
+ /* Maintain request queue statistics. */
+ ascq->q_cur_cnt[tid]--;
+ ASC_ASSERT(ascq->q_cur_cnt[tid] >= 0);
+ REQTIMESTAT("asc_dequeue", ascq, reqp, tid);
+#endif /* ADVANSYS_STATS */
+ }
+ ASC_DBG1(3, "asc_dequeue: reqp %x\n", (unsigned) reqp);
+ return reqp;
+}
+
+/*
+ * Return a pointer to a singly linked list of all the requests queued
+ * for 'tid' on the 'asc_queue_t' pointed to by 'ascq'.
+ *
+ * If 'lastpp' is not NULL, '*lastpp' will be set to point to the
+ * the last request returned in the singly linked list.
+ *
+ * 'tid' should either be a valid target id or if it is ASC_TID_ALL,
+ * then all queued requests are concatenated into one list and
+ * returned.
+ *
+ * Note: If 'lastpp' is used to append a new list to the end of
+ * an old list, only change the old list last pointer if '*lastpp'
+ * (or the function return value) is not NULL, i.e. use a temporary
+ * variable for 'lastpp' and check its value after the function return
+ * before assigning it to the list last pointer.
+ *
+ * Unfortunately collecting queuing time statistics adds overhead to
+ * the function that isn't inherent to the function's algorithm.
+ */
+STATIC REQP
+asc_dequeue_list(asc_queue_t *ascq, REQP *lastpp, int tid)
+{
+ REQP firstp, lastp;
+ int i;
+
+ ASC_DBG2(3, "asc_dequeue_list: ascq %x, tid %d\n", (unsigned) ascq, tid);
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ ASC_ASSERT((tid == ASC_TID_ALL) || (tid >= 0 && tid <= ADV_MAX_TID));
+
+ /*
+ * If 'tid' is not ASC_TID_ALL, return requests only for
+ * the specified 'tid'. If 'tid' is ASC_TID_ALL, return all
+ * requests for all tids.
+ */
+ if (tid != ASC_TID_ALL) {
+ /* Return all requests for the specified 'tid'. */
+ if ((ascq->q_tidmask & ADV_TID_TO_TIDMASK(tid)) == 0) {
+ /* List is empty; Set first and last return pointers to NULL. */
+ firstp = lastp = NULL;
+ } else {
+ firstp = ascq->q_first[tid];
+ lastp = ascq->q_last[tid];
+ ascq->q_first[tid] = ascq->q_last[tid] = NULL;
+ ascq->q_tidmask &= ~ADV_TID_TO_TIDMASK(tid);
+#ifdef ADVANSYS_STATS
+ {
+ REQP reqp;
+ ascq->q_cur_cnt[tid] = 0;
+ for (reqp = firstp; reqp; reqp = REQPNEXT(reqp)) {
+ REQTIMESTAT("asc_dequeue_list", ascq, reqp, tid);
+ }
+ }
+#endif /* ADVANSYS_STATS */
+ }
+ } else {
+ /* Return all requests for all tids. */
+ firstp = lastp = NULL;
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if (ascq->q_tidmask & ADV_TID_TO_TIDMASK(i)) {
+ if (firstp == NULL) {
+ firstp = ascq->q_first[i];
+ lastp = ascq->q_last[i];
+ } else {
+ ASC_ASSERT(lastp != NULL);
+ REQPNEXT(lastp) = ascq->q_first[i];
+ lastp = ascq->q_last[i];
+ }
+ ascq->q_first[i] = ascq->q_last[i] = NULL;
+ ascq->q_tidmask &= ~ADV_TID_TO_TIDMASK(i);
+#ifdef ADVANSYS_STATS
+ ascq->q_cur_cnt[i] = 0;
+#endif /* ADVANSYS_STATS */
+ }
+ }
+#ifdef ADVANSYS_STATS
+ {
+ REQP reqp;
+ for (reqp = firstp; reqp; reqp = REQPNEXT(reqp)) {
+ REQTIMESTAT("asc_dequeue_list", ascq, reqp, reqp->target);
+ }
+ }
+#endif /* ADVANSYS_STATS */
+ }
+ if (lastpp) {
+ *lastpp = lastp;
+ }
+ ASC_DBG1(3, "asc_dequeue_list: firstp %x\n", (unsigned) firstp);
+ return firstp;
+}
+
+/*
+ * Remove the specified 'REQP' from the specified queue for
+ * the specified target device. Clear the 'tidmask' bit for the
+ * device if no more commands are left queued for it.
+ *
+ * 'REQPNEXT(reqp)' returns reqp's the next pointer.
+ *
+ * Return ASC_TRUE if the command was found and removed,
+ * otherwise return ASC_FALSE.
+ */
+STATIC int
+asc_rmqueue(asc_queue_t *ascq, REQP reqp)
+{
+ REQP currp, prevp;
+ int tid;
+ int ret = ASC_FALSE;
+
+ ASC_DBG2(3, "asc_rmqueue: ascq %x, reqp %x\n",
+ (unsigned) ascq, (unsigned) reqp);
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ ASC_ASSERT(reqp != NULL);
+
+ tid = REQPTID(reqp);
+ ASC_ASSERT(tid >= 0 && tid <= ADV_MAX_TID);
+
+ /*
+ * Handle the common case of 'reqp' being the first
+ * entry on the queue.
+ */
+ if (reqp == ascq->q_first[tid]) {
+ ret = ASC_TRUE;
+ ascq->q_first[tid] = REQPNEXT(reqp);
+ /* If the queue is now empty, clear its bit and the last pointer. */
+ if (ascq->q_first[tid] == NULL) {
+ ascq->q_tidmask &= ~ADV_TID_TO_TIDMASK(tid);
+ ASC_ASSERT(ascq->q_last[tid] == reqp);
+ ascq->q_last[tid] = NULL;
+ }
+ } else if (ascq->q_first[tid] != NULL) {
+ ASC_ASSERT(ascq->q_last[tid] != NULL);
+ /*
+ * Because the case of 'reqp' being the first entry has been
+ * handled above and it is known the queue is not empty, if
+ * 'reqp' is found on the queue it is guaranteed the queue will
+ * not become empty and that 'q_first[tid]' will not be changed.
+ *
+ * Set 'prevp' to the first entry, 'currp' to the second entry,
+ * and search for 'reqp'.
+ */
+ for (prevp = ascq->q_first[tid], currp = REQPNEXT(prevp);
+ currp; prevp = currp, currp = REQPNEXT(currp)) {
+ if (currp == reqp) {
+ ret = ASC_TRUE;
+ REQPNEXT(prevp) = REQPNEXT(currp);
+ REQPNEXT(reqp) = NULL;
+ if (ascq->q_last[tid] == reqp) {
+ ascq->q_last[tid] = prevp;
+ }
+ break;
+ }
+ }
+ }
+#ifdef ADVANSYS_STATS
+ /* Maintain request queue statistics. */
+ if (ret == ASC_TRUE) {
+ ascq->q_cur_cnt[tid]--;
+ REQTIMESTAT("asc_rmqueue", ascq, reqp, tid);
+ }
+ ASC_ASSERT(ascq->q_cur_cnt[tid] >= 0);
+#endif /* ADVANSYS_STATS */
+ ASC_DBG2(3, "asc_rmqueue: reqp %x, ret %d\n", (unsigned) reqp, ret);
+ return ret;
+}
+
+/*
+ * If the specified 'REQP' is queued on the specified queue for
+ * the specified target device, return ASC_TRUE.
+ */
+STATIC int
+asc_isqueued(asc_queue_t *ascq, REQP reqp)
+{
+ REQP treqp;
+ int tid;
+ int ret = ASC_FALSE;
+
+ ASC_DBG2(3, "asc_isqueued: ascq %x, reqp %x\n",
+ (unsigned) ascq, (unsigned) reqp);
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ ASC_ASSERT(reqp != NULL);
+
+ tid = REQPTID(reqp);
+ ASC_ASSERT(tid >= 0 && tid <= ADV_MAX_TID);
+
+ for (treqp = ascq->q_first[tid]; treqp; treqp = REQPNEXT(treqp)) {
+ ASC_ASSERT(ascq->q_tidmask & ADV_TID_TO_TIDMASK(tid));
+ if (treqp == reqp) {
+ ret = ASC_TRUE;
+ break;
+ }
+ }
+ ASC_DBG1(3, "asc_isqueued: ret %x\n", ret);
+ return ret;
+}
+
+/*
+ * Execute as many queued requests as possible for the specified queue.
+ *
+ * Calls asc_execute_scsi_cmnd() to execute a REQP/Scsi_Cmnd.
+ */
+STATIC void
+asc_execute_queue(asc_queue_t *ascq)
+{
+ ADV_SCSI_BIT_ID_TYPE scan_tidmask;
+ REQP reqp;
+ int i;
+
+ ASC_DBG1(1, "asc_execute_queue: ascq %x\n", (unsigned) ascq);
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ /*
+ * Execute queued commands for devices attached to
+ * the current board in round-robin fashion.
+ */
+ scan_tidmask = ascq->q_tidmask;
+ do {
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if (scan_tidmask & ADV_TID_TO_TIDMASK(i)) {
+ if ((reqp = asc_dequeue(ascq, i)) == NULL) {
+ scan_tidmask &= ~ADV_TID_TO_TIDMASK(i);
+ } else if (asc_execute_scsi_cmnd((Scsi_Cmnd *) reqp)
+ == ASC_BUSY) {
+ scan_tidmask &= ~ADV_TID_TO_TIDMASK(i);
+ /* Put the request back at front of the list. */
+ asc_enqueue(ascq, reqp, ASC_FRONT);
+ }
+ }
+ }
+ } while (scan_tidmask);
+ return;
+}
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,0)
+/*
+ * asc_prt_board_devices()
+ *
+ * Print driver information for devices attached to the board.
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_board_devices(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ int leftlen;
+ int totlen;
+ int len;
+ int chip_scsi_id;
+ int i;
+
+ boardp = ASC_BOARDP(shp);
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen,
+"\nDevice Information for AdvanSys SCSI Host %d:\n", shp->host_no);
+ ASC_PRT_NEXT();
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ chip_scsi_id = boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id;
+ } else {
+ chip_scsi_id = boardp->dvc_var.adv_dvc_var.chip_scsi_id;
+ }
+
+ len = asc_prt_line(cp, leftlen, "Target IDs Detected:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if (boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) {
+ len = asc_prt_line(cp, leftlen, " %X,", i);
+ ASC_PRT_NEXT();
+ }
+ }
+ len = asc_prt_line(cp, leftlen, " (%X=Host Adapter)\n", chip_scsi_id);
+ ASC_PRT_NEXT();
+
+ return totlen;
+}
+
+/*
+ * Display Wide Board BIOS Information.
+ */
+STATIC int
+asc_prt_adv_bios(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ int leftlen;
+ int totlen;
+ int len;
+ int upgrade = ASC_FALSE;
+ ushort major, minor, letter;
+
+ boardp = ASC_BOARDP(shp);
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen, "\nROM BIOS Version: ");
+ ASC_PRT_NEXT();
+
+ /*
+ * If the BIOS saved a valid signature, then fill in
+ * the BIOS code segment base address.
+ */
+ if (boardp->bios_signature != 0x55AA) {
+ len = asc_prt_line(cp, leftlen, "Pre-3.1\n");
+ ASC_PRT_NEXT();
+ upgrade = ASC_TRUE;
+ } else {
+ major = (boardp->bios_version >> 12) & 0xF;
+ minor = (boardp->bios_version >> 8) & 0xF;
+ letter = (boardp->bios_version & 0xFF);
+
+ len = asc_prt_line(cp, leftlen, "%d.%d%c\n",
+ major, minor, letter >= 26 ? '?' : letter + 'A');
+ ASC_PRT_NEXT();
+
+ /* Current available ROM BIOS release is 3.1C. */
+ if (major < 3 || (major <= 3 && minor < 1) ||
+ (major <= 3 && minor <= 1 && letter < ('C'- 'A'))) {
+ upgrade = ASC_TRUE;
+ }
+ }
+ if (upgrade == ASC_TRUE) {
+ len = asc_prt_line(cp, leftlen,
+"Newer version of ROM BIOS available: ftp://ftp.advansys.com/pub\n");
+ ASC_PRT_NEXT();
+ }
+
+ return totlen;
+}
+
+/*
+ * Add serial number to information bar if signature AAh
+ * is found in at bit 15-9 (7 bits) of word 1.
+ *
+ * Serial Number consists fo 12 alpha-numeric digits.
+ *
+ * 1 - Product type (A,B,C,D..) Word0: 15-13 (3 bits)
+ * 2 - MFG Location (A,B,C,D..) Word0: 12-10 (3 bits)
+ * 3-4 - Product ID (0-99) Word0: 9-0 (10 bits)
+ * 5 - Product revision (A-J) Word0: " "
+ *
+ * Signature Word1: 15-9 (7 bits)
+ * 6 - Year (0-9) Word1: 8-6 (3 bits) & Word2: 15 (1 bit)
+ * 7-8 - Week of the year (1-52) Word1: 5-0 (6 bits)
+ *
+ * 9-12 - Serial Number (A001-Z999) Word2: 14-0 (15 bits)
+ *
+ * Note 1: Only production cards will have a serial number.
+ *
+ * Note 2: Signature is most significant 7 bits (0xFE).
+ *
+ * Returns ASC_TRUE if serial number found, otherwise returns ASC_FALSE.
+ */
+STATIC int
+asc_get_eeprom_string(ushort *serialnum, uchar *cp)
+{
+ ushort w, num;
+
+ if ((serialnum[1] & 0xFE00) != ((ushort) 0xAA << 8)) {
+ return ASC_FALSE;
+ } else {
+ /*
+ * First word - 6 digits.
+ */
+ w = serialnum[0];
+
+ /* Product type - 1st digit. */
+ if ((*cp = 'A' + ((w & 0xE000) >> 13)) == 'H') {
+ /* Product type is P=Prototype */
+ *cp += 0x8;
+ }
+ cp++;
+
+ /* Manufacturing location - 2nd digit. */
+ *cp++ = 'A' + ((w & 0x1C00) >> 10);
+
+ /* Product ID - 3rd, 4th digits. */
+ num = w & 0x3FF;
+ *cp++ = '0' + (num / 100);
+ num %= 100;
+ *cp++ = '0' + (num / 10);
+
+ /* Product revision - 5th digit. */
+ *cp++ = 'A' + (num % 10);
+
+ /*
+ * Second word
+ */
+ w = serialnum[1];
+
+ /*
+ * Year - 6th digit.
+ *
+ * If bit 15 of third word is set, then the
+ * last digit of the year is greater than 7.
+ */
+ if (serialnum[2] & 0x8000) {
+ *cp++ = '8' + ((w & 0x1C0) >> 6);
+ } else {
+ *cp++ = '0' + ((w & 0x1C0) >> 6);
+ }
+
+ /* Week of year - 7th, 8th digits. */
+ num = w & 0x003F;
+ *cp++ = '0' + num / 10;
+ num %= 10;
+ *cp++ = '0' + num;
+
+ /*
+ * Third word
+ */
+ w = serialnum[2] & 0x7FFF;
+
+ /* Serial number - 9th digit. */
+ *cp++ = 'A' + (w / 1000);
+
+ /* 10th, 11th, 12th digits. */
+ num = w % 1000;
+ *cp++ = '0' + num / 100;
+ num %= 100;
+ *cp++ = '0' + num / 10;
+ num %= 10;
+ *cp++ = '0' + num;
+
+ *cp = '\0'; /* Null Terminate the string. */
+ return ASC_TRUE;
+ }
+}
+
+/*
+ * asc_prt_asc_board_eeprom()
+ *
+ * Print board EEPROM configuration.
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_asc_board_eeprom(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ ASC_DVC_VAR *asc_dvc_varp;
+ int leftlen;
+ int totlen;
+ int len;
+ ASCEEP_CONFIG *ep;
+ int i;
+ int isa_dma_speed[] = { 10, 8, 7, 6, 5, 4, 3, 2 };
+ uchar serialstr[13];
+
+ boardp = ASC_BOARDP(shp);
+ asc_dvc_varp = &boardp->dvc_var.asc_dvc_var;
+ ep = &boardp->eep_config.asc_eep;
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen,
+"\nEEPROM Settings for AdvanSys SCSI Host %d:\n", shp->host_no);
+ ASC_PRT_NEXT();
+
+ if (asc_get_eeprom_string((ushort *) &ep->adapter_info[0], serialstr) ==
+ ASC_TRUE) {
+ len = asc_prt_line(cp, leftlen, " Serial Number: %s\n", serialstr);
+ ASC_PRT_NEXT();
+ } else {
+ if (ep->adapter_info[5] == 0xBB) {
+ len = asc_prt_line(cp, leftlen,
+ " Default Settings Used for EEPROM-less Adapter.\n");
+ ASC_PRT_NEXT();
+ } else {
+ len = asc_prt_line(cp, leftlen,
+ " Serial Number Signature Not Present.\n");
+ ASC_PRT_NEXT();
+ }
+ }
+
+ len = asc_prt_line(cp, leftlen,
+" Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
+ ep->chip_scsi_id, ep->max_total_qng, ep->max_tag_qng);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" cntl %x, no_scam %x\n",
+ ep->cntl, ep->no_scam);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Target ID: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %d", i);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Disconnects: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->disc_enable & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Command Queuing: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->use_cmd_qng & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Start Motor: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->start_motor & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Synchronous Transfer:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->init_sdtr & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ if (asc_dvc_varp->bus_type & ASC_IS_ISA) {
+ len = asc_prt_line(cp, leftlen,
+" Host ISA DMA speed: %d MB/S\n",
+ isa_dma_speed[ep->isa_dma_speed]);
+ ASC_PRT_NEXT();
+ }
+
+ return totlen;
+}
+
+/*
+ * asc_prt_adv_board_eeprom()
+ *
+ * Print board EEPROM configuration.
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_adv_board_eeprom(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ ADV_DVC_VAR *adv_dvc_varp;
+ int leftlen;
+ int totlen;
+ int len;
+ int i;
+ char *termstr;
+ uchar serialstr[13];
+ ADVEEP_CONFIG *ep;
+
+ boardp = ASC_BOARDP(shp);
+ adv_dvc_varp = &boardp->dvc_var.adv_dvc_var;
+ ep = &boardp->eep_config.adv_eep;
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen,
+"\nEEPROM Settings for AdvanSys SCSI Host %d:\n", shp->host_no);
+ ASC_PRT_NEXT();
+
+ if (asc_get_eeprom_string(&ep->serial_number_word1, serialstr) ==
+ ASC_TRUE) {
+ len = asc_prt_line(cp, leftlen, " Serial Number: %s\n", serialstr);
+ ASC_PRT_NEXT();
+ } else {
+ len = asc_prt_line(cp, leftlen,
+ " Serial Number Signature Not Present.\n");
+ ASC_PRT_NEXT();
+ }
+
+ len = asc_prt_line(cp, leftlen,
+" Host SCSI ID: %u, Host Queue Size: %u, Device Queue Size: %u\n",
+ ep->adapter_scsi_id, ep->max_host_qng, ep->max_dvc_qng);
+ ASC_PRT_NEXT();
+
+ switch (ep->termination) {
+ case 1:
+ termstr = "Low Off/High Off";
+ break;
+ case 2:
+ termstr = "Low Off/High On";
+ break;
+ case 3:
+ termstr = "Low On/High On";
+ break;
+ default:
+ case 0:
+ termstr = "Automatic";
+ break;
+ }
+
+ len = asc_prt_line(cp, leftlen,
+" termination: %u (%s), bios_ctrl: %x\n",
+ ep->termination, termstr, ep->bios_ctrl);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Target ID: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %X", i);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Disconnects: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->disc_enable & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Command Queuing: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->tagqng_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Start Motor: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->start_motor & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Synchronous Transfer:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->sdtr_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Ultra Transfer: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->ultra_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Wide Transfer: ");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ len = asc_prt_line(cp, leftlen, " %c",
+ (ep->wdtr_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ return totlen;
+}
+
+/*
+ * asc_prt_driver_conf()
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_driver_conf(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ int leftlen;
+ int totlen;
+ int len;
+ int chip_scsi_id;
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+ int i;
+#endif /* version >= v1.3.89 */
+
+ boardp = ASC_BOARDP(shp);
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen,
+"\nLinux Driver Configuration and Information for AdvanSys SCSI Host %d:\n",
+ shp->host_no);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,89)
+" host_busy %u, last_reset %u, max_id %u, max_lun %u\n",
+ shp->host_busy, shp->last_reset, shp->max_id, shp->max_lun);
+#else /* version >= v1.3.89 */
+" host_busy %u, last_reset %u, max_id %u, max_lun %u, max_channel %u\n",
+ shp->host_busy, shp->last_reset, shp->max_id, shp->max_lun,
+ shp->max_channel);
+#endif /* version >= v1.3.89 */
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,57)
+" can_queue %d, this_id %d, sg_tablesize %u, cmd_per_lun %u\n",
+ shp->can_queue, shp->this_id, shp->sg_tablesize, shp->cmd_per_lun);
+#else /* version >= v1.3.57 */
+" unique_id %d, can_queue %d, this_id %d, sg_tablesize %u, cmd_per_lun %u\n",
+ shp->unique_id, shp->can_queue, shp->this_id, shp->sg_tablesize,
+ shp->cmd_per_lun);
+#endif /* version >= v1.3.57 */
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,57)
+" unchecked_isa_dma %d, loaded_as_module %d\n",
+ shp->unchecked_isa_dma, shp->loaded_as_module);
+#else /* version >= v1.3.57 */
+" unchecked_isa_dma %d, use_clustering %d, loaded_as_module %d\n",
+ shp->unchecked_isa_dma, shp->use_clustering, shp->loaded_as_module);
+#endif /* version >= v1.3.57 */
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, " flags %x, last_reset %x, jiffies %x\n",
+ boardp->flags, boardp->last_reset, jiffies);
+ ASC_PRT_NEXT();
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ chip_scsi_id = boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id;
+ } else {
+ chip_scsi_id = boardp->dvc_var.adv_dvc_var.chip_scsi_id;
+ }
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+ if (boardp->flags & ASC_SELECT_QUEUE_DEPTHS) {
+ len = asc_prt_line(cp, leftlen, " queue_depth:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ if (boardp->device[i] == NULL) {
+ continue;
+ }
+ len = asc_prt_line(cp, leftlen, " %X:%d",
+ i, boardp->device[i]->queue_depth);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+ }
+#endif /* version >= v1.3.89 */
+
+#if ASC_QUEUE_FLOW_CONTROL
+ if (ASC_NARROW_BOARD(boardp)) {
+ len = asc_prt_line(cp, leftlen, " queue_curr_depth:");
+ ASC_PRT_NEXT();
+ /* Use ASC_MAX_TID for Narrow Board. */
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if ((boardp->asc_dvc_cfg.chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ if (boardp->device[i] == NULL) {
+ continue;
+ }
+ len = asc_prt_line(cp, leftlen, " %d:%d",
+ i, boardp->device[i]->queue_curr_depth);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, " queue_count:");
+ ASC_PRT_NEXT();
+ /* Use ASC_MAX_TID for Narrow Board. */
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if ((boardp->asc_dvc_cfg.chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ if (boardp->device[i] == NULL) {
+ continue;
+ }
+ len = asc_prt_line(cp, leftlen, " %d:%d",
+ i, boardp->device[i]->queue_count);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+ }
+#endif /* ASC_QUEUE_FLOW_CONTROL */
+
+ return totlen;
+}
+
+/*
+ * asc_prt_asc_board_info()
+ *
+ * Print dynamic board configuration information.
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_asc_board_info(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ int leftlen;
+ int totlen;
+ int len;
+ ASC_DVC_VAR *v;
+ ASC_DVC_CFG *c;
+ int i;
+
+ boardp = ASC_BOARDP(shp);
+ v = &boardp->dvc_var.asc_dvc_var;
+ c = &boardp->dvc_cfg.asc_dvc_cfg;
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen,
+"\nAsc Library Configuration and Statistics for AdvanSys SCSI Host %d:\n",
+ shp->host_no);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" chip_version %u, lib_version %x, lib_serial_no %u, mcode_date %x\n",
+ c->chip_version, c->lib_version, c->lib_serial_no, c->mcode_date);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" mcode_version %x, err_code %u\n",
+ c->mcode_version, v->err_code);
+ ASC_PRT_NEXT();
+
+ /* Current number of commands waiting for the host. */
+ len = asc_prt_line(cp, leftlen,
+" Total Command Pending: %d\n", v->cur_total_qng);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Command Queuing:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if ((boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ len = asc_prt_line(cp, leftlen, " %d:%c",
+ i, (v->use_tagged_qng & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ /* Current number of commands waiting for a device. */
+ len = asc_prt_line(cp, leftlen,
+" Command Queue Pending:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if ((boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ len = asc_prt_line(cp, leftlen, " %d:%u", i, v->cur_dvc_qng[i]);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ /* Current limit on number of commands that can be sent to a device. */
+ len = asc_prt_line(cp, leftlen,
+" Command Queue Limit:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if ((boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ len = asc_prt_line(cp, leftlen, " %d:%u", i, v->max_dvc_qng[i]);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ /* Indicate whether the device has returned queue full status. */
+ len = asc_prt_line(cp, leftlen,
+" Command Queue Full:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if ((boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ if (boardp->queue_full & ADV_TID_TO_TIDMASK(i)) {
+ len = asc_prt_line(cp, leftlen, " %d:Y-%d",
+ i, boardp->queue_full_cnt[i]);
+ } else {
+ len = asc_prt_line(cp, leftlen, " %d:N", i);
+ }
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Synchronous Transfer:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if ((boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ len = asc_prt_line(cp, leftlen, " %d:%c",
+ i, (v->sdtr_done & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ uchar syn_period_ix;
+
+ if ((boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+ if ((v->sdtr_done & ADV_TID_TO_TIDMASK(i)) == 0) {
+ continue;
+ }
+ syn_period_ix = (boardp->sdtr_data[i] >> 4) & (v->max_sdtr_index - 1);
+ len = asc_prt_line(cp, leftlen, " %d:", i);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+ " Transfer Period Factor: %d (%d.%d Mhz),",
+ v->sdtr_period_tbl[syn_period_ix],
+ 250 / v->sdtr_period_tbl[syn_period_ix],
+ ASC_TENTHS(250, v->sdtr_period_tbl[syn_period_ix]));
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, " REQ/ACK Offset: %d\n",
+ boardp->sdtr_data[i] & ASC_SYN_MAX_OFFSET);
+ ASC_PRT_NEXT();
+ }
+
+ return totlen;
+}
+
+/*
+ * asc_prt_adv_board_info()
+ *
+ * Print dynamic board configuration information.
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_adv_board_info(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ asc_board_t *boardp;
+ int leftlen;
+ int totlen;
+ int len;
+ int i;
+ ADV_DVC_VAR *v;
+ ADV_DVC_CFG *c;
+ AdvPortAddr iop_base;
+ ushort chip_scsi_id;
+ ushort lramword;
+ uchar lrambyte;
+ ushort sdtr_able;
+ ushort period;
+
+ boardp = ASC_BOARDP(shp);
+ v = &boardp->dvc_var.adv_dvc_var;
+ c = &boardp->dvc_cfg.adv_dvc_cfg;
+ iop_base = v->iop_base;
+ chip_scsi_id = v->chip_scsi_id;
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_line(cp, leftlen,
+"\nAdv Library Configuration and Statistics for AdvanSys SCSI Host %d:\n",
+ shp->host_no);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" iop_base %lx, cable_detect: %X, err_code %u, idle_cmd_done %u\n",
+ v->iop_base,
+ AdvReadWordRegister(iop_base, IOPW_SCSI_CFG1) & CABLE_DETECT,
+ v->err_code, v->idle_cmd_done);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" chip_version %u, lib_version %x, mcode_date %x, mcode_version %x\n",
+ c->chip_version, c->lib_version, c->mcode_date, c->mcode_version);
+ ASC_PRT_NEXT();
+
+ AdvReadWordLram(iop_base, ASC_MC_TAGQNG_ABLE, lramword);
+ len = asc_prt_line(cp, leftlen,
+" Queuing Enabled:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ len = asc_prt_line(cp, leftlen, " %X:%c",
+ i, (lramword & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Queue Limit:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ AdvReadByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + i, lrambyte);
+
+ len = asc_prt_line(cp, leftlen, " %X:%d", i, lrambyte);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Command Pending:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ AdvReadByteLram(iop_base, ASC_MC_NUMBER_OF_QUEUED_CMD + i, lrambyte);
+
+ len = asc_prt_line(cp, leftlen, " %X:%d", i, lrambyte);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ AdvReadWordLram(iop_base, ASC_MC_WDTR_ABLE, lramword);
+ len = asc_prt_line(cp, leftlen,
+" Wide Enabled:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ len = asc_prt_line(cp, leftlen, " %X:%c",
+ i, (lramword & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" Transfer Bit Width:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ AdvReadWordLram(iop_base, ASC_MC_DEVICE_HSHK_CFG_TABLE + (2 * i),
+ lramword);
+ len = asc_prt_line(cp, leftlen, " %X:%d",
+ i, (lramword & 0x8000) ? 16 : 8);
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ AdvReadWordLram(iop_base, ASC_MC_SDTR_ABLE, sdtr_able);
+ len = asc_prt_line(cp, leftlen,
+" Synchronous Enabled:");
+ ASC_PRT_NEXT();
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ len = asc_prt_line(cp, leftlen, " %X:%c",
+ i, (sdtr_able & ADV_TID_TO_TIDMASK(i)) ? 'Y' : 'N');
+ ASC_PRT_NEXT();
+ }
+ len = asc_prt_line(cp, leftlen, "\n");
+ ASC_PRT_NEXT();
+
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+
+ AdvReadWordLram(iop_base, ASC_MC_DEVICE_HSHK_CFG_TABLE + (2 * i),
+ lramword);
+ lramword &= ~0x8000;
+
+ if ((chip_scsi_id == i) ||
+ ((sdtr_able & ADV_TID_TO_TIDMASK(i)) == 0) ||
+ (lramword == 0)) {
+ continue;
+ }
+
+ len = asc_prt_line(cp, leftlen, " %X:", i);
+ ASC_PRT_NEXT();
+
+ period = (((lramword >> 8) * 25) + 50)/4;
+
+ len = asc_prt_line(cp, leftlen,
+ " Transfer Period Factor: %d (%d.%d Mhz),",
+ period, 250/period, ASC_TENTHS(250, period));
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, " REQ/ACK Offset: %d\n",
+ lramword & 0x1F);
+ ASC_PRT_NEXT();
+ }
+
+ return totlen;
+}
+
+/*
+ * asc_proc_copy()
+ *
+ * Copy proc information to a read buffer taking into account the current
+ * read offset in the file and the remaining space in the read buffer.
+ */
+STATIC int
+asc_proc_copy(off_t advoffset, off_t offset, char *curbuf, int leftlen,
+ char *cp, int cplen)
+{
+ int cnt = 0;
+
+ ASC_DBG3(2, "asc_proc_copy: offset %d, advoffset %d, cplen %d\n",
+ (unsigned) offset, (unsigned) advoffset, cplen);
+ if (offset <= advoffset) {
+ /* Read offset below current offset, copy everything. */
+ cnt = ASC_MIN(cplen, leftlen);
+ ASC_DBG3(2, "asc_proc_copy: curbuf %x, cp %x, cnt %d\n",
+ (unsigned) curbuf, (unsigned) cp, cnt);
+ memcpy(curbuf, cp, cnt);
+ } else if (offset < advoffset + cplen) {
+ /* Read offset within current range, partial copy. */
+ cnt = (advoffset + cplen) - offset;
+ cp = (cp + cplen) - cnt;
+ cnt = ASC_MIN(cnt, leftlen);
+ ASC_DBG3(2, "asc_proc_copy: curbuf %x, cp %x, cnt %d\n",
+ (unsigned) curbuf, (unsigned) cp, cnt);
+ memcpy(curbuf, cp, cnt);
+ }
+ return cnt;
+}
+
+/*
+ * asc_prt_line()
+ *
+ * If 'cp' is NULL print to the console, otherwise print to a buffer.
+ *
+ * Return 0 if printing to the console, otherwise return the number of
+ * bytes written to the buffer.
+ *
+ * Note: If any single line is greater than ASC_PRTLINE_SIZE bytes the stack
+ * will be corrupted. 's[]' is defined to be ASC_PRTLINE_SIZE bytes.
+ */
+STATIC int
+asc_prt_line(char *buf, int buflen, char *fmt, ...)
+{
+ va_list args;
+ int ret;
+ char s[ASC_PRTLINE_SIZE];
+
+ va_start(args, fmt);
+ ret = vsprintf(s, fmt, args);
+ ASC_ASSERT(ret < ASC_PRTLINE_SIZE);
+ if (buf == NULL) {
+ (void) printk(s);
+ ret = 0;
+ } else {
+ ret = ASC_MIN(buflen, ret);
+ memcpy(buf, s, ret);
+ }
+ va_end(args);
+ return ret;
+}
+#endif /* version >= v1.3.0 */
+
+
+/*
+ * --- Functions Required by the Asc Library
+ */
+
+/*
+ * Delay for 'n' milliseconds. Don't use the 'jiffies'
+ * global variable which is incremented once every 5 ms
+ * from a timer interrupt, because this function may be
+ * called when interrupts are disabled.
+ */
+STATIC void
+DvcSleepMilliSecond(ulong n)
+{
+ ulong i;
+
+ ASC_DBG1(4, "DvcSleepMilliSecond: %lu\n", n);
+ for (i = 0; i < n; i++) {
+ udelay(1000);
+ }
+}
+
+STATIC int
+DvcEnterCritical(void)
+{
+ int flags;
+
+ save_flags(flags);
+ cli();
+ return flags;
+}
+
+STATIC void
+DvcLeaveCritical(int flags)
+{
+ restore_flags(flags);
+}
+
+STATIC ulong
+DvcGetSGList(ASC_DVC_VAR *asc_dvc_sg, uchar *buf_addr, ulong buf_len,
+ ASC_SG_HEAD *asc_sg_head_ptr)
+{
+ ulong buf_size;
+
+ buf_size = buf_len;
+ asc_sg_head_ptr->entry_cnt = 1;
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0)
+ asc_sg_head_ptr->sg_list[0].addr = (ulong) buf_addr;
+#else /* version >= v2.0.0 */
+ asc_sg_head_ptr->sg_list[0].addr = virt_to_bus(buf_addr);
+#endif /* version >= v2.0.0 */
+ asc_sg_head_ptr->sg_list[0].bytes = buf_size;
+ return buf_size;
+}
+
+/*
+ * void
+ * DvcPutScsiQ(PortAddr iop_base, ushort s_addr, ushort *outbuf, int words)
+ *
+ * Calling/Exit State:
+ * none
+ *
+ * Description:
+ * Output an ASC_SCSI_Q structure to the chip
+ */
+STATIC void
+DvcPutScsiQ(PortAddr iop_base, ushort s_addr, ushort *outbuf, int words)
+{
+ int i;
+
+ ASC_DBG_PRT_HEX(2, "DvcPutScsiQ", (uchar *) outbuf, 2 * words);
+ AscSetChipLramAddr(iop_base, s_addr);
+ for (i = 0; i < words; i++, outbuf++) {
+ if (i == 2 || i == 10) {
+ continue;
+ }
+ AscSetChipLramDataNoSwap(iop_base, *outbuf);
+ }
+}
+
+/*
+ * void
+ * DvcGetQinfo(PortAddr iop_base, ushort s_addr, ushort *inbuf, int words)
+ *
+ * Calling/Exit State:
+ * none
+ *
+ * Description:
+ * Input an ASC_QDONE_INFO structure from the chip
+ */
+STATIC void
+DvcGetQinfo(PortAddr iop_base, ushort s_addr, ushort *inbuf, int words)
+{
+ int i;
+
+ AscSetChipLramAddr(iop_base, s_addr);
+ for (i = 0; i < words; i++, inbuf++) {
+ if (i == 5) {
+ continue;
+ }
+ *inbuf = AscGetChipLramDataNoSwap(iop_base);
+ }
+ ASC_DBG_PRT_HEX(2, "DvcGetQinfo", (uchar *) inbuf, 2 * words);
+}
+
+/*
+ * void DvcOutPortWords(ushort iop_base, ushort &outbuf, int words)
+ *
+ * Calling/Exit State:
+ * none
+ *
+ * Description:
+ * output a buffer to an i/o port address
+ */
+STATIC void
+DvcOutPortWords(ushort iop_base, ushort *outbuf, int words)
+{
+ int i;
+
+ for (i = 0; i < words; i++, outbuf++)
+ outpw(iop_base, *outbuf);
+}
+
+/*
+ * void DvcInPortWords(ushort iop_base, ushort &outbuf, int words)
+ *
+ * Calling/Exit State:
+ * none
+ *
+ * Description:
+ * input a buffer from an i/o port address
+ */
+STATIC void
+DvcInPortWords(ushort iop_base, ushort *inbuf, int words)
+{
+ int i;
+
+ for (i = 0; i < words; i++, inbuf++)
+ *inbuf = inpw(iop_base);
+}
+
+/*
+ * void DvcOutPortDWords(PortAddr port, ulong *pdw, int dwords)
+ *
+ * Calling/Exit State:
+ * none
+ *
+ * Description:
+ * output a buffer of 32-bit integers to an i/o port address in
+ * 16 bit integer units
+ */
+STATIC void
+DvcOutPortDWords(PortAddr port, ulong *pdw, int dwords)
+{
+ int i;
+ int words;
+ ushort *pw;
+
+ pw = (ushort *) pdw;
+ words = dwords << 1;
+ for(i = 0; i < words; i++, pw++) {
+ outpw(port, *pw);
+ }
+ return;
+}
+
+/*
+ * Read a PCI configuration byte.
+ */
+ASC_INITFUNC(
+STATIC uchar
+DvcReadPCIConfigByte(
+ ASC_DVC_VAR asc_ptr_type *asc_dvc,
+ ushort offset)
+)
+{
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+ PCI_DATA pciData;
+
+ pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info);
+ pciData.slot = ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info);
+ pciData.func = ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info);
+ pciData.offset = offset;
+ pciData.type = pci_scan_method;
+ return asc_get_cfg_byte(&pciData);
+#else /* ASC_CONFIG_PCI */
+ return 0;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */
+#ifdef CONFIG_PCI
+ uchar byte_data;
+ pcibios_read_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info),
+ PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info),
+ ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)),
+ offset, &byte_data);
+ return byte_data;
+#else /* CONFIG_PCI */
+ return 0;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */
+}
+
+/*
+ * Write a PCI configuration byte.
+ */
+ASC_INITFUNC(
+STATIC void
+DvcWritePCIConfigByte(
+ ASC_DVC_VAR asc_ptr_type *asc_dvc,
+ ushort offset,
+ uchar byte_data)
+)
+{
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+ PCI_DATA pciData;
+
+ pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info);
+ pciData.slot = ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info);
+ pciData.func = ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info);
+ pciData.offset = offset;
+ pciData.type = pci_scan_method;
+ asc_put_cfg_byte(&pciData, byte_data);
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */
+#ifdef CONFIG_PCI
+ pcibios_write_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info),
+ PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info),
+ ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)),
+ offset, byte_data);
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */
+}
+
+/*
+ * Return the BIOS address of the adapter at the specified
+ * I/O port and with the specified bus type.
+ */
+ASC_INITFUNC(
+STATIC ushort
+AscGetChipBiosAddress(
+ PortAddr iop_base,
+ ushort bus_type
+)
+)
+{
+ ushort cfg_lsw ;
+ ushort bios_addr ;
+
+ /*
+ * The PCI BIOS is re-located by the motherboard BIOS. Because
+ * of this the driver can not determine where a PCI BIOS is
+ * loaded and executes.
+ */
+ if (bus_type & ASC_IS_PCI)
+ {
+ return(0);
+ }
+
+ if((bus_type & ASC_IS_EISA) != 0)
+ {
+ cfg_lsw = AscGetEisaChipCfg(iop_base) ;
+ cfg_lsw &= 0x000F ;
+ bios_addr = (ushort)(ASC_BIOS_MIN_ADDR +
+ (cfg_lsw * ASC_BIOS_BANK_SIZE)) ;
+ return(bios_addr) ;
+ }/* if */
+
+ cfg_lsw = AscGetChipCfgLsw(iop_base) ;
+
+ /*
+ * ISA PnP uses the top bit as the 32K BIOS flag
+ */
+ if (bus_type == ASC_IS_ISAPNP)
+ {
+ cfg_lsw &= 0x7FFF;
+ }/* if */
+
+ bios_addr = (ushort)(((cfg_lsw >> 12) * ASC_BIOS_BANK_SIZE) +
+ ASC_BIOS_MIN_ADDR) ;
+ return(bios_addr) ;
+}
+
+
+/*
+ * --- Functions Required by the Adv Library
+ */
+
+/*
+ * DvcGetPhyAddr()
+ *
+ * Return the physical address of 'vaddr' and set '*lenp' to the
+ * number of physically contiguous bytes that follow 'vaddr'.
+ * 'flag' indicates the type of structure whose physical address
+ * is being translated.
+ *
+ * Note: Because Linux currently doesn't page the kernel and all
+ * kernel buffers are physically contiguous, leave '*lenp' unchanged.
+ */
+ulong
+DvcGetPhyAddr(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq,
+ uchar *vaddr, long *lenp, int flag)
+{
+ ulong paddr;
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0)
+ paddr = (ulong) vaddr;
+#else /* version >= v2.0.0 */
+ paddr = virt_to_bus(vaddr);
+#endif /* version >= v2.0.0 */
+
+ ASC_DBG4(4,
+ "DvcGetPhyAddr: vaddr 0x%lx, lenp 0x%lx *lenp %lu, paddr 0x%lx\n",
+ (ulong) vaddr, (ulong) lenp, (ulong) *((ulong *) lenp), paddr);
+
+ return paddr;
+}
+
+/*
+ * Read a PCI configuration byte.
+ */
+ASC_INITFUNC(
+STATIC uchar
+DvcAdvReadPCIConfigByte(
+ ADV_DVC_VAR *asc_dvc,
+ ushort offset)
+)
+{
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+ PCI_DATA pciData;
+
+ pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info);
+ pciData.slot = ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info);
+ pciData.func = ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info);
+ pciData.offset = offset;
+ pciData.type = pci_scan_method;
+ return asc_get_cfg_byte(&pciData);
+#else /* ASC_CONFIG_PCI */
+ return 0;
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */
+#ifdef CONFIG_PCI
+ uchar byte_data;
+ pcibios_read_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info),
+ PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info),
+ ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)),
+ offset, &byte_data);
+ return byte_data;
+#else /* CONFIG_PCI */
+ return 0;
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */
+}
+
+/*
+ * Write a PCI configuration byte.
+ */
+ASC_INITFUNC(
+STATIC void
+DvcAdvWritePCIConfigByte(
+ ADV_DVC_VAR *asc_dvc,
+ ushort offset,
+ uchar byte_data)
+)
+{
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,93)
+#ifdef ASC_CONFIG_PCI
+ PCI_DATA pciData;
+
+ pciData.bus = ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info);
+ pciData.slot = ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info);
+ pciData.func = ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info);
+ pciData.offset = offset;
+ pciData.type = pci_scan_method;
+ asc_put_cfg_byte(&pciData, byte_data);
+#endif /* ASC_CONFIG_PCI */
+#else /* version >= v2.1.93 */
+#ifdef CONFIG_PCI
+ pcibios_write_config_byte(ASC_PCI_ID2BUS(asc_dvc->cfg->pci_slot_info),
+ PCI_DEVFN(ASC_PCI_ID2DEV(asc_dvc->cfg->pci_slot_info),
+ ASC_PCI_ID2FUNC(asc_dvc->cfg->pci_slot_info)),
+ offset, byte_data);
+#endif /* CONFIG_PCI */
+#endif /* version >= v2.1.93 */
+}
+
+/*
+ * --- Tracing and Debugging Functions
+ */
+
+#ifdef ADVANSYS_STATS
+/*
+ * asc_prt_board_stats()
+ *
+ * Note: no single line should be greater than ASC_PRTLINE_SIZE,
+ * cf. asc_prt_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_board_stats(struct Scsi_Host *shp, char *cp, int cplen)
+{
+ int leftlen;
+ int totlen;
+ int len;
+ struct asc_stats *s;
+ int i;
+ ushort chip_scsi_id;
+ asc_board_t *boardp;
+ asc_queue_t *active;
+ asc_queue_t *waiting;
+
+ leftlen = cplen;
+ totlen = len = 0;
+
+ boardp = ASC_BOARDP(shp);
+ s = &boardp->asc_stats;
+
+ len = asc_prt_line(cp, leftlen,
+"\nLinux Driver Statistics for AdvanSys SCSI Host %d:\n", shp->host_no);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" command %lu, queuecommand %lu, abort %lu, reset %lu, biosparam %lu\n",
+ s->command, s->queuecommand, s->abort, s->reset, s->biosparam);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" interrupt %lu, callback %lu, done %lu\n",
+ s->interrupt, s->callback, s->done);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" exe_noerror %lu, exe_busy %lu, exe_error %lu, exe_unknown %lu\n",
+ s->exe_noerror, s->exe_busy, s->exe_error, s->exe_unknown);
+ ASC_PRT_NEXT();
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ len = asc_prt_line(cp, leftlen,
+" build_error %lu\n",
+ s->build_error);
+ } else {
+ len = asc_prt_line(cp, leftlen,
+" build_error %lu, build_noreq %lu, build_nosg %lu\n",
+ s->build_error, s->adv_build_noreq, s->adv_build_nosg);
+ }
+ ASC_PRT_NEXT();
+
+ /*
+ * Display data transfer statistics.
+ */
+ if (s->cont_cnt > 0) {
+ len = asc_prt_line(cp, leftlen, " cont_cnt %lu, ", s->cont_cnt);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, "cont_xfer %lu.%01lu kb ",
+ s->cont_xfer/2,
+ ASC_TENTHS(s->cont_xfer, 2));
+ ASC_PRT_NEXT();
+
+ /* Contiguous transfer average size */
+ len = asc_prt_line(cp, leftlen, "avg_xfer %lu.%01lu kb\n",
+ (s->cont_xfer/2)/s->cont_cnt,
+ ASC_TENTHS((s->cont_xfer/2), s->cont_cnt));
+ ASC_PRT_NEXT();
+ }
+
+ if (s->sg_cnt > 0) {
+
+ len = asc_prt_line(cp, leftlen, " sg_cnt %lu, sg_elem %lu, ",
+ s->sg_cnt, s->sg_elem);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, "sg_xfer %lu.%01lu kb\n",
+ s->sg_xfer/2,
+ ASC_TENTHS(s->sg_xfer, 2));
+ ASC_PRT_NEXT();
+
+ /* Scatter gather transfer statistics */
+ len = asc_prt_line(cp, leftlen, " avg_num_elem %lu.%01lu, ",
+ s->sg_elem/s->sg_cnt,
+ ASC_TENTHS(s->sg_elem, s->sg_cnt));
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, "avg_elem_size %lu.%01lu kb, ",
+ (s->sg_xfer/2)/s->sg_elem,
+ ASC_TENTHS((s->sg_xfer/2), s->sg_elem));
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen, "avg_xfer_size %lu.%01lu kb\n",
+ (s->sg_xfer/2)/s->sg_cnt,
+ ASC_TENTHS((s->sg_xfer/2), s->sg_cnt));
+ ASC_PRT_NEXT();
+ }
+
+ /*
+ * Display request queuing statistics.
+ */
+ len = asc_prt_line(cp, leftlen,
+" Active and Waiting Request Queues (Time Unit: %d HZ):\n", HZ);
+ ASC_PRT_NEXT();
+
+ active = &ASC_BOARDP(shp)->active;
+ waiting = &ASC_BOARDP(shp)->waiting;
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ chip_scsi_id = boardp->dvc_cfg.asc_dvc_cfg.chip_scsi_id;
+ } else {
+ chip_scsi_id = boardp->dvc_var.adv_dvc_var.chip_scsi_id;
+ }
+
+ for (i = 0; i <= ADV_MAX_TID; i++) {
+
+ if ((chip_scsi_id == i) ||
+ ((boardp->init_tidmask & ADV_TID_TO_TIDMASK(i)) == 0)) {
+ continue;
+ }
+
+ if (active->q_tot_cnt[i] > 0 || waiting->q_tot_cnt[i] > 0) {
+ len = asc_prt_line(cp, leftlen, " target %d\n", i);
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" active: cnt [cur %d, max %d, tot %u], time [min %d, max %d, avg %lu.%01lu]\n",
+ active->q_cur_cnt[i], active->q_max_cnt[i],
+ active->q_tot_cnt[i],
+ active->q_min_tim[i], active->q_max_tim[i],
+ (active->q_tot_cnt[i] == 0) ? 0 :
+ (active->q_tot_tim[i]/active->q_tot_cnt[i]),
+ (active->q_tot_cnt[i] == 0) ? 0 :
+ ASC_TENTHS(active->q_tot_tim[i], active->q_tot_cnt[i]));
+ ASC_PRT_NEXT();
+
+ len = asc_prt_line(cp, leftlen,
+" waiting: cnt [cur %d, max %d, tot %u], time [min %u, max %u, avg %lu.%01lu]\n",
+ waiting->q_cur_cnt[i], waiting->q_max_cnt[i],
+ waiting->q_tot_cnt[i],
+ waiting->q_min_tim[i], waiting->q_max_tim[i],
+ (waiting->q_tot_cnt[i] == 0) ? 0 :
+ (waiting->q_tot_tim[i]/waiting->q_tot_cnt[i]),
+ (waiting->q_tot_cnt[i] == 0) ? 0 :
+ ASC_TENTHS(waiting->q_tot_tim[i], waiting->q_tot_cnt[i]));
+ ASC_PRT_NEXT();
+ }
+ }
+
+ return totlen;
+}
+#endif /* ADVANSYS_STATS */
+
+#ifdef ADVANSYS_DEBUG
+/*
+ * asc_prt_scsi_host()
+ */
+STATIC void
+asc_prt_scsi_host(struct Scsi_Host *s)
+{
+ asc_board_t *boardp;
+
+ boardp = ASC_BOARDP(s);
+
+ printk("Scsi_Host at addr %x\n", (unsigned) s);
+ printk(
+" next %x, extra_bytes %u, host_busy %u, host_no %d, last_reset %d,\n",
+ (unsigned) s->next, s->extra_bytes, s->host_busy, s->host_no,
+ (unsigned) s->last_reset);
+
+ printk(
+" host_wait %x, host_queue %x, hostt %x, block %x,\n",
+ (unsigned) s->host_wait, (unsigned) s->host_queue,
+ (unsigned) s->hostt, (unsigned) s->block);
+
+ printk(
+" wish_block %d, base %x, io_port %d, n_io_port %d, irq %d, dma_channel %d,\n",
+ s->wish_block, (unsigned) s->base, s->io_port, s->n_io_port,
+ s->irq, s->dma_channel);
+
+ printk(
+" this_id %d, can_queue %d,\n", s->this_id, s->can_queue);
+
+ printk(
+" cmd_per_lun %d, sg_tablesize %d, unchecked_isa_dma %d, loaded_as_module %d\n",
+ s->cmd_per_lun, s->sg_tablesize, s->unchecked_isa_dma,
+ s->loaded_as_module);
+
+ if (ASC_NARROW_BOARD(boardp)) {
+ asc_prt_asc_dvc_var(&ASC_BOARDP(s)->dvc_var.asc_dvc_var);
+ asc_prt_asc_dvc_cfg(&ASC_BOARDP(s)->dvc_cfg.asc_dvc_cfg);
+ } else {
+ asc_prt_adv_dvc_var(&ASC_BOARDP(s)->dvc_var.adv_dvc_var);
+ asc_prt_adv_dvc_cfg(&ASC_BOARDP(s)->dvc_cfg.adv_dvc_cfg);
+ }
+}
+
+/*
+ * asc_prt_scsi_cmnd()
+ */
+STATIC void
+asc_prt_scsi_cmnd(Scsi_Cmnd *s)
+{
+ printk("Scsi_Cmnd at addr %x\n", (unsigned) s);
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0)
+ printk(
+" host %x, device %x, target %u, lun %u\n",
+ (unsigned) s->host, (unsigned) s->device, s->target, s->lun);
+#else /* version >= v1.3.0 */
+ printk(
+" host %x, device %x, target %u, lun %u, channel %u,\n",
+ (unsigned) s->host, (unsigned) s->device, s->target, s->lun,
+ s->channel);
+#endif /* version >= v1.3.0 */
+
+ asc_prt_hex(" CDB", s->cmnd, s->cmd_len);
+
+ printk(
+" use_sg %u, sglist_len %u, abort_reason %x\n",
+ s->use_sg, s->sglist_len, s->abort_reason);
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,89)
+ printk(
+" retries %d, allowed %d\n",
+ s->retries, s->allowed);
+#else /* version >= v1.3.89 */
+ printk(
+" serial_number %x, serial_number_at_timeout %x, retries %d, allowed %d\n",
+ (unsigned) s->serial_number, (unsigned) s->serial_number_at_timeout,
+ s->retries, s->allowed);
+#endif /* version >= v1.3.89 */
+
+ printk(
+" timeout_per_command %d, timeout_total %d, timeout %d\n",
+ s->timeout_per_command, s->timeout_total, s->timeout);
+
+ printk(
+" internal_timeout %u, flags %u, this_count %d\n",
+ s->internal_timeout, s->flags, s->this_count);
+
+ printk(
+" scsi_done %x, done %x, host_scribble %x, result %x\n",
+ (unsigned) s->scsi_done, (unsigned) s->done,
+ (unsigned) s->host_scribble, s->result);
+
+ printk(
+" tag %u, pid %u\n",
+ (unsigned) s->tag, (unsigned) s->pid);
+}
+
+/*
+ * asc_prt_asc_dvc_var()
+ */
+STATIC void
+asc_prt_asc_dvc_var(ASC_DVC_VAR *h)
+{
+ printk("ASC_DVC_VAR at addr %x\n", (unsigned) h);
+
+ printk(
+" iop_base %x, err_code %x, dvc_cntl %x, bug_fix_cntl %d,\n",
+ h->iop_base, h->err_code, h->dvc_cntl, h->bug_fix_cntl);
+
+ printk(
+" bus_type %d, isr_callback %x, exe_callback %x, init_sdtr %x,\n",
+ h->bus_type, (unsigned) h->isr_callback, (unsigned) h->exe_callback,
+ (unsigned) h->init_sdtr);
+
+ printk(
+" sdtr_done %x, use_tagged_qng %x, unit_not_ready %x, chip_no %x,\n",
+ (unsigned) h->sdtr_done, (unsigned) h->use_tagged_qng,
+ (unsigned) h->unit_not_ready, (unsigned) h->chip_no);
+
+ printk(
+" queue_full_or_busy %x, start_motor %x, scsi_reset_wait %x, irq_no %x,\n",
+ (unsigned) h->queue_full_or_busy, (unsigned) h->start_motor,
+ (unsigned) h->scsi_reset_wait, (unsigned) h->irq_no);
+
+ printk(
+" is_in_int %x, max_total_qng %x, cur_total_qng %x, in_critical_cnt %x,\n",
+ (unsigned) h->is_in_int, (unsigned) h->max_total_qng,
+ (unsigned) h->cur_total_qng, (unsigned) h->in_critical_cnt);
+
+ printk(
+" last_q_shortage %x, init_state %x, no_scam %x, pci_fix_asyn_xfer %x,\n",
+ (unsigned) h->last_q_shortage, (unsigned) h->init_state,
+ (unsigned) h->no_scam, (unsigned) h->pci_fix_asyn_xfer);
+
+ printk(
+" cfg %x, saved_ptr2func %x\n",
+ (unsigned) h->cfg, (unsigned) h->saved_ptr2func);
+}
+
+/*
+ * asc_prt_asc_dvc_cfg()
+ */
+STATIC void
+asc_prt_asc_dvc_cfg(ASC_DVC_CFG *h)
+{
+ printk("ASC_DVC_CFG at addr %x\n", (unsigned) h);
+
+ printk(
+" can_tagged_qng %x, cmd_qng_enabled %x, disc_enable %x, sdtr_enable %x,\n",
+ h->can_tagged_qng, h->cmd_qng_enabled, h->disc_enable,
+ h->sdtr_enable);
+
+ printk(
+" chip_scsi_id %d, isa_dma_speed %d, isa_dma_channel %d, chip_version %d,\n",
+ h->chip_scsi_id, h->isa_dma_speed, h->isa_dma_channel,
+ h->chip_version);
+
+ printk(
+" pci_device_id %d, lib_serial_no %x, lib_version %x, mcode_date %x,\n",
+ h->pci_device_id, h->lib_serial_no, h->lib_version, h->mcode_date);
+
+ printk(
+" mcode_version %d, overrun_buf %x\n",
+ h->mcode_version, (unsigned) h->overrun_buf);
+}
+
+/*
+ * asc_prt_asc_scsi_q()
+ */
+STATIC void
+asc_prt_asc_scsi_q(ASC_SCSI_Q *q)
+{
+ ASC_SG_HEAD *sgp;
+ int i;
+
+ printk("ASC_SCSI_Q at addr %x\n", (unsigned) q);
+
+ printk(
+" target_ix %u, target_lun %u, srb_ptr %x, tag_code %u,\n",
+ q->q2.target_ix, q->q1.target_lun,
+ (unsigned) q->q2.srb_ptr, q->q2.tag_code);
+
+ printk(
+" data_addr %x, data_cnt %lu, sense_addr %x, sense_len %u,\n",
+ (unsigned) q->q1.data_addr, q->q1.data_cnt,
+ (unsigned) q->q1.sense_addr, q->q1.sense_len);
+
+ printk(
+" cdbptr %x, cdb_len %u, sg_head %x, sg_queue_cnt %u\n",
+ (unsigned) q->cdbptr, q->q2.cdb_len,
+ (unsigned) q->sg_head, q->q1.sg_queue_cnt);
+
+ if (q->sg_head) {
+ sgp = q->sg_head;
+ printk("ASC_SG_HEAD at addr %x\n", (unsigned) sgp);
+ printk(" entry_cnt %u, queue_cnt %u\n", sgp->entry_cnt, sgp->queue_cnt);
+ for (i = 0; i < sgp->entry_cnt; i++) {
+ printk(" [%u]: addr %x, bytes %lu\n",
+ i, (unsigned) sgp->sg_list[i].addr, sgp->sg_list[i].bytes);
+ }
+
+ }
+}
+
+/*
+ * asc_prt_asc_qdone_info()
+ */
+STATIC void
+asc_prt_asc_qdone_info(ASC_QDONE_INFO *q)
+{
+ printk("ASC_QDONE_INFO at addr %x\n", (unsigned) q);
+ printk(
+" srb_ptr %x, target_ix %u, cdb_len %u, tag_code %u, done_stat %x\n",
+ (unsigned) q->d2.srb_ptr, q->d2.target_ix, q->d2.cdb_len,
+ q->d2.tag_code, q->d3.done_stat);
+ printk(
+" host_stat %x, scsi_stat %x, scsi_msg %x\n",
+ q->d3.host_stat, q->d3.scsi_stat, q->d3.scsi_msg);
+}
+
+/*
+ * asc_prt_adv_dvc_var()
+ *
+ * Display an ADV_DVC_VAR structure.
+ */
+STATIC void
+asc_prt_adv_dvc_var(ADV_DVC_VAR *h)
+{
+ printk(" ADV_DVC_VAR at addr 0x%lx\n", (ulong) h);
+
+ printk(
+" iop_base 0x%lx, err_code 0x%x, ultra_able 0x%x\n",
+ (ulong) h->iop_base, h->err_code, (unsigned) h->ultra_able);
+
+ printk(
+" isr_callback 0x%x, sdtr_able 0x%x, wdtr_able 0x%x\n",
+ (unsigned) h->isr_callback, (unsigned) h->wdtr_able,
+ (unsigned) h->sdtr_able);
+
+ printk(
+" start_motor 0x%x, scsi_reset_wait 0x%x, irq_no 0x%x,\n",
+ (unsigned) h->start_motor,
+ (unsigned) h->scsi_reset_wait, (unsigned) h->irq_no);
+
+ printk(
+" max_host_qng 0x%x, cur_host_qng 0x%x, max_dvc_qng 0x%x\n",
+ (unsigned) h->max_host_qng, (unsigned) h->cur_host_qng,
+ (unsigned) h->max_dvc_qng);
+
+ printk(
+" no_scam 0x%x, tagqng_able 0x%x, chip_scsi_id 0x%x, cfg 0x%lx\n",
+ (unsigned) h->no_scam, (unsigned) h->tagqng_able,
+ (unsigned) h->chip_scsi_id, (ulong) h->cfg);
+
+}
+
+/*
+ * asc_prt_adv_dvc_cfg()
+ *
+ * Display an ADV_DVC_CFG structure.
+ */
+STATIC void
+asc_prt_adv_dvc_cfg(ADV_DVC_CFG *h)
+{
+ printk(" ADV_DVC_CFG at addr 0x%lx\n", (ulong) h);
+
+ printk(
+" disc_enable 0x%x, termination 0x%x\n",
+ h->disc_enable, h->termination);
+
+ printk(
+" chip_version 0x%x, mcode_date 0x%x\n",
+ h->chip_version, h->mcode_date);
+
+ printk(
+" mcode_version 0x%x, pci_device_id 0x%x, lib_version 0x%x\n",
+ h->mcode_version, h->pci_device_id, h->lib_version);
+
+ printk(
+" control_flag 0x%x, pci_slot_info 0x%x\n",
+ h->control_flag, h->pci_slot_info);
+}
+
+/*
+ * asc_prt_adv_scsi_req_q()
+ *
+ * Display an ADV_SCSI_REQ_Q structure.
+ */
+STATIC void
+asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *q)
+{
+ int i;
+ struct asc_sg_block *sg_ptr;
+
+ printk("ADV_SCSI_REQ_Q at addr %x\n", (unsigned) q);
+
+ printk(
+" target_id %u, target_lun %u, srb_ptr 0x%lx, a_flag 0x%x\n",
+ q->target_id, q->target_lun, q->srb_ptr, q->a_flag);
+
+ printk(" cntl 0x%x, data_addr 0x%lx, vdata_addr 0x%lx\n",
+ q->cntl, q->data_addr, q->vdata_addr);
+
+ printk(
+" data_cnt %lu, sense_addr 0x%lx, sense_len %u,\n",
+ q->data_cnt, q->sense_addr, q->sense_len);
+
+ printk(
+" cdb_len %u, done_status 0x%x, host_status 0x%x, scsi_status 0x%x\n",
+ q->cdb_len, q->done_status, q->host_status, q->scsi_status);
+
+ printk(
+" vsense_addr 0x%lx, scsiq_ptr 0x%lx, ux_wk_data_cnt %lu\n",
+ (ulong) q->vsense_addr, (ulong) q->scsiq_ptr,
+ (ulong) q->ux_wk_data_cnt);
+
+ printk(
+" sg_list_ptr 0x%lx, sg_real_addr 0x%lx, sg_entry_cnt %u\n",
+ (ulong) q->sg_list_ptr, (ulong) q->sg_real_addr, q->sg_entry_cnt);
+
+ printk(
+" ux_sg_ix %u, orig_sense_len %u\n",
+ q->ux_sg_ix, q->orig_sense_len);
+
+ /* Display the request's ADV_SG_BLOCK structures. */
+ for (sg_ptr = q->sg_list_ptr, i = 0; sg_ptr != NULL;
+ sg_ptr = sg_ptr->sg_ptr, i++) {
+ /*
+ * 'sg_ptr' is a physical address. Convert it to a virtual
+ * address by indexing 'i' into the virtual address array
+ * 'sg_list_ptr'.
+ *
+ * At the end of the each iteration of the loop 'sg_ptr' is
+ * converted back into a physical address by setting 'sg_ptr'
+ * to the next pointer 'sg_ptr->sg_ptr'.
+ */
+ sg_ptr = &(((ADV_SG_BLOCK *) (q->sg_list_ptr))[i]);
+ asc_prt_adv_sgblock(i, sg_ptr);
+ }
+}
+
+/*
+ * asc_prt_adv_sgblock()
+ *
+ * Display an ADV_SG_BLOCK structure.
+ */
+STATIC void
+asc_prt_adv_sgblock(int sgblockno, ADV_SG_BLOCK *b)
+{
+ int i, s;
+
+ /* Calculate starting entry number for the current block. */
+ s = sgblockno * NO_OF_SG_PER_BLOCK;
+
+ printk(" ADV_SG_BLOCK at addr 0x%lx (sgblockno %lu)\n",
+ (ulong) b, (ulong) sgblockno);
+ printk(
+" first_entry_no %lu, last_entry_no %lu, sg_ptr 0x%lx\n",
+ (ulong) b->first_entry_no, (ulong) b->last_entry_no, (ulong) b->sg_ptr);
+ ASC_ASSERT(b->first_entry_no - s >= 0);
+ ASC_ASSERT(b->last_entry_no - s >= 0);
+ ASC_ASSERT(b->last_entry_no - s <= NO_OF_SG_PER_BLOCK);
+ ASC_ASSERT(b->first_entry_no - s <= NO_OF_SG_PER_BLOCK);
+ ASC_ASSERT(b->first_entry_no - s <= NO_OF_SG_PER_BLOCK);
+ ASC_ASSERT(b->first_entry_no - s <= b->last_entry_no - s);
+ for (i = b->first_entry_no - s; i <= b->last_entry_no - s; i++) {
+ printk(" [%lu]: sg_addr 0x%lx, sg_count 0x%lx\n",
+ (ulong) i, (ulong) b->sg_list[i].sg_addr,
+ (ulong) b->sg_list[i].sg_count);
+ }
+}
+
+/*
+ * asc_prt_hex()
+ *
+ * Print hexadecimal output in 4 byte groupings 32 bytes
+ * or 8 double-words per line.
+ */
+STATIC void
+asc_prt_hex(char *f, uchar *s, int l)
+{
+ int i;
+ int j;
+ int k;
+ int m;
+
+ printk("%s: (%d bytes)\n", f, l);
+
+ for (i = 0; i < l; i += 32) {
+
+ /* Display a maximum of 8 double-words per line. */
+ if ((k = (l - i) / 4) >= 8) {
+ k = 8;
+ m = 0;
+ } else {
+ m = (l - i) % 4 ;
+ }
+
+ for (j = 0; j < k; j++) {
+ printk(" %2.2X%2.2X%2.2X%2.2X",
+ (unsigned) s[i+(j*4)], (unsigned) s[i+(j*4)+1],
+ (unsigned) s[i+(j*4)+2], (unsigned) s[i+(j*4)+3]);
+ }
+
+ switch (m) {
+ case 0:
+ default:
+ break;
+ case 1:
+ printk(" %2.2X",
+ (unsigned) s[i+(j*4)]);
+ break;
+ case 2:
+ printk(" %2.2X%2.2X",
+ (unsigned) s[i+(j*4)],
+ (unsigned) s[i+(j*4)+1]);
+ break;
+ case 3:
+ printk(" %2.2X%2.2X%2.2X",
+ (unsigned) s[i+(j*4)+1],
+ (unsigned) s[i+(j*4)+2],
+ (unsigned) s[i+(j*4)+3]);
+ break;
+ }
+
+ printk("\n");
+ }
+}
+#endif /* ADVANSYS_DEBUG */
+
+#ifdef ADVANSYS_ASSERT
+/*
+ * interrupts_enabled()
+ *
+ * Return 1 if interrupts are enabled, otherwise return 0.
+ */
+STATIC int
+interrupts_enabled(void)
+{
+ int flags;
+
+ save_flags(flags);
+ if (flags & 0x0200) {
+ return ASC_TRUE;
+ } else {
+ return ASC_FALSE;
+ }
+}
+#endif /* ADVANSYS_ASSERT */
+
+
+/*
+ * --- Asc Library Functions
+ */
+
+ASC_INITFUNC(
+STATIC ushort
+AscGetEisaChipCfg(
+ PortAddr iop_base
+)
+)
+{
+ PortAddr eisa_cfg_iop;
+
+ eisa_cfg_iop = (PortAddr) ASC_GET_EISA_SLOT(iop_base) |
+ (PortAddr) (ASC_EISA_CFG_IOP_MASK);
+ return (inpw(eisa_cfg_iop));
+}
+
+ASC_INITFUNC(
+STATIC uchar
+AscSetChipScsiID(
+ PortAddr iop_base,
+ uchar new_host_id
+)
+)
+{
+ ushort cfg_lsw;
+
+ if (AscGetChipScsiID(iop_base) == new_host_id) {
+ return (new_host_id);
+ }
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+ cfg_lsw &= 0xF8FF;
+ cfg_lsw |= (ushort) ((new_host_id & ASC_MAX_TID) << 8);
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ return (AscGetChipScsiID(iop_base));
+}
+
+ASC_INITFUNC(
+STATIC uchar
+AscGetChipScsiCtrl(
+ PortAddr iop_base
+)
+)
+{
+ uchar sc;
+
+ AscSetBank(iop_base, 1);
+ sc = inp(iop_base + IOP_REG_SC);
+ AscSetBank(iop_base, 0);
+ return (sc);
+}
+
+ASC_INITFUNC(
+STATIC uchar
+AscGetChipVersion(
+ PortAddr iop_base,
+ ushort bus_type
+)
+)
+{
+ if ((bus_type & ASC_IS_EISA) != 0) {
+ PortAddr eisa_iop;
+ uchar revision;
+ eisa_iop = (PortAddr) ASC_GET_EISA_SLOT(iop_base) |
+ (PortAddr) ASC_EISA_REV_IOP_MASK;
+ revision = inp(eisa_iop);
+ return ((uchar) ((ASC_CHIP_MIN_VER_EISA - 1) + revision));
+ }
+ return (AscGetChipVerNo(iop_base));
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscGetChipBusType(
+ PortAddr iop_base
+)
+)
+{
+ ushort chip_ver;
+
+ chip_ver = AscGetChipVerNo(iop_base);
+ if (
+ (chip_ver >= ASC_CHIP_MIN_VER_VL)
+ && (chip_ver <= ASC_CHIP_MAX_VER_VL)
+) {
+ if (
+ ((iop_base & 0x0C30) == 0x0C30)
+ || ((iop_base & 0x0C50) == 0x0C50)
+) {
+ return (ASC_IS_EISA);
+ }
+ return (ASC_IS_VL);
+ }
+ if ((chip_ver >= ASC_CHIP_MIN_VER_ISA) &&
+ (chip_ver <= ASC_CHIP_MAX_VER_ISA)) {
+ if (chip_ver >= ASC_CHIP_MIN_VER_ISA_PNP) {
+ return (ASC_IS_ISAPNP);
+ }
+ return (ASC_IS_ISA);
+ } else if ((chip_ver >= ASC_CHIP_MIN_VER_PCI) &&
+ (chip_ver <= ASC_CHIP_MAX_VER_PCI)) {
+ return (ASC_IS_PCI);
+ }
+ return (0);
+}
+
+ASC_INITFUNC(
+STATIC ulong
+AscLoadMicroCode(
+ PortAddr iop_base,
+ ushort s_addr,
+ ushort *mcode_buf,
+ ushort mcode_size
+)
+)
+{
+ ulong chksum;
+ ushort mcode_word_size;
+ ushort mcode_chksum;
+
+ mcode_word_size = (ushort) (mcode_size >> 1);
+ AscMemWordSetLram(iop_base, s_addr, 0, mcode_word_size);
+ AscMemWordCopyToLram(iop_base, s_addr, mcode_buf, mcode_word_size);
+ chksum = AscMemSumLramWord(iop_base, s_addr, mcode_word_size);
+ mcode_chksum = (ushort) AscMemSumLramWord(iop_base,
+ (ushort) ASC_CODE_SEC_BEG,
+ (ushort) ((mcode_size - s_addr - (ushort) ASC_CODE_SEC_BEG) / 2));
+ AscWriteLramWord(iop_base, ASCV_MCODE_CHKSUM_W, mcode_chksum);
+ AscWriteLramWord(iop_base, ASCV_MCODE_SIZE_W, mcode_size);
+ return (chksum);
+}
+
+ASC_INITFUNC(
+STATIC int
+AscFindSignature(
+ PortAddr iop_base
+)
+)
+{
+ ushort sig_word;
+
+ if (AscGetChipSignatureByte(iop_base) == (uchar) ASC_1000_ID1B) {
+ sig_word = AscGetChipSignatureWord(iop_base);
+ if ((sig_word == (ushort) ASC_1000_ID0W) ||
+ (sig_word == (ushort) ASC_1000_ID0W_FIX)) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+STATIC uchar _isa_pnp_inited ASC_INITDATA = 0;
+STATIC PortAddr _asc_def_iop_base[ASC_IOADR_TABLE_MAX_IX] ASC_INITDATA =
+{
+ 0x100, ASC_IOADR_1, 0x120, ASC_IOADR_2, 0x140, ASC_IOADR_3, ASC_IOADR_4,
+ ASC_IOADR_5, ASC_IOADR_6, ASC_IOADR_7, ASC_IOADR_8
+};
+
+ASC_INITFUNC(
+STATIC PortAddr
+AscSearchIOPortAddr(
+ PortAddr iop_beg,
+ ushort bus_type
+)
+)
+{
+ if (bus_type & ASC_IS_VL) {
+ while ((iop_beg = AscSearchIOPortAddr11(iop_beg)) != 0) {
+ if (AscGetChipVersion(iop_beg, bus_type) <= ASC_CHIP_MAX_VER_VL) {
+ return (iop_beg);
+ }
+ }
+ return (0);
+ }
+ if (bus_type & ASC_IS_ISA) {
+ if (_isa_pnp_inited == 0) {
+ AscSetISAPNPWaitForKey();
+ _isa_pnp_inited++;
+ }
+ while ((iop_beg = AscSearchIOPortAddr11(iop_beg)) != 0) {
+ if ((AscGetChipVersion(iop_beg, bus_type) & ASC_CHIP_VER_ISA_BIT) != 0) {
+ return (iop_beg);
+ }
+ }
+ return (0);
+ }
+ if (bus_type & ASC_IS_EISA) {
+ if ((iop_beg = AscSearchIOPortAddrEISA(iop_beg)) != 0) {
+ return (iop_beg);
+ }
+ return (0);
+ }
+ return (0);
+}
+
+ASC_INITFUNC(
+STATIC PortAddr
+AscSearchIOPortAddr11(
+ PortAddr s_addr
+)
+)
+{
+ int i;
+ PortAddr iop_base;
+
+ for (i = 0; i < ASC_IOADR_TABLE_MAX_IX; i++) {
+ if (_asc_def_iop_base[i] > s_addr) {
+ break;
+ }
+ }
+ for (; i < ASC_IOADR_TABLE_MAX_IX; i++) {
+ iop_base = _asc_def_iop_base[i];
+ if (check_region(iop_base, ASC_IOADR_GAP) != 0) {
+ ASC_DBG1(1,
+ "AscSearchIOPortAddr11: check_region() failed I/O port %x\n",
+ iop_base);
+ continue;
+ }
+ ASC_DBG1(1, "AscSearchIOPortAddr11: probing I/O port %x\n", iop_base);
+ if (AscFindSignature(iop_base)) {
+ return (iop_base);
+ }
+ }
+ return (0);
+}
+
+ASC_INITFUNC(
+STATIC void
+AscToggleIRQAct(
+ PortAddr iop_base
+)
+)
+{
+ AscSetChipStatus(iop_base, CIW_IRQ_ACT);
+ AscSetChipStatus(iop_base, 0);
+ return;
+}
+
+ASC_INITFUNC(
+STATIC void
+AscSetISAPNPWaitForKey(
+ void)
+)
+{
+ outp(ASC_ISA_PNP_PORT_ADDR, 0x02);
+ outp(ASC_ISA_PNP_PORT_WRITE, 0x02);
+ return;
+}
+
+ASC_INITFUNC(
+STATIC uchar
+AscGetChipIRQ(
+ PortAddr iop_base,
+ ushort bus_type
+)
+)
+{
+ ushort cfg_lsw;
+ uchar chip_irq;
+
+ if ((bus_type & ASC_IS_EISA) != 0) {
+ cfg_lsw = AscGetEisaChipCfg(iop_base);
+ chip_irq = (uchar) (((cfg_lsw >> 8) & 0x07) + 10);
+ if ((chip_irq == 13) || (chip_irq > 15)) {
+ return (0);
+ }
+ return (chip_irq);
+ }
+ if ((bus_type & ASC_IS_VL) != 0) {
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+ chip_irq = (uchar) (((cfg_lsw >> 2) & 0x07));
+ if ((chip_irq == 0) ||
+ (chip_irq == 4) ||
+ (chip_irq == 7)) {
+ return (0);
+ }
+ return ((uchar) (chip_irq + (ASC_MIN_IRQ_NO - 1)));
+ }
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+ chip_irq = (uchar) (((cfg_lsw >> 2) & 0x03));
+ if (chip_irq == 3)
+ chip_irq += (uchar) 2;
+ return ((uchar) (chip_irq + ASC_MIN_IRQ_NO));
+}
+
+ASC_INITFUNC(
+STATIC uchar
+AscSetChipIRQ(
+ PortAddr iop_base,
+ uchar irq_no,
+ ushort bus_type
+)
+)
+{
+ ushort cfg_lsw;
+
+ if ((bus_type & ASC_IS_VL) != 0) {
+ if (irq_no != 0) {
+ if ((irq_no < ASC_MIN_IRQ_NO) || (irq_no > ASC_MAX_IRQ_NO)) {
+ irq_no = 0;
+ } else {
+ irq_no -= (uchar) ((ASC_MIN_IRQ_NO - 1));
+ }
+ }
+ cfg_lsw = (ushort) (AscGetChipCfgLsw(iop_base) & 0xFFE3);
+ cfg_lsw |= (ushort) 0x0010;
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ AscToggleIRQAct(iop_base);
+ cfg_lsw = (ushort) (AscGetChipCfgLsw(iop_base) & 0xFFE0);
+ cfg_lsw |= (ushort) ((irq_no & 0x07) << 2);
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ AscToggleIRQAct(iop_base);
+ return (AscGetChipIRQ(iop_base, bus_type));
+ }
+ if ((bus_type & (ASC_IS_ISA)) != 0) {
+ if (irq_no == 15)
+ irq_no -= (uchar) 2;
+ irq_no -= (uchar) ASC_MIN_IRQ_NO;
+ cfg_lsw = (ushort) (AscGetChipCfgLsw(iop_base) & 0xFFF3);
+ cfg_lsw |= (ushort) ((irq_no & 0x03) << 2);
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ return (AscGetChipIRQ(iop_base, bus_type));
+ }
+ return (0);
+}
+
+ASC_INITFUNC(
+STATIC void
+AscEnableIsaDma(
+ uchar dma_channel
+)
+)
+{
+ if (dma_channel < 4) {
+ outp(0x000B, (ushort) (0xC0 | dma_channel));
+ outp(0x000A, dma_channel);
+ } else if (dma_channel < 8) {
+ outp(0x00D6, (ushort) (0xC0 | (dma_channel - 4)));
+ outp(0x00D4, (ushort) (dma_channel - 4));
+ }
+ return;
+}
+
+STATIC int
+AscIsrChipHalted(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ EXT_MSG ext_msg;
+ EXT_MSG out_msg;
+ ushort halt_q_addr;
+ int sdtr_accept;
+ ushort int_halt_code;
+ ASC_SCSI_BIT_ID_TYPE scsi_busy;
+ ASC_SCSI_BIT_ID_TYPE target_id;
+ PortAddr iop_base;
+ uchar tag_code;
+ uchar q_status;
+ uchar halt_qp;
+ uchar sdtr_data;
+ uchar target_ix;
+ uchar q_cntl, tid_no;
+ uchar cur_dvc_qng;
+ uchar asyn_sdtr;
+ uchar scsi_status;
+ asc_board_t *boardp;
+
+ ASC_ASSERT(asc_dvc->drv_ptr != 0);
+ boardp = (asc_board_t *) asc_dvc->drv_ptr;
+
+ iop_base = asc_dvc->iop_base;
+ int_halt_code = AscReadLramWord(iop_base, ASCV_HALTCODE_W);
+
+ halt_qp = AscReadLramByte(iop_base, ASCV_CURCDB_B);
+ halt_q_addr = ASC_QNO_TO_QADDR(halt_qp);
+ target_ix = AscReadLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_TARGET_IX));
+ q_cntl = AscReadLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL));
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ target_id = (uchar) ASC_TID_TO_TARGET_ID(tid_no);
+ if (asc_dvc->pci_fix_asyn_xfer & target_id) {
+
+ asyn_sdtr = ASYN_SDTR_DATA_FIX_PCI_REV_AB;
+ } else {
+ asyn_sdtr = 0;
+ }
+ if (int_halt_code == ASC_HALT_DISABLE_ASYN_USE_SYN_FIX) {
+ if (asc_dvc->pci_fix_asyn_xfer & target_id) {
+ AscSetChipSDTR(iop_base, 0, tid_no);
+ boardp->sdtr_data[tid_no] = 0;
+ }
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else if (int_halt_code == ASC_HALT_ENABLE_ASYN_USE_SYN_FIX) {
+ if (asc_dvc->pci_fix_asyn_xfer & target_id) {
+ AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
+ boardp->sdtr_data[tid_no] = asyn_sdtr;
+ }
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else if (int_halt_code == ASC_HALT_EXTMSG_IN) {
+
+ AscMemWordCopyFromLram(iop_base,
+ ASCV_MSGIN_BEG,
+ (ushort *) & ext_msg,
+ (ushort) (sizeof (EXT_MSG) >> 1));
+
+ if (ext_msg.msg_type == MS_EXTEND &&
+ ext_msg.msg_req == MS_SDTR_CODE &&
+ ext_msg.msg_len == MS_SDTR_LEN) {
+ sdtr_accept = TRUE;
+ if ((ext_msg.req_ack_offset > ASC_SYN_MAX_OFFSET)) {
+
+ sdtr_accept = FALSE;
+ ext_msg.req_ack_offset = ASC_SYN_MAX_OFFSET;
+ }
+ if ((ext_msg.xfer_period <
+ asc_dvc->sdtr_period_tbl[asc_dvc->host_init_sdtr_index]) ||
+ (ext_msg.xfer_period >
+ asc_dvc->sdtr_period_tbl[asc_dvc->max_sdtr_index])) {
+ sdtr_accept = FALSE;
+ ext_msg.xfer_period =
+ asc_dvc->sdtr_period_tbl[asc_dvc->host_init_sdtr_index];
+ }
+ if (sdtr_accept) {
+ sdtr_data = AscCalSDTRData(asc_dvc, ext_msg.xfer_period,
+ ext_msg.req_ack_offset);
+ if ((sdtr_data == 0xFF)) {
+
+ q_cntl |= QC_MSG_OUT;
+ asc_dvc->init_sdtr &= ~target_id;
+ asc_dvc->sdtr_done &= ~target_id;
+ AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
+ boardp->sdtr_data[tid_no] = asyn_sdtr;
+ }
+ }
+ if (ext_msg.req_ack_offset == 0) {
+
+ q_cntl &= ~QC_MSG_OUT;
+ asc_dvc->init_sdtr &= ~target_id;
+ asc_dvc->sdtr_done &= ~target_id;
+ AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
+ } else {
+ if (sdtr_accept && (q_cntl & QC_MSG_OUT)) {
+
+ q_cntl &= ~QC_MSG_OUT;
+ asc_dvc->sdtr_done |= target_id;
+ asc_dvc->init_sdtr |= target_id;
+ asc_dvc->pci_fix_asyn_xfer &= ~target_id;
+ sdtr_data = AscCalSDTRData(asc_dvc, ext_msg.xfer_period,
+ ext_msg.req_ack_offset);
+ AscSetChipSDTR(iop_base, sdtr_data, tid_no);
+ boardp->sdtr_data[tid_no] = sdtr_data;
+ } else {
+
+ q_cntl |= QC_MSG_OUT;
+ AscMsgOutSDTR(asc_dvc,
+ ext_msg.xfer_period,
+ ext_msg.req_ack_offset);
+ asc_dvc->pci_fix_asyn_xfer &= ~target_id;
+ sdtr_data = AscCalSDTRData(asc_dvc, ext_msg.xfer_period,
+ ext_msg.req_ack_offset);
+ AscSetChipSDTR(iop_base, sdtr_data, tid_no);
+ boardp->sdtr_data[tid_no] = sdtr_data;
+ asc_dvc->sdtr_done |= target_id;
+ asc_dvc->init_sdtr |= target_id;
+ }
+ }
+
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else if (ext_msg.msg_type == MS_EXTEND &&
+ ext_msg.msg_req == MS_WDTR_CODE &&
+ ext_msg.msg_len == MS_WDTR_LEN) {
+
+ ext_msg.wdtr_width = 0;
+ AscMemWordCopyToLram(iop_base,
+ ASCV_MSGOUT_BEG,
+ (ushort *) & ext_msg,
+ (ushort) (sizeof (EXT_MSG) >> 1));
+ q_cntl |= QC_MSG_OUT;
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else {
+
+ ext_msg.msg_type = M1_MSG_REJECT;
+ AscMemWordCopyToLram(iop_base,
+ ASCV_MSGOUT_BEG,
+ (ushort *) & ext_msg,
+ (ushort) (sizeof (EXT_MSG) >> 1));
+ q_cntl |= QC_MSG_OUT;
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ }
+ } else if (int_halt_code == ASC_HALT_CHK_CONDITION) {
+
+ q_cntl |= QC_REQ_SENSE;
+
+ if ((asc_dvc->init_sdtr & target_id) != 0) {
+
+ asc_dvc->sdtr_done &= ~target_id;
+
+ sdtr_data = AscGetMCodeInitSDTRAtID(iop_base, tid_no);
+ q_cntl |= QC_MSG_OUT;
+ AscMsgOutSDTR(asc_dvc,
+ asc_dvc->sdtr_period_tbl[(sdtr_data >> 4) &
+ (uchar) (asc_dvc->max_sdtr_index - 1)],
+ (uchar) (sdtr_data & (uchar) ASC_SYN_MAX_OFFSET));
+ }
+
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+
+ tag_code = AscReadLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_TAG_CODE));
+ tag_code &= 0xDC;
+ if (
+ (asc_dvc->pci_fix_asyn_xfer & target_id)
+ && !(asc_dvc->pci_fix_asyn_xfer_always & target_id)
+) {
+
+ tag_code |= (ASC_TAG_FLAG_DISABLE_DISCONNECT
+ | ASC_TAG_FLAG_DISABLE_ASYN_USE_SYN_FIX);
+
+ }
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_TAG_CODE),
+ tag_code);
+
+ q_status = AscReadLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_STATUS));
+ q_status |= (QS_READY | QS_BUSY);
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ q_status);
+
+ scsi_busy = AscReadLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B);
+ scsi_busy &= ~target_id;
+ AscWriteLramByte(iop_base, (ushort) ASCV_SCSIBUSY_B, scsi_busy);
+
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else if (int_halt_code == ASC_HALT_SDTR_REJECTED) {
+
+ AscMemWordCopyFromLram(iop_base,
+ ASCV_MSGOUT_BEG,
+ (ushort *) & out_msg,
+ (ushort) (sizeof (EXT_MSG) >> 1));
+
+ if ((out_msg.msg_type == MS_EXTEND) &&
+ (out_msg.msg_len == MS_SDTR_LEN) &&
+ (out_msg.msg_req == MS_SDTR_CODE)) {
+
+ asc_dvc->init_sdtr &= ~target_id;
+ asc_dvc->sdtr_done &= ~target_id;
+ AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
+ boardp->sdtr_data[tid_no] = asyn_sdtr;
+ }
+ q_cntl &= ~QC_MSG_OUT;
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else if (int_halt_code == ASC_HALT_SS_QUEUE_FULL) {
+
+ scsi_status = AscReadLramByte(iop_base,
+ (ushort) ((ushort) halt_q_addr + (ushort) ASC_SCSIQ_SCSI_STATUS));
+ cur_dvc_qng = AscReadLramByte(iop_base,
+ (ushort) ((ushort) ASC_QADR_BEG + (ushort) target_ix));
+ if ((cur_dvc_qng > 0) &&
+ (asc_dvc->cur_dvc_qng[tid_no] > 0)) {
+
+ scsi_busy = AscReadLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B);
+ scsi_busy |= target_id;
+ AscWriteLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B, scsi_busy);
+ asc_dvc->queue_full_or_busy |= target_id;
+
+ if (scsi_status == SS_QUEUE_FULL) {
+ if (cur_dvc_qng > ASC_MIN_TAGGED_CMD) {
+ cur_dvc_qng -= 1;
+ asc_dvc->max_dvc_qng[tid_no] = cur_dvc_qng;
+
+ AscWriteLramByte(iop_base,
+ (ushort) ((ushort) ASCV_MAX_DVC_QNG_BEG +
+ (ushort) tid_no),
+ cur_dvc_qng);
+
+ /*
+ * Set the device queue depth to the number of
+ * active requests when the QUEUE FULL condition
+ * was encountered.
+ */
+ boardp->queue_full |= target_id;
+ boardp->queue_full_cnt[tid_no] = cur_dvc_qng;
+#if ASC_QUEUE_FLOW_CONTROL
+ if (boardp->device[tid_no] != NULL &&
+ boardp->device[tid_no]->queue_curr_depth >
+ cur_dvc_qng) {
+ boardp->device[tid_no]->queue_curr_depth =
+ cur_dvc_qng;
+ }
+#endif /* ASC_QUEUE_FLOW_CONTROL */
+ }
+ }
+ }
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ }
+ return (0);
+}
+
+STATIC uchar
+_AscCopyLramScsiDoneQ(
+ PortAddr iop_base,
+ ushort q_addr,
+ REG ASC_QDONE_INFO * scsiq,
+ ulong max_dma_count
+)
+{
+ ushort _val;
+ uchar sg_queue_cnt;
+
+ DvcGetQinfo(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_DONE_INFO_BEG),
+ (ushort *) scsiq,
+ (ushort) ((sizeof (ASC_SCSIQ_2) + sizeof (ASC_SCSIQ_3)) / 2));
+ _val = AscReadLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS));
+ scsiq->q_status = (uchar) _val;
+ scsiq->q_no = (uchar) (_val >> 8);
+ _val = AscReadLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_CNTL));
+ scsiq->cntl = (uchar) _val;
+ sg_queue_cnt = (uchar) (_val >> 8);
+ _val = AscReadLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_SENSE_LEN));
+ scsiq->sense_len = (uchar) _val;
+ scsiq->extra_bytes = (uchar) (_val >> 8);
+ scsiq->remain_bytes = AscReadLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_DW_REMAIN_XFER_CNT));
+ scsiq->remain_bytes &= max_dma_count;
+ return (sg_queue_cnt);
+}
+
+STATIC int
+AscIsrQDone(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ uchar next_qp;
+ uchar n_q_used;
+ uchar sg_list_qp;
+ uchar sg_queue_cnt;
+ uchar q_cnt;
+ uchar done_q_tail;
+ uchar tid_no;
+ ASC_SCSI_BIT_ID_TYPE scsi_busy;
+ ASC_SCSI_BIT_ID_TYPE target_id;
+ PortAddr iop_base;
+ ushort q_addr;
+ ushort sg_q_addr;
+ uchar cur_target_qng;
+ ASC_QDONE_INFO scsiq_buf;
+ REG ASC_QDONE_INFO *scsiq;
+ int false_overrun;
+ ASC_ISR_CALLBACK asc_isr_callback;
+
+ iop_base = asc_dvc->iop_base;
+ asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback;
+ n_q_used = 1;
+ scsiq = (ASC_QDONE_INFO *) & scsiq_buf;
+ done_q_tail = (uchar) AscGetVarDoneQTail(iop_base);
+ q_addr = ASC_QNO_TO_QADDR(done_q_tail);
+ next_qp = AscReadLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_FWD));
+ if (next_qp != ASC_QLINK_END) {
+ AscPutVarDoneQTail(iop_base, next_qp);
+ q_addr = ASC_QNO_TO_QADDR(next_qp);
+ sg_queue_cnt = _AscCopyLramScsiDoneQ(iop_base, q_addr, scsiq,
+ asc_dvc->max_dma_count);
+ AscWriteLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ (uchar) (scsiq->q_status & (uchar) ~ (QS_READY | QS_ABORTED)));
+ tid_no = ASC_TIX_TO_TID(scsiq->d2.target_ix);
+ target_id = ASC_TIX_TO_TARGET_ID(scsiq->d2.target_ix);
+ if ((scsiq->cntl & QC_SG_HEAD) != 0) {
+ sg_q_addr = q_addr;
+ sg_list_qp = next_qp;
+ for (q_cnt = 0; q_cnt < sg_queue_cnt; q_cnt++) {
+ sg_list_qp = AscReadLramByte(iop_base,
+ (ushort) (sg_q_addr + (ushort) ASC_SCSIQ_B_FWD));
+ sg_q_addr = ASC_QNO_TO_QADDR(sg_list_qp);
+ if (sg_list_qp == ASC_QLINK_END) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_SG_Q_LINKS);
+ scsiq->d3.done_stat = QD_WITH_ERROR;
+ scsiq->d3.host_stat = QHSTA_D_QDONE_SG_LIST_CORRUPTED;
+ goto FATAL_ERR_QDONE;
+ }
+ AscWriteLramByte(iop_base,
+ (ushort) (sg_q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ QS_FREE);
+ }
+ n_q_used = sg_queue_cnt + 1;
+ AscPutVarDoneQTail(iop_base, sg_list_qp);
+ }
+ if (asc_dvc->queue_full_or_busy & target_id) {
+ cur_target_qng = AscReadLramByte(iop_base,
+ (ushort) ((ushort) ASC_QADR_BEG + (ushort) scsiq->d2.target_ix));
+ if (cur_target_qng < asc_dvc->max_dvc_qng[tid_no]) {
+ scsi_busy = AscReadLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B);
+ scsi_busy &= ~target_id;
+ AscWriteLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B, scsi_busy);
+ asc_dvc->queue_full_or_busy &= ~target_id;
+ }
+ }
+ if (asc_dvc->cur_total_qng >= n_q_used) {
+ asc_dvc->cur_total_qng -= n_q_used;
+ if (asc_dvc->cur_dvc_qng[tid_no] != 0) {
+ asc_dvc->cur_dvc_qng[tid_no]--;
+ }
+ } else {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_CUR_QNG);
+ scsiq->d3.done_stat = QD_WITH_ERROR;
+ goto FATAL_ERR_QDONE;
+ }
+ if ((scsiq->d2.srb_ptr == 0UL) ||
+ ((scsiq->q_status & QS_ABORTED) != 0)) {
+ return (0x11);
+ } else if (scsiq->q_status == QS_DONE) {
+ false_overrun = FALSE;
+ if (scsiq->extra_bytes != 0) {
+ scsiq->remain_bytes += (ulong) scsiq->extra_bytes;
+ }
+ if (scsiq->d3.done_stat == QD_WITH_ERROR) {
+ if (scsiq->d3.host_stat == QHSTA_M_DATA_OVER_RUN) {
+ if ((scsiq->cntl & (QC_DATA_IN | QC_DATA_OUT)) == 0) {
+ scsiq->d3.done_stat = QD_NO_ERROR;
+ scsiq->d3.host_stat = QHSTA_NO_ERROR;
+ } else if (false_overrun) {
+ scsiq->d3.done_stat = QD_NO_ERROR;
+ scsiq->d3.host_stat = QHSTA_NO_ERROR;
+ }
+ } else if (scsiq->d3.host_stat ==
+ QHSTA_M_HUNG_REQ_SCSI_BUS_RESET) {
+ AscStopChip(iop_base);
+ AscSetChipControl(iop_base,
+ (uchar) (CC_SCSI_RESET | CC_HALT));
+ DvcDelayNanoSecond(asc_dvc, 60000);
+ AscSetChipControl(iop_base, CC_HALT);
+ AscSetChipStatus(iop_base, CIW_CLR_SCSI_RESET_INT);
+ AscSetChipStatus(iop_base, 0);
+ AscSetChipControl(iop_base, 0);
+ }
+ }
+ if ((scsiq->cntl & QC_NO_CALLBACK) == 0) {
+ (*asc_isr_callback) (asc_dvc, scsiq);
+ } else {
+ if ((AscReadLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_CDB_BEG)) ==
+ SCSICMD_StartStopUnit)) {
+ asc_dvc->unit_not_ready &= ~target_id;
+ if (scsiq->d3.done_stat != QD_NO_ERROR) {
+ asc_dvc->start_motor &= ~target_id;
+ }
+ }
+ }
+ return (1);
+ } else {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_Q_STATUS);
+ FATAL_ERR_QDONE:
+ if ((scsiq->cntl & QC_NO_CALLBACK) == 0) {
+ (*asc_isr_callback) (asc_dvc, scsiq);
+ }
+ return (0x80);
+ }
+ }
+ return (0);
+}
+
+STATIC int
+AscISR(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ ASC_CS_TYPE chipstat;
+ PortAddr iop_base;
+ ushort saved_ram_addr;
+ uchar ctrl_reg;
+ uchar saved_ctrl_reg;
+ int int_pending;
+ int status;
+ uchar host_flag;
+
+ iop_base = asc_dvc->iop_base;
+ int_pending = FALSE;
+ if (((asc_dvc->init_state & ASC_INIT_STATE_END_LOAD_MC) == 0)
+ || (asc_dvc->isr_callback == 0)
+) {
+ return (ERR);
+ }
+ if (asc_dvc->in_critical_cnt != 0) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_ON_CRITICAL);
+ return (ERR);
+ }
+ if (asc_dvc->is_in_int) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_RE_ENTRY);
+ return (ERR);
+ }
+ asc_dvc->is_in_int = TRUE;
+ ctrl_reg = AscGetChipControl(iop_base);
+ saved_ctrl_reg = ctrl_reg & (~(CC_SCSI_RESET | CC_CHIP_RESET |
+ CC_SINGLE_STEP | CC_DIAG | CC_TEST));
+ chipstat = AscGetChipStatus(iop_base);
+ if (chipstat & CSW_SCSI_RESET_LATCH) {
+ if (!(asc_dvc->bus_type & (ASC_IS_VL | ASC_IS_EISA))) {
+ int_pending = TRUE;
+ asc_dvc->sdtr_done = 0;
+ saved_ctrl_reg &= (uchar) (~CC_HALT);
+ while (AscGetChipStatus(iop_base) & CSW_SCSI_RESET_ACTIVE) ;
+ AscSetChipControl(iop_base, (CC_CHIP_RESET | CC_HALT));
+ AscSetChipControl(iop_base, CC_HALT);
+ AscSetChipStatus(iop_base, CIW_CLR_SCSI_RESET_INT);
+ AscSetChipStatus(iop_base, 0);
+ chipstat = AscGetChipStatus(iop_base);
+ }
+ }
+ saved_ram_addr = AscGetChipLramAddr(iop_base);
+ host_flag = AscReadLramByte(iop_base,
+ ASCV_HOST_FLAG_B) & (uchar) (~ASC_HOST_FLAG_IN_ISR);
+ AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B,
+ (uchar) (host_flag | (uchar) ASC_HOST_FLAG_IN_ISR));
+ if ((chipstat & CSW_INT_PENDING)
+ || (int_pending)
+) {
+ AscAckInterrupt(iop_base);
+ int_pending = TRUE;
+ if ((chipstat & CSW_HALTED) &&
+ (ctrl_reg & CC_SINGLE_STEP)) {
+ if (AscIsrChipHalted(asc_dvc) == ERR) {
+ goto ISR_REPORT_QDONE_FATAL_ERROR;
+ } else {
+ saved_ctrl_reg &= (uchar) (~CC_HALT);
+ }
+ } else {
+ ISR_REPORT_QDONE_FATAL_ERROR:
+ if ((asc_dvc->dvc_cntl & ASC_CNTL_INT_MULTI_Q) != 0) {
+ while (((status = AscIsrQDone(asc_dvc)) & 0x01) != 0) {
+ }
+ } else {
+ do {
+ if ((status = AscIsrQDone(asc_dvc)) == 1) {
+ break;
+ }
+ } while (status == 0x11);
+ }
+ if ((status & 0x80) != 0)
+ int_pending = ERR;
+ }
+ }
+ AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B, host_flag);
+ AscSetChipLramAddr(iop_base, saved_ram_addr);
+ AscSetChipControl(iop_base, saved_ctrl_reg);
+ asc_dvc->is_in_int = FALSE;
+ return (int_pending);
+}
+
+STATIC uchar _asc_mcode_buf[] ASC_INITDATA =
+{
+ 0x01, 0x03, 0x01, 0x19, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0x10, 0x0A, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x23, 0x00, 0x24, 0x00, 0x00, 0x00, 0x07, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x88, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x73, 0x48, 0x04, 0x36, 0x00, 0x00, 0xA2, 0xC2, 0x00, 0x80, 0x73, 0x03, 0x23, 0x36, 0x40,
+ 0xB6, 0x00, 0x36, 0x00, 0x05, 0xD6, 0x0C, 0xD2, 0x12, 0xDA, 0x00, 0xA2, 0xC2, 0x00, 0x92, 0x80,
+ 0x1E, 0x98, 0x50, 0x00, 0xF5, 0x00, 0x48, 0x98, 0xDF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80,
+ 0x4F, 0x00, 0xF5, 0x00, 0x48, 0x98, 0xEF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80, 0x80, 0x62,
+ 0x92, 0x80, 0x00, 0x46, 0x17, 0xEE, 0x13, 0xEA, 0x02, 0x01, 0x09, 0xD8, 0xCD, 0x04, 0x4D, 0x00,
+ 0x00, 0xA3, 0xD6, 0x00, 0xA6, 0x97, 0x7F, 0x23, 0x04, 0x61, 0x84, 0x01, 0xE6, 0x84, 0xD2, 0xC1,
+ 0x80, 0x73, 0xCD, 0x04, 0x4D, 0x00, 0x00, 0xA3, 0xE2, 0x01, 0xA6, 0x97, 0xCE, 0x81, 0x00, 0x33,
+ 0x02, 0x00, 0xC0, 0x88, 0x80, 0x73, 0x80, 0x77, 0x00, 0x01, 0x01, 0xA1, 0x02, 0x01, 0x4F, 0x00,
+ 0x84, 0x97, 0x07, 0xA6, 0x0C, 0x01, 0x00, 0x33, 0x03, 0x00, 0xC0, 0x88, 0x03, 0x03, 0x03, 0xDE,
+ 0x00, 0x33, 0x05, 0x00, 0xC0, 0x88, 0xCE, 0x00, 0x69, 0x60, 0xCE, 0x00, 0x02, 0x03, 0x4A, 0x60,
+ 0x00, 0xA2, 0x80, 0x01, 0x80, 0x63, 0x07, 0xA6, 0x2C, 0x01, 0x80, 0x81, 0x03, 0x03, 0x80, 0x63,
+ 0xE2, 0x00, 0x07, 0xA6, 0x3C, 0x01, 0x00, 0x33, 0x04, 0x00, 0xC0, 0x88, 0x03, 0x07, 0x02, 0x01,
+ 0x04, 0xCA, 0x0D, 0x23, 0x68, 0x98, 0x4D, 0x04, 0x04, 0x85, 0x05, 0xD8, 0x0D, 0x23, 0x68, 0x98,
+ 0xCD, 0x04, 0x15, 0x23, 0xF6, 0x88, 0xFB, 0x23, 0x02, 0x61, 0x82, 0x01, 0x80, 0x63, 0x02, 0x03,
+ 0x06, 0xA3, 0x6A, 0x01, 0x00, 0x33, 0x0A, 0x00, 0xC0, 0x88, 0x4E, 0x00, 0x07, 0xA3, 0x76, 0x01,
+ 0x00, 0x33, 0x0B, 0x00, 0xC0, 0x88, 0xCD, 0x04, 0x36, 0x2D, 0x00, 0x33, 0x1A, 0x00, 0xC0, 0x88,
+ 0x50, 0x04, 0x90, 0x81, 0x06, 0xAB, 0x8A, 0x01, 0x90, 0x81, 0x4E, 0x00, 0x07, 0xA3, 0x9A, 0x01,
+ 0x50, 0x00, 0x00, 0xA3, 0x44, 0x01, 0x00, 0x05, 0x84, 0x81, 0x46, 0x97, 0x02, 0x01, 0x05, 0xC6,
+ 0x04, 0x23, 0xA0, 0x01, 0x15, 0x23, 0xA1, 0x01, 0xC6, 0x81, 0xFD, 0x23, 0x02, 0x61, 0x82, 0x01,
+ 0x0A, 0xDA, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA0, 0xBC, 0x01, 0x80, 0x63, 0xCD, 0x04, 0x36, 0x2D,
+ 0x00, 0x33, 0x1B, 0x00, 0xC0, 0x88, 0x06, 0x23, 0x68, 0x98, 0xCD, 0x04, 0xE6, 0x84, 0x06, 0x01,
+ 0x00, 0xA2, 0xDC, 0x01, 0x57, 0x60, 0x00, 0xA0, 0xE2, 0x01, 0xE6, 0x84, 0x80, 0x23, 0xA0, 0x01,
+ 0xE6, 0x84, 0x80, 0x73, 0x4B, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x08, 0x02, 0x04, 0x01, 0x0C, 0xDE,
+ 0x02, 0x01, 0x03, 0xCC, 0x4F, 0x00, 0x84, 0x97, 0x04, 0x82, 0x08, 0x23, 0x02, 0x41, 0x82, 0x01,
+ 0x4F, 0x00, 0x62, 0x97, 0x48, 0x04, 0x84, 0x80, 0xF0, 0x97, 0x00, 0x46, 0x56, 0x00, 0x03, 0xC0,
+ 0x01, 0x23, 0xE8, 0x00, 0x81, 0x73, 0x06, 0x29, 0x03, 0x42, 0x06, 0xE2, 0x03, 0xEE, 0x67, 0xEB,
+ 0x11, 0x23, 0xF6, 0x88, 0x04, 0x98, 0xF4, 0x80, 0x80, 0x73, 0x80, 0x77, 0x07, 0xA4, 0x32, 0x02,
+ 0x7C, 0x95, 0x06, 0xA6, 0x3C, 0x02, 0x03, 0xA6, 0x4C, 0x04, 0xC0, 0x88, 0x04, 0x01, 0x03, 0xD8,
+ 0xB2, 0x98, 0x6A, 0x96, 0x4E, 0x82, 0xFE, 0x95, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0xB6, 0x2D,
+ 0x02, 0xA6, 0x78, 0x02, 0x07, 0xA6, 0x66, 0x02, 0x06, 0xA6, 0x6A, 0x02, 0x03, 0xA6, 0x6E, 0x02,
+ 0x00, 0x33, 0x10, 0x00, 0xC0, 0x88, 0x7C, 0x95, 0x50, 0x82, 0x60, 0x96, 0x50, 0x82, 0x04, 0x23,
+ 0xA0, 0x01, 0x14, 0x23, 0xA1, 0x01, 0x3C, 0x84, 0x04, 0x01, 0x0C, 0xDC, 0xE0, 0x23, 0x25, 0x61,
+ 0xEF, 0x00, 0x14, 0x01, 0x4F, 0x04, 0xA8, 0x01, 0x6F, 0x00, 0xA5, 0x01, 0x03, 0x23, 0xA4, 0x01,
+ 0x06, 0x23, 0x9C, 0x01, 0x24, 0x2B, 0x1C, 0x01, 0x02, 0xA6, 0xB6, 0x02, 0x07, 0xA6, 0x66, 0x02,
+ 0x06, 0xA6, 0x6A, 0x02, 0x03, 0xA6, 0x20, 0x04, 0x01, 0xA6, 0xC0, 0x02, 0x00, 0xA6, 0xC0, 0x02,
+ 0x00, 0x33, 0x12, 0x00, 0xC0, 0x88, 0x00, 0x0E, 0x80, 0x63, 0x00, 0x43, 0x00, 0xA0, 0x98, 0x02,
+ 0x4D, 0x04, 0x04, 0x01, 0x0B, 0xDC, 0xE7, 0x23, 0x04, 0x61, 0x84, 0x01, 0x10, 0x31, 0x12, 0x35,
+ 0x14, 0x01, 0xEC, 0x00, 0x6C, 0x38, 0x00, 0x3F, 0x00, 0x00, 0xF6, 0x82, 0x18, 0x23, 0x04, 0x61,
+ 0x18, 0xA0, 0xEE, 0x02, 0x04, 0x01, 0x9C, 0xC8, 0x00, 0x33, 0x1F, 0x00, 0xC0, 0x88, 0x08, 0x31,
+ 0x0A, 0x35, 0x0C, 0x39, 0x0E, 0x3D, 0x7E, 0x98, 0xB6, 0x2D, 0x01, 0xA6, 0x20, 0x03, 0x00, 0xA6,
+ 0x20, 0x03, 0x07, 0xA6, 0x18, 0x03, 0x06, 0xA6, 0x1C, 0x03, 0x03, 0xA6, 0x20, 0x04, 0x02, 0xA6,
+ 0x78, 0x02, 0x00, 0x33, 0x33, 0x00, 0xC0, 0x88, 0x7C, 0x95, 0xFA, 0x82, 0x60, 0x96, 0xFA, 0x82,
+ 0x82, 0x98, 0x80, 0x42, 0x7E, 0x98, 0x60, 0xE4, 0x04, 0x01, 0x29, 0xC8, 0x31, 0x05, 0x07, 0x01,
+ 0x00, 0xA2, 0x60, 0x03, 0x00, 0x43, 0x87, 0x01, 0x05, 0x05, 0x86, 0x98, 0x7E, 0x98, 0x00, 0xA6,
+ 0x22, 0x03, 0x07, 0xA6, 0x58, 0x03, 0x03, 0xA6, 0x3C, 0x04, 0x06, 0xA6, 0x5C, 0x03, 0x01, 0xA6,
+ 0x22, 0x03, 0x00, 0x33, 0x25, 0x00, 0xC0, 0x88, 0x7C, 0x95, 0x3E, 0x83, 0x60, 0x96, 0x3E, 0x83,
+ 0x04, 0x01, 0x0C, 0xCE, 0x03, 0xC8, 0x00, 0x33, 0x42, 0x00, 0xC0, 0x88, 0x00, 0x01, 0x05, 0x05,
+ 0xFF, 0xA2, 0x7E, 0x03, 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x3A, 0x83, 0x05, 0x05, 0x15, 0x01,
+ 0x00, 0xA2, 0x9E, 0x03, 0xEC, 0x00, 0x6E, 0x00, 0x95, 0x01, 0x6C, 0x38, 0x00, 0x3F, 0x00, 0x00,
+ 0x01, 0xA6, 0x9A, 0x03, 0x00, 0xA6, 0x9A, 0x03, 0x12, 0x84, 0x80, 0x42, 0x7E, 0x98, 0x01, 0xA6,
+ 0xA8, 0x03, 0x00, 0xA6, 0xC0, 0x03, 0x12, 0x84, 0xA6, 0x98, 0x80, 0x42, 0x01, 0xA6, 0xA8, 0x03,
+ 0x07, 0xA6, 0xB6, 0x03, 0xD8, 0x83, 0x7C, 0x95, 0xAC, 0x83, 0x00, 0x33, 0x2F, 0x00, 0xC0, 0x88,
+ 0xA6, 0x98, 0x80, 0x42, 0x00, 0xA6, 0xC0, 0x03, 0x07, 0xA6, 0xCE, 0x03, 0xD8, 0x83, 0x7C, 0x95,
+ 0xC4, 0x83, 0x00, 0x33, 0x26, 0x00, 0xC0, 0x88, 0x38, 0x2B, 0x80, 0x32, 0x80, 0x36, 0x04, 0x23,
+ 0xA0, 0x01, 0x12, 0x23, 0xA1, 0x01, 0x12, 0x84, 0x06, 0xF0, 0x06, 0xA4, 0xF6, 0x03, 0x80, 0x6B,
+ 0x05, 0x23, 0x83, 0x03, 0x80, 0x63, 0x03, 0xA6, 0x10, 0x04, 0x07, 0xA6, 0x08, 0x04, 0x06, 0xA6,
+ 0x0C, 0x04, 0x00, 0x33, 0x17, 0x00, 0xC0, 0x88, 0x7C, 0x95, 0xF6, 0x83, 0x60, 0x96, 0xF6, 0x83,
+ 0x20, 0x84, 0x06, 0xF0, 0x06, 0xA4, 0x20, 0x04, 0x80, 0x6B, 0x05, 0x23, 0x83, 0x03, 0x80, 0x63,
+ 0xB6, 0x2D, 0x03, 0xA6, 0x3C, 0x04, 0x07, 0xA6, 0x34, 0x04, 0x06, 0xA6, 0x38, 0x04, 0x00, 0x33,
+ 0x30, 0x00, 0xC0, 0x88, 0x7C, 0x95, 0x20, 0x84, 0x60, 0x96, 0x20, 0x84, 0x1D, 0x01, 0x06, 0xCC,
+ 0x00, 0x33, 0x00, 0x84, 0xC0, 0x20, 0x00, 0x23, 0xEA, 0x00, 0x81, 0x62, 0xA2, 0x0D, 0x80, 0x63,
+ 0x07, 0xA6, 0x5A, 0x04, 0x00, 0x33, 0x18, 0x00, 0xC0, 0x88, 0x03, 0x03, 0x80, 0x63, 0xA3, 0x01,
+ 0x07, 0xA4, 0x64, 0x04, 0x23, 0x01, 0x00, 0xA2, 0x86, 0x04, 0x0A, 0xA0, 0x76, 0x04, 0xE0, 0x00,
+ 0x00, 0x33, 0x1D, 0x00, 0xC0, 0x88, 0x0B, 0xA0, 0x82, 0x04, 0xE0, 0x00, 0x00, 0x33, 0x1E, 0x00,
+ 0xC0, 0x88, 0x42, 0x23, 0xF6, 0x88, 0x00, 0x23, 0x22, 0xA3, 0xE6, 0x04, 0x08, 0x23, 0x22, 0xA3,
+ 0xA2, 0x04, 0x28, 0x23, 0x22, 0xA3, 0xAE, 0x04, 0x02, 0x23, 0x22, 0xA3, 0xC4, 0x04, 0x42, 0x23,
+ 0xF6, 0x88, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA0, 0xAE, 0x04, 0x45, 0x23, 0xF6, 0x88, 0x04, 0x98,
+ 0x00, 0xA2, 0xC0, 0x04, 0xB2, 0x98, 0x00, 0x33, 0x00, 0x82, 0xC0, 0x20, 0x81, 0x62, 0xF0, 0x81,
+ 0x47, 0x23, 0xF6, 0x88, 0x04, 0x01, 0x0B, 0xDE, 0x04, 0x98, 0xB2, 0x98, 0x00, 0x33, 0x00, 0x81,
+ 0xC0, 0x20, 0x81, 0x62, 0x14, 0x01, 0x00, 0xA0, 0x08, 0x02, 0x43, 0x23, 0xF6, 0x88, 0x04, 0x23,
+ 0xA0, 0x01, 0x44, 0x23, 0xA1, 0x01, 0x80, 0x73, 0x4D, 0x00, 0x03, 0xA3, 0xF4, 0x04, 0x00, 0x33,
+ 0x27, 0x00, 0xC0, 0x88, 0x04, 0x01, 0x04, 0xDC, 0x02, 0x23, 0xA2, 0x01, 0x04, 0x23, 0xA0, 0x01,
+ 0x04, 0x98, 0x26, 0x95, 0x4B, 0x00, 0xF6, 0x00, 0x4F, 0x04, 0x4F, 0x00, 0x00, 0xA3, 0x22, 0x05,
+ 0x00, 0x05, 0x76, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x1C, 0x05, 0x0A, 0x85, 0x46, 0x97, 0xCD, 0x04,
+ 0x24, 0x85, 0x48, 0x04, 0x84, 0x80, 0x02, 0x01, 0x03, 0xDA, 0x80, 0x23, 0x82, 0x01, 0x34, 0x85,
+ 0x02, 0x23, 0xA0, 0x01, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x40, 0x05, 0x1D, 0x01, 0x04, 0xD6,
+ 0xFF, 0x23, 0x86, 0x41, 0x4B, 0x60, 0xCB, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x49, 0x00, 0x81, 0x01,
+ 0x04, 0x01, 0x02, 0xC8, 0x30, 0x01, 0x80, 0x01, 0xF7, 0x04, 0x03, 0x01, 0x49, 0x04, 0x80, 0x01,
+ 0xC9, 0x00, 0x00, 0x05, 0x00, 0x01, 0xFF, 0xA0, 0x60, 0x05, 0x77, 0x04, 0x01, 0x23, 0xEA, 0x00,
+ 0x5D, 0x00, 0xFE, 0xC7, 0x00, 0x62, 0x00, 0x23, 0xEA, 0x00, 0x00, 0x63, 0x07, 0xA4, 0xF8, 0x05,
+ 0x03, 0x03, 0x02, 0xA0, 0x8E, 0x05, 0xF4, 0x85, 0x00, 0x33, 0x2D, 0x00, 0xC0, 0x88, 0x04, 0xA0,
+ 0xB8, 0x05, 0x80, 0x63, 0x00, 0x23, 0xDF, 0x00, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0xA4, 0x05,
+ 0x1D, 0x01, 0x06, 0xD6, 0x02, 0x23, 0x02, 0x41, 0x82, 0x01, 0x50, 0x00, 0x62, 0x97, 0x04, 0x85,
+ 0x04, 0x23, 0x02, 0x41, 0x82, 0x01, 0x04, 0x85, 0x08, 0xA0, 0xBE, 0x05, 0xF4, 0x85, 0x03, 0xA0,
+ 0xC4, 0x05, 0xF4, 0x85, 0x01, 0xA0, 0xCE, 0x05, 0x88, 0x00, 0x80, 0x63, 0xCC, 0x86, 0x07, 0xA0,
+ 0xEE, 0x05, 0x5F, 0x00, 0x00, 0x2B, 0xDF, 0x08, 0x00, 0xA2, 0xE6, 0x05, 0x80, 0x67, 0x80, 0x63,
+ 0x01, 0xA2, 0x7A, 0x06, 0x7C, 0x85, 0x06, 0x23, 0x68, 0x98, 0x48, 0x23, 0xF6, 0x88, 0x07, 0x23,
+ 0x80, 0x00, 0x06, 0x87, 0x80, 0x63, 0x7C, 0x85, 0x00, 0x23, 0xDF, 0x00, 0x00, 0x63, 0x4A, 0x00,
+ 0x06, 0x61, 0x00, 0xA2, 0x36, 0x06, 0x1D, 0x01, 0x16, 0xD4, 0xC0, 0x23, 0x07, 0x41, 0x83, 0x03,
+ 0x80, 0x63, 0x06, 0xA6, 0x1C, 0x06, 0x00, 0x33, 0x37, 0x00, 0xC0, 0x88, 0x1D, 0x01, 0x01, 0xD6,
+ 0x20, 0x23, 0x63, 0x60, 0x83, 0x03, 0x80, 0x63, 0x02, 0x23, 0xDF, 0x00, 0x07, 0xA6, 0x7C, 0x05,
+ 0xEF, 0x04, 0x6F, 0x00, 0x00, 0x63, 0x4B, 0x00, 0x06, 0x41, 0xCB, 0x00, 0x52, 0x00, 0x06, 0x61,
+ 0x00, 0xA2, 0x4E, 0x06, 0x1D, 0x01, 0x03, 0xCA, 0xC0, 0x23, 0x07, 0x41, 0x00, 0x63, 0x1D, 0x01,
+ 0x04, 0xCC, 0x00, 0x33, 0x00, 0x83, 0xC0, 0x20, 0x81, 0x62, 0x80, 0x23, 0x07, 0x41, 0x00, 0x63,
+ 0x80, 0x67, 0x08, 0x23, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63, 0x01, 0x23, 0xDF, 0x00, 0x06, 0xA6,
+ 0x84, 0x06, 0x07, 0xA6, 0x7C, 0x05, 0x80, 0x67, 0x80, 0x63, 0x00, 0x33, 0x00, 0x40, 0xC0, 0x20,
+ 0x81, 0x62, 0x00, 0x63, 0x00, 0x00, 0xFE, 0x95, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, 0x94, 0x06,
+ 0x07, 0xA6, 0x7C, 0x05, 0x00, 0x00, 0x01, 0xA0, 0x14, 0x07, 0x00, 0x2B, 0x40, 0x0E, 0x80, 0x63,
+ 0x01, 0x00, 0x06, 0xA6, 0xAA, 0x06, 0x07, 0xA6, 0x7C, 0x05, 0x40, 0x0E, 0x80, 0x63, 0x00, 0x43,
+ 0x00, 0xA0, 0xA2, 0x06, 0x06, 0xA6, 0xBC, 0x06, 0x07, 0xA6, 0x7C, 0x05, 0x80, 0x67, 0x40, 0x0E,
+ 0x80, 0x63, 0x07, 0xA6, 0x7C, 0x05, 0x00, 0x23, 0xDF, 0x00, 0x00, 0x63, 0x07, 0xA6, 0xD6, 0x06,
+ 0x00, 0x33, 0x2A, 0x00, 0xC0, 0x88, 0x03, 0x03, 0x80, 0x63, 0x89, 0x00, 0x0A, 0x2B, 0x07, 0xA6,
+ 0xE8, 0x06, 0x00, 0x33, 0x29, 0x00, 0xC0, 0x88, 0x00, 0x43, 0x00, 0xA2, 0xF4, 0x06, 0xC0, 0x0E,
+ 0x80, 0x63, 0xDE, 0x86, 0xC0, 0x0E, 0x00, 0x33, 0x00, 0x80, 0xC0, 0x20, 0x81, 0x62, 0x04, 0x01,
+ 0x02, 0xDA, 0x80, 0x63, 0x7C, 0x85, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, 0x8C, 0x06, 0x00, 0x33,
+ 0x2C, 0x00, 0xC0, 0x88, 0x0C, 0xA2, 0x2E, 0x07, 0xFE, 0x95, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6,
+ 0x2C, 0x07, 0x07, 0xA6, 0x7C, 0x05, 0x00, 0x33, 0x3D, 0x00, 0xC0, 0x88, 0x00, 0x00, 0x80, 0x67,
+ 0x83, 0x03, 0x80, 0x63, 0x0C, 0xA0, 0x44, 0x07, 0x07, 0xA6, 0x7C, 0x05, 0xBF, 0x23, 0x04, 0x61,
+ 0x84, 0x01, 0xE6, 0x84, 0x00, 0x63, 0xF0, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x00, 0x01, 0xF2, 0x00,
+ 0x01, 0x05, 0x80, 0x01, 0x72, 0x04, 0x71, 0x00, 0x81, 0x01, 0x70, 0x04, 0x80, 0x05, 0x81, 0x05,
+ 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x70, 0x00, 0x81, 0x01,
+ 0x70, 0x04, 0x71, 0x00, 0x81, 0x01, 0x72, 0x00, 0x80, 0x01, 0x71, 0x04, 0x70, 0x00, 0x80, 0x01,
+ 0x70, 0x04, 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x00, 0x01, 0xF1, 0x00, 0x70, 0x00,
+ 0x80, 0x01, 0x70, 0x04, 0x71, 0x00, 0x80, 0x01, 0x72, 0x00, 0x81, 0x01, 0x71, 0x04, 0x70, 0x00,
+ 0x81, 0x01, 0x70, 0x04, 0x00, 0x63, 0x00, 0x23, 0xB3, 0x01, 0x83, 0x05, 0xA3, 0x01, 0xA2, 0x01,
+ 0xA1, 0x01, 0x01, 0x23, 0xA0, 0x01, 0x00, 0x01, 0xC8, 0x00, 0x03, 0xA1, 0xC4, 0x07, 0x00, 0x33,
+ 0x07, 0x00, 0xC0, 0x88, 0x80, 0x05, 0x81, 0x05, 0x04, 0x01, 0x11, 0xC8, 0x48, 0x00, 0xB0, 0x01,
+ 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x05, 0x01, 0x48, 0x04, 0x00, 0x43, 0x00, 0xA2, 0xE4, 0x07,
+ 0x00, 0x05, 0xDA, 0x87, 0x00, 0x01, 0xC8, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x05, 0x05, 0x00, 0x63,
+ 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, 0x80, 0x43, 0x76, 0x08, 0x80, 0x02,
+ 0x77, 0x04, 0x00, 0x63, 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, 0x00, 0xA0,
+ 0x14, 0x08, 0x16, 0x88, 0x00, 0x43, 0x76, 0x08, 0x80, 0x02, 0x77, 0x04, 0x00, 0x63, 0xF3, 0x04,
+ 0x00, 0x23, 0xF4, 0x00, 0x74, 0x00, 0x80, 0x43, 0xF4, 0x00, 0xCF, 0x40, 0x00, 0xA2, 0x44, 0x08,
+ 0x74, 0x04, 0x02, 0x01, 0xF7, 0xC9, 0xF6, 0xD9, 0x00, 0x01, 0x01, 0xA1, 0x24, 0x08, 0x04, 0x98,
+ 0x26, 0x95, 0x24, 0x88, 0x73, 0x04, 0x00, 0x63, 0xF3, 0x04, 0x75, 0x04, 0x5A, 0x88, 0x02, 0x01,
+ 0x04, 0xD8, 0x46, 0x97, 0x04, 0x98, 0x26, 0x95, 0x4A, 0x88, 0x75, 0x00, 0x00, 0xA3, 0x64, 0x08,
+ 0x00, 0x05, 0x4E, 0x88, 0x73, 0x04, 0x00, 0x63, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, 0x76, 0x08,
+ 0x00, 0x33, 0x3E, 0x00, 0xC0, 0x88, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63, 0x38, 0x2B,
+ 0x9C, 0x88, 0x38, 0x2B, 0x92, 0x88, 0x32, 0x09, 0x31, 0x05, 0x92, 0x98, 0x05, 0x05, 0xB2, 0x09,
+ 0x00, 0x63, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3A, 0x00, 0x3E, 0x00, 0x63, 0x80, 0x32, 0x80, 0x36,
+ 0x80, 0x3A, 0x80, 0x3E, 0x00, 0x63, 0x38, 0x2B, 0x40, 0x32, 0x40, 0x36, 0x40, 0x3A, 0x40, 0x3E,
+ 0x00, 0x63, 0x5A, 0x20, 0xC9, 0x40, 0x00, 0xA0, 0xB2, 0x08, 0x5D, 0x00, 0xFE, 0xC3, 0x00, 0x63,
+ 0x80, 0x73, 0xE6, 0x20, 0x02, 0x23, 0xE8, 0x00, 0x82, 0x73, 0xFF, 0xFD, 0x80, 0x73, 0x13, 0x23,
+ 0xF6, 0x88, 0x66, 0x20, 0xC0, 0x20, 0x04, 0x23, 0xA0, 0x01, 0xA1, 0x23, 0xA1, 0x01, 0x81, 0x62,
+ 0xE0, 0x88, 0x80, 0x73, 0x80, 0x77, 0x68, 0x00, 0x00, 0xA2, 0x80, 0x00, 0x03, 0xC2, 0xF1, 0xC7,
+ 0x41, 0x23, 0xF6, 0x88, 0x11, 0x23, 0xA1, 0x01, 0x04, 0x23, 0xA0, 0x01, 0xE6, 0x84,
+};
+
+STATIC ushort _asc_mcode_size ASC_INITDATA = sizeof(_asc_mcode_buf);
+STATIC ulong _asc_mcode_chksum ASC_INITDATA = 0x012B5442UL;
+
+#define ASC_SYN_OFFSET_ONE_DISABLE_LIST 16
+STATIC uchar _syn_offset_one_disable_cmd[ASC_SYN_OFFSET_ONE_DISABLE_LIST] =
+{
+ SCSICMD_Inquiry,
+ SCSICMD_RequestSense,
+ SCSICMD_ReadCapacity,
+ SCSICMD_ReadTOC,
+ SCSICMD_ModeSelect6,
+ SCSICMD_ModeSense6,
+ SCSICMD_ModeSelect10,
+ SCSICMD_ModeSense10,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ 0xFF
+};
+
+STATIC int
+AscExeScsiQueue(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ REG ASC_SCSI_Q * scsiq
+)
+{
+ PortAddr iop_base;
+ int last_int_level;
+ int sta;
+ int n_q_required;
+ int disable_syn_offset_one_fix;
+ int i;
+ ulong addr;
+ ASC_EXE_CALLBACK asc_exe_callback;
+ ushort sg_entry_cnt = 0;
+ ushort sg_entry_cnt_minus_one = 0;
+ uchar target_ix;
+ uchar tid_no;
+ uchar sdtr_data;
+ uchar extra_bytes;
+ uchar scsi_cmd;
+ uchar disable_cmd;
+ ASC_SG_HEAD *sg_head;
+ ulong data_cnt;
+
+ iop_base = asc_dvc->iop_base;
+ sg_head = scsiq->sg_head;
+ asc_exe_callback = (ASC_EXE_CALLBACK) asc_dvc->exe_callback;
+ if (asc_dvc->err_code != 0)
+ return (ERR);
+ if (scsiq == (ASC_SCSI_Q *) 0L) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_SCSIQ_NULL_PTR);
+ return (ERR);
+ }
+ scsiq->q1.q_no = 0;
+ if ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES) == 0) {
+ scsiq->q1.extra_bytes = 0;
+ }
+ sta = 0;
+ target_ix = scsiq->q2.target_ix;
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ n_q_required = 1;
+ if (scsiq->cdbptr[0] == SCSICMD_RequestSense) {
+ if ((asc_dvc->init_sdtr & scsiq->q1.target_id) != 0) {
+ asc_dvc->sdtr_done &= ~scsiq->q1.target_id ;
+ sdtr_data = AscGetMCodeInitSDTRAtID(iop_base, tid_no);
+ AscMsgOutSDTR(asc_dvc,
+ asc_dvc->sdtr_period_tbl[(sdtr_data >> 4) &
+ (uchar) (asc_dvc->max_sdtr_index - 1)],
+ (uchar) (sdtr_data & (uchar) ASC_SYN_MAX_OFFSET));
+ scsiq->q1.cntl |= (QC_MSG_OUT | QC_URGENT);
+ }
+ }
+ last_int_level = DvcEnterCritical();
+ if (asc_dvc->in_critical_cnt != 0) {
+ DvcLeaveCritical(last_int_level);
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_CRITICAL_RE_ENTRY);
+ return (ERR);
+ }
+ asc_dvc->in_critical_cnt++;
+ if ((scsiq->q1.cntl & QC_SG_HEAD) != 0) {
+ if ((sg_entry_cnt = sg_head->entry_cnt) == 0) {
+ asc_dvc->in_critical_cnt--;
+ DvcLeaveCritical(last_int_level);
+ return (ERR);
+ }
+ if (sg_entry_cnt > ASC_MAX_SG_LIST) {
+ return (ERR);
+ }
+ if (sg_entry_cnt == 1) {
+ scsiq->q1.data_addr = sg_head->sg_list[0].addr;
+ scsiq->q1.data_cnt = sg_head->sg_list[0].bytes;
+ scsiq->q1.cntl &= ~(QC_SG_HEAD | QC_SG_SWAP_QUEUE);
+ }
+ sg_entry_cnt_minus_one = sg_entry_cnt - 1;
+ }
+ scsi_cmd = scsiq->cdbptr[0];
+ disable_syn_offset_one_fix = FALSE;
+ if ((asc_dvc->pci_fix_asyn_xfer & scsiq->q1.target_id) &&
+ !(asc_dvc->pci_fix_asyn_xfer_always & scsiq->q1.target_id)) {
+ if (scsiq->q1.cntl & QC_SG_HEAD) {
+ data_cnt = 0;
+ for (i = 0; i < sg_entry_cnt; i++) {
+ data_cnt += sg_head->sg_list[i].bytes;
+ }
+ } else {
+ data_cnt = scsiq->q1.data_cnt;
+ }
+ if (data_cnt != 0UL) {
+ if (data_cnt < 512UL) {
+ disable_syn_offset_one_fix = TRUE;
+ } else {
+ for (i = 0; i < ASC_SYN_OFFSET_ONE_DISABLE_LIST; i++) {
+ disable_cmd = _syn_offset_one_disable_cmd[i];
+ if (disable_cmd == 0xFF) {
+ break;
+ }
+ if (scsi_cmd == disable_cmd) {
+ disable_syn_offset_one_fix = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (disable_syn_offset_one_fix) {
+ scsiq->q2.tag_code &= ~M2_QTAG_MSG_SIMPLE;
+ scsiq->q2.tag_code |= (ASC_TAG_FLAG_DISABLE_ASYN_USE_SYN_FIX |
+ ASC_TAG_FLAG_DISABLE_DISCONNECT);
+ } else {
+ scsiq->q2.tag_code &= 0x23;
+ }
+ if ((scsiq->q1.cntl & QC_SG_HEAD) != 0) {
+ if (asc_dvc->bug_fix_cntl) {
+ if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_IF_NOT_DWB) {
+ if ((scsi_cmd == SCSICMD_Read6) ||
+ (scsi_cmd == SCSICMD_Read10)) {
+ addr = sg_head->sg_list[sg_entry_cnt_minus_one].addr +
+ sg_head->sg_list[sg_entry_cnt_minus_one].bytes;
+ extra_bytes = (uchar) ((ushort) addr & 0x0003);
+ if ((extra_bytes != 0) &&
+ ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES)
+ == 0)) {
+ scsiq->q2.tag_code |= ASC_TAG_FLAG_EXTRA_BYTES;
+ scsiq->q1.extra_bytes = extra_bytes;
+ sg_head->sg_list[sg_entry_cnt_minus_one].bytes -=
+ (ulong) extra_bytes;
+ }
+ }
+ }
+ }
+ sg_head->entry_to_copy = sg_head->entry_cnt;
+ n_q_required = AscSgListToQueue(sg_entry_cnt);
+ if ((AscGetNumOfFreeQueue(asc_dvc, target_ix, n_q_required) >=
+ (uint) n_q_required) || ((scsiq->q1.cntl & QC_URGENT) != 0)) {
+ if ((sta = AscSendScsiQueue(asc_dvc, scsiq,
+ n_q_required)) == 1) {
+ asc_dvc->in_critical_cnt--;
+ if (asc_exe_callback != 0) {
+ (*asc_exe_callback) (asc_dvc, scsiq);
+ }
+ DvcLeaveCritical(last_int_level);
+ return (sta);
+ }
+ }
+ } else {
+ if (asc_dvc->bug_fix_cntl) {
+ if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_IF_NOT_DWB) {
+ if ((scsi_cmd == SCSICMD_Read6) ||
+ (scsi_cmd == SCSICMD_Read10)) {
+ addr = scsiq->q1.data_addr + scsiq->q1.data_cnt;
+ extra_bytes = (uchar) ((ushort) addr & 0x0003);
+ if ((extra_bytes != 0) &&
+ ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES)
+ == 0)) {
+ if (((ushort) scsiq->q1.data_cnt & 0x01FF) == 0) {
+ scsiq->q2.tag_code |= ASC_TAG_FLAG_EXTRA_BYTES;
+ scsiq->q1.data_cnt -= (ulong) extra_bytes;
+ scsiq->q1.extra_bytes = extra_bytes;
+ }
+ }
+ }
+ }
+ }
+ n_q_required = 1;
+ if ((AscGetNumOfFreeQueue(asc_dvc, target_ix, 1) >= 1) ||
+ ((scsiq->q1.cntl & QC_URGENT) != 0)) {
+ if ((sta = AscSendScsiQueue(asc_dvc, scsiq,
+ n_q_required)) == 1) {
+ asc_dvc->in_critical_cnt--;
+ if (asc_exe_callback != 0) {
+ (*asc_exe_callback) (asc_dvc, scsiq);
+ }
+ DvcLeaveCritical(last_int_level);
+ return (sta);
+ }
+ }
+ }
+ asc_dvc->in_critical_cnt--;
+ DvcLeaveCritical(last_int_level);
+ return (sta);
+}
+
+STATIC int
+AscSendScsiQueue(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ REG ASC_SCSI_Q * scsiq,
+ uchar n_q_required
+)
+{
+ PortAddr iop_base;
+ uchar free_q_head;
+ uchar next_qp;
+ uchar tid_no;
+ uchar target_ix;
+ int sta;
+
+ iop_base = asc_dvc->iop_base;
+ target_ix = scsiq->q2.target_ix;
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ sta = 0;
+ free_q_head = (uchar) AscGetVarFreeQHead(iop_base);
+ if (n_q_required > 1) {
+ if ((next_qp = AscAllocMultipleFreeQueue(iop_base,
+ free_q_head, (uchar) (n_q_required)))
+ != (uchar) ASC_QLINK_END) {
+ asc_dvc->last_q_shortage = 0;
+ scsiq->sg_head->queue_cnt = n_q_required - 1;
+ scsiq->q1.q_no = free_q_head;
+ if ((sta = AscPutReadySgListQueue(asc_dvc, scsiq,
+ free_q_head)) == 1) {
+ AscPutVarFreeQHead(iop_base, next_qp);
+ asc_dvc->cur_total_qng += (uchar) (n_q_required);
+ asc_dvc->cur_dvc_qng[tid_no]++;
+ }
+ return (sta);
+ }
+ } else if (n_q_required == 1) {
+ if ((next_qp = AscAllocFreeQueue(iop_base,
+ free_q_head)) != ASC_QLINK_END) {
+ scsiq->q1.q_no = free_q_head;
+ if ((sta = AscPutReadyQueue(asc_dvc, scsiq,
+ free_q_head)) == 1) {
+ AscPutVarFreeQHead(iop_base, next_qp);
+ asc_dvc->cur_total_qng++;
+ asc_dvc->cur_dvc_qng[tid_no]++;
+ }
+ return (sta);
+ }
+ }
+ return (sta);
+}
+
+STATIC int
+AscSgListToQueue(
+ int sg_list
+)
+{
+ int n_sg_list_qs;
+
+ n_sg_list_qs = ((sg_list - 1) / ASC_SG_LIST_PER_Q);
+ if (((sg_list - 1) % ASC_SG_LIST_PER_Q) != 0)
+ n_sg_list_qs++;
+ return (n_sg_list_qs + 1);
+}
+
+
+STATIC uint
+AscGetNumOfFreeQueue(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ uchar target_ix,
+ uchar n_qs
+)
+{
+ uint cur_used_qs;
+ uint cur_free_qs;
+ ASC_SCSI_BIT_ID_TYPE target_id;
+ uchar tid_no;
+
+ target_id = ASC_TIX_TO_TARGET_ID(target_ix);
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ if ((asc_dvc->unit_not_ready & target_id) ||
+ (asc_dvc->queue_full_or_busy & target_id)) {
+ return (0);
+ }
+ if (n_qs == 1) {
+ cur_used_qs = (uint) asc_dvc->cur_total_qng +
+ (uint) asc_dvc->last_q_shortage +
+ (uint) ASC_MIN_FREE_Q;
+ } else {
+ cur_used_qs = (uint) asc_dvc->cur_total_qng +
+ (uint) ASC_MIN_FREE_Q;
+ }
+ if ((uint) (cur_used_qs + n_qs) <= (uint) asc_dvc->max_total_qng) {
+ cur_free_qs = (uint) asc_dvc->max_total_qng - cur_used_qs;
+ if (asc_dvc->cur_dvc_qng[tid_no] >=
+ asc_dvc->max_dvc_qng[tid_no]) {
+ return (0);
+ }
+ return (cur_free_qs);
+ }
+ if (n_qs > 1) {
+ if ((n_qs > asc_dvc->last_q_shortage) && (n_qs <= (asc_dvc->max_total_qng - ASC_MIN_FREE_Q))) {
+ asc_dvc->last_q_shortage = n_qs;
+ }
+ }
+ return (0);
+}
+
+STATIC int
+AscPutReadyQueue(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ REG ASC_SCSI_Q * scsiq,
+ uchar q_no
+)
+{
+ ushort q_addr;
+ uchar tid_no;
+ uchar sdtr_data;
+ uchar syn_period_ix;
+ uchar syn_offset;
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+ if (((asc_dvc->init_sdtr & scsiq->q1.target_id) != 0) &&
+ ((asc_dvc->sdtr_done & scsiq->q1.target_id) == 0)) {
+ tid_no = ASC_TIX_TO_TID(scsiq->q2.target_ix);
+ sdtr_data = AscGetMCodeInitSDTRAtID(iop_base, tid_no);
+ syn_period_ix = (sdtr_data >> 4) & (asc_dvc->max_sdtr_index - 1);
+ syn_offset = sdtr_data & ASC_SYN_MAX_OFFSET;
+ AscMsgOutSDTR(asc_dvc,
+ asc_dvc->sdtr_period_tbl[syn_period_ix],
+ syn_offset);
+ scsiq->q1.cntl |= QC_MSG_OUT;
+ }
+ q_addr = ASC_QNO_TO_QADDR(q_no);
+ if ((scsiq->q1.target_id & asc_dvc->use_tagged_qng) == 0) {
+ scsiq->q2.tag_code &= ~M2_QTAG_MSG_SIMPLE;
+ }
+ scsiq->q1.status = QS_FREE;
+ AscMemWordCopyToLram(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_CDB_BEG),
+ (ushort *) scsiq->cdbptr,
+ (ushort) ((ushort) scsiq->q2.cdb_len >> 1));
+ DvcPutScsiQ(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_CPY_BEG),
+ (ushort *) & scsiq->q1.cntl,
+ (ushort) ((((sizeof (ASC_SCSIQ_1) + sizeof (ASC_SCSIQ_2)) / 2) - 1)));
+ AscWriteLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ (ushort) (((ushort) scsiq->q1.q_no << 8) | (ushort) QS_READY));
+ return (1);
+}
+
+STATIC int
+AscPutReadySgListQueue(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ REG ASC_SCSI_Q * scsiq,
+ uchar q_no
+)
+{
+ int sta;
+ int i;
+ ASC_SG_HEAD *sg_head;
+ ASC_SG_LIST_Q scsi_sg_q;
+ ulong saved_data_addr;
+ ulong saved_data_cnt;
+ PortAddr iop_base;
+ ushort sg_list_dwords;
+ ushort sg_index;
+ ushort sg_entry_cnt;
+ ushort q_addr;
+ uchar next_qp;
+
+ iop_base = asc_dvc->iop_base;
+ sg_head = scsiq->sg_head;
+ saved_data_addr = scsiq->q1.data_addr;
+ saved_data_cnt = scsiq->q1.data_cnt;
+ scsiq->q1.data_addr = sg_head->sg_list[0].addr;
+ scsiq->q1.data_cnt = sg_head->sg_list[0].bytes;
+ sg_entry_cnt = sg_head->entry_cnt - 1;
+ if (sg_entry_cnt != 0) {
+ scsiq->q1.cntl |= QC_SG_HEAD;
+ q_addr = ASC_QNO_TO_QADDR(q_no);
+ sg_index = 1;
+ scsiq->q1.sg_queue_cnt = sg_head->queue_cnt;
+ scsi_sg_q.sg_head_qp = q_no;
+ scsi_sg_q.cntl = QCSG_SG_XFER_LIST;
+ for (i = 0; i < sg_head->queue_cnt; i++) {
+ scsi_sg_q.seq_no = i + 1;
+ if (sg_entry_cnt > ASC_SG_LIST_PER_Q) {
+ sg_list_dwords = (uchar) (ASC_SG_LIST_PER_Q * 2);
+ sg_entry_cnt -= ASC_SG_LIST_PER_Q;
+ if (i == 0) {
+ scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q;
+ scsi_sg_q.sg_cur_list_cnt = ASC_SG_LIST_PER_Q;
+ } else {
+ scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q - 1;
+ scsi_sg_q.sg_cur_list_cnt = ASC_SG_LIST_PER_Q - 1;
+ }
+ } else {
+ scsi_sg_q.cntl |= QCSG_SG_XFER_END;
+ sg_list_dwords = sg_entry_cnt << 1;
+ if (i == 0) {
+ scsi_sg_q.sg_list_cnt = sg_entry_cnt;
+ scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt;
+ } else {
+ scsi_sg_q.sg_list_cnt = sg_entry_cnt - 1;
+ scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt - 1;
+ }
+ sg_entry_cnt = 0;
+ }
+ next_qp = AscReadLramByte(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_B_FWD));
+ scsi_sg_q.q_no = next_qp;
+ q_addr = ASC_QNO_TO_QADDR(next_qp);
+ AscMemWordCopyToLram(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_SGHD_CPY_BEG),
+ (ushort *) & scsi_sg_q,
+ (ushort) (sizeof (ASC_SG_LIST_Q) >> 1));
+ AscMemDWordCopyToLram(iop_base,
+ (ushort) (q_addr + ASC_SGQ_LIST_BEG),
+ (ulong *) & sg_head->sg_list[sg_index],
+ (ushort) sg_list_dwords);
+ sg_index += ASC_SG_LIST_PER_Q;
+ }
+ } else {
+ scsiq->q1.cntl &= ~QC_SG_HEAD;
+ }
+ sta = AscPutReadyQueue(asc_dvc, scsiq, q_no);
+ scsiq->q1.data_addr = saved_data_addr;
+ scsiq->q1.data_cnt = saved_data_cnt;
+ return (sta);
+}
+
+STATIC int
+AscAbortSRB(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ulong srb_ptr
+)
+{
+ int sta;
+ ASC_SCSI_BIT_ID_TYPE saved_unit_not_ready;
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+ sta = ERR;
+ saved_unit_not_ready = asc_dvc->unit_not_ready;
+ asc_dvc->unit_not_ready = 0xFF;
+ AscWaitISRDone(asc_dvc);
+ if (AscStopQueueExe(iop_base) == 1) {
+ if (AscRiscHaltedAbortSRB(asc_dvc, srb_ptr) == 1) {
+ sta = 1;
+ AscCleanUpBusyQueue(iop_base);
+ AscStartQueueExe(iop_base);
+ } else {
+ sta = 0;
+ AscStartQueueExe(iop_base);
+ }
+ }
+ asc_dvc->unit_not_ready = saved_unit_not_ready;
+ return (sta);
+}
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+STATIC int
+AscResetDevice(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ uchar target_ix
+)
+{
+ PortAddr iop_base;
+ int sta;
+ uchar tid_no;
+
+ ASC_SCSI_BIT_ID_TYPE target_id;
+ int i;
+ ASC_SCSI_REQ_Q scsiq_buf;
+ ASC_SCSI_REQ_Q *scsiq;
+ uchar *buf;
+ ASC_SCSI_BIT_ID_TYPE saved_unit_not_ready;
+ iop_base = asc_dvc->iop_base;
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ target_id = ASC_TID_TO_TARGET_ID(tid_no);
+ saved_unit_not_ready = asc_dvc->unit_not_ready;
+ asc_dvc->unit_not_ready = target_id;
+ sta = ERR;
+ AscWaitTixISRDone(asc_dvc, target_ix);
+ if (AscStopQueueExe(iop_base) == 1) {
+ if (AscRiscHaltedAbortTIX(asc_dvc, target_ix) == 1) {
+ AscCleanUpBusyQueue(iop_base);
+ AscStartQueueExe(iop_base);
+ AscWaitTixISRDone(asc_dvc, target_ix);
+ sta = TRUE;
+ scsiq = (ASC_SCSI_REQ_Q *) & scsiq_buf;
+ buf = (uchar *) & scsiq_buf;
+ for (i = 0; i < sizeof (ASC_SCSI_REQ_Q); i++) {
+ *buf++ = 0x00;
+ }
+ scsiq->r1.status = (uchar) QS_READY;
+ scsiq->r2.cdb_len = 6;
+ scsiq->r2.tag_code = M2_QTAG_MSG_SIMPLE;
+ scsiq->r1.target_id = target_id;
+ scsiq->r2.target_ix = ASC_TIDLUN_TO_IX(tid_no, 0);
+ scsiq->cdbptr = (uchar *) scsiq->cdb;
+ scsiq->r1.cntl = QC_NO_CALLBACK | QC_MSG_OUT | QC_URGENT;
+ AscWriteLramByte(asc_dvc->iop_base, ASCV_MSGOUT_BEG,
+ M1_BUS_DVC_RESET);
+ asc_dvc->unit_not_ready &= ~target_id;
+ asc_dvc->sdtr_done |= target_id;
+ if (AscExeScsiQueue(asc_dvc, (ASC_SCSI_Q *) scsiq)
+ == 1) {
+ asc_dvc->unit_not_ready = target_id;
+ DvcSleepMilliSecond(1000);
+ _AscWaitQDone(iop_base, (ASC_SCSI_Q *) scsiq);
+ if (AscStopQueueExe(iop_base) == 1) {
+ AscCleanUpDiscQueue(iop_base);
+ AscStartQueueExe(iop_base);
+ if (asc_dvc->pci_fix_asyn_xfer & target_id) {
+ AscSetRunChipSynRegAtID(iop_base, tid_no,
+ ASYN_SDTR_DATA_FIX_PCI_REV_AB);
+ }
+ AscWaitTixISRDone(asc_dvc, target_ix);
+ }
+ } else {
+ sta = 0;
+ }
+ asc_dvc->sdtr_done &= ~target_id;
+ } else {
+ sta = ERR;
+ AscStartQueueExe(iop_base);
+ }
+ }
+ asc_dvc->unit_not_ready = saved_unit_not_ready;
+ return (sta);
+}
+#endif /* version >= v1.3.89 */
+
+STATIC int
+AscResetSB(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ int sta;
+ int i;
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+ asc_dvc->unit_not_ready = 0xFF;
+ sta = TRUE;
+ AscWaitISRDone(asc_dvc);
+ AscStopQueueExe(iop_base);
+ asc_dvc->sdtr_done = 0;
+ AscResetChipAndScsiBus(asc_dvc);
+ DvcSleepMilliSecond((ulong) ((ushort) asc_dvc->scsi_reset_wait * 1000));
+ AscReInitLram(asc_dvc);
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ asc_dvc->cur_dvc_qng[i] = 0;
+ if (asc_dvc->pci_fix_asyn_xfer & (ASC_SCSI_BIT_ID_TYPE) (0x01 << i)) {
+ AscSetChipSynRegAtID(iop_base, i, ASYN_SDTR_DATA_FIX_PCI_REV_AB);
+ }
+ }
+ asc_dvc->err_code = 0;
+ AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR);
+ if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) {
+ sta = ERR;
+ }
+ if (AscStartChip(iop_base) == 0) {
+ sta = ERR;
+ }
+ AscStartQueueExe(iop_base);
+ asc_dvc->unit_not_ready = 0;
+ asc_dvc->queue_full_or_busy = 0;
+ return (sta);
+}
+
+STATIC int
+AscSetRunChipSynRegAtID(
+ PortAddr iop_base,
+ uchar tid_no,
+ uchar sdtr_data
+)
+{
+ int sta = FALSE;
+
+ if (AscHostReqRiscHalt(iop_base)) {
+ sta = AscSetChipSynRegAtID(iop_base, tid_no, sdtr_data);
+ AscStartChip(iop_base);
+ return (sta);
+ }
+ return (sta);
+}
+
+STATIC int
+AscSetChipSynRegAtID(
+ PortAddr iop_base,
+ uchar id,
+ uchar sdtr_data
+)
+{
+ ASC_SCSI_BIT_ID_TYPE org_id;
+ int i;
+ int sta = TRUE;
+
+ AscSetBank(iop_base, 1);
+ org_id = AscReadChipDvcID(iop_base);
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if (org_id == (0x01 << i))
+ break;
+ }
+ org_id = i;
+ AscWriteChipDvcID(iop_base, id);
+ if (AscReadChipDvcID(iop_base) == (0x01 << id)) {
+ AscSetBank(iop_base, 0);
+ AscSetChipSyn(iop_base, sdtr_data);
+ if (AscGetChipSyn(iop_base) != sdtr_data) {
+ sta = FALSE;
+ }
+ } else {
+ sta = FALSE;
+ }
+ AscSetBank(iop_base, 1);
+ AscWriteChipDvcID(iop_base, org_id);
+ AscSetBank(iop_base, 0);
+ return (sta);
+}
+
+STATIC int
+AscReInitLram(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ AscInitLram(asc_dvc);
+ AscInitQLinkVar(asc_dvc);
+ return (0);
+}
+
+STATIC ushort
+AscInitLram(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ uchar i;
+ ushort s_addr;
+ PortAddr iop_base;
+ ushort warn_code;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+ AscMemWordSetLram(iop_base, ASC_QADR_BEG, 0,
+ (ushort) (((int) (asc_dvc->max_total_qng + 2 + 1) * 64) >> 1)
+);
+ i = ASC_MIN_ACTIVE_QNO;
+ s_addr = ASC_QADR_BEG + ASC_QBLK_SIZE;
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_FWD),
+ (uchar) (i + 1));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_BWD),
+ (uchar) (asc_dvc->max_total_qng));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_QNO),
+ (uchar) i);
+ i++;
+ s_addr += ASC_QBLK_SIZE;
+ for (; i < asc_dvc->max_total_qng; i++, s_addr += ASC_QBLK_SIZE) {
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_FWD),
+ (uchar) (i + 1));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_BWD),
+ (uchar) (i - 1));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_QNO),
+ (uchar) i);
+ }
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_FWD),
+ (uchar) ASC_QLINK_END);
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_BWD),
+ (uchar) (asc_dvc->max_total_qng - 1));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_QNO),
+ (uchar) asc_dvc->max_total_qng);
+ i++;
+ s_addr += ASC_QBLK_SIZE;
+ for (; i <= (uchar) (asc_dvc->max_total_qng + 3);
+ i++, s_addr += ASC_QBLK_SIZE) {
+ AscWriteLramByte(iop_base,
+ (ushort) (s_addr + (ushort) ASC_SCSIQ_B_FWD), i);
+ AscWriteLramByte(iop_base,
+ (ushort) (s_addr + (ushort) ASC_SCSIQ_B_BWD), i);
+ AscWriteLramByte(iop_base,
+ (ushort) (s_addr + (ushort) ASC_SCSIQ_B_QNO), i);
+ }
+ return (warn_code);
+}
+
+STATIC ushort
+AscInitQLinkVar(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ PortAddr iop_base;
+ int i;
+ ushort lram_addr;
+
+ iop_base = asc_dvc->iop_base;
+ AscPutRiscVarFreeQHead(iop_base, 1);
+ AscPutRiscVarDoneQTail(iop_base, asc_dvc->max_total_qng);
+ AscPutVarFreeQHead(iop_base, 1);
+ AscPutVarDoneQTail(iop_base, asc_dvc->max_total_qng);
+ AscWriteLramByte(iop_base, ASCV_BUSY_QHEAD_B,
+ (uchar) ((int) asc_dvc->max_total_qng + 1));
+ AscWriteLramByte(iop_base, ASCV_DISC1_QHEAD_B,
+ (uchar) ((int) asc_dvc->max_total_qng + 2));
+ AscWriteLramByte(iop_base, (ushort) ASCV_TOTAL_READY_Q_B,
+ asc_dvc->max_total_qng);
+ AscWriteLramWord(iop_base, ASCV_ASCDVC_ERR_CODE_W, 0);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, 0);
+ AscWriteLramByte(iop_base, ASCV_SCSIBUSY_B, 0);
+ AscWriteLramByte(iop_base, ASCV_WTM_FLAG_B, 0);
+ AscPutQDoneInProgress(iop_base, 0);
+ lram_addr = ASC_QADR_BEG;
+ for (i = 0; i < 32; i++, lram_addr += 2) {
+ AscWriteLramWord(iop_base, lram_addr, 0);
+ }
+ return (0);
+}
+
+STATIC int
+AscSetLibErrorCode(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ushort err_code
+)
+{
+ if (asc_dvc->err_code == 0) {
+ asc_dvc->err_code = err_code;
+ AscWriteLramWord(asc_dvc->iop_base, ASCV_ASCDVC_ERR_CODE_W,
+ err_code);
+ }
+ return (err_code);
+}
+
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+STATIC int
+_AscWaitQDone(
+ PortAddr iop_base,
+ REG ASC_SCSI_Q * scsiq
+)
+{
+ ushort q_addr;
+ uchar q_status;
+ int count = 0;
+
+ while (scsiq->q1.q_no == 0) ;
+ q_addr = ASC_QNO_TO_QADDR(scsiq->q1.q_no);
+ do {
+ q_status = AscReadLramByte(iop_base, q_addr + ASC_SCSIQ_B_STATUS);
+ DvcSleepMilliSecond(100L);
+ if (count++ > 30) {
+ return (0);
+ }
+ } while ((q_status & QS_READY) != 0);
+ return (1);
+}
+#endif /* version >= v1.3.89 */
+
+STATIC uchar
+AscMsgOutSDTR(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ uchar sdtr_period,
+ uchar sdtr_offset
+)
+{
+ EXT_MSG sdtr_buf;
+ uchar sdtr_period_index;
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+ sdtr_buf.msg_type = MS_EXTEND;
+ sdtr_buf.msg_len = MS_SDTR_LEN;
+ sdtr_buf.msg_req = MS_SDTR_CODE;
+ sdtr_buf.xfer_period = sdtr_period;
+ sdtr_offset &= ASC_SYN_MAX_OFFSET;
+ sdtr_buf.req_ack_offset = sdtr_offset;
+ if ((sdtr_period_index =
+ AscGetSynPeriodIndex(asc_dvc, sdtr_period)) <=
+ asc_dvc->max_sdtr_index) {
+ AscMemWordCopyToLram(iop_base,
+ ASCV_MSGOUT_BEG,
+ (ushort *) & sdtr_buf,
+ (ushort) (sizeof (EXT_MSG) >> 1));
+ return ((sdtr_period_index << 4) | sdtr_offset);
+ } else {
+
+ sdtr_buf.req_ack_offset = 0;
+ AscMemWordCopyToLram(iop_base,
+ ASCV_MSGOUT_BEG,
+ (ushort *) & sdtr_buf,
+ (ushort) (sizeof (EXT_MSG) >> 1));
+ return (0);
+ }
+}
+
+STATIC uchar
+AscCalSDTRData(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ uchar sdtr_period,
+ uchar syn_offset
+)
+{
+ uchar byte;
+ uchar sdtr_period_ix;
+
+ sdtr_period_ix = AscGetSynPeriodIndex(asc_dvc, sdtr_period);
+ if (
+ (sdtr_period_ix > asc_dvc->max_sdtr_index)
+) {
+ return (0xFF);
+ }
+ byte = (sdtr_period_ix << 4) | (syn_offset & ASC_SYN_MAX_OFFSET);
+ return (byte);
+}
+
+STATIC void
+AscSetChipSDTR(
+ PortAddr iop_base,
+ uchar sdtr_data,
+ uchar tid_no
+)
+{
+ AscSetChipSynRegAtID(iop_base, tid_no, sdtr_data);
+ AscPutMCodeSDTRDoneAtID(iop_base, tid_no, sdtr_data);
+ return;
+}
+
+STATIC uchar
+AscGetSynPeriodIndex(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ruchar syn_time
+)
+{
+ ruchar *period_table;
+ int max_index;
+ int min_index;
+ int i;
+
+ period_table = asc_dvc->sdtr_period_tbl;
+ max_index = (int) asc_dvc->max_sdtr_index;
+ min_index = (int)asc_dvc->host_init_sdtr_index ;
+ if ((syn_time <= period_table[max_index])) {
+ for (i = min_index; i < (max_index - 1); i++) {
+ if (syn_time <= period_table[i]) {
+ return ((uchar) i);
+ }
+ }
+ return ((uchar) max_index);
+ } else {
+ return ((uchar) (max_index + 1));
+ }
+}
+
+STATIC uchar
+AscAllocFreeQueue(
+ PortAddr iop_base,
+ uchar free_q_head
+)
+{
+ ushort q_addr;
+ uchar next_qp;
+ uchar q_status;
+
+ q_addr = ASC_QNO_TO_QADDR(free_q_head);
+ q_status = (uchar) AscReadLramByte(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_B_STATUS));
+ next_qp = AscReadLramByte(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_B_FWD));
+ if (((q_status & QS_READY) == 0) && (next_qp != ASC_QLINK_END)) {
+ return (next_qp);
+ }
+ return (ASC_QLINK_END);
+}
+
+STATIC uchar
+AscAllocMultipleFreeQueue(
+ PortAddr iop_base,
+ uchar free_q_head,
+ uchar n_free_q
+)
+{
+ uchar i;
+
+ for (i = 0; i < n_free_q; i++) {
+ if ((free_q_head = AscAllocFreeQueue(iop_base, free_q_head))
+ == ASC_QLINK_END) {
+ return (ASC_QLINK_END);
+ }
+ }
+ return (free_q_head);
+}
+
+STATIC int
+AscRiscHaltedAbortSRB(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ulong srb_ptr
+)
+{
+ PortAddr iop_base;
+ ushort q_addr;
+ uchar q_no;
+ ASC_QDONE_INFO scsiq_buf;
+ ASC_QDONE_INFO *scsiq;
+ ASC_ISR_CALLBACK asc_isr_callback;
+ int last_int_level;
+
+ iop_base = asc_dvc->iop_base;
+ asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback;
+ last_int_level = DvcEnterCritical();
+ scsiq = (ASC_QDONE_INFO *) & scsiq_buf;
+ for (q_no = ASC_MIN_ACTIVE_QNO; q_no <= asc_dvc->max_total_qng;
+ q_no++) {
+ q_addr = ASC_QNO_TO_QADDR(q_no);
+ scsiq->d2.srb_ptr = AscReadLramDWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_D_SRBPTR));
+ if (scsiq->d2.srb_ptr == srb_ptr) {
+ _AscCopyLramScsiDoneQ(iop_base, q_addr, scsiq, asc_dvc->max_dma_count);
+ if (((scsiq->q_status & QS_READY) != 0)
+ && ((scsiq->q_status & QS_ABORTED) == 0)
+ && ((scsiq->cntl & QCSG_SG_XFER_LIST) == 0)) {
+ scsiq->q_status |= QS_ABORTED;
+ scsiq->d3.done_stat = QD_ABORTED_BY_HOST;
+ AscWriteLramDWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_D_SRBPTR),
+ 0L);
+ AscWriteLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ scsiq->q_status);
+ (*asc_isr_callback) (asc_dvc, scsiq);
+ return (1);
+ }
+ }
+ }
+ DvcLeaveCritical(last_int_level);
+ return (0);
+}
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+STATIC int
+AscRiscHaltedAbortTIX(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ uchar target_ix
+)
+{
+ PortAddr iop_base;
+ ushort q_addr;
+ uchar q_no;
+ ASC_QDONE_INFO scsiq_buf;
+ ASC_QDONE_INFO *scsiq;
+ ASC_ISR_CALLBACK asc_isr_callback;
+ int last_int_level;
+
+ iop_base = asc_dvc->iop_base;
+ asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback;
+ last_int_level = DvcEnterCritical();
+ scsiq = (ASC_QDONE_INFO *) & scsiq_buf;
+ for (q_no = ASC_MIN_ACTIVE_QNO; q_no <= asc_dvc->max_total_qng;
+ q_no++) {
+ q_addr = ASC_QNO_TO_QADDR(q_no);
+ _AscCopyLramScsiDoneQ(iop_base, q_addr, scsiq, asc_dvc->max_dma_count);
+ if (((scsiq->q_status & QS_READY) != 0) &&
+ ((scsiq->q_status & QS_ABORTED) == 0) &&
+ ((scsiq->cntl & QCSG_SG_XFER_LIST) == 0)) {
+ if (scsiq->d2.target_ix == target_ix) {
+ scsiq->q_status |= QS_ABORTED;
+ scsiq->d3.done_stat = QD_ABORTED_BY_HOST;
+ AscWriteLramDWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_D_SRBPTR),
+ 0L);
+ AscWriteLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ scsiq->q_status);
+ (*asc_isr_callback) (asc_dvc, scsiq);
+ }
+ }
+ }
+ DvcLeaveCritical(last_int_level);
+ return (1);
+}
+#endif /* version >= v1.3.89 */
+
+STATIC int
+AscHostReqRiscHalt(
+ PortAddr iop_base
+)
+{
+ int count = 0;
+ int sta = 0;
+ uchar saved_stop_code;
+
+ if (AscIsChipHalted(iop_base))
+ return (1);
+ saved_stop_code = AscReadLramByte(iop_base, ASCV_STOP_CODE_B);
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
+ ASC_STOP_HOST_REQ_RISC_HALT | ASC_STOP_REQ_RISC_STOP
+);
+ do {
+ if (AscIsChipHalted(iop_base)) {
+ sta = 1;
+ break;
+ }
+ DvcSleepMilliSecond(100);
+ } while (count++ < 20);
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, saved_stop_code);
+ return (sta);
+}
+
+STATIC int
+AscStopQueueExe(
+ PortAddr iop_base
+)
+{
+ int count = 0;
+
+ if (AscReadLramByte(iop_base, ASCV_STOP_CODE_B) == 0) {
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
+ ASC_STOP_REQ_RISC_STOP);
+ do {
+ if (
+ AscReadLramByte(iop_base, ASCV_STOP_CODE_B) &
+ ASC_STOP_ACK_RISC_STOP) {
+ return (1);
+ }
+ DvcSleepMilliSecond(100);
+ } while (count++ < 20);
+ }
+ return (0);
+}
+
+STATIC int
+AscStartQueueExe(
+ PortAddr iop_base
+)
+{
+ if (AscReadLramByte(iop_base, ASCV_STOP_CODE_B) != 0) {
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, 0);
+ }
+ return (1);
+}
+
+STATIC int
+AscCleanUpBusyQueue(
+ PortAddr iop_base
+)
+{
+ int count;
+ uchar stop_code;
+
+ count = 0;
+ if (AscReadLramByte(iop_base, ASCV_STOP_CODE_B) != 0) {
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
+ ASC_STOP_CLEAN_UP_BUSY_Q);
+ do {
+ stop_code = AscReadLramByte(iop_base, ASCV_STOP_CODE_B);
+ if ((stop_code & ASC_STOP_CLEAN_UP_BUSY_Q) == 0)
+ break;
+ DvcSleepMilliSecond(100);
+ } while (count++ < 20);
+ }
+ return (1);
+}
+
+#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(1,3,89)
+STATIC int
+AscCleanUpDiscQueue(
+ PortAddr iop_base
+)
+{
+ int count;
+ uchar stop_code;
+
+ count = 0;
+ if (AscReadLramByte(iop_base, ASCV_STOP_CODE_B) != 0) {
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
+ ASC_STOP_CLEAN_UP_DISC_Q);
+ do {
+ stop_code = AscReadLramByte(iop_base, ASCV_STOP_CODE_B);
+ if ((stop_code & ASC_STOP_CLEAN_UP_DISC_Q) == 0)
+ break;
+ DvcSleepMilliSecond(100);
+ } while (count++ < 20);
+ }
+ return (1);
+}
+#endif /* version >= v1.3.89 */
+
+STATIC int
+AscWaitTixISRDone(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ uchar target_ix
+)
+{
+ uchar cur_req;
+ uchar tid_no;
+ int i = 0;
+
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ while (i++ < 10) {
+ if ((cur_req = asc_dvc->cur_dvc_qng[tid_no]) == 0) {
+ break;
+ }
+ DvcSleepMilliSecond(1000L);
+ if (asc_dvc->cur_dvc_qng[tid_no] == cur_req) {
+ break;
+ }
+ }
+ return (1);
+}
+
+STATIC int
+AscWaitISRDone(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ int tid;
+
+ for (tid = 0; tid <= ASC_MAX_TID; tid++) {
+ AscWaitTixISRDone(asc_dvc, ASC_TID_TO_TIX(tid));
+ }
+ return (1);
+}
+
+STATIC ulong
+AscGetOnePhyAddr(
+ REG ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ uchar * buf_addr,
+ ulong buf_size
+)
+{
+ ASC_MIN_SG_HEAD sg_head;
+
+ sg_head.entry_cnt = ASC_MIN_SG_LIST;
+ if (DvcGetSGList(asc_dvc, (uchar *) buf_addr,
+ buf_size, (ASC_SG_HEAD *) & sg_head) != buf_size) {
+ return (0L);
+ }
+ if (sg_head.entry_cnt > 1) {
+ return (0L);
+ }
+ return (sg_head.sg_list[0].addr);
+}
+
+STATIC void
+DvcDelayMicroSecond(ADV_DVC_VAR *asc_dvc, ushort micro_sec)
+{
+ udelay(micro_sec);
+}
+
+STATIC void
+DvcDelayNanoSecond(ASC_DVC_VAR asc_ptr_type * asc_dvc, ulong nano_sec)
+{
+ udelay((nano_sec + 999)/1000);
+}
+
+ASC_INITFUNC(
+STATIC ulong
+AscGetEisaProductID(
+ PortAddr iop_base
+)
+)
+{
+ PortAddr eisa_iop;
+ ushort product_id_high, product_id_low;
+ ulong product_id;
+
+ eisa_iop = ASC_GET_EISA_SLOT(iop_base) | ASC_EISA_PID_IOP_MASK;
+ product_id_low = inpw(eisa_iop);
+ product_id_high = inpw(eisa_iop + 2);
+ product_id = ((ulong) product_id_high << 16) | (ulong) product_id_low;
+ return (product_id);
+}
+
+ASC_INITFUNC(
+STATIC PortAddr
+AscSearchIOPortAddrEISA(
+ PortAddr iop_base
+)
+)
+{
+ ulong eisa_product_id;
+
+ if (iop_base == 0) {
+ iop_base = ASC_EISA_MIN_IOP_ADDR;
+ } else {
+ if (iop_base == ASC_EISA_MAX_IOP_ADDR)
+ return (0);
+ if ((iop_base & 0x0050) == 0x0050) {
+ iop_base += ASC_EISA_BIG_IOP_GAP;
+ } else {
+ iop_base += ASC_EISA_SMALL_IOP_GAP;
+ }
+ }
+ while (iop_base <= ASC_EISA_MAX_IOP_ADDR) {
+ eisa_product_id = AscGetEisaProductID(iop_base);
+ if ((eisa_product_id == ASC_EISA_ID_740) ||
+ (eisa_product_id == ASC_EISA_ID_750)) {
+ if (AscFindSignature(iop_base)) {
+ inpw(iop_base + 4);
+ return (iop_base);
+ }
+ }
+ if (iop_base == ASC_EISA_MAX_IOP_ADDR)
+ return (0);
+ if ((iop_base & 0x0050) == 0x0050) {
+ iop_base += ASC_EISA_BIG_IOP_GAP;
+ } else {
+ iop_base += ASC_EISA_SMALL_IOP_GAP;
+ }
+ }
+ return (0);
+}
+
+STATIC int
+AscStartChip(
+ PortAddr iop_base
+)
+{
+ AscSetChipControl(iop_base, 0);
+ if ((AscGetChipStatus(iop_base) & CSW_HALTED) != 0) {
+ return (0);
+ }
+ return (1);
+}
+
+STATIC int
+AscStopChip(
+ PortAddr iop_base
+)
+{
+ uchar cc_val;
+
+ cc_val = AscGetChipControl(iop_base) & (~(CC_SINGLE_STEP | CC_TEST | CC_DIAG));
+ AscSetChipControl(iop_base, (uchar) (cc_val | CC_HALT));
+ AscSetChipIH(iop_base, INS_HALT);
+ AscSetChipIH(iop_base, INS_RFLAG_WTM);
+ if ((AscGetChipStatus(iop_base) & CSW_HALTED) == 0) {
+ return (0);
+ }
+ return (1);
+}
+
+STATIC int
+AscIsChipHalted(
+ PortAddr iop_base
+)
+{
+ if ((AscGetChipStatus(iop_base) & CSW_HALTED) != 0) {
+ if ((AscGetChipControl(iop_base) & CC_HALT) != 0) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+STATIC void
+AscSetChipIH(
+ PortAddr iop_base,
+ ushort ins_code
+)
+{
+ AscSetBank(iop_base, 1);
+ AscWriteChipIH(iop_base, ins_code);
+ AscSetBank(iop_base, 0);
+ return;
+}
+
+STATIC void
+AscAckInterrupt(
+ PortAddr iop_base
+)
+{
+ uchar host_flag;
+ uchar risc_flag;
+ ushort loop;
+
+ loop = 0;
+ do {
+ risc_flag = AscReadLramByte(iop_base, ASCV_RISC_FLAG_B);
+ if (loop++ > 0x7FFF) {
+ break;
+ }
+ } while ((risc_flag & ASC_RISC_FLAG_GEN_INT) != 0);
+ host_flag = AscReadLramByte(iop_base, ASCV_HOST_FLAG_B) & (~ASC_HOST_FLAG_ACK_INT);
+ AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B,
+ (uchar) (host_flag | ASC_HOST_FLAG_ACK_INT));
+ AscSetChipStatus(iop_base, CIW_INT_ACK);
+ loop = 0;
+ while (AscGetChipStatus(iop_base) & CSW_INT_PENDING) {
+ AscSetChipStatus(iop_base, CIW_INT_ACK);
+ if (loop++ > 3) {
+ break;
+ }
+ }
+ AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B, host_flag);
+ return;
+}
+
+STATIC void
+AscDisableInterrupt(
+ PortAddr iop_base
+)
+{
+ ushort cfg;
+
+ cfg = AscGetChipCfgLsw(iop_base);
+ AscSetChipCfgLsw(iop_base, cfg & (~ASC_CFG0_HOST_INT_ON));
+ return;
+}
+
+STATIC void
+AscEnableInterrupt(
+ PortAddr iop_base
+)
+{
+ ushort cfg;
+
+ cfg = AscGetChipCfgLsw(iop_base);
+ AscSetChipCfgLsw(iop_base, cfg | ASC_CFG0_HOST_INT_ON);
+ return;
+}
+
+
+
+STATIC void
+AscSetBank(
+ PortAddr iop_base,
+ uchar bank
+)
+{
+ uchar val;
+
+ val = AscGetChipControl(iop_base) &
+ (~(CC_SINGLE_STEP | CC_TEST | CC_DIAG | CC_SCSI_RESET | CC_CHIP_RESET));
+ if (bank == 1) {
+ val |= CC_BANK_ONE;
+ } else if (bank == 2) {
+ val |= CC_DIAG | CC_BANK_ONE;
+ } else {
+ val &= ~CC_BANK_ONE;
+ }
+ AscSetChipControl(iop_base, val);
+ return;
+}
+
+STATIC int
+AscResetChipAndScsiBus(
+ ASC_DVC_VAR *asc_dvc
+)
+{
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+ while (AscGetChipStatus(iop_base) & CSW_SCSI_RESET_ACTIVE) ;
+ AscStopChip(iop_base);
+ AscSetChipControl(iop_base, CC_CHIP_RESET | CC_SCSI_RESET | CC_HALT);
+ DvcDelayNanoSecond(asc_dvc, 60000);
+ AscSetChipIH(iop_base, INS_RFLAG_WTM);
+ AscSetChipIH(iop_base, INS_HALT);
+ AscSetChipControl(iop_base, CC_CHIP_RESET | CC_HALT);
+ AscSetChipControl(iop_base, CC_HALT);
+ DvcSleepMilliSecond(200);
+ AscSetChipStatus(iop_base, CIW_CLR_SCSI_RESET_INT);
+ AscSetChipStatus(iop_base, 0);
+ return (AscIsChipHalted(iop_base));
+}
+
+ASC_INITFUNC(
+STATIC ulong
+AscGetMaxDmaCount(
+ ushort bus_type
+)
+)
+{
+ if (bus_type & ASC_IS_ISA)
+ return (ASC_MAX_ISA_DMA_COUNT);
+ else if (bus_type & (ASC_IS_EISA | ASC_IS_VL))
+ return (ASC_MAX_VL_DMA_COUNT);
+ return (ASC_MAX_PCI_DMA_COUNT);
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscGetIsaDmaChannel(
+ PortAddr iop_base
+)
+)
+{
+ ushort channel;
+
+ channel = AscGetChipCfgLsw(iop_base) & 0x0003;
+ if (channel == 0x03)
+ return (0);
+ else if (channel == 0x00)
+ return (7);
+ return (channel + 4);
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscSetIsaDmaChannel(
+ PortAddr iop_base,
+ ushort dma_channel
+)
+)
+{
+ ushort cfg_lsw;
+ uchar value;
+
+ if ((dma_channel >= 5) && (dma_channel <= 7)) {
+ if (dma_channel == 7)
+ value = 0x00;
+ else
+ value = dma_channel - 4;
+ cfg_lsw = AscGetChipCfgLsw(iop_base) & 0xFFFC;
+ cfg_lsw |= value;
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ return (AscGetIsaDmaChannel(iop_base));
+ }
+ return (0);
+}
+
+ASC_INITFUNC(
+STATIC uchar
+AscSetIsaDmaSpeed(
+ PortAddr iop_base,
+ uchar speed_value
+)
+)
+{
+ speed_value &= 0x07;
+ AscSetBank(iop_base, 1);
+ AscWriteChipDmaSpeed(iop_base, speed_value);
+ AscSetBank(iop_base, 0);
+ return (AscGetIsaDmaSpeed(iop_base));
+}
+
+ASC_INITFUNC(
+STATIC uchar
+AscGetIsaDmaSpeed(
+ PortAddr iop_base
+)
+)
+{
+ uchar speed_value;
+
+ AscSetBank(iop_base, 1);
+ speed_value = AscReadChipDmaSpeed(iop_base);
+ speed_value &= 0x07;
+ AscSetBank(iop_base, 0);
+ return (speed_value);
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscReadPCIConfigWord(
+ ASC_DVC_VAR asc_ptr_type *asc_dvc,
+ ushort pci_config_offset)
+)
+{
+ uchar lsb, msb;
+
+ lsb = DvcReadPCIConfigByte(asc_dvc, pci_config_offset);
+ msb = DvcReadPCIConfigByte(asc_dvc, pci_config_offset + 1);
+ return ((ushort) ((msb << 8) | lsb));
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscInitGetConfig(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+)
+{
+ ushort warn_code;
+ PortAddr iop_base;
+ ushort PCIDeviceID;
+ ushort PCIVendorID;
+ uchar PCIRevisionID;
+ uchar prevCmdRegBits;
+
+ warn_code = 0;
+ iop_base = asc_dvc->iop_base;
+ asc_dvc->init_state = ASC_INIT_STATE_BEG_GET_CFG;
+ if (asc_dvc->err_code != 0) {
+ return (UW_ERR);
+ }
+ if (asc_dvc->bus_type == ASC_IS_PCI) {
+ PCIVendorID = AscReadPCIConfigWord(asc_dvc,
+ AscPCIConfigVendorIDRegister);
+
+ PCIDeviceID = AscReadPCIConfigWord(asc_dvc,
+ AscPCIConfigDeviceIDRegister);
+
+ PCIRevisionID = DvcReadPCIConfigByte(asc_dvc,
+ AscPCIConfigRevisionIDRegister);
+
+ if (PCIVendorID != ASC_PCI_VENDORID) {
+ warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
+ }
+ prevCmdRegBits = DvcReadPCIConfigByte(asc_dvc,
+ AscPCIConfigCommandRegister);
+
+ if ((prevCmdRegBits & AscPCICmdRegBits_IOMemBusMaster) !=
+ AscPCICmdRegBits_IOMemBusMaster) {
+ DvcWritePCIConfigByte(asc_dvc,
+ AscPCIConfigCommandRegister,
+ (prevCmdRegBits |
+ AscPCICmdRegBits_IOMemBusMaster));
+
+ if ((DvcReadPCIConfigByte(asc_dvc,
+ AscPCIConfigCommandRegister)
+ & AscPCICmdRegBits_IOMemBusMaster)
+ != AscPCICmdRegBits_IOMemBusMaster) {
+ warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
+ }
+ }
+ if ((PCIDeviceID == ASC_PCI_DEVICEID_1200A) ||
+ (PCIDeviceID == ASC_PCI_DEVICEID_1200B)) {
+ DvcWritePCIConfigByte(asc_dvc,
+ AscPCIConfigLatencyTimer, 0x00);
+ if (DvcReadPCIConfigByte(asc_dvc, AscPCIConfigLatencyTimer)
+ != 0x00) {
+ warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
+ }
+ } else if (PCIDeviceID == ASC_PCI_DEVICEID_ULTRA) {
+ if (DvcReadPCIConfigByte(asc_dvc,
+ AscPCIConfigLatencyTimer) < 0x20) {
+ DvcWritePCIConfigByte(asc_dvc,
+ AscPCIConfigLatencyTimer, 0x20);
+
+ if (DvcReadPCIConfigByte(asc_dvc,
+ AscPCIConfigLatencyTimer) < 0x20) {
+ warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
+ }
+ }
+ }
+ }
+
+ if (AscFindSignature(iop_base)) {
+ warn_code |= AscInitAscDvcVar(asc_dvc);
+ warn_code |= AscInitFromEEP(asc_dvc);
+ asc_dvc->init_state |= ASC_INIT_STATE_END_GET_CFG;
+ if (asc_dvc->scsi_reset_wait > ASC_MAX_SCSI_RESET_WAIT) {
+ asc_dvc->scsi_reset_wait = ASC_MAX_SCSI_RESET_WAIT;
+ }
+ } else {
+ asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
+ }
+ return(warn_code);
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscInitSetConfig(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+)
+{
+ ushort warn_code = 0;
+
+ asc_dvc->init_state |= ASC_INIT_STATE_BEG_SET_CFG;
+ if (asc_dvc->err_code != 0)
+ return (UW_ERR);
+ if (AscFindSignature(asc_dvc->iop_base)) {
+ warn_code |= AscInitFromAscDvcVar(asc_dvc);
+ asc_dvc->init_state |= ASC_INIT_STATE_END_SET_CFG;
+ } else {
+ asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
+ }
+ return (warn_code);
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscInitFromAscDvcVar(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+)
+{
+ PortAddr iop_base;
+ ushort cfg_msw;
+ ushort warn_code;
+ ushort pci_device_id;
+
+ iop_base = asc_dvc->iop_base;
+ pci_device_id = asc_dvc->cfg->pci_device_id;
+ warn_code = 0;
+ cfg_msw = AscGetChipCfgMsw(iop_base);
+ if ((cfg_msw & ASC_CFG_MSW_CLR_MASK) != 0) {
+ cfg_msw &= (~(ASC_CFG_MSW_CLR_MASK));
+ warn_code |= ASC_WARN_CFG_MSW_RECOVER;
+ AscSetChipCfgMsw(iop_base, cfg_msw);
+ }
+ if ((asc_dvc->cfg->cmd_qng_enabled & asc_dvc->cfg->disc_enable) !=
+ asc_dvc->cfg->cmd_qng_enabled) {
+ asc_dvc->cfg->disc_enable = asc_dvc->cfg->cmd_qng_enabled;
+ warn_code |= ASC_WARN_CMD_QNG_CONFLICT;
+ }
+ if (AscGetChipStatus(iop_base) & CSW_AUTO_CONFIG) {
+ warn_code |= ASC_WARN_AUTO_CONFIG;
+ }
+ if ((asc_dvc->bus_type & (ASC_IS_ISA | ASC_IS_VL)) != 0) {
+ if (AscSetChipIRQ(iop_base, asc_dvc->irq_no, asc_dvc->bus_type)
+ != asc_dvc->irq_no) {
+ asc_dvc->err_code |= ASC_IERR_SET_IRQ_NO;
+ }
+ }
+ if (asc_dvc->bus_type & ASC_IS_PCI) {
+ cfg_msw &= 0xFFC0;
+ AscSetChipCfgMsw(iop_base, cfg_msw);
+ if ((asc_dvc->bus_type & ASC_IS_PCI_ULTRA) == ASC_IS_PCI_ULTRA) {
+ } else {
+ if ((pci_device_id == ASC_PCI_DEVICE_ID_REV_A) ||
+ (pci_device_id == ASC_PCI_DEVICE_ID_REV_B)) {
+ asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_IF_NOT_DWB;
+ asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_ASYN_USE_SYN;
+ }
+ }
+ } else if (asc_dvc->bus_type == ASC_IS_ISAPNP) {
+ if (AscGetChipVersion(iop_base, asc_dvc->bus_type)
+ == ASC_CHIP_VER_ASYN_BUG) {
+ asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_ASYN_USE_SYN;
+ }
+ }
+ if (AscSetChipScsiID(iop_base, asc_dvc->cfg->chip_scsi_id) !=
+ asc_dvc->cfg->chip_scsi_id) {
+ asc_dvc->err_code |= ASC_IERR_SET_SCSI_ID;
+ }
+ if (asc_dvc->bus_type & ASC_IS_ISA) {
+ AscSetIsaDmaChannel(iop_base, asc_dvc->cfg->isa_dma_channel);
+ AscSetIsaDmaSpeed(iop_base, asc_dvc->cfg->isa_dma_speed);
+ }
+ return (warn_code);
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscInitAsc1000Driver(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+)
+{
+ ushort warn_code;
+ PortAddr iop_base;
+ extern ushort _asc_mcode_size;
+ extern ulong _asc_mcode_chksum;
+ extern uchar _asc_mcode_buf[];
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+ if ((asc_dvc->dvc_cntl & ASC_CNTL_RESET_SCSI) &&
+ !(asc_dvc->init_state & ASC_INIT_RESET_SCSI_DONE)) {
+ AscResetChipAndScsiBus(asc_dvc);
+ DvcSleepMilliSecond((ulong) ((ushort) asc_dvc->scsi_reset_wait * 1000));
+ }
+ asc_dvc->init_state |= ASC_INIT_STATE_BEG_LOAD_MC;
+ if (asc_dvc->err_code != 0)
+ return (UW_ERR);
+ if (!AscFindSignature(asc_dvc->iop_base)) {
+ asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
+ return (warn_code);
+ }
+ AscDisableInterrupt(iop_base);
+ warn_code |= AscInitLram(asc_dvc);
+ if (asc_dvc->err_code != 0)
+ return (UW_ERR);
+ if (AscLoadMicroCode(iop_base, 0, (ushort *) _asc_mcode_buf,
+ _asc_mcode_size) != _asc_mcode_chksum) {
+ asc_dvc->err_code |= ASC_IERR_MCODE_CHKSUM;
+ return (warn_code);
+ }
+ warn_code |= AscInitMicroCodeVar(asc_dvc);
+ asc_dvc->init_state |= ASC_INIT_STATE_END_LOAD_MC;
+ AscEnableInterrupt(iop_base);
+ return (warn_code);
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscInitAscDvcVar(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+)
+{
+ int i;
+ PortAddr iop_base;
+ ushort warn_code;
+ uchar chip_version;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+ asc_dvc->err_code = 0;
+ if ((asc_dvc->bus_type &
+ (ASC_IS_ISA | ASC_IS_PCI | ASC_IS_EISA | ASC_IS_VL)) == 0) {
+ asc_dvc->err_code |= ASC_IERR_NO_BUS_TYPE;
+ }
+ AscSetChipControl(iop_base, CC_HALT);
+ AscSetChipStatus(iop_base, 0);
+ asc_dvc->bug_fix_cntl = 0;
+ asc_dvc->pci_fix_asyn_xfer = 0;
+ asc_dvc->pci_fix_asyn_xfer_always = 0;
+ asc_dvc->init_state = 0;
+ asc_dvc->sdtr_done = 0;
+ asc_dvc->cur_total_qng = 0;
+ asc_dvc->is_in_int = 0;
+ asc_dvc->in_critical_cnt = 0;
+ asc_dvc->last_q_shortage = 0;
+ asc_dvc->use_tagged_qng = 0;
+ asc_dvc->no_scam = 0;
+ asc_dvc->unit_not_ready = 0;
+ asc_dvc->queue_full_or_busy = 0;
+ asc_dvc->redo_scam = 0 ;
+ asc_dvc->res2 = 0 ;
+ asc_dvc->host_init_sdtr_index = 0 ;
+ asc_dvc->res7 = 0 ;
+ asc_dvc->res8 = 0 ;
+ asc_dvc->cfg->can_tagged_qng = 0 ;
+ asc_dvc->cfg->cmd_qng_enabled = 0;
+ asc_dvc->dvc_cntl = ASC_DEF_DVC_CNTL;
+ asc_dvc->init_sdtr = 0;
+ asc_dvc->max_total_qng = ASC_DEF_MAX_TOTAL_QNG;
+ asc_dvc->scsi_reset_wait = 3;
+ asc_dvc->start_motor = ASC_SCSI_WIDTH_BIT_SET;
+ asc_dvc->max_dma_count = AscGetMaxDmaCount(asc_dvc->bus_type);
+ asc_dvc->cfg->sdtr_enable = ASC_SCSI_WIDTH_BIT_SET;
+ asc_dvc->cfg->disc_enable = ASC_SCSI_WIDTH_BIT_SET;
+ asc_dvc->cfg->chip_scsi_id = ASC_DEF_CHIP_SCSI_ID;
+ asc_dvc->cfg->lib_serial_no = ASC_LIB_SERIAL_NUMBER;
+ asc_dvc->cfg->lib_version = (ASC_LIB_VERSION_MAJOR << 8) |
+ ASC_LIB_VERSION_MINOR;
+ chip_version = AscGetChipVersion(iop_base, asc_dvc->bus_type);
+ asc_dvc->cfg->chip_version = chip_version;
+ asc_dvc->sdtr_period_tbl[0] = SYN_XFER_NS_0;
+ asc_dvc->sdtr_period_tbl[1] = SYN_XFER_NS_1;
+ asc_dvc->sdtr_period_tbl[2] = SYN_XFER_NS_2;
+ asc_dvc->sdtr_period_tbl[3] = SYN_XFER_NS_3;
+ asc_dvc->sdtr_period_tbl[4] = SYN_XFER_NS_4;
+ asc_dvc->sdtr_period_tbl[5] = SYN_XFER_NS_5;
+ asc_dvc->sdtr_period_tbl[6] = SYN_XFER_NS_6;
+ asc_dvc->sdtr_period_tbl[7] = SYN_XFER_NS_7;
+ asc_dvc->max_sdtr_index = 7;
+ if ((asc_dvc->bus_type & ASC_IS_PCI) &&
+ (chip_version >= ASC_CHIP_VER_PCI_ULTRA_3150)) {
+ asc_dvc->bus_type = ASC_IS_PCI_ULTRA;
+ asc_dvc->sdtr_period_tbl[0] = SYN_ULTRA_XFER_NS_0;
+ asc_dvc->sdtr_period_tbl[1] = SYN_ULTRA_XFER_NS_1;
+ asc_dvc->sdtr_period_tbl[2] = SYN_ULTRA_XFER_NS_2;
+ asc_dvc->sdtr_period_tbl[3] = SYN_ULTRA_XFER_NS_3;
+ asc_dvc->sdtr_period_tbl[4] = SYN_ULTRA_XFER_NS_4;
+ asc_dvc->sdtr_period_tbl[5] = SYN_ULTRA_XFER_NS_5;
+ asc_dvc->sdtr_period_tbl[6] = SYN_ULTRA_XFER_NS_6;
+ asc_dvc->sdtr_period_tbl[7] = SYN_ULTRA_XFER_NS_7;
+ asc_dvc->sdtr_period_tbl[8] = SYN_ULTRA_XFER_NS_8;
+ asc_dvc->sdtr_period_tbl[9] = SYN_ULTRA_XFER_NS_9;
+ asc_dvc->sdtr_period_tbl[10] = SYN_ULTRA_XFER_NS_10;
+ asc_dvc->sdtr_period_tbl[11] = SYN_ULTRA_XFER_NS_11;
+ asc_dvc->sdtr_period_tbl[12] = SYN_ULTRA_XFER_NS_12;
+ asc_dvc->sdtr_period_tbl[13] = SYN_ULTRA_XFER_NS_13;
+ asc_dvc->sdtr_period_tbl[14] = SYN_ULTRA_XFER_NS_14;
+ asc_dvc->sdtr_period_tbl[15] = SYN_ULTRA_XFER_NS_15;
+ asc_dvc->max_sdtr_index = 15;
+ if (chip_version == ASC_CHIP_VER_PCI_ULTRA_3150)
+ {
+ AscSetExtraControl(iop_base,
+ (SEC_ACTIVE_NEGATE | SEC_SLEW_RATE));
+ } else if (chip_version >= ASC_CHIP_VER_PCI_ULTRA_3050) {
+ AscSetExtraControl(iop_base,
+ (SEC_ACTIVE_NEGATE | SEC_ENABLE_FILTER));
+ }
+ }
+ if (asc_dvc->bus_type == ASC_IS_PCI) {
+ AscSetExtraControl(iop_base, (SEC_ACTIVE_NEGATE | SEC_SLEW_RATE));
+ }
+
+ asc_dvc->cfg->isa_dma_speed = ASC_DEF_ISA_DMA_SPEED;
+ if (AscGetChipBusType(iop_base) == ASC_IS_ISAPNP) {
+ AscSetChipIFC(iop_base, IFC_INIT_DEFAULT);
+ asc_dvc->bus_type = ASC_IS_ISAPNP;
+ }
+ if ((asc_dvc->bus_type & ASC_IS_ISA) != 0) {
+ asc_dvc->cfg->isa_dma_channel = (uchar) AscGetIsaDmaChannel(iop_base);
+ }
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ asc_dvc->cur_dvc_qng[i] = 0;
+ asc_dvc->max_dvc_qng[i] = ASC_MAX_SCSI1_QNG;
+ asc_dvc->scsiq_busy_head[i] = (ASC_SCSI_Q *) 0L;
+ asc_dvc->scsiq_busy_tail[i] = (ASC_SCSI_Q *) 0L;
+ asc_dvc->cfg->max_tag_qng[i] = ASC_MAX_INRAM_TAG_QNG;
+ }
+ return (warn_code);
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscInitFromEEP(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+)
+{
+ ASCEEP_CONFIG eep_config_buf;
+ ASCEEP_CONFIG *eep_config;
+ PortAddr iop_base;
+ ushort chksum;
+ ushort warn_code;
+ ushort cfg_msw, cfg_lsw;
+ int i;
+ int write_eep = 0;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0x00FE);
+ AscStopQueueExe(iop_base);
+ if ((AscStopChip(iop_base) == FALSE) ||
+ (AscGetChipScsiCtrl(iop_base) != 0)) {
+ asc_dvc->init_state |= ASC_INIT_RESET_SCSI_DONE;
+ AscResetChipAndScsiBus(asc_dvc);
+ DvcSleepMilliSecond((ulong) ((ushort) asc_dvc->scsi_reset_wait * 1000));
+ }
+ if (AscIsChipHalted(iop_base) == FALSE) {
+ asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
+ return (warn_code);
+ }
+ AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR);
+ if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) {
+ asc_dvc->err_code |= ASC_IERR_SET_PC_ADDR;
+ return (warn_code);
+ }
+ eep_config = (ASCEEP_CONFIG *) & eep_config_buf;
+ cfg_msw = AscGetChipCfgMsw(iop_base);
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+ if ((cfg_msw & ASC_CFG_MSW_CLR_MASK) != 0) {
+ cfg_msw &= (~(ASC_CFG_MSW_CLR_MASK));
+ warn_code |= ASC_WARN_CFG_MSW_RECOVER;
+ AscSetChipCfgMsw(iop_base, cfg_msw);
+ }
+ chksum = AscGetEEPConfig(iop_base, eep_config, asc_dvc->bus_type);
+ if (chksum == 0) {
+ chksum = 0xaa55;
+ }
+ if (AscGetChipStatus(iop_base) & CSW_AUTO_CONFIG) {
+ warn_code |= ASC_WARN_AUTO_CONFIG;
+ if (asc_dvc->cfg->chip_version == 3) {
+ if (eep_config->cfg_lsw != cfg_lsw) {
+ warn_code |= ASC_WARN_EEPROM_RECOVER;
+ eep_config->cfg_lsw = AscGetChipCfgLsw(iop_base);
+ }
+ if (eep_config->cfg_msw != cfg_msw) {
+ warn_code |= ASC_WARN_EEPROM_RECOVER;
+ eep_config->cfg_msw = AscGetChipCfgMsw(iop_base);
+ }
+ }
+ }
+ eep_config->cfg_msw &= ~ASC_CFG_MSW_CLR_MASK;
+ eep_config->cfg_lsw |= ASC_CFG0_HOST_INT_ON;
+ if (chksum != eep_config->chksum) {
+ if (AscGetChipVersion(iop_base, asc_dvc->bus_type) ==
+ ASC_CHIP_VER_PCI_ULTRA_3050 )
+ {
+ eep_config->init_sdtr = 0xFF;
+ eep_config->disc_enable = 0xFF;
+ eep_config->start_motor = 0xFF;
+ eep_config->use_cmd_qng = 0;
+ eep_config->max_total_qng = 0xF0;
+ eep_config->max_tag_qng = 0x20;
+ eep_config->cntl = 0xBFFF;
+ eep_config->chip_scsi_id = 7;
+ eep_config->no_scam = 0;
+ eep_config->adapter_info[0] = 0;
+ eep_config->adapter_info[1] = 0;
+ eep_config->adapter_info[2] = 0;
+ eep_config->adapter_info[3] = 0;
+ eep_config->adapter_info[4] = 0;
+ /* Indicate EEPROM-less board. */
+ eep_config->adapter_info[5] = 0xBB;
+ } else {
+ write_eep = 1 ;
+ warn_code |= ASC_WARN_EEPROM_CHKSUM ;
+ }
+ }
+ asc_dvc->cfg->sdtr_enable = eep_config->init_sdtr ;
+ asc_dvc->cfg->disc_enable = eep_config->disc_enable;
+ asc_dvc->cfg->cmd_qng_enabled = eep_config->use_cmd_qng;
+ asc_dvc->cfg->isa_dma_speed = eep_config->isa_dma_speed;
+ asc_dvc->start_motor = eep_config->start_motor;
+ asc_dvc->dvc_cntl = eep_config->cntl;
+ asc_dvc->no_scam = eep_config->no_scam;
+ asc_dvc->cfg->adapter_info[0] = eep_config->adapter_info[0];
+ asc_dvc->cfg->adapter_info[1] = eep_config->adapter_info[1];
+ asc_dvc->cfg->adapter_info[2] = eep_config->adapter_info[2];
+ asc_dvc->cfg->adapter_info[3] = eep_config->adapter_info[3];
+ asc_dvc->cfg->adapter_info[4] = eep_config->adapter_info[4];
+ asc_dvc->cfg->adapter_info[5] = eep_config->adapter_info[5];
+ if (!AscTestExternalLram(asc_dvc)) {
+ if (((asc_dvc->bus_type & ASC_IS_PCI_ULTRA) == ASC_IS_PCI_ULTRA)) {
+ eep_config->max_total_qng = ASC_MAX_PCI_ULTRA_INRAM_TOTAL_QNG;
+ eep_config->max_tag_qng = ASC_MAX_PCI_ULTRA_INRAM_TAG_QNG;
+ } else {
+ eep_config->cfg_msw |= 0x0800;
+ cfg_msw |= 0x0800;
+ AscSetChipCfgMsw(iop_base, cfg_msw);
+ eep_config->max_total_qng = ASC_MAX_PCI_INRAM_TOTAL_QNG;
+ eep_config->max_tag_qng = ASC_MAX_INRAM_TAG_QNG;
+ }
+ } else {
+ }
+ if (eep_config->max_total_qng < ASC_MIN_TOTAL_QNG) {
+ eep_config->max_total_qng = ASC_MIN_TOTAL_QNG;
+ }
+ if (eep_config->max_total_qng > ASC_MAX_TOTAL_QNG) {
+ eep_config->max_total_qng = ASC_MAX_TOTAL_QNG;
+ }
+ if (eep_config->max_tag_qng > eep_config->max_total_qng) {
+ eep_config->max_tag_qng = eep_config->max_total_qng;
+ }
+ if (eep_config->max_tag_qng < ASC_MIN_TAG_Q_PER_DVC) {
+ eep_config->max_tag_qng = ASC_MIN_TAG_Q_PER_DVC;
+ }
+ asc_dvc->max_total_qng = eep_config->max_total_qng;
+ if ((eep_config->use_cmd_qng & eep_config->disc_enable) !=
+ eep_config->use_cmd_qng) {
+ eep_config->disc_enable = eep_config->use_cmd_qng;
+ warn_code |= ASC_WARN_CMD_QNG_CONFLICT;
+ }
+ if (asc_dvc->bus_type & (ASC_IS_ISA | ASC_IS_VL | ASC_IS_EISA)) {
+ asc_dvc->irq_no = AscGetChipIRQ(iop_base, asc_dvc->bus_type);
+ }
+ eep_config->chip_scsi_id &= ASC_MAX_TID;
+ asc_dvc->cfg->chip_scsi_id = eep_config->chip_scsi_id;
+ if (((asc_dvc->bus_type & ASC_IS_PCI_ULTRA) == ASC_IS_PCI_ULTRA) &&
+ !(asc_dvc->dvc_cntl & ASC_CNTL_SDTR_ENABLE_ULTRA)) {
+ asc_dvc->host_init_sdtr_index = ASC_SDTR_ULTRA_PCI_10MB_INDEX;
+ }
+
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ asc_dvc->dos_int13_table[i] = eep_config->dos_int13_table[i];
+ asc_dvc->cfg->max_tag_qng[i] = eep_config->max_tag_qng;
+ asc_dvc->cfg->sdtr_period_offset[i] =
+ (uchar) (ASC_DEF_SDTR_OFFSET |
+ (asc_dvc->host_init_sdtr_index << 4));
+ }
+ eep_config->cfg_msw = AscGetChipCfgMsw(iop_base);
+ if (write_eep) {
+ (void) AscSetEEPConfig(iop_base, eep_config, asc_dvc->bus_type);
+ }
+ return (warn_code);
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscInitMicroCodeVar(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+)
+{
+ int i;
+ ushort warn_code;
+ PortAddr iop_base;
+ ulong phy_addr;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ AscPutMCodeInitSDTRAtID(iop_base, i,
+ asc_dvc->cfg->sdtr_period_offset[i]
+);
+ }
+ AscInitQLinkVar(asc_dvc);
+ AscWriteLramByte(iop_base, ASCV_DISC_ENABLE_B,
+ asc_dvc->cfg->disc_enable);
+ AscWriteLramByte(iop_base, ASCV_HOSTSCSI_ID_B,
+ ASC_TID_TO_TARGET_ID(asc_dvc->cfg->chip_scsi_id));
+ if ((phy_addr = AscGetOnePhyAddr(asc_dvc,
+ (uchar *) asc_dvc->cfg->overrun_buf,
+ ASC_OVERRUN_BSIZE)) == 0L) {
+ asc_dvc->err_code |= ASC_IERR_GET_PHY_ADDR;
+ } else {
+ phy_addr = (phy_addr & 0xFFFFFFF8UL) + 8;
+ AscWriteLramDWord(iop_base, ASCV_OVERRUN_PADDR_D, phy_addr);
+ AscWriteLramDWord(iop_base, ASCV_OVERRUN_BSIZE_D,
+ ASC_OVERRUN_BSIZE - 8);
+ }
+ asc_dvc->cfg->mcode_date = AscReadLramWord(iop_base,
+ (ushort) ASCV_MC_DATE_W);
+ asc_dvc->cfg->mcode_version = AscReadLramWord(iop_base,
+ (ushort) ASCV_MC_VER_W);
+ AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR);
+ if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) {
+ asc_dvc->err_code |= ASC_IERR_SET_PC_ADDR;
+ return (warn_code);
+ }
+ if (AscStartChip(iop_base) != 1) {
+ asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
+ return (warn_code);
+ }
+ return (warn_code);
+}
+
+ASC_INITFUNC(
+STATIC int
+AscTestExternalLram(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+)
+{
+ PortAddr iop_base;
+ ushort q_addr;
+ ushort saved_word;
+ int sta;
+
+ iop_base = asc_dvc->iop_base;
+ sta = 0;
+ q_addr = ASC_QNO_TO_QADDR(241);
+ saved_word = AscReadLramWord(iop_base, q_addr);
+ AscSetChipLramAddr(iop_base, q_addr);
+ AscSetChipLramData(iop_base, 0x55AA);
+ DvcSleepMilliSecond(10);
+ AscSetChipLramAddr(iop_base, q_addr);
+ if (AscGetChipLramData(iop_base) == 0x55AA) {
+ sta = 1;
+ AscWriteLramWord(iop_base, q_addr, saved_word);
+ }
+ return (sta);
+}
+
+ASC_INITFUNC(
+STATIC int
+AscWriteEEPCmdReg(
+ PortAddr iop_base,
+ uchar cmd_reg
+)
+)
+{
+ uchar read_back;
+ int retry;
+
+ retry = 0;
+ while (TRUE) {
+ AscSetChipEEPCmd(iop_base, cmd_reg);
+ DvcSleepMilliSecond(1);
+ read_back = AscGetChipEEPCmd(iop_base);
+ if (read_back == cmd_reg) {
+ return (1);
+ }
+ if (retry++ > ASC_EEP_MAX_RETRY) {
+ return (0);
+ }
+ }
+}
+
+ASC_INITFUNC(
+STATIC int
+AscWriteEEPDataReg(
+ PortAddr iop_base,
+ ushort data_reg
+)
+)
+{
+ ushort read_back;
+ int retry;
+
+ retry = 0;
+ while (TRUE) {
+ AscSetChipEEPData(iop_base, data_reg);
+ DvcSleepMilliSecond(1);
+ read_back = AscGetChipEEPData(iop_base);
+ if (read_back == data_reg) {
+ return (1);
+ }
+ if (retry++ > ASC_EEP_MAX_RETRY) {
+ return (0);
+ }
+ }
+}
+
+ASC_INITFUNC(
+STATIC void
+AscWaitEEPRead(
+ void
+)
+)
+{
+ DvcSleepMilliSecond(1);
+ return;
+}
+
+ASC_INITFUNC(
+STATIC void
+AscWaitEEPWrite(
+ void
+)
+)
+{
+ DvcSleepMilliSecond(20);
+ return;
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscReadEEPWord(
+ PortAddr iop_base,
+ uchar addr
+)
+)
+{
+ ushort read_wval;
+ uchar cmd_reg;
+
+ AscWriteEEPCmdReg(iop_base, ASC_EEP_CMD_WRITE_DISABLE);
+ AscWaitEEPRead();
+ cmd_reg = addr | ASC_EEP_CMD_READ;
+ AscWriteEEPCmdReg(iop_base, cmd_reg);
+ AscWaitEEPRead();
+ read_wval = AscGetChipEEPData(iop_base);
+ AscWaitEEPRead();
+ return (read_wval);
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscWriteEEPWord(
+ PortAddr iop_base,
+ uchar addr,
+ ushort word_val
+)
+)
+{
+ ushort read_wval;
+
+ read_wval = AscReadEEPWord(iop_base, addr);
+ if (read_wval != word_val) {
+ AscWriteEEPCmdReg(iop_base, ASC_EEP_CMD_WRITE_ABLE);
+ AscWaitEEPRead();
+ AscWriteEEPDataReg(iop_base, word_val);
+ AscWaitEEPRead();
+ AscWriteEEPCmdReg(iop_base,
+ (uchar) ((uchar) ASC_EEP_CMD_WRITE | addr));
+ AscWaitEEPWrite();
+ AscWriteEEPCmdReg(iop_base, ASC_EEP_CMD_WRITE_DISABLE);
+ AscWaitEEPRead();
+ return (AscReadEEPWord(iop_base, addr));
+ }
+ return (read_wval);
+}
+
+ASC_INITFUNC(
+STATIC ushort
+AscGetEEPConfig(
+ PortAddr iop_base,
+ ASCEEP_CONFIG * cfg_buf, ushort bus_type
+)
+)
+{
+ ushort wval;
+ ushort sum;
+ ushort *wbuf;
+ int cfg_beg;
+ int cfg_end;
+ int s_addr;
+ int isa_pnp_wsize;
+
+ wbuf = (ushort *) cfg_buf;
+ sum = 0;
+ isa_pnp_wsize = 0;
+ for (s_addr = 0; s_addr < (2 + isa_pnp_wsize); s_addr++, wbuf++) {
+ wval = AscReadEEPWord(iop_base, (uchar) s_addr);
+ sum += wval;
+ *wbuf = wval;
+ }
+ if (bus_type & ASC_IS_VL) {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG_VL;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR_VL;
+ } else {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR;
+ }
+ for (s_addr = cfg_beg; s_addr <= (cfg_end - 1);
+ s_addr++, wbuf++) {
+ wval = AscReadEEPWord(iop_base, (uchar) s_addr);
+ sum += wval;
+ *wbuf = wval;
+ }
+ *wbuf = AscReadEEPWord(iop_base, (uchar) s_addr);
+ return (sum);
+}
+
+ASC_INITFUNC(
+STATIC int
+AscSetEEPConfigOnce(
+ PortAddr iop_base,
+ ASCEEP_CONFIG * cfg_buf, ushort bus_type
+)
+)
+{
+ int n_error;
+ ushort *wbuf;
+ ushort sum;
+ int s_addr;
+ int cfg_beg;
+ int cfg_end;
+
+ wbuf = (ushort *) cfg_buf;
+ n_error = 0;
+ sum = 0;
+ for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
+ sum += *wbuf;
+ if (*wbuf != AscWriteEEPWord(iop_base, (uchar) s_addr, *wbuf)) {
+ n_error++;
+ }
+ }
+ if (bus_type & ASC_IS_VL) {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG_VL;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR_VL;
+ } else {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR;
+ }
+ for (s_addr = cfg_beg; s_addr <= (cfg_end - 1);
+ s_addr++, wbuf++) {
+ sum += *wbuf;
+ if (*wbuf != AscWriteEEPWord(iop_base, (uchar) s_addr, *wbuf)) {
+ n_error++;
+ }
+ }
+ *wbuf = sum;
+ if (sum != AscWriteEEPWord(iop_base, (uchar) s_addr, sum)) {
+ n_error++;
+ }
+ wbuf = (ushort *) cfg_buf;
+ for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
+ if (*wbuf != AscReadEEPWord(iop_base, (uchar) s_addr)) {
+ n_error++;
+ }
+ }
+ for (s_addr = cfg_beg; s_addr <= cfg_end;
+ s_addr++, wbuf++) {
+ if (*wbuf != AscReadEEPWord(iop_base, (uchar) s_addr)) {
+ n_error++;
+ }
+ }
+ return (n_error);
+}
+
+ASC_INITFUNC(
+STATIC int
+AscSetEEPConfig(
+ PortAddr iop_base,
+ ASCEEP_CONFIG * cfg_buf, ushort bus_type
+)
+)
+{
+ int retry;
+ int n_error;
+
+ retry = 0;
+ while (TRUE) {
+ if ((n_error = AscSetEEPConfigOnce(iop_base, cfg_buf,
+ bus_type)) == 0) {
+ break;
+ }
+ if (++retry > ASC_EEP_MAX_RETRY) {
+ break;
+ }
+ }
+ return (n_error);
+}
+
+STATIC void
+AscAsyncFix(
+ ASC_DVC_VAR asc_ptr_type *asc_dvc,
+ uchar tid_no,
+ ASC_SCSI_INQUIRY *inq)
+{
+ uchar dvc_type;
+ ASC_SCSI_BIT_ID_TYPE tid_bits;
+
+ dvc_type = inq->byte0.peri_dvc_type;
+ tid_bits = ASC_TIX_TO_TARGET_ID(tid_no);
+
+ if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_ASYN_USE_SYN) {
+ if (!(asc_dvc->init_sdtr & tid_bits)) {
+ if ((dvc_type == SCSI_TYPE_CDROM) &&
+ (AscCompareString((uchar *) inq->vendor_id,
+ (uchar *) "HP ", 3) == 0)) {
+ asc_dvc->pci_fix_asyn_xfer_always |= tid_bits;
+ }
+ asc_dvc->pci_fix_asyn_xfer |= tid_bits;
+ if ((dvc_type == SCSI_TYPE_PROC) ||
+ (dvc_type == SCSI_TYPE_SCANNER)) {
+ asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+ }
+ if ((dvc_type == SCSI_TYPE_SASD) &&
+ (AscCompareString((uchar *) inq->vendor_id,
+ (uchar *) "TANDBERG", 8) == 0) &&
+ (AscCompareString((uchar *) inq->product_id,
+ (uchar *) " TDC 36", 7) == 0)) {
+ asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+ }
+ if ((dvc_type == SCSI_TYPE_SASD) &&
+ (AscCompareString((uchar *) inq->vendor_id,
+ (uchar *) "WANGTEK ", 8) == 0)) {
+ asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+ }
+
+ if ((dvc_type == SCSI_TYPE_CDROM) &&
+ (AscCompareString((uchar *) inq->vendor_id,
+ (uchar *) "NEC ", 8) == 0) &&
+ (AscCompareString((uchar *) inq->product_id,
+ (uchar *) "CD-ROM DRIVE ", 16) == 0)) {
+ asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+ }
+
+ if ((dvc_type == SCSI_TYPE_CDROM) &&
+ (AscCompareString((uchar *) inq->vendor_id,
+ (uchar *) "YAMAHA", 6) == 0) &&
+ (AscCompareString((uchar *) inq->product_id,
+ (uchar *) "CDR400", 6) == 0)) {
+ asc_dvc->pci_fix_asyn_xfer &= ~tid_bits;
+ }
+ if (asc_dvc->pci_fix_asyn_xfer & tid_bits) {
+ AscSetRunChipSynRegAtID(asc_dvc->iop_base, tid_no,
+ ASYN_SDTR_DATA_FIX_PCI_REV_AB);
+ }
+ }
+ }
+ return;
+}
+
+STATIC int
+AscTagQueuingSafe(ASC_SCSI_INQUIRY *inq)
+{
+ if ((inq->add_len >= 32) &&
+ (AscCompareString((uchar *) inq->vendor_id,
+ (uchar *) "QUANTUM XP34301", 15) == 0) &&
+ (AscCompareString((uchar *) inq->product_rev_level,
+ (uchar *) "1071", 4) == 0))
+ {
+ return 0;
+ }
+ return 1;
+}
+
+STATIC void
+AscInquiryHandling(ASC_DVC_VAR asc_ptr_type *asc_dvc,
+ uchar tid_no, ASC_SCSI_INQUIRY *inq)
+{
+ ASC_SCSI_BIT_ID_TYPE tid_bit = ASC_TIX_TO_TARGET_ID(tid_no);
+ ASC_SCSI_BIT_ID_TYPE orig_init_sdtr, orig_use_tagged_qng;
+
+ orig_init_sdtr = asc_dvc->init_sdtr;
+ orig_use_tagged_qng = asc_dvc->use_tagged_qng;
+
+ asc_dvc->init_sdtr &= ~tid_bit;
+ asc_dvc->cfg->can_tagged_qng &= ~tid_bit;
+ asc_dvc->use_tagged_qng &= ~tid_bit;
+
+ if (inq->byte3.rsp_data_fmt >= 2 || inq->byte2.ansi_apr_ver >= 2) {
+ if ((asc_dvc->cfg->sdtr_enable & tid_bit) && inq->byte7.Sync) {
+ asc_dvc->init_sdtr |= tid_bit;
+ }
+ if ((asc_dvc->cfg->cmd_qng_enabled & tid_bit) && inq->byte7.CmdQue) {
+ if (AscTagQueuingSafe(inq)) {
+ asc_dvc->use_tagged_qng |= tid_bit;
+ asc_dvc->cfg->can_tagged_qng |= tid_bit;
+ }
+ }
+ }
+ if (orig_use_tagged_qng != asc_dvc->use_tagged_qng) {
+ AscWriteLramByte(asc_dvc->iop_base, ASCV_DISC_ENABLE_B,
+ asc_dvc->cfg->disc_enable);
+ AscWriteLramByte(asc_dvc->iop_base, ASCV_USE_TAGGED_QNG_B,
+ asc_dvc->use_tagged_qng);
+ AscWriteLramByte(asc_dvc->iop_base, ASCV_CAN_TAGGED_QNG_B,
+ asc_dvc->cfg->can_tagged_qng);
+
+ asc_dvc->max_dvc_qng[tid_no] =
+ asc_dvc->cfg->max_tag_qng[tid_no];
+ AscWriteLramByte(asc_dvc->iop_base,
+ (ushort) (ASCV_MAX_DVC_QNG_BEG + tid_no),
+ asc_dvc->max_dvc_qng[tid_no]);
+ }
+ if (orig_init_sdtr != asc_dvc->init_sdtr) {
+ AscAsyncFix(asc_dvc, tid_no, inq);
+ }
+ return;
+}
+
+STATIC int
+AscCompareString(
+ ruchar * str1,
+ ruchar * str2,
+ int len
+)
+{
+ int i;
+ int diff;
+
+ for (i = 0; i < len; i++) {
+ diff = (int) (str1[i] - str2[i]);
+ if (diff != 0)
+ return (diff);
+ }
+ return (0);
+}
+
+STATIC uchar
+AscReadLramByte(
+ PortAddr iop_base,
+ ushort addr
+)
+{
+ uchar byte_data;
+ ushort word_data;
+
+ if (isodd_word(addr)) {
+ AscSetChipLramAddr(iop_base, addr - 1);
+ word_data = AscGetChipLramData(iop_base);
+ byte_data = (uchar) ((word_data >> 8) & 0xFF);
+ } else {
+ AscSetChipLramAddr(iop_base, addr);
+ word_data = AscGetChipLramData(iop_base);
+ byte_data = (uchar) (word_data & 0xFF);
+ }
+ return (byte_data);
+}
+
+STATIC ushort
+AscReadLramWord(
+ PortAddr iop_base,
+ ushort addr
+)
+{
+ ushort word_data;
+
+ AscSetChipLramAddr(iop_base, addr);
+ word_data = AscGetChipLramData(iop_base);
+ return (word_data);
+}
+
+STATIC ulong
+AscReadLramDWord(
+ PortAddr iop_base,
+ ushort addr
+)
+{
+ ushort val_low, val_high;
+ ulong dword_data;
+
+ AscSetChipLramAddr(iop_base, addr);
+ val_low = AscGetChipLramData(iop_base);
+ val_high = AscGetChipLramData(iop_base);
+ dword_data = ((ulong) val_high << 16) | (ulong) val_low;
+ return (dword_data);
+}
+
+STATIC void
+AscWriteLramWord(
+ PortAddr iop_base,
+ ushort addr,
+ ushort word_val
+)
+{
+ AscSetChipLramAddr(iop_base, addr);
+ AscSetChipLramData(iop_base, word_val);
+ return;
+}
+
+STATIC void
+AscWriteLramDWord(
+ PortAddr iop_base,
+ ushort addr,
+ ulong dword_val
+)
+{
+ ushort word_val;
+
+ AscSetChipLramAddr(iop_base, addr);
+ word_val = (ushort) dword_val;
+ AscSetChipLramData(iop_base, word_val);
+ word_val = (ushort) (dword_val >> 16);
+ AscSetChipLramData(iop_base, word_val);
+ return;
+}
+
+STATIC void
+AscWriteLramByte(
+ PortAddr iop_base,
+ ushort addr,
+ uchar byte_val
+)
+{
+ ushort word_data;
+
+ if (isodd_word(addr)) {
+ addr--;
+ word_data = AscReadLramWord(iop_base, addr);
+ word_data &= 0x00FF;
+ word_data |= (((ushort) byte_val << 8) & 0xFF00);
+ } else {
+ word_data = AscReadLramWord(iop_base, addr);
+ word_data &= 0xFF00;
+ word_data |= ((ushort) byte_val & 0x00FF);
+ }
+ AscWriteLramWord(iop_base, addr, word_data);
+ return;
+}
+
+STATIC void
+AscMemWordCopyToLram(
+ PortAddr iop_base,
+ ushort s_addr,
+ ushort * s_buffer,
+ int words
+)
+{
+ AscSetChipLramAddr(iop_base, s_addr);
+ DvcOutPortWords(iop_base + IOP_RAM_DATA, s_buffer, words);
+ return;
+}
+
+STATIC void
+AscMemDWordCopyToLram(
+ PortAddr iop_base,
+ ushort s_addr,
+ ulong * s_buffer,
+ int dwords
+)
+{
+ AscSetChipLramAddr(iop_base, s_addr);
+ DvcOutPortDWords(iop_base + IOP_RAM_DATA, s_buffer, dwords);
+ return;
+}
+
+STATIC void
+AscMemWordCopyFromLram(
+ PortAddr iop_base,
+ ushort s_addr,
+ ushort * d_buffer,
+ int words
+)
+{
+ AscSetChipLramAddr(iop_base, s_addr);
+ DvcInPortWords(iop_base + IOP_RAM_DATA, d_buffer, words);
+ return;
+}
+
+STATIC ulong
+AscMemSumLramWord(
+ PortAddr iop_base,
+ ushort s_addr,
+ rint words
+)
+{
+ ulong sum;
+ int i;
+
+ sum = 0L;
+ for (i = 0; i < words; i++, s_addr += 2) {
+ sum += AscReadLramWord(iop_base, s_addr);
+ }
+ return (sum);
+}
+
+STATIC void
+AscMemWordSetLram(
+ PortAddr iop_base,
+ ushort s_addr,
+ ushort set_wval,
+ rint words
+)
+{
+ rint i;
+
+ AscSetChipLramAddr(iop_base, s_addr);
+ for (i = 0; i < words; i++) {
+ AscSetChipLramData(iop_base, set_wval);
+ }
+ return;
+}
+
+
+/*
+ * --- Adv Library Functions
+ */
+
+/* a_qswap.h */
+STATIC unsigned char _adv_mcode_buf[] ASC_INITDATA = {
+ 0x9C, 0xF0, 0x80, 0x01, 0x00, 0xF0, 0x44, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x01, 0xD6, 0x11, 0x00, 0x00, 0x70, 0x01,
+ 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x10, 0x2D, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0xF7, 0x70, 0x01, 0x0C, 0x1C, 0x06, 0xF7, 0x02, 0x00, 0x00, 0xF2, 0xD6, 0x0A,
+ 0x04, 0xF7, 0x70, 0x01, 0x06, 0xF7, 0x02, 0x00, 0x3E, 0x57, 0x3C, 0x56, 0x0C, 0x1C, 0x00, 0xFC,
+ 0xA6, 0x00, 0x01, 0x58, 0xAA, 0x13, 0x20, 0xF0, 0xA6, 0x03, 0x06, 0xEC, 0xB9, 0x00, 0x0E, 0x47,
+ 0x03, 0xE6, 0x10, 0x00, 0xCE, 0x45, 0x02, 0x13, 0x3E, 0x57, 0x06, 0xEA, 0xB9, 0x00, 0x47, 0x4B,
+ 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x01, 0x48, 0x4E, 0x12, 0x03, 0xF6, 0xC0, 0x00,
+ 0x00, 0xF2, 0x68, 0x0A, 0x41, 0x58, 0x03, 0xF6, 0xD0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x49, 0x44,
+ 0x59, 0xF0, 0x0A, 0x02, 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x44, 0x58, 0x00, 0xF2,
+ 0xE2, 0x0D, 0x02, 0xCC, 0x4A, 0xE4, 0x01, 0x00, 0x55, 0xF0, 0x08, 0x03, 0x45, 0xF4, 0x02, 0x00,
+ 0x83, 0x5A, 0x04, 0xCC, 0x01, 0x4A, 0x12, 0x12, 0x00, 0xF2, 0xE2, 0x0D, 0x00, 0xCD, 0x48, 0xE4,
+ 0x01, 0x00, 0xE9, 0x13, 0x00, 0xF2, 0xC6, 0x0F, 0xFA, 0x10, 0x0E, 0x47, 0x03, 0xE6, 0x10, 0x00,
+ 0xCE, 0x45, 0x02, 0x13, 0x3E, 0x57, 0xCE, 0x47, 0x97, 0x13, 0x04, 0xEC, 0xB4, 0x00, 0x00, 0xF2,
+ 0xE2, 0x0D, 0x00, 0xCD, 0x48, 0xE4, 0x00, 0x00, 0x12, 0x12, 0x3E, 0x57, 0x06, 0xCC, 0x45, 0xF4,
+ 0x02, 0x00, 0x83, 0x5A, 0x00, 0xCC, 0x00, 0xEA, 0xB4, 0x00, 0x92, 0x10, 0x00, 0xF0, 0x8C, 0x01,
+ 0x43, 0xF0, 0x5C, 0x02, 0x44, 0xF0, 0x60, 0x02, 0x45, 0xF0, 0x64, 0x02, 0x46, 0xF0, 0x68, 0x02,
+ 0x47, 0xF0, 0x6E, 0x02, 0x48, 0xF0, 0x9E, 0x02, 0xB9, 0x54, 0x62, 0x10, 0x00, 0x1C, 0x5A, 0x10,
+ 0x02, 0x1C, 0x56, 0x10, 0x1E, 0x1C, 0x52, 0x10, 0x00, 0xF2, 0x1E, 0x11, 0x50, 0x10, 0x06, 0xFC,
+ 0xA8, 0x00, 0x03, 0xF6, 0xBE, 0x00, 0x00, 0xF2, 0x4E, 0x0A, 0x8C, 0x10, 0x01, 0xF6, 0x01, 0x00,
+ 0x01, 0xFA, 0xA8, 0x00, 0x00, 0xF2, 0x2C, 0x0B, 0x06, 0x10, 0xB9, 0x54, 0x01, 0xFA, 0xA8, 0x00,
+ 0x03, 0xF6, 0xBE, 0x00, 0x00, 0xF2, 0x58, 0x0A, 0x01, 0xFC, 0xA8, 0x00, 0x20, 0x10, 0x58, 0x1C,
+ 0x00, 0xF2, 0x1C, 0x0B, 0x5A, 0x1C, 0x01, 0xF6, 0x01, 0x00, 0x38, 0x54, 0x00, 0xFA, 0xA6, 0x00,
+ 0x01, 0xFA, 0xA8, 0x00, 0x20, 0x1C, 0x00, 0xF0, 0x72, 0x01, 0x01, 0xF6, 0x01, 0x00, 0x38, 0x54,
+ 0x00, 0xFA, 0xA6, 0x00, 0x01, 0xFA, 0xA8, 0x00, 0x20, 0x1C, 0x00, 0xF0, 0x80, 0x01, 0x03, 0xF6,
+ 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x01, 0x48, 0x0A, 0x13, 0x00, 0xF2, 0x38, 0x10, 0x00, 0xF2,
+ 0x54, 0x0F, 0x24, 0x10, 0x03, 0xF6, 0xC0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x02, 0xF6, 0xD0, 0x00,
+ 0x02, 0x57, 0x03, 0x59, 0x01, 0xCC, 0x49, 0x44, 0x5B, 0xF0, 0x04, 0x03, 0x00, 0xF2, 0x9C, 0x0F,
+ 0x00, 0xF0, 0x80, 0x01, 0x00, 0xF2, 0x14, 0x10, 0x0C, 0x1C, 0x02, 0x4B, 0xBF, 0x57, 0x9E, 0x43,
+ 0x77, 0x57, 0x07, 0x4B, 0x20, 0xF0, 0xA6, 0x03, 0x40, 0x1C, 0x1E, 0xF0, 0x30, 0x03, 0x26, 0xF0,
+ 0x2C, 0x03, 0xA0, 0xF0, 0x1A, 0x03, 0x11, 0xF0, 0xA6, 0x03, 0x12, 0x10, 0x9F, 0xF0, 0x3E, 0x03,
+ 0x46, 0x1C, 0x82, 0xE7, 0x05, 0x00, 0x9E, 0xE7, 0x11, 0x00, 0x00, 0xF0, 0x06, 0x0A, 0x0C, 0x1C,
+ 0x48, 0x1C, 0x46, 0x1C, 0x38, 0x54, 0x00, 0xEC, 0xBA, 0x00, 0x08, 0x44, 0x00, 0xEA, 0xBA, 0x00,
+ 0x03, 0xF6, 0xC0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x08, 0x44, 0x00, 0x4C, 0x82, 0xE7, 0x02, 0x00,
+ 0x00, 0xF2, 0x12, 0x11, 0x00, 0xF2, 0x12, 0x11, 0x85, 0xF0, 0x70, 0x03, 0x00, 0xF2, 0x60, 0x0B,
+ 0x06, 0xF0, 0x80, 0x03, 0x09, 0xF0, 0x24, 0x09, 0x1E, 0xF0, 0xFC, 0x09, 0x00, 0xF0, 0x02, 0x0A,
+ 0x00, 0xFC, 0xBE, 0x00, 0x98, 0x57, 0x55, 0xF0, 0xAC, 0x04, 0x01, 0xE6, 0x0C, 0x00, 0x00, 0xF2,
+ 0x4E, 0x0D, 0x00, 0xF2, 0x12, 0x11, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, 0xC8, 0x11, 0x01, 0xF0,
+ 0x7C, 0x02, 0x00, 0xF0, 0x8A, 0x02, 0x46, 0x1C, 0x0C, 0x1C, 0x67, 0x1B, 0xBF, 0x57, 0x77, 0x57,
+ 0x02, 0x4B, 0x48, 0x1C, 0x32, 0x1C, 0x00, 0xF2, 0x92, 0x0D, 0x30, 0x1C, 0x96, 0xF0, 0xBC, 0x03,
+ 0xB1, 0xF0, 0xC0, 0x03, 0x1E, 0xF0, 0xFC, 0x09, 0x85, 0xF0, 0x02, 0x0A, 0x00, 0xFC, 0xBE, 0x00,
+ 0x98, 0x57, 0x14, 0x12, 0x01, 0xE6, 0x0C, 0x00, 0x00, 0xF2, 0x4E, 0x0D, 0x00, 0xF2, 0x12, 0x11,
+ 0x01, 0xF0, 0x7C, 0x02, 0x00, 0xF0, 0x8A, 0x02, 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A,
+ 0x01, 0x48, 0x55, 0xF0, 0x98, 0x04, 0x03, 0x82, 0x03, 0xFC, 0xA0, 0x00, 0x9B, 0x57, 0x40, 0x12,
+ 0x69, 0x18, 0x00, 0xF2, 0x12, 0x11, 0x85, 0xF0, 0x42, 0x04, 0x69, 0x08, 0x00, 0xF2, 0x12, 0x11,
+ 0x85, 0xF0, 0x02, 0x0A, 0x68, 0x08, 0x4C, 0x44, 0x28, 0x12, 0x44, 0x48, 0x03, 0xF6, 0xE0, 0x00,
+ 0x00, 0xF2, 0x68, 0x0A, 0x45, 0x58, 0x00, 0xF2, 0xF6, 0x0D, 0x00, 0xCC, 0x01, 0x48, 0x55, 0xF0,
+ 0x98, 0x04, 0x4C, 0x44, 0xEF, 0x13, 0x00, 0xF2, 0xC6, 0x0F, 0x00, 0xF2, 0x14, 0x10, 0x08, 0x10,
+ 0x68, 0x18, 0x45, 0x5A, 0x00, 0xF2, 0xF6, 0x0D, 0x04, 0x80, 0x18, 0xE4, 0x10, 0x00, 0x28, 0x12,
+ 0x01, 0xE6, 0x06, 0x00, 0x04, 0x80, 0x18, 0xE4, 0x01, 0x00, 0x04, 0x12, 0x01, 0xE6, 0x0D, 0x00,
+ 0x00, 0xF2, 0x4E, 0x0D, 0x00, 0xF2, 0x12, 0x11, 0x04, 0xE6, 0x02, 0x00, 0x9E, 0xE7, 0x15, 0x00,
+ 0x01, 0xF0, 0x1C, 0x0A, 0x00, 0xF0, 0x02, 0x0A, 0x69, 0x08, 0x05, 0x80, 0x48, 0xE4, 0x00, 0x00,
+ 0x0C, 0x12, 0x00, 0xE6, 0x11, 0x00, 0x00, 0xEA, 0xB8, 0x00, 0x00, 0xF2, 0xB6, 0x10, 0x82, 0xE7,
+ 0x02, 0x00, 0x1C, 0x90, 0x40, 0x5C, 0x00, 0x16, 0x01, 0xE6, 0x06, 0x00, 0x00, 0xF2, 0x4E, 0x0D,
+ 0x01, 0xF0, 0x80, 0x01, 0x1E, 0xF0, 0x80, 0x01, 0x00, 0xF0, 0xA0, 0x04, 0x42, 0x5B, 0x06, 0xF7,
+ 0x03, 0x00, 0x46, 0x59, 0xBF, 0x57, 0x77, 0x57, 0x01, 0xE6, 0x80, 0x00, 0x07, 0x80, 0x31, 0x44,
+ 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, 0x56, 0x13, 0x20, 0x80, 0x48, 0xE4, 0x03, 0x00, 0x4E, 0x12,
+ 0x00, 0xFC, 0xA2, 0x00, 0x98, 0x57, 0x55, 0xF0, 0x1C, 0x05, 0x31, 0xE4, 0x40, 0x00, 0x00, 0xFC,
+ 0xA0, 0x00, 0x98, 0x57, 0x36, 0x12, 0x4C, 0x1C, 0x00, 0xF2, 0x12, 0x11, 0x89, 0x48, 0x00, 0xF2,
+ 0x12, 0x11, 0x86, 0xF0, 0x2E, 0x05, 0x82, 0xE7, 0x06, 0x00, 0x1B, 0x80, 0x48, 0xE4, 0x22, 0x00,
+ 0x5B, 0xF0, 0x0C, 0x05, 0x48, 0xE4, 0x20, 0x00, 0x59, 0xF0, 0x10, 0x05, 0x00, 0xE6, 0x20, 0x00,
+ 0x09, 0x48, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0x2E, 0x05, 0x83, 0x80, 0x04, 0x10, 0x00, 0xF2,
+ 0xA2, 0x0D, 0x00, 0xE6, 0x01, 0x00, 0x00, 0xEA, 0x26, 0x01, 0x01, 0xEA, 0x27, 0x01, 0x04, 0x80,
+ 0x18, 0xE4, 0x10, 0x00, 0x36, 0x12, 0xB9, 0x54, 0x00, 0xF2, 0xF6, 0x0E, 0x01, 0xE6, 0x06, 0x00,
+ 0x04, 0x80, 0x18, 0xE4, 0x01, 0x00, 0x04, 0x12, 0x01, 0xE6, 0x0D, 0x00, 0x00, 0xF2, 0x4E, 0x0D,
+ 0x00, 0xF2, 0x12, 0x11, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, 0xC8, 0x11, 0x04, 0xE6, 0x02, 0x00,
+ 0x9E, 0xE7, 0x15, 0x00, 0x01, 0xF0, 0x1C, 0x0A, 0x00, 0xF0, 0x02, 0x0A, 0x00, 0xFC, 0x20, 0x01,
+ 0x98, 0x57, 0x34, 0x12, 0x00, 0xFC, 0x24, 0x01, 0x98, 0x57, 0x2C, 0x13, 0xB9, 0x54, 0x00, 0xF2,
+ 0xF6, 0x0E, 0x86, 0xF0, 0xA8, 0x05, 0x03, 0xF6, 0x01, 0x00, 0x00, 0xF2, 0x8C, 0x0E, 0x85, 0xF0,
+ 0x9E, 0x05, 0x82, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x60, 0x0B, 0x82, 0xE7, 0x02, 0x00, 0x00, 0xFC,
+ 0x24, 0x01, 0xB0, 0x57, 0x00, 0xFA, 0x24, 0x01, 0x00, 0xFC, 0x9E, 0x00, 0x98, 0x57, 0x5A, 0x12,
+ 0x00, 0xFC, 0xB6, 0x00, 0x98, 0x57, 0x52, 0x13, 0x03, 0xE6, 0x0C, 0x00, 0x00, 0xFC, 0x9C, 0x00,
+ 0x98, 0x57, 0x04, 0x13, 0x03, 0xE6, 0x19, 0x00, 0x05, 0xE6, 0x08, 0x00, 0x00, 0xF6, 0x00, 0x01,
+ 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, 0x00, 0xDC, 0x18, 0xF4, 0x00, 0x80, 0x04, 0x13, 0x05, 0xE6,
+ 0x0F, 0x00, 0xB9, 0x54, 0x00, 0xF2, 0xF6, 0x0E, 0x86, 0xF0, 0x0A, 0x06, 0x00, 0xF2, 0xBA, 0x0E,
+ 0x85, 0xF0, 0x00, 0x06, 0x82, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x60, 0x0B, 0x82, 0xE7, 0x02, 0x00,
+ 0x00, 0xFC, 0xB6, 0x00, 0xB0, 0x57, 0x00, 0xFA, 0xB6, 0x00, 0x01, 0xF6, 0x01, 0x00, 0x00, 0xF2,
+ 0xF6, 0x0E, 0x9C, 0x32, 0x4E, 0x1C, 0x32, 0x1C, 0x00, 0xF2, 0x92, 0x0D, 0x30, 0x1C, 0x82, 0xE7,
+ 0x04, 0x00, 0xB1, 0xF0, 0x22, 0x06, 0x0A, 0xF0, 0x3E, 0x06, 0x05, 0xF0, 0xD6, 0x06, 0x06, 0xF0,
+ 0xDC, 0x06, 0x09, 0xF0, 0x24, 0x09, 0x1E, 0xF0, 0xFC, 0x09, 0x00, 0xF0, 0x02, 0x0A, 0x04, 0x80,
+ 0x18, 0xE4, 0x20, 0x00, 0x30, 0x12, 0x09, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x21, 0x80,
+ 0x18, 0xE4, 0xE0, 0x00, 0x09, 0x48, 0x00, 0xF2, 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2,
+ 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x99, 0xA4, 0x00, 0xF2, 0x12, 0x11,
+ 0x09, 0xE7, 0x00, 0x00, 0x9A, 0x10, 0x04, 0x80, 0x18, 0xE4, 0x02, 0x00, 0x34, 0x12, 0x09, 0xE7,
+ 0x1B, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x21, 0x80, 0x18, 0xE4, 0xE0, 0x00, 0x09, 0x48, 0x00, 0xF2,
+ 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF2,
+ 0x12, 0x11, 0x09, 0xE7, 0x01, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x09, 0xE7, 0x00, 0x00, 0x00, 0xF0,
+ 0x0C, 0x09, 0xBB, 0x55, 0x9A, 0x81, 0x03, 0xF7, 0x20, 0x00, 0x09, 0x6F, 0x93, 0x45, 0x55, 0xF0,
+ 0xE2, 0x06, 0xB1, 0xF0, 0xC2, 0x06, 0x0A, 0xF0, 0xBA, 0x06, 0x09, 0xF0, 0x24, 0x09, 0x1E, 0xF0,
+ 0xFC, 0x09, 0x00, 0xF0, 0x02, 0x0A, 0x00, 0xF2, 0x60, 0x0B, 0x47, 0x10, 0x09, 0xE7, 0x08, 0x00,
+ 0x41, 0x10, 0x05, 0x80, 0x48, 0xE4, 0x00, 0x00, 0x1E, 0x12, 0x00, 0xE6, 0x11, 0x00, 0x00, 0xEA,
+ 0xB8, 0x00, 0x00, 0xF2, 0xB6, 0x10, 0x2C, 0x90, 0xAE, 0x90, 0x08, 0x50, 0x8A, 0x50, 0x38, 0x54,
+ 0x1F, 0x40, 0x00, 0xF2, 0xB4, 0x0D, 0x08, 0x10, 0x08, 0x90, 0x8A, 0x90, 0x30, 0x50, 0xB2, 0x50,
+ 0x9C, 0x32, 0x0C, 0x92, 0x8E, 0x92, 0x38, 0x54, 0x04, 0x80, 0x30, 0xE4, 0x08, 0x00, 0x04, 0x40,
+ 0x0C, 0x1C, 0x00, 0xF6, 0x03, 0x00, 0xB1, 0xF0, 0x26, 0x07, 0x9E, 0xF0, 0x3A, 0x07, 0x01, 0x48,
+ 0x55, 0xF0, 0xFC, 0x09, 0x0C, 0x1C, 0x10, 0x44, 0xED, 0x10, 0x0B, 0xF0, 0x5E, 0x07, 0x0C, 0xF0,
+ 0x62, 0x07, 0x05, 0xF0, 0x52, 0x07, 0x06, 0xF0, 0x58, 0x07, 0x09, 0xF0, 0x24, 0x09, 0x00, 0xF0,
+ 0x02, 0x0A, 0x00, 0xF2, 0x60, 0x0B, 0xCF, 0x10, 0x09, 0xE7, 0x08, 0x00, 0xC9, 0x10, 0x2E, 0x1C,
+ 0x02, 0x10, 0x2C, 0x1C, 0xAA, 0xF0, 0x64, 0x07, 0xAC, 0xF0, 0x72, 0x07, 0x40, 0x10, 0x34, 0x1C,
+ 0xF3, 0x10, 0xAD, 0xF0, 0x7C, 0x07, 0xC8, 0x10, 0x36, 0x1C, 0xE9, 0x10, 0x2B, 0xF0, 0x82, 0x08,
+ 0x6B, 0x18, 0x18, 0xF4, 0x00, 0xFE, 0x20, 0x12, 0x01, 0x58, 0xD2, 0xF0, 0x82, 0x08, 0x76, 0x18,
+ 0x18, 0xF4, 0x03, 0x00, 0xEC, 0x12, 0x00, 0xFC, 0x22, 0x01, 0x18, 0xF4, 0x01, 0x00, 0xE2, 0x12,
+ 0x0B, 0xF0, 0x64, 0x07, 0x0C, 0xF0, 0x64, 0x07, 0x36, 0x1C, 0x34, 0x1C, 0xB7, 0x10, 0x38, 0x54,
+ 0xB9, 0x54, 0x84, 0x80, 0x19, 0xE4, 0x20, 0x00, 0xB2, 0x13, 0x85, 0x80, 0x81, 0x48, 0x66, 0x12,
+ 0x04, 0x80, 0x18, 0xE4, 0x08, 0x00, 0x58, 0x13, 0x1F, 0x80, 0x08, 0x44, 0xC8, 0x44, 0x9F, 0x12,
+ 0x1F, 0x40, 0x34, 0x91, 0xB6, 0x91, 0x44, 0x55, 0xE5, 0x55, 0x02, 0xEC, 0xB8, 0x00, 0x02, 0x49,
+ 0xBB, 0x55, 0x82, 0x81, 0xC0, 0x55, 0x48, 0xF4, 0x0F, 0x00, 0x5A, 0xF0, 0x1A, 0x08, 0x4A, 0xE4,
+ 0x17, 0x00, 0xD5, 0xF0, 0xFA, 0x07, 0x02, 0xF6, 0x0F, 0x00, 0x02, 0xF4, 0x02, 0x00, 0x02, 0xEA,
+ 0xB8, 0x00, 0x04, 0x91, 0x86, 0x91, 0x02, 0x4B, 0x2C, 0x90, 0x08, 0x50, 0x2E, 0x90, 0x0A, 0x50,
+ 0x2C, 0x51, 0xAE, 0x51, 0x00, 0xF2, 0xB6, 0x10, 0x38, 0x54, 0x00, 0xF2, 0xB4, 0x0D, 0x56, 0x10,
+ 0x34, 0x91, 0xB6, 0x91, 0x0C, 0x10, 0x04, 0x80, 0x18, 0xE4, 0x08, 0x00, 0x41, 0x12, 0x0C, 0x91,
+ 0x8E, 0x91, 0x04, 0x80, 0x18, 0xE4, 0xF7, 0x00, 0x04, 0x40, 0x30, 0x90, 0xB2, 0x90, 0x36, 0x10,
+ 0x02, 0x80, 0x48, 0xE4, 0x10, 0x00, 0x31, 0x12, 0x82, 0xE7, 0x10, 0x00, 0x84, 0x80, 0x19, 0xE4,
+ 0x20, 0x00, 0x10, 0x13, 0x0C, 0x90, 0x8E, 0x90, 0x5D, 0xF0, 0x78, 0x07, 0x0C, 0x58, 0x8D, 0x58,
+ 0x00, 0xF0, 0x64, 0x07, 0x38, 0x54, 0xB9, 0x54, 0x19, 0x80, 0xF1, 0x10, 0x3A, 0x55, 0x19, 0x81,
+ 0xBB, 0x55, 0x10, 0x90, 0x92, 0x90, 0x10, 0x58, 0x91, 0x58, 0x14, 0x59, 0x95, 0x59, 0x00, 0xF0,
+ 0x64, 0x07, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, 0x06, 0x12, 0x6C, 0x19, 0x19, 0x41, 0x7C, 0x10,
+ 0x6C, 0x19, 0x0C, 0x51, 0xED, 0x19, 0x8E, 0x51, 0x6B, 0x18, 0x18, 0xF4, 0x00, 0xFF, 0x02, 0x13,
+ 0x6A, 0x10, 0x01, 0x58, 0xD2, 0xF0, 0xC0, 0x08, 0x76, 0x18, 0x18, 0xF4, 0x03, 0x00, 0x0A, 0x12,
+ 0x00, 0xFC, 0x22, 0x01, 0x18, 0xF4, 0x01, 0x00, 0x06, 0x13, 0x9E, 0xE7, 0x16, 0x00, 0x4C, 0x10,
+ 0xD1, 0xF0, 0xCA, 0x08, 0x9E, 0xE7, 0x17, 0x00, 0x42, 0x10, 0xD0, 0xF0, 0xD4, 0x08, 0x9E, 0xE7,
+ 0x19, 0x00, 0x38, 0x10, 0xCF, 0xF0, 0xDE, 0x08, 0x9E, 0xE7, 0x20, 0x00, 0x2E, 0x10, 0xCE, 0xF0,
+ 0xE8, 0x08, 0x9E, 0xE7, 0x21, 0x00, 0x24, 0x10, 0xCD, 0xF0, 0xF2, 0x08, 0x9E, 0xE7, 0x22, 0x00,
+ 0x1A, 0x10, 0xCC, 0xF0, 0x04, 0x09, 0x84, 0x80, 0x19, 0xE4, 0x04, 0x00, 0x06, 0x12, 0x9E, 0xE7,
+ 0x12, 0x00, 0x08, 0x10, 0xCB, 0xF0, 0x0C, 0x09, 0x9E, 0xE7, 0x24, 0x00, 0xB1, 0xF0, 0x0C, 0x09,
+ 0x05, 0xF0, 0x1E, 0x09, 0x09, 0xF0, 0x24, 0x09, 0x1E, 0xF0, 0xFC, 0x09, 0xE4, 0x10, 0x00, 0xF2,
+ 0x60, 0x0B, 0xE9, 0x10, 0x9C, 0x32, 0x82, 0xE7, 0x20, 0x00, 0x32, 0x1C, 0xE9, 0x09, 0x00, 0xF2,
+ 0x12, 0x11, 0x85, 0xF0, 0x02, 0x0A, 0x69, 0x08, 0x01, 0xF0, 0x44, 0x09, 0x1E, 0xF0, 0xFC, 0x09,
+ 0x00, 0xF0, 0x38, 0x09, 0x30, 0x44, 0x06, 0x12, 0x9E, 0xE7, 0x42, 0x00, 0xB8, 0x10, 0x04, 0xF6,
+ 0x01, 0x00, 0xB3, 0x45, 0x74, 0x12, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00, 0x22, 0x13, 0x4B, 0xE4,
+ 0x02, 0x00, 0x36, 0x12, 0x4B, 0xE4, 0x28, 0x00, 0xAC, 0x13, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2,
+ 0xC8, 0x11, 0x03, 0xF6, 0xD0, 0x00, 0xFA, 0x14, 0x82, 0xE7, 0x01, 0x00, 0x00, 0xF0, 0x80, 0x01,
+ 0x9E, 0xE7, 0x44, 0x00, 0x4B, 0xE4, 0x02, 0x00, 0x06, 0x12, 0x03, 0xE6, 0x02, 0x00, 0x76, 0x10,
+ 0x00, 0xF2, 0xA2, 0x0D, 0x03, 0xE6, 0x02, 0x00, 0x6C, 0x10, 0x00, 0xF2, 0xA2, 0x0D, 0x19, 0x82,
+ 0x34, 0x46, 0x0A, 0x13, 0x03, 0xE6, 0x02, 0x00, 0x9E, 0xE7, 0x43, 0x00, 0x68, 0x10, 0x04, 0x80,
+ 0x30, 0xE4, 0x20, 0x00, 0x04, 0x40, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, 0xC8, 0x11, 0x82, 0xE7,
+ 0x01, 0x00, 0x06, 0xF7, 0x02, 0x00, 0x00, 0xF0, 0x08, 0x03, 0x04, 0x80, 0x18, 0xE4, 0x20, 0x00,
+ 0x06, 0x12, 0x03, 0xE6, 0x02, 0x00, 0x3E, 0x10, 0x04, 0x80, 0x18, 0xE4, 0x02, 0x00, 0x3A, 0x12,
+ 0x04, 0x80, 0x18, 0xE4, 0xFD, 0x00, 0x04, 0x40, 0x1C, 0x1C, 0x9D, 0xF0, 0xEA, 0x09, 0x1C, 0x1C,
+ 0x9D, 0xF0, 0xF0, 0x09, 0xC1, 0x10, 0x9E, 0xE7, 0x13, 0x00, 0x0A, 0x10, 0x9E, 0xE7, 0x41, 0x00,
+ 0x04, 0x10, 0x9E, 0xE7, 0x24, 0x00, 0x00, 0xFC, 0xBE, 0x00, 0x98, 0x57, 0xD5, 0xF0, 0x8A, 0x02,
+ 0x04, 0xE6, 0x04, 0x00, 0x06, 0x10, 0x04, 0xE6, 0x04, 0x00, 0x9D, 0x41, 0x1C, 0x42, 0x9F, 0xE7,
+ 0x00, 0x00, 0x06, 0xF7, 0x02, 0x00, 0x03, 0xF6, 0xE0, 0x00, 0x3C, 0x14, 0x44, 0x58, 0x45, 0x58,
+ 0x00, 0xF2, 0xF6, 0x0D, 0x00, 0xF2, 0x7E, 0x10, 0x00, 0xF2, 0xC6, 0x0F, 0x3C, 0x14, 0x1E, 0x1C,
+ 0x00, 0xF0, 0x80, 0x01, 0x12, 0x1C, 0x22, 0x1C, 0xD2, 0x14, 0x00, 0xF0, 0x72, 0x01, 0x83, 0x59,
+ 0x03, 0xDC, 0x73, 0x57, 0x80, 0x5D, 0x00, 0x16, 0x83, 0x59, 0x03, 0xDC, 0x38, 0x54, 0x70, 0x57,
+ 0x33, 0x54, 0x3B, 0x54, 0x80, 0x5D, 0x00, 0x16, 0x03, 0x57, 0x83, 0x59, 0x38, 0x54, 0x00, 0xCC,
+ 0x00, 0x16, 0x03, 0x57, 0x83, 0x59, 0x00, 0x4C, 0x00, 0x16, 0x02, 0x80, 0x48, 0xE4, 0x01, 0x00,
+ 0x0E, 0x12, 0x48, 0xE4, 0x05, 0x00, 0x08, 0x12, 0x00, 0xF2, 0xBC, 0x11, 0x00, 0xF2, 0xC8, 0x11,
+ 0xC1, 0x5A, 0x3A, 0x55, 0x02, 0xEC, 0xB5, 0x00, 0x45, 0x59, 0x00, 0xF2, 0xF6, 0x0D, 0x83, 0x58,
+ 0x30, 0xE7, 0x00, 0x00, 0x10, 0x4D, 0x30, 0xE7, 0x40, 0x00, 0x10, 0x4F, 0x38, 0x90, 0xBA, 0x90,
+ 0x10, 0x5C, 0x80, 0x5C, 0x83, 0x5A, 0x10, 0x4E, 0x04, 0xEA, 0xB5, 0x00, 0x43, 0x5B, 0x03, 0xF4,
+ 0xE0, 0x00, 0x83, 0x59, 0x04, 0xCC, 0x01, 0x4A, 0x0A, 0x12, 0x45, 0x5A, 0x00, 0xF2, 0xF6, 0x0D,
+ 0x00, 0xF2, 0x38, 0x10, 0x00, 0x16, 0x08, 0x1C, 0x00, 0xFC, 0xAC, 0x00, 0x06, 0x58, 0x67, 0x18,
+ 0x18, 0xF4, 0x8F, 0xE1, 0x01, 0xFC, 0xAE, 0x00, 0x19, 0xF4, 0x70, 0x1E, 0xB0, 0x54, 0x07, 0x58,
+ 0x00, 0xFC, 0xB0, 0x00, 0x08, 0x58, 0x00, 0xFC, 0xB2, 0x00, 0x09, 0x58, 0x0A, 0x1C, 0x00, 0xE6,
+ 0x0F, 0x00, 0x00, 0xEA, 0xB9, 0x00, 0x38, 0x54, 0x00, 0xFA, 0x24, 0x01, 0x00, 0xFA, 0xB6, 0x00,
+ 0x18, 0x1C, 0x14, 0x1C, 0x10, 0x1C, 0x32, 0x1C, 0x12, 0x1C, 0x00, 0x16, 0x3E, 0x57, 0x0C, 0x14,
+ 0x0E, 0x47, 0x07, 0xE6, 0x10, 0x00, 0xCE, 0x47, 0xF5, 0x13, 0x00, 0x16, 0x00, 0xF2, 0xA2, 0x0D,
+ 0x02, 0x4B, 0x03, 0xF6, 0xE0, 0x00, 0x00, 0xF2, 0x68, 0x0A, 0x01, 0x48, 0x20, 0x12, 0x44, 0x58,
+ 0x45, 0x58, 0x9E, 0xE7, 0x15, 0x00, 0x9C, 0xE7, 0x04, 0x00, 0x00, 0xF2, 0xF6, 0x0D, 0x00, 0xF2,
+ 0x7E, 0x10, 0x00, 0xF2, 0xC6, 0x0F, 0x00, 0xF2, 0x7A, 0x0A, 0x1E, 0x1C, 0xD5, 0x10, 0x00, 0x16,
+ 0x69, 0x08, 0x48, 0xE4, 0x04, 0x00, 0x64, 0x12, 0x48, 0xE4, 0x02, 0x00, 0x20, 0x12, 0x48, 0xE4,
+ 0x03, 0x00, 0x1A, 0x12, 0x48, 0xE4, 0x08, 0x00, 0x14, 0x12, 0x48, 0xE4, 0x01, 0x00, 0xF0, 0x12,
+ 0x48, 0xE4, 0x07, 0x00, 0x12, 0x12, 0x01, 0xE6, 0x07, 0x00, 0x00, 0xF2, 0x4E, 0x0D, 0x00, 0xF2,
+ 0x12, 0x11, 0x05, 0xF0, 0x60, 0x0B, 0x00, 0x16, 0x00, 0xE6, 0x01, 0x00, 0x00, 0xEA, 0x99, 0x00,
+ 0x02, 0x80, 0x48, 0xE4, 0x03, 0x00, 0xE7, 0x12, 0x48, 0xE4, 0x06, 0x00, 0xE1, 0x12, 0x01, 0xE6,
+ 0x06, 0x00, 0x00, 0xF2, 0x4E, 0x0D, 0x00, 0xF2, 0x12, 0x11, 0x04, 0xE6, 0x02, 0x00, 0x9E, 0xE7,
+ 0x15, 0x00, 0x01, 0xF0, 0x1C, 0x0A, 0x00, 0xF0, 0x02, 0x0A, 0x00, 0x16, 0x02, 0x80, 0x48, 0xE4,
+ 0x10, 0x00, 0x1C, 0x12, 0x82, 0xE7, 0x08, 0x00, 0x3C, 0x56, 0x03, 0x82, 0x00, 0xF2, 0xE2, 0x0D,
+ 0x30, 0xE7, 0x08, 0x00, 0x04, 0xF7, 0x70, 0x01, 0x06, 0xF7, 0x02, 0x00, 0x00, 0xF0, 0x80, 0x01,
+ 0x6C, 0x19, 0xED, 0x19, 0x5D, 0xF0, 0xD4, 0x0B, 0x44, 0x55, 0xE5, 0x55, 0x59, 0xF0, 0x52, 0x0C,
+ 0x04, 0x55, 0xA5, 0x55, 0x1F, 0x80, 0x01, 0xEC, 0xB8, 0x00, 0x82, 0x48, 0x82, 0x80, 0x49, 0x44,
+ 0x2E, 0x13, 0x01, 0xEC, 0xB8, 0x00, 0x41, 0xE4, 0x02, 0x00, 0x01, 0xEA, 0xB8, 0x00, 0x49, 0xE4,
+ 0x11, 0x00, 0x59, 0xF0, 0x2E, 0x0C, 0x01, 0xE6, 0x17, 0x00, 0x01, 0xEA, 0xB8, 0x00, 0x02, 0x4B,
+ 0x88, 0x90, 0xAC, 0x50, 0x8A, 0x90, 0xAE, 0x50, 0x01, 0xEC, 0xB8, 0x00, 0x82, 0x48, 0x82, 0x80,
+ 0x10, 0x44, 0x02, 0x4B, 0x1F, 0x40, 0xC0, 0x44, 0x00, 0xF2, 0xB4, 0x0D, 0x04, 0x55, 0xA5, 0x55,
+ 0x9F, 0x10, 0x0C, 0x51, 0x8E, 0x51, 0x30, 0x90, 0xB2, 0x90, 0x00, 0x56, 0xA1, 0x56, 0x30, 0x50,
+ 0xB2, 0x50, 0x34, 0x90, 0xB6, 0x90, 0x40, 0x56, 0xE1, 0x56, 0x34, 0x50, 0xB6, 0x50, 0x65, 0x10,
+ 0xB1, 0xF0, 0x70, 0x0C, 0x85, 0xF0, 0xCA, 0x0B, 0xE9, 0x09, 0x4B, 0xE4, 0x03, 0x00, 0x78, 0x12,
+ 0x4B, 0xE4, 0x02, 0x00, 0x01, 0x13, 0xB1, 0xF0, 0x86, 0x0C, 0x85, 0xF0, 0xCA, 0x0B, 0x69, 0x08,
+ 0x48, 0xE4, 0x03, 0x00, 0xD5, 0xF0, 0x86, 0x0B, 0x00, 0xF2, 0x12, 0x11, 0x85, 0xF0, 0xCA, 0x0B,
+ 0xE8, 0x09, 0x3C, 0x56, 0x00, 0xFC, 0x20, 0x01, 0x98, 0x57, 0x02, 0x13, 0xBB, 0x45, 0x4B, 0xE4,
+ 0x00, 0x00, 0x08, 0x12, 0x03, 0xE6, 0x01, 0x00, 0x04, 0xF6, 0x00, 0x80, 0xA8, 0x14, 0xD2, 0x14,
+ 0x30, 0x1C, 0x02, 0x80, 0x48, 0xE4, 0x03, 0x00, 0x10, 0x13, 0x00, 0xFC, 0xB6, 0x00, 0x98, 0x57,
+ 0x02, 0x13, 0x4C, 0x1C, 0x3E, 0x1C, 0x00, 0xF0, 0x8E, 0x0B, 0x00, 0xFC, 0x24, 0x01, 0xB0, 0x57,
+ 0x00, 0xFA, 0x24, 0x01, 0x4C, 0x1C, 0x3E, 0x1C, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0x8E, 0x0B,
+ 0x00, 0xF2, 0x8C, 0x0E, 0x00, 0xF0, 0x8E, 0x0B, 0xB1, 0xF0, 0xF8, 0x0C, 0x85, 0xF0, 0x86, 0x0B,
+ 0x69, 0x08, 0x48, 0xE4, 0x01, 0x00, 0xD5, 0xF0, 0x86, 0x0B, 0xFC, 0x14, 0x42, 0x58, 0x6C, 0x14,
+ 0x80, 0x14, 0x30, 0x1C, 0x4A, 0xF4, 0x02, 0x00, 0x55, 0xF0, 0x86, 0x0B, 0x4A, 0xF4, 0x01, 0x00,
+ 0x0E, 0x12, 0x02, 0x80, 0x48, 0xE4, 0x03, 0x00, 0x06, 0x13, 0x3E, 0x1C, 0x00, 0xF0, 0x8E, 0x0B,
+ 0x00, 0xFC, 0xB6, 0x00, 0xB0, 0x57, 0x00, 0xFA, 0xB6, 0x00, 0x4C, 0x1C, 0x3E, 0x1C, 0x00, 0xF2,
+ 0x12, 0x11, 0x86, 0xF0, 0x8E, 0x0B, 0x00, 0xF2, 0xBA, 0x0E, 0x00, 0xF0, 0x8E, 0x0B, 0x4C, 0x1C,
+ 0xB1, 0xF0, 0x50, 0x0D, 0x85, 0xF0, 0x5C, 0x0D, 0x69, 0x08, 0xF3, 0x10, 0x86, 0xF0, 0x64, 0x0D,
+ 0x4E, 0x1C, 0x89, 0x48, 0x00, 0x16, 0x00, 0xF6, 0x00, 0x01, 0x00, 0x57, 0x00, 0x57, 0x03, 0x58,
+ 0x00, 0xDC, 0x18, 0xF4, 0xFF, 0x7F, 0x30, 0x56, 0x00, 0x5C, 0x00, 0x16, 0x00, 0xF6, 0x00, 0x01,
+ 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, 0x00, 0xDC, 0x18, 0xF4, 0x00, 0x80, 0x30, 0x56, 0x00, 0x5C,
+ 0x00, 0x16, 0x00, 0xF6, 0x00, 0x01, 0x00, 0x57, 0x00, 0x57, 0x03, 0x58, 0x00, 0xDC, 0x0B, 0x58,
+ 0x00, 0x16, 0x03, 0xF6, 0x24, 0x01, 0x00, 0xF2, 0x58, 0x0A, 0x03, 0xF6, 0xB6, 0x00, 0x00, 0xF2,
+ 0x58, 0x0A, 0x00, 0x16, 0x02, 0xEC, 0xB8, 0x00, 0x02, 0x49, 0x18, 0xF4, 0xFF, 0x00, 0x00, 0x54,
+ 0x00, 0x54, 0x00, 0x54, 0x00, 0xF4, 0x08, 0x00, 0xE1, 0x18, 0x80, 0x54, 0x03, 0x58, 0x00, 0xDD,
+ 0x01, 0xDD, 0x02, 0xDD, 0x03, 0xDC, 0x02, 0x4B, 0x30, 0x50, 0xB2, 0x50, 0x34, 0x51, 0xB6, 0x51,
+ 0x00, 0x16, 0x45, 0x5A, 0x1D, 0xF4, 0xFF, 0x00, 0x85, 0x56, 0x85, 0x56, 0x85, 0x56, 0x05, 0xF4,
+ 0x02, 0x12, 0x83, 0x5A, 0x00, 0x16, 0x1D, 0xF4, 0xFF, 0x00, 0x85, 0x56, 0x85, 0x56, 0x85, 0x56,
+ 0x05, 0xF4, 0x00, 0x12, 0x83, 0x5A, 0x00, 0x16, 0x38, 0x54, 0xBB, 0x55, 0x3C, 0x56, 0xBD, 0x56,
+ 0x00, 0xF2, 0x12, 0x11, 0x85, 0xF0, 0x82, 0x0E, 0xE9, 0x09, 0xC1, 0x59, 0x00, 0xF2, 0x12, 0x11,
+ 0x85, 0xF0, 0x82, 0x0E, 0xE8, 0x0A, 0x83, 0x55, 0x83, 0x55, 0x4B, 0xF4, 0x90, 0x01, 0x5C, 0xF0,
+ 0x36, 0x0E, 0xBD, 0x56, 0x40, 0x10, 0x4B, 0xF4, 0x30, 0x00, 0x59, 0xF0, 0x48, 0x0E, 0x01, 0xF6,
+ 0x0C, 0x00, 0x00, 0xF6, 0x01, 0x00, 0x2E, 0x10, 0x02, 0xFC, 0x9C, 0x00, 0x9A, 0x57, 0x14, 0x13,
+ 0x4B, 0xF4, 0x64, 0x00, 0x59, 0xF0, 0x64, 0x0E, 0x03, 0xF6, 0x64, 0x00, 0x01, 0xF6, 0x19, 0x00,
+ 0x00, 0xF6, 0x01, 0x00, 0x43, 0xF4, 0x33, 0x00, 0x56, 0xF0, 0x76, 0x0E, 0x04, 0xF4, 0x00, 0x01,
+ 0x43, 0xF4, 0x19, 0x00, 0xF3, 0x10, 0xB4, 0x56, 0xC3, 0x58, 0x02, 0xFC, 0x9E, 0x00, 0x9A, 0x57,
+ 0x08, 0x13, 0x3C, 0x56, 0x00, 0xF6, 0x02, 0x00, 0x00, 0x16, 0x00, 0x16, 0x09, 0xE7, 0x01, 0x00,
+ 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0xB8, 0x0E, 0x09, 0xE7, 0x02, 0x00, 0x00, 0xF2, 0x12, 0x11,
+ 0x86, 0xF0, 0xB8, 0x0E, 0x09, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0xB8, 0x0E,
+ 0x4E, 0x1C, 0x89, 0x49, 0x00, 0xF2, 0x12, 0x11, 0x00, 0x16, 0x09, 0xE7, 0x01, 0x00, 0x00, 0xF2,
+ 0x12, 0x11, 0x86, 0xF0, 0xF2, 0x0E, 0x09, 0xE7, 0x03, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0,
+ 0xF2, 0x0E, 0x09, 0xE7, 0x01, 0x00, 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0xF2, 0x0E, 0x89, 0x49,
+ 0x00, 0xF2, 0x12, 0x11, 0x86, 0xF0, 0xF2, 0x0E, 0x4E, 0x1C, 0x89, 0x4A, 0x00, 0xF2, 0x12, 0x11,
+ 0x00, 0x16, 0x3C, 0x56, 0x00, 0x16, 0x00, 0xEC, 0x26, 0x01, 0x48, 0xE4, 0x01, 0x00, 0x1E, 0x13,
+ 0x38, 0x44, 0x00, 0xEA, 0x26, 0x01, 0x49, 0xF4, 0x00, 0x00, 0x04, 0x12, 0x4E, 0x1C, 0x02, 0x10,
+ 0x4C, 0x1C, 0x01, 0xEC, 0x27, 0x01, 0x89, 0x48, 0x00, 0xF2, 0x12, 0x11, 0x02, 0x14, 0x00, 0x16,
+ 0x85, 0xF0, 0x52, 0x0F, 0x38, 0x54, 0x00, 0xEA, 0x99, 0x00, 0x00, 0xF2, 0x60, 0x0B, 0x02, 0x80,
+ 0x48, 0xE4, 0x06, 0x00, 0x1C, 0x13, 0x00, 0xEC, 0x99, 0x00, 0x48, 0xE4, 0x01, 0x00, 0x0A, 0x12,
+ 0x04, 0x80, 0x30, 0xE4, 0x01, 0x00, 0x04, 0x40, 0x08, 0x10, 0x04, 0x80, 0x18, 0xE4, 0xFE, 0x00,
+ 0x04, 0x40, 0x00, 0x16, 0x02, 0xF6, 0xE0, 0x00, 0x02, 0x57, 0x03, 0x59, 0x01, 0xCC, 0x81, 0x48,
+ 0x22, 0x12, 0x00, 0x4E, 0x83, 0x5A, 0x90, 0x4C, 0x20, 0xE7, 0x00, 0x00, 0xC3, 0x58, 0x1B, 0xF4,
+ 0xFF, 0x00, 0x83, 0x55, 0x83, 0x55, 0x83, 0x55, 0x03, 0xF4, 0x00, 0x12, 0x8B, 0x55, 0x83, 0x59,
+ 0x00, 0x4E, 0x00, 0x16, 0x00, 0x4E, 0x02, 0xF6, 0xF0, 0x00, 0x02, 0x57, 0x03, 0x59, 0x00, 0x4E,
+ 0x83, 0x5A, 0x30, 0xE7, 0x00, 0x00, 0x20, 0xE7, 0x00, 0x00, 0x00, 0x16, 0x02, 0xF6, 0xF0, 0x00,
+ 0x02, 0x57, 0x03, 0x59, 0x01, 0xCC, 0x00, 0x4E, 0x83, 0x5A, 0x30, 0xE7, 0x00, 0x00, 0x80, 0x4C,
+ 0xC3, 0x58, 0x1B, 0xF4, 0xFF, 0x00, 0x83, 0x55, 0x83, 0x55, 0x83, 0x55, 0x03, 0xF4, 0x00, 0x12,
+ 0x83, 0x59, 0x00, 0x4E, 0x00, 0x16, 0x03, 0xF6, 0xE0, 0x00, 0x03, 0x57, 0x83, 0x59, 0x3A, 0x55,
+ 0x02, 0xCC, 0x45, 0x5A, 0x00, 0xF2, 0xF6, 0x0D, 0xC0, 0x5A, 0x40, 0x5C, 0x38, 0x54, 0x00, 0xCD,
+ 0x01, 0xCC, 0x4A, 0x46, 0x0A, 0x13, 0x83, 0x59, 0x00, 0x4C, 0x01, 0x48, 0x16, 0x13, 0x0C, 0x10,
+ 0xC5, 0x58, 0x00, 0xF2, 0xF6, 0x0D, 0x00, 0x4C, 0x01, 0x48, 0x08, 0x13, 0x05, 0xF6, 0xF0, 0x00,
+ 0x05, 0x57, 0x08, 0x10, 0x45, 0x58, 0x00, 0xF2, 0xF6, 0x0D, 0x8D, 0x56, 0x83, 0x5A, 0x80, 0x4C,
+ 0x05, 0x17, 0x00, 0x16, 0x02, 0x4B, 0x06, 0xF7, 0x04, 0x00, 0x62, 0x0B, 0x03, 0x82, 0x00, 0xF2,
+ 0xE2, 0x0D, 0x02, 0x80, 0x00, 0x4C, 0x45, 0xF4, 0x02, 0x00, 0x52, 0x14, 0x06, 0xF7, 0x02, 0x00,
+ 0x06, 0x14, 0x00, 0xF2, 0x54, 0x0F, 0x00, 0x16, 0x02, 0x4B, 0x01, 0xF6, 0xFF, 0x00, 0x38, 0x1C,
+ 0x05, 0xF4, 0x04, 0x00, 0x83, 0x5A, 0x18, 0xDF, 0x19, 0xDF, 0x1D, 0xF7, 0x3C, 0x00, 0xB8, 0xF0,
+ 0x4E, 0x10, 0x9C, 0x14, 0x01, 0x48, 0x1C, 0x13, 0x0E, 0xF7, 0x3C, 0x00, 0x03, 0xF7, 0x04, 0x00,
+ 0xAF, 0x19, 0x03, 0x42, 0x45, 0xF4, 0x02, 0x00, 0x83, 0x5A, 0x02, 0xCC, 0x02, 0x41, 0x45, 0xF4,
+ 0x02, 0x00, 0x00, 0x16, 0x91, 0x44, 0xD5, 0xF0, 0x3E, 0x10, 0x00, 0xF0, 0x9E, 0x02, 0x01, 0xF6,
+ 0xFF, 0x00, 0x38, 0x1C, 0x05, 0xF4, 0x04, 0x00, 0x83, 0x5A, 0x18, 0xDF, 0x19, 0xDF, 0x0E, 0xF7,
+ 0x3C, 0x00, 0x03, 0xF7, 0x04, 0x00, 0x0F, 0x79, 0x1C, 0xF7, 0x3C, 0x00, 0xB8, 0xF0, 0x9C, 0x10,
+ 0x4E, 0x14, 0x01, 0x48, 0x06, 0x13, 0x45, 0xF4, 0x04, 0x00, 0x00, 0x16, 0x91, 0x44, 0xD5, 0xF0,
+ 0x82, 0x10, 0x00, 0xF0, 0x9E, 0x02, 0x02, 0xF6, 0xFF, 0x00, 0x38, 0x1C, 0x2C, 0xBC, 0xAE, 0xBC,
+ 0xE2, 0x08, 0x00, 0xEC, 0xB8, 0x00, 0x02, 0x48, 0x1D, 0xF7, 0x80, 0x00, 0xB8, 0xF0, 0xCC, 0x10,
+ 0x1E, 0x14, 0x01, 0x48, 0x0E, 0x13, 0x0E, 0xF7, 0x80, 0x00, 0x38, 0x54, 0x03, 0x58, 0xAF, 0x19,
+ 0x82, 0x48, 0x00, 0x16, 0x82, 0x48, 0x12, 0x45, 0xD5, 0xF0, 0xBA, 0x10, 0x00, 0xF0, 0x9E, 0x02,
+ 0x39, 0xF0, 0xF8, 0x10, 0x38, 0x44, 0x00, 0x16, 0x7E, 0x18, 0x18, 0xF4, 0x03, 0x00, 0x04, 0x13,
+ 0x61, 0x18, 0x00, 0x16, 0x38, 0x1C, 0x00, 0xFC, 0x22, 0x01, 0x18, 0xF4, 0x01, 0x00, 0xF1, 0x12,
+ 0xE3, 0x10, 0x30, 0x44, 0x30, 0x44, 0x30, 0x44, 0xB1, 0xF0, 0x18, 0x11, 0x00, 0x16, 0x3E, 0x57,
+ 0x03, 0xF6, 0xE0, 0x00, 0x03, 0x57, 0x83, 0x59, 0x04, 0xCC, 0x01, 0x4A, 0x6A, 0x12, 0x45, 0x5A,
+ 0x00, 0xF2, 0xF6, 0x0D, 0x02, 0x4B, 0x70, 0x14, 0x34, 0x13, 0x02, 0x80, 0x48, 0xE4, 0x08, 0x00,
+ 0x18, 0x12, 0x9C, 0xE7, 0x02, 0x00, 0x9E, 0xE7, 0x15, 0x00, 0x00, 0xF2, 0xC6, 0x0F, 0x00, 0xF2,
+ 0x7A, 0x0A, 0x1E, 0x1C, 0x01, 0xF6, 0x01, 0x00, 0x00, 0x16, 0x30, 0xE4, 0x10, 0x00, 0x04, 0x40,
+ 0x00, 0xF2, 0xE2, 0x0D, 0x20, 0xE7, 0x01, 0x00, 0x01, 0xF6, 0x01, 0x00, 0x00, 0x16, 0x04, 0xDC,
+ 0x01, 0x4A, 0x24, 0x12, 0x45, 0x5A, 0x00, 0xF2, 0xF6, 0x0D, 0x43, 0x5B, 0x06, 0xEC, 0x98, 0x00,
+ 0x00, 0xF2, 0x38, 0x10, 0xC6, 0x59, 0x20, 0x14, 0x0A, 0x13, 0x00, 0xF2, 0xC6, 0x0F, 0x00, 0xF2,
+ 0x14, 0x10, 0xA7, 0x10, 0x83, 0x5A, 0xD7, 0x10, 0x0E, 0x47, 0x07, 0xE6, 0x10, 0x00, 0xCE, 0x47,
+ 0x5A, 0xF0, 0x20, 0x11, 0xB9, 0x54, 0x00, 0x16, 0x14, 0x90, 0x96, 0x90, 0x02, 0xFC, 0xA8, 0x00,
+ 0x03, 0xFC, 0xAA, 0x00, 0x48, 0x55, 0x02, 0x13, 0xC9, 0x55, 0x00, 0x16, 0x00, 0xEC, 0xBA, 0x00,
+ 0x10, 0x44, 0x00, 0xEA, 0xBA, 0x00, 0x00, 0x16, 0x03, 0xF6, 0xC0, 0x00, 0x00, 0xF2, 0x68, 0x0A,
+ 0x10, 0x44, 0x00, 0x4C, 0x00, 0x16
+};
+
+unsigned short _adv_mcode_size ASC_INITDATA =
+ sizeof(_adv_mcode_buf); /* 0x11D6 */
+unsigned long _adv_mcode_chksum ASC_INITDATA = 0x03494981UL;
+
+/* a_init.c */
+/*
+ * EEPROM Configuration.
+ *
+ * All drivers should use this structure to set the default EEPROM
+ * configuration. The BIOS now uses this structure when it is built.
+ * Additional structure information can be found in a_condor.h where
+ * the structure is defined.
+ */
+STATIC ADVEEP_CONFIG
+Default_EEPROM_Config ASC_INITDATA = {
+ ADV_EEPROM_BIOS_ENABLE, /* cfg_msw */
+ 0x0000, /* cfg_lsw */
+ 0xFFFF, /* disc_enable */
+ 0xFFFF, /* wdtr_able */
+ 0xFFFF, /* sdtr_able */
+ 0xFFFF, /* start_motor */
+ 0xFFFF, /* tagqng_able */
+ 0xFFFF, /* bios_scan */
+ 0, /* scam_tolerant */
+ 7, /* adapter_scsi_id */
+ 0, /* bios_boot_delay */
+ 3, /* scsi_reset_delay */
+ 0, /* bios_id_lun */
+ 0, /* termination */
+ 0, /* reserved1 */
+ 0xFFEF, /* bios_ctrl */
+ 0xFFFF, /* ultra_able */
+ 0, /* reserved2 */
+ ASC_DEF_MAX_HOST_QNG, /* max_host_qng */
+ ASC_DEF_MAX_DVC_QNG, /* max_dvc_qng */
+ 0, /* dvc_cntl */
+ 0, /* bug_fix */
+ 0, /* serial_number_word1 */
+ 0, /* serial_number_word2 */
+ 0, /* serial_number_word3 */
+ 0, /* check_sum */
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, /* oem_name[16] */
+ 0, /* dvc_err_code */
+ 0, /* adv_err_code */
+ 0, /* adv_err_addr */
+ 0, /* saved_dvc_err_code */
+ 0, /* saved_adv_err_code */
+ 0, /* saved_adv_err_addr */
+ 0 /* num_of_err */
+};
+
+/*
+ * Initialize the ADV_DVC_VAR structure.
+ *
+ * On failure set the ADV_DVC_VAR field 'err_code' and return ADV_ERROR.
+ *
+ * For a non-fatal error return a warning code. If there are no warnings
+ * then 0 is returned.
+ */
+ASC_INITFUNC(
+int
+AdvInitGetConfig(ADV_DVC_VAR *asc_dvc)
+)
+{
+ ushort warn_code;
+ AdvPortAddr iop_base;
+ uchar pci_cmd_reg;
+ int status;
+
+ warn_code = 0;
+ asc_dvc->err_code = 0;
+ iop_base = asc_dvc->iop_base;
+
+ /*
+ * PCI Command Register
+ */
+
+ if (((pci_cmd_reg = DvcAdvReadPCIConfigByte(asc_dvc,
+ AscPCIConfigCommandRegister))
+ & AscPCICmdRegBits_BusMastering)
+ != AscPCICmdRegBits_BusMastering)
+ {
+ pci_cmd_reg |= AscPCICmdRegBits_BusMastering;
+
+ DvcAdvWritePCIConfigByte(asc_dvc,
+ AscPCIConfigCommandRegister, pci_cmd_reg);
+
+ if (((DvcAdvReadPCIConfigByte(asc_dvc, AscPCIConfigCommandRegister))
+ & AscPCICmdRegBits_BusMastering)
+ != AscPCICmdRegBits_BusMastering)
+ {
+ warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
+ }
+ }
+
+ /*
+ * PCI Latency Timer
+ *
+ * If the "latency timer" register is 0x20 or above, then we don't need
+ * to change it. Otherwise, set it to 0x20 (i.e. set it to 0x20 if it
+ * comes up less than 0x20).
+ */
+ if (DvcAdvReadPCIConfigByte(asc_dvc, AscPCIConfigLatencyTimer) < 0x20) {
+ DvcAdvWritePCIConfigByte(asc_dvc, AscPCIConfigLatencyTimer, 0x20);
+ if (DvcAdvReadPCIConfigByte(asc_dvc, AscPCIConfigLatencyTimer) < 0x20)
+ {
+ warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
+ }
+ }
+
+ /*
+ * Save the state of the PCI Configuration Command Register
+ * "Parity Error Response Control" Bit. If the bit is clear (0),
+ * in AdvInitAsc3550Driver() tell the microcode to ignore DMA
+ * parity errors.
+ */
+ asc_dvc->cfg->control_flag = 0;
+ if (((DvcAdvReadPCIConfigByte(asc_dvc, AscPCIConfigCommandRegister)
+ & AscPCICmdRegBits_ParErrRespCtrl)) == 0)
+ {
+ asc_dvc->cfg->control_flag |= CONTROL_FLAG_IGNORE_PERR;
+ }
+
+ asc_dvc->cur_host_qng = 0;
+
+ asc_dvc->cfg->lib_version = (ADV_LIB_VERSION_MAJOR << 8) |
+ ADV_LIB_VERSION_MINOR;
+ asc_dvc->cfg->chip_version =
+ AdvGetChipVersion(iop_base, asc_dvc->bus_type);
+
+ /*
+ * Reset the chip to start and allow register writes.
+ */
+ if (AdvFindSignature(iop_base) == 0)
+ {
+ asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
+ return ADV_ERROR;
+ }
+ else {
+
+ AdvResetChip(asc_dvc);
+
+ if ((status = AdvInitFromEEP(asc_dvc)) == ADV_ERROR)
+ {
+ return ADV_ERROR;
+ }
+ warn_code |= status;
+
+ /*
+ * Reset the SCSI Bus if the EEPROM indicates that SCSI Bus
+ * Resets should be performed.
+ */
+ if (asc_dvc->bios_ctrl & BIOS_CTRL_RESET_SCSI_BUS)
+ {
+ AdvResetSCSIBus(asc_dvc);
+ }
+ }
+
+ return warn_code;
+}
+
+/*
+ * Initialize the ASC3550.
+ *
+ * On failure set the ADV_DVC_VAR field 'err_code' and return ADV_ERROR.
+ *
+ * For a non-fatal error return a warning code. If there are no warnings
+ * then 0 is returned.
+ */
+ASC_INITFUNC(
+int
+AdvInitAsc3550Driver(ADV_DVC_VAR *asc_dvc)
+)
+{
+ AdvPortAddr iop_base;
+ ushort warn_code;
+ ulong sum;
+ int begin_addr;
+ int end_addr;
+ int code_sum;
+ int word;
+ int rql_addr; /* RISC Queue List address */
+ int i;
+ ushort scsi_cfg1;
+ uchar biosmem[ASC_MC_BIOSLEN]; /* BIOS RISC Memory 0x40-0x8F. */
+
+ /* If there is already an error, don't continue. */
+ if (asc_dvc->err_code != 0)
+ {
+ return ADV_ERROR;
+ }
+
+ warn_code = 0;
+ iop_base = asc_dvc->iop_base;
+
+ /*
+ * Save the RISC memory BIOS region before writing the microcode.
+ * The BIOS may already be loaded and using its RISC LRAM region
+ * so its region must be saved and restored.
+ *
+ * Note: This code makes the assumption, which is currently true,
+ * that a chip reset does not clear RISC LRAM.
+ */
+ for (i = 0; i < ASC_MC_BIOSLEN; i++)
+ {
+ AdvReadByteLram(iop_base, ASC_MC_BIOSMEM + i, biosmem[i]);
+ }
+
+ /*
+ * Load the Microcode
+ *
+ * Write the microcode image to RISC memory starting at address 0.
+ */
+ AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, 0);
+ for (word = 0; word < _adv_mcode_size; word += 2)
+ {
+ AdvWriteWordAutoIncLram(iop_base,
+ *((ushort *) (&_adv_mcode_buf[word])));
+ }
+
+ /*
+ * Clear the rest of Condor's Internal RAM (8KB).
+ */
+ for (; word < ADV_CONDOR_MEMSIZE; word += 2)
+ {
+ AdvWriteWordAutoIncLram(iop_base, 0);
+ }
+
+ /*
+ * Verify the microcode checksum.
+ */
+ sum = 0;
+ AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, 0);
+ for (word = 0; word < _adv_mcode_size; word += 2)
+ {
+ sum += AdvReadWordAutoIncLram(iop_base);
+ }
+
+ if (sum != _adv_mcode_chksum)
+ {
+ asc_dvc->err_code |= ASC_IERR_MCODE_CHKSUM;
+ return ADV_ERROR;
+ }
+
+ /*
+ * Restore the RISC memory BIOS region.
+ */
+ for (i = 0; i < ASC_MC_BIOSLEN; i++)
+ {
+ AdvWriteByteLram(iop_base, ASC_MC_BIOSMEM + i, biosmem[i]);
+ }
+
+ /*
+ * Calculate and write the microcode code checksum to the microcode
+ * code checksum location ASC_MC_CODE_CHK_SUM (0x2C).
+ */
+ AdvReadWordLram(iop_base, ASC_MC_CODE_BEGIN_ADDR, begin_addr);
+ AdvReadWordLram(iop_base, ASC_MC_CODE_END_ADDR, end_addr);
+ code_sum = 0;
+ for (word = begin_addr; word < end_addr; word += 2)
+ {
+ code_sum += *((ushort *) (&_adv_mcode_buf[word]));
+ }
+ AdvWriteWordLram(iop_base, ASC_MC_CODE_CHK_SUM, code_sum);
+
+ /*
+ * Read microcode version and date.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_VERSION_DATE, asc_dvc->cfg->mcode_date);
+ AdvReadWordLram(iop_base, ASC_MC_VERSION_NUM, asc_dvc->cfg->mcode_version);
+
+ /*
+ * Initialize microcode operating variables
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_ADAPTER_SCSI_ID,
+ asc_dvc->chip_scsi_id);
+
+ /*
+ * If the PCI Configuration Command Register "Parity Error Response
+ * Control" Bit was clear (0), then set the microcode variable
+ * 'control_flag' CONTROL_FLAG_IGNORE_PERR flag to tell the microcode
+ * to ignore DMA parity errors.
+ */
+ if (asc_dvc->cfg->control_flag & CONTROL_FLAG_IGNORE_PERR)
+ {
+ /*
+ * Note: Don't remove the use of a temporary variable in
+ * the following code, otherwise the Microsoft C compiler
+ * will turn the following lines into a no-op.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_CONTROL_FLAG, word);
+ word |= CONTROL_FLAG_IGNORE_PERR;
+ AdvWriteWordLram(iop_base, ASC_MC_CONTROL_FLAG, word);
+ }
+
+ /*
+ * Set default microcode operating variables for WDTR, SDTR, and
+ * command tag queuing based on the EEPROM configuration values.
+ *
+ * These ADV_DVC_VAR fields and the microcode variables will be
+ * changed in AdvInquiryHandling() if it is found a device is
+ * incapable of a particular feature.
+ */
+
+ /*
+ * Set the microcode ULTRA target mask from EEPROM value. The
+ * SDTR target mask overrides the ULTRA target mask in the
+ * microcode so it is safe to set this value without determining
+ * whether the device supports SDTR.
+ *
+ * Note: There is no way to know whether a device supports ULTRA
+ * speed without attempting a SDTR ULTRA speed negotiation with
+ * the device. The device will reject the speed if it does not
+ * support it by responding with an SDTR message containing a
+ * slower speed.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_ULTRA_ABLE, asc_dvc->ultra_able);
+ AdvWriteWordLram(iop_base, ASC_MC_DISC_ENABLE, asc_dvc->cfg->disc_enable);
+
+
+ /*
+ * Set SCSI_CFG0 Microcode Default Value.
+ *
+ * The microcode will set the SCSI_CFG0 register using this value
+ * after it is started below.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SCSI_CFG0,
+ PARITY_EN | SEL_TMO_LONG | OUR_ID_EN | asc_dvc->chip_scsi_id);
+
+ /*
+ * Determine SCSI_CFG1 Microcode Default Value.
+ *
+ * The microcode will set the SCSI_CFG1 register using this value
+ * after it is started below.
+ */
+
+ /* Read current SCSI_CFG1 Register value. */
+ scsi_cfg1 = AdvReadWordRegister(iop_base, IOPW_SCSI_CFG1);
+
+ /*
+ * If all three connectors are in use, return an error.
+ */
+ if ((scsi_cfg1 & CABLE_ILLEGAL_A) == 0 ||
+ (scsi_cfg1 & CABLE_ILLEGAL_B) == 0)
+ {
+ asc_dvc->err_code |= ASC_IERR_ILLEGAL_CONNECTION;
+ return ADV_ERROR;
+ }
+
+ /*
+ * If the internal narrow cable is reversed all of the SCSI_CTRL
+ * register signals will be set. Check for and return an error if
+ * this condition is found.
+ */
+ if ((AdvReadWordRegister(iop_base, IOPW_SCSI_CTRL) & 0x3F07) == 0x3F07)
+ {
+ asc_dvc->err_code |= ASC_IERR_REVERSED_CABLE;
+ return ADV_ERROR;
+ }
+
+ /*
+ * If this is a differential board and a single-ended device
+ * is attached to one of the connectors, return an error.
+ */
+ if ((scsi_cfg1 & DIFF_MODE) && (scsi_cfg1 & DIFF_SENSE) == 0)
+ {
+ asc_dvc->err_code |= ASC_IERR_SINGLE_END_DEVICE;
+ return ADV_ERROR;
+ }
+
+ /*
+ * If automatic termination control is enabled, then set the
+ * termination value based on a table listed in a_condor.h.
+ *
+ * If manual termination was specified with an EEPROM setting
+ * then 'termination' was set-up in AdvInitFromEEP() and
+ * is ready to be 'ored' into SCSI_CFG1.
+ */
+ if (asc_dvc->cfg->termination == 0)
+ {
+ /*
+ * The software always controls termination by setting TERM_CTL_SEL.
+ * If TERM_CTL_SEL were set to 0, the hardware would set termination.
+ */
+ asc_dvc->cfg->termination |= TERM_CTL_SEL;
+
+ switch(scsi_cfg1 & CABLE_DETECT)
+ {
+ /* TERM_CTL_H: on, TERM_CTL_L: on */
+ case 0x3: case 0x7: case 0xB: case 0xD: case 0xE: case 0xF:
+ asc_dvc->cfg->termination |= (TERM_CTL_H | TERM_CTL_L);
+ break;
+
+ /* TERM_CTL_H: on, TERM_CTL_L: off */
+ case 0x1: case 0x5: case 0x9: case 0xA: case 0xC:
+ asc_dvc->cfg->termination |= TERM_CTL_H;
+ break;
+
+ /* TERM_CTL_H: off, TERM_CTL_L: off */
+ case 0x2: case 0x6:
+ break;
+ }
+ }
+
+ /*
+ * Clear any set TERM_CTL_H and TERM_CTL_L bits.
+ */
+ scsi_cfg1 &= ~TERM_CTL;
+
+ /*
+ * Invert the TERM_CTL_H and TERM_CTL_L bits and then
+ * set 'scsi_cfg1'. The TERM_POL bit does not need to be
+ * referenced, because the hardware internally inverts
+ * the Termination High and Low bits if TERM_POL is set.
+ */
+ scsi_cfg1 |= (TERM_CTL_SEL | (~asc_dvc->cfg->termination & TERM_CTL));
+
+ /*
+ * Set SCSI_CFG1 Microcode Default Value
+ *
+ * Set filter value and possibly modified termination control
+ * bits in the Microcode SCSI_CFG1 Register Value.
+ *
+ * The microcode will set the SCSI_CFG1 register using this value
+ * after it is started below.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SCSI_CFG1,
+ FLTR_11_TO_20NS | scsi_cfg1);
+
+ /*
+ * Set SEL_MASK Microcode Default Value
+ *
+ * The microcode will set the SEL_MASK register using this value
+ * after it is started below.
+ */
+ AdvWriteWordLram(iop_base, ASC_MC_DEFAULT_SEL_MASK,
+ ADV_TID_TO_TIDMASK(asc_dvc->chip_scsi_id));
+
+ /*
+ * Link all the RISC Queue Lists together in a doubly-linked
+ * NULL terminated list.
+ *
+ * Skip the NULL (0) queue which is not used.
+ */
+ for (i = 1, rql_addr = ASC_MC_RISC_Q_LIST_BASE + ASC_MC_RISC_Q_LIST_SIZE;
+ i < ASC_MC_RISC_Q_TOTAL_CNT;
+ i++, rql_addr += ASC_MC_RISC_Q_LIST_SIZE)
+ {
+ /*
+ * Set the current RISC Queue List's RQL_FWD and RQL_BWD pointers
+ * in a one word write and set the state (RQL_STATE) to free.
+ */
+ AdvWriteWordLram(iop_base, rql_addr, ((i + 1) + ((i - 1) << 8)));
+ AdvWriteByteLram(iop_base, rql_addr + RQL_STATE, ASC_MC_QS_FREE);
+ }
+
+ /*
+ * Set the Host and RISC Queue List pointers.
+ *
+ * Both sets of pointers are initialized with the same values:
+ * ASC_MC_RISC_Q_FIRST(0x01) and ASC_MC_RISC_Q_LAST (0xFF).
+ */
+ AdvWriteByteLram(iop_base, ASC_MC_HOST_NEXT_READY, ASC_MC_RISC_Q_FIRST);
+ AdvWriteByteLram(iop_base, ASC_MC_HOST_NEXT_DONE, ASC_MC_RISC_Q_LAST);
+
+ AdvWriteByteLram(iop_base, ASC_MC_RISC_NEXT_READY, ASC_MC_RISC_Q_FIRST);
+ AdvWriteByteLram(iop_base, ASC_MC_RISC_NEXT_DONE, ASC_MC_RISC_Q_LAST);
+
+ /*
+ * Finally, set up the last RISC Queue List (255) with
+ * a NULL forward pointer.
+ */
+ AdvWriteWordLram(iop_base, rql_addr, (ASC_MC_NULL_Q + ((i - 1) << 8)));
+ AdvWriteByteLram(iop_base, rql_addr + RQL_STATE, ASC_MC_QS_FREE);
+
+ AdvWriteByteRegister(iop_base, IOPB_INTR_ENABLES,
+ (ADV_INTR_ENABLE_HOST_INTR | ADV_INTR_ENABLE_GLOBAL_INTR));
+
+ /*
+ * Note: Don't remove the use of a temporary variable in
+ * the following code, otherwise the Microsoft C compiler
+ * will turn the following lines into a no-op.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_CODE_BEGIN_ADDR, word);
+ AdvWriteWordRegister(iop_base, IOPW_PC, word);
+
+ /* finally, finally, gentlemen, start your engine */
+ AdvWriteWordRegister(iop_base, IOPW_RISC_CSR, ADV_RISC_CSR_RUN);
+
+ return warn_code;
+}
+
+/*
+ * Read the board's EEPROM configuration. Set fields in ADV_DVC_VAR and
+ * ADV_DVC_CFG based on the EEPROM settings. The chip is stopped while
+ * all of this is done.
+ *
+ * On failure set the ADV_DVC_VAR field 'err_code' and return ADV_ERROR.
+ *
+ * For a non-fatal error return a warning code. If there are no warnings
+ * then 0 is returned.
+ *
+ * Note: Chip is stopped on entry.
+ */
+ASC_INITFUNC(
+STATIC int
+AdvInitFromEEP(ADV_DVC_VAR *asc_dvc)
+)
+{
+ AdvPortAddr iop_base;
+ ushort warn_code;
+ ADVEEP_CONFIG eep_config;
+ int i;
+
+ iop_base = asc_dvc->iop_base;
+
+ warn_code = 0;
+
+ /*
+ * Read the board's EEPROM configuration.
+ *
+ * Set default values if a bad checksum is found.
+ */
+ if (AdvGetEEPConfig(iop_base, &eep_config) != eep_config.check_sum)
+ {
+ warn_code |= ASC_WARN_EEPROM_CHKSUM;
+
+ /*
+ * Set EEPROM default values.
+ */
+ for (i = 0; i < sizeof(ADVEEP_CONFIG); i++)
+ {
+ *((uchar *) &eep_config + i) =
+ *((uchar *) &Default_EEPROM_Config + i);
+ }
+
+ /*
+ * Assume the 6 byte board serial number that was read
+ * from EEPROM is correct even if the EEPROM checksum
+ * failed.
+ */
+ eep_config.serial_number_word3 =
+ AdvReadEEPWord(iop_base, ASC_EEP_DVC_CFG_END - 1);
+ eep_config.serial_number_word2 =
+ AdvReadEEPWord(iop_base, ASC_EEP_DVC_CFG_END - 2);
+ eep_config.serial_number_word1 =
+ AdvReadEEPWord(iop_base, ASC_EEP_DVC_CFG_END - 3);
+ AdvSetEEPConfig(iop_base, &eep_config);
+ }
+
+ /*
+ * Set ADV_DVC_VAR and ADV_DVC_CFG variables from the
+ * EEPROM configuration that was read.
+ *
+ * This is the mapping of EEPROM fields to Adv Library fields.
+ */
+ asc_dvc->wdtr_able = eep_config.wdtr_able;
+ asc_dvc->sdtr_able = eep_config.sdtr_able;
+ asc_dvc->ultra_able = eep_config.ultra_able;
+ asc_dvc->tagqng_able = eep_config.tagqng_able;
+ asc_dvc->cfg->disc_enable = eep_config.disc_enable;
+ asc_dvc->max_host_qng = eep_config.max_host_qng;
+ asc_dvc->max_dvc_qng = eep_config.max_dvc_qng;
+ asc_dvc->chip_scsi_id = (eep_config.adapter_scsi_id & ADV_MAX_TID);
+ asc_dvc->start_motor = eep_config.start_motor;
+ asc_dvc->scsi_reset_wait = eep_config.scsi_reset_delay;
+ asc_dvc->cfg->bios_boot_wait = eep_config.bios_boot_delay;
+ asc_dvc->bios_ctrl = eep_config.bios_ctrl;
+ asc_dvc->no_scam = eep_config.scam_tolerant;
+ asc_dvc->cfg->serial1 = eep_config.serial_number_word1;
+ asc_dvc->cfg->serial2 = eep_config.serial_number_word2;
+ asc_dvc->cfg->serial3 = eep_config.serial_number_word3;
+
+ /*
+ * Set the host maximum queuing (max. 253, min. 16) and the per device
+ * maximum queuing (max. 63, min. 4).
+ */
+ if (eep_config.max_host_qng > ASC_DEF_MAX_HOST_QNG)
+ {
+ eep_config.max_host_qng = ASC_DEF_MAX_HOST_QNG;
+ } else if (eep_config.max_host_qng < ASC_DEF_MIN_HOST_QNG)
+ {
+ /* If the value is zero, assume it is uninitialized. */
+ if (eep_config.max_host_qng == 0)
+ {
+ eep_config.max_host_qng = ASC_DEF_MAX_HOST_QNG;
+ } else
+ {
+ eep_config.max_host_qng = ASC_DEF_MIN_HOST_QNG;
+ }
+ }
+
+ if (eep_config.max_dvc_qng > ASC_DEF_MAX_DVC_QNG)
+ {
+ eep_config.max_dvc_qng = ASC_DEF_MAX_DVC_QNG;
+ } else if (eep_config.max_dvc_qng < ASC_DEF_MIN_DVC_QNG)
+ {
+ /* If the value is zero, assume it is uninitialized. */
+ if (eep_config.max_dvc_qng == 0)
+ {
+ eep_config.max_dvc_qng = ASC_DEF_MAX_DVC_QNG;
+ } else
+ {
+ eep_config.max_dvc_qng = ASC_DEF_MIN_DVC_QNG;
+ }
+ }
+
+ /*
+ * If 'max_dvc_qng' is greater than 'max_host_qng', then
+ * set 'max_dvc_qng' to 'max_host_qng'.
+ */
+ if (eep_config.max_dvc_qng > eep_config.max_host_qng)
+ {
+ eep_config.max_dvc_qng = eep_config.max_host_qng;
+ }
+
+ /*
+ * Set ADV_DVC_VAR 'max_host_qng' and ADV_DVC_CFG 'max_dvc_qng'
+ * values based on possibly adjusted EEPROM values.
+ */
+ asc_dvc->max_host_qng = eep_config.max_host_qng;
+ asc_dvc->max_dvc_qng = eep_config.max_dvc_qng;
+
+
+ /*
+ * If the EEPROM 'termination' field is set to automatic (0), then set
+ * the ADV_DVC_CFG 'termination' field to automatic also.
+ *
+ * If the termination is specified with a non-zero 'termination'
+ * value check that a legal value is set and set the ADV_DVC_CFG
+ * 'termination' field appropriately.
+ */
+ if (eep_config.termination == 0)
+ {
+ asc_dvc->cfg->termination = 0; /* auto termination */
+ } else
+ {
+ /* Enable manual control with low off / high off. */
+ if (eep_config.termination == 1)
+ {
+ asc_dvc->cfg->termination = TERM_CTL_SEL;
+
+ /* Enable manual control with low off / high on. */
+ } else if (eep_config.termination == 2)
+ {
+ asc_dvc->cfg->termination = TERM_CTL_SEL | TERM_CTL_H;
+
+ /* Enable manual control with low on / high on. */
+ } else if (eep_config.termination == 3)
+ {
+ asc_dvc->cfg->termination = TERM_CTL_SEL | TERM_CTL_H | TERM_CTL_L;
+ } else
+ {
+ /*
+ * The EEPROM 'termination' field contains a bad value. Use
+ * automatic termination instead.
+ */
+ asc_dvc->cfg->termination = 0;
+ warn_code |= ASC_WARN_EEPROM_TERMINATION;
+ }
+ }
+
+ return warn_code;
+}
+
+/*
+ * Read EEPROM configuration into the specified buffer.
+ *
+ * Return a checksum based on the EEPROM configuration read.
+ */
+ASC_INITFUNC(
+STATIC ushort
+AdvGetEEPConfig(AdvPortAddr iop_base, ADVEEP_CONFIG *cfg_buf)
+)
+{
+ ushort wval, chksum;
+ ushort *wbuf;
+ int eep_addr;
+
+ wbuf = (ushort *) cfg_buf;
+ chksum = 0;
+
+ for (eep_addr = ASC_EEP_DVC_CFG_BEGIN;
+ eep_addr < ASC_EEP_DVC_CFG_END;
+ eep_addr++, wbuf++)
+ {
+ wval = AdvReadEEPWord(iop_base, eep_addr);
+ chksum += wval;
+ *wbuf = wval;
+ }
+ *wbuf = AdvReadEEPWord(iop_base, eep_addr);
+ wbuf++;
+ for (eep_addr = ASC_EEP_DVC_CTL_BEGIN;
+ eep_addr < ASC_EEP_MAX_WORD_ADDR;
+ eep_addr++, wbuf++)
+ {
+ *wbuf = AdvReadEEPWord(iop_base, eep_addr);
+ }
+ return chksum;
+}
+
+/*
+ * Read the EEPROM from specified location
+ */
+ASC_INITFUNC(
+STATIC ushort
+AdvReadEEPWord(AdvPortAddr iop_base, int eep_word_addr)
+)
+{
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD,
+ ASC_EEP_CMD_READ | eep_word_addr);
+ AdvWaitEEPCmd(iop_base);
+ return AdvReadWordRegister(iop_base, IOPW_EE_DATA);
+}
+
+/*
+ * Wait for EEPROM command to complete
+ */
+ASC_INITFUNC(
+STATIC void
+AdvWaitEEPCmd(AdvPortAddr iop_base)
+)
+{
+ int eep_delay_ms;
+
+ for (eep_delay_ms = 0; eep_delay_ms < ASC_EEP_DELAY_MS; eep_delay_ms++)
+ {
+ if (AdvReadWordRegister(iop_base, IOPW_EE_CMD) & ASC_EEP_CMD_DONE)
+ {
+ break;
+ }
+ DvcSleepMilliSecond(1);
+ }
+ if ((AdvReadWordRegister(iop_base, IOPW_EE_CMD) & ASC_EEP_CMD_DONE) == 0)
+ {
+ ADV_ASSERT(0);
+ }
+ return;
+}
+
+/*
+ * Write the EEPROM from 'cfg_buf'.
+ */
+ASC_INITFUNC(
+STATIC void
+AdvSetEEPConfig(AdvPortAddr iop_base, ADVEEP_CONFIG *cfg_buf)
+)
+{
+ ushort *wbuf;
+ ushort addr, chksum;
+
+ wbuf = (ushort *) cfg_buf;
+ chksum = 0;
+
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE_ABLE);
+ AdvWaitEEPCmd(iop_base);
+
+ /*
+ * Write EEPROM from word 0 to word 15
+ */
+ for (addr = ASC_EEP_DVC_CFG_BEGIN;
+ addr < ASC_EEP_DVC_CFG_END; addr++, wbuf++)
+ {
+ chksum += *wbuf;
+ AdvWriteWordRegister(iop_base, IOPW_EE_DATA, *wbuf);
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr);
+ AdvWaitEEPCmd(iop_base);
+ DvcSleepMilliSecond(ASC_EEP_DELAY_MS);
+ }
+
+ /*
+ * Write EEPROM checksum at word 18
+ */
+ AdvWriteWordRegister(iop_base, IOPW_EE_DATA, chksum);
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr);
+ AdvWaitEEPCmd(iop_base);
+ wbuf++; /* skip over check_sum */
+
+ /*
+ * Write EEPROM OEM name at words 19 to 26
+ */
+ for (addr = ASC_EEP_DVC_CTL_BEGIN;
+ addr < ASC_EEP_MAX_WORD_ADDR; addr++, wbuf++)
+ {
+ AdvWriteWordRegister(iop_base, IOPW_EE_DATA, *wbuf);
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE | addr);
+ AdvWaitEEPCmd(iop_base);
+ }
+ AdvWriteWordRegister(iop_base, IOPW_EE_CMD, ASC_EEP_CMD_WRITE_DISABLE);
+ AdvWaitEEPCmd(iop_base);
+ return;
+}
+
+/*
+ * This function resets the chip and SCSI bus
+ *
+ * It is up to the caller to add a delay to let the bus settle after
+ * calling this function.
+ *
+ * The SCSI_CFG0, SCSI_CFG1, and MEM_CFG registers are set-up in
+ * AdvInitAsc3550Driver(). Here when doing a write to one of these
+ * registers read first and then write.
+ *
+ * Note: A SCSI Bus Reset can not be done until after the EEPROM
+ * configuration is read to determine whether SCSI Bus Resets
+ * should be performed.
+ */
+ASC_INITFUNC(
+STATIC void
+AdvResetChip(ADV_DVC_VAR *asc_dvc)
+)
+{
+ AdvPortAddr iop_base;
+ ushort word;
+ uchar byte;
+
+ iop_base = asc_dvc->iop_base;
+
+ /*
+ * Reset Chip.
+ */
+ AdvWriteWordRegister(iop_base, IOPW_CTRL_REG, ADV_CTRL_REG_CMD_RESET);
+ DvcSleepMilliSecond(100);
+ AdvWriteWordRegister(iop_base, IOPW_CTRL_REG, ADV_CTRL_REG_CMD_WR_IO_REG);
+
+ /*
+ * Initialize Chip registers.
+ *
+ * Note: Don't remove the use of a temporary variable in the following
+ * code, otherwise the Microsoft C compiler will turn the following lines
+ * into a no-op.
+ */
+ byte = AdvReadByteRegister(iop_base, IOPB_MEM_CFG);
+ byte |= RAM_SZ_8KB;
+ AdvWriteByteRegister(iop_base, IOPB_MEM_CFG, byte);
+
+ word = AdvReadWordRegister(iop_base, IOPW_SCSI_CFG1);
+ word &= ~BIG_ENDIAN;
+ AdvWriteWordRegister(iop_base, IOPW_SCSI_CFG1, word);
+
+ /*
+ * Setting the START_CTL_EMFU 3:2 bits sets a FIFO threshold
+ * of 128 bytes. This register is only accessible to the host.
+ */
+ AdvWriteByteRegister(iop_base, IOPB_DMA_CFG0,
+ START_CTL_EMFU | READ_CMD_MRM);
+}
+
+/* a_advlib.c */
+/*
+ * Description:
+ * Send a SCSI request to the ASC3550 chip
+ *
+ * If there is no SG list for the request, set 'sg_entry_cnt' to 0.
+ *
+ * If 'sg_real_addr' is non-zero on entry, AscGetSGList() will not be
+ * called. It is assumed the caller has already initialized 'sg_real_addr'.
+ *
+ * Return:
+ * ADV_SUCCESS(1) - the request is in the mailbox
+ * ADV_BUSY(0) - total request count > 253, try later
+ * ADV_ERROR(-1) - invalid scsi request Q
+ */
+STATIC int
+AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc,
+ ADV_SCSI_REQ_Q *scsiq)
+{
+ if (scsiq == (ADV_SCSI_REQ_Q *) 0L)
+ {
+ /* 'scsiq' should never be NULL. */
+ ADV_ASSERT(0);
+ return ADV_ERROR;
+ }
+
+ return AdvSendScsiCmd(asc_dvc, scsiq);
+}
+
+/*
+ * Reset SCSI Bus and purge all outstanding requests.
+ *
+ * Return Value:
+ * ADV_TRUE(1) - All requests are purged and SCSI Bus is reset.
+ *
+ * Note: Should always return ADV_TRUE.
+ */
+STATIC int
+AdvResetSB(ADV_DVC_VAR *asc_dvc)
+{
+ int status;
+
+ status = AdvSendIdleCmd(asc_dvc, (ushort) IDLE_CMD_SCSI_RESET, 0L, 0);
+
+ AdvResetSCSIBus(asc_dvc);
+
+ return status;
+}
+
+/*
+ * Reset SCSI Bus and delay.
+ */
+STATIC void
+AdvResetSCSIBus(ADV_DVC_VAR *asc_dvc)
+{
+ AdvPortAddr iop_base;
+ ushort scsi_ctrl;
+
+ iop_base = asc_dvc->iop_base;
+
+ /*
+ * The microcode currently sets the SCSI Bus Reset signal while
+ * handling the AscSendIdleCmd() IDLE_CMD_SCSI_RESET command above.
+ * But the SCSI Bus Reset Hold Time in the microcode is not deterministic
+ * (it may in fact be for less than the SCSI Spec. minimum of 25 us).
+ * Therefore on return the Adv Library sets the SCSI Bus Reset signal
+ * for ASC_SCSI_RESET_HOLD_TIME_US, which is defined to be greater
+ * than 25 us.
+ */
+ scsi_ctrl = AdvReadWordRegister(iop_base, IOPW_SCSI_CTRL);
+ AdvWriteWordRegister(iop_base, IOPW_SCSI_CTRL,
+ scsi_ctrl | ADV_SCSI_CTRL_RSTOUT);
+ DvcDelayMicroSecond(asc_dvc, (ushort) ASC_SCSI_RESET_HOLD_TIME_US);
+ AdvWriteWordRegister(iop_base, IOPW_SCSI_CTRL,
+ scsi_ctrl & ~ADV_SCSI_CTRL_RSTOUT);
+
+ DvcSleepMilliSecond((ulong) asc_dvc->scsi_reset_wait * 1000);
+}
+
+
+/*
+ * Adv Library Interrupt Service Routine
+ *
+ * This function is called by a driver's interrupt service routine.
+ * The function disables and re-enables interrupts.
+ *
+ * When a microcode idle command is completed, the ADV_DVC_VAR
+ * 'idle_cmd_done' field is set to ADV_TRUE.
+ *
+ * Note: AdvISR() can be called when interrupts are disabled or even
+ * when there is no hardware interrupt condition present. It will
+ * always check for completed idle commands and microcode requests.
+ * This is an important feature that shouldn't be changed because it
+ * allows commands to be completed from polling mode loops.
+ *
+ * Return:
+ * ADV_TRUE(1) - interrupt was pending
+ * ADV_FALSE(0) - no interrupt was pending
+ */
+STATIC int
+AdvISR(ADV_DVC_VAR *asc_dvc)
+{
+ AdvPortAddr iop_base;
+ uchar int_stat;
+ ushort next_done_loc, target_bit;
+ int completed_q;
+ int flags;
+ ADV_SCSI_REQ_Q *scsiq;
+ ASC_REQ_SENSE *sense_data;
+ int ret;
+
+ flags = DvcEnterCritical();
+ iop_base = asc_dvc->iop_base;
+
+ if (AdvIsIntPending(iop_base))
+ {
+ ret = ADV_TRUE;
+ } else
+ {
+ ret = ADV_FALSE;
+ }
+
+ /* Reading the register clears the interrupt. */
+ int_stat = AdvReadByteRegister(iop_base, IOPB_INTR_STATUS_REG);
+
+ if (int_stat & ADV_INTR_STATUS_INTRB)
+ {
+ asc_dvc->idle_cmd_done = ADV_TRUE;
+ }
+
+ /*
+ * Notify the driver of a hardware detected SCSI Bus Reset.
+ */
+ if (int_stat & ADV_INTR_STATUS_INTRC)
+ {
+ if (asc_dvc->sbreset_callback != 0)
+ {
+ (*(ADV_SBRESET_CALLBACK) asc_dvc->sbreset_callback)(asc_dvc);
+ }
+ }
+
+ /*
+ * ASC_MC_HOST_NEXT_DONE (0x129) is actually the last completed RISC
+ * Queue List request. Its forward pointer (RQL_FWD) points to the
+ * current completed RISC Queue List request.
+ */
+ AdvReadByteLram(iop_base, ASC_MC_HOST_NEXT_DONE, next_done_loc);
+ next_done_loc = ASC_MC_RISC_Q_LIST_BASE +
+ (next_done_loc * ASC_MC_RISC_Q_LIST_SIZE) + RQL_FWD;
+
+ AdvReadByteLram(iop_base, next_done_loc, completed_q);
+
+ /* Loop until all completed Q's are processed. */
+ while (completed_q != ASC_MC_NULL_Q)
+ {
+ AdvWriteByteLram(iop_base, ASC_MC_HOST_NEXT_DONE, completed_q);
+
+ next_done_loc = ASC_MC_RISC_Q_LIST_BASE +
+ (completed_q * ASC_MC_RISC_Q_LIST_SIZE);
+
+ /*
+ * Read the ADV_SCSI_REQ_Q virtual address pointer from
+ * the RISC list entry. The microcode has changed the
+ * ADV_SCSI_REQ_Q physical address to its virtual address.
+ *
+ * Refer to comments at the end of AdvSendScsiCmd() for
+ * more information on the RISC list structure.
+ */
+ {
+ ushort lsw, msw;
+ AdvReadWordLram(iop_base, next_done_loc + RQL_PHYADDR, lsw);
+ AdvReadWordLram(iop_base, next_done_loc + RQL_PHYADDR + 2, msw);
+
+ scsiq = (ADV_SCSI_REQ_Q *) (((ulong) msw << 16) | lsw);
+ }
+ ADV_ASSERT(scsiq != NULL);
+
+ target_bit = ADV_TID_TO_TIDMASK(scsiq->target_id);
+
+ /*
+ * Clear request microcode control flag.
+ */
+ scsiq->cntl = 0;
+
+ /*
+ * Check Condition handling
+ */
+ if ((scsiq->done_status == QD_WITH_ERROR) &&
+ (scsiq->scsi_status == SS_CHK_CONDITION) &&
+ (sense_data = (ASC_REQ_SENSE *) scsiq->vsense_addr) != 0 &&
+ (scsiq->orig_sense_len - scsiq->sense_len) >= ASC_MIN_SENSE_LEN)
+ {
+ /*
+ * Command returned with a check condition and valid
+ * sense data.
+ */
+ }
+ /*
+ * If the command that completed was a SCSI INQUIRY and
+ * LUN 0 was sent the command, then process the INQUIRY
+ * command information for the device.
+ */
+ else if (scsiq->done_status == QD_NO_ERROR &&
+ scsiq->cdb[0] == SCSICMD_Inquiry &&
+ scsiq->target_lun == 0)
+ {
+ AdvInquiryHandling(asc_dvc, scsiq);
+ }
+
+
+ /* Change the RISC Queue List state to free. */
+ AdvWriteByteLram(iop_base, next_done_loc + RQL_STATE, ASC_MC_QS_FREE);
+
+ /* Get the RISC Queue List forward pointer. */
+ AdvReadByteLram(iop_base, next_done_loc + RQL_FWD, completed_q);
+
+ /*
+ * Notify the driver of the completed request by passing
+ * the ADV_SCSI_REQ_Q pointer to its callback function.
+ */
+ ADV_ASSERT(asc_dvc->cur_host_qng > 0);
+ asc_dvc->cur_host_qng--;
+ scsiq->a_flag |= ADV_SCSIQ_DONE;
+ (*(ADV_ISR_CALLBACK) asc_dvc->isr_callback)(asc_dvc, scsiq);
+ /*
+ * Note: After the driver callback function is called, 'scsiq'
+ * can no longer be referenced.
+ *
+ * Fall through and continue processing other completed
+ * requests...
+ */
+
+ /*
+ * Disable interrupts again in case the driver inadvertently
+ * enabled interrupts in its callback function.
+ *
+ * The DvcEnterCritical() return value is ignored, because
+ * the 'flags' saved when AdvISR() was first entered will be
+ * used to restore the interrupt flag on exit.
+ */
+ (void) DvcEnterCritical();
+ }
+ DvcLeaveCritical(flags);
+ return ret;
+}
+
+/*
+ * Send an idle command to the chip and wait for completion.
+ *
+ * Interrupts do not have to be enabled on entry.
+ *
+ * Return Values:
+ * ADV_TRUE - command completed successfully
+ * ADV_FALSE - command failed
+ */
+STATIC int
+AdvSendIdleCmd(ADV_DVC_VAR *asc_dvc,
+ ushort idle_cmd,
+ ulong idle_cmd_parameter,
+ int flags)
+{
+ int last_int_level;
+ ulong i;
+ AdvPortAddr iop_base;
+ int ret;
+
+ asc_dvc->idle_cmd_done = 0;
+
+ last_int_level = DvcEnterCritical();
+ iop_base = asc_dvc->iop_base;
+
+ /*
+ * Write the idle command value after the idle command parameter
+ * has been written to avoid a race condition. If the order is not
+ * followed, the microcode may process the idle command before the
+ * parameters have been written to LRAM.
+ */
+ AdvWriteDWordLram(iop_base, ASC_MC_IDLE_PARA_STAT, idle_cmd_parameter);
+ AdvWriteWordLram(iop_base, ASC_MC_IDLE_CMD, idle_cmd);
+ DvcLeaveCritical(last_int_level);
+
+ /*
+ * If the 'flags' argument contains the ADV_NOWAIT flag, then
+ * return with success.
+ */
+ if (flags & ADV_NOWAIT)
+ {
+ return ADV_TRUE;
+ }
+
+ for (i = 0; i < SCSI_WAIT_10_SEC * SCSI_MS_PER_SEC; i++)
+ {
+ /*
+ * 'idle_cmd_done' is set by AdvISR().
+ */
+ if (asc_dvc->idle_cmd_done)
+ {
+ break;
+ }
+ DvcSleepMilliSecond(1);
+
+ /*
+ * If interrupts were disabled on entry to AdvSendIdleCmd(),
+ * then they will still be disabled here. Call AdvISR() to
+ * check for the idle command completion.
+ */
+ (void) AdvISR(asc_dvc);
+ }
+
+ last_int_level = DvcEnterCritical();
+
+ if (asc_dvc->idle_cmd_done == ADV_FALSE)
+ {
+ ADV_ASSERT(0); /* The idle command should never timeout. */
+ return ADV_FALSE;
+ } else
+ {
+ AdvReadWordLram(iop_base, ASC_MC_IDLE_PARA_STAT, ret);
+ return ret;
+ }
+}
+
+/*
+ * Send the SCSI request block to the adapter
+ *
+ * Each of the 255 Adv Library/Microcode RISC Lists or mailboxes has the
+ * following structure:
+ *
+ * 0: RQL_FWD - RISC list forward pointer (1 byte)
+ * 1: RQL_BWD - RISC list backward pointer (1 byte)
+ * 2: RQL_STATE - RISC list state byte - free, ready, done, aborted (1 byte)
+ * 3: RQL_TID - request target id (1 byte)
+ * 4: RQL_PHYADDR - ADV_SCSI_REQ_Q physical pointer (4 bytes)
+ *
+ * Return:
+ * ADV_SUCCESS(1) - the request is in the mailbox
+ * ADV_BUSY(0) - total request count > 253, try later
+ */
+STATIC int
+AdvSendScsiCmd(
+ ADV_DVC_VAR *asc_dvc,
+ ADV_SCSI_REQ_Q *scsiq)
+{
+ ushort next_ready_loc;
+ uchar next_ready_loc_fwd;
+ int last_int_level;
+ AdvPortAddr iop_base;
+ long req_size;
+ ulong q_phy_addr;
+
+ /*
+ * The ADV_SCSI_REQ_Q 'target_id' field should never be equal
+ * to the host adapter ID or exceed ADV_MAX_TID.
+ */
+ if (scsiq->target_id == asc_dvc->chip_scsi_id ||
+ scsiq->target_id > ADV_MAX_TID)
+ {
+ scsiq->host_status = QHSTA_M_INVALID_DEVICE;
+ scsiq->done_status = QD_WITH_ERROR;
+ return ADV_ERROR;
+ }
+
+ iop_base = asc_dvc->iop_base;
+
+ last_int_level = DvcEnterCritical();
+
+ if (asc_dvc->cur_host_qng >= asc_dvc->max_host_qng)
+ {
+ DvcLeaveCritical(last_int_level);
+ return ADV_BUSY;
+ } else
+ {
+ ADV_ASSERT(asc_dvc->cur_host_qng < ASC_MC_RISC_Q_TOTAL_CNT);
+ asc_dvc->cur_host_qng++;
+ }
+
+ /*
+ * Clear the ADV_SCSI_REQ_Q done flag.
+ */
+ scsiq->a_flag &= ~ADV_SCSIQ_DONE;
+
+ /*
+ * Save the original sense buffer length.
+ *
+ * After the request completes 'sense_len' will be set to the residual
+ * byte count of the Auto-Request Sense if a command returns CHECK
+ * CONDITION and the Sense Data is valid indicated by 'host_status' not
+ * being set to QHSTA_M_AUTO_REQ_SENSE_FAIL. To determine the valid
+ * Sense Data Length subtract 'sense_len' from 'orig_sense_len'.
+ */
+ scsiq->orig_sense_len = scsiq->sense_len;
+
+ AdvReadByteLram(iop_base, ASC_MC_HOST_NEXT_READY, next_ready_loc);
+ next_ready_loc = ASC_MC_RISC_Q_LIST_BASE +
+ (next_ready_loc * ASC_MC_RISC_Q_LIST_SIZE);
+
+ /*
+ * Write the physical address of the Q to the mailbox.
+ * We need to skip the first four bytes, because the microcode
+ * uses them internally for linking Q's together.
+ */
+ req_size = sizeof(ADV_SCSI_REQ_Q);
+ q_phy_addr = DvcGetPhyAddr(asc_dvc, scsiq,
+ (uchar *) scsiq, &req_size,
+ ADV_IS_SCSIQ_FLAG);
+ ADV_ASSERT(ADV_DWALIGN(q_phy_addr) == q_phy_addr);
+ ADV_ASSERT(req_size >= sizeof(ADV_SCSI_REQ_Q));
+
+ scsiq->scsiq_ptr = (ADV_SCSI_REQ_Q *) scsiq;
+
+ /*
+ * The RISC list structure, which 'next_ready_loc' is a pointer
+ * to in microcode LRAM, has the format detailed in the comment
+ * header for this function.
+ *
+ * Write the ADV_SCSI_REQ_Q physical pointer to 'next_ready_loc' request.
+ */
+ AdvWriteDWordLram(iop_base, next_ready_loc + RQL_PHYADDR, q_phy_addr);
+
+ /* Write target_id to 'next_ready_loc' request. */
+ AdvWriteByteLram(iop_base, next_ready_loc + RQL_TID, scsiq->target_id);
+
+ /*
+ * Set the ASC_MC_HOST_NEXT_READY (0x128) microcode variable to
+ * the 'next_ready_loc' request forward pointer.
+ *
+ * Do this *before* changing the 'next_ready_loc' queue to QS_READY.
+ * After the state is changed to QS_READY 'RQL_FWD' will be changed
+ * by the microcode.
+ *
+ * NOTE: The temporary variable 'next_ready_loc_fwd' is required to
+ * prevent some compilers from optimizing out 'AdvReadByteLram()' if
+ * it were used as the 3rd argument to 'AdvWriteByteLram()'.
+ */
+ AdvReadByteLram(iop_base, next_ready_loc + RQL_FWD, next_ready_loc_fwd);
+ AdvWriteByteLram(iop_base, ASC_MC_HOST_NEXT_READY, next_ready_loc_fwd);
+
+ /*
+ * Change the state of 'next_ready_loc' request from QS_FREE to
+ * QS_READY which will cause the microcode to pick it up and
+ * execute it.
+ *
+ * Can't reference 'next_ready_loc' after changing the request
+ * state to QS_READY. The microcode now owns the request.
+ */
+ AdvWriteByteLram(iop_base, next_ready_loc + RQL_STATE, ASC_MC_QS_READY);
+
+ DvcLeaveCritical(last_int_level);
+ return ADV_SUCCESS;
+}
+
+/*
+ * Inquiry Information Byte 7 Handling
+ *
+ * Handle SCSI Inquiry Command information for a device by setting
+ * microcode operating variables that affect WDTR, SDTR, and Tag
+ * Queuing.
+ */
+STATIC void
+AdvInquiryHandling(
+ ADV_DVC_VAR *asc_dvc,
+ ADV_SCSI_REQ_Q *scsiq)
+{
+ AdvPortAddr iop_base;
+ uchar tid;
+ ASC_SCSI_INQUIRY *inq;
+ ushort tidmask;
+ ushort cfg_word;
+
+ /*
+ * AdvInquiryHandling() requires up to INQUIRY information Byte 7
+ * to be available.
+ *
+ * If less than 8 bytes of INQUIRY information were requested or less
+ * than 8 bytes were transferred, then return. cdb[4] is the request
+ * length and the ADV_SCSI_REQ_Q 'data_cnt' field is set by the
+ * microcode to the transfer residual count.
+ */
+ if (scsiq->cdb[4] < 8 || (scsiq->cdb[4] - scsiq->data_cnt) < 8)
+ {
+ return;
+ }
+
+ iop_base = asc_dvc->iop_base;
+ tid = scsiq->target_id;
+ inq = (ASC_SCSI_INQUIRY *) scsiq->vdata_addr;
+
+ /*
+ * WDTR, SDTR, and Tag Queuing cannot be enabled for old devices.
+ */
+ if (inq->byte3.rsp_data_fmt < 2 && inq->byte2.ansi_apr_ver < 2)
+ {
+ return;
+ } else
+ {
+ /*
+ * INQUIRY Byte 7 Handling
+ *
+ * Use a device's INQUIRY byte 7 to determine whether it
+ * supports WDTR, SDTR, and Tag Queuing. If the feature
+ * is enabled in the EEPROM and the device supports the
+ * feature, then enable it in the microcode.
+ */
+
+ tidmask = ADV_TID_TO_TIDMASK(tid);
+
+ /*
+ * Wide Transfers
+ *
+ * If the EEPROM enabled WDTR for the device and the device
+ * supports wide bus (16 bit) transfers, then turn on the
+ * device's 'wdtr_able' bit and write the new value to the
+ * microcode.
+ */
+ if ((asc_dvc->wdtr_able & tidmask) && inq->byte7.WBus16)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_WDTR_ABLE, cfg_word);
+ if ((cfg_word & tidmask) == 0)
+ {
+ cfg_word |= tidmask;
+ AdvWriteWordLram(iop_base, ASC_MC_WDTR_ABLE, cfg_word);
+
+ /*
+ * Clear the microcode "WDTR negotiation" done indicator
+ * for the target to cause it to negotiate with the new
+ * setting set above.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_WDTR_DONE, cfg_word);
+ cfg_word &= ~tidmask;
+ AdvWriteWordLram(iop_base, ASC_MC_WDTR_DONE, cfg_word);
+ }
+ }
+
+ /*
+ * Synchronous Transfers
+ *
+ * If the EEPROM enabled SDTR for the device and the device
+ * supports synchronous transfers, then turn on the device's
+ * 'sdtr_able' bit. Write the new value to the microcode.
+ */
+ if ((asc_dvc->sdtr_able & tidmask) && inq->byte7.Sync)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_SDTR_ABLE, cfg_word);
+ if ((cfg_word & tidmask) == 0)
+ {
+ cfg_word |= tidmask;
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_ABLE, cfg_word);
+
+ /*
+ * Clear the microcode "SDTR negotiation" done indicator
+ * for the target to cause it to negotiate with the new
+ * setting set above.
+ */
+ AdvReadWordLram(iop_base, ASC_MC_SDTR_DONE, cfg_word);
+ cfg_word &= ~tidmask;
+ AdvWriteWordLram(iop_base, ASC_MC_SDTR_DONE, cfg_word);
+ }
+ }
+
+ /*
+ * If the EEPROM enabled Tag Queuing for device and the
+ * device supports Tag Queuing, then turn on the device's
+ * 'tagqng_enable' bit in the microcode and set the microcode
+ * maximum command count to the ADV_DVC_VAR 'max_dvc_qng'
+ * value.
+ *
+ * Tag Queuing is disabled for the BIOS which runs in polled
+ * mode and would see no benefit from Tag Queuing. Also by
+ * disabling Tag Queuing in the BIOS devices with Tag Queuing
+ * bugs will at least work with the BIOS.
+ */
+ if ((asc_dvc->tagqng_able & tidmask) && inq->byte7.CmdQue)
+ {
+ AdvReadWordLram(iop_base, ASC_MC_TAGQNG_ABLE, cfg_word);
+ cfg_word |= tidmask;
+ AdvWriteWordLram(iop_base, ASC_MC_TAGQNG_ABLE, cfg_word);
+ AdvWriteByteLram(iop_base, ASC_MC_NUMBER_OF_MAX_CMD + tid,
+ asc_dvc->max_dvc_qng);
+ }
+ }
+}
diff --git a/linux/src/drivers/scsi/advansys.h b/linux/src/drivers/scsi/advansys.h
new file mode 100644
index 00000000..72e8aefe
--- /dev/null
+++ b/linux/src/drivers/scsi/advansys.h
@@ -0,0 +1,174 @@
+/* $Id: advansys.h,v 1.1 1999/04/26 05:54:08 tb Exp $ */
+
+/*
+ * advansys.h - Linux Host Driver for AdvanSys SCSI Adapters
+ *
+ * Copyright (c) 1995-1998 Advanced System Products, Inc.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * There is an AdvanSys Linux WWW page at:
+ * http://www.advansys.com/linux.html
+ *
+ * The latest version of the AdvanSys driver is available at:
+ * ftp://ftp.advansys.com/pub/linux
+ *
+ * Please send questions, comments, bug reports to:
+ * bobf@advansys.com (Bob Frey)
+ */
+
+#ifndef _ADVANSYS_H
+#define _ADVANSYS_H
+
+/* Convert Linux Version, Patch-level, Sub-level to LINUX_VERSION_CODE. */
+#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif /* LINUX_VERSION_CODE */
+
+/*
+ * Scsi_Host_Template function prototypes.
+ */
+int advansys_detect(Scsi_Host_Template *);
+int advansys_release(struct Scsi_Host *);
+const char *advansys_info(struct Scsi_Host *);
+int advansys_command(Scsi_Cmnd *);
+int advansys_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int advansys_abort(Scsi_Cmnd *);
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,89)
+int advansys_reset(Scsi_Cmnd *);
+#else /* version >= v1.3.89 */
+int advansys_reset(Scsi_Cmnd *, unsigned int);
+#endif /* version >= v1.3.89 */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0)
+int advansys_biosparam(Disk *, int, int[]);
+#else /* version >= v1.3.0 */
+int advansys_biosparam(Disk *, kdev_t, int[]);
+extern struct proc_dir_entry proc_scsi_advansys;
+int advansys_proc_info(char *, char **, off_t, int, int, int);
+#endif /* version >= v1.3.0 */
+
+/* init/main.c setup function */
+void advansys_setup(char *, int *);
+
+/*
+ * AdvanSys Host Driver Scsi_Host_Template (struct SHT) from hosts.h.
+ */
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0)
+#define ADVANSYS { \
+ NULL, /* struct SHT *next */ \
+ NULL, /* int *usage_count */ \
+ "advansys", /* char *name */ \
+ advansys_detect, /* int (*detect)(struct SHT *) */ \
+ advansys_release, /* int (*release)(struct Scsi_Host *) */ \
+ advansys_info, /* const char *(*info)(struct Scsi_Host *) */ \
+ advansys_command, /* int (*command)(Scsi_Cmnd *) */ \
+ advansys_queuecommand, \
+ /* int (*queuecommand)(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)) */ \
+ advansys_abort, /* int (*abort)(Scsi_Cmnd *) */ \
+ advansys_reset, /* int (*reset)(Scsi_Cmnd *) */ \
+ NULL, /* int (*slave_attach)(int, int) */ \
+ advansys_biosparam, /* int (* bios_param)(Disk *, int, int []) */ \
+ /* \
+ * The following fields are set per adapter in advansys_detect(). \
+ */ \
+ 0, /* int can_queue */ \
+ 0, /* int this_id */ \
+ 0, /* short unsigned int sg_tablesize */ \
+ 0, /* short cmd_per_lun */ \
+ 0, /* unsigned char present */ \
+ /* \
+ * Because the driver may control an ISA adapter 'unchecked_isa_dma' \
+ * must be set. The flag will be cleared in advansys_detect for non-ISA \
+ * adapters. Refer to the comment in scsi_module.c for more information. \
+ */ \
+ 1, /* unsigned unchecked_isa_dma:1 */ \
+ /* \
+ * All adapters controlled by this driver are capable of large \
+ * scatter-gather lists. According to the mid-level SCSI documentation \
+ * this obviates any performance gain provided by setting \
+ * 'use_clustering'. But empirically while CPU utilization is increased \
+ * by enabling clustering, I/O throughput increases as well. \
+ */ \
+ ENABLE_CLUSTERING, /* unsigned use_clustering:1 */ \
+}
+#elif LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,75)
+#define ADVANSYS { \
+ NULL, /* struct SHT *next */ \
+ NULL, \
+ /* version < v2.1.23 long *usage_count */ \
+ /* version >= v2.1.23 struct module * */ \
+ &proc_scsi_advansys, /* struct proc_dir_entry *proc_dir */ \
+ advansys_proc_info, \
+ /* int (*proc_info)(char *, char **, off_t, int, int, int) */ \
+ "advansys", /* const char *name */ \
+ advansys_detect, /* int (*detect)(struct SHT *) */ \
+ advansys_release, /* int (*release)(struct Scsi_Host *) */ \
+ advansys_info, /* const char *(*info)(struct Scsi_Host *) */ \
+ advansys_command, /* int (*command)(Scsi_Cmnd *) */ \
+ advansys_queuecommand, \
+ /* int (*queuecommand)(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)) */ \
+ advansys_abort, /* int (*abort)(Scsi_Cmnd *) */ \
+ advansys_reset, \
+ /* version < v1.3.89 int (*reset)(Scsi_Cmnd *) */ \
+ /* version >= v1.3.89 int (*reset)(Scsi_Cmnd *, unsigned int) */ \
+ NULL, /* int (*slave_attach)(int, int) */ \
+ advansys_biosparam, /* int (* bios_param)(Disk *, kdev_t, int []) */ \
+ /* \
+ * The following fields are set per adapter in advansys_detect(). \
+ */ \
+ 0, /* int can_queue */ \
+ 0, /* int this_id */ \
+ 0, /* short unsigned int sg_tablesize */ \
+ 0, /* short cmd_per_lun */ \
+ 0, /* unsigned char present */ \
+ /* \
+ * Because the driver may control an ISA adapter 'unchecked_isa_dma' \
+ * must be set. The flag will be cleared in advansys_detect for non-ISA \
+ * adapters. Refer to the comment in scsi_module.c for more information. \
+ */ \
+ 1, /* unsigned unchecked_isa_dma:1 */ \
+ /* \
+ * All adapters controlled by this driver are capable of large \
+ * scatter-gather lists. According to the mid-level SCSI documentation \
+ * this obviates any performance gain provided by setting \
+ * 'use_clustering'. But empirically while CPU utilization is increased \
+ * by enabling clustering, I/O throughput increases as well. \
+ */ \
+ ENABLE_CLUSTERING, /* unsigned use_clustering:1 */ \
+}
+#else /* version >= v2.1.75 */
+#define ADVANSYS { \
+ proc_dir: &proc_scsi_advansys, \
+ proc_info: advansys_proc_info, \
+ name: "advansys", \
+ detect: advansys_detect, \
+ release: advansys_release, \
+ info: advansys_info, \
+ command: advansys_command, \
+ queuecommand: advansys_queuecommand, \
+ abort: advansys_abort, \
+ reset: advansys_reset, \
+ bios_param: advansys_biosparam, \
+ /* \
+ * Because the driver may control an ISA adapter 'unchecked_isa_dma' \
+ * must be set. The flag will be cleared in advansys_detect for non-ISA \
+ * adapters. Refer to the comment in scsi_module.c for more information. \
+ */ \
+ unchecked_isa_dma: 1, \
+ /* \
+ * All adapters controlled by this driver are capable of large \
+ * scatter-gather lists. According to the mid-level SCSI documentation \
+ * this obviates any performance gain provided by setting \
+ * 'use_clustering'. But empirically while CPU utilization is increased \
+ * by enabling clustering, I/O throughput increases as well. \
+ */ \
+ use_clustering: ENABLE_CLUSTERING, \
+}
+#endif /* version >= v2.1.75 */
+#endif /* _ADVANSYS_H */
diff --git a/linux/src/drivers/scsi/aha152x.c b/linux/src/drivers/scsi/aha152x.c
new file mode 100644
index 00000000..e7c344d0
--- /dev/null
+++ b/linux/src/drivers/scsi/aha152x.c
@@ -0,0 +1,3277 @@
+/* aha152x.c -- Adaptec AHA-152x driver
+ * Author: Jürgen E. Fischer, fischer@et-inf.fho-emden.de
+ * Copyright 1993, 1994, 1995, 1996 Jürgen E. Fischer
+ *
+ *
+ * This driver is based on
+ * fdomain.c -- Future Domain TMC-16x0 driver
+ * which is
+ * Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ *
+ * $Id: aha152x.c,v 1.1 1999/04/26 05:54:09 tb Exp $
+ *
+ * $Log: aha152x.c,v $
+ * Revision 1.18 1996/09/07 20:10:40 fischer
+ * - fixed can_queue handling (multiple outstanding commands working again)
+ *
+ * Revision 1.17 1996/08/17 16:05:14 fischer
+ * - biosparam improved
+ * - interrupt verification
+ * - updated documentation
+ * - cleanups
+ *
+ * Revision 1.16 1996/06/09 00:04:56 root
+ * - added configuration symbols for insmod (aha152x/aha152x1)
+ *
+ * Revision 1.15 1996/04/30 14:52:06 fischer
+ * - proc info fixed
+ * - support for extended translation for >1GB disks
+ *
+ * Revision 1.14 1996/01/17 15:11:20 fischer
+ * - fixed lockup in MESSAGE IN phase after reconnection
+ *
+ * Revision 1.13 1996/01/09 02:15:53 fischer
+ * - some cleanups
+ * - moved request_irq behind controller initialization
+ * (to avoid spurious interrupts)
+ *
+ * Revision 1.12 1995/12/16 12:26:07 fischer
+ * - barrier()s added
+ * - configurable RESET delay added
+ *
+ * Revision 1.11 1995/12/06 21:18:35 fischer
+ * - some minor updates
+ *
+ * Revision 1.10 1995/07/22 19:18:45 fischer
+ * - support for 2 controllers
+ * - started synchronous data transfers (not working yet)
+ *
+ * Revision 1.9 1995/03/18 09:20:24 root
+ * - patches for PCMCIA and modules
+ *
+ * Revision 1.8 1995/01/21 22:07:19 root
+ * - snarf_region => request_region
+ * - aha152x_intr interface change
+ *
+ * Revision 1.7 1995/01/02 23:19:36 root
+ * - updated COMMAND_SIZE to cmd_len
+ * - changed sti() to restore_flags()
+ * - fixed some #ifdef which generated warnings
+ *
+ * Revision 1.6 1994/11/24 20:35:27 root
+ * - problem with odd number of bytes in fifo fixed
+ *
+ * Revision 1.5 1994/10/30 14:39:56 root
+ * - abort code fixed
+ * - debugging improved
+ *
+ * Revision 1.4 1994/09/12 11:33:01 root
+ * - irqaction to request_irq
+ * - abortion updated
+ *
+ * Revision 1.3 1994/08/04 13:53:05 root
+ * - updates for mid-level-driver changes
+ * - accept unexpected BUSFREE phase as error condition
+ * - parity check now configurable
+ *
+ * Revision 1.2 1994/07/03 12:56:36 root
+ * - cleaned up debugging code
+ * - more tweaking on reset delays
+ * - updated abort/reset code (pretty untested...)
+ *
+ * Revision 1.1 1994/05/28 21:18:49 root
+ * - update for mid-level interface change (abort-reset)
+ * - delays after resets adjusted for some slow devices
+ *
+ * Revision 1.0 1994/03/25 12:52:00 root
+ * - Fixed "more data than expected" problem
+ * - added new BIOS signatures
+ *
+ * Revision 0.102 1994/01/31 20:44:12 root
+ * - minor changes in insw/outsw handling
+ *
+ * Revision 0.101 1993/12/13 01:16:27 root
+ * - fixed STATUS phase (non-GOOD stati were dropped sometimes;
+ * fixes problems with CD-ROM sector size detection & media change)
+ *
+ * Revision 0.100 1993/12/10 16:58:47 root
+ * - fix for unsuccessful selections in case of non-continuous id assignments
+ * on the scsi bus.
+ *
+ * Revision 0.99 1993/10/24 16:19:59 root
+ * - fixed DATA IN (rare read errors gone)
+ *
+ * Revision 0.98 1993/10/17 12:54:44 root
+ * - fixed some recent fixes (shame on me)
+ * - moved initialization of scratch area to aha152x_queue
+ *
+ * Revision 0.97 1993/10/09 18:53:53 root
+ * - DATA IN fixed. Rarely left data in the fifo.
+ *
+ * Revision 0.96 1993/10/03 00:53:59 root
+ * - minor changes on DATA IN
+ *
+ * Revision 0.95 1993/09/24 10:36:01 root
+ * - change handling of MSGI after reselection
+ * - fixed sti/cli
+ * - minor changes
+ *
+ * Revision 0.94 1993/09/18 14:08:22 root
+ * - fixed bug in multiple outstanding command code
+ * - changed detection
+ * - support for kernel command line configuration
+ * - reset corrected
+ * - changed message handling
+ *
+ * Revision 0.93 1993/09/15 20:41:19 root
+ * - fixed bugs with multiple outstanding commands
+ *
+ * Revision 0.92 1993/09/13 02:46:33 root
+ * - multiple outstanding commands work (no problems with IBM drive)
+ *
+ * Revision 0.91 1993/09/12 20:51:46 root
+ * added multiple outstanding commands
+ * (some problem with this $%&? IBM device remain)
+ *
+ * Revision 0.9 1993/09/12 11:11:22 root
+ * - corrected auto-configuration
+ * - changed the auto-configuration (added some '#define's)
+ * - added support for dis-/reconnection
+ *
+ * Revision 0.8 1993/09/06 23:09:39 root
+ * - added support for the drive activity light
+ * - minor changes
+ *
+ * Revision 0.7 1993/09/05 14:30:15 root
+ * - improved phase detection
+ * - now using the new snarf_region code of 0.99pl13
+ *
+ * Revision 0.6 1993/09/02 11:01:38 root
+ * first public release; added some signatures and biosparam()
+ *
+ * Revision 0.5 1993/08/30 10:23:30 root
+ * fixed timing problems with my IBM drive
+ *
+ * Revision 0.4 1993/08/29 14:06:52 root
+ * fixed some problems with timeouts due incomplete commands
+ *
+ * Revision 0.3 1993/08/28 15:55:03 root
+ * writing data works too. mounted and worked on a dos partition
+ *
+ * Revision 0.2 1993/08/27 22:42:07 root
+ * reading data works. Mounted a msdos partition.
+ *
+ * Revision 0.1 1993/08/25 13:38:30 root
+ * first "damn thing doesn't work" version
+ *
+ * Revision 0.0 1993/08/14 19:54:25 root
+ * empty function bodies; detect() works.
+ *
+ *
+ **************************************************************************
+
+
+
+ DESCRIPTION:
+
+ This is the Linux low-level SCSI driver for Adaptec AHA-1520/1522 SCSI
+ host adapters.
+
+
+ CONFIGURATION ARGUMENTS:
+
+ IOPORT base io address (0x340/0x140)
+ IRQ interrupt level (9-12; default 11)
+ SCSI_ID scsi id of controller (0-7; default 7)
+ RECONNECT allow targets to disconnect from the bus (0/1; default 1 [on])
+ PARITY enable parity checking (0/1; default 1 [on])
+ SYNCHRONOUS enable synchronous transfers (0/1; default 0 [off])
+ (NOT WORKING YET)
+ DELAY: bus reset delay (default 100)
+ EXT_TRANS: enable extended translation (0/1: default 0 [off])
+ (see NOTES below)
+
+ COMPILE TIME CONFIGURATION (put into AHA152X in drivers/scsi/Makefile):
+
+ -DAUTOCONF
+ use configuration the controller reports (AHA-152x only)
+
+ -DSKIP_BIOSTEST
+ Don't test for BIOS signature (AHA-1510 or disabled BIOS)
+
+ -DSETUP0="{ IOPORT, IRQ, SCSI_ID, RECONNECT, PARITY, SYNCHRONOUS, DELAY, EXT_TRANS }"
+ override for the first controller
+
+ -DSETUP1="{ IOPORT, IRQ, SCSI_ID, RECONNECT, PARITY, SYNCHRONOUS, DELAY, EXT_TRANS }"
+ override for the second controller
+
+
+ LILO COMMAND LINE OPTIONS:
+
+ aha152x=<IOPORT>[,<IRQ>[,<SCSI-ID>[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY> [,<EXT_TRANS]]]]]]]
+
+ The normal configuration can be overridden by specifying a command line.
+ When you do this, the BIOS test is skipped. Entered values have to be
+ valid (known). Don't use values that aren't supported under normal
+ operation. If you think that you need other values: contact me.
+ For two controllers use the aha152x statement twice.
+
+
+ SYMBOLS FOR MODULE CONFIGURATION:
+
+ aha152x=IOPORT,IRQ,SCSI_ID,RECONNECT,PARITY,SYNCHRONOUS,DELAY,EXT_TRANS
+ configuration override of first controller
+
+
+ aha152x1=IOPORT,IRQ,SCSI_ID,RECONNECT,PARITY,SYNCHRONOUS,DELAY,EXT_TRANS
+ configuration override of second controller
+
+
+ NOTES ON EXT_TRANS:
+
+ SCSI uses block numbers to address blocks/sectors on a device.
+ The BIOS uses a cylinder/head/sector addressing scheme (C/H/S)
+ scheme instead. DOS expects a BIOS or driver that understands this
+ C/H/S addressing.
+
+ The number of cylinders/heads/sectors is called geometry and is required
+ as base for requests in C/H/S adressing. SCSI only knows about the
+ total capacity of disks in blocks (sectors).
+
+ Therefore the SCSI BIOS/DOS driver has to calculate a logical/virtual
+ geometry just to be able to support that addressing scheme. The geometry
+ returned by the SCSI BIOS is a pure calculation and has nothing to
+ do with the real/physical geometry of the disk (which is usually
+ irrelevant anyway).
+
+ Basically this has no impact at all on Linux, because it also uses block
+ instead of C/H/S addressing. Unfortunately C/H/S addressing is also used
+ in the partition table and therefore every operating system has to know
+ the right geometry to be able to interpret it.
+
+ Moreover there are certain limitations to the C/H/S addressing scheme,
+ namely the address space is limited to upto 255 heads, upto 63 sectors
+ and a maximum of 1023 cylinders.
+
+ The AHA-1522 BIOS calculates the geometry by fixing the number of heads
+ to 64, the number of sectors to 32 and by calculating the number of
+ cylinders by dividing the capacity reported by the disk by 64*32 (1 MB).
+ This is considered to be the default translation.
+
+ With respect to the limit of 1023 cylinders using C/H/S you can only
+ address the first GB of your disk in the partition table. Therefore
+ BIOSes of some newer controllers based on the AIC-6260/6360 support
+ extended translation. This means that the BIOS uses 255 for heads,
+ 63 for sectors and then divides the capacity of the disk by 255*63
+ (about 8 MB), as soon it sees a disk greater than 1 GB. That results
+ in a maximum of about 8 GB adressable diskspace in the partition table
+ (but there are already bigger disks out there today).
+
+ To make it even more complicated the translation mode might/might
+ not be configurable in certain BIOS setups.
+
+ This driver does some more or less failsafe guessing to get the
+ geometry right in most cases:
+
+ - for disks<1GB: use default translation (C/32/64)
+ - for disks>1GB:
+ - take current geometry from the partition table
+ (using scsicam_bios_param and accept only `valid' geometries,
+ ie. either (C/32/64) or (C/63/255)). This can be extended
+ translation even if it's not enabled in the driver.
+ - if that fails, take extended translation if enabled by override,
+ kernel or module parameter, otherwise take default translation and
+ ask the user for verification. This might on not yet partitioned
+ disks or
+
+
+ REFERENCES USED:
+
+ "AIC-6260 SCSI Chip Specification", Adaptec Corporation.
+
+ "SCSI COMPUTER SYSTEM INTERFACE - 2 (SCSI-2)", X3T9.2/86-109 rev. 10h
+
+ "Writing a SCSI device driver for Linux", Rik Faith (faith@cs.unc.edu)
+
+ "Kernel Hacker's Guide", Michael K. Johnson (johnsonm@sunsite.unc.edu)
+
+ "Adaptec 1520/1522 User's Guide", Adaptec Corporation.
+
+ Michael K. Johnson (johnsonm@sunsite.unc.edu)
+
+ Drew Eckhardt (drew@cs.colorado.edu)
+
+ Eric Youngdale (ericy@cais.com)
+
+ special thanks to Eric Youngdale for the free(!) supplying the
+ documentation on the chip.
+
+ **************************************************************************/
+
+#ifdef PCMCIA
+#define MODULE
+#endif
+
+#include <linux/module.h>
+
+#ifdef PCMCIA
+#undef MODULE
+#endif
+
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
+#include "constants.h"
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+
+#include "aha152x.h"
+#include <linux/stat.h>
+
+#include <scsi/scsicam.h>
+
+struct proc_dir_entry proc_scsi_aha152x = {
+ PROC_SCSI_AHA152X, 7, "aha152x",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* DEFINES */
+
+/* For PCMCIA cards, always use AUTOCONF */
+#if defined(PCMCIA) || defined(MODULE)
+#if !defined(AUTOCONF)
+#define AUTOCONF
+#endif
+#endif
+
+#if !defined(AUTOCONF) && !defined(SETUP0)
+#error define AUTOCONF or SETUP0
+#endif
+
+#if defined(DEBUG_AHA152X)
+
+#undef SKIP_PORTS /* don't display ports */
+
+#undef DEBUG_QUEUE /* debug queue() */
+#undef DEBUG_RESET /* debug reset() */
+#undef DEBUG_INTR /* debug intr() */
+#undef DEBUG_SELECTION /* debug selection part in intr() */
+#undef DEBUG_MSGO /* debug message out phase in intr() */
+#undef DEBUG_MSGI /* debug message in phase in intr() */
+#undef DEBUG_STATUS /* debug status phase in intr() */
+#undef DEBUG_CMD /* debug command phase in intr() */
+#undef DEBUG_DATAI /* debug data in phase in intr() */
+#undef DEBUG_DATAO /* debug data out phase in intr() */
+#undef DEBUG_ABORT /* debug abort() */
+#undef DEBUG_DONE /* debug done() */
+#undef DEBUG_BIOSPARAM /* debug biosparam() */
+
+#undef DEBUG_RACE /* debug race conditions */
+#undef DEBUG_PHASES /* debug phases (useful to trace) */
+#undef DEBUG_QUEUES /* debug reselection */
+
+/* recently used for debugging */
+#if 0
+#endif
+
+#define DEBUG_SELECTION
+#define DEBUG_PHASES
+#define DEBUG_RESET
+#define DEBUG_ABORT
+
+#define DEBUG_DEFAULT (debug_reset|debug_abort)
+
+#endif
+
+/* END OF DEFINES */
+
+extern unsigned long loops_per_sec;
+
+#define DELAY_DEFAULT 100
+
+/* some additional "phases" for getphase() */
+#define P_BUSFREE 1
+#define P_PARITY 2
+
+/* possible irq range */
+#define IRQ_MIN 9
+#define IRQ_MAX 12
+#define IRQS IRQ_MAX-IRQ_MIN+1
+
+enum {
+ not_issued = 0x0001,
+ in_selection = 0x0002,
+ disconnected = 0x0004,
+ aborted = 0x0008,
+ sent_ident = 0x0010,
+ in_other = 0x0020,
+ in_sync = 0x0040,
+ sync_ok = 0x0080,
+};
+
+#if defined(MODULE)
+#if defined(DEBUG_AHA152X)
+int aha152x[] = { 0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0, DEBUG_DEFAULT };
+int aha152x1[] = { 0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0, DEBUG_DEFAULT };
+#else
+int aha152x[] = { 0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0 };
+int aha152x1[] = { 0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0 };
+#endif
+#endif
+
+/* set by aha152x_setup according to the command line */
+static int setup_count=0;
+static struct aha152x_setup {
+ int io_port;
+ int irq;
+ int scsiid;
+ int reconnect;
+ int parity;
+ int synchronous;
+ int delay;
+ int ext_trans;
+#ifdef DEBUG_AHA152X
+ int debug;
+#endif
+ char *conf;
+} setup[2];
+
+static struct Scsi_Host *aha152x_host[IRQS];
+
+#define HOSTDATA(shpnt) ((struct aha152x_hostdata *) &shpnt->hostdata)
+#define CURRENT_SC (HOSTDATA(shpnt)->current_SC)
+#define ISSUE_SC (HOSTDATA(shpnt)->issue_SC)
+#define DISCONNECTED_SC (HOSTDATA(shpnt)->disconnected_SC)
+#define DELAY (HOSTDATA(shpnt)->delay)
+#define EXT_TRANS (HOSTDATA(shpnt)->ext_trans)
+#define SYNCRATE (HOSTDATA(shpnt)->syncrate[CURRENT_SC->target])
+#define MSG(i) (HOSTDATA(shpnt)->message[i])
+#define MSGLEN (HOSTDATA(shpnt)->message_len)
+#define ADDMSG(x) (MSG(MSGLEN++)=x)
+
+struct aha152x_hostdata {
+ Scsi_Cmnd *issue_SC;
+ Scsi_Cmnd *current_SC;
+ Scsi_Cmnd *disconnected_SC;
+ int aborting;
+ int abortion_complete;
+ int abort_result;
+ int commands;
+
+ int reconnect;
+ int parity;
+ int synchronous;
+ int delay;
+ int ext_trans;
+
+ int swint;
+
+ unsigned char syncrate[8];
+
+ unsigned char message[256];
+ int message_len;
+
+#ifdef DEBUG_AHA152X
+ int debug;
+#endif
+};
+
+void aha152x_intr(int irq, void *dev_id, struct pt_regs *);
+void aha152x_done(struct Scsi_Host *shpnt, int error);
+void aha152x_setup(char *str, int *ints);
+int aha152x_checksetup(struct aha152x_setup *setup);
+
+static void aha152x_reset_ports(struct Scsi_Host *shpnt);
+static void aha152x_panic(struct Scsi_Host *shpnt, char *msg);
+
+static void disp_ports(struct Scsi_Host *shpnt);
+static void show_command(Scsi_Cmnd *ptr);
+static void show_queues(struct Scsi_Host *shpnt);
+static void disp_enintr(struct Scsi_Host *shpnt);
+
+#if defined(DEBUG_RACE)
+static void enter_driver(const char *);
+static void leave_driver(const char *);
+#endif
+
+/* possible i/o addresses for the AIC-6260 */
+static unsigned short ports[] =
+{
+ 0x340, /* default first */
+ 0x140
+};
+#define PORT_COUNT (sizeof(ports) / sizeof(unsigned short))
+
+#if !defined(SKIP_BIOSTEST)
+/* possible locations for the Adaptec BIOS */
+static void *addresses[] =
+{
+ (void *) 0xdc000, /* default first */
+ (void *) 0xc8000,
+ (void *) 0xcc000,
+ (void *) 0xd0000,
+ (void *) 0xd4000,
+ (void *) 0xd8000,
+ (void *) 0xe0000,
+ (void *) 0xeb800, /* VTech Platinum SMP */
+ (void *) 0xf0000,
+};
+#define ADDRESS_COUNT (sizeof(addresses) / sizeof(void *))
+
+/* signatures for various AIC-6[23]60 based controllers.
+ The point in detecting signatures is to avoid useless and maybe
+ harmful probes on ports. I'm not sure that all listed boards pass
+ auto-configuration. For those which fail the BIOS signature is
+ obsolete, because user intervention to supply the configuration is
+ needed anyway. May be an information whether or not the BIOS supports
+ extended translation could be also useful here. */
+static struct signature {
+ char *signature;
+ int sig_offset;
+ int sig_length;
+} signatures[] =
+{
+ { "Adaptec AHA-1520 BIOS", 0x102e, 21 }, /* Adaptec 152x */
+ { "Adaptec AHA-1520B", 0x0b, 17 }, /* Adaptec 152x rev B */
+ { "Adaptec AHA-1520B/1522B", 0x3e20, 23 }, /* Adaptec 1520B/1522B */
+ { "Adaptec ASW-B626 BIOS", 0x1029, 21 }, /* on-board controller */
+ { "Adaptec BIOS: ASW-B626", 0x0f, 22 }, /* on-board controller */
+ { "Adaptec ASW-B626 S2", 0x2e6c, 19 }, /* on-board controller */
+ { "Adaptec BIOS:AIC-6360", 0xc, 21 }, /* on-board controller */
+ { "ScsiPro SP-360 BIOS", 0x2873, 19 }, /* ScsiPro-Controller */
+ { "GA-400 LOCAL BUS SCSI BIOS", 0x102e, 26 }, /* Gigabyte Local-Bus-SCSI */
+ { "Adaptec BIOS:AVA-282X", 0xc, 21 }, /* Adaptec 282x */
+ { "Adaptec IBM Dock II SCSI", 0x2edd, 24 }, /* IBM Thinkpad Dock II */
+ { "Adaptec BIOS:AHA-1532P", 0x1c, 22 }, /* IBM Thinkpad Dock II SCSI */
+};
+#define SIGNATURE_COUNT (sizeof(signatures) / sizeof(struct signature))
+#endif
+
+
+static void do_pause(unsigned amount) /* Pause for amount*10 milliseconds */
+{
+ unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */
+
+ while (jiffies < the_time)
+ barrier();
+}
+
+/*
+ * queue services:
+ */
+static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
+{
+ Scsi_Cmnd *end;
+
+ new_SC->host_scribble = (unsigned char *) NULL;
+ if(!*SC)
+ *SC=new_SC;
+ else {
+ for(end=*SC; end->host_scribble; end = (Scsi_Cmnd *) end->host_scribble)
+ ;
+ end->host_scribble = (unsigned char *) new_SC;
+ }
+}
+
+static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd **SC)
+{
+ Scsi_Cmnd *ptr;
+
+ ptr=*SC;
+ if(ptr)
+ *SC= (Scsi_Cmnd *) (*SC)->host_scribble;
+ return ptr;
+}
+
+static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun)
+{
+ Scsi_Cmnd *ptr, *prev;
+
+ for(ptr=*SC, prev=NULL;
+ ptr && ((ptr->target!=target) || (ptr->lun!=lun));
+ prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ ;
+
+ if(ptr){
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ *SC= (Scsi_Cmnd *) ptr->host_scribble;
+ }
+
+ return ptr;
+}
+
+/*
+ * read inbound byte and wait for ACK to get low
+ */
+static void make_acklow(struct Scsi_Host *shpnt)
+{
+ SETPORT(SXFRCTL0, CH1|SPIOEN);
+ GETPORT(SCSIDAT);
+ SETPORT(SXFRCTL0, CH1);
+
+ while(TESTHI(SCSISIG, ACKI))
+ barrier();
+}
+
+/*
+ * detect current phase more reliable:
+ * phase is valid, when the target asserts REQ after we've deasserted ACK.
+ *
+ * return value is a valid phase or an error code.
+ *
+ * errorcodes:
+ * P_BUSFREE BUS FREE phase detected
+ * P_PARITY parity error in DATA phase
+ */
+static int getphase(struct Scsi_Host *shpnt)
+{
+ int phase, sstat1;
+
+ while(1) {
+ do {
+ while(!((sstat1 = GETPORT(SSTAT1)) & (BUSFREE|SCSIRSTI|REQINIT)))
+ barrier();
+ if(sstat1 & BUSFREE)
+ return P_BUSFREE;
+ if(sstat1 & SCSIRSTI) {
+ printk("aha152x: RESET IN\n");
+ SETPORT(SSTAT1, SCSIRSTI);
+ }
+ } while(TESTHI(SCSISIG, ACKI) || TESTLO(SSTAT1, REQINIT));
+
+ SETPORT(SSTAT1, CLRSCSIPERR);
+
+ phase = GETPORT(SCSISIG) & P_MASK ;
+
+ if(TESTHI(SSTAT1, SCSIPERR)) {
+ if((phase & (CDO|MSGO))==0) /* DATA phase */
+ return P_PARITY;
+
+ make_acklow(shpnt);
+ } else
+ return phase;
+ }
+}
+
+/* called from init/main.c */
+void aha152x_setup(char *str, int *ints)
+{
+ if(setup_count>2)
+ panic("aha152x: you can only configure up to two controllers\n");
+
+ setup[setup_count].conf = str;
+ setup[setup_count].io_port = ints[0] >= 1 ? ints[1] : 0x340;
+ setup[setup_count].irq = ints[0] >= 2 ? ints[2] : 11;
+ setup[setup_count].scsiid = ints[0] >= 3 ? ints[3] : 7;
+ setup[setup_count].reconnect = ints[0] >= 4 ? ints[4] : 1;
+ setup[setup_count].parity = ints[0] >= 5 ? ints[5] : 1;
+ setup[setup_count].synchronous = ints[0] >= 6 ? ints[6] : 0 /* FIXME: 1 */;
+ setup[setup_count].delay = ints[0] >= 7 ? ints[7] : DELAY_DEFAULT;
+ setup[setup_count].ext_trans = ints[0] >= 8 ? ints[8] : 0;
+#ifdef DEBUG_AHA152X
+ setup[setup_count].debug = ints[0] >= 9 ? ints[9] : DEBUG_DEFAULT;
+ if(ints[0]>9) {
+ printk("aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
+ "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>[,<DEBUG>]]]]]]]]\n");
+#else
+ if(ints[0]>8) {
+ printk("aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
+ "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>]]]]]]]\n");
+#endif
+ } else
+ setup_count++;
+}
+
+/*
+ * Test, if port_base is valid.
+ */
+static int aha152x_porttest(int io_port)
+{
+ int i;
+
+ if(check_region(io_port, IO_RANGE))
+ return 0;
+
+ SETPORT(io_port+O_DMACNTRL1, 0); /* reset stack pointer */
+ for(i=0; i<16; i++)
+ SETPORT(io_port+O_STACK, i);
+
+ SETPORT(io_port+O_DMACNTRL1, 0); /* reset stack pointer */
+ for(i=0; i<16 && GETPORT(io_port+O_STACK)==i; i++)
+ ;
+
+ return(i==16);
+}
+
+int aha152x_checksetup(struct aha152x_setup *setup)
+{
+ int i;
+
+#ifndef PCMCIA
+ for(i=0; i<PORT_COUNT && (setup->io_port != ports[i]); i++)
+ ;
+
+ if(i==PORT_COUNT)
+ return 0;
+#endif
+
+ if(!aha152x_porttest(setup->io_port))
+ return 0;
+
+ if(setup->irq<IRQ_MIN && setup->irq>IRQ_MAX)
+ return 0;
+
+ if((setup->scsiid < 0) || (setup->scsiid > 7))
+ return 0;
+
+ if((setup->reconnect < 0) || (setup->reconnect > 1))
+ return 0;
+
+ if((setup->parity < 0) || (setup->parity > 1))
+ return 0;
+
+ if((setup->synchronous < 0) || (setup->synchronous > 1))
+ return 0;
+
+ if((setup->ext_trans < 0) || (setup->ext_trans > 1))
+ return 0;
+
+
+ return 1;
+}
+
+void aha152x_swintr(int irqno, void *dev_id, struct pt_regs * regs)
+{
+ struct Scsi_Host *shpnt = aha152x_host[irqno-IRQ_MIN];
+
+ if(!shpnt)
+ panic("aha152x: catched software interrupt for unknown controller.\n");
+
+ HOSTDATA(shpnt)->swint++;
+}
+
+
+int aha152x_detect(Scsi_Host_Template * tpnt)
+{
+ int i, j, ok;
+#if defined(AUTOCONF)
+ aha152x_config conf;
+#endif
+
+ tpnt->proc_dir = &proc_scsi_aha152x;
+
+ for(i=0; i<IRQS; i++)
+ aha152x_host[i] = (struct Scsi_Host *) NULL;
+
+ if(setup_count) {
+ printk("aha152x: processing commandline: ");
+
+ for(i=0; i<setup_count; i++)
+ if(!aha152x_checksetup(&setup[i])) {
+ printk("\naha152x: %s\n", setup[i].conf);
+ printk("aha152x: invalid line (controller=%d)\n", i+1);
+ }
+
+ printk("ok\n");
+ }
+
+#ifdef SETUP0
+ if(setup_count<2) {
+ struct aha152x_setup override = SETUP0;
+
+ if(setup_count==0 || (override.io_port != setup[0].io_port))
+ if(!aha152x_checksetup(&override)) {
+ printk("\naha152x: invalid override SETUP0={0x%x,%d,%d,%d,%d,%d,%d,%d}\n",
+ override.io_port,
+ override.irq,
+ override.scsiid,
+ override.reconnect,
+ override.parity,
+ override.synchronous,
+ override.delay,
+ override.ext_trans);
+ } else
+ setup[setup_count++] = override;
+ }
+#endif
+
+#ifdef SETUP1
+ if(setup_count<2) {
+ struct aha152x_setup override = SETUP1;
+
+ if(setup_count==0 || (override.io_port != setup[0].io_port))
+ if(!aha152x_checksetup(&override)) {
+ printk("\naha152x: invalid override SETUP1={0x%x,%d,%d,%d,%d,%d,%d,%d}\n",
+ override.io_port,
+ override.irq,
+ override.scsiid,
+ override.reconnect,
+ override.parity,
+ override.synchronous,
+ override.delay,
+ override.ext_trans);
+ } else
+ setup[setup_count++] = override;
+ }
+#endif
+
+#if defined(MODULE)
+ if(setup_count<2 && aha152x[0]!=0) {
+ setup[setup_count].conf = "";
+ setup[setup_count].io_port = aha152x[0];
+ setup[setup_count].irq = aha152x[1];
+ setup[setup_count].scsiid = aha152x[2];
+ setup[setup_count].reconnect = aha152x[3];
+ setup[setup_count].parity = aha152x[4];
+ setup[setup_count].synchronous = aha152x[5];
+ setup[setup_count].delay = aha152x[6];
+ setup[setup_count].ext_trans = aha152x[7];
+#ifdef DEBUG_AHA152X
+ setup[setup_count].debug = aha152x[8];
+#endif
+ if(aha152x_checksetup(&setup[setup_count]))
+ setup_count++;
+ else
+ printk("\naha152x: invalid module argument aha152x=0x%x,%d,%d,%d,%d,%d,%d,%d\n",
+ setup[setup_count].io_port,
+ setup[setup_count].irq,
+ setup[setup_count].scsiid,
+ setup[setup_count].reconnect,
+ setup[setup_count].parity,
+ setup[setup_count].synchronous,
+ setup[setup_count].delay,
+ setup[setup_count].ext_trans);
+ }
+
+ if(setup_count<2 && aha152x1[0]!=0) {
+ setup[setup_count].conf = "";
+ setup[setup_count].io_port = aha152x1[0];
+ setup[setup_count].irq = aha152x1[1];
+ setup[setup_count].scsiid = aha152x1[2];
+ setup[setup_count].reconnect = aha152x1[3];
+ setup[setup_count].parity = aha152x1[4];
+ setup[setup_count].synchronous = aha152x1[5];
+ setup[setup_count].delay = aha152x1[6];
+ setup[setup_count].ext_trans = aha152x1[7];
+#ifdef DEBUG_AHA152X
+ setup[setup_count].debug = aha152x1[8];
+#endif
+ if(aha152x_checksetup(&setup[setup_count]))
+ setup_count++;
+ else
+ printk("\naha152x: invalid module argument aha152x1=0x%x,%d,%d,%d,%d,%d,%d,%d\n",
+ setup[setup_count].io_port,
+ setup[setup_count].irq,
+ setup[setup_count].scsiid,
+ setup[setup_count].reconnect,
+ setup[setup_count].parity,
+ setup[setup_count].synchronous,
+ setup[setup_count].delay,
+ setup[setup_count].ext_trans);
+ }
+#endif
+
+#if defined(AUTOCONF)
+ if(setup_count<2) {
+#if !defined(SKIP_BIOSTEST)
+ ok=0;
+ for(i=0; i < ADDRESS_COUNT && !ok; i++)
+ for(j=0; (j < SIGNATURE_COUNT) && !ok; j++)
+ ok=!memcmp((void *) addresses[i]+signatures[j].sig_offset,
+ (void *) signatures[j].signature,
+ (int) signatures[j].sig_length);
+
+ if(!ok && setup_count==0)
+ return 0;
+
+ printk("aha152x: BIOS test: passed, ");
+#else
+ printk("aha152x: ");
+#endif /* !SKIP_BIOSTEST */
+
+ ok=0;
+ for(i=0; i<PORT_COUNT && setup_count<2; i++) {
+ if((setup_count==1) && (setup[0].io_port == ports[i]))
+ continue;
+
+ if(aha152x_porttest(ports[i])) {
+ ok++;
+ setup[setup_count].io_port = ports[i];
+
+ conf.cf_port =
+ (GETPORT(ports[i]+O_PORTA)<<8) + GETPORT(ports[i]+O_PORTB);
+
+ setup[setup_count].irq = IRQ_MIN + conf.cf_irq;
+ setup[setup_count].scsiid = conf.cf_id;
+ setup[setup_count].reconnect = conf.cf_tardisc;
+ setup[setup_count].parity = !conf.cf_parity;
+ setup[setup_count].synchronous = 0 /* FIXME: conf.cf_syncneg */;
+ setup[setup_count].delay = DELAY_DEFAULT;
+ setup[setup_count].ext_trans = 0;
+#ifdef DEBUG_AHA152X
+ setup[setup_count].debug = DEBUG_DEFAULT;
+#endif
+ setup_count++;
+ }
+ }
+
+ if(ok)
+ printk("auto configuration: ok, ");
+ }
+#endif
+
+ printk("detected %d controller(s)\n", setup_count);
+
+ for(i=0; i<setup_count; i++) {
+ struct Scsi_Host *shpnt;
+ unsigned long int the_time;
+
+ shpnt = aha152x_host[setup[i].irq-IRQ_MIN] =
+ scsi_register(tpnt, sizeof(struct aha152x_hostdata));
+
+ shpnt->io_port = setup[i].io_port;
+ shpnt->n_io_port = IO_RANGE;
+ shpnt->irq = setup[i].irq;
+
+ ISSUE_SC = (Scsi_Cmnd *) NULL;
+ CURRENT_SC = (Scsi_Cmnd *) NULL;
+ DISCONNECTED_SC = (Scsi_Cmnd *) NULL;
+
+ HOSTDATA(shpnt)->reconnect = setup[i].reconnect;
+ HOSTDATA(shpnt)->parity = setup[i].parity;
+ HOSTDATA(shpnt)->synchronous = setup[i].synchronous;
+ HOSTDATA(shpnt)->delay = setup[i].delay;
+ HOSTDATA(shpnt)->ext_trans = setup[i].ext_trans;
+#ifdef DEBUG_AHA152X
+ HOSTDATA(shpnt)->debug = setup[i].debug;
+#endif
+
+ HOSTDATA(shpnt)->aborting = 0;
+ HOSTDATA(shpnt)->abortion_complete = 0;
+ HOSTDATA(shpnt)->abort_result = 0;
+ HOSTDATA(shpnt)->commands = 0;
+
+ HOSTDATA(shpnt)->message_len = 0;
+
+ for(j=0; j<8; j++)
+ HOSTDATA(shpnt)->syncrate[j] = 0;
+
+ SETPORT(SCSIID, setup[i].scsiid << 4);
+ shpnt->this_id=setup[i].scsiid;
+
+ if(setup[i].reconnect)
+ shpnt->can_queue=AHA152X_MAXQUEUE;
+
+ /* RESET OUT */
+ SETBITS(SCSISEQ, SCSIRSTO);
+ do_pause(30);
+ CLRBITS(SCSISEQ, SCSIRSTO);
+ do_pause(setup[i].delay);
+
+ aha152x_reset_ports(shpnt);
+
+ printk("aha152x%d: vital data: PORTBASE=0x%03x, IRQ=%d, SCSI ID=%d,"
+ " reconnect=%s, parity=%s, synchronous=%s, delay=%d, extended translation=%s\n",
+ i,
+ shpnt->io_port,
+ shpnt->irq,
+ shpnt->this_id,
+ HOSTDATA(shpnt)->reconnect ? "enabled" : "disabled",
+ HOSTDATA(shpnt)->parity ? "enabled" : "disabled",
+ HOSTDATA(shpnt)->synchronous ? "enabled" : "disabled",
+ HOSTDATA(shpnt)->delay,
+ HOSTDATA(shpnt)->ext_trans ? "enabled" : "disabled");
+
+ request_region(shpnt->io_port, IO_RANGE, "aha152x"); /* Register */
+
+ /* not expecting any interrupts */
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, 0);
+
+ SETBITS(DMACNTRL0, INTEN);
+
+ ok = request_irq(shpnt->irq, aha152x_swintr, SA_INTERRUPT, "aha152x", NULL);
+ if(ok<0) {
+ if(ok == -EINVAL)
+ printk("aha152x%d: bad IRQ %d.\n", i, shpnt->irq);
+ else if(ok == -EBUSY)
+ printk("aha152x%d: IRQ %d already in use.\n", i, shpnt->irq);
+ else
+ printk("\naha152x%d: Unexpected error code %d on requesting IRQ %d.\n", i, ok, shpnt->irq);
+ printk("aha152x: driver needs an IRQ.\n");
+
+ scsi_unregister(shpnt);
+ shpnt=aha152x_host[shpnt->irq-IRQ_MIN]=0;
+ continue;
+ }
+
+ HOSTDATA(shpnt)->swint=0;
+
+ printk("aha152x: trying software interrupt, ");
+ SETBITS(DMACNTRL0, SWINT);
+
+ the_time=jiffies+100;
+ while(!HOSTDATA(shpnt)->swint && jiffies<the_time)
+ barrier();
+
+ free_irq(shpnt->irq,0);
+
+ if(!HOSTDATA(shpnt)->swint) {
+ if(TESTHI(DMASTAT, INTSTAT)) {
+ printk("lost.\n");
+ } else {
+ printk("failed.\n");
+ }
+
+ printk("aha152x: IRQ %d possibly wrong. Please verify.\n", shpnt->irq);
+
+ scsi_unregister(shpnt);
+ shpnt=aha152x_host[shpnt->irq-IRQ_MIN]=0;
+ continue;
+ }
+
+ printk("ok.\n");
+
+ CLRBITS(DMACNTRL0, SWINT);
+
+ /* clear interrupts */
+ SETPORT(SSTAT0, 0x7f);
+ SETPORT(SSTAT1, 0xef);
+
+ if(request_irq(shpnt->irq,aha152x_intr,SA_INTERRUPT,"aha152x",NULL)<0) {
+ printk("aha152x: failed to reassign interrupt.\n");
+ }
+ }
+
+ return (setup_count>0);
+}
+
+/*
+ * Queue a command and setup interrupts for a free bus.
+ */
+int aha152x_queue(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ struct Scsi_Host *shpnt = SCpnt->host;
+ unsigned long flags;
+
+#if defined(DEBUG_RACE)
+ enter_driver("queue");
+#else
+#if defined(DEBUG_QUEUE)
+ if(HOSTDATA(shpnt)->debug & debug_queue)
+ printk("aha152x: queue(), ");
+#endif
+#endif
+
+#if defined(DEBUG_QUEUE)
+ if(HOSTDATA(shpnt)->debug & debug_queue) {
+ printk("SCpnt (target = %d lun = %d cmnd = ",
+ SCpnt->target, SCpnt->lun);
+ print_command(SCpnt->cmnd);
+ printk(", cmd_len=%d, pieces = %d size = %u), ",
+ SCpnt->cmd_len, SCpnt->use_sg, SCpnt->request_bufflen);
+ disp_ports(shpnt);
+ }
+#endif
+
+ SCpnt->scsi_done = done;
+
+ /* setup scratch area
+ SCp.ptr : buffer pointer
+ SCp.this_residual : buffer length
+ SCp.buffer : next buffer
+ SCp.buffers_residual : left buffers in list
+ SCp.phase : current state of the command */
+ SCpnt->SCp.phase = not_issued;
+ if (SCpnt->use_sg) {
+ SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->request_buffer;
+ SCpnt->SCp.ptr = SCpnt->SCp.buffer->address;
+ SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+ SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
+ } else {
+ SCpnt->SCp.ptr = (char *)SCpnt->request_buffer;
+ SCpnt->SCp.this_residual = SCpnt->request_bufflen;
+ SCpnt->SCp.buffer = NULL;
+ SCpnt->SCp.buffers_residual = 0;
+ }
+
+ SCpnt->SCp.Status = CHECK_CONDITION;
+ SCpnt->SCp.Message = 0;
+ SCpnt->SCp.have_data_in = 0;
+ SCpnt->SCp.sent_command = 0;
+
+ /* Turn led on, when this is the first command. */
+ save_flags(flags);
+ cli();
+ HOSTDATA(shpnt)->commands++;
+ if(HOSTDATA(shpnt)->commands==1)
+ SETPORT(PORTA, 1);
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("i+ (%d), ", HOSTDATA(shpnt)->commands);
+#endif
+ append_SC(&ISSUE_SC, SCpnt);
+
+ /* Enable bus free interrupt, when we aren't currently on the bus */
+ if(!CURRENT_SC) {
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+ }
+ restore_flags(flags);
+
+#if defined(DEBUG_RACE)
+ leave_driver("queue");
+#endif
+
+ return 0;
+}
+
+/*
+ * We only support commands in interrupt-driven fashion
+ */
+int aha152x_command(Scsi_Cmnd *SCpnt)
+{
+ printk("aha152x: interrupt driven driver; use aha152x_queue()\n");
+ return -1;
+}
+
+/*
+ * Abort a queued command
+ * (commands that are on the bus can't be aborted easily)
+ */
+int aha152x_abort(Scsi_Cmnd *SCpnt)
+{
+ struct Scsi_Host *shpnt = SCpnt->host;
+ unsigned long flags;
+ Scsi_Cmnd *ptr, *prev;
+
+ save_flags(flags);
+ cli();
+
+#if defined(DEBUG_ABORT)
+ if(HOSTDATA(shpnt)->debug & debug_abort) {
+ printk("aha152x: abort(), SCpnt=0x%08x, ", (unsigned int) SCpnt);
+ show_queues(shpnt);
+ }
+#endif
+
+ /* look for command in issue queue */
+ for(ptr=ISSUE_SC, prev=NULL;
+ ptr && ptr!=SCpnt;
+ prev=ptr, ptr=(Scsi_Cmnd *) ptr->host_scribble)
+ ;
+
+ if(ptr) {
+ /* dequeue */
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ ISSUE_SC = (Scsi_Cmnd *) ptr->host_scribble;
+
+ HOSTDATA(shpnt)->commands--;
+
+ restore_flags(flags);
+
+ ptr->host_scribble = NULL;
+ ptr->result = DID_ABORT << 16;
+ ptr->scsi_done(ptr);
+
+ return SCSI_ABORT_SUCCESS;
+ }
+
+ /* if the bus is busy or a command is currently processed,
+ we can't do anything more */
+ if (TESTLO(SSTAT1, BUSFREE) || (CURRENT_SC && CURRENT_SC!=SCpnt)) {
+ /* fail abortion, if bus is busy */
+
+ if(!CURRENT_SC)
+ printk("bus busy w/o current command, ");
+
+ restore_flags(flags);
+
+ return SCSI_ABORT_BUSY;
+ }
+
+ /* bus is free */
+
+ if(CURRENT_SC) {
+ HOSTDATA(shpnt)->commands--;
+
+ /* target entered bus free before COMMAND COMPLETE, nothing to abort */
+ restore_flags(flags);
+ CURRENT_SC->result = DID_ERROR << 16;
+ CURRENT_SC->scsi_done(CURRENT_SC);
+ CURRENT_SC = (Scsi_Cmnd *) NULL;
+
+ return SCSI_ABORT_SUCCESS;
+ }
+
+ /* look for command in disconnected queue */
+ for(ptr=DISCONNECTED_SC, prev=NULL;
+ ptr && ptr!=SCpnt;
+ prev=ptr, ptr=(Scsi_Cmnd *) ptr->host_scribble)
+ ;
+
+ if(!ptr) {
+ /* command wasn't found */
+ printk("command not found\n");
+ restore_flags(flags);
+
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ if(!HOSTDATA(shpnt)->aborting) {
+ /* dequeue */
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ DISCONNECTED_SC = (Scsi_Cmnd *) ptr->host_scribble;
+
+ HOSTDATA(shpnt)->commands--;
+
+ /* set command current and initiate selection,
+ let the interrupt routine take care of the abortion */
+ CURRENT_SC = ptr;
+ ptr->SCp.phase = in_selection|aborted;
+ SETPORT(SCSIID, (shpnt->this_id << OID_) | CURRENT_SC->target);
+
+ ADDMSG(ABORT);
+
+ /* enable interrupts for SELECTION OUT DONE and SELECTION TIME OUT */
+ SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0));
+ SETPORT(SIMODE1, ENSELTIMO);
+
+ /* Enable SELECTION OUT sequence */
+ SETBITS(SCSISEQ, ENSELO | ENAUTOATNO);
+
+ SETBITS(DMACNTRL0, INTEN);
+ HOSTDATA(shpnt)->abort_result=SCSI_ABORT_SUCCESS;
+ HOSTDATA(shpnt)->aborting++;
+ HOSTDATA(shpnt)->abortion_complete=0;
+
+ sti(); /* Hi Eric, guess what ;-) */
+
+ /* sleep until the abortion is complete */
+ while(!HOSTDATA(shpnt)->abortion_complete)
+ barrier();
+ HOSTDATA(shpnt)->aborting=0;
+
+ return HOSTDATA(shpnt)->abort_result;
+ } else {
+ /* we're already aborting a command */
+ restore_flags(flags);
+
+ return SCSI_ABORT_BUSY;
+ }
+}
+
+/*
+ * Restore default values to the AIC-6260 registers and reset the fifos
+ */
+static void aha152x_reset_ports(struct Scsi_Host *shpnt)
+{
+ /* disable interrupts */
+ SETPORT(DMACNTRL0, RSTFIFO);
+
+ SETPORT(SCSISEQ, 0);
+
+ SETPORT(SXFRCTL1, 0);
+ SETPORT(SCSISIG, 0);
+ SETPORT(SCSIRATE, 0);
+
+ /* clear all interrupt conditions */
+ SETPORT(SSTAT0, 0x7f);
+ SETPORT(SSTAT1, 0xef);
+
+ SETPORT(SSTAT4, SYNCERR|FWERR|FRERR);
+
+ SETPORT(DMACNTRL0, 0);
+ SETPORT(DMACNTRL1, 0);
+
+ SETPORT(BRSTCNTRL, 0xf1);
+
+ /* clear SCSI fifo and transfer count */
+ SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
+ SETPORT(SXFRCTL0, CH1);
+
+ /* enable interrupts */
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+}
+
+/*
+ * Reset registers, reset a hanging bus and
+ * kill active and disconnected commands for target w/o soft reset
+ */
+int aha152x_reset(Scsi_Cmnd *SCpnt, unsigned int unused)
+{
+ struct Scsi_Host *shpnt = SCpnt->host;
+ unsigned long flags;
+ Scsi_Cmnd *ptr, *prev, *next;
+
+ aha152x_reset_ports(shpnt);
+
+ /* Reset, if bus hangs */
+ if(TESTLO(SSTAT1, BUSFREE)) {
+ CLRBITS(DMACNTRL0, INTEN);
+
+#if defined(DEBUG_RESET)
+ if(HOSTDATA(shpnt)->debug & debug_reset) {
+ printk("aha152x: reset(), bus not free: SCSI RESET OUT\n");
+ show_queues(shpnt);
+ }
+#endif
+
+ ptr=CURRENT_SC;
+ if(ptr && !ptr->device->soft_reset) {
+ ptr->host_scribble = NULL;
+ ptr->result = DID_RESET << 16;
+ ptr->scsi_done(CURRENT_SC);
+ CURRENT_SC=NULL;
+ }
+
+ save_flags(flags);
+ cli();
+ prev=NULL; ptr=DISCONNECTED_SC;
+ while(ptr) {
+ if(!ptr->device->soft_reset) {
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ DISCONNECTED_SC = (Scsi_Cmnd *) ptr->host_scribble;
+
+ next = (Scsi_Cmnd *) ptr->host_scribble;
+
+ ptr->host_scribble = NULL;
+ ptr->result = DID_RESET << 16;
+ ptr->scsi_done(ptr);
+
+ ptr = next;
+ } else {
+ prev=ptr;
+ ptr = (Scsi_Cmnd *) ptr->host_scribble;
+ }
+ }
+ restore_flags(flags);
+
+#if defined(DEBUG_RESET)
+ if(HOSTDATA(shpnt)->debug & debug_reset) {
+ printk("commands on targets w/ soft-resets:\n");
+ show_queues(shpnt);
+ }
+#endif
+
+ /* RESET OUT */
+ SETPORT(SCSISEQ, SCSIRSTO);
+ do_pause(30);
+ SETPORT(SCSISEQ, 0);
+ do_pause(DELAY);
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+
+ SETPORT(DMACNTRL0, INTEN);
+ }
+
+ return SCSI_RESET_SUCCESS;
+}
+
+/*
+ * Return the "logical geometry"
+ */
+int aha152x_biosparam(Scsi_Disk * disk, kdev_t dev, int *info_array)
+{
+ struct Scsi_Host *shpnt=disk->device->host;
+
+#if defined(DEBUG_BIOSPARAM)
+ if(HOSTDATA(shpnt)->debug & debug_biosparam)
+ printk("aha152x_biosparam: dev=%s, size=%d, ",
+ kdevname(dev), disk->capacity);
+#endif
+
+ /* try default translation */
+ info_array[0]=64;
+ info_array[1]=32;
+ info_array[2]=disk->capacity / (64 * 32);
+
+ /* for disks >1GB do some guessing */
+ if(info_array[2]>=1024) {
+ int info[3];
+
+ /* try to figure out the geometry from the partition table */
+ if(scsicam_bios_param(disk, dev, info)<0 ||
+ !((info[0]==64 && info[1]==32) || (info[0]==255 && info[1]==63))) {
+ if(EXT_TRANS) {
+ printk("aha152x: unable to verify geometry for disk with >1GB.\n"
+ " using extended translation.\n");
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = disk->capacity / (255 * 63);
+ } else {
+ printk("aha152x: unable to verify geometry for disk with >1GB.\n"
+ " Using default translation. Please verify yourself.\n"
+ " Perhaps you need to enable extended translation in the driver.\n"
+ " See /usr/src/linux/drivers/scsi/aha152x.c for details.\n");
+ }
+ } else {
+ info_array[0]=info[0];
+ info_array[1]=info[1];
+ info_array[2]=info[2];
+
+ if(info[0]==255 && !EXT_TRANS) {
+ printk("aha152x: current partition table is using extended translation.\n"
+ " using it also, although it's not explicty enabled.\n");
+ }
+ }
+ }
+
+#if defined(DEBUG_BIOSPARAM)
+ if(HOSTDATA(shpnt)->debug & debug_biosparam) {
+ printk("bios geometry: head=%d, sec=%d, cyl=%d\n",
+ info_array[0], info_array[1], info_array[2]);
+ printk("WARNING: check, if the bios geometry is correct.\n");
+ }
+#endif
+
+ return 0;
+}
+
+/*
+ * Internal done function
+ */
+void aha152x_done(struct Scsi_Host *shpnt, int error)
+{
+ unsigned long flags;
+ Scsi_Cmnd *done_SC;
+
+#if defined(DEBUG_DONE)
+ if(HOSTDATA(shpnt)->debug & debug_done) {
+ printk("\naha152x: done(), ");
+ disp_ports(shpnt);
+ }
+#endif
+
+ if(CURRENT_SC) {
+#if defined(DEBUG_DONE)
+ if(HOSTDATA(shpnt)->debug & debug_done)
+ printk("done(%x), ", error);
+#endif
+
+ save_flags(flags);
+ cli();
+
+ done_SC = CURRENT_SC;
+ CURRENT_SC = NULL;
+
+ /* turn led off, when no commands are in the driver */
+ HOSTDATA(shpnt)->commands--;
+ if(!HOSTDATA(shpnt)->commands)
+ SETPORT(PORTA, 0); /* turn led off */
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("ok (%d), ", HOSTDATA(shpnt)->commands);
+#endif
+ restore_flags(flags);
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+
+#if 0
+/* Why poll for the BUS FREE phase, when we have setup the interrupt!? */
+#if defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & debug_phases)
+ printk("BUS FREE loop, ");
+#endif
+ while(TESTLO(SSTAT1, BUSFREE))
+ barrier();
+#if defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & debug_phases)
+ printk("BUS FREE\n");
+#endif
+#endif
+
+ done_SC->result = error;
+ if(done_SC->scsi_done) {
+#if defined(DEBUG_DONE)
+ if(HOSTDATA(shpnt)->debug & debug_done)
+ printk("calling scsi_done, ");
+#endif
+ done_SC->scsi_done(done_SC);
+#if defined(DEBUG_DONE)
+ if(HOSTDATA(shpnt)->debug & debug_done)
+ printk("done returned, ");
+#endif
+ } else
+ panic("aha152x: current_SC->scsi_done() == NULL");
+ } else
+ aha152x_panic(shpnt, "done() called outside of command");
+}
+
+/*
+ * Interrupts handler (main routine of the driver)
+ */
+void aha152x_intr(int irqno, void *dev_id, struct pt_regs * regs)
+{
+ struct Scsi_Host *shpnt = aha152x_host[irqno-IRQ_MIN];
+ unsigned int flags;
+ int done=0, phase;
+
+#if defined(DEBUG_RACE)
+ enter_driver("intr");
+#else
+#if defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & debug_intr)
+ printk("\naha152x: intr(), ");
+#endif
+#endif
+
+ if(!shpnt)
+ panic("aha152x: catched interrupt for unknown controller.\n");
+
+ /* no more interrupts from the controller, while we're busy.
+ INTEN has to be restored, when we're ready to leave
+ intr(). To avoid race conditions, we have to return
+ immediately afterwards. */
+ CLRBITS(DMACNTRL0, INTEN);
+ sti(); /* Yes, sti() really needs to be here */
+
+ /* disconnected target is trying to reconnect.
+ Only possible, if we have disconnected nexuses and
+ nothing is occupying the bus.
+ */
+ if(TESTHI(SSTAT0, SELDI) &&
+ DISCONNECTED_SC &&
+ (!CURRENT_SC || (CURRENT_SC->SCp.phase & in_selection)) ) {
+ int identify_msg, target, i;
+
+ /* Avoid conflicts when a target reconnects
+ while we are trying to connect to another. */
+ if(CURRENT_SC) {
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("i+, ");
+#endif
+ save_flags(flags);
+ cli();
+ append_SC(&ISSUE_SC, CURRENT_SC);
+ CURRENT_SC=NULL;
+ restore_flags(flags);
+ }
+
+ /* disable sequences */
+ SETPORT(SCSISEQ, 0);
+ SETPORT(SSTAT0, CLRSELDI);
+ SETPORT(SSTAT1, CLRBUSFREE);
+
+#if defined(DEBUG_QUEUES) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_queues|debug_phases))
+ printk("reselected, ");
+#endif
+
+ i = GETPORT(SELID) & ~(1 << shpnt->this_id);
+ target=0;
+
+ if(i==0)
+ aha152x_panic(shpnt, "reconnecting target unknown");
+
+ for(; (i & 1)==0; target++, i>>=1)
+ ;
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("SELID=%02x, target=%d, ", GETPORT(SELID), target);
+#endif
+ SETPORT(SCSIID, (shpnt->this_id << OID_) | target);
+ SETPORT(SCSISEQ, ENRESELI);
+
+ if(TESTLO(SSTAT0, SELDI))
+ aha152x_panic(shpnt, "RESELI failed");
+
+ SETPORT(SCSIRATE, HOSTDATA(shpnt)->syncrate[target]&0x7f);
+
+ SETPORT(SCSISIG, P_MSGI);
+
+ /* Get identify message */
+ if((i=getphase(shpnt))!=P_MSGI) {
+ printk("target doesn't enter MSGI to identify (phase=%02x)\n", i);
+ aha152x_panic(shpnt, "unknown lun");
+ }
+ SETPORT(SCSISEQ, 0);
+
+ SETPORT(SXFRCTL0, CH1);
+
+ identify_msg = GETPORT(SCSIBUS);
+
+ if(!(identify_msg & IDENTIFY_BASE)) {
+ printk("target=%d, inbound message (%02x) != IDENTIFY\n",
+ target, identify_msg);
+ aha152x_panic(shpnt, "unknown lun");
+ }
+
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("identify=%02x, lun=%d, ", identify_msg, identify_msg & 0x3f);
+#endif
+
+ save_flags(flags);
+ cli();
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("d-, ");
+#endif
+ CURRENT_SC = remove_SC(&DISCONNECTED_SC, target, identify_msg & 0x3f);
+
+ if(!CURRENT_SC) {
+ printk("lun=%d, ", identify_msg & 0x3f);
+ aha152x_panic(shpnt, "no disconnected command for that lun");
+ }
+
+ CURRENT_SC->SCp.phase &= ~disconnected;
+ restore_flags(flags);
+
+ make_acklow(shpnt);
+ if(getphase(shpnt)!=P_MSGI) {
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
+#if defined(DEBUG_RACE)
+ leave_driver("(reselected) intr");
+#endif
+ SETBITS(DMACNTRL0, INTEN);
+ return;
+ }
+ }
+
+ /* Check, if we aren't busy with a command */
+ if(!CURRENT_SC) {
+ /* bus is free to issue a queued command */
+ if(TESTHI(SSTAT1, BUSFREE) && ISSUE_SC) {
+ save_flags(flags);
+ cli();
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("i-, ");
+#endif
+ CURRENT_SC = remove_first_SC(&ISSUE_SC);
+ restore_flags(flags);
+
+#if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_selection|debug_phases))
+ printk("issuing command, ");
+#endif
+ CURRENT_SC->SCp.phase = in_selection;
+
+#if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_selection|debug_phases))
+ printk("selecting %d, ", CURRENT_SC->target);
+#endif
+ SETPORT(SCSIID, (shpnt->this_id << OID_) | CURRENT_SC->target);
+
+ /* Enable interrupts for SELECTION OUT DONE and SELECTION OUT INITIATED */
+ SETPORT(SXFRCTL1, HOSTDATA(shpnt)->parity ? (ENSPCHK|ENSTIMER) : ENSTIMER);
+
+ /* enable interrupts for SELECTION OUT DONE and SELECTION TIME OUT */
+ SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0));
+ SETPORT(SIMODE1, ENSELTIMO);
+
+ /* Enable SELECTION OUT sequence */
+ SETBITS(SCSISEQ, ENSELO | ENAUTOATNO);
+
+ } else {
+ /* No command we are busy with and no new to issue */
+ printk("aha152x: ignoring spurious interrupt, nothing to do\n");
+ if(TESTHI(DMACNTRL0, SWINT)) {
+ printk("aha152x: SWINT is set! Why?\n");
+ CLRBITS(DMACNTRL0, SWINT);
+ }
+ show_queues(shpnt);
+ }
+
+#if defined(DEBUG_RACE)
+ leave_driver("(selecting) intr");
+#endif
+ SETBITS(DMACNTRL0, INTEN);
+ return;
+ }
+
+ /* the bus is busy with something */
+
+#if defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & debug_intr)
+ disp_ports(shpnt);
+#endif
+
+ /* we are waiting for the result of a selection attempt */
+ if(CURRENT_SC->SCp.phase & in_selection) {
+ if(TESTLO(SSTAT1, SELTO)) {
+ /* no timeout */
+ if(TESTHI(SSTAT0, SELDO)) {
+ /* clear BUS FREE interrupt */
+ SETPORT(SSTAT1, CLRBUSFREE);
+
+ /* Disable SELECTION OUT sequence */
+ CLRBITS(SCSISEQ, ENSELO|ENAUTOATNO);
+
+ /* Disable SELECTION OUT DONE interrupt */
+ CLRBITS(SIMODE0, ENSELDO);
+ CLRBITS(SIMODE1, ENSELTIMO);
+
+ if(TESTLO(SSTAT0, SELDO)) {
+ printk("aha152x: passing bus free condition\n");
+
+#if defined(DEBUG_RACE)
+ leave_driver("(passing bus free) intr");
+#endif
+ SETBITS(DMACNTRL0, INTEN);
+
+ if(CURRENT_SC->SCp.phase & aborted) {
+ HOSTDATA(shpnt)->abort_result=SCSI_ABORT_ERROR;
+ HOSTDATA(shpnt)->abortion_complete++;
+ }
+
+ aha152x_done(shpnt, DID_NO_CONNECT << 16);
+
+ return;
+ }
+#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_selection|debug_phases))
+ printk("SELDO (SELID=%x), ", GETPORT(SELID));
+#endif
+
+ /* selection was done */
+ SETPORT(SSTAT0, CLRSELDO);
+
+#if defined(DEBUG_ABORT)
+ if((HOSTDATA(shpnt)->debug & debug_abort) && (CURRENT_SC->SCp.phase & aborted))
+ printk("(ABORT) target selected, ");
+#endif
+
+ CURRENT_SC->SCp.phase &= ~in_selection;
+ CURRENT_SC->SCp.phase |= in_other;
+
+ ADDMSG(IDENTIFY(HOSTDATA(shpnt)->reconnect,CURRENT_SC->lun));
+
+ if(!(SYNCRATE&0x80) && HOSTDATA(shpnt)->synchronous) {
+ ADDMSG(EXTENDED_MESSAGE);
+ ADDMSG(3);
+ ADDMSG(EXTENDED_SDTR);
+ ADDMSG(50);
+ ADDMSG(8);
+
+ printk("outbound SDTR: ");
+ print_msg(&MSG(MSGLEN-5));
+
+ SYNCRATE=0x80;
+ CURRENT_SC->SCp.phase |= in_sync;
+ }
+
+#if defined(DEBUG_RACE)
+ leave_driver("(SELDO) intr");
+#endif
+ SETPORT(SCSIRATE, SYNCRATE&0x7f);
+
+ SETPORT(SCSISIG, P_MSGO);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENREQINIT|ENBUSFREE);
+ SETBITS(DMACNTRL0, INTEN);
+
+ return;
+ } else
+ aha152x_panic(shpnt, "neither timeout nor selection\007");
+ } else {
+#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_selection|debug_phases))
+ printk("SELTO, ");
+#endif
+ /* end selection attempt */
+ CLRBITS(SCSISEQ, ENSELO|ENAUTOATNO);
+
+ /* timeout */
+ SETPORT(SSTAT1, CLRSELTIMO);
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+ SETBITS(DMACNTRL0, INTEN);
+#if defined(DEBUG_RACE)
+ leave_driver("(SELTO) intr");
+#endif
+
+ if(CURRENT_SC->SCp.phase & aborted) {
+#if defined(DEBUG_ABORT)
+ if(HOSTDATA(shpnt)->debug & debug_abort)
+ printk("(ABORT) selection timeout, ");
+#endif
+ HOSTDATA(shpnt)->abort_result=SCSI_ABORT_ERROR;
+ HOSTDATA(shpnt)->abortion_complete++;
+ }
+
+ if(TESTLO(SSTAT0, SELINGO))
+ /* ARBITRATION not won */
+ aha152x_done(shpnt, DID_BUS_BUSY << 16);
+ else
+ /* ARBITRATION won, but SELECTION failed */
+ aha152x_done(shpnt, DID_NO_CONNECT << 16);
+
+ return;
+ }
+ }
+
+ /* enable interrupt, when target leaves current phase */
+ phase = getphase(shpnt);
+ if(!(phase & ~P_MASK)) /* "real" phase */
+ SETPORT(SCSISIG, phase);
+ SETPORT(SSTAT1, CLRPHASECHG);
+ CURRENT_SC->SCp.phase =
+ (CURRENT_SC->SCp.phase & ~((P_MASK|1)<<16)) | (phase << 16);
+
+ /* information transfer phase */
+ switch(phase) {
+ case P_MSGO: /* MESSAGE OUT */
+ {
+ int i, identify=0, abort=0;
+
+#if defined(DEBUG_INTR) || defined(DEBUG_MSGO) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_msgo|debug_phases))
+ printk("MESSAGE OUT, ");
+#endif
+ if(MSGLEN==0) {
+ ADDMSG(MESSAGE_REJECT);
+#if defined(DEBUG_MSGO)
+ if(HOSTDATA(shpnt)->debug & debug_msgo)
+ printk("unexpected MESSAGE OUT phase; rejecting, ");
+#endif
+ }
+
+ CLRBITS(SXFRCTL0, ENDMA);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENREQINIT|ENBUSFREE);
+
+ /* wait for data latch to become ready or a phase change */
+ while(TESTLO(DMASTAT, INTSTAT))
+ barrier();
+
+#if defined(DEBUG_MSGO)
+ if(HOSTDATA(shpnt)->debug & debug_msgo) {
+ int i;
+
+ printk("messages (");
+ for(i=0; i<MSGLEN; i+=print_msg(&MSG(i)), printk(" "))
+ ;
+ printk("), ");
+ }
+#endif
+
+ for(i=0; i<MSGLEN && TESTLO(SSTAT1, PHASEMIS); i++) {
+#if defined(DEBUG_MSGO)
+ if(HOSTDATA(shpnt)->debug & debug_msgo)
+ printk("%x ", MSG(i));
+#endif
+ if(i==MSGLEN-1) {
+ /* Leave MESSAGE OUT after transfer */
+ SETPORT(SSTAT1, CLRATNO);
+ }
+
+ SETPORT(SCSIDAT, MSG(i));
+
+ make_acklow(shpnt);
+ getphase(shpnt);
+
+ if(MSG(i)==IDENTIFY(HOSTDATA(shpnt)->reconnect,CURRENT_SC->lun))
+ identify++;
+
+ if(MSG(i)==ABORT)
+ abort++;
+
+ }
+
+ MSGLEN=0;
+
+ if(identify)
+ CURRENT_SC->SCp.phase |= sent_ident;
+
+ if(abort) {
+ /* revive abort(); abort() enables interrupts */
+ HOSTDATA(shpnt)->abort_result=SCSI_ABORT_SUCCESS;
+ HOSTDATA(shpnt)->abortion_complete++;
+
+ CURRENT_SC->SCp.phase &= ~(P_MASK<<16);
+
+ /* exit */
+ SETBITS(DMACNTRL0, INTEN);
+#if defined(DEBUG_RACE)
+ leave_driver("(ABORT) intr");
+#endif
+ aha152x_done(shpnt, DID_ABORT<<16);
+
+ return;
+ }
+ }
+ break;
+
+ case P_CMD: /* COMMAND phase */
+#if defined(DEBUG_INTR) || defined(DEBUG_CMD) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_cmd|debug_phases))
+ printk("COMMAND, ");
+#endif
+ if(!(CURRENT_SC->SCp.sent_command)) {
+ int i;
+
+ CLRBITS(SXFRCTL0, ENDMA);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENREQINIT|ENBUSFREE);
+
+ /* wait for data latch to become ready or a phase change */
+ while(TESTLO(DMASTAT, INTSTAT))
+ barrier();
+
+ for(i=0; i<CURRENT_SC->cmd_len && TESTLO(SSTAT1, PHASEMIS); i++) {
+ SETPORT(SCSIDAT, CURRENT_SC->cmnd[i]);
+
+ make_acklow(shpnt);
+ getphase(shpnt);
+ }
+
+ if(i<CURRENT_SC->cmd_len && TESTHI(SSTAT1, PHASEMIS))
+ aha152x_panic(shpnt, "target left COMMAND");
+
+ CURRENT_SC->SCp.sent_command++;
+ } else
+ aha152x_panic(shpnt, "Nothing to send while in COMMAND");
+ break;
+
+ case P_MSGI: /* MESSAGE IN phase */
+ {
+ int start_sync=0;
+
+#if defined(DEBUG_INTR) || defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_msgi|debug_phases))
+ printk("MESSAGE IN, ");
+#endif
+ SETPORT(SXFRCTL0, CH1);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENBUSFREE);
+
+ while(phase == P_MSGI) {
+ CURRENT_SC->SCp.Message = GETPORT(SCSIDAT);
+ switch(CURRENT_SC->SCp.Message) {
+ case DISCONNECT:
+#if defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_msgi|debug_phases))
+ printk("target disconnected, ");
+#endif
+ CURRENT_SC->SCp.Message = 0;
+ CURRENT_SC->SCp.phase |= disconnected;
+ if(!HOSTDATA(shpnt)->reconnect)
+ aha152x_panic(shpnt, "target was not allowed to disconnect");
+
+ break;
+
+ case COMMAND_COMPLETE:
+#if defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_msgi|debug_phases))
+ printk("inbound message (COMMAND COMPLETE), ");
+#endif
+ done++;
+ break;
+
+ case MESSAGE_REJECT:
+ if(CURRENT_SC->SCp.phase & in_sync) {
+ CURRENT_SC->SCp.phase &= ~in_sync;
+ SYNCRATE=0x80;
+ printk("synchronous rejected, ");
+ } else
+ printk("inbound message (MESSAGE REJECT), ");
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ printk("inbound message (MESSAGE REJECT), ");
+#endif
+ break;
+
+ case SAVE_POINTERS:
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ printk("inbound message (SAVE DATA POINTERS), ");
+#endif
+ break;
+
+ case RESTORE_POINTERS:
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ printk("inbound message (RESTORE DATA POINTERS), ");
+#endif
+ break;
+
+ case EXTENDED_MESSAGE:
+ {
+ char buffer[16];
+ int i;
+
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ printk("inbound message (EXTENDED MESSAGE), ");
+#endif
+ make_acklow(shpnt);
+ if(getphase(shpnt)!=P_MSGI)
+ break;
+
+ buffer[0]=EXTENDED_MESSAGE;
+ buffer[1]=GETPORT(SCSIDAT);
+
+ for(i=0; i<buffer[1] &&
+ (make_acklow(shpnt), getphase(shpnt)==P_MSGI); i++)
+ buffer[2+i]=GETPORT(SCSIDAT);
+
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ print_msg(buffer);
+#endif
+
+ switch(buffer [2]) {
+ case EXTENDED_SDTR:
+ {
+ long ticks;
+
+ if(buffer[1]!=3)
+ aha152x_panic(shpnt, "SDTR message length != 3");
+
+ if(!HOSTDATA(shpnt)->synchronous)
+ break;
+
+ printk("inbound SDTR: "); print_msg(buffer);
+
+ ticks=(buffer[3]*4+49)/50;
+
+ if(CURRENT_SC->SCp.phase & in_sync) {
+ /* we initiated SDTR */
+ if(ticks>9 || buffer[4]<1 || buffer[4]>8)
+ aha152x_panic(shpnt, "received SDTR invalid");
+
+ SYNCRATE |= ((ticks-2)<<4) + buffer[4];
+ } else if(ticks<=9 && buffer[4]>=1) {
+ if(buffer[4]>8)
+ buffer[4]=8;
+
+ ADDMSG(EXTENDED_MESSAGE);
+ ADDMSG(3);
+ ADDMSG(EXTENDED_SDTR);
+ if(ticks<4) {
+ ticks=4;
+ ADDMSG(50);
+ } else
+ ADDMSG(buffer[3]);
+
+ ADDMSG(buffer[4]);
+
+ printk("outbound SDTR: ");
+ print_msg(&MSG(MSGLEN-5));
+
+ CURRENT_SC->SCp.phase |= in_sync;
+
+ SYNCRATE |= ((ticks-2)<<4) + buffer[4];
+
+ start_sync++;
+ } else {
+ /* requested SDTR is too slow, do it asynchronously */
+ ADDMSG(MESSAGE_REJECT);
+ SYNCRATE = 0;
+ }
+
+ SETPORT(SCSIRATE, SYNCRATE&0x7f);
+ }
+ break;
+
+ case EXTENDED_MODIFY_DATA_POINTER:
+ case EXTENDED_EXTENDED_IDENTIFY:
+ case EXTENDED_WDTR:
+ default:
+ ADDMSG(MESSAGE_REJECT);
+ break;
+ }
+ }
+ break;
+
+ default:
+ printk("unsupported inbound message %x, ", CURRENT_SC->SCp.Message);
+ break;
+
+ }
+
+ make_acklow(shpnt);
+ phase=getphase(shpnt);
+ }
+
+ if(start_sync)
+ CURRENT_SC->SCp.phase |= in_sync;
+ else
+ CURRENT_SC->SCp.phase &= ~in_sync;
+
+ if(MSGLEN>0)
+ SETPORT(SCSISIG, P_MSGI|ATNO);
+
+ /* clear SCSI fifo on BUSFREE */
+ if(phase==P_BUSFREE)
+ SETPORT(SXFRCTL0, CH1|CLRCH1);
+
+ if(CURRENT_SC->SCp.phase & disconnected) {
+ save_flags(flags);
+ cli();
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("d+, ");
+#endif
+ append_SC(&DISCONNECTED_SC, CURRENT_SC);
+ CURRENT_SC->SCp.phase |= 1<<16;
+ CURRENT_SC = NULL;
+ restore_flags(flags);
+
+ SETBITS(SCSISEQ, ENRESELI);
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+
+ SETBITS(DMACNTRL0, INTEN);
+
+ return;
+ }
+ }
+ break;
+
+ case P_STATUS: /* STATUS IN phase */
+#if defined(DEBUG_STATUS) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_status|debug_intr|debug_phases))
+ printk("STATUS, ");
+#endif
+ SETPORT(SXFRCTL0, CH1);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENREQINIT|ENBUSFREE);
+
+ if(TESTHI(SSTAT1, PHASEMIS))
+ printk("aha152x: passing STATUS phase");
+
+ CURRENT_SC->SCp.Status = GETPORT(SCSIBUS);
+ make_acklow(shpnt);
+ getphase(shpnt);
+
+#if defined(DEBUG_STATUS)
+ if(HOSTDATA(shpnt)->debug & debug_status) {
+ printk("inbound status ");
+ print_status(CURRENT_SC->SCp.Status);
+ printk(", ");
+ }
+#endif
+ break;
+
+ case P_DATAI: /* DATA IN phase */
+ {
+ int fifodata, data_count, done;
+
+#if defined(DEBUG_DATAI) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_datai|debug_intr|debug_phases))
+ printk("DATA IN, ");
+#endif
+
+#if 0
+ if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT))
+ printk("aha152x: P_DATAI: %d(%d) bytes left in FIFO, resetting\n",
+ GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT));
+#endif
+
+ /* reset host fifo */
+ SETPORT(DMACNTRL0, RSTFIFO);
+ SETPORT(DMACNTRL0, RSTFIFO|ENDMA);
+
+ SETPORT(SXFRCTL0, CH1|SCSIEN|DMAEN);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
+
+ /* done is set when the FIFO is empty after the target left DATA IN */
+ done=0;
+
+ /* while the target stays in DATA to transfer data */
+ while (!done) {
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("expecting data, ");
+#endif
+ /* wait for PHASEMIS or full FIFO */
+ while(TESTLO(DMASTAT, DFIFOFULL|INTSTAT))
+ barrier();
+
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("ok, ");
+#endif
+
+ if(TESTHI(DMASTAT, DFIFOFULL))
+ fifodata=GETPORT(FIFOSTAT);
+ else {
+ /* wait for SCSI fifo to get empty */
+ while(TESTLO(SSTAT2, SEMPTY))
+ barrier();
+
+ /* rest of data in FIFO */
+ fifodata=GETPORT(FIFOSTAT);
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("last transfer, ");
+#endif
+ done=1;
+ }
+
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("fifodata=%d, ", fifodata);
+#endif
+
+ while(fifodata && CURRENT_SC->SCp.this_residual) {
+ data_count=fifodata;
+
+ /* limit data transfer to size of first sg buffer */
+ if(data_count > CURRENT_SC->SCp.this_residual)
+ data_count = CURRENT_SC->SCp.this_residual;
+
+ fifodata -= data_count;
+
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("data_count=%d, ", data_count);
+#endif
+
+ if(data_count&1) {
+ /* get a single byte in byte mode */
+ SETBITS(DMACNTRL0, _8BIT);
+ *CURRENT_SC->SCp.ptr++ = GETPORT(DATAPORT);
+ CURRENT_SC->SCp.this_residual--;
+ }
+
+ if(data_count>1) {
+ CLRBITS(DMACNTRL0, _8BIT);
+ data_count >>= 1; /* Number of words */
+ insw(DATAPORT, CURRENT_SC->SCp.ptr, data_count);
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ /* show what comes with the last transfer */
+ if(done) {
+#if 0
+ int i;
+ unsigned char *data;
+#endif
+
+ printk("data on last transfer (%d bytes) ",
+ 2*data_count);
+#if 0
+ printk("data on last transfer (%d bytes: ",
+ 2*data_count);
+ data = (unsigned char *) CURRENT_SC->SCp.ptr;
+ for(i=0; i<2*data_count; i++)
+ printk("%2x ", *data++);
+ printk("), ");
+#endif
+ }
+#endif
+ CURRENT_SC->SCp.ptr += 2 * data_count;
+ CURRENT_SC->SCp.this_residual -= 2 * data_count;
+ }
+
+ /* if this buffer is full and there are more buffers left */
+ if(!CURRENT_SC->SCp.this_residual &&
+ CURRENT_SC->SCp.buffers_residual) {
+ /* advance to next buffer */
+ CURRENT_SC->SCp.buffers_residual--;
+ CURRENT_SC->SCp.buffer++;
+ CURRENT_SC->SCp.ptr = CURRENT_SC->SCp.buffer->address;
+ CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length;
+ }
+ }
+
+ /*
+ * FIFO should be empty
+ */
+ if(fifodata>0) {
+ printk("aha152x: more data than expected (%d bytes)\n",
+ GETPORT(FIFOSTAT));
+ SETBITS(DMACNTRL0, _8BIT);
+ printk("aha152x: data (");
+ while(fifodata--)
+ printk("%2x ", GETPORT(DATAPORT));
+ printk(")\n");
+ }
+
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ if(!fifodata)
+ printk("fifo empty, ");
+ else
+ printk("something left in fifo, ");
+#endif
+ }
+
+#if defined(DEBUG_DATAI)
+ if((HOSTDATA(shpnt)->debug & debug_datai) &&
+ (CURRENT_SC->SCp.buffers_residual ||
+ CURRENT_SC->SCp.this_residual))
+ printk("left buffers (buffers=%d, bytes=%d), ",
+ CURRENT_SC->SCp.buffers_residual, CURRENT_SC->SCp.this_residual);
+#endif
+ /* transfer can be considered ended, when SCSIEN reads back zero */
+ CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
+ while(TESTHI(SXFRCTL0, SCSIEN))
+ barrier();
+ CLRBITS(DMACNTRL0, ENDMA);
+
+#if defined(DEBUG_DATAI) || defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & (debug_datai|debug_intr))
+ printk("got %d bytes, ", GETSTCNT());
+#endif
+
+ CURRENT_SC->SCp.have_data_in++;
+ }
+ break;
+
+ case P_DATAO: /* DATA OUT phase */
+ {
+ int data_count;
+
+#if defined(DEBUG_DATAO) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_datao|debug_intr|debug_phases))
+ printk("DATA OUT, ");
+#endif
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("got data to send (bytes=%d, buffers=%d), ",
+ CURRENT_SC->SCp.this_residual,
+ CURRENT_SC->SCp.buffers_residual);
+#endif
+
+ if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT)) {
+ printk("%d(%d) left in FIFO, ",
+ GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT));
+ aha152x_panic(shpnt, "FIFO should be empty");
+ }
+
+ SETPORT(SXFRCTL0, CH1|CLRSTCNT|CLRCH1);
+ SETPORT(SXFRCTL0, SCSIEN|DMAEN|CH1);
+
+ SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO);
+ SETPORT(DMACNTRL0, ENDMA|WRITE_READ);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
+
+ /* while current buffer is not empty or
+ there are more buffers to transfer */
+ while(TESTLO(SSTAT1, PHASEMIS) &&
+ (CURRENT_SC->SCp.this_residual ||
+ CURRENT_SC->SCp.buffers_residual)) {
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("sending data (left: bytes=%d, buffers=%d), waiting, ",
+ CURRENT_SC->SCp.this_residual,
+ CURRENT_SC->SCp.buffers_residual);
+#endif
+ /* transfer rest of buffer, but max. 128 byte */
+ data_count =
+ CURRENT_SC->SCp.this_residual > 128 ?
+ 128 : CURRENT_SC->SCp.this_residual ;
+
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("data_count=%d, ", data_count);
+#endif
+
+ if(data_count&1) {
+ /* put a single byte in byte mode */
+ SETBITS(DMACNTRL0, _8BIT);
+ SETPORT(DATAPORT, *CURRENT_SC->SCp.ptr++);
+ CURRENT_SC->SCp.this_residual--;
+ }
+ if(data_count>1) {
+ CLRBITS(DMACNTRL0, _8BIT);
+ data_count >>= 1; /* number of words */
+ outsw(DATAPORT, CURRENT_SC->SCp.ptr, data_count);
+ CURRENT_SC->SCp.ptr += 2 * data_count;
+ CURRENT_SC->SCp.this_residual -= 2 * data_count;
+ }
+
+ /* wait for FIFO to get empty */
+ while(TESTLO(DMASTAT, DFIFOEMP|INTSTAT))
+ barrier();
+
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("fifo (%d bytes), transfered (%d bytes), ",
+ GETPORT(FIFOSTAT), GETSTCNT());
+#endif
+
+ /* if this buffer is empty and there are more buffers left */
+ if(TESTLO(SSTAT1, PHASEMIS) &&
+ !CURRENT_SC->SCp.this_residual &&
+ CURRENT_SC->SCp.buffers_residual) {
+ /* advance to next buffer */
+ CURRENT_SC->SCp.buffers_residual--;
+ CURRENT_SC->SCp.buffer++;
+ CURRENT_SC->SCp.ptr = CURRENT_SC->SCp.buffer->address;
+ CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length;
+ }
+ }
+
+ if(CURRENT_SC->SCp.this_residual || CURRENT_SC->SCp.buffers_residual) {
+ /* target leaves DATA OUT for an other phase (perhaps disconnect) */
+
+ /* data in fifos has to be resend */
+ data_count = GETPORT(SSTAT2) & (SFULL|SFCNT);
+
+ data_count += GETPORT(FIFOSTAT) ;
+ CURRENT_SC->SCp.ptr -= data_count;
+ CURRENT_SC->SCp.this_residual += data_count;
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("left data (bytes=%d, buffers=%d), fifos (bytes=%d), "
+ "transfer incomplete, resetting fifo, ",
+ CURRENT_SC->SCp.this_residual,
+ CURRENT_SC->SCp.buffers_residual,
+ data_count);
+#endif
+ SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO);
+ CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
+ CLRBITS(DMACNTRL0, ENDMA);
+ } else {
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("waiting for SCSI fifo to get empty, ");
+#endif
+ /* wait for SCSI fifo to get empty */
+ while(TESTLO(SSTAT2, SEMPTY))
+ barrier();
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("ok, left data (bytes=%d, buffers=%d) ",
+ CURRENT_SC->SCp.this_residual,
+ CURRENT_SC->SCp.buffers_residual);
+#endif
+ CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
+
+ /* transfer can be considered ended, when SCSIEN reads back zero */
+ while(TESTHI(SXFRCTL0, SCSIEN))
+ barrier();
+
+ CLRBITS(DMACNTRL0, ENDMA);
+ }
+
+#if defined(DEBUG_DATAO) || defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & (debug_datao|debug_intr))
+ printk("sent %d data bytes, ", GETSTCNT());
+#endif
+ }
+ break;
+
+ case P_BUSFREE: /* BUSFREE */
+#if defined(DEBUG_RACE)
+ leave_driver("(BUSFREE) intr");
+#endif
+#if defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & debug_phases)
+ printk("unexpected BUS FREE, ");
+#endif
+ CURRENT_SC->SCp.phase &= ~(P_MASK<<16);
+
+ aha152x_done(shpnt, DID_ERROR << 16); /* Don't know any better */
+ return;
+ break;
+
+ case P_PARITY: /* parity error in DATA phase */
+#if defined(DEBUG_RACE)
+ leave_driver("(DID_PARITY) intr");
+#endif
+ printk("PARITY error in DATA phase, ");
+
+ CURRENT_SC->SCp.phase &= ~(P_MASK<<16);
+
+ SETBITS(DMACNTRL0, INTEN);
+ aha152x_done(shpnt, DID_PARITY << 16);
+ return;
+ break;
+
+ default:
+ printk("aha152x: unexpected phase\n");
+ break;
+ }
+
+ if(done) {
+#if defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & debug_intr)
+ printk("command done.\n");
+#endif
+#if defined(DEBUG_RACE)
+ leave_driver("(done) intr");
+#endif
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+ SETPORT(SCSISEQ, DISCONNECTED_SC ? ENRESELI : 0);
+
+ SETBITS(DMACNTRL0, INTEN);
+
+ aha152x_done(shpnt,
+ (CURRENT_SC->SCp.Status & 0xff)
+ | ((CURRENT_SC->SCp.Message & 0xff) << 8)
+ | (DID_OK << 16));
+
+#if defined(DEBUG_RACE)
+ printk("done returned (DID_OK: Status=%x; Message=%x).\n",
+ CURRENT_SC->SCp.Status, CURRENT_SC->SCp.Message);
+#endif
+ return;
+ }
+
+ if(CURRENT_SC)
+ CURRENT_SC->SCp.phase |= 1<<16;
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
+#if defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & debug_intr)
+ disp_enintr(shpnt);
+#endif
+#if defined(DEBUG_RACE)
+ leave_driver("(PHASEEND) intr");
+#endif
+
+ SETBITS(DMACNTRL0, INTEN);
+ return;
+}
+
+/*
+ * Dump the current driver status and panic...
+ */
+static void aha152x_panic(struct Scsi_Host *shpnt, char *msg)
+{
+ printk("\naha152x: %s\n", msg);
+ show_queues(shpnt);
+ panic("aha152x panic");
+}
+
+/*
+ * Display registers of AIC-6260
+ */
+static void disp_ports(struct Scsi_Host *shpnt)
+{
+#ifdef DEBUG_AHA152X
+ int s;
+
+#ifdef SKIP_PORTS
+ if(HOSTDATA(shpnt)->debug & debug_skipports)
+ return;
+#endif
+
+ printk("\n%s: ", CURRENT_SC ? "on bus" : "waiting");
+
+ s=GETPORT(SCSISEQ);
+ printk("SCSISEQ (");
+ if(s & TEMODEO) printk("TARGET MODE ");
+ if(s & ENSELO) printk("SELO ");
+ if(s & ENSELI) printk("SELI ");
+ if(s & ENRESELI) printk("RESELI ");
+ if(s & ENAUTOATNO) printk("AUTOATNO ");
+ if(s & ENAUTOATNI) printk("AUTOATNI ");
+ if(s & ENAUTOATNP) printk("AUTOATNP ");
+ if(s & SCSIRSTO) printk("SCSIRSTO ");
+ printk(");");
+
+ printk(" SCSISIG (");
+ s=GETPORT(SCSISIG);
+ switch(s & P_MASK) {
+ case P_DATAO:
+ printk("DATA OUT");
+ break;
+ case P_DATAI:
+ printk("DATA IN");
+ break;
+ case P_CMD:
+ printk("COMMAND");
+ break;
+ case P_STATUS:
+ printk("STATUS");
+ break;
+ case P_MSGO:
+ printk("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ printk("MESSAGE IN");
+ break;
+ default:
+ printk("*illegal*");
+ break;
+ }
+
+ printk("); ");
+
+ printk("INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");
+
+ printk("SSTAT (");
+ s=GETPORT(SSTAT0);
+ if(s & TARGET) printk("TARGET ");
+ if(s & SELDO) printk("SELDO ");
+ if(s & SELDI) printk("SELDI ");
+ if(s & SELINGO) printk("SELINGO ");
+ if(s & SWRAP) printk("SWRAP ");
+ if(s & SDONE) printk("SDONE ");
+ if(s & SPIORDY) printk("SPIORDY ");
+ if(s & DMADONE) printk("DMADONE ");
+
+ s=GETPORT(SSTAT1);
+ if(s & SELTO) printk("SELTO ");
+ if(s & ATNTARG) printk("ATNTARG ");
+ if(s & SCSIRSTI) printk("SCSIRSTI ");
+ if(s & PHASEMIS) printk("PHASEMIS ");
+ if(s & BUSFREE) printk("BUSFREE ");
+ if(s & SCSIPERR) printk("SCSIPERR ");
+ if(s & PHASECHG) printk("PHASECHG ");
+ if(s & REQINIT) printk("REQINIT ");
+ printk("); ");
+
+
+ printk("SSTAT (");
+
+ s=GETPORT(SSTAT0) & GETPORT(SIMODE0);
+
+ if(s & TARGET) printk("TARGET ");
+ if(s & SELDO) printk("SELDO ");
+ if(s & SELDI) printk("SELDI ");
+ if(s & SELINGO) printk("SELINGO ");
+ if(s & SWRAP) printk("SWRAP ");
+ if(s & SDONE) printk("SDONE ");
+ if(s & SPIORDY) printk("SPIORDY ");
+ if(s & DMADONE) printk("DMADONE ");
+
+ s=GETPORT(SSTAT1) & GETPORT(SIMODE1);
+
+ if(s & SELTO) printk("SELTO ");
+ if(s & ATNTARG) printk("ATNTARG ");
+ if(s & SCSIRSTI) printk("SCSIRSTI ");
+ if(s & PHASEMIS) printk("PHASEMIS ");
+ if(s & BUSFREE) printk("BUSFREE ");
+ if(s & SCSIPERR) printk("SCSIPERR ");
+ if(s & PHASECHG) printk("PHASECHG ");
+ if(s & REQINIT) printk("REQINIT ");
+ printk("); ");
+
+ printk("SXFRCTL0 (");
+
+ s=GETPORT(SXFRCTL0);
+ if(s & SCSIEN) printk("SCSIEN ");
+ if(s & DMAEN) printk("DMAEN ");
+ if(s & CH1) printk("CH1 ");
+ if(s & CLRSTCNT) printk("CLRSTCNT ");
+ if(s & SPIOEN) printk("SPIOEN ");
+ if(s & CLRCH1) printk("CLRCH1 ");
+ printk("); ");
+
+ printk("SIGNAL (");
+
+ s=GETPORT(SCSISIG);
+ if(s & ATNI) printk("ATNI ");
+ if(s & SELI) printk("SELI ");
+ if(s & BSYI) printk("BSYI ");
+ if(s & REQI) printk("REQI ");
+ if(s & ACKI) printk("ACKI ");
+ printk("); ");
+
+ printk("SELID (%02x), ", GETPORT(SELID));
+
+ printk("SSTAT2 (");
+
+ s=GETPORT(SSTAT2);
+ if(s & SOFFSET) printk("SOFFSET ");
+ if(s & SEMPTY) printk("SEMPTY ");
+ if(s & SFULL) printk("SFULL ");
+ printk("); SFCNT (%d); ", s & (SFULL|SFCNT));
+
+ s=GETPORT(SSTAT3);
+ printk("SCSICNT (%d), OFFCNT(%d), ", (s&0xf0)>>4, s&0x0f);
+
+ printk("SSTAT4 (");
+ s=GETPORT(SSTAT4);
+ if(s & SYNCERR) printk("SYNCERR ");
+ if(s & FWERR) printk("FWERR ");
+ if(s & FRERR) printk("FRERR ");
+ printk("); ");
+
+ printk("DMACNTRL0 (");
+ s=GETPORT(DMACNTRL0);
+ printk("%s ", s & _8BIT ? "8BIT" : "16BIT");
+ printk("%s ", s & DMA ? "DMA" : "PIO" );
+ printk("%s ", s & WRITE_READ ? "WRITE" : "READ" );
+ if(s & ENDMA) printk("ENDMA ");
+ if(s & INTEN) printk("INTEN ");
+ if(s & RSTFIFO) printk("RSTFIFO ");
+ if(s & SWINT) printk("SWINT ");
+ printk("); ");
+
+ printk("DMASTAT (");
+ s=GETPORT(DMASTAT);
+ if(s & ATDONE) printk("ATDONE ");
+ if(s & WORDRDY) printk("WORDRDY ");
+ if(s & DFIFOFULL) printk("DFIFOFULL ");
+ if(s & DFIFOEMP) printk("DFIFOEMP ");
+ printk(")");
+
+ printk("\n");
+#endif
+}
+
+/*
+ * display enabled interrupts
+ */
+static void disp_enintr(struct Scsi_Host *shpnt)
+{
+ int s;
+
+ printk("enabled interrupts (");
+
+ s=GETPORT(SIMODE0);
+ if(s & ENSELDO) printk("ENSELDO ");
+ if(s & ENSELDI) printk("ENSELDI ");
+ if(s & ENSELINGO) printk("ENSELINGO ");
+ if(s & ENSWRAP) printk("ENSWRAP ");
+ if(s & ENSDONE) printk("ENSDONE ");
+ if(s & ENSPIORDY) printk("ENSPIORDY ");
+ if(s & ENDMADONE) printk("ENDMADONE ");
+
+ s=GETPORT(SIMODE1);
+ if(s & ENSELTIMO) printk("ENSELTIMO ");
+ if(s & ENATNTARG) printk("ENATNTARG ");
+ if(s & ENPHASEMIS) printk("ENPHASEMIS ");
+ if(s & ENBUSFREE) printk("ENBUSFREE ");
+ if(s & ENSCSIPERR) printk("ENSCSIPERR ");
+ if(s & ENPHASECHG) printk("ENPHASECHG ");
+ if(s & ENREQINIT) printk("ENREQINIT ");
+ printk(")\n");
+}
+
+#if defined(DEBUG_RACE)
+
+static const char *should_leave;
+static int in_driver=0;
+
+/*
+ * Only one routine can be in the driver at once.
+ */
+static void enter_driver(const char *func)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ printk("aha152x: entering %s() (%x)\n", func, jiffies);
+ if(in_driver) {
+ printk("%s should leave first.\n", should_leave);
+ panic("aha152x: already in driver\n");
+ }
+
+ in_driver++;
+ should_leave=func;
+ restore_flags(flags);
+}
+
+static void leave_driver(const char *func)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ printk("\naha152x: leaving %s() (%x)\n", func, jiffies);
+ if(!in_driver) {
+ printk("aha152x: %s already left.\n", should_leave);
+ panic("aha152x: %s already left driver.\n");
+ }
+
+ in_driver--;
+ should_leave=func;
+ restore_flags(flags);
+}
+#endif
+
+/*
+ * Show the command data of a command
+ */
+static void show_command(Scsi_Cmnd *ptr)
+{
+ printk("0x%08x: target=%d; lun=%d; cmnd=(",
+ (unsigned int) ptr, ptr->target, ptr->lun);
+
+ print_command(ptr->cmnd);
+
+ printk("); residual=%d; buffers=%d; phase |",
+ ptr->SCp.this_residual, ptr->SCp.buffers_residual);
+
+ if(ptr->SCp.phase & not_issued ) printk("not issued|");
+ if(ptr->SCp.phase & in_selection) printk("in selection|");
+ if(ptr->SCp.phase & disconnected) printk("disconnected|");
+ if(ptr->SCp.phase & aborted ) printk("aborted|");
+ if(ptr->SCp.phase & sent_ident ) printk("send_ident|");
+ if(ptr->SCp.phase & in_other) {
+ printk("; in other(");
+ switch((ptr->SCp.phase >> 16) & P_MASK) {
+ case P_DATAO:
+ printk("DATA OUT");
+ break;
+ case P_DATAI:
+ printk("DATA IN");
+ break;
+ case P_CMD:
+ printk("COMMAND");
+ break;
+ case P_STATUS:
+ printk("STATUS");
+ break;
+ case P_MSGO:
+ printk("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ printk("MESSAGE IN");
+ break;
+ default:
+ printk("*illegal*");
+ break;
+ }
+ printk(")");
+ if(ptr->SCp.phase & (1<<16))
+ printk("; phaseend");
+ }
+ printk("; next=0x%08x\n", (unsigned int) ptr->host_scribble);
+}
+
+/*
+ * Dump the queued data
+ */
+static void show_queues(struct Scsi_Host *shpnt)
+{
+ unsigned long flags;
+ Scsi_Cmnd *ptr;
+
+ save_flags(flags);
+ cli();
+ printk("QUEUE STATUS:\nissue_SC:\n");
+ for(ptr=ISSUE_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ show_command(ptr);
+
+ printk("current_SC:\n");
+ if(CURRENT_SC)
+ show_command(CURRENT_SC);
+ else
+ printk("none\n");
+
+ printk("disconnected_SC:\n");
+ for(ptr=DISCONNECTED_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ show_command(ptr);
+
+ disp_ports(shpnt);
+ disp_enintr(shpnt);
+ restore_flags(flags);
+}
+
+int aha152x_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
+{
+ return(-ENOSYS); /* Currently this is a no-op */
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) pos += sprintf(pos, ## args)
+
+static int get_command(char *pos, Scsi_Cmnd *ptr)
+{
+ char *start = pos;
+ int i;
+
+ SPRINTF("0x%08x: target=%d; lun=%d; cmnd=( ",
+ (unsigned int) ptr, ptr->target, ptr->lun);
+
+ for(i=0; i<COMMAND_SIZE(ptr->cmnd[0]); i++)
+ SPRINTF("0x%02x ", ptr->cmnd[i]);
+
+ SPRINTF("); residual=%d; buffers=%d; phase |",
+ ptr->SCp.this_residual, ptr->SCp.buffers_residual);
+
+ if(ptr->SCp.phase & not_issued ) SPRINTF("not issued|");
+ if(ptr->SCp.phase & in_selection) SPRINTF("in selection|");
+ if(ptr->SCp.phase & disconnected) SPRINTF("disconnected|");
+ if(ptr->SCp.phase & aborted ) SPRINTF("aborted|");
+ if(ptr->SCp.phase & sent_ident ) SPRINTF("send_ident|");
+ if(ptr->SCp.phase & in_other) {
+ SPRINTF("; in other(");
+ switch((ptr->SCp.phase >> 16) & P_MASK) {
+ case P_DATAO:
+ SPRINTF("DATA OUT");
+ break;
+ case P_DATAI:
+ SPRINTF("DATA IN");
+ break;
+ case P_CMD:
+ SPRINTF("COMMAND");
+ break;
+ case P_STATUS:
+ SPRINTF("STATUS");
+ break;
+ case P_MSGO:
+ SPRINTF("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ SPRINTF("MESSAGE IN");
+ break;
+ default:
+ SPRINTF("*illegal*");
+ break;
+ }
+ SPRINTF(")");
+ if(ptr->SCp.phase & (1<<16))
+ SPRINTF("; phaseend");
+ }
+ SPRINTF("; next=0x%08x\n", (unsigned int) ptr->host_scribble);
+
+ return(pos-start);
+}
+
+static int get_ports(struct Scsi_Host *shpnt, char *pos)
+{
+ char *start = pos;
+ int s;
+
+#ifdef SKIP_PORTS
+ if(HOSTDATA(shpnt)->debug & debug_skipports)
+ return;
+#endif
+
+ SPRINTF("\n%s: ", CURRENT_SC ? "on bus" : "waiting");
+
+ s=GETPORT(SCSISEQ);
+ SPRINTF("SCSISEQ (");
+ if(s & TEMODEO) SPRINTF("TARGET MODE ");
+ if(s & ENSELO) SPRINTF("SELO ");
+ if(s & ENSELI) SPRINTF("SELI ");
+ if(s & ENRESELI) SPRINTF("RESELI ");
+ if(s & ENAUTOATNO) SPRINTF("AUTOATNO ");
+ if(s & ENAUTOATNI) SPRINTF("AUTOATNI ");
+ if(s & ENAUTOATNP) SPRINTF("AUTOATNP ");
+ if(s & SCSIRSTO) SPRINTF("SCSIRSTO ");
+ SPRINTF(");");
+
+ SPRINTF(" SCSISIG (");
+ s=GETPORT(SCSISIG);
+ switch(s & P_MASK) {
+ case P_DATAO:
+ SPRINTF("DATA OUT");
+ break;
+ case P_DATAI:
+ SPRINTF("DATA IN");
+ break;
+ case P_CMD:
+ SPRINTF("COMMAND");
+ break;
+ case P_STATUS:
+ SPRINTF("STATUS");
+ break;
+ case P_MSGO:
+ SPRINTF("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ SPRINTF("MESSAGE IN");
+ break;
+ default:
+ SPRINTF("*illegal*");
+ break;
+ }
+
+ SPRINTF("); ");
+
+ SPRINTF("INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");
+
+ SPRINTF("SSTAT (");
+ s=GETPORT(SSTAT0);
+ if(s & TARGET) SPRINTF("TARGET ");
+ if(s & SELDO) SPRINTF("SELDO ");
+ if(s & SELDI) SPRINTF("SELDI ");
+ if(s & SELINGO) SPRINTF("SELINGO ");
+ if(s & SWRAP) SPRINTF("SWRAP ");
+ if(s & SDONE) SPRINTF("SDONE ");
+ if(s & SPIORDY) SPRINTF("SPIORDY ");
+ if(s & DMADONE) SPRINTF("DMADONE ");
+
+ s=GETPORT(SSTAT1);
+ if(s & SELTO) SPRINTF("SELTO ");
+ if(s & ATNTARG) SPRINTF("ATNTARG ");
+ if(s & SCSIRSTI) SPRINTF("SCSIRSTI ");
+ if(s & PHASEMIS) SPRINTF("PHASEMIS ");
+ if(s & BUSFREE) SPRINTF("BUSFREE ");
+ if(s & SCSIPERR) SPRINTF("SCSIPERR ");
+ if(s & PHASECHG) SPRINTF("PHASECHG ");
+ if(s & REQINIT) SPRINTF("REQINIT ");
+ SPRINTF("); ");
+
+
+ SPRINTF("SSTAT (");
+
+ s=GETPORT(SSTAT0) & GETPORT(SIMODE0);
+
+ if(s & TARGET) SPRINTF("TARGET ");
+ if(s & SELDO) SPRINTF("SELDO ");
+ if(s & SELDI) SPRINTF("SELDI ");
+ if(s & SELINGO) SPRINTF("SELINGO ");
+ if(s & SWRAP) SPRINTF("SWRAP ");
+ if(s & SDONE) SPRINTF("SDONE ");
+ if(s & SPIORDY) SPRINTF("SPIORDY ");
+ if(s & DMADONE) SPRINTF("DMADONE ");
+
+ s=GETPORT(SSTAT1) & GETPORT(SIMODE1);
+
+ if(s & SELTO) SPRINTF("SELTO ");
+ if(s & ATNTARG) SPRINTF("ATNTARG ");
+ if(s & SCSIRSTI) SPRINTF("SCSIRSTI ");
+ if(s & PHASEMIS) SPRINTF("PHASEMIS ");
+ if(s & BUSFREE) SPRINTF("BUSFREE ");
+ if(s & SCSIPERR) SPRINTF("SCSIPERR ");
+ if(s & PHASECHG) SPRINTF("PHASECHG ");
+ if(s & REQINIT) SPRINTF("REQINIT ");
+ SPRINTF("); ");
+
+ SPRINTF("SXFRCTL0 (");
+
+ s=GETPORT(SXFRCTL0);
+ if(s & SCSIEN) SPRINTF("SCSIEN ");
+ if(s & DMAEN) SPRINTF("DMAEN ");
+ if(s & CH1) SPRINTF("CH1 ");
+ if(s & CLRSTCNT) SPRINTF("CLRSTCNT ");
+ if(s & SPIOEN) SPRINTF("SPIOEN ");
+ if(s & CLRCH1) SPRINTF("CLRCH1 ");
+ SPRINTF("); ");
+
+ SPRINTF("SIGNAL (");
+
+ s=GETPORT(SCSISIG);
+ if(s & ATNI) SPRINTF("ATNI ");
+ if(s & SELI) SPRINTF("SELI ");
+ if(s & BSYI) SPRINTF("BSYI ");
+ if(s & REQI) SPRINTF("REQI ");
+ if(s & ACKI) SPRINTF("ACKI ");
+ SPRINTF("); ");
+
+ SPRINTF("SELID (%02x), ", GETPORT(SELID));
+
+ SPRINTF("SSTAT2 (");
+
+ s=GETPORT(SSTAT2);
+ if(s & SOFFSET) SPRINTF("SOFFSET ");
+ if(s & SEMPTY) SPRINTF("SEMPTY ");
+ if(s & SFULL) SPRINTF("SFULL ");
+ SPRINTF("); SFCNT (%d); ", s & (SFULL|SFCNT));
+
+ s=GETPORT(SSTAT3);
+ SPRINTF("SCSICNT (%d), OFFCNT(%d), ", (s&0xf0)>>4, s&0x0f);
+
+ SPRINTF("SSTAT4 (");
+ s=GETPORT(SSTAT4);
+ if(s & SYNCERR) SPRINTF("SYNCERR ");
+ if(s & FWERR) SPRINTF("FWERR ");
+ if(s & FRERR) SPRINTF("FRERR ");
+ SPRINTF("); ");
+
+ SPRINTF("DMACNTRL0 (");
+ s=GETPORT(DMACNTRL0);
+ SPRINTF("%s ", s & _8BIT ? "8BIT" : "16BIT");
+ SPRINTF("%s ", s & DMA ? "DMA" : "PIO" );
+ SPRINTF("%s ", s & WRITE_READ ? "WRITE" : "READ" );
+ if(s & ENDMA) SPRINTF("ENDMA ");
+ if(s & INTEN) SPRINTF("INTEN ");
+ if(s & RSTFIFO) SPRINTF("RSTFIFO ");
+ if(s & SWINT) SPRINTF("SWINT ");
+ SPRINTF("); ");
+
+ SPRINTF("DMASTAT (");
+ s=GETPORT(DMASTAT);
+ if(s & ATDONE) SPRINTF("ATDONE ");
+ if(s & WORDRDY) SPRINTF("WORDRDY ");
+ if(s & DFIFOFULL) SPRINTF("DFIFOFULL ");
+ if(s & DFIFOEMP) SPRINTF("DFIFOEMP ");
+ SPRINTF(")\n\n");
+
+ SPRINTF("enabled interrupts (");
+
+ s=GETPORT(SIMODE0);
+ if(s & ENSELDO) SPRINTF("ENSELDO ");
+ if(s & ENSELDI) SPRINTF("ENSELDI ");
+ if(s & ENSELINGO) SPRINTF("ENSELINGO ");
+ if(s & ENSWRAP) SPRINTF("ENSWRAP ");
+ if(s & ENSDONE) SPRINTF("ENSDONE ");
+ if(s & ENSPIORDY) SPRINTF("ENSPIORDY ");
+ if(s & ENDMADONE) SPRINTF("ENDMADONE ");
+
+ s=GETPORT(SIMODE1);
+ if(s & ENSELTIMO) SPRINTF("ENSELTIMO ");
+ if(s & ENATNTARG) SPRINTF("ENATNTARG ");
+ if(s & ENPHASEMIS) SPRINTF("ENPHASEMIS ");
+ if(s & ENBUSFREE) SPRINTF("ENBUSFREE ");
+ if(s & ENSCSIPERR) SPRINTF("ENSCSIPERR ");
+ if(s & ENPHASECHG) SPRINTF("ENPHASECHG ");
+ if(s & ENREQINIT) SPRINTF("ENREQINIT ");
+ SPRINTF(")\n");
+
+ return (pos-start);
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) do { if(pos < buffer + length) pos += sprintf(pos, ## args); } while(0)
+
+int aha152x_proc_info(char *buffer, char **start,
+ off_t offset, int length, int hostno, int inout)
+{
+ int i;
+ char *pos = buffer;
+ struct Scsi_Host *shpnt;
+ unsigned long flags;
+ Scsi_Cmnd *ptr;
+
+ for(i=0, shpnt= (struct Scsi_Host *) NULL; i<IRQS; i++)
+ if(aha152x_host[i] && aha152x_host[i]->host_no == hostno)
+ shpnt=aha152x_host[i];
+
+ if(!shpnt)
+ return(-ESRCH);
+
+ if(inout) /* Has data been written to the file ? */
+ return(aha152x_set_info(buffer, length, shpnt));
+
+ SPRINTF(AHA152X_REVID "\n");
+
+ save_flags(flags);
+ cli();
+
+ SPRINTF("ioports 0x%04x to 0x%04x\n",
+ shpnt->io_port, shpnt->io_port+shpnt->n_io_port-1);
+ SPRINTF("interrupt 0x%02x\n", shpnt->irq);
+ SPRINTF("disconnection/reconnection %s\n",
+ HOSTDATA(shpnt)->reconnect ? "enabled" : "disabled");
+ SPRINTF("parity checking %s\n",
+ HOSTDATA(shpnt)->parity ? "enabled" : "disabled");
+ SPRINTF("synchronous transfers %s\n",
+ HOSTDATA(shpnt)->synchronous ? "enabled" : "disabled");
+ SPRINTF("%d commands currently queued\n", HOSTDATA(shpnt)->commands);
+
+ if(HOSTDATA(shpnt)->synchronous) {
+#if 0
+ SPRINTF("synchronously operating targets (tick=%ld ns):\n",
+ 250000000/loops_per_sec);
+ for(i=0; i<8; i++)
+ if(HOSTDATA(shpnt)->syncrate[i]&0x7f)
+ SPRINTF("target %d: period %dT/%ldns; req/ack offset %d\n",
+ i,
+ (((HOSTDATA(shpnt)->syncrate[i]&0x70)>>4)+2),
+ (((HOSTDATA(shpnt)->syncrate[i]&0x70)>>4)+2)*
+ 250000000/loops_per_sec,
+ HOSTDATA(shpnt)->syncrate[i]&0x0f);
+#else
+ SPRINTF("synchronously operating targets (tick=50 ns):\n");
+ for(i=0; i<8; i++)
+ if(HOSTDATA(shpnt)->syncrate[i]&0x7f)
+ SPRINTF("target %d: period %dT/%dns; req/ack offset %d\n",
+ i,
+ (((HOSTDATA(shpnt)->syncrate[i]&0x70)>>4)+2),
+ (((HOSTDATA(shpnt)->syncrate[i]&0x70)>>4)+2)*50,
+ HOSTDATA(shpnt)->syncrate[i]&0x0f);
+#endif
+ }
+
+#ifdef DEBUG_AHA152X
+#define PDEBUG(flags,txt) if(HOSTDATA(shpnt)->debug & flags) SPRINTF("(%s) ", txt);
+
+ SPRINTF("enabled debugging options: ");
+
+ PDEBUG(debug_skipports, "skip ports");
+ PDEBUG(debug_queue, "queue");
+ PDEBUG(debug_intr, "interrupt");
+ PDEBUG(debug_selection, "selection");
+ PDEBUG(debug_msgo, "message out");
+ PDEBUG(debug_msgi, "message in");
+ PDEBUG(debug_status, "status");
+ PDEBUG(debug_cmd, "command");
+ PDEBUG(debug_datai, "data in");
+ PDEBUG(debug_datao, "data out");
+ PDEBUG(debug_abort, "abort");
+ PDEBUG(debug_done, "done");
+ PDEBUG(debug_biosparam, "bios parameters");
+ PDEBUG(debug_phases, "phases");
+ PDEBUG(debug_queues, "queues");
+ PDEBUG(debug_reset, "reset");
+
+ SPRINTF("\n");
+#endif
+
+ SPRINTF("\nqueue status:\n");
+ if(ISSUE_SC) {
+ SPRINTF("not yet issued commands:\n");
+ for(ptr=ISSUE_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ pos += get_command(pos, ptr);
+ } else
+ SPRINTF("no not yet issued commands\n");
+
+ if(CURRENT_SC) {
+ SPRINTF("current command:\n");
+ pos += get_command(pos, CURRENT_SC);
+ } else
+ SPRINTF("no current command\n");
+
+ if(DISCONNECTED_SC) {
+ SPRINTF("disconnected commands:\n");
+ for(ptr=DISCONNECTED_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ pos += get_command(pos, ptr);
+ } else
+ SPRINTF("no disconnected commands\n");
+
+ restore_flags(flags);
+
+ pos += get_ports(shpnt, pos);
+
+ *start=buffer+offset;
+ if (pos - buffer < offset)
+ return 0;
+ else if (pos - buffer - offset < length)
+ return pos - buffer - offset;
+ else
+ return length;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = AHA152X;
+
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/aha152x.h b/linux/src/drivers/scsi/aha152x.h
new file mode 100644
index 00000000..ca1a202d
--- /dev/null
+++ b/linux/src/drivers/scsi/aha152x.h
@@ -0,0 +1,357 @@
+#ifndef _AHA152X_H
+#define _AHA152X_H
+
+/*
+ * $Id: aha152x.h,v 1.1 1999/04/26 05:54:10 tb Exp $
+ */
+
+#if defined(__KERNEL__)
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include <asm/io.h>
+
+int aha152x_detect(Scsi_Host_Template *);
+int aha152x_command(Scsi_Cmnd *);
+int aha152x_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int aha152x_abort(Scsi_Cmnd *);
+int aha152x_reset(Scsi_Cmnd *, unsigned int);
+int aha152x_biosparam(Disk *, kdev_t, int*);
+int aha152x_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout);
+
+/* number of queueable commands
+ (unless we support more than 1 cmd_per_lun this should do) */
+#define AHA152X_MAXQUEUE 7
+
+#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.1 $"
+
+extern struct proc_dir_entry proc_scsi_aha152x;
+
+/* Initial value of Scsi_Host entry */
+#define AHA152X { /* next */ 0, \
+ /* usage_count */ 0, \
+ /* proc_dir */ &proc_scsi_aha152x, \
+ /* proc_info */ aha152x_proc_info, \
+ /* name */ AHA152X_REVID, \
+ /* detect */ aha152x_detect, \
+ /* release */ 0, \
+ /* info */ 0, \
+ /* command */ aha152x_command, \
+ /* queuecommand */ aha152x_queue, \
+ /* abort */ aha152x_abort, \
+ /* reset */ aha152x_reset, \
+ /* slave_attach */ 0, \
+ /* bios_param */ aha152x_biosparam, \
+ /* can_queue */ 1, \
+ /* this_id */ 7, \
+ /* sg_tablesize */ SG_ALL, \
+ /* cmd_per_lun */ 1, \
+ /* present */ 0, \
+ /* unchecked_isa_dma */ 0, \
+ /* use_clustering */ DISABLE_CLUSTERING }
+#endif
+
+
+/* port addresses */
+#define SCSISEQ (shpnt->io_port+0x00) /* SCSI sequence control */
+#define SXFRCTL0 (shpnt->io_port+0x01) /* SCSI transfer control 0 */
+#define SXFRCTL1 (shpnt->io_port+0x02) /* SCSI transfer control 1 */
+#define SCSISIG (shpnt->io_port+0x03) /* SCSI signal in/out */
+#define SCSIRATE (shpnt->io_port+0x04) /* SCSI rate control */
+#define SELID (shpnt->io_port+0x05) /* selection/reselection ID */
+#define SCSIID SELID /* SCSI ID */
+#define SCSIDAT (shpnt->io_port+0x06) /* SCSI latched data */
+#define SCSIBUS (shpnt->io_port+0x07) /* SCSI data bus */
+#define STCNT0 (shpnt->io_port+0x08) /* SCSI transfer count 0 */
+#define STCNT1 (shpnt->io_port+0x09) /* SCSI transfer count 1 */
+#define STCNT2 (shpnt->io_port+0x0a) /* SCSI transfer count 2 */
+#define SSTAT0 (shpnt->io_port+0x0b) /* SCSI interrupt status 0 */
+#define SSTAT1 (shpnt->io_port+0x0c) /* SCSI interrupt status 1 */
+#define SSTAT2 (shpnt->io_port+0x0d) /* SCSI interrupt status 2 */
+#define SCSITEST (shpnt->io_port+0x0e) /* SCSI test control */
+#define SSTAT3 SCSITEST /* SCSI interrupt status 3 */
+#define SSTAT4 (shpnt->io_port+0x0f) /* SCSI status 4 */
+#define SIMODE0 (shpnt->io_port+0x10) /* SCSI interrupt mode 0 */
+#define SIMODE1 (shpnt->io_port+0x11) /* SCSI interrupt mode 1 */
+#define DMACNTRL0 (shpnt->io_port+0x12) /* DMA control 0 */
+#define DMACNTRL1 (shpnt->io_port+0x13) /* DMA control 1 */
+#define DMASTAT (shpnt->io_port+0x14) /* DMA status */
+#define FIFOSTAT (shpnt->io_port+0x15) /* FIFO status */
+#define DATAPORT (shpnt->io_port+0x16) /* DATA port */
+#define BRSTCNTRL (shpnt->io_port+0x18) /* burst control */
+#define PORTA (shpnt->io_port+0x1a) /* PORT A */
+#define PORTB (shpnt->io_port+0x1b) /* PORT B */
+#define REV (shpnt->io_port+0x1c) /* revision */
+#define STACK (shpnt->io_port+0x1d) /* stack */
+#define TEST (shpnt->io_port+0x1e) /* test register */
+
+/* used in aha152x_porttest */
+#define O_PORTA 0x1a /* PORT A */
+#define O_PORTB 0x1b /* PORT B */
+#define O_DMACNTRL1 0x13 /* DMA control 1 */
+#define O_STACK 0x1d /* stack */
+#define IO_RANGE 0x20
+
+/* bits and bitmasks to ports */
+
+/* SCSI sequence control */
+#define TEMODEO 0x80
+#define ENSELO 0x40
+#define ENSELI 0x20
+#define ENRESELI 0x10
+#define ENAUTOATNO 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNP 0x02
+#define SCSIRSTO 0x01
+
+/* SCSI transfer control 0 */
+#define SCSIEN 0x80
+#define DMAEN 0x40
+#define CH1 0x20
+#define CLRSTCNT 0x10
+#define SPIOEN 0x08
+#define CLRCH1 0x02
+
+/* SCSI transfer control 1 */
+#define BITBUCKET 0x80
+#define SWRAPEN 0x40
+#define ENSPCHK 0x20
+#define STIMESEL 0x18 /* mask */
+#define STIMESEL_ 3
+#define ENSTIMER 0x04
+#define BYTEALIGN 0x02
+
+/* SCSI signal IN */
+#define CDI 0x80
+#define IOI 0x40
+#define MSGI 0x20
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+/* SCSI Phases */
+#define P_MASK (MSGI|CDI|IOI)
+#define P_DATAO (0)
+#define P_DATAI (IOI)
+#define P_CMD (CDI)
+#define P_STATUS (CDI|IOI)
+#define P_MSGO (MSGI|CDI)
+#define P_MSGI (MSGI|CDI|IOI)
+
+/* SCSI signal OUT */
+#define CDO 0x80
+#define IOO 0x40
+#define MSGO 0x20
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+/* SCSI rate control */
+#define SXFR 0x70 /* mask */
+#define SXFR_ 4
+#define SOFS 0x0f /* mask */
+
+/* SCSI ID */
+#define OID 0x70
+#define OID_ 4
+#define TID 0x07
+
+/* SCSI transfer count */
+#define GETSTCNT() ( (GETPORT(STCNT2)<<16) \
+ + (GETPORT(STCNT1)<< 8) \
+ + GETPORT(STCNT0) )
+
+#define SETSTCNT(X) { SETPORT(STCNT2, ((X) & 0xFF0000) >> 16); \
+ SETPORT(STCNT1, ((X) & 0x00FF00) >> 8); \
+ SETPORT(STCNT0, ((X) & 0x0000FF) ); }
+
+/* SCSI interrupt status */
+#define TARGET 0x80
+#define SELDO 0x40
+#define SELDI 0x20
+#define SELINGO 0x10
+#define SWRAP 0x08
+#define SDONE 0x04
+#define SPIORDY 0x02
+#define DMADONE 0x01
+
+#define SETSDONE 0x80
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRSWRAP 0x08
+#define CLRSDONE 0x04
+#define CLRSPIORDY 0x02
+#define CLRDMADONE 0x01
+
+/* SCSI status 1 */
+#define SELTO 0x80
+#define ATNTARG 0x40
+#define SCSIRSTI 0x20
+#define PHASEMIS 0x10
+#define BUSFREE 0x08
+#define SCSIPERR 0x04
+#define PHASECHG 0x02
+#define REQINIT 0x01
+
+#define CLRSELTIMO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRPHASECHG 0x02
+#define CLRREQINIT 0x01
+
+/* SCSI status 2 */
+#define SOFFSET 0x20
+#define SEMPTY 0x10
+#define SFULL 0x08
+#define SFCNT 0x07 /* mask */
+
+/* SCSI status 3 */
+#define SCSICNT 0xf0 /* mask */
+#define SCSICNT_ 4
+#define OFFCNT 0x0f /* mask */
+
+/* SCSI TEST control */
+#define SCTESTU 0x08
+#define SCTESTD 0x04
+#define STCTEST 0x01
+
+/* SCSI status 4 */
+#define SYNCERR 0x04
+#define FWERR 0x02
+#define FRERR 0x01
+
+#define CLRSYNCERR 0x04
+#define CLRFWERR 0x02
+#define CLRFRERR 0x01
+
+/* SCSI interrupt mode 0 */
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENSWRAP 0x08
+#define ENSDONE 0x04
+#define ENSPIORDY 0x02
+#define ENDMADONE 0x01
+
+/* SCSI interrupt mode 1 */
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENPHASECHG 0x02
+#define ENREQINIT 0x01
+
+/* DMA control 0 */
+#define ENDMA 0x80
+#define _8BIT 0x40
+#define DMA 0x20
+#define WRITE_READ 0x08
+#define INTEN 0x04
+#define RSTFIFO 0x02
+#define SWINT 0x01
+
+/* DMA control 1 */
+#define PWRDWN 0x80
+#define STK 0x07 /* mask */
+
+/* DMA status */
+#define ATDONE 0x80
+#define WORDRDY 0x40
+#define INTSTAT 0x20
+#define DFIFOFULL 0x10
+#define DFIFOEMP 0x08
+
+/* BURST control */
+#define BON 0xf0
+#define BOFF 0x0f
+
+/* TEST REGISTER */
+#define BOFFTMR 0x40
+#define BONTMR 0x20
+#define STCNTH 0x10
+#define STCNTM 0x08
+#define STCNTL 0x04
+#define SCSIBLK 0x02
+#define DMABLK 0x01
+
+/* On the AHA-152x board PORTA and PORTB contain
+ some information about the board's configuration. */
+typedef union {
+ struct {
+ unsigned reserved:2; /* reserved */
+ unsigned tardisc:1; /* Target disconnect: 0=disabled, 1=enabled */
+ unsigned syncneg:1; /* Initial sync neg: 0=disabled, 1=enabled */
+ unsigned msgclasses:2; /* Message classes
+ 0=#4
+ 1=#0, #1, #2, #3, #4
+ 2=#0, #3, #4
+ 3=#0, #4
+ */
+ unsigned boot:1; /* boot: 0=disabled, 1=enabled */
+ unsigned dma:1; /* Transfer mode: 0=PIO; 1=DMA */
+ unsigned id:3; /* SCSI-id */
+ unsigned irq:2; /* IRQ-Channel: 0,3=12, 1=10, 2=11 */
+ unsigned dmachan:2; /* DMA-Channel: 0=0, 1=5, 2=6, 3=7 */
+ unsigned parity:1; /* SCSI-parity: 1=enabled 0=disabled */
+ } fields;
+ unsigned short port;
+} aha152x_config ;
+
+#define cf_parity fields.parity
+#define cf_dmachan fields.dmachan
+#define cf_irq fields.irq
+#define cf_id fields.id
+#define cf_dma fields.dma
+#define cf_boot fields.boot
+#define cf_msgclasses fields.msgclasses
+#define cf_syncneg fields.syncneg
+#define cf_tardisc fields.tardisc
+#define cf_port port
+
+/* Some macros to manipulate ports and their bits */
+
+#define SETPORT(PORT, VAL) outb( (VAL), (PORT) )
+#define SETPORTP(PORT, VAL) outb_p( (VAL), (PORT) )
+#define SETPORTW(PORT, VAL) outw( (VAL), (PORT) )
+
+#define GETPORT(PORT) inb( PORT )
+#define GETPORTW(PORT) inw( PORT )
+
+#define SETBITS(PORT, BITS) outb( (inb(PORT) | (BITS)), (PORT) )
+#define CLRBITS(PORT, BITS) outb( (inb(PORT) & ~(BITS)), (PORT) )
+#define CLRSETBITS(PORT, CLR, SET) outb( (inb(PORT) & ~(CLR)) | (SET) , (PORT) )
+
+#define TESTHI(PORT, BITS) ((inb(PORT) & (BITS)) == BITS)
+#define TESTLO(PORT, BITS) ((inb(PORT) & (BITS)) == 0)
+
+#ifdef DEBUG_AHA152X
+enum {
+ debug_skipports = 0x0001,
+ debug_queue = 0x0002,
+ debug_intr = 0x0004,
+ debug_selection = 0x0008,
+ debug_msgo = 0x0010,
+ debug_msgi = 0x0020,
+ debug_status = 0x0040,
+ debug_cmd = 0x0080,
+ debug_datai = 0x0100,
+ debug_datao = 0x0200,
+ debug_abort = 0x0400,
+ debug_done = 0x0800,
+ debug_biosparam = 0x1000,
+ debug_phases = 0x2000,
+ debug_queues = 0x4000,
+ debug_reset = 0x8000,
+};
+#endif
+
+#endif /* _AHA152X_H */
diff --git a/linux/src/drivers/scsi/aha1542.c b/linux/src/drivers/scsi/aha1542.c
new file mode 100644
index 00000000..0366fd31
--- /dev/null
+++ b/linux/src/drivers/scsi/aha1542.c
@@ -0,0 +1,1325 @@
+/* $Id: aha1542.c,v 1.1 1999/04/26 05:54:11 tb Exp $
+ * linux/kernel/aha1542.c
+ *
+ * Copyright (C) 1992 Tommy Thorn
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * Modified by Eric Youngdale
+ * Use request_irq and request_dma to help prevent unexpected conflicts
+ * Set up on-board DMA controller, such that we do not have to
+ * have the bios enabled to use the aha1542.
+ * Modified by David Gentzel
+ * Don't call request_dma if dma mask is 0 (for BusLogic BT-445S VL-Bus
+ * controller).
+ * Modified by Matti Aarnio
+ * Accept parameters from LILO cmd-line. -- 1-Oct-94
+ * Modified by Mike McLagan <mike.mclagan@linux.org>
+ * Recognise extended mode on AHA1542CP, different bit than 1542CF
+ * 1-Jan-97
+ * Modified by Bjorn L. Thordarson and Einar Thor Einarsson
+ * Recognize that DMA0 is valid DMA channel -- 13-Jul-98
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+
+#include "aha1542.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_aha1542 = {
+ PROC_SCSI_AHA1542, 7, "aha1542",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+/*
+static const char RCSid[] = "$Header: cvs/gnumach/linux/src/drivers/scsi/Attic/aha1542.c,v 1.1 1999/04/26 05:54:11 tb Exp $";
+*/
+
+/* The adaptec can be configured for quite a number of addresses, but
+I generally do not want the card poking around at random. We allow
+two addresses - this allows people to use the Adaptec with a Midi
+card, which also used 0x330 -- can be overridden with LILO! */
+
+#define MAXBOARDS 2 /* Increase this and the sizes of the
+ arrays below, if you need more.. */
+
+static unsigned int bases[MAXBOARDS]={0x330, 0x334};
+
+/* set by aha1542_setup according to the command line */
+static int setup_called[MAXBOARDS] = {0,0};
+static int setup_buson[MAXBOARDS] = {0,0};
+static int setup_busoff[MAXBOARDS] = {0,0};
+static int setup_dmaspeed[MAXBOARDS] = {-1,-1};
+
+static char *setup_str[MAXBOARDS] = {(char *)NULL,(char *)NULL};
+
+/*
+ * LILO params: aha1542=<PORTBASE>[,<BUSON>,<BUSOFF>[,<DMASPEED>]]
+ *
+ * Where: <PORTBASE> is any of the valid AHA addresses:
+ * 0x130, 0x134, 0x230, 0x234, 0x330, 0x334
+ * <BUSON> is the time (in microsecs) that AHA spends on the AT-bus
+ * when transferring data. 1542A power-on default is 11us,
+ * valid values are in range: 2..15 (decimal)
+ * <BUSOFF> is the time that AHA spends OFF THE BUS after while
+ * it is transferring data (not to monopolize the bus).
+ * Power-on default is 4us, valid range: 1..64 microseconds.
+ * <DMASPEED> Default is jumper selected (1542A: on the J1),
+ * but experimenter can alter it with this.
+ * Valid values: 5, 6, 7, 8, 10 (MB/s)
+ * Factory default is 5 MB/s.
+ */
+
+#define BIOS_TRANSLATION_1632 0 /* Used by some old 1542A boards */
+#define BIOS_TRANSLATION_6432 1 /* Default case these days */
+#define BIOS_TRANSLATION_25563 2 /* Big disk case */
+
+struct aha1542_hostdata{
+ /* This will effectively start both of them at the first mailbox */
+ int bios_translation; /* Mapping bios uses - for compatibility */
+ int aha1542_last_mbi_used;
+ int aha1542_last_mbo_used;
+ Scsi_Cmnd * SCint[AHA1542_MAILBOXES];
+ struct mailbox mb[2*AHA1542_MAILBOXES];
+ struct ccb ccb[AHA1542_MAILBOXES];
+};
+
+#define HOSTDATA(host) ((struct aha1542_hostdata *) &host->hostdata)
+
+static struct Scsi_Host * aha_host[7] = {NULL,}; /* One for each IRQ level (9-15) */
+
+
+
+
+#define WAITnexttimeout 3000000
+
+static void setup_mailboxes(int base_io, struct Scsi_Host * shpnt);
+static int aha1542_restart(struct Scsi_Host * shost);
+
+#define aha1542_intr_reset(base) outb(IRST, CONTROL(base))
+
+#define WAIT(port, mask, allof, noneof) \
+ { register WAITbits; \
+ register WAITtimeout = WAITnexttimeout; \
+ while (1) { \
+ WAITbits = inb(port) & (mask); \
+ if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
+ break; \
+ if (--WAITtimeout == 0) goto fail; \
+ } \
+ }
+
+/* Similar to WAIT, except we use the udelay call to regulate the
+ amount of time we wait. */
+#define WAITd(port, mask, allof, noneof, timeout) \
+ { register WAITbits; \
+ register WAITtimeout = timeout; \
+ while (1) { \
+ WAITbits = inb(port) & (mask); \
+ if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
+ break; \
+ udelay(1000); \
+ if (--WAITtimeout == 0) goto fail; \
+ } \
+ }
+
+static void aha1542_stat(void)
+{
+/* int s = inb(STATUS), i = inb(INTRFLAGS);
+ printk("status=%x intrflags=%x\n", s, i, WAITnexttimeout-WAITtimeout); */
+}
+
+/* This is a bit complicated, but we need to make sure that an interrupt
+ routine does not send something out while we are in the middle of this.
+ Fortunately, it is only at boot time that multi-byte messages
+ are ever sent. */
+static int aha1542_out(unsigned int base, unchar *cmdp, int len)
+{
+ unsigned long flags = 0;
+
+ save_flags(flags);
+ if(len == 1) {
+ while(1==1){
+ WAIT(STATUS(base), CDF, 0, CDF);
+ cli();
+ if(inb(STATUS(base)) & CDF) {restore_flags(flags); continue;}
+ outb(*cmdp, DATA(base));
+ restore_flags(flags);
+ return 0;
+ }
+ } else {
+ cli();
+ while (len--)
+ {
+ WAIT(STATUS(base), CDF, 0, CDF);
+ outb(*cmdp++, DATA(base));
+ }
+ restore_flags(flags);
+ }
+ return 0;
+ fail:
+ restore_flags(flags);
+ printk("aha1542_out failed(%d): ", len+1); aha1542_stat();
+ return 1;
+}
+
+/* Only used at boot time, so we do not need to worry about latency as much
+ here */
+static int aha1542_in(unsigned int base, unchar *cmdp, int len)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ while (len--)
+ {
+ WAIT(STATUS(base), DF, DF, 0);
+ *cmdp++ = inb(DATA(base));
+ }
+ restore_flags(flags);
+ return 0;
+ fail:
+ restore_flags(flags);
+ printk("aha1542_in failed(%d): ", len+1); aha1542_stat();
+ return 1;
+}
+
+/* Similar to aha1542_in, except that we wait a very short period of time.
+ We use this if we know the board is alive and awake, but we are not sure
+ if the board will respond to the command we are about to send or not */
+static int aha1542_in1(unsigned int base, unchar *cmdp, int len)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ while (len--)
+ {
+ WAITd(STATUS(base), DF, DF, 0, 100);
+ *cmdp++ = inb(DATA(base));
+ }
+ restore_flags(flags);
+ return 0;
+ fail:
+ restore_flags(flags);
+ return 1;
+}
+
+static int makecode(unsigned hosterr, unsigned scsierr)
+{
+ switch (hosterr) {
+ case 0x0:
+ case 0xa: /* Linked command complete without error and linked normally */
+ case 0xb: /* Linked command complete without error, interrupt generated */
+ hosterr = 0;
+ break;
+
+ case 0x11: /* Selection time out-The initiator selection or target
+ reselection was not complete within the SCSI Time out period */
+ hosterr = DID_TIME_OUT;
+ break;
+
+ case 0x12: /* Data overrun/underrun-The target attempted to transfer more data
+ than was allocated by the Data Length field or the sum of the
+ Scatter / Gather Data Length fields. */
+
+ case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
+
+ case 0x15: /* MBO command was not 00, 01 or 02-The first byte of the CB was
+ invalid. This usually indicates a software failure. */
+
+ case 0x16: /* Invalid CCB Operation Code-The first byte of the CCB was invalid.
+ This usually indicates a software failure. */
+
+ case 0x17: /* Linked CCB does not have the same LUN-A subsequent CCB of a set
+ of linked CCB's does not specify the same logical unit number as
+ the first. */
+ case 0x18: /* Invalid Target Direction received from Host-The direction of a
+ Target Mode CCB was invalid. */
+
+ case 0x19: /* Duplicate CCB Received in Target Mode-More than once CCB was
+ received to service data transfer between the same target LUN
+ and initiator SCSI ID in the same direction. */
+
+ case 0x1a: /* Invalid CCB or Segment List Parameter-A segment list with a zero
+ length segment or invalid segment list boundaries was received.
+ A CCB parameter was invalid. */
+ DEB(printk("Aha1542: %x %x\n", hosterr, scsierr));
+ hosterr = DID_ERROR; /* Couldn't find any better */
+ break;
+
+ case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus
+ phase sequence was requested by the target. The host adapter
+ will generate a SCSI Reset Condition, notifying the host with
+ a SCRD interrupt */
+ hosterr = DID_RESET;
+ break;
+ default:
+ printk("makecode: unknown hoststatus %x\n", hosterr);
+ break;
+ }
+ return scsierr|(hosterr << 16);
+}
+
+static int aha1542_test_port(int bse, struct Scsi_Host * shpnt)
+{
+ int i;
+ unchar inquiry_cmd[] = {CMD_INQUIRY };
+ unchar inquiry_result[4];
+ unchar *cmdp;
+ int len;
+ volatile int debug = 0;
+
+ /* Quick and dirty test for presence of the card. */
+ if(inb(STATUS(bse)) == 0xff) return 0;
+
+ /* Reset the adapter. I ought to make a hard reset, but it's not really necessary */
+
+ /* DEB(printk("aha1542_test_port called \n")); */
+
+ /* In case some other card was probing here, reset interrupts */
+ aha1542_intr_reset(bse); /* reset interrupts, so they don't block */
+
+ outb(SRST|IRST/*|SCRST*/, CONTROL(bse));
+
+ i = jiffies + 2;
+ while (i>jiffies); /* Wait a little bit for things to settle down. */
+
+ debug = 1;
+ /* Expect INIT and IDLE, any of the others are bad */
+ WAIT(STATUS(bse), STATMASK, INIT|IDLE, STST|DIAGF|INVDCMD|DF|CDF);
+
+ debug = 2;
+ /* Shouldn't have generated any interrupts during reset */
+ if (inb(INTRFLAGS(bse))&INTRMASK) goto fail;
+
+
+ /* Perform a host adapter inquiry instead so we do not need to set
+ up the mailboxes ahead of time */
+
+ aha1542_out(bse, inquiry_cmd, 1);
+
+ debug = 3;
+ len = 4;
+ cmdp = &inquiry_result[0];
+
+ while (len--)
+ {
+ WAIT(STATUS(bse), DF, DF, 0);
+ *cmdp++ = inb(DATA(bse));
+ }
+
+ debug = 8;
+ /* Reading port should reset DF */
+ if (inb(STATUS(bse)) & DF) goto fail;
+
+ debug = 9;
+ /* When HACC, command is completed, and we're though testing */
+ WAIT(INTRFLAGS(bse), HACC, HACC, 0);
+ /* now initialize adapter */
+
+ debug = 10;
+ /* Clear interrupts */
+ outb(IRST, CONTROL(bse));
+
+ debug = 11;
+
+ return debug; /* 1 = ok */
+ fail:
+ return 0; /* 0 = not ok */
+}
+
+/* A "high" level interrupt handler */
+static void aha1542_intr_handle(int irq, void *dev_id, struct pt_regs *regs)
+{
+ void (*my_done)(Scsi_Cmnd *) = NULL;
+ int errstatus, mbi, mbo, mbistatus;
+ int number_serviced;
+ unsigned int flags;
+ struct Scsi_Host * shost;
+ Scsi_Cmnd * SCtmp;
+ int flag;
+ int needs_restart;
+ struct mailbox * mb;
+ struct ccb *ccb;
+
+ shost = aha_host[irq - 9];
+ if(!shost) panic("Splunge!");
+
+ mb = HOSTDATA(shost)->mb;
+ ccb = HOSTDATA(shost)->ccb;
+
+#ifdef DEBUG
+ {
+ flag = inb(INTRFLAGS(shost->io_port));
+ printk("aha1542_intr_handle: ");
+ if (!(flag&ANYINTR)) printk("no interrupt?");
+ if (flag&MBIF) printk("MBIF ");
+ if (flag&MBOA) printk("MBOF ");
+ if (flag&HACC) printk("HACC ");
+ if (flag&SCRD) printk("SCRD ");
+ printk("status %02x\n", inb(STATUS(shost->io_port)));
+ };
+#endif
+ number_serviced = 0;
+ needs_restart = 0;
+
+ while(1==1){
+ flag = inb(INTRFLAGS(shost->io_port));
+
+ /* Check for unusual interrupts. If any of these happen, we should
+ probably do something special, but for now just printing a message
+ is sufficient. A SCSI reset detected is something that we really
+ need to deal with in some way. */
+ if (flag & ~MBIF) {
+ if (flag&MBOA) printk("MBOF ");
+ if (flag&HACC) printk("HACC ");
+ if (flag&SCRD) {
+ needs_restart = 1;
+ printk("SCRD ");
+ }
+ }
+
+ aha1542_intr_reset(shost->io_port);
+
+ save_flags(flags);
+ cli();
+ mbi = HOSTDATA(shost)->aha1542_last_mbi_used + 1;
+ if (mbi >= 2*AHA1542_MAILBOXES) mbi = AHA1542_MAILBOXES;
+
+ do{
+ if(mb[mbi].status != 0) break;
+ mbi++;
+ if (mbi >= 2*AHA1542_MAILBOXES) mbi = AHA1542_MAILBOXES;
+ } while (mbi != HOSTDATA(shost)->aha1542_last_mbi_used);
+
+ if(mb[mbi].status == 0){
+ restore_flags(flags);
+ /* Hmm, no mail. Must have read it the last time around */
+ if (!number_serviced && !needs_restart)
+ printk("aha1542.c: interrupt received, but no mail.\n");
+ /* We detected a reset. Restart all pending commands for
+ devices that use the hard reset option */
+ if(needs_restart) aha1542_restart(shost);
+ return;
+ };
+
+ mbo = (scsi2int(mb[mbi].ccbptr) - ((unsigned int) &ccb[0])) / sizeof(struct ccb);
+ mbistatus = mb[mbi].status;
+ mb[mbi].status = 0;
+ HOSTDATA(shost)->aha1542_last_mbi_used = mbi;
+ restore_flags(flags);
+
+#ifdef DEBUG
+ {
+ if (ccb[mbo].tarstat|ccb[mbo].hastat)
+ printk("aha1542_command: returning %x (status %d)\n",
+ ccb[mbo].tarstat + ((int) ccb[mbo].hastat << 16), mb[mbi].status);
+ };
+#endif
+
+ if(mbistatus == 3) continue; /* Aborted command not found */
+
+#ifdef DEBUG
+ printk("...done %d %d\n",mbo, mbi);
+#endif
+
+ SCtmp = HOSTDATA(shost)->SCint[mbo];
+
+ if (!SCtmp || !SCtmp->scsi_done) {
+ printk("aha1542_intr_handle: Unexpected interrupt\n");
+ printk("tarstat=%x, hastat=%x idlun=%x ccb#=%d \n", ccb[mbo].tarstat,
+ ccb[mbo].hastat, ccb[mbo].idlun, mbo);
+ return;
+ }
+
+ my_done = SCtmp->scsi_done;
+ if (SCtmp->host_scribble) scsi_free(SCtmp->host_scribble, 512);
+
+ /* Fetch the sense data, and tuck it away, in the required slot. The
+ Adaptec automatically fetches it, and there is no guarantee that
+ we will still have it in the cdb when we come back */
+ if (ccb[mbo].tarstat == 2)
+ memcpy(SCtmp->sense_buffer, &ccb[mbo].cdb[ccb[mbo].cdblen],
+ sizeof(SCtmp->sense_buffer));
+
+
+ /* is there mail :-) */
+
+ /* more error checking left out here */
+ if (mbistatus != 1)
+ /* This is surely wrong, but I don't know what's right */
+ errstatus = makecode(ccb[mbo].hastat, ccb[mbo].tarstat);
+ else
+ errstatus = 0;
+
+#ifdef DEBUG
+ if(errstatus) printk("(aha1542 error:%x %x %x) ",errstatus,
+ ccb[mbo].hastat, ccb[mbo].tarstat);
+#endif
+
+ if (ccb[mbo].tarstat == 2) {
+#ifdef DEBUG
+ int i;
+#endif
+ DEB(printk("aha1542_intr_handle: sense:"));
+#ifdef DEBUG
+ for (i = 0; i < 12; i++)
+ printk("%02x ", ccb[mbo].cdb[ccb[mbo].cdblen+i]);
+ printk("\n");
+#endif
+ /*
+ DEB(printk("aha1542_intr_handle: buf:"));
+ for (i = 0; i < bufflen; i++)
+ printk("%02x ", ((unchar *)buff)[i]);
+ printk("\n");
+ */
+ }
+ DEB(if (errstatus) printk("aha1542_intr_handle: returning %6x\n", errstatus));
+ SCtmp->result = errstatus;
+ HOSTDATA(shost)->SCint[mbo] = NULL; /* This effectively frees up the mailbox slot, as
+ far as queuecommand is concerned */
+ my_done(SCtmp);
+ number_serviced++;
+ };
+}
+
+int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ unchar ahacmd = CMD_START_SCSI;
+ unchar direction;
+ unchar *cmd = (unchar *) SCpnt->cmnd;
+ unchar target = SCpnt->target;
+ unchar lun = SCpnt->lun;
+ unsigned long flags;
+ void *buff = SCpnt->request_buffer;
+ int bufflen = SCpnt->request_bufflen;
+ int mbo;
+ struct mailbox * mb;
+ struct ccb *ccb;
+
+ DEB(int i);
+
+ mb = HOSTDATA(SCpnt->host)->mb;
+ ccb = HOSTDATA(SCpnt->host)->ccb;
+
+ DEB(if (target > 1) {
+ SCpnt->result = DID_TIME_OUT << 16;
+ done(SCpnt); return 0;});
+
+ if(*cmd == REQUEST_SENSE){
+#ifndef DEBUG
+ if (bufflen != sizeof(SCpnt->sense_buffer)) {
+ printk("Wrong buffer length supplied for request sense (%d)\n",bufflen);
+ };
+#endif
+ SCpnt->result = 0;
+ done(SCpnt);
+ return 0;
+ };
+
+#ifdef DEBUG
+ if (*cmd == READ_10 || *cmd == WRITE_10)
+ i = xscsi2int(cmd+2);
+ else if (*cmd == READ_6 || *cmd == WRITE_6)
+ i = scsi2int(cmd+2);
+ else
+ i = -1;
+ if (done)
+ printk("aha1542_queuecommand: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
+ else
+ printk("aha1542_command: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
+ aha1542_stat();
+ printk("aha1542_queuecommand: dumping scsi cmd:");
+ for (i = 0; i < SCpnt->cmd_len; i++) printk("%02x ", cmd[i]);
+ printk("\n");
+ if (*cmd == WRITE_10 || *cmd == WRITE_6)
+ return 0; /* we are still testing, so *don't* write */
+#endif
+/* Use the outgoing mailboxes in a round-robin fashion, because this
+ is how the host adapter will scan for them */
+
+ save_flags(flags);
+ cli();
+ mbo = HOSTDATA(SCpnt->host)->aha1542_last_mbo_used + 1;
+ if (mbo >= AHA1542_MAILBOXES) mbo = 0;
+
+ do{
+ if(mb[mbo].status == 0 && HOSTDATA(SCpnt->host)->SCint[mbo] == NULL)
+ break;
+ mbo++;
+ if (mbo >= AHA1542_MAILBOXES) mbo = 0;
+ } while (mbo != HOSTDATA(SCpnt->host)->aha1542_last_mbo_used);
+
+ if(mb[mbo].status || HOSTDATA(SCpnt->host)->SCint[mbo])
+ panic("Unable to find empty mailbox for aha1542.\n");
+
+ HOSTDATA(SCpnt->host)->SCint[mbo] = SCpnt; /* This will effectively prevent someone else from
+ screwing with this cdb. */
+
+ HOSTDATA(SCpnt->host)->aha1542_last_mbo_used = mbo;
+ restore_flags(flags);
+
+#ifdef DEBUG
+ printk("Sending command (%d %x)...",mbo, done);
+#endif
+
+ any2scsi(mb[mbo].ccbptr, &ccb[mbo]); /* This gets trashed for some reason*/
+
+ memset(&ccb[mbo], 0, sizeof(struct ccb));
+
+ ccb[mbo].cdblen = SCpnt->cmd_len;
+
+ direction = 0;
+ if (*cmd == READ_10 || *cmd == READ_6)
+ direction = 8;
+ else if (*cmd == WRITE_10 || *cmd == WRITE_6)
+ direction = 16;
+
+ memcpy(ccb[mbo].cdb, cmd, ccb[mbo].cdblen);
+
+ if (SCpnt->use_sg) {
+ struct scatterlist * sgpnt;
+ struct chain * cptr;
+#ifdef DEBUG
+ unsigned char * ptr;
+#endif
+ int i;
+ ccb[mbo].op = 2; /* SCSI Initiator Command w/scatter-gather*/
+ SCpnt->host_scribble = (unsigned char *) scsi_malloc(512);
+ sgpnt = (struct scatterlist *) SCpnt->request_buffer;
+ cptr = (struct chain *) SCpnt->host_scribble;
+ if (cptr == NULL) panic("aha1542.c: unable to allocate DMA memory\n");
+ for(i=0; i<SCpnt->use_sg; i++) {
+ if(sgpnt[i].length == 0 || SCpnt->use_sg > 16 ||
+ (((int)sgpnt[i].address) & 1) || (sgpnt[i].length & 1)){
+ unsigned char * ptr;
+ printk("Bad segment list supplied to aha1542.c (%d, %d)\n",SCpnt->use_sg,i);
+ for(i=0;i<SCpnt->use_sg;i++){
+ printk("%d: %x %x %d\n",i,(unsigned int) sgpnt[i].address, (unsigned int) sgpnt[i].alt_address,
+ sgpnt[i].length);
+ };
+ printk("cptr %x: ",(unsigned int) cptr);
+ ptr = (unsigned char *) &cptr[i];
+ for(i=0;i<18;i++) printk("%02x ", ptr[i]);
+ panic("Foooooooood fight!");
+ };
+ any2scsi(cptr[i].dataptr, sgpnt[i].address);
+ if(((unsigned int) sgpnt[i].address) & 0xff000000) goto baddma;
+ any2scsi(cptr[i].datalen, sgpnt[i].length);
+ };
+ any2scsi(ccb[mbo].datalen, SCpnt->use_sg * sizeof(struct chain));
+ any2scsi(ccb[mbo].dataptr, cptr);
+#ifdef DEBUG
+ printk("cptr %x: ",cptr);
+ ptr = (unsigned char *) cptr;
+ for(i=0;i<18;i++) printk("%02x ", ptr[i]);
+#endif
+ } else {
+ ccb[mbo].op = 0; /* SCSI Initiator Command */
+ SCpnt->host_scribble = NULL;
+ any2scsi(ccb[mbo].datalen, bufflen);
+ if(((unsigned int) buff & 0xff000000)) goto baddma;
+ any2scsi(ccb[mbo].dataptr, buff);
+ };
+ ccb[mbo].idlun = (target&7)<<5 | direction | (lun & 7); /*SCSI Target Id*/
+ ccb[mbo].rsalen = 16;
+ ccb[mbo].linkptr[0] = ccb[mbo].linkptr[1] = ccb[mbo].linkptr[2] = 0;
+ ccb[mbo].commlinkid = 0;
+
+#ifdef DEBUG
+ { int i;
+ printk("aha1542_command: sending.. ");
+ for (i = 0; i < sizeof(ccb[mbo])-10; i++)
+ printk("%02x ", ((unchar *)&ccb[mbo])[i]);
+ };
+#endif
+
+ if (done) {
+ DEB(printk("aha1542_queuecommand: now waiting for interrupt "); aha1542_stat());
+ SCpnt->scsi_done = done;
+ mb[mbo].status = 1;
+ aha1542_out(SCpnt->host->io_port, &ahacmd, 1); /* start scsi command */
+ DEB(aha1542_stat());
+ }
+ else
+ printk("aha1542_queuecommand: done can't be NULL\n");
+
+ return 0;
+ baddma:
+ panic("Buffer at address > 16Mb used for 1542B");
+}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+ SCpnt->SCp.Status++;
+}
+
+int aha1542_command(Scsi_Cmnd * SCpnt)
+{
+ DEB(printk("aha1542_command: ..calling aha1542_queuecommand\n"));
+
+ aha1542_queuecommand(SCpnt, internal_done);
+
+ SCpnt->SCp.Status = 0;
+ while (!SCpnt->SCp.Status)
+ barrier();
+ return SCpnt->result;
+}
+
+/* Initialize mailboxes */
+static void setup_mailboxes(int bse, struct Scsi_Host * shpnt)
+{
+ int i;
+ struct mailbox * mb;
+ struct ccb *ccb;
+
+ unchar cmd[5] = {CMD_MBINIT, AHA1542_MAILBOXES, 0, 0, 0};
+
+ mb = HOSTDATA(shpnt)->mb;
+ ccb = HOSTDATA(shpnt)->ccb;
+
+ for(i=0; i<AHA1542_MAILBOXES; i++){
+ mb[i].status = mb[AHA1542_MAILBOXES+i].status = 0;
+ any2scsi(mb[i].ccbptr, &ccb[i]);
+ };
+ aha1542_intr_reset(bse); /* reset interrupts, so they don't block */
+ any2scsi((cmd+2), mb);
+ aha1542_out(bse, cmd, 5);
+ WAIT(INTRFLAGS(bse), INTRMASK, HACC, 0);
+ while (0) {
+ fail:
+ printk("aha1542_detect: failed setting up mailboxes\n");
+ }
+ aha1542_intr_reset(bse);
+}
+
+static int aha1542_getconfig(int base_io, unsigned char * irq_level, unsigned char * dma_chan, unsigned char * scsi_id)
+{
+ unchar inquiry_cmd[] = {CMD_RETCONF };
+ unchar inquiry_result[3];
+ int i;
+ i = inb(STATUS(base_io));
+ if (i & DF) {
+ i = inb(DATA(base_io));
+ };
+ aha1542_out(base_io, inquiry_cmd, 1);
+ aha1542_in(base_io, inquiry_result, 3);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ while (0) {
+ fail:
+ printk("aha1542_detect: query board settings\n");
+ }
+ aha1542_intr_reset(base_io);
+ switch(inquiry_result[0]){
+ case 0x80:
+ *dma_chan = 7;
+ break;
+ case 0x40:
+ *dma_chan = 6;
+ break;
+ case 0x20:
+ *dma_chan = 5;
+ break;
+ case 0x01:
+ *dma_chan = 0;
+ break;
+ case 0:
+ /* This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel.
+ Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this. */
+ *dma_chan = 0xFF;
+ break;
+ default:
+ printk("Unable to determine Adaptec DMA priority. Disabling board\n");
+ return -1;
+ };
+ switch(inquiry_result[1]){
+ case 0x40:
+ *irq_level = 15;
+ break;
+ case 0x20:
+ *irq_level = 14;
+ break;
+ case 0x8:
+ *irq_level = 12;
+ break;
+ case 0x4:
+ *irq_level = 11;
+ break;
+ case 0x2:
+ *irq_level = 10;
+ break;
+ case 0x1:
+ *irq_level = 9;
+ break;
+ default:
+ printk("Unable to determine Adaptec IRQ level. Disabling board\n");
+ return -1;
+ };
+ *scsi_id=inquiry_result[2] & 7;
+ return 0;
+}
+
+/* This function should only be called for 1542C boards - we can detect
+ the special firmware settings and unlock the board */
+
+static int aha1542_mbenable(int base)
+{
+ static unchar mbenable_cmd[3];
+ static unchar mbenable_result[2];
+ int retval;
+
+ retval = BIOS_TRANSLATION_6432;
+
+ mbenable_cmd[0]=CMD_EXTBIOS;
+ aha1542_out(base,mbenable_cmd,1);
+ if(aha1542_in1(base,mbenable_result,2))
+ return retval;
+ WAITd(INTRFLAGS(base),INTRMASK,HACC,0,100);
+ aha1542_intr_reset(base);
+
+ if ((mbenable_result[0] & 0x08) || mbenable_result[1]) {
+ mbenable_cmd[0]=CMD_MBENABLE;
+ mbenable_cmd[1]=0;
+ mbenable_cmd[2]=mbenable_result[1];
+
+ if((mbenable_result[0] & 0x08) && (mbenable_result[1] & 0x03)) retval = BIOS_TRANSLATION_25563;
+
+ aha1542_out(base,mbenable_cmd,3);
+ WAIT(INTRFLAGS(base),INTRMASK,HACC,0);
+ };
+ while(0) {
+fail:
+ printk("aha1542_mbenable: Mailbox init failed\n");
+ }
+aha1542_intr_reset(base);
+return retval;
+}
+
+/* Query the board to find out if it is a 1542 or a 1740, or whatever. */
+static int aha1542_query(int base_io, int * transl)
+{
+ unchar inquiry_cmd[] = {CMD_INQUIRY };
+ unchar inquiry_result[4];
+ int i;
+ i = inb(STATUS(base_io));
+ if (i & DF) {
+ i = inb(DATA(base_io));
+ };
+ aha1542_out(base_io, inquiry_cmd, 1);
+ aha1542_in(base_io, inquiry_result, 4);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ while (0) {
+ fail:
+ printk("aha1542_detect: query card type\n");
+ }
+ aha1542_intr_reset(base_io);
+
+ *transl = BIOS_TRANSLATION_6432; /* Default case */
+
+/* For an AHA1740 series board, we ignore the board since there is a
+ hardware bug which can lead to wrong blocks being returned if the board
+ is operating in the 1542 emulation mode. Since there is an extended mode
+ driver, we simply ignore the board and let the 1740 driver pick it up.
+*/
+
+ if (inquiry_result[0] == 0x43) {
+ printk("aha1542.c: Emulation mode not supported for AHA 174N hardware.\n");
+ return 1;
+ };
+
+ /* Always call this - boards that do not support extended bios translation
+ will ignore the command, and we will set the proper default */
+
+ *transl = aha1542_mbenable(base_io);
+
+ return 0;
+}
+
+/* called from init/main.c */
+void aha1542_setup( char *str, int *ints)
+{
+ const char *ahausage = "aha1542: usage: aha1542=<PORTBASE>[,<BUSON>,<BUSOFF>[,<DMASPEED>]]\n";
+ static int setup_idx = 0;
+ int setup_portbase;
+
+ if(setup_idx >= MAXBOARDS)
+ {
+ printk("aha1542: aha1542_setup called too many times! Bad LILO params ?\n");
+ printk(" Entryline 1: %s\n",setup_str[0]);
+ printk(" Entryline 2: %s\n",setup_str[1]);
+ printk(" This line: %s\n",str);
+ return;
+ }
+ if (ints[0] < 1 || ints[0] > 4)
+ {
+ printk("aha1542: %s\n", str );
+ printk(ahausage);
+ printk("aha1542: Wrong parameters may cause system malfunction.. We try anyway..\n");
+ }
+
+ setup_called[setup_idx]=ints[0];
+ setup_str[setup_idx]=str;
+
+ setup_portbase = ints[0] >= 1 ? ints[1] : 0; /* Preserve the default value.. */
+ setup_buson [setup_idx] = ints[0] >= 2 ? ints[2] : 7;
+ setup_busoff [setup_idx] = ints[0] >= 3 ? ints[3] : 5;
+ if (ints[0] >= 4) {
+ int atbt = -1;
+ switch (ints[4]) {
+ case 5:
+ atbt = 0x00;
+ break;
+ case 6:
+ atbt = 0x04;
+ break;
+ case 7:
+ atbt = 0x01;
+ break;
+ case 8:
+ atbt = 0x02;
+ break;
+ case 10:
+ atbt = 0x03;
+ break;
+ default:
+ printk("aha1542: %s\n", str );
+ printk(ahausage);
+ printk("aha1542: Valid values for DMASPEED are 5-8, 10 MB/s. Using jumper defaults.\n");
+ break;
+ }
+ setup_dmaspeed[setup_idx] = atbt;
+ }
+
+ if (setup_portbase != 0)
+ bases[setup_idx] = setup_portbase;
+
+ ++setup_idx;
+}
+
+/* return non-zero on detection */
+int aha1542_detect(Scsi_Host_Template * tpnt)
+{
+ unsigned char dma_chan;
+ unsigned char irq_level;
+ unsigned char scsi_id;
+ unsigned long flags;
+ unsigned int base_io;
+ int trans;
+ struct Scsi_Host * shpnt = NULL;
+ int count = 0;
+ int indx;
+
+ DEB(printk("aha1542_detect: \n"));
+
+ tpnt->proc_dir = &proc_scsi_aha1542;
+
+ for(indx = 0; indx < sizeof(bases)/sizeof(bases[0]); indx++)
+ if(bases[indx] != 0 && !check_region(bases[indx], 4)) {
+ shpnt = scsi_register(tpnt,
+ sizeof(struct aha1542_hostdata));
+
+ /* For now we do this - until kmalloc is more intelligent
+ we are resigned to stupid hacks like this */
+ if ((unsigned int) shpnt > 0xffffff) {
+ printk("Invalid address for shpnt with 1542.\n");
+ goto unregister;
+ }
+
+ if(!aha1542_test_port(bases[indx], shpnt)) goto unregister;
+
+
+ base_io = bases[indx];
+
+ /* Set the Bus on/off-times as not to ruin floppy performance */
+ {
+ unchar oncmd[] = {CMD_BUSON_TIME, 7};
+ unchar offcmd[] = {CMD_BUSOFF_TIME, 5};
+
+ if(setup_called[indx])
+ {
+ oncmd[1] = setup_buson[indx];
+ offcmd[1] = setup_busoff[indx];
+ }
+
+ aha1542_intr_reset(base_io);
+ aha1542_out(base_io, oncmd, 2);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ aha1542_intr_reset(base_io);
+ aha1542_out(base_io, offcmd, 2);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ if (setup_dmaspeed[indx] >= 0)
+ {
+ unchar dmacmd[] = {CMD_DMASPEED, 0};
+ dmacmd[1] = setup_dmaspeed[indx];
+ aha1542_intr_reset(base_io);
+ aha1542_out(base_io, dmacmd, 2);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ }
+ while (0) {
+ fail:
+ printk("aha1542_detect: setting bus on/off-time failed\n");
+ }
+ aha1542_intr_reset(base_io);
+ }
+ if(aha1542_query(base_io, &trans)) goto unregister;
+
+ if (aha1542_getconfig(base_io, &irq_level, &dma_chan, &scsi_id) == -1) goto unregister;
+
+ printk("Configuring Adaptec (SCSI-ID %d) at IO:%x, IRQ %d", scsi_id, base_io, irq_level);
+ if (dma_chan != 0xFF)
+ printk(", DMA priority %d", dma_chan);
+ printk("\n");
+
+ DEB(aha1542_stat());
+ setup_mailboxes(base_io, shpnt);
+
+ DEB(aha1542_stat());
+
+ DEB(printk("aha1542_detect: enable interrupt channel %d\n", irq_level));
+ save_flags(flags);
+ cli();
+ if (request_irq(irq_level,aha1542_intr_handle, 0, "aha1542", NULL)) {
+ printk("Unable to allocate IRQ for adaptec controller.\n");
+ restore_flags(flags);
+ goto unregister;
+ }
+
+ if (dma_chan != 0xFF) {
+ if (request_dma(dma_chan,"aha1542")) {
+ printk("Unable to allocate DMA channel for Adaptec.\n");
+ free_irq(irq_level, NULL);
+ restore_flags(flags);
+ goto unregister;
+ }
+
+ if (dma_chan == 0 || dma_chan >= 5) {
+ set_dma_mode(dma_chan, DMA_MODE_CASCADE);
+ enable_dma(dma_chan);
+ }
+ }
+ aha_host[irq_level - 9] = shpnt;
+ shpnt->this_id = scsi_id;
+ shpnt->unique_id = base_io;
+ shpnt->io_port = base_io;
+ shpnt->n_io_port = 4; /* Number of bytes of I/O space used */
+ shpnt->dma_channel = dma_chan;
+ shpnt->irq = irq_level;
+ HOSTDATA(shpnt)->bios_translation = trans;
+ if(trans == BIOS_TRANSLATION_25563)
+ printk("aha1542.c: Using extended bios translation\n");
+ HOSTDATA(shpnt)->aha1542_last_mbi_used = (2*AHA1542_MAILBOXES - 1);
+ HOSTDATA(shpnt)->aha1542_last_mbo_used = (AHA1542_MAILBOXES - 1);
+ memset(HOSTDATA(shpnt)->SCint, 0, sizeof(HOSTDATA(shpnt)->SCint));
+ restore_flags(flags);
+#if 0
+ DEB(printk(" *** READ CAPACITY ***\n"));
+
+ {
+ unchar buf[8];
+ static unchar cmd[] = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i;
+
+ for (i = 0; i < sizeof(buf); ++i) buf[i] = 0x87;
+ for (i = 0; i < 2; ++i)
+ if (!aha1542_command(i, cmd, buf, sizeof(buf))) {
+ printk("aha_detect: LU %d sector_size %d device_size %d\n",
+ i, xscsi2int(buf+4), xscsi2int(buf));
+ }
+ }
+
+ DEB(printk(" *** NOW RUNNING MY OWN TEST *** \n"));
+
+ for (i = 0; i < 4; ++i)
+ {
+ unsigned char cmd[10];
+ static buffer[512];
+
+ cmd[0] = READ_10;
+ cmd[1] = 0;
+ xany2scsi(cmd+2, i);
+ cmd[6] = 0;
+ cmd[7] = 0;
+ cmd[8] = 1;
+ cmd[9] = 0;
+ aha1542_command(0, cmd, buffer, 512);
+ }
+#endif
+ request_region(bases[indx], 4,"aha1542"); /* Register the IO ports that we use */
+ count++;
+ continue;
+ unregister:
+ scsi_unregister(shpnt);
+ continue;
+
+ };
+
+ return count;
+}
+
+static int aha1542_restart(struct Scsi_Host * shost)
+{
+ int i;
+ int count = 0;
+#if 0
+ unchar ahacmd = CMD_START_SCSI;
+#endif
+
+ for(i=0; i< AHA1542_MAILBOXES; i++)
+ if(HOSTDATA(shost)->SCint[i] &&
+ !(HOSTDATA(shost)->SCint[i]->device->soft_reset))
+ {
+#if 0
+ HOSTDATA(shost)->mb[i].status = 1; /* Indicate ready to restart... */
+#endif
+ count++;
+ }
+
+ printk("Potential to restart %d stalled commands...\n", count);
+#if 0
+ /* start scsi command */
+ if (count) aha1542_out(shost->io_port, &ahacmd, 1);
+#endif
+ return 0;
+}
+
+/* The abort command does not leave the device in a clean state where
+ it is available to be used again. Until this gets worked out, we will
+ leave it commented out. */
+
+int aha1542_abort(Scsi_Cmnd * SCpnt)
+{
+#if 0
+ unchar ahacmd = CMD_START_SCSI;
+ unsigned long flags;
+ struct mailbox * mb;
+ int mbi, mbo, i;
+
+ printk("In aha1542_abort: %x %x\n",
+ inb(STATUS(SCpnt->host->io_port)),
+ inb(INTRFLAGS(SCpnt->host->io_port)));
+
+ save_flags(flags);
+ cli();
+ mb = HOSTDATA(SCpnt->host)->mb;
+ mbi = HOSTDATA(SCpnt->host)->aha1542_last_mbi_used + 1;
+ if (mbi >= 2*AHA1542_MAILBOXES) mbi = AHA1542_MAILBOXES;
+
+ do{
+ if(mb[mbi].status != 0) break;
+ mbi++;
+ if (mbi >= 2*AHA1542_MAILBOXES) mbi = AHA1542_MAILBOXES;
+ } while (mbi != HOSTDATA(SCpnt->host)->aha1542_last_mbi_used);
+ restore_flags(flags);
+
+ if(mb[mbi].status) {
+ printk("Lost interrupt discovered on irq %d - attempting to recover\n",
+ SCpnt->host->irq);
+ aha1542_intr_handle(SCpnt->host->irq, NULL);
+ return 0;
+ }
+
+ /* OK, no lost interrupt. Try looking to see how many pending commands
+ we think we have. */
+
+ for(i=0; i< AHA1542_MAILBOXES; i++)
+ if(HOSTDATA(SCpnt->host)->SCint[i])
+ {
+ if(HOSTDATA(SCpnt->host)->SCint[i] == SCpnt) {
+ printk("Timed out command pending for %s\n",
+ kdevname(SCpnt->request.rq_dev));
+ if (HOSTDATA(SCpnt->host)->mb[i].status) {
+ printk("OGMB still full - restarting\n");
+ aha1542_out(SCpnt->host->io_port, &ahacmd, 1);
+ };
+ } else
+ printk("Other pending command %s\n",
+ kdevname(SCpnt->request.rq_dev));
+ }
+
+#endif
+
+ DEB(printk("aha1542_abort\n"));
+#if 0
+ save_flags(flags);
+ cli();
+ for(mbo = 0; mbo < AHA1542_MAILBOXES; mbo++)
+ if (SCpnt == HOSTDATA(SCpnt->host)->SCint[mbo]){
+ mb[mbo].status = 2; /* Abort command */
+ aha1542_out(SCpnt->host->io_port, &ahacmd, 1); /* start scsi command */
+ restore_flags(flags);
+ break;
+ };
+#endif
+ return SCSI_ABORT_SNOOZE;
+}
+
+/* We do not implement a reset function here, but the upper level code
+ assumes that it will get some kind of response for the command in
+ SCpnt. We must oblige, or the command will hang the scsi system.
+ For a first go, we assume that the 1542 notifies us with all of the
+ pending commands (it does implement soft reset, after all). */
+
+int aha1542_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+{
+ unchar ahacmd = CMD_START_SCSI;
+ int i;
+
+ /*
+ * See if a bus reset was suggested.
+ */
+ if( reset_flags & SCSI_RESET_SUGGEST_BUS_RESET )
+ {
+ /*
+ * This does a scsi reset for all devices on the bus.
+ * In principle, we could also reset the 1542 - should
+ * we do this? Try this first, and we can add that later
+ * if it turns out to be useful.
+ */
+ outb(HRST | SCRST, CONTROL(SCpnt->host->io_port));
+
+ /*
+ * Wait for the thing to settle down a bit. Unfortunately
+ * this is going to basically lock up the machine while we
+ * wait for this to complete. To be 100% correct, we need to
+ * check for timeout, and if we are doing something like this
+ * we are pretty desperate anyways.
+ */
+ WAIT(STATUS(SCpnt->host->io_port),
+ STATMASK, INIT|IDLE, STST|DIAGF|INVDCMD|DF|CDF);
+
+ /*
+ * We need to do this too before the 1542 can interact with
+ * us again.
+ */
+ setup_mailboxes(SCpnt->host->io_port, SCpnt->host);
+
+ /*
+ * Now try to pick up the pieces. Restart all commands
+ * that are currently active on the bus, and reset all of
+ * the datastructures. We have some time to kill while
+ * things settle down, so print a nice message.
+ */
+ printk("Sent BUS RESET to scsi host %d\n", SCpnt->host->host_no);
+
+ for(i=0; i< AHA1542_MAILBOXES; i++)
+ if(HOSTDATA(SCpnt->host)->SCint[i] != NULL)
+ {
+ Scsi_Cmnd * SCtmp;
+ SCtmp = HOSTDATA(SCpnt->host)->SCint[i];
+ SCtmp->result = DID_RESET << 16;
+ if (SCtmp->host_scribble) scsi_free(SCtmp->host_scribble, 512);
+ printk("Sending DID_RESET for target %d\n", SCpnt->target);
+ SCtmp->scsi_done(SCpnt);
+
+ HOSTDATA(SCpnt->host)->SCint[i] = NULL;
+ HOSTDATA(SCpnt->host)->mb[i].status = 0;
+ }
+ /*
+ * Now tell the mid-level code what we did here. Since
+ * we have restarted all of the outstanding commands,
+ * then report SUCCESS.
+ */
+ return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET);
+fail:
+ printk("aha1542.c: Unable to perform hard reset.\n");
+ printk("Power cycle machine to reset\n");
+ return (SCSI_RESET_ERROR | SCSI_RESET_BUS_RESET);
+
+
+ }
+ else
+ {
+ /* This does a selective reset of just the one device */
+ /* First locate the ccb for this command */
+ for(i=0; i< AHA1542_MAILBOXES; i++)
+ if(HOSTDATA(SCpnt->host)->SCint[i] == SCpnt)
+ {
+ HOSTDATA(SCpnt->host)->ccb[i].op = 0x81; /* BUS DEVICE RESET */
+ /* Now tell the 1542 to flush all pending commands for this target */
+ aha1542_out(SCpnt->host->io_port, &ahacmd, 1);
+
+ /* Here is the tricky part. What to do next. Do we get an interrupt
+ for the commands that we aborted with the specified target, or
+ do we generate this on our own? Try it without first and see
+ what happens */
+ printk("Sent BUS DEVICE RESET to target %d\n", SCpnt->target);
+
+ /* If the first does not work, then try the second. I think the
+ first option is more likely to be correct. Free the command
+ block for all commands running on this target... */
+ for(i=0; i< AHA1542_MAILBOXES; i++)
+ if(HOSTDATA(SCpnt->host)->SCint[i] &&
+ HOSTDATA(SCpnt->host)->SCint[i]->target == SCpnt->target)
+ {
+ Scsi_Cmnd * SCtmp;
+ SCtmp = HOSTDATA(SCpnt->host)->SCint[i];
+ SCtmp->result = DID_RESET << 16;
+ if (SCtmp->host_scribble) scsi_free(SCtmp->host_scribble, 512);
+ printk("Sending DID_RESET for target %d\n", SCpnt->target);
+ SCtmp->scsi_done(SCpnt);
+
+ HOSTDATA(SCpnt->host)->SCint[i] = NULL;
+ HOSTDATA(SCpnt->host)->mb[i].status = 0;
+ }
+ return SCSI_RESET_SUCCESS;
+ }
+ }
+ /* No active command at this time, so this means that each time we got
+ some kind of response the last time through. Tell the mid-level code
+ to request sense information in order to decide what to do next. */
+ return SCSI_RESET_PUNT;
+}
+
+#include "sd.h"
+
+int aha1542_biosparam(Scsi_Disk * disk, kdev_t dev, int * ip)
+{
+ int translation_algorithm;
+ int size = disk->capacity;
+
+ translation_algorithm = HOSTDATA(disk->device->host)->bios_translation;
+
+ if((size>>11) > 1024 && translation_algorithm == BIOS_TRANSLATION_25563) {
+ /* Please verify that this is the same as what DOS returns */
+ ip[0] = 255;
+ ip[1] = 63;
+ ip[2] = size /255/63;
+ } else {
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ }
+
+ return 0;
+}
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = AHA1542;
+
+#include "scsi_module.c"
+#endif
+
diff --git a/linux/src/drivers/scsi/aha1542.h b/linux/src/drivers/scsi/aha1542.h
new file mode 100644
index 00000000..ee82833a
--- /dev/null
+++ b/linux/src/drivers/scsi/aha1542.h
@@ -0,0 +1,171 @@
+#ifndef _AHA1542_H
+
+/* $Id: aha1542.h,v 1.1 1999/04/26 05:54:12 tb Exp $
+ *
+ * Header file for the adaptec 1542 driver for Linux
+ *
+ * $Log: aha1542.h,v $
+ * Revision 1.1 1992/07/24 06:27:38 root
+ * Initial revision
+ *
+ * Revision 1.2 1992/07/04 18:41:49 root
+ * Replaced distribution with current drivers
+ *
+ * Revision 1.3 1992/06/23 23:58:20 root
+ * Fixes.
+ *
+ * Revision 1.2 1992/05/26 22:13:23 root
+ * Changed bug that prevented DMA above first 2 mbytes.
+ *
+ * Revision 1.1 1992/05/22 21:00:29 root
+ * Initial revision
+ *
+ * Revision 1.1 1992/04/24 18:01:50 root
+ * Initial revision
+ *
+ * Revision 1.1 1992/04/02 03:23:13 drew
+ * Initial revision
+ *
+ * Revision 1.3 1992/01/27 14:46:29 tthorn
+ * *** empty log message ***
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+/* I/O Port interface 4.2 */
+/* READ */
+#define STATUS(base) base
+#define STST 0x80 /* Self Test in Progress */
+#define DIAGF 0x40 /* Internal Diagnostic Failure */
+#define INIT 0x20 /* Mailbox Initialization Required */
+#define IDLE 0x10 /* SCSI Host Adapter Idle */
+#define CDF 0x08 /* Command/Data Out Port Full */
+#define DF 0x04 /* Data In Port Full */
+#define INVDCMD 0x01 /* Invalid H A Command */
+#define STATMASK 0xfd /* 0x02 is reserved */
+
+#define INTRFLAGS(base) (STATUS(base)+2)
+#define ANYINTR 0x80 /* Any Interrupt */
+#define SCRD 0x08 /* SCSI Reset Detected */
+#define HACC 0x04 /* HA Command Complete */
+#define MBOA 0x02 /* MBO Empty */
+#define MBIF 0x01 /* MBI Full */
+#define INTRMASK 0x8f
+
+/* WRITE */
+#define CONTROL(base) STATUS(base)
+#define HRST 0x80 /* Hard Reset */
+#define SRST 0x40 /* Soft Reset */
+#define IRST 0x20 /* Interrupt Reset */
+#define SCRST 0x10 /* SCSI Bus Reset */
+
+/* READ/WRITE */
+#define DATA(base) (STATUS(base)+1)
+#define CMD_NOP 0x00 /* No Operation */
+#define CMD_MBINIT 0x01 /* Mailbox Initialization */
+#define CMD_START_SCSI 0x02 /* Start SCSI Command */
+#define CMD_INQUIRY 0x04 /* Adapter Inquiry */
+#define CMD_EMBOI 0x05 /* Enable MailBox Out Interrupt */
+#define CMD_BUSON_TIME 0x07 /* Set Bus-On Time */
+#define CMD_BUSOFF_TIME 0x08 /* Set Bus-Off Time */
+#define CMD_DMASPEED 0x09 /* Set AT Bus Transfer Speed */
+#define CMD_RETDEVS 0x0a /* Return Installed Devices */
+#define CMD_RETCONF 0x0b /* Return Configuration Data */
+#define CMD_RETSETUP 0x0d /* Return Setup Data */
+#define CMD_ECHO 0x1f /* ECHO Command Data */
+
+#define CMD_EXTBIOS 0x28 /* Return extend bios information only 1542C */
+#define CMD_MBENABLE 0x29 /* Set Mailbox Interface enable only 1542C */
+
+/* Mailbox Definition 5.2.1 and 5.2.2 */
+struct mailbox {
+ unchar status; /* Command/Status */
+ unchar ccbptr[3]; /* msb, .., lsb */
+};
+
+/* This is used with scatter-gather */
+struct chain {
+ unchar datalen[3]; /* Size of this part of chain */
+ unchar dataptr[3]; /* Location of data */
+};
+
+/* These belong in scsi.h also */
+#define any2scsi(up, p) \
+(up)[0] = (((unsigned long)(p)) >> 16) ; \
+(up)[1] = (((unsigned long)(p)) >> 8); \
+(up)[2] = ((unsigned long)(p));
+
+#define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) )
+
+#define xany2scsi(up, p) \
+(up)[0] = ((long)(p)) >> 24; \
+(up)[1] = ((long)(p)) >> 16; \
+(up)[2] = ((long)(p)) >> 8; \
+(up)[3] = ((long)(p));
+
+#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \
+ + (((long)(up)[2]) << 8) + ((long)(up)[3]) )
+
+#define MAX_CDB 12
+#define MAX_SENSE 14
+
+struct ccb { /* Command Control Block 5.3 */
+ unchar op; /* Command Control Block Operation Code */
+ unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */
+ /* Outbound data transfer, length is checked*/
+ /* Inbound data transfer, length is checked */
+ /* Logical Unit Number */
+ unchar cdblen; /* SCSI Command Length */
+ unchar rsalen; /* Request Sense Allocation Length/Disable */
+ unchar datalen[3]; /* Data Length (msb, .., lsb) */
+ unchar dataptr[3]; /* Data Pointer */
+ unchar linkptr[3]; /* Link Pointer */
+ unchar commlinkid; /* Command Linking Identifier */
+ unchar hastat; /* Host Adapter Status (HASTAT) */
+ unchar tarstat; /* Target Device Status */
+ unchar reserved[2];
+ unchar cdb[MAX_CDB+MAX_SENSE];/* SCSI Command Descriptor Block */
+ /* REQUEST SENSE */
+};
+
+int aha1542_detect(Scsi_Host_Template *);
+int aha1542_command(Scsi_Cmnd *);
+int aha1542_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int aha1542_abort(Scsi_Cmnd *);
+int aha1542_reset(Scsi_Cmnd *, unsigned int);
+int aha1542_biosparam(Disk *, kdev_t, int*);
+
+#define AHA1542_MAILBOXES 8
+#define AHA1542_SCATTER 16
+#define AHA1542_CMDLUN 1
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+extern struct proc_dir_entry proc_scsi_aha1542;
+
+#define AHA1542 { NULL, NULL, \
+ &proc_scsi_aha1542,/* proc_dir_entry */ \
+ NULL, \
+ "Adaptec 1542", \
+ aha1542_detect, \
+ NULL, \
+ NULL, \
+ aha1542_command, \
+ aha1542_queuecommand, \
+ aha1542_abort, \
+ aha1542_reset, \
+ NULL, \
+ aha1542_biosparam, \
+ AHA1542_MAILBOXES, \
+ 7, \
+ AHA1542_SCATTER, \
+ AHA1542_CMDLUN, \
+ 0, \
+ 1, \
+ ENABLE_CLUSTERING}
+
+#endif
diff --git a/linux/src/drivers/scsi/aha1740.c b/linux/src/drivers/scsi/aha1740.c
new file mode 100644
index 00000000..013218ca
--- /dev/null
+++ b/linux/src/drivers/scsi/aha1740.c
@@ -0,0 +1,614 @@
+/* $Id: aha1740.c,v 1.1 1999/04/26 05:54:13 tb Exp $
+ * 1993/03/31
+ * linux/kernel/aha1740.c
+ *
+ * Based loosely on aha1542.c which is
+ * Copyright (C) 1992 Tommy Thorn and
+ * Modified by Eric Youngdale
+ *
+ * This file is aha1740.c, written and
+ * Copyright (C) 1992,1993 Brad McLean
+ *
+ * Modifications to makecode and queuecommand
+ * for proper handling of multiple devices courteously
+ * provided by Michael Weller, March, 1993
+ *
+ * Multiple adapter support, extended translation detection,
+ * update to current scsi subsystem changes, proc fs support,
+ * working (!) module support based on patches from Andreas Arens,
+ * by Andreas Degert <ad@papyrus.hamburg.com>, 2/1997
+ *
+ * aha1740_makecode may still need even more work
+ * if it doesn't work for your devices, take a look.
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <asm/dma.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#include "aha1740.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_aha1740 = {
+ PROC_SCSI_AHA1740, 7, "aha1740",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* IF YOU ARE HAVING PROBLEMS WITH THIS DRIVER, AND WANT TO WATCH
+ IT WORK, THEN:
+#define DEBUG
+*/
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+/*
+static const char RCSid[] = "$Header: cvs/gnumach/linux/src/drivers/scsi/Attic/aha1740.c,v 1.1 1999/04/26 05:54:13 tb Exp $";
+*/
+
+struct aha1740_hostdata {
+ unsigned int slot;
+ unsigned int translation;
+ unsigned int last_ecb_used;
+ struct ecb ecb[AHA1740_ECBS];
+};
+
+#define HOSTDATA(host) ((struct aha1740_hostdata *) &host->hostdata)
+
+/* One for each IRQ level (9-15) */
+static struct Scsi_Host * aha_host[8] = {NULL, };
+
+int aha1740_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
+{
+ int len;
+ struct Scsi_Host * shpnt;
+ struct aha1740_hostdata *host;
+
+ if (inout)
+ return(-ENOSYS);
+
+ for (len = 0; len < 8; len++) {
+ shpnt = aha_host[len];
+ if (shpnt && shpnt->host_no == hostno)
+ break;
+ }
+ host = HOSTDATA(shpnt);
+
+ len = sprintf(buffer, "aha174x at IO:%x, IRQ %d, SLOT %d.\n"
+ "Extended translation %sabled.\n",
+ shpnt->io_port, shpnt->irq, host->slot,
+ host->translation ? "en" : "dis");
+
+ if (offset > len) {
+ *start = buffer;
+ return 0;
+ }
+
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ return len;
+}
+
+
+int aha1740_makecode(unchar *sense, unchar *status)
+{
+ struct statusword
+ {
+ ushort don:1, /* Command Done - No Error */
+ du:1, /* Data underrun */
+ :1, qf:1, /* Queue full */
+ sc:1, /* Specification Check */
+ dor:1, /* Data overrun */
+ ch:1, /* Chaining Halted */
+ intr:1, /* Interrupt issued */
+ asa:1, /* Additional Status Available */
+ sns:1, /* Sense information Stored */
+ :1, ini:1, /* Initialization Required */
+ me:1, /* Major error or exception */
+ :1, eca:1, /* Extended Contingent alliance */
+ :1;
+ } status_word;
+ int retval = DID_OK;
+
+ status_word = * (struct statusword *) status;
+#ifdef DEBUG
+ printk("makecode from %x,%x,%x,%x %x,%x,%x,%x",
+ status[0], status[1], status[2], status[3],
+ sense[0], sense[1], sense[2], sense[3]);
+#endif
+ if (!status_word.don) /* Anything abnormal was detected */
+ {
+ if ( (status[1]&0x18) || status_word.sc ) /*Additional info available*/
+ {
+ /* Use the supplied info for further diagnostics */
+ switch ( status[2] )
+ {
+ case 0x12:
+ if ( status_word.dor )
+ retval=DID_ERROR; /* It's an Overrun */
+ /* If not overrun, assume underrun and ignore it! */
+ case 0x00: /* No info, assume no error, should not occur */
+ break;
+ case 0x11:
+ case 0x21:
+ retval=DID_TIME_OUT;
+ break;
+ case 0x0a:
+ retval=DID_BAD_TARGET;
+ break;
+ case 0x04:
+ case 0x05:
+ retval=DID_ABORT;
+ /* Either by this driver or the AHA1740 itself */
+ break;
+ default:
+ retval=DID_ERROR; /* No further diagnostics possible */
+ }
+ }
+ else
+ { /* Michael suggests, and Brad concurs: */
+ if ( status_word.qf )
+ {
+ retval = DID_TIME_OUT; /* forces a redo */
+ /* I think this specific one should not happen -Brad */
+ printk("aha1740.c: WARNING: AHA1740 queue overflow!\n");
+ }
+ else if ( status[0]&0x60 )
+ {
+ retval = DID_ERROR; /* Didn't find a better error */
+ }
+ /* In any other case return DID_OK so for example
+ CONDITION_CHECKS make it through to the appropriate
+ device driver */
+ }
+ }
+ /* Under all circumstances supply the target status -Michael */
+ return status[3] | retval << 16;
+}
+
+int aha1740_test_port(unsigned int base)
+{
+ char name[4], tmp;
+
+ /* Okay, look for the EISA ID's */
+ name[0]= 'A' -1 + ((tmp = inb(HID0(base))) >> 2); /* First character */
+ name[1]= 'A' -1 + ((tmp & 3) << 3);
+ name[1]+= ((tmp = inb(HID1(base))) >> 5)&0x7; /* Second Character */
+ name[2]= 'A' -1 + (tmp & 0x1f); /* Third Character */
+ name[3]=0;
+ tmp = inb(HID2(base));
+ if ( strcmp ( name, HID_MFG ) || inb(HID2(base)) != HID_PRD )
+ return 0; /* Not an Adaptec 174x */
+
+/* if ( inb(HID3(base)) != HID_REV )
+ printk("aha174x: Warning; board revision of %d; expected %d\n",
+ inb(HID3(base)),HID_REV); */
+
+ if ( inb(EBCNTRL(base)) != EBCNTRL_VALUE )
+ {
+ printk("aha174x: Board detected, but EBCNTRL = %x, so disabled it.\n",
+ inb(EBCNTRL(base)));
+ return 0;
+ }
+
+ if ( inb(PORTADR(base)) & PORTADDR_ENH )
+ return 1; /* Okay, we're all set */
+
+ printk("aha174x: Board detected, but not in enhanced mode, so disabled it.\n");
+ return 0;
+}
+
+/* A "high" level interrupt handler */
+void aha1740_intr_handle(int irq, void *dev_id, struct pt_regs * regs)
+{
+ void (*my_done)(Scsi_Cmnd *);
+ int errstatus, adapstat;
+ int number_serviced;
+ struct ecb *ecbptr;
+ Scsi_Cmnd *SCtmp;
+ unsigned int base;
+
+ if (!aha_host[irq - 9])
+ panic("aha1740.c: Irq from unknown host!\n");
+ base = aha_host[irq - 9]->io_port;
+ number_serviced = 0;
+
+ while(inb(G2STAT(base)) & G2STAT_INTPEND)
+ {
+ DEB(printk("aha1740_intr top of loop.\n"));
+ adapstat = inb(G2INTST(base));
+ ecbptr = (struct ecb *) bus_to_virt(inl(MBOXIN0(base)));
+ outb(G2CNTRL_IRST,G2CNTRL(base)); /* interrupt reset */
+
+ switch ( adapstat & G2INTST_MASK )
+ {
+ case G2INTST_CCBRETRY:
+ case G2INTST_CCBERROR:
+ case G2INTST_CCBGOOD:
+ /* Host Ready -> Mailbox in complete */
+ outb(G2CNTRL_HRDY,G2CNTRL(base));
+ if (!ecbptr)
+ {
+ printk("Aha1740 null ecbptr in interrupt (%x,%x,%x,%d)\n",
+ inb(G2STAT(base)),adapstat,
+ inb(G2INTST(base)), number_serviced++);
+ continue;
+ }
+ SCtmp = ecbptr->SCpnt;
+ if (!SCtmp)
+ {
+ printk("Aha1740 null SCtmp in interrupt (%x,%x,%x,%d)\n",
+ inb(G2STAT(base)),adapstat,
+ inb(G2INTST(base)), number_serviced++);
+ continue;
+ }
+ if (SCtmp->host_scribble)
+ scsi_free(SCtmp->host_scribble, 512);
+ /* Fetch the sense data, and tuck it away, in the required slot.
+ The Adaptec automatically fetches it, and there is no
+ guarantee that we will still have it in the cdb when we come
+ back */
+ if ( (adapstat & G2INTST_MASK) == G2INTST_CCBERROR )
+ {
+ memcpy(SCtmp->sense_buffer, ecbptr->sense,
+ sizeof(SCtmp->sense_buffer));
+ errstatus = aha1740_makecode(ecbptr->sense,ecbptr->status);
+ }
+ else
+ errstatus = 0;
+ DEB(if (errstatus) printk("aha1740_intr_handle: returning %6x\n",
+ errstatus));
+ SCtmp->result = errstatus;
+ my_done = ecbptr->done;
+ memset(ecbptr,0,sizeof(struct ecb));
+ if ( my_done )
+ my_done(SCtmp);
+ break;
+ case G2INTST_HARDFAIL:
+ printk(KERN_ALERT "aha1740 hardware failure!\n");
+ panic("aha1740.c"); /* Goodbye */
+ case G2INTST_ASNEVENT:
+ printk("aha1740 asynchronous event: %02x %02x %02x %02x %02x\n",
+ adapstat, inb(MBOXIN0(base)), inb(MBOXIN1(base)),
+ inb(MBOXIN2(base)), inb(MBOXIN3(base))); /* Say What? */
+ /* Host Ready -> Mailbox in complete */
+ outb(G2CNTRL_HRDY,G2CNTRL(base));
+ break;
+ case G2INTST_CMDGOOD:
+ /* set immediate command success flag here: */
+ break;
+ case G2INTST_CMDERROR:
+ /* Set immediate command failure flag here: */
+ break;
+ }
+ number_serviced++;
+ }
+}
+
+int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ unchar direction;
+ unchar *cmd = (unchar *) SCpnt->cmnd;
+ unchar target = SCpnt->target;
+ struct aha1740_hostdata *host = HOSTDATA(SCpnt->host);
+ unsigned long flags;
+ void *buff = SCpnt->request_buffer;
+ int bufflen = SCpnt->request_bufflen;
+ int ecbno;
+ DEB(int i);
+
+ if(*cmd == REQUEST_SENSE)
+ {
+ if (bufflen != sizeof(SCpnt->sense_buffer))
+ {
+ printk("Wrong buffer length supplied for request sense (%d)\n",
+ bufflen);
+ }
+ SCpnt->result = 0;
+ done(SCpnt);
+ return 0;
+ }
+
+#ifdef DEBUG
+ if (*cmd == READ_10 || *cmd == WRITE_10)
+ i = xscsi2int(cmd+2);
+ else if (*cmd == READ_6 || *cmd == WRITE_6)
+ i = scsi2int(cmd+2);
+ else
+ i = -1;
+ printk("aha1740_queuecommand: dev %d cmd %02x pos %d len %d ",
+ target, *cmd, i, bufflen);
+ printk("scsi cmd:");
+ for (i = 0; i < SCpnt->cmd_len; i++) printk("%02x ", cmd[i]);
+ printk("\n");
+#endif
+
+ /* locate an available ecb */
+ save_flags(flags);
+ cli();
+ ecbno = host->last_ecb_used + 1; /* An optimization */
+ if (ecbno >= AHA1740_ECBS)
+ ecbno = 0;
+ do {
+ if (!host->ecb[ecbno].cmdw)
+ break;
+ ecbno++;
+ if (ecbno >= AHA1740_ECBS)
+ ecbno = 0;
+ } while (ecbno != host->last_ecb_used);
+
+ if (host->ecb[ecbno].cmdw)
+ panic("Unable to find empty ecb for aha1740.\n");
+
+ host->ecb[ecbno].cmdw = AHA1740CMD_INIT; /* SCSI Initiator Command
+ doubles as reserved flag */
+
+ host->last_ecb_used = ecbno;
+ restore_flags(flags);
+
+#ifdef DEBUG
+ printk("Sending command (%d %x)...", ecbno, done);
+#endif
+
+ host->ecb[ecbno].cdblen = SCpnt->cmd_len; /* SCSI Command Descriptor Block Length */
+
+ direction = 0;
+ if (*cmd == READ_10 || *cmd == READ_6)
+ direction = 1;
+ else if (*cmd == WRITE_10 || *cmd == WRITE_6)
+ direction = 0;
+
+ memcpy(host->ecb[ecbno].cdb, cmd, SCpnt->cmd_len);
+
+ if (SCpnt->use_sg)
+ {
+ struct scatterlist * sgpnt;
+ struct aha1740_chain * cptr;
+ int i;
+ DEB(unsigned char * ptr);
+
+ host->ecb[ecbno].sg = 1; /* SCSI Initiator Command w/scatter-gather*/
+ SCpnt->host_scribble = (unsigned char *) scsi_malloc(512);
+ sgpnt = (struct scatterlist *) SCpnt->request_buffer;
+ cptr = (struct aha1740_chain *) SCpnt->host_scribble;
+ if (cptr == NULL) panic("aha1740.c: unable to allocate DMA memory\n");
+ for(i=0; i<SCpnt->use_sg; i++)
+ {
+ cptr[i].datalen = sgpnt[i].length;
+ cptr[i].dataptr = virt_to_bus(sgpnt[i].address);
+ }
+ host->ecb[ecbno].datalen = SCpnt->use_sg * sizeof(struct aha1740_chain);
+ host->ecb[ecbno].dataptr = virt_to_bus(cptr);
+#ifdef DEBUG
+ printk("cptr %x: ",cptr);
+ ptr = (unsigned char *) cptr;
+ for(i=0;i<24;i++) printk("%02x ", ptr[i]);
+#endif
+ }
+ else
+ {
+ SCpnt->host_scribble = NULL;
+ host->ecb[ecbno].datalen = bufflen;
+ host->ecb[ecbno].dataptr = virt_to_bus(buff);
+ }
+ host->ecb[ecbno].lun = SCpnt->lun;
+ host->ecb[ecbno].ses = 1; /* Suppress underrun errors */
+ host->ecb[ecbno].dir = direction;
+ host->ecb[ecbno].ars = 1; /* Yes, get the sense on an error */
+ host->ecb[ecbno].senselen = 12;
+ host->ecb[ecbno].senseptr = virt_to_bus(host->ecb[ecbno].sense);
+ host->ecb[ecbno].statusptr = virt_to_bus(host->ecb[ecbno].status);
+ host->ecb[ecbno].done = done;
+ host->ecb[ecbno].SCpnt = SCpnt;
+#ifdef DEBUG
+ {
+ int i;
+ printk("aha1740_command: sending.. ");
+ for (i = 0; i < sizeof(host->ecb[ecbno]) - 10; i++)
+ printk("%02x ", ((unchar *)&host->ecb[ecbno])[i]);
+ }
+ printk("\n");
+#endif
+ if (done)
+ { /* The Adaptec Spec says the card is so fast that the loops will
+ only be executed once in the code below. Even if this was true
+ with the fastest processors when the spec was written, it doesn't
+ seem to be true with todays fast processors. We print a warning
+ if the code is executed more often than LOOPCNT_WARN. If this
+ happens, it should be investigated. If the count reaches
+ LOOPCNT_MAX, we assume something is broken; since there is no
+ way to return an error (the return value is ignored by the
+ mid-level scsi layer) we have to panic (and maybe that's the
+ best thing we can do then anyhow). */
+
+#define LOOPCNT_WARN 10 /* excessive mbxout wait -> syslog-msg */
+#define LOOPCNT_MAX 1000000 /* mbxout deadlock -> panic() after ~ 2 sec. */
+ int loopcnt;
+ unsigned int base = SCpnt->host->io_port;
+ DEB(printk("aha1740[%d] critical section\n",ecbno));
+ save_flags(flags);
+ cli();
+ for (loopcnt = 0; ; loopcnt++) {
+ if (inb(G2STAT(base)) & G2STAT_MBXOUT) break;
+ if (loopcnt == LOOPCNT_WARN) {
+ printk("aha1740[%d]_mbxout wait!\n",ecbno);
+ cli(); /* printk may have done a sti()! */
+ }
+ if (loopcnt == LOOPCNT_MAX)
+ panic("aha1740.c: mbxout busy!\n");
+ }
+ outl(virt_to_bus(host->ecb + ecbno), MBOXOUT0(base));
+ for (loopcnt = 0; ; loopcnt++) {
+ if (! (inb(G2STAT(base)) & G2STAT_BUSY)) break;
+ if (loopcnt == LOOPCNT_WARN) {
+ printk("aha1740[%d]_attn wait!\n",ecbno);
+ cli();
+ }
+ if (loopcnt == LOOPCNT_MAX)
+ panic("aha1740.c: attn wait failed!\n");
+ }
+ outb(ATTN_START | (target & 7), ATTN(base)); /* Start it up */
+ restore_flags(flags);
+ DEB(printk("aha1740[%d] request queued.\n",ecbno));
+ }
+ else
+ printk(KERN_ALERT "aha1740_queuecommand: done can't be NULL\n");
+ return 0;
+}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+ SCpnt->SCp.Status++;
+}
+
+int aha1740_command(Scsi_Cmnd * SCpnt)
+{
+ aha1740_queuecommand(SCpnt, internal_done);
+ SCpnt->SCp.Status = 0;
+ while (!SCpnt->SCp.Status)
+ barrier();
+ return SCpnt->result;
+}
+
+/* Query the board for its irq_level. Nothing else matters
+ in enhanced mode on an EISA bus. */
+
+void aha1740_getconfig(unsigned int base, unsigned int *irq_level,
+ unsigned int *translation)
+{
+ static int intab[] = { 9, 10, 11, 12, 0, 14, 15, 0 };
+
+ *irq_level = intab[inb(INTDEF(base)) & 0x7];
+ *translation = inb(RESV1(base)) & 0x1;
+ outb(inb(INTDEF(base)) | 0x10, INTDEF(base));
+}
+
+int aha1740_detect(Scsi_Host_Template * tpnt)
+{
+ int count = 0, slot;
+
+ DEB(printk("aha1740_detect: \n"));
+
+ for ( slot=MINEISA; slot <= MAXEISA; slot++ )
+ {
+ int slotbase;
+ unsigned int irq_level, translation;
+ struct Scsi_Host *shpnt;
+ struct aha1740_hostdata *host;
+ slotbase = SLOTBASE(slot);
+ /*
+ * The ioports for eisa boards are generally beyond that used in the
+ * check/allocate region code, but this may change at some point,
+ * so we go through the motions.
+ */
+ if (check_region(slotbase, SLOTSIZE)) /* See if in use */
+ continue;
+ if (!aha1740_test_port(slotbase))
+ continue;
+ aha1740_getconfig(slotbase,&irq_level,&translation);
+ if ((inb(G2STAT(slotbase)) &
+ (G2STAT_MBXOUT|G2STAT_BUSY)) != G2STAT_MBXOUT)
+ { /* If the card isn't ready, hard reset it */
+ outb(G2CNTRL_HRST, G2CNTRL(slotbase));
+ outb(0, G2CNTRL(slotbase));
+ }
+ printk("Configuring aha174x at IO:%x, IRQ %d\n", slotbase, irq_level);
+ printk("aha174x: Extended translation %sabled.\n",
+ translation ? "en" : "dis");
+ DEB(printk("aha1740_detect: enable interrupt channel %d\n",irq_level));
+ if (request_irq(irq_level,aha1740_intr_handle,0,"aha1740",NULL)) {
+ printk("Unable to allocate IRQ for adaptec controller.\n");
+ continue;
+ }
+ shpnt = scsi_register(tpnt, sizeof(struct aha1740_hostdata));
+ request_region(slotbase, SLOTSIZE, "aha1740");
+ shpnt->base = 0;
+ shpnt->io_port = slotbase;
+ shpnt->n_io_port = SLOTSIZE;
+ shpnt->irq = irq_level;
+ shpnt->dma_channel = 0xff;
+ host = HOSTDATA(shpnt);
+ host->slot = slot;
+ host->translation = translation;
+ aha_host[irq_level - 9] = shpnt;
+ count++;
+ }
+ return count;
+}
+
+/* Note: They following two functions do not apply very well to the Adaptec,
+ which basically manages its own affairs quite well without our interference,
+ so I haven't put anything into them. I can faintly imagine someone with a
+ *very* badly behaved SCSI target (perhaps an old tape?) wanting the abort(),
+ but it hasn't happened yet, and doing aborts brings the Adaptec to its
+ knees. I cannot (at this moment in time) think of any reason to reset the
+ card once it's running. So there. */
+
+int aha1740_abort(Scsi_Cmnd * SCpnt)
+{
+ DEB(printk("aha1740_abort called\n"));
+ return SCSI_ABORT_SNOOZE;
+}
+
+/* We do not implement a reset function here, but the upper level code assumes
+ that it will get some kind of response for the command in SCpnt. We must
+ oblige, or the command will hang the scsi system */
+
+int aha1740_reset(Scsi_Cmnd * SCpnt, unsigned int ignored)
+{
+ DEB(printk("aha1740_reset called\n"));
+ return SCSI_RESET_PUNT;
+}
+
+int aha1740_biosparam(Disk * disk, kdev_t dev, int* ip)
+{
+ int size = disk->capacity;
+ int extended = HOSTDATA(disk->device->host)->translation;
+
+ DEB(printk("aha1740_biosparam\n"));
+ if (extended && (ip[2] > 1024))
+ {
+ ip[0] = 255;
+ ip[1] = 63;
+ ip[2] = size / (255 * 63);
+ }
+ else
+ {
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ }
+ return 0;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = AHA1740;
+
+#include "scsi_module.c"
+#endif
+
+/* Okay, you made it all the way through. As of this writing, 3/31/93, I'm
+brad@saturn.gaylord.com or brad@bradpc.gaylord.com. I'll try to help as time
+permits if you have any trouble with this driver. Happy Linuxing! */
diff --git a/linux/src/drivers/scsi/aha1740.h b/linux/src/drivers/scsi/aha1740.h
new file mode 100644
index 00000000..478e59ad
--- /dev/null
+++ b/linux/src/drivers/scsi/aha1740.h
@@ -0,0 +1,196 @@
+#ifndef _AHA1740_H
+
+/* $Id: aha1740.h,v 1.1 1999/04/26 05:54:13 tb Exp $
+ *
+ * Header file for the adaptec 1740 driver for Linux
+ *
+ * With minor revisions 3/31/93
+ * Written and (C) 1992,1993 Brad McLean. See aha1740.c
+ * for more info
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+/* Eisa Enhanced mode operation - slot locating and addressing */
+#define MINEISA 1 /* I don't have an EISA Spec to know these ranges, so I */
+#define MAXEISA 8 /* Just took my machine's specifications. Adjust to fit.*/
+ /* I just saw an ad, and bumped this from 6 to 8 */
+#define SLOTBASE(x) ((x << 12) + 0xc80)
+#define SLOTSIZE 0x5c
+
+/* EISA configuration registers & values */
+#define HID0(base) (base + 0x0)
+#define HID1(base) (base + 0x1)
+#define HID2(base) (base + 0x2)
+#define HID3(base) (base + 0x3)
+#define EBCNTRL(base) (base + 0x4)
+#define PORTADR(base) (base + 0x40)
+#define BIOSADR(base) (base + 0x41)
+#define INTDEF(base) (base + 0x42)
+#define SCSIDEF(base) (base + 0x43)
+#define BUSDEF(base) (base + 0x44)
+#define RESV0(base) (base + 0x45)
+#define RESV1(base) (base + 0x46)
+#define RESV2(base) (base + 0x47)
+
+#define HID_MFG "ADP"
+#define HID_PRD 0
+#define HID_REV 2
+#define EBCNTRL_VALUE 1
+#define PORTADDR_ENH 0x80
+/* READ */
+#define G2INTST(base) (base + 0x56)
+#define G2STAT(base) (base + 0x57)
+#define MBOXIN0(base) (base + 0x58)
+#define MBOXIN1(base) (base + 0x59)
+#define MBOXIN2(base) (base + 0x5a)
+#define MBOXIN3(base) (base + 0x5b)
+#define G2STAT2(base) (base + 0x5c)
+
+#define G2INTST_MASK 0xf0 /* isolate the status */
+#define G2INTST_CCBGOOD 0x10 /* CCB Completed */
+#define G2INTST_CCBRETRY 0x50 /* CCB Completed with a retry */
+#define G2INTST_HARDFAIL 0x70 /* Adapter Hardware Failure */
+#define G2INTST_CMDGOOD 0xa0 /* Immediate command success */
+#define G2INTST_CCBERROR 0xc0 /* CCB Completed with error */
+#define G2INTST_ASNEVENT 0xd0 /* Asynchronous Event Notification */
+#define G2INTST_CMDERROR 0xe0 /* Immediate command error */
+
+#define G2STAT_MBXOUT 4 /* Mailbox Out Empty Bit */
+#define G2STAT_INTPEND 2 /* Interrupt Pending Bit */
+#define G2STAT_BUSY 1 /* Busy Bit (attention pending) */
+
+#define G2STAT2_READY 0 /* Host Ready Bit */
+
+/* WRITE (and ReadBack) */
+#define MBOXOUT0(base) (base + 0x50)
+#define MBOXOUT1(base) (base + 0x51)
+#define MBOXOUT2(base) (base + 0x52)
+#define MBOXOUT3(base) (base + 0x53)
+#define ATTN(base) (base + 0x54)
+#define G2CNTRL(base) (base + 0x55)
+
+#define ATTN_IMMED 0x10 /* Immediate Command */
+#define ATTN_START 0x40 /* Start CCB */
+#define ATTN_ABORT 0x50 /* Abort CCB */
+
+#define G2CNTRL_HRST 0x80 /* Hard Reset */
+#define G2CNTRL_IRST 0x40 /* Clear EISA Interrupt */
+#define G2CNTRL_HRDY 0x20 /* Sets HOST ready */
+
+/* This is used with scatter-gather */
+struct aha1740_chain {
+ u32 dataptr; /* Location of data */
+ u32 datalen; /* Size of this part of chain */
+};
+
+/* These belong in scsi.h */
+#define any2scsi(up, p) \
+(up)[0] = (((unsigned long)(p)) >> 16) ; \
+(up)[1] = (((unsigned long)(p)) >> 8); \
+(up)[2] = ((unsigned long)(p));
+
+#define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) )
+
+#define xany2scsi(up, p) \
+(up)[0] = ((long)(p)) >> 24; \
+(up)[1] = ((long)(p)) >> 16; \
+(up)[2] = ((long)(p)) >> 8; \
+(up)[3] = ((long)(p));
+
+#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \
+ + (((long)(up)[2]) << 8) + ((long)(up)[3]) )
+
+#define MAX_CDB 12
+#define MAX_SENSE 14
+#define MAX_STATUS 32
+
+struct ecb { /* Enhanced Control Block 6.1 */
+ u16 cmdw; /* Command Word */
+ /* Flag Word 1 */
+ u16 cne:1, /* Control Block Chaining */
+ :6, di:1, /* Disable Interrupt */
+ :2, ses:1, /* Suppress Underrun error */
+ :1, sg:1, /* Scatter/Gather */
+ :1, dsb:1, /* Disable Status Block */
+ ars:1; /* Automatic Request Sense */
+ /* Flag Word 2 */
+ u16 lun:3, /* Logical Unit */
+ tag:1, /* Tagged Queuing */
+ tt:2, /* Tag Type */
+ nd:1, /* No Disconnect */
+ :1, dat:1, /* Data transfer - check direction */
+ dir:1, /* Direction of transfer 1 = datain */
+ st:1, /* Suppress Transfer */
+ chk:1, /* Calculate Checksum */
+ :2, rec:1, :1; /* Error Recovery */
+ u16 nil0; /* nothing */
+ u32 dataptr; /* Data or Scatter List ptr */
+ u32 datalen; /* Data or Scatter List len */
+ u32 statusptr; /* Status Block ptr */
+ u32 linkptr; /* Chain Address */
+ u32 nil1; /* nothing */
+ u32 senseptr; /* Sense Info Pointer */
+ u8 senselen; /* Sense Length */
+ u8 cdblen; /* CDB Length */
+ u16 datacheck; /* Data checksum */
+ u8 cdb[MAX_CDB]; /* CDB area */
+/* Hardware defined portion ends here, rest is driver defined */
+ u8 sense[MAX_SENSE]; /* Sense area */
+ u8 status[MAX_STATUS]; /* Status area */
+ Scsi_Cmnd *SCpnt; /* Link to the SCSI Command Block */
+ void (*done)(Scsi_Cmnd *); /* Completion Function */
+};
+
+#define AHA1740CMD_NOP 0x00 /* No OP */
+#define AHA1740CMD_INIT 0x01 /* Initiator SCSI Command */
+#define AHA1740CMD_DIAG 0x05 /* Run Diagnostic Command */
+#define AHA1740CMD_SCSI 0x06 /* Initialize SCSI */
+#define AHA1740CMD_SENSE 0x08 /* Read Sense Information */
+#define AHA1740CMD_DOWN 0x09 /* Download Firmware (yeah, I bet!) */
+#define AHA1740CMD_RINQ 0x0a /* Read Host Adapter Inquiry Data */
+#define AHA1740CMD_TARG 0x10 /* Target SCSI Command */
+
+int aha1740_detect(Scsi_Host_Template *);
+int aha1740_command(Scsi_Cmnd *);
+int aha1740_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int aha1740_abort(Scsi_Cmnd *);
+int aha1740_reset(Scsi_Cmnd *, unsigned int);
+int aha1740_biosparam(Disk *, kdev_t, int*);
+int aha1740_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout);
+
+#define AHA1740_ECBS 32
+#define AHA1740_SCATTER 16
+#define AHA1740_CMDLUN 1
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+extern struct proc_dir_entry proc_scsi_aha1740;
+
+#define AHA1740 {NULL, NULL, \
+ &proc_scsi_aha1740, \
+ aha1740_proc_info, \
+ "Adaptec 174x (EISA)", \
+ aha1740_detect, \
+ NULL, \
+ NULL, \
+ aha1740_command, \
+ aha1740_queuecommand, \
+ aha1740_abort, \
+ aha1740_reset, \
+ NULL, \
+ aha1740_biosparam, \
+ AHA1740_ECBS, \
+ 7, \
+ AHA1740_SCATTER, \
+ AHA1740_CMDLUN, \
+ 0, \
+ 0, \
+ ENABLE_CLUSTERING}
+
+#endif
diff --git a/linux/src/drivers/scsi/aic7xxx.c b/linux/src/drivers/scsi/aic7xxx.c
new file mode 100644
index 00000000..5cec782d
--- /dev/null
+++ b/linux/src/drivers/scsi/aic7xxx.c
@@ -0,0 +1,11300 @@
+/*+M*************************************************************************
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * Copyright (c) 1994 John Aycock
+ * The University of Calgary Department of Computer Science.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Sources include the Adaptec 1740 driver (aha1740.c), the Ultrastor 24F
+ * driver (ultrastor.c), various Linux kernel source, the Adaptec EISA
+ * config file (!adp7771.cfg), the Adaptec AHA-2740A Series User's Guide,
+ * the Linux Kernel Hacker's Guide, Writing a SCSI Device Driver for Linux,
+ * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA overlay file
+ * (adp7770.ovl), the Adaptec AHA-2740 Series Technical Reference Manual,
+ * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
+ * ANSI SCSI-2 specification (draft 10c), ...
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
+ *
+ * Substantially modified to include support for wide and twin bus
+ * adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ * SCB paging, and other rework of the code.
+ *
+ * Parts of this driver were also based on the FreeBSD driver by
+ * Justin T. Gibbs. His copyright follows:
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU Public License ("GPL") and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aic7xxx.c,v 1.1 1999/04/26 05:54:15 tb Exp $
+ *---------------------------------------------------------------------------
+ *
+ * Thanks also go to (in alphabetical order) the following:
+ *
+ * Rory Bolt - Sequencer bug fixes
+ * Jay Estabrook - Initial DEC Alpha support
+ * Doug Ledford - Much needed abort/reset bug fixes
+ * Kai Makisara - DMAing of SCBs
+ *
+ * A Boot time option was also added for not resetting the scsi bus.
+ *
+ * Form: aic7xxx=extended
+ * aic7xxx=no_reset
+ * aic7xxx=ultra
+ * aic7xxx=irq_trigger:[0,1] # 0 edge, 1 level
+ * aic7xxx=verbose
+ *
+ * Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
+ *
+ * $Id: aic7xxx.c,v 1.1 1999/04/26 05:54:15 tb Exp $
+ *-M*************************************************************************/
+
+/*+M**************************************************************************
+ *
+ * Further driver modifications made by Doug Ledford <dledford@redhat.com>
+ *
+ * Copyright (c) 1997-1998 Doug Ledford
+ *
+ * These changes are released under the same licensing terms as the FreeBSD
+ * driver written by Justin Gibbs. Please see his Copyright notice above
+ * for the exact terms and conditions covering my changes as well as the
+ * warranty statement.
+ *
+ * Modifications made to the aic7xxx.c,v 4.1 driver from Dan Eischen include
+ * but are not limited to:
+ *
+ * 1: Import of the latest FreeBSD sequencer code for this driver
+ * 2: Modification of kernel code to accomodate different sequencer semantics
+ * 3: Extensive changes throughout kernel portion of driver to improve
+ * abort/reset processing and error hanndling
+ * 4: Other work contributed by various people on the Internet
+ * 5: Changes to printk information and verbosity selection code
+ * 6: General reliability related changes, especially in IRQ management
+ * 7: Modifications to the default probe/attach order for supported cards
+ * 8: SMP friendliness has been improved
+ *
+ * Overall, this driver represents a significant departure from the official
+ * aic7xxx driver released by Dan Eischen in two ways. First, in the code
+ * itself. A diff between the two version of the driver is now a several
+ * thousand line diff. Second, in approach to solving the same problem. The
+ * problem is importing the FreeBSD aic7xxx driver code to linux can be a
+ * difficult and time consuming process, that also can be error prone. Dan
+ * Eischen's official driver uses the approach that the linux and FreeBSD
+ * drivers should be as identical as possible. To that end, his next version
+ * of this driver will be using a mid-layer code library that he is developing
+ * to moderate communications between the linux mid-level SCSI code and the
+ * low level FreeBSD driver. He intends to be able to essentially drop the
+ * FreeBSD driver into the linux kernel with only a few minor tweaks to some
+ * include files and the like and get things working, making for fast easy
+ * imports of the FreeBSD code into linux.
+ *
+ * I disagree with Dan's approach. Not that I don't think his way of doing
+ * things would be nice, easy to maintain, and create a more uniform driver
+ * between FreeBSD and Linux. I have no objection to those issues. My
+ * disagreement is on the needed functionality. There simply are certain
+ * things that are done differently in FreeBSD than linux that will cause
+ * problems for this driver regardless of any middle ware Dan implements.
+ * The biggest example of this at the moment is interrupt semantics. Linux
+ * doesn't provide the same protection techniques as FreeBSD does, nor can
+ * they be easily implemented in any middle ware code since they would truly
+ * belong in the kernel proper and would effect all drivers. For the time
+ * being, I see issues such as these as major stumbling blocks to the
+ * reliability of code based upon such middle ware. Therefore, I choose to
+ * use a different approach to importing the FreeBSD code that doesn't
+ * involve any middle ware type code. My approach is to import the sequencer
+ * code from FreeBSD wholesale. Then, to only make changes in the kernel
+ * portion of the driver as they are needed for the new sequencer semantics.
+ * In this way, the portion of the driver that speaks to the rest of the
+ * linux kernel is fairly static and can be changed/modified to solve
+ * any problems one might encounter without concern for the FreeBSD driver.
+ *
+ * Note: If time and experience should prove me wrong that the middle ware
+ * code Dan writes is reliable in its operation, then I'll retract my above
+ * statements. But, for those that don't know, I'm from Missouri (in the US)
+ * and our state motto is "The Show-Me State". Well, before I will put
+ * faith into it, you'll have to show me that it works :)
+ *
+ *_M*************************************************************************/
+
+/*
+ * The next three defines are user configurable. These should be the only
+ * defines a user might need to get in here and change. There are other
+ * defines buried deeper in the code, but those really shouldn't need touched
+ * under normal conditions.
+ */
+
+/*
+ * AIC7XXX_FAKE_NEGOTIATION_CMDS
+ * We now have two distinctly different methods of device negotiation
+ * in this code. The two methods are selected by either defining or not
+ * defining this option. The difference is as follows:
+ *
+ * With AIC7XXX_FAKE_NEGOTIATION_CMDS not set (commented out)
+ * When the driver is in need of issuing a negotiation command for any
+ * given device, it will add the negotiation message on to part of a
+ * regular SCSI command for the device. In the process, if the device
+ * is configured for and using tagged queueing, then the code will
+ * also issue that single command as a non-tagged command, attach the
+ * negotiation message to that one command, and use a temporary
+ * queue depth of one to keep the untagged and tagged commands from
+ * overlapping.
+ * Pros: This doesn't use any extra SCB structures, it's simple, it
+ * works most of the time (if not all of the time now), and
+ * since we get the device capability info frmo the INQUIRY data
+ * now, shouldn't cause any problems.
+ * Cons: When we need to send a negotiation command to a device, we
+ * must use a command that is being sent to LUN 0 of the device.
+ * If we try sending one to high LUN numbers, then some devices
+ * get noticeably upset. Since we have to wait for a command with
+ * LUN == 0 to come along, we may not be able to renegotiate when
+ * we want if the user is actually using say LUN 1 of a CD Changer
+ * instead of using LUN 0 for an extended period of time.
+ *
+ * With AIC7XXX_FAKE_NEGOTIATION_CMDS defined
+ * When we need to negotiate with a device, instead of attaching our
+ * negotiation message to an existing command, we insert our own
+ * fictional Scsi_Cmnd into the chain that has the negotiation message
+ * attached to it. We send this one command as untagged regardless
+ * of the device type, and we fiddle with the queue depth the same as
+ * we would with the option unset to avoid overlapping commands. The
+ * primary difference between this and the unset option is that the
+ * negotiation message is no longer attached to a specific command,
+ * instead it is its own command and is merely triggered by a
+ * combination of both A) We need to negotiate and B) The mid level
+ * SCSI code has sent us a command. We still don't do any negotiation
+ * unless there is a valid SCSI command to be processed.
+ * Pros: This fixes the problem above in the Cons section. Since we
+ * issue our own fake command, we can set the LUN to 0 regardless
+ * of what the LUN is in the real command. It also means that if
+ * the device get's nasty over negotiation issues, it won't be
+ * showing up on a regular command, so we won't get any SENSE buffer
+ * data or STATUS_BYTE returns to the mid level code that are caused
+ * by snits in the negotiation code.
+ * Cons: We add more code, and more complexity. This means more ways
+ * in which things could break. It means a larger driver. It means
+ * more resource consumption for the fake commands. However, the
+ * biggest problem is this. Take a system where there is a CD-ROM
+ * on the SCSI bus. Someone has a CD in the CD-ROM and is using it.
+ * For some reason the SCSI bus gets reset. We don't touch the
+ * CD-ROM again for quite a period of time (so we don't renegotiate
+ * after the reset until we do touch the CD-ROM again). In the
+ * time while we aren't using the CD-ROM, the current disc is
+ * removed and a new one put in. When we go to check that disc, we
+ * will first have to renegotiate. In so doing, we issue our fake
+ * SCSI command, which happens to be TEST_UNIT_READY. The CD-ROM
+ * negotiates with us, then responds to our fake command with a
+ * CHECK_CONDITION status. We REQUEST_SENSE from the CD-ROM, it
+ * then sends the SENSE data to our fake command to tell it that
+ * it has been through a disc change. There, now we've cleared out
+ * the SENSE data along with our negotiation command, and when the
+ * real command executes, it won't pick up that the CD was changed.
+ * That's the biggest Con to this approach. In the future, I could
+ * probably code around this problem though, so this option is still
+ * viable.
+ *
+ * So, which command style should you use? I would appreciate it if people
+ * could try out both types. I want to know about any cases where one
+ * method works and the other doesn't. If one method works on significantly
+ * more systems than another, then it will become the default. If the second
+ * option turns out to work best, then I'll find a way to work around that
+ * big con I listed.
+ *
+ * -- July 7, 02:33
+ * OK...I just added some code that should make the Con listed for the
+ * fake commands a non issue now. However, it needs testing. For now,
+ * I'm going to make the default to use the fake commands, we'll see how
+ * it goes.
+ */
+
+#define AIC7XXX_FAKE_NEGOTIATION_CMDS
+
+/*
+ * AIC7XXX_STRICT_PCI_SETUP
+ * Should we assume the PCI config options on our controllers are set with
+ * sane and proper values, or should we be anal about our PCI config
+ * registers and force them to what we want? The main advantage to
+ * defining this option is on non-Intel hardware where the BIOS may not
+ * have been run to set things up, or if you have one of the BIOSless
+ * Adaptec controllers, such as a 2910, that don't get set up by the
+ * BIOS. However, keep in mind that we really do set the most important
+ * items in the driver regardless of this setting, this only controls some
+ * of the more esoteric PCI options on these cards. In that sense, I
+ * would default to leaving this off. However, if people wish to try
+ * things both ways, that would also help me to know if there are some
+ * machines where it works one way but not another.
+ *
+ * -- July 7, 17:09
+ * OK...I need this on my machine for testing, so the default is to
+ * leave it defined.
+ *
+ * -- July 7, 18:49
+ * I needed it for testing, but it didn't make any difference, so back
+ * off she goes.
+ *
+ * -- July 16, 23:04
+ * I turned it back on to try and compensate for the 2.1.x PCI code
+ * which no longer relies solely on the BIOS and now tries to set
+ * things itself.
+ */
+
+#define AIC7XXX_STRICT_PCI_SETUP
+
+/*
+ * AIC7XXX_VERBOSE_DEBUGGING
+ * This option enables a lot of extra printk();s in the code, surrounded
+ * by if (aic7xxx_verbose ...) statements. Executing all of those if
+ * statements and the extra checks can get to where it actually does have
+ * an impact on CPU usage and such, as well as code size. Disabling this
+ * define will keep some of those from becoming part of the code.
+ *
+ * NOTE: Currently, this option has no real effect, I will be adding the
+ * various #ifdef's in the code later when I've decided a section is
+ * complete and no longer needs debugging. OK...a lot of things are now
+ * surrounded by this define, so turning this off does have an impact.
+ */
+
+/*
+ * #define AIC7XXX_VERBOSE_DEBUGGING
+ */
+
+#if defined(MODULE) || defined(PCMCIA)
+#include <linux/module.h>
+#endif
+
+#if defined(PCMCIA)
+# undef MODULE
+#endif
+
+#include <stdarg.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/blk.h>
+#include <linux/tqueue.h>
+#include <linux/tasks.h>
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "aic7xxx.h"
+
+#include "aic7xxx/sequencer.h"
+#include "aic7xxx/scsi_message.h"
+#include "aic7xxx_reg.h"
+
+#include <linux/stat.h>
+#include <linux/malloc.h> /* for kmalloc() */
+
+#include <linux/config.h> /* for CONFIG_PCI */
+
+/*
+ * To generate the correct addresses for the controller to issue
+ * on the bus. Originally added for DEC Alpha support.
+ */
+#define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a))
+
+struct proc_dir_entry proc_scsi_aic7xxx = {
+ PROC_SCSI_AIC7XXX, 7, "aic7xxx",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2,
+ 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+#define AIC7XXX_C_VERSION "5.1.4"
+
+#define NUMBER(arr) (sizeof(arr) / sizeof(arr[0]))
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define ALL_TARGETS -1
+#define ALL_CHANNELS -1
+#define ALL_LUNS -1
+#define MAX_TARGETS 16
+#define MAX_LUNS 8
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#ifndef KERNEL_VERSION
+# define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
+#endif
+
+/*
+ * We need the bios32.h file if we are kernel version 2.1.92 or less. The
+ * full set of pci_* changes wasn't in place until 2.1.93
+ */
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,1,92)
+# if defined(__sparc_v9__) || defined(__powerpc__)
+# error "PPC and Sparc platforms are only support under 2.1.92 and above"
+# endif
+# include <linux/bios32.h>
+#endif
+
+#if defined(__powerpc__)
+# define MMAPIO
+# ifdef mb
+# undef mb
+# endif
+# define mb() \
+ __asm__ __volatile__("eieio" ::: "memory")
+#elif defined(__i386__)
+# define MMAPIO
+# ifdef mb
+# undef mb
+# endif
+# define mb() \
+ __asm__ __volatile__("lock ; addl $0,0(%%esp)": : :"memory")
+#elif defined(__alpha__)
+# ifdef mb
+# undef mb
+# endif
+# define mb() \
+ __asm__ __volatile__("mb": : :"memory")
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
+# include <asm/spinlock.h>
+# include <linux/smp.h>
+# define cpuid smp_processor_id()
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+# define DRIVER_LOCK_INIT \
+ spin_lock_init(&p->spin_lock);
+# define DRIVER_LOCK \
+ if(!p->cpu_lock_count[cpuid]) { \
+ spin_lock_irqsave(&p->spin_lock, cpu_flags); \
+ p->cpu_lock_count[cpuid]++; \
+ } else { \
+ p->cpu_lock_count[cpuid]++; \
+ }
+# define DRIVER_UNLOCK \
+ if(--p->cpu_lock_count[cpuid] == 0) \
+ spin_unlock_irqrestore(&p->spin_lock, cpu_flags);
+# else
+# define DRIVER_LOCK_INIT
+# define DRIVER_LOCK
+# define DRIVER_UNLOCK
+# endif
+#else
+# define cpuid 0
+# define DRIVER_LOCK_INIT
+# define DRIVER_LOCK \
+ save_flags(cpu_flags); \
+ cli();
+# define DRIVER_UNLOCK \
+ restore_flags(cpu_flags);
+# define le32_to_cpu(x) (x)
+# define cpu_to_le32(x) (x)
+#endif
+
+/*
+ * You can try raising me if tagged queueing is enabled, or lowering
+ * me if you only have 4 SCBs.
+ */
+#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
+#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
+#endif
+
+/* Set this to the delay in seconds after SCSI bus reset. */
+#ifdef CONFIG_AIC7XXX_RESET_DELAY
+#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY
+#else
+#define AIC7XXX_RESET_DELAY 5
+#endif
+
+/*
+ * Control collection of SCSI transfer statistics for the /proc filesystem.
+ *
+ * NOTE: Do NOT enable this when running on kernels version 1.2.x and below.
+ * NOTE: This does affect performance since it has to maintain statistics.
+ */
+#ifdef CONFIG_AIC7XXX_PROC_STATS
+#define AIC7XXX_PROC_STATS
+#endif
+
+/*
+ * NOTE: Uncommenting the define below no longer has any effect, the
+ * tagged queue value array is always active now. I've added
+ * a setup option to set this particular array and I'm hoping
+ * insmod will be smart enough to set it properly as well. It's
+ * by use of this array that a person can enable tagged queueing.
+ * The DEFAULT_TAG_COMMANDS define has been changed to disable
+ * tagged queueing by default, so if your devices can handle tagged
+ * queueing you will need to add a line to their lilo.conf file like:
+ * append="aic7xxx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ *
+ * Set this for defining the number of tagged commands on a device
+ * by device, and controller by controller basis. The first set
+ * of tagged commands will be used for the first detected aic7xxx
+ * controller, the second set will be used for the second detected
+ * aic7xxx controller, and so on. These values will *only* be used
+ * for targets that are tagged queueing capable; these values will
+ * be ignored in all other cases. The tag_commands is an array of
+ * 16 to allow for wide and twin adapters. Twin adapters will use
+ * indexes 0-7 for channel 0, and indexes 8-15 for channel 1.
+ *
+ * *** Determining commands per LUN ***
+ *
+ * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its
+ * own algorithm to determine the commands/LUN. If SCB paging is
+ * enabled, which is always now, the default is 8 commands per lun
+ * that indicates it supports tagged queueing. All non-tagged devices
+ * use an internal queue depth of 3, with no more than one of those
+ * three commands active at one time.
+ */
+/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */
+
+typedef struct
+{
+ unsigned char tag_commands[16]; /* Allow for wide/twin adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Make a define that will tell the driver not to use tagged queueing
+ * by default.
+ */
+#define DEFAULT_TAG_COMMANDS {255, 255, 255, 255, 255, 255, 255, 255,\
+ 255, 255, 255, 255, 255, 255, 255, 255}
+
+/*
+ * Modify this as you see fit for your system. By setting tag_commands
+ * to 0, the driver will use it's own algorithm for determining the
+ * number of commands to use (see above). When 255, the driver will
+ * not enable tagged queueing for that particular device. When positive
+ * (> 0) and (< 255) the values in the array are used for the queue_depth.
+ * Note that the maximum value for an entry is 254, but you're insane if
+ * you try to use that many commands on one device.
+ *
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to use its own algorithm for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3. It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+
+/*
+ * NOTE: The below structure is for reference only, the actual structure
+ * to modify in order to change things is located around line
+ * number 1305
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+ {DEFAULT_TAG_COMMANDS},
+ {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 255, 4, 4, 4}},
+ {DEFAULT_TAG_COMMANDS},
+ {{255, 16, 4, 255, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+*/
+
+/*
+ * Define an array of board names that can be indexed by aha_type.
+ * Don't forget to change this when changing the types!
+ */
+static const char *board_names[] = {
+ "AIC-7xxx Unknown", /* AIC_NONE */
+ "Adaptec AIC-7810 Hardware RAID Controller", /* AIC_7810 */
+ "Adaptec AIC-7770 SCSI host adapter", /* AIC_7770 */
+ "Adaptec AHA-274X SCSI host adapter", /* AIC_7771 */
+ "Adaptec AHA-284X SCSI host adapter", /* AIC_284x */
+ "Adaptec AIC-7850 SCSI host adapter", /* AIC_7850 */
+ "Adaptec AIC-7855 SCSI host adapter", /* AIC_7855 */
+ "Adaptec AIC-7860 Ultra SCSI host adapter", /* AIC_7860 */
+ "Adaptec AHA-2940A Ultra SCSI host adapter", /* AIC_7861 */
+ "Adaptec AIC-7870 SCSI host adapter", /* AIC_7870 */
+ "Adaptec AHA-294X SCSI host adapter", /* AIC_7871 */
+ "Adaptec AHA-394X SCSI host adapter", /* AIC_7872 */
+ "Adaptec AHA-398X SCSI host adapter", /* AIC_7873 */
+ "Adaptec AHA-2944 SCSI host adapter", /* AIC_7874 */
+ "Adaptec AIC-7880 Ultra SCSI host adapter", /* AIC_7880 */
+ "Adaptec AHA-294X Ultra SCSI host adapter", /* AIC_7881 */
+ "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */
+ "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */
+ "Adaptec AHA-2944 Ultra SCSI host adapter", /* AIC_7884 */
+ "Adaptec AIC-7895 Ultra SCSI host adapter", /* AIC_7895 */
+ "Adaptec AIC-7890/1 Ultra2 SCSI host adapter", /* AIC_7890 */
+ "Adaptec AHA-294X Ultra2 SCSI host adapter", /* AIC_7890 */
+ "Adaptec AIC-7896/7 Ultra2 SCSI host adapter", /* AIC_7896 */
+ "Adaptec AHA-394X Ultra2 SCSI host adapter" /* AIC_7897 */
+};
+
+/*
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW DID_ERROR
+
+/*
+ * What we want to do is have the higher level scsi driver requeue
+ * the command to us. There is no specific driver status for this
+ * condition, but the higher level scsi driver will requeue the
+ * command on a DID_BUS_BUSY error.
+ *
+ * Upon further inspection and testing, it seems that DID_BUS_BUSY
+ * will *always* retry the command. We can get into an infinite loop
+ * if this happens when we really want some sort of counter that
+ * will automatically abort/reset the command after so many retries.
+ * Using DID_ERROR will do just that. (Made by a suggestion by
+ * Doug Ledford 8/1/96)
+ */
+#define DID_RETRY_COMMAND DID_ERROR
+
+#define HSCSIID 0x07
+#define SCSI_RESET 0x040
+
+/*
+ * EISA/VL-bus stuff
+ */
+#define MINSLOT 1
+#define MAXSLOT 15
+#define SLOTBASE(x) ((x) << 12)
+#define BASE_TO_SLOT(x) ((x) >> 12)
+
+/*
+ * Standard EISA Host ID regs (Offset from slot base)
+ */
+#define AHC_HID0 0x80 /* 0,1: msb of ID2, 2-7: ID1 */
+#define AHC_HID1 0x81 /* 0-4: ID3, 5-7: LSB ID2 */
+#define AHC_HID2 0x82 /* product */
+#define AHC_HID3 0x83 /* firmware revision */
+
+/*
+ * AIC-7770 I/O range to reserve for a card
+ */
+#define MINREG 0xC00
+#define MAXREG 0xCBF
+
+#define INTDEF 0x5C /* Interrupt Definition Register */
+
+/*
+ * AIC-78X0 PCI registers
+ */
+#define CLASS_PROGIF_REVID 0x08
+#define DEVREVID 0x000000FFul
+#define PROGINFC 0x0000FF00ul
+#define SUBCLASS 0x00FF0000ul
+#define BASECLASS 0xFF000000ul
+
+#define CSIZE_LATTIME 0x0C
+#define CACHESIZE 0x0000003Ful /* only 5 bits */
+#define LATTIME 0x0000FF00ul
+
+#define DEVCONFIG 0x40
+#define SCBSIZE32 0x00010000ul /* aic789X only */
+#define MPORTMODE 0x00000400ul /* aic7870 only */
+#define RAMPSM 0x00000200ul /* aic7870 only */
+#define RAMPSM_ULTRA2 0x00000004
+#define VOLSENSE 0x00000100ul
+#define SCBRAMSEL 0x00000080ul
+#define SCBRAMSEL_ULTRA2 0x00000008
+#define MRDCEN 0x00000040ul
+#define EXTSCBTIME 0x00000020ul /* aic7870 only */
+#define EXTSCBPEN 0x00000010ul /* aic7870 only */
+#define BERREN 0x00000008ul
+#define DACEN 0x00000004ul
+#define STPWLEVEL 0x00000002ul
+#define DIFACTNEGEN 0x00000001ul /* aic7870 only */
+
+#define SCAMCTL 0x1a /* Ultra2 only */
+#define CCSCBBADDR 0xf0 /* aic7895/6/7 */
+
+/*
+ * Define the different types of SEEPROMs on aic7xxx adapters
+ * and make it also represent the address size used in accessing
+ * its registers. The 93C46 chips have 1024 bits organized into
+ * 64 16-bit words, while the 93C56 chips have 2048 bits organized
+ * into 128 16-bit words. The C46 chips use 6 bits to address
+ * each word, while the C56 and C66 (4096 bits) use 8 bits to
+ * address each word.
+ */
+typedef enum {C46 = 6, C56_66 = 8} seeprom_chip_type;
+
+/*
+ *
+ * Define the format of the SEEPROM registers (16 bits).
+ *
+ */
+struct seeprom_config {
+
+/*
+ * SCSI ID Configuration Flags
+ */
+#define CFXFER 0x0007 /* synchronous transfer rate */
+#define CFSYNCH 0x0008 /* enable synchronous transfer */
+#define CFDISC 0x0010 /* enable disconnection */
+#define CFWIDEB 0x0020 /* wide bus device (wide card) */
+#define CFSYNCHISULTRA 0x0040 /* CFSYNC is an ultra offset */
+#define CFNEWULTRAFORMAT 0x0080 /* Use the Ultra2 SEEPROM format */
+#define CFSTART 0x0100 /* send start unit SCSI command */
+#define CFINCBIOS 0x0200 /* include in BIOS scan */
+#define CFRNFOUND 0x0400 /* report even if not found */
+#define CFMULTILUN 0x0800 /* probe mult luns in BIOS scan */
+#define CFWBCACHEYES 0x4000 /* Enable W-Behind Cache on drive */
+#define CFWBCACHENC 0xc000 /* Don't change W-Behind Cache */
+/* UNUSED 0x3000 */
+ unsigned short device_flags[16]; /* words 0-15 */
+
+/*
+ * BIOS Control Bits
+ */
+#define CFSUPREM 0x0001 /* support all removable drives */
+#define CFSUPREMB 0x0002 /* support removable drives for boot only */
+#define CFBIOSEN 0x0004 /* BIOS enabled */
+/* UNUSED 0x0008 */
+#define CFSM2DRV 0x0010 /* support more than two drives */
+#define CF284XEXTEND 0x0020 /* extended translation (284x cards) */
+/* UNUSED 0x0040 */
+#define CFEXTEND 0x0080 /* extended translation enabled */
+/* UNUSED 0xFF00 */
+ unsigned short bios_control; /* word 16 */
+
+/*
+ * Host Adapter Control Bits
+ */
+#define CFAUTOTERM 0x0001 /* Perform Auto termination */
+#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable (Ultra cards) */
+#define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */
+#define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */
+#define CFSTERM 0x0004 /* SCSI low byte termination */
+#define CFWSTERM 0x0008 /* SCSI high byte termination (wide card) */
+#define CFSPARITY 0x0010 /* SCSI parity */
+#define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */
+#define CFRESETB 0x0040 /* reset SCSI bus at boot */
+#define CFBPRIMARY 0x0100 /* Channel B primary on 7895 chipsets */
+#define CFSEAUTOTERM 0x0400 /* aic7890 Perform SE Auto Term */
+#define CFLVDSTERM 0x0800 /* aic7890 LVD Termination */
+/* UNUSED 0xF280 */
+ unsigned short adapter_control; /* word 17 */
+
+/*
+ * Bus Release, Host Adapter ID
+ */
+#define CFSCSIID 0x000F /* host adapter SCSI ID */
+/* UNUSED 0x00F0 */
+#define CFBRTIME 0xFF00 /* bus release time */
+ unsigned short brtime_id; /* word 18 */
+
+/*
+ * Maximum targets
+ */
+#define CFMAXTARG 0x00FF /* maximum targets */
+/* UNUSED 0xFF00 */
+ unsigned short max_targets; /* word 19 */
+
+ unsigned short res_1[11]; /* words 20-30 */
+ unsigned short checksum; /* word 31 */
+};
+
+#define SELBUS_MASK 0x0a
+#define SELNARROW 0x00
+#define SELBUSB 0x08
+#define SINGLE_BUS 0x00
+
+#define SCB_TARGET(scb) \
+ (((scb)->hscb->target_channel_lun & TID) >> 4)
+#define SCB_LUN(scb) \
+ ((scb)->hscb->target_channel_lun & LID)
+#define SCB_IS_SCSIBUS_B(scb) \
+ (((scb)->hscb->target_channel_lun & SELBUSB) != 0)
+
+/*
+ * If an error occurs during a data transfer phase, run the command
+ * to completion - it's easier that way - making a note of the error
+ * condition in this location. This then will modify a DID_OK status
+ * into an appropriate error for the higher-level SCSI code.
+ */
+#define aic7xxx_error(cmd) ((cmd)->SCp.Status)
+
+/*
+ * Keep track of the targets returned status.
+ */
+#define aic7xxx_status(cmd) ((cmd)->SCp.sent_command)
+
+/*
+ * The position of the SCSI commands scb within the scb array.
+ */
+#define aic7xxx_position(cmd) ((cmd)->SCp.have_data_in)
+
+/*
+ * So we can keep track of our host structs
+ */
+static struct aic7xxx_host *first_aic7xxx = NULL;
+
+/*
+ * As of Linux 2.1, the mid-level SCSI code uses virtual addresses
+ * in the scatter-gather lists. We need to convert the virtual
+ * addresses to physical addresses.
+ */
+struct hw_scatterlist {
+ unsigned int address;
+ unsigned int length;
+};
+
+/*
+ * Maximum number of SG segments these cards can support.
+ */
+#define AIC7XXX_MAX_SG 128
+
+/*
+ * The maximum number of SCBs we could have for ANY type
+ * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
+ * SEQUENCER CODE IF THIS IS MODIFIED!
+ */
+#define AIC7XXX_MAXSCB 255
+
+
+struct aic7xxx_hwscb {
+/* ------------ Begin hardware supported fields ---------------- */
+/* 0*/ unsigned char control;
+/* 1*/ unsigned char target_channel_lun; /* 4/1/3 bits */
+/* 2*/ unsigned char target_status;
+/* 3*/ unsigned char SG_segment_count;
+/* 4*/ unsigned int SG_list_pointer;
+/* 8*/ unsigned char residual_SG_segment_count;
+/* 9*/ unsigned char residual_data_count[3];
+/*12*/ unsigned int data_pointer;
+/*16*/ unsigned int data_count;
+/*20*/ unsigned int SCSI_cmd_pointer;
+/*24*/ unsigned char SCSI_cmd_length;
+/*25*/ unsigned char tag; /* Index into our kernel SCB array.
+ * Also used as the tag for tagged I/O
+ */
+#define SCB_PIO_TRANSFER_SIZE 26 /* amount we need to upload/download
+ * via PIO to initialize a transaction.
+ */
+/*26*/ unsigned char next; /* Used to thread SCBs awaiting selection
+ * or disconnected down in the sequencer.
+ */
+/*27*/ unsigned char prev;
+/*28*/ unsigned int pad; /*
+ * Unused by the kernel, but we require
+ * the padding so that the array of
+ * hardware SCBs is alligned on 32 byte
+ * boundaries so the sequencer can index
+ */
+};
+
+typedef enum {
+ SCB_FREE = 0x0000,
+ SCB_WAITINGQ = 0x0002,
+ SCB_ACTIVE = 0x0004,
+ SCB_SENSE = 0x0008,
+ SCB_ABORT = 0x0010,
+ SCB_DEVICE_RESET = 0x0020,
+ SCB_RESET = 0x0040,
+ SCB_RECOVERY_SCB = 0x0080,
+ SCB_WAS_BUSY = 0x0100,
+ SCB_MSGOUT_SENT = 0x0200,
+ SCB_MSGOUT_SDTR = 0x0400,
+ SCB_MSGOUT_WDTR = 0x0800,
+ SCB_MSGOUT_BITS = SCB_MSGOUT_SENT |
+ SCB_MSGOUT_SDTR |
+ SCB_MSGOUT_WDTR,
+ SCB_QUEUED_ABORT = 0x1000,
+ SCB_QUEUED_FOR_DONE = 0x2000
+} scb_flag_type;
+
+typedef enum {
+ AHC_FNONE = 0x00000000,
+ AHC_PAGESCBS = 0x00000001,
+ AHC_CHANNEL_B_PRIMARY = 0x00000002,
+ AHC_USEDEFAULTS = 0x00000004,
+ AHC_INDIRECT_PAGING = 0x00000008,
+ AHC_CHNLB = 0x00000020,
+ AHC_CHNLC = 0x00000040,
+ AHC_EXTEND_TRANS_A = 0x00000100,
+ AHC_EXTEND_TRANS_B = 0x00000200,
+ AHC_TERM_ENB_A = 0x00000400,
+ AHC_TERM_ENB_SE_LOW = 0x00000400,
+ AHC_TERM_ENB_B = 0x00000800,
+ AHC_TERM_ENB_SE_HIGH = 0x00000800,
+ AHC_HANDLING_REQINITS = 0x00001000,
+ AHC_TARGETMODE = 0x00002000,
+ AHC_NEWEEPROM_FMT = 0x00004000,
+ /*
+ * Here ends the FreeBSD defined flags and here begins the linux defined
+ * flags. NOTE: I did not preserve the old flag name during this change
+ * specifically to force me to evaluate what flags were being used properly
+ * and what flags weren't. This way, I could clean up the flag usage on
+ * a use by use basis. Doug Ledford
+ */
+ AHC_A_SCANNED = 0x00100000,
+ AHC_B_SCANNED = 0x00200000,
+ AHC_MULTI_CHANNEL = 0x00400000,
+ AHC_BIOS_ENABLED = 0x00800000,
+ AHC_SEEPROM_FOUND = 0x01000000,
+ AHC_TERM_ENB_LVD = 0x02000000,
+ AHC_ABORT_PENDING = 0x04000000,
+ AHC_RESET_PENDING = 0x08000000,
+#define AHC_IN_ISR_BIT 28
+ AHC_IN_ISR = 0x10000000,
+ AHC_IN_ABORT = 0x20000000,
+ AHC_IN_RESET = 0x40000000,
+ AHC_EXTERNAL_SRAM = 0x80000000
+} ahc_flag_type;
+
+typedef enum {
+ AHC_NONE = 0x0000,
+ AHC_CHIPID_MASK = 0x00ff,
+ AHC_AIC7770 = 0x0001,
+ AHC_AIC7850 = 0x0002,
+ AHC_AIC7860 = 0x0003,
+ AHC_AIC7870 = 0x0004,
+ AHC_AIC7880 = 0x0005,
+ AHC_AIC7890 = 0x0006,
+ AHC_AIC7895 = 0x0007,
+ AHC_AIC7896 = 0x0008,
+ AHC_VL = 0x0100,
+ AHC_EISA = 0x0200,
+ AHC_PCI = 0x0400,
+} ahc_chip;
+
+typedef enum {
+ AHC_FENONE = 0x0000,
+ AHC_ULTRA = 0x0001,
+ AHC_ULTRA2 = 0x0002,
+ AHC_WIDE = 0x0004,
+ AHC_TWIN = 0x0008,
+ AHC_MORE_SRAM = 0x0010,
+ AHC_CMD_CHAN = 0x0020,
+ AHC_QUEUE_REGS = 0x0040,
+ AHC_SG_PRELOAD = 0x0080,
+ AHC_SPIOCAP = 0x0100,
+ AHC_AIC7770_FE = AHC_FENONE,
+ AHC_AIC7850_FE = AHC_SPIOCAP,
+ AHC_AIC7860_FE = AHC_ULTRA|AHC_SPIOCAP,
+ AHC_AIC7870_FE = AHC_FENONE,
+ AHC_AIC7880_FE = AHC_ULTRA,
+ AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2|
+ AHC_QUEUE_REGS|AHC_SG_PRELOAD,
+ AHC_AIC7895_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA,
+ AHC_AIC7896_FE = AHC_AIC7890_FE,
+} ahc_feature;
+
+struct aic7xxx_scb {
+ struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */
+ Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */
+ struct aic7xxx_scb *q_next; /* next scb in queue */
+ volatile scb_flag_type flags; /* current state of scb */
+ struct hw_scatterlist *sg_list; /* SG list in adapter format */
+ void *kmalloc_ptr;
+ unsigned char tag_action;
+ unsigned char sg_count;
+ unsigned char sense_cmd[6]; /*
+ * Allocate 6 characters for
+ * sense command.
+ */
+ unsigned int sg_length; /* We init this during buildscb so we
+ * don't have to calculate anything
+ * during underflow/overflow/stat code
+ */
+};
+
+/*
+ * Define a linked list of SCBs.
+ */
+typedef struct {
+ struct aic7xxx_scb *head;
+ struct aic7xxx_scb *tail;
+} scb_queue_type;
+
+static struct {
+ unsigned char errno;
+ const char *errmesg;
+} hard_error[] = {
+ { ILLHADDR, "Illegal Host Access" },
+ { ILLSADDR, "Illegal Sequencer Address referenced" },
+ { ILLOPCODE, "Illegal Opcode in sequencer program" },
+ { SQPARERR, "Sequencer Ram Parity Error" },
+ { DPARERR, "Data-Path Ram Parity Error" },
+ { MPARERR, "Scratch Ram/SCB Array Ram Parity Error" },
+ { PCIERRSTAT,"PCI Error detected" },
+ { CIOPARERR, "CIOBUS Parity Error" }
+};
+
+static unsigned char
+generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
+
+typedef struct {
+ scb_queue_type free_scbs; /*
+ * SCBs assigned to free slot on
+ * card (no paging required)
+ */
+ struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB];
+ struct aic7xxx_hwscb *hscbs;
+ unsigned char numscbs; /* current number of scbs */
+ unsigned char maxhscbs; /* hardware scbs */
+ unsigned char maxscbs; /* max scbs including pageable scbs */
+ void *hscb_kmalloc_ptr;
+} scb_data_type;
+
+struct target_cmd {
+ unsigned char mesg_bytes[4];
+ unsigned char command[28];
+};
+
+#define AHC_TRANS_CUR 0x0001
+#define AHC_TRANS_ACTIVE 0x0002
+#define AHC_TRANS_GOAL 0x0004
+#define AHC_TRANS_USER 0x0008
+#define AHC_TRANS_QUITE 0x0010
+typedef struct {
+ unsigned char cur_width;
+ unsigned char goal_width;
+ unsigned char cur_period;
+ unsigned char goal_period;
+ unsigned char cur_offset;
+ unsigned char goal_offset;
+ unsigned char user_width;
+ unsigned char user_period;
+ unsigned char user_offset;
+} transinfo_type;
+
+/*
+ * Define a structure used for each host adapter. Note, in order to avoid
+ * problems with architectures I can't test on (because I don't have one,
+ * such as the Alpha based systems) which happen to give faults for
+ * non-aligned memory accesses, care was taken to align this structure
+ * in a way that gauranteed all accesses larger than 8 bits were aligned
+ * on the appropriate boundary. It's also organized to try and be more
+ * cache line efficient. Be careful when changing this lest you might hurt
+ * overall performance and bring down the wrath of the masses.
+ */
+struct aic7xxx_host {
+ /*
+ * This is the first 64 bytes in the host struct
+ */
+
+ struct Scsi_Host *host; /* pointer to scsi host */
+ struct aic7xxx_host *next; /* allow for multiple IRQs */
+ int host_no; /* SCSI host number */
+ unsigned long base; /* card base address */
+ volatile unsigned char *maddr; /* memory mapped address */
+ unsigned long mbase; /* I/O memory address */
+ volatile ahc_flag_type flags;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
+ spinlock_t spin_lock;
+#endif
+ volatile unsigned char cpu_lock_count[NR_CPUS];
+ ahc_chip chip; /* chip type */
+ ahc_feature features; /* chip features */
+ unsigned long last_reset;
+ unsigned long isr_count; /* Interrupt count */
+ unsigned long spurious_int;
+ struct target_cmd *targetcmds;
+ unsigned int num_targetcmds;
+ unsigned short discenable; /* Targets allowed to disconnect */
+ unsigned short tagenable; /* Targets using tagged I/O */
+ unsigned short orderedtag; /* Ordered Q tags allowed */
+ volatile unsigned char activescbs; /* active scbs */
+ volatile unsigned char max_activescbs;
+ unsigned char unpause; /* unpause value for HCNTRL */
+ unsigned char pause; /* pause value for HCNTRL */
+ volatile unsigned char qoutfifonext;
+ volatile unsigned char qinfifonext;
+
+ /*
+ * MAX_TARGETS is currently == 16, so that makes these entries the next
+ * 64 bytes
+ */
+
+#define DEVICE_PRESENT 0x01
+#define BUS_DEVICE_RESET_PENDING 0x02
+#define DEVICE_TIMEOUT 0x04
+#define DEVICE_PRINT_SDTR 0x08
+#define DEVICE_PRINT_WDTR 0x10
+#define DEVICE_SUCCESS 0x20
+#define DEVICE_TAGGED_SUCCESS 0x40
+#define DEVICE_SCANNED 0x80
+ volatile unsigned char dev_flags[MAX_TARGETS];
+ volatile unsigned char dev_active_cmds[MAX_TARGETS];
+ volatile unsigned char dev_temp_queue_depth[MAX_TARGETS];
+ unsigned char dev_commands_sent[MAX_TARGETS];
+
+ /*
+ * The next 128 (or 256 on 64 bit machines)....
+ */
+ Scsi_Cmnd *dev_wdtr_cmnd[MAX_TARGETS];
+ Scsi_Cmnd *dev_sdtr_cmnd[MAX_TARGETS];
+
+ /*
+ * The next 64....
+ */
+
+ long dev_last_reset[MAX_TARGETS];
+
+ /*
+ * The next 64....
+ */
+
+ unsigned char dev_mid_level_queue_depth[MAX_TARGETS];
+ unsigned char dev_last_queue_full[MAX_TARGETS];
+ unsigned char dev_last_queue_full_count[MAX_TARGETS];
+ unsigned char dev_max_queue_depth[MAX_TARGETS];
+
+ /*
+ * The next 128....
+ */
+
+ volatile scb_queue_type delayed_scbs[MAX_TARGETS];
+
+ /*
+ *
+ */
+
+ struct timer_list dev_timer[MAX_TARGETS];
+
+ /*
+ * The next 64....
+ */
+
+ unsigned char msg_buf[9]; /* The message for the target */
+ unsigned char msg_type;
+#define MSG_TYPE_NONE 0x00
+#define MSG_TYPE_INITIATOR_MSGOUT 0x01
+#define MSG_TYPE_INITIATOR_MSGIN 0x02
+ unsigned char msg_len; /* Length of message */
+ unsigned char msg_index; /* Index into msg_buf array */
+ transinfo_type transinfo[MAX_TARGETS];
+ volatile scb_queue_type waiting_scbs; /*
+ * SCBs waiting for space in
+ * the QINFIFO.
+ */
+ scb_data_type *scb_data;
+
+ struct aic7xxx_cmd_queue {
+ Scsi_Cmnd *head;
+ Scsi_Cmnd *tail;
+ } completeq;
+
+
+ /*
+ * We put the less frequently used host structure items after the more
+ * frequently used items to try and ease the burden on the cache subsystem.
+ * These entries are not *commonly* accessed, whereas the preceding entries
+ * are accessed very often. The only exceptions are the qinfifo, qoutfifo,
+ * and untagged_scbs array. But, they are often accessed only once and each
+ * access into these arrays is likely to blow a cache line, so they are put
+ * down here so we can minimize the number of cache lines required to hold
+ * the preceeding entries.
+ */
+
+ volatile unsigned char untagged_scbs[256];
+ volatile unsigned char qoutfifo[256];
+ volatile unsigned char qinfifo[256];
+ unsigned int irq; /* IRQ for this adapter */
+ volatile unsigned short needsdtr;
+ volatile unsigned short sdtr_pending;
+ volatile unsigned short needwdtr;
+ volatile unsigned short wdtr_pending;
+ int instance; /* aic7xxx instance number */
+ int scsi_id; /* host adapter SCSI ID */
+ int scsi_id_b; /* channel B for twin adapters */
+ unsigned int bios_address;
+ int board_name_index;
+ unsigned long reset_start;
+ unsigned short needsdtr_copy; /* default config */
+ unsigned short needwdtr_copy; /* default config */
+ unsigned short ultraenb; /* Ultra mode target list */
+ unsigned short bios_control; /* bios control - SEEPROM */
+ unsigned short adapter_control; /* adapter control - SEEPROM */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ struct pci_dev *pdev;
+#endif
+ unsigned char pci_bus;
+ unsigned char pci_device_fn;
+ struct seeprom_config sc;
+ unsigned short sc_type;
+ unsigned short sc_size;
+
+ /*
+ * Statistics Kept:
+ *
+ * Total Xfers (count for each command that has a data xfer),
+ * broken down further by reads && writes.
+ *
+ * Binned sizes, writes && reads:
+ * < 512, 512, 1-2K, 2-4K, 4-8K, 8-16K, 16-32K, 32-64K, 64K-128K, > 128K
+ *
+ * Total amounts read/written above 512 bytes (amts under ignored)
+ *
+ * NOTE: Enabling this feature is likely to cause a noticeable performance
+ * decrease as the accesses into the stats structures blows apart multiple
+ * cache lines and is CPU time consuming. We keep the xfer count always
+ * for use by the aic7xxx_proc.c code, but only do the bins if the
+ * proc stats code is enabled.
+ */
+ struct aic7xxx_xferstats {
+ long xfers; /* total xfer count */
+ long w_total; /* total writes */
+ long w_total512; /* 512 byte blocks written */
+ long r_total; /* total reads */
+ long r_total512; /* 512 byte blocks read */
+#ifdef AIC7XXX_PROC_STATS
+ long w_bins[10]; /* binned write */
+ long r_bins[10]; /* binned reads */
+#endif /* AIC7XXX_PROC_STATS */
+ } stats[MAX_TARGETS][MAX_LUNS]; /* [(channel << 3)|target][lun] */
+};
+
+/*
+ * Valid SCSIRATE values. (p. 3-17)
+ * Provides a mapping of transfer periods in ns/4 to the proper value to
+ * stick in the SCSIRATE reg to use that transfer rate.
+ */
+#define AHC_SYNCRATE_ULTRA2 0
+#define AHC_SYNCRATE_ULTRA 2
+#define AHC_SYNCRATE_FAST 5
+static struct aic7xxx_syncrate {
+ /* Rates in Ultra mode have bit 8 of sxfr set */
+#define ULTRA_SXFR 0x100
+ int sxfr_ultra2;
+ int sxfr;
+ unsigned char period;
+ const char *rate[2];
+} aic7xxx_syncrates[] = {
+ { 0x13, 0x000, 10, {"40.0", "80.0"} },
+ { 0x14, 0x000, 11, {"33.0", "66.6"} },
+ { 0x15, 0x100, 12, {"20.0", "40.0"} },
+ { 0x16, 0x110, 15, {"16.0", "32.0"} },
+ { 0x17, 0x120, 18, {"13.4", "26.8"} },
+ { 0x18, 0x000, 25, {"10.0", "20.0"} },
+ { 0x19, 0x010, 31, {"8.0", "16.0"} },
+ { 0x1a, 0x020, 37, {"6.67", "13.3"} },
+ { 0x1b, 0x030, 43, {"5.7", "11.4"} },
+ { 0x10, 0x040, 50, {"5.0", "10.0"} },
+ { 0x00, 0x050, 56, {"4.4", "8.8" } },
+ { 0x00, 0x060, 62, {"4.0", "8.0" } },
+ { 0x00, 0x070, 68, {"3.6", "7.2" } },
+ { 0x00, 0x000, 0, {NULL, NULL} },
+};
+
+#define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1), \
+ (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
+ ((scb->hscb)->target_channel_lun & 0x07)
+
+#define CTL_OF_CMD(cmd) ((cmd->channel) & 0x01), \
+ ((cmd->target) & 0x0f), \
+ ((cmd->lun) & 0x07)
+
+#define TARGET_INDEX(cmd) ((cmd)->target | ((cmd)->channel << 3))
+
+/*
+ * A nice little define to make doing our printks a little easier
+ */
+
+#define WARN_LEAD KERN_WARNING "(scsi%d:%d:%d:%d) "
+#define INFO_LEAD KERN_INFO "(scsi%d:%d:%d:%d) "
+
+/*
+ * XXX - these options apply unilaterally to _all_ 274x/284x/294x
+ * cards in the system. This should be fixed. Exceptions to this
+ * rule are noted in the comments.
+ */
+
+
+/*
+ * Skip the scsi bus reset. Non 0 make us skip the reset at startup. This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static unsigned int aic7xxx_no_reset = 0;
+/*
+ * Certain PCI motherboards will scan PCI devices from highest to lowest,
+ * others scan from lowest to highest, and they tend to do all kinds of
+ * strange things when they come into contact with PCI bridge chips. The
+ * net result of all this is that the PCI card that is actually used to boot
+ * the machine is very hard to detect. Most motherboards go from lowest
+ * PCI slot number to highest, and the first SCSI controller found is the
+ * one you boot from. The only exceptions to this are when a controller
+ * has its BIOS disabled. So, we by default sort all of our SCSI controllers
+ * from lowest PCI slot number to highest PCI slot number. We also force
+ * all controllers with their BIOS disabled to the end of the list. This
+ * works on *almost* all computers. Where it doesn't work, we have this
+ * option. Setting this option to non-0 will reverse the order of the sort
+ * to highest first, then lowest, but will still leave cards with their BIOS
+ * disabled at the very end. That should fix everyone up unless there are
+ * really strange cirumstances.
+ */
+static int aic7xxx_reverse_scan = 0;
+/*
+ * This setting enables a hack to fix the IRQ settings on buggy 7895
+ * MB controller setups:
+ * -1 == Disable this hack
+ * 0 == Use the Channel A IRQ for both channels
+ * 1 == Use the Channel B IRQ for both channels
+ */
+static unsigned int aic7xxx_extended = 0;
+/*
+ * The IRQ trigger method used on EISA controllers. Does not effect PCI cards.
+ * -1 = Use detected settings.
+ * 0 = Force Edge triggered mode.
+ * 1 = Force Level triggered mode.
+ */
+static int aic7xxx_irq_trigger = -1;
+/*
+ * This variable is used to override the termination settings on a controller.
+ * This should not be used under normal conditions. However, in the case
+ * that a controller does not have a readable SEEPROM (so that we can't
+ * read the SEEPROM settings directly) and that a controller has a buggered
+ * version of the cable detection logic, this can be used to force the
+ * correct termination. It is preferable to use the manual termination
+ * settings in the BIOS if possible, but some motherboard controllers store
+ * those settings in a format we can't read. In other cases, auto term
+ * should also work, but the chipset was put together with no auto term
+ * logic (common on motherboard controllers). In those cases, we have
+ * 32 bits here to work with. That's good for 8 controllers/channels. The
+ * bits are organized as 4 bits per channel, with scsi0 getting the lowest
+ * 4 bits in the int. A 1 in a bit position indicates the termination setting
+ * that corresponds to that bit should be enabled, a 0 is disabled.
+ * It looks something like this:
+ *
+ * 0x0f = 1111-Single Ended Low Byte Termination on/off
+ * ||\-Single Ended High Byte Termination on/off
+ * |\-LVD Low Byte Termination on/off
+ * \-LVD High Byte Termination on/off
+ *
+ * For non-Ultra2 controllers, the upper 2 bits are not important. So, to
+ * enable both high byte and low byte termination on scsi0, I would need to
+ * make sure that the override_term variable was set to 0x03 (bits 0011).
+ * To make sure that all termination is enabled on an Ultra2 controller at
+ * scsi2 and only high byte termination on scsi1 and high and low byte
+ * termination on scsi0, I would set override_term=0xf23 (bits 1111 0010 0011)
+ *
+ * For the most part, users should never have to use this, that's why I
+ * left it fairly cryptic instead of easy to understand. If you need it,
+ * most likely someone will be telling you what your's needs to be set to.
+ */
+static int aic7xxx_override_term = -1;
+/*
+ * Certain motherboard chipset controllers tend to screw
+ * up the polarity of the term enable output pin. Use this variable
+ * to force the correct polarity for your system. This is a bitfield variable
+ * similar to the previous one, but this one has one bit per channel instead
+ * of four.
+ * 0 = Force the setting to active low.
+ * 1 = Force setting to active high.
+ * Most Adaptec cards are active high, several motherboards are active low.
+ * To force a 2940 card at SCSI 0 to active high and a motherboard 7895
+ * controller at scsi1 and scsi2 to active low, and a 2910 card at scsi3
+ * to active high, you would need to set stpwlev=0x9 (bits 1001).
+ *
+ * People shouldn't need to use this, but if you are experiencing lots of
+ * SCSI timeout problems, this may help. There is one sure way to test what
+ * this option needs to be. Using a boot floppy to boot the system, configure
+ * your system to enable all SCSI termination (in the Adaptec SCSI BIOS) and
+ * if needed then also pass a value to override_term to make sure that the
+ * driver is enabling SCSI termination, then set this variable to either 0
+ * or 1. When the driver boots, make sure there are *NO* SCSI cables
+ * connected to your controller. If it finds and inits the controller
+ * without problem, then the setting you passed to stpwlev was correct. If
+ * the driver goes into a reset loop and hangs the system, then you need the
+ * other setting for this variable. If neither setting lets the machine
+ * boot then you have definite termination problems that may not be fixable.
+ */
+static int aic7xxx_stpwlev = -1;
+/*
+ * Set this to non-0 in order to force the driver to panic the kernel
+ * and print out debugging info on a SCSI abort or reset cycle.
+ */
+static int aic7xxx_panic_on_abort = 0;
+/*
+ * PCI bus parity checking of the Adaptec controllers. This is somewhat
+ * dubious at best. To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations, it can generate tons of false error messages.
+ * It's included in the driver for completeness.
+ * 0 = Shut off PCI parity check
+ * -1 = Normal polarity pci parity checking
+ * 1 = reverse polarity pci parity checking
+ *
+ * NOTE: you can't actually pass -1 on the lilo prompt. So, to set this
+ * variable to -1 you would actually want to simply pass the variable
+ * name without a number. That will invert the 0 which will result in
+ * -1.
+ */
+static int aic7xxx_pci_parity = 0;
+/*
+ * Set this to any non-0 value to cause us to dump the contents of all
+ * the card's registers in a hex dump format tailored to each model of
+ * controller.
+ *
+ * NOTE: THE CONTROLLER IS LEFT IN AN UNUSEABLE STATE BY THIS OPTION.
+ * YOU CANNOT BOOT UP WITH THIS OPTION, IT IS FOR DEBUGGING PURPOSES
+ * ONLY
+ */
+static int aic7xxx_dump_card = 0;
+/*
+ * Set this to a non-0 value to make us dump out the 32 bit instruction
+ * registers on the card after completing the sequencer download. This
+ * allows the actual sequencer download to be verified. It is possible
+ * to use this option and still boot up and run your system. This is
+ * only intended for debugging purposes.
+ */
+static int aic7xxx_dump_sequencer = 0;
+/*
+ * Certain newer motherboards have put new PCI based devices into the
+ * IO spaces that used to typically be occupied by VLB or EISA cards.
+ * This overlap can cause these newer motherboards to lock up when scanned
+ * for older EISA and VLB devices. Setting this option to non-0 will
+ * cause the driver to skip scanning for any VLB or EISA controllers and
+ * only support the PCI controllers. NOTE: this means that if the kernel
+ * os compiled with PCI support disabled, then setting this to non-0
+ * would result in never finding any devices :)
+ */
+static int aic7xxx_no_probe = 0;
+
+/*
+ * So that insmod can find the variable and make it point to something
+ */
+#ifdef MODULE
+static char * aic7xxx = NULL;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,18)
+MODULE_PARM(aic7xxx, "s");
+#endif
+
+/*
+ * Just in case someone uses commas to separate items on the insmod
+ * command line, we define a dummy buffer here to avoid having insmod
+ * write wild stuff into our code segment
+ */
+static char dummy_buffer[60] = "Please don't trounce on me insmod!!\n";
+
+#endif
+
+/*
+ * See the comments earlier in the file for what this item is all about
+ * If you have more than 4 controllers, you will need to increase the
+ * the number of items in the array below. Additionally, if you don't
+ * want to have lilo pass a humongous config line to the aic7xxx driver,
+ * then you can get in and manually adjust these instead of leaving them
+ * at the default. Pay attention to the comments earlier in this file
+ * concerning this array if you are going to hand modify these values.
+ */
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS},
+ {DEFAULT_TAG_COMMANDS}
+};
+
+#define VERBOSE_NORMAL 0x0000
+#define VERBOSE_NEGOTIATION 0x0001
+#define VERBOSE_SEQINT 0x0002
+#define VERBOSE_SCSIINT 0x0004
+#define VERBOSE_PROBE 0x0008
+#define VERBOSE_PROBE2 0x0010
+#define VERBOSE_NEGOTIATION2 0x0020
+#define VERBOSE_MINOR_ERROR 0x0040
+#define VERBOSE_TRACING 0x0080
+#define VERBOSE_ABORT 0x0f00
+#define VERBOSE_ABORT_MID 0x0100
+#define VERBOSE_ABORT_FIND 0x0200
+#define VERBOSE_ABORT_PROCESS 0x0400
+#define VERBOSE_ABORT_RETURN 0x0800
+#define VERBOSE_RESET 0xf000
+#define VERBOSE_RESET_MID 0x1000
+#define VERBOSE_RESET_FIND 0x2000
+#define VERBOSE_RESET_PROCESS 0x4000
+#define VERBOSE_RESET_RETURN 0x8000
+static int aic7xxx_verbose = VERBOSE_NORMAL | VERBOSE_NEGOTIATION |
+ VERBOSE_PROBE; /* verbose messages */
+
+
+/****************************************************************************
+ *
+ * We're going to start putting in function declarations so that order of
+ * functions is no longer important. As needed, they are added here.
+ *
+ ***************************************************************************/
+
+static void aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd);
+static void aic7xxx_print_card(struct aic7xxx_host *p);
+static void aic7xxx_print_scratch_ram(struct aic7xxx_host *p);
+static void aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+static void aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer);
+#endif
+
+/****************************************************************************
+ *
+ * These functions are now used. They happen to be wrapped in useless
+ * inb/outb port read/writes around the real reads and writes because it
+ * seems that certain very fast CPUs have a problem dealing with us when
+ * going at full speed.
+ *
+ ***************************************************************************/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+static inline void
+mdelay(int milliseconds)
+{
+ int i;
+
+ for(i=0; i<milliseconds; i++)
+ udelay(1000);
+}
+#endif
+
+static inline unsigned char
+aic_inb(struct aic7xxx_host *p, long port)
+{
+#ifdef MMAPIO
+ unsigned char x;
+ if(p->maddr)
+ {
+ x = p->maddr[port];
+ }
+ else
+ {
+ x = inb(p->base + port);
+ }
+ mb();
+ return(x);
+#else
+ return(inb(p->base + port));
+#endif
+}
+
+static inline void
+aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
+{
+#ifdef MMAPIO
+ if(p->maddr)
+ {
+ p->maddr[port] = val;
+ }
+ else
+ {
+ outb(val, p->base + port);
+ }
+ mb();
+#else
+ outb(val, p->base + port);
+#endif
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_setup
+ *
+ * Description:
+ * Handle Linux boot parameters. This routine allows for assigning a value
+ * to a parameter with a ':' between the parameter and the value.
+ * ie. aic7xxx=unpause:0x0A,extended
+ *-F*************************************************************************/
+void
+aic7xxx_setup(char *s, int *dummy)
+{
+ int i, n;
+ char *p;
+ char *end;
+
+ static struct {
+ const char *name;
+ unsigned int *flag;
+ } options[] = {
+ { "extended", &aic7xxx_extended },
+ { "no_reset", &aic7xxx_no_reset },
+ { "irq_trigger", &aic7xxx_irq_trigger },
+ { "verbose", &aic7xxx_verbose },
+ { "reverse_scan",&aic7xxx_reverse_scan },
+ { "override_term", &aic7xxx_override_term },
+ { "stpwlev", &aic7xxx_stpwlev },
+ { "no_probe", &aic7xxx_no_probe },
+ { "panic_on_abort", &aic7xxx_panic_on_abort },
+ { "pci_parity", &aic7xxx_pci_parity },
+ { "dump_card", &aic7xxx_dump_card },
+ { "dump_sequencer", &aic7xxx_dump_sequencer },
+ { "tag_info", NULL }
+ };
+
+ end = strchr(s, '\0');
+
+ for (p = strtok(s, ",."); p; p = strtok(NULL, ",."))
+ {
+ for (i = 0; i < NUMBER(options); i++)
+ {
+ n = strlen(options[i].name);
+ if (!strncmp(options[i].name, p, n))
+ {
+ if (!strncmp(p, "tag_info", n))
+ {
+ if (p[n] == ':')
+ {
+ char *base;
+ char *tok, *tok_end, *tok_end2;
+ char tok_list[] = { '.', ',', '{', '}', '\0' };
+ int i, instance = -1, device = -1;
+ unsigned char done = FALSE;
+
+ base = p;
+ tok = base + n + 1; /* Forward us just past the ':' */
+ tok_end = strchr(tok, '\0');
+ if (tok_end < end)
+ *tok_end = ',';
+ while(!done)
+ {
+ switch(*tok)
+ {
+ case '{':
+ if (instance == -1)
+ instance = 0;
+ else if (device == -1)
+ device = 0;
+ tok++;
+ break;
+ case '}':
+ if (device != -1)
+ device = -1;
+ else if (instance != -1)
+ instance = -1;
+ tok++;
+ break;
+ case ',':
+ case '.':
+ if (instance == -1)
+ done = TRUE;
+ else if (device >= 0)
+ device++;
+ else if (instance >= 0)
+ instance++;
+ if ( (device >= MAX_TARGETS) ||
+ (instance >= NUMBER(aic7xxx_tag_info)) )
+ done = TRUE;
+ tok++;
+ if (!done)
+ {
+ base = tok;
+ }
+ break;
+ case '\0':
+ done = TRUE;
+ break;
+ default:
+ done = TRUE;
+ tok_end = strchr(tok, '\0');
+ for(i=0; tok_list[i]; i++)
+ {
+ tok_end2 = strchr(tok, tok_list[i]);
+ if ( (tok_end2) && (tok_end2 < tok_end) )
+ {
+ tok_end = tok_end2;
+ done = FALSE;
+ }
+ }
+ if ( (instance >= 0) && (device >= 0) &&
+ (instance < NUMBER(aic7xxx_tag_info)) &&
+ (device < MAX_TARGETS) )
+ aic7xxx_tag_info[instance].tag_commands[device] =
+ simple_strtoul(tok, NULL, 0) & 0xff;
+ tok = tok_end;
+ break;
+ }
+ }
+ while((p != base) && (p != NULL))
+ p = strtok(NULL, ",.");
+ }
+ }
+ else if (p[n] == ':')
+ {
+ *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
+ }
+ else if (!strncmp(p, "verbose", n))
+ {
+ *(options[i].flag) = 0xff09;
+ }
+ else
+ {
+ *(options[i].flag) = ~(*(options[i].flag));
+ }
+ }
+ }
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * pause_sequencer
+ *
+ * Description:
+ * Pause the sequencer and wait for it to actually stop - this
+ * is important since the sequencer can disable pausing for critical
+ * sections.
+ *-F*************************************************************************/
+static inline void
+pause_sequencer(struct aic7xxx_host *p)
+{
+ aic_outb(p, p->pause, HCNTRL);
+ while ((aic_inb(p, HCNTRL) & PAUSE) == 0)
+ {
+ ;
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * unpause_sequencer
+ *
+ * Description:
+ * Unpause the sequencer. Unremarkable, yet done often enough to
+ * warrant an easy way to do it.
+ *-F*************************************************************************/
+static inline void
+unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
+{
+ if (unpause_always ||
+ ( !(aic_inb(p, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) &&
+ !(p->flags & AHC_HANDLING_REQINITS) ) )
+ {
+ aic_outb(p, p->unpause, HCNTRL);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * restart_sequencer
+ *
+ * Description:
+ * Restart the sequencer program from address zero. This assumes
+ * that the sequencer is already paused.
+ *-F*************************************************************************/
+static inline void
+restart_sequencer(struct aic7xxx_host *p)
+{
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
+ aic_outb(p, FASTMODE, SEQCTL);
+}
+
+/*
+ * We include the aic7xxx_seq.c file here so that the other defines have
+ * already been made, and so that it comes before the code that actually
+ * downloads the instructions (since we don't typically use function
+ * prototype, our code has to be ordered that way, it's a left-over from
+ * the original driver days.....I should fix it some time DL).
+ */
+#include "aic7xxx_seq.c"
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_check_patch
+ *
+ * Description:
+ * See if the next patch to download should be downloaded.
+ *-F*************************************************************************/
+static int
+aic7xxx_check_patch(struct aic7xxx_host *p,
+ struct sequencer_patch **start_patch, int start_instr, int *skip_addr)
+{
+ struct sequencer_patch *cur_patch;
+ struct sequencer_patch *last_patch;
+ int num_patches;
+
+ num_patches = sizeof(sequencer_patches)/sizeof(struct sequencer_patch);
+ last_patch = &sequencer_patches[num_patches];
+ cur_patch = *start_patch;
+
+ while ((cur_patch < last_patch) && (start_instr == cur_patch->begin))
+ {
+ if (cur_patch->patch_func(p) == 0)
+ {
+ /*
+ * Start rejecting code.
+ */
+ *skip_addr = start_instr + cur_patch->skip_instr;
+ cur_patch += cur_patch->skip_patch;
+ }
+ else
+ {
+ /*
+ * Found an OK patch. Advance the patch pointer to the next patch
+ * and wait for our instruction pointer to get here.
+ */
+ cur_patch++;
+ }
+ }
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /*
+ * Still skipping
+ */
+ return (0);
+ return(1);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_download_instr
+ *
+ * Description:
+ * Find the next patch to download.
+ *-F*************************************************************************/
+static void
+aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr,
+ unsigned char *dconsts)
+{
+ union ins_formats instr;
+ struct ins_format1 *fmt1_ins;
+ struct ins_format3 *fmt3_ins;
+ unsigned char opcode;
+
+ instr = *(union ins_formats*) &seqprog[instrptr * 4];
+
+ instr.integer = le32_to_cpu(instr.integer);
+
+ fmt1_ins = &instr.format1;
+ fmt3_ins = NULL;
+
+ /* Pull the opcode */
+ opcode = instr.format1.opcode;
+ switch (opcode)
+ {
+ case AIC_OP_JMP:
+ case AIC_OP_JC:
+ case AIC_OP_JNC:
+ case AIC_OP_CALL:
+ case AIC_OP_JNE:
+ case AIC_OP_JNZ:
+ case AIC_OP_JE:
+ case AIC_OP_JZ:
+ {
+ struct sequencer_patch *cur_patch;
+ int address_offset;
+ unsigned int address;
+ int skip_addr;
+ int i;
+
+ fmt3_ins = &instr.format3;
+ address_offset = 0;
+ address = fmt3_ins->address;
+ cur_patch = sequencer_patches;
+ skip_addr = 0;
+
+ for (i = 0; i < address;)
+ {
+ aic7xxx_check_patch(p, &cur_patch, i, &skip_addr);
+ if (skip_addr > i)
+ {
+ int end_addr;
+
+ end_addr = MIN(address, skip_addr);
+ address_offset += end_addr - i;
+ i = skip_addr;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ address -= address_offset;
+ fmt3_ins->address = address;
+ /* Fall Through to the next code section */
+ }
+ case AIC_OP_OR:
+ case AIC_OP_AND:
+ case AIC_OP_XOR:
+ case AIC_OP_ADD:
+ case AIC_OP_ADC:
+ case AIC_OP_BMOV:
+ if (fmt1_ins->parity != 0)
+ {
+ fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+ }
+ fmt1_ins->parity = 0;
+ /* Fall Through to the next code section */
+ case AIC_OP_ROL:
+ if ((p->features & AHC_ULTRA2) != 0)
+ {
+ int i, count;
+
+ /* Calculate odd parity for the instruction */
+ for ( i=0, count=0; i < 31; i++)
+ {
+ unsigned int mask;
+
+ mask = 0x01 << i;
+ if ((instr.integer & mask) != 0)
+ count++;
+ }
+ if (!(count & 0x01))
+ instr.format1.parity = 1;
+ }
+ else
+ {
+ if (fmt3_ins != NULL)
+ {
+ instr.integer = fmt3_ins->immediate |
+ (fmt3_ins->source << 8) |
+ (fmt3_ins->address << 16) |
+ (fmt3_ins->opcode << 25);
+ }
+ else
+ {
+ instr.integer = fmt1_ins->immediate |
+ (fmt1_ins->source << 8) |
+ (fmt1_ins->destination << 16) |
+ (fmt1_ins->ret << 24) |
+ (fmt1_ins->opcode << 25);
+ }
+ }
+ aic_outb(p, (instr.integer & 0xff), SEQRAM);
+ aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM);
+ aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM);
+ aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM);
+ break;
+
+ default:
+ panic("aic7xxx: Unknown opcode encountered in sequencer program.");
+ break;
+ }
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_loadseq
+ *
+ * Description:
+ * Load the sequencer code into the controller memory.
+ *-F*************************************************************************/
+static void
+aic7xxx_loadseq(struct aic7xxx_host *p)
+{
+ struct sequencer_patch *cur_patch;
+ int i;
+ int downloaded;
+ int skip_addr;
+ unsigned char download_consts[4] = {0, 0, 0, 0};
+
+ if (aic7xxx_verbose & VERBOSE_PROBE)
+ {
+ printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no);
+ }
+ download_consts[TMODE_NUMCMDS] = p->num_targetcmds;
+ cur_patch = &sequencer_patches[0];
+ downloaded = 0;
+ skip_addr = 0;
+
+ aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
+
+ for (i = 0; i < sizeof(seqprog) / 4; i++)
+ {
+ if (aic7xxx_check_patch(p, &cur_patch, i, &skip_addr) == 0)
+ {
+ /* Skip this instruction for this configuration. */
+ continue;
+ }
+ aic7xxx_download_instr(p, i, &download_consts[0]);
+ downloaded++;
+ }
+
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
+ aic_outb(p, FASTMODE | FAILDIS, SEQCTL);
+ unpause_sequencer(p, TRUE);
+ mdelay(1);
+ pause_sequencer(p);
+ aic_outb(p, FASTMODE, SEQCTL);
+ if (aic7xxx_verbose & VERBOSE_PROBE)
+ {
+ printk(" %d instructions downloaded\n", downloaded);
+ }
+ if (aic7xxx_dump_sequencer)
+ aic7xxx_print_sequencer(p, downloaded);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_print_sequencer
+ *
+ * Description:
+ * Print the contents of the sequencer memory to the screen.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded)
+{
+ int i, k, temp;
+
+ aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
+
+ k = 0;
+ for (i=0; i < downloaded; i++)
+ {
+ if ( k == 0 )
+ printk("%03x: ", i);
+ temp = aic_inb(p, SEQRAM);
+ temp |= (aic_inb(p, SEQRAM) << 8);
+ temp |= (aic_inb(p, SEQRAM) << 16);
+ temp |= (aic_inb(p, SEQRAM) << 24);
+ printk("%08x", temp);
+ if ( ++k == 8 )
+ {
+ printk("\n");
+ k = 0;
+ }
+ else
+ printk(" ");
+ }
+ aic_outb(p, 0, SEQADDR0);
+ aic_outb(p, 0, SEQADDR1);
+ aic_outb(p, FASTMODE | FAILDIS, SEQCTL);
+ unpause_sequencer(p, TRUE);
+ mdelay(1);
+ pause_sequencer(p);
+ aic_outb(p, FASTMODE, SEQCTL);
+ printk("\n");
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_delay
+ *
+ * Description:
+ * Delay for specified amount of time. We use mdelay because the timer
+ * interrupt is not guaranteed to be enabled. This will cause an
+ * infinite loop since jiffies (clock ticks) is not updated.
+ *-F*************************************************************************/
+static void
+aic7xxx_delay(int seconds)
+{
+ mdelay(seconds * 1000);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_info
+ *
+ * Description:
+ * Return a string describing the driver.
+ *-F*************************************************************************/
+const char *
+aic7xxx_info(struct Scsi_Host *dooh)
+{
+ static char buffer[256];
+ char *bp;
+ struct aic7xxx_host *p;
+
+ bp = &buffer[0];
+ p = (struct aic7xxx_host *)dooh->hostdata;
+ memset(bp, 0, sizeof(buffer));
+ strcpy(bp, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
+ strcat(bp, AIC7XXX_C_VERSION);
+ strcat(bp, "/");
+ strcat(bp, AIC7XXX_H_VERSION);
+ strcat(bp, "\n");
+ strcat(bp, " <");
+ strcat(bp, board_names[p->board_name_index]);
+ strcat(bp, ">");
+
+ return(bp);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_find_syncrate
+ *
+ * Description:
+ * Look up the valid period to SCSIRATE conversion in our table
+ *-F*************************************************************************/
+static struct aic7xxx_syncrate *
+aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period,
+ unsigned int maxsync)
+{
+ struct aic7xxx_syncrate *syncrate;
+
+ syncrate = &aic7xxx_syncrates[maxsync];
+ while ( (syncrate->rate[0] != NULL) &&
+ (!(p->features & AHC_ULTRA2) || syncrate->sxfr_ultra2) )
+ {
+ if ( *period <= syncrate->period )
+ {
+ /*
+ * When responding to a target that requests sync, the requested rate
+ * may fall between two rates that we can output, but still be a rate
+ * that we can receive. Because of this, we want to respond with the
+ * same rate that it sent to us even if the persiod we use to send
+ * data to it is lower. Only lower the response period if we must.
+ */
+ if(syncrate == &aic7xxx_syncrates[maxsync])
+ {
+ *period = syncrate->period;
+ }
+ break;
+ }
+ syncrate++;
+ }
+ if ( (*period == 0) || (syncrate->rate[0] == NULL) ||
+ ((p->features & AHC_ULTRA2) && (syncrate->sxfr_ultra2 == 0)) )
+ {
+ /*
+ * Use async transfers for this target
+ */
+ *period = 0;
+ syncrate = NULL;
+ }
+ return (syncrate);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_find_period
+ *
+ * Description:
+ * Look up the valid SCSIRATE to period conversion in our table
+ *-F*************************************************************************/
+static unsigned int
+aic7xxx_find_period(struct aic7xxx_host *p, unsigned int scsirate,
+ unsigned int maxsync)
+{
+ struct aic7xxx_syncrate *syncrate;
+
+ if ((p->features & AHC_ULTRA2) != 0)
+ {
+ scsirate &= SXFR_ULTRA2;
+ }
+ else
+ {
+ scsirate &= SXFR;
+ }
+
+ syncrate = &aic7xxx_syncrates[maxsync];
+ while (syncrate->rate[0] != NULL)
+ {
+ if ((p->features & AHC_ULTRA2) != 0)
+ {
+ if (syncrate->sxfr_ultra2 == 0)
+ break;
+ else if (scsirate == syncrate->sxfr_ultra2)
+ return (syncrate->period);
+ }
+ else if (scsirate == (syncrate->sxfr & ~ULTRA_SXFR))
+ {
+ return (syncrate->period);
+ }
+ syncrate++;
+ }
+ return (0); /* async */
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_validate_offset
+ *
+ * Description:
+ * Set a valid offset value for a particular card in use and transfer
+ * settings in use.
+ *-F*************************************************************************/
+static void
+aic7xxx_validate_offset(struct aic7xxx_host *p,
+ struct aic7xxx_syncrate *syncrate, unsigned int *offset, int wide)
+{
+ unsigned int maxoffset;
+
+ /* Limit offset to what the card (and device) can do */
+ if (syncrate == NULL)
+ {
+ maxoffset = 0;
+ }
+ else if (p->features & AHC_ULTRA2)
+ {
+ maxoffset = MAX_OFFSET_ULTRA2;
+ }
+ else
+ {
+ if (wide)
+ maxoffset = MAX_OFFSET_16BIT;
+ else
+ maxoffset = MAX_OFFSET_8BIT;
+ }
+ *offset = MIN(*offset, maxoffset);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_set_syncrate
+ *
+ * Description:
+ * Set the actual syncrate down in the card and in our host structs
+ *-F*************************************************************************/
+static void
+aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
+ int target, int channel, unsigned int period, unsigned int offset,
+ unsigned int type)
+{
+ unsigned char tindex;
+ unsigned short target_mask;
+ unsigned char lun;
+ unsigned int old_period, old_offset;
+
+ tindex = target | (channel << 3);
+ target_mask = 0x01 << tindex;
+ lun = aic_inb(p, SCB_TCL) & 0x07;
+
+ if (syncrate == NULL)
+ {
+ period = 0;
+ offset = 0;
+ }
+
+ old_period = p->transinfo[tindex].cur_period;
+ old_offset = p->transinfo[tindex].cur_offset;
+
+
+ if (type & AHC_TRANS_CUR)
+ {
+ unsigned int scsirate;
+
+ scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+ if (p->features & AHC_ULTRA2)
+ {
+ scsirate &= ~SXFR_ULTRA2;
+ if (syncrate != NULL)
+ {
+ scsirate |= syncrate->sxfr_ultra2;
+ }
+ if (type & AHC_TRANS_ACTIVE)
+ {
+ aic_outb(p, offset, SCSIOFFSET);
+ }
+ aic_outb(p, offset, TARG_OFFSET + tindex);
+ }
+ else /* Not an Ultra2 controller */
+ {
+ scsirate &= ~(SXFR|SOFS);
+ p->ultraenb &= ~target_mask;
+ if (syncrate != NULL)
+ {
+ if (syncrate->sxfr & ULTRA_SXFR)
+ {
+ p->ultraenb |= target_mask;
+ }
+ scsirate |= (syncrate->sxfr & SXFR);
+ scsirate |= (offset & SOFS);
+ }
+ if (type & AHC_TRANS_ACTIVE)
+ {
+ unsigned char sxfrctl0;
+
+ sxfrctl0 = aic_inb(p, SXFRCTL0);
+ sxfrctl0 &= ~FAST20;
+ if (p->ultraenb & target_mask)
+ sxfrctl0 |= FAST20;
+ aic_outb(p, sxfrctl0, SXFRCTL0);
+ }
+ aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB);
+ aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1 );
+ }
+ if (type & AHC_TRANS_ACTIVE)
+ {
+ aic_outb(p, scsirate, SCSIRATE);
+ }
+ aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
+ p->transinfo[tindex].cur_period = period;
+ p->transinfo[tindex].cur_offset = offset;
+ if ( !(type & AHC_TRANS_QUITE) &&
+ (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+ (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
+ {
+ if (offset)
+ {
+ int rate_mod = (scsirate & WIDEXFER) ? 1 : 0;
+
+ printk(INFO_LEAD "Synchronous at %s Mbyte/sec, "
+ "offset %d.\n", p->host_no, channel, target, lun,
+ syncrate->rate[rate_mod], offset);
+ }
+ else
+ {
+ printk(INFO_LEAD "Using asynchronous transfers.\n",
+ p->host_no, channel, target, lun);
+ }
+ p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
+ }
+ }
+
+ if (type & AHC_TRANS_GOAL)
+ {
+ p->transinfo[tindex].goal_period = period;
+ p->transinfo[tindex].goal_offset = offset;
+ }
+
+ if (type & AHC_TRANS_USER)
+ {
+ p->transinfo[tindex].user_period = period;
+ p->transinfo[tindex].user_offset = offset;
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_set_width
+ *
+ * Description:
+ * Set the actual width down in the card and in our host structs
+ *-F*************************************************************************/
+static void
+aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun,
+ unsigned int width, unsigned int type)
+{
+ unsigned char tindex;
+ unsigned short target_mask;
+ unsigned int old_width, new_offset;
+
+ tindex = target | (channel << 3);
+ target_mask = 1 << tindex;
+
+ old_width = p->transinfo[tindex].cur_width;
+
+ if (p->features & AHC_ULTRA2)
+ new_offset = MAX_OFFSET_ULTRA2;
+ else if (width == MSG_EXT_WDTR_BUS_16_BIT)
+ new_offset = MAX_OFFSET_16BIT;
+ else
+ new_offset = MAX_OFFSET_8BIT;
+
+ if (type & AHC_TRANS_CUR)
+ {
+ unsigned char scsirate;
+
+ scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+
+ scsirate &= ~WIDEXFER;
+ if (width == MSG_EXT_WDTR_BUS_16_BIT)
+ scsirate |= WIDEXFER;
+
+ aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
+
+ if (type & AHC_TRANS_ACTIVE)
+ aic_outb(p, scsirate, SCSIRATE);
+
+ p->transinfo[tindex].cur_width = width;
+
+ if ((aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+ (p->dev_flags[tindex] & DEVICE_PRINT_WDTR))
+ {
+ printk(INFO_LEAD "Using %s transfers\n", p->host_no, channel, target,
+ lun, (scsirate & WIDEXFER) ? "Wide(16bit)" : "Narrow(8bit)" );
+ p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
+ }
+ }
+
+ if (type & AHC_TRANS_GOAL)
+ p->transinfo[tindex].goal_width = width;
+ if (type & AHC_TRANS_USER)
+ p->transinfo[tindex].user_width = width;
+
+ /*
+ * Having just set the width, the SDTR should come next, and we need a valid
+ * offset for the SDTR. So, we make sure we put a valid one in here now as
+ * the goal_offset.
+ */
+ if (p->transinfo[tindex].goal_offset)
+ p->transinfo[tindex].goal_offset = new_offset;
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ * scbq_init
+ *
+ * Description:
+ * SCB queue initialization.
+ *
+ *-F*************************************************************************/
+static void
+scbq_init(volatile scb_queue_type *queue)
+{
+ queue->head = NULL;
+ queue->tail = NULL;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * scbq_insert_head
+ *
+ * Description:
+ * Add an SCB to the head of the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_insert_head(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned long cpu_flags;
+#endif
+
+ DRIVER_LOCK
+ scb->q_next = queue->head;
+ queue->head = scb;
+ if (queue->tail == NULL) /* If list was empty, update tail. */
+ queue->tail = queue->head;
+ DRIVER_UNLOCK
+}
+
+/*+F*************************************************************************
+ * Function:
+ * scbq_remove_head
+ *
+ * Description:
+ * Remove an SCB from the head of the list.
+ *
+ *-F*************************************************************************/
+static inline struct aic7xxx_scb *
+scbq_remove_head(volatile scb_queue_type *queue)
+{
+ struct aic7xxx_scb * scbp;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned long cpu_flags;
+#endif
+
+ DRIVER_LOCK
+ scbp = queue->head;
+ if (queue->head != NULL)
+ queue->head = queue->head->q_next;
+ if (queue->head == NULL) /* If list is now empty, update tail. */
+ queue->tail = NULL;
+ DRIVER_UNLOCK
+ return(scbp);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * scbq_remove
+ *
+ * Description:
+ * Removes an SCB from the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_remove(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned long cpu_flags;
+#endif
+
+ DRIVER_LOCK
+ if (queue->head == scb)
+ {
+ /* At beginning of queue, remove from head. */
+ scbq_remove_head(queue);
+ }
+ else
+ {
+ struct aic7xxx_scb *curscb = queue->head;
+
+ /*
+ * Search until the next scb is the one we're looking for, or
+ * we run out of queue.
+ */
+ while ((curscb != NULL) && (curscb->q_next != scb))
+ {
+ curscb = curscb->q_next;
+ }
+ if (curscb != NULL)
+ {
+ /* Found it. */
+ curscb->q_next = scb->q_next;
+ if (scb->q_next == NULL)
+ {
+ /* Update the tail when removing the tail. */
+ queue->tail = curscb;
+ }
+ }
+ }
+ DRIVER_UNLOCK
+}
+
+/*+F*************************************************************************
+ * Function:
+ * scbq_insert_tail
+ *
+ * Description:
+ * Add an SCB at the tail of the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_insert_tail(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned long cpu_flags;
+#endif
+
+ DRIVER_LOCK
+ scb->q_next = NULL;
+ if (queue->tail != NULL) /* Add the scb at the end of the list. */
+ queue->tail->q_next = scb;
+ queue->tail = scb; /* Update the tail. */
+ if (queue->head == NULL) /* If list was empty, update head. */
+ queue->head = queue->tail;
+ DRIVER_UNLOCK
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_match_scb
+ *
+ * Description:
+ * Checks to see if an scb matches the target/channel as specified.
+ * If target is ALL_TARGETS (-1), then we're looking for any device
+ * on the specified channel; this happens when a channel is going
+ * to be reset and all devices on that channel must be aborted.
+ *-F*************************************************************************/
+static int
+aic7xxx_match_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
+ int target, int channel, int lun, unsigned char tag)
+{
+ int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
+ int chan = (scb->hscb->target_channel_lun >> 3) & 0x01;
+ int slun = scb->hscb->target_channel_lun & 0x07;
+ int match;
+
+ match = ((chan == channel) || (channel == ALL_CHANNELS));
+ if (match != 0)
+ match = ((targ == target) || (target == ALL_TARGETS));
+ if (match != 0)
+ match = ((lun == slun) || (lun == ALL_LUNS));
+ if (match != 0)
+ match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ {
+ printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) %s search criteria"
+ " (scsi%d:%d:%d:%d:tag%d)\n", p->host_no, CTL_OF_SCB(scb),
+ scb->hscb->tag, (match) ? "matches" : "doesn't match",
+ p->host_no, channel, target, lun, tag);
+ }
+
+ return (match);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_add_curscb_to_free_list
+ *
+ * Description:
+ * Adds the current scb (in SCBPTR) to the list of free SCBs.
+ *-F*************************************************************************/
+static void
+aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p)
+{
+ /*
+ * Invalidate the tag so that aic7xxx_find_scb doesn't think
+ * it's active
+ */
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic_outb(p, 0, SCB_CONTROL);
+
+ aic_outb(p, aic_inb(p, FREE_SCBH), SCB_NEXT);
+ aic_outb(p, aic_inb(p, SCBPTR), FREE_SCBH);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_rem_scb_from_disc_list
+ *
+ * Description:
+ * Removes the current SCB from the disconnected list and adds it
+ * to the free list.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr)
+{
+ unsigned char next;
+ unsigned char prev;
+
+ aic_outb(p, scbptr, SCBPTR);
+ next = aic_inb(p, SCB_NEXT);
+ prev = aic_inb(p, SCB_PREV);
+ aic7xxx_add_curscb_to_free_list(p);
+
+ if (prev != SCB_LIST_NULL)
+ {
+ aic_outb(p, prev, SCBPTR);
+ aic_outb(p, next, SCB_NEXT);
+ }
+ else
+ {
+ aic_outb(p, next, DISCONNECTED_SCBH);
+ }
+
+ if (next != SCB_LIST_NULL)
+ {
+ aic_outb(p, next, SCBPTR);
+ aic_outb(p, prev, SCB_PREV);
+ }
+ return next;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_busy_target
+ *
+ * Description:
+ * Set the specified target busy.
+ *-F*************************************************************************/
+static inline void
+aic7xxx_busy_target(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ p->untagged_scbs[scb->hscb->target_channel_lun] = scb->hscb->tag;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_index_busy_target
+ *
+ * Description:
+ * Returns the index of the busy target, and optionally sets the
+ * target inactive.
+ *-F*************************************************************************/
+static inline unsigned char
+aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char tcl,
+ int unbusy)
+{
+ unsigned char busy_scbid;
+
+ busy_scbid = p->untagged_scbs[tcl];
+ if (unbusy)
+ {
+ p->untagged_scbs[tcl] = SCB_LIST_NULL;
+ }
+ return (busy_scbid);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_find_scb
+ *
+ * Description:
+ * Look through the SCB array of the card and attempt to find the
+ * hardware SCB that corresponds to the passed in SCB. Return
+ * SCB_LIST_NULL if unsuccessful. This routine assumes that the
+ * card is already paused.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ unsigned char saved_scbptr;
+ unsigned char curindex;
+
+ saved_scbptr = aic_inb(p, SCBPTR);
+ curindex = 0;
+ for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
+ {
+ aic_outb(p, curindex, SCBPTR);
+ if (aic_inb(p, SCB_TAG) == scb->hscb->tag)
+ {
+ break;
+ }
+ }
+ aic_outb(p, saved_scbptr, SCBPTR);
+ if (curindex >= p->scb_data->maxhscbs)
+ {
+ curindex = SCB_LIST_NULL;
+ }
+
+ return (curindex);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_allocate_scb
+ *
+ * Description:
+ * Get an SCB from the free list or by allocating a new one.
+ *-F*************************************************************************/
+static int
+aic7xxx_allocate_scb(struct aic7xxx_host *p)
+{
+ struct aic7xxx_scb *scbp = NULL;
+ int scb_size = sizeof(struct aic7xxx_scb) +
+ sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;
+ int i;
+ int step = PAGE_SIZE / 1024;
+ unsigned long scb_count = 0;
+ struct hw_scatterlist *hsgp;
+ struct aic7xxx_scb *scb_ap;
+ unsigned long temp;
+
+
+ if (p->scb_data->numscbs < p->scb_data->maxscbs)
+ {
+ /*
+ * Calculate the optimal number of SCBs to allocate.
+ *
+ * NOTE: This formula works because the sizeof(sg_array) is always
+ * 1024. Therefore, scb_size * i would always be > PAGE_SIZE *
+ * (i/step). The (i-1) allows the left hand side of the equation
+ * to grow into the right hand side to a point of near perfect
+ * efficiency since scb_size * (i -1) is growing slightly faster
+ * than the right hand side. If the number of SG array elements
+ * is changed, this function may not be near so efficient any more.
+ */
+ for ( i=step;; i *= 2 )
+ {
+ if ( (scb_size * (i-1)) >= ( (PAGE_SIZE * (i/step)) - 64 ) )
+ {
+ i /= 2;
+ break;
+ }
+ }
+ scb_count = MIN( (i-1), p->scb_data->maxscbs - p->scb_data->numscbs);
+ scb_ap = (struct aic7xxx_scb *)kmalloc(scb_size * scb_count, GFP_ATOMIC);
+ if (scb_ap != NULL)
+ {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ {
+ if (p->scb_data->numscbs == 0)
+ printk(INFO_LEAD "Allocating initial %ld SCB structures.\n",
+ p->host_no, -1, -1, -1, scb_count);
+ else
+ printk(INFO_LEAD "Allocating %ld additional SCB structures.\n",
+ p->host_no, -1, -1, -1, scb_count);
+ }
+#endif
+ memset(scb_ap, 0, scb_count * scb_size);
+ temp = (unsigned long) &scb_ap[scb_count];
+ temp += 1023;
+ temp &= ~1023;
+ hsgp = (struct hw_scatterlist *)temp;
+ for (i=0; i < scb_count; i++)
+ {
+ scbp = &scb_ap[i];
+ scbp->hscb = &p->scb_data->hscbs[p->scb_data->numscbs];
+ scbp->sg_list = &hsgp[i * AIC7XXX_MAX_SG];
+ memset(scbp->hscb, 0, sizeof(struct aic7xxx_hwscb));
+ scbp->hscb->tag = p->scb_data->numscbs;
+ /*
+ * Place in the scb array; never is removed
+ */
+ p->scb_data->scb_array[p->scb_data->numscbs++] = scbp;
+ scbq_insert_head(&p->scb_data->free_scbs, scbp);
+ }
+ scbp->kmalloc_ptr = scb_ap;
+ }
+ else
+ {
+ return(0);
+ }
+ }
+ return(scb_count);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_queue_cmd_complete
+ *
+ * Description:
+ * Due to race conditions present in the SCSI subsystem, it is easier
+ * to queue completed commands, then call scsi_done() on them when
+ * we're finished. This function queues the completed commands.
+ *-F*************************************************************************/
+static void
+aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
+{
+ cmd->host_scribble = (char *)p->completeq.head;
+ p->completeq.head = cmd;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_done_cmds_complete
+ *
+ * Description:
+ * Process the completed command queue.
+ *-F*************************************************************************/
+static void
+aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
+{
+ Scsi_Cmnd *cmd;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned int cpu_flags = 0;
+
+ DRIVER_LOCK
+ while (p->completeq.head != NULL)
+ {
+ cmd = p->completeq.head;
+ p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
+ cmd->host_scribble = NULL;
+ sti();
+ cmd->scsi_done(cmd);
+ cli();
+ }
+ DRIVER_UNLOCK
+#else
+ while (p->completeq.head != NULL)
+ {
+ cmd = p->completeq.head;
+ p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
+ cmd->host_scribble = NULL;
+ cmd->scsi_done(cmd);
+ }
+#endif
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_free_scb
+ *
+ * Description:
+ * Free the scb and insert into the free scb list.
+ *-F*************************************************************************/
+static void
+aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+
+ scb->flags = SCB_FREE;
+ scb->cmd = NULL;
+ scb->sg_count = 0;
+ scb->sg_length = 0;
+ scb->tag_action = 0;
+ scb->hscb->control = 0;
+ scb->hscb->target_status = 0;
+ scb->hscb->target_channel_lun = SCB_LIST_NULL;
+
+ scbq_insert_head(&p->scb_data->free_scbs, scb);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_done
+ *
+ * Description:
+ * Calls the higher level scsi done function and frees the scb.
+ *-F*************************************************************************/
+static void
+aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ Scsi_Cmnd *cmd = scb->cmd;
+ int tindex = TARGET_INDEX(cmd);
+ struct aic7xxx_scb *scbp;
+ unsigned char queue_depth;
+
+ if (scb->flags & SCB_RECOVERY_SCB)
+ {
+ p->flags &= ~AHC_ABORT_PENDING;
+ }
+ if (scb->flags & SCB_RESET)
+ {
+ cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff);
+ }
+ else if (scb->flags & SCB_ABORT)
+ {
+ cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff);
+ }
+ else if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
+ {
+ if ( (cmd->cmnd[0] == INQUIRY) && (cmd->result == DID_OK) )
+ {
+ char *buffer;
+
+ if(cmd->use_sg)
+ {
+ struct scatterlist *sg;
+
+ sg = (struct scatterlist *)cmd->request_buffer;
+ buffer = (char *)sg[0].address;
+ }
+ else
+ {
+ buffer = (char *)cmd->request_buffer;
+ }
+#define WIDE_INQUIRY_BITS 0x60
+#define SYNC_INQUIRY_BITS 0x10
+ if ( (buffer[7] & WIDE_INQUIRY_BITS) &&
+ (p->features & AHC_WIDE) )
+ {
+ p->needwdtr |= (1<<tindex);
+ p->needwdtr_copy |= (1<<tindex);
+ if ( (p->flags & AHC_SEEPROM_FOUND) &&
+ (p->transinfo[tindex].user_width != MSG_EXT_WDTR_BUS_16_BIT) )
+ p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
+ else
+ p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_16_BIT;
+ }
+ else
+ {
+ p->needwdtr &= ~(1<<tindex);
+ p->needwdtr_copy &= ~(1<<tindex);
+ pause_sequencer(p);
+ aic7xxx_set_width(p, cmd->target, cmd->channel, cmd->lun,
+ MSG_EXT_WDTR_BUS_8_BIT, (AHC_TRANS_ACTIVE |
+ AHC_TRANS_GOAL |
+ AHC_TRANS_CUR) );
+ unpause_sequencer(p, FALSE);
+ }
+ if (buffer[7] & SYNC_INQUIRY_BITS)
+ {
+ p->needsdtr |= (1<<tindex);
+ p->needsdtr_copy |= (1<<tindex);
+
+ if (p->flags & AHC_SEEPROM_FOUND)
+ p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period;
+ else if (p->features & AHC_ULTRA2)
+ p->transinfo[tindex].goal_period =
+ aic7xxx_syncrates[AHC_SYNCRATE_ULTRA2].period;
+ else if (p->features & AHC_ULTRA)
+ p->transinfo[tindex].goal_period =
+ aic7xxx_syncrates[AHC_SYNCRATE_ULTRA].period;
+ else
+ p->transinfo[tindex].goal_period =
+ aic7xxx_syncrates[AHC_SYNCRATE_FAST].period;
+
+ if (p->features & AHC_ULTRA2)
+ p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+ else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT)
+ p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+ else
+ p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+ }
+ else
+ {
+ p->needsdtr &= ~(1<<tindex);
+ p->needsdtr_copy &= ~(1<<tindex);
+ p->transinfo[tindex].goal_period = 0;
+ p->transinfo[tindex].goal_offset = 0;
+ }
+ p->dev_flags[tindex] |= DEVICE_SCANNED;
+ p->dev_flags[tindex] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
+#undef WIDE_INQUIRY_BITS
+#undef SYNC_INQUIRY_BITS
+ }
+ }
+ else if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
+ {
+ unsigned short mask;
+ int message_error = FALSE;
+
+ mask = 0x01 << tindex;
+
+ /*
+ * Check to see if we get an invalid message or a message error
+ * after failing to negotiate a wide or sync transfer message.
+ */
+ if ((scb->flags & SCB_SENSE) &&
+ ((scb->cmd->sense_buffer[12] == 0x43) || /* INVALID_MESSAGE */
+ (scb->cmd->sense_buffer[12] == 0x49))) /* MESSAGE_ERROR */
+ {
+ message_error = TRUE;
+ }
+
+ if (scb->flags & SCB_MSGOUT_WDTR)
+ {
+ p->wdtr_pending &= ~mask;
+ if (message_error)
+ {
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+ (p->dev_flags[tindex] & DEVICE_PRINT_WDTR) )
+ {
+ printk(INFO_LEAD "Device failed to complete Wide Negotiation "
+ "processing and\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "returned a sense error code for invalid message, "
+ "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "Wide negotiation to this device.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
+ }
+ p->needwdtr &= ~mask;
+ p->needwdtr_copy &= ~mask;
+ }
+ }
+ if (scb->flags & SCB_MSGOUT_SDTR)
+ {
+ p->sdtr_pending &= ~mask;
+ if (message_error)
+ {
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+ (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
+ {
+ printk(INFO_LEAD "Device failed to complete Sync Negotiation "
+ "processing and\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "returned a sense error code for invalid message, "
+ "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+ printk(INFO_LEAD "Sync negotiation to this device.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
+ }
+ p->needsdtr &= ~mask;
+ p->needsdtr_copy &= ~mask;
+ }
+ }
+ }
+ queue_depth = p->dev_temp_queue_depth[tindex];
+ if (queue_depth >= p->dev_active_cmds[tindex])
+ {
+ scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
+ if (scbp)
+ {
+ if (queue_depth == 1)
+ {
+ /*
+ * Give extra preference to untagged devices, such as CD-R devices
+ * This makes it more likely that a drive *won't* stuff up while
+ * waiting on data at a critical time, such as CD-R writing and
+ * audio CD ripping operations. Should also benefit tape drives.
+ */
+ scbq_insert_head(&p->waiting_scbs, scbp);
+ }
+ else
+ {
+ scbq_insert_tail(&p->waiting_scbs, scbp);
+ }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Moving SCB from delayed to waiting queue.\n",
+ p->host_no, CTL_OF_SCB(scbp));
+#endif
+ if (queue_depth > p->dev_active_cmds[tindex])
+ {
+ scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
+ if (scbp)
+ scbq_insert_tail(&p->waiting_scbs, scbp);
+ }
+ }
+ }
+ if ( !(scb->tag_action) && (p->tagenable & (1<<tindex)) )
+ {
+ p->dev_temp_queue_depth[tindex] = p->dev_max_queue_depth[tindex];
+ }
+ p->dev_active_cmds[tindex]--;
+ p->activescbs--;
+
+ /*
+ * If this was an untagged I/O, unbusy the target so the sequencer won't
+ * mistake things later
+ */
+ if (aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, FALSE) ==
+ scb->hscb->tag)
+ {
+ aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, TRUE);
+ }
+
+ {
+ int actual;
+
+ /*
+ * XXX: we should actually know how much actually transferred
+ * XXX: for each command, but apparently that's too difficult.
+ *
+ * We set a lower limit of 512 bytes on the transfer length. We
+ * ignore anything less than this because we don't have a real
+ * reason to count it. Read/Writes to tapes are usually about 20K
+ * and disks are a minimum of 512 bytes unless you want to count
+ * non-read/write commands (such as TEST_UNIT_READY) which we don't
+ */
+ actual = scb->sg_length;
+ if ((actual >= 512) && (((cmd->result >> 16) & 0xf) == DID_OK))
+ {
+ struct aic7xxx_xferstats *sp;
+#ifdef AIC7XXX_PROC_STATS
+ long *ptr;
+ int x;
+#endif /* AIC7XXX_PROC_STATS */
+
+ sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7];
+ sp->xfers++;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if ( (sp->xfers > 16) && (aic7xxx_verbose > 0xffff) )
+ aic7xxx_verbose &= 0xffff;
+#endif
+
+ /*
+ * For block devices, cmd->request.cmd is always == either READ or
+ * WRITE. For character devices, this isn't always set properly, so
+ * we check data_cmnd[0]. This catches the conditions for st.c, but
+ * I'm still not sure if request.cmd is valid for sg devices.
+ */
+ if ( (cmd->request.cmd == WRITE) || (cmd->data_cmnd[0] == WRITE_6) ||
+ (cmd->data_cmnd[0] == WRITE_FILEMARKS) )
+ {
+ sp->w_total++;
+ sp->w_total512 += (actual >> 9);
+#ifdef AIC7XXX_PROC_STATS
+ ptr = sp->w_bins;
+#endif /* AIC7XXX_PROC_STATS */
+ }
+ else
+ {
+ sp->r_total++;
+ sp->r_total512 += (actual >> 9);
+#ifdef AIC7XXX_PROC_STATS
+ ptr = sp->r_bins;
+#endif /* AIC7XXX_PROC_STATS */
+ }
+#ifdef AIC7XXX_PROC_STATS
+ for (x = 9; x <= 17; x++)
+ {
+ if (actual < (1 << x))
+ {
+ ptr[x - 9]++;
+ break;
+ }
+ }
+ if (x > 17)
+ {
+ ptr[x - 9]++;
+ }
+#endif /* AIC7XXX_PROC_STATS */
+ }
+ }
+ aic7xxx_free_scb(p, scb);
+ aic7xxx_queue_cmd_complete(p, cmd);
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_run_done_queue
+ *
+ * Description:
+ * Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the
+ * aborted list, and adds each scb to the free list. If complete
+ * is TRUE, we also process the commands complete list.
+ *-F*************************************************************************/
+static void
+aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
+{
+ struct aic7xxx_scb *scb;
+ int i, found = 0;
+
+ for (i = 0; i < p->scb_data->numscbs; i++)
+ {
+ scb = p->scb_data->scb_array[i];
+ if (scb->flags & SCB_QUEUED_FOR_DONE)
+ {
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Aborting scb %d\n",
+ p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
+ found++;
+ aic7xxx_done(p, scb);
+ }
+ }
+ if (aic7xxx_verbose & (VERBOSE_ABORT_RETURN | VERBOSE_RESET_RETURN))
+ {
+ printk(INFO_LEAD "%d commands found and queued for "
+ "completion.\n", p->host_no, -1, -1, -1, found);
+ }
+ if (complete)
+ {
+ aic7xxx_done_cmds_complete(p);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_abort_waiting_scb
+ *
+ * Description:
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
+ unsigned char scbpos, unsigned char prev)
+{
+ unsigned char curscb, next;
+
+ /*
+ * Select the SCB we want to abort and pull the next pointer out of it.
+ */
+ curscb = aic_inb(p, SCBPTR);
+ aic_outb(p, scbpos, SCBPTR);
+ next = aic_inb(p, SCB_NEXT);
+
+ aic7xxx_add_curscb_to_free_list(p);
+
+ /*
+ * Update the waiting list
+ */
+ if (prev == SCB_LIST_NULL)
+ {
+ /*
+ * First in the list
+ */
+ aic_outb(p, next, WAITING_SCBH);
+ }
+ else
+ {
+ /*
+ * Select the scb that pointed to us and update its next pointer.
+ */
+ aic_outb(p, prev, SCBPTR);
+ aic_outb(p, next, SCB_NEXT);
+ }
+ /*
+ * Point us back at the original scb position and inform the SCSI
+ * system that the command has been aborted.
+ */
+ aic_outb(p, curscb, SCBPTR);
+ return (next);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_search_qinfifo
+ *
+ * Description:
+ * Search the queue-in FIFO for matching SCBs and conditionally
+ * requeue. Returns the number of matching SCBs.
+ *-F*************************************************************************/
+static int
+aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, int channel,
+ int lun, unsigned char tag, int flags, int requeue,
+ volatile scb_queue_type *queue)
+{
+ int found;
+ unsigned char qinpos, qintail;
+ struct aic7xxx_scb *scbp;
+
+ found = 0;
+ qinpos = aic_inb(p, QINPOS);
+ qintail = p->qinfifonext;
+
+ p->qinfifonext = qinpos;
+
+ while (qinpos != qintail)
+ {
+ scbp = p->scb_data->scb_array[p->qinfifo[qinpos++]];
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+ {
+ /*
+ * We found an scb that needs to be removed.
+ */
+ if (requeue && (queue != NULL))
+ {
+ if (scbp->flags & SCB_WAITINGQ)
+ {
+ scbq_remove(queue, scbp);
+ scbq_remove(&p->waiting_scbs, scbp);
+ scbq_remove(&p->delayed_scbs[TARGET_INDEX(scbp->cmd)], scbp);
+ p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
+ p->activescbs++;
+ }
+ scbq_insert_tail(queue, scbp);
+ p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]--;
+ p->activescbs--;
+ scbp->flags |= SCB_WAITINGQ;
+ if ( !(scbp->tag_action & TAG_ENB) )
+ {
+ aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+ TRUE);
+ }
+ }
+ else if (requeue)
+ {
+ p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
+ }
+ else
+ {
+ /*
+ * Preserve any SCB_RECOVERY_SCB flags on this scb then set the
+ * flags we were called with, presumeably so aic7xxx_run_done_queue
+ * can find this scb
+ */
+ scbp->flags = flags | (scbp->flags & SCB_RECOVERY_SCB);
+ if (aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+ FALSE) == scbp->hscb->tag)
+ {
+ aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+ TRUE);
+ }
+ }
+ found++;
+ }
+ else
+ {
+ p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
+ }
+ }
+ /*
+ * Now that we've done the work, clear out any left over commands in the
+ * qinfifo and update the KERNEL_QINPOS down on the card.
+ *
+ * NOTE: This routine expect the sequencer to already be paused when
+ * it is run....make sure it's that way!
+ */
+ qinpos = p->qinfifonext;
+ while(qinpos != qintail)
+ {
+ p->qinfifo[qinpos++] = SCB_LIST_NULL;
+ }
+ if (p->features & AHC_QUEUE_REGS)
+ aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+ else
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+
+ return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_scb_on_qoutfifo
+ *
+ * Description:
+ * Is the scb that was passed to us currently on the qoutfifo?
+ *-F*************************************************************************/
+static int
+aic7xxx_scb_on_qoutfifo(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ int i=0;
+
+ while(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] != SCB_LIST_NULL)
+ {
+ if(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] == scb->hscb->tag)
+ return TRUE;
+ else
+ i++;
+ }
+ return FALSE;
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_reset_device
+ *
+ * Description:
+ * The device at the given target/channel has been reset. Abort
+ * all active and queued scbs for that target/channel. This function
+ * need not worry about linked next pointers because if was a MSG_ABORT_TAG
+ * then we had a tagged command (no linked next), if it was MSG_ABORT or
+ * MSG_BUS_DEV_RESET then the device won't know about any commands any more
+ * and no busy commands will exist, and if it was a bus reset, then nothing
+ * knows about any linked next commands any more. In all cases, we don't
+ * need to worry about the linked next or busy scb, we just need to clear
+ * them.
+ *-F*************************************************************************/
+static void
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
+ int lun, unsigned char tag)
+{
+ struct aic7xxx_scb *scbp;
+ unsigned char active_scb, tcl;
+ int i = 0, j, init_lists = FALSE;
+
+ /*
+ * Restore this when we're done
+ */
+ active_scb = aic_inb(p, SCBPTR);
+
+ if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS))
+ printk(INFO_LEAD "Reset device, active_scb %d\n",
+ p->host_no, channel, target, lun, active_scb);
+ /*
+ * Deal with the busy target and linked next issues.
+ */
+ {
+ int min_target, max_target;
+ struct aic7xxx_scb *scbp, *prev_scbp;
+
+ /* Make all targets 'relative' to bus A. */
+ if (target == ALL_TARGETS)
+ {
+ switch (channel)
+ {
+ case 0:
+ min_target = 0;
+ max_target = (p->features & AHC_WIDE) ? 15 : 7;
+ break;
+ case 1:
+ min_target = 8;
+ max_target = 15;
+ break;
+ case ALL_CHANNELS:
+ default:
+ min_target = 0;
+ max_target = (p->features & (AHC_TWIN|AHC_WIDE)) ? 15 : 7;
+ break;
+ }
+ }
+ else
+ {
+ min_target = target | (channel << 3);
+ max_target = min_target;
+ }
+
+
+ for (i = min_target; i <= max_target; i++)
+ {
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning up status information "
+ "and delayed_scbs.\n", p->host_no, channel, i, lun);
+ if ( !(p->dev_flags[i] & DEVICE_TAGGED_SUCCESS) &&
+ (p->dev_active_cmds[i]) &&
+ (p->tagenable & (0x01 << i)) )
+ {
+ printk(INFO_LEAD "Device appears to be choking on tagged commands.\n",
+ p->host_no, channel, i, lun);
+ printk(INFO_LEAD "Will use untagged I/O instead.\n", p->host_no,
+ channel, i, lun);
+ p->dev_max_queue_depth[i] = 1;
+ p->dev_temp_queue_depth[i] = 1;
+ p->tagenable &= ~(0x01 << i);
+ p->orderedtag &= ~(0x01 << i);
+ }
+ p->dev_flags[i] &= ~BUS_DEVICE_RESET_PENDING;
+ if ( tag == SCB_LIST_NULL )
+ {
+ p->dev_flags[i] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
+ p->dev_last_reset[i] = jiffies;
+ p->dev_last_queue_full_count[i] = 0;
+ p->dev_last_queue_full[i] = 0;
+ p->dev_temp_queue_depth[i] =
+ p->dev_max_queue_depth[i];
+ /*
+ * In case this isn't a full bus reset, we want to add a 4 second timer in
+ * here so that we can delay all re-sent commands for this device for the
+ * 4 seconds and then have our timer routine pick them back up.
+ */
+ if(p->dev_timer[i].expires)
+ {
+ del_timer(&p->dev_timer[i]);
+ }
+ p->dev_timer[i].expires = jiffies + (4 * HZ);
+ add_timer(&p->dev_timer[i]);
+ }
+ for(j=0; j<MAX_LUNS; j++)
+ {
+ if (channel == 1)
+ tcl = ((i << 4) & 0x70) | (channel << 3) | j;
+ else
+ tcl = (i << 4) | (channel << 3) | j;
+ if ( (aic7xxx_index_busy_target(p, tcl, FALSE) == tag) ||
+ (tag == SCB_LIST_NULL) )
+ aic7xxx_index_busy_target(p, tcl, /* unbusy */ TRUE);
+ }
+ j = 0;
+ prev_scbp = NULL;
+ scbp = p->delayed_scbs[i].head;
+ while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) )
+ {
+ prev_scbp = scbp;
+ scbp = scbp->q_next;
+ if ( prev_scbp == scbp )
+ {
+ if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+ printk(WARN_LEAD "Yikes!! scb->q_next == scb "
+ "in the delayed_scbs queue!\n", p->host_no, channel, i, lun);
+ scbp = NULL;
+ prev_scbp->q_next = NULL;
+ p->delayed_scbs[i].tail = prev_scbp;
+ }
+ if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
+ {
+ scbq_remove(&p->delayed_scbs[i], prev_scbp);
+ if (prev_scbp->flags & SCB_WAITINGQ)
+ {
+ p->dev_active_cmds[i]++;
+ p->activescbs++;
+ }
+ prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ }
+ }
+ if ( j > (p->scb_data->maxscbs + 1) )
+ {
+ if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+ printk(WARN_LEAD "Yikes!! There's a loop in the "
+ "delayed_scbs queue!\n", p->host_no, channel, i, lun);
+ scbq_init(&p->delayed_scbs[i]);
+ }
+ if ( (p->delayed_scbs[i].head == NULL) &&
+ (p->dev_timer[i].expires) )
+ {
+ del_timer(&p->dev_timer[i]);
+ p->dev_timer[i].expires = 0;
+ }
+ }
+ }
+
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning QINFIFO.\n", p->host_no, channel, target, lun );
+ aic7xxx_search_qinfifo(p, target, channel, lun, tag,
+ SCB_RESET | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE, NULL);
+
+/*
+ * Search the waiting_scbs queue for matches, this catches any SCB_QUEUED
+ * ABORT/RESET commands.
+ */
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning waiting_scbs.\n", p->host_no, channel,
+ target, lun );
+ {
+ struct aic7xxx_scb *scbp, *prev_scbp;
+
+ j = 0;
+ prev_scbp = NULL;
+ scbp = p->waiting_scbs.head;
+ while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) )
+ {
+ prev_scbp = scbp;
+ scbp = scbp->q_next;
+ if ( prev_scbp == scbp )
+ {
+ if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+ printk(WARN_LEAD "Yikes!! scb->q_next == scb "
+ "in the waiting_scbs queue!\n", p->host_no, CTL_OF_SCB(scbp));
+ scbp = NULL;
+ prev_scbp->q_next = NULL;
+ p->waiting_scbs.tail = prev_scbp;
+ }
+ if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
+ {
+ scbq_remove(&p->waiting_scbs, prev_scbp);
+ if (prev_scbp->flags & SCB_WAITINGQ)
+ {
+ p->dev_active_cmds[TARGET_INDEX(prev_scbp->cmd)]++;
+ p->activescbs++;
+ }
+ prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ }
+ }
+ if ( j > (p->scb_data->maxscbs + 1) )
+ {
+ if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+ printk(WARN_LEAD "Yikes!! There's a loop in the "
+ "waiting_scbs queue!\n", p->host_no, channel, target, lun);
+ scbq_init(&p->waiting_scbs);
+ }
+ }
+
+
+ /*
+ * Search waiting for selection list.
+ */
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning waiting for selection "
+ "list.\n", p->host_no, channel, target, lun);
+ {
+ unsigned char next, prev, scb_index;
+
+ next = aic_inb(p, WAITING_SCBH); /* Start at head of list. */
+ prev = SCB_LIST_NULL;
+ j = 0;
+ while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
+ {
+ aic_outb(p, next, SCBPTR);
+ scb_index = aic_inb(p, SCB_TAG);
+ if (scb_index >= p->scb_data->numscbs)
+ {
+ /*
+ * No aic7xxx_verbose check here.....we want to see this since it
+ * means either the kernel driver or the sequencer screwed things up
+ */
+ printk(WARN_LEAD "Waiting List inconsistency; SCB index=%d, "
+ "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
+ p->scb_data->numscbs);
+ next = aic_inb(p, SCB_NEXT);
+ aic7xxx_add_curscb_to_free_list(p);
+ }
+ else
+ {
+ scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+ {
+ next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
+ if (scbp->flags & SCB_WAITINGQ)
+ {
+ p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
+ p->activescbs++;
+ }
+ scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ if (prev == SCB_LIST_NULL)
+ {
+ /*
+ * This is either the first scb on the waiting list, or we
+ * have already yanked the first and haven't left any behind.
+ * Either way, we need to turn off the selection hardware if
+ * it isn't already off.
+ */
+ aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+ aic_outb(p, CLRSELTIMEO, CLRSINT1);
+ }
+ }
+ else
+ {
+ prev = next;
+ next = aic_inb(p, SCB_NEXT);
+ }
+ }
+ }
+ if ( j > (p->scb_data->maxscbs + 1) )
+ {
+ printk(WARN_LEAD "Yikes!! There is a loop in the waiting for "
+ "selection list!\n", p->host_no, channel, target, lun);
+ init_lists = TRUE;
+ }
+ }
+
+ /*
+ * Go through disconnected list and remove any entries we have queued
+ * for completion, zeroing their control byte too.
+ */
+ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+ printk(INFO_LEAD "Cleaning disconnected scbs "
+ "list.\n", p->host_no, channel, target, lun);
+ if (p->features & AHC_PAGESCBS)
+ {
+ unsigned char next, prev, scb_index;
+
+ next = aic_inb(p, DISCONNECTED_SCBH);
+ prev = SCB_LIST_NULL;
+ j = 0;
+ while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
+ {
+ aic_outb(p, next, SCBPTR);
+ scb_index = aic_inb(p, SCB_TAG);
+ if (scb_index > p->scb_data->numscbs)
+ {
+ printk(WARN_LEAD "Disconnected List inconsistency; SCB index=%d, "
+ "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
+ p->scb_data->numscbs);
+ next = aic7xxx_rem_scb_from_disc_list(p, next);
+ }
+ else
+ {
+ scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+ {
+ next = aic7xxx_rem_scb_from_disc_list(p, next);
+ if (scbp->flags & SCB_WAITINGQ)
+ {
+ p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
+ p->activescbs++;
+ }
+ scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ scbp->hscb->control = 0;
+ }
+ else
+ {
+ prev = next;
+ next = aic_inb(p, SCB_NEXT);
+ }
+ }
+ }
+ if ( j > (p->scb_data->maxscbs + 1) )
+ {
+ printk(WARN_LEAD "Yikes!! There is a loop in the disconnected list!\n",
+ p->host_no, channel, target, lun);
+ init_lists = TRUE;
+ }
+ }
+
+ /*
+ * Walk the free list making sure no entries on the free list have
+ * a valid SCB_TAG value or SCB_CONTROL byte.
+ */
+ if (p->features & AHC_PAGESCBS)
+ {
+ unsigned char next;
+
+ j = 0;
+ next = aic_inb(p, FREE_SCBH);
+ if ( (next >= p->scb_data->maxhscbs) && (next != SCB_LIST_NULL) )
+ {
+ printk(WARN_LEAD "Bogus FREE_SCBH!.\n", p->host_no, channel,
+ target, lun);
+ init_lists = TRUE;
+ next = SCB_LIST_NULL;
+ }
+ while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
+ {
+ aic_outb(p, next, SCBPTR);
+ if (aic_inb(p, SCB_TAG) < p->scb_data->numscbs)
+ {
+ printk(WARN_LEAD "Free list inconsistency!.\n", p->host_no, channel,
+ target, lun);
+ init_lists = TRUE;
+ next = SCB_LIST_NULL;
+ }
+ else
+ {
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic_outb(p, 0, SCB_CONTROL);
+ next = aic_inb(p, SCB_NEXT);
+ }
+ }
+ if ( j > (p->scb_data->maxscbs + 1) )
+ {
+ printk(WARN_LEAD "Yikes!! There is a loop in the free list!\n",
+ p->host_no, channel, target, lun);
+ init_lists = TRUE;
+ }
+ }
+
+ /*
+ * Go through the hardware SCB array looking for commands that
+ * were active but not on any list.
+ */
+ if (init_lists)
+ {
+ aic_outb(p, SCB_LIST_NULL, FREE_SCBH);
+ aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
+ aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
+ }
+ for (i = p->scb_data->maxhscbs - 1; i >= 0; i--)
+ {
+ unsigned char scbid;
+
+ aic_outb(p, i, SCBPTR);
+ if (init_lists)
+ {
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
+ aic_outb(p, SCB_LIST_NULL, SCB_PREV);
+ aic_outb(p, 0, SCB_CONTROL);
+ aic7xxx_add_curscb_to_free_list(p);
+ }
+ else
+ {
+ scbid = aic_inb(p, SCB_TAG);
+ if (scbid < p->scb_data->numscbs)
+ {
+ scbp = p->scb_data->scb_array[scbid];
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+ {
+ aic_outb(p, 0, SCB_CONTROL);
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic7xxx_add_curscb_to_free_list(p);
+ }
+ }
+ }
+ }
+
+ /*
+ * Go through the entire SCB array now and look for commands for
+ * for this target that are stillactive. These are other (most likely
+ * tagged) commands that were disconnected when the reset occurred.
+ * Any commands we find here we know this about, it wasn't on any queue,
+ * it wasn't in the qinfifo, it wasn't in the disconnected or waiting
+ * lists, so it really must have been a paged out SCB. In that case,
+ * we shouldn't need to bother with updating any counters, just mark
+ * the correct flags and go on.
+ */
+ for (i = 0; i < p->scb_data->numscbs; i++)
+ {
+ scbp = p->scb_data->scb_array[i];
+ if ((scbp->flags & SCB_ACTIVE) &&
+ aic7xxx_match_scb(p, scbp, target, channel, lun, tag) &&
+ !aic7xxx_scb_on_qoutfifo(p, scbp))
+ {
+ if (scbp->flags & SCB_WAITINGQ)
+ {
+ scbq_remove(&p->waiting_scbs, scbp);
+ scbq_remove(&p->delayed_scbs[TARGET_INDEX(scbp->cmd)], scbp);
+ p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
+ p->activescbs++;
+ }
+ scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+ scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+ }
+ }
+
+ aic_outb(p, active_scb, SCBPTR);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_clear_intstat
+ *
+ * Description:
+ * Clears the interrupt status.
+ *-F*************************************************************************/
+static void
+aic7xxx_clear_intstat(struct aic7xxx_host *p)
+{
+ /* Clear any interrupt conditions this may have caused. */
+ aic_outb(p, CLRSELDO | CLRSELDI | CLRSELINGO, CLRSINT0);
+ aic_outb(p, CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
+ CLRPHASECHG | CLRREQINIT, CLRSINT1);
+ aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT | CLRPARERR, CLRINT);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_reset_current_bus
+ *
+ * Description:
+ * Reset the current SCSI bus.
+ *-F*************************************************************************/
+static void
+aic7xxx_reset_current_bus(struct aic7xxx_host *p)
+{
+
+ /* Disable reset interrupts. */
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENSCSIRST, SIMODE1);
+
+ /* Turn off the bus' current operations, after all, we shouldn't have any
+ * valid commands left to cause a RSELI and SELO once we've tossed the
+ * bus away with this reset, so we might as well shut down the sequencer
+ * until the bus is restarted as oppossed to saving the current settings
+ * and restoring them (which makes no sense to me). */
+
+ /* Turn on the bus reset. */
+ aic_outb(p, aic_inb(p, SCSISEQ) | SCSIRSTO, SCSISEQ);
+ while ( (aic_inb(p, SCSISEQ) & SCSIRSTO) == 0)
+ mdelay(5);
+
+ mdelay(10);
+
+ /* Turn off the bus reset. */
+ aic_outb(p, 0, SCSISEQ);
+ mdelay(5);
+
+ aic7xxx_clear_intstat(p);
+ /* Re-enable reset interrupts. */
+ aic_outb(p, aic_inb(p, SIMODE1) | ENSCSIRST, SIMODE1);
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_reset_channel
+ *
+ * Description:
+ * Reset the channel.
+ *-F*************************************************************************/
+static void
+aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset)
+{
+ unsigned long offset_min, offset_max;
+ unsigned char sblkctl;
+ int cur_channel;
+
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Reset channel called, %s initiate reset.\n",
+ p->host_no, channel, -1, -1, (initiate_reset==TRUE) ? "will" : "won't" );
+
+
+ if (channel == 1)
+ {
+ p->needsdtr |= (p->needsdtr_copy & 0xFF00);
+ p->sdtr_pending &= 0x00FF;
+ offset_min = 8;
+ offset_max = 16;
+ }
+ else
+ {
+ if (p->features & AHC_WIDE)
+ {
+ p->needsdtr = p->needsdtr_copy;
+ p->needwdtr = p->needwdtr_copy;
+ p->sdtr_pending = 0x0;
+ p->wdtr_pending = 0x0;
+ offset_min = 0;
+ offset_max = 16;
+ }
+ else
+ {
+ /* Channel A */
+ p->needsdtr |= (p->needsdtr_copy & 0x00FF);
+ p->sdtr_pending &= 0xFF00;
+ offset_min = 0;
+ offset_max = 8;
+ }
+ }
+
+ while (offset_min < offset_max)
+ {
+ /*
+ * Revert to async/narrow transfers until we renegotiate.
+ */
+ aic_outb(p, 0, TARG_SCSIRATE + offset_min);
+ if (p->features & AHC_ULTRA2)
+ {
+ aic_outb(p, 0, TARG_OFFSET + offset_min);
+ }
+ offset_min++;
+ }
+
+ /*
+ * Reset the bus and unpause/restart the controller
+ */
+ sblkctl = aic_inb(p, SBLKCTL);
+ if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+ cur_channel = (sblkctl & SELBUSB) >> 3;
+ else
+ cur_channel = 0;
+ if ( (cur_channel != channel) && (p->features & AHC_TWIN) )
+ {
+ /*
+ * Case 1: Command for another bus is active
+ */
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Stealthily resetting idle channel.\n", p->host_no,
+ channel, -1, -1);
+ /*
+ * Stealthily reset the other bus without upsetting the current bus.
+ */
+ aic_outb(p, sblkctl ^ SELBUSB, SBLKCTL);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENBUSFREE, SIMODE1);
+ if (initiate_reset)
+ {
+ aic7xxx_reset_current_bus(p);
+ }
+ aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
+ aic7xxx_clear_intstat(p);
+ aic_outb(p, sblkctl, SBLKCTL);
+ }
+ else
+ {
+ /*
+ * Case 2: A command from this bus is active or we're idle.
+ */
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Resetting currently active channel.\n", p->host_no,
+ channel, -1, -1);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
+ SIMODE1);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ p->msg_type = MSG_TYPE_NONE;
+ p->msg_len = 0;
+ if (initiate_reset)
+ {
+ aic7xxx_reset_current_bus(p);
+ }
+ aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
+ aic7xxx_clear_intstat(p);
+ }
+ if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+ printk(INFO_LEAD "Channel reset\n", p->host_no, channel, -1, -1);
+ /*
+ * Clean up all the state information for the pending transactions
+ * on this bus.
+ */
+ aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
+
+ /*
+ * Convince Mid Level SCSI code to leave us be for a little bit...
+ */
+ p->last_reset = jiffies;
+ p->host->last_reset = (jiffies + (HZ * AIC7XXX_RESET_DELAY));
+
+ if ( !(p->features & AHC_TWIN) )
+ {
+ restart_sequencer(p);
+ }
+
+ return;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_run_waiting_queues
+ *
+ * Description:
+ * Scan the awaiting_scbs queue downloading and starting as many
+ * scbs as we can.
+ *-F*************************************************************************/
+static void
+aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
+{
+ struct aic7xxx_scb *scb;
+ int tindex;
+ int sent;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned long cpu_flags = 0;
+#endif
+
+
+ if (p->waiting_scbs.head == NULL)
+ return;
+
+ sent = 0;
+
+ /*
+ * First handle SCBs that are waiting but have been assigned a slot.
+ */
+ DRIVER_LOCK
+ while ((scb = scbq_remove_head(&p->waiting_scbs)) != NULL)
+ {
+ tindex = TARGET_INDEX(scb->cmd);
+ if ( !scb->tag_action && (p->tagenable & (1<<tindex)) )
+ {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Reducing Queue depth for untagged command.\n",
+ p->host_no, CTL_OF_SCB(scb));
+#endif
+ p->dev_temp_queue_depth[tindex] = 1;
+ }
+ if ( (p->dev_active_cmds[tindex] >=
+ p->dev_temp_queue_depth[tindex]) ||
+ (p->dev_last_reset[tindex] >= (jiffies - (4 * HZ))) )
+ {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Moving SCB to Delayed Queue.\n",
+ p->host_no, CTL_OF_SCB(scb));
+#endif
+ scbq_insert_tail(&p->delayed_scbs[tindex], scb);
+ if ( !(p->dev_timer[tindex].expires) &&
+ !(p->dev_active_cmds[tindex]) )
+ {
+ p->dev_timer[tindex].expires = p->dev_last_reset[tindex] + (4 * HZ);
+ add_timer(&p->dev_timer[tindex]);
+ }
+ }
+ else
+ {
+ scb->flags &= ~SCB_WAITINGQ;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Sending command %d/0x%x to QINFIFO\n", p->host_no,
+ CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+#endif
+ p->dev_active_cmds[tindex]++;
+ p->activescbs++;
+ if ( !(scb->tag_action) )
+ {
+ aic7xxx_busy_target(p, scb);
+ }
+ p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+ sent++;
+ }
+ }
+ if (sent)
+ {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ {
+ printk(INFO_LEAD "Sending commands to QINFIFO\n", p->host_no,
+ -1, -1, -1);
+ if ( (p->isr_count < 16) && (aic7xxx_panic_on_abort) &&
+ (p->flags & AHC_PAGESCBS) )
+ aic7xxx_check_scbs(p, "While sending commands to QINFIFO");
+ }
+#endif
+ if (p->features & AHC_QUEUE_REGS)
+ aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+ else
+ {
+ pause_sequencer(p);
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+ unpause_sequencer(p, FALSE);
+ }
+ if (p->activescbs > p->max_activescbs)
+ p->max_activescbs = p->activescbs;
+ }
+ DRIVER_UNLOCK
+}
+
+#ifdef CONFIG_PCI
+
+#define DPE 0x80
+#define SSE 0x40
+#define RMA 0x20
+#define RTA 0x10
+#define STA 0x08
+#define DPR 0x01
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_pci_intr
+ *
+ * Description:
+ * Check the scsi card for PCI errors and clear the interrupt
+ *
+ * NOTE: If you don't have this function and a 2940 card encounters
+ * a PCI error condition, the machine will end up locked as the
+ * interrupt handler gets slammed with non-stop PCI error interrupts
+ *-F*************************************************************************/
+static void
+aic7xxx_pci_intr(struct aic7xxx_host *p)
+{
+ unsigned char status1;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ pci_read_config_byte(p->pdev, PCI_STATUS + 1, &status1);
+#else
+ pcibios_read_config_byte(p->pci_bus, p->pci_device_fn,
+ PCI_STATUS + 1, &status1);
+#endif
+
+ if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+ printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
+ "phase.\n", p->host_no, -1, -1, -1);
+ if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+ printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
+ -1, -1, -1);
+ if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+ printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
+ -1, -1, -1);
+ if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+ printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
+ -1, -1, -1);
+ if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+ printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
+ -1, -1, -1);
+ if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+ printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
+ "PERR#\n", p->host_no, -1, -1, -1);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ pci_write_config_byte(p->pdev, PCI_STATUS + 1, status1);
+#else
+ pcibios_write_config_byte(p->pci_bus, p->pci_device_fn,
+ PCI_STATUS + 1, status1);
+#endif
+ if (status1 & (DPR|RMA|RTA))
+ aic_outb(p, CLRPARERR, CLRINT);
+
+ if ( (aic7xxx_panic_on_abort) && (p->spurious_int > 500) )
+ aic7xxx_panic_abort(p, NULL);
+
+}
+#endif /* CONFIG_PCI */
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_timer
+ *
+ * Description:
+ * Take expired extries off of delayed queues and place on waiting queue
+ * then run waiting queue to start commands.
+ ***************************************************************************/
+static void
+aic7xxx_timer(struct aic7xxx_host *p)
+{
+ int i, j;
+ unsigned long cpu_flags = 0;
+ struct aic7xxx_scb *scb;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ DRIVER_LOCK
+#else
+ spin_lock_irqsave(&io_request_lock, cpu_flags);
+#endif
+ for(i=0; i<MAX_TARGETS; i++)
+ {
+ if ( (p->dev_timer[i].expires) &&
+ (p->dev_timer[i].expires <= jiffies) )
+ {
+ p->dev_timer[i].expires = 0;
+ if ( (p->dev_timer[i].prev != NULL) ||
+ (p->dev_timer[i].next != NULL) )
+ {
+ del_timer(&p->dev_timer[i]);
+ }
+ p->dev_temp_queue_depth[i] = p->dev_max_queue_depth[i];
+ j = 0;
+ while ( ((scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL) &&
+ (j++ < p->scb_data->numscbs) )
+ {
+ scbq_insert_tail(&p->waiting_scbs, scb);
+ }
+ if (j == p->scb_data->numscbs)
+ {
+ printk(INFO_LEAD "timer: Yikes, loop in delayed_scbs list.\n",
+ p->host_no, 0, i, -1);
+ scbq_init(&p->delayed_scbs[i]);
+ scbq_init(&p->waiting_scbs);
+ /*
+ * Well, things are screwed now, wait for a reset to clean the junk
+ * out.
+ */
+ }
+ }
+ }
+ aic7xxx_run_waiting_queues(p);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ DRIVER_UNLOCK
+#else
+ spin_unlock_irqrestore(&io_request_lock, cpu_flags);
+#endif
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_construct_sdtr
+ *
+ * Description:
+ * Constucts a synchronous data transfer message in the message
+ * buffer on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_sdtr(struct aic7xxx_host *p, unsigned char period,
+ unsigned char offset)
+{
+ p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+ p->msg_buf[p->msg_index++] = MSG_EXT_SDTR_LEN;
+ p->msg_buf[p->msg_index++] = MSG_EXT_SDTR;
+ p->msg_buf[p->msg_index++] = period;
+ p->msg_buf[p->msg_index++] = offset;
+ p->msg_len += 5;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_construct_wdtr
+ *
+ * Description:
+ * Constucts a wide data transfer message in the message buffer
+ * on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_wdtr(struct aic7xxx_host *p, unsigned char bus_width)
+{
+ p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+ p->msg_buf[p->msg_index++] = MSG_EXT_WDTR_LEN;
+ p->msg_buf[p->msg_index++] = MSG_EXT_WDTR;
+ p->msg_buf[p->msg_index++] = bus_width;
+ p->msg_len += 4;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_calc_residual
+ *
+ * Description:
+ * Calculate the residual data not yet transferred.
+ *-F*************************************************************************/
+static void
+aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ struct aic7xxx_hwscb *hscb;
+ Scsi_Cmnd *cmd;
+ int actual, i;
+
+ cmd = scb->cmd;
+ hscb = scb->hscb;
+
+ /*
+ * Don't destroy valid residual information with
+ * residual coming from a check sense operation.
+ */
+ if (((scb->hscb->control & DISCONNECTED) == 0) &&
+ (scb->flags & SCB_SENSE) == 0)
+ {
+ /*
+ * We had an underflow. At this time, there's only
+ * one other driver that bothers to check for this,
+ * and cmd->underflow seems to be set rather half-
+ * heartedly in the higher-level SCSI code.
+ */
+ actual = scb->sg_length;
+ for (i=1; i < hscb->residual_SG_segment_count; i++)
+ {
+ actual -= scb->sg_list[scb->sg_count - i].length;
+ }
+ actual -= (hscb->residual_data_count[2] << 16) |
+ (hscb->residual_data_count[1] << 8) |
+ hscb->residual_data_count[0];
+
+ if (actual < cmd->underflow)
+ {
+ if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+ printk(INFO_LEAD "Underflow - Wanted %u, %s %u, residual SG "
+ "count %d.\n", p->host_no, CTL_OF_SCB(scb), cmd->underflow,
+ (cmd->request.cmd == WRITE) ? "wrote" : "read", actual,
+ hscb->residual_SG_segment_count);
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ aic7xxx_status(cmd) = hscb->target_status;
+ }
+ }
+
+ /*
+ * Clean out the residual information in the SCB for the
+ * next consumer.
+ */
+ hscb->residual_data_count[2] = 0;
+ hscb->residual_data_count[1] = 0;
+ hscb->residual_data_count[0] = 0;
+ hscb->residual_SG_segment_count = 0;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_device_reset
+ *
+ * Description:
+ * Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, int channel)
+{
+ unsigned short targ_mask;
+ unsigned char tindex = target;
+
+ tindex |= ((channel & 0x01) << 3);
+
+ targ_mask = (0x01 << tindex);
+ /*
+ * Go back to async/narrow transfers and renegotiate.
+ */
+ p->needsdtr |= (p->needsdtr_copy & targ_mask);
+ p->needwdtr |= (p->needwdtr_copy & targ_mask);
+ p->sdtr_pending &= ~targ_mask;
+ p->wdtr_pending &= ~targ_mask;
+ aic_outb(p, 0, TARG_SCSIRATE + tindex);
+ if (p->features & AHC_ULTRA2)
+ aic_outb(p, 0, TARG_OFFSET + tindex);
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel,
+ target, -1);
+ aic7xxx_run_done_queue(p, /*complete*/ FALSE);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_seqint
+ *
+ * Description:
+ * Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
+{
+ struct aic7xxx_scb *scb;
+ unsigned short target_mask;
+ unsigned char target, lun, tindex;
+ unsigned char queue_flag = FALSE;
+ char channel;
+
+ target = ((aic_inb(p, SAVED_TCL) >> 4) & 0x0f);
+ if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+ channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+ else
+ channel = 0;
+ tindex = target + (channel << 3);
+ lun = aic_inb(p, SAVED_TCL) & 0x07;
+ target_mask = (0x01 << tindex);
+
+ /*
+ * Go ahead and clear the SEQINT now, that avoids any interrupt race
+ * conditions later on in case we enable some other interrupt.
+ */
+ aic_outb(p, CLRSEQINT, CLRINT);
+ switch (intstat & SEQINT_MASK)
+ {
+ case NO_MATCH:
+ {
+ aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
+ SCSISEQ);
+ printk(WARN_LEAD "No active SCB for reconnecting target - Issuing "
+ "BUS DEVICE RESET.\n", p->host_no, channel, target, lun);
+ printk(WARN_LEAD " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
+ p->host_no, channel, target, lun,
+ aic_inb(p, SAVED_TCL), aic_inb(p, ARG_1),
+ (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
+ }
+ break;
+
+ case SEND_REJECT:
+ {
+ if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+ printk(INFO_LEAD "Rejecting unknown message (0x%x) received from "
+ "target, SEQ_FLAGS=0x%x\n", p->host_no, channel, target, lun,
+ aic_inb(p, ACCUM), aic_inb(p, SEQ_FLAGS));
+ }
+ break;
+
+ case NO_IDENT:
+ {
+ /*
+ * The reconnecting target either did not send an identify
+ * message, or did, but we didn't find an SCB to match and
+ * before it could respond to our ATN/abort, it hit a dataphase.
+ * The only safe thing to do is to blow it away with a bus
+ * reset.
+ */
+ if (aic7xxx_verbose & (VERBOSE_SEQINT | VERBOSE_RESET_MID))
+ printk(INFO_LEAD "Target did not send an IDENTIFY message; "
+ "LASTPHASE 0x%x, SAVED_TCL 0x%x\n", p->host_no, channel, target,
+ lun, aic_inb(p, LASTPHASE), aic_inb(p, SAVED_TCL));
+
+ aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
+ aic7xxx_run_done_queue(p, FALSE);
+
+ }
+ break;
+
+ case BAD_PHASE:
+ if (aic_inb(p, LASTPHASE) == P_BUSFREE)
+ {
+ if (aic7xxx_verbose & VERBOSE_SEQINT)
+ printk(INFO_LEAD "Missed busfree.\n", p->host_no, channel,
+ target, lun);
+ restart_sequencer(p);
+ }
+ else
+ {
+ if (aic7xxx_verbose & VERBOSE_SEQINT)
+ printk(INFO_LEAD "Unknown scsi bus phase, continuing\n", p->host_no,
+ channel, target, lun);
+ }
+ break;
+
+ case EXTENDED_MSG:
+ {
+ p->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+ p->msg_len = 0;
+ p->msg_index = 0;
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Enabling REQINITs for MSG_IN\n", p->host_no,
+ channel, target, lun);
+#endif
+
+ /*
+ * To actually receive the message, simply turn on
+ * REQINIT interrupts and let our interrupt handler
+ * do the rest (REQINIT should already be true).
+ */
+ p->flags |= AHC_HANDLING_REQINITS;
+ aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
+
+ /*
+ * We don't want the sequencer unpaused yet so we return early
+ */
+ return;
+ }
+
+ case REJECT_MSG:
+ {
+ /*
+ * What we care about here is if we had an outstanding SDTR
+ * or WDTR message for this target. If we did, this is a
+ * signal that the target is refusing negotiation.
+ */
+ unsigned char scb_index;
+ unsigned char last_msg;
+
+ scb_index = aic_inb(p, SCB_TAG);
+ scb = p->scb_data->scb_array[scb_index];
+ last_msg = aic_inb(p, LAST_MSG);
+
+ if ( (last_msg == MSG_IDENTIFYFLAG) &&
+ (scb->tag_action) &&
+ !(scb->flags & SCB_MSGOUT_BITS) )
+ {
+ if ((scb->tag_action == MSG_ORDERED_Q_TAG) &&
+ (p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS))
+ {
+ /*
+ * OK...the device seems able to accept tagged commands, but
+ * not ordered tag commands, only simple tag commands. So, we
+ * disable ordered tag commands and go on with life just like
+ * normal.
+ */
+ p->orderedtag &= ~target_mask;
+ scb->tag_action = MSG_SIMPLE_Q_TAG;
+ scb->hscb->control &= ~SCB_TAG_TYPE;
+ scb->hscb->control |= MSG_SIMPLE_Q_TAG;
+ aic_outb(p, scb->hscb->control, SCB_CONTROL);
+ /*
+ * OK..we set the tag type to simple tag command, now we re-assert
+ * ATNO and hope this will take us into the identify phase again
+ * so we can resend the tag type and info to the device.
+ */
+ aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+ }
+ else if ( (scb->tag_action == MSG_SIMPLE_Q_TAG) &&
+ !(p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS) )
+ {
+ unsigned char i, reset = 0;
+ struct aic7xxx_scb *scbp;
+ int old_verbose;
+ /*
+ * Hmmmm....the device is flaking out on tagged commands. The
+ * bad thing is that we already have tagged commands enabled in
+ * the device struct in the mid level code. We also have a queue
+ * set according to the tagged queue depth. Gonna have to live
+ * with it by controlling our queue depth internally and making
+ * sure we don't set the tagged command flag any more.
+ */
+ p->tagenable &= ~target_mask;
+ p->orderedtag &= ~target_mask;
+ p->dev_max_queue_depth[tindex] =
+ p->dev_temp_queue_depth[tindex] = 1;
+ /*
+ * We set this command up as a bus device reset. However, we have
+ * to clear the tag type as it's causing us problems. We shouldnt
+ * have to worry about any other commands being active, since if
+ * the device is refusing tagged commands, this should be the
+ * first tagged command sent to the device, however, we do have
+ * to worry about any other tagged commands that may already be
+ * in the qinfifo. The easiest way to do this, is to issue a BDR,
+ * send all the commands back to the mid level code, then let them
+ * come back and get rebuilt as untagged commands.
+ */
+ scb->tag_action = 0;
+ scb->hscb->control &= ~(TAG_ENB | SCB_TAG_TYPE);
+ aic_outb(p, scb->hscb->control, SCB_CONTROL);
+
+ old_verbose = aic7xxx_verbose;
+ aic7xxx_verbose &= ~(VERBOSE_RESET|VERBOSE_ABORT);
+ for (i=0; i!=p->scb_data->numscbs; i++)
+ {
+ scbp = p->scb_data->scb_array[i];
+ if ((scbp->flags & SCB_ACTIVE) && (scbp != scb))
+ {
+ if (aic7xxx_match_scb(p, scbp, target, channel, lun, i))
+ {
+ aic7xxx_reset_device(p, target, channel, lun, i);
+ reset++;
+ }
+ aic7xxx_run_done_queue(p, FALSE);
+ }
+ }
+ aic7xxx_verbose = old_verbose;
+ /*
+ * Wait until after the for loop to set the busy index since
+ * aic7xxx_reset_device will clear the busy index during its
+ * operation.
+ */
+ aic7xxx_busy_target(p, scb);
+ printk(INFO_LEAD "Device is refusing tagged commands, using "
+ "untagged I/O.\n", p->host_no, channel, target, lun);
+ aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+ }
+ }
+ else if (scb->flags & SCB_MSGOUT_WDTR)
+ {
+ /*
+ * note 8bit xfers and clear flag
+ */
+ p->needwdtr &= ~target_mask;
+ p->needwdtr_copy &= ~target_mask;
+ p->wdtr_pending &= ~target_mask;
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
+ (AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR));
+ aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+ AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
+ if ( (p->needsdtr_copy & target_mask) &&
+ !(p->sdtr_pending & target_mask) )
+ {
+ p->sdtr_pending |= target_mask;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ }
+ }
+ else if (scb->flags & SCB_MSGOUT_SDTR)
+ {
+ /*
+ * note asynch xfers and clear flag
+ */
+ p->needsdtr &= ~target_mask;
+ p->needsdtr_copy &= ~target_mask;
+ p->sdtr_pending &= ~target_mask;
+ scb->flags &= ~SCB_MSGOUT_SDTR;
+ aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+ (AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL));
+ }
+ else if (aic7xxx_verbose & VERBOSE_SEQINT)
+ {
+ /*
+ * Otherwise, we ignore it.
+ */
+ printk(INFO_LEAD "Received MESSAGE_REJECT for unknown cause. "
+ "Ignoring.\n", p->host_no, channel, target, lun);
+ }
+ }
+ break;
+
+ case BAD_STATUS:
+ {
+ unsigned char scb_index;
+ struct aic7xxx_hwscb *hscb;
+ Scsi_Cmnd *cmd;
+
+ /* The sequencer will notify us when a command has an error that
+ * would be of interest to the kernel. This allows us to leave
+ * the sequencer running in the common case of command completes
+ * without error. The sequencer will have DMA'd the SCB back
+ * up to us, so we can reference the drivers SCB array.
+ *
+ * Set the default return value to 0 indicating not to send
+ * sense. The sense code will change this if needed and this
+ * reduces code duplication.
+ */
+ aic_outb(p, 0, RETURN_1);
+ scb_index = aic_inb(p, SCB_TAG);
+ if (scb_index > p->scb_data->numscbs)
+ {
+ printk(WARN_LEAD "Invalid SCB during SEQINT 0x%02x, SCB_TAG %d.\n",
+ p->host_no, channel, target, lun, intstat, scb_index);
+ break;
+ }
+ scb = p->scb_data->scb_array[scb_index];
+ hscb = scb->hscb;
+
+ if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk(WARN_LEAD "Invalid SCB during SEQINT 0x%x, scb %d, flags 0x%x,"
+ " cmd 0x%lx.\n", p->host_no, channel, target, lun, intstat,
+ scb_index, scb->flags, (unsigned long) scb->cmd);
+ }
+ else
+ {
+ cmd = scb->cmd;
+ hscb->target_status = aic_inb(p, SCB_TARGET_STATUS);
+ aic7xxx_status(cmd) = hscb->target_status;
+
+ cmd->result = hscb->target_status;
+
+ switch (status_byte(hscb->target_status))
+ {
+ case GOOD:
+ if (aic7xxx_verbose & VERBOSE_SEQINT)
+ printk(INFO_LEAD "Interrupted for status of GOOD???\n",
+ p->host_no, CTL_OF_SCB(scb));
+ break;
+
+ case COMMAND_TERMINATED:
+ case CHECK_CONDITION:
+ if ( !(scb->flags & SCB_SENSE) )
+ {
+ /*
+ * XXX - How do we save the residual (if there is one).
+ */
+ if ( hscb->residual_SG_segment_count != 0 )
+ aic7xxx_calculate_residual(p, scb);
+
+ /*
+ * Send a sense command to the requesting target.
+ * XXX - revisit this and get rid of the memcopys.
+ */
+ memcpy(&scb->sense_cmd[0], &generic_sense[0],
+ sizeof(generic_sense));
+
+ scb->sense_cmd[1] = (cmd->lun << 5);
+ scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
+
+ scb->sg_list[0].address =
+ cpu_to_le32(VIRT_TO_BUS(&cmd->sense_buffer[0]));
+ scb->sg_list[0].length =
+ cpu_to_le32(sizeof(cmd->sense_buffer));
+
+ /*
+ * XXX - We should allow disconnection, but can't as it
+ * might allow overlapped tagged commands.
+ */
+ /* hscb->control &= DISCENB; */
+ hscb->control = 0;
+ hscb->target_status = 0;
+ hscb->SG_list_pointer =
+ cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[0]));
+ hscb->data_pointer = scb->sg_list[0].address;
+ hscb->data_count = scb->sg_list[0].length;
+ hscb->SCSI_cmd_pointer =
+ cpu_to_le32(VIRT_TO_BUS(&scb->sense_cmd[0]));
+ hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
+ hscb->residual_SG_segment_count = 0;
+ hscb->residual_data_count[0] = 0;
+ hscb->residual_data_count[1] = 0;
+ hscb->residual_data_count[2] = 0;
+
+ scb->sg_count = hscb->SG_segment_count = 1;
+ scb->sg_length = sizeof(cmd->sense_buffer);
+ scb->tag_action = 0;
+ /*
+ * This problem could be caused if the target has lost power
+ * or found some other way to loose the negotiation settings,
+ * so if needed, we'll re-negotiate while doing the sense cmd.
+ * However, if this SCB already was attempting to negotiate,
+ * then we assume this isn't the problem and skip this part.
+ */
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+ if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) &&
+ (p->dev_flags[tindex] & DEVICE_SCANNED) &&
+ !(p->wdtr_pending & target_mask) &&
+ !(p->sdtr_pending & target_mask) )
+ {
+ p->needwdtr |= (p->needwdtr_copy & target_mask);
+ p->needsdtr |= (p->needsdtr_copy & target_mask);
+ }
+ else if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) ||
+ (scb->cmd == p->dev_sdtr_cmnd[tindex]) )
+ {
+ /*
+ * This is already a negotiation command, so we must have
+ * already done either WDTR or SDTR (or maybe both). So
+ * we simply check sdtr_pending and needsdtr to see if we
+ * should throw out SDTR on this command.
+ *
+ * Note: Don't check the needsdtr_copy here, instead just
+ * check to see if WDTR wiped out our SDTR and set needsdtr.
+ * Even if WDTR did wipe out SDTR and set needsdtr, if
+ * parse_msg() then turned around and started our SDTR
+ * in back to back fasion, then conclusion of that should
+ * have negated any needsdtr setting. That's why we only
+ * check needsdtr and sdtr_pending.
+ */
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) &&
+ !(p->sdtr_pending & target_mask) &&
+ (p->needsdtr & target_mask) )
+ {
+ p->sdtr_pending |= target_mask;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ }
+
+ /*
+ * This is the important part though. We are getting sense
+ * info back from this device. It's going into a fake
+ * command. We need to put that into the real command
+ * instead so that the mid level SCSI code can act upon it.
+ * So, when we set up these fake commands, the next pointer
+ * is used to point to the real command. Use that to change
+ * the address of our sense_buffer[] to the real command.
+ * However, don't do this if the real command is also a
+ * TEST_UNIT_READY as it will most likely pull down its own
+ * SENSE information anyway.
+ */
+ if (cmd->next->cmnd[0] != TEST_UNIT_READY)
+ {
+ scb->sg_list[0].address =
+ cpu_to_le32(VIRT_TO_BUS(&cmd->next->sense_buffer[0]));
+ hscb->data_pointer = scb->sg_list[0].address;
+ }
+ }
+#else
+ if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) &&
+ !(scb->flags & SCB_MSGOUT_BITS) &&
+ (scb->cmd->lun == 0) &&
+ (p->dev_flags[TARGET_INDEX(scb->cmd)] & DEVICE_SCANNED) )
+ {
+ if ( (p->needwdtr_copy & target_mask) &&
+ !(p->wdtr_pending & target_mask) &&
+ !(p->sdtr_pending & target_mask) )
+ {
+ p->needwdtr |= target_mask;
+ p->wdtr_pending |= target_mask;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_WDTR;
+ }
+ if ( p->needsdtr_copy & target_mask )
+ {
+ p->needsdtr |= target_mask;
+ if ( !(p->wdtr_pending & target_mask) &&
+ !(p->sdtr_pending & target_mask) )
+ {
+ p->sdtr_pending |= target_mask;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ }
+ }
+ }
+ else
+ scb->flags &= ~SCB_MSGOUT_BITS;
+#endif /* AIC7XXX_FAKE_NEGOTIATION_CMDS */
+ scb->flags |= SCB_SENSE;
+ /*
+ * Ensure the target is busy since this will be an
+ * an untagged request.
+ */
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ {
+ if (scb->flags & SCB_MSGOUT_BITS)
+ printk(INFO_LEAD "Requesting SENSE with %s\n", p->host_no,
+ CTL_OF_SCB(scb), (scb->flags & SCB_MSGOUT_SDTR) ?
+ "SDTR" : "WDTR");
+ else
+ printk(INFO_LEAD "Requesting SENSE, no MSG\n", p->host_no,
+ CTL_OF_SCB(scb));
+ }
+#endif
+ aic7xxx_busy_target(p, scb);
+ aic_outb(p, SEND_SENSE, RETURN_1);
+ aic7xxx_error(cmd) = DID_OK;
+ break;
+ } /* first time sense, no errors */
+ aic7xxx_error(cmd) = DID_OK;
+ scb->flags &= ~SCB_SENSE;
+ break;
+
+ case QUEUE_FULL:
+ queue_flag = TRUE; /* Mark that this is a QUEUE_FULL and */
+ case BUSY: /* drop through to here */
+ {
+ struct aic7xxx_scb *next_scbp, *prev_scbp;
+ unsigned char active_hscb, next_hscb, prev_hscb, scb_index;
+ /*
+ * We have to look three places for queued commands:
+ * 1: QINFIFO
+ * 2: p->waiting_scbs queue
+ * 3: WAITING_SCBS list on card (for commands that are started
+ * but haven't yet made it to the device)
+ */
+ aic7xxx_search_qinfifo(p, target, channel, lun,
+ SCB_LIST_NULL, 0, TRUE,
+ &p->delayed_scbs[tindex]);
+ next_scbp = p->waiting_scbs.head;
+ while ( next_scbp != NULL )
+ {
+ prev_scbp = next_scbp;
+ next_scbp = next_scbp->q_next;
+ if ( aic7xxx_match_scb(p, prev_scbp, target, channel, lun,
+ SCB_LIST_NULL) )
+ {
+ scbq_remove(&p->waiting_scbs, prev_scbp);
+ scbq_insert_tail(&p->delayed_scbs[tindex],
+ prev_scbp);
+ }
+ }
+ next_scbp = NULL;
+ active_hscb = aic_inb(p, SCBPTR);
+ prev_hscb = next_hscb = scb_index = SCB_LIST_NULL;
+ next_hscb = aic_inb(p, WAITING_SCBH);
+ while (next_hscb != SCB_LIST_NULL)
+ {
+ aic_outb(p, next_hscb, SCBPTR);
+ scb_index = aic_inb(p, SCB_TAG);
+ if (scb_index < p->scb_data->numscbs)
+ {
+ next_scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(p, next_scbp, target, channel, lun,
+ SCB_LIST_NULL) )
+ {
+ if (next_scbp->flags & SCB_WAITINGQ)
+ {
+ p->dev_active_cmds[tindex]++;
+ p->activescbs--;
+ scbq_remove(&p->delayed_scbs[tindex], next_scbp);
+ scbq_remove(&p->waiting_scbs, next_scbp);
+ }
+ scbq_insert_head(&p->delayed_scbs[tindex],
+ next_scbp);
+ next_scbp->flags |= SCB_WAITINGQ;
+ p->dev_active_cmds[tindex]--;
+ p->activescbs--;
+ next_hscb = aic_inb(p, SCB_NEXT);
+ aic_outb(p, 0, SCB_CONTROL);
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic7xxx_add_curscb_to_free_list(p);
+ if (prev_hscb == SCB_LIST_NULL)
+ {
+ /* We were first on the list,
+ * so we kill the selection
+ * hardware. Let the sequencer
+ * re-init the hardware itself
+ */
+ aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+ aic_outb(p, CLRSELTIMEO, CLRSINT1);
+ aic_outb(p, next_hscb, WAITING_SCBH);
+ }
+ else
+ {
+ aic_outb(p, prev_hscb, SCBPTR);
+ aic_outb(p, next_hscb, SCB_NEXT);
+ }
+ }
+ else
+ {
+ prev_hscb = next_hscb;
+ next_hscb = aic_inb(p, SCB_NEXT);
+ }
+ } /* scb_index >= p->scb_data->numscbs */
+ }
+ aic_outb(p, active_hscb, SCBPTR);
+ if (scb->flags & SCB_WAITINGQ)
+ {
+ scbq_remove(&p->delayed_scbs[tindex], scb);
+ scbq_remove(&p->waiting_scbs, scb);
+ p->dev_active_cmds[tindex]++;
+ p->activescbs++;
+ }
+ scbq_insert_head(&p->delayed_scbs[tindex], scb);
+ p->dev_active_cmds[tindex]--;
+ p->activescbs--;
+ scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
+
+ if (p->dev_timer[tindex].expires == 0)
+ {
+ if ( p->dev_active_cmds[tindex] )
+ {
+ p->dev_timer[tindex].expires = jiffies + (HZ * 2);
+ add_timer(&p->dev_timer[tindex]);
+ }
+ else
+ {
+ p->dev_timer[tindex].expires = jiffies + (HZ / 2);
+ add_timer(&p->dev_timer[tindex]);
+ }
+ }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+ {
+ if (queue_flag)
+ printk(INFO_LEAD "Queue full received; queue depth %d, "
+ "active %d\n", p->host_no, CTL_OF_SCB(scb),
+ p->dev_max_queue_depth[tindex],
+ p->dev_active_cmds[tindex]);
+ else
+ printk(INFO_LEAD "Target busy\n", p->host_no, CTL_OF_SCB(scb));
+
+ }
+#endif
+ if (queue_flag)
+ {
+ p->dev_temp_queue_depth[tindex] =
+ p->dev_active_cmds[tindex];
+ if ( p->dev_last_queue_full[tindex] !=
+ p->dev_active_cmds[tindex] )
+ {
+ p->dev_last_queue_full[tindex] =
+ p->dev_active_cmds[tindex];
+ p->dev_last_queue_full_count[tindex] = 0;
+ }
+ else
+ {
+ p->dev_last_queue_full_count[tindex]++;
+ }
+ if ( (p->dev_last_queue_full_count[tindex] > 14) &&
+ (p->dev_active_cmds[tindex] > 4) )
+ {
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ printk(INFO_LEAD "Queue depth reduced to %d\n", p->host_no,
+ CTL_OF_SCB(scb), p->dev_active_cmds[tindex]);
+ p->dev_max_queue_depth[tindex] =
+ p->dev_active_cmds[tindex];
+ p->dev_last_queue_full[tindex] = 0;
+ p->dev_last_queue_full_count[tindex] = 0;
+ }
+ }
+ break;
+ }
+
+ default:
+ if (aic7xxx_verbose & VERBOSE_SEQINT)
+ printk(INFO_LEAD "Unexpected target status 0x%x.\n", p->host_no,
+ CTL_OF_SCB(scb), scb->hscb->target_status);
+ if (!aic7xxx_error(cmd))
+ {
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
+ break;
+ } /* end switch */
+ } /* end else of */
+ }
+ break;
+
+ case AWAITING_MSG:
+ {
+ unsigned char scb_index, msg_out;
+
+ scb_index = aic_inb(p, SCB_TAG);
+ msg_out = aic_inb(p, MSG_OUT);
+ scb = p->scb_data->scb_array[scb_index];
+ p->msg_index = p->msg_len = 0;
+ /*
+ * This SCB had a MK_MESSAGE set in its control byte informing
+ * the sequencer that we wanted to send a special message to
+ * this target.
+ */
+
+ if ( !(scb->flags & SCB_DEVICE_RESET) &&
+ (aic_inb(p, MSG_OUT) == MSG_IDENTIFYFLAG) &&
+ (scb->hscb->control & TAG_ENB) )
+ {
+ p->msg_buf[p->msg_index++] = scb->tag_action;
+ p->msg_buf[p->msg_index++] = scb->hscb->tag;
+ p->msg_len += 2;
+ }
+
+ if (scb->flags & SCB_DEVICE_RESET)
+ {
+ p->msg_buf[p->msg_index++] = MSG_BUS_DEV_RESET;
+ p->msg_len++;
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Bus device reset mailed.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ }
+ else if (scb->flags & SCB_ABORT)
+ {
+ if (scb->tag_action)
+ {
+ p->msg_buf[p->msg_index++] = MSG_ABORT_TAG;
+ }
+ else
+ {
+ p->msg_buf[p->msg_index++] = MSG_ABORT;
+ }
+ p->msg_len++;
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "Abort message mailed.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ }
+ else if (scb->flags & SCB_MSGOUT_WDTR)
+ {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Sending WDTR message.\n", p->host_no,
+ CTL_OF_SCB(scb));
+#endif
+ aic7xxx_construct_wdtr(p,
+ p->transinfo[TARGET_INDEX(scb->cmd)].goal_width);
+ }
+ else if (scb->flags & SCB_MSGOUT_SDTR)
+ {
+ unsigned int max_sync, period;
+ /*
+ * We need to set an accurate goal_offset instead of
+ * the ridiculously high one we default to. We should
+ * now know if we are wide. Plus, the WDTR code will
+ * set our goal_offset for us as well.
+ */
+ if (p->transinfo[tindex].goal_offset)
+ {
+ if (p->features & AHC_ULTRA2)
+ p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+ else if (p->transinfo[tindex].cur_width == MSG_EXT_WDTR_BUS_16_BIT)
+ p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+ else
+ p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+ }
+ /*
+ * Now that the device is selected, use the bits in SBLKCTL and
+ * SSTAT2 to determine the max sync rate for this device.
+ */
+ if (p->features & AHC_ULTRA2)
+ {
+ if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+ !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+ {
+ max_sync = AHC_SYNCRATE_ULTRA2;
+ }
+ else
+ {
+ max_sync = AHC_SYNCRATE_ULTRA;
+ }
+ }
+ else if (p->features & AHC_ULTRA)
+ {
+ max_sync = AHC_SYNCRATE_ULTRA;
+ }
+ else
+ {
+ max_sync = AHC_SYNCRATE_FAST;
+ }
+ period = p->transinfo[tindex].goal_period;
+ aic7xxx_find_syncrate(p, &period, max_sync);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no,
+ CTL_OF_SCB(scb),
+ p->transinfo[tindex].goal_period,
+ p->transinfo[tindex].goal_offset);
+#endif
+ aic7xxx_construct_sdtr(p, period,
+ p->transinfo[tindex].goal_offset);
+ }
+ else
+ {
+ sti();
+ panic("aic7xxx: AWAITING_MSG for an SCB that does "
+ "not have a waiting message.\n");
+ }
+ /*
+ * We've set everything up to send our message, now to actually do
+ * so we need to enable reqinit interrupts and let the interrupt
+ * handler do the rest. We don't want to unpause the sequencer yet
+ * though so we'll return early. We also have to make sure that
+ * we clear the SEQINT *BEFORE* we set the REQINIT handler active
+ * or else it's possible on VLB cards to loose the first REQINIT
+ * interrupt. Edge triggered EISA cards could also loose this
+ * interrupt, although PCI and level triggered cards should not
+ * have this problem since they continually interrupt the kernel
+ * until we take care of the situation.
+ */
+ scb->flags |= SCB_MSGOUT_SENT;
+ p->msg_index = 0;
+ p->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ p->flags |= AHC_HANDLING_REQINITS;
+ aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
+ return;
+ }
+ break;
+
+ case DATA_OVERRUN:
+ {
+ unsigned char scb_index = aic_inb(p, SCB_TAG);
+ unsigned char lastphase = aic_inb(p, LASTPHASE);
+ unsigned int i;
+
+ scb = (p->scb_data->scb_array[scb_index]);
+ /*
+ * XXX - What do we really want to do on an overrun? The
+ * mid-level SCSI code should handle this, but for now,
+ * we'll just indicate that the command should retried.
+ * If we retrieved sense info on this target, then the
+ * base SENSE info should have been saved prior to the
+ * overrun error. In that case, we return DID_OK and let
+ * the mid level code pick up on the sense info. Otherwise
+ * we return DID_ERROR so the command will get retried.
+ */
+ if ( !(scb->flags & SCB_SENSE) )
+ {
+ printk(WARN_LEAD "Data overrun detected in %s phase, tag %d;\n",
+ p->host_no, CTL_OF_SCB(scb),
+ (lastphase == P_DATAIN) ? "Data-In" : "Data-Out", scb->hscb->tag);
+ printk(KERN_WARNING " %s seen Data Phase. Length=%d, NumSGs=%d.\n",
+ (aic_inb(p, SEQ_FLAGS) & DPHASE) ? "Have" : "Haven't",
+ scb->sg_length, scb->sg_count);
+ for (i = 0; i < scb->sg_count; i++)
+ {
+ printk(KERN_WARNING " sg[%d] - Addr 0x%x : Length %d\n",
+ i,
+ le32_to_cpu(scb->sg_list[i].address),
+ le32_to_cpu(scb->sg_list[i].length) );
+ }
+ aic7xxx_error(scb->cmd) = DID_ERROR;
+ }
+ else
+ printk(INFO_LEAD "Data Overrun during SEND_SENSE operation.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ }
+ break;
+
+#if AIC7XXX_NOT_YET
+ case TRACEPOINT:
+ {
+ printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no, channel,
+ target, lun);
+ }
+ break;
+
+ case TRACEPOINT2:
+ {
+ printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no, channel,
+ target, lun);
+ }
+ break;
+
+ /* XXX Fill these in later */
+ case MSG_BUFFER_BUSY:
+ printk("aic7xxx: Message buffer busy.\n");
+ break;
+ case MSGIN_PHASEMIS:
+ printk("aic7xxx: Message-in phasemis.\n");
+ break;
+#endif
+
+ default: /* unknown */
+ printk(WARN_LEAD "Unknown SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
+ p->host_no, channel, target, lun, intstat,
+ aic_inb(p, SCSISIGI));
+ break;
+ }
+
+ /*
+ * Clear the sequencer interrupt and unpause the sequencer.
+ */
+ unpause_sequencer(p, /* unpause always */ TRUE);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_parse_msg
+ *
+ * Description:
+ * Parses incoming messages into actions on behalf of
+ * aic7xxx_handle_reqinit
+ *_F*************************************************************************/
+static int
+aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ int reject, reply, done;
+ unsigned char target_scsirate, tindex;
+ unsigned short target_mask;
+ unsigned char target, channel, lun;
+
+ target = scb->cmd->target;
+ channel = scb->cmd->channel;
+ lun = scb->cmd->lun;
+ reply = reject = done = FALSE;
+ tindex = TARGET_INDEX(scb->cmd);
+ target_scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+ target_mask = (0x01 << tindex);
+
+ /*
+ * Parse as much of the message as is availible,
+ * rejecting it if we don't support it. When
+ * the entire message is availible and has been
+ * handled, return TRUE indicating that we have
+ * parsed an entire message.
+ */
+
+ if (p->msg_buf[0] != MSG_EXTENDED)
+ {
+ reject = TRUE;
+ }
+
+ /*
+ * Just accept the length byte outright and perform
+ * more checking once we know the message type.
+ */
+
+ if ( !reject && (p->msg_len > 2) )
+ {
+ switch(p->msg_buf[2])
+ {
+ case MSG_EXT_SDTR:
+ {
+ unsigned int period, offset;
+ unsigned char maxsync, saved_offset;
+ struct aic7xxx_syncrate *syncrate;
+
+ if (p->msg_buf[1] != MSG_EXT_SDTR_LEN)
+ {
+ reject = TRUE;
+ break;
+ }
+
+ if (p->msg_len < (MSG_EXT_SDTR_LEN + 2))
+ {
+ break;
+ }
+
+ period = p->msg_buf[3];
+ saved_offset = offset = p->msg_buf[4];
+
+ if (p->features & AHC_ULTRA2)
+ {
+ if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+ !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+ {
+ maxsync = AHC_SYNCRATE_ULTRA2;
+ }
+ else
+ {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ }
+ }
+ else if (p->features & AHC_ULTRA)
+ {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ }
+ else
+ {
+ maxsync = AHC_SYNCRATE_FAST;
+ }
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ {
+ printk(INFO_LEAD "Finished receipt of SDTR, parsing %d/%d\n",
+ p->host_no, CTL_OF_SCB(scb), period, offset);
+ syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
+ printk(INFO_LEAD "After find_syncrate() %d/%d\n",
+ p->host_no, CTL_OF_SCB(scb), period, offset);
+ aic7xxx_validate_offset(p, syncrate, &offset,
+ target_scsirate & WIDEXFER);
+ printk(INFO_LEAD "After validate_offset() %d/%d\n",
+ p->host_no, CTL_OF_SCB(scb), period, offset);
+ aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+ offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+ printk(INFO_LEAD "Final values of Period/Offset as set: %d/%d\n",
+ p->host_no, CTL_OF_SCB(scb), period, offset);
+ }
+ else
+ {
+ syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
+ aic7xxx_validate_offset(p, syncrate, &offset,
+ target_scsirate & WIDEXFER);
+ aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+ offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+ }
+#else
+ syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
+ aic7xxx_validate_offset(p, syncrate, &offset,
+ target_scsirate & WIDEXFER);
+ aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+ offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+#endif
+
+ if (offset == 0)
+ {
+ /*
+ * Uhh ohh, things fell through to async....update the goal
+ * items and the needsdtr_copy to reflect this...
+ */
+ aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+ offset, AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+ p->needsdtr_copy &= ~target_mask;
+ }
+ /*
+ * Did we start this, if not, or if we went to low and had to
+ * go async, then send an SDTR back to the target
+ */
+ p->needsdtr &= ~target_mask;
+ p->sdtr_pending &= ~target_mask;
+ if ( ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) ==
+ (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) &&
+ (offset == saved_offset) )
+ {
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ }
+ else
+ {
+ /*
+ * Send a reply SDTR back. Even if we sent the first one, it
+ * is valid to send another one out immediately to re-negotiate
+ * things, and a few devices don't like getting rejects after
+ * we already sent them one SDTR. Just send an SDTR for async
+ * this time if need be (or for the correct params if we didn't
+ * start all of this). If this is a Reject Reply type message,
+ * then we've put the async settings into the goal area for
+ * future reference (when we get the AWAITING_MSG interrupt).
+ * If this is a case where we are responding to the target's
+ * initiated SDTR, then leave our own goal and user values in
+ * place (unless the device hasn't been scanned yet, in which
+ * case, put the user values into the goal values so we don't
+ * send out an Async message).
+ */
+ if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) )
+ {
+ p->transinfo[tindex].goal_width =
+ p->transinfo[tindex].user_width;
+ p->transinfo[tindex].goal_period =
+ p->transinfo[tindex].user_period;
+ p->transinfo[tindex].goal_offset =
+ p->transinfo[tindex].user_offset;
+ p->needwdtr_copy |= target_mask;
+ p->needsdtr_copy |= target_mask;
+ }
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ }
+ done = TRUE;
+ break;
+ }
+ case MSG_EXT_WDTR:
+ {
+ unsigned char bus_width;
+
+ if (p->msg_buf[1] != MSG_EXT_WDTR_LEN)
+ {
+ reject = TRUE;
+ break;
+ }
+
+ if (p->msg_len < (MSG_EXT_WDTR_LEN + 2))
+ {
+ break;
+ }
+
+ bus_width = p->msg_buf[3];
+ if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR)) ==
+ (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR) )
+ {
+ switch(bus_width)
+ {
+ default:
+ {
+ reject = TRUE;
+ if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+ ((p->dev_flags[tindex] & DEVICE_PRINT_WDTR) ||
+ (aic7xxx_verbose > 0xffff)) )
+ {
+ printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
+ p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
+ p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
+ }
+ } /* We fall through on purpose */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ {
+ bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ p->needwdtr_copy &= ~target_mask;
+ break;
+ }
+ case MSG_EXT_WDTR_BUS_16_BIT:
+ {
+ break;
+ }
+ }
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ p->wdtr_pending &= ~target_mask;
+ p->needwdtr &= ~target_mask;
+ }
+ else
+ {
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ scb->flags |= SCB_MSGOUT_WDTR;
+ reply = TRUE;
+ if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) )
+ {
+ /*
+ * Well, we now know the WDTR and SYNC caps of this device since
+ * it contacted us first, mark it as such and copy the user stuff
+ * over to the goal stuff.
+ */
+ p->transinfo[tindex].goal_width =
+ p->transinfo[tindex].user_width;
+ p->transinfo[tindex].goal_period =
+ p->transinfo[tindex].user_period;
+ p->transinfo[tindex].goal_offset =
+ p->transinfo[tindex].user_offset;
+ p->needwdtr_copy |= target_mask;
+ p->needsdtr_copy |= target_mask;
+ }
+ switch(bus_width)
+ {
+ default:
+ {
+ if ( (p->features & AHC_WIDE) &&
+ (p->transinfo[tindex].goal_width ==
+ MSG_EXT_WDTR_BUS_16_BIT) )
+ {
+ bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ break;
+ }
+ } /* Fall through if we aren't a wide card */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ {
+ p->needwdtr_copy &= ~target_mask;
+ bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ break;
+ }
+ }
+ p->needwdtr &= ~target_mask;
+ p->wdtr_pending &= ~target_mask;
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ }
+ aic7xxx_set_width(p, target, channel, lun, bus_width,
+ AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+
+ /*
+ * By virtue of the SCSI spec, a WDTR message negates any existing
+ * SDTR negotiations. So, even if needsdtr isn't marked for this
+ * device, we still have to do a new SDTR message if the device
+ * supports SDTR at all. Therefore, we check needsdtr_copy instead
+ * of needstr.
+ */
+ aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+ AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
+ if ( (p->needsdtr_copy & target_mask) &&
+ !(p->sdtr_pending & target_mask))
+ {
+ p->needsdtr |= target_mask;
+ if ( !reject && !reply )
+ {
+ scb->flags &= ~SCB_MSGOUT_WDTR;
+ if (p->transinfo[tindex].goal_period)
+ {
+ p->sdtr_pending |= target_mask;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ }
+ }
+ }
+ done = TRUE;
+ break;
+ }
+ default:
+ {
+ reject = TRUE;
+ break;
+ }
+ } /* end of switch(p->msg_type) */
+ } /* end of if (!reject && (p->msg_len > 2)) */
+
+ if (reject)
+ {
+ aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT);
+ aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+ done = TRUE;
+ }
+ return(done);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_reqinit
+ *
+ * Description:
+ * Interrupt handler for REQINIT interrupts (used to transfer messages to
+ * and from devices).
+ *_F*************************************************************************/
+static void
+aic7xxx_handle_reqinit(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ unsigned char lastbyte;
+ unsigned char phasemis;
+ int done = FALSE;
+
+ switch(p->msg_type)
+ {
+ case MSG_TYPE_INITIATOR_MSGOUT:
+ {
+ if (p->msg_len == 0)
+ panic("aic7xxx: REQINIT with no active message!\n");
+
+ lastbyte = (p->msg_index == (p->msg_len - 1));
+ phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK) != P_MESGOUT;
+
+ if (lastbyte || phasemis)
+ {
+ /* Time to end the message */
+ p->msg_len = 0;
+ p->msg_type = MSG_TYPE_NONE;
+ /*
+ * NOTE-TO-MYSELF: If you clear the REQINIT after you
+ * disable REQINITs, then cases of REJECT_MSG stop working
+ * and hang the bus
+ */
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+
+ if (phasemis == 0)
+ {
+ aic_outb(p, p->msg_buf[p->msg_index], SINDEX);
+ aic_outb(p, 0, RETURN_1);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Completed sending of REQINIT message.\n",
+ p->host_no, CTL_OF_SCB(scb));
+#endif
+ }
+ else
+ {
+ aic_outb(p, MSGOUT_PHASEMIS, RETURN_1);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "PHASEMIS while sending REQINIT message.\n",
+ p->host_no, CTL_OF_SCB(scb));
+#endif
+ }
+ unpause_sequencer(p, TRUE);
+ }
+ else
+ {
+ /*
+ * Present the byte on the bus (clearing REQINIT) but don't
+ * unpause the sequencer.
+ */
+ aic_outb(p, CLRREQINIT, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ aic_outb(p, p->msg_buf[p->msg_index++], SCSIDATL);
+ }
+ break;
+ }
+ case MSG_TYPE_INITIATOR_MSGIN:
+ {
+ phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK ) != P_MESGIN;
+
+ if (phasemis == 0)
+ {
+ p->msg_len++;
+ /* Pull the byte in without acking it */
+ p->msg_buf[p->msg_index] = aic_inb(p, SCSIBUSL);
+ done = aic7xxx_parse_msg(p, scb);
+ /* Ack the byte */
+ aic_outb(p, CLRREQINIT, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ aic_inb(p, SCSIDATL);
+ p->msg_index++;
+ }
+ if (phasemis || done)
+ {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ {
+ if (phasemis)
+ printk(INFO_LEAD "PHASEMIS while receiving REQINIT message.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ else
+ printk(INFO_LEAD "Completed receipt of REQINIT message.\n",
+ p->host_no, CTL_OF_SCB(scb));
+ }
+#endif
+ /* Time to end our message session */
+ p->msg_len = 0;
+ p->msg_type = MSG_TYPE_NONE;
+ aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ unpause_sequencer(p, TRUE);
+ }
+ break;
+ }
+ default:
+ {
+ panic("aic7xxx: Unknown REQINIT message type.\n");
+ break;
+ }
+ } /* End of switch(p->msg_type) */
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_scsiint
+ *
+ * Description:
+ * Interrupt handler for SCSI interrupts (SCSIINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
+{
+ unsigned char scb_index;
+ unsigned char status;
+ struct aic7xxx_scb *scb;
+
+ scb_index = aic_inb(p, SCB_TAG);
+ status = aic_inb(p, SSTAT1);
+
+ if (scb_index < p->scb_data->numscbs)
+ {
+ scb = p->scb_data->scb_array[scb_index];
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ {
+ scb = NULL;
+ }
+ }
+ else
+ {
+ scb = NULL;
+ }
+
+
+ if ((status & SCSIRSTI) != 0)
+ {
+ int channel;
+
+ if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+ channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+ else
+ channel = 0;
+
+ if (aic7xxx_verbose & VERBOSE_RESET)
+ printk(WARN_LEAD "Someone else reset the channel!!\n",
+ p->host_no, channel, -1, -1);
+ /*
+ * Go through and abort all commands for the channel, but do not
+ * reset the channel again.
+ */
+ aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
+ aic7xxx_run_done_queue(p, FALSE);
+ scb = NULL;
+ }
+ else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
+ {
+ /*
+ * First look at what phase we were last in. If it's message-out,
+ * chances are pretty good that the bus free was in response to
+ * one of our abort requests.
+ */
+ unsigned char lastphase = aic_inb(p, LASTPHASE);
+ unsigned char saved_tcl = aic_inb(p, SAVED_TCL);
+ unsigned char target = (saved_tcl >> 4) & 0x0F;
+ int channel;
+ int printerror = TRUE;
+
+ if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+ channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+ else
+ channel = 0;
+
+ aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
+ SCSISEQ);
+ if (lastphase == P_MESGOUT)
+ {
+ unsigned char message;
+
+ message = aic_inb(p, SINDEX);
+
+ if ((message == MSG_ABORT) || (message == MSG_ABORT_TAG))
+ {
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB %d abort delivered.\n", p->host_no,
+ CTL_OF_SCB(scb), scb->hscb->tag);
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS,
+ (message == MSG_ABORT) ? SCB_LIST_NULL : scb->hscb->tag );
+ aic7xxx_run_done_queue(p, FALSE);
+ scb = NULL;
+ printerror = 0;
+ }
+ else if (message == MSG_BUS_DEV_RESET)
+ {
+ aic7xxx_handle_device_reset(p, target, channel);
+ scb = NULL;
+ printerror = 0;
+ }
+ }
+ if (printerror != 0)
+ {
+ if (scb != NULL)
+ {
+ unsigned char tag;
+
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ {
+ tag = scb->hscb->tag;
+ }
+ else
+ {
+ tag = SCB_LIST_NULL;
+ }
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
+ aic7xxx_run_done_queue(p, FALSE);
+ }
+ printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, "
+ "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase,
+ (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
+ scb = NULL;
+ }
+ aic_outb(p, MSG_NOOP, MSG_OUT);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
+ SIMODE1);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ aic_outb(p, CLRBUSFREE, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ restart_sequencer(p);
+ unpause_sequencer(p, TRUE);
+ }
+ else if ((status & SELTO) != 0)
+ {
+ unsigned char scbptr;
+ unsigned char nextscb;
+ Scsi_Cmnd *cmd;
+
+ scbptr = aic_inb(p, WAITING_SCBH);
+ if (scbptr > p->scb_data->maxhscbs)
+ {
+ /*
+ * I'm still trying to track down exactly how this happens, but until
+ * I find it, this code will make sure we aren't passing bogus values
+ * into the SCBPTR register, even if that register will just wrap
+ * things around, we still don't like having out of range variables.
+ *
+ * NOTE: Don't check the aic7xxx_verbose variable, I want this message
+ * to always be displayed.
+ */
+ printk(INFO_LEAD "Invalid WAITING_SCBH value %d, improvising.\n",
+ p->host_no, -1, -1, -1, scbptr);
+ if (p->scb_data->maxhscbs > 4)
+ scbptr &= (p->scb_data->maxhscbs - 1);
+ else
+ scbptr &= 0x03;
+ }
+ aic_outb(p, scbptr, SCBPTR);
+ scb_index = aic_inb(p, SCB_TAG);
+
+ scb = NULL;
+ if (scb_index < p->scb_data->numscbs)
+ {
+ scb = p->scb_data->scb_array[scb_index];
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ {
+ scb = NULL;
+ }
+ }
+ if (scb == NULL)
+ {
+ printk(WARN_LEAD "Referenced SCB %d not valid during SELTO.\n",
+ p->host_no, -1, -1, -1, scb_index);
+ printk(KERN_WARNING " SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x "
+ "SSTAT1 = 0x%x\n", aic_inb(p, SCSISEQ),
+ aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+ aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
+ if (aic7xxx_panic_on_abort)
+ aic7xxx_panic_abort(p, NULL);
+ }
+ else
+ {
+ cmd = scb->cmd;
+ cmd->result = (DID_TIME_OUT << 16);
+
+ /*
+ * Clear out this hardware SCB
+ */
+ aic_outb(p, 0, SCB_CONTROL);
+
+ /*
+ * Clear out a few values in the card that are in an undetermined
+ * state.
+ */
+ aic_outb(p, MSG_NOOP, MSG_OUT);
+
+ /*
+ * Shift the waiting for selection queue forward
+ */
+ nextscb = aic_inb(p, SCB_NEXT);
+ aic_outb(p, nextscb, WAITING_SCBH);
+
+ /*
+ * Put this SCB back on the free list.
+ */
+ aic7xxx_add_curscb_to_free_list(p);
+ /*
+ * XXX - If we queued an abort tag, go clean up the disconnected list.
+ * We know that this particular SCB had to be the queued abort since
+ * the disconnected SCB would have gotten a reconnect instead.
+ * However, if this is an abort command, then DID_TIMEOUT isn't
+ * appropriate, neither is returning the command for that matter.
+ * What we need to do then is to let the command timeout again so
+ * we get a reset since this abort just failed.
+ */
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Selection Timeout.\n", p->host_no, CTL_OF_SCB(scb));
+#endif
+ if (p->flags & SCB_QUEUED_ABORT)
+ {
+ cmd->result = 0;
+ scb->flags &= ~SCB_QUEUED_ABORT;
+ scb = NULL;
+ }
+ }
+ /*
+ * Restarting the sequencer will stop the selection and make sure devices
+ * are allowed to reselect in.
+ */
+ aic_outb(p, 0, SCSISEQ);
+ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ restart_sequencer(p);
+ unpause_sequencer(p, TRUE);
+ }
+ else if (scb == NULL)
+ {
+ printk(WARN_LEAD "aic7xxx_isr - referenced scb not valid "
+ "during scsiint 0x%x scb(%d)\n"
+ " SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n",
+ p->host_no, -1, -1, -1, status, scb_index, aic_inb(p, SIMODE0),
+ aic_inb(p, SIMODE1), aic_inb(p, SSTAT0),
+ (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
+ /*
+ * Turn off the interrupt and set status to zero, so that it
+ * falls through the rest of the SCSIINT code.
+ */
+ aic_outb(p, status, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ unpause_sequencer(p, /* unpause always */ TRUE);
+ scb = NULL;
+ }
+ else if (status & SCSIPERR)
+ {
+ /*
+ * Determine the bus phase and queue an appropriate message.
+ */
+ char *phase;
+ Scsi_Cmnd *cmd;
+ unsigned char mesg_out = MSG_NOOP;
+ unsigned char lastphase = aic_inb(p, LASTPHASE);
+
+ cmd = scb->cmd;
+ switch (lastphase)
+ {
+ case P_DATAOUT:
+ phase = "Data-Out";
+ break;
+ case P_DATAIN:
+ phase = "Data-In";
+ mesg_out = MSG_INITIATOR_DET_ERR;
+ break;
+ case P_COMMAND:
+ phase = "Command";
+ break;
+ case P_MESGOUT:
+ phase = "Message-Out";
+ break;
+ case P_STATUS:
+ phase = "Status";
+ mesg_out = MSG_INITIATOR_DET_ERR;
+ break;
+ case P_MESGIN:
+ phase = "Message-In";
+ mesg_out = MSG_PARITY_ERROR;
+ break;
+ default:
+ phase = "unknown";
+ break;
+ }
+
+ /*
+ * A parity error has occurred during a data
+ * transfer phase. Flag it and continue.
+ */
+ printk(WARN_LEAD "Parity error during %s phase.\n",
+ p->host_no, CTL_OF_SCB(scb), phase);
+
+ /*
+ * We've set the hardware to assert ATN if we get a parity
+ * error on "in" phases, so all we need to do is stuff the
+ * message buffer with the appropriate message. "In" phases
+ * have set mesg_out to something other than MSG_NOP.
+ */
+ if (mesg_out != MSG_NOOP)
+ {
+ aic_outb(p, mesg_out, MSG_OUT);
+ scb = NULL;
+ }
+ aic_outb(p, CLRSCSIPERR, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ }
+ else if ( (status & REQINIT) &&
+ (p->flags & AHC_HANDLING_REQINITS) )
+ {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Handling REQINIT, SSTAT1=0x%x.\n", p->host_no,
+ CTL_OF_SCB(scb), aic_inb(p, SSTAT1));
+#endif
+ aic7xxx_handle_reqinit(p, scb);
+ return;
+ }
+ else
+ {
+ /*
+ * We don't know what's going on. Turn off the
+ * interrupt source and try to continue.
+ */
+ if (aic7xxx_verbose & VERBOSE_SCSIINT)
+ printk(INFO_LEAD "Unknown SCSIINT status, SSTAT1(0x%x).\n",
+ p->host_no, -1, -1, -1, status);
+ aic_outb(p, status, CLRSINT1);
+ aic_outb(p, CLRSCSIINT, CLRINT);
+ unpause_sequencer(p, /* unpause always */ TRUE);
+ scb = NULL;
+ }
+ if (scb != NULL)
+ {
+ aic7xxx_done(p, scb);
+ }
+}
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+static void
+aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer)
+{
+ unsigned char saved_scbptr, free_scbh, dis_scbh, wait_scbh, temp;
+ int i, bogus, lost;
+ static unsigned char scb_status[AIC7XXX_MAXSCB];
+
+#define SCB_NO_LIST 0
+#define SCB_FREE_LIST 1
+#define SCB_WAITING_LIST 2
+#define SCB_DISCONNECTED_LIST 4
+#define SCB_CURRENTLY_ACTIVE 8
+
+ /*
+ * Note, these checks will fail on a regular basis once the machine moves
+ * beyond the bus scan phase. The problem is race conditions concerning
+ * the scbs and where they are linked in. When you have 30 or so commands
+ * outstanding on the bus, and run this twice with every interrupt, the
+ * chances get pretty good that you'll catch the sequencer with an SCB
+ * only partially linked in. Therefore, once we pass the scan phase
+ * of the bus, we really should disable this function.
+ */
+ bogus = FALSE;
+ memset(&scb_status[0], 0, sizeof(scb_status));
+ pause_sequencer(p);
+ saved_scbptr = aic_inb(p, SCBPTR);
+ if (saved_scbptr >= p->scb_data->maxhscbs)
+ {
+ printk("Bogus SCBPTR %d\n", saved_scbptr);
+ bogus = TRUE;
+ }
+ scb_status[saved_scbptr] = SCB_CURRENTLY_ACTIVE;
+ free_scbh = aic_inb(p, FREE_SCBH);
+ if ( (free_scbh != SCB_LIST_NULL) &&
+ (free_scbh >= p->scb_data->maxhscbs) )
+ {
+ printk("Bogus FREE_SCBH %d\n", free_scbh);
+ bogus = TRUE;
+ }
+ else
+ {
+ temp = free_scbh;
+ while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+ {
+ if(scb_status[temp] & 0x07)
+ {
+ printk("HSCB %d on multiple lists, status 0x%02x", temp,
+ scb_status[temp] | SCB_FREE_LIST);
+ bogus = TRUE;
+ }
+ scb_status[temp] |= SCB_FREE_LIST;
+ aic_outb(p, temp, SCBPTR);
+ temp = aic_inb(p, SCB_NEXT);
+ }
+ }
+
+ dis_scbh = aic_inb(p, DISCONNECTED_SCBH);
+ if ( (dis_scbh != SCB_LIST_NULL) &&
+ (dis_scbh >= p->scb_data->maxhscbs) )
+ {
+ printk("Bogus DISCONNECTED_SCBH %d\n", dis_scbh);
+ bogus = TRUE;
+ }
+ else
+ {
+ temp = dis_scbh;
+ while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+ {
+ if(scb_status[temp] & 0x07)
+ {
+ printk("HSCB %d on multiple lists, status 0x%02x", temp,
+ scb_status[temp] | SCB_DISCONNECTED_LIST);
+ bogus = TRUE;
+ }
+ scb_status[temp] |= SCB_DISCONNECTED_LIST;
+ aic_outb(p, temp, SCBPTR);
+ temp = aic_inb(p, SCB_NEXT);
+ }
+ }
+
+ wait_scbh = aic_inb(p, WAITING_SCBH);
+ if ( (wait_scbh != SCB_LIST_NULL) &&
+ (wait_scbh >= p->scb_data->maxhscbs) )
+ {
+ printk("Bogus WAITING_SCBH %d\n", wait_scbh);
+ bogus = TRUE;
+ }
+ else
+ {
+ temp = wait_scbh;
+ while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+ {
+ if(scb_status[temp] & 0x07)
+ {
+ printk("HSCB %d on multiple lists, status 0x%02x", temp,
+ scb_status[temp] | SCB_WAITING_LIST);
+ bogus = TRUE;
+ }
+ scb_status[temp] |= SCB_WAITING_LIST;
+ aic_outb(p, temp, SCBPTR);
+ temp = aic_inb(p, SCB_NEXT);
+ }
+ }
+
+ lost=0;
+ for(i=0; i < p->scb_data->maxhscbs; i++)
+ {
+ aic_outb(p, i, SCBPTR);
+ temp = aic_inb(p, SCB_NEXT);
+ if ( ((temp != SCB_LIST_NULL) &&
+ (temp >= p->scb_data->maxhscbs)) )
+ {
+ printk("HSCB %d bad, SCB_NEXT invalid(%d).\n", i, temp);
+ bogus = TRUE;
+ }
+ if ( temp == i )
+ {
+ printk("HSCB %d bad, SCB_NEXT points to self.\n", i);
+ bogus = TRUE;
+ }
+ temp = aic_inb(p, SCB_PREV);
+ if ((temp != SCB_LIST_NULL) &&
+ (temp >= p->scb_data->maxhscbs))
+ {
+ printk("HSCB %d bad, SCB_PREV invalid(%d).\n", i, temp);
+ bogus = TRUE;
+ }
+ if (scb_status[i] == 0)
+ lost++;
+ if (lost > 1)
+ {
+ printk("Too many lost scbs.\n");
+ bogus=TRUE;
+ }
+ }
+ aic_outb(p, saved_scbptr, SCBPTR);
+ unpause_sequencer(p, FALSE);
+ if (bogus)
+ {
+ printk("Bogus parameters found in card SCB array structures.\n");
+ printk("%s\n", buffer);
+ aic7xxx_panic_abort(p, NULL);
+ }
+ return;
+}
+#endif
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_isr
+ *
+ * Description:
+ * SCSI controller interrupt handler.
+ *-F*************************************************************************/
+static void
+aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct aic7xxx_host *p;
+ unsigned char intstat;
+
+ p = (struct aic7xxx_host *)dev_id;
+
+ /*
+ * Just a few sanity checks. Make sure that we have an int pending.
+ * Also, if PCI, then we are going to check for a PCI bus error status
+ * should we get too many spurious interrupts.
+ */
+ if (!((intstat = aic_inb(p, INTSTAT)) & INT_PEND))
+ {
+#ifdef CONFIG_PCI
+ if ( (p->chip & AHC_PCI) && (p->spurious_int > 500) &&
+ !(p->flags & AHC_HANDLING_REQINITS) )
+ {
+ if ( aic_inb(p, ERROR) & PCIERRSTAT )
+ {
+ aic7xxx_pci_intr(p);
+ }
+ p->spurious_int = 0;
+ }
+ else if ( !(p->flags & AHC_HANDLING_REQINITS) )
+ {
+ p->spurious_int++;
+ }
+#endif
+ return;
+ }
+
+ p->spurious_int = 0;
+
+ /*
+ * Keep track of interrupts for /proc/scsi
+ */
+ p->isr_count++;
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
+ (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
+ aic7xxx_check_scbs(p, "Bogus settings at start of interrupt.");
+#endif
+
+ /*
+ * Handle all the interrupt sources - especially for SCSI
+ * interrupts, we won't get a second chance at them.
+ */
+ if (intstat & CMDCMPLT)
+ {
+ struct aic7xxx_scb *scb = NULL;
+ Scsi_Cmnd *cmd;
+ unsigned char scb_index;
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if(aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1);
+#endif
+
+ /*
+ * Clear interrupt status before running the completion loop.
+ * This eliminates a race condition whereby a command could
+ * complete between the last check of qoutfifo and the
+ * CLRCMDINT statement. This would result in us thinking the
+ * qoutfifo was empty when it wasn't, and in actuality be a lost
+ * completion interrupt. With multiple devices or tagged queueing
+ * this could be very bad if we caught all but the last completion
+ * and no more are imediately sent.
+ */
+ aic_outb(p, CLRCMDINT, CLRINT);
+ /*
+ * The sequencer will continue running when it
+ * issues this interrupt. There may be >1 commands
+ * finished, so loop until we've processed them all.
+ */
+
+ while (p->qoutfifo[p->qoutfifonext] != SCB_LIST_NULL)
+ {
+ scb_index = p->qoutfifo[p->qoutfifonext];
+ p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL;
+ if ( scb_index >= p->scb_data->numscbs )
+ scb = NULL;
+ else
+ scb = p->scb_data->scb_array[scb_index];
+ if (scb == NULL)
+ {
+ printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no,
+ -1, -1, -1, scb_index);
+ continue;
+ }
+ else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags "
+ "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags,
+ (unsigned long) scb->cmd);
+ continue;
+ }
+ else if (scb->flags & SCB_QUEUED_ABORT)
+ {
+ pause_sequencer(p);
+ if ( ((aic_inb(p, LASTPHASE) & PHASE_MASK) != P_BUSFREE) &&
+ (aic_inb(p, SCB_TAG) == scb->hscb->tag) )
+ {
+ unpause_sequencer(p, FALSE);
+ continue;
+ }
+ aic7xxx_reset_device(p, scb->cmd->target, scb->cmd->channel,
+ scb->cmd->lun, scb->hscb->tag);
+ scb->flags &= ~(SCB_QUEUED_FOR_DONE | SCB_RESET | SCB_ABORT |
+ SCB_QUEUED_ABORT);
+ unpause_sequencer(p, FALSE);
+ }
+ else if (scb->flags & SCB_ABORT)
+ {
+ /*
+ * We started to abort this, but it completed on us, let it
+ * through as successful
+ */
+ scb->flags &= ~(SCB_ABORT|SCB_RESET);
+ }
+ switch (status_byte(scb->hscb->target_status))
+ {
+ case QUEUE_FULL:
+ case BUSY:
+ scb->hscb->target_status = 0;
+ scb->cmd->result = 0;
+ aic7xxx_error(scb->cmd) = DID_OK;
+ break;
+ default:
+ cmd = scb->cmd;
+ if (scb->hscb->residual_SG_segment_count != 0)
+ {
+ aic7xxx_calculate_residual(p, scb);
+ }
+ cmd->result |= (aic7xxx_error(cmd) << 16);
+ if (scb->tag_action)
+ p->dev_flags[TARGET_INDEX(cmd)] |=
+ DEVICE_TAGGED_SUCCESS | DEVICE_SUCCESS | DEVICE_PRESENT;
+ else
+ p->dev_flags[TARGET_INDEX(cmd)] |=
+ DEVICE_SUCCESS | DEVICE_PRESENT;
+ aic7xxx_done(p, scb);
+ break;
+ }
+ }
+ }
+
+ if (intstat & BRKADRINT)
+ {
+ int i;
+ unsigned char errno = aic_inb(p, ERROR);
+
+ printk(KERN_ERR "(scsi%d) BRKADRINT error(0x%x):\n", p->host_no, errno);
+ for (i = 0; i < NUMBER(hard_error); i++)
+ {
+ if (errno & hard_error[i].errno)
+ {
+ printk(KERN_ERR " %s\n", hard_error[i].errmesg);
+ }
+ }
+ printk(KERN_ERR "(scsi%d) SEQADDR=0x%x\n", p->host_no,
+ (((aic_inb(p, SEQADDR1) << 8) & 0x100) | aic_inb(p, SEQADDR0)));
+ if (aic7xxx_panic_on_abort)
+ aic7xxx_panic_abort(p, NULL);
+#ifdef CONFIG_PCI
+ if (errno & PCIERRSTAT)
+ aic7xxx_pci_intr(p);
+#endif
+ if (errno & (SQPARERR | ILLOPCODE | ILLSADDR))
+ {
+ sti();
+ panic("aic7xxx: unrecoverable BRKADRINT.\n");
+ }
+ if (errno & ILLHADDR)
+ {
+ printk(KERN_ERR "(scsi%d) BUG! Driver accessed chip without first "
+ "pausing controller!\n", p->host_no);
+ }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (errno & DPARERR)
+ {
+ if (aic_inb(p, DMAPARAMS) & DIRECTION)
+ printk("(scsi%d) while DMAing SCB from host to card.\n", p->host_no);
+ else
+ printk("(scsi%d) while DMAing SCB from card to host.\n", p->host_no);
+ }
+#endif
+ aic_outb(p, CLRPARERR | CLRBRKADRINT, CLRINT);
+ unpause_sequencer(p, FALSE);
+ }
+
+ if (intstat & SEQINT)
+ {
+ aic7xxx_handle_seqint(p, intstat);
+ }
+
+ if (intstat & SCSIINT)
+ {
+ aic7xxx_handle_scsiint(p, intstat);
+ }
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
+ (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
+ aic7xxx_check_scbs(p, "Bogus settings at end of interrupt.");
+#endif
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ * do_aic7xxx_isr
+ *
+ * Description:
+ * This is a gross hack to solve a problem in linux kernels 2.1.85 and
+ * above. Please, children, do not try this at home, and if you ever see
+ * anything like it, please inform the Gross Hack Police immediately
+ *-F*************************************************************************/
+static void
+do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long cpu_flags;
+ struct aic7xxx_host *p;
+
+ p = (struct aic7xxx_host *)dev_id;
+ if(!p)
+ return;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,95)
+ spin_lock_irqsave(&io_request_lock, cpu_flags);
+ if(test_and_set_bit(AHC_IN_ISR_BIT, &p->flags))
+ {
+ return;
+ }
+ do
+ {
+ aic7xxx_isr(irq, dev_id, regs);
+ } while ( (aic_inb(p, INTSTAT) & INT_PEND) );
+ aic7xxx_done_cmds_complete(p);
+ aic7xxx_run_waiting_queues(p);
+ clear_bit(AHC_IN_ISR_BIT, &p->flags);
+ spin_unlock_irqrestore(&io_request_lock, cpu_flags);
+#else
+ if(set_bit(AHC_IN_ISR_BIT, (int *)&p->flags))
+ {
+ return;
+ }
+ DRIVER_LOCK
+ do
+ {
+ aic7xxx_isr(irq, dev_id, regs);
+ } while ( (aic_inb(p, INTSTAT) & INT_PEND) );
+ DRIVER_UNLOCK
+ aic7xxx_done_cmds_complete(p);
+ aic7xxx_run_waiting_queues(p);
+ clear_bit(AHC_IN_ISR_BIT, (int *)&p->flags);
+#endif
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_device_queue_depth
+ *
+ * Description:
+ * Determines the queue depth for a given device. There are two ways
+ * a queue depth can be obtained for a tagged queueing device. One
+ * way is the default queue depth which is determined by whether
+ * AIC7XXX_CMDS_PER_LUN is defined. If it is defined, then it is used
+ * as the default queue depth. Otherwise, we use either 4 or 8 as the
+ * default queue depth (dependent on the number of hardware SCBs).
+ * The other way we determine queue depth is through the use of the
+ * aic7xxx_tag_info array which is enabled by defining
+ * AIC7XXX_TAGGED_QUEUEING_BY_DEVICE. This array can be initialized
+ * with queue depths for individual devices. It also allows tagged
+ * queueing to be [en|dis]abled for a specific adapter.
+ *-F*************************************************************************/
+static void
+aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
+{
+ int default_depth = 3;
+ unsigned char tindex;
+ unsigned short target_mask;
+
+ tindex = device->id | (device->channel << 3);
+ target_mask = (1 << tindex);
+
+ device->queue_depth = default_depth;
+ p->dev_mid_level_queue_depth[tindex] = 3;
+ p->dev_temp_queue_depth[tindex] = 1;
+ p->dev_max_queue_depth[tindex] = 1;
+ p->tagenable &= ~target_mask;
+
+ if (device->tagged_supported)
+ {
+ int tag_enabled = TRUE;
+
+#ifdef AIC7XXX_CMDS_PER_LUN
+ default_depth = AIC7XXX_CMDS_PER_LUN;
+#else
+ default_depth = 8; /* Not many SCBs to work with. */
+#endif
+
+ if (!(p->discenable & target_mask))
+ {
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ printk(INFO_LEAD "Disconnection disabled, unable to "
+ "enable tagged queueing.\n",
+ p->host_no, device->channel, device->id, device->lun);
+ }
+ else
+ {
+ if (p->instance >= NUMBER(aic7xxx_tag_info))
+ {
+ static int print_warning = TRUE;
+ if(print_warning)
+ {
+ printk(KERN_INFO "aic7xxx: WARNING, insufficient tag_info instances for"
+ " installed controllers.\n");
+ printk(KERN_INFO "aic7xxx: Please update the aic7xxx_tag_info array in"
+ " the aic7xxx.c source file.\n");
+ print_warning = FALSE;
+ }
+ device->queue_depth = default_depth;
+ }
+ else
+ {
+
+ if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 255)
+ {
+ tag_enabled = FALSE;
+ device->queue_depth = 3; /* Tagged queueing is disabled. */
+ }
+ else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
+ {
+ device->queue_depth = default_depth;
+ }
+ else
+ {
+ device->queue_depth =
+ aic7xxx_tag_info[p->instance].tag_commands[tindex];
+ }
+ }
+ if ((device->tagged_queue == 0) && tag_enabled)
+ {
+ if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+ {
+ printk(INFO_LEAD "Enabled tagged queuing, queue depth %d.\n",
+ p->host_no, device->channel, device->id,
+ device->lun, device->queue_depth);
+ }
+ p->dev_max_queue_depth[tindex] = device->queue_depth;
+ p->dev_temp_queue_depth[tindex] = device->queue_depth;
+ p->dev_mid_level_queue_depth[tindex] = device->queue_depth;
+ p->tagenable |= target_mask;
+ p->orderedtag |= target_mask;
+ device->tagged_queue = 1;
+ device->current_tag = SCB_LIST_NULL;
+ }
+ }
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_select_queue_depth
+ *
+ * Description:
+ * Sets the queue depth for each SCSI device hanging off the input
+ * host adapter. We use a queue depth of 2 for devices that do not
+ * support tagged queueing. If AIC7XXX_CMDS_PER_LUN is defined, we
+ * use that for tagged queueing devices; otherwise we use our own
+ * algorithm for determining the queue depth based on the maximum
+ * SCBs for the controller.
+ *-F*************************************************************************/
+static void
+aic7xxx_select_queue_depth(struct Scsi_Host *host,
+ Scsi_Device *scsi_devs)
+{
+ Scsi_Device *device;
+ struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
+ int scbnum;
+
+ scbnum = 0;
+ for (device = scsi_devs; device != NULL; device = device->next)
+ {
+ if (device->host == host)
+ {
+ aic7xxx_device_queue_depth(p, device);
+ scbnum += device->queue_depth;
+ }
+ }
+ while (scbnum > p->scb_data->numscbs)
+ {
+ /*
+ * Pre-allocate the needed SCBs to get around the possibility of having
+ * to allocate some when memory is more or less exhausted and we need
+ * the SCB in order to perform a swap operation (possible deadlock)
+ */
+ if ( aic7xxx_allocate_scb(p) == 0 )
+ return;
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_probe
+ *
+ * Description:
+ * Probing for EISA boards: it looks like the first two bytes
+ * are a manufacturer code - three characters, five bits each:
+ *
+ * BYTE 0 BYTE 1 BYTE 2 BYTE 3
+ * ?1111122 22233333 PPPPPPPP RRRRRRRR
+ *
+ * The characters are baselined off ASCII '@', so add that value
+ * to each to get the real ASCII code for it. The next two bytes
+ * appear to be a product and revision number, probably vendor-
+ * specific. This is what is being searched for at each port,
+ * and what should probably correspond to the ID= field in the
+ * ECU's .cfg file for the card - if your card is not detected,
+ * make sure your signature is listed in the array.
+ *
+ * The fourth byte's lowest bit seems to be an enabled/disabled
+ * flag (rest of the bits are reserved?).
+ *
+ * NOTE: This function is only needed on Intel and Alpha platforms,
+ * the other platforms we support don't have EISA/VLB busses. So,
+ * we #ifdef this entire function to avoid compiler warnings about
+ * an unused function.
+ *-F*************************************************************************/
+#if defined(__i386__) || defined(__alpha__)
+static int
+aic7xxx_probe(int slot, int base, ahc_flag_type *flags)
+{
+ int i;
+ unsigned char buf[4];
+
+ static struct {
+ int n;
+ unsigned char signature[sizeof(buf)];
+ ahc_chip type;
+ int bios_disabled;
+ } AIC7xxx[] = {
+ { 4, { 0x04, 0x90, 0x77, 0x70 },
+ AHC_AIC7770|AHC_EISA, FALSE }, /* mb 7770 */
+ { 4, { 0x04, 0x90, 0x77, 0x71 },
+ AHC_AIC7770|AHC_EISA, FALSE }, /* host adapter 274x */
+ { 4, { 0x04, 0x90, 0x77, 0x56 },
+ AHC_AIC7770|AHC_VL, FALSE }, /* 284x BIOS enabled */
+ { 4, { 0x04, 0x90, 0x77, 0x57 },
+ AHC_AIC7770|AHC_VL, TRUE } /* 284x BIOS disabled */
+ };
+
+ /*
+ * The VL-bus cards need to be primed by
+ * writing before a signature check.
+ */
+ for (i = 0; i < sizeof(buf); i++)
+ {
+ outb(0x80 + i, base);
+ buf[i] = inb(base + i);
+ }
+
+ for (i = 0; i < NUMBER(AIC7xxx); i++)
+ {
+ /*
+ * Signature match on enabled card?
+ */
+ if (!memcmp(buf, AIC7xxx[i].signature, AIC7xxx[i].n))
+ {
+ if (inb(base + 4) & 1)
+ {
+ if (AIC7xxx[i].bios_disabled)
+ {
+ *flags |= AHC_USEDEFAULTS;
+ }
+ else
+ {
+ *flags |= AHC_BIOS_ENABLED;
+ }
+ return (i);
+ }
+
+ printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
+ "disabled at slot %d, ignored.\n", slot);
+ }
+ }
+
+ return (-1);
+}
+#endif /* (__i386__) || (__alpha__) */
+
+
+/*+F*************************************************************************
+ * Function:
+ * read_2840_seeprom
+ *
+ * Description:
+ * Reads the 2840 serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ *
+ * See read_seeprom (for the 2940) for the instruction set of the 93C46
+ * chip.
+ *
+ * The 2840 interface to the 93C46 serial EEPROM is through the
+ * STATUS_2840 and SEECTL_2840 registers. The CS_2840, CK_2840, and
+ * DO_2840 bits of the SEECTL_2840 register are connected to the chip
+ * select, clock, and data out lines respectively of the serial EEPROM.
+ * The DI_2840 bit of the STATUS_2840 is connected to the data in line
+ * of the serial EEPROM. The EEPROM_TF bit of STATUS_2840 register is
+ * useful in that it gives us an 800 nsec timer. After a read from the
+ * SEECTL_2840 register the timing flag is cleared and goes high 800 nsec
+ * later.
+ *-F*************************************************************************/
+static int
+read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
+{
+ int i = 0, k = 0;
+ unsigned char temp;
+ unsigned short checksum = 0;
+ unsigned short *seeprom = (unsigned short *) sc;
+ struct seeprom_cmd {
+ unsigned char len;
+ unsigned char bits[3];
+ };
+ struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
+
+#define CLOCK_PULSE(p) \
+ while ((aic_inb(p, STATUS_2840) & EEPROM_TF) == 0) \
+ { \
+ ; /* Do nothing */ \
+ } \
+ (void) aic_inb(p, SEECTL_2840);
+
+ /*
+ * Read the first 32 registers of the seeprom. For the 2840,
+ * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers
+ * but only the first 32 are used by Adaptec BIOS. The loop
+ * will range from 0 to 31.
+ */
+ for (k = 0; k < (sizeof(*sc) / 2); k++)
+ {
+ /*
+ * Send chip select for one clock cycle.
+ */
+ aic_outb(p, CK_2840 | CS_2840, SEECTL_2840);
+ CLOCK_PULSE(p);
+
+ /*
+ * Now we're ready to send the read command followed by the
+ * address of the 16-bit register we want to read.
+ */
+ for (i = 0; i < seeprom_read.len; i++)
+ {
+ temp = CS_2840 | seeprom_read.bits[i];
+ aic_outb(p, temp, SEECTL_2840);
+ CLOCK_PULSE(p);
+ temp = temp ^ CK_2840;
+ aic_outb(p, temp, SEECTL_2840);
+ CLOCK_PULSE(p);
+ }
+ /*
+ * Send the 6 bit address (MSB first, LSB last).
+ */
+ for (i = 5; i >= 0; i--)
+ {
+ temp = k;
+ temp = (temp >> i) & 1; /* Mask out all but lower bit. */
+ temp = CS_2840 | temp;
+ aic_outb(p, temp, SEECTL_2840);
+ CLOCK_PULSE(p);
+ temp = temp ^ CK_2840;
+ aic_outb(p, temp, SEECTL_2840);
+ CLOCK_PULSE(p);
+ }
+
+ /*
+ * Now read the 16 bit register. An initial 0 precedes the
+ * register contents which begins with bit 15 (MSB) and ends
+ * with bit 0 (LSB). The initial 0 will be shifted off the
+ * top of our word as we let the loop run from 0 to 16.
+ */
+ for (i = 0; i <= 16; i++)
+ {
+ temp = CS_2840;
+ aic_outb(p, temp, SEECTL_2840);
+ CLOCK_PULSE(p);
+ temp = temp ^ CK_2840;
+ seeprom[k] = (seeprom[k] << 1) | (aic_inb(p, STATUS_2840) & DI_2840);
+ aic_outb(p, temp, SEECTL_2840);
+ CLOCK_PULSE(p);
+ }
+ /*
+ * The serial EEPROM has a checksum in the last word. Keep a
+ * running checksum for all words read except for the last
+ * word. We'll verify the checksum after all words have been
+ * read.
+ */
+ if (k < (sizeof(*sc) / 2) - 1)
+ {
+ checksum = checksum + seeprom[k];
+ }
+
+ /*
+ * Reset the chip select for the next command cycle.
+ */
+ aic_outb(p, 0, SEECTL_2840);
+ CLOCK_PULSE(p);
+ aic_outb(p, CK_2840, SEECTL_2840);
+ CLOCK_PULSE(p);
+ aic_outb(p, 0, SEECTL_2840);
+ CLOCK_PULSE(p);
+ }
+
+#if 0
+ printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
+ printk("Serial EEPROM:");
+ for (k = 0; k < (sizeof(*sc) / 2); k++)
+ {
+ if (((k % 8) == 0) && (k != 0))
+ {
+ printk("\n ");
+ }
+ printk(" 0x%x", seeprom[k]);
+ }
+ printk("\n");
+#endif
+
+ if (checksum != sc->checksum)
+ {
+ printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
+ return (0);
+ }
+
+ return (1);
+#undef CLOCK_PULSE
+}
+
+/*+F*************************************************************************
+ * Function:
+ * acquire_seeprom
+ *
+ * Description:
+ * Acquires access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static int
+acquire_seeprom(struct aic7xxx_host *p)
+{
+ int wait;
+
+ /*
+ * Request access of the memory port. When access is
+ * granted, SEERDY will go high. We use a 1 second
+ * timeout which should be near 1 second more than
+ * is needed. Reason: after the 7870 chip reset, there
+ * should be no contention.
+ */
+ aic_outb(p, SEEMS, SEECTL);
+ wait = 1000; /* 1000 msec = 1 second */
+ while ((wait > 0) && ((aic_inb(p, SEECTL) & SEERDY) == 0))
+ {
+ wait--;
+ mdelay(1); /* 1 msec */
+ }
+ if ((aic_inb(p, SEECTL) & SEERDY) == 0)
+ {
+ aic_outb(p, 0, SEECTL);
+ return (0);
+ }
+ return (1);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * release_seeprom
+ *
+ * Description:
+ * Releases access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static void
+release_seeprom(struct aic7xxx_host *p)
+{
+ aic_outb(p, 0, SEECTL);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * read_seeprom
+ *
+ * Description:
+ * Reads the serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ *
+ * The instruction set of the 93C46/56/66 chips is as follows:
+ *
+ * Start OP
+ * Function Bit Code Address Data Description
+ * -------------------------------------------------------------------
+ * READ 1 10 A5 - A0 Reads data stored in memory,
+ * starting at specified address
+ * EWEN 1 00 11XXXX Write enable must precede
+ * all programming modes
+ * ERASE 1 11 A5 - A0 Erase register A5A4A3A2A1A0
+ * WRITE 1 01 A5 - A0 D15 - D0 Writes register
+ * ERAL 1 00 10XXXX Erase all registers
+ * WRAL 1 00 01XXXX D15 - D0 Writes to all registers
+ * EWDS 1 00 00XXXX Disables all programming
+ * instructions
+ * *Note: A value of X for address is a don't care condition.
+ * *Note: The 93C56 and 93C66 have 8 address bits.
+ *
+ *
+ * The 93C46 has a four wire interface: clock, chip select, data in, and
+ * data out. In order to perform one of the above functions, you need
+ * to enable the chip select for a clock period (typically a minimum of
+ * 1 usec, with the clock high and low a minimum of 750 and 250 nsec
+ * respectively. While the chip select remains high, you can clock in
+ * the instructions (above) starting with the start bit, followed by the
+ * OP code, Address, and Data (if needed). For the READ instruction, the
+ * requested 16-bit register contents is read from the data out line but
+ * is preceded by an initial zero (leading 0, followed by 16-bits, MSB
+ * first). The clock cycling from low to high initiates the next data
+ * bit to be sent from the chip.
+ *
+ * The 78xx interface to the 93C46 serial EEPROM is through the SEECTL
+ * register. After successful arbitration for the memory port, the
+ * SEECS bit of the SEECTL register is connected to the chip select.
+ * The SEECK, SEEDO, and SEEDI are connected to the clock, data out,
+ * and data in lines respectively. The SEERDY bit of SEECTL is useful
+ * in that it gives us an 800 nsec timer. After a write to the SEECTL
+ * register, the SEERDY goes high 800 nsec later. The one exception
+ * to this is when we first request access to the memory port. The
+ * SEERDY goes high to signify that access has been granted and, for
+ * this case, has no implied timing.
+ *-F*************************************************************************/
+static int
+read_seeprom(struct aic7xxx_host *p, int offset,
+ unsigned short *scarray, unsigned int len, seeprom_chip_type chip)
+{
+ int i = 0, k;
+ unsigned char temp;
+ unsigned short checksum = 0;
+ struct seeprom_cmd {
+ unsigned char len;
+ unsigned char bits[3];
+ };
+ struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
+
+#define CLOCK_PULSE(p) \
+ while ((aic_inb(p, SEECTL) & SEERDY) == 0) \
+ { \
+ ; /* Do nothing */ \
+ }
+
+ /*
+ * Request access of the memory port.
+ */
+ if (acquire_seeprom(p) == 0)
+ {
+ return (0);
+ }
+
+ /*
+ * Read 'len' registers of the seeprom. For the 7870, the 93C46
+ * SEEPROM is a 1024-bit device with 64 16-bit registers but only
+ * the first 32 are used by Adaptec BIOS. Some adapters use the
+ * 93C56 SEEPROM which is a 2048-bit device. The loop will range
+ * from 0 to 'len' - 1.
+ */
+ for (k = 0; k < len; k++)
+ {
+ /*
+ * Send chip select for one clock cycle.
+ */
+ aic_outb(p, SEEMS | SEECK | SEECS, SEECTL);
+ CLOCK_PULSE(p);
+
+ /*
+ * Now we're ready to send the read command followed by the
+ * address of the 16-bit register we want to read.
+ */
+ for (i = 0; i < seeprom_read.len; i++)
+ {
+ temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
+ aic_outb(p, temp, SEECTL);
+ CLOCK_PULSE(p);
+ temp = temp ^ SEECK;
+ aic_outb(p, temp, SEECTL);
+ CLOCK_PULSE(p);
+ }
+ /*
+ * Send the 6 or 8 bit address (MSB first, LSB last).
+ */
+ for (i = ((int) chip - 1); i >= 0; i--)
+ {
+ temp = k + offset;
+ temp = (temp >> i) & 1; /* Mask out all but lower bit. */
+ temp = SEEMS | SEECS | (temp << 1);
+ aic_outb(p, temp, SEECTL);
+ CLOCK_PULSE(p);
+ temp = temp ^ SEECK;
+ aic_outb(p, temp, SEECTL);
+ CLOCK_PULSE(p);
+ }
+
+ /*
+ * Now read the 16 bit register. An initial 0 precedes the
+ * register contents which begins with bit 15 (MSB) and ends
+ * with bit 0 (LSB). The initial 0 will be shifted off the
+ * top of our word as we let the loop run from 0 to 16.
+ */
+ for (i = 0; i <= 16; i++)
+ {
+ temp = SEEMS | SEECS;
+ aic_outb(p, temp, SEECTL);
+ CLOCK_PULSE(p);
+ temp = temp ^ SEECK;
+ scarray[k] = (scarray[k] << 1) | (aic_inb(p, SEECTL) & SEEDI);
+ aic_outb(p, temp, SEECTL);
+ CLOCK_PULSE(p);
+ }
+
+ /*
+ * The serial EEPROM should have a checksum in the last word.
+ * Keep a running checksum for all words read except for the
+ * last word. We'll verify the checksum after all words have
+ * been read.
+ */
+ if (k < (len - 1))
+ {
+ checksum = checksum + scarray[k];
+ }
+
+ /*
+ * Reset the chip select for the next command cycle.
+ */
+ aic_outb(p, SEEMS, SEECTL);
+ CLOCK_PULSE(p);
+ aic_outb(p, SEEMS | SEECK, SEECTL);
+ CLOCK_PULSE(p);
+ aic_outb(p, SEEMS, SEECTL);
+ CLOCK_PULSE(p);
+ }
+
+ /*
+ * Release access to the memory port and the serial EEPROM.
+ */
+ release_seeprom(p);
+
+#if 0
+ printk("Computed checksum 0x%x, checksum read 0x%x\n",
+ checksum, scarray[len - 1]);
+ printk("Serial EEPROM:");
+ for (k = 0; k < len; k++)
+ {
+ if (((k % 8) == 0) && (k != 0))
+ {
+ printk("\n ");
+ }
+ printk(" 0x%x", scarray[k]);
+ }
+ printk("\n");
+#endif
+ if (checksum != scarray[len - 1])
+ {
+ return (0);
+ }
+
+ return (1);
+#undef CLOCK_PULSE
+}
+
+/*+F*************************************************************************
+ * Function:
+ * write_brdctl
+ *
+ * Description:
+ * Writes a value to the BRDCTL register.
+ *-F*************************************************************************/
+static void
+write_brdctl(struct aic7xxx_host *p, unsigned char value)
+{
+ unsigned char brdctl;
+
+ if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
+ {
+ brdctl = BRDSTB;
+ if (p->flags & AHC_CHNLB)
+ brdctl |= BRDCS;
+ }
+ else if (p->features & AHC_ULTRA2)
+ brdctl = 0;
+ else
+ brdctl = BRDSTB | BRDCS;
+ aic_outb(p, brdctl, BRDCTL);
+ udelay(1);
+ brdctl |= value;
+ aic_outb(p, brdctl, BRDCTL);
+ udelay(1);
+ if (p->features & AHC_ULTRA2)
+ brdctl |= BRDSTB_ULTRA2;
+ else
+ brdctl &= ~BRDSTB;
+ aic_outb(p, brdctl, BRDCTL);
+ udelay(1);
+ if (p->features & AHC_ULTRA2)
+ brdctl = 0;
+ else
+ brdctl &= ~BRDCS;
+ aic_outb(p, brdctl, BRDCTL);
+ udelay(1);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * read_brdctl
+ *
+ * Description:
+ * Reads the BRDCTL register.
+ *-F*************************************************************************/
+static unsigned char
+read_brdctl(struct aic7xxx_host *p)
+{
+ unsigned char brdctl, value;
+
+ if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
+ {
+ brdctl = BRDRW;
+ if (p->flags & AHC_CHNLB)
+ brdctl |= BRDCS;
+ }
+ else if (p->features & AHC_ULTRA2)
+ brdctl = BRDRW_ULTRA2;
+ else
+ brdctl = BRDRW | BRDCS;
+ aic_outb(p, brdctl, BRDCTL);
+ udelay(1);
+ value = aic_inb(p, BRDCTL);
+ aic_outb(p, 0, BRDCTL);
+ udelay(1);
+ return (value);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic785x_cable_detect
+ *
+ * Description:
+ * Detect the cables that are present on aic785x class controller chips
+ *-F*************************************************************************/
+static void
+aic785x_cable_detect(struct aic7xxx_host *p, int *int_50,
+ int *ext_present, int *eeprom)
+{
+ unsigned char brdctl;
+
+ aic_outb(p, BRDRW | BRDCS, BRDCTL);
+ udelay(1);
+ aic_outb(p, 0, BRDCTL);
+ udelay(1);
+ brdctl = aic_inb(p, BRDCTL);
+ udelay(1);
+ *int_50 = !(brdctl & BRDDAT5);
+ *ext_present = !(brdctl & BRDDAT6);
+ *eeprom = (aic_inb(p, SPIOCAP) & EEPROM);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic787x_cable_detect
+ *
+ * Description:
+ * Detect the cables that are present on aic787x class controller chips
+ *
+ * NOTE: This functions assumes the SEEPROM will have already been aquired
+ * prior to invocation of this function.
+ *-F*************************************************************************/
+static void
+aic787x_cable_detect(struct aic7xxx_host *p, int *int_50, int *int_68,
+ int *ext_present, int *eeprom)
+{
+ unsigned char brdctl;
+
+ /*
+ * First read the status of our cables. Set the rom bank to
+ * 0 since the bank setting serves as a multiplexor for the
+ * cable detection logic. BRDDAT5 controls the bank switch.
+ */
+ write_brdctl(p, 0);
+
+ /*
+ * Now we read the state of the two internal connectors. BRDDAT6
+ * is internal 50, BRDDAT7 is internal 68. For each, the cable is
+ * present if the bit is 0
+ */
+ brdctl = read_brdctl(p);
+ *int_50 = !(brdctl & BRDDAT6);
+ *int_68 = !(brdctl & BRDDAT7);
+
+ /*
+ * Set the bank bit in brdctl and then read the external cable state
+ * and the EEPROM status
+ */
+ write_brdctl(p, BRDDAT5);
+ brdctl = read_brdctl(p);
+
+ *ext_present = !(brdctl & BRDDAT6);
+ *eeprom = !(brdctl & BRDDAT7);
+
+ /*
+ * We're done, the calling function will release the SEEPROM for us
+ */
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic787x_ultra2_term_detect
+ *
+ * Description:
+ * Detect the termination settings present on ultra2 class controllers
+ *
+ * NOTE: This functions assumes the SEEPROM will have already been aquired
+ * prior to invocation of this function.
+ *-F*************************************************************************/
+static void
+aic7xxx_ultra2_term_detect(struct aic7xxx_host *p, int *enableSE_low,
+ int *enableSE_high, int *enableLVD_low,
+ int *enableLVD_high, int *eprom_present)
+{
+ unsigned char brdctl;
+
+ brdctl = read_brdctl(p);
+
+ *eprom_present = (brdctl & BRDDAT7);
+ *enableSE_high = (brdctl & BRDDAT6);
+ *enableSE_low = (brdctl & BRDDAT5);
+ *enableLVD_high = (brdctl & BRDDAT4);
+ *enableLVD_low = (brdctl & BRDDAT3);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * configure_termination
+ *
+ * Description:
+ * Configures the termination settings on PCI adapters that have
+ * SEEPROMs available.
+ *-F*************************************************************************/
+static void
+configure_termination(struct aic7xxx_host *p)
+{
+ int internal50_present = 0;
+ int internal68_present = 0;
+ int external_present = 0;
+ int eprom_present = 0;
+ int enableSE_low = 0;
+ int enableSE_high = 0;
+ int enableLVD_low = 0;
+ int enableLVD_high = 0;
+ unsigned char brddat = 0;
+ unsigned char max_target = 0;
+ unsigned char sxfrctl1 = aic_inb(p, SXFRCTL1);
+
+ if (acquire_seeprom(p))
+ {
+ if (p->features & (AHC_WIDE|AHC_TWIN))
+ max_target = 16;
+ else
+ max_target = 8;
+ aic_outb(p, SEEMS | SEECS, SEECTL);
+ sxfrctl1 &= ~STPWEN;
+ if ( (p->adapter_control & CFAUTOTERM) ||
+ (p->features & AHC_ULTRA2) )
+ {
+ if ( (p->adapter_control & CFAUTOTERM) && !(p->features & AHC_ULTRA2) )
+ {
+ printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n",
+ p->host_no);
+ printk(KERN_INFO "(scsi%d) Please verify driver detected settings are "
+ "correct.\n", p->host_no);
+ printk(KERN_INFO "(scsi%d) If not, then please properly set the device "
+ "termination\n", p->host_no);
+ printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting CTRL-A "
+ "when prompted\n", p->host_no);
+ printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no);
+ }
+ /* Configure auto termination. */
+
+ if (p->features & AHC_ULTRA2)
+ {
+ if (aic7xxx_override_term == -1)
+ aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high,
+ &enableLVD_low, &enableLVD_high,
+ &eprom_present);
+ if (!(p->adapter_control & CFSEAUTOTERM))
+ {
+ enableSE_low = (p->adapter_control & CFSTERM);
+ enableSE_high = (p->adapter_control & CFWSTERM);
+ }
+ if (!(p->adapter_control & CFAUTOTERM))
+ {
+ enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM);
+ }
+ internal50_present = 0;
+ internal68_present = 1;
+ external_present = 1;
+ }
+ else if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 )
+ {
+ aic787x_cable_detect(p, &internal50_present, &internal68_present,
+ &external_present, &eprom_present);
+ }
+ else
+ {
+ aic785x_cable_detect(p, &internal50_present, &external_present,
+ &eprom_present);
+ }
+
+ if (max_target <= 8)
+ internal68_present = 0;
+
+ if ( !(p->features & AHC_ULTRA2) )
+ {
+ if (max_target > 8)
+ {
+ printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, "
+ "Ext-68 %s)\n", p->host_no,
+ internal50_present ? "YES" : "NO",
+ internal68_present ? "YES" : "NO",
+ external_present ? "YES" : "NO");
+ }
+ else
+ {
+ printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Ext-50 %s)\n",
+ p->host_no,
+ internal50_present ? "YES" : "NO",
+ external_present ? "YES" : "NO");
+ }
+ }
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no,
+ eprom_present ? "is" : "is not");
+
+ /*
+ * Now set the termination based on what we found. BRDDAT6
+ * controls wide termination enable.
+ * Flash Enable = BRDDAT7
+ * SE High Term Enable = BRDDAT6
+ * SE Low Term Enable = BRDDAT5 (7890)
+ * LVD High Term Enable = BRDDAT4 (7890)
+ */
+ if ( !(p->features & AHC_ULTRA2) &&
+ (internal50_present && internal68_present && external_present) )
+ {
+ printk(KERN_INFO "(scsi%d) Illegal cable configuration!! Only two\n",
+ p->host_no);
+ printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be "
+ "in use at a time!\n", p->host_no);
+ /*
+ * Force termination (low and high byte) on. This is safer than
+ * leaving it completely off, especially since this message comes
+ * most often from motherboard controllers that don't even have 3
+ * connectors, but instead are failing the cable detection.
+ */
+ internal50_present = external_present = 0;
+ enableSE_high = enableSE_low = 1;
+ }
+
+ if ((max_target > 8) &&
+ ((external_present == 0) || (internal68_present == 0) ||
+ (enableSE_high != 0)))
+ {
+ brddat |= BRDDAT6;
+ p->flags |= AHC_TERM_ENB_SE_HIGH;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
+ p->host_no);
+ }
+
+ if ( (((internal50_present ? 1 : 0) +
+ (internal68_present ? 1 : 0) +
+ (external_present ? 1 : 0)) <= 1) ||
+ (enableSE_low != 0) )
+ {
+ if (p->features & AHC_ULTRA2)
+ brddat |= BRDDAT5;
+ else
+ sxfrctl1 |= STPWEN;
+ p->flags |= AHC_TERM_ENB_SE_LOW;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
+ p->host_no);
+ }
+
+ if (enableLVD_low != 0)
+ {
+ sxfrctl1 |= STPWEN;
+ p->flags |= AHC_TERM_ENB_LVD;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) LVD Low byte termination Enabled\n",
+ p->host_no);
+ }
+
+ if (enableLVD_high != 0)
+ {
+ brddat |= BRDDAT4;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) LVD High byte termination Enabled\n",
+ p->host_no);
+ }
+ }
+ else
+ {
+ if (p->adapter_control & CFSTERM)
+ {
+ if (p->features & AHC_ULTRA2)
+ brddat |= BRDDAT5;
+ else
+ sxfrctl1 |= STPWEN;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
+ p->host_no);
+ }
+
+ if (p->adapter_control & CFWSTERM)
+ {
+ brddat |= BRDDAT6;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
+ p->host_no);
+ }
+ }
+ write_brdctl(p, brddat);
+ release_seeprom(p);
+ aic_outb(p, sxfrctl1, SXFRCTL1);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * detect_maxscb
+ *
+ * Description:
+ * Detects the maximum number of SCBs for the controller and returns
+ * the count and a mask in p (p->maxscbs, p->qcntmask).
+ *-F*************************************************************************/
+static void
+detect_maxscb(struct aic7xxx_host *p)
+{
+ int i;
+
+ /*
+ * It's possible that we've already done this for multichannel
+ * adapters.
+ */
+ if (p->scb_data->maxhscbs == 0)
+ {
+ /*
+ * We haven't initialized the SCB settings yet. Walk the SCBs to
+ * determince how many there are.
+ */
+ aic_outb(p, 0, FREE_SCBH);
+
+ for (i = 0; i < AIC7XXX_MAXSCB; i++)
+ {
+ aic_outb(p, i, SCBPTR);
+ aic_outb(p, i, SCB_CONTROL);
+ if (aic_inb(p, SCB_CONTROL) != i)
+ break;
+ aic_outb(p, 0, SCBPTR);
+ if (aic_inb(p, SCB_CONTROL) != 0)
+ break;
+
+ aic_outb(p, i, SCBPTR);
+ aic_outb(p, 0, SCB_CONTROL); /* Clear the control byte. */
+ aic_outb(p, i + 1, SCB_NEXT); /* Set the next pointer. */
+ aic_outb(p, i - 1, SCB_PREV); /* Set the prev pointer. */
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG); /* Make the tag invalid. */
+ aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS); /* no busy untagged */
+ aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+1);/* targets active yet */
+ aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+2);
+ aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+3);
+ }
+
+ /* Make sure the last SCB terminates the free list. */
+ aic_outb(p, i - 1, SCBPTR);
+ aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
+
+ /* Ensure we clear the first (0) SCBs control byte. */
+ aic_outb(p, 0, SCBPTR);
+ aic_outb(p, 0, SCB_CONTROL);
+
+ p->scb_data->maxhscbs = i;
+ /*
+ * Use direct indexing instead for speed
+ */
+ if ( i == AIC7XXX_MAXSCB )
+ p->flags &= ~AHC_PAGESCBS;
+ }
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_register
+ *
+ * Description:
+ * Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *-F*************************************************************************/
+static int
+aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p,
+ int reset_delay)
+{
+ int i, result;
+ int max_targets;
+ int found = 1;
+ unsigned char term, scsi_conf;
+ struct Scsi_Host *host;
+
+ /*
+ * Lock out other contenders for our i/o space.
+ */
+ request_region(p->base, MAXREG - MINREG, "aic7xxx");
+
+
+ host = p->host;
+
+ p->scb_data->maxscbs = AIC7XXX_MAXSCB;
+ host->can_queue = AIC7XXX_MAXSCB;
+ host->cmd_per_lun = 3;
+ host->sg_tablesize = AIC7XXX_MAX_SG;
+ host->select_queue_depths = aic7xxx_select_queue_depth;
+ host->this_id = p->scsi_id;
+ host->io_port = p->base;
+ host->n_io_port = 0xFF;
+ host->base = (unsigned char *) p->mbase;
+ host->irq = p->irq;
+ if (p->features & AHC_WIDE)
+ {
+ host->max_id = 16;
+ }
+ if (p->features & AHC_TWIN)
+ {
+ host->max_channel = 1;
+ }
+
+ p->host = host;
+ p->last_reset = 0;
+ p->host_no = host->host_no;
+ host->unique_id = p->instance;
+ p->isr_count = 0;
+ p->next = NULL;
+ p->completeq.head = NULL;
+ p->completeq.tail = NULL;
+ scbq_init(&p->scb_data->free_scbs);
+ scbq_init(&p->waiting_scbs);
+
+ for (i = 0; i < NUMBER(p->untagged_scbs); i++)
+ {
+ p->untagged_scbs[i] = SCB_LIST_NULL;
+ p->qinfifo[i] = SCB_LIST_NULL;
+ p->qoutfifo[i] = SCB_LIST_NULL;
+ }
+ /*
+ * We currently have no commands of any type
+ */
+ p->qinfifonext = 0;
+ p->qoutfifonext = 0;
+
+ for (i = 0; i < MAX_TARGETS; i++)
+ {
+ p->dev_commands_sent[i] = 0;
+ p->dev_flags[i] = 0;
+ p->dev_active_cmds[i] = 0;
+ p->dev_last_reset[i] = 0;
+ p->dev_last_queue_full[i] = 0;
+ p->dev_last_queue_full_count[i] = 0;
+ p->dev_max_queue_depth[i] = 1;
+ p->dev_temp_queue_depth[i] = 1;
+ p->dev_mid_level_queue_depth[i] = 3;
+ scbq_init(&p->delayed_scbs[i]);
+ init_timer(&p->dev_timer[i]);
+ p->dev_timer[i].expires = 0;
+ p->dev_timer[i].data = (unsigned long)p;
+ p->dev_timer[i].function = (void *)aic7xxx_timer;
+ }
+
+ printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no,
+ board_names[p->board_name_index]);
+ switch(p->chip)
+ {
+ case (AHC_AIC7770|AHC_EISA):
+ printk("EISA slot %d\n", p->pci_device_fn);
+ break;
+ case (AHC_AIC7770|AHC_VL):
+ printk("VLB slot %d\n", p->pci_device_fn);
+ break;
+ default:
+ printk("PCI %d/%d\n", PCI_SLOT(p->pci_device_fn),
+ PCI_FUNC(p->pci_device_fn));
+ break;
+ }
+ if (p->features & AHC_TWIN)
+ {
+ printk(KERN_INFO "(scsi%d) Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
+ p->host_no, p->scsi_id, p->scsi_id_b);
+ }
+ else
+ {
+ char *channel;
+
+ channel = "";
+
+ if ((p->flags & AHC_MULTI_CHANNEL) != 0)
+ {
+ channel = " A";
+
+ if ( (p->flags & (AHC_CHNLB|AHC_CHNLC)) != 0 )
+ {
+ channel = (p->flags & AHC_CHNLB) ? " B" : " C";
+ }
+ }
+ if (p->features & AHC_WIDE)
+ {
+ printk(KERN_INFO "(scsi%d) Wide ", p->host_no);
+ }
+ else
+ {
+ printk(KERN_INFO "(scsi%d) Narrow ", p->host_no);
+ }
+ printk("Channel%s, SCSI ID=%d, ", channel, p->scsi_id);
+ }
+ aic_outb(p, 0, SEQ_FLAGS);
+
+ /*
+ * Detect SCB parameters and initialize the SCB array.
+ */
+ detect_maxscb(p);
+ printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs);
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n",
+ p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis",
+ p->base, p->irq);
+ printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n",
+ p->host_no, p->mbase, (unsigned long)p->maddr);
+ }
+
+#ifdef CONFIG_PCI
+ /*
+ * Now that we know our instance number, we can set the flags we need to
+ * force termination if need be.
+ */
+ if (aic7xxx_stpwlev != -1)
+ {
+ /*
+ * This option only applies to PCI controllers.
+ */
+ if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
+ {
+ unsigned char devconfig;
+
+#if LINUX_KERNEL_VERSION > KERNEL_VERSION(2,1,92)
+ pci_read_config_byte(p->pdev, DEVCONFIG, &devconfig);
+#else
+ pcibios_read_config_byte(p->pci_bus, p->pci_device_fn,
+ DEVCONFIG, &devconfig);
+#endif
+ if ( (aic7xxx_stpwlev >> p->instance) & 0x01 )
+ {
+ devconfig |= 0x02;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("(scsi%d) Force setting STPWLEV bit\n", p->host_no);
+ }
+ else
+ {
+ devconfig &= ~0x02;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("(scsi%d) Force clearing STPWLEV bit\n", p->host_no);
+ }
+#if LINUX_KERNEL_VERSION > KERNEL_VERSION(2,1,92)
+ pci_write_config_byte(p->pdev, DEVCONFIG, devconfig);
+#else
+ pcibios_write_config_byte(p->pci_bus, p->pci_device_fn,
+ DEVCONFIG, devconfig);
+#endif
+ }
+ }
+#endif
+
+ /*
+ * That took care of devconfig and stpwlev, now for the actual termination
+ * settings.
+ */
+ if (aic7xxx_override_term != -1)
+ {
+ /*
+ * Again, this only applies to PCI controllers. We don't have problems
+ * with the termination on 274x controllers to the best of my knowledge.
+ */
+ if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
+ {
+ unsigned char term_override;
+
+ term_override = ( (aic7xxx_override_term >> (p->instance * 4)) & 0x0f);
+ p->adapter_control &=
+ ~(CFSTERM|CFWSTERM|CFLVDSTERM|CFAUTOTERM|CFSEAUTOTERM);
+ if ( (p->features & AHC_ULTRA2) && (term_override & 0x0c) )
+ {
+ p->adapter_control |= CFLVDSTERM;
+ }
+ if (term_override & 0x02)
+ {
+ p->adapter_control |= CFWSTERM;
+ }
+ if (term_override & 0x01)
+ {
+ p->adapter_control |= CFSTERM;
+ }
+ }
+ }
+
+ if ( (p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1) )
+ {
+ if (p->features & AHC_SPIOCAP)
+ {
+ if ( aic_inb(p, SPIOCAP) & SSPIOCPS )
+ /*
+ * Update the settings in sxfrctl1 to match the termination
+ * settings.
+ */
+ configure_termination(p);
+ }
+ else if ((p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870)
+ {
+ configure_termination(p);
+ }
+ }
+
+ /*
+ * Clear out any possible pending interrupts.
+ */
+ aic7xxx_clear_intstat(p);
+
+ /*
+ * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
+ */
+ if (p->features & AHC_TWIN)
+ {
+ /* Select channel B */
+ aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
+
+ term = ((p->flags & AHC_TERM_ENB_B) != 0) ? STPWEN : 0;
+ aic_outb(p, p->scsi_id_b, SCSIID);
+ scsi_conf = aic_inb(p, SCSICONF + 1);
+ aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+ aic_outb(p, (scsi_conf & ENSPCHK) | term |
+ ENSTIMER | ACTNEGEN, SXFRCTL1);
+ aic_outb(p, 0, SIMODE0);
+ aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
+ aic_outb(p, 0, SCSIRATE);
+
+ /* Select channel A */
+ aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
+ }
+
+ term = ((p->flags & AHC_TERM_ENB_SE_LOW) != 0) ? STPWEN : 0;
+ if (p->features & AHC_ULTRA2)
+ aic_outb(p, p->scsi_id, SCSIID_ULTRA2);
+ else
+ aic_outb(p, p->scsi_id, SCSIID);
+ scsi_conf = aic_inb(p, SCSICONF);
+ aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+ aic_outb(p, (scsi_conf & ENSPCHK) | term |
+ ENSTIMER | ACTNEGEN, SXFRCTL1);
+ aic_outb(p, 0, SIMODE0);
+ aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
+ aic_outb(p, 0, SCSIRATE);
+ if ( p->features & AHC_ULTRA2)
+ aic_outb(p, 0, SCSIOFFSET);
+
+ /*
+ * Look at the information that board initialization or the board
+ * BIOS has left us. In the lower four bits of each target's
+ * scratch space any value other than 0 indicates that we should
+ * initiate synchronous transfers. If it's zero, the user or the
+ * BIOS has decided to disable synchronous negotiation to that
+ * target so we don't activate the needsdtr flag.
+ */
+ if ((p->features & (AHC_TWIN|AHC_WIDE)) == 0)
+ {
+ max_targets = 8;
+ }
+ else
+ {
+ max_targets = 16;
+ }
+
+ if (!(aic7xxx_no_reset))
+ {
+ /*
+ * If we reset the bus, then clear the transfer settings, else leave
+ * them be
+ */
+ for (i = 0; i < max_targets; i++)
+ {
+ aic_outb(p, 0, TARG_SCSIRATE + i);
+ if (p->features & AHC_ULTRA2)
+ {
+ aic_outb(p, 0, TARG_OFFSET + i);
+ }
+ p->transinfo[i].cur_offset = 0;
+ p->transinfo[i].cur_period = 0;
+ p->transinfo[i].cur_width = MSG_EXT_WDTR_BUS_8_BIT;
+ }
+
+ /*
+ * If we reset the bus, then clear the transfer settings, else leave
+ * them be.
+ */
+ aic_outb(p, 0, ULTRA_ENB);
+ aic_outb(p, 0, ULTRA_ENB + 1);
+ p->ultraenb = 0;
+ }
+
+ /*
+ * Allocate enough hardware scbs to handle the maximum number of
+ * concurrent transactions we can have. We have to make sure that
+ * the allocated memory is contiguous memory. The Linux kmalloc
+ * routine should only allocate contiguous memory, but note that
+ * this could be a problem if kmalloc() is changed.
+ */
+ {
+ size_t array_size;
+ unsigned int hscb_physaddr;
+ unsigned long temp;
+
+ array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb);
+ if (p->scb_data->hscbs == NULL)
+ {
+ /*
+ * A little padding so we can align thing the way we want
+ */
+ p->scb_data->hscbs = kmalloc(array_size + 0x1f, GFP_ATOMIC);
+ }
+ if (p->scb_data->hscbs == NULL)
+ {
+ printk("(scsi%d) Unable to allocate hardware SCB array; "
+ "failing detection.\n", p->host_no);
+ p->irq = 0;
+ return(0);
+ }
+ /*
+ * Save the actual kmalloc buffer pointer off, then align our
+ * buffer to a 32 byte boundary
+ */
+ p->scb_data->hscb_kmalloc_ptr = p->scb_data->hscbs;
+ temp = (unsigned long)p->scb_data->hscbs;
+ temp += 0x1f;
+ temp &= ~0x1f;
+ p->scb_data->hscbs = (struct aic7xxx_hwscb *)temp;
+ /* At least the control byte of each SCB needs to be 0. */
+ memset(p->scb_data->hscbs, 0, array_size);
+
+ /* Tell the sequencer where it can find the hardware SCB array. */
+ hscb_physaddr = VIRT_TO_BUS(p->scb_data->hscbs);
+ aic_outb(p, hscb_physaddr & 0xFF, HSCB_ADDR);
+ aic_outb(p, (hscb_physaddr >> 8) & 0xFF, HSCB_ADDR + 1);
+ aic_outb(p, (hscb_physaddr >> 16) & 0xFF, HSCB_ADDR + 2);
+ aic_outb(p, (hscb_physaddr >> 24) & 0xFF, HSCB_ADDR + 3);
+
+ /* Set up the fifo areas at the same time */
+ hscb_physaddr = VIRT_TO_BUS(&p->untagged_scbs[0]);
+ aic_outb(p, hscb_physaddr & 0xFF, SCBID_ADDR);
+ aic_outb(p, (hscb_physaddr >> 8) & 0xFF, SCBID_ADDR + 1);
+ aic_outb(p, (hscb_physaddr >> 16) & 0xFF, SCBID_ADDR + 2);
+ aic_outb(p, (hscb_physaddr >> 24) & 0xFF, SCBID_ADDR + 3);
+ }
+
+ /* The Q-FIFOs we just set up are all empty */
+ aic_outb(p, 0, QINPOS);
+ aic_outb(p, 0, KERNEL_QINPOS);
+ aic_outb(p, 0, QOUTPOS);
+
+ if(p->features & AHC_QUEUE_REGS)
+ {
+ aic_outb(p, SCB_QSIZE_256, QOFF_CTLSTA);
+ aic_outb(p, 0, SDSCB_QOFF);
+ aic_outb(p, 0, SNSCB_QOFF);
+ aic_outb(p, 0, HNSCB_QOFF);
+ }
+
+ /*
+ * We don't have any waiting selections or disconnected SCBs.
+ */
+ aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
+ aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
+
+ /*
+ * Message out buffer starts empty
+ */
+ aic_outb(p, MSG_NOOP, MSG_OUT);
+ aic_outb(p, MSG_NOOP, LAST_MSG);
+
+ /*
+ * Set all the other asundry items that haven't been set yet.
+ * This includes just dumping init values to a lot of registers simply
+ * to make sure they've been touched and are ready for use parity wise
+ * speaking.
+ */
+ aic_outb(p, 0, TMODE_CMDADDR);
+ aic_outb(p, 0, TMODE_CMDADDR + 1);
+ aic_outb(p, 0, TMODE_CMDADDR + 2);
+ aic_outb(p, 0, TMODE_CMDADDR + 3);
+ aic_outb(p, 0, TMODE_CMDADDR_NEXT);
+
+ /*
+ * Link us into the list of valid hosts
+ */
+ p->next = first_aic7xxx;
+ first_aic7xxx = p;
+
+ /*
+ * Clear out any possible pending interrupts, again.
+ */
+ aic7xxx_clear_intstat(p);
+
+ /*
+ * Allocate the first set of scbs for this controller. This is to stream-
+ * line code elsewhere in the driver. If we have to check for the existence
+ * of scbs in certain code sections, it slows things down. However, as
+ * soon as we register the IRQ for this card, we could get an interrupt that
+ * includes possibly the SCSI_RSTI interrupt. If we catch that interrupt
+ * then we are likely to segfault if we don't have at least one chunk of
+ * SCBs allocated or add checks all through the reset code to make sure
+ * that the SCBs have been allocated which is an invalid running condition
+ * and therefore I think it's preferable to simply pre-allocate the first
+ * chunk of SCBs.
+ */
+ aic7xxx_allocate_scb(p);
+
+ /*
+ * Load the sequencer program, then re-enable the board -
+ * resetting the AIC-7770 disables it, leaving the lights
+ * on with nobody home.
+ */
+ aic7xxx_loadseq(p);
+
+ if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+ {
+ aic_outb(p, ENABLE, BCTL); /* Enable the boards BUS drivers. */
+ }
+
+ if ( !(aic7xxx_no_reset) )
+ {
+ if (p->features & AHC_TWIN)
+ {
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no);
+ aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
+ aic7xxx_reset_current_bus(p);
+ aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
+ }
+ /* Reset SCSI bus A. */
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ { /* In case we are a 3940, 3985, or 7895, print the right channel */
+ char *channel = "";
+ if (p->flags & AHC_MULTI_CHANNEL)
+ {
+ channel = " A";
+ if (p->flags & (AHC_CHNLB|AHC_CHNLC))
+ channel = (p->flags & AHC_CHNLB) ? " B" : " C";
+ }
+ printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel);
+ }
+
+ /*
+ * Some of the new Ultra2 chipsets need a longer delay after a chip
+ * reset than just the init setup creates, so we have to delay here
+ * before we go into a reset in order to make the chips happy.
+ */
+ if (p->features & AHC_ULTRA2)
+ mdelay(250);
+ aic7xxx_reset_current_bus(p);
+
+ /*
+ * Delay for the reset delay.
+ */
+ if (!reset_delay)
+ aic7xxx_delay(AIC7XXX_RESET_DELAY);
+ }
+ else
+ {
+ if (!reset_delay)
+ {
+ printk(KERN_INFO "(scsi%d) Not resetting SCSI bus. Note: Don't use "
+ "the no_reset\n", p->host_no);
+ printk(KERN_INFO "(scsi%d) option unless you have a verifiable need "
+ "for it.\n", p->host_no);
+ printk(KERN_INFO "(scsi%d) The no_reset option is known to break some "
+ "systems,\n", p->host_no);
+ printk(KERN_INFO "(scsi%d) and is not supported by the driver author\n",
+ p->host_no);
+ aic7xxx_delay(AIC7XXX_RESET_DELAY);
+ }
+ }
+
+ /*
+ * Register IRQ with the kernel. Only allow sharing IRQs with
+ * PCI devices.
+ */
+ if (!(p->chip & AHC_PCI))
+ {
+ result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p));
+ }
+ else
+ {
+ result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ,
+ "aic7xxx", p));
+ if (result < 0)
+ {
+ result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
+ "aic7xxx", p));
+ }
+ }
+ if (result < 0)
+ {
+ printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring "
+ "controller.\n", p->host_no, p->irq);
+ p->irq = 0;
+ return (0);
+ }
+
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+
+ return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_chip_reset
+ *
+ * Description:
+ * Perform a chip reset on the aic7xxx SCSI controller. The controller
+ * is paused upon return.
+ *-F*************************************************************************/
+int
+aic7xxx_chip_reset(struct aic7xxx_host *p)
+{
+ unsigned char sblkctl;
+ int wait;
+
+ /*
+ * For some 274x boards, we must clear the CHIPRST bit and pause
+ * the sequencer. For some reason, this makes the driver work.
+ */
+ aic_outb(p, PAUSE | CHIPRST, HCNTRL);
+
+ /*
+ * In the future, we may call this function as a last resort for
+ * error handling. Let's be nice and not do any unecessary delays.
+ */
+ wait = 1000; /* 1 second (1000 * 1 msec) */
+ while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK))
+ {
+ mdelay(1); /* 1 msec */
+ }
+
+ pause_sequencer(p);
+
+ sblkctl = aic_inb(p, SBLKCTL) & (SELBUSB|SELWIDE);
+ if (p->chip & AHC_PCI)
+ sblkctl &= ~SELBUSB;
+ switch( sblkctl )
+ {
+ case 0: /* normal narrow card */
+ break;
+ case 2: /* Wide card */
+ p->features |= AHC_WIDE;
+ break;
+ case 8: /* Twin card */
+ p->features |= AHC_TWIN;
+ p->flags |= AHC_MULTI_CHANNEL;
+ break;
+ default: /* hmmm...we don't know what this is */
+ printk(KERN_WARNING "aic7xxx: Unsupported adapter type %d, ignoring.\n",
+ aic_inb(p, SBLKCTL) & 0x0a);
+ return(-1);
+ }
+ return(0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_alloc
+ *
+ * Description:
+ * Allocate and initialize a host structure. Returns NULL upon error
+ * and a pointer to a aic7xxx_host struct upon success.
+ *-F*************************************************************************/
+static struct aic7xxx_host *
+aic7xxx_alloc(Scsi_Host_Template *sht, struct aic7xxx_host *temp)
+{
+ struct aic7xxx_host *p = NULL;
+ struct Scsi_Host *host;
+ int i;
+
+ /*
+ * Allocate a storage area by registering us with the mid-level
+ * SCSI layer.
+ */
+ host = scsi_register(sht, sizeof(struct aic7xxx_host));
+
+ if (host != NULL)
+ {
+ p = (struct aic7xxx_host *) host->hostdata;
+ memset(p, 0, sizeof(struct aic7xxx_host));
+ *p = *temp;
+ p->host = host;
+
+ p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
+ if (p->scb_data != NULL)
+ {
+ memset(p->scb_data, 0, sizeof(scb_data_type));
+ scbq_init (&p->scb_data->free_scbs);
+ }
+ else
+ {
+ /*
+ * For some reason we don't have enough memory. Free the
+ * allocated memory for the aic7xxx_host struct, and return NULL.
+ */
+ release_region(p->base, MAXREG - MINREG);
+ scsi_unregister(host);
+ return(NULL);
+ }
+ p->host_no = host->host_no;
+ p->tagenable = 0;
+ p->orderedtag = 0;
+ for (i=0; i<MAX_TARGETS; i++)
+ {
+ p->transinfo[i].goal_period = 0;
+ p->transinfo[i].goal_offset = 0;
+ p->transinfo[i].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
+ }
+ DRIVER_LOCK_INIT
+ }
+ return (p);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_free
+ *
+ * Description:
+ * Frees and releases all resources associated with an instance of
+ * the driver (struct aic7xxx_host *).
+ *-F*************************************************************************/
+static void
+aic7xxx_free(struct aic7xxx_host *p)
+{
+ int i;
+
+ /*
+ * Free the allocated hardware SCB space.
+ */
+ if (p->scb_data != NULL)
+ {
+ if (p->scb_data->hscbs != NULL)
+ {
+ kfree(p->scb_data->hscb_kmalloc_ptr);
+ p->scb_data->hscbs = p->scb_data->hscb_kmalloc_ptr = NULL;
+ }
+ /*
+ * Free the driver SCBs. These were allocated on an as-need
+ * basis. We allocated these in groups depending on how many
+ * we could fit into a given amount of RAM. The tail SCB for
+ * these allocations has a pointer to the alloced area.
+ */
+ for (i = 0; i < p->scb_data->numscbs; i++)
+ {
+ if (p->scb_data->scb_array[i]->kmalloc_ptr != NULL)
+ kfree(p->scb_data->scb_array[i]->kmalloc_ptr);
+ p->scb_data->scb_array[i] = NULL;
+ }
+
+ /*
+ * Free the SCB data area.
+ */
+ kfree(p->scb_data);
+ }
+
+ /*
+ * Free any alloced Scsi_Cmnd structures that might be around for
+ * negotiation purposes....
+ */
+ for (i = 0; i < MAX_TARGETS; i++)
+ {
+ if(p->dev_wdtr_cmnd[i])
+ kfree(p->dev_wdtr_cmnd[i]);
+ if(p->dev_sdtr_cmnd[i])
+ kfree(p->dev_sdtr_cmnd[i]);
+ }
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_load_seeprom
+ *
+ * Description:
+ * Load the seeprom and configure adapter and target settings.
+ * Returns 1 if the load was successful and 0 otherwise.
+ *-F*************************************************************************/
+static void
+aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
+{
+ int have_seeprom = 0;
+ int i, max_targets, mask;
+ unsigned char scsirate, scsi_conf;
+ unsigned short scarray[128];
+ struct seeprom_config *sc = (struct seeprom_config *) scarray;
+
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
+ }
+ switch (p->chip)
+ {
+ case (AHC_AIC7770|AHC_EISA): /* None of these adapters have seeproms. */
+ if (aic_inb(p, SCSICONF) & TERM_ENB)
+ p->flags |= AHC_TERM_ENB_A;
+ if ( (p->features & AHC_TWIN) && (aic_inb(p, SCSICONF + 1) & TERM_ENB) )
+ p->flags |= AHC_TERM_ENB_B;
+ break;
+
+ case (AHC_AIC7770|AHC_VL):
+ have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
+ break;
+
+ default:
+ have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+ scarray, p->sc_size, p->sc_type);
+ if (!have_seeprom)
+ {
+ if(p->sc_type == C46)
+ have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+ scarray, p->sc_size, C56_66);
+ else
+ have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+ scarray, p->sc_size, C46);
+ }
+ break;
+ }
+
+ if (!have_seeprom)
+ {
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("\naic7xxx: No SEEPROM available.\n");
+ }
+ p->flags |= AHC_NEWEEPROM_FMT;
+ if (aic_inb(p, SCSISEQ) == 0)
+ {
+ p->flags |= AHC_USEDEFAULTS;
+ p->flags &= ~AHC_BIOS_ENABLED;
+ p->scsi_id = p->scsi_id_b = 7;
+ *sxfrctl1 |= STPWEN;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("aic7xxx: Using default values.\n");
+ }
+ }
+ else if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("aic7xxx: Using leftover BIOS values.\n");
+ }
+ if ( *sxfrctl1 & STPWEN )
+ {
+ p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
+ sc->adapter_control &= ~CFAUTOTERM;
+ sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM;
+ }
+ p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
+ }
+ else
+ {
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("done\n");
+ }
+
+ /*
+ * Note things in our flags
+ */
+ p->flags |= AHC_SEEPROM_FOUND;
+
+ /*
+ * Update the settings in sxfrctl1 to match the termination settings.
+ */
+ *sxfrctl1 = 0;
+
+ /*
+ * Get our SCSI ID from the SEEPROM setting...
+ */
+ p->scsi_id = (sc->brtime_id & CFSCSIID);
+
+ /*
+ * First process the settings that are different between the VLB
+ * and PCI adapter seeproms.
+ */
+ if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7770)
+ {
+ /* VLB adapter seeproms */
+ if (sc->bios_control & CF284XEXTEND)
+ p->flags |= AHC_EXTEND_TRANS_A;
+
+ if (sc->adapter_control & CF284XSTERM)
+ {
+ *sxfrctl1 |= STPWEN;
+ p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
+ }
+ }
+ else
+ {
+ /* PCI adapter seeproms */
+ if (sc->bios_control & CFEXTEND)
+ p->flags |= AHC_EXTEND_TRANS_A;
+ if (sc->bios_control & CFBIOSEN)
+ p->flags |= AHC_BIOS_ENABLED;
+ else
+ p->flags &= ~AHC_BIOS_ENABLED;
+
+ if (sc->adapter_control & CFSTERM)
+ {
+ *sxfrctl1 |= STPWEN;
+ p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
+ }
+ }
+ p->sc = *sc;
+ }
+
+ p->discenable = 0;
+
+ /*
+ * Limit to 16 targets just in case. The 2842 for one is known to
+ * blow the max_targets setting, future cards might also.
+ */
+ max_targets = MIN(sc->max_targets & CFMAXTARG,
+ ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8));
+
+ if (have_seeprom)
+ {
+ for (i = 0; i < max_targets; i++)
+ {
+ if( ((p->features & AHC_ULTRA) &&
+ !(sc->adapter_control & CFULTRAEN) &&
+ (sc->device_flags[i] & CFSYNCHISULTRA)) ||
+ (sc->device_flags[i] & CFNEWULTRAFORMAT) )
+ {
+ p->flags |= AHC_NEWEEPROM_FMT;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < max_targets; i++)
+ {
+ mask = (0x01 << i);
+ if (!have_seeprom)
+ {
+ if(aic_inb(p, SCSISEQ) != 0)
+ {
+ /*
+ * OK...the BIOS set things up and left behind the settings we need.
+ * Just make our sc->device_flags[i] entry match what the card has
+ * set for this device.
+ */
+ p->discenable =
+ ~(aic_inb(p, DISC_DSB) | (aic_inb(p, DISC_DSB + 1) << 8) );
+ p->ultraenb =
+ (aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8) );
+ sc->device_flags[i] = (p->discenable & mask) ? CFDISC : 0;
+ if (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER)
+ sc->device_flags[i] |= CFWIDEB;
+ if (p->features & AHC_ULTRA2)
+ {
+ if (aic_inb(p, TARG_OFFSET + i))
+ {
+ sc->device_flags[i] |= CFSYNCH;
+ sc->device_flags[i] |= (aic_inb(p, TARG_SCSIRATE + i) & 0x07);
+ if ( (aic_inb(p, TARG_SCSIRATE + i) & 0x18) == 0x18 )
+ sc->device_flags[i] |= CFSYNCHISULTRA;
+ }
+ }
+ else
+ {
+ if (aic_inb(p, TARG_SCSIRATE + i) & ~WIDEXFER)
+ {
+ sc->device_flags[i] |= CFSYNCH;
+ if (p->features & AHC_ULTRA)
+ sc->device_flags[i] |= ((p->ultraenb & mask) ?
+ CFSYNCHISULTRA : 0);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Assume the BIOS has NOT been run on this card and nothing between
+ * the card and the devices is configured yet.
+ */
+ sc->device_flags[i] = CFDISC;
+ if (p->features & AHC_WIDE)
+ sc->device_flags[i] |= CFWIDEB;
+ if (p->features & AHC_ULTRA2)
+ sc->device_flags[i] |= 3;
+ else if (p->features & AHC_ULTRA)
+ sc->device_flags[i] |= CFSYNCHISULTRA;
+ sc->device_flags[i] |= CFSYNCH;
+ aic_outb(p, 0, TARG_SCSIRATE + i);
+ if (p->features & AHC_ULTRA2)
+ aic_outb(p, 0, TARG_OFFSET + i);
+ }
+ }
+ if (sc->device_flags[i] & CFDISC)
+ {
+ p->discenable |= mask;
+ }
+ if (p->flags & AHC_NEWEEPROM_FMT)
+ {
+ if (sc->device_flags[i] & CFSYNCHISULTRA)
+ {
+ p->ultraenb |= mask;
+ }
+ else if (sc->device_flags[i] & CFNEWULTRAFORMAT)
+ {
+ if ( (sc->device_flags[i] & (CFSYNCHISULTRA | CFXFER)) == 0x03 )
+ {
+ sc->device_flags[i] &= ~CFXFER;
+ sc->device_flags[i] |= CFSYNCHISULTRA;
+ p->ultraenb |= mask;
+ }
+ }
+ }
+ else if (sc->adapter_control & CFULTRAEN)
+ {
+ p->ultraenb |= mask;
+ }
+ if ( (sc->device_flags[i] & CFSYNCH) == 0)
+ {
+ sc->device_flags[i] &= ~CFXFER;
+ p->ultraenb &= ~mask;
+ p->transinfo[i].user_offset = 0;
+ p->transinfo[i].user_period = 0;
+ p->transinfo[i].cur_offset = 0;
+ p->transinfo[i].cur_period = 0;
+ p->needsdtr_copy &= ~mask;
+ }
+ else
+ {
+ if (p->features & AHC_ULTRA2)
+ {
+ p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2;
+ p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i);
+ scsirate = (sc->device_flags[i] & CFXFER) |
+ ((p->ultraenb & mask) ? 0x18 : 0x10);
+ p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate,
+ AHC_SYNCRATE_ULTRA2);
+ p->transinfo[i].cur_period = aic7xxx_find_period(p,
+ aic_inb(p, TARG_SCSIRATE + i),
+ AHC_SYNCRATE_ULTRA2);
+ }
+ else
+ {
+ scsirate = (sc->device_flags[i] & CFXFER) << 4;
+ if (sc->device_flags[i] & CFWIDEB)
+ p->transinfo[i].user_offset = MAX_OFFSET_16BIT;
+ else
+ p->transinfo[i].user_offset = MAX_OFFSET_8BIT;
+ if (p->features & AHC_ULTRA)
+ {
+ short ultraenb;
+ ultraenb = aic_inb(p, ULTRA_ENB) |
+ (aic_inb(p, ULTRA_ENB + 1) << 8);
+ p->transinfo[i].user_period = aic7xxx_find_period(p,
+ scsirate,
+ (p->ultraenb & mask) ?
+ AHC_SYNCRATE_ULTRA :
+ AHC_SYNCRATE_FAST);
+ p->transinfo[i].cur_period = aic7xxx_find_period(p,
+ aic_inb(p, TARG_SCSIRATE + i),
+ (ultraenb & mask) ?
+ AHC_SYNCRATE_ULTRA :
+ AHC_SYNCRATE_FAST);
+ }
+ else
+ p->transinfo[i].user_period = aic7xxx_find_period(p,
+ scsirate, AHC_SYNCRATE_FAST);
+ }
+ p->needsdtr_copy |= mask;
+ }
+ if ( (sc->device_flags[i] & CFWIDEB) && (p->features & AHC_WIDE) )
+ {
+ p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_16_BIT;
+ p->needwdtr_copy |= mask;
+ }
+ else
+ {
+ p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_8_BIT;
+ p->needwdtr_copy &= ~mask;
+ }
+ p->transinfo[i].cur_width =
+ (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER) ?
+ MSG_EXT_WDTR_BUS_16_BIT : MSG_EXT_WDTR_BUS_8_BIT;
+ }
+ aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
+ aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
+ p->needwdtr = p->needwdtr_copy;
+ p->needsdtr = p->needsdtr_copy;
+ p->wdtr_pending = p->sdtr_pending = 0;
+
+ /*
+ * We set the p->ultraenb from the SEEPROM to begin with, but now we make
+ * it match what is already down in the card. If we are doing a reset
+ * on the card then this will get put back to a default state anyway.
+ * This allows us to not have to pre-emptively negotiate when using the
+ * no_reset option.
+ */
+ if (p->features & AHC_ULTRA)
+ p->ultraenb = aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8);
+
+
+ scsi_conf = (p->scsi_id & HSCSIID);
+
+ if(have_seeprom)
+ {
+ p->adapter_control = sc->adapter_control;
+ p->bios_control = sc->bios_control;
+
+ switch (p->chip & AHC_CHIPID_MASK)
+ {
+ case AHC_AIC7895:
+ case AHC_AIC7896:
+ if (p->adapter_control & CFBPRIMARY)
+ p->flags |= AHC_CHANNEL_B_PRIMARY;
+ default:
+ break;
+ }
+
+ if (sc->adapter_control & CFSPARITY)
+ scsi_conf |= ENSPCHK;
+ }
+ else
+ {
+ scsi_conf |= ENSPCHK | RESET_SCSI;
+ }
+
+ /*
+ * Only set the SCSICONF and SCSICONF + 1 registers if we are a PCI card.
+ * The 2842 and 2742 cards already have these registers set and we don't
+ * want to muck with them since we don't set all the bits they do.
+ */
+ if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI )
+ {
+ /* Set the host ID */
+ aic_outb(p, scsi_conf, SCSICONF);
+ /* In case we are a wide card */
+ aic_outb(p, p->scsi_id, SCSICONF + 1);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_detect
+ *
+ * Description:
+ * Try to detect and register an Adaptec 7770 or 7870 SCSI controller.
+ *
+ * XXX - This should really be called aic7xxx_probe(). A sequence of
+ * probe(), attach()/detach(), and init() makes more sense than
+ * one do-it-all function. This may be useful when (and if) the
+ * mid-level SCSI code is overhauled.
+ *-F*************************************************************************/
+int
+aic7xxx_detect(Scsi_Host_Template *template)
+{
+ struct aic7xxx_host *temp_p = NULL;
+ struct aic7xxx_host *current_p = NULL;
+ struct aic7xxx_host *list_p = NULL;
+ int found = 0;
+#if defined(__i386__) || defined(__alpha__)
+ ahc_flag_type flags = 0;
+ int type;
+#endif
+ unsigned char sxfrctl1;
+#if defined(__i386__) || defined(__alpha__)
+ unsigned char hcntrl, hostconf;
+ unsigned int slot, base;
+#endif
+
+#ifdef MODULE
+ /*
+ * If we are called as a module, the aic7xxx pointer may not be null
+ * and it would point to our bootup string, just like on the lilo
+ * command line. IF not NULL, then process this config string with
+ * aic7xxx_setup
+ */
+ if(aic7xxx)
+ aic7xxx_setup(aic7xxx, NULL);
+ if(dummy_buffer[0] != 'P')
+ printk(KERN_WARNING "aic7xxx: Please read the file /usr/src/linux/drivers"
+ "/scsi/README.aic7xxx\n"
+ "aic7xxx: to see the proper way to specify options to the aic7xxx "
+ "module\n"
+ "aic7xxx: Specifically, don't use any commas when passing arguments to\n"
+ "aic7xxx: insmod or else it might trash certain memory areas.\n");
+#endif
+
+ template->proc_dir = &proc_scsi_aic7xxx;
+ template->sg_tablesize = AIC7XXX_MAX_SG;
+
+
+#if defined(__i386__) || defined(__alpha__)
+ /*
+ * EISA/VL-bus card signature probe.
+ */
+ slot = MINSLOT;
+ while ( (slot <= MAXSLOT) && !(aic7xxx_no_probe) )
+ {
+ base = SLOTBASE(slot) + MINREG;
+
+ if (check_region(base, MAXREG - MINREG))
+ {
+ /*
+ * Some other driver has staked a
+ * claim to this i/o region already.
+ */
+ slot++;
+ continue; /* back to the beginning of the for loop */
+ }
+ flags = 0;
+ type = aic7xxx_probe(slot, base + AHC_HID0, &flags);
+ if (type == -1)
+ {
+ slot++;
+ continue;
+ }
+ temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC);
+ if (temp_p == NULL)
+ {
+ printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+ slot++;
+ continue; /* back to the beginning of the while loop */
+ }
+ /*
+ * Pause the card preserving the IRQ type. Allow the operator
+ * to override the IRQ trigger.
+ */
+ if (aic7xxx_irq_trigger == 1)
+ hcntrl = IRQMS; /* Level */
+ else if (aic7xxx_irq_trigger == 0)
+ hcntrl = 0; /* Edge */
+ else
+ hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */
+ memset(temp_p, 0, sizeof(struct aic7xxx_host));
+ temp_p->unpause = hcntrl | INTEN;
+ temp_p->pause = hcntrl | PAUSE | INTEN;
+ temp_p->base = base;
+ temp_p->mbase = 0;
+ temp_p->maddr = 0;
+ temp_p->pci_bus = 0;
+ temp_p->pci_device_fn = slot;
+ aic_outb(temp_p, hcntrl | PAUSE, HCNTRL);
+ while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ;
+ if (aic7xxx_chip_reset(temp_p) == -1)
+ temp_p->irq = 0;
+ else
+ temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F;
+ temp_p->flags |= AHC_PAGESCBS;
+
+ switch (temp_p->irq)
+ {
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ break;
+
+ default:
+ printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
+ "level %d, ignoring.\n", temp_p->irq);
+ kfree(temp_p);
+ slot++;
+ continue; /* back to the beginning of the while loop */
+ }
+
+ /*
+ * We are commited now, everything has been checked and this card
+ * has been found, now we just set it up
+ */
+
+ /*
+ * Insert our new struct into the list at the end
+ */
+ if (list_p == NULL)
+ {
+ list_p = current_p = temp_p;
+ }
+ else
+ {
+ current_p = list_p;
+ while (current_p->next != NULL)
+ current_p = current_p->next;
+ current_p->next = temp_p;
+ }
+ if (aic7xxx_extended)
+ {
+ temp_p->flags |= AHC_EXTEND_TRANS_A;
+ if (temp_p->flags & AHC_MULTI_CHANNEL)
+ temp_p->flags |= AHC_EXTEND_TRANS_B;
+ }
+
+ switch (type)
+ {
+ case 0:
+ temp_p->board_name_index = 2;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("aic7xxx: <%s> at EISA %d\n",
+ board_names[2], slot);
+ /* FALLTHROUGH */
+ case 1:
+ {
+ temp_p->chip = AHC_AIC7770 | AHC_EISA;
+ temp_p->features |= AHC_AIC7770_FE;
+ temp_p->bios_control = aic_inb(temp_p, HA_274_BIOSCTRL);
+
+ /*
+ * Get the primary channel information. Right now we don't
+ * do anything with this, but someday we will be able to inform
+ * the mid-level SCSI code which channel is primary.
+ */
+ if (temp_p->board_name_index == 0)
+ {
+ temp_p->board_name_index = 3;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("aic7xxx: <%s> at EISA %d\n",
+ board_names[3], slot);
+ }
+ if (temp_p->bios_control & CHANNEL_B_PRIMARY)
+ {
+ temp_p->flags |= AHC_CHANNEL_B_PRIMARY;
+ }
+
+ if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED)
+ {
+ temp_p->flags &= ~AHC_BIOS_ENABLED;
+ }
+ else
+ {
+ temp_p->flags &= ~AHC_USEDEFAULTS;
+ temp_p->flags |= AHC_BIOS_ENABLED;
+ if ( (temp_p->bios_control & 0x20) == 0 )
+ {
+ temp_p->bios_address = 0xcc000;
+ temp_p->bios_address += (0x4000 * (temp_p->bios_control & 0x07));
+ }
+ else
+ {
+ temp_p->bios_address = 0xd0000;
+ temp_p->bios_address += (0x8000 * (temp_p->bios_control & 0x06));
+ }
+ }
+ temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8;
+ temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1);
+ if (temp_p->features & AHC_WIDE)
+ {
+ temp_p->scsi_id = temp_p->adapter_control & HWSCSIID;
+ temp_p->scsi_id_b = temp_p->scsi_id;
+ }
+ else
+ {
+ temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID;
+ temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID;
+ }
+ aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+ break;
+ }
+
+ case 2:
+ case 3:
+ temp_p->chip = AHC_AIC7770 | AHC_VL;
+ temp_p->features |= AHC_AIC7770_FE;
+ if (type == 2)
+ temp_p->flags |= AHC_BIOS_ENABLED;
+ else
+ temp_p->flags &= ~AHC_BIOS_ENABLED;
+ if (aic_inb(temp_p, SCSICONF) & TERM_ENB)
+ sxfrctl1 = STPWEN;
+ aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+ temp_p->board_name_index = 4;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("aic7xxx: <%s> at VLB %d\n",
+ board_names[2], slot);
+ switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL )
+ {
+ case 0x00:
+ temp_p->bios_address = 0xe0000;
+ break;
+ case 0x20:
+ temp_p->bios_address = 0xc8000;
+ break;
+ case 0x40:
+ temp_p->bios_address = 0xd0000;
+ break;
+ case 0x60:
+ temp_p->bios_address = 0xd8000;
+ break;
+ default:
+ break; /* can't get here */
+ }
+ break;
+
+ default: /* Won't get here. */
+ break;
+ }
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%lx, IRQ %d (%s)\n",
+ (temp_p->flags & AHC_USEDEFAULTS) ? "dis" : "en", temp_p->base,
+ temp_p->irq,
+ (temp_p->pause & IRQMS) ? "level sensitive" : "edge triggered");
+ printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+ (temp_p->flags & AHC_EXTEND_TRANS_A) ? "en" : "dis");
+ }
+
+ /*
+ * Set the FIFO threshold and the bus off time.
+ */
+ hostconf = aic_inb(temp_p, HOSTCONF);
+ aic_outb(temp_p, hostconf & DFTHRSH, BUSSPD);
+ aic_outb(temp_p, (hostconf << 2) & BOFF, BUSTIME);
+ slot++;
+ found++;
+ }
+
+#endif /* defined(__i386__) || defined(__alpha__) */
+
+#ifdef CONFIG_PCI
+ /*
+ * PCI-bus probe.
+ */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ if (pci_present())
+#else
+ if (pcibios_present())
+#endif
+ {
+ struct
+ {
+ unsigned short vendor_id;
+ unsigned short device_id;
+ ahc_chip chip;
+ ahc_flag_type flags;
+ ahc_feature features;
+ int board_name_index;
+ unsigned short seeprom_size;
+ unsigned short seeprom_type;
+ } const aic_pdevs[] = {
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7810, AHC_NONE,
+ AHC_FNONE, AHC_FENONE, 1,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AHC_AIC7850,
+ AHC_PAGESCBS, AHC_AIC7850_FE, 5,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
+ AHC_PAGESCBS, AHC_AIC7850_FE, 6,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7860_FE, 7,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_AIC7860,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7860_FE, 8,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 9,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_AIC7870,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 10,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_AIC7870,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7870_FE, 11,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_AIC7870,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7870_FE, 12,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_AIC7870,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 13,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 14,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 15,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7880_FE, 16,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7880_FE, 17,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880,
+ AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7895_FE, 19,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890, AHC_AIC7890,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7890_FE, 20,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+ AHC_AIC7890_FE, 21,
+ 32, C46 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7896_FE, 22,
+ 32, C56_66 },
+ {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896,
+ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+ AHC_AIC7896_FE, 23,
+ 32, C56_66 },
+ };
+
+ unsigned short command;
+ unsigned int devconfig, i, oldverbose;
+#ifdef MMAPIO
+ unsigned long page_offset, base;
+#endif
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ struct pci_dev *pdev = NULL;
+#else
+ int index;
+ unsigned int piobase, mmapbase;
+ unsigned char pci_bus, pci_devfn, pci_irq;
+#endif
+
+ for (i = 0; i < NUMBER(aic_pdevs); i++)
+ {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ pdev = NULL;
+ while ((pdev = pci_find_device(aic_pdevs[i].vendor_id,
+ aic_pdevs[i].device_id,
+ pdev)))
+#else
+ index = 0;
+ while (!(pcibios_find_device(aic_pdevs[i].vendor_id,
+ aic_pdevs[i].device_id,
+ index++, &pci_bus, &pci_devfn)) )
+#endif
+ {
+ if ( i == 0 ) /* We found one, but it's the 7810 RAID cont. */
+ {
+ if (aic7xxx_verbose & (VERBOSE_PROBE|VERBOSE_PROBE2))
+ {
+ printk(KERN_INFO "aic7xxx: The 7810 RAID controller is not "
+ "supported by\n");
+ printk(KERN_INFO " this driver, we are ignoring it.\n");
+ }
+ }
+ else if ( (temp_p = kmalloc(sizeof(struct aic7xxx_host),
+ GFP_ATOMIC)) != NULL )
+ {
+ memset(temp_p, 0, sizeof(struct aic7xxx_host));
+ temp_p->chip = aic_pdevs[i].chip | AHC_PCI;
+ temp_p->flags = aic_pdevs[i].flags;
+ temp_p->features = aic_pdevs[i].features;
+ temp_p->board_name_index = aic_pdevs[i].board_name_index;
+ temp_p->sc_size = aic_pdevs[i].seeprom_size;
+ temp_p->sc_type = aic_pdevs[i].seeprom_type;
+
+ /*
+ * Read sundry information from PCI BIOS.
+ */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ temp_p->irq = pdev->irq;
+ temp_p->pdev = pdev;
+ temp_p->pci_bus = pdev->bus->number;
+ temp_p->pci_device_fn = pdev->devfn;
+ temp_p->base = pdev->base_address[0];
+ temp_p->mbase = pdev->base_address[1];
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("aic7xxx: <%s> at PCI %d/%d\n",
+ board_names[aic_pdevs[i].board_name_index],
+ PCI_SLOT(temp_p->pdev->devfn),
+ PCI_FUNC(temp_p->pdev->devfn));
+ pci_read_config_word(pdev, PCI_COMMAND, &command);
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n",
+ (int)command);
+ }
+#ifdef AIC7XXX_STRICT_PCI_SETUP
+ command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
+ PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER |
+ PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#else
+ command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#endif
+ if (aic7xxx_pci_parity == 0)
+ command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+ pci_write_config_word(pdev, PCI_COMMAND, command);
+#ifdef AIC7XXX_STRICT_PCI_SETUP
+ pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
+ }
+ devconfig |= 0x80000000;
+ if ((aic7xxx_pci_parity == 0) || (aic7xxx_pci_parity == -1))
+ {
+ devconfig &= ~(0x00000008);
+ }
+ else
+ {
+ devconfig |= 0x00000008;
+ }
+ pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+#endif /* AIC7XXX_STRICT_PCI_SETUP */
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) */
+ temp_p->pci_bus = pci_bus;
+ temp_p->pci_device_fn = pci_devfn;
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk("aic7xxx: <%s> at PCI %d/%d\n",
+ board_names[aic_pdevs[i].board_name_index],
+ PCI_SLOT(temp_p->pci_device_fn),
+ PCI_FUNC(temp_p->pci_device_fn));
+ pcibios_read_config_byte(pci_bus, pci_devfn, PCI_INTERRUPT_LINE,
+ &pci_irq);
+ temp_p->irq = pci_irq;
+ pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_0,
+ &piobase);
+ temp_p->base = piobase;
+ pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_1,
+ &mmapbase);
+ temp_p->mbase = mmapbase;
+ pcibios_read_config_word(pci_bus, pci_devfn, PCI_COMMAND, &command);
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n",
+ (int)command);
+ }
+#ifdef AIC7XXX_STRICT_PCI_SETUP
+ command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
+ PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER |
+ PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#else
+ command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#endif
+ if (aic7xxx_pci_parity == 0)
+ command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+ pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND, command);
+#ifdef AIC7XXX_STRICT_PCI_SETUP
+ pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, &devconfig);
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ {
+ printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
+ }
+ devconfig |= 0x80000000;
+ if ((aic7xxx_pci_parity == 0) || (aic7xxx_pci_parity == -1))
+ {
+ devconfig &= ~(0x00000008);
+ }
+ else
+ {
+ devconfig |= 0x00000008;
+ }
+ pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, devconfig);
+#endif /* AIC7XXX_STRICT_PCI_SETUP */
+#endif /* LINUIX_VERSION_CODE > KERNEL_VERSION(2,1,92) */
+
+ /*
+ * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so
+ * we mask it off.
+ */
+ temp_p->base &= PCI_BASE_ADDRESS_IO_MASK;
+ temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK;
+ temp_p->unpause = INTEN;
+ temp_p->pause = temp_p->unpause | PAUSE;
+
+#ifdef MMAPIO
+ base = temp_p->mbase & PAGE_MASK;
+ page_offset = temp_p->mbase - base;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+ temp_p->maddr = ioremap_nocache(base, page_offset + 256);
+#else
+ temp_p->maddr = vremap(base, page_offset + 256);
+#endif
+ if(temp_p->maddr)
+ {
+ temp_p->maddr += page_offset;
+ }
+#endif
+
+ pause_sequencer(temp_p);
+
+ /*
+ * Clear out any pending PCI error status messages. Also set
+ * verbose to 0 so that we don't emit strange PCI error messages
+ * while cleaning out the current status bits.
+ */
+ oldverbose = aic7xxx_verbose;
+ aic7xxx_verbose = 0;
+ aic7xxx_pci_intr(temp_p);
+ aic7xxx_verbose = oldverbose;
+
+ temp_p->bios_address = 0;
+
+ /*
+ * Remember how the card was setup in case there is no seeprom.
+ */
+ if (temp_p->features & AHC_ULTRA2)
+ temp_p->scsi_id = aic_inb(temp_p, SCSIID_ULTRA2) & OID;
+ else
+ temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID;
+ /*
+ * Get current termination setting
+ */
+ sxfrctl1 = aic_inb(temp_p, SXFRCTL1) & STPWEN;
+
+ if (aic7xxx_chip_reset(temp_p) == -1)
+ {
+ kfree(temp_p);
+ temp_p = NULL;
+ continue;
+ }
+
+ /*
+ * Doing a switch based upon i is really gross, but since Justin
+ * changed around the chip ID stuff, we can't use that any more.
+ * Since we don't scan the devices the same way as FreeBSD, we end
+ * up doing this gross hack in order to avoid totally splitting
+ * away from Justin's init code in ahc_pci.c
+ */
+ switch (i)
+ {
+ case 7: /* 3940 */
+ case 12: /* 3940-Ultra */
+ switch(PCI_SLOT(temp_p->pci_device_fn))
+ {
+ case 5:
+ temp_p->flags |= AHC_CHNLB;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 8: /* 3985 */
+ case 13: /* 3985-Ultra */
+ switch(PCI_SLOT(temp_p->pci_device_fn))
+ {
+ case 8:
+ temp_p->flags |= AHC_CHNLB;
+ break;
+ case 12:
+ temp_p->flags |= AHC_CHNLC;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 15:
+ case 18:
+ case 19:
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ if (PCI_FUNC(temp_p->pdev->devfn) != 0)
+ {
+ temp_p->flags |= AHC_CHNLB;
+ }
+ /*
+ * The 7895 is the only chipset that sets the SCBSIZE32 param
+ * in the DEVCONFIG register. The Ultra2 chipsets use
+ * the DSCOMMAND0 register instead.
+ */
+ if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
+ {
+ pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+ devconfig |= SCBSIZE32;
+ pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+ }
+#else
+ if (PCI_FUNC(temp_p->pci_device_fn) != 0)
+ {
+ temp_p->flags |= AHC_CHNLB;
+ }
+ /*
+ * The 7895 is the only chipset that sets the SCBSIZE32 param
+ * in the DEVCONFIG register. The Ultra2 chipsets use
+ * the DSCOMMAND0 register instead.
+ */
+ if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
+ {
+ pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+ &devconfig);
+ devconfig |= SCBSIZE32;
+ pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+ devconfig);
+ }
+#endif
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Loading of the SEEPROM needs to come after we've set the flags
+ * to indicate possible CHNLB and CHNLC assigments. Otherwise,
+ * on 394x and 398x cards we'll end up reading the wrong settings
+ * for channels B and C
+ */
+ switch (temp_p->chip & AHC_CHIPID_MASK)
+ {
+ case AHC_AIC7890:
+ case AHC_AIC7896:
+ aic_outb(temp_p, 0, SCAMCTL);
+ /*
+ * We used to set DPARCKEN in this register, but after talking
+ * to a tech from Adaptec, I found out they don't use that
+ * particular bit in their own register settings, and when you
+ * combine that with the fact that I determined that we were
+ * seeing Data-Path Parity Errors on things we shouldn't see
+ * them on, I think there is a bug in the silicon and the way
+ * to work around it is to disable this particular check. Also
+ * This bug only showed up on certain commands, so it seems to
+ * be pattern related or some such. The commands we would
+ * typically send as a linux TEST_UNIT_READY or INQUIRY command
+ * could cause it to be triggered, while regular commands that
+ * actually made reasonable use of the SG array capabilities
+ * seemed not to cause the problem.
+ */
+ /*
+ aic_outb(temp_p, aic_inb(temp_p, DSCOMMAND0) |
+ CACHETHEN | DPARCKEN | MPARCKEN |
+ USCBSIZE32 | CIOPARCKEN,
+ DSCOMMAND0);
+ */
+ aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+ CACHETHEN | MPARCKEN | USCBSIZE32 |
+ CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0);
+ /* FALLTHROUGH */
+ default:
+ /*
+ * We attempt to read a SEEPROM on *everything*. If we fail,
+ * then we fail, but this covers things like 2910c cards that
+ * now have SEEPROMs with their 7856 chipset that we would
+ * otherwise ignore. They still don't have a BIOS, but they
+ * have a SEEPROM that the SCSISelect utility on the Adaptec
+ * diskettes can configure.
+ */
+ aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+ break;
+ case AHC_AIC7850:
+ case AHC_AIC7860:
+ /*
+ * Set the DSCOMMAND0 register on these cards different from
+ * on the 789x cards. Also, read the SEEPROM as well.
+ */
+ aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+ CACHETHEN | MPARCKEN) & ~DPARCKEN,
+ DSCOMMAND0);
+ aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+ break;
+ case AHC_AIC7880:
+ /*
+ * Only set the DSCOMMAND0 register if this is a Rev B.
+ * chipset. For those, we also enable Ultra mode by
+ * force due to brain-damage on the part of some BIOSes
+ * We overload the devconfig variable here since we can.
+ */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+#else
+ pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+ &devconfig);
+#endif
+ if ((devconfig & 0xff) >= 1)
+ {
+ aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+ CACHETHEN | MPARCKEN) & ~DPARCKEN,
+ DSCOMMAND0);
+ }
+ aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+ break;
+ }
+
+
+ /*
+ * and then we need another switch based on the type in order to
+ * make sure the channel B primary flag is set properly on 7895
+ * controllers....Arrrgggghhh!!! We also have to catch the fact
+ * that when you disable the BIOS on the 7895 on the Intel DK440LX
+ * motherboard, and possibly others, it only sets the BIOS disabled
+ * bit on the A channel...I think I'm starting to lean towards
+ * going postal....
+ */
+ switch(temp_p->chip & AHC_CHIPID_MASK)
+ {
+ case AHC_AIC7895:
+ case AHC_AIC7896:
+ current_p = list_p;
+ while(current_p != NULL)
+ {
+ if ( (current_p->pci_bus == temp_p->pci_bus) &&
+ (PCI_SLOT(current_p->pci_device_fn) ==
+ PCI_SLOT(temp_p->pci_device_fn)) )
+ {
+ if ( PCI_FUNC(current_p->pci_device_fn) == 0 )
+ {
+ temp_p->flags |=
+ (current_p->flags & AHC_CHANNEL_B_PRIMARY);
+ temp_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
+ temp_p->flags |=
+ (current_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
+ }
+ else
+ {
+ current_p->flags |=
+ (temp_p->flags & AHC_CHANNEL_B_PRIMARY);
+ current_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
+ current_p->flags |=
+ (temp_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
+ }
+ }
+ current_p = current_p->next;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * We do another switch based on i so that we can exclude all
+ * 3895 devices from the next option since the 3895 cards use
+ * shared external SCB RAM while all other cards have dedicated
+ * external SCB RAM per channel. Also exclude the 7850 and
+ * 7860 based stuff since they can have garbage in the bit
+ * that indicates external RAM and get some of this stuff
+ * wrong as a result.
+ */
+ switch(temp_p->chip & AHC_CHIPID_MASK)
+ {
+ default:
+ break;
+ case AHC_AIC7895:
+ case AHC_AIC7896:
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+#else
+ pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+ &devconfig);
+#endif
+ if (temp_p->features & AHC_ULTRA2)
+ {
+ if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2)
+ {
+ aic_outb(temp_p,
+ aic_inb(temp_p, DSCOMMAND0) & ~SCBRAMSEL_ULTRA2,
+ DSCOMMAND0);
+ temp_p->flags |= AHC_EXTERNAL_SRAM;
+ devconfig |= EXTSCBPEN;
+ }
+ }
+ else if (devconfig & RAMPSM)
+ {
+ devconfig &= ~SCBRAMSEL;
+ devconfig |= EXTSCBPEN;
+ temp_p->flags |= AHC_EXTERNAL_SRAM;
+ }
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+#else
+ pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+ devconfig);
+#endif
+ if ( (temp_p->flags & AHC_EXTERNAL_SRAM) &&
+ (temp_p->flags & AHC_CHNLB) )
+ aic_outb(temp_p, 1, CCSCBBADDR);
+ break;
+ }
+
+ /*
+ * Take the LED out of diagnostic mode
+ */
+ aic_outb(temp_p,
+ (aic_inb(temp_p, SBLKCTL) & ~(DIAGLEDEN | DIAGLEDON)),
+ SBLKCTL);
+
+ /*
+ * We don't know where this is set in the SEEPROM or by the
+ * BIOS, so we default to 100%. On Ultra2 controllers, use 75%
+ * instead.
+ */
+ if (temp_p->features & AHC_ULTRA2)
+ {
+ aic_outb(temp_p, RD_DFTHRSH_75 | WR_DFTHRSH_75, DFF_THRSH);
+ }
+ else
+ {
+ aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
+ }
+
+ if (aic7xxx_extended)
+ temp_p->flags |= AHC_EXTEND_TRANS_A;
+
+ if ( list_p == NULL )
+ {
+ list_p = current_p = temp_p;
+ }
+ else
+ {
+ current_p = list_p;
+ while(current_p->next != NULL)
+ current_p = current_p->next;
+ current_p->next = temp_p;
+ }
+ temp_p->next = NULL;
+ found++;
+ } /* Found an Adaptec PCI device. */
+ else /* Well, we found one, but we couldn't get any memory */
+ {
+ printk("aic7xxx: Found <%s>\n",
+ board_names[aic_pdevs[i].board_name_index]);
+ printk(KERN_INFO "aic7xxx: Unable to allocate device memory, "
+ "skipping.\n");
+ }
+ } /* while(pdev=....) */
+ } /* for PCI_DEVICES */
+ } /* PCI BIOS present */
+#endif CONFIG_PCI
+ /*
+ * Now, we re-order the probed devices by BIOS address and BUS class.
+ * In general, we follow this algorithm to make the adapters show up
+ * in the same order under linux that the computer finds them.
+ * 1: All VLB/EISA cards with BIOS_ENABLED first, according to BIOS
+ * address, going from lowest to highest.
+ * 2: All PCI controllers with BIOS_ENABLED next, according to BIOS
+ * address, going from lowest to highest.
+ * 3: Remaining VLB/EISA controllers going in slot order.
+ * 4: Remaining PCI controllers, going in PCI device order (reversable)
+ */
+
+ {
+ struct aic7xxx_host *sort_list[4] = { NULL, NULL, NULL, NULL };
+ struct aic7xxx_host *vlb, *pci;
+ struct aic7xxx_host *prev_p;
+ struct aic7xxx_host *p;
+ unsigned char left;
+
+ prev_p = vlb = pci = NULL;
+
+ temp_p = list_p;
+ while (temp_p != NULL)
+ {
+ switch(temp_p->chip & ~AHC_CHIPID_MASK)
+ {
+ case AHC_EISA:
+ case AHC_VL:
+ {
+ p = temp_p;
+ if (p->flags & AHC_BIOS_ENABLED)
+ vlb = sort_list[0];
+ else
+ vlb = sort_list[2];
+
+ if (vlb == NULL)
+ {
+ vlb = temp_p;
+ temp_p = temp_p->next;
+ vlb->next = NULL;
+ }
+ else
+ {
+ current_p = vlb;
+ prev_p = NULL;
+ while ( (current_p != NULL) &&
+ (current_p->bios_address < temp_p->bios_address))
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ if (prev_p != NULL)
+ {
+ prev_p->next = temp_p;
+ temp_p = temp_p->next;
+ prev_p->next->next = current_p;
+ }
+ else
+ {
+ vlb = temp_p;
+ temp_p = temp_p->next;
+ vlb->next = current_p;
+ }
+ }
+
+ if (p->flags & AHC_BIOS_ENABLED)
+ sort_list[0] = vlb;
+ else
+ sort_list[2] = vlb;
+
+ break;
+ }
+ default: /* All PCI controllers fall through to default */
+ {
+
+ p = temp_p;
+ if (p->flags & AHC_BIOS_ENABLED)
+ pci = sort_list[1];
+ else
+ pci = sort_list[3];
+
+ if (pci == NULL)
+ {
+ pci = temp_p;
+ temp_p = temp_p->next;
+ pci->next = NULL;
+ }
+ else
+ {
+ current_p = pci;
+ prev_p = NULL;
+ if (!aic7xxx_reverse_scan)
+ {
+ while ( (current_p != NULL) &&
+ ( (PCI_SLOT(current_p->pci_device_fn) |
+ (current_p->pci_bus << 8)) <
+ (PCI_SLOT(temp_p->pci_device_fn) |
+ (temp_p->pci_bus << 8)) ) )
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ }
+ else
+ {
+ while ( (current_p != NULL) &&
+ ( (PCI_SLOT(current_p->pci_device_fn) |
+ (current_p->pci_bus << 8)) >
+ (PCI_SLOT(temp_p->pci_device_fn) |
+ (temp_p->pci_bus << 8)) ) )
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ }
+ /*
+ * Are we dealing with a 7985 where we need to sort the
+ * channels as well, if so, the bios_address values should
+ * be the same
+ */
+ if ( (current_p) && (temp_p->flags & AHC_MULTI_CHANNEL) &&
+ (temp_p->pci_bus == current_p->pci_bus) &&
+ (PCI_SLOT(temp_p->pci_device_fn) ==
+ PCI_SLOT(current_p->pci_device_fn)) )
+ {
+ if (temp_p->flags & AHC_CHNLB)
+ {
+ if ( !(temp_p->flags & AHC_CHANNEL_B_PRIMARY) )
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ }
+ else
+ {
+ if (temp_p->flags & AHC_CHANNEL_B_PRIMARY)
+ {
+ prev_p = current_p;
+ current_p = current_p->next;
+ }
+ }
+ }
+ if (prev_p != NULL)
+ {
+ prev_p->next = temp_p;
+ temp_p = temp_p->next;
+ prev_p->next->next = current_p;
+ }
+ else
+ {
+ pci = temp_p;
+ temp_p = temp_p->next;
+ pci->next = current_p;
+ }
+ }
+
+ if (p->flags & AHC_BIOS_ENABLED)
+ sort_list[1] = pci;
+ else
+ sort_list[3] = pci;
+
+ break;
+ }
+ } /* End of switch(temp_p->type) */
+ } /* End of while (temp_p != NULL) */
+ /*
+ * At this point, the cards have been broken into 4 sorted lists, now
+ * we run through the lists in order and register each controller
+ */
+ {
+ int i;
+
+ left = found;
+ for (i=0; i<NUMBER(sort_list); i++)
+ {
+ temp_p = sort_list[i];
+ while(temp_p != NULL)
+ {
+ template->name = board_names[temp_p->board_name_index];
+ p = aic7xxx_alloc(template, temp_p);
+ if (p != NULL)
+ {
+ p->instance = found - left;
+ if (aic7xxx_register(template, p, (--left)) == 0)
+ {
+ found--;
+ aic7xxx_release(p->host);
+ scsi_unregister(p->host);
+ }
+ else if (aic7xxx_dump_card)
+ {
+ pause_sequencer(p);
+ aic7xxx_print_card(p);
+ aic7xxx_print_scratch_ram(p);
+ unpause_sequencer(p, TRUE);
+ }
+ }
+ current_p = temp_p;
+ temp_p = (struct aic7xxx_host *)temp_p->next;
+ kfree(current_p);
+ }
+ }
+ }
+ }
+ return (found);
+}
+
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_negotiation_complete
+ *
+ * Description:
+ * Handle completion events for our Negotiation commands. Clear out the
+ * struct and get it ready for its next use.
+ *-F*************************************************************************/
+static void
+aic7xxx_negotiation_complete(Scsi_Cmnd *cmd)
+{
+ return;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_build_negotiation_command
+ *
+ * Description:
+ * Build a Scsi_Cmnd structure to perform negotiation with or else send
+ * a pre-built command specifically for this purpose.
+ *-F*************************************************************************/
+static void
+aic7xxx_build_negotiation_cmnd(struct aic7xxx_host *p, Scsi_Cmnd *old_cmd,
+ int tindex)
+{
+
+ if ( (p->needwdtr & (1<<tindex)) && !(p->wdtr_pending & (1<<tindex)) )
+ {
+ if(p->dev_wdtr_cmnd[tindex] == NULL)
+ {
+ Scsi_Cmnd *cmd;
+
+ if (!(p->dev_wdtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) )
+ {
+ return;
+ }
+ cmd = p->dev_wdtr_cmnd[tindex];
+ memset(cmd, 0, sizeof(Scsi_Cmnd));
+ memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd));
+ memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd));
+ memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd));
+ cmd->lun = 0;
+ cmd->request_bufflen = 0;
+ cmd->request_buffer = NULL;
+ cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0;
+ cmd->bufflen = 0;
+ cmd->buffer = NULL;
+ cmd->underflow = 0;
+ cmd->cmd_len = 6;
+ }
+ /*
+ * Before sending this thing out, we also amke the cmd->next pointer
+ * point to the real command so we can stuff any possible SENSE data
+ * intp the real command instead of this fake command. This has to be
+ * done each time the command is built, not just the first time, hence
+ * it's outside of the above if()...
+ */
+ p->dev_wdtr_cmnd[tindex]->next = old_cmd;
+ aic7xxx_queue(p->dev_wdtr_cmnd[tindex],
+ aic7xxx_negotiation_complete);
+ }
+ else if ( (p->needsdtr & (1<<tindex)) && !(p->sdtr_pending & (1<<tindex)) &&
+ !(p->wdtr_pending & (1<<tindex)) )
+ {
+ if(p->dev_sdtr_cmnd[tindex] == NULL)
+ {
+ Scsi_Cmnd *cmd;
+
+ if (!(p->dev_sdtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) )
+ {
+ return;
+ }
+ cmd = p->dev_sdtr_cmnd[tindex];
+ memset(cmd, 0, sizeof(Scsi_Cmnd));
+ memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd));
+ memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd));
+ memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd));
+ cmd->lun = 0;
+ cmd->request_bufflen = 0;
+ cmd->request_buffer = NULL;
+ cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0;
+ cmd->bufflen = 0;
+ cmd->buffer = NULL;
+ cmd->underflow = 0;
+ cmd->cmd_len = 6;
+ }
+ /*
+ * Before sending this thing out, we also amke the cmd->next pointer
+ * point to the real command so we can stuff any possible SENSE data
+ * intp the real command instead of this fake command. This has to be
+ * done each time the command is built, not just the first time, hence
+ * it's outside of the above if()...
+ */
+ p->dev_sdtr_cmnd[tindex]->next = old_cmd;
+ aic7xxx_queue(p->dev_sdtr_cmnd[tindex],
+ aic7xxx_negotiation_complete);
+ }
+}
+
+#endif
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_print_scb
+ *
+ * Description:
+ * Dump the byte codes for an about to be sent SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ int i;
+ unsigned char *x;
+
+ x = (unsigned char *)&scb->hscb->control;
+
+ for(i=0; i<32; i++)
+ {
+ printk("%02x ", x[i]);
+ }
+ printk("\n");
+}
+#endif
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_buildscb
+ *
+ * Description:
+ * Build a SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
+ struct aic7xxx_scb *scb)
+{
+ unsigned short mask;
+ struct aic7xxx_hwscb *hscb;
+
+ mask = (0x01 << TARGET_INDEX(cmd));
+ hscb = scb->hscb;
+
+ /*
+ * Setup the control byte if we need negotiation and have not
+ * already requested it.
+ */
+ hscb->control = 0;
+ scb->tag_action = 0;
+ if (p->discenable & mask)
+ {
+ hscb->control |= DISCENB;
+ if (p->tagenable & mask)
+ {
+ cmd->tag = hscb->tag;
+ p->dev_commands_sent[TARGET_INDEX(cmd)]++;
+ if (p->dev_commands_sent[TARGET_INDEX(cmd)] < 200)
+ {
+ hscb->control |= MSG_SIMPLE_Q_TAG;
+ scb->tag_action = MSG_SIMPLE_Q_TAG;
+ }
+ else
+ {
+ if (p->orderedtag & mask)
+ {
+ hscb->control |= MSG_ORDERED_Q_TAG;
+ scb->tag_action = MSG_ORDERED_Q_TAG;
+ }
+ else
+ {
+ hscb->control |= MSG_SIMPLE_Q_TAG;
+ scb->tag_action = MSG_SIMPLE_Q_TAG;
+ }
+ p->dev_commands_sent[TARGET_INDEX(cmd)] = 0;
+ }
+ }
+ }
+ if (p->dev_flags[TARGET_INDEX(cmd)] & DEVICE_SCANNED)
+ {
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+ if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) )
+ {
+ if (cmd == p->dev_wdtr_cmnd[TARGET_INDEX(cmd)])
+ {
+ p->wdtr_pending |= mask;
+ scb->flags |= SCB_MSGOUT_WDTR;
+ hscb->control &= DISCENB;
+ hscb->control |= MK_MESSAGE;
+ scb->tag_action = 0;
+ }
+ else
+ {
+ aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd));
+ }
+ }
+ else if ( (p->needsdtr & mask) && !(p->sdtr_pending & mask) &&
+ !(p->wdtr_pending & mask) )
+ {
+ if (cmd == p->dev_sdtr_cmnd[TARGET_INDEX(cmd)])
+ {
+ p->sdtr_pending |= mask;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ hscb->control &= DISCENB;
+ hscb->control |= MK_MESSAGE;
+ scb->tag_action = 0;
+ }
+ else if (cmd != p->dev_wdtr_cmnd[TARGET_INDEX(cmd)])
+ {
+ aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd));
+ }
+ }
+#else
+ if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) &&
+ !(p->sdtr_pending & mask) && (cmd->lun == 0) )
+ {
+ p->wdtr_pending |= mask;
+ scb->flags |= SCB_MSGOUT_WDTR;
+ hscb->control &= DISCENB;
+ hscb->control |= MK_MESSAGE;
+ scb->tag_action = 0;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Building WDTR command.\n", p->host_no,
+ CTL_OF_CMD(cmd));
+#endif
+ }
+ else if ( (p->needsdtr & mask) && !(p->wdtr_pending & mask) &&
+ !(p->sdtr_pending & mask) && (cmd->lun == 0) )
+ {
+ p->sdtr_pending |= mask;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ hscb->control &= DISCENB;
+ hscb->control |= MK_MESSAGE;
+ scb->tag_action = 0;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (aic7xxx_verbose > 0xffff)
+ printk(INFO_LEAD "Building SDTR command.\n", p->host_no,
+ CTL_OF_CMD(cmd));
+#endif
+ }
+#endif
+ }
+ hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
+ ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
+
+ /*
+ * The interpretation of request_buffer and request_bufflen
+ * changes depending on whether or not use_sg is zero; a
+ * non-zero use_sg indicates the number of elements in the
+ * scatter-gather array.
+ */
+
+ /*
+ * XXX - this relies on the host data being stored in a
+ * little-endian format.
+ */
+ hscb->SCSI_cmd_length = cmd->cmd_len;
+ hscb->SCSI_cmd_pointer = cpu_to_le32(VIRT_TO_BUS(cmd->cmnd));
+
+ if (cmd->use_sg)
+ {
+ struct scatterlist *sg; /* Must be mid-level SCSI code scatterlist */
+
+ /*
+ * We must build an SG list in adapter format, as the kernel's SG list
+ * cannot be used directly because of data field size (__alpha__)
+ * differences and the kernel SG list uses virtual addresses where
+ * we need physical addresses.
+ */
+ int i;
+
+ sg = (struct scatterlist *)cmd->request_buffer;
+ scb->sg_length = 0;
+ /*
+ * Copy the segments into the SG array. NOTE!!! - We used to
+ * have the first entry both in the data_pointer area and the first
+ * SG element. That has changed somewhat. We still have the first
+ * entry in both places, but now we download the address of
+ * scb->sg_list[1] instead of 0 to the sg pointer in the hscb.
+ */
+ for (i = 0; i < cmd->use_sg; i++)
+ {
+ scb->sg_list[i].address = cpu_to_le32(VIRT_TO_BUS(sg[i].address));
+ scb->sg_list[i].length = cpu_to_le32(sg[i].length);
+ scb->sg_length += sg[i].length;
+ }
+ /* Copy the first SG into the data pointer area. */
+ hscb->data_pointer = scb->sg_list[0].address;
+ hscb->data_count = scb->sg_list[0].length;
+ scb->sg_count = cmd->use_sg;
+ hscb->SG_segment_count = cmd->use_sg;
+ hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[1]));
+
+ }
+ else
+ {
+ if (cmd->request_bufflen)
+ {
+ scb->sg_count = 1;
+ scb->sg_list[0].address = cpu_to_le32(VIRT_TO_BUS(cmd->request_buffer));
+ scb->sg_list[0].length = cpu_to_le32(cmd->request_bufflen);
+ scb->sg_length = cmd->request_bufflen;
+ hscb->SG_segment_count = 1;
+ hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[0]));
+ hscb->data_count = scb->sg_list[0].length;
+ hscb->data_pointer = scb->sg_list[0].address;
+ }
+ else
+ {
+ scb->sg_count = 0;
+ scb->sg_length = 0;
+ hscb->SG_segment_count = 0;
+ hscb->SG_list_pointer = 0;
+ hscb->data_count = 0;
+ hscb->data_pointer = 0;
+ }
+ }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if((cmd->cmnd[0] == TEST_UNIT_READY) && (aic7xxx_verbose & VERBOSE_PROBE2))
+ {
+ aic7xxx_print_scb(p, scb);
+ }
+#endif
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_queue
+ *
+ * Description:
+ * Queue a SCB to the controller.
+ *-F*************************************************************************/
+int
+aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
+{
+ struct aic7xxx_host *p;
+ struct aic7xxx_scb *scb;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ int tindex = TARGET_INDEX(cmd);
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned long cpu_flags = 0;
+#endif
+
+ p = (struct aic7xxx_host *) cmd->host->hostdata;
+ /*
+ * Check to see if channel was scanned.
+ */
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+ if (!(p->flags & AHC_A_SCANNED) && (cmd->channel == 0))
+ {
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(INFO_LEAD "Scanning channel for devices.\n",
+ p->host_no, 0, -1, -1);
+ p->flags |= AHC_A_SCANNED;
+ }
+ else
+ {
+ if (!(p->flags & AHC_B_SCANNED) && (cmd->channel == 1))
+ {
+ if (aic7xxx_verbose & VERBOSE_PROBE2)
+ printk(INFO_LEAD "Scanning channel for devices.\n",
+ p->host_no, 1, -1, -1);
+ p->flags |= AHC_B_SCANNED;
+ }
+ }
+
+ if (p->dev_active_cmds[tindex] > (cmd->device->queue_depth + 1))
+ {
+ printk(WARN_LEAD "Commands queued exceeds queue "
+ "depth, active=%d\n",
+ p->host_no, CTL_OF_CMD(cmd),
+ p->dev_active_cmds[tindex]);
+ if ( p->dev_active_cmds[tindex] > 220 )
+ p->dev_active_cmds[tindex] = 0;
+ }
+#endif
+
+ scb = scbq_remove_head(&p->scb_data->free_scbs);
+ if (scb == NULL)
+ {
+ DRIVER_LOCK
+ aic7xxx_allocate_scb(p);
+ DRIVER_UNLOCK
+ scb = scbq_remove_head(&p->scb_data->free_scbs);
+ }
+ if (scb == NULL)
+ {
+ printk(WARN_LEAD "Couldn't get a free SCB.\n", p->host_no,
+ CTL_OF_CMD(cmd));
+ cmd->result = (DID_BUS_BUSY << 16);
+ DRIVER_LOCK
+ aic7xxx_queue_cmd_complete(p, cmd);
+ DRIVER_UNLOCK
+ return 0;
+ }
+ else
+ {
+ scb->cmd = cmd;
+ aic7xxx_position(cmd) = scb->hscb->tag;
+
+ /*
+ * Construct the SCB beforehand, so the sequencer is
+ * paused a minimal amount of time.
+ */
+ aic7xxx_buildscb(p, cmd, scb);
+
+ /*
+ * Make sure the Scsi_Cmnd pointer is saved, the struct it points to
+ * is set up properly, and the parity error flag is reset, then send
+ * the SCB to the sequencer and watch the fun begin.
+ */
+ cmd->scsi_done = fn;
+ cmd->result = DID_OK;
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+ aic7xxx_error(cmd) = DID_OK;
+ aic7xxx_status(cmd) = 0;
+ cmd->host_scribble = NULL;
+
+ scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
+
+ DRIVER_LOCK
+ scbq_insert_tail(&p->waiting_scbs, scb);
+ if ( (p->flags & (AHC_IN_ISR | AHC_IN_ABORT | AHC_IN_RESET)) == 0)
+ {
+ aic7xxx_run_waiting_queues(p);
+ }
+ DRIVER_UNLOCK
+ }
+ return (0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_bus_device_reset
+ *
+ * Description:
+ * Abort or reset the current SCSI command(s). If the scb has not
+ * previously been aborted, then we attempt to send a BUS_DEVICE_RESET
+ * message to the target. If the scb has previously been unsuccessfully
+ * aborted, then we will reset the channel and have all devices renegotiate.
+ * Returns an enumerated type that indicates the status of the operation.
+ *-F*************************************************************************/
+static int
+aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
+{
+ struct aic7xxx_scb *scb;
+ struct aic7xxx_hwscb *hscb;
+ int result = -1;
+ int channel;
+ unsigned char saved_scbptr, lastphase;
+ unsigned char hscb_index;
+ int disconnected;
+
+ scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+ hscb = scb->hscb;
+
+ lastphase = aic_inb(p, LASTPHASE);
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ {
+ printk(INFO_LEAD "Bus Device reset, scb flags 0x%x, ",
+ p->host_no, CTL_OF_SCB(scb), scb->flags);
+ switch (lastphase)
+ {
+ case P_DATAOUT:
+ printk("Data-Out phase\n");
+ break;
+ case P_DATAIN:
+ printk("Data-In phase\n");
+ break;
+ case P_COMMAND:
+ printk("Command phase\n");
+ break;
+ case P_MESGOUT:
+ printk("Message-Out phase\n");
+ break;
+ case P_STATUS:
+ printk("Status phase\n");
+ break;
+ case P_MESGIN:
+ printk("Message-In phase\n");
+ break;
+ default:
+ /*
+ * We're not in a valid phase, so assume we're idle.
+ */
+ printk("while idle, LASTPHASE = 0x%x\n", lastphase);
+ break;
+ }
+ printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
+ "0x%x\n", p->host_no, CTL_OF_SCB(scb),
+ aic_inb(p, SCSISIGI),
+ aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+ aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
+ }
+
+ channel = cmd->channel;
+
+ /*
+ * Send a Device Reset Message:
+ * The target that is holding up the bus may not be the same as
+ * the one that triggered this timeout (different commands have
+ * different timeout lengths). Our strategy here is to queue an
+ * abort message to the timed out target if it is disconnected.
+ * Otherwise, if we have an active target we stuff the message buffer
+ * with an abort message and assert ATN in the hopes that the target
+ * will let go of the bus and go to the mesgout phase. If this
+ * fails, we'll get another timeout a few seconds later which will
+ * attempt a bus reset.
+ */
+ saved_scbptr = aic_inb(p, SCBPTR);
+ disconnected = FALSE;
+
+ if (lastphase != P_BUSFREE)
+ {
+ if (aic_inb(p, SCB_TAG) >= p->scb_data->numscbs)
+ {
+ printk(WARN_LEAD "Invalid SCB ID %d is active, "
+ "SCB flags = 0x%x.\n", p->host_no,
+ CTL_OF_CMD(cmd), scb->hscb->tag, scb->flags);
+ return(SCSI_RESET_ERROR);
+ }
+ if (scb->hscb->tag == aic_inb(p, SCB_TAG))
+ {
+ if ( (lastphase != P_MESGOUT) && (lastphase != P_MESGIN) )
+ {
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Device reset message in "
+ "message buffer\n", p->host_no, CTL_OF_SCB(scb));
+ scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+ aic7xxx_error(scb->cmd) = DID_RESET;
+ p->dev_flags[TARGET_INDEX(scb->cmd)] &=
+ ~DEVICE_SUCCESS;
+ p->dev_flags[TARGET_INDEX(scb->cmd)] |=
+ BUS_DEVICE_RESET_PENDING;
+ /* Send the abort message to the active SCB. */
+ aic_outb(p, HOST_MSG, MSG_OUT);
+ aic_outb(p, lastphase | ATNO, SCSISIGO);
+ return(SCSI_RESET_PENDING);
+ }
+ else
+ {
+ /* We want to send out the message, but it could screw an already */
+ /* in place and being used message. Instead, we return an error */
+ /* to try and start the bus reset phase since this command is */
+ /* probably hung (aborts failed, and now reset is failing). We */
+ /* also make sure to set BUS_DEVICE_RESET_PENDING so we won't try */
+ /* any more on this device, but instead will escalate to a bus or */
+ /* host reset (additionally, we won't try to abort any more). */
+ printk(WARN_LEAD "Device reset, Message buffer "
+ "in use\n", p->host_no, CTL_OF_SCB(scb));
+ scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+ aic7xxx_error(scb->cmd) = DID_RESET;
+ p->dev_flags[TARGET_INDEX(scb->cmd)] &=
+ ~DEVICE_SUCCESS;
+ p->dev_flags[TARGET_INDEX(scb->cmd)] |=
+ BUS_DEVICE_RESET_PENDING;
+ return(SCSI_RESET_ERROR);
+ }
+ }
+ } /* if (last_phase != P_BUSFREE).....indicates we are idle and can work */
+ hscb_index = aic7xxx_find_scb(p, scb);
+ if (hscb_index == SCB_LIST_NULL)
+ {
+ disconnected = (aic7xxx_scb_on_qoutfifo(p, scb)) ? FALSE : TRUE;
+ }
+ else
+ {
+ aic_outb(p, hscb_index, SCBPTR);
+ if (aic_inb(p, SCB_CONTROL) & DISCONNECTED)
+ {
+ disconnected = TRUE;
+ }
+ }
+ if (disconnected)
+ {
+ /*
+ * Simply set the MK_MESSAGE flag and the SEQINT handler will do
+ * the rest on a reconnect.
+ */
+ scb->hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+ p->dev_flags[TARGET_INDEX(scb->cmd)] &= ~DEVICE_SUCCESS;
+ p->dev_flags[TARGET_INDEX(scb->cmd)] |=
+ BUS_DEVICE_RESET_PENDING;
+ if (hscb_index != SCB_LIST_NULL)
+ {
+ unsigned char scb_control;
+
+ aic_outb(p, hscb_index, SCBPTR);
+ scb_control = aic_inb(p, SCB_CONTROL);
+ aic_outb(p, scb_control | MK_MESSAGE, SCB_CONTROL);
+ }
+ /*
+ * Actually requeue this SCB in case we can select the
+ * device before it reconnects. If the transaction we
+ * want to abort is not tagged, then this will be the only
+ * outstanding command and we can simply shove it on the
+ * qoutfifo and be done. If it is tagged, then it goes right
+ * in with all the others, no problem :) We need to add it
+ * to the qinfifo and let the sequencer know it is there.
+ * Now, the only problem left to deal with is, *IF* this
+ * command completes, in spite of the MK_MESSAGE bit in the
+ * control byte, then we need to pick that up in the interrupt
+ * routine and clean things up. This *shouldn't* ever happen.
+ */
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Queueing device reset "
+ "command.\n", p->host_no, CTL_OF_SCB(scb));
+ p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+ if (p->features & AHC_QUEUE_REGS)
+ aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+ else
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+ scb->flags |= SCB_QUEUED_ABORT;
+ result = SCSI_RESET_PENDING;
+ }
+ else if (result == -1)
+ {
+ result = SCSI_RESET_ERROR;
+ }
+ aic_outb(p, saved_scbptr, SCBPTR);
+ return (result);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_panic_abort
+ *
+ * Description:
+ * Abort the current SCSI command(s).
+ *-F*************************************************************************/
+void
+aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+ int i, mask, found, need_tag;
+ struct aic7xxx_scb *scb;
+ unsigned char qinpos, hscbp;
+
+ found = FALSE;
+#endif
+
+ printk("aic7xxx driver version %s/%s\n", AIC7XXX_C_VERSION,
+ UTS_RELEASE);
+ printk("Controller type:\n %s\n", board_names[p->board_name_index]);
+ printk("p->flags=0x%x, p->chip=0x%x, p->features=0x%x, "
+ "sequencer %s paused\n",
+ p->flags, p->chip, p->features,
+ (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" );
+ pause_sequencer(p);
+ disable_irq(p->irq);
+ aic7xxx_print_card(p);
+ aic7xxx_print_scratch_ram(p);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+ for(i=0; i<MAX_TARGETS; i++)
+ {
+ if(p->dev_flags[i] & DEVICE_PRESENT)
+ {
+ mask = (0x01 << i);
+ printk(INFO_LEAD "dev_flags=0x%x, WDTR:%c/%c/%c, SDTR:%c/%c/%c,"
+ " q_depth=%d:%d:%d\n",
+ p->host_no, 0, i, 0, p->dev_flags[i],
+ (p->wdtr_pending & mask) ? 'Y' : 'N',
+ (p->needwdtr & mask) ? 'Y' : 'N',
+ (p->needwdtr_copy & mask) ? 'Y' : 'N',
+ (p->sdtr_pending & mask) ? 'Y' : 'N',
+ (p->needsdtr & mask) ? 'Y' : 'N',
+ (p->needsdtr_copy & mask) ? 'Y' : 'N',
+ p->dev_active_cmds[i],
+ p->dev_max_queue_depth[i], p->dev_mid_level_queue_depth[i]);
+ printk(INFO_LEAD "targ_scsirate=0x%x", p->host_no, 0, i, 0,
+ aic_inb(p, TARG_SCSIRATE + i));
+ if (p->features & AHC_ULTRA2)
+ printk(", targ_offset=%d", aic_inb(p, TARG_OFFSET + i));
+ printk("\n");
+ }
+ }
+ /*
+ * Search for this command and see if we can't track it down, it's the
+ * one causing the timeout. Print out this command first, then all other
+ * active commands afterwords.
+ */
+ need_tag = -1;
+ if ( cmd )
+ {
+ scb = p->scb_data->scb_array[aic7xxx_position(cmd)];
+ if ( (scb->flags & SCB_ACTIVE) && (scb->cmd == cmd) )
+ {
+ printk("Timed out command is scb #%d:\n", scb->hscb->tag);
+ printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag,
+ scb->flags, scb->hscb->control, scb->hscb->target_channel_lun,
+ (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" );
+ need_tag = scb->hscb->tag;
+ if (scb->flags & SCB_WAITINGQ) found=TRUE;
+ }
+ }
+ printk("QINFIFO: (TAG) ");
+ qinpos = aic_inb(p, QINPOS);
+ while ( qinpos != p->qinfifonext )
+ {
+ if (p->qinfifo[qinpos] == need_tag)
+ found=TRUE;
+ printk("%d ", p->qinfifo[qinpos++]);
+ }
+ printk("\n");
+ printk("Current SCB: (SCBPTR/TAG/CONTROL) %d/%d/0x%x\n", aic_inb(p, SCBPTR),
+ aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL) );
+ if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE;
+ printk("WAITING_SCBS: (SCBPTR/TAG/CONTROL) %d->",
+ hscbp = aic_inb(p, WAITING_SCBH));
+ while (hscbp != SCB_LIST_NULL)
+ {
+ aic_outb(p, hscbp, SCBPTR);
+ printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL));
+ hscbp = aic_inb(p, SCB_NEXT);
+ if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE;
+ }
+ printk("\n");
+ printk("DISCONNECTED_SCBS: (SCBPTR/TAG/CONTROL) %d->",
+ hscbp = aic_inb(p, DISCONNECTED_SCBH));
+ while (hscbp != SCB_LIST_NULL)
+ {
+ aic_outb(p, hscbp, SCBPTR);
+ printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL));
+ hscbp = aic_inb(p, SCB_NEXT);
+ if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE;
+ }
+ printk("\n");
+ printk("FREE_SCBS: (SCBPTR/TAG/CONTROL) %d->",
+ hscbp = aic_inb(p, FREE_SCBH));
+ while (hscbp != SCB_LIST_NULL)
+ {
+ aic_outb(p, hscbp, SCBPTR);
+ printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL));
+ hscbp = aic_inb(p, SCB_NEXT);
+ }
+ printk("\n");
+
+ if (found == FALSE)
+ {
+ /*
+ * We haven't found the offending SCB yet, and it should be around
+ * somewhere, so go look for it in the cards SCBs.
+ */
+ printk("SCBPTR CONTROL TAG PREV NEXT\n");
+ for(i=0; i<p->scb_data->maxhscbs; i++)
+ {
+ aic_outb(p, i, SCBPTR);
+ printk(" %3d %02x %02x %02x %02x\n", i,
+ aic_inb(p, SCB_CONTROL), aic_inb(p, SCB_TAG),
+ aic_inb(p, SCB_PREV), aic_inb(p, SCB_NEXT));
+ }
+ }
+
+
+ for (i=0; i < p->scb_data->numscbs; i++)
+ {
+ scb = p->scb_data->scb_array[i];
+ if ( (scb->flags & SCB_ACTIVE) && (scb->cmd != cmd) )
+ {
+ printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag,
+ scb->flags, scb->hscb->control, scb->hscb->target_channel_lun,
+ (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" );
+ }
+ }
+#endif
+ sti();
+ for(;;) barrier();
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_abort
+ *
+ * Description:
+ * Abort the current SCSI command(s).
+ *-F*************************************************************************/
+int
+aic7xxx_abort(Scsi_Cmnd *cmd)
+{
+ struct aic7xxx_scb *scb = NULL;
+ struct aic7xxx_host *p;
+ int result, found=0;
+ unsigned char tmp_char, saved_hscbptr, next_hscbptr, prev_hscbptr;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned long cpu_flags = 0;
+#endif
+ Scsi_Cmnd *cmd_next, *cmd_prev;
+
+ p = (struct aic7xxx_host *) cmd->host->hostdata;
+ scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+
+ /*
+ * I added a new config option to the driver: "panic_on_abort" that will
+ * cause the driver to panic and the machine to stop on the first abort
+ * or reset call into the driver. At that point, it prints out a lot of
+ * usefull information for me which I can then use to try and debug the
+ * problem. Simply enable the boot time prompt in order to activate this
+ * code.
+ */
+ if (aic7xxx_panic_on_abort)
+ aic7xxx_panic_abort(p, cmd);
+
+ DRIVER_LOCK
+
+/*
+ * Run the isr to grab any command in the QOUTFIFO and any other misc.
+ * assundry tasks. This should also set up the bh handler if there is
+ * anything to be done, but it won't run until we are done here since
+ * we are following a straight code path without entering the scheduler
+ * code.
+ */
+
+ pause_sequencer(p);
+ while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
+ {
+ aic7xxx_isr(p->irq, p, (void *)NULL);
+ pause_sequencer(p);
+ aic7xxx_done_cmds_complete(p);
+ }
+
+ if ((scb == NULL) || (cmd->serial_number != cmd->serial_number_at_timeout))
+ /* Totally bogus cmd since it points beyond our */
+ { /* valid SCB range or doesn't even match it's own*/
+ /* timeout serial number. */
+ if (aic7xxx_verbose & VERBOSE_ABORT_MID)
+ printk(INFO_LEAD "Abort called with bogus Scsi_Cmnd "
+ "pointer.\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_ABORT_NOT_RUNNING);
+ }
+ if (scb->cmd != cmd) /* Hmmm...either this SCB is currently free with a */
+ { /* NULL cmd pointer (NULLed out when freed) or it */
+ /* has already been recycled for another command */
+ /* Either way, this SCB has nothing to do with this*/
+ /* command and we need to deal with cmd without */
+ /* touching the SCB. */
+ /* The theory here is to return a value that will */
+ /* make the queued for complete command actually */
+ /* finish successfully, or to indicate that we */
+ /* don't have this cmd any more and the mid level */
+ /* code needs to find it. */
+ cmd_next = p->completeq.head;
+ cmd_prev = NULL;
+ while (cmd_next != NULL)
+ {
+ if (cmd_next == cmd)
+ {
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "Abort called for command "
+ "on completeq, completing.\n", p->host_no, CTL_OF_CMD(cmd));
+ if ( cmd_prev == NULL )
+ p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
+ else
+ cmd_prev->host_scribble = cmd_next->host_scribble;
+ cmd_next->scsi_done(cmd_next);
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_ABORT_NOT_RUNNING); /* It's already back as a successful
+ * completion */
+ }
+ cmd_prev = cmd_next;
+ cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
+ }
+ if (aic7xxx_verbose & VERBOSE_ABORT_MID)
+ printk(INFO_LEAD "Abort called for already completed"
+ " command.\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_ABORT_NOT_RUNNING);
+ }
+
+/* At this point we know the following:
+ * the SCB pointer is valid
+ * the command pointer passed in to us and the scb->cmd pointer match
+ * this then means that the command we need to abort is the same as the
+ * command held by the scb pointer and is a valid abort request.
+ * Now, we just have to figure out what to do from here. Current plan is:
+ * if we have already been here on this command, escalate to a reset
+ * if scb is on waiting list or QINFIFO, send it back as aborted, but
+ * we also need to be aware of the possibility that we could be using
+ * a faked negotiation command that is holding this command up, if
+ * so we need to take care of that command instead, which means we
+ * would then treat this one like it was sitting around disconnected
+ * instead.
+ * if scb is on WAITING_SCB list in sequencer, free scb and send back
+ * if scb is disconnected and not completed, abort with abort message
+ * if scb is currently running, then it may be causing the bus to hang
+ * so we want a return value that indicates a reset would be appropriate
+ * if the command does not finish shortly
+ * if scb is already complete but not on completeq, we're screwed because
+ * this can't happen (except if the command is in the QOUTFIFO, in which
+ * case we would like it to complete successfully instead of having to
+ * to be re-done)
+ * All other scenarios already dealt with by previous code.
+ */
+
+ if ( scb->flags & (SCB_ABORT | SCB_RESET | SCB_QUEUED_ABORT) )
+ {
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB aborted once already, "
+ "escalating.\n", p->host_no, CTL_OF_SCB(scb));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_ABORT_SNOOZE);
+ }
+ if ( (p->flags & (AHC_RESET_PENDING | AHC_ABORT_PENDING)) ||
+ (p->dev_flags[TARGET_INDEX(scb->cmd)] &
+ BUS_DEVICE_RESET_PENDING) )
+ {
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "Reset/Abort pending for this "
+ "device, not wasting our time.\n", p->host_no, CTL_OF_SCB(scb));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_ABORT_PENDING);
+ }
+
+ found = 0;
+ p->flags |= AHC_IN_ABORT;
+ if (aic7xxx_verbose & VERBOSE_ABORT)
+ printk(INFO_LEAD "Aborting scb %d, flags 0x%x\n",
+ p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+
+/*
+ * First, let's check to see if the currently running command is our target
+ * since if it is, the return is fairly easy and quick since we don't want
+ * to touch the command in case it might complete, but we do want a timeout
+ * in case it's actually hung, so we really do nothing, but tell the mid
+ * level code to reset the timeout.
+ */
+
+ if ( scb->hscb->tag == aic_inb(p, SCB_TAG) )
+ {
+ /*
+ * Check to see if the sequencer is just sitting on this command, or
+ * if it's actively being run.
+ */
+ result = aic_inb(p, LASTPHASE);
+ switch (result)
+ {
+ case P_DATAOUT: /* For any of these cases, we can assume we are */
+ case P_DATAIN: /* an active command and act according. For */
+ case P_COMMAND: /* anything else we are going to fall on through*/
+ case P_STATUS: /* The SCSI_ABORT_SNOOZE will give us two abort */
+ case P_MESGOUT: /* chances to finish and then escalate to a */
+ case P_MESGIN: /* reset call */
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB is currently active. "
+ "Waiting on completion.\n", p->host_no, CTL_OF_SCB(scb));
+ unpause_sequencer(p, FALSE);
+ p->flags &= ~AHC_IN_ABORT;
+ scb->flags |= SCB_RECOVERY_SCB; /* Note the fact that we've been */
+ p->flags |= AHC_ABORT_PENDING; /* here so we will know not to */
+ DRIVER_UNLOCK /* muck with other SCBs if this */
+ return(SCSI_ABORT_PENDING); /* one doesn't complete and clear */
+ break; /* out. */
+ default:
+ break;
+ }
+ }
+
+ if ((found == 0) && (scb->flags & SCB_WAITINGQ))
+ {
+ int tindex = TARGET_INDEX(cmd);
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+ unsigned short mask;
+
+ mask = (1 << tindex);
+
+ if (p->wdtr_pending & mask)
+ {
+ if (p->dev_wdtr_cmnd[tindex]->next != cmd)
+ found = 1;
+ else
+ found = 0;
+ }
+ else if (p->sdtr_pending & mask)
+ {
+ if (p->dev_sdtr_cmnd[tindex]->next != cmd)
+ found = 1;
+ else
+ found = 0;
+ }
+ else
+ {
+ found = 1;
+ }
+ if (found == 0)
+ {
+ /*
+ * OK..this means the command we are currently getting an abort
+ * for has an outstanding negotiation command in front of it.
+ * We don't really have a way to tie back into the negotiation
+ * commands, so we just send this back as pending, then it
+ * will get reset in 2 seconds.
+ */
+ unpause_sequencer(p, TRUE);
+ scb->flags |= SCB_ABORT;
+ DRIVER_UNLOCK
+ return(SCSI_ABORT_PENDING);
+ }
+#endif
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB found on waiting list and "
+ "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+ scbq_remove(&p->waiting_scbs, scb);
+ scbq_remove(&p->delayed_scbs[tindex], scb);
+ p->dev_active_cmds[tindex]++;
+ p->activescbs++;
+ scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
+ scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
+ found = 1;
+ }
+
+/*
+ * We just checked the waiting_q, now for the QINFIFO
+ */
+ if ( found == 0 )
+ {
+ if ( ((found = aic7xxx_search_qinfifo(p, cmd->target,
+ cmd->channel,
+ cmd->lun, scb->hscb->tag, SCB_ABORT | SCB_QUEUED_FOR_DONE,
+ FALSE, NULL)) != 0) &&
+ (aic7xxx_verbose & VERBOSE_ABORT_PROCESS))
+ printk(INFO_LEAD "SCB found in QINFIFO and "
+ "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+ }
+
+/*
+ * QINFIFO, waitingq, completeq done. Next, check WAITING_SCB list in card
+ */
+
+ if ( found == 0 )
+ {
+ unsigned char scb_next_ptr;
+ prev_hscbptr = SCB_LIST_NULL;
+ saved_hscbptr = aic_inb(p, SCBPTR);
+ next_hscbptr = aic_inb(p, WAITING_SCBH);
+ while ( next_hscbptr != SCB_LIST_NULL )
+ {
+ aic_outb(p, next_hscbptr, SCBPTR );
+ if ( scb->hscb->tag == aic_inb(p, SCB_TAG) )
+ {
+ found = 1;
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB found on hardware waiting"
+ " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
+ if ( prev_hscbptr == SCB_LIST_NULL )
+ {
+ aic_outb(p, aic_inb(p, SCB_NEXT), WAITING_SCBH);
+ /* stop the selection since we just
+ * grabbed the scb out from under the
+ * card
+ */
+ aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+ aic_outb(p, CLRSELTIMEO, CLRSINT1);
+ }
+ else
+ {
+ scb_next_ptr = aic_inb(p, SCB_NEXT);
+ aic_outb(p, prev_hscbptr, SCBPTR);
+ aic_outb(p, scb_next_ptr, SCB_NEXT);
+ aic_outb(p, next_hscbptr, SCBPTR);
+ }
+ aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+ aic_outb(p, 0, SCB_CONTROL);
+ aic7xxx_add_curscb_to_free_list(p);
+ scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
+ break;
+ }
+ prev_hscbptr = next_hscbptr;
+ next_hscbptr = aic_inb(p, SCB_NEXT);
+ }
+ aic_outb(p, saved_hscbptr, SCBPTR );
+ }
+
+/*
+ * Hmmm...completeq, QOUTFIFO, QINFIFO, WAITING_SCBH, waitingq all checked.
+ * OK...the sequencer's paused, interrupts are off, and we haven't found the
+ * command anyplace where it could be easily aborted. Time for the hard
+ * work. We also know the command is valid. This essentially means the
+ * command is disconnected, or connected but not into any phases yet, which
+ * we know due to the tests we ran earlier on the current active scb phase.
+ * At this point we can queue the abort tag and go on with life.
+ */
+
+ if ( found == 0 )
+ {
+ p->flags |= AHC_ABORT_PENDING;
+ scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+ scb->hscb->control |= MK_MESSAGE;
+ result=aic7xxx_find_scb(p, scb);
+ if ( result != SCB_LIST_NULL )
+ {
+ saved_hscbptr = aic_inb(p, SCBPTR);
+ aic_outb(p, result, SCBPTR);
+ tmp_char = aic_inb(p, SCB_CONTROL);
+ aic_outb(p, tmp_char | MK_MESSAGE, SCB_CONTROL);
+ aic_outb(p, saved_hscbptr, SCBPTR);
+ }
+ if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+ printk(INFO_LEAD "SCB disconnected. Queueing Abort"
+ " SCB.\n", p->host_no, CTL_OF_SCB(scb));
+ p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+ if (p->features & AHC_QUEUE_REGS)
+ aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+ else
+ aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+ }
+ if (found)
+ {
+ aic7xxx_run_done_queue(p, TRUE);
+ aic7xxx_run_waiting_queues(p);
+ }
+ p->flags &= ~AHC_IN_ABORT;
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+
+/*
+ * On the return value. If we found the command and aborted it, then we know
+ * it's already sent back and there is no reason for a further timeout, so
+ * we use SCSI_ABORT_SUCCESS. On the queued abort side, we aren't so certain
+ * there hasn't been a bus hang or something that might keep the abort from
+ * from completing. Therefore, we use SCSI_ABORT_PENDING. The first time this
+ * is passed back, the timeout on the command gets extended, the second time
+ * we pass this back, the mid level SCSI code calls our reset function, which
+ * would shake loose a hung bus.
+ */
+ if ( found != 0 )
+ return(SCSI_ABORT_SUCCESS);
+ else
+ return(SCSI_ABORT_PENDING);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_reset
+ *
+ * Description:
+ * Resetting the bus always succeeds - is has to, otherwise the
+ * kernel will panic! Try a surgical technique - sending a BUS
+ * DEVICE RESET message - on the offending target before pulling
+ * the SCSI bus reset line.
+ *-F*************************************************************************/
+int
+aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
+{
+ struct aic7xxx_scb *scb = NULL;
+ struct aic7xxx_host *p;
+ int tindex;
+ int result = -1;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+ unsigned long cpu_flags = 0;
+#endif
+#define DEVICE_RESET 0x01
+#define BUS_RESET 0x02
+#define HOST_RESET 0x04
+#define FAIL 0x08
+#define RESET_DELAY 0x10
+ int action;
+ Scsi_Cmnd *cmd_prev, *cmd_next;
+
+
+ if ( cmd == NULL )
+ {
+ printk(KERN_WARNING "(scsi?:?:?:?) Reset called with NULL Scsi_Cmnd "
+ "pointer, failing.\n");
+ return(SCSI_RESET_SNOOZE);
+ }
+
+ p = (struct aic7xxx_host *) cmd->host->hostdata;
+ scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+ tindex = TARGET_INDEX(cmd);
+
+ /*
+ * I added a new config option to the driver: "panic_on_abort" that will
+ * cause the driver to panic and the machine to stop on the first abort
+ * or reset call into the driver. At that point, it prints out a lot of
+ * usefull information for me which I can then use to try and debug the
+ * problem. Simply enable the boot time prompt in order to activate this
+ * code.
+ */
+ if (aic7xxx_panic_on_abort)
+ aic7xxx_panic_abort(p, cmd);
+
+ DRIVER_LOCK
+
+ pause_sequencer(p);
+ while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
+ {
+ aic7xxx_isr(p->irq, p, (void *)NULL );
+ pause_sequencer(p);
+ aic7xxx_done_cmds_complete(p);
+ }
+
+ if (scb == NULL)
+ {
+ if (aic7xxx_verbose & VERBOSE_RESET_MID)
+ printk(INFO_LEAD "Reset called with bogus Scsi_Cmnd"
+ "->SCB mapping, improvising.\n", p->host_no, CTL_OF_CMD(cmd));
+ if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
+ {
+ action = HOST_RESET;
+ }
+ else
+ {
+ action = BUS_RESET;
+ }
+ }
+ else if (scb->cmd != cmd)
+ {
+ if (aic7xxx_verbose & VERBOSE_RESET_MID)
+ printk(INFO_LEAD "Reset called with recycled SCB "
+ "for cmd.\n", p->host_no, CTL_OF_CMD(cmd));
+ cmd_prev = NULL;
+ cmd_next = p->completeq.head;
+ while ( cmd_next != NULL )
+ {
+ if (cmd_next == cmd)
+ {
+ if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+ printk(INFO_LEAD "Reset, found cmd on completeq"
+ ", completing.\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_RESET_NOT_RUNNING);
+ }
+ cmd_prev = cmd_next;
+ cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
+ }
+ if ( !(flags & SCSI_RESET_SYNCHRONOUS) )
+ {
+ if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+ printk(INFO_LEAD "Reset, cmd not found,"
+ " failing.\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_RESET_NOT_RUNNING);
+ }
+ else
+ {
+ if (aic7xxx_verbose & VERBOSE_RESET_MID)
+ printk(INFO_LEAD "Reset called, no scb, "
+ "flags 0x%x\n", p->host_no, CTL_OF_CMD(cmd), flags);
+ scb = NULL;
+ action = HOST_RESET;
+ }
+ }
+ else
+ {
+ if (aic7xxx_verbose & VERBOSE_RESET_MID)
+ printk(INFO_LEAD "Reset called, scb %d, flags "
+ "0x%x\n", p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+ if ( aic7xxx_scb_on_qoutfifo(p, scb) )
+ {
+ if(aic7xxx_verbose & VERBOSE_RESET_RETURN)
+ printk(INFO_LEAD "SCB on qoutfifo, returning.\n", p->host_no,
+ CTL_OF_SCB(scb));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_RESET_NOT_RUNNING);
+ }
+ if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
+ {
+ action = HOST_RESET;
+ }
+ else if ( flags & SCSI_RESET_SUGGEST_BUS_RESET )
+ {
+ action = BUS_RESET;
+ }
+ else
+ {
+ action = DEVICE_RESET;
+ }
+ }
+ if ( (action & DEVICE_RESET) &&
+ (p->dev_flags[tindex] & BUS_DEVICE_RESET_PENDING) )
+ {
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Bus device reset already sent to "
+ "device, escalating.\n", p->host_no, CTL_OF_CMD(cmd));
+ action = BUS_RESET;
+ }
+ if ( (action & DEVICE_RESET) &&
+ (scb->flags & SCB_QUEUED_ABORT) )
+ {
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ {
+ printk(INFO_LEAD "Have already attempted to reach "
+ "device with queued\n", p->host_no, CTL_OF_CMD(cmd));
+ printk(INFO_LEAD "message, will escalate to bus "
+ "reset.\n", p->host_no, CTL_OF_CMD(cmd));
+ }
+ action = BUS_RESET;
+ }
+ if ( (action & DEVICE_RESET) &&
+ (p->flags & (AHC_RESET_PENDING | AHC_ABORT_PENDING)) )
+ {
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Bus device reset stupid when "
+ "other action has failed.\n", p->host_no, CTL_OF_CMD(cmd));
+ action = BUS_RESET;
+ }
+ if ( (action & BUS_RESET) && !(p->features & AHC_TWIN) )
+ {
+ action = HOST_RESET;
+ }
+ if ( ((jiffies - p->dev_last_reset[tindex]) < (HZ * 3)) &&
+ !(action & (HOST_RESET | BUS_RESET)))
+ {
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ {
+ printk(INFO_LEAD "Reset called too soon after last "
+ "reset without requesting\n", p->host_no, CTL_OF_CMD(cmd));
+ printk(INFO_LEAD "bus or host reset, escalating.\n", p->host_no,
+ CTL_OF_CMD(cmd));
+ }
+ action = BUS_RESET;
+ }
+ if ( ((jiffies - p->last_reset) < (HZ * 3)) &&
+ (action & (HOST_RESET | BUS_RESET)) )
+ {
+ if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+ printk(INFO_LEAD "Reset called too soon after "
+ "last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd));
+ action = RESET_DELAY;
+ }
+ if ( (action & (BUS_RESET | HOST_RESET)) && (p->flags & AHC_IN_RESET)
+ && ((jiffies - p->reset_start) > (2 * HZ * 3)) )
+ {
+ printk(KERN_ERR "(scsi%d:%d:%d:%d) Yikes!! Card must have left to go "
+ "back to Adaptec!!\n", p->host_no, CTL_OF_CMD(cmd));
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_RESET_SNOOZE);
+ }
+/*
+ * By this point, we want to already know what we are going to do and
+ * only have the following code implement our course of action.
+ */
+ switch (action)
+ {
+ case RESET_DELAY:
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_RESET_PENDING);
+ break;
+ case FAIL:
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(SCSI_RESET_ERROR);
+ break;
+ case DEVICE_RESET:
+ p->flags |= AHC_IN_RESET;
+ result = aic7xxx_bus_device_reset(p, cmd);
+ aic7xxx_run_done_queue(p, TRUE);
+ /* We can't rely on run_waiting_queues to unpause the sequencer for
+ * PCI based controllers since we use AAP */
+ aic7xxx_run_waiting_queues(p);
+ unpause_sequencer(p, FALSE);
+ p->flags &= ~AHC_IN_RESET;
+ DRIVER_UNLOCK
+ return(result);
+ break;
+ case BUS_RESET:
+ case HOST_RESET:
+ default:
+ p->reset_start = jiffies;
+ p->flags |= AHC_IN_RESET;
+ aic7xxx_reset_channel(p, cmd->channel, TRUE);
+ if ( (p->features & AHC_TWIN) && (action & HOST_RESET) )
+ {
+ aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE);
+ restart_sequencer(p);
+ }
+ p->last_reset = jiffies;
+ if (action != HOST_RESET)
+ result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+ else
+ {
+ result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
+ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE),
+ SIMODE1);
+ aic7xxx_clear_intstat(p);
+ p->flags &= ~AHC_HANDLING_REQINITS;
+ p->msg_type = MSG_TYPE_NONE;
+ p->msg_index = 0;
+ p->msg_len = 0;
+ }
+ aic7xxx_run_done_queue(p, TRUE);
+ p->flags &= ~AHC_IN_RESET;
+ /* We can't rely on run_waiting_queues to unpause the sequencer for
+ * PCI based controllers since we use AAP */
+ aic7xxx_run_waiting_queues(p);
+ unpause_sequencer(p, FALSE);
+ DRIVER_UNLOCK
+ return(result);
+ break;
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_biosparam
+ *
+ * Description:
+ * Return the disk geometry for the given SCSI device.
+ *-F*************************************************************************/
+int
+aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[])
+{
+ int heads, sectors, cylinders;
+ struct aic7xxx_host *p;
+
+ p = (struct aic7xxx_host *) disk->device->host->hostdata;
+
+ /*
+ * XXX - if I could portably find the card's configuration
+ * information, then this could be autodetected instead
+ * of left to a boot-time switch.
+ */
+ heads = 64;
+ sectors = 32;
+ cylinders = disk->capacity / (heads * sectors);
+
+ if ((p->flags & AHC_EXTEND_TRANS_A) && (cylinders > 1024))
+ {
+ heads = 255;
+ sectors = 63;
+ cylinders = disk->capacity / (heads * sectors);
+ }
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ return (0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_release
+ *
+ * Description:
+ * Free the passed in Scsi_Host memory structures prior to unloading the
+ * module.
+ *-F*************************************************************************/
+int
+aic7xxx_release(struct Scsi_Host *host)
+{
+ struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
+ struct aic7xxx_host *next, *prev;
+
+ if(p->irq)
+ free_irq(p->irq, p);
+ release_region(p->base, MAXREG - MINREG);
+#ifdef MMAPIO
+ if(p->maddr)
+ {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+ vfree((void *) (((unsigned long) p->maddr) & PAGE_MASK));
+#else
+ iounmap((void *) (((unsigned long) p->maddr) & PAGE_MASK));
+#endif
+ }
+#endif /* MMAPIO */
+ prev = NULL;
+ next = first_aic7xxx;
+ while(next != NULL)
+ {
+ if(next == p)
+ {
+ if(prev == NULL)
+ first_aic7xxx = next->next;
+ else
+ prev->next = next->next;
+ }
+ else
+ {
+ prev = next;
+ }
+ next = next->next;
+ }
+ aic7xxx_free(p);
+ return(0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_print_card
+ *
+ * Description:
+ * Print out all of the control registers on the card
+ *
+ * NOTE: This function is not yet safe for use on the VLB and EISA
+ * controllers, so it isn't used on those controllers at all.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_card(struct aic7xxx_host *p)
+{
+ int i, j, k, chip;
+ static struct register_ranges {
+ int num_ranges;
+ int range_val[32];
+ } cards_ds[] = {
+ { 0, {0,} }, /* none */
+ {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1f, 0x1f, 0x60, 0x60, /*7771*/
+ 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9b, 0x9f} },
+ { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7850*/
+ 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+ { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7860*/
+ 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+ {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1c, 0x1f, 0x60, 0x60, /*7870*/
+ 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+ {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1a, 0x1c, 0x1f, 0x60, 0x60, /*7880*/
+ 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+ {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7890*/
+ 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
+ 0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
+ 0xfe, 0xff} },
+ {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1b, 0x1f, 0x60, 0x60, /*7895*/
+ 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a,
+ 0x9f, 0x9f, 0xe0, 0xf1} },
+ {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7896*/
+ 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
+ 0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
+ 0xfe, 0xff} },
+ };
+#ifdef CONFIG_PCI
+ static struct register_ranges cards_ns[] = {
+ { 0, {0,} }, /* none */
+ { 0, {0,} }, /* 7771 */
+ { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33,
+ 0x3c, 0x41, 0x43, 0x47} },
+ { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33,
+ 0x3c, 0x41, 0x43, 0x47} },
+ { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x33, 0x3c, 0x41} },
+ { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47} },
+ { 5, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3} },
+ { 6, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47,
+ 0xdc, 0xe3} },
+ { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3,
+ 0xff, 0xff} }
+ };
+#endif
+ chip = p->chip & AHC_CHIPID_MASK;
+ /*
+ * Let's run through the PCI space first....
+ */
+ printk("%s at ",
+ board_names[p->board_name_index]);
+ switch(p->chip & ~AHC_CHIPID_MASK)
+ {
+ case AHC_VL:
+ printk("VLB Slot %d.\n", p->pci_device_fn);
+ break;
+ case AHC_EISA:
+ printk("EISA Slot %d.\n", p->pci_device_fn);
+ break;
+ case AHC_PCI:
+ default:
+ printk("PCI %d/%d.\n", PCI_SLOT(p->pci_device_fn),
+ PCI_FUNC(p->pci_device_fn));
+ break;
+ }
+
+#ifdef CONFIG_PCI
+ {
+ unsigned char temp;
+
+ printk("PCI Dump:\n");
+ k=0;
+ for(i=0; i<cards_ns[chip].num_ranges; i++)
+ {
+ for(j = cards_ns[chip].range_val[ i * 2 ];
+ j <= cards_ns[chip].range_val[ i * 2 + 1 ] ;
+ j++)
+ {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+ pci_read_config_byte(p->pdev, j, &temp);
+#else
+ pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, j, &temp);
+#endif
+ printk("%02x:%02x ", j, temp);
+ if(++k == 13)
+ {
+ printk("\n");
+ k = 0;
+ }
+ }
+ }
+ }
+ if(k != 0)
+ printk("\n");
+#endif /* CONFIG_PCI */
+
+ /*
+ * Now the registers on the card....
+ */
+ printk("Card Dump:\n");
+ k = 0;
+ for(i=0; i<cards_ds[chip].num_ranges; i++)
+ {
+ for(j = cards_ds[chip].range_val[ i * 2 ];
+ j <= cards_ds[chip].range_val[ i * 2 + 1 ] ;
+ j++)
+ {
+ printk("%02x:%02x ", j, aic_inb(p, j));
+ if(++k == 13)
+ {
+ printk("\n");
+ k=0;
+ }
+ }
+ }
+ if(k != 0)
+ printk("\n");
+ if (p->flags & AHC_SEEPROM_FOUND)
+ {
+ unsigned short *sc1;
+ sc1 = (unsigned short *)&p->sc;
+
+ printk("SEEPROM dump.\n");
+ for(i=1; i<=32; i++)
+ {
+ printk("0x%04x", sc1[i-1]);
+ if ( (i % 8) == 0 )
+ printk("\n");
+ else
+ printk(" ");
+ }
+ }
+
+ /*
+ * If this was an Ultra2 controller, then we just hosed the card in terms
+ * of the QUEUE REGS. This function is only called at init time or by
+ * the panic_abort function, so it's safe to assume a generic init time
+ * setting here
+ */
+
+ if(p->features & AHC_QUEUE_REGS)
+ {
+ aic_outb(p, 0, SDSCB_QOFF);
+ aic_outb(p, 0, SNSCB_QOFF);
+ aic_outb(p, 0, HNSCB_QOFF);
+ }
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_print_scratch_ram
+ *
+ * Description:
+ * Print out the scratch RAM values on the card.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_scratch_ram(struct aic7xxx_host *p)
+{
+ int i, k;
+
+ k = 0;
+ printk("Scratch RAM:\n");
+ for(i = SRAM_BASE; i < SEQCTL; i++)
+ {
+ printk("%02x:%02x ", i, aic_inb(p, i));
+ if(++k == 13)
+ {
+ printk("\n");
+ k=0;
+ }
+ }
+ if (p->features & AHC_MORE_SRAM)
+ {
+ for(i = TARG_OFFSET; i < 0x80; i++)
+ {
+ printk("%02x:%02x ", i, aic_inb(p, i));
+ if(++k == 13)
+ {
+ printk("\n");
+ k=0;
+ }
+ }
+ }
+ printk("\n");
+}
+
+
+#include "aic7xxx_proc.c"
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = AIC7XXX;
+
+#include "scsi_module.c"
+#endif
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 2
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -2
+ * c-argdecl-indent: 2
+ * c-label-offset: -2
+ * c-continued-statement-offset: 2
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/aic7xxx.h b/linux/src/drivers/scsi/aic7xxx.h
new file mode 100644
index 00000000..2386d1f4
--- /dev/null
+++ b/linux/src/drivers/scsi/aic7xxx.h
@@ -0,0 +1,114 @@
+/*+M*************************************************************************
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * Copyright (c) 1994 John Aycock
+ * The University of Calgary Department of Computer Science.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: aic7xxx.h,v 1.1 1999/04/26 05:54:18 tb Exp $
+ *-M*************************************************************************/
+#ifndef _aic7xxx_h
+#define _aic7xxx_h
+
+#define AIC7XXX_H_VERSION "3.2.4"
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
+#endif
+
+#if defined(__i386__)
+# define AIC7XXX_BIOSPARAM aic7xxx_biosparam
+#else
+# define AIC7XXX_BIOSPARAM NULL
+#endif
+
+/*
+ * Scsi_Host_Template (see hosts.h) for AIC-7xxx - some fields
+ * to do with card config are filled in after the card is detected.
+ */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,65)
+#define AIC7XXX { \
+ next: NULL, \
+ module: NULL, \
+ proc_dir: NULL, \
+ proc_info: aic7xxx_proc_info, \
+ name: NULL, \
+ detect: aic7xxx_detect, \
+ release: aic7xxx_release, \
+ info: aic7xxx_info, \
+ command: NULL, \
+ queuecommand: aic7xxx_queue, \
+ eh_strategy_handler: NULL, \
+ eh_abort_handler: NULL, \
+ eh_device_reset_handler: NULL, \
+ eh_bus_reset_handler: NULL, \
+ eh_host_reset_handler: NULL, \
+ abort: aic7xxx_abort, \
+ reset: aic7xxx_reset, \
+ slave_attach: NULL, \
+ bios_param: AIC7XXX_BIOSPARAM, \
+ can_queue: 255, /* max simultaneous cmds */\
+ this_id: -1, /* scsi id of host adapter */\
+ sg_tablesize: 0, /* max scatter-gather cmds */\
+ cmd_per_lun: 3, /* cmds per lun (linked cmds) */\
+ present: 0, /* number of 7xxx's present */\
+ unchecked_isa_dma: 0, /* no memory DMA restrictions */\
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 0 \
+}
+#else
+#define AIC7XXX { \
+ next: NULL, \
+ usage_count: NULL, \
+ proc_dir: NULL, \
+ proc_info: aic7xxx_proc_info, \
+ name: NULL, \
+ detect: aic7xxx_detect, \
+ release: aic7xxx_release, \
+ info: aic7xxx_info, \
+ command: NULL, \
+ queuecommand: aic7xxx_queue, \
+ abort: aic7xxx_abort, \
+ reset: aic7xxx_reset, \
+ slave_attach: NULL, \
+ bios_param: AIC7XXX_BIOSPARAM, \
+ can_queue: 255, /* max simultaneous cmds */\
+ this_id: -1, /* scsi id of host adapter */\
+ sg_tablesize: 0, /* max scatter-gather cmds */\
+ cmd_per_lun: 3, /* cmds per lun (linked cmds) */\
+ present: 0, /* number of 7xxx's present */\
+ unchecked_isa_dma: 0, /* no memory DMA restrictions */\
+ use_clustering: ENABLE_CLUSTERING \
+}
+#endif
+
+extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+extern int aic7xxx_biosparam(Disk *, kdev_t, int[]);
+extern int aic7xxx_detect(Scsi_Host_Template *);
+extern int aic7xxx_command(Scsi_Cmnd *);
+extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int);
+extern int aic7xxx_abort(Scsi_Cmnd *);
+extern int aic7xxx_release(struct Scsi_Host *);
+
+extern const char *aic7xxx_info(struct Scsi_Host *);
+
+extern int aic7xxx_proc_info(char *, char **, off_t, int, int, int);
+
+#endif /* _aic7xxx_h */
diff --git a/linux/src/drivers/scsi/aic7xxx/scsi_message.h b/linux/src/drivers/scsi/aic7xxx/scsi_message.h
new file mode 100644
index 00000000..16c40138
--- /dev/null
+++ b/linux/src/drivers/scsi/aic7xxx/scsi_message.h
@@ -0,0 +1,41 @@
+/* Messages (1 byte) */ /* I/T (M)andatory or (O)ptional */
+#define MSG_CMDCOMPLETE 0x00 /* M/M */
+#define MSG_EXTENDED 0x01 /* O/O */
+#define MSG_SAVEDATAPOINTER 0x02 /* O/O */
+#define MSG_RESTOREPOINTERS 0x03 /* O/O */
+#define MSG_DISCONNECT 0x04 /* O/O */
+#define MSG_INITIATOR_DET_ERR 0x05 /* M/M */
+#define MSG_ABORT 0x06 /* O/M */
+#define MSG_MESSAGE_REJECT 0x07 /* M/M */
+#define MSG_NOOP 0x08 /* M/M */
+#define MSG_PARITY_ERROR 0x09 /* M/M */
+#define MSG_LINK_CMD_COMPLETE 0x0a /* O/O */
+#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */
+#define MSG_BUS_DEV_RESET 0x0c /* O/M */
+#define MSG_ABORT_TAG 0x0d /* O/O */
+#define MSG_CLEAR_QUEUE 0x0e /* O/O */
+#define MSG_INIT_RECOVERY 0x0f /* O/O */
+#define MSG_REL_RECOVERY 0x10 /* O/O */
+#define MSG_TERM_IO_PROC 0x11 /* O/O */
+
+/* Messages (2 byte) */
+#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */
+#define MSG_HEAD_OF_Q_TAG 0x21 /* O/O */
+#define MSG_ORDERED_Q_TAG 0x22 /* O/O */
+#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */
+
+/* Identify message */ /* M/M */
+#define MSG_IDENTIFYFLAG 0x80
+#define MSG_IDENTIFY_DISCFLAG 0x40
+#define MSG_IDENTIFY(lun, disc) (((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun))
+#define MSG_ISIDENTIFY(m) ((m) & MSG_IDENTIFYFLAG)
+
+/* Extended messages (opcode and length) */
+#define MSG_EXT_SDTR 0x01
+#define MSG_EXT_SDTR_LEN 0x03
+
+#define MSG_EXT_WDTR 0x03
+#define MSG_EXT_WDTR_LEN 0x02
+#define MSG_EXT_WDTR_BUS_8_BIT 0x00
+#define MSG_EXT_WDTR_BUS_16_BIT 0x01
+#define MSG_EXT_WDTR_BUS_32_BIT 0x02
diff --git a/linux/src/drivers/scsi/aic7xxx/sequencer.h b/linux/src/drivers/scsi/aic7xxx/sequencer.h
new file mode 100644
index 00000000..7c0121e0
--- /dev/null
+++ b/linux/src/drivers/scsi/aic7xxx/sequencer.h
@@ -0,0 +1,135 @@
+/*
+ * Instruction formats for the sequencer program downloaded to
+ * Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997, 1998 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU Public License ("GPL") and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sequencer.h,v 1.1 1999/04/26 05:55:33 tb Exp $
+ */
+
+#ifdef __LITTLE_ENDIAN_BITFIELD
+struct ins_format1 {
+ unsigned int
+ immediate : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
+};
+
+struct ins_format2 {
+ unsigned int
+ shift_control : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
+};
+
+struct ins_format3 {
+ unsigned int
+ immediate : 8,
+ source : 9,
+ address : 10,
+ opcode : 4,
+ parity : 1;
+};
+#elif defined(__BIG_ENDIAN_BITFIELD)
+struct ins_format1 {
+ unsigned int
+ parity : 1,
+ opcode : 4,
+ ret : 1,
+ destination : 9,
+ source : 9,
+ immediate : 8;
+};
+
+struct ins_format2 {
+ unsigned int
+ parity : 1,
+ opcode : 4,
+ ret : 1,
+ destination : 9,
+ source : 9,
+ shift_control : 8;
+};
+
+struct ins_format3 {
+ unsigned int
+ parity : 1,
+ opcode : 4,
+ address : 10,
+ source : 9,
+ immediate : 8;
+};
+#endif
+
+union ins_formats {
+ struct ins_format1 format1;
+ struct ins_format2 format2;
+ struct ins_format3 format3;
+ unsigned char bytes[4];
+ unsigned int integer;
+};
+struct instruction {
+ union ins_formats format;
+ unsigned int srcline;
+ struct symbol *patch_label;
+ struct {
+ struct instruction *stqe_next;
+ } links;
+};
+
+#define AIC_OP_OR 0x0
+#define AIC_OP_AND 0x1
+#define AIC_OP_XOR 0x2
+#define AIC_OP_ADD 0x3
+#define AIC_OP_ADC 0x4
+#define AIC_OP_ROL 0x5
+#define AIC_OP_BMOV 0x6
+
+#define AIC_OP_JMP 0x8
+#define AIC_OP_JC 0x9
+#define AIC_OP_JNC 0xa
+#define AIC_OP_CALL 0xb
+#define AIC_OP_JNE 0xc
+#define AIC_OP_JNZ 0xd
+#define AIC_OP_JE 0xe
+#define AIC_OP_JZ 0xf
+
+/* Pseudo Ops */
+#define AIC_OP_SHL 0x10
+#define AIC_OP_SHR 0x20
+#define AIC_OP_ROR 0x30
diff --git a/linux/src/drivers/scsi/aic7xxx_proc.c b/linux/src/drivers/scsi/aic7xxx_proc.c
new file mode 100644
index 00000000..451ad99c
--- /dev/null
+++ b/linux/src/drivers/scsi/aic7xxx_proc.c
@@ -0,0 +1,397 @@
+/*+M*************************************************************************
+ * Adaptec AIC7xxx device driver proc support for Linux.
+ *
+ * Copyright (c) 1995, 1996 Dean W. Gehnert
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ----------------------------------------------------------------
+ * o Modified from the EATA-DMA /proc support.
+ * o Additional support for device block statistics provided by
+ * Matthew Jacob.
+ * o Correction of overflow by Heinz Mauelshagen
+ * o Adittional corrections by Doug Ledford
+ *
+ * Dean W. Gehnert, deang@teleport.com, 05/01/96
+ *
+ * $Id: aic7xxx_proc.c,v 1.1 1999/04/26 05:54:19 tb Exp $
+ *-M*************************************************************************/
+
+#define BLS (&aic7xxx_buffer[size])
+#define HDRB \
+" < 512 512-1K 1-2K 2-4K 4-8K 8-16K 16-32K 32-64K 64-128K >128K"
+
+#ifdef PROC_DEBUG
+extern int vsprintf(char *, const char *, va_list);
+
+static void
+proc_debug(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[256];
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ printk(buf);
+ va_end(ap);
+}
+#else /* PROC_DEBUG */
+# define proc_debug(fmt, args...)
+#endif /* PROC_DEBUG */
+
+static int aic7xxx_buffer_size = 0;
+static char *aic7xxx_buffer = NULL;
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_set_info
+ *
+ * Description:
+ * Set parameters for the driver from the /proc filesystem.
+ *-F*************************************************************************/
+int
+aic7xxx_set_info(char *buffer, int length, struct Scsi_Host *HBAptr)
+{
+ proc_debug("aic7xxx_set_info(): %s\n", buffer);
+ return (-ENOSYS); /* Currently this is a no-op */
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_proc_info
+ *
+ * Description:
+ * Return information to handle /proc support for the driver.
+ *-F*************************************************************************/
+int
+aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+ struct Scsi_Host *HBAptr;
+ struct aic7xxx_host *p;
+ int size = 0;
+ unsigned char i;
+ struct aic7xxx_xferstats *sp;
+ unsigned char target, lun;
+
+ HBAptr = NULL;
+
+ for(p=first_aic7xxx; p->host->host_no != hostno; p=p->next)
+ ;
+
+ if (!p)
+ {
+ size += sprintf(buffer, "Can't find adapter for host number %d\n", hostno);
+ if (size > length)
+ {
+ return (size);
+ }
+ else
+ {
+ return (length);
+ }
+ }
+
+ HBAptr = p->host;
+
+ if (inout == TRUE) /* Has data been written to the file? */
+ {
+ return (aic7xxx_set_info(buffer, length, HBAptr));
+ }
+
+ p = (struct aic7xxx_host *) HBAptr->hostdata;
+
+ /*
+ * It takes roughly 1K of space to hold all relevant card info, not
+ * counting any proc stats, so we start out with a 1.5k buffer size and
+ * if proc_stats is defined, then we sweep the stats structure to see
+ * how many drives we will be printing out for and add 384 bytes per
+ * device with active stats.
+ *
+ * Hmmmm...that 1.5k seems to keep growing as items get added so they
+ * can be easily viewed for debugging purposes. So, we bumped that
+ * 1.5k to 4k so we can quit having to bump it all the time.
+ */
+
+ size = 4096;
+ for (target = 0; target < MAX_TARGETS; target++)
+ {
+ for (lun = 0; lun < MAX_LUNS; lun++)
+ {
+ if (p->stats[target][lun].xfers != 0)
+#ifdef AIC7XXX_PROC_STATS
+ size += 512;
+#else
+ size += 256;
+#endif
+ }
+ }
+ if (aic7xxx_buffer_size != size)
+ {
+ if (aic7xxx_buffer != NULL)
+ {
+ kfree(aic7xxx_buffer);
+ aic7xxx_buffer_size = 0;
+ }
+ aic7xxx_buffer = kmalloc(size, GFP_KERNEL);
+ }
+ if (aic7xxx_buffer == NULL)
+ {
+ size = sprintf(buffer, "AIC7xxx - kmalloc error at line %d\n",
+ __LINE__);
+ return size;
+ }
+ aic7xxx_buffer_size = size;
+
+ size = 0;
+ size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
+ size += sprintf(BLS, "%s/", AIC7XXX_C_VERSION);
+ size += sprintf(BLS, "%s", AIC7XXX_H_VERSION);
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, "Compile Options:\n");
+#ifdef AIC7XXX_RESET_DELAY
+ size += sprintf(BLS, " AIC7XXX_RESET_DELAY : %d\n", AIC7XXX_RESET_DELAY);
+#endif
+ size += sprintf(BLS, " AIC7XXX_TAGGED_QUEUEING: Adapter Support Enabled\n");
+ size += sprintf(BLS, " Check below to see "
+ "which\n"
+ " devices use tagged "
+ "queueing\n");
+ size += sprintf(BLS, " AIC7XXX_PAGE_ENABLE : Enabled (This is no longer "
+ "an option)\n");
+#ifdef AIC7XXX_PROC_STATS
+ size += sprintf(BLS, " AIC7XXX_PROC_STATS : Enabled\n");
+#else
+ size += sprintf(BLS, " AIC7XXX_PROC_STATS : Disabled\n");
+#endif
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, "Adapter Configuration:\n");
+ size += sprintf(BLS, " SCSI Adapter: %s\n",
+ board_names[p->board_name_index]);
+ if (p->flags & AHC_TWIN)
+ size += sprintf(BLS, " Twin Channel\n");
+ else
+ {
+ char *channel = "";
+ char *ultra = "";
+ char *wide = "Narrow ";
+ if (p->flags & AHC_MULTI_CHANNEL)
+ {
+ channel = " Channel A";
+ if (p->flags & (AHC_CHNLB|AHC_CHNLC))
+ channel = (p->flags & AHC_CHNLB) ? " Channel B" : " Channel C";
+ }
+ if (p->features & AHC_WIDE)
+ wide = "Wide ";
+ if (p->features & AHC_ULTRA2)
+ ultra = "Ultra2-LVD/SE ";
+ else if (p->features & AHC_ULTRA)
+ ultra = "Ultra ";
+ size += sprintf(BLS, " %s%sController%s\n",
+ ultra, wide, channel);
+ }
+ if( !(p->maddr) )
+ {
+ size += sprintf(BLS, " Programmed I/O Base: %lx\n", p->base);
+ }
+ else
+ {
+ size += sprintf(BLS, " PCI MMAPed I/O Base: 0x%lx\n", p->mbase);
+ }
+ if( (p->chip & (AHC_VL | AHC_EISA)) )
+ {
+ size += sprintf(BLS, " BIOS Memory Address: 0x%08x\n", p->bios_address);
+ }
+ size += sprintf(BLS, " Adapter SEEPROM Config: %s\n",
+ (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." :
+ ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." :
+ "SEEPROM not found, using leftover BIOS values.") );
+ size += sprintf(BLS, " Adaptec SCSI BIOS: %s\n",
+ (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
+ size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq);
+ size += sprintf(BLS, " SCBs: Active %d, Max Active %d,\n",
+ p->activescbs, p->max_activescbs);
+ size += sprintf(BLS, " Allocated %d, HW %d, "
+ "Page %d\n", p->scb_data->numscbs, p->scb_data->maxhscbs,
+ p->scb_data->maxscbs);
+ if (p->flags & AHC_EXTERNAL_SRAM)
+ size += sprintf(BLS, " Using External SCB SRAM\n");
+ size += sprintf(BLS, " Interrupts: %ld", p->isr_count);
+ if (p->chip & AHC_EISA)
+ {
+ size += sprintf(BLS, " %s\n",
+ (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
+ }
+ else
+ {
+ size += sprintf(BLS, "\n");
+ }
+ size += sprintf(BLS, " BIOS Control Word: 0x%04x\n",
+ p->bios_control);
+ size += sprintf(BLS, " Adapter Control Word: 0x%04x\n",
+ p->adapter_control);
+ size += sprintf(BLS, " Extended Translation: %sabled\n",
+ (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis");
+ size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable);
+ if (p->features & (AHC_ULTRA | AHC_ULTRA2))
+ {
+ size += sprintf(BLS, " Ultra Enable Flags: 0x%04x\n", p->ultraenb);
+ }
+ size += sprintf(BLS, " Tag Queue Enable Flags: 0x%04x\n", p->tagenable);
+ size += sprintf(BLS, "Ordered Queue Tag Flags: 0x%04x\n", p->orderedtag);
+#ifdef AIC7XXX_CMDS_PER_LUN
+ size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_LUN);
+#else
+ size += sprintf(BLS, "Default Tag Queue Depth: %d\n", 8);
+#endif
+ size += sprintf(BLS, " Tagged Queue By Device array for aic7xxx host "
+ "instance %d:\n", p->instance);
+ size += sprintf(BLS, " {");
+ for(i=0; i < (MAX_TARGETS - 1); i++)
+ size += sprintf(BLS, "%d,",aic7xxx_tag_info[p->instance].tag_commands[i]);
+ size += sprintf(BLS, "%d}\n",aic7xxx_tag_info[p->instance].tag_commands[i]);
+ size += sprintf(BLS, " Actual queue depth per device for aic7xxx host "
+ "instance %d:\n", p->instance);
+ size += sprintf(BLS, " {");
+ for(i=0; i < (MAX_TARGETS - 1); i++)
+ size += sprintf(BLS, "%d,", p->dev_max_queue_depth[i]);
+ size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]);
+
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, "Statistics:\n");
+ for (target = 0; target < MAX_TARGETS; target++)
+ {
+ for (lun = 0; lun < MAX_LUNS; lun++)
+ {
+ sp = &p->stats[target][lun];
+ if (sp->xfers == 0)
+ {
+ continue;
+ }
+ if (p->features & AHC_TWIN)
+ {
+ size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+ p->host_no, (target >> 3), (target & 0x7), lun);
+ }
+ else
+ {
+ size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+ p->host_no, 0, target, lun);
+ }
+ size += sprintf(BLS, " Device using %s/%s\n",
+ (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ?
+ "Wide" : "Narrow",
+ (p->transinfo[target].cur_offset != 0) ?
+ "Sync transfers at" : "Async transfers." );
+ if (p->transinfo[target].cur_offset != 0)
+ {
+ struct aic7xxx_syncrate *sync_rate;
+ int period = p->transinfo[target].cur_period;
+ int rate = (p->transinfo[target].cur_width ==
+ MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
+
+ sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2);
+ if (sync_rate != NULL)
+ {
+ size += sprintf(BLS, " %s MByte/sec, offset %d\n",
+ sync_rate->rate[rate],
+ p->transinfo[target].cur_offset );
+ }
+ else
+ {
+ size += sprintf(BLS, " 3.3 MByte/sec, offset %d\n",
+ p->transinfo[target].cur_offset );
+ }
+ }
+ size += sprintf(BLS, " Device Negotiation Settings\n");
+ size += sprintf(BLS, " Period Offset Bus Width\n");
+ size += sprintf(BLS, "User %03d %03d %d\n",
+ p->transinfo[target].user_period,
+ p->transinfo[target].user_offset,
+ p->transinfo[target].user_width);
+ size += sprintf(BLS, "Goal %03d %03d %d\n",
+ p->transinfo[target].goal_period,
+ p->transinfo[target].goal_offset,
+ p->transinfo[target].goal_width);
+ size += sprintf(BLS, "Current %03d %03d %d\n",
+ p->transinfo[target].cur_period,
+ p->transinfo[target].cur_offset,
+ p->transinfo[target].cur_width);
+ size += sprintf(BLS, " Total transfers %ld (%ld read;%ld written)\n",
+ sp->xfers, sp->r_total, sp->w_total);
+ size += sprintf(BLS, " blks(512) rd=%ld; blks(512) wr=%ld\n",
+ sp->r_total512, sp->w_total512);
+#ifdef AIC7XXX_PROC_STATS
+ size += sprintf(BLS, "%s\n", HDRB);
+ size += sprintf(BLS, " Reads:");
+ for (i = 0; i < NUMBER(sp->r_bins); i++)
+ {
+ size += sprintf(BLS, "%6ld ", sp->r_bins[i]);
+ }
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, "Writes:");
+ for (i = 0; i < NUMBER(sp->w_bins); i++)
+ {
+ size += sprintf(BLS, "%6ld ", sp->w_bins[i]);
+ }
+#endif /* AIC7XXX_PROC_STATS */
+ size += sprintf(BLS, "\n\n");
+ }
+ }
+
+ if (size >= aic7xxx_buffer_size)
+ {
+ printk(KERN_WARNING "aic7xxx: Overflow in aic7xxx_proc.c\n");
+ }
+
+ if (offset > size - 1)
+ {
+ kfree(aic7xxx_buffer);
+ aic7xxx_buffer = NULL;
+ aic7xxx_buffer_size = length = 0;
+ *start = NULL;
+ }
+ else
+ {
+ *start = &aic7xxx_buffer[offset]; /* Start of wanted data */
+ if (size - offset < length)
+ {
+ length = size - offset;
+ }
+ }
+
+ return (length);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 2
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -2
+ * c-argdecl-indent: 2
+ * c-label-offset: -2
+ * c-continued-statement-offset: 2
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/aic7xxx_reg.h b/linux/src/drivers/scsi/aic7xxx_reg.h
new file mode 100644
index 00000000..d12d1b6e
--- /dev/null
+++ b/linux/src/drivers/scsi/aic7xxx_reg.h
@@ -0,0 +1,587 @@
+/*
+ * DO NOT EDIT - This file is automatically generated.
+ */
+
+#define SCSISEQ 0x00
+#define TEMODE 0x80
+#define ENSELO 0x40
+#define ENSELI 0x20
+#define ENRSELI 0x10
+#define ENAUTOATNO 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNP 0x02
+#define SCSIRSTO 0x01
+
+#define SXFRCTL0 0x01
+#define DFON 0x80
+#define DFPEXP 0x40
+#define FAST20 0x20
+#define CLRSTCNT 0x10
+#define SPIOEN 0x08
+#define SCAMEN 0x04
+#define CLRCHN 0x02
+
+#define SXFRCTL1 0x02
+#define BITBUCKET 0x80
+#define SWRAPEN 0x40
+#define ENSPCHK 0x20
+#define STIMESEL 0x18
+#define ENSTIMER 0x04
+#define ACTNEGEN 0x02
+#define STPWEN 0x01
+
+#define SCSISIGO 0x03
+#define CDO 0x80
+#define IOO 0x40
+#define MSGO 0x20
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+#define SCSISIGI 0x03
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+#define SCSIRATE 0x04
+#define WIDEXFER 0x80
+#define SXFR_ULTRA2 0x7f
+#define SXFR 0x70
+#define SOFS 0x0f
+
+#define SCSIID 0x05
+#define SCSIOFFSET 0x05
+#define SOFS_ULTRA2 0x7f
+
+#define SCSIDATL 0x06
+
+#define SCSIDATH 0x07
+
+#define STCNT 0x08
+
+#define CLRSINT0 0x0b
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRSWRAP 0x08
+#define CLRSPIORDY 0x02
+
+#define SSTAT0 0x0b
+#define TARGET 0x80
+#define SELDO 0x40
+#define SELDI 0x20
+#define SELINGO 0x10
+#define IOERR 0x08
+#define SWRAP 0x08
+#define SDONE 0x04
+#define SPIORDY 0x02
+#define DMADONE 0x01
+
+#define CLRSINT1 0x0c
+#define CLRSELTIMEO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRPHASECHG 0x02
+#define CLRREQINIT 0x01
+
+#define SSTAT1 0x0c
+#define SELTO 0x80
+#define ATNTARG 0x40
+#define SCSIRSTI 0x20
+#define PHASEMIS 0x10
+#define BUSFREE 0x08
+#define SCSIPERR 0x04
+#define PHASECHG 0x02
+#define REQINIT 0x01
+
+#define SSTAT2 0x0d
+#define OVERRUN 0x80
+#define SFCNT 0x1f
+#define EXP_ACTIVE 0x10
+
+#define SSTAT3 0x0e
+#define SCSICNT 0xf0
+#define OFFCNT 0x0f
+
+#define SCSIID_ULTRA2 0x0f
+#define OID 0x0f
+
+#define SIMODE0 0x10
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENIOERR 0x08
+#define ENSWRAP 0x08
+#define ENSDONE 0x04
+#define ENSPIORDY 0x02
+#define ENDMADONE 0x01
+
+#define SIMODE1 0x11
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENPHASECHG 0x02
+#define ENREQINIT 0x01
+
+#define SCSIBUSL 0x12
+
+#define SCSIBUSH 0x13
+
+#define SHADDR 0x14
+
+#define SELTIMER 0x18
+#define STAGE6 0x20
+#define STAGE5 0x10
+#define STAGE4 0x08
+#define STAGE3 0x04
+#define STAGE2 0x02
+#define STAGE1 0x01
+
+#define SELID 0x19
+#define SELID_MASK 0xf0
+#define ONEBIT 0x08
+
+#define SPIOCAP 0x1b
+#define SOFT1 0x80
+#define SOFT0 0x40
+#define SOFTCMDEN 0x20
+#define HAS_BRDCTL 0x10
+#define SEEPROM 0x08
+#define EEPROM 0x04
+#define ROM 0x02
+#define SSPIOCPS 0x01
+
+#define BRDCTL 0x1d
+#define BRDDAT7 0x80
+#define BRDDAT6 0x40
+#define BRDDAT5 0x20
+#define BRDDAT4 0x10
+#define BRDSTB 0x10
+#define BRDCS 0x08
+#define BRDDAT3 0x08
+#define BRDDAT2 0x04
+#define BRDRW 0x04
+#define BRDRW_ULTRA2 0x02
+#define BRDCTL1 0x02
+#define BRDSTB_ULTRA2 0x01
+#define BRDCTL0 0x01
+
+#define SEECTL 0x1e
+#define EXTARBACK 0x80
+#define EXTARBREQ 0x40
+#define SEEMS 0x20
+#define SEERDY 0x10
+#define SEECS 0x08
+#define SEECK 0x04
+#define SEEDO 0x02
+#define SEEDI 0x01
+
+#define SBLKCTL 0x1f
+#define DIAGLEDEN 0x80
+#define DIAGLEDON 0x40
+#define AUTOFLUSHDIS 0x20
+#define ENAB40 0x08
+#define ENAB20 0x04
+#define SELWIDE 0x02
+#define XCVR 0x01
+
+#define SRAM_BASE 0x20
+
+#define TARG_SCSIRATE 0x20
+
+#define ULTRA_ENB 0x30
+
+#define DISC_DSB 0x32
+
+#define MSG_OUT 0x34
+
+#define DMAPARAMS 0x35
+#define PRELOADEN 0x80
+#define WIDEODD 0x40
+#define SCSIEN 0x20
+#define SDMAENACK 0x10
+#define SDMAEN 0x10
+#define HDMAEN 0x08
+#define HDMAENACK 0x08
+#define DIRECTION 0x04
+#define FIFOFLUSH 0x02
+#define FIFORESET 0x01
+
+#define SEQ_FLAGS 0x36
+#define IDENTIFY_SEEN 0x80
+#define SCBPTR_VALID 0x20
+#define DPHASE 0x10
+#define AMTARGET 0x08
+#define WIDE_BUS 0x02
+#define TWIN_BUS 0x01
+
+#define SAVED_TCL 0x37
+
+#define SG_COUNT 0x38
+
+#define SG_NEXT 0x39
+
+#define LASTPHASE 0x3d
+#define P_MESGIN 0xe0
+#define PHASE_MASK 0xe0
+#define P_STATUS 0xc0
+#define P_MESGOUT 0xa0
+#define P_COMMAND 0x80
+#define CDI 0x80
+#define IOI 0x40
+#define P_DATAIN 0x40
+#define MSGI 0x20
+#define P_BUSFREE 0x01
+#define P_DATAOUT 0x00
+
+#define WAITING_SCBH 0x3e
+
+#define DISCONNECTED_SCBH 0x3f
+
+#define FREE_SCBH 0x40
+
+#define HSCB_ADDR 0x41
+
+#define SCBID_ADDR 0x45
+
+#define TMODE_CMDADDR 0x49
+
+#define KERNEL_QINPOS 0x4d
+
+#define QINPOS 0x4e
+
+#define QOUTPOS 0x4f
+
+#define TMODE_CMDADDR_NEXT 0x50
+
+#define ARG_1 0x51
+#define RETURN_1 0x51
+#define SEND_MSG 0x80
+#define SEND_SENSE 0x40
+#define SEND_REJ 0x20
+#define MSGOUT_PHASEMIS 0x10
+
+#define ARG_2 0x52
+#define RETURN_2 0x52
+
+#define LAST_MSG 0x53
+
+#define PREFETCH_CNT 0x54
+
+#define SCSICONF 0x5a
+#define TERM_ENB 0x80
+#define RESET_SCSI 0x40
+#define HWSCSIID 0x0f
+#define HSCSIID 0x07
+
+#define HOSTCONF 0x5d
+
+#define HA_274_BIOSCTRL 0x5f
+#define BIOSMODE 0x30
+#define BIOSDISABLED 0x30
+#define CHANNEL_B_PRIMARY 0x08
+
+#define SEQCTL 0x60
+#define PERRORDIS 0x80
+#define PAUSEDIS 0x40
+#define FAILDIS 0x20
+#define FASTMODE 0x10
+#define BRKADRINTEN 0x08
+#define STEP 0x04
+#define SEQRESET 0x02
+#define LOADRAM 0x01
+
+#define SEQRAM 0x61
+
+#define SEQADDR0 0x62
+
+#define SEQADDR1 0x63
+#define SEQADDR1_MASK 0x01
+
+#define ACCUM 0x64
+
+#define SINDEX 0x65
+
+#define DINDEX 0x66
+
+#define ALLONES 0x69
+
+#define ALLZEROS 0x6a
+
+#define NONE 0x6a
+
+#define FLAGS 0x6b
+#define ZERO 0x02
+#define CARRY 0x01
+
+#define SINDIR 0x6c
+
+#define DINDIR 0x6d
+
+#define FUNCTION1 0x6e
+
+#define STACK 0x6f
+
+#define TARG_OFFSET 0x70
+
+#define BCTL 0x84
+#define ACE 0x08
+#define ENABLE 0x01
+
+#define DSCOMMAND0 0x84
+#define INTSCBRAMSEL 0x08
+#define RAMPS 0x04
+#define USCBSIZE32 0x02
+#define CIOPARCKEN 0x01
+
+#define DSCOMMAND 0x84
+#define CACHETHEN 0x80
+#define DPARCKEN 0x40
+#define MPARCKEN 0x20
+#define EXTREQLCK 0x10
+
+#define BUSTIME 0x85
+#define BOFF 0xf0
+#define BON 0x0f
+
+#define BUSSPD 0x86
+#define DFTHRSH 0xc0
+#define STBOFF 0x38
+#define STBON 0x07
+
+#define DSPCISTATUS 0x86
+#define DFTHRSH_100 0xc0
+
+#define HCNTRL 0x87
+#define POWRDN 0x40
+#define SWINT 0x10
+#define IRQMS 0x08
+#define PAUSE 0x04
+#define INTEN 0x02
+#define CHIPRST 0x01
+#define CHIPRSTACK 0x01
+
+#define HADDR 0x88
+
+#define HCNT 0x8c
+
+#define SCBPTR 0x90
+
+#define INTSTAT 0x91
+#define SEQINT_MASK 0xf1
+#define DATA_OVERRUN 0xe1
+#define MSGIN_PHASEMIS 0xd1
+#define TRACEPOINT2 0xc1
+#define TRACEPOINT 0xb1
+#define AWAITING_MSG 0xa1
+#define RESIDUAL 0x81
+#define BAD_STATUS 0x71
+#define REJECT_MSG 0x61
+#define ABORT_REQUESTED 0x51
+#define EXTENDED_MSG 0x41
+#define NO_MATCH 0x31
+#define NO_IDENT 0x21
+#define SEND_REJECT 0x11
+#define INT_PEND 0x0f
+#define BRKADRINT 0x08
+#define SCSIINT 0x04
+#define CMDCMPLT 0x02
+#define BAD_PHASE 0x01
+#define SEQINT 0x01
+
+#define CLRINT 0x92
+#define CLRPARERR 0x10
+#define CLRBRKADRINT 0x08
+#define CLRSCSIINT 0x04
+#define CLRCMDINT 0x02
+#define CLRSEQINT 0x01
+
+#define ERROR 0x92
+#define CIOPARERR 0x80
+#define PCIERRSTAT 0x40
+#define MPARERR 0x20
+#define DPARERR 0x10
+#define SQPARERR 0x08
+#define ILLOPCODE 0x04
+#define ILLSADDR 0x02
+#define ILLHADDR 0x01
+
+#define DFCNTRL 0x93
+
+#define DFSTATUS 0x94
+#define PRELOAD_AVAIL 0x80
+#define DWORDEMP 0x20
+#define MREQPEND 0x10
+#define HDONE 0x08
+#define DFTHRESH 0x04
+#define FIFOFULL 0x02
+#define FIFOEMP 0x01
+
+#define DFDAT 0x99
+
+#define SCBCNT 0x9a
+#define SCBAUTO 0x80
+#define SCBCNT_MASK 0x1f
+
+#define QINFIFO 0x9b
+
+#define QINCNT 0x9c
+
+#define QOUTFIFO 0x9d
+
+#define QOUTCNT 0x9e
+
+#define SFUNCT 0x9f
+
+#define SCB_CONTROL 0xa0
+#define MK_MESSAGE 0x80
+#define DISCENB 0x40
+#define TAG_ENB 0x20
+#define DISCONNECTED 0x04
+#define SCB_TAG_TYPE 0x03
+
+#define SCB_BASE 0xa0
+
+#define SCB_TCL 0xa1
+#define TID 0xf0
+#define SELBUSB 0x08
+#define LID 0x07
+
+#define SCB_TARGET_STATUS 0xa2
+
+#define SCB_SGCOUNT 0xa3
+
+#define SCB_SGPTR 0xa4
+
+#define SCB_RESID_SGCNT 0xa8
+
+#define SCB_RESID_DCNT 0xa9
+
+#define SCB_DATAPTR 0xac
+
+#define SCB_DATACNT 0xb0
+
+#define SCB_CMDPTR 0xb4
+
+#define SCB_CMDLEN 0xb8
+
+#define SCB_TAG 0xb9
+
+#define SCB_NEXT 0xba
+
+#define SCB_PREV 0xbb
+
+#define SCB_BUSYTARGETS 0xbc
+
+#define SEECTL_2840 0xc0
+#define CS_2840 0x04
+#define CK_2840 0x02
+#define DO_2840 0x01
+
+#define STATUS_2840 0xc1
+#define EEPROM_TF 0x80
+#define BIOS_SEL 0x60
+#define ADSEL 0x1e
+#define DI_2840 0x01
+
+#define CCHADDR 0xe0
+
+#define CCHCNT 0xe8
+
+#define CCSGRAM 0xe9
+
+#define CCSGADDR 0xea
+
+#define CCSGCTL 0xeb
+#define CCSGDONE 0x80
+#define CCSGEN 0x08
+#define FLAG 0x02
+#define CCSGRESET 0x01
+
+#define CCSCBRAM 0xec
+
+#define CCSCBADDR 0xed
+
+#define CCSCBCTL 0xee
+#define CCSCBDONE 0x80
+#define ARRDONE 0x40
+#define CCARREN 0x10
+#define CCSCBEN 0x08
+#define CCSCBDIR 0x04
+#define CCSCBRESET 0x01
+
+#define CCSCBCNT 0xef
+
+#define CCSCBPTR 0xf1
+
+#define HNSCB_QOFF 0xf4
+
+#define SNSCB_QOFF 0xf6
+
+#define SDSCB_QOFF 0xf8
+
+#define QOFF_CTLSTA 0xfa
+#define SCB_AVAIL 0x40
+#define SNSCB_ROLLOVER 0x20
+#define SDSCB_ROLLOVER 0x10
+#define SCB_QSIZE 0x07
+#define SCB_QSIZE_256 0x06
+
+#define DFF_THRSH 0xfb
+#define WR_DFTHRSH 0x70
+#define WR_DFTHRSH_MAX 0x70
+#define WR_DFTHRSH_90 0x60
+#define WR_DFTHRSH_85 0x50
+#define WR_DFTHRSH_75 0x40
+#define WR_DFTHRSH_63 0x30
+#define WR_DFTHRSH_50 0x20
+#define WR_DFTHRSH_25 0x10
+#define RD_DFTHRSH_MAX 0x07
+#define RD_DFTHRSH 0x07
+#define RD_DFTHRSH_90 0x06
+#define RD_DFTHRSH_85 0x05
+#define RD_DFTHRSH_75 0x04
+#define RD_DFTHRSH_63 0x03
+#define RD_DFTHRSH_50 0x02
+#define RD_DFTHRSH_25 0x01
+#define WR_DFTHRSH_MIN 0x00
+#define RD_DFTHRSH_MIN 0x00
+
+#define SG_CACHEPTR 0xfc
+#define SG_USER_DATA 0xfc
+#define LAST_SEG 0x02
+#define LAST_SEG_DONE 0x01
+
+
+#define CMD_GROUP2_BYTE_DELTA 0xfa
+#define MAX_OFFSET_8BIT 0x0f
+#define BUS_16_BIT 0x01
+#define QINFIFO_OFFSET 0x02
+#define CMD_GROUP5_BYTE_DELTA 0x0b
+#define CMD_GROUP_CODE_SHIFT 0x05
+#define MAX_OFFSET_ULTRA2 0x7f
+#define MAX_OFFSET_16BIT 0x08
+#define BUS_8_BIT 0x00
+#define QOUTFIFO_OFFSET 0x01
+#define UNTAGGEDSCB_OFFSET 0x00
+#define CCSGRAM_MAXSEGS 0x10
+#define SCB_LIST_NULL 0xff
+#define SG_SIZEOF 0x08
+#define CMD_GROUP4_BYTE_DELTA 0x04
+#define CMD_GROUP0_BYTE_DELTA 0xfc
+#define HOST_MSG 0xff
+#define BUS_32_BIT 0x02
+#define CCSGADDR_MAX 0x80
+
+
+/* Downloaded Constant Definitions */
+#define TMODE_NUMCMDS 0x00
diff --git a/linux/src/drivers/scsi/aic7xxx_seq.c b/linux/src/drivers/scsi/aic7xxx_seq.c
new file mode 100644
index 00000000..9205cc4a
--- /dev/null
+++ b/linux/src/drivers/scsi/aic7xxx_seq.c
@@ -0,0 +1,769 @@
+/*
+ * DO NOT EDIT - This file is automatically generated.
+ */
+static unsigned char seqprog[] = {
+ 0xff, 0x6a, 0x06, 0x08,
+ 0x32, 0x6a, 0x00, 0x00,
+ 0x12, 0x6a, 0x00, 0x00,
+ 0xff, 0x6a, 0xd6, 0x09,
+ 0xff, 0x6a, 0xdc, 0x09,
+ 0x00, 0x65, 0x38, 0x59,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0xff, 0x4e, 0xc8, 0x08,
+ 0xbf, 0x60, 0xc0, 0x08,
+ 0x60, 0x0b, 0x7c, 0x68,
+ 0x40, 0x00, 0x0e, 0x68,
+ 0x08, 0x1f, 0x3e, 0x10,
+ 0x60, 0x0b, 0x7c, 0x68,
+ 0x40, 0x00, 0x0e, 0x68,
+ 0x08, 0x1f, 0x3e, 0x10,
+ 0xff, 0x3e, 0x3e, 0x60,
+ 0x40, 0xfa, 0x10, 0x78,
+ 0xff, 0xf6, 0xd4, 0x08,
+ 0x01, 0x4e, 0x9c, 0x18,
+ 0x40, 0x60, 0xc0, 0x00,
+ 0x00, 0x4d, 0x10, 0x70,
+ 0x01, 0x4e, 0x9c, 0x18,
+ 0xbf, 0x60, 0xc0, 0x08,
+ 0x00, 0x6a, 0x72, 0x5c,
+ 0xff, 0x4e, 0xc8, 0x18,
+ 0x02, 0x6a, 0x88, 0x5b,
+ 0xff, 0x52, 0x20, 0x09,
+ 0x0d, 0x6a, 0x6a, 0x00,
+ 0x00, 0x52, 0xfe, 0x5b,
+ 0xff, 0x3e, 0x74, 0x09,
+ 0xff, 0x90, 0x7c, 0x08,
+ 0xff, 0x3e, 0x20, 0x09,
+ 0x00, 0x65, 0x44, 0x58,
+ 0x00, 0x65, 0x0e, 0x40,
+ 0xf7, 0x1f, 0xca, 0x08,
+ 0x08, 0xa1, 0xc8, 0x08,
+ 0x00, 0x65, 0xca, 0x00,
+ 0xff, 0x65, 0x3e, 0x08,
+ 0xf0, 0xa1, 0xc8, 0x08,
+ 0x0f, 0x0f, 0x1e, 0x08,
+ 0x00, 0x0f, 0x1e, 0x00,
+ 0xf0, 0xa1, 0xc8, 0x08,
+ 0x0f, 0x05, 0x0a, 0x08,
+ 0x00, 0x05, 0x0a, 0x00,
+ 0x5a, 0x6a, 0x00, 0x04,
+ 0x12, 0x65, 0xc8, 0x00,
+ 0x00, 0x01, 0x02, 0x00,
+ 0x31, 0x6a, 0xca, 0x00,
+ 0x80, 0x37, 0x64, 0x68,
+ 0xff, 0x65, 0xca, 0x18,
+ 0xff, 0x37, 0xdc, 0x08,
+ 0xff, 0x6e, 0xc8, 0x08,
+ 0x00, 0x6c, 0x6c, 0x78,
+ 0x20, 0x01, 0x02, 0x00,
+ 0x4c, 0x37, 0xc8, 0x28,
+ 0x08, 0x1f, 0x74, 0x78,
+ 0x08, 0x37, 0x6e, 0x00,
+ 0x08, 0x64, 0xc8, 0x00,
+ 0x70, 0x64, 0xca, 0x18,
+ 0xff, 0x6c, 0x0a, 0x08,
+ 0x20, 0x64, 0xca, 0x18,
+ 0xff, 0x6c, 0x08, 0x0c,
+ 0x40, 0x0b, 0x04, 0x69,
+ 0x80, 0x0b, 0xf6, 0x78,
+ 0xa4, 0x6a, 0x06, 0x00,
+ 0x40, 0x6a, 0x16, 0x00,
+ 0x10, 0x03, 0xf2, 0x78,
+ 0xff, 0x50, 0xc8, 0x08,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x49, 0x6a, 0xee, 0x5b,
+ 0x01, 0x6a, 0x26, 0x01,
+ 0xff, 0x6a, 0xca, 0x08,
+ 0x08, 0x01, 0x02, 0x00,
+ 0x02, 0x0b, 0x92, 0x78,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0xff, 0x06, 0xcc, 0x08,
+ 0xff, 0x66, 0x32, 0x09,
+ 0x01, 0x65, 0xca, 0x18,
+ 0x80, 0x66, 0xa0, 0x78,
+ 0xff, 0x66, 0xa2, 0x08,
+ 0x10, 0x03, 0x90, 0x68,
+ 0xfc, 0x65, 0xc8, 0x18,
+ 0x00, 0x65, 0xa8, 0x48,
+ 0xff, 0x6a, 0x32, 0x01,
+ 0x01, 0x64, 0x18, 0x19,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x09,
+ 0x84, 0x6a, 0x06, 0x00,
+ 0x08, 0x01, 0x02, 0x00,
+ 0x02, 0x0b, 0xb2, 0x78,
+ 0xff, 0x06, 0xc8, 0x08,
+ 0xff, 0x64, 0x32, 0x09,
+ 0xff, 0x6a, 0xca, 0x08,
+ 0x5b, 0x64, 0xc8, 0x28,
+ 0x00, 0x62, 0xc4, 0x18,
+ 0xfc, 0x65, 0xca, 0x18,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0xfa, 0x65, 0xca, 0x18,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0x04, 0x65, 0xca, 0x18,
+ 0x0b, 0x65, 0xca, 0x18,
+ 0xff, 0x65, 0xc8, 0x08,
+ 0x00, 0x8c, 0x18, 0x19,
+ 0x02, 0x0b, 0xce, 0x78,
+ 0x01, 0x65, 0xd4, 0x60,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0xff, 0x06, 0x32, 0x09,
+ 0xff, 0x65, 0xca, 0x18,
+ 0xff, 0x65, 0xce, 0x68,
+ 0x0a, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0x64, 0x5c,
+ 0x40, 0x51, 0xe6, 0x78,
+ 0xe4, 0x6a, 0x06, 0x00,
+ 0x08, 0x01, 0x02, 0x00,
+ 0x04, 0x6a, 0x18, 0x5b,
+ 0x01, 0x50, 0xa0, 0x18,
+ 0x00, 0x50, 0xec, 0xe0,
+ 0xff, 0x6a, 0xa0, 0x08,
+ 0xff, 0x6a, 0x3a, 0x01,
+ 0x02, 0x6a, 0x22, 0x01,
+ 0x40, 0x51, 0xf2, 0x68,
+ 0xff, 0x6a, 0x06, 0x08,
+ 0x00, 0x65, 0x0e, 0x40,
+ 0x20, 0x6a, 0x16, 0x00,
+ 0xf0, 0x19, 0x6e, 0x08,
+ 0x08, 0x6a, 0x18, 0x00,
+ 0x08, 0x11, 0x22, 0x00,
+ 0x08, 0x6a, 0x5a, 0x58,
+ 0x08, 0x6a, 0x68, 0x00,
+ 0x00, 0x65, 0x18, 0x41,
+ 0x12, 0x6a, 0x00, 0x00,
+ 0x40, 0x6a, 0x16, 0x00,
+ 0xff, 0x3e, 0x20, 0x09,
+ 0xff, 0xba, 0x7c, 0x08,
+ 0xff, 0xa1, 0x6e, 0x08,
+ 0x08, 0x6a, 0x18, 0x00,
+ 0x08, 0x11, 0x22, 0x00,
+ 0x08, 0x6a, 0x5a, 0x58,
+ 0x80, 0x6a, 0x68, 0x00,
+ 0x80, 0x36, 0x6c, 0x00,
+ 0x00, 0x65, 0xd2, 0x5b,
+ 0xff, 0x3d, 0xc8, 0x08,
+ 0xbf, 0x64, 0x48, 0x79,
+ 0x80, 0x64, 0xf0, 0x71,
+ 0xa0, 0x64, 0x0e, 0x72,
+ 0xc0, 0x64, 0x08, 0x72,
+ 0xe0, 0x64, 0x52, 0x72,
+ 0x01, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0x18, 0x41,
+ 0xf7, 0x11, 0x22, 0x08,
+ 0x00, 0x65, 0x38, 0x59,
+ 0xff, 0x06, 0xd4, 0x08,
+ 0xf7, 0x01, 0x02, 0x08,
+ 0x09, 0x0c, 0x32, 0x79,
+ 0x08, 0x0c, 0x0e, 0x68,
+ 0x01, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0x26, 0x09,
+ 0xff, 0x6a, 0x08, 0x08,
+ 0xdf, 0x01, 0x02, 0x08,
+ 0x01, 0x6a, 0x7a, 0x00,
+ 0x03, 0x36, 0x6c, 0x0c,
+ 0x08, 0x6a, 0xcc, 0x00,
+ 0xa9, 0x6a, 0xe8, 0x5b,
+ 0x00, 0x65, 0x66, 0x41,
+ 0xa8, 0x6a, 0x6a, 0x00,
+ 0x79, 0x6a, 0x6a, 0x00,
+ 0x40, 0x3d, 0x50, 0x69,
+ 0x04, 0x35, 0x6a, 0x00,
+ 0x00, 0x65, 0x3a, 0x5b,
+ 0x80, 0x6a, 0xd4, 0x01,
+ 0x10, 0x36, 0x42, 0x69,
+ 0x10, 0x36, 0x6c, 0x00,
+ 0x07, 0xac, 0x10, 0x31,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0xac, 0x6a, 0xe0, 0x5b,
+ 0x00, 0x65, 0xda, 0x5b,
+ 0xff, 0xa3, 0x70, 0x08,
+ 0x39, 0x6a, 0xcc, 0x00,
+ 0xa4, 0x6a, 0xe6, 0x5b,
+ 0xff, 0x38, 0x74, 0x69,
+ 0x80, 0x02, 0x04, 0x00,
+ 0xe7, 0x35, 0x6a, 0x08,
+ 0x03, 0x69, 0x18, 0x31,
+ 0xff, 0x6a, 0x10, 0x00,
+ 0xff, 0x6a, 0x12, 0x00,
+ 0xff, 0x6a, 0x14, 0x00,
+ 0x01, 0x38, 0x7a, 0x61,
+ 0x02, 0xfc, 0xf8, 0x01,
+ 0xbf, 0x35, 0x6a, 0x08,
+ 0xff, 0x69, 0xca, 0x08,
+ 0xff, 0x35, 0x26, 0x09,
+ 0x04, 0x0b, 0x7e, 0x69,
+ 0x04, 0x0b, 0x8a, 0x69,
+ 0x10, 0x0c, 0x80, 0x79,
+ 0x04, 0x0b, 0x88, 0x69,
+ 0xff, 0x6a, 0xca, 0x08,
+ 0x00, 0x35, 0x22, 0x5b,
+ 0x80, 0x02, 0xd6, 0x69,
+ 0xff, 0x65, 0xc8, 0x79,
+ 0xff, 0x38, 0x70, 0x18,
+ 0xff, 0x38, 0xc8, 0x79,
+ 0x80, 0xea, 0xaa, 0x61,
+ 0xef, 0x38, 0xc8, 0x18,
+ 0x80, 0x6a, 0xc8, 0x00,
+ 0x00, 0x65, 0x9c, 0x49,
+ 0x33, 0x38, 0xc8, 0x28,
+ 0xff, 0x64, 0xd0, 0x09,
+ 0x04, 0x39, 0xc0, 0x31,
+ 0x09, 0x6a, 0xd6, 0x01,
+ 0x80, 0xeb, 0xa2, 0x79,
+ 0xf7, 0xeb, 0xd6, 0x09,
+ 0x08, 0xeb, 0xa6, 0x69,
+ 0x01, 0x6a, 0xd6, 0x01,
+ 0x08, 0xe9, 0x10, 0x31,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x39, 0x6a, 0xe6, 0x5b,
+ 0x08, 0x6a, 0x18, 0x01,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x09,
+ 0x0d, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0x64, 0x5c,
+ 0x88, 0x6a, 0x54, 0x5c,
+ 0x00, 0x65, 0xda, 0x5b,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0x08, 0x39, 0x72, 0x18,
+ 0x00, 0x3a, 0x74, 0x20,
+ 0x10, 0x0c, 0x66, 0x79,
+ 0x80, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0xe0, 0x59,
+ 0xff, 0x08, 0x52, 0x09,
+ 0xff, 0x09, 0x54, 0x09,
+ 0xff, 0x0a, 0x56, 0x09,
+ 0xff, 0x38, 0x50, 0x09,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x00, 0x65, 0x18, 0x41,
+ 0x00, 0x65, 0xe0, 0x59,
+ 0x12, 0x01, 0x02, 0x00,
+ 0x7f, 0x02, 0x04, 0x08,
+ 0xe1, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0x18, 0x41,
+ 0x04, 0x93, 0xea, 0x69,
+ 0xdf, 0x93, 0x26, 0x09,
+ 0x20, 0x93, 0xe4, 0x69,
+ 0x02, 0x93, 0x26, 0x01,
+ 0x01, 0x94, 0xe6, 0x79,
+ 0xd7, 0x93, 0x26, 0x09,
+ 0x08, 0x93, 0xec, 0x69,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x00, 0x65, 0x3a, 0x5b,
+ 0x02, 0xfc, 0xf8, 0x01,
+ 0x05, 0xb4, 0x10, 0x31,
+ 0x02, 0x6a, 0x1a, 0x31,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0xb4, 0x6a, 0xe4, 0x5b,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x09,
+ 0x00, 0x65, 0xda, 0x5b,
+ 0x3d, 0x6a, 0x22, 0x5b,
+ 0xac, 0x6a, 0x22, 0x5b,
+ 0x00, 0x65, 0x18, 0x41,
+ 0x00, 0x65, 0x3a, 0x5b,
+ 0xff, 0x06, 0x44, 0x09,
+ 0x00, 0x65, 0x18, 0x41,
+ 0xff, 0x34, 0xca, 0x08,
+ 0x80, 0x65, 0x32, 0x62,
+ 0x0f, 0xa1, 0xca, 0x08,
+ 0x07, 0xa1, 0xca, 0x08,
+ 0x40, 0xa0, 0xc8, 0x08,
+ 0x00, 0x65, 0xca, 0x00,
+ 0x80, 0x65, 0xca, 0x00,
+ 0x80, 0xa0, 0x22, 0x7a,
+ 0xff, 0x65, 0x0c, 0x08,
+ 0x00, 0x65, 0x34, 0x42,
+ 0x20, 0xa0, 0x3a, 0x7a,
+ 0xff, 0x65, 0x0c, 0x08,
+ 0x00, 0x65, 0xd2, 0x5b,
+ 0xa0, 0x3d, 0x46, 0x62,
+ 0x23, 0xa0, 0x0c, 0x08,
+ 0x00, 0x65, 0xd2, 0x5b,
+ 0xa0, 0x3d, 0x46, 0x62,
+ 0x00, 0xb9, 0x3a, 0x42,
+ 0xff, 0x65, 0x3a, 0x62,
+ 0xa1, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0x10, 0x51, 0x46, 0x72,
+ 0x40, 0x6a, 0x18, 0x00,
+ 0xff, 0x65, 0x0c, 0x08,
+ 0x00, 0x65, 0xd2, 0x5b,
+ 0xa0, 0x3d, 0x46, 0x62,
+ 0x10, 0x3d, 0x06, 0x00,
+ 0x00, 0x65, 0x0e, 0x42,
+ 0x40, 0x6a, 0x18, 0x00,
+ 0xff, 0x34, 0xa6, 0x08,
+ 0x80, 0x34, 0x4e, 0x62,
+ 0x7f, 0xa0, 0x40, 0x09,
+ 0x08, 0x6a, 0x68, 0x00,
+ 0x00, 0x65, 0x18, 0x41,
+ 0x64, 0x6a, 0x12, 0x5b,
+ 0x80, 0x64, 0xbe, 0x6a,
+ 0x04, 0x64, 0xa4, 0x72,
+ 0x02, 0x64, 0xaa, 0x72,
+ 0x00, 0x6a, 0x6c, 0x72,
+ 0x03, 0x64, 0xba, 0x72,
+ 0x01, 0x64, 0xa0, 0x72,
+ 0x07, 0x64, 0x00, 0x73,
+ 0x08, 0x64, 0x68, 0x72,
+ 0x11, 0x6a, 0x22, 0x01,
+ 0x07, 0x6a, 0x04, 0x5b,
+ 0xff, 0x06, 0xd4, 0x08,
+ 0x00, 0x65, 0x18, 0x41,
+ 0xff, 0xa8, 0x70, 0x6a,
+ 0xff, 0xa2, 0x88, 0x7a,
+ 0x01, 0x6a, 0x6a, 0x00,
+ 0x00, 0xb9, 0xfe, 0x5b,
+ 0xff, 0xa2, 0x88, 0x7a,
+ 0x71, 0x6a, 0x22, 0x01,
+ 0xff, 0x6a, 0xd4, 0x08,
+ 0x40, 0x51, 0x88, 0x62,
+ 0x0d, 0x6a, 0x6a, 0x00,
+ 0x00, 0xb9, 0xfe, 0x5b,
+ 0xff, 0x3e, 0x74, 0x09,
+ 0xff, 0x90, 0x7c, 0x08,
+ 0x00, 0x65, 0x44, 0x58,
+ 0x00, 0x65, 0x2a, 0x41,
+ 0x20, 0xa0, 0x90, 0x6a,
+ 0xff, 0x37, 0xc8, 0x08,
+ 0x00, 0x6a, 0xa8, 0x5b,
+ 0xff, 0x6a, 0xbe, 0x5b,
+ 0xff, 0xf8, 0xc8, 0x08,
+ 0xff, 0x4f, 0xc8, 0x08,
+ 0x01, 0x6a, 0xa8, 0x5b,
+ 0x00, 0xb9, 0xbe, 0x5b,
+ 0x01, 0x4f, 0x9e, 0x18,
+ 0x02, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0x6c, 0x5c,
+ 0x00, 0x65, 0x2a, 0x41,
+ 0x41, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0x18, 0x41,
+ 0x04, 0xa0, 0x40, 0x01,
+ 0x00, 0x65, 0x84, 0x5c,
+ 0x00, 0x65, 0x2a, 0x41,
+ 0x10, 0x36, 0x68, 0x7a,
+ 0xff, 0x38, 0x46, 0x09,
+ 0xa4, 0x6a, 0xcc, 0x00,
+ 0x39, 0x6a, 0xe6, 0x5b,
+ 0xac, 0x6a, 0xcc, 0x00,
+ 0x14, 0x6a, 0xe6, 0x5b,
+ 0xa9, 0x6a, 0xe8, 0x5b,
+ 0x00, 0x65, 0x68, 0x42,
+ 0xef, 0x36, 0x6c, 0x08,
+ 0x00, 0x65, 0x68, 0x42,
+ 0x0f, 0x64, 0xc8, 0x08,
+ 0x07, 0x64, 0xc8, 0x08,
+ 0x00, 0x37, 0x6e, 0x00,
+ 0x00, 0x65, 0x78, 0x5b,
+ 0xff, 0x51, 0xce, 0x72,
+ 0x20, 0x36, 0xde, 0x7a,
+ 0x00, 0x90, 0x5c, 0x5b,
+ 0x00, 0x65, 0xe0, 0x42,
+ 0xff, 0x06, 0xd4, 0x08,
+ 0x00, 0x65, 0xd2, 0x5b,
+ 0xe0, 0x3d, 0xfa, 0x62,
+ 0x20, 0x12, 0xfa, 0x62,
+ 0x51, 0x6a, 0x08, 0x5b,
+ 0xff, 0x51, 0x20, 0x09,
+ 0x20, 0xa0, 0xfa, 0x7a,
+ 0x00, 0x90, 0x5c, 0x5b,
+ 0x00, 0x65, 0x56, 0x5b,
+ 0xff, 0x37, 0xc8, 0x08,
+ 0x00, 0xa1, 0xf2, 0x62,
+ 0x04, 0xa0, 0xf2, 0x7a,
+ 0xfb, 0xa0, 0x40, 0x09,
+ 0x80, 0x36, 0x6c, 0x00,
+ 0x80, 0xa0, 0x68, 0x7a,
+ 0x7f, 0xa0, 0x40, 0x09,
+ 0xff, 0x6a, 0x04, 0x5b,
+ 0x00, 0x65, 0x68, 0x42,
+ 0x04, 0xa0, 0xf8, 0x7a,
+ 0x00, 0x65, 0x84, 0x5c,
+ 0x00, 0x65, 0xfa, 0x42,
+ 0x00, 0x65, 0x6c, 0x5c,
+ 0x31, 0x6a, 0x22, 0x01,
+ 0x0c, 0x6a, 0x04, 0x5b,
+ 0x00, 0x65, 0x68, 0x42,
+ 0x61, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0x68, 0x42,
+ 0x10, 0x3d, 0x06, 0x00,
+ 0xff, 0x65, 0x68, 0x0c,
+ 0xff, 0x06, 0xd4, 0x08,
+ 0x01, 0x0c, 0x0a, 0x7b,
+ 0x04, 0x0c, 0x0a, 0x6b,
+ 0xe0, 0x03, 0x7a, 0x08,
+ 0xe0, 0x3d, 0x1e, 0x63,
+ 0xff, 0x65, 0xcc, 0x08,
+ 0xff, 0x12, 0xda, 0x0c,
+ 0xff, 0x06, 0xd4, 0x0c,
+ 0xff, 0x65, 0x0c, 0x08,
+ 0x02, 0x0b, 0x1a, 0x7b,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0xd1, 0x6a, 0x22, 0x01,
+ 0x00, 0x65, 0x18, 0x41,
+ 0xff, 0x65, 0x26, 0x09,
+ 0x01, 0x0b, 0x32, 0x6b,
+ 0x10, 0x0c, 0x24, 0x7b,
+ 0x04, 0x0b, 0x2c, 0x6b,
+ 0xff, 0x6a, 0xca, 0x08,
+ 0x04, 0x93, 0x30, 0x6b,
+ 0x01, 0x94, 0x2e, 0x7b,
+ 0x10, 0x94, 0x30, 0x6b,
+ 0xc7, 0x93, 0x26, 0x09,
+ 0xff, 0x99, 0xd4, 0x08,
+ 0x08, 0x93, 0x34, 0x6b,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x80, 0x36, 0x38, 0x6b,
+ 0x21, 0x6a, 0x22, 0x05,
+ 0xff, 0x65, 0x20, 0x09,
+ 0xff, 0x51, 0x46, 0x63,
+ 0xff, 0x37, 0xc8, 0x08,
+ 0xa1, 0x6a, 0x50, 0x43,
+ 0xff, 0x51, 0xc8, 0x08,
+ 0xb9, 0x6a, 0x50, 0x43,
+ 0xff, 0xba, 0x54, 0x73,
+ 0xff, 0xba, 0x20, 0x09,
+ 0xff, 0x65, 0xca, 0x18,
+ 0x00, 0x6c, 0x4a, 0x63,
+ 0xff, 0x90, 0xca, 0x0c,
+ 0xff, 0x6a, 0xca, 0x04,
+ 0x20, 0x36, 0x72, 0x7b,
+ 0x00, 0x90, 0x3e, 0x5b,
+ 0xff, 0x65, 0x72, 0x73,
+ 0xff, 0xba, 0x66, 0x73,
+ 0xff, 0xbb, 0xcc, 0x08,
+ 0xff, 0xba, 0x20, 0x09,
+ 0xff, 0x66, 0x76, 0x09,
+ 0xff, 0x65, 0x20, 0x09,
+ 0xff, 0xbb, 0x70, 0x73,
+ 0xff, 0xba, 0xcc, 0x08,
+ 0xff, 0xbb, 0x20, 0x09,
+ 0xff, 0x66, 0x74, 0x09,
+ 0xff, 0x65, 0x20, 0x0d,
+ 0xff, 0xba, 0x7e, 0x0c,
+ 0x00, 0x6a, 0x72, 0x5c,
+ 0x0d, 0x6a, 0x6a, 0x00,
+ 0x00, 0x51, 0xfe, 0x43,
+ 0xff, 0x3f, 0xcc, 0x73,
+ 0xff, 0x6a, 0xa2, 0x00,
+ 0x00, 0x3f, 0x3e, 0x5b,
+ 0xff, 0x65, 0xcc, 0x73,
+ 0x20, 0x36, 0x6c, 0x00,
+ 0x20, 0xa0, 0x86, 0x6b,
+ 0xff, 0xb9, 0xa2, 0x0c,
+ 0xff, 0x6a, 0xa2, 0x04,
+ 0xff, 0x65, 0xa4, 0x08,
+ 0xe0, 0x6a, 0xcc, 0x00,
+ 0x45, 0x6a, 0xf2, 0x5b,
+ 0x01, 0x6a, 0xd0, 0x01,
+ 0x09, 0x6a, 0xd6, 0x01,
+ 0x80, 0xeb, 0x92, 0x7b,
+ 0x01, 0x6a, 0xd6, 0x01,
+ 0x01, 0xe9, 0xa4, 0x34,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x45, 0x6a, 0xf2, 0x5b,
+ 0x01, 0x6a, 0x18, 0x01,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x09,
+ 0x0d, 0x6a, 0x26, 0x01,
+ 0x00, 0x65, 0x64, 0x5c,
+ 0xff, 0x99, 0xa4, 0x0c,
+ 0xff, 0x65, 0xa4, 0x08,
+ 0xe0, 0x6a, 0xcc, 0x00,
+ 0x45, 0x6a, 0xf2, 0x5b,
+ 0x01, 0x6a, 0xd0, 0x01,
+ 0x01, 0x6a, 0xdc, 0x05,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x45, 0x6a, 0xf2, 0x5b,
+ 0x01, 0x6a, 0x18, 0x01,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x09,
+ 0x01, 0x6a, 0x26, 0x05,
+ 0x01, 0x65, 0xd8, 0x31,
+ 0x09, 0xee, 0xdc, 0x01,
+ 0x80, 0xee, 0xc2, 0x7b,
+ 0xff, 0x6a, 0xdc, 0x0d,
+ 0xff, 0x65, 0x32, 0x09,
+ 0x0a, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0x64, 0x44,
+ 0xff, 0x37, 0xc8, 0x08,
+ 0x00, 0x6a, 0x88, 0x5b,
+ 0xff, 0x52, 0xa2, 0x0c,
+ 0x01, 0x0c, 0xd2, 0x7b,
+ 0x04, 0x0c, 0xd2, 0x6b,
+ 0xe0, 0x03, 0x7a, 0x08,
+ 0xff, 0x3d, 0x06, 0x0c,
+ 0xff, 0x8c, 0x10, 0x08,
+ 0xff, 0x8d, 0x12, 0x08,
+ 0xff, 0x8e, 0x14, 0x0c,
+ 0xff, 0x6c, 0xda, 0x08,
+ 0xff, 0x6c, 0xda, 0x08,
+ 0xff, 0x6c, 0xda, 0x08,
+ 0xff, 0x6c, 0xda, 0x08,
+ 0xff, 0x6c, 0xda, 0x08,
+ 0xff, 0x6c, 0xda, 0x08,
+ 0xff, 0x6c, 0xda, 0x0c,
+ 0x3d, 0x64, 0xa4, 0x28,
+ 0x55, 0x64, 0xc8, 0x28,
+ 0x00, 0x6c, 0xda, 0x18,
+ 0xff, 0x52, 0xc8, 0x08,
+ 0x00, 0x6c, 0xda, 0x20,
+ 0xff, 0x6a, 0xc8, 0x08,
+ 0x00, 0x6c, 0xda, 0x20,
+ 0x00, 0x6c, 0xda, 0x24,
+ 0xff, 0x65, 0xc8, 0x08,
+ 0xe0, 0x6a, 0xcc, 0x00,
+ 0x41, 0x6a, 0xee, 0x5b,
+ 0xff, 0x90, 0xe2, 0x09,
+ 0x20, 0x6a, 0xd0, 0x01,
+ 0x04, 0x35, 0x10, 0x7c,
+ 0x1d, 0x6a, 0xdc, 0x01,
+ 0xdc, 0xee, 0x0c, 0x64,
+ 0x00, 0x65, 0x1c, 0x44,
+ 0x01, 0x6a, 0xdc, 0x01,
+ 0x20, 0xa0, 0xd8, 0x31,
+ 0x09, 0xee, 0xdc, 0x01,
+ 0x80, 0xee, 0x16, 0x7c,
+ 0x19, 0x6a, 0xdc, 0x01,
+ 0xd8, 0xee, 0x1a, 0x64,
+ 0xff, 0x6a, 0xdc, 0x09,
+ 0x18, 0xee, 0x1e, 0x6c,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0x88, 0x6a, 0xcc, 0x00,
+ 0x41, 0x6a, 0xee, 0x5b,
+ 0x20, 0x6a, 0x18, 0x01,
+ 0xff, 0x6a, 0x1a, 0x09,
+ 0xff, 0x6a, 0x1c, 0x09,
+ 0xff, 0x35, 0x26, 0x09,
+ 0x04, 0x35, 0x48, 0x6c,
+ 0xa0, 0x6a, 0xca, 0x00,
+ 0x20, 0x65, 0xc8, 0x18,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0xff, 0x6c, 0x32, 0x09,
+ 0x00, 0x65, 0x34, 0x64,
+ 0x0a, 0x93, 0x26, 0x01,
+ 0x00, 0x65, 0x64, 0x5c,
+ 0x04, 0x35, 0x38, 0x7b,
+ 0xa0, 0x6a, 0x54, 0x5c,
+ 0x00, 0x65, 0x56, 0x5c,
+ 0x00, 0x65, 0x56, 0x5c,
+ 0x00, 0x65, 0x56, 0x44,
+ 0xff, 0x65, 0xcc, 0x08,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x08,
+ 0xff, 0x99, 0xda, 0x0c,
+ 0x08, 0x94, 0x64, 0x7c,
+ 0xf7, 0x93, 0x26, 0x09,
+ 0x08, 0x93, 0x68, 0x6c,
+ 0xff, 0x6a, 0xd4, 0x0c,
+ 0xff, 0x40, 0x74, 0x09,
+ 0xff, 0x90, 0x80, 0x08,
+ 0xff, 0x6a, 0x72, 0x05,
+ 0xff, 0x40, 0x80, 0x64,
+ 0xff, 0x3f, 0x78, 0x64,
+ 0xff, 0x6a, 0xca, 0x04,
+ 0xff, 0x3f, 0x20, 0x09,
+ 0x01, 0x6a, 0x6a, 0x00,
+ 0x00, 0xb9, 0xfe, 0x5b,
+ 0x00, 0x90, 0x5c, 0x43,
+ 0xff, 0x40, 0x20, 0x09,
+ 0xff, 0xba, 0x80, 0x0c,
+ 0xff, 0x6a, 0x76, 0x01,
+ 0xff, 0x3f, 0x74, 0x09,
+ 0xff, 0x90, 0x7e, 0x08,
+ 0xff, 0xba, 0x38, 0x73,
+ 0xff, 0xba, 0x20, 0x09,
+ 0xff, 0x3f, 0x76, 0x09,
+ 0xff, 0x3f, 0x20, 0x0d,
+};
+
+static int aic7xxx_patch12_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch12_func(struct aic7xxx_host *p)
+{
+ return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895);
+}
+
+static int aic7xxx_patch11_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch11_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_WIDE) != 0);
+}
+
+static int aic7xxx_patch10_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch10_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_ULTRA2) == 0);
+}
+
+static int aic7xxx_patch9_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch9_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_ULTRA) != 0);
+}
+
+static int aic7xxx_patch8_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch8_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_ULTRA2) != 0);
+}
+
+static int aic7xxx_patch7_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch7_func(struct aic7xxx_host *p)
+{
+ return ((p->flags & AHC_PAGESCBS) == 0);
+}
+
+static int aic7xxx_patch6_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch6_func(struct aic7xxx_host *p)
+{
+ return ((p->flags & AHC_PAGESCBS) != 0);
+}
+
+static int aic7xxx_patch5_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch5_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_QUEUE_REGS) != 0);
+}
+
+static int aic7xxx_patch4_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch4_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_TWIN) != 0);
+}
+
+static int aic7xxx_patch3_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch3_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_QUEUE_REGS) == 0);
+}
+
+static int aic7xxx_patch2_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch2_func(struct aic7xxx_host *p)
+{
+ return ((p->features & AHC_CMD_CHAN) != 0);
+}
+
+static int aic7xxx_patch1_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch1_func(struct aic7xxx_host *p)
+{
+ return ((p->flags & AHC_TARGETMODE) != 0);
+}
+
+static int aic7xxx_patch0_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch0_func(struct aic7xxx_host *p)
+{
+ return (0);
+}
+
+struct sequencer_patch {
+ int (*patch_func)(struct aic7xxx_host *);
+ unsigned int begin :10,
+ skip_instr :10,
+ skip_patch :12;
+} sequencer_patches[] = {
+ { aic7xxx_patch1_func, 1, 1, 2 },
+ { aic7xxx_patch0_func, 2, 1, 1 },
+ { aic7xxx_patch2_func, 3, 2, 1 },
+ { aic7xxx_patch3_func, 7, 1, 1 },
+ { aic7xxx_patch3_func, 8, 1, 1 },
+ { aic7xxx_patch4_func, 11, 4, 1 },
+ { aic7xxx_patch5_func, 16, 3, 2 },
+ { aic7xxx_patch0_func, 19, 4, 1 },
+ { aic7xxx_patch6_func, 23, 1, 1 },
+ { aic7xxx_patch7_func, 26, 1, 1 },
+ { aic7xxx_patch4_func, 34, 4, 1 },
+ { aic7xxx_patch8_func, 38, 3, 2 },
+ { aic7xxx_patch0_func, 41, 3, 1 },
+ { aic7xxx_patch9_func, 47, 7, 1 },
+ { aic7xxx_patch4_func, 55, 3, 1 },
+ { aic7xxx_patch8_func, 58, 2, 1 },
+ { aic7xxx_patch1_func, 63, 60, 1 },
+ { aic7xxx_patch8_func, 164, 1, 2 },
+ { aic7xxx_patch0_func, 165, 1, 1 },
+ { aic7xxx_patch2_func, 169, 1, 1 },
+ { aic7xxx_patch2_func, 172, 1, 2 },
+ { aic7xxx_patch0_func, 173, 2, 1 },
+ { aic7xxx_patch10_func, 175, 1, 1 },
+ { aic7xxx_patch8_func, 182, 1, 2 },
+ { aic7xxx_patch0_func, 183, 3, 1 },
+ { aic7xxx_patch8_func, 187, 1, 2 },
+ { aic7xxx_patch0_func, 188, 1, 1 },
+ { aic7xxx_patch8_func, 189, 7, 2 },
+ { aic7xxx_patch0_func, 196, 1, 1 },
+ { aic7xxx_patch2_func, 201, 13, 2 },
+ { aic7xxx_patch0_func, 214, 8, 1 },
+ { aic7xxx_patch10_func, 222, 1, 1 },
+ { aic7xxx_patch8_func, 227, 1, 1 },
+ { aic7xxx_patch8_func, 228, 1, 1 },
+ { aic7xxx_patch8_func, 233, 1, 1 },
+ { aic7xxx_patch8_func, 235, 2, 1 },
+ { aic7xxx_patch8_func, 240, 8, 1 },
+ { aic7xxx_patch8_func, 249, 1, 1 },
+ { aic7xxx_patch2_func, 250, 2, 2 },
+ { aic7xxx_patch0_func, 252, 4, 1 },
+ { aic7xxx_patch10_func, 256, 2, 2 },
+ { aic7xxx_patch0_func, 258, 1, 1 },
+ { aic7xxx_patch11_func, 265, 1, 2 },
+ { aic7xxx_patch0_func, 266, 1, 1 },
+ { aic7xxx_patch5_func, 328, 1, 2 },
+ { aic7xxx_patch0_func, 329, 1, 1 },
+ { aic7xxx_patch3_func, 332, 1, 1 },
+ { aic7xxx_patch11_func, 351, 1, 2 },
+ { aic7xxx_patch0_func, 352, 1, 1 },
+ { aic7xxx_patch6_func, 356, 1, 1 },
+ { aic7xxx_patch7_func, 364, 3, 2 },
+ { aic7xxx_patch0_func, 367, 1, 1 },
+ { aic7xxx_patch1_func, 396, 3, 1 },
+ { aic7xxx_patch10_func, 410, 1, 1 },
+ { aic7xxx_patch2_func, 453, 7, 2 },
+ { aic7xxx_patch0_func, 460, 8, 1 },
+ { aic7xxx_patch2_func, 469, 4, 2 },
+ { aic7xxx_patch0_func, 473, 6, 1 },
+ { aic7xxx_patch2_func, 479, 4, 2 },
+ { aic7xxx_patch0_func, 483, 3, 1 },
+ { aic7xxx_patch2_func, 512, 17, 4 },
+ { aic7xxx_patch12_func, 520, 4, 2 },
+ { aic7xxx_patch0_func, 524, 2, 1 },
+ { aic7xxx_patch0_func, 529, 33, 1 },
+ { aic7xxx_patch6_func, 566, 2, 1 },
+ { aic7xxx_patch6_func, 569, 9, 1 },
+
+};
diff --git a/linux/src/drivers/scsi/constants.c b/linux/src/drivers/scsi/constants.c
new file mode 100644
index 00000000..1495a5d8
--- /dev/null
+++ b/linux/src/drivers/scsi/constants.c
@@ -0,0 +1,683 @@
+/*
+ * ASCII values for a number of symbolic constants, printing functions,
+ * etc.
+ */
+
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#define CONST_COMMAND 0x01
+#define CONST_STATUS 0x02
+#define CONST_SENSE 0x04
+#define CONST_XSENSE 0x08
+#define CONST_CMND 0x10
+#define CONST_MSG 0x20
+#define CONST_HOST 0x40
+#define CONST_DRIVER 0x80
+
+static const char unknown[] = "UNKNOWN";
+
+#ifdef CONFIG_SCSI_CONSTANTS
+#ifdef CONSTANTS
+#undef CONSTANTS
+#endif
+#define CONSTANTS (CONST_COMMAND | CONST_STATUS | CONST_SENSE | CONST_XSENSE \
+ | CONST_CMND | CONST_MSG | CONST_HOST | CONST_DRIVER)
+#endif
+
+#if (CONSTANTS & CONST_COMMAND)
+static const char * group_0_commands[] = {
+/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense",
+/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks",
+/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown,
+/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
+/* 13-16 */ unknown, "Recover Buffered Data", "Mode Select", "Reserve",
+/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit",
+/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic",
+/* 1e-1f */ "Prevent/Allow Medium Removal", unknown,
+};
+
+
+static const char *group_1_commands[] = {
+/* 20-22 */ unknown, unknown, unknown,
+/* 23-28 */ unknown, unknown, "Read Capacity", unknown, unknown, "Read (10)",
+/* 29-2d */ unknown, "Write (10)", "Seek (10)", unknown, unknown,
+/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal",
+/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position",
+/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data",
+/* 38-3c */ "Medium Scan", "Compare","Copy Verify", "Write Buffer", "Read Buffer",
+/* 3d-3f */ "Update Block", "Read Long", "Write Long",
+};
+
+
+static const char *group_2_commands[] = {
+/* 40-41 */ "Change Definition", "Write Same",
+/* 42-48 */ unknown, "Read TOC", unknown, unknown, unknown, unknown, unknown,
+/* 49-4f */ unknown, unknown, unknown, "Log Select", "Log Sense", unknown, unknown,
+/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)",
+/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown,
+/* 5c-5f */ unknown, unknown, unknown,
+};
+
+
+
+#define group(opcode) (((opcode) >> 5) & 7)
+
+#define RESERVED_GROUP 0
+#define VENDOR_GROUP 1
+#define NOTEXT_GROUP 2
+
+static const char **commands[] = {
+ group_0_commands, group_1_commands, group_2_commands,
+ (const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP,
+ (const char **) NOTEXT_GROUP, (const char **) VENDOR_GROUP,
+ (const char **) VENDOR_GROUP
+};
+
+static const char reserved[] = "RESERVED";
+static const char vendor[] = "VENDOR SPECIFIC";
+
+static void print_opcode(int opcode) {
+ const char **table = commands[ group(opcode) ];
+ switch ((unsigned long) table) {
+ case RESERVED_GROUP:
+ printk("%s(0x%02x) ", reserved, opcode);
+ break;
+ case NOTEXT_GROUP:
+ printk("%s(0x%02x) ", unknown, opcode);
+ break;
+ case VENDOR_GROUP:
+ printk("%s(0x%02x) ", vendor, opcode);
+ break;
+ default:
+ if (table[opcode & 0x1f] != unknown)
+ printk("%s ",table[opcode & 0x1f]);
+ else
+ printk("%s(0x%02x) ", unknown, opcode);
+ break;
+ }
+}
+#else /* CONST & CONST_COMMAND */
+static void print_opcode(int opcode) {
+ printk("0x%02x ", opcode);
+}
+#endif
+
+void print_command (unsigned char *command) {
+ int i,s;
+ print_opcode(command[0]);
+ for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+ printk("%02x ", command[i]);
+ printk("\n");
+}
+
+#if (CONSTANTS & CONST_STATUS)
+static const char * statuses[] = {
+/* 0-4 */ "Good", "Check Condition", "Condition Good", unknown, "Busy",
+/* 5-9 */ unknown, unknown, unknown, "Intermediate Good", unknown,
+/* a-d */ "Intermediate Good", unknown, "Reservation Conflict", unknown,
+/* e-f */ unknown, unknown,
+};
+#endif
+
+void print_status (int status) {
+ status = (status >> 1) & 0xf;
+#if (CONSTANTS & CONST_STATUS)
+ printk("%s ",statuses[status]);
+#else
+ printk("0x%0x ", status);
+#endif
+}
+
+#if (CONSTANTS & CONST_XSENSE)
+#define D 0x001 /* DIRECT ACCESS DEVICE (disk) */
+#define T 0x002 /* SEQUENTIAL ACCESS DEVICE (tape) */
+#define L 0x004 /* PRINTER DEVICE */
+#define P 0x008 /* PROCESSOR DEVICE */
+#define W 0x010 /* WRITE ONCE READ MULTIPLE DEVICE */
+#define R 0x020 /* READ ONLY (CD-ROM) DEVICE */
+#define S 0x040 /* SCANNER DEVICE */
+#define O 0x080 /* OPTICAL MEMORY DEVICE */
+#define M 0x100 /* MEDIA CHANGER DEVICE */
+#define C 0x200 /* COMMUNICATION DEVICE */
+
+struct error_info{
+ unsigned char code1, code2;
+ unsigned short int devices;
+ const char * text;
+};
+
+struct error_info2{
+ unsigned char code1, code2_min, code2_max;
+ unsigned short int devices;
+ const char * text;
+};
+
+static struct error_info2 additional2[] =
+{
+ {0x40,0x00,0x7f,D,"Ram failure (%x)"},
+ {0x40,0x80,0xff,D|T|L|P|W|R|S|O|M|C,"Diagnostic failure on component (%x)"},
+ {0x41,0x00,0xff,D,"Data path failure (%x)"},
+ {0x42,0x00,0xff,D,"Power-on or self-test failure (%x)"},
+ {0, 0, 0, 0, NULL}
+};
+
+static struct error_info additional[] =
+{
+ {0x00,0x01,T,"Filemark detected"},
+ {0x00,0x02,T|S,"End-of-partition/medium detected"},
+ {0x00,0x03,T,"Setmark detected"},
+ {0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
+ {0x00,0x05,T|S,"End-of-data detected"},
+ {0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"},
+ {0x00,0x11,R,"Audio play operation in progress"},
+ {0x00,0x12,R,"Audio play operation paused"},
+ {0x00,0x13,R,"Audio play operation successfully completed"},
+ {0x00,0x14,R,"Audio play operation stopped due to error"},
+ {0x00,0x15,R,"No current audio status to return"},
+ {0x01,0x00,D|W|O,"No index/sector signal"},
+ {0x02,0x00,D|W|R|O|M,"No seek complete"},
+ {0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"},
+ {0x03,0x01,T,"No write current"},
+ {0x03,0x02,T,"Excessive write errors"},
+ {0x04,0x00,D|T|L|P|W|R|S|O|M|C,
+ "Logical unit not ready, cause not reportable"},
+ {0x04,0x01,D|T|L|P|W|R|S|O|M|C,
+ "Logical unit is in process of becoming ready"},
+ {0x04,0x02,D|T|L|P|W|R|S|O|M|C,
+ "Logical unit not ready, initializing command required"},
+ {0x04,0x03,D|T|L|P|W|R|S|O|M|C,
+ "Logical unit not ready, manual intervention required"},
+ {0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"},
+ {0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"},
+ {0x06,0x00,D|W|R|O|M,"No reference position found"},
+ {0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"},
+ {0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"},
+ {0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"},
+ {0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"},
+ {0x09,0x00,D|T|W|R|O,"Track following error"},
+ {0x09,0x01,W|R|O,"Tracking servo failure"},
+ {0x09,0x02,W|R|O,"Focus servo failure"},
+ {0x09,0x03,W|R|O,"Spindle servo failure"},
+ {0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"},
+ {0x0C,0x00,T|S,"Write error"},
+ {0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"},
+ {0x0C,0x02,D|W|O,"Write error - auto reallocation failed"},
+ {0x10,0x00,D|W|O,"Id crc or ecc error"},
+ {0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"},
+ {0x11,0x01,D|T|W|S|O,"Read retries exhausted"},
+ {0x11,0x02,D|T|W|S|O,"Error too long to correct"},
+ {0x11,0x03,D|T|W|S|O,"Multiple read errors"},
+ {0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"},
+ {0x11,0x05,W|R|O,"L-ec uncorrectable error"},
+ {0x11,0x06,W|R|O,"Circ unrecovered error"},
+ {0x11,0x07,W|O,"Data resynchronization error"},
+ {0x11,0x08,T,"Incomplete block read"},
+ {0x11,0x09,T,"No gap found"},
+ {0x11,0x0A,D|T|O,"Miscorrected error"},
+ {0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"},
+ {0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"},
+ {0x12,0x00,D|W|O,"Address mark not found for id field"},
+ {0x13,0x00,D|W|O,"Address mark not found for data field"},
+ {0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"},
+ {0x14,0x01,D|T|W|R|O,"Record not found"},
+ {0x14,0x02,T,"Filemark or setmark not found"},
+ {0x14,0x03,T,"End-of-data not found"},
+ {0x14,0x04,T,"Block sequence error"},
+ {0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"},
+ {0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"},
+ {0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"},
+ {0x16,0x00,D|W|O,"Data synchronization mark error"},
+ {0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"},
+ {0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"},
+ {0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"},
+ {0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"},
+ {0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"},
+ {0x17,0x05,D|W|R|O,"Recovered data using previous sector id"},
+ {0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"},
+ {0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"},
+ {0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"},
+ {0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"},
+ {0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"},
+ {0x18,0x03,R,"Recovered data with circ"},
+ {0x18,0x04,R,"Recovered data with lec"},
+ {0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"},
+ {0x19,0x00,D|O,"Defect list error"},
+ {0x19,0x01,D|O,"Defect list not available"},
+ {0x19,0x02,D|O,"Defect list error in primary list"},
+ {0x19,0x03,D|O,"Defect list error in grown list"},
+ {0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"},
+ {0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"},
+ {0x1C,0x00,D|O,"Defect list not found"},
+ {0x1C,0x01,D|O,"Primary defect list not found"},
+ {0x1C,0x02,D|O,"Grown defect list not found"},
+ {0x1D,0x00,D|W|O,"Miscompare during verify operation"},
+ {0x1E,0x00,D|W|O,"Recovered id with ecc correction"},
+ {0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"},
+ {0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"},
+ {0x21,0x01,M,"Invalid element address"},
+ {0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"},
+ {0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"},
+ {0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"},
+ {0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"},
+ {0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"},
+ {0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"},
+ {0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"},
+ {0x27,0x00,D|T|W|O,"Write protected"},
+ {0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"},
+ {0x28,0x01,M,"Import or export element accessed"},
+ {0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"},
+ {0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"},
+ {0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"},
+ {0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"},
+ {0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"},
+ {0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"},
+ {0x2C,0x01,S,"Too many windows specified"},
+ {0x2C,0x02,S,"Invalid combination of windows specified"},
+ {0x2D,0x00,T,"Overwrite error on update in place"},
+ {0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"},
+ {0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"},
+ {0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"},
+ {0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"},
+ {0x30,0x03,D|T,"Cleaning cartridge installed"},
+ {0x31,0x00,D|T|W|O,"Medium format corrupted"},
+ {0x31,0x01,D|L|O,"Format command failed"},
+ {0x32,0x00,D|W|O,"No defect spare location available"},
+ {0x32,0x01,D|W|O,"Defect list update failure"},
+ {0x33,0x00,T,"Tape length error"},
+ {0x36,0x00,L,"Ribbon, ink, or toner failure"},
+ {0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"},
+ {0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"},
+ {0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"},
+ {0x3B,0x00,T|L,"Sequential positioning error"},
+ {0x3B,0x01,T,"Tape position error at beginning-of-medium"},
+ {0x3B,0x02,T,"Tape position error at end-of-medium"},
+ {0x3B,0x03,L,"Tape or electronic vertical forms unit not ready"},
+ {0x3B,0x04,L,"Slew failure"},
+ {0x3B,0x05,L,"Paper jam"},
+ {0x3B,0x06,L,"Failed to sense top-of-form"},
+ {0x3B,0x07,L,"Failed to sense bottom-of-form"},
+ {0x3B,0x08,T,"Reposition error"},
+ {0x3B,0x09,S,"Read past end of medium"},
+ {0x3B,0x0A,S,"Read past beginning of medium"},
+ {0x3B,0x0B,S,"Position past end of medium"},
+ {0x3B,0x0C,S,"Position past beginning of medium"},
+ {0x3B,0x0D,M,"Medium destination element full"},
+ {0x3B,0x0E,M,"Medium source element empty"},
+ {0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"},
+ {0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"},
+ {0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"},
+ {0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"},
+ {0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"},
+ {0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"},
+ {0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"},
+ {0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"},
+ {0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"},
+ {0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"},
+ {0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"},
+ {0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"},
+ {0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"},
+ {0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"},
+ {0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"},
+ {0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"},
+ {0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"},
+ {0x50,0x00,T,"Write append error"},
+ {0x50,0x01,T,"Write append position error"},
+ {0x50,0x02,T,"Position error related to timing"},
+ {0x51,0x00,T|O,"Erase failure"},
+ {0x52,0x00,T,"Cartridge fault"},
+ {0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"},
+ {0x53,0x01,T,"Unload tape failure"},
+ {0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"},
+ {0x54,0x00,P,"Scsi to host system interface failure"},
+ {0x55,0x00,P,"System resource failure"},
+ {0x57,0x00,R,"Unable to recover table-of-contents"},
+ {0x58,0x00,O,"Generation does not exist"},
+ {0x59,0x00,O,"Updated block read"},
+ {0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"},
+ {0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"},
+ {0x5A,0x02,D|T|W|O,"Operator selected write protect"},
+ {0x5A,0x03,D|T|W|O,"Operator selected write permit"},
+ {0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"},
+ {0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"},
+ {0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"},
+ {0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"},
+ {0x5C,0x00,D|O,"Rpl status change"},
+ {0x5C,0x01,D|O,"Spindles synchronized"},
+ {0x5C,0x02,D|O,"Spindles not synchronized"},
+ {0x60,0x00,S,"Lamp failure"},
+ {0x61,0x00,S,"Video acquisition error"},
+ {0x61,0x01,S,"Unable to acquire video"},
+ {0x61,0x02,S,"Out of focus"},
+ {0x62,0x00,S,"Scan head positioning error"},
+ {0x63,0x00,R,"End of user area encountered on this track"},
+ {0x64,0x00,R,"Illegal mode for this track"},
+ {0, 0, 0, NULL}
+};
+#endif
+
+#if (CONSTANTS & CONST_SENSE)
+static const char *snstext[] = {
+ "None", /* There is no sense information */
+ "Recovered Error", /* The last command completed successfully
+ but used error correction */
+ "Not Ready", /* The addressed target is not ready */
+ "Medium Error", /* Data error detected on the medium */
+ "Hardware Error", /* Controller or device failure */
+ "Illegal Request",
+ "Unit Attention", /* Removable medium was changed, or
+ the target has been reset */
+ "Data Protect", /* Access to the data is blocked */
+ "Blank Check", /* Reached unexpected written or unwritten
+ region of the medium */
+ "Key=9", /* Vendor specific */
+ "Copy Aborted", /* COPY or COMPARE was aborted */
+ "Aborted Command", /* The target aborted the command */
+ "Equal", /* A SEARCH DATA command found data equal */
+ "Volume Overflow", /* Medium full with still data to be written */
+ "Miscompare", /* Source data and data on the medium
+ do not agree */
+ "Key=15" /* Reserved */
+};
+#endif
+
+/* Print sense information */
+void print_sense(const char * devclass, Scsi_Cmnd * SCpnt)
+{
+ int i, s;
+ int sense_class, valid, code;
+ unsigned char * sense_buffer = SCpnt->sense_buffer;
+ const char * error = NULL;
+
+ sense_class = (sense_buffer[0] >> 4) & 0x07;
+ code = sense_buffer[0] & 0xf;
+ valid = sense_buffer[0] & 0x80;
+
+ if (sense_class == 7) { /* extended sense data */
+ s = sense_buffer[7] + 8;
+ if(s > sizeof(SCpnt->sense_buffer))
+ s = sizeof(SCpnt->sense_buffer);
+
+ if (!valid)
+ printk("extra data not valid ");
+
+ if (sense_buffer[2] & 0x80)
+ printk( "FMK "); /* current command has read a filemark */
+ if (sense_buffer[2] & 0x40)
+ printk( "EOM "); /* end-of-medium condition exists */
+ if (sense_buffer[2] & 0x20)
+ printk( "ILI "); /* incorrect block length requested */
+
+ switch (code) {
+ case 0x0:
+ error = "Current"; /* error concerns current command */
+ break;
+ case 0x1:
+ error = "Deferred"; /* error concerns some earlier command */
+ /* e.g., an earlier write to disk cache succeeded, but
+ now the disk discovers that it cannot write the data */
+ break;
+ default:
+ error = "Invalid";
+ }
+
+ printk("%s error ", error);
+
+#if (CONSTANTS & CONST_SENSE)
+ printk( "%s%s: sense key %s\n", devclass,
+ kdevname(SCpnt->request.rq_dev), snstext[sense_buffer[2] & 0x0f]);
+#else
+ printk("%s%s: sns = %2x %2x\n", devclass,
+ kdevname(SCpnt->request.rq_dev), sense_buffer[0], sense_buffer[2]);
+#endif
+
+ /* Check to see if additional sense information is available */
+ if(sense_buffer[7] + 7 < 13 ||
+ (sense_buffer[12] == 0 && sense_buffer[13] == 0)) goto done;
+
+#if (CONSTANTS & CONST_XSENSE)
+ for(i=0; additional[i].text; i++)
+ if(additional[i].code1 == sense_buffer[12] &&
+ additional[i].code2 == sense_buffer[13])
+ printk("Additional sense indicates %s\n", additional[i].text);
+
+ for(i=0; additional2[i].text; i++)
+ if(additional2[i].code1 == sense_buffer[12] &&
+ additional2[i].code2_min >= sense_buffer[13] &&
+ additional2[i].code2_max <= sense_buffer[13]) {
+ printk("Additional sense indicates ");
+ printk(additional2[i].text, sense_buffer[13]);
+ printk("\n");
+ };
+#else
+ printk("ASC=%2x ASCQ=%2x\n", sense_buffer[12], sense_buffer[13]);
+#endif
+ } else { /* non-extended sense data */
+
+ /*
+ * Standard says:
+ * sense_buffer[0] & 0200 : address valid
+ * sense_buffer[0] & 0177 : vendor-specific error code
+ * sense_buffer[1] & 0340 : vendor-specific
+ * sense_buffer[1..3] : 21-bit logical block address
+ */
+
+#if (CONSTANTS & CONST_SENSE)
+ if (sense_buffer[0] < 15)
+ printk("%s%s: old sense key %s\n", devclass,
+ kdevname(SCpnt->request.rq_dev), snstext[sense_buffer[0] & 0x0f]);
+ else
+#endif
+ printk("%s%s: sns = %2x %2x\n", devclass,
+ kdevname(SCpnt->request.rq_dev), sense_buffer[0], sense_buffer[2]);
+
+ printk("Non-extended sense class %d code 0x%0x ", sense_class, code);
+ s = 4;
+ }
+
+ done:
+#if !(CONSTANTS & CONST_SENSE)
+ printk("Raw sense data:");
+ for (i = 0; i < s; ++i)
+ printk("0x%02x ", sense_buffer[i]);
+ printk("\n");
+#endif
+ return;
+}
+
+#if (CONSTANTS & CONST_MSG)
+static const char *one_byte_msgs[] = {
+/* 0x00 */ "Command Complete", NULL, "Save Pointers",
+/* 0x03 */ "Restore Pointers", "Disconnect", "Initiator Error",
+/* 0x06 */ "Abort", "Message Reject", "Nop", "Message Parity Error",
+/* 0x0a */ "Linked Command Complete", "Linked Command Complete w/flag",
+/* 0x0c */ "Bus device reset", "Abort Tag", "Clear Queue",
+/* 0x0f */ "Initiate Recovery", "Release Recovery"
+};
+
+#define NO_ONE_BYTE_MSGS (sizeof(one_byte_msgs) / sizeof (const char *))
+
+static const char *two_byte_msgs[] = {
+/* 0x20 */ "Simple Queue Tag", "Head of Queue Tag", "Ordered Queue Tag"
+/* 0x23 */ "Ignore Wide Residue"
+};
+
+#define NO_TWO_BYTE_MSGS (sizeof(two_byte_msgs) / sizeof (const char *))
+
+static const char *extended_msgs[] = {
+/* 0x00 */ "Modify Data Pointer", "Synchronous Data Transfer Request",
+/* 0x02 */ "SCSI-I Extended Identify", "Wide Data Transfer Request"
+};
+
+#define NO_EXTENDED_MSGS (sizeof(two_byte_msgs) / sizeof (const char *))
+#endif /* (CONSTANTS & CONST_MSG) */
+
+int print_msg (const unsigned char *msg) {
+ int len = 0, i;
+ if (msg[0] == EXTENDED_MESSAGE) {
+ len = 3 + msg[1];
+#if (CONSTANTS & CONST_MSG)
+ if (msg[2] < NO_EXTENDED_MSGS)
+ printk ("%s ", extended_msgs[msg[2]]);
+ else
+ printk ("Extended Message, reserved code (0x%02x) ", (int) msg[2]);
+ switch (msg[2]) {
+ case EXTENDED_MODIFY_DATA_POINTER:
+ printk("pointer = %d", (int) (msg[3] << 24) | (msg[4] << 16) |
+ (msg[5] << 8) | msg[6]);
+ break;
+ case EXTENDED_SDTR:
+ printk("period = %d ns, offset = %d", (int) msg[3] * 4, (int)
+ msg[4]);
+ break;
+ case EXTENDED_WDTR:
+ printk("width = 2^%d bytes", msg[3]);
+ break;
+ default:
+ for (i = 2; i < len; ++i)
+ printk("%02x ", msg[i]);
+ }
+#else
+ for (i = 0; i < len; ++i)
+ printk("%02x ", msg[i]);
+#endif
+ /* Identify */
+ } else if (msg[0] & 0x80) {
+#if (CONSTANTS & CONST_MSG)
+ printk("Identify disconnect %sallowed %s %d ",
+ (msg[0] & 0x40) ? "" : "not ",
+ (msg[0] & 0x20) ? "target routine" : "lun",
+ msg[0] & 0x7);
+#else
+ printk("%02x ", msg[0]);
+#endif
+ len = 1;
+ /* Normal One byte */
+ } else if (msg[0] < 0x1f) {
+#if (CONSTANTS & CONST_MSG)
+ if (msg[0] < NO_ONE_BYTE_MSGS)
+ printk(one_byte_msgs[msg[0]]);
+ else
+ printk("reserved (%02x) ", msg[0]);
+#else
+ printk("%02x ", msg[0]);
+#endif
+ len = 1;
+ /* Two byte */
+ } else if (msg[0] <= 0x2f) {
+#if (CONSTANTS & CONST_MSG)
+ if ((msg[0] - 0x20) < NO_TWO_BYTE_MSGS)
+ printk("%s %02x ", two_byte_msgs[msg[0] - 0x20],
+ msg[1]);
+ else
+ printk("reserved two byte (%02x %02x) ",
+ msg[0], msg[1]);
+#else
+ printk("%02x %02x", msg[0], msg[1]);
+#endif
+ len = 2;
+ } else
+#if (CONSTANTS & CONST_MSG)
+ printk(reserved);
+#else
+ printk("%02x ", msg[0]);
+#endif
+ return len;
+}
+
+void print_Scsi_Cmnd (Scsi_Cmnd *cmd) {
+ printk("scsi%d : destination target %d, lun %d\n",
+ cmd->host->host_no,
+ cmd->target,
+ cmd->lun);
+ printk(" command = ");
+ print_command (cmd->cmnd);
+}
+
+#if (CONSTANTS & CONST_HOST)
+static const char * hostbyte_table[]={
+"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET",
+"DID_ABORT", "DID_PARITY", "DID_ERROR", "DID_RESET", "DID_BAD_INTR",NULL};
+
+void print_hostbyte(int scsiresult)
+{ static int maxcode=0;
+ int i;
+
+ if(!maxcode) {
+ for(i=0;hostbyte_table[i];i++) ;
+ maxcode=i-1;
+ }
+ printk("Hostbyte=0x%02x",host_byte(scsiresult));
+ if(host_byte(scsiresult)>maxcode) {
+ printk("is invalid ");
+ return;
+ }
+ printk("(%s) ",hostbyte_table[host_byte(scsiresult)]);
+}
+#else
+void print_hostbyte(int scsiresult)
+{ printk("Hostbyte=0x%02x ",host_byte(scsiresult));
+}
+#endif
+
+#if (CONSTANTS & CONST_DRIVER)
+static const char * driverbyte_table[]={
+"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR",
+"DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",NULL };
+
+static const char * driversuggest_table[]={"SUGGEST_OK",
+"SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE",
+unknown,unknown,unknown, "SUGGEST_SENSE",NULL};
+
+
+void print_driverbyte(int scsiresult)
+{ static int driver_max=0,suggest_max=0;
+ int i,dr=driver_byte(scsiresult)&DRIVER_MASK,
+ su=(driver_byte(scsiresult)&SUGGEST_MASK)>>4;
+
+ if(!driver_max) {
+ for(i=0;driverbyte_table[i];i++) ;
+ driver_max=i;
+ for(i=0;driversuggest_table[i];i++) ;
+ suggest_max=i;
+ }
+ printk("Driverbyte=0x%02x",driver_byte(scsiresult));
+ printk("(%s,%s) ",
+ dr<driver_max ? driverbyte_table[dr]:"invalid",
+ su<suggest_max ? driversuggest_table[su]:"invalid");
+}
+#else
+void print_driverbyte(int scsiresult)
+{ printk("Driverbyte=0x%02x ",driver_byte(scsiresult));
+}
+#endif
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/constants.h b/linux/src/drivers/scsi/constants.h
new file mode 100644
index 00000000..e10527ea
--- /dev/null
+++ b/linux/src/drivers/scsi/constants.h
@@ -0,0 +1,6 @@
+#ifndef _CONSTANTS_H
+#define _CONSTANTS_H
+extern int print_msg(unsigned char *);
+extern void print_status(int);
+extern void print_Scsi_Cmnd (Scsi_Cmnd *);
+#endif /* def _CONSTANTS_H */
diff --git a/linux/src/drivers/scsi/dc390.h b/linux/src/drivers/scsi/dc390.h
new file mode 100644
index 00000000..18c7e037
--- /dev/null
+++ b/linux/src/drivers/scsi/dc390.h
@@ -0,0 +1,147 @@
+/***********************************************************************
+ * FILE NAME : DC390.H *
+ * BY : C.L. Huang *
+ * Description: Device Driver for Tekram DC-390(T) PCI SCSI *
+ * Bus Master Host Adapter *
+ ***********************************************************************/
+
+/* Kernel version autodetection */
+
+#include <linux/version.h>
+/* Convert Linux Version, Patch-level, Sub-level to LINUX_VERSION_CODE. */
+#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,50)
+#define VERSION_ELF_1_2_13
+#elseif LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,95)
+#define VERSION_1_3_85
+#else
+#define VERSION_2_0_0
+#endif
+
+/*
+ * AMD 53C974 driver, header file
+ */
+
+#ifndef DC390_H
+#define DC390_H
+
+#if defined(HOSTS_C) || defined(MODULE)
+
+#ifdef VERSION_2_0_0
+#include <scsi/scsicam.h>
+#else
+#include <linux/scsicam.h>
+#endif
+
+extern int DC390_detect(Scsi_Host_Template *psht);
+extern int DC390_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+extern int DC390_abort(Scsi_Cmnd *cmd);
+
+#ifdef VERSION_2_0_0
+extern int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags);
+#else
+extern int DC390_reset(Scsi_Cmnd *cmd);
+#endif
+
+#ifdef VERSION_ELF_1_2_13
+extern int DC390_bios_param(Disk *disk, int devno, int geom[]);
+#else
+extern int DC390_bios_param(Disk *disk, kdev_t devno, int geom[]);
+#endif
+
+#ifdef MODULE
+static int DC390_release(struct Scsi_Host *);
+#else
+#define DC390_release NULL
+#endif
+
+#ifndef VERSION_ELF_1_2_13
+extern struct proc_dir_entry proc_scsi_tmscsim;
+extern int tmscsim_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout);
+#endif
+
+#ifdef VERSION_2_0_0
+
+#define DC390_T { \
+ NULL, /* *next */ \
+ NULL, /* *usage_count */ \
+ &proc_scsi_tmscsim, /* *proc_dir */ \
+ tmscsim_proc_info, /* (*proc_info)() */ \
+ "Tekram DC390(T) V1.11 Feb-05-1997", /* *name */ \
+ DC390_detect, \
+ DC390_release, /* (*release)() */ \
+ NULL, /* *(*info)() */ \
+ NULL, /* (*command)() */ \
+ DC390_queue_command, \
+ DC390_abort, \
+ DC390_reset, \
+ NULL, /* slave attach */\
+ DC390_bios_param, \
+ 10,/* can queue(-1) */ \
+ 7, /* id(-1) */ \
+ SG_ALL, \
+ 2, /* cmd per lun(2) */ \
+ 0, /* present */ \
+ 0, /* unchecked isa dma */ \
+ DISABLE_CLUSTERING \
+ }
+#endif
+
+
+#ifdef VERSION_1_3_85
+
+#define DC390_T { \
+ NULL, /* *next */ \
+ NULL, /* *usage_count */ \
+ &proc_scsi_tmscsim, /* *proc_dir */ \
+ tmscsim_proc_info, /* (*proc_info)() */ \
+ "Tekram DC390(T) V1.11 Feb-05-1997", /* *name */ \
+ DC390_detect, \
+ DC390_release, /* (*release)() */ \
+ NULL, /* *(*info)() */ \
+ NULL, /* (*command)() */ \
+ DC390_queue_command, \
+ DC390_abort, \
+ DC390_reset, \
+ NULL, /* slave attach */\
+ DC390_bios_param, \
+ 10,/* can queue(-1) */ \
+ 7, /* id(-1) */ \
+ SG_ALL, \
+ 2, /* cmd per lun(2) */ \
+ 0, /* present */ \
+ 0, /* unchecked isa dma */ \
+ DISABLE_CLUSTERING \
+ }
+#endif
+
+
+#ifdef VERSION_ELF_1_2_13
+
+#define DC390_T { \
+ NULL, \
+ NULL, \
+ "Tekram DC390(T) V1.11 Feb-05-1997",\
+ DC390_detect, \
+ DC390_release, \
+ NULL, /* info */ \
+ NULL, /* command, deprecated */ \
+ DC390_queue_command, \
+ DC390_abort, \
+ DC390_reset, \
+ NULL, /* slave attach */\
+ DC390_bios_param, \
+ 10,/* can queue(-1) */ \
+ 7, /* id(-1) */ \
+ 16,/* old (SG_ALL) */ \
+ 2, /* cmd per lun(2) */ \
+ 0, /* present */ \
+ 0, /* unchecked isa dma */ \
+ DISABLE_CLUSTERING \
+ }
+#endif
+
+#endif /* defined(HOSTS_C) || defined(MODULE) */
+
+#endif /* DC390_H */
diff --git a/linux/src/drivers/scsi/dc390w.h b/linux/src/drivers/scsi/dc390w.h
new file mode 100644
index 00000000..7e4030a7
--- /dev/null
+++ b/linux/src/drivers/scsi/dc390w.h
@@ -0,0 +1,145 @@
+/***********************************************************************
+ * FILE NAME : DC390W.H *
+ * BY : C.L. Huang *
+ * Description: Device Driver for Tekram DC-390W/U/F (T) PCI SCSI *
+ * Bus Master Host Adapter *
+ ***********************************************************************/
+
+/* Kernel version autodetection */
+
+#include <linux/version.h>
+/* Convert Linux Version, Patch-level, Sub-level to LINUX_VERSION_CODE. */
+#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
+
+#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,50)
+#define VERSION_ELF_1_2_13
+#elseif LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,95)
+#define VERSION_1_3_85
+#else
+#define VERSION_2_0_0
+#endif
+
+/*
+ * NCR 53c825A,875 driver, header file
+ */
+
+#ifndef DC390W_H
+#define DC390W_H
+
+#if defined(HOSTS_C) || defined(MODULE)
+
+#ifdef VERSION_2_0_0
+#include <scsi/scsicam.h>
+#else
+#include <linux/scsicam.h>
+#endif
+
+extern int DC390W_detect(Scsi_Host_Template *psht);
+extern int DC390W_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+extern int DC390W_abort(Scsi_Cmnd *cmd);
+
+#ifdef VERSION_2_0_0
+extern int DC390W_reset(Scsi_Cmnd *cmd, unsigned int resetFlags);
+#else
+extern int DC390W_reset(Scsi_Cmnd *cmd);
+#endif
+
+#ifdef VERSION_ELF_1_2_13
+extern int DC390W_bios_param(Disk *disk, int devno, int geom[]);
+#else
+extern int DC390W_bios_param(Disk *disk, kdev_t devno, int geom[]);
+#endif
+
+#ifdef MODULE
+static int DC390W_release(struct Scsi_Host *);
+#else
+#define DC390W_release NULL
+#endif
+
+#ifndef VERSION_ELF_1_2_13
+extern struct proc_dir_entry proc_scsi_tmscsiw;
+extern int tmscsiw_proc_info(char*, char**, off_t, int, int, int);
+#endif
+
+#ifdef VERSION_2_0_0
+
+#define DC390WUF { \
+ NULL, /* *next */ \
+ NULL, /* *usage_count */ \
+ &proc_scsi_tmscsiw, /* *proc_dir */ \
+ tmscsiw_proc_info, /* (*proc_info)() */ \
+ "Tekram DC390WUF(T) V1.12 Feb-17-1997", /* *name */ \
+ DC390W_detect, \
+ DC390W_release, /* (*release)() */ \
+ NULL, /* *(*info)() */ \
+ NULL, /* (*command)() */ \
+ DC390W_queue_command, \
+ DC390W_abort, \
+ DC390W_reset, \
+ NULL, /* slave attach */\
+ DC390W_bios_param, \
+ 10,/* can queue(-1) */ \
+ 7, /* id(-1) */ \
+ 32,/* old (SG_ALL) */ \
+ 2, /* cmd per lun(2) */ \
+ 0, /* present */ \
+ 0, /* unchecked isa dma */ \
+ ENABLE_CLUSTERING \
+ }
+#endif
+
+#ifdef VERSION_1_3_85
+
+#define DC390WUF { \
+ NULL, /* *next */ \
+ NULL, /* *usage_count */ \
+ &proc_scsi_tmscsiw, /* *proc_dir */ \
+ tmscsiw_proc_info, /* (*proc_info)() */ \
+ "Tekram DC390WUF(T) V1.12 Feb-17-1997", /* *name */ \
+ DC390W_detect, \
+ DC390W_release, /* (*release)() */ \
+ NULL, /* *(*info)() */ \
+ NULL, /* (*command)() */ \
+ DC390W_queue_command, \
+ DC390W_abort, \
+ DC390W_reset, \
+ NULL, /* slave attach */\
+ DC390W_bios_param, \
+ 10,/* can queue(-1) */ \
+ 7, /* id(-1) */ \
+ 16,/* old (SG_ALL) */ \
+ 2, /* cmd per lun(2) */ \
+ 0, /* present */ \
+ 0, /* unchecked isa dma */ \
+ ENABLE_CLUSTERING \
+ }
+#endif
+
+#ifdef VERSION_ELF_1_2_13
+
+#define DC390WUF { \
+ NULL, \
+ NULL, \
+ "Tekram DC390WUF(T) V1.12 Feb-17-1997",\
+ DC390W_detect, \
+ DC390W_release, \
+ NULL, /* info */ \
+ NULL, /* command, deprecated */ \
+ DC390W_queue_command, \
+ DC390W_abort, \
+ DC390W_reset, \
+ NULL, /* slave attach */\
+ DC390W_bios_param, \
+ 10,/* can queue(-1) */ \
+ 7, /* id(-1) */ \
+ 16,/* old (SG_ALL) */ \
+ 2, /* cmd per lun(2) */ \
+ 0, /* present */ \
+ 0, /* unchecked isa dma */ \
+ ENABLE_CLUSTERING \
+ }
+#endif
+
+#endif /* defined(HOSTS_C) || defined(MODULE) */
+
+#endif /* DC390W_H */
diff --git a/linux/src/drivers/scsi/dtc.c b/linux/src/drivers/scsi/dtc.c
new file mode 100644
index 00000000..94c3e333
--- /dev/null
+++ b/linux/src/drivers/scsi/dtc.c
@@ -0,0 +1,400 @@
+
+#define AUTOSENSE
+#define PSEUDO_DMA
+#define DONT_USE_INTR
+#define UNSAFE /* Leave interrupts enabled during pseudo-dma I/O */
+#define xNDEBUG (NDEBUG_INTR+NDEBUG_RESELECTION+\
+ NDEBUG_SELECTION+NDEBUG_ARBITRATION)
+#define DMA_WORKS_RIGHT
+
+
+/*
+ * DTC 3180/3280 driver, by
+ * Ray Van Tassle rayvt@comm.mot.com
+ *
+ * taken from ...
+ * Trantor T128/T128F/T228 driver by...
+ *
+ * Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * DISTRIBUTION RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+*/
+
+/*
+ * Options :
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
+ * increase compared to polled I/O.
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers.
+ * You probably want this.
+ *
+ * The card is detected and initialized in one of several ways :
+ * 1. Autoprobe (default) - since the board is memory mapped,
+ * a BIOS signature is scanned for to locate the registers.
+ * An interrupt is triggered to autoprobe for the interrupt
+ * line.
+ *
+ * 2. With command line overrides - dtc=address,irq may be
+ * used on the LILO command line to override the defaults.
+ *
+*/
+
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at */
+/* 1 = blue
+ 2 = green
+ 3 = cyan
+ 4 = red
+ 5 = magenta
+ 6 = yellow
+ 7 = white
+*/
+#if 0
+#define rtrc(i) {inb(0x3da); outb(0x31, 0x3c0); outb((i), 0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+
+
+#include <asm/system.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/blk.h>
+#include <asm/io.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "dtc.h"
+#define AUTOPROBE_IRQ
+#include "NCR5380.h"
+#include "constants.h"
+#include "sd.h"
+#include<linux/stat.h>
+#include<linux/string.h>
+
+struct proc_dir_entry proc_scsi_dtc = {
+ PROC_SCSI_T128, 7, "dtc3x80",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+ };
+
+
+static struct override {
+ __u32 address;
+ int irq;
+} overrides
+#ifdef OVERRIDE
+[] = OVERRIDE;
+#else
+[4] = {{0, IRQ_AUTO}, {0, IRQ_AUTO}, {0, IRQ_AUTO},
+ {0, IRQ_AUTO}};
+#endif
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+static struct base {
+ __u32 address;
+ int noauto;
+} bases[] = {{0xcc000, 0}, {0xc8000, 0},
+{0xdc000, 0}, {0xd8000, 0}};
+
+#define NO_BASES (sizeof (bases) / sizeof (struct base))
+
+static const struct signature {
+ const char *string;
+ int offset;
+} signatures[] = { {"DATA TECHNOLOGY CORPORATION BIOS", 0x25}, };
+
+#define NO_SIGNATURES (sizeof (signatures) / sizeof (struct signature))
+
+/*
+ * Function : dtc_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ *
+*/
+
+void dtc_setup(char *str, int *ints) {
+ static int commandline_current = 0;
+ int i;
+ if (ints[0] != 2)
+ printk("dtc_setup: usage dtc=address,irq\n");
+ else
+ if (commandline_current < NO_OVERRIDES) {
+ overrides[commandline_current].address = ints[1];
+ overrides[commandline_current].irq = ints[2];
+ for (i = 0; i < NO_BASES; ++i)
+ if (bases[i].address == ints[1]) {
+ bases[i].noauto = 1;
+ break;
+ }
+ ++commandline_current;
+ }
+}
+
+/*
+ * Function : int dtc_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : detects and initializes DTC 3180/3280 controllers
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+*/
+
+
+int dtc_detect(Scsi_Host_Template * tpnt) {
+ static int current_override = 0, current_base = 0;
+ struct Scsi_Host *instance;
+ unsigned char *base;
+ int sig, count;
+
+ tpnt->proc_dir = &proc_scsi_dtc;
+ tpnt->proc_info = &dtc_proc_info;
+
+ for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+ base = NULL;
+
+ if (overrides[current_override].address)
+ base = (unsigned char *)overrides[current_override].address;
+ else
+ for (; !base && (current_base < NO_BASES); ++current_base) {
+#if (DTCDEBUG & DTCDEBUG_INIT)
+ printk("scsi : probing address %08x\n", (unsigned int) bases[current_base].address);
+#endif
+ for (sig = 0; sig < NO_SIGNATURES; ++sig)
+ if (!bases[current_base].noauto && !memcmp
+ ((unsigned char *)(bases[current_base].address + signatures[sig].offset),
+ signatures[sig].string, strlen(signatures[sig].string))) {
+ base = (unsigned char *)bases[current_base].address;
+#if (DTCDEBUG & DTCDEBUG_INIT)
+ printk("scsi-dtc : detected board.\n");
+#endif
+ break;
+ }
+ }
+
+#if defined(DTCDEBUG) && (DTCDEBUG & DTCDEBUG_INIT)
+ printk("scsi-dtc : base = %08x\n", (unsigned int) base);
+#endif
+
+ if (!base)
+ break;
+
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+ instance->base = base;
+
+ NCR5380_init(instance, 0);
+
+ NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR); /* Enable int's */
+ if (overrides[current_override].irq != IRQ_AUTO)
+ instance->irq = overrides[current_override].irq;
+ else
+ instance->irq = NCR5380_probe_irq(instance, DTC_IRQS);
+
+#ifndef DONT_USE_INTR
+/* With interrupts enabled, it will sometimes hang when doing heavy
+ * reads. So better not enable them until I finger it out. */
+ if (instance->irq != IRQ_NONE)
+ if (request_irq(instance->irq, dtc_intr, SA_INTERRUPT, "dtc")) {
+ printk("scsi%d : IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = IRQ_NONE;
+ }
+
+ if (instance->irq == IRQ_NONE) {
+ printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+ printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ }
+#else
+ if (instance->irq != IRQ_NONE)
+ printk("scsi%d : interrupts not used. Might as well not jumper it.\n",
+ instance->host_no);
+ instance->irq = IRQ_NONE;
+#endif
+#if defined(DTCDEBUG) && (DTCDEBUG & DTCDEBUG_INIT)
+ printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
+#endif
+
+ printk("scsi%d : at 0x%05X", instance->host_no, (int)instance->base);
+ if (instance->irq == IRQ_NONE)
+ printk (" interrupts disabled");
+ else
+ printk (" irq %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ CAN_QUEUE, CMD_PER_LUN, DTC_PUBLIC_RELEASE);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ ++current_override;
+ ++count;
+ }
+ return count;
+}
+
+/*
+ * Function : int dtc_biosparam(Disk * disk, kdev_t dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for
+ * the specified device / size.
+ *
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ * major / minor, ip[] = {heads, sectors, cylinders}
+ *
+ * Returns : always 0 (success), initializes ip
+ *
+*/
+
+/*
+ * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+*/
+
+int dtc_biosparam(Disk * disk, kdev_t dev, int * ip)
+{
+ int size = disk->capacity;
+
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ return 0;
+}
+
+/****************************************************************
+ * Function : int NCR5380_pread (struct Scsi_Host *instance,
+ * unsigned char *dst, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma read function, reads len bytes to
+ * dst
+ *
+ * Inputs : dst = destination, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+*/
+
+static int dtc_maxi = 0;
+static int dtc_wmaxi = 0;
+
+static inline int NCR5380_pread (struct Scsi_Host *instance,
+ unsigned char *dst, int len)
+ {
+ unsigned char *d = dst;
+ int i; /* For counting time spent in the poll-loop */
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ i = 0;
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(MODE_REG, MR_ENABLE_EOP_INTR | MR_DMA_MODE);
+ if (instance->irq == IRQ_NONE)
+ NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ);
+ else
+ NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ | CSR_INT_BASE);
+ NCR5380_write(DTC_BLK_CNT, len >> 7); /* Block count */
+ rtrc(1);
+ while (len > 0) {
+ rtrc(2);
+ while (NCR5380_read(DTC_CONTROL_REG) & CSR_HOST_BUF_NOT_RDY)
+ ++i;
+ rtrc(3);
+ memcpy(d, (char *)(base + DTC_DATA_BUF), 128);
+ d += 128;
+ len -= 128;
+ rtrc(7); /*** with int's on, it sometimes hangs after here.
+ * Looks like something makes HBNR go away. */
+ }
+ rtrc(4);
+ while ( !(NCR5380_read(DTC_CONTROL_REG) & D_CR_ACCESS))
+ ++i;
+ NCR5380_write(MODE_REG, 0); /* Clear the operating mode */
+ rtrc(0);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ if (i > dtc_maxi)
+ dtc_maxi = i;
+ return(0);
+}
+
+/****************************************************************
+ * Function : int NCR5380_pwrite (struct Scsi_Host *instance,
+ * unsigned char *src, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from
+ * src
+ *
+ * Inputs : src = source, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+*/
+
+static inline int NCR5380_pwrite (struct Scsi_Host *instance,
+ unsigned char *src, int len) {
+ int i;
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(MODE_REG, MR_ENABLE_EOP_INTR | MR_DMA_MODE);
+ /* set direction (write) */
+ if (instance->irq == IRQ_NONE)
+ NCR5380_write(DTC_CONTROL_REG, 0);
+ else
+ NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR);
+ NCR5380_write(DTC_BLK_CNT, len >> 7); /* Block count */
+ for (i = 0; len > 0; ++i) {
+ rtrc(5);
+ /* Poll until the host buffer can accept data. */
+ while (NCR5380_read(DTC_CONTROL_REG) & CSR_HOST_BUF_NOT_RDY)
+ ++i;
+ rtrc(3);
+ memcpy((char *)(base + DTC_DATA_BUF), src, 128);
+ src += 128;
+ len -= 128;
+ }
+ rtrc(4);
+ while ( !(NCR5380_read(DTC_CONTROL_REG) & D_CR_ACCESS))
+ ++i;
+ rtrc(6);
+ /* Wait until the last byte has been sent to the disk */
+ while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT))
+ ++i;
+ rtrc(7);
+ /* Check for parity error here. fixme. */
+ NCR5380_write(MODE_REG, 0); /* Clear the operating mode */
+ rtrc(0);
+ if (i > dtc_wmaxi)
+ dtc_wmaxi = i;
+ return (0);
+}
+
+#include "NCR5380.c"
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = DTC3x80;
+
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/dtc.h b/linux/src/drivers/scsi/dtc.h
new file mode 100644
index 00000000..4c41237b
--- /dev/null
+++ b/linux/src/drivers/scsi/dtc.h
@@ -0,0 +1,169 @@
+/*
+ * DTC controller, taken from T128 driver by...
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * DISTRIBUTION RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ *
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+#ifndef DTC3280_H
+#define DTC3280_H
+
+#define DTC_PUBLIC_RELEASE 1
+
+/*#define DTCDEBUG 0x1*/
+#define DTCDEBUG_INIT 0x1
+#define DTCDEBUG_TRANSFER 0x2
+
+/*
+ * The DTC3180 & 3280 boards are memory mapped.
+ *
+ */
+
+/*
+ */
+/* Offset from DTC_5380_OFFSET */
+#define DTC_CONTROL_REG 0x100 /* rw */
+#define D_CR_ACCESS 0x80 /* ro set=can access 3280 registers */
+#define CSR_DIR_READ 0x40 /* rw direction, 1 = read 0 = write */
+
+#define CSR_RESET 0x80 /* wo Resets 53c400 */
+#define CSR_5380_REG 0x80 /* ro 5380 registers can be accessed */
+#define CSR_TRANS_DIR 0x40 /* rw Data transfer direction */
+#define CSR_SCSI_BUFF_INTR 0x20 /* rw Enable int on transfer ready */
+#define CSR_5380_INTR 0x10 /* rw Enable 5380 interrupts */
+#define CSR_SHARED_INTR 0x08 /* rw Interrupt sharing */
+#define CSR_HOST_BUF_NOT_RDY 0x04 /* ro Host buffer not ready */
+#define CSR_SCSI_BUF_RDY 0x02 /* ro SCSI buffer ready */
+#define CSR_GATED_5380_IRQ 0x01 /* ro Last block xferred */
+#define CSR_INT_BASE (CSR_SCSI_BUFF_INTR | CSR_5380_INTR)
+
+
+#define DTC_BLK_CNT 0x101 /* rw
+ * # of 128-byte blocks to transfer */
+
+
+#define D_CR_ACCESS 0x80 /* ro set=can access 3280 registers */
+
+#define DTC_SWITCH_REG 0x3982 /* ro - DIP switches */
+#define DTC_RESUME_XFER 0x3982 /* wo - resume data xfer
+ * after disconnect/reconnect*/
+
+#define DTC_5380_OFFSET 0x3880 /* 8 registers here, see NCR5380.h */
+
+/*!!!! for dtc, it's a 128 byte buffer at 3900 !!! */
+#define DTC_DATA_BUF 0x3900 /* rw 128 bytes long */
+
+
+#ifndef ASM
+int dtc_abort(Scsi_Cmnd *);
+int dtc_biosparam(Disk *, kdev_t, int*);
+int dtc_detect(Scsi_Host_Template *);
+int dtc_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int dtc_reset(Scsi_Cmnd *, unsigned int reset_flags);
+int dtc_proc_info (char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 32
+#endif
+
+/*
+ * I hadn't thought of this with the earlier drivers - but to prevent
+ * macro definition conflicts, we shouldn't define all of the internal
+ * macros when this is being used solely for the host stub.
+ */
+
+#if defined(HOSTS_C) || defined(MODULE)
+
+#define DTC3x80 {NULL, NULL, NULL, NULL, \
+ "DTC 3180/3280 ", dtc_detect, NULL, \
+ NULL, \
+ NULL, dtc_queue_command, dtc_abort, dtc_reset, NULL, \
+ dtc_biosparam, \
+ /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \
+ /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
+
+#endif
+
+#ifndef HOSTS_C
+
+#define NCR5380_implementation_fields \
+ volatile unsigned char *base
+
+#define NCR5380_local_declare() \
+ volatile unsigned char *base
+
+#define NCR5380_setup(instance) \
+ base = (volatile unsigned char *) (instance)->base
+
+#define DTC_address(reg) (base + DTC_5380_OFFSET + reg)
+
+#define dbNCR5380_read(reg) \
+ (rval=*(DTC_address(reg)), \
+ (((unsigned char) printk("DTC : read register %d at addr %08x is: %02x\n"\
+ , (reg), (int)DTC_address(reg), rval)), rval ) )
+
+#define dbNCR5380_write(reg, value) do { \
+ printk("DTC : write %02x to register %d at address %08x\n", \
+ (value), (reg), (int)DTC_address(reg)); \
+ *(DTC_address(reg)) = (value);} while(0)
+
+
+#if !(DTCDEBUG & DTCDEBUG_TRANSFER)
+#define NCR5380_read(reg) (*(DTC_address(reg)))
+#define NCR5380_write(reg, value) (*(DTC_address(reg)) = (value))
+#else
+#define NCR5380_read(reg) (*(DTC_address(reg)))
+#define xNCR5380_read(reg) \
+ (((unsigned char) printk("DTC : read register %d at address %08x\n"\
+ , (reg), DTC_address(reg))), *(DTC_address(reg)))
+
+#define NCR5380_write(reg, value) do { \
+ printk("DTC : write %02x to register %d at address %08x\n", \
+ (value), (reg), (int)DTC_address(reg)); \
+ *(DTC_address(reg)) = (value); } while(0)
+#endif
+
+#define NCR5380_intr dtc_intr
+#define NCR5380_queue_command dtc_queue_command
+#define NCR5380_abort dtc_abort
+#define NCR5380_reset dtc_reset
+#define NCR5380_proc_info dtc_proc_info
+
+/* 15 12 11 10
+ 1001 1100 0000 0000 */
+
+#define DTC_IRQS 0x9c00
+
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* DTC3280_H */
diff --git a/linux/src/drivers/scsi/eata.c b/linux/src/drivers/scsi/eata.c
new file mode 100644
index 00000000..8d7ba083
--- /dev/null
+++ b/linux/src/drivers/scsi/eata.c
@@ -0,0 +1,2329 @@
+/*
+ * eata.c - Low-level driver for EATA/DMA SCSI host adapters.
+ *
+ * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111
+ * + Added command line option (rs:[y|n]) to reverse the scan order
+ * of PCI boards. The default is rs:y, which reverses the BIOS order
+ * while registering PCI boards. The default value rs:y generates
+ * the same order of all previous revisions of this driver.
+ * Pls. note that "BIOS order" might have been reversed itself
+ * after the 2.1.9x PCI modifications in the linux kernel.
+ * The rs value is ignored when the explicit list of addresses
+ * is used by the "eata=port0,port1,..." command line option.
+ * + Added command line option (et:[y|n]) to force use of extended
+ * translation (255 heads, 63 sectors) as disk geometry.
+ * The default is et:n, which uses the disk geometry returned
+ * by scsicam_bios_param. The default value et:n is compatible with
+ * all previous revisions of this driver.
+ *
+ * 28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104
+ * Increased busy timeout from 10 msec. to 200 msec. while
+ * processing interrupts.
+ *
+ * 16 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102
+ * Improved abort handling during the eh recovery process.
+ *
+ * 13 May 1998 Rev. 4.30 for linux 2.0.33 and 2.1.101
+ * The driver is now fully SMP safe, including the
+ * abort and reset routines.
+ * Added command line options (eh:[y|n]) to choose between
+ * new_eh_code and the old scsi code.
+ * If linux version >= 2.1.101 the default is eh:y, while the eh
+ * option is ignored for previous releases and the old scsi code
+ * is used.
+ *
+ * 18 Apr 1998 Rev. 4.20 for linux 2.0.33 and 2.1.97
+ * Reworked interrupt handler.
+ *
+ * 11 Apr 1998 rev. 4.05 for linux 2.0.33 and 2.1.95
+ * Major reliability improvement: when a batch with overlapping
+ * requests is detected, requests are queued one at a time
+ * eliminating any possible board or drive reordering.
+ *
+ * 10 Apr 1998 rev. 4.04 for linux 2.0.33 and 2.1.95
+ * Improved SMP support (if linux version >= 2.1.95).
+ *
+ * 9 Apr 1998 rev. 4.03 for linux 2.0.33 and 2.1.94
+ * Added support for new PCI code and IO-APIC remapping of irqs.
+ * Performance improvement: when sequential i/o is detected,
+ * always use direct sort instead of reverse sort.
+ *
+ * 4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92
+ * io_port is now unsigned long.
+ *
+ * 17 Mar 1998 rev. 4.01 for linux 2.0.33 and 2.1.88
+ * Use new scsi error handling code (if linux version >= 2.1.88).
+ * Use new interrupt code.
+ *
+ * 12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55
+ * Use of udelay inside the wait loops to avoid timeout
+ * problems with fast cpus.
+ * Removed check about useless calls to the interrupt service
+ * routine (reported on SMP systems only).
+ * At initialization time "sorted/unsorted" is displayed instead
+ * of "linked/unlinked" to reinforce the fact that "linking" is
+ * nothing but "elevator sorting" in the actual implementation.
+ *
+ * 17 May 1997 rev. 3.10 for linux 2.0.30 and 2.1.38
+ * Use of serial_number_at_timeout in abort and reset processing.
+ * Use of the __initfunc and __initdata macro in setup code.
+ * Minor cleanups in the list_statistics code.
+ * Increased controller busy timeout in order to better support
+ * slow SCSI devices.
+ *
+ * 24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
+ * When loading as a module, parameter passing is now supported
+ * both in 2.0 and in 2.1 style.
+ * Fixed data transfer direction for some SCSI opcodes.
+ * Immediate acknowledge to request sense commands.
+ * Linked commands to each disk device are now reordered by elevator
+ * sorting. Rare cases in which reordering of write requests could
+ * cause wrong results are managed.
+ * Fixed spurious timeouts caused by long simple queue tag sequences.
+ * New command line option (tm:[0-3]) to choose the type of tags:
+ * 0 -> mixed (default); 1 -> simple; 2 -> head; 3 -> ordered.
+ *
+ * 18 Jan 1997 rev. 2.60 for linux 2.1.21 and 2.0.28
+ * Added command line options to enable/disable linked commands
+ * (lc:[y|n]), tagged commands (tc:[y|n]) and to set the max queue
+ * depth (mq:xx). Default is "eata=lc:n,tc:n,mq:16".
+ * Improved command linking.
+ * Documented how to setup RAID-0 with DPT SmartRAID boards.
+ *
+ * 8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27
+ * Added linked command support.
+ * Improved detection of PCI boards using ISA base addresses.
+ *
+ * 3 Dec 1996 rev. 2.40 for linux 2.1.14 and 2.0.27
+ * Added support for tagged commands and queue depth adjustment.
+ *
+ * 22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26
+ * When CONFIG_PCI is defined, BIOS32 is used to include in the
+ * list of i/o ports to be probed all the PCI SCSI controllers.
+ * The list of i/o ports to be probed can be overwritten by the
+ * "eata=port0,port1,...." boot command line option.
+ * Scatter/gather lists are now allocated by a number of kmalloc
+ * calls, in order to avoid the previous size limit of 64Kb.
+ *
+ * 16 Nov 1996 rev. 2.20 for linux 2.1.10 and 2.0.25
+ * Added support for EATA 2.0C, PCI, multichannel and wide SCSI.
+ *
+ * 27 Sep 1996 rev. 2.12 for linux 2.1.0
+ * Portability cleanups (virtual/bus addressing, little/big endian
+ * support).
+ *
+ * 09 Jul 1996 rev. 2.11 for linux 2.0.4
+ * Number of internal retries is now limited.
+ *
+ * 16 Apr 1996 rev. 2.10 for linux 1.3.90
+ * New argument "reset_flags" to the reset routine.
+ *
+ * 6 Jul 1995 rev. 2.01 for linux 1.3.7
+ * Update required by the new /proc/scsi support.
+ *
+ * 11 Mar 1995 rev. 2.00 for linux 1.2.0
+ * Fixed a bug which prevented media change detection for removable
+ * disk drives.
+ *
+ * 23 Feb 1995 rev. 1.18 for linux 1.1.94
+ * Added a check for scsi_register returning NULL.
+ *
+ * 11 Feb 1995 rev. 1.17 for linux 1.1.91
+ * Now DEBUG_RESET is disabled by default.
+ * Register a board even if it does not assert DMA protocol support
+ * (DPT SK2011B does not report correctly the dmasup bit).
+ *
+ * 9 Feb 1995 rev. 1.16 for linux 1.1.90
+ * Use host->wish_block instead of host->block.
+ * New list of Data Out SCSI commands.
+ *
+ * 8 Feb 1995 rev. 1.15 for linux 1.1.89
+ * Cleared target_time_out counter while performing a reset.
+ * All external symbols renamed to avoid possible name conflicts.
+ *
+ * 28 Jan 1995 rev. 1.14 for linux 1.1.86
+ * Added module support.
+ * Log and do a retry when a disk drive returns a target status
+ * different from zero on a recovered error.
+ *
+ * 24 Jan 1995 rev. 1.13 for linux 1.1.85
+ * Use optimized board configuration, with a measured performance
+ * increase in the range 10%-20% on i/o throughput.
+ *
+ * 16 Jan 1995 rev. 1.12 for linux 1.1.81
+ * Fix mscp structure comments (no functional change).
+ * Display a message if check_region detects a port address
+ * already in use.
+ *
+ * 17 Dec 1994 rev. 1.11 for linux 1.1.74
+ * Use the scsicam_bios_param routine. This allows an easy
+ * migration path from disk partition tables created using
+ * different SCSI drivers and non optimal disk geometry.
+ *
+ * 15 Dec 1994 rev. 1.10 for linux 1.1.74
+ * Added support for ISA EATA boards (DPT PM2011, DPT PM2021).
+ * The host->block flag is set for all the detected ISA boards.
+ * The detect routine no longer enforces LEVEL triggering
+ * for EISA boards, it just prints a warning message.
+ *
+ * 30 Nov 1994 rev. 1.09 for linux 1.1.68
+ * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only.
+ * Added optional support for using a single board at a time.
+ *
+ * 18 Nov 1994 rev. 1.08 for linux 1.1.64
+ * Forces sg_tablesize = 64 and can_queue = 64 if these
+ * values are not correctly detected (DPT PM2012).
+ *
+ * 14 Nov 1994 rev. 1.07 for linux 1.1.63 Final BETA release.
+ * 04 Aug 1994 rev. 1.00 for linux 1.1.39 First BETA release.
+ *
+ *
+ * This driver is based on the CAM (Common Access Method Committee)
+ * EATA (Enhanced AT Bus Attachment) rev. 2.0A, using DMA protocol.
+ *
+ * Copyright (C) 1994-1998 Dario Ballabio (dario@milano.europe.dg.com)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ */
+
+/*
+ *
+ * Here is a brief description of the DPT SCSI host adapters.
+ * All these boards provide an EATA/DMA compatible programming interface
+ * and are fully supported by this driver in any configuration, including
+ * multiple SCSI channels:
+ *
+ * PM2011B/9X - Entry Level ISA
+ * PM2021A/9X - High Performance ISA
+ * PM2012A Old EISA
+ * PM2012B Old EISA
+ * PM2022A/9X - Entry Level EISA
+ * PM2122A/9X - High Performance EISA
+ * PM2322A/9X - Extra High Performance EISA
+ * PM3021 - SmartRAID Adapter for ISA
+ * PM3222 - SmartRAID Adapter for EISA (PM3222W is 16-bit wide SCSI)
+ * PM3224 - SmartRAID Adapter for PCI (PM3224W is 16-bit wide SCSI)
+ *
+ * The above list is just an indication: as a matter of fact all DPT
+ * boards using the EATA/DMA protocol are supported by this driver,
+ * since they use exactely the same programming interface.
+ *
+ * The DPT PM2001 provides only the EATA/PIO interface and hence is not
+ * supported by this driver.
+ *
+ * This code has been tested with up to 3 Distributed Processing Technology
+ * PM2122A/9X (DPT SCSI BIOS v002.D1, firmware v05E.0) EISA controllers,
+ * in any combination of private and shared IRQ.
+ * PCI support has been tested using up to 2 DPT PM3224W (DPT SCSI BIOS
+ * v003.D0, firmware v07G.0).
+ *
+ * DPT SmartRAID boards support "Hardware Array" - a group of disk drives
+ * which are all members of the same RAID-0, RAID-1 or RAID-5 array implemented
+ * in host adapter hardware. Hardware Arrays are fully compatible with this
+ * driver, since they look to it as a single disk drive.
+ *
+ * WARNING: to create a RAID-0 "Hardware Array" you must select "Other Unix"
+ * as the current OS in the DPTMGR "Initial System Installation" menu.
+ * Otherwise RAID-0 is generated as an "Array Group" (i.e. software RAID-0),
+ * which is not supported by the actual SCSI subsystem.
+ * To get the "Array Group" functionality, the Linux MD driver must be used
+ * instead of the DPT "Array Group" feature.
+ *
+ * Multiple ISA, EISA and PCI boards can be configured in the same system.
+ * It is suggested to put all the EISA boards on the same IRQ level, all
+ * the PCI boards on another IRQ level, while ISA boards cannot share
+ * interrupts.
+ *
+ * If you configure multiple boards on the same IRQ, the interrupt must
+ * be _level_ triggered (not _edge_ triggered).
+ *
+ * This driver detects EATA boards by probes at fixed port addresses,
+ * so no BIOS32 or PCI BIOS support is required.
+ * The suggested way to detect a generic EATA PCI board is to force on it
+ * any unused EISA address, even if there are other controllers on the EISA
+ * bus, or even if you system has no EISA bus at all.
+ * Do not force any ISA address on EATA PCI boards.
+ *
+ * If PCI bios support is configured into the kernel, BIOS32 is used to
+ * include in the list of i/o ports to be probed all the PCI SCSI controllers.
+ *
+ * Due to a DPT BIOS "feature", it might not be possible to force an EISA
+ * address on more then a single DPT PCI board, so in this case you have to
+ * let the PCI BIOS assign the addresses.
+ *
+ * The sequence of detection probes is:
+ *
+ * - ISA 0x1F0;
+ * - PCI SCSI controllers (only if BIOS32 is available);
+ * - EISA/PCI 0x1C88 through 0xFC88 (corresponding to EISA slots 1 to 15);
+ * - ISA 0x170, 0x230, 0x330.
+ *
+ * The above list of detection probes can be totally replaced by the
+ * boot command line option: "eata=port0,port1,port2,...", where the
+ * port0, port1... arguments are ISA/EISA/PCI addresses to be probed.
+ * For example using "eata=0x7410,0x7450,0x230", the driver probes
+ * only the two PCI addresses 0x7410 and 0x7450 and the ISA address 0x230,
+ * in this order; "eata=0" totally disables this driver.
+ *
+ * After the optional list of detection probes, other possible command line
+ * options are:
+ *
+ * eh:y use new scsi code (linux 2.2 only);
+ * eh:n use old scsi code;
+ * et:y force use of extended translation (255 heads, 63 sectors);
+ * et:n use disk geometry detected by scsicam_bios_param;
+ * rs:y reverse scan order while detecting PCI boards;
+ * rs:n use BIOS order while detecting PCI boards;
+ * lc:y enables linked commands;
+ * lc:n disables linked commands;
+ * tc:y enables tagged commands;
+ * tc:n disables tagged commands;
+ * tm:0 use head/simple/ordered queue tag sequences;
+ * tm:1 use only simple queue tags;
+ * tm:2 use only head of queue tags;
+ * tm:3 use only ordered queue tags;
+ * mq:xx set the max queue depth to the value xx (2 <= xx <= 32).
+ *
+ * The default value is: "eata=lc:n,tc:n,mq:16,tm:0,et:n,rs:n".
+ * An example using the list of detection probes could be:
+ * "eata=0x7410,0x230,lc:y,tc:n,mq:4,eh:n,et:n".
+ *
+ * When loading as a module, parameters can be specified as well.
+ * The above example would be (use 1 in place of y and 0 in place of n):
+ *
+ * modprobe eata io_port=0x7410,0x230 linked_comm=1 tagged_comm=0 \
+ * max_queue_depth=4 tag_mode=0 use_new_eh_code=0 \
+ * ext_tran=0 rev_scan=1
+ *
+ * ----------------------------------------------------------------------------
+ * In this implementation, linked commands are designed to work with any DISK
+ * or CD-ROM, since this linking has only the intent of clustering (time-wise)
+ * and reordering by elevator sorting commands directed to each device,
+ * without any relation with the actual SCSI protocol between the controller
+ * and the device.
+ * If Q is the queue depth reported at boot time for each device (also named
+ * cmds/lun) and Q > 2, whenever there is already an active command to the
+ * device all other commands to the same device (up to Q-1) are kept waiting
+ * in the elevator sorting queue. When the active command completes, the
+ * commands in this queue are sorted by sector address. The sort is chosen
+ * between increasing or decreasing by minimizing the seek distance between
+ * the sector of the commands just completed and the sector of the first
+ * command in the list to be sorted.
+ * Trivial math assures that the unsorted average seek distance when doing
+ * random seeks over S sectors is S/3.
+ * When (Q-1) requests are uniformly distributed over S sectors, the average
+ * distance between two adjacent requests is S/((Q-1) + 1), so the sorted
+ * average seek distance for (Q-1) random requests over S sectors is S/Q.
+ * The elevator sorting hence divides the seek distance by a factor Q/3.
+ * The above pure geometric remarks are valid in all cases and the
+ * driver effectively reduces the seek distance by the predicted factor
+ * when there are Q concurrent read i/o operations on the device, but this
+ * does not necessarily results in a noticeable performance improvement:
+ * your mileage may vary....
+ *
+ * Note: command reordering inside a batch of queued commands could cause
+ * wrong results only if there is at least one write request and the
+ * intersection (sector-wise) of all requests is not empty.
+ * When the driver detects a batch including overlapping requests
+ * (a really rare event) strict serial (pid) order is enforced.
+ * ----------------------------------------------------------------------------
+ * The extended translation option (et:y) is useful when using large physical
+ * disks/arrays. It could also be useful when switching between Adaptec boards
+ * and DPT boards without reformatting the disk.
+ * When a boot disk is partitioned with extended translation, in order to
+ * be able to boot it with a DPT board is could be necessary to add to
+ * lilo.conf additional commands as in the following example:
+ *
+ * fix-table
+ * disk=/dev/sda bios=0x80 sectors=63 heads=128 cylindres=546
+ *
+ * where the above geometry should be replaced with the one reported at
+ * power up by the DPT controller.
+ * ----------------------------------------------------------------------------
+ *
+ * The boards are named EATA0, EATA1,... according to the detection order.
+ *
+ * In order to support multiple ISA boards in a reliable way,
+ * the driver sets host->wish_block = TRUE for all ISA boards.
+ */
+
+#include <linux/version.h>
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#define MAX_INT_PARAM 10
+
+#if defined(MODULE)
+#include <linux/module.h>
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,26)
+MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i");
+MODULE_PARM(linked_comm, "i");
+MODULE_PARM(tagged_comm, "i");
+MODULE_PARM(link_statistics, "i");
+MODULE_PARM(max_queue_depth, "i");
+MODULE_PARM(tag_mode, "i");
+MODULE_PARM(use_new_eh_code, "i");
+MODULE_PARM(ext_tran, "i");
+MODULE_PARM(rev_scan, "i");
+MODULE_AUTHOR("Dario Ballabio");
+#endif
+
+#endif
+
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <linux/proc_fs.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include "eata.h"
+#include <linux/stat.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,93)
+#include <linux/bios32.h>
+#endif
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,36)
+#include <linux/init.h>
+#else
+#define __initfunc(A) A
+#define __initdata
+#define __init
+#endif
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
+#include <asm/spinlock.h>
+#define IRQ_FLAGS
+#define IRQ_LOCK
+#define IRQ_LOCK_SAVE
+#define IRQ_UNLOCK
+#define IRQ_UNLOCK_RESTORE
+#define SPIN_FLAGS unsigned long spin_flags;
+#define SPIN_LOCK spin_lock_irq(&io_request_lock);
+#define SPIN_LOCK_SAVE spin_lock_irqsave(&io_request_lock, spin_flags);
+#define SPIN_UNLOCK spin_unlock_irq(&io_request_lock);
+#define SPIN_UNLOCK_RESTORE \
+ spin_unlock_irqrestore(&io_request_lock, spin_flags);
+static int use_new_eh_code = TRUE;
+#else
+#define IRQ_FLAGS unsigned long irq_flags;
+#define IRQ_LOCK cli();
+#define IRQ_LOCK_SAVE do {save_flags(irq_flags); cli();} while (0);
+#define IRQ_UNLOCK sti();
+#define IRQ_UNLOCK_RESTORE do {restore_flags(irq_flags);} while (0);
+#define SPIN_FLAGS
+#define SPIN_LOCK
+#define SPIN_LOCK_SAVE
+#define SPIN_UNLOCK
+#define SPIN_UNLOCK_RESTORE
+static int use_new_eh_code = FALSE;
+#endif
+
+struct proc_dir_entry proc_scsi_eata2x = {
+ PROC_SCSI_EATA2X, 6, "eata2x",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* Subversion values */
+#define ISA 0
+#define ESA 1
+
+#undef FORCE_CONFIG
+
+#undef DEBUG_LINKED_COMMANDS
+#undef DEBUG_DETECT
+#undef DEBUG_PCI_DETECT
+#undef DEBUG_INTERRUPT
+#undef DEBUG_RESET
+#undef DEBUG_GENERATE_ERRORS
+#undef DEBUG_GENERATE_ABORTS
+#undef DEBUG_GEOMETRY
+
+#define MAX_ISA 4
+#define MAX_VESA 0
+#define MAX_EISA 15
+#define MAX_PCI 16
+#define MAX_BOARDS (MAX_ISA + MAX_VESA + MAX_EISA + MAX_PCI)
+#define MAX_CHANNEL 4
+#define MAX_LUN 32
+#define MAX_TARGET 32
+#define MAX_MAILBOXES 64
+#define MAX_SGLIST 64
+#define MAX_LARGE_SGLIST 122
+#define MAX_INTERNAL_RETRIES 64
+#define MAX_CMD_PER_LUN 2
+#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
+
+#define SKIP ULONG_MAX
+#define FALSE 0
+#define TRUE 1
+#define FREE 0
+#define IN_USE 1
+#define LOCKED 2
+#define IN_RESET 3
+#define IGNORE 4
+#define READY 5
+#define ABORTING 6
+#define NO_DMA 0xff
+#define MAXLOOP 10000
+#define TAG_MIXED 0
+#define TAG_SIMPLE 1
+#define TAG_HEAD 2
+#define TAG_ORDERED 3
+
+#define REG_CMD 7
+#define REG_STATUS 7
+#define REG_AUX_STATUS 8
+#define REG_DATA 0
+#define REG_DATA2 1
+#define REG_SEE 6
+#define REG_LOW 2
+#define REG_LM 3
+#define REG_MID 4
+#define REG_MSB 5
+#define REGION_SIZE 9
+#define MAX_ISA_ADDR 0x03ff
+#define MIN_EISA_ADDR 0x1c88
+#define MAX_EISA_ADDR 0xfc88
+#define BSY_ASSERTED 0x80
+#define DRQ_ASSERTED 0x08
+#define ABSY_ASSERTED 0x01
+#define IRQ_ASSERTED 0x02
+#define READ_CONFIG_PIO 0xf0
+#define SET_CONFIG_PIO 0xf1
+#define SEND_CP_PIO 0xf2
+#define RECEIVE_SP_PIO 0xf3
+#define TRUNCATE_XFR_PIO 0xf4
+#define RESET_PIO 0xf9
+#define READ_CONFIG_DMA 0xfd
+#define SET_CONFIG_DMA 0xfe
+#define SEND_CP_DMA 0xff
+#define ASOK 0x00
+#define ASST 0x01
+
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+#define YESNO(a) ((a) ? 'y' : 'n')
+#define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM)
+
+/* "EATA", in Big Endian format */
+#define EATA_SIGNATURE 0x41544145
+
+/* Number of valid bytes in the board config structure for EATA 2.0x */
+#define EATA_2_0A_SIZE 28
+#define EATA_2_0B_SIZE 30
+#define EATA_2_0C_SIZE 34
+
+/* Board info structure */
+struct eata_info {
+ ulong data_len; /* Number of valid bytes after this field */
+ ulong sign; /* ASCII "EATA" signature */
+ unchar :4, /* unused low nibble */
+ version:4; /* EATA version, should be 0x1 */
+ unchar ocsena:1, /* Overlap Command Support Enabled */
+ tarsup:1, /* Target Mode Supported */
+ trnxfr:1, /* Truncate Transfer Cmd NOT Necessary */
+ morsup:1, /* More Supported */
+ dmasup:1, /* DMA Supported */
+ drqvld:1, /* DRQ Index (DRQX) is valid */
+ ata:1, /* This is an ATA device */
+ haaval:1; /* Host Adapter Address Valid */
+ ushort cp_pad_len; /* Number of pad bytes after cp_len */
+ unchar host_addr[4]; /* Host Adapter SCSI ID for channels 3, 2, 1, 0 */
+ ulong cp_len; /* Number of valid bytes in cp */
+ ulong sp_len; /* Number of valid bytes in sp */
+ ushort queue_size; /* Max number of cp that can be queued */
+ ushort unused;
+ ushort scatt_size; /* Max number of entries in scatter/gather table */
+ unchar irq:4, /* Interrupt Request assigned to this controller */
+ irq_tr:1, /* 0 for edge triggered, 1 for level triggered */
+ second:1, /* 1 if this is a secondary (not primary) controller */
+ drqx:2; /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
+ unchar sync; /* 1 if scsi target id 7...0 is running sync scsi */
+
+ /* Structure extension defined in EATA 2.0B */
+ unchar isaena:1, /* ISA i/o addressing is disabled/enabled */
+ forcaddr:1, /* Port address has been forced */
+ large_sg:1, /* 1 if large SG lists are supported */
+ res1:1,
+ :4;
+ unchar max_id:5, /* Max SCSI target ID number */
+ max_chan:3; /* Max SCSI channel number on this board */
+
+ /* Structure extension defined in EATA 2.0C */
+ unchar max_lun; /* Max SCSI LUN number */
+ unchar :4,
+ m1:1, /* This is a PCI with an M1 chip installed */
+ idquest:1, /* RAIDNUM returned is questionable */
+ pci:1, /* This board is PCI */
+ eisa:1; /* This board is EISA */
+ unchar raidnum; /* Uniquely identifies this HBA in a system */
+ unchar notused;
+
+ ushort ipad[247];
+ };
+
+/* Board config structure */
+struct eata_config {
+ ushort len; /* Number of bytes following this field */
+ unchar edis:1, /* Disable EATA interface after config command */
+ ocena:1, /* Overlapped Commands Enabled */
+ mdpena:1, /* Transfer all Modified Data Pointer Messages */
+ tarena:1, /* Target Mode Enabled for this controller */
+ :4;
+ unchar cpad[511];
+ };
+
+/* Returned status packet structure */
+struct mssp {
+ unchar adapter_status:7, /* State related to current command */
+ eoc:1; /* End Of Command (1 = command completed) */
+ unchar target_status; /* SCSI status received after data transfer */
+ unchar unused[2];
+ ulong inv_res_len; /* Number of bytes not transferred */
+ struct mscp *cpp; /* Address set in cp */
+ char mess[12];
+ };
+
+struct sg_list {
+ unsigned int address; /* Segment Address */
+ unsigned int num_bytes; /* Segment Length */
+ };
+
+/* MailBox SCSI Command Packet */
+struct mscp {
+ unchar sreset:1, /* SCSI Bus Reset Signal should be asserted */
+ init:1, /* Re-initialize controller and self test */
+ reqsen:1, /* Transfer Request Sense Data to addr using DMA */
+ sg:1, /* Use Scatter/Gather */
+ :1,
+ interp:1, /* The controller interprets cp, not the target */
+ dout:1, /* Direction of Transfer is Out (Host to Target) */
+ din:1; /* Direction of Transfer is In (Target to Host) */
+ unchar sense_len; /* Request Sense Length */
+ unchar unused[3];
+ unchar fwnest:1, /* Send command to a component of an Array Group */
+ :7;
+ unchar phsunit:1, /* Send to Target Physical Unit (bypass RAID) */
+ iat:1, /* Inhibit Address Translation */
+ hbaci:1, /* Inhibit HBA Caching for this command */
+ :5;
+ unchar target:5, /* SCSI target ID */
+ channel:3; /* SCSI channel number */
+ unchar lun:5, /* SCSI logical unit number */
+ luntar:1, /* This cp is for Target (not LUN) */
+ dispri:1, /* Disconnect Privilege granted */
+ one:1; /* 1 */
+ unchar mess[3]; /* Massage to/from Target */
+ unchar cdb[12]; /* Command Descriptor Block */
+ ulong data_len; /* If sg=0 Data Length, if sg=1 sglist length */
+ struct mscp *cpp; /* Address to be returned in sp */
+ ulong data_address; /* If sg=0 Data Address, if sg=1 sglist address */
+ ulong sp_addr; /* Address where sp is DMA'ed when cp completes */
+ ulong sense_addr; /* Address where Sense Data is DMA'ed on error */
+ Scsi_Cmnd *SCpnt;
+ unsigned int index; /* cp index */
+ struct sg_list *sglist;
+ };
+
+struct hostdata {
+ struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */
+ unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
+ unsigned int last_cp_used; /* Index of last mailbox used */
+ unsigned int iocount; /* Total i/o done for this board */
+ int board_number; /* Number of this board */
+ char board_name[16]; /* Name of this board */
+ char board_id[256]; /* data from INQUIRY on this board */
+ int in_reset; /* True if board is doing a reset */
+ int target_to[MAX_TARGET][MAX_CHANNEL]; /* N. of timeout errors on target */
+ int target_redo[MAX_TARGET][MAX_CHANNEL]; /* If TRUE redo i/o on target */
+ unsigned int retries; /* Number of internal retries */
+ unsigned long last_retried_pid; /* Pid of last retried command */
+ unsigned char subversion; /* Bus type, either ISA or EISA/PCI */
+ unsigned char protocol_rev; /* EATA 2.0 rev., 'A' or 'B' or 'C' */
+ struct mssp sp[2]; /* Returned status for this board */
+ };
+
+static struct Scsi_Host *sh[MAX_BOARDS + 1];
+static const char *driver_name = "EATA";
+static char sha[MAX_BOARDS];
+
+/* Initialize num_boards so that ihdlr can work while detect is in progress */
+static unsigned int num_boards = MAX_BOARDS;
+
+static unsigned long io_port[] __initdata = {
+
+ /* Space for MAX_INT_PARAM ports usable while loading as a module */
+ SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
+ SKIP, SKIP,
+
+ /* First ISA */
+ 0x1f0,
+
+ /* Space for MAX_PCI ports possibly reported by PCI_BIOS */
+ SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
+ SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
+
+ /* MAX_EISA ports */
+ 0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
+ 0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88,
+
+ /* Other (MAX_ISA - 1) ports */
+ 0x170, 0x230, 0x330,
+
+ /* End of list */
+ 0x0
+ };
+
+#define HD(board) ((struct hostdata *) &sh[board]->hostdata)
+#define BN(board) (HD(board)->board_name)
+
+#define H2DEV(x) htonl(x)
+#define DEV2H(x) H2DEV(x)
+#define V2DEV(addr) ((addr) ? H2DEV(virt_to_bus((void *)addr)) : 0)
+#define DEV2V(addr) ((addr) ? DEV2H(bus_to_virt((unsigned long)addr)) : 0)
+
+static void do_interrupt_handler(int, void *, struct pt_regs *);
+static void flush_dev(Scsi_Device *, unsigned long, unsigned int, unsigned int);
+static int do_trace = FALSE;
+static int setup_done = FALSE;
+static int link_statistics = 0;
+static int tag_mode = TAG_MIXED;
+static int ext_tran = FALSE;
+static int rev_scan = TRUE;
+
+#if defined(CONFIG_SCSI_EATA_TAGGED_QUEUE)
+static int tagged_comm = TRUE;
+#else
+static int tagged_comm = FALSE;
+#endif
+
+#if defined(CONFIG_SCSI_EATA_LINKED_COMMANDS)
+static int linked_comm = TRUE;
+#else
+static int linked_comm = FALSE;
+#endif
+
+#if defined(CONFIG_SCSI_EATA_MAX_TAGS)
+static int max_queue_depth = CONFIG_SCSI_EATA_MAX_TAGS;
+#else
+static int max_queue_depth = MAX_CMD_PER_LUN;
+#endif
+
+static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
+ Scsi_Device *dev;
+ int j, ntag = 0, nuntag = 0, tqd, utqd;
+ IRQ_FLAGS
+
+ IRQ_LOCK_SAVE
+ j = ((struct hostdata *) host->hostdata)->board_number;
+
+ for(dev = devlist; dev; dev = dev->next) {
+
+ if (dev->host != host) continue;
+
+ if (TLDEV(dev->type) && (dev->tagged_supported || linked_comm))
+ ntag++;
+ else
+ nuntag++;
+ }
+
+ utqd = MAX_CMD_PER_LUN;
+
+ tqd = (host->can_queue - utqd * nuntag) / (ntag ? ntag : 1);
+
+ if (tqd > max_queue_depth) tqd = max_queue_depth;
+
+ if (tqd < MAX_CMD_PER_LUN) tqd = MAX_CMD_PER_LUN;
+
+ for(dev = devlist; dev; dev = dev->next) {
+ char *tag_suffix = "", *link_suffix = "";
+
+ if (dev->host != host) continue;
+
+ if (TLDEV(dev->type) && (dev->tagged_supported || linked_comm))
+ dev->queue_depth = tqd;
+ else
+ dev->queue_depth = utqd;
+
+ if (TLDEV(dev->type)) {
+ if (linked_comm && dev->queue_depth > 2)
+ link_suffix = ", sorted";
+ else
+ link_suffix = ", unsorted";
+ }
+
+ if (tagged_comm && dev->tagged_supported && TLDEV(dev->type)) {
+ dev->tagged_queue = 1;
+ dev->current_tag = 1;
+ }
+
+ if (dev->tagged_supported && TLDEV(dev->type) && dev->tagged_queue)
+ tag_suffix = ", tagged";
+ else if (dev->tagged_supported && TLDEV(dev->type))
+ tag_suffix = ", untagged";
+
+ printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s%s.\n",
+ BN(j), host->host_no, dev->channel, dev->id, dev->lun,
+ dev->queue_depth, link_suffix, tag_suffix);
+ }
+
+ IRQ_UNLOCK_RESTORE
+ return;
+}
+
+static inline int wait_on_busy(unsigned long iobase, unsigned int loop) {
+
+ while (inb(iobase + REG_AUX_STATUS) & ABSY_ASSERTED) {
+ udelay(1L);
+ if (--loop == 0) return TRUE;
+ }
+
+ return FALSE;
+}
+
+static inline int do_dma(unsigned long iobase, unsigned int addr, unchar cmd) {
+
+ if (wait_on_busy(iobase, (addr ? MAXLOOP * 100 : MAXLOOP))) return TRUE;
+
+ if ((addr = V2DEV(addr))) {
+ outb((char) (addr >> 24), iobase + REG_LOW);
+ outb((char) (addr >> 16), iobase + REG_LM);
+ outb((char) (addr >> 8), iobase + REG_MID);
+ outb((char) addr, iobase + REG_MSB);
+ }
+
+ outb(cmd, iobase + REG_CMD);
+ return FALSE;
+}
+
+static inline int read_pio(unsigned long iobase, ushort *start, ushort *end) {
+ unsigned int loop = MAXLOOP;
+ ushort *p;
+
+ for (p = start; p <= end; p++) {
+
+ while (!(inb(iobase + REG_STATUS) & DRQ_ASSERTED)) {
+ udelay(1L);
+ if (--loop == 0) return TRUE;
+ }
+
+ loop = MAXLOOP;
+ *p = inw(iobase);
+ }
+
+ return FALSE;
+}
+
+__initfunc (static inline int
+ get_pci_irq(unsigned long port_base, unsigned char *apic_irq)) {
+
+#if defined(CONFIG_PCI)
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
+
+ unsigned int addr;
+ struct pci_dev *dev = NULL;
+
+ if (!pci_present()) return FALSE;
+
+ while((dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) {
+
+ if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &addr)) continue;
+
+#if defined(DEBUG_PCI_DETECT)
+ printk("%s: get_pci_irq, bus %d, devfn 0x%x, addr 0x%x, apic_irq %u.\n",
+ driver_name, dev->bus->number, dev->devfn, addr, dev->irq);
+#endif
+
+ if ((addr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
+ continue;
+
+ if ((addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0 == port_base) {
+ *apic_irq = dev->irq;
+ return TRUE;
+ }
+
+ }
+
+#endif /* end new style PCI code */
+
+#endif /* end CONFIG_PCI */
+
+ return FALSE;
+}
+
+__initfunc (static inline int port_detect \
+ (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
+ unsigned char irq, dma_channel, subversion, i;
+ unsigned char protocol_rev, apic_irq;
+ struct eata_info info;
+ char *bus_type, dma_name[16], tag_type;
+
+ /* Allowed DMA channels for ISA (0 indicates reserved) */
+ unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
+
+ char name[16];
+
+ sprintf(name, "%s%d", driver_name, j);
+
+ if(check_region(port_base, REGION_SIZE)) {
+ printk("%s: address 0x%03lx in use, skipping probe.\n", name, port_base);
+ return FALSE;
+ }
+
+ if (do_dma(port_base, 0, READ_CONFIG_PIO)) return FALSE;
+
+ /* Read the info structure */
+ if (read_pio(port_base, (ushort *)&info, (ushort *)&info.ipad[0]))
+ return FALSE;
+
+ /* Check the controller "EATA" signature */
+ if (info.sign != EATA_SIGNATURE) return FALSE;
+
+ if (DEV2H(info.data_len) < EATA_2_0A_SIZE) {
+ printk("%s: config structure size (%ld bytes) too short, detaching.\n",
+ name, DEV2H(info.data_len));
+ return FALSE;
+ }
+ else if (DEV2H(info.data_len) == EATA_2_0A_SIZE)
+ protocol_rev = 'A';
+ else if (DEV2H(info.data_len) == EATA_2_0B_SIZE)
+ protocol_rev = 'B';
+ else
+ protocol_rev = 'C';
+
+ if (!setup_done && j > 0 && j <= MAX_PCI) {
+ bus_type = "PCI";
+ subversion = ESA;
+ }
+ else if (port_base > MAX_EISA_ADDR || (protocol_rev == 'C' && info.pci)) {
+ bus_type = "PCI";
+ subversion = ESA;
+ }
+ else if (port_base >= MIN_EISA_ADDR || (protocol_rev == 'C' && info.eisa)) {
+ bus_type = "EISA";
+ subversion = ESA;
+ }
+ else if (protocol_rev == 'C' && !info.eisa && !info.pci) {
+ bus_type = "ISA";
+ subversion = ISA;
+ }
+ else if (port_base > MAX_ISA_ADDR) {
+ bus_type = "PCI";
+ subversion = ESA;
+ }
+ else {
+ bus_type = "ISA";
+ subversion = ISA;
+ }
+
+ if (!info.haaval || info.ata) {
+ printk("%s: address 0x%03lx, unusable %s board (%d%d), detaching.\n",
+ name, port_base, bus_type, info.haaval, info.ata);
+ return FALSE;
+ }
+
+ if (info.drqvld) {
+
+ if (subversion == ESA)
+ printk("%s: warning, weird %s board using DMA.\n", name, bus_type);
+
+ subversion = ISA;
+ dma_channel = dma_channel_table[3 - info.drqx];
+ }
+ else {
+
+ if (subversion == ISA)
+ printk("%s: warning, weird %s board not using DMA.\n", name, bus_type);
+
+ subversion = ESA;
+ dma_channel = NO_DMA;
+ }
+
+ if (!info.dmasup)
+ printk("%s: warning, DMA protocol support not asserted.\n", name);
+
+ irq = info.irq;
+
+ if (subversion == ESA && !info.irq_tr)
+ printk("%s: warning, LEVEL triggering is suggested for IRQ %u.\n",
+ name, irq);
+
+ if (get_pci_irq(port_base, &apic_irq) && (irq != apic_irq)) {
+ printk("%s: IRQ %u mapped to IO-APIC IRQ %u.\n", name, irq, apic_irq);
+ irq = apic_irq;
+ }
+
+ /* Board detected, allocate its IRQ */
+ if (request_irq(irq, do_interrupt_handler,
+ SA_INTERRUPT | ((subversion == ESA) ? SA_SHIRQ : 0),
+ driver_name, (void *) &sha[j])) {
+ printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
+ return FALSE;
+ }
+
+ if (subversion == ISA && request_dma(dma_channel, driver_name)) {
+ printk("%s: unable to allocate DMA channel %u, detaching.\n",
+ name, dma_channel);
+ free_irq(irq, &sha[j]);
+ return FALSE;
+ }
+
+#if defined(FORCE_CONFIG)
+ {
+ struct eata_config config;
+
+ /* Set board configuration */
+ memset((char *)&config, 0, sizeof(struct eata_config));
+ config.len = (ushort) htons((ushort)510);
+ config.ocena = TRUE;
+
+ if (do_dma(port_base, (unsigned int)&config, SET_CONFIG_DMA)) {
+ printk("%s: busy timeout sending configuration, detaching.\n", name);
+ return FALSE;
+ }
+ }
+#endif
+
+ sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
+
+ if (sh[j] == NULL) {
+ printk("%s: unable to register host, detaching.\n", name);
+
+ free_irq(irq, &sha[j]);
+
+ if (subversion == ISA) free_dma(dma_channel);
+
+ return FALSE;
+ }
+
+ sh[j]->io_port = port_base;
+ sh[j]->unique_id = port_base;
+ sh[j]->n_io_port = REGION_SIZE;
+ sh[j]->dma_channel = dma_channel;
+ sh[j]->irq = irq;
+ sh[j]->sg_tablesize = (ushort) ntohs(info.scatt_size);
+ sh[j]->this_id = (ushort) info.host_addr[3];
+ sh[j]->can_queue = (ushort) ntohs(info.queue_size);
+ sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
+ sh[j]->select_queue_depths = select_queue_depths;
+
+ /* Register the I/O space that we use */
+ request_region(sh[j]->io_port, sh[j]->n_io_port, driver_name);
+
+ memset(HD(j), 0, sizeof(struct hostdata));
+ HD(j)->subversion = subversion;
+ HD(j)->protocol_rev = protocol_rev;
+ HD(j)->board_number = j;
+
+ if (HD(j)->subversion == ESA)
+ sh[j]->unchecked_isa_dma = FALSE;
+ else {
+ sh[j]->wish_block = TRUE;
+ sh[j]->unchecked_isa_dma = TRUE;
+ disable_dma(dma_channel);
+ clear_dma_ff(dma_channel);
+ set_dma_mode(dma_channel, DMA_MODE_CASCADE);
+ enable_dma(dma_channel);
+ }
+
+ strcpy(BN(j), name);
+
+ /* DPT PM2012 does not allow to detect sg_tablesize correctly */
+ if (sh[j]->sg_tablesize > MAX_SGLIST || sh[j]->sg_tablesize < 2) {
+ printk("%s: detect, wrong n. of SG lists %d, fixed.\n",
+ BN(j), sh[j]->sg_tablesize);
+ sh[j]->sg_tablesize = MAX_SGLIST;
+ }
+
+ /* DPT PM2012 does not allow to detect can_queue correctly */
+ if (sh[j]->can_queue > MAX_MAILBOXES || sh[j]->can_queue < 2) {
+ printk("%s: detect, wrong n. of mbox %d, fixed.\n",
+ BN(j), sh[j]->can_queue);
+ sh[j]->can_queue = MAX_MAILBOXES;
+ }
+
+ if (protocol_rev != 'A') {
+
+ if (info.max_chan > 0 && info.max_chan < MAX_CHANNEL)
+ sh[j]->max_channel = info.max_chan;
+
+ if (info.max_id > 7 && info.max_id < MAX_TARGET)
+ sh[j]->max_id = info.max_id + 1;
+
+ if (info.large_sg && sh[j]->sg_tablesize == MAX_SGLIST)
+ sh[j]->sg_tablesize = MAX_LARGE_SGLIST;
+ }
+
+ if (protocol_rev == 'C') {
+
+ if (info.max_lun > 7 && info.max_lun < MAX_LUN)
+ sh[j]->max_lun = info.max_lun + 1;
+ }
+
+ if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "BMST");
+ else sprintf(dma_name, "DMA %u", dma_channel);
+
+ for (i = 0; i < sh[j]->can_queue; i++)
+ if (! ((&HD(j)->cp[i])->sglist = kmalloc(
+ sh[j]->sg_tablesize * sizeof(struct sg_list),
+ (sh[j]->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC))) {
+ printk("%s: kmalloc SGlist failed, mbox %d, detaching.\n", BN(j), i);
+ eata2x_release(sh[j]);
+ return FALSE;
+ }
+
+ if (max_queue_depth > MAX_TAGGED_CMD_PER_LUN)
+ max_queue_depth = MAX_TAGGED_CMD_PER_LUN;
+
+ if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
+
+ if (tagged_comm) {
+ if (tag_mode == TAG_SIMPLE) tag_type = '1';
+ else if (tag_mode == TAG_HEAD) tag_type = '2';
+ else if (tag_mode == TAG_ORDERED) tag_type = '3';
+ else tag_type = 'y';
+ }
+ else tag_type = 'n';
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
+ sh[j]->hostt->use_new_eh_code = use_new_eh_code;
+#else
+ use_new_eh_code = FALSE;
+#endif
+
+ if (j == 0) {
+ printk("EATA/DMA 2.0x: Copyright (C) 1994-1998 Dario Ballabio.\n");
+ printk("%s config options -> tc:%c, lc:%c, mq:%d, eh:%c, rs:%c, et:%c.\n",
+ driver_name, tag_type, YESNO(linked_comm), max_queue_depth,
+ YESNO(use_new_eh_code), YESNO(rev_scan), YESNO(ext_tran));
+ }
+
+ printk("%s: 2.0%c, %s 0x%03lx, IRQ %u, %s, SG %d, MB %d.\n",
+ BN(j), HD(j)->protocol_rev, bus_type, (unsigned long)sh[j]->io_port,
+ sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue);
+
+ if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
+ printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
+ BN(j), sh[j]->max_id, sh[j]->max_lun);
+
+ for (i = 0; i <= sh[j]->max_channel; i++)
+ printk("%s: SCSI channel %u enabled, host target ID %d.\n",
+ BN(j), i, info.host_addr[3 - i]);
+
+#if defined(DEBUG_DETECT)
+ printk("%s: Vers. 0x%x, ocs %u, tar %u, trnxfr %u, more %u, SYNC 0x%x, "\
+ "sec. %u, infol %ld, cpl %ld spl %ld.\n", name, info.version,
+ info.ocsena, info.tarsup, info.trnxfr, info.morsup, info.sync,
+ info.second, DEV2H(info.data_len), DEV2H(info.cp_len),
+ DEV2H(info.sp_len));
+
+ if (protocol_rev == 'B' || protocol_rev == 'C')
+ printk("%s: isaena %u, forcaddr %u, max_id %u, max_chan %u, "\
+ "large_sg %u, res1 %u.\n", name, info.isaena, info.forcaddr,
+ info.max_id, info.max_chan, info.large_sg, info.res1);
+
+ if (protocol_rev == 'C')
+ printk("%s: max_lun %u, m1 %u, idquest %u, pci %u, eisa %u, "\
+ "raidnum %u.\n", name, info.max_lun, info.m1, info.idquest,
+ info.pci, info.eisa, info.raidnum);
+#endif
+
+ return TRUE;
+}
+
+__initfunc (void eata2x_setup(char *str, int *ints)) {
+ int i, argc = ints[0];
+ char *cur = str, *pc;
+
+ if (argc > 0) {
+
+ if (argc > MAX_INT_PARAM) argc = MAX_INT_PARAM;
+
+ for (i = 0; i < argc; i++) io_port[i] = ints[i + 1];
+
+ io_port[i] = 0;
+ setup_done = TRUE;
+ }
+
+ while (cur && (pc = strchr(cur, ':'))) {
+ int val = 0, c = *++pc;
+
+ if (c == 'n' || c == 'N') val = FALSE;
+ else if (c == 'y' || c == 'Y') val = TRUE;
+ else val = (int) simple_strtoul(pc, NULL, 0);
+
+ if (!strncmp(cur, "lc:", 3)) linked_comm = val;
+ else if (!strncmp(cur, "tc:", 3)) tagged_comm = val;
+ else if (!strncmp(cur, "tm:", 3)) tag_mode = val;
+ else if (!strncmp(cur, "mq:", 3)) max_queue_depth = val;
+ else if (!strncmp(cur, "ls:", 3)) link_statistics = val;
+ else if (!strncmp(cur, "eh:", 3)) use_new_eh_code = val;
+ else if (!strncmp(cur, "et:", 3)) ext_tran = val;
+ else if (!strncmp(cur, "rs:", 3)) rev_scan = val;
+
+ if ((cur = strchr(cur, ','))) ++cur;
+ }
+
+ return;
+}
+
+__initfunc (static void add_pci_ports(void)) {
+
+#if defined(CONFIG_PCI)
+
+ unsigned int addr, k;
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93)
+
+ struct pci_dev *dev = NULL;
+
+ if (!pci_present()) return;
+
+ for (k = 0; k < MAX_PCI; k++) {
+
+ if (!(dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) break;
+
+ if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &addr)) continue;
+
+#if defined(DEBUG_PCI_DETECT)
+ printk("%s: detect, seq. %d, bus %d, devfn 0x%x, addr 0x%x.\n",
+ driver_name, k, dev->bus->number, dev->devfn, addr);
+#endif
+
+ if ((addr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
+ continue;
+
+ /* Order addresses according to rev_scan value */
+ io_port[MAX_INT_PARAM + (rev_scan ? (MAX_PCI - k) : (1 + k))] =
+ (addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0;
+ }
+
+#else /* else old style PCI code */
+
+ unsigned short i = 0;
+ unsigned char bus, devfn;
+
+ if (!pcibios_present()) return;
+
+ for (k = 0; k < MAX_PCI; k++) {
+
+ if (pcibios_find_class(PCI_CLASS_STORAGE_SCSI << 8, i++, &bus, &devfn)
+ != PCIBIOS_SUCCESSFUL) break;
+
+ if (pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &addr)
+ != PCIBIOS_SUCCESSFUL) continue;
+
+#if defined(DEBUG_PCI_DETECT)
+ printk("%s: detect, seq. %d, bus %d, devfn 0x%x, addr 0x%x.\n",
+ driver_name, k, bus, devfn, addr);
+#endif
+
+ if ((addr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
+ continue;
+
+ /* Order addresses according to rev_scan value */
+ io_port[MAX_INT_PARAM + (rev_scan ? (MAX_PCI - k) : (1 + k))] =
+ (addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0;
+ }
+
+#endif /* end old style PCI code */
+
+#endif /* end CONFIG_PCI */
+
+ return;
+}
+
+__initfunc (int eata2x_detect(Scsi_Host_Template *tpnt)) {
+ unsigned int j = 0, k;
+ IRQ_FLAGS
+
+ IRQ_LOCK_SAVE
+ tpnt->proc_dir = &proc_scsi_eata2x;
+
+#if defined(MODULE)
+ /* io_port could have been modified when loading as a module */
+ if(io_port[0] != SKIP) {
+ setup_done = TRUE;
+ io_port[MAX_INT_PARAM] = 0;
+ }
+#endif
+
+ for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL;
+
+ if (!setup_done) add_pci_ports();
+
+ for (k = 0; io_port[k]; k++) {
+
+ if (io_port[k] == SKIP) continue;
+
+ if (j < MAX_BOARDS && port_detect(io_port[k], j, tpnt)) j++;
+ }
+
+ num_boards = j;
+ IRQ_UNLOCK_RESTORE
+ return j;
+}
+
+static inline void build_sg_list(struct mscp *cpp, Scsi_Cmnd *SCpnt) {
+ unsigned int k;
+ struct scatterlist *sgpnt;
+
+ sgpnt = (struct scatterlist *) SCpnt->request_buffer;
+
+ for (k = 0; k < SCpnt->use_sg; k++) {
+ cpp->sglist[k].address = V2DEV(sgpnt[k].address);
+ cpp->sglist[k].num_bytes = H2DEV(sgpnt[k].length);
+ }
+
+ cpp->data_address = V2DEV(cpp->sglist);
+ cpp->data_len = H2DEV((SCpnt->use_sg * sizeof(struct sg_list)));
+}
+
+static inline int do_qcomm(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
+ unsigned int i, j, k;
+ struct mscp *cpp;
+ struct mssp *spp;
+
+ static const unsigned char data_out_cmds[] = {
+ 0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e,
+ 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40,
+ 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b
+ };
+
+ static const unsigned char data_none_cmds[] = {
+ 0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e,
+ 0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47,
+ 0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5
+ };
+
+ /* j is the board number */
+ j = ((struct hostdata *) SCpnt->host->hostdata)->board_number;
+
+ if (SCpnt->host_scribble)
+ panic("%s: qcomm, pid %ld, SCpnt %p already active.\n",
+ BN(j), SCpnt->pid, SCpnt);
+
+ /* i is the mailbox number, look for the first free mailbox
+ starting from last_cp_used */
+ i = HD(j)->last_cp_used + 1;
+
+ for (k = 0; k < sh[j]->can_queue; k++, i++) {
+
+ if (i >= sh[j]->can_queue) i = 0;
+
+ if (HD(j)->cp_stat[i] == FREE) {
+ HD(j)->last_cp_used = i;
+ break;
+ }
+ }
+
+ if (k == sh[j]->can_queue) {
+ printk("%s: qcomm, no free mailbox.\n", BN(j));
+ return 1;
+ }
+
+ /* Set pointer to control packet structure */
+ cpp = &HD(j)->cp[i];
+
+ memset(cpp, 0, sizeof(struct mscp) - sizeof(struct sg_list *));
+
+ /* Set pointer to status packet structure */
+ spp = &HD(j)->sp[0];
+
+ /* The EATA protocol uses Big Endian format */
+ cpp->sp_addr = V2DEV(spp);
+
+ cpp->cpp = cpp;
+ SCpnt->scsi_done = done;
+ cpp->index = i;
+ SCpnt->host_scribble = (unsigned char *) &cpp->index;
+
+ if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%d, pid %ld.\n",
+ BN(j), i, SCpnt->channel, SCpnt->target,
+ SCpnt->lun, SCpnt->pid);
+
+ for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
+ if (SCpnt->cmnd[0] == data_out_cmds[k]) {
+ cpp->dout = TRUE;
+ break;
+ }
+
+ if ((cpp->din = !cpp->dout))
+ for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
+ if (SCpnt->cmnd[0] == data_none_cmds[k]) {
+ cpp->din = FALSE;
+ break;
+ }
+
+ cpp->reqsen = TRUE;
+ cpp->dispri = TRUE;
+#if 0
+ if (SCpnt->device->type == TYPE_TAPE) cpp->hbaci = TRUE;
+#endif
+ cpp->one = TRUE;
+ cpp->channel = SCpnt->channel;
+ cpp->target = SCpnt->target;
+ cpp->lun = SCpnt->lun;
+ cpp->SCpnt = SCpnt;
+ cpp->sense_addr = V2DEV(SCpnt->sense_buffer);
+ cpp->sense_len = sizeof SCpnt->sense_buffer;
+
+ if (SCpnt->device->tagged_queue) {
+
+ if (HD(j)->target_redo[SCpnt->target][SCpnt->channel] ||
+ HD(j)->target_to[SCpnt->target][SCpnt->channel])
+ cpp->mess[0] = ORDERED_QUEUE_TAG;
+ else if (tag_mode == TAG_SIMPLE) cpp->mess[0] = SIMPLE_QUEUE_TAG;
+ else if (tag_mode == TAG_HEAD) cpp->mess[0] = HEAD_OF_QUEUE_TAG;
+ else if (tag_mode == TAG_ORDERED) cpp->mess[0] = ORDERED_QUEUE_TAG;
+ else if (SCpnt->device->current_tag == 0)
+ cpp->mess[0] = ORDERED_QUEUE_TAG;
+ else if (SCpnt->device->current_tag == 1)
+ cpp->mess[0] = HEAD_OF_QUEUE_TAG;
+ else
+ cpp->mess[0] = SIMPLE_QUEUE_TAG;
+
+ cpp->mess[1] = SCpnt->device->current_tag++;
+ }
+
+ if (SCpnt->use_sg) {
+ cpp->sg = TRUE;
+ build_sg_list(cpp, SCpnt);
+ }
+ else {
+ cpp->data_address = V2DEV(SCpnt->request_buffer);
+ cpp->data_len = H2DEV(SCpnt->request_bufflen);
+ }
+
+ memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+
+ if (linked_comm && SCpnt->device->queue_depth > 2
+ && TLDEV(SCpnt->device->type)) {
+ HD(j)->cp_stat[i] = READY;
+ flush_dev(SCpnt->device, SCpnt->request.sector, j, FALSE);
+ return 0;
+ }
+
+ /* Send control packet to the board */
+ if (do_dma(sh[j]->io_port, (unsigned int) cpp, SEND_CP_DMA)) {
+ SCpnt->host_scribble = NULL;
+ printk("%s: qcomm, target %d.%d:%d, pid %ld, adapter busy.\n",
+ BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid);
+ return 1;
+ }
+
+ HD(j)->cp_stat[i] = IN_USE;
+ return 0;
+}
+
+int eata2x_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
+ int rtn;
+ IRQ_FLAGS
+
+ IRQ_LOCK_SAVE
+ rtn = do_qcomm(SCpnt, done);
+ IRQ_UNLOCK_RESTORE
+ return rtn;
+}
+
+static inline int do_old_abort(Scsi_Cmnd *SCarg) {
+ unsigned int i, j;
+
+ j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
+
+ if (SCarg->host_scribble == NULL ||
+ (SCarg->serial_number_at_timeout &&
+ (SCarg->serial_number != SCarg->serial_number_at_timeout))) {
+ printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
+ BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ i = *(unsigned int *)SCarg->host_scribble;
+ printk("%s: abort, mbox %d, target %d.%d:%d, pid %ld.\n",
+ BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+
+ if (i >= sh[j]->can_queue)
+ panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: abort, timeout error.\n", BN(j));
+ return SCSI_ABORT_ERROR;
+ }
+
+ if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: abort, mbox %d is free.\n", BN(j), i);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_USE) {
+ printk("%s: abort, mbox %d is in use.\n", BN(j), i);
+
+ if (SCarg != HD(j)->cp[i].SCpnt)
+ panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
+ BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
+
+ if (inb(sh[j]->io_port + REG_AUX_STATUS) & IRQ_ASSERTED)
+ printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i);
+
+ return SCSI_ABORT_SNOOZE;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_RESET) {
+ printk("%s: abort, mbox %d is in reset.\n", BN(j), i);
+ return SCSI_ABORT_ERROR;
+ }
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ printk("%s: abort, mbox %d is locked.\n", BN(j), i);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
+ SCarg->result = DID_ABORT << 16;
+ SCarg->host_scribble = NULL;
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
+ BN(j), i, SCarg->pid);
+ SCarg->scsi_done(SCarg);
+ return SCSI_ABORT_SUCCESS;
+ }
+
+ panic("%s: abort, mbox %d, invalid cp_stat.\n", BN(j), i);
+}
+
+int eata2x_old_abort(Scsi_Cmnd *SCarg) {
+ int rtn;
+ IRQ_FLAGS
+
+ IRQ_LOCK_SAVE
+ rtn = do_old_abort(SCarg);
+ IRQ_UNLOCK_RESTORE
+ return rtn;
+}
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
+
+static inline int do_abort(Scsi_Cmnd *SCarg) {
+ unsigned int i, j;
+
+ j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
+
+ if (SCarg->host_scribble == NULL) {
+ printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
+ BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+ return SUCCESS;
+ }
+
+ i = *(unsigned int *)SCarg->host_scribble;
+ printk("%s: abort, mbox %d, target %d.%d:%d, pid %ld.\n",
+ BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+
+ if (i >= sh[j]->can_queue)
+ panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: abort, timeout error.\n", BN(j));
+ return FAILED;
+ }
+
+ if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: abort, mbox %d is free.\n", BN(j), i);
+ return SUCCESS;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_USE) {
+ printk("%s: abort, mbox %d is in use.\n", BN(j), i);
+
+ if (SCarg != HD(j)->cp[i].SCpnt)
+ panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
+ BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
+
+ if (inb(sh[j]->io_port + REG_AUX_STATUS) & IRQ_ASSERTED)
+ printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i);
+
+ if (SCarg->eh_state == SCSI_STATE_TIMEOUT) {
+ SCarg->host_scribble = NULL;
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s, abort, mbox %d, eh_state timeout, pid %ld.\n",
+ BN(j), i, SCarg->pid);
+ return SUCCESS;
+ }
+
+ return FAILED;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_RESET) {
+ printk("%s: abort, mbox %d is in reset.\n", BN(j), i);
+ return FAILED;
+ }
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ printk("%s: abort, mbox %d is locked.\n", BN(j), i);
+ return SUCCESS;
+ }
+
+ if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
+ SCarg->result = DID_ABORT << 16;
+ SCarg->host_scribble = NULL;
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
+ BN(j), i, SCarg->pid);
+ SCarg->scsi_done(SCarg);
+ return SUCCESS;
+ }
+
+ panic("%s: abort, mbox %d, invalid cp_stat.\n", BN(j), i);
+}
+
+int eata2x_abort(Scsi_Cmnd *SCarg) {
+
+ return do_abort(SCarg);
+}
+
+#endif /* new_eh_code */
+
+static inline int do_old_reset(Scsi_Cmnd *SCarg) {
+ unsigned int i, j, time, k, c, limit = 0;
+ int arg_done = FALSE;
+ Scsi_Cmnd *SCpnt;
+
+ j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
+ printk("%s: reset, enter, target %d.%d:%d, pid %ld.\n",
+ BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+
+ if (SCarg->host_scribble == NULL)
+ printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->pid);
+
+ if (SCarg->serial_number_at_timeout &&
+ (SCarg->serial_number != SCarg->serial_number_at_timeout)) {
+ printk("%s: reset, pid %ld, reset not running.\n", BN(j), SCarg->pid);
+ return SCSI_RESET_NOT_RUNNING;
+ }
+
+ if (HD(j)->in_reset) {
+ printk("%s: reset, exit, already in reset.\n", BN(j));
+ return SCSI_RESET_ERROR;
+ }
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: reset, exit, timeout error.\n", BN(j));
+ return SCSI_RESET_ERROR;
+ }
+
+ HD(j)->retries = 0;
+
+ for (c = 0; c <= sh[j]->max_channel; c++)
+ for (k = 0; k < sh[j]->max_id; k++) {
+ HD(j)->target_redo[k][c] = TRUE;
+ HD(j)->target_to[k][c] = 0;
+ }
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ if (HD(j)->cp_stat[i] == FREE) continue;
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
+ continue;
+ }
+
+ if (!(SCpnt = HD(j)->cp[i].SCpnt))
+ panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+ if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
+ HD(j)->cp_stat[i] = ABORTING;
+ printk("%s: reset, mbox %d aborting, pid %ld.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else {
+ HD(j)->cp_stat[i] = IN_RESET;
+ printk("%s: reset, mbox %d in reset, pid %ld.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
+
+ if (SCpnt->scsi_done == NULL)
+ panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
+
+ if (SCpnt == SCarg) arg_done = TRUE;
+ }
+
+ if (do_dma(sh[j]->io_port, 0, RESET_PIO)) {
+ printk("%s: reset, cannot reset, timeout error.\n", BN(j));
+ return SCSI_RESET_ERROR;
+ }
+
+ printk("%s: reset, board reset done, enabling interrupts.\n", BN(j));
+
+#if defined(DEBUG_RESET)
+ do_trace = TRUE;
+#endif
+
+ HD(j)->in_reset = TRUE;
+ SPIN_UNLOCK
+ IRQ_UNLOCK
+ time = jiffies;
+ while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L);
+ IRQ_LOCK
+ SPIN_LOCK
+ printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ if (HD(j)->cp_stat[i] == IN_RESET) {
+ SCpnt = HD(j)->cp[i].SCpnt;
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox is still waiting for its interrupt */
+ HD(j)->cp_stat[i] = LOCKED;
+
+ printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else if (HD(j)->cp_stat[i] == ABORTING) {
+ SCpnt = HD(j)->cp[i].SCpnt;
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox was never queued to the adapter */
+ HD(j)->cp_stat[i] = FREE;
+
+ printk("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else
+
+ /* Any other mailbox has already been set free by interrupt */
+ continue;
+
+ SCpnt->scsi_done(SCpnt);
+ IRQ_LOCK
+ }
+
+ HD(j)->in_reset = FALSE;
+ do_trace = FALSE;
+
+ if (arg_done) {
+ printk("%s: reset, exit, success.\n", BN(j));
+ return SCSI_RESET_SUCCESS;
+ }
+ else {
+ printk("%s: reset, exit, wakeup.\n", BN(j));
+ return SCSI_RESET_PUNT;
+ }
+}
+
+int eata2x_old_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
+ int rtn;
+ IRQ_FLAGS
+
+ IRQ_LOCK_SAVE
+ rtn = do_old_reset(SCarg);
+ IRQ_UNLOCK_RESTORE
+ return rtn;
+}
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
+
+static inline int do_reset(Scsi_Cmnd *SCarg) {
+ unsigned int i, j, time, k, c, limit = 0;
+ int arg_done = FALSE;
+ Scsi_Cmnd *SCpnt;
+
+ j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
+ printk("%s: reset, enter, target %d.%d:%d, pid %ld.\n",
+ BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+
+ if (SCarg->host_scribble == NULL)
+ printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->pid);
+
+ if (HD(j)->in_reset) {
+ printk("%s: reset, exit, already in reset.\n", BN(j));
+ return FAILED;
+ }
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: reset, exit, timeout error.\n", BN(j));
+ return FAILED;
+ }
+
+ HD(j)->retries = 0;
+
+ for (c = 0; c <= sh[j]->max_channel; c++)
+ for (k = 0; k < sh[j]->max_id; k++) {
+ HD(j)->target_redo[k][c] = TRUE;
+ HD(j)->target_to[k][c] = 0;
+ }
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ if (HD(j)->cp_stat[i] == FREE) continue;
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
+ continue;
+ }
+
+ if (!(SCpnt = HD(j)->cp[i].SCpnt))
+ panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+ if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
+ HD(j)->cp_stat[i] = ABORTING;
+ printk("%s: reset, mbox %d aborting, pid %ld.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else {
+ HD(j)->cp_stat[i] = IN_RESET;
+ printk("%s: reset, mbox %d in reset, pid %ld.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
+
+ if (SCpnt->scsi_done == NULL)
+ panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
+
+ if (SCpnt == SCarg) arg_done = TRUE;
+ }
+
+ if (do_dma(sh[j]->io_port, 0, RESET_PIO)) {
+ printk("%s: reset, cannot reset, timeout error.\n", BN(j));
+ return FAILED;
+ }
+
+ printk("%s: reset, board reset done, enabling interrupts.\n", BN(j));
+
+#if defined(DEBUG_RESET)
+ do_trace = TRUE;
+#endif
+
+ HD(j)->in_reset = TRUE;
+ SPIN_UNLOCK
+ IRQ_UNLOCK
+ time = jiffies;
+ while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L);
+ IRQ_LOCK
+ SPIN_LOCK
+ printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ if (HD(j)->cp_stat[i] == IN_RESET) {
+ SCpnt = HD(j)->cp[i].SCpnt;
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox is still waiting for its interrupt */
+ HD(j)->cp_stat[i] = LOCKED;
+
+ printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else if (HD(j)->cp_stat[i] == ABORTING) {
+ SCpnt = HD(j)->cp[i].SCpnt;
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox was never queued to the adapter */
+ HD(j)->cp_stat[i] = FREE;
+
+ printk("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else
+
+ /* Any other mailbox has already been set free by interrupt */
+ continue;
+
+ SCpnt->scsi_done(SCpnt);
+ IRQ_LOCK
+ }
+
+ HD(j)->in_reset = FALSE;
+ do_trace = FALSE;
+
+ if (arg_done) printk("%s: reset, exit, pid %ld done.\n", BN(j), SCarg->pid);
+ else printk("%s: reset, exit.\n", BN(j));
+
+ return SUCCESS;
+}
+
+int eata2x_reset(Scsi_Cmnd *SCarg) {
+
+ return do_reset(SCarg);
+}
+
+#endif /* new_eh_code */
+
+int eata2x_biosparam(Disk *disk, kdev_t dev, int *dkinfo) {
+ int size = disk->capacity;
+
+ if (ext_tran || (scsicam_bios_param(disk, dev, dkinfo) < 0)) {
+ dkinfo[0] = 255;
+ dkinfo[1] = 63;
+ dkinfo[2] = size / (dkinfo[0] * dkinfo[1]);
+ }
+
+#if defined (DEBUG_GEOMETRY)
+ printk ("%s: biosparam, head=%d, sec=%d, cyl=%d.\n", driver_name,
+ dkinfo[0], dkinfo[1], dkinfo[2]);
+#endif
+
+ return FALSE;
+}
+
+static void sort(unsigned long sk[], unsigned int da[], unsigned int n,
+ unsigned int rev) {
+ unsigned int i, j, k, y;
+ unsigned long x;
+
+ for (i = 0; i < n - 1; i++) {
+ k = i;
+
+ for (j = k + 1; j < n; j++)
+ if (rev) {
+ if (sk[j] > sk[k]) k = j;
+ }
+ else {
+ if (sk[j] < sk[k]) k = j;
+ }
+
+ if (k != i) {
+ x = sk[k]; sk[k] = sk[i]; sk[i] = x;
+ y = da[k]; da[k] = da[i]; da[i] = y;
+ }
+ }
+
+ return;
+ }
+
+static inline int reorder(unsigned int j, unsigned long cursec,
+ unsigned int ihdlr, unsigned int il[], unsigned int n_ready) {
+ Scsi_Cmnd *SCpnt;
+ struct mscp *cpp;
+ unsigned int k, n;
+ unsigned int rev = FALSE, s = TRUE, r = TRUE;
+ unsigned int input_only = TRUE, overlap = FALSE;
+ unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
+ unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0, iseek = 0;
+ unsigned long ioseek = 0;
+
+ static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
+ static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
+ static unsigned int readysorted = 0, revcount = 0;
+ static unsigned long seeksorted = 0, seeknosort = 0;
+
+ if (link_statistics && !(++flushcount % link_statistics))
+ printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\
+ " av %ldK as %ldK.\n", flushcount, batchcount, inputcount,
+ ovlcount, readycount, readysorted, sortcount, revcount,
+ seeknosort / (readycount + 1),
+ seeksorted / (readycount + 1));
+
+ if (n_ready <= 1) return FALSE;
+
+ for (n = 0; n < n_ready; n++) {
+ k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+ if (!cpp->din) input_only = FALSE;
+
+ if (SCpnt->request.sector < minsec) minsec = SCpnt->request.sector;
+ if (SCpnt->request.sector > maxsec) maxsec = SCpnt->request.sector;
+
+ sl[n] = SCpnt->request.sector;
+ ioseek += SCpnt->request.nr_sectors;
+
+ if (!n) continue;
+
+ if (sl[n] < sl[n - 1]) s = FALSE;
+ if (sl[n] > sl[n - 1]) r = FALSE;
+
+ if (link_statistics) {
+ if (sl[n] > sl[n - 1])
+ seek += sl[n] - sl[n - 1];
+ else
+ seek += sl[n - 1] - sl[n];
+ }
+
+ }
+
+ if (link_statistics) {
+ if (cursec > sl[0]) seek += cursec - sl[0]; else seek += sl[0] - cursec;
+ }
+
+ if (cursec > ((maxsec + minsec) / 2)) rev = TRUE;
+
+ if (ioseek > ((maxsec - minsec) / 2)) rev = FALSE;
+
+ if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev);
+
+ if (!input_only) for (n = 0; n < n_ready; n++) {
+ k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+ ll[n] = SCpnt->request.nr_sectors; pl[n] = SCpnt->pid;
+
+ if (!n) continue;
+
+ if ((sl[n] == sl[n - 1]) || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n]))
+ || (rev && ((sl[n] + ll[n]) > sl[n - 1]))) overlap = TRUE;
+ }
+
+ if (overlap) sort(pl, il, n_ready, FALSE);
+
+ if (link_statistics) {
+ if (cursec > sl[0]) iseek = cursec - sl[0]; else iseek = sl[0] - cursec;
+ batchcount++; readycount += n_ready, seeknosort += seek / 1024;
+ if (input_only) inputcount++;
+ if (overlap) { ovlcount++; seeksorted += iseek / 1024; }
+ else seeksorted += (iseek + maxsec - minsec) / 1024;
+ if (rev && !r) { revcount++; readysorted += n_ready; }
+ if (!rev && !s) { sortcount++; readysorted += n_ready; }
+ }
+
+#if defined(DEBUG_LINKED_COMMANDS)
+ if (link_statistics && (overlap || !(flushcount % link_statistics)))
+ for (n = 0; n < n_ready; n++) {
+ k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+ printk("%s %d.%d:%d pid %ld mb %d fc %d nr %d sec %ld ns %ld"\
+ " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
+ (ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target,
+ SCpnt->lun, SCpnt->pid, k, flushcount, n_ready,
+ SCpnt->request.sector, SCpnt->request.nr_sectors, cursec,
+ YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only),
+ YESNO(overlap), cpp->din);
+ }
+#endif
+ return overlap;
+}
+
+static void flush_dev(Scsi_Device *dev, unsigned long cursec, unsigned int j,
+ unsigned int ihdlr) {
+ Scsi_Cmnd *SCpnt;
+ struct mscp *cpp;
+ unsigned int k, n, n_ready = 0, il[MAX_MAILBOXES];
+
+ for (k = 0; k < sh[j]->can_queue; k++) {
+
+ if (HD(j)->cp_stat[k] != READY && HD(j)->cp_stat[k] != IN_USE) continue;
+
+ cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+ if (SCpnt->device != dev) continue;
+
+ if (HD(j)->cp_stat[k] == IN_USE) return;
+
+ il[n_ready++] = k;
+ }
+
+ if (reorder(j, cursec, ihdlr, il, n_ready)) n_ready = 1;
+
+ for (n = 0; n < n_ready; n++) {
+ k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+ if (do_dma(sh[j]->io_port, (unsigned int) cpp, SEND_CP_DMA)) {
+ printk("%s: %s, target %d.%d:%d, pid %ld, mbox %d, adapter"\
+ " busy, will abort.\n", BN(j), (ihdlr ? "ihdlr" : "qcomm"),
+ SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid, k);
+ HD(j)->cp_stat[k] = ABORTING;
+ continue;
+ }
+
+ HD(j)->cp_stat[k] = IN_USE;
+ }
+
+}
+
+static inline void ihdlr(int irq, unsigned int j) {
+ Scsi_Cmnd *SCpnt;
+ unsigned int i, k, c, status, tstatus, reg;
+ struct mssp *dspp, *spp;
+ struct mscp *cpp;
+
+ if (sh[j]->irq != irq)
+ panic("%s: ihdlr, irq %d, sh[j]->irq %d.\n", BN(j), irq, sh[j]->irq);
+
+ /* Check if this board need to be serviced */
+ if (!(inb(sh[j]->io_port + REG_AUX_STATUS) & IRQ_ASSERTED)) return;
+
+ HD(j)->iocount++;
+
+ if (do_trace) printk("%s: ihdlr, enter, irq %d, count %d.\n", BN(j), irq,
+ HD(j)->iocount);
+
+ /* Check if this board is still busy */
+ if (wait_on_busy(sh[j]->io_port, 20 * MAXLOOP)) {
+ reg = inb(sh[j]->io_port + REG_STATUS);
+ printk("%s: ihdlr, busy timeout error, irq %d, reg 0x%x, count %d.\n",
+ BN(j), irq, reg, HD(j)->iocount);
+ return;
+ }
+
+ dspp = &HD(j)->sp[0];
+ spp = &HD(j)->sp[1];
+
+ /* Make a local copy just before clearing the interrupt indication */
+ memcpy(spp, dspp, sizeof(struct mssp));
+
+ /* Clear the completion flag and cp pointer on the dynamic copy of sp */
+ memset(dspp, 0, sizeof(struct mssp));
+
+ /* Read the status register to clear the interrupt indication */
+ reg = inb(sh[j]->io_port + REG_STATUS);
+
+ /* Reject any sp with supspect data */
+ if (spp->eoc == FALSE)
+ printk("%s: ihdlr, spp->eoc == FALSE, irq %d, reg 0x%x, count %d.\n",
+ BN(j), irq, reg, HD(j)->iocount);
+ if (spp->cpp == NULL)
+ printk("%s: ihdlr, spp->cpp == NULL, irq %d, reg 0x%x, count %d.\n",
+ BN(j), irq, reg, HD(j)->iocount);
+ if (spp->eoc == FALSE || spp->cpp == NULL) return;
+
+ cpp = spp->cpp;
+
+#if defined(DEBUG_GENERATE_ABORTS)
+ if ((HD(j)->iocount > 500) && ((HD(j)->iocount % 500) < 3)) return;
+#endif
+
+ /* Find the mailbox to be serviced on this board */
+ i = cpp - HD(j)->cp;
+
+ if (cpp < HD(j)->cp || cpp >= HD(j)->cp + sh[j]->can_queue
+ || i >= sh[j]->can_queue)
+ panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n", BN(j),
+ cpp, HD(j)->cp);
+
+ if (HD(j)->cp_stat[i] == IGNORE) {
+ HD(j)->cp_stat[i] = FREE;
+ return;
+ }
+ else if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: ihdlr, mbox %d unlocked, count %d.\n", BN(j), i,
+ HD(j)->iocount);
+ return;
+ }
+ else if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: ihdlr, mbox %d is free, count %d.\n", BN(j), i,
+ HD(j)->iocount);
+ return;
+ }
+ else if (HD(j)->cp_stat[i] == IN_RESET)
+ printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
+ else if (HD(j)->cp_stat[i] != IN_USE)
+ panic("%s: ihdlr, mbox %d, invalid cp_stat: %d.\n",
+ BN(j), i, HD(j)->cp_stat[i]);
+
+ HD(j)->cp_stat[i] = FREE;
+ SCpnt = cpp->SCpnt;
+
+ if (SCpnt == NULL) panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n", BN(j), i,
+ SCpnt->pid, SCpnt);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d.\n",
+ BN(j), i, SCpnt->pid, *(unsigned int *)SCpnt->host_scribble);
+
+ if (linked_comm && SCpnt->device->queue_depth > 2
+ && TLDEV(SCpnt->device->type))
+ flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
+
+ tstatus = status_byte(spp->target_status);
+
+#if defined(DEBUG_GENERATE_ERRORS)
+ if ((HD(j)->iocount > 500) && ((HD(j)->iocount % 200) < 2))
+ spp->adapter_status = 0x01;
+#endif
+
+ switch (spp->adapter_status) {
+ case ASOK: /* status OK */
+
+ /* Forces a reset if a disk drive keeps returning BUSY */
+ if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
+ status = DID_ERROR << 16;
+
+ /* If there was a bus reset, redo operation on each target */
+ else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
+ && HD(j)->target_redo[SCpnt->target][SCpnt->channel])
+ status = DID_BUS_BUSY << 16;
+
+ /* Works around a flaw in scsi.c */
+ else if (tstatus == CHECK_CONDITION
+ && SCpnt->device->type == TYPE_DISK
+ && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+ status = DID_BUS_BUSY << 16;
+
+ else
+ status = DID_OK << 16;
+
+ if (tstatus == GOOD)
+ HD(j)->target_redo[SCpnt->target][SCpnt->channel] = FALSE;
+
+ if (spp->target_status && SCpnt->device->type == TYPE_DISK)
+ printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
+ "target_status 0x%x, sense key 0x%x.\n", BN(j),
+ SCpnt->channel, SCpnt->target, SCpnt->lun,
+ SCpnt->pid, spp->target_status,
+ SCpnt->sense_buffer[2]);
+
+ HD(j)->target_to[SCpnt->target][SCpnt->channel] = 0;
+
+ if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
+
+ break;
+ case ASST: /* Selection Time Out */
+ case 0x02: /* Command Time Out */
+
+ if (HD(j)->target_to[SCpnt->target][SCpnt->channel] > 1)
+ status = DID_ERROR << 16;
+ else {
+ status = DID_TIME_OUT << 16;
+ HD(j)->target_to[SCpnt->target][SCpnt->channel]++;
+ }
+
+ break;
+
+ /* Perform a limited number of internal retries */
+ case 0x03: /* SCSI Bus Reset Received */
+ case 0x04: /* Initial Controller Power-up */
+
+ for (c = 0; c <= sh[j]->max_channel; c++)
+ for (k = 0; k < sh[j]->max_id; k++)
+ HD(j)->target_redo[k][c] = TRUE;
+
+ if (SCpnt->device->type != TYPE_TAPE
+ && HD(j)->retries < MAX_INTERNAL_RETRIES) {
+
+#if defined(DID_SOFT_ERROR)
+ status = DID_SOFT_ERROR << 16;
+#else
+ status = DID_BUS_BUSY << 16;
+#endif
+ HD(j)->retries++;
+ HD(j)->last_retried_pid = SCpnt->pid;
+ }
+ else
+ status = DID_ERROR << 16;
+
+ break;
+ case 0x05: /* Unexpected Bus Phase */
+ case 0x06: /* Unexpected Bus Free */
+ case 0x07: /* Bus Parity Error */
+ case 0x08: /* SCSI Hung */
+ case 0x09: /* Unexpected Message Reject */
+ case 0x0a: /* SCSI Bus Reset Stuck */
+ case 0x0b: /* Auto Request-Sense Failed */
+ case 0x0c: /* Controller Ram Parity Error */
+ default:
+ status = DID_ERROR << 16;
+ break;
+ }
+
+ SCpnt->result = status | spp->target_status;
+
+#if defined(DEBUG_INTERRUPT)
+ if (SCpnt->result || do_trace)
+#else
+ if ((spp->adapter_status != ASOK && HD(j)->iocount > 1000) ||
+ (spp->adapter_status != ASOK &&
+ spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
+ do_trace || msg_byte(spp->target_status))
+#endif
+ printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
+ " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
+ BN(j), i, spp->adapter_status, spp->target_status,
+ SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
+ reg, HD(j)->iocount);
+
+ /* Set the command state to inactive */
+ SCpnt->host_scribble = NULL;
+
+ SCpnt->scsi_done(SCpnt);
+
+ if (do_trace) printk("%s: ihdlr, exit, irq %d, count %d.\n", BN(j), irq,
+ HD(j)->iocount);
+
+ return;
+}
+
+static void do_interrupt_handler(int irq, void *shap, struct pt_regs *regs) {
+ unsigned int j;
+ IRQ_FLAGS
+ SPIN_FLAGS
+
+ /* Check if the interrupt must be processed by this handler */
+ if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return;
+
+ SPIN_LOCK_SAVE
+ IRQ_LOCK_SAVE
+ ihdlr(irq, j);
+ IRQ_UNLOCK_RESTORE
+ SPIN_UNLOCK_RESTORE
+}
+
+int eata2x_release(struct Scsi_Host *shpnt) {
+ unsigned int i, j;
+ IRQ_FLAGS
+
+ IRQ_LOCK_SAVE
+
+ for (j = 0; sh[j] != NULL && sh[j] != shpnt; j++);
+
+ if (sh[j] == NULL) panic("%s: release, invalid Scsi_Host pointer.\n",
+ driver_name);
+
+ for (i = 0; i < sh[j]->can_queue; i++)
+ if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist);
+
+ free_irq(sh[j]->irq, &sha[j]);
+
+ if (sh[j]->dma_channel != NO_DMA) free_dma(sh[j]->dma_channel);
+
+ release_region(sh[j]->io_port, sh[j]->n_io_port);
+ scsi_unregister(sh[j]);
+ IRQ_UNLOCK_RESTORE
+ return FALSE;
+}
+
+#if defined(MODULE)
+Scsi_Host_Template driver_template = EATA;
+
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/eata.h b/linux/src/drivers/scsi/eata.h
new file mode 100644
index 00000000..f1641f42
--- /dev/null
+++ b/linux/src/drivers/scsi/eata.h
@@ -0,0 +1,60 @@
+/*
+ * eata.h - used by the low-level driver for EATA/DMA SCSI host adapters.
+ */
+#ifndef _EATA_H
+#define _EATA_H
+
+#include <scsi/scsicam.h>
+#include <linux/version.h>
+
+int eata2x_detect(Scsi_Host_Template *);
+int eata2x_release(struct Scsi_Host *);
+int eata2x_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int eata2x_abort(Scsi_Cmnd *);
+int eata2x_old_abort(Scsi_Cmnd *);
+int eata2x_reset(Scsi_Cmnd *);
+int eata2x_old_reset(Scsi_Cmnd *, unsigned int);
+int eata2x_biosparam(Disk *, kdev_t, int *);
+
+#define EATA_VERSION "4.33.00"
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
+
+#define EATA { \
+ name: "EATA/DMA 2.0x rev. " EATA_VERSION " ", \
+ detect: eata2x_detect, \
+ release: eata2x_release, \
+ queuecommand: eata2x_queuecommand, \
+ abort: eata2x_old_abort, \
+ reset: eata2x_old_reset, \
+ eh_abort_handler: eata2x_abort, \
+ eh_device_reset_handler: NULL, \
+ eh_bus_reset_handler: NULL, \
+ eh_host_reset_handler: eata2x_reset, \
+ bios_param: eata2x_biosparam, \
+ this_id: 7, \
+ unchecked_isa_dma: 1, \
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 1 /* Enable new error code */ \
+ }
+
+#else /* Use old scsi code */
+
+#define EATA { \
+ name: "EATA/DMA 2.0x rev. " EATA_VERSION " ", \
+ detect: eata2x_detect, \
+ release: eata2x_release, \
+ queuecommand: eata2x_queuecommand, \
+ abort: eata2x_old_abort, \
+ reset: eata2x_old_reset, \
+ bios_param: eata2x_biosparam, \
+ this_id: 7, \
+ unchecked_isa_dma: 1, \
+ use_clustering: ENABLE_CLUSTERING \
+ }
+
+#endif
+
+#endif
diff --git a/linux/src/drivers/scsi/eata_dma.c b/linux/src/drivers/scsi/eata_dma.c
new file mode 100644
index 00000000..2b7da3db
--- /dev/null
+++ b/linux/src/drivers/scsi/eata_dma.c
@@ -0,0 +1,1603 @@
+/************************************************************
+ * *
+ * Linux EATA SCSI driver *
+ * *
+ * based on the CAM document CAM/89-004 rev. 2.0c, *
+ * DPT's driver kit, some internal documents and source, *
+ * and several other Linux scsi drivers and kernel docs. *
+ * *
+ * The driver currently: *
+ * -supports all ISA based EATA-DMA boards *
+ * like PM2011, PM2021, PM2041, PM3021 *
+ * -supports all EISA based EATA-DMA boards *
+ * like PM2012B, PM2022, PM2122, PM2322, PM2042, *
+ * PM3122, PM3222, PM3332 *
+ * -supports all PCI based EATA-DMA boards *
+ * like PM2024, PM2124, PM2044, PM2144, PM3224, *
+ * PM3334 *
+ * -supports the Wide, Ultra Wide and Differential *
+ * versions of the boards *
+ * -supports multiple HBAs with & without IRQ sharing *
+ * -supports all SCSI channels on multi channel boards *
+ * -supports ix86 and MIPS, untested on ALPHA *
+ * -needs identical IDs on all channels of a HBA *
+ * -can be loaded as module *
+ * -displays statistical and hardware information *
+ * in /proc/scsi/eata_dma *
+ * -provides rudimentary latency measurement *
+ * possibilities via /proc/scsi/eata_dma/<hostnum> *
+ * *
+ * (c)1993-96 Michael Neuffer *
+ * mike@i-Connect.Net *
+ * neuffer@mail.uni-mainz.de *
+ * *
+ * This program is free software; you can redistribute it *
+ * and/or modify it under the terms of the GNU General *
+ * Public License as published by the Free Software *
+ * Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be *
+ * useful, but WITHOUT ANY WARRANTY; without even the *
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A *
+ * PARTICULAR PURPOSE. See the GNU General Public License *
+ * for more details. *
+ * *
+ * You should have received a copy of the GNU General *
+ * Public License along with this kernel; if not, write to *
+ * the Free Software Foundation, Inc., 675 Mass Ave, *
+ * Cambridge, MA 02139, USA. *
+ * *
+ * I have to thank DPT for their excellent support. I took *
+ * me almost a year and a stopover at their HQ, on my first *
+ * trip to the USA, to get it, but since then they've been *
+ * very helpful and tried to give me all the infos and *
+ * support I need. *
+ * *
+ * Thanks also to Simon Shapiro, Greg Hosler and Mike *
+ * Jagdis who did a lot of testing and found quite a number *
+ * of bugs during the development. *
+ ************************************************************
+ * last change: 96/10/21 OS: Linux 2.0.23 *
+ ************************************************************/
+
+/* Look in eata_dma.h for configuration and revision information */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/pgtable.h>
+#ifdef __mips__
+#include <asm/cachectl.h>
+#endif
+#include <linux/blk.h>
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
+#include "eata_dma.h"
+#include "eata_dma_proc.h"
+
+#include <linux/stat.h>
+#include <linux/config.h> /* for CONFIG_PCI */
+
+struct proc_dir_entry proc_scsi_eata_dma = {
+ PROC_SCSI_EATA, 8, "eata_dma",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+static u32 ISAbases[] =
+{0x1F0, 0x170, 0x330, 0x230};
+static unchar EISAbases[] =
+{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+static uint registered_HBAs = 0;
+static struct Scsi_Host *last_HBA = NULL;
+static struct Scsi_Host *first_HBA = NULL;
+static unchar reg_IRQ[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static unchar reg_IRQL[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static struct eata_sp *status = 0; /* Statuspacket array */
+static void *dma_scratch = 0;
+
+static struct eata_register *fake_int_base;
+static int fake_int_result;
+static int fake_int_happened;
+
+static ulong int_counter = 0;
+static ulong queue_counter = 0;
+
+void eata_scsi_done (Scsi_Cmnd * scmd)
+{
+ scmd->request.rq_status = RQ_SCSI_DONE;
+
+ if (scmd->request.sem != NULL)
+ up(scmd->request.sem);
+
+ return;
+}
+
+void eata_fake_int_handler(s32 irq, void *dev_id, struct pt_regs * regs)
+{
+ fake_int_result = inb((ulong)fake_int_base + HA_RSTATUS);
+ fake_int_happened = TRUE;
+ DBG(DBG_INTR3, printk("eata_fake_int_handler called irq%d base %p"
+ " res %#x\n", irq, fake_int_base, fake_int_result));
+ return;
+}
+
+#include "eata_dma_proc.c"
+
+#ifdef MODULE
+int eata_release(struct Scsi_Host *sh)
+{
+ uint i;
+ if (sh->irq && reg_IRQ[sh->irq] == 1) free_irq(sh->irq, NULL);
+ else reg_IRQ[sh->irq]--;
+
+ scsi_init_free((void *)status, 512);
+ scsi_init_free((void *)dma_scratch - 4, 1024);
+ for (i = 0; i < sh->can_queue; i++){ /* Free all SG arrays */
+ if(SD(sh)->ccb[i].sg_list != NULL)
+ scsi_init_free((void *) SD(sh)->ccb[i].sg_list,
+ sh->sg_tablesize * sizeof(struct eata_sg_list));
+ }
+
+ if (SD(sh)->channel == 0) {
+ if (sh->dma_channel != BUSMASTER) free_dma(sh->dma_channel);
+ if (sh->io_port && sh->n_io_port)
+ release_region(sh->io_port, sh->n_io_port);
+ }
+ return(TRUE);
+}
+#endif
+
+
+inline void eata_latency_in(struct eata_ccb *cp, hostdata *hd)
+{
+ uint time;
+ time = jiffies - cp->timestamp;
+ if(hd->all_lat[1] > time)
+ hd->all_lat[1] = time;
+ if(hd->all_lat[2] < time)
+ hd->all_lat[2] = time;
+ hd->all_lat[3] += time;
+ hd->all_lat[0]++;
+ if((cp->rw_latency) == WRITE) { /* was WRITE */
+ if(hd->writes_lat[cp->sizeindex][1] > time)
+ hd->writes_lat[cp->sizeindex][1] = time;
+ if(hd->writes_lat[cp->sizeindex][2] < time)
+ hd->writes_lat[cp->sizeindex][2] = time;
+ hd->writes_lat[cp->sizeindex][3] += time;
+ hd->writes_lat[cp->sizeindex][0]++;
+ } else if((cp->rw_latency) == READ) {
+ if(hd->reads_lat[cp->sizeindex][1] > time)
+ hd->reads_lat[cp->sizeindex][1] = time;
+ if(hd->reads_lat[cp->sizeindex][2] < time)
+ hd->reads_lat[cp->sizeindex][2] = time;
+ hd->reads_lat[cp->sizeindex][3] += time;
+ hd->reads_lat[cp->sizeindex][0]++;
+ }
+}
+
+inline void eata_latency_out(struct eata_ccb *cp, Scsi_Cmnd *cmd)
+{
+ int x, z;
+ short *sho;
+ long *lon;
+ x = 0; /* just to keep GCC quiet */
+ cp->timestamp = jiffies; /* For latency measurements */
+ switch(cmd->cmnd[0]) {
+ case WRITE_6:
+ x = cmd->cmnd[4]/2;
+ cp->rw_latency = WRITE;
+ break;
+ case READ_6:
+ x = cmd->cmnd[4]/2;
+ cp->rw_latency = READ;
+ break;
+ case WRITE_10:
+ sho = (short *) &cmd->cmnd[7];
+ x = ntohs(*sho)/2;
+ cp->rw_latency = WRITE;
+ break;
+ case READ_10:
+ sho = (short *) &cmd->cmnd[7];
+ x = ntohs(*sho)/2;
+ cp->rw_latency = READ;
+ break;
+ case WRITE_12:
+ lon = (long *) &cmd->cmnd[6];
+ x = ntohl(*lon)/2;
+ cp->rw_latency = WRITE;
+ break;
+ case READ_12:
+ lon = (long *) &cmd->cmnd[6];
+ x = ntohl(*lon)/2;
+ cp->rw_latency = READ;
+ break;
+ default:
+ cp->rw_latency = OTHER;
+ break;
+ }
+ if (cmd->cmnd[0] == WRITE_6 || cmd->cmnd[0] == WRITE_10 ||
+ cmd->cmnd[0] == WRITE_12 || cmd->cmnd[0] == READ_6 ||
+ cmd->cmnd[0] == READ_10 || cmd->cmnd[0] == READ_12) {
+ for(z = 0; (x > (1 << z)) && (z <= 11); z++)
+ /* nothing */;
+ cp->sizeindex = z;
+ }
+}
+
+
+void eata_int_handler(int irq, void *dev_id, struct pt_regs * regs)
+{
+ uint i, result = 0;
+ uint hba_stat, scsi_stat, eata_stat;
+ Scsi_Cmnd *cmd;
+ struct eata_ccb *ccb;
+ struct eata_sp *sp;
+ uint base;
+ uint x;
+ struct Scsi_Host *sh;
+
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) {
+ if (sh->irq != irq)
+ continue;
+
+ while(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) {
+
+ int_counter++;
+
+ sp = &SD(sh)->sp;
+#ifdef __mips__
+ sys_cacheflush(sp, sizeof(struct eata_sp), 2);
+#endif
+ ccb = sp->ccb;
+
+ if(ccb == NULL) {
+ eata_stat = inb((uint)sh->base + HA_RSTATUS);
+ printk("eata_dma: int_handler, Spurious IRQ %d "
+ "received. CCB pointer not set.\n", irq);
+ break;
+ }
+
+ cmd = ccb->cmd;
+ base = (uint) cmd->host->base;
+ hba_stat = sp->hba_stat;
+
+ scsi_stat = (sp->scsi_stat >> 1) & 0x1f;
+
+ if (sp->EOC == FALSE) {
+ eata_stat = inb(base + HA_RSTATUS);
+ printk(KERN_WARNING "eata_dma: int_handler, board: %x cmd %lx "
+ "returned unfinished.\n"
+ "EATA: %x HBA: %x SCSI: %x spadr %lx spadrirq %lx, "
+ "irq%d\n", base, (long)ccb, eata_stat, hba_stat,
+ scsi_stat,(long)&status, (long)&status[irq], irq);
+ cmd->result = DID_ERROR << 16;
+ ccb->status = FREE;
+ cmd->scsi_done(cmd);
+ break;
+ }
+
+ sp->EOC = FALSE; /* Clean out this flag */
+
+ if (ccb->status == LOCKED || ccb->status == RESET) {
+ printk("eata_dma: int_handler, reseted command pid %ld returned"
+ "\n", cmd->pid);
+ DBG(DBG_INTR && DBG_DELAY, DELAY(1));
+ }
+
+ eata_stat = inb(base + HA_RSTATUS);
+ DBG(DBG_INTR, printk("IRQ %d received, base %#.4x, pid %ld, "
+ "target: %x, lun: %x, ea_s: %#.2x, hba_s: "
+ "%#.2x \n", irq, base, cmd->pid, cmd->target,
+ cmd->lun, eata_stat, hba_stat));
+
+ switch (hba_stat) {
+ case HA_NO_ERROR: /* NO Error */
+ if(HD(cmd)->do_latency == TRUE && ccb->timestamp)
+ eata_latency_in(ccb, HD(cmd));
+ result = DID_OK << 16;
+ break;
+ case HA_ERR_SEL_TO: /* Selection Timeout */
+ case HA_ERR_CMD_TO: /* Command Timeout */
+ result = DID_TIME_OUT << 16;
+ break;
+ case HA_BUS_RESET: /* SCSI Bus Reset Received */
+ result = DID_RESET << 16;
+ DBG(DBG_STATUS, printk(KERN_WARNING "scsi%d: BUS RESET "
+ "received on cmd %ld\n",
+ HD(cmd)->HBA_number, cmd->pid));
+ break;
+ case HA_INIT_POWERUP: /* Initial Controller Power-up */
+ if (cmd->device->type != TYPE_TAPE)
+ result = DID_BUS_BUSY << 16;
+ else
+ result = DID_ERROR << 16;
+
+ for (i = 0; i < MAXTARGET; i++)
+ DBG(DBG_STATUS, printk(KERN_DEBUG "scsi%d: cmd pid %ld "
+ "returned with INIT_POWERUP\n",
+ HD(cmd)->HBA_number, cmd->pid));
+ break;
+ case HA_CP_ABORT_NA:
+ case HA_CP_ABORTED:
+ result = DID_ABORT << 16;
+ DBG(DBG_STATUS, printk(KERN_WARNING "scsi%d: aborted cmd "
+ "returned\n", HD(cmd)->HBA_number));
+ break;
+ case HA_CP_RESET_NA:
+ case HA_CP_RESET:
+ HD(cmd)->resetlevel[cmd->channel] = 0;
+ result = DID_RESET << 16;
+ DBG(DBG_STATUS, printk(KERN_WARNING "scsi%d: reseted cmd "
+ "pid %ldreturned\n",
+ HD(cmd)->HBA_number, cmd->pid));
+ case HA_SCSI_HUNG: /* SCSI Hung */
+ printk(KERN_ERR "scsi%d: SCSI hung\n", HD(cmd)->HBA_number);
+ result = DID_ERROR << 16;
+ break;
+ case HA_RSENSE_FAIL: /* Auto Request-Sense Failed */
+ DBG(DBG_STATUS, printk(KERN_ERR "scsi%d: Auto Request Sense "
+ "Failed\n", HD(cmd)->HBA_number));
+ result = DID_ERROR << 16;
+ break;
+ case HA_UNX_BUSPHASE: /* Unexpected Bus Phase */
+ case HA_UNX_BUS_FREE: /* Unexpected Bus Free */
+ case HA_BUS_PARITY: /* Bus Parity Error */
+ case HA_UNX_MSGRJCT: /* Unexpected Message Reject */
+ case HA_RESET_STUCK: /* SCSI Bus Reset Stuck */
+ case HA_PARITY_ERR: /* Controller Ram Parity */
+ default:
+ result = DID_ERROR << 16;
+ break;
+ }
+ cmd->result = result | (scsi_stat << 1);
+
+#if DBG_INTR2
+ if (scsi_stat || result || hba_stat || eata_stat != 0x50
+ || cmd->scsi_done == NULL || cmd->device->id == 7)
+ printk("HBA: %d, channel %d, id: %d, lun %d, pid %ld:\n"
+ "eata_stat %#x, hba_stat %#.2x, scsi_stat %#.2x, "
+ "sense_key: %#x, result: %#.8x\n", x,
+ cmd->device->channel, cmd->device->id, cmd->device->lun,
+ cmd->pid, eata_stat, hba_stat, scsi_stat,
+ cmd->sense_buffer[2] & 0xf, cmd->result);
+ DBG(DBG_INTR&&DBG_DELAY,DELAY(1));
+#endif
+
+ ccb->status = FREE; /* now we can release the slot */
+ cmd->scsi_done(cmd);
+ }
+ }
+
+ return;
+}
+
+inline int eata_send_command(u32 addr, u32 base, u8 command)
+{
+ long loop = R_LIMIT;
+
+ while (inb(base + HA_RAUXSTAT) & HA_ABUSY)
+ if (--loop == 0)
+ return(FALSE);
+
+ if(addr != (u32) NULL)
+ addr = virt_to_bus((void *)addr);
+
+ /*
+ * This is overkill.....but the MIPSen seem to need this
+ * and it will be optimized away for i86 and ALPHA machines.
+ */
+ flush_cache_all();
+
+ /* And now the address in nice little byte chunks */
+#ifdef __LITTLE_ENDIAN
+ outb(addr, base + HA_WDMAADDR);
+ outb(addr >> 8, base + HA_WDMAADDR + 1);
+ outb(addr >> 16, base + HA_WDMAADDR + 2);
+ outb(addr >> 24, base + HA_WDMAADDR + 3);
+#else
+ outb(addr >> 24, base + HA_WDMAADDR);
+ outb(addr >> 16, base + HA_WDMAADDR + 1);
+ outb(addr >> 8, base + HA_WDMAADDR + 2);
+ outb(addr, base + HA_WDMAADDR + 3);
+#endif
+ outb(command, base + HA_WCOMMAND);
+ return(TRUE);
+}
+
+inline int eata_send_immediate(u32 base, u32 addr, u8 ifc, u8 code, u8 code2)
+{
+ if(addr != (u32) NULL)
+ addr = virt_to_bus((void *)addr);
+
+ /*
+ * This is overkill.....but the MIPSen seem to need this
+ * and it will be optimized away for i86 and ALPHA machines.
+ */
+ flush_cache_all();
+
+ outb(0x0, base + HA_WDMAADDR - 1);
+ if(addr){
+#ifdef __LITTLE_ENDIAN
+ outb(addr, base + HA_WDMAADDR);
+ outb(addr >> 8, base + HA_WDMAADDR + 1);
+ outb(addr >> 16, base + HA_WDMAADDR + 2);
+ outb(addr >> 24, base + HA_WDMAADDR + 3);
+#else
+ outb(addr >> 24, base + HA_WDMAADDR);
+ outb(addr >> 16, base + HA_WDMAADDR + 1);
+ outb(addr >> 8, base + HA_WDMAADDR + 2);
+ outb(addr, base + HA_WDMAADDR + 3);
+#endif
+ } else {
+ outb(0x0, base + HA_WDMAADDR);
+ outb(0x0, base + HA_WDMAADDR + 1);
+ outb(code2, base + HA_WCODE2);
+ outb(code, base + HA_WCODE);
+ }
+
+ outb(ifc, base + HA_WIFC);
+ outb(EATA_CMD_IMMEDIATE, base + HA_WCOMMAND);
+ return(TRUE);
+}
+
+int eata_queue(Scsi_Cmnd * cmd, void (* done) (Scsi_Cmnd *))
+{
+ unsigned int i, x, y;
+ ulong flags;
+ hostdata *hd;
+ struct Scsi_Host *sh;
+ struct eata_ccb *ccb;
+ struct scatterlist *sl;
+
+
+ save_flags(flags);
+ cli();
+
+#if 0
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) {
+ if(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) {
+ printk("eata_dma: scsi%d interrupt pending in eata_queue.\n"
+ " Calling interrupt handler.\n", sh->host_no);
+ eata_int_handler(sh->irq, 0, 0);
+ }
+ }
+#endif
+
+ queue_counter++;
+
+ hd = HD(cmd);
+ sh = cmd->host;
+
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->sense_buffer[0] != 0) {
+ DBG(DBG_REQSENSE, printk(KERN_DEBUG "Tried to REQUEST SENSE\n"));
+ cmd->result = DID_OK << 16;
+ done(cmd);
+
+ return(0);
+ }
+
+ /* check for free slot */
+ for (y = hd->last_ccb + 1, x = 0; x < sh->can_queue; x++, y++) {
+ if (y >= sh->can_queue)
+ y = 0;
+ if (hd->ccb[y].status == FREE)
+ break;
+ }
+
+ hd->last_ccb = y;
+
+ if (x >= sh->can_queue) {
+ cmd->result = DID_BUS_BUSY << 16;
+ DBG(DBG_QUEUE && DBG_ABNORM,
+ printk(KERN_CRIT "eata_queue pid %ld, HBA QUEUE FULL..., "
+ "returning DID_BUS_BUSY\n", cmd->pid));
+ done(cmd);
+ restore_flags(flags);
+ return(0);
+ }
+ ccb = &hd->ccb[y];
+
+ memset(ccb, 0, sizeof(struct eata_ccb) - sizeof(struct eata_sg_list *));
+
+ ccb->status = USED; /* claim free slot */
+
+ restore_flags(flags);
+
+ DBG(DBG_QUEUE, printk("eata_queue pid %ld, target: %x, lun: %x, y %d\n",
+ cmd->pid, cmd->target, cmd->lun, y));
+ DBG(DBG_QUEUE && DBG_DELAY, DELAY(1));
+
+ if(hd->do_latency == TRUE)
+ eata_latency_out(ccb, cmd);
+
+ cmd->scsi_done = (void *)done;
+
+ switch (cmd->cmnd[0]) {
+ case CHANGE_DEFINITION: case COMPARE: case COPY:
+ case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
+ case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case WRITE_6: case WRITE_10: case WRITE_VERIFY:
+ case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME:
+ case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12:
+ case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW:
+ case MEDIUM_SCAN: case SEND_VOLUME_TAG:
+ case 0xea: /* alternate number for WRITE LONG */
+ ccb->DataOut = TRUE; /* Output mode */
+ break;
+ case TEST_UNIT_READY:
+ default:
+ ccb->DataIn = TRUE; /* Input mode */
+ }
+
+ /* FIXME: This will have to be changed once the midlevel driver
+ * allows different HBA IDs on every channel.
+ */
+ if (cmd->target == sh->this_id)
+ ccb->Interpret = TRUE; /* Interpret command */
+
+ if (cmd->use_sg) {
+ ccb->scatter = TRUE; /* SG mode */
+ if (ccb->sg_list == NULL) {
+ ccb->sg_list = kmalloc(sh->sg_tablesize * sizeof(struct eata_sg_list),
+ GFP_ATOMIC | GFP_DMA);
+ }
+ if (ccb->sg_list == NULL)
+ panic("eata_dma: Run out of DMA memory for SG lists !\n");
+ ccb->cp_dataDMA = htonl(virt_to_bus(ccb->sg_list));
+
+ ccb->cp_datalen = htonl(cmd->use_sg * sizeof(struct eata_sg_list));
+ sl=(struct scatterlist *)cmd->request_buffer;
+ for(i = 0; i < cmd->use_sg; i++, sl++){
+ ccb->sg_list[i].data = htonl(virt_to_bus(sl->address));
+ ccb->sg_list[i].len = htonl((u32) sl->length);
+ }
+ } else {
+ ccb->scatter = FALSE;
+ ccb->cp_datalen = htonl(cmd->request_bufflen);
+ ccb->cp_dataDMA = htonl(virt_to_bus(cmd->request_buffer));
+ }
+
+ ccb->Auto_Req_Sen = TRUE;
+ ccb->cp_reqDMA = htonl(virt_to_bus(cmd->sense_buffer));
+ ccb->reqlen = sizeof(cmd->sense_buffer);
+
+ ccb->cp_id = cmd->target;
+ ccb->cp_channel = cmd->channel;
+ ccb->cp_lun = cmd->lun;
+ ccb->cp_dispri = TRUE;
+ ccb->cp_identify = TRUE;
+ memcpy(ccb->cp_cdb, cmd->cmnd, cmd->cmd_len);
+
+ ccb->cp_statDMA = htonl(virt_to_bus(&(hd->sp)));
+
+ ccb->cp_viraddr = ccb; /* This will be passed thru, so we don't need to
+ * convert it */
+ ccb->cmd = cmd;
+ cmd->host_scribble = (char *)&hd->ccb[y];
+
+ if(eata_send_command((u32) ccb, (u32) sh->base, EATA_CMD_DMA_SEND_CP) == FALSE) {
+ cmd->result = DID_BUS_BUSY << 16;
+ DBG(DBG_QUEUE && DBG_ABNORM,
+ printk("eata_queue target %d, pid %ld, HBA busy, "
+ "returning DID_BUS_BUSY\n",cmd->target, cmd->pid));
+ ccb->status = FREE;
+ done(cmd);
+ return(0);
+ }
+ DBG(DBG_QUEUE, printk("Queued base %#.4x pid: %ld target: %x lun: %x "
+ "slot %d irq %d\n", (s32)sh->base, cmd->pid,
+ cmd->target, cmd->lun, y, sh->irq));
+ DBG(DBG_QUEUE && DBG_DELAY, DELAY(1));
+
+ return(0);
+}
+
+
+int eata_abort(Scsi_Cmnd * cmd)
+{
+ ulong loop = HZ / 2;
+ ulong flags;
+ int x;
+ struct Scsi_Host *sh;
+
+ save_flags(flags);
+ cli();
+
+ DBG(DBG_ABNORM, printk("eata_abort called pid: %ld target: %x lun: %x"
+ " reason %x\n", cmd->pid, cmd->target, cmd->lun,
+ cmd->abort_reason));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+
+ /* Some interrupt controllers seem to loose interrupts */
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) {
+ if(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) {
+ printk("eata_dma: scsi%d interrupt pending in eata_abort.\n"
+ " Calling interrupt handler.\n", sh->host_no);
+ eata_int_handler(sh->irq, 0, 0);
+ }
+ }
+
+ while (inb((u32)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY) {
+ if (--loop == 0) {
+ printk("eata_dma: abort, timeout error.\n");
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ restore_flags(flags);
+ return (SCSI_ABORT_ERROR);
+ }
+ }
+ if (CD(cmd)->status == RESET) {
+ printk("eata_dma: abort, command reset error.\n");
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ restore_flags(flags);
+ return (SCSI_ABORT_ERROR);
+ }
+ if (CD(cmd)->status == LOCKED) {
+ DBG(DBG_ABNORM, printk("eata_dma: abort, queue slot locked.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ restore_flags(flags);
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ if (CD(cmd)->status == USED) {
+ DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_BUSY\n"));
+ restore_flags(flags);
+ return (SCSI_ABORT_BUSY); /* SNOOZE */
+ }
+ if (CD(cmd)->status == FREE) {
+ DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_NOT_RUNNING\n"));
+ restore_flags(flags);
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ restore_flags(flags);
+ panic("eata_dma: abort: invalid slot status\n");
+}
+
+int eata_reset(Scsi_Cmnd * cmd, unsigned int resetflags)
+{
+ uint x;
+ ulong loop = loops_per_sec / 3;
+ ulong flags;
+ unchar success = FALSE;
+ Scsi_Cmnd *sp;
+ struct Scsi_Host *sh;
+
+ save_flags(flags);
+ cli();
+
+ DBG(DBG_ABNORM, printk("eata_reset called pid:%ld target: %x lun: %x"
+ " reason %x\n", cmd->pid, cmd->target, cmd->lun,
+ cmd->abort_reason));
+
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) {
+ if(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) {
+ printk("eata_dma: scsi%d interrupt pending in eata_reset.\n"
+ " Calling interrupt handler.\n", sh->host_no);
+ eata_int_handler(sh->irq, 0, 0);
+ }
+ }
+
+ if (HD(cmd)->state == RESET) {
+ printk("eata_reset: exit, already in reset.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_RESET_ERROR);
+ }
+
+ while (inb((u32)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY)
+ if (--loop == 0) {
+ printk("eata_reset: exit, timeout error.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_RESET_ERROR);
+ }
+
+ for (x = 0; x < cmd->host->can_queue; x++) {
+ if (HD(cmd)->ccb[x].status == FREE)
+ continue;
+
+ if (HD(cmd)->ccb[x].status == LOCKED) {
+ HD(cmd)->ccb[x].status = FREE;
+ printk("eata_reset: locked slot %d forced free.\n", x);
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ continue;
+ }
+
+
+ sp = HD(cmd)->ccb[x].cmd;
+ HD(cmd)->ccb[x].status = RESET;
+
+ if (sp == NULL)
+ panic("eata_reset: slot %d, sp==NULL.\n", x);
+
+ printk("eata_reset: slot %d in reset, pid %ld.\n", x, sp->pid);
+
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+
+ if (sp == cmd)
+ success = TRUE;
+ }
+
+ /* hard reset the HBA */
+ inb((u32) (cmd->host->base) + HA_RSTATUS); /* This might cause trouble */
+ eata_send_command(0, (u32) cmd->host->base, EATA_CMD_RESET);
+
+ HD(cmd)->state = RESET;
+
+ DBG(DBG_ABNORM, printk("eata_reset: board reset done, enabling "
+ "interrupts.\n"));
+
+ DELAY(2); /* In theorie we should get interrupts and set free all
+ * used queueslots */
+
+ DBG(DBG_ABNORM, printk("eata_reset: interrupts disabled again.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+
+ for (x = 0; x < cmd->host->can_queue; x++) {
+
+ /* Skip slots already set free by interrupt and those that
+ * are still LOCKED from the last reset */
+ if (HD(cmd)->ccb[x].status != RESET)
+ continue;
+
+ sp = HD(cmd)->ccb[x].cmd;
+ sp->result = DID_RESET << 16;
+
+ /* This mailbox is still waiting for its interrupt */
+ HD(cmd)->ccb[x].status = LOCKED;
+
+ printk("eata_reset: slot %d locked, DID_RESET, pid %ld done.\n",
+ x, sp->pid);
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+
+ sp->scsi_done(sp);
+ }
+
+ HD(cmd)->state = FALSE;
+ restore_flags(flags);
+
+ if (success) {
+ DBG(DBG_ABNORM, printk("eata_reset: exit, pending.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_RESET_PENDING);
+ } else {
+ DBG(DBG_ABNORM, printk("eata_reset: exit, wakeup.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_RESET_PUNT);
+ }
+}
+
+/* Here we try to determine the optimum queue depth for
+ * each attached device.
+ *
+ * At the moment the algorithm is rather simple
+ */
+static void eata_select_queue_depths(struct Scsi_Host *host,
+ Scsi_Device *devicelist)
+{
+ Scsi_Device *device;
+ int devcount = 0;
+ int factor = 0;
+
+#if CRIPPLE_QUEUE
+ for(device = devicelist; device != NULL; device = device->next) {
+ if(device->host == host)
+ device->queue_depth = 2;
+ }
+#else
+ /* First we do a sample run go find out what we have */
+ for(device = devicelist; device != NULL; device = device->next) {
+ if (device->host == host) {
+ devcount++;
+ switch(device->type) {
+ case TYPE_DISK:
+ case TYPE_MOD:
+ factor += TYPE_DISK_QUEUE;
+ break;
+ case TYPE_TAPE:
+ factor += TYPE_TAPE_QUEUE;
+ break;
+ case TYPE_WORM:
+ case TYPE_ROM:
+ factor += TYPE_ROM_QUEUE;
+ break;
+ case TYPE_PROCESSOR:
+ case TYPE_SCANNER:
+ default:
+ factor += TYPE_OTHER_QUEUE;
+ break;
+ }
+ }
+ }
+
+ DBG(DBG_REGISTER, printk(KERN_DEBUG "scsi%d: needed queueslots %d\n",
+ host->host_no, factor));
+
+ if(factor == 0) /* We don't want to get a DIV BY ZERO error */
+ factor = 1;
+
+ factor = (SD(host)->queuesize * 10) / factor;
+
+ DBG(DBG_REGISTER, printk(KERN_DEBUG "scsi%d: using factor %dE-1\n",
+ host->host_no, factor));
+
+ /* Now that have the factor we can set the individual queuesizes */
+ for(device = devicelist; device != NULL; device = device->next) {
+ if(device->host == host) {
+ if(SD(device->host)->bustype != IS_ISA){
+ switch(device->type) {
+ case TYPE_DISK:
+ case TYPE_MOD:
+ device->queue_depth = (TYPE_DISK_QUEUE * factor) / 10;
+ break;
+ case TYPE_TAPE:
+ device->queue_depth = (TYPE_TAPE_QUEUE * factor) / 10;
+ break;
+ case TYPE_WORM:
+ case TYPE_ROM:
+ device->queue_depth = (TYPE_ROM_QUEUE * factor) / 10;
+ break;
+ case TYPE_PROCESSOR:
+ case TYPE_SCANNER:
+ default:
+ device->queue_depth = (TYPE_OTHER_QUEUE * factor) / 10;
+ break;
+ }
+ } else /* ISA forces us to limit the queue depth because of the
+ * bounce buffer memory overhead. I know this is cruel */
+ device->queue_depth = 2;
+
+ /*
+ * It showed that we need to set an upper limit of commands
+ * we can allow to queue for a single device on the bus.
+ * If we get above that limit, the broken midlevel SCSI code
+ * will produce bogus timeouts and aborts en masse. :-(
+ */
+ if(device->queue_depth > UPPER_DEVICE_QUEUE_LIMIT)
+ device->queue_depth = UPPER_DEVICE_QUEUE_LIMIT;
+ if(device->queue_depth == 0)
+ device->queue_depth = 1;
+
+ printk(KERN_INFO "scsi%d: queue depth for target %d on channel %d "
+ "set to %d\n", host->host_no, device->id, device->channel,
+ device->queue_depth);
+ }
+ }
+#endif
+}
+
+#if CHECK_BLINK
+int check_blink_state(long base)
+{
+ ushort loops = 10;
+ u32 blinkindicator;
+ u32 state = 0x12345678;
+ u32 oldstate = 0;
+
+ blinkindicator = htonl(0x54504442);
+ while ((loops--) && (state != oldstate)) {
+ oldstate = state;
+ state = inl((uint) base + 1);
+ }
+
+ DBG(DBG_BLINK, printk("Did Blink check. Status: %d\n",
+ (state == oldstate) && (state == blinkindicator)));
+
+ if ((state == oldstate) && (state == blinkindicator))
+ return(TRUE);
+ else
+ return (FALSE);
+}
+#endif
+
+char * get_board_data(u32 base, u32 irq, u32 id)
+{
+ struct eata_ccb *cp;
+ struct eata_sp *sp;
+ static char *buff;
+ ulong i;
+
+ cp = (struct eata_ccb *) scsi_init_malloc(sizeof(struct eata_ccb),
+ GFP_ATOMIC | GFP_DMA);
+ sp = (struct eata_sp *) scsi_init_malloc(sizeof(struct eata_sp),
+ GFP_ATOMIC | GFP_DMA);
+
+ buff = dma_scratch;
+
+ memset(cp, 0, sizeof(struct eata_ccb));
+ memset(sp, 0, sizeof(struct eata_sp));
+ memset(buff, 0, 256);
+
+ cp->DataIn = TRUE;
+ cp->Interpret = TRUE; /* Interpret command */
+ cp->cp_dispri = TRUE;
+ cp->cp_identify = TRUE;
+
+ cp->cp_datalen = htonl(56);
+ cp->cp_dataDMA = htonl(virt_to_bus(buff));
+ cp->cp_statDMA = htonl(virt_to_bus(sp));
+ cp->cp_viraddr = cp;
+
+ cp->cp_id = id;
+ cp->cp_lun = 0;
+
+ cp->cp_cdb[0] = INQUIRY;
+ cp->cp_cdb[1] = 0;
+ cp->cp_cdb[2] = 0;
+ cp->cp_cdb[3] = 0;
+ cp->cp_cdb[4] = 56;
+ cp->cp_cdb[5] = 0;
+
+ fake_int_base = (struct eata_register *) base;
+ fake_int_result = FALSE;
+ fake_int_happened = FALSE;
+
+ eata_send_command((u32) cp, (u32) base, EATA_CMD_DMA_SEND_CP);
+
+ i = jiffies + (3 * HZ);
+ while (fake_int_happened == FALSE && jiffies <= i)
+ barrier();
+
+ DBG(DBG_INTR3, printk(KERN_DEBUG "fake_int_result: %#x hbastat %#x "
+ "scsistat %#x, buff %p sp %p\n",
+ fake_int_result, (u32) (sp->hba_stat /*& 0x7f*/),
+ (u32) sp->scsi_stat, buff, sp));
+
+ scsi_init_free((void *)cp, sizeof(struct eata_ccb));
+ scsi_init_free((void *)sp, sizeof(struct eata_sp));
+
+ if ((fake_int_result & HA_SERROR) || jiffies > i){
+ printk(KERN_WARNING "eata_dma: trying to reset HBA at %x to clear "
+ "possible blink state\n", base);
+ /* hard reset the HBA */
+ inb((u32) (base) + HA_RSTATUS);
+ eata_send_command(0, base, EATA_CMD_RESET);
+ DELAY(1);
+ return (NULL);
+ } else
+ return (buff);
+}
+
+
+int get_conf_PIO(u32 base, struct get_conf *buf)
+{
+ ulong loop = R_LIMIT;
+ u16 *p;
+
+ if(check_region(base, 9))
+ return (FALSE);
+
+ memset(buf, 0, sizeof(struct get_conf));
+
+ while (inb(base + HA_RSTATUS) & HA_SBUSY)
+ if (--loop == 0)
+ return (FALSE);
+
+ fake_int_base = (struct eata_register *) base;
+ fake_int_result = FALSE;
+ fake_int_happened = FALSE;
+
+ DBG(DBG_PIO && DBG_PROBE,
+ printk("Issuing PIO READ CONFIG to HBA at %#x\n", base));
+ eata_send_command(0, base, EATA_CMD_PIO_READ_CONFIG);
+
+ loop = R_LIMIT;
+ for (p = (u16 *) buf;
+ (long)p <= ((long)buf + (sizeof(struct get_conf) / 2)); p++) {
+ while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
+ if (--loop == 0)
+ return (FALSE);
+
+ loop = R_LIMIT;
+ *p = inw(base + HA_RDATA);
+ }
+
+ if (!(inb(base + HA_RSTATUS) & HA_SERROR)) { /* Error ? */
+ if (htonl(EATA_SIGNATURE) == buf->signature) {
+ DBG(DBG_PIO&&DBG_PROBE, printk("EATA Controller found at %x "
+ "EATA Level: %x\n", (uint) base,
+ (uint) (buf->version)));
+
+ while (inb(base + HA_RSTATUS) & HA_SDRQ)
+ inw(base + HA_RDATA);
+ return (TRUE);
+ }
+ } else {
+ DBG(DBG_PROBE, printk("eata_dma: get_conf_PIO, error during transfer "
+ "for HBA at %lx\n", (long)base));
+ }
+ return (FALSE);
+}
+
+
+void print_config(struct get_conf *gc)
+{
+ printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d DMAS:%d\n",
+ (u32) ntohl(gc->len), gc->version,
+ gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support,
+ gc->DMA_support);
+ printk("DMAV:%d HAAV:%d SCSIID0:%d ID1:%d ID2:%d QUEUE:%d SG:%d SEC:%d\n",
+ gc->DMA_valid, gc->HAA_valid, gc->scsi_id[3], gc->scsi_id[2],
+ gc->scsi_id[1], ntohs(gc->queuesiz), ntohs(gc->SGsiz), gc->SECOND);
+ printk("IRQ:%d IRQT:%d DMAC:%d FORCADR:%d SG_64K:%d SG_UAE:%d MID:%d "
+ "MCH:%d MLUN:%d\n",
+ gc->IRQ, gc->IRQ_TR, (8 - gc->DMA_channel) & 7, gc->FORCADR,
+ gc->SG_64K, gc->SG_UAE, gc->MAX_ID, gc->MAX_CHAN, gc->MAX_LUN);
+ printk("RIDQ:%d PCI:%d EISA:%d\n",
+ gc->ID_qest, gc->is_PCI, gc->is_EISA);
+ DBG(DPT_DEBUG, DELAY(14));
+}
+
+short register_HBA(u32 base, struct get_conf *gc, Scsi_Host_Template * tpnt,
+ u8 bustype)
+{
+ ulong size = 0;
+ unchar dma_channel = 0;
+ char *buff = 0;
+ unchar bugs = 0;
+ struct Scsi_Host *sh;
+ hostdata *hd;
+ int x;
+
+
+ DBG(DBG_REGISTER, print_config(gc));
+
+ if (gc->DMA_support == FALSE) {
+ printk("The EATA HBA at %#.4x does not support DMA.\n"
+ "Please use the EATA-PIO driver.\n", base);
+ return (FALSE);
+ }
+ if(gc->HAA_valid == FALSE || ntohl(gc->len) < 0x22)
+ gc->MAX_CHAN = 0;
+
+ if (reg_IRQ[gc->IRQ] == FALSE) { /* Interrupt already registered ? */
+ if (!request_irq(gc->IRQ, (void *) eata_fake_int_handler, SA_INTERRUPT,
+ "eata_dma", NULL)){
+ reg_IRQ[gc->IRQ]++;
+ if (!gc->IRQ_TR)
+ reg_IRQL[gc->IRQ] = TRUE; /* IRQ is edge triggered */
+ } else {
+ printk("Couldn't allocate IRQ %d, Sorry.", gc->IRQ);
+ return (FALSE);
+ }
+ } else { /* More than one HBA on this IRQ */
+ if (reg_IRQL[gc->IRQ] == TRUE) {
+ printk("Can't support more than one HBA on this IRQ,\n"
+ " if the IRQ is edge triggered. Sorry.\n");
+ return (FALSE);
+ } else
+ reg_IRQ[gc->IRQ]++;
+ }
+
+
+ /* If DMA is supported but DMA_valid isn't set to indicate that
+ * the channel number is given we must have pre 2.0 firmware (1.7?)
+ * which leaves us to guess since the "newer ones" also don't set the
+ * DMA_valid bit.
+ */
+ if (gc->DMA_support && !gc->DMA_valid && gc->DMA_channel) {
+ printk(KERN_WARNING "eata_dma: If you are using a pre 2.0 firmware "
+ "please update it !\n"
+ " You can get new firmware releases from ftp.dpt.com\n");
+ gc->DMA_channel = (base == 0x1f0 ? 3 /* DMA=5 */ : 2 /* DMA=6 */);
+ gc->DMA_valid = TRUE;
+ }
+
+ /* if gc->DMA_valid it must be an ISA HBA and we have to register it */
+ dma_channel = BUSMASTER;
+ if (gc->DMA_valid) {
+ if (request_dma(dma_channel = (8 - gc->DMA_channel) & 7, "eata_dma")) {
+ printk(KERN_WARNING "Unable to allocate DMA channel %d for ISA HBA"
+ " at %#.4x.\n", dma_channel, base);
+ reg_IRQ[gc->IRQ]--;
+ if (reg_IRQ[gc->IRQ] == 0)
+ free_irq(gc->IRQ, NULL);
+ if (gc->IRQ_TR == FALSE)
+ reg_IRQL[gc->IRQ] = FALSE;
+ return (FALSE);
+ }
+ }
+
+ if (dma_channel != BUSMASTER) {
+ disable_dma(dma_channel);
+ clear_dma_ff(dma_channel);
+ set_dma_mode(dma_channel, DMA_MODE_CASCADE);
+ enable_dma(dma_channel);
+ }
+
+ if (bustype != IS_EISA && bustype != IS_ISA)
+ buff = get_board_data(base, gc->IRQ, gc->scsi_id[3]);
+
+ if (buff == NULL) {
+ if (bustype == IS_EISA || bustype == IS_ISA) {
+ bugs = bugs || BROKEN_INQUIRY;
+ } else {
+ if (gc->DMA_support == FALSE)
+ printk(KERN_WARNING "HBA at %#.4x doesn't support DMA. "
+ "Sorry\n", base);
+ else
+ printk(KERN_WARNING "HBA at %#.4x does not react on INQUIRY. "
+ "Sorry.\n", base);
+ if (gc->DMA_valid)
+ free_dma(dma_channel);
+ reg_IRQ[gc->IRQ]--;
+ if (reg_IRQ[gc->IRQ] == 0)
+ free_irq(gc->IRQ, NULL);
+ if (gc->IRQ_TR == FALSE)
+ reg_IRQL[gc->IRQ] = FALSE;
+ return (FALSE);
+ }
+ }
+
+ if (gc->DMA_support == FALSE && buff != NULL)
+ printk(KERN_WARNING "HBA %.12sat %#.4x doesn't set the DMA_support "
+ "flag correctly.\n", &buff[16], base);
+
+ request_region(base, 9, "eata_dma"); /* We already checked the
+ * availability, so this
+ * should not fail.
+ */
+
+ if(ntohs(gc->queuesiz) == 0) {
+ gc->queuesiz = ntohs(64);
+ printk(KERN_WARNING "Warning: Queue size has to be corrected. Assuming"
+ " 64 queueslots\n"
+ " This might be a PM2012B with a defective Firmware\n"
+ " Contact DPT support@dpt.com for an upgrade\n");
+ }
+
+ size = sizeof(hostdata) + ((sizeof(struct eata_ccb) + sizeof(long))
+ * ntohs(gc->queuesiz));
+
+ DBG(DBG_REGISTER, printk("scsi_register size: %ld\n", size));
+
+ sh = scsi_register(tpnt, size);
+
+ if(sh != NULL) {
+
+ hd = SD(sh);
+
+ memset(hd->reads, 0, sizeof(u32) * 26);
+
+ sh->select_queue_depths = eata_select_queue_depths;
+
+ hd->bustype = bustype;
+
+ /*
+ * If we are using a ISA board, we can't use extended SG,
+ * because we would need excessive amounts of memory for
+ * bounce buffers.
+ */
+ if (gc->SG_64K==TRUE && ntohs(gc->SGsiz)==64 && hd->bustype!=IS_ISA){
+ sh->sg_tablesize = SG_SIZE_BIG;
+ } else {
+ sh->sg_tablesize = ntohs(gc->SGsiz);
+ if (sh->sg_tablesize > SG_SIZE || sh->sg_tablesize == 0) {
+ if (sh->sg_tablesize == 0)
+ printk(KERN_WARNING "Warning: SG size had to be fixed.\n"
+ "This might be a PM2012 with a defective Firmware"
+ "\nContact DPT support@dpt.com for an upgrade\n");
+ sh->sg_tablesize = SG_SIZE;
+ }
+ }
+ hd->sgsize = sh->sg_tablesize;
+ }
+
+ if(sh != NULL) {
+ sh->can_queue = hd->queuesize = ntohs(gc->queuesiz);
+ sh->cmd_per_lun = 0;
+ }
+
+ if(sh == NULL) {
+ DBG(DBG_REGISTER, printk(KERN_NOTICE "eata_dma: couldn't register HBA"
+ " at%x \n", base));
+ scsi_unregister(sh);
+ if (gc->DMA_valid)
+ free_dma(dma_channel);
+
+ reg_IRQ[gc->IRQ]--;
+ if (reg_IRQ[gc->IRQ] == 0)
+ free_irq(gc->IRQ, NULL);
+ if (gc->IRQ_TR == FALSE)
+ reg_IRQL[gc->IRQ] = FALSE;
+ return (FALSE);
+ }
+
+
+ hd->broken_INQUIRY = (bugs & BROKEN_INQUIRY);
+
+ if(hd->broken_INQUIRY == TRUE) {
+ strcpy(hd->vendor, "DPT");
+ strcpy(hd->name, "??????????");
+ strcpy(hd->revision, "???.?");
+ hd->firmware_revision = 0;
+ } else {
+ strncpy(hd->vendor, &buff[8], 8);
+ hd->vendor[8] = 0;
+ strncpy(hd->name, &buff[16], 17);
+ hd->name[17] = 0;
+ hd->revision[0] = buff[32];
+ hd->revision[1] = buff[33];
+ hd->revision[2] = buff[34];
+ hd->revision[3] = '.';
+ hd->revision[4] = buff[35];
+ hd->revision[5] = 0;
+ hd->firmware_revision = (buff[32] << 24) + (buff[33] << 16)
+ + (buff[34] << 8) + buff[35];
+ }
+
+ if (hd->firmware_revision >= (('0'<<24) + ('7'<<16) + ('G'<< 8) + '0'))
+ hd->immediate_support = 1;
+ else
+ hd->immediate_support = 0;
+
+ switch (ntohl(gc->len)) {
+ case 0x1c:
+ hd->EATA_revision = 'a';
+ break;
+ case 0x1e:
+ hd->EATA_revision = 'b';
+ break;
+ case 0x22:
+ hd->EATA_revision = 'c';
+ break;
+ case 0x24:
+ hd->EATA_revision = 'z';
+ default:
+ hd->EATA_revision = '?';
+ }
+
+
+ if(ntohl(gc->len) >= 0x22) {
+ sh->max_id = gc->MAX_ID + 1;
+ sh->max_lun = gc->MAX_LUN + 1;
+ } else {
+ sh->max_id = 8;
+ sh->max_lun = 8;
+ }
+
+ hd->HBA_number = sh->host_no;
+ hd->channel = gc->MAX_CHAN;
+ sh->max_channel = gc->MAX_CHAN;
+ sh->unique_id = base;
+ sh->base = (char *) base;
+ sh->io_port = base;
+ sh->n_io_port = 9;
+ sh->irq = gc->IRQ;
+ sh->dma_channel = dma_channel;
+
+ /* FIXME:
+ * SCSI midlevel code should support different HBA ids on every channel
+ */
+ sh->this_id = gc->scsi_id[3];
+
+ if (gc->SECOND)
+ hd->primary = FALSE;
+ else
+ hd->primary = TRUE;
+
+ sh->wish_block = FALSE;
+
+ if (hd->bustype != IS_ISA) {
+ sh->unchecked_isa_dma = FALSE;
+ } else {
+ sh->unchecked_isa_dma = TRUE; /* We're doing ISA DMA */
+ }
+
+ for(x = 0; x <= 11; x++){ /* Initialize min. latency */
+ hd->writes_lat[x][1] = 0xffffffff;
+ hd->reads_lat[x][1] = 0xffffffff;
+ }
+ hd->all_lat[1] = 0xffffffff;
+
+ hd->next = NULL; /* build a linked list of all HBAs */
+ hd->prev = last_HBA;
+ if(hd->prev != NULL)
+ SD(hd->prev)->next = sh;
+ last_HBA = sh;
+ if (first_HBA == NULL)
+ first_HBA = sh;
+ registered_HBAs++;
+
+ return (TRUE);
+}
+
+
+
+void find_EISA(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+ u32 base;
+ int i;
+
+#if CHECKPAL
+ u8 pal1, pal2, pal3;
+#endif
+
+ for (i = 0; i < MAXEISA; i++) {
+ if (EISAbases[i] == TRUE) { /* Still a possibility ? */
+
+ base = 0x1c88 + (i * 0x1000);
+#if CHECKPAL
+ pal1 = inb((u16)base - 8);
+ pal2 = inb((u16)base - 7);
+ pal3 = inb((u16)base - 6);
+
+ if (((pal1 == DPT_ID1) && (pal2 == DPT_ID2)) ||
+ ((pal1 == NEC_ID1) && (pal2 == NEC_ID2) && (pal3 == NEC_ID3))||
+ ((pal1 == ATT_ID1) && (pal2 == ATT_ID2) && (pal3 == ATT_ID3))){
+ DBG(DBG_PROBE, printk("EISA EATA id tags found: %x %x %x \n",
+ (int)pal1, (int)pal2, (int)pal3));
+#endif
+ if (get_conf_PIO(base, buf) == TRUE) {
+ if (buf->IRQ) {
+ DBG(DBG_EISA, printk("Registering EISA HBA\n"));
+ register_HBA(base, buf, tpnt, IS_EISA);
+ } else
+ printk("eata_dma: No valid IRQ. HBA removed from list\n");
+ }
+#if CHECK_BLINK
+ else {
+ if (check_blink_state(base))
+ printk("HBA is in BLINK state. Consult your HBAs "
+ "Manual to correct this.\n");
+ }
+#endif
+ /* Nothing found here so we take it from the list */
+ EISAbases[i] = 0;
+#if CHECKPAL
+ }
+#endif
+ }
+ }
+ return;
+}
+
+void find_ISA(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+ int i;
+
+ for (i = 0; i < MAXISA; i++) {
+ if (ISAbases[i]) {
+ if (get_conf_PIO(ISAbases[i],buf) == TRUE){
+ DBG(DBG_ISA, printk("Registering ISA HBA\n"));
+ register_HBA(ISAbases[i], buf, tpnt, IS_ISA);
+ }
+#if CHECK_BLINK
+ else {
+ if (check_blink_state(ISAbases[i]))
+ printk("HBA is in BLINK state. Consult your HBAs "
+ "Manual to correct this.\n");
+ }
+#endif
+ ISAbases[i] = 0;
+ }
+ }
+ return;
+}
+
+void find_PCI(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+
+#ifndef CONFIG_PCI
+ printk("eata_dma: kernel PCI support not enabled. Skipping scan for PCI HBAs.\n");
+#else
+
+ u8 pci_bus, pci_device_fn;
+ static s16 pci_index = 0; /* Device index to PCI BIOS calls */
+ u32 base = 0;
+ u16 com_adr;
+ u16 rev_device;
+ u32 error, i, x;
+ u8 pal1, pal2, pal3;
+
+ if (pcibios_present()) {
+ for (i = 0; i <= MAXPCI; ++i, ++pci_index) {
+ if (pcibios_find_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT,
+ pci_index, &pci_bus, &pci_device_fn))
+ break;
+ DBG(DBG_PROBE && DBG_PCI,
+ printk("eata_dma: find_PCI, HBA at bus %d, device %d,"
+ " function %d, index %d\n", (s32)pci_bus,
+ (s32)((pci_device_fn & 0xf8) >> 3),
+ (s32)(pci_device_fn & 7), pci_index));
+
+ if (!(error = pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_CLASS_DEVICE, &rev_device))) {
+ if (rev_device == PCI_CLASS_STORAGE_SCSI) {
+ if (!(error = pcibios_read_config_word(pci_bus,
+ pci_device_fn, PCI_COMMAND,
+ (u16 *) & com_adr))) {
+ if (!((com_adr & PCI_COMMAND_IO) &&
+ (com_adr & PCI_COMMAND_MASTER))) {
+ printk("eata_dma: find_PCI, HBA has IO or"
+ " BUSMASTER mode disabled\n");
+ continue;
+ }
+ } else
+ printk("eata_dma: find_PCI, error %x while reading "
+ "PCI_COMMAND\n", error);
+ } else
+ printk("eata_dma: find_PCI, DEVICECLASSID %x didn't match\n",
+ rev_device);
+ } else {
+ printk("eata_dma: find_PCI, error %x while reading "
+ "PCI_CLASS_BASE\n",
+ error);
+ continue;
+ }
+
+ if (!(error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, (int *) &base))){
+
+ /* Check if the address is valid */
+ if (base & 0x01) {
+ base &= 0xfffffffe;
+ /* EISA tag there ? */
+ pal1 = inb(base);
+ pal2 = inb(base + 1);
+ pal3 = inb(base + 2);
+ if (((pal1 == DPT_ID1) && (pal2 == DPT_ID2)) ||
+ ((pal1 == NEC_ID1) && (pal2 == NEC_ID2) &&
+ (pal3 == NEC_ID3)) ||
+ ((pal1 == ATT_ID1) && (pal2 == ATT_ID2) &&
+ (pal3 == ATT_ID3)))
+ base += 0x08;
+ else
+ base += 0x10; /* Now, THIS is the real address */
+
+ if (base != 0x1f8) {
+ /* We didn't find it in the primary search */
+ if (get_conf_PIO(base, buf) == TRUE) {
+
+ /* OK. We made it till here, so we can go now
+ * and register it. We only have to check and
+ * eventually remove it from the EISA and ISA list
+ */
+ DBG(DBG_PCI, printk("Registering PCI HBA\n"));
+ register_HBA(base, buf, tpnt, IS_PCI);
+
+ if (base < 0x1000) {
+ for (x = 0; x < MAXISA; ++x) {
+ if (ISAbases[x] == base) {
+ ISAbases[x] = 0;
+ break;
+ }
+ }
+ } else if ((base & 0x0fff) == 0x0c88)
+ EISAbases[(base >> 12) & 0x0f] = 0;
+ continue; /* break; */
+ }
+#if CHECK_BLINK
+ else if (check_blink_state(base) == TRUE) {
+ printk("eata_dma: HBA is in BLINK state.\n"
+ "Consult your HBAs manual to correct this.\n");
+ }
+#endif
+ }
+ }
+ } else {
+ printk("eata_dma: error %x while reading "
+ "PCI_BASE_ADDRESS_0\n", error);
+ }
+ }
+ } else {
+ printk("eata_dma: No BIOS32 extensions present. This driver release "
+ "still depends on it.\n"
+ " Skipping scan for PCI HBAs. \n");
+ }
+#endif /* #ifndef CONFIG_PCI */
+ return;
+}
+
+int eata_detect(Scsi_Host_Template * tpnt)
+{
+ struct Scsi_Host *HBA_ptr;
+ struct get_conf gc;
+ int i;
+
+ DBG((DBG_PROBE && DBG_DELAY) || DPT_DEBUG,
+ printk("Using lots of delays to let you read the debugging output\n"));
+
+ tpnt->proc_dir = &proc_scsi_eata_dma;
+
+ status = scsi_init_malloc(512, GFP_ATOMIC | GFP_DMA);
+ dma_scratch = scsi_init_malloc(1024, GFP_ATOMIC | GFP_DMA);
+
+ if(status == NULL || dma_scratch == NULL) {
+ printk("eata_dma: can't allocate enough memory to probe for hosts !\n");
+ return(0);
+ }
+
+ dma_scratch += 4;
+
+ find_PCI(&gc, tpnt);
+
+ find_EISA(&gc, tpnt);
+
+ find_ISA(&gc, tpnt);
+
+ for (i = 0; i <= MAXIRQ; i++) { /* Now that we know what we have, we */
+ if (reg_IRQ[i] >= 1){ /* exchange the interrupt handler which */
+ free_irq(i, NULL); /* we used for probing with the real one */
+ request_irq(i, (void *)(eata_int_handler), SA_INTERRUPT|SA_SHIRQ,
+ "eata_dma", NULL);
+ }
+ }
+
+ HBA_ptr = first_HBA;
+
+ if (registered_HBAs != 0) {
+ printk("EATA (Extended Attachment) driver version: %d.%d%s"
+ "\ndeveloped in co-operation with DPT\n"
+ "(c) 1993-96 Michael Neuffer, mike@i-Connect.Net\n",
+ VER_MAJOR, VER_MINOR, VER_SUB);
+ printk("Registered HBAs:");
+ printk("\nHBA no. Boardtype Revis EATA Bus BaseIO IRQ"
+ " DMA Ch ID Pr QS S/G IS\n");
+ for (i = 1; i <= registered_HBAs; i++) {
+ printk("scsi%-2d: %.12s v%s 2.0%c %s %#.4x %2d",
+ HBA_ptr->host_no, SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
+ SD(HBA_ptr)->EATA_revision, (SD(HBA_ptr)->bustype == 'P')?
+ "PCI ":(SD(HBA_ptr)->bustype == 'E')?"EISA":"ISA ",
+ (u32) HBA_ptr->base, HBA_ptr->irq);
+ if(HBA_ptr->dma_channel != BUSMASTER)
+ printk(" %2x ", HBA_ptr->dma_channel);
+ else
+ printk(" %s", "BMST");
+ printk(" %d %d %c %3d %3d %c\n",
+ SD(HBA_ptr)->channel+1, HBA_ptr->this_id,
+ (SD(HBA_ptr)->primary == TRUE)?'Y':'N',
+ HBA_ptr->can_queue, HBA_ptr->sg_tablesize,
+ (SD(HBA_ptr)->immediate_support == TRUE)?'Y':'N');
+ HBA_ptr = SD(HBA_ptr)->next;
+ }
+ } else {
+ scsi_init_free((void *)status, 512);
+ }
+
+ scsi_init_free((void *)dma_scratch - 4, 1024);
+
+ DBG(DPT_DEBUG, DELAY(12));
+
+ return(registered_HBAs);
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = EATA_DMA;
+#include "scsi_module.c"
+#endif
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/eata_dma.h b/linux/src/drivers/scsi/eata_dma.h
new file mode 100644
index 00000000..a23931bf
--- /dev/null
+++ b/linux/src/drivers/scsi/eata_dma.h
@@ -0,0 +1,128 @@
+/********************************************************
+* Header file for eata_dma.c Linux EATA-DMA SCSI driver *
+* (c) 1993-96 Michael Neuffer *
+* mike@i-Connect.Net *
+* neuffer@mail.uni-mainz.de *
+*********************************************************
+* last change: 96/10/14 *
+********************************************************/
+
+#ifndef _EATA_DMA_H
+#define _EATA_DMA_H
+
+#ifndef HOSTS_C
+
+#include "eata_generic.h"
+
+
+#define VER_MAJOR 2
+#define VER_MINOR 5
+#define VER_SUB "9b"
+
+
+/************************************************************************
+ * Here you can switch parts of the code on and of *
+ ************************************************************************/
+
+#define CHECKPAL 0 /* EISA pal checking on/off */
+#define CHECK_BLINK 1 /* Switch Blink state check off, might *
+ * be nessessary for some MIPS machines*/
+#define CRIPPLE_QUEUE 0 /* Only enable this if the interrupt
+ * controller on your motherboard is
+ * broken and you are experiencing
+ * massive interrupt losses */
+
+/************************************************************************
+ * Debug options. *
+ * Enable DEBUG and whichever options you require. *
+ ************************************************************************/
+#define DEBUG_EATA 1 /* Enable debug code. */
+#define DPT_DEBUG 0 /* Bobs special */
+#define DBG_DELAY 0 /* Build in delays so debug messages can be
+ * be read before they vanish of the top of
+ * the screen! */
+#define DBG_PROBE 0 /* Debug probe routines. */
+#define DBG_PCI 0 /* Trace PCI routines */
+#define DBG_EISA 0 /* Trace EISA routines */
+#define DBG_ISA 0 /* Trace ISA routines */
+#define DBG_BLINK 0 /* Trace Blink check */
+#define DBG_PIO 0 /* Trace get_config_PIO */
+#define DBG_COM 0 /* Trace command call */
+#define DBG_QUEUE 0 /* Trace command queueing. */
+#define DBG_QUEUE2 0 /* Trace command queueing SG. */
+#define DBG_INTR 0 /* Trace interrupt service routine. */
+#define DBG_INTR2 0 /* Trace interrupt service routine. */
+#define DBG_INTR3 0 /* Trace get_board_data interrupts. */
+#define DBG_REQSENSE 0 /* Trace request sense commands */
+#define DBG_RESET 0 /* Trace reset calls */
+#define DBG_STATUS 0 /* Trace status generation */
+#define DBG_PROC 0 /* Debug proc-fs related statistics */
+#define DBG_PROC_WRITE 0
+#define DBG_REGISTER 0 /* */
+#define DBG_ABNORM 1 /* Debug abnormal actions (reset, abort)*/
+
+#if DEBUG_EATA
+#define DBG(x, y) if ((x)) {y;}
+#else
+#define DBG(x, y)
+#endif
+
+#endif /* !HOSTS_C */
+
+int eata_detect(Scsi_Host_Template *);
+const char *eata_info(struct Scsi_Host *);
+int eata_command(Scsi_Cmnd *);
+int eata_queue(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int eata_abort(Scsi_Cmnd *);
+int eata_reset(Scsi_Cmnd *, unsigned int);
+int eata_proc_info(char *, char **, off_t, int, int, int);
+#ifdef MODULE
+int eata_release(struct Scsi_Host *);
+#else
+#define eata_release NULL
+#endif
+
+#include <scsi/scsicam.h>
+
+#define EATA_DMA { \
+ NULL, NULL, \
+ NULL, /* proc_dir_entry */ \
+ eata_proc_info, /* procinfo */ \
+ "EATA (Extended Attachment) HBA driver", \
+ eata_detect, \
+ eata_release, \
+ NULL, NULL, \
+ eata_queue, \
+ eata_abort, \
+ eata_reset, \
+ NULL, /* Slave attach */ \
+ scsicam_bios_param, \
+ 0, /* Canqueue */ \
+ 0, /* this_id */ \
+ 0, /* sg_tablesize */ \
+ 0, /* cmd_per_lun */ \
+ 0, /* present */ \
+ 1, /* True if ISA */ \
+ ENABLE_CLUSTERING }
+
+
+#endif /* _EATA_DMA_H */
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/eata_dma_proc.c b/linux/src/drivers/scsi/eata_dma_proc.c
new file mode 100644
index 00000000..3a3a19aa
--- /dev/null
+++ b/linux/src/drivers/scsi/eata_dma_proc.c
@@ -0,0 +1,493 @@
+
+void swap_statistics(u8 *p)
+{
+ u32 y;
+ u32 *lp, h_lp;
+ u16 *sp, h_sp;
+ u8 *bp;
+
+ lp = (u32 *)p;
+ sp = ((short *)lp) + 1; /* Convert Header */
+ h_sp = *sp = ntohs(*sp);
+ lp++;
+
+ do {
+ sp = (u16 *)lp; /* Convert SubHeader */
+ *sp = ntohs(*sp);
+ bp = (u8 *) lp;
+ y = *(bp + 3);
+ lp++;
+ for (h_lp = (u32)lp; (u32)lp < h_lp + ((u32)*(bp + 3)); lp++)
+ *lp = ntohl(*lp);
+ }while ((u32)lp < ((u32)p) + 4 + h_sp);
+
+}
+
+/*
+ * eata_set_info
+ * buffer : pointer to the data that has been written to the hostfile
+ * length : number of bytes written to the hostfile
+ * HBA_ptr: pointer to the Scsi_Host struct
+ */
+int eata_set_info(char *buffer, int length, struct Scsi_Host *HBA_ptr)
+{
+ int orig_length = length;
+
+ if (length >= 8 && strncmp(buffer, "eata_dma", 8) == 0) {
+ buffer += 9;
+ length -= 9;
+ if(length >= 8 && strncmp(buffer, "latency", 7) == 0) {
+ SD(HBA_ptr)->do_latency = TRUE;
+ return(orig_length);
+ }
+
+ if(length >=10 && strncmp(buffer, "nolatency", 9) == 0) {
+ SD(HBA_ptr)->do_latency = FALSE;
+ return(orig_length);
+ }
+
+ printk("Unknown command:%s length: %d\n", buffer, length);
+ } else
+ printk("Wrong Signature:%10s\n", buffer);
+
+ return(-EINVAL);
+}
+
+/*
+ * eata_proc_info
+ * inout : decides on the direction of the dataflow and the meaning of the
+ * variables
+ * buffer: If inout==FALSE data is being written to it else read from it
+ * *start: If inout==FALSE start of the valid data in the buffer
+ * offset: If inout==FALSE offset from the beginning of the imaginary file
+ * from which we start writing into the buffer
+ * length: If inout==FALSE max number of bytes to be written into the buffer
+ * else number of bytes in the buffer
+ */
+int eata_proc_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+
+ Scsi_Device *scd, SDev;
+ struct Scsi_Host *HBA_ptr;
+ Scsi_Cmnd scmd;
+ char cmnd[10];
+ static u8 buff[512];
+ static u8 buff2[512];
+ hst_cmd_stat *rhcs, *whcs;
+ coco *cc;
+ scsitrans *st;
+ scsimod *sm;
+ hobu *hb;
+ scbu *sb;
+ boty *bt;
+ memco *mc;
+ firm *fm;
+ subinf *si;
+ pcinf *pi;
+ arrlim *al;
+ int i, x;
+ int size, len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+ scd = NULL;
+
+ HBA_ptr = first_HBA;
+ for (i = 1; i <= registered_HBAs; i++) {
+ if (HBA_ptr->host_no == hostno)
+ break;
+ HBA_ptr = SD(HBA_ptr)->next;
+ }
+
+ if(inout == TRUE) /* Has data been written to the file ? */
+ return(eata_set_info(buffer, length, HBA_ptr));
+
+ if (offset == 0)
+ memset(buff, 0, sizeof(buff));
+
+ cc = (coco *) (buff + 0x148);
+ st = (scsitrans *)(buff + 0x164);
+ sm = (scsimod *) (buff + 0x16c);
+ hb = (hobu *) (buff + 0x172);
+ sb = (scbu *) (buff + 0x178);
+ bt = (boty *) (buff + 0x17e);
+ mc = (memco *) (buff + 0x186);
+ fm = (firm *) (buff + 0x18e);
+ si = (subinf *) (buff + 0x196);
+ pi = (pcinf *) (buff + 0x19c);
+ al = (arrlim *) (buff + 0x1a2);
+
+ size = sprintf(buffer+len, "EATA (Extended Attachment) driver version: "
+ "%d.%d%s\n",VER_MAJOR, VER_MINOR, VER_SUB);
+ len += size; pos = begin + len;
+ size = sprintf(buffer + len, "queued commands: %10ld\n"
+ "processed interrupts:%10ld\n", queue_counter, int_counter);
+ len += size; pos = begin + len;
+
+ size = sprintf(buffer + len, "\nscsi%-2d: HBA %.10s\n",
+ HBA_ptr->host_no, SD(HBA_ptr)->name);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Firmware revision: v%s\n",
+ SD(HBA_ptr)->revision);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Hardware Configuration:\n");
+ len += size;
+ pos = begin + len;
+
+ if(SD(HBA_ptr)->broken_INQUIRY == TRUE) {
+ if (HBA_ptr->dma_channel == BUSMASTER)
+ size = sprintf(buffer + len, "DMA: BUSMASTER\n");
+ else
+ size = sprintf(buffer + len, "DMA: %d\n", HBA_ptr->dma_channel);
+ len += size;
+ pos = begin + len;
+
+ size = sprintf(buffer + len, "Base IO : %#.4x\n", (u32) HBA_ptr->base);
+ len += size;
+ pos = begin + len;
+
+ size = sprintf(buffer + len, "Host Bus: EISA\n");
+ len += size;
+ pos = begin + len;
+
+ } else {
+ memset(&SDev, 0, sizeof(Scsi_Device));
+ memset(&scmd, 0, sizeof(Scsi_Cmnd));
+
+ SDev.host = HBA_ptr;
+ SDev.id = HBA_ptr->this_id;
+ SDev.lun = 0;
+ SDev.channel = 0;
+
+ cmnd[0] = LOG_SENSE;
+ cmnd[1] = 0;
+ cmnd[2] = 0x33 + (3<<6);
+ cmnd[3] = 0;
+ cmnd[4] = 0;
+ cmnd[5] = 0;
+ cmnd[6] = 0;
+ cmnd[7] = 0x00;
+ cmnd[8] = 0x66;
+ cmnd[9] = 0;
+
+ scmd.cmd_len = 10;
+
+ scmd.host = HBA_ptr;
+ scmd.device = &SDev;
+ scmd.target = HBA_ptr->this_id;
+ scmd.lun = 0;
+ scmd.channel = 0;
+ scmd.use_sg = 0;
+
+ /*
+ * Do the command and wait for it to finish.
+ */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scmd.request.rq_status = RQ_SCSI_BUSY;
+ scmd.request.sem = &sem;
+ scsi_do_cmd (&scmd, cmnd, buff + 0x144, 0x66,
+ eata_scsi_done, 1 * HZ, 1);
+ down(&sem);
+ }
+
+ size = sprintf(buffer + len, "IRQ: %2d, %s triggered\n", cc->interrupt,
+ (cc->intt == TRUE)?"level":"edge");
+ len += size;
+ pos = begin + len;
+ if (HBA_ptr->dma_channel == 0xff)
+ size = sprintf(buffer + len, "DMA: BUSMASTER\n");
+ else
+ size = sprintf(buffer + len, "DMA: %d\n", HBA_ptr->dma_channel);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "CPU: MC680%02d %dMHz\n", bt->cpu_type,
+ bt->cpu_speed);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Base IO : %#.4x\n", (u32) HBA_ptr->base);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Host Bus: %s\n",
+ (SD(HBA_ptr)->bustype == IS_PCI)?"PCI ":
+ (SD(HBA_ptr)->bustype == IS_EISA)?"EISA":"ISA ");
+
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "SCSI Bus:%s%s Speed: %sMB/sec. %s\n",
+ (sb->wide == TRUE)?" WIDE":"",
+ (sb->dif == TRUE)?" DIFFERENTIAL":"",
+ (sb->speed == 0)?"5":(sb->speed == 1)?"10":"20",
+ (sb->ext == TRUE)?"With external cable detection":"");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "SCSI channel expansion Module: %s present\n",
+ (bt->sx1 == TRUE)?"SX1 (one channel)":
+ ((bt->sx2 == TRUE)?"SX2 (two channels)":"not"));
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "SmartRAID hardware: %spresent.\n",
+ (cc->srs == TRUE)?"":"not ");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, " Type: %s\n",
+ ((cc->key == TRUE)?((bt->dmi == TRUE)?"integrated"
+ :((bt->dm4 == TRUE)?"DM401X"
+ :(bt->dm4k == TRUE)?"DM4000"
+ :"-"))
+ :"-"));
+ len += size;
+ pos = begin + len;
+
+ size = sprintf(buffer + len, " Max array groups: %d\n",
+ (al->code == 0x0e)?al->max_groups:7);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, " Max drives per RAID 0 array: %d\n",
+ (al->code == 0x0e)?al->raid0_drv:7);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, " Max drives per RAID 3/5 array: %d\n",
+ (al->code == 0x0e)?al->raid35_drv:7);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Cache Module: %spresent.\n",
+ (cc->csh)?"":"not ");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, " Type: %s\n",
+ ((cc->csh == TRUE)?((bt->cmi == TRUE)?"integrated"
+ :((bt->cm4 == TRUE)?"CM401X"
+ :((bt->cm4k == TRUE)?"CM4000"
+ :"-")))
+ :"-"));
+ len += size;
+ pos = begin + len;
+ for (x = 0; x <= 3; x++) {
+ size = sprintf(buffer + len, " Bank%d: %dMB with%s ECC\n",x,
+ mc->banksize[x] & 0x7f,
+ (mc->banksize[x] & 0x80)?"":"out");
+ len += size;
+ pos = begin + len;
+ }
+ size = sprintf(buffer + len, "Timer Mod.: %spresent\n",
+ (cc->tmr == TRUE)?"":"not ");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "NVRAM : %spresent\n",
+ (cc->nvr == TRUE)?"":"not ");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "SmartROM : %sabled\n",
+ (bt->srom == TRUE)?"dis":"en");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Alarm : %s\n",
+ (bt->alrm == TRUE)?"on":"off");
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ if(SD(HBA_ptr)->do_latency == FALSE) {
+
+ cmnd[0] = LOG_SENSE;
+ cmnd[1] = 0;
+ cmnd[2] = 0x32 + (3<<6);
+ cmnd[3] = 0;
+ cmnd[4] = 0;
+ cmnd[5] = 0;
+ cmnd[6] = 0;
+ cmnd[7] = 0x01;
+ cmnd[8] = 0x44;
+ cmnd[9] = 0;
+
+ scmd.cmd_len = 10;
+
+ /*
+ * Do the command and wait for it to finish.
+ */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scmd.request.rq_status = RQ_SCSI_BUSY;
+ scmd.request.sem = &sem;
+ scsi_do_cmd (&scmd, cmnd, buff2, 0x144,
+ eata_scsi_done, 1 * HZ, 1);
+ down(&sem);
+ }
+
+ swap_statistics(buff2);
+ rhcs = (hst_cmd_stat *)(buff2 + 0x2c);
+ whcs = (hst_cmd_stat *)(buff2 + 0x8c);
+
+ for (x = 0; x <= 11; x++) {
+ SD(HBA_ptr)->reads[x] += rhcs->sizes[x];
+ SD(HBA_ptr)->writes[x] += whcs->sizes[x];
+ SD(HBA_ptr)->reads[12] += rhcs->sizes[x];
+ SD(HBA_ptr)->writes[12] += whcs->sizes[x];
+ }
+ size = sprintf(buffer + len, "Host<->Disk command statistics:\n"
+ " Reads: Writes:\n");
+ len += size;
+ pos = begin + len;
+ for (x = 0; x <= 10; x++) {
+ size = sprintf(buffer+len,"%5dk:%12u %12u\n", 1 << x,
+ SD(HBA_ptr)->reads[x],
+ SD(HBA_ptr)->writes[x]);
+ len += size;
+ pos = begin + len;
+ }
+ size = sprintf(buffer+len,">1024k:%12u %12u\n",
+ SD(HBA_ptr)->reads[11],
+ SD(HBA_ptr)->writes[11]);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer+len,"Sum :%12u %12u\n",
+ SD(HBA_ptr)->reads[12],
+ SD(HBA_ptr)->writes[12]);
+ len += size;
+ pos = begin + len;
+ }
+ }
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ if(SD(HBA_ptr)->do_latency == TRUE) {
+ int factor = 1024/HZ;
+ size = sprintf(buffer + len, "Host Latency Command Statistics:\n"
+ "Current timer resolution: %2dms\n"
+ " Reads: Min:(ms) Max:(ms) Ave:(ms)\n",
+ factor);
+ len += size;
+ pos = begin + len;
+ for (x = 0; x <= 10; x++) {
+ size = sprintf(buffer+len,"%5dk:%12u %12u %12u %12u\n",
+ 1 << x,
+ SD(HBA_ptr)->reads_lat[x][0],
+ (SD(HBA_ptr)->reads_lat[x][1] == 0xffffffff)
+ ? 0:(SD(HBA_ptr)->reads_lat[x][1] * factor),
+ SD(HBA_ptr)->reads_lat[x][2] * factor,
+ SD(HBA_ptr)->reads_lat[x][3] * factor /
+ ((SD(HBA_ptr)->reads_lat[x][0])
+ ? SD(HBA_ptr)->reads_lat[x][0]:1));
+ len += size;
+ pos = begin + len;
+ }
+ size = sprintf(buffer+len,">1024k:%12u %12u %12u %12u\n",
+ SD(HBA_ptr)->reads_lat[11][0],
+ (SD(HBA_ptr)->reads_lat[11][1] == 0xffffffff)
+ ? 0:(SD(HBA_ptr)->reads_lat[11][1] * factor),
+ SD(HBA_ptr)->reads_lat[11][2] * factor,
+ SD(HBA_ptr)->reads_lat[11][3] * factor /
+ ((SD(HBA_ptr)->reads_lat[x][0])
+ ? SD(HBA_ptr)->reads_lat[x][0]:1));
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ size = sprintf(buffer + len,
+ " Writes: Min:(ms) Max:(ms) Ave:(ms)\n");
+ len += size;
+ pos = begin + len;
+ for (x = 0; x <= 10; x++) {
+ size = sprintf(buffer+len,"%5dk:%12u %12u %12u %12u\n",
+ 1 << x,
+ SD(HBA_ptr)->writes_lat[x][0],
+ (SD(HBA_ptr)->writes_lat[x][1] == 0xffffffff)
+ ? 0:(SD(HBA_ptr)->writes_lat[x][1] * factor),
+ SD(HBA_ptr)->writes_lat[x][2] * factor,
+ SD(HBA_ptr)->writes_lat[x][3] * factor /
+ ((SD(HBA_ptr)->writes_lat[x][0])
+ ? SD(HBA_ptr)->writes_lat[x][0]:1));
+ len += size;
+ pos = begin + len;
+ }
+ size = sprintf(buffer+len,">1024k:%12u %12u %12u %12u\n",
+ SD(HBA_ptr)->writes_lat[11][0],
+ (SD(HBA_ptr)->writes_lat[11][1] == 0xffffffff)
+ ? 0:(SD(HBA_ptr)->writes_lat[x][1] * factor),
+ SD(HBA_ptr)->writes_lat[11][2] * factor,
+ SD(HBA_ptr)->writes_lat[11][3] * factor /
+ ((SD(HBA_ptr)->writes_lat[x][0])
+ ? SD(HBA_ptr)->writes_lat[x][0]:1));
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+
+#if 0
+ scd = scsi_devices;
+
+ size = sprintf(buffer+len,"Attached devices: %s\n", (scd)?"":"none");
+ len += size;
+ pos = begin + len;
+
+ while (scd) {
+ if (scd->host == HBA_ptr) {
+ proc_print_scsidevice(scd, buffer, &size, len);
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+ scd = scd->next;
+ }
+#endif
+
+ stop_output:
+ DBG(DBG_PROC, printk("2pos: %ld offset: %ld len: %d\n", pos, offset, len));
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len = length; /* Ending slop */
+ DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len));
+
+ return (len);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/eata_dma_proc.h b/linux/src/drivers/scsi/eata_dma_proc.h
new file mode 100644
index 00000000..d49f348e
--- /dev/null
+++ b/linux/src/drivers/scsi/eata_dma_proc.h
@@ -0,0 +1,260 @@
+
+struct lun_map {
+ __u8 id:5,
+ chan:3;
+ __u8 lun;
+};
+
+typedef struct emul_pp {
+ __u8 p_code:6,
+ null:1,
+ p_save:1;
+ __u8 p_length;
+ __u16 cylinder;
+ __u8 heads;
+ __u8 sectors;
+ __u8 null2;
+ __u8 s_lunmap:4,
+ ems:1;
+ __u16 drive_type; /* In Little Endian ! */
+ struct lun_map lunmap[4];
+}emulpp;
+
+
+/* Log Sense pages */
+
+typedef struct log_sheader {
+ __u8 page_code,
+ reserved;
+ __u16 length;
+}logsh;
+
+
+/* Log Sense Statistics */
+
+typedef struct read_command_statistics {
+ __u16 code; /* 0x01 */
+ __u8 flags;
+ __u8 length; /* 0x24 */
+ __u32 h_commands,
+ uncached,
+ la_cmds,
+ la_blks,
+ la_hits,
+ missed,
+ hits,
+ seq_la_blks,
+ seq_la_hits;
+}r_cmd_stat;
+
+typedef struct write_command_statistics {
+ __u16 code; /* 0x03 */
+ __u8 flags;
+ __u8 length; /* 0x28 */
+ __u32 h_commands,
+ uncached,
+ thru,
+ bypass,
+ soft_err,
+ hits,
+ b_idle,
+ b_activ,
+ b_blks,
+ b_blks_clean;
+}w_cmd_stat;
+
+typedef struct host_command_statistics {
+ __u16 code; /* 0x02, 0x04 */
+ __u8 flags;
+ __u8 length; /* 0x30 */
+ __u32 sizes[12];
+}hst_cmd_stat;
+
+typedef struct physical_command_statistics {
+ __u16 code; /* 0x06, 0x07 */
+ __u8 flags;
+ __u8 length; /* 0x34 */
+ __u32 sizes[13];
+}phy_cmd_stat;
+
+typedef struct misc_device_statistics {
+ __u16 code; /* 0x05 */
+ __u8 flags;
+ __u8 length; /* 0x10 */
+ __u32 disconnect,
+ pass_thru,
+ sg_commands,
+ stripe_boundary_crosses;
+}msc_stats;
+
+/* Configuration Pages */
+
+typedef struct controller_configuration {
+ __u16 code; /* 0x01 */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 intt:1,
+ sec:1,
+ csh:1,
+ key:1,
+ tmr:1,
+ srs:1,
+ nvr:1;
+ __u8 interrupt;
+}coco;
+
+typedef struct controller_hardware_errors {
+ __u16 code; /* 0x02 */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 unused:1,
+ per:1;
+ __u8 interrupt;
+}coher;
+
+typedef struct memory_map {
+ __u16 code; /* 0x03, 0x04 */
+ __u8 flags;
+ __u8 length; /* 0x04 */
+ __u32 memory_map;
+}mema;
+
+typedef struct scsi_transfer {
+ __u16 code; /* 0x05 */
+ __u8 flags;
+ __u8 length; /* 0x04 */
+ __u8 offset,
+ period;
+ __u16 speed;
+}scsitrans;
+
+typedef struct scsi_modes {
+ __u16 code; /* 0x06 */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 que:1,
+ cdis:1,
+ wtru:1,
+ dasd:1,
+ ncr:1,
+ awre:1;
+ __u8 reserved;
+}scsimod;
+
+typedef struct host_bus {
+ __u16 code; /* 0x07 */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 speed:6,
+ pci:1,
+ eisa:1;
+ __u8 reserved;
+}hobu;
+
+typedef struct scsi_bus {
+ __u16 code; /* 0x08 */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 speed:4,
+ res:1,
+ ext:1,
+ wide:1,
+ dif:1;
+ __u8 busnum;
+}scbu;
+
+typedef struct board_type {
+ __u16 code; /* 0x09 */
+ __u8 flags;
+ __u8 length; /* 0x04 */
+ __u8 unused:1,
+ cmi:1,
+ dmi:1,
+ cm4k:1,
+ cm4:1,
+ dm4k:1,
+ dm4:1,
+ hba:1;
+ __u8 cpu_type,
+ cpu_speed;
+ __u8 sx1:1,
+ sx2:1,
+ unused2:4,
+ alrm:1,
+ srom:1;
+}boty;
+
+typedef struct memory_config {
+ __u16 code; /* 0x0a */
+ __u8 flags;
+ __u8 length; /* 0x04 */
+ __u8 banksize[4];
+}memco;
+
+typedef struct firmware_info {
+ __u16 code; /* 0x0b */
+ __u8 flags;
+ __u8 length; /* 0x04 */
+ __u8 dnld:1,
+ bs528:1,
+ fmt:1,
+ fw528:1;
+ __u8 unused1,
+ fw_type,
+ unused;
+}firm;
+
+typedef struct subsystem_info {
+ __u16 code; /* 0x0c */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 shlf:1,
+ swap:1,
+ noss:1;
+ __u8 reserved;
+}subinf;
+
+typedef struct per_channel_info {
+ __u16 code; /* 0x0d */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 channel;
+ __u8 shlf:1,
+ swap:1,
+ noss:1,
+ srs:1,
+ que:1,
+ ext:1,
+ wide:1,
+ diff:1;
+}pcinf;
+
+typedef struct array_limits {
+ __u16 code; /* 0x0e */
+ __u8 flags;
+ __u8 length; /* 0x04 */
+ __u8 max_groups,
+ raid0_drv,
+ raid35_drv,
+ unused;
+}arrlim;
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
+
diff --git a/linux/src/drivers/scsi/eata_generic.h b/linux/src/drivers/scsi/eata_generic.h
new file mode 100644
index 00000000..c884def0
--- /dev/null
+++ b/linux/src/drivers/scsi/eata_generic.h
@@ -0,0 +1,414 @@
+/********************************************************
+* Header file for eata_dma.c and eata_pio.c *
+* Linux EATA SCSI drivers *
+* (c) 1993-96 Michael Neuffer *
+* mike@i-Connect.Net *
+* neuffer@mail.uni-mainz.de *
+*********************************************************
+* last change: 96/08/14 *
+********************************************************/
+
+
+#ifndef _EATA_GENERIC_H
+#define _EATA_GENERIC_H
+
+
+
+/*********************************************
+ * Misc. definitions *
+ *********************************************/
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define min(a,b) ((a<b)?(a):(b))
+
+#define R_LIMIT 0x20000
+
+#define MAXISA 4
+#define MAXEISA 16
+#define MAXPCI 16
+#define MAXIRQ 16
+#define MAXTARGET 16
+#define MAXCHANNEL 3
+
+#define IS_ISA 'I'
+#define IS_EISA 'E'
+#define IS_PCI 'P'
+
+#define BROKEN_INQUIRY 1
+
+#define BUSMASTER 0xff
+#define PIO 0xfe
+
+#define EATA_SIGNATURE 0x45415441 /* BIG ENDIAN coded "EATA" sig. */
+
+#define DPT_ID1 0x12
+#define DPT_ID2 0x14
+
+#define ATT_ID1 0x06
+#define ATT_ID2 0x94
+#define ATT_ID3 0x0
+
+#define NEC_ID1 0x38
+#define NEC_ID2 0xa3
+#define NEC_ID3 0x82
+
+
+#define EATA_CP_SIZE 44
+
+#define MAX_PCI_DEVICES 32 /* Maximum # Of Devices Per Bus */
+#define MAX_METHOD_2 16 /* Max Devices For Method 2 */
+#define MAX_PCI_BUS 16 /* Maximum # Of Busses Allowed */
+
+#define SG_SIZE 64
+#define SG_SIZE_BIG 252 /* max. 8096 elements, 64k */
+
+#define UPPER_DEVICE_QUEUE_LIMIT 64 /* The limit we have to set for the
+ * device queue to keep the broken
+ * midlevel SCSI code from producing
+ * bogus timeouts
+ */
+
+#define TYPE_DISK_QUEUE 16
+#define TYPE_TAPE_QUEUE 4
+#define TYPE_ROM_QUEUE 4
+#define TYPE_OTHER_QUEUE 2
+
+#define FREE 0
+#define OK 0
+#define NO_TIMEOUT 0
+#define USED 1
+#define TIMEOUT 2
+#define RESET 4
+#define LOCKED 8
+#define ABORTED 16
+
+#define READ 0
+#define WRITE 1
+#define OTHER 2
+
+#define HD(cmd) ((hostdata *)&(cmd->host->hostdata))
+#define CD(cmd) ((struct eata_ccb *)(cmd->host_scribble))
+#define SD(host) ((hostdata *)&(host->hostdata))
+
+#define DELAY(x) { ulong flags, i; \
+ save_flags(flags); sti(); \
+ i = jiffies + (x * HZ); \
+ while (jiffies < i); \
+ restore_flags(flags); }
+
+/***********************************************
+ * EATA Command & Register definitions *
+ ***********************************************/
+#define PCI_REG_DPTconfig 0x40
+#define PCI_REG_PumpModeAddress 0x44
+#define PCI_REG_PumpModeData 0x48
+#define PCI_REG_ConfigParam1 0x50
+#define PCI_REG_ConfigParam2 0x54
+
+
+#define EATA_CMD_PIO_SETUPTEST 0xc6
+#define EATA_CMD_PIO_READ_CONFIG 0xf0
+#define EATA_CMD_PIO_SET_CONFIG 0xf1
+#define EATA_CMD_PIO_SEND_CP 0xf2
+#define EATA_CMD_PIO_RECEIVE_SP 0xf3
+#define EATA_CMD_PIO_TRUNC 0xf4
+
+#define EATA_CMD_RESET 0xf9
+#define EATA_CMD_IMMEDIATE 0xfa
+
+#define EATA_CMD_DMA_READ_CONFIG 0xfd
+#define EATA_CMD_DMA_SET_CONFIG 0xfe
+#define EATA_CMD_DMA_SEND_CP 0xff
+
+#define ECS_EMULATE_SENSE 0xd4
+
+#define EATA_GENERIC_ABORT 0x00
+#define EATA_SPECIFIC_RESET 0x01
+#define EATA_BUS_RESET 0x02
+#define EATA_SPECIFIC_ABORT 0x03
+#define EATA_QUIET_INTR 0x04
+#define EATA_COLD_BOOT_HBA 0x06 /* Only as a last resort */
+#define EATA_FORCE_IO 0x07
+
+#define HA_CTRLREG 0x206 /* control register for HBA */
+#define HA_CTRL_DISINT 0x02 /* CTRLREG: disable interrupts */
+#define HA_CTRL_RESCPU 0x04 /* CTRLREG: reset processor */
+#define HA_CTRL_8HEADS 0x08 /* CTRLREG: set for drives with*
+ * >=8 heads (WD1003 rudimentary :-) */
+
+#define HA_WCOMMAND 0x07 /* command register offset */
+#define HA_WIFC 0x06 /* immediate command offset */
+#define HA_WCODE 0x05
+#define HA_WCODE2 0x04
+#define HA_WDMAADDR 0x02 /* DMA address LSB offset */
+#define HA_RAUXSTAT 0x08 /* aux status register offset*/
+#define HA_RSTATUS 0x07 /* status register offset */
+#define HA_RDATA 0x00 /* data register (16bit) */
+#define HA_WDATA 0x00 /* data register (16bit) */
+
+#define HA_ABUSY 0x01 /* aux busy bit */
+#define HA_AIRQ 0x02 /* aux IRQ pending bit */
+#define HA_SERROR 0x01 /* pr. command ended in error*/
+#define HA_SMORE 0x02 /* more data soon to come */
+#define HA_SCORR 0x04 /* data corrected */
+#define HA_SDRQ 0x08 /* data request active */
+#define HA_SSC 0x10 /* seek complete */
+#define HA_SFAULT 0x20 /* write fault */
+#define HA_SREADY 0x40 /* drive ready */
+#define HA_SBUSY 0x80 /* drive busy */
+#define HA_SDRDY HA_SSC+HA_SREADY+HA_SDRQ
+
+/**********************************************
+ * Message definitions *
+ **********************************************/
+
+#define HA_NO_ERROR 0x00 /* No Error */
+#define HA_ERR_SEL_TO 0x01 /* Selection Timeout */
+#define HA_ERR_CMD_TO 0x02 /* Command Timeout */
+#define HA_BUS_RESET 0x03 /* SCSI Bus Reset Received */
+#define HA_INIT_POWERUP 0x04 /* Initial Controller Power-up */
+#define HA_UNX_BUSPHASE 0x05 /* Unexpected Bus Phase */
+#define HA_UNX_BUS_FREE 0x06 /* Unexpected Bus Free */
+#define HA_BUS_PARITY 0x07 /* Bus Parity Error */
+#define HA_SCSI_HUNG 0x08 /* SCSI Hung */
+#define HA_UNX_MSGRJCT 0x09 /* Unexpected Message Rejected */
+#define HA_RESET_STUCK 0x0a /* SCSI Bus Reset Stuck */
+#define HA_RSENSE_FAIL 0x0b /* Auto Request-Sense Failed */
+#define HA_PARITY_ERR 0x0c /* Controller Ram Parity Error */
+#define HA_CP_ABORT_NA 0x0d /* Abort Message sent to non-active cmd */
+#define HA_CP_ABORTED 0x0e /* Abort Message sent to active cmd */
+#define HA_CP_RESET_NA 0x0f /* Reset Message sent to non-active cmd */
+#define HA_CP_RESET 0x10 /* Reset Message sent to active cmd */
+#define HA_ECC_ERR 0x11 /* Controller Ram ECC Error */
+#define HA_PCI_PARITY 0x12 /* PCI Parity Error */
+#define HA_PCI_MABORT 0x13 /* PCI Master Abort */
+#define HA_PCI_TABORT 0x14 /* PCI Target Abort */
+#define HA_PCI_STABORT 0x15 /* PCI Signaled Target Abort */
+
+/**********************************************
+ * Other definitions *
+ **********************************************/
+
+struct reg_bit { /* reading this one will clear the interrupt */
+ __u8 error:1; /* previous command ended in an error */
+ __u8 more:1; /* more DATA coming soon, poll BSY & DRQ (PIO) */
+ __u8 corr:1; /* data read was successfully corrected with ECC*/
+ __u8 drq:1; /* data request active */
+ __u8 sc:1; /* seek complete */
+ __u8 fault:1; /* write fault */
+ __u8 ready:1; /* drive ready */
+ __u8 busy:1; /* controller busy */
+};
+
+struct reg_abit { /* reading this won't clear the interrupt */
+ __u8 abusy:1; /* auxiliary busy */
+ __u8 irq:1; /* set when drive interrupt is asserted */
+ __u8 dummy:6;
+};
+
+struct eata_register { /* EATA register set */
+ __u8 data_reg[2]; /* R, couldn't figure this one out */
+ __u8 cp_addr[4]; /* W, CP address register */
+ union {
+ __u8 command; /* W, command code: [read|set] conf, send CP*/
+ struct reg_bit status; /* R, see register_bit1 */
+ __u8 statusbyte;
+ } ovr;
+ struct reg_abit aux_stat; /* R, see register_bit2 */
+};
+
+struct get_conf { /* Read Configuration Array */
+ __u32 len; /* Should return 0x22, 0x24, etc */
+ __u32 signature; /* Signature MUST be "EATA" */
+ __u8 version2:4,
+ version:4; /* EATA Version level */
+ __u8 OCS_enabled:1, /* Overlap Command Support enabled */
+ TAR_support:1, /* SCSI Target Mode supported */
+ TRNXFR:1, /* Truncate Transfer Cmd not necessary *
+ * Only used in PIO Mode */
+ MORE_support:1, /* MORE supported (only PIO Mode) */
+ DMA_support:1, /* DMA supported Driver uses only *
+ * this mode */
+ DMA_valid:1, /* DRQ value in Byte 30 is valid */
+ ATA:1, /* ATA device connected (not supported) */
+ HAA_valid:1; /* Hostadapter Address is valid */
+
+ __u16 cppadlen; /* Number of pad bytes send after CD data *
+ * set to zero for DMA commands */
+ __u8 scsi_id[4]; /* SCSI ID of controller 2-0 Byte 0 res. *
+ * if not, zero is returned */
+ __u32 cplen; /* CP length: number of valid cp bytes */
+ __u32 splen; /* Number of bytes returned after *
+ * Receive SP command */
+ __u16 queuesiz; /* max number of queueable CPs */
+ __u16 dummy;
+ __u16 SGsiz; /* max number of SG table entries */
+ __u8 IRQ:4, /* IRQ used this HA */
+ IRQ_TR:1, /* IRQ Trigger: 0=edge, 1=level */
+ SECOND:1, /* This is a secondary controller */
+ DMA_channel:2; /* DRQ index, DRQ is 2comp of DRQX */
+ __u8 sync; /* device at ID 7 tru 0 is running in *
+ * synchronous mode, this will disappear */
+ __u8 DSBLE:1, /* ISA i/o addressing is disabled */
+ FORCADR:1, /* i/o address has been forced */
+ SG_64K:1,
+ SG_UAE:1,
+ :4;
+ __u8 MAX_ID:5, /* Max number of SCSI target IDs */
+ MAX_CHAN:3; /* Number of SCSI busses on HBA */
+ __u8 MAX_LUN; /* Max number of LUNs */
+ __u8 :3,
+ AUTOTRM:1,
+ M1_inst:1,
+ ID_qest:1, /* Raidnum ID is questionable */
+ is_PCI:1, /* HBA is PCI */
+ is_EISA:1; /* HBA is EISA */
+ __u8 RAIDNUM; /* unique HBA identifier */
+ __u8 unused[474];
+};
+
+struct eata_sg_list
+{
+ __u32 data;
+ __u32 len;
+};
+
+struct eata_ccb { /* Send Command Packet structure */
+
+ __u8 SCSI_Reset:1, /* Cause a SCSI Bus reset on the cmd */
+ HBA_Init:1, /* Cause Controller to reinitialize */
+ Auto_Req_Sen:1, /* Do Auto Request Sense on errors */
+ scatter:1, /* Data Ptr points to a SG Packet */
+ Resrvd:1, /* RFU */
+ Interpret:1, /* Interpret the SCSI cdb of own use */
+ DataOut:1, /* Data Out phase with command */
+ DataIn:1; /* Data In phase with command */
+ __u8 reqlen; /* Request Sense Length *
+ * Valid if Auto_Req_Sen=1 */
+ __u8 unused[3];
+ __u8 FWNEST:1, /* send cmd to phys RAID component */
+ unused2:7;
+ __u8 Phsunit:1, /* physical unit on mirrored pair */
+ I_AT:1, /* inhibit address translation */
+ I_HBA_C:1, /* HBA inhibit caching */
+ unused3:5;
+
+ __u8 cp_id:5, /* SCSI Device ID of target */
+ cp_channel:3; /* SCSI Channel # of HBA */
+ __u8 cp_lun:3,
+ :2,
+ cp_luntar:1, /* CP is for target ROUTINE */
+ cp_dispri:1, /* Grant disconnect privilege */
+ cp_identify:1; /* Always TRUE */
+ __u8 cp_msg1; /* Message bytes 0-3 */
+ __u8 cp_msg2;
+ __u8 cp_msg3;
+ __u8 cp_cdb[12]; /* Command Descriptor Block */
+ __u32 cp_datalen; /* Data Transfer Length *
+ * If scatter=1 len of sg package */
+ void *cp_viraddr; /* address of this ccb */
+ __u32 cp_dataDMA; /* Data Address, if scatter=1 *
+ * address of scatter packet */
+ __u32 cp_statDMA; /* address for Status Packet */
+ __u32 cp_reqDMA; /* Request Sense Address, used if *
+ * CP command ends with error */
+ /* Additional CP info begins here */
+ __u32 timestamp; /* Needed to measure command latency */
+ __u32 timeout;
+ __u8 sizeindex;
+ __u8 rw_latency;
+ __u8 retries;
+ __u8 status; /* status of this queueslot */
+ Scsi_Cmnd *cmd; /* address of cmd */
+ struct eata_sg_list *sg_list;
+};
+
+
+struct eata_sp {
+ __u8 hba_stat:7, /* HBA status */
+ EOC:1; /* True if command finished */
+ __u8 scsi_stat; /* Target SCSI status */
+ __u8 reserved[2];
+ __u32 residue_len; /* Number of bytes not transferred */
+ struct eata_ccb *ccb; /* Address set in COMMAND PACKET */
+ __u8 msg[12];
+};
+
+typedef struct hstd {
+ __u8 vendor[9];
+ __u8 name[18];
+ __u8 revision[6];
+ __u8 EATA_revision;
+ __u32 firmware_revision;
+ __u8 HBA_number;
+ __u8 bustype; /* bustype of HBA */
+ __u8 channel; /* # of avail. scsi channels */
+ __u8 state; /* state of HBA */
+ __u8 primary; /* true if primary */
+ __u8 more_support:1, /* HBA supports MORE flag */
+ immediate_support:1, /* HBA supports IMMEDIATE CMDs*/
+ broken_INQUIRY:1; /* This is an EISA HBA with *
+ * broken INQUIRY */
+ __u8 do_latency; /* Latency measurement flag */
+ __u32 reads[13];
+ __u32 writes[13];
+ __u32 reads_lat[12][4];
+ __u32 writes_lat[12][4];
+ __u32 all_lat[4];
+ __u8 resetlevel[MAXCHANNEL];
+ __u32 last_ccb; /* Last used ccb */
+ __u32 cplen; /* size of CP in words */
+ __u16 cppadlen; /* pad length of cp in words */
+ __u16 queuesize;
+ __u16 sgsize; /* # of entries in the SG list*/
+ __u16 devflags; /* bits set for detected devices */
+ __u8 hostid; /* SCSI ID of HBA */
+ __u8 moresupport; /* HBA supports MORE flag */
+ struct Scsi_Host *next;
+ struct Scsi_Host *prev;
+ struct eata_sp sp; /* status packet */
+ struct eata_ccb ccb[0]; /* ccb array begins here */
+}hostdata;
+
+/* structure for max. 2 emulated drives */
+struct drive_geom_emul {
+ __u8 trans; /* translation flag 1=transl */
+ __u8 channel; /* SCSI channel number */
+ __u8 HBA; /* HBA number (prim/sec) */
+ __u8 id; /* drive id */
+ __u8 lun; /* drive lun */
+ __u32 heads; /* number of heads */
+ __u32 sectors; /* number of sectors */
+ __u32 cylinder; /* number of cylinders */
+};
+
+struct geom_emul {
+ __u8 bios_drives; /* number of emulated drives */
+ struct drive_geom_emul drv[2]; /* drive structures */
+};
+
+#endif /* _EATA_GENERIC_H */
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/eata_pio.c b/linux/src/drivers/scsi/eata_pio.c
new file mode 100644
index 00000000..90dc81f3
--- /dev/null
+++ b/linux/src/drivers/scsi/eata_pio.c
@@ -0,0 +1,1042 @@
+/************************************************************
+ * *
+ * Linux EATA SCSI PIO driver *
+ * *
+ * based on the CAM document CAM/89-004 rev. 2.0c, *
+ * DPT's driver kit, some internal documents and source, *
+ * and several other Linux scsi drivers and kernel docs. *
+ * *
+ * The driver currently: *
+ * -supports all EATA-PIO boards *
+ * -only supports DASD devices *
+ * *
+ * (c)1993-96 Michael Neuffer, Alfred Arnold *
+ * neuffer@goofy.zdv.uni-mainz.de *
+ * a.arnold@kfa-juelich.de *
+ * *
+ * This program is free software; you can redistribute it *
+ * and/or modify it under the terms of the GNU General *
+ * Public License as published by the Free Software *
+ * Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be *
+ * useful, but WITHOUT ANY WARRANTY; without even the *
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A *
+ * PARTICULAR PURPOSE. See the GNU General Public License *
+ * for more details. *
+ * *
+ * You should have received a copy of the GNU General *
+ * Public License along with this kernel; if not, write to *
+ * the Free Software Foundation, Inc., 675 Mass Ave, *
+ * Cambridge, MA 02139, USA. *
+ * *
+ ************************************************************
+ * last change: 96/07/16 OS: Linux 2.0.8 *
+ ************************************************************/
+
+/* Look in eata_pio.h for configuration information */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <asm/io.h>
+#include "eata_pio.h"
+#include "eata_dma_proc.h"
+#include "scsi.h"
+#include "sd.h"
+
+#include <linux/stat.h>
+#include <linux/config.h> /* for CONFIG_PCI */
+
+struct proc_dir_entry proc_scsi_eata_pio = {
+ PROC_SCSI_EATA_PIO, 9, "eata_pio",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+static uint ISAbases[MAXISA] =
+{0x1F0, 0x170, 0x330, 0x230};
+static uint ISAirqs[MAXISA] =
+{14,12,15,11};
+static unchar EISAbases[] =
+{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+static uint registered_HBAs = 0;
+static struct Scsi_Host *last_HBA = NULL;
+static struct Scsi_Host *first_HBA = NULL;
+static unchar reg_IRQ[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static unchar reg_IRQL[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static ulong int_counter = 0;
+static ulong queue_counter = 0;
+
+#include "eata_pio_proc.c"
+
+#ifdef MODULE
+int eata_pio_release(struct Scsi_Host *sh)
+{
+ if (sh->irq && reg_IRQ[sh->irq] == 1) free_irq(sh->irq, NULL);
+ else reg_IRQ[sh->irq]--;
+ if (SD(sh)->channel == 0) {
+ if (sh->io_port && sh->n_io_port)
+ release_region(sh->io_port, sh->n_io_port);
+ }
+ return(TRUE);
+}
+#endif
+
+void IncStat(Scsi_Pointer *SCp, uint Increment)
+{
+ SCp->ptr+=Increment;
+ if ((SCp->this_residual-=Increment)==0)
+ {
+ if ((--SCp->buffers_residual)==0) SCp->Status=FALSE;
+ else
+ {
+ SCp->buffer++;
+ SCp->ptr=SCp->buffer->address;
+ SCp->this_residual=SCp->buffer->length;
+ }
+ }
+}
+
+void eata_pio_int_handler(int irq, void *dev_id, struct pt_regs * regs)
+{
+ uint eata_stat = 0xfffff;
+ Scsi_Cmnd *cmd;
+ hostdata *hd;
+ struct eata_ccb *cp;
+ uint base;
+ ulong flags;
+ uint x,z;
+ struct Scsi_Host *sh;
+ ushort zwickel=0;
+ unchar stat,odd;
+
+ save_flags(flags);
+ cli();
+
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->prev) {
+ if (sh->irq != irq)
+ continue;
+ if (inb((uint)sh->base + HA_RSTATUS) & HA_SBUSY)
+ continue;
+
+ int_counter++;
+
+ hd=SD(sh);
+
+ cp = &hd->ccb[0];
+ cmd = cp->cmd;
+ base = (uint) cmd->host->base;
+
+ do
+ {
+ stat=inb(base+HA_RSTATUS);
+ if (stat&HA_SDRQ)
+ if (cp->DataIn)
+ {
+ z=256; odd=FALSE;
+ while ((cmd->SCp.Status)&&((z>0)||(odd)))
+ {
+ if (odd)
+ {
+ *(cmd->SCp.ptr)=zwickel>>8;
+ IncStat(&cmd->SCp,1);
+ odd=FALSE;
+ }
+ x=min(z,cmd->SCp.this_residual/2);
+ insw(base+HA_RDATA,cmd->SCp.ptr,x);
+ z-=x;
+ IncStat(&cmd->SCp,2*x);
+ if ((z>0)&&(cmd->SCp.this_residual==1))
+ {
+ zwickel=inw(base+HA_RDATA);
+ *(cmd->SCp.ptr)=zwickel&0xff;
+ IncStat(&cmd->SCp,1); z--;
+ odd=TRUE;
+ }
+ }
+ while (z>0) {
+ zwickel=inw(base+HA_RDATA);
+ z--;
+ }
+ }
+ else /* cp->DataOut */
+ {
+ odd=FALSE; z=256;
+ while ((cmd->SCp.Status)&&((z>0)||(odd)))
+ {
+ if (odd)
+ {
+ zwickel+=*(cmd->SCp.ptr)<<8;
+ IncStat(&cmd->SCp,1);
+ outw(zwickel,base+HA_RDATA);
+ z--;
+ odd=FALSE;
+ }
+ x=min(z,cmd->SCp.this_residual/2);
+ outsw(base+HA_RDATA,cmd->SCp.ptr,x);
+ z-=x;
+ IncStat(&cmd->SCp,2*x);
+ if ((z>0)&&(cmd->SCp.this_residual==1))
+ {
+ zwickel=*(cmd->SCp.ptr);
+ zwickel&=0xff;
+ IncStat(&cmd->SCp,1);
+ odd=TRUE;
+ }
+ }
+ while (z>0||odd) {
+ outw(zwickel,base+HA_RDATA);
+ z--;
+ odd=FALSE;
+ }
+ }
+ }
+ while ((stat&HA_SDRQ)||((stat&HA_SMORE)&&hd->moresupport));
+
+ /* terminate handler if HBA goes busy again, i.e. transfers
+ * more data */
+
+ if (stat&HA_SBUSY) break;
+
+ /* OK, this is quite stupid, but I haven't found any correct
+ * way to get HBA&SCSI status so far */
+
+ if (!(inb(base+HA_RSTATUS)&HA_SERROR))
+ {
+ cmd->result=(DID_OK<<16);
+ hd->devflags|=(1<<cp->cp_id);
+ }
+ else if (hd->devflags&1<<cp->cp_id)
+ cmd->result=(DID_OK<<16)+0x02;
+ else cmd->result=(DID_NO_CONNECT<<16);
+
+ if (cp->status == LOCKED) {
+ cp->status = FREE;
+ eata_stat = inb(base + HA_RSTATUS);
+ printk(KERN_NOTICE "eata_pio: int_handler, freeing locked "
+ "queueslot\n");
+ DBG(DBG_INTR&&DBG_DELAY,DELAY(1));
+ restore_flags(flags);
+ return;
+ }
+
+#if DBG_INTR2
+ if (stat != 0x50)
+ printk(KERN_DEBUG "stat: %#.2x, result: %#.8x\n", stat,
+ cmd->result);
+ DBG(DBG_INTR&&DBG_DELAY,DELAY(1));
+#endif
+
+ cp->status = FREE; /* now we can release the slot */
+
+ restore_flags(flags);
+ cmd->scsi_done(cmd);
+ save_flags(flags);
+ cli();
+ }
+ restore_flags(flags);
+
+ return;
+}
+
+inline uint eata_pio_send_command(uint base, unchar command)
+{
+ uint loop = HZ/2;
+
+ while (inb(base + HA_RSTATUS) & HA_SBUSY)
+ if (--loop == 0)
+ return(TRUE);
+
+ /* Enable interrupts for HBA. It is not the best way to do it at this
+ * place, but I hope that it doesn't interfere with the IDE driver
+ * initialization this way */
+
+ outb(HA_CTRL_8HEADS,base+HA_CTRLREG);
+
+ outb(command, base + HA_WCOMMAND);
+ return(FALSE);
+}
+
+int eata_pio_queue(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ uint x, y;
+ long flags;
+ uint base;
+
+ hostdata *hd;
+ struct Scsi_Host *sh;
+ struct eata_ccb *cp;
+
+ save_flags(flags);
+ cli();
+
+ queue_counter++;
+
+ hd = HD(cmd);
+ sh = cmd->host;
+ base = (uint) sh->base;
+
+ /* use only slot 0, as 2001 can handle only one cmd at a time */
+
+ y = x = 0;
+
+ if (hd->ccb[y].status!=FREE) {
+
+ DBG(DBG_QUEUE, printk(KERN_EMERG "can_queue %d, x %d, y %d\n",
+ sh->can_queue,x,y));
+#if DEBUG_EATA
+ panic(KERN_EMERG "eata_pio: run out of queue slots cmdno:%ld "
+ "intrno: %ld\n", queue_counter, int_counter);
+#else
+ panic(KERN_EMERG "eata_pio: run out of queue slots....\n");
+#endif
+ }
+
+ cp = &hd->ccb[y];
+
+ memset(cp, 0, sizeof(struct eata_ccb));
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+
+ cp->status = USED; /* claim free slot */
+
+ DBG(DBG_QUEUE, printk(KERN_DEBUG "eata_pio_queue pid %ld, target: %x, lun:"
+ " %x, y %d\n", cmd->pid, cmd->target, cmd->lun, y));
+ DBG(DBG_QUEUE && DBG_DELAY, DELAY(1));
+
+ cmd->scsi_done = (void *)done;
+
+ switch (cmd->cmnd[0]) {
+ case CHANGE_DEFINITION: case COMPARE: case COPY:
+ case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
+ case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case WRITE_6: case WRITE_10: case WRITE_VERIFY:
+ case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME:
+ case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12:
+ case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW:
+ case MEDIUM_SCAN: case SEND_VOLUME_TAG:
+ case 0xea: /* alternate number for WRITE LONG */
+ cp->DataOut = TRUE; /* Output mode */
+ break;
+ case TEST_UNIT_READY:
+ default:
+ cp->DataIn = TRUE; /* Input mode */
+ }
+
+ cp->Interpret = (cmd->target == hd->hostid);
+ cp->cp_datalen = htonl((ulong)cmd->request_bufflen);
+ cp->Auto_Req_Sen = FALSE;
+ cp->cp_reqDMA = htonl(0);
+ cp->reqlen = 0;
+
+ cp->cp_id = cmd->target;
+ cp->cp_lun = cmd->lun;
+ cp->cp_dispri = FALSE;
+ cp->cp_identify = TRUE;
+ memcpy(cp->cp_cdb, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd));
+
+ cp->cp_statDMA = htonl(0);
+
+ cp->cp_viraddr = cp;
+ cp->cmd = cmd;
+ cmd->host_scribble = (char *)&hd->ccb[y];
+
+ if (cmd->use_sg == 0)
+ {
+ cmd->SCp.buffers_residual=1;
+ cmd->SCp.ptr = cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ cmd->SCp.buffer = NULL;
+ } else {
+ cmd->SCp.buffer = cmd->request_buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg;
+ cmd->SCp.ptr = cmd->SCp.buffer->address;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ }
+ cmd->SCp.Status = (cmd->SCp.this_residual != 0); /* TRUE as long as bytes
+ * are to transfer */
+
+ if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP))
+ {
+ cmd->result = DID_BUS_BUSY << 16;
+ printk(KERN_NOTICE "eata_pio_queue target %d, pid %ld, HBA busy, "
+ "returning DID_BUS_BUSY, done.\n", cmd->target, cmd->pid);
+ done(cmd);
+ cp->status = FREE;
+ restore_flags(flags);
+ return (0);
+ }
+ while (!(inb(base + HA_RSTATUS) & HA_SDRQ));
+ outsw(base + HA_RDATA, cp, hd->cplen);
+ outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND);
+ for (x = 0; x < hd->cppadlen; x++) outw(0, base + HA_RDATA);
+
+ DBG(DBG_QUEUE,printk(KERN_DEBUG "Queued base %#.4lx pid: %ld target: %x "
+ "lun: %x slot %d irq %d\n", (long)sh->base, cmd->pid,
+ cmd->target, cmd->lun, y, sh->irq));
+ DBG(DBG_QUEUE && DBG_DELAY, DELAY(1));
+
+ restore_flags(flags);
+ return (0);
+}
+
+int eata_pio_abort(Scsi_Cmnd * cmd)
+{
+ ulong flags;
+ uint loop = HZ;
+
+ save_flags(flags);
+ cli();
+
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_abort called pid: %ld "
+ "target: %x lun: %x reason %x\n", cmd->pid,
+ cmd->target, cmd->lun, cmd->abort_reason));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+
+
+ while (inb((uint)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY)
+ if (--loop == 0) {
+ printk(KERN_WARNING "eata_pio: abort, timeout error.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_ABORT_ERROR);
+ }
+ if (CD(cmd)->status == FREE) {
+ DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_NOT_RUNNING\n"));
+ restore_flags(flags);
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ if (CD(cmd)->status == USED) {
+ DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_BUSY\n"));
+ restore_flags(flags);
+ return (SCSI_ABORT_BUSY); /* SNOOZE */
+ }
+ if (CD(cmd)->status == RESET) {
+ restore_flags(flags);
+ printk(KERN_WARNING "eata_pio: abort, command reset error.\n");
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_ABORT_ERROR);
+ }
+ if (CD(cmd)->status == LOCKED) {
+ restore_flags(flags);
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio: abort, queue slot "
+ "locked.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ restore_flags(flags);
+ panic("eata_pio: abort: invalid slot status\n");
+}
+
+int eata_pio_reset(Scsi_Cmnd * cmd, unsigned int dummy)
+{
+ uint x, time, limit = 0;
+ ulong flags;
+ unchar success = FALSE;
+ Scsi_Cmnd *sp;
+
+ save_flags(flags);
+ cli();
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset called pid:%ld target:"
+ " %x lun: %x reason %x\n", cmd->pid, cmd->target,
+ cmd->lun, cmd->abort_reason));
+
+ if (HD(cmd)->state == RESET) {
+ printk(KERN_WARNING "eata_pio_reset: exit, already in reset.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_RESET_ERROR);
+ }
+
+ /* force all slots to be free */
+
+ for (x = 0; x < cmd->host->can_queue; x++) {
+
+ if (HD(cmd)->ccb[x].status == FREE)
+ continue;
+
+ sp = HD(cmd)->ccb[x].cmd;
+ HD(cmd)->ccb[x].status = RESET;
+ printk(KERN_WARNING "eata_pio_reset: slot %d in reset, pid %ld.\n", x,
+ sp->pid);
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+
+ if (sp == NULL)
+ panic("eata_pio_reset: slot %d, sp==NULL.\n", x);
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ }
+
+ /* hard reset the HBA */
+ outb(EATA_CMD_RESET, (uint) cmd->host->base+HA_WCOMMAND);
+
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: board reset done.\n"));
+ HD(cmd)->state = RESET;
+
+ time = jiffies;
+ while (jiffies < (time + (3 * HZ)) && limit++ < 10000000);
+
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: interrupts disabled, "
+ "loops %d.\n", limit));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+
+ for (x = 0; x < cmd->host->can_queue; x++) {
+
+ /* Skip slots already set free by interrupt */
+ if (HD(cmd)->ccb[x].status != RESET)
+ continue;
+
+ sp = HD(cmd)->ccb[x].cmd;
+ sp->result = DID_RESET << 16;
+
+ /* This mailbox is terminated */
+ printk(KERN_WARNING "eata_pio_reset: reset ccb %d.\n",x);
+ HD(cmd)->ccb[x].status = FREE;
+
+ restore_flags(flags);
+ sp->scsi_done(sp);
+ cli();
+ }
+
+ HD(cmd)->state = FALSE;
+ restore_flags(flags);
+
+ if (success) { /* hmmm... */
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, success.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_RESET_SUCCESS);
+ } else {
+ DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, wakeup.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+ return (SCSI_RESET_PUNT);
+ }
+}
+
+char * get_pio_board_data(ulong base, uint irq, uint id, ulong cplen, ushort cppadlen)
+{
+ struct eata_ccb cp;
+ static char buff[256];
+ int z;
+
+ memset(&cp, 0, sizeof(struct eata_ccb));
+ memset(buff, 0, sizeof(buff));
+
+ cp.DataIn = TRUE;
+ cp.Interpret = TRUE; /* Interpret command */
+
+ cp.cp_datalen = htonl(254);
+ cp.cp_dataDMA = htonl(0);
+
+ cp.cp_id = id;
+ cp.cp_lun = 0;
+
+ cp.cp_cdb[0] = INQUIRY;
+ cp.cp_cdb[1] = 0;
+ cp.cp_cdb[2] = 0;
+ cp.cp_cdb[3] = 0;
+ cp.cp_cdb[4] = 254;
+ cp.cp_cdb[5] = 0;
+
+ if (eata_pio_send_command((uint) base, EATA_CMD_PIO_SEND_CP))
+ return (NULL);
+ while (!(inb(base + HA_RSTATUS) & HA_SDRQ));
+ outsw(base + HA_RDATA, &cp, cplen);
+ outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND);
+ for (z = 0; z < cppadlen; z++) outw(0, base + HA_RDATA);
+
+ while (inb(base + HA_RSTATUS) & HA_SBUSY);
+ if (inb(base + HA_RSTATUS) & HA_SERROR)
+ return (NULL);
+ else if (!(inb(base + HA_RSTATUS) & HA_SDRQ))
+ return (NULL);
+ else
+ {
+ insw(base+HA_RDATA, &buff, 127);
+ while (inb(base + HA_RSTATUS)&HA_SDRQ) inw(base + HA_RDATA);
+ return (buff);
+ }
+}
+
+int get_pio_conf_PIO(u32 base, struct get_conf *buf)
+{
+ ulong loop = HZ/2;
+ int z;
+ ushort *p;
+
+ if(check_region(base, 9))
+ return (FALSE);
+
+ memset(buf, 0, sizeof(struct get_conf));
+
+ while (inb(base + HA_RSTATUS) & HA_SBUSY)
+ if (--loop == 0)
+ return (FALSE);
+
+ DBG(DBG_PIO && DBG_PROBE,
+ printk(KERN_DEBUG "Issuing PIO READ CONFIG to HBA at %#x\n", base));
+ eata_pio_send_command(base, EATA_CMD_PIO_READ_CONFIG);
+
+ loop = HZ/2;
+ for (p = (ushort *) buf;
+ (long)p <= ((long)buf + (sizeof(struct get_conf) / 2)); p++) {
+ while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
+ if (--loop == 0)
+ return (FALSE);
+
+ loop = HZ/2;
+ *p = inw(base + HA_RDATA);
+ }
+ if (!(inb(base + HA_RSTATUS) & HA_SERROR)) { /* Error ? */
+ if (htonl(EATA_SIGNATURE) == buf->signature) {
+ DBG(DBG_PIO&&DBG_PROBE, printk(KERN_NOTICE "EATA Controller found "
+ "at %#4x EATA Level: %x\n", base,
+ (uint) (buf->version)));
+
+ while (inb(base + HA_RSTATUS) & HA_SDRQ)
+ inw(base + HA_RDATA);
+ if(ALLOW_DMA_BOARDS == FALSE) {
+ for (z = 0; z < MAXISA; z++)
+ if (base == ISAbases[z]) {
+ buf->IRQ = ISAirqs[z];
+ break;
+ }
+ }
+ return (TRUE);
+ }
+ } else {
+ DBG(DBG_PROBE, printk("eata_dma: get_conf_PIO, error during transfer "
+ "for HBA at %x\n", base));
+ }
+ return (FALSE);
+}
+
+void print_pio_config(struct get_conf *gc)
+{
+ printk("Please check values: (read config data)\n");
+ printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d\n",
+ (uint) ntohl(gc->len), gc->version,
+ gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support);
+ printk("HAAV:%d SCSIID0:%d ID1:%d ID2:%d QUEUE:%d SG:%d SEC:%d\n",
+ gc->HAA_valid, gc->scsi_id[3], gc->scsi_id[2],
+ gc->scsi_id[1], ntohs(gc->queuesiz), ntohs(gc->SGsiz), gc->SECOND);
+ printk("IRQ:%d IRQT:%d FORCADR:%d MCH:%d RIDQ:%d\n",
+ gc->IRQ, gc->IRQ_TR, gc->FORCADR,
+ gc->MAX_CHAN, gc->ID_qest);
+ DBG(DPT_DEBUG, DELAY(14));
+}
+
+static uint print_selftest(uint base)
+{
+ unchar buffer[512];
+#ifdef VERBOSE_SETUP
+ int z;
+#endif
+
+ printk("eata_pio: executing controller self test & setup...\n");
+ while (inb(base + HA_RSTATUS) & HA_SBUSY);
+ outb(EATA_CMD_PIO_SETUPTEST, base + HA_WCOMMAND);
+ do {
+ while (inb(base + HA_RSTATUS) & HA_SBUSY)
+ /* nothing */ ;
+ if (inb(base + HA_RSTATUS) & HA_SDRQ)
+ {
+ insw(base + HA_RDATA, &buffer, 256);
+#ifdef VERBOSE_SETUP
+ /* no beeps please... */
+ for (z = 0; z < 511 && buffer[z]; z++)
+ if (buffer[z] != 7) printk("%c", buffer[z]);
+#endif
+ }
+ } while (inb(base+HA_RSTATUS) & (HA_SBUSY|HA_SDRQ));
+
+ return (!(inb(base+HA_RSTATUS) & HA_SERROR));
+}
+
+int register_pio_HBA(long base, struct get_conf *gc, Scsi_Host_Template * tpnt)
+{
+ ulong size = 0;
+ char *buff;
+ ulong cplen;
+ ushort cppadlen;
+ struct Scsi_Host *sh;
+ hostdata *hd;
+
+ DBG(DBG_REGISTER, print_pio_config(gc));
+
+ if (gc->DMA_support == TRUE) {
+ printk("HBA at %#.4lx supports DMA. Please use EATA-DMA driver.\n",base);
+ if(ALLOW_DMA_BOARDS == FALSE)
+ return (FALSE);
+ }
+
+ if ((buff = get_pio_board_data((uint)base, gc->IRQ, gc->scsi_id[3],
+ cplen =(htonl(gc->cplen )+1)/2,
+ cppadlen=(htons(gc->cppadlen)+1)/2)) == NULL)
+ {
+ printk("HBA at %#lx didn't react on INQUIRY. Sorry.\n", (ulong) base);
+ return (FALSE);
+ }
+
+ if (print_selftest(base) == FALSE && ALLOW_DMA_BOARDS == FALSE)
+ {
+ printk("HBA at %#lx failed while performing self test & setup.\n",
+ (ulong) base);
+ return (FALSE);
+ }
+
+ if (!reg_IRQ[gc->IRQ]) { /* Interrupt already registered ? */
+ if (!request_irq(gc->IRQ, eata_pio_int_handler, SA_INTERRUPT,
+ "EATA-PIO", NULL)){
+ reg_IRQ[gc->IRQ]++;
+ if (!gc->IRQ_TR)
+ reg_IRQL[gc->IRQ] = TRUE; /* IRQ is edge triggered */
+ } else {
+ printk("Couldn't allocate IRQ %d, Sorry.\n", gc->IRQ);
+ return (FALSE);
+ }
+ } else { /* More than one HBA on this IRQ */
+ if (reg_IRQL[gc->IRQ] == TRUE) {
+ printk("Can't support more than one HBA on this IRQ,\n"
+ " if the IRQ is edge triggered. Sorry.\n");
+ return (FALSE);
+ } else
+ reg_IRQ[gc->IRQ]++;
+ }
+
+ request_region(base, 8, "eata_pio");
+
+ size = sizeof(hostdata) + (sizeof(struct eata_ccb) * ntohs(gc->queuesiz));
+
+ sh = scsi_register(tpnt, size);
+ hd = SD(sh);
+
+ memset(hd->ccb, 0, (sizeof(struct eata_ccb) * ntohs(gc->queuesiz)));
+ memset(hd->reads, 0, sizeof(ulong) * 26);
+
+ strncpy(SD(sh)->vendor, &buff[8], 8);
+ SD(sh)->vendor[8] = 0;
+ strncpy(SD(sh)->name, &buff[16], 17);
+ SD(sh)->name[17] = 0;
+ SD(sh)->revision[0] = buff[32];
+ SD(sh)->revision[1] = buff[33];
+ SD(sh)->revision[2] = buff[34];
+ SD(sh)->revision[3] = '.';
+ SD(sh)->revision[4] = buff[35];
+ SD(sh)->revision[5] = 0;
+
+ switch (ntohl(gc->len)) {
+ case 0x1c:
+ SD(sh)->EATA_revision = 'a';
+ break;
+ case 0x1e:
+ SD(sh)->EATA_revision = 'b';
+ break;
+ case 0x22:
+ SD(sh)->EATA_revision = 'c';
+ break;
+ case 0x24:
+ SD(sh)->EATA_revision = 'z';
+ default:
+ SD(sh)->EATA_revision = '?';
+ }
+
+ if(ntohl(gc->len) >= 0x22) {
+ if (gc->is_PCI == TRUE)
+ hd->bustype = IS_PCI;
+ else if (gc->is_EISA == TRUE)
+ hd->bustype = IS_EISA;
+ else
+ hd->bustype = IS_ISA;
+ } else {
+ if (buff[21] == '4')
+ hd->bustype = IS_PCI;
+ else if (buff[21] == '2')
+ hd->bustype = IS_EISA;
+ else
+ hd->bustype = IS_ISA;
+ }
+
+ SD(sh)->cplen=cplen;
+ SD(sh)->cppadlen=cppadlen;
+ SD(sh)->hostid=gc->scsi_id[3];
+ SD(sh)->devflags=1<<gc->scsi_id[3];
+ SD(sh)->moresupport=gc->MORE_support;
+ sh->unique_id = base;
+ sh->base = (char *) base;
+ sh->io_port = base;
+ sh->n_io_port = 8;
+ sh->irq = gc->IRQ;
+ sh->dma_channel = PIO;
+ sh->this_id = gc->scsi_id[3];
+ sh->can_queue = 1;
+ sh->cmd_per_lun = 1;
+ sh->sg_tablesize = SG_ALL;
+
+ hd->channel = 0;
+
+ sh->max_id = 8;
+ sh->max_lun = 8;
+
+ if (gc->SECOND)
+ hd->primary = FALSE;
+ else
+ hd->primary = TRUE;
+
+ sh->unchecked_isa_dma = FALSE; /* We can only do PIO */
+
+ hd->next = NULL; /* build a linked list of all HBAs */
+ hd->prev = last_HBA;
+ if(hd->prev != NULL)
+ SD(hd->prev)->next = sh;
+ last_HBA = sh;
+ if (first_HBA == NULL)
+ first_HBA = sh;
+ registered_HBAs++;
+ return (1);
+}
+
+void find_pio_ISA(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+ int i;
+
+ for (i = 0; i < MAXISA; i++) {
+ if (ISAbases[i]) {
+ if (get_pio_conf_PIO(ISAbases[i], buf) == TRUE){
+ register_pio_HBA(ISAbases[i], buf, tpnt);
+ }
+ ISAbases[i] = 0;
+ }
+ }
+ return;
+}
+
+void find_pio_EISA(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+ u32 base;
+ int i;
+
+#if CHECKPAL
+ u8 pal1, pal2, pal3;
+#endif
+
+ for (i = 0; i < MAXEISA; i++) {
+ if (EISAbases[i] == TRUE) { /* Still a possibility ? */
+
+ base = 0x1c88 + (i * 0x1000);
+#if CHECKPAL
+ pal1 = inb((u16)base - 8);
+ pal2 = inb((u16)base - 7);
+ pal3 = inb((u16)base - 6);
+
+ if (((pal1 == 0x12) && (pal2 == 0x14)) ||
+ ((pal1 == 0x38) && (pal2 == 0xa3) && (pal3 == 0x82)) ||
+ ((pal1 == 0x06) && (pal2 == 0x94) && (pal3 == 0x24))) {
+ DBG(DBG_PROBE, printk(KERN_NOTICE "EISA EATA id tags found: "
+ "%x %x %x \n",
+ (int)pal1, (int)pal2, (int)pal3));
+#endif
+ if (get_pio_conf_PIO(base, buf) == TRUE) {
+ DBG(DBG_PROBE && DBG_EISA, print_pio_config(buf));
+ if (buf->IRQ) {
+ register_pio_HBA(base, buf, tpnt);
+ } else
+ printk(KERN_NOTICE "eata_dma: No valid IRQ. HBA "
+ "removed from list\n");
+ }
+ /* Nothing found here so we take it from the list */
+ EISAbases[i] = 0;
+#if CHECKPAL
+ }
+#endif
+ }
+ }
+ return;
+}
+
+void find_pio_PCI(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+
+#ifndef CONFIG_PCI
+ printk(KERN_ERR "eata_pio: kernel PCI support not enabled. Skipping scan "
+ "for PCI HBAs.\n");
+#else
+
+ u8 pci_bus, pci_device_fn;
+ static s16 pci_index = 0; /* Device index to PCI BIOS calls */
+ u32 base = 0;
+ u16 com_adr;
+ u16 rev_device;
+ u32 error, i, x;
+
+ if (pcibios_present()) {
+ for (i = 0; i <= MAXPCI; ++i, ++pci_index) {
+ if (pcibios_find_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT,
+ pci_index, &pci_bus, &pci_device_fn))
+ break;
+ DBG(DBG_PROBE && DBG_PCI,
+ printk("eata_pio: HBA at bus %d, device %d,"
+ " function %d, index %d\n", (s32)pci_bus,
+ (s32)((pci_device_fn & 0xf8) >> 3),
+ (s32)(pci_device_fn & 7), pci_index));
+
+ if (!(error = pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_CLASS_DEVICE, &rev_device))) {
+ if (rev_device == PCI_CLASS_STORAGE_SCSI) {
+ if (!(error = pcibios_read_config_word(pci_bus,
+ pci_device_fn, PCI_COMMAND,
+ (u16 *) & com_adr))) {
+ if (!((com_adr & PCI_COMMAND_IO) &&
+ (com_adr & PCI_COMMAND_MASTER))) {
+ printk("HBA has IO or BUSMASTER mode disabled\n");
+ continue;
+ }
+ } else
+ printk("eata_pio: error %x while reading "
+ "PCI_COMMAND\n", error);
+ } else
+ printk("DEVICECLASSID %x didn't match\n", rev_device);
+ } else {
+ printk("eata_pio: error %x while reading PCI_CLASS_BASE\n",
+ error);
+ continue;
+ }
+
+ if (!(error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, (int *) &base))){
+
+ /* Check if the address is valid */
+ if (base & 0x01) {
+ base &= 0xfffffffe;
+ /* EISA tag there ? */
+ if ((inb(base) == 0x12) && (inb(base + 1) == 0x14))
+ continue; /* Jep, it's forced, so move on */
+ base += 0x10; /* Now, THIS is the real address */
+ if (base != 0x1f8) {
+ /* We didn't find it in the primary search */
+ if (get_pio_conf_PIO(base, buf) == TRUE) {
+ if (buf->FORCADR) /* If the address is forced */
+ continue; /* we'll find it later */
+
+ /* OK. We made it till here, so we can go now
+ * and register it. We only have to check and
+ * eventually remove it from the EISA and ISA list
+ */
+
+ register_pio_HBA(base, buf, tpnt);
+
+ if (base < 0x1000) {
+ for (x = 0; x < MAXISA; ++x) {
+ if (ISAbases[x] == base) {
+ ISAbases[x] = 0;
+ break;
+ }
+ }
+ } else if ((base & 0x0fff) == 0x0c88) {
+ x = (base >> 12) & 0x0f;
+ EISAbases[x] = 0;
+ }
+ continue; /* break; */
+ }
+ }
+ }
+ } else
+ printk("eata_pio: error %x while reading "
+ "PCI_BASE_ADDRESS_0\n", error);
+ }
+ } else
+ printk("eata_pio: No BIOS32 extensions present. This driver release "
+ "still depends on it.\n"
+ " Skipping scan for PCI HBAs.\n");
+#endif /* #ifndef CONFIG_PCI */
+ return;
+}
+
+
+int eata_pio_detect(Scsi_Host_Template * tpnt)
+{
+ struct Scsi_Host *HBA_ptr;
+ struct get_conf gc;
+ int i;
+
+ DBG((DBG_PROBE && DBG_DELAY) || DPT_DEBUG,
+ printk("Using lots of delays to let you read the debugging output\n"));
+
+ tpnt->proc_dir = &proc_scsi_eata_pio;
+
+ find_pio_PCI(&gc, tpnt);
+
+ find_pio_EISA(&gc, tpnt);
+
+ find_pio_ISA(&gc, tpnt);
+
+ for (i = 0; i <= MAXIRQ; i++)
+ if (reg_IRQ[i])
+ request_irq(i, eata_pio_int_handler, SA_INTERRUPT, "EATA-PIO", NULL);
+
+ HBA_ptr = first_HBA;
+
+ if (registered_HBAs != 0) {
+ printk("EATA (Extended Attachment) PIO driver version: %d.%d%s\n"
+ "(c) 1993-95 Michael Neuffer, neuffer@goofy.zdv.uni-mainz.de\n"
+ " Alfred Arnold, a.arnold@kfa-juelich.de\n"
+ "This release only supports DASD devices (harddisks)\n",
+ VER_MAJOR, VER_MINOR, VER_SUB);
+
+ printk("Registered HBAs:\n");
+ printk("HBA no. Boardtype: Revis: EATA: Bus: BaseIO: IRQ: Ch: ID: Pr:"
+ " QS: SG: CPL:\n");
+ for (i = 1; i <= registered_HBAs; i++) {
+ printk("scsi%-2d: %.10s v%s 2.0%c %s %#.4x %2d %d %d %c"
+ " %2d %2d %2d\n",
+ HBA_ptr->host_no, SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
+ SD(HBA_ptr)->EATA_revision, (SD(HBA_ptr)->bustype == 'P')?
+ "PCI ":(SD(HBA_ptr)->bustype == 'E')?"EISA":"ISA ",
+ (uint) HBA_ptr->base, HBA_ptr->irq, SD(HBA_ptr)->channel,
+ HBA_ptr->this_id, (SD(HBA_ptr)->primary == TRUE)?'Y':'N',
+ HBA_ptr->can_queue, HBA_ptr->sg_tablesize,
+ HBA_ptr->cmd_per_lun);
+ HBA_ptr = SD(HBA_ptr)->next;
+ }
+ }
+ DBG(DPT_DEBUG,DELAY(12));
+
+ return (registered_HBAs);
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = EATA_PIO;
+
+#include "scsi_module.c"
+#endif
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/eata_pio.h b/linux/src/drivers/scsi/eata_pio.h
new file mode 100644
index 00000000..333f7c4a
--- /dev/null
+++ b/linux/src/drivers/scsi/eata_pio.h
@@ -0,0 +1,116 @@
+/********************************************************
+* Header file for eata_pio.c Linux EATA-PIO SCSI driver *
+* (c) 1993-96 Michael Neuffer *
+*********************************************************
+* last change: 96/05/05 *
+********************************************************/
+
+
+#ifndef _EATA_PIO_H
+#define _EATA_PIO_H
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include <scsi/scsicam.h>
+
+#ifndef HOSTS_C
+#include "eata_generic.h"
+
+#define VER_MAJOR 0
+#define VER_MINOR 0
+#define VER_SUB "1b"
+
+/************************************************************************
+ * Here you can switch parts of the code on and of *
+ ************************************************************************/
+
+#define VERBOSE_SETUP /* show startup screen of 2001 */
+#define ALLOW_DMA_BOARDS 1
+
+/************************************************************************
+ * Debug options. *
+ * Enable DEBUG and whichever options you require. *
+ ************************************************************************/
+#define DEBUG_EATA 1 /* Enable debug code. */
+#define DPT_DEBUG 0 /* Bobs special */
+#define DBG_DELAY 0 /* Build in delays so debug messages can be
+ * be read before they vanish of the top of
+ * the screen!
+ */
+#define DBG_PROBE 0 /* Debug probe routines. */
+#define DBG_ISA 0 /* Trace ISA routines */
+#define DBG_EISA 0 /* Trace EISA routines */
+#define DBG_PCI 0 /* Trace PCI routines */
+#define DBG_PIO 0 /* Trace get_config_PIO */
+#define DBG_COM 0 /* Trace command call */
+#define DBG_QUEUE 0 /* Trace command queueing. */
+#define DBG_INTR 0 /* Trace interrupt service routine. */
+#define DBG_INTR2 0 /* Trace interrupt service routine. */
+#define DBG_PROC 0 /* Debug proc-fs related statistics */
+#define DBG_PROC_WRITE 0
+#define DBG_REGISTER 0 /* */
+#define DBG_ABNORM 1 /* Debug abnormal actions (reset, abort) */
+
+#if DEBUG_EATA
+#define DBG(x, y) if ((x)) {y;}
+#else
+#define DBG(x, y)
+#endif
+
+#endif /* !HOSTS_C */
+
+int eata_pio_detect(Scsi_Host_Template *);
+const char *eata_pio_info(struct Scsi_Host *);
+int eata_pio_command(Scsi_Cmnd *);
+int eata_pio_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int eata_pio_abort(Scsi_Cmnd *);
+int eata_pio_reset(Scsi_Cmnd *, unsigned int);
+int eata_pio_proc_info(char *, char **, off_t, int, int, int);
+#ifdef MODULE
+int eata_pio_release(struct Scsi_Host *);
+#else
+#define eata_pio_release NULL
+#endif
+
+
+#define EATA_PIO { \
+ NULL, NULL, \
+ NULL, /* proc_dir_entry */ \
+ eata_pio_proc_info, /* procinfo */ \
+ "EATA (Extended Attachment) PIO driver", \
+ eata_pio_detect, \
+ eata_pio_release, \
+ NULL, NULL, \
+ eata_pio_queue, \
+ eata_pio_abort, \
+ eata_pio_reset, \
+ NULL, /* Slave attach */ \
+ scsicam_bios_param, \
+ 0, /* Canqueue */ \
+ 0, /* this_id */ \
+ 0, /* sg_tablesize */ \
+ 0, /* cmd_per_lun */ \
+ 0, /* present */ \
+ 1, /* True if ISA */ \
+ ENABLE_CLUSTERING }
+
+#endif /* _EATA_PIO_H */
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/eata_pio_proc.c b/linux/src/drivers/scsi/eata_pio_proc.c
new file mode 100644
index 00000000..54783f2c
--- /dev/null
+++ b/linux/src/drivers/scsi/eata_pio_proc.c
@@ -0,0 +1,135 @@
+
+/*
+ * eata_set_info
+ * buffer : pointer to the data that has been written to the hostfile
+ * length : number of bytes written to the hostfile
+ * HBA_ptr: pointer to the Scsi_Host struct
+ */
+int eata_pio_set_info(char *buffer, int length, struct Scsi_Host *HBA_ptr)
+{
+ DBG(DBG_PROC_WRITE, printk("%s\n", buffer));
+ return(-ENOSYS); /* Currently this is a no-op */
+}
+
+/*
+ * eata_proc_info
+ * inout : decides on the direction of the dataflow and the meaning of the
+ * variables
+ * buffer: If inout==FALSE data is being written to it else read from it
+ * *start: If inout==FALSE start of the valid data in the buffer
+ * offset: If inout==FALSE offset from the beginning of the imaginary file
+ * from which we start writing into the buffer
+ * length: If inout==FALSE max number of bytes to be written into the buffer
+ * else number of bytes in the buffer
+ */
+int eata_pio_proc_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+
+ Scsi_Device *scd;
+ struct Scsi_Host *HBA_ptr;
+ static u8 buff[512];
+ int i;
+ int size, len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+
+ HBA_ptr = first_HBA;
+ for (i = 1; i <= registered_HBAs; i++) {
+ if (HBA_ptr->host_no == hostno)
+ break;
+ HBA_ptr = SD(HBA_ptr)->next;
+ }
+
+ if(inout == TRUE) /* Has data been written to the file ? */
+ return(eata_pio_set_info(buffer, length, HBA_ptr));
+
+ if (offset == 0)
+ memset(buff, 0, sizeof(buff));
+
+ size = sprintf(buffer+len, "EATA (Extended Attachment) PIO driver version: "
+ "%d.%d%s\n",VER_MAJOR, VER_MINOR, VER_SUB);
+ len += size; pos = begin + len;
+ size = sprintf(buffer + len, "queued commands: %10ld\n"
+ "processed interrupts:%10ld\n", queue_counter, int_counter);
+ len += size; pos = begin + len;
+
+ size = sprintf(buffer + len, "\nscsi%-2d: HBA %.10s\n",
+ HBA_ptr->host_no, SD(HBA_ptr)->name);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Firmware revision: v%s\n",
+ SD(HBA_ptr)->revision);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "IO: PIO\n");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Base IO : %#.4x\n", (u32) HBA_ptr->base);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Host Bus: %s\n",
+ (SD(HBA_ptr)->bustype == 'P')?"PCI ":
+ (SD(HBA_ptr)->bustype == 'E')?"EISA":"ISA ");
+
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ scd = scsi_devices;
+
+ size = sprintf(buffer+len,"Attached devices: %s\n", (scd)?"":"none");
+ len += size;
+ pos = begin + len;
+
+ while (scd) {
+ if (scd->host == HBA_ptr) {
+ proc_print_scsidevice(scd, buffer, &size, len);
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+ scd = scd->next;
+ }
+
+ stop_output:
+ DBG(DBG_PROC, printk("2pos: %ld offset: %ld len: %d\n", pos, offset, len));
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len = length; /* Ending slop */
+ DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len));
+
+ return (len);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * tab-width: 8
+ * End:
+ */
+
diff --git a/linux/src/drivers/scsi/fdomain.c b/linux/src/drivers/scsi/fdomain.c
new file mode 100644
index 00000000..df71f5ce
--- /dev/null
+++ b/linux/src/drivers/scsi/fdomain.c
@@ -0,0 +1,2082 @@
+/* fdomain.c -- Future Domain TMC-16x0 SCSI driver
+ * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu
+ * Revised: Sat Nov 2 09:27:47 1996 by root@cs.unc.edu
+ * Author: Rickard E. Faith, faith@cs.unc.edu
+ * Copyright 1992, 1993, 1994, 1995, 1996 Rickard E. Faith
+ *
+ * $Id: fdomain.c,v 1.1 1999/04/26 05:54:32 tb Exp $
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ **************************************************************************
+
+ SUMMARY:
+
+ Future Domain BIOS versions supported for autodetect:
+ 2.0, 3.0, 3.2, 3.4 (1.0), 3.5 (2.0), 3.6, 3.61
+ Chips are supported:
+ TMC-1800, TMC-18C50, TMC-18C30, TMC-36C70
+ Boards supported:
+ Future Domain TMC-1650, TMC-1660, TMC-1670, TMC-1680, TMC-1610M/MER/MEX
+ Future Domain TMC-3260 (PCI)
+ Quantum ISA-200S, ISA-250MG
+ Adaptec AHA-2920 (PCI)
+ IBM ?
+ LILO command-line options:
+ fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]
+
+
+
+ DESCRIPTION:
+
+ This is the Linux low-level SCSI driver for Future Domain TMC-1660/1680
+ TMC-1650/1670, and TMC-3260 SCSI host adapters. The 1650 and 1670 have a
+ 25-pin external connector, whereas the 1660 and 1680 have a SCSI-2 50-pin
+ high-density external connector. The 1670 and 1680 have floppy disk
+ controllers built in. The TMC-3260 is a PCI bus card.
+
+ Future Domain's older boards are based on the TMC-1800 chip, and this
+ driver was originally written for a TMC-1680 board with the TMC-1800 chip.
+ More recently, boards are being produced with the TMC-18C50 and TMC-18C30
+ chips. The latest and greatest board may not work with this driver. If
+ you have to patch this driver so that it will recognize your board's BIOS
+ signature, then the driver may fail to function after the board is
+ detected.
+
+ Please note that the drive ordering that Future Domain implemented in BIOS
+ versions 3.4 and 3.5 is the opposite of the order (currently) used by the
+ rest of the SCSI industry. If you have BIOS version 3.4 or 3.5, and have
+ more then one drive, then the drive ordering will be the reverse of that
+ which you see under DOS. For example, under DOS SCSI ID 0 will be D: and
+ SCSI ID 1 will be C: (the boot device). Under Linux, SCSI ID 0 will be
+ /dev/sda and SCSI ID 1 will be /dev/sdb. The Linux ordering is consistent
+ with that provided by all the other SCSI drivers for Linux. If you want
+ this changed, you will probably have to patch the higher level SCSI code.
+ If you do so, please send me patches that are protected by #ifdefs.
+
+ If you have a TMC-8xx or TMC-9xx board, then this is not the driver for
+ your board. Please refer to the Seagate driver for more information and
+ possible support.
+
+
+
+ HISTORY:
+
+ Linux Driver Driver
+ Version Version Date Support/Notes
+
+ 0.0 3 May 1992 V2.0 BIOS; 1800 chip
+ 0.97 1.9 28 Jul 1992
+ 0.98.6 3.1 27 Nov 1992
+ 0.99 3.2 9 Dec 1992
+
+ 0.99.3 3.3 10 Jan 1993 V3.0 BIOS
+ 0.99.5 3.5 18 Feb 1993
+ 0.99.10 3.6 15 May 1993 V3.2 BIOS; 18C50 chip
+ 0.99.11 3.17 3 Jul 1993 (now under RCS)
+ 0.99.12 3.18 13 Aug 1993
+ 0.99.14 5.6 31 Oct 1993 (reselection code removed)
+
+ 0.99.15 5.9 23 Jan 1994 V3.4 BIOS (preliminary)
+ 1.0.8/1.1.1 5.15 1 Apr 1994 V3.4 BIOS; 18C30 chip (preliminary)
+ 1.0.9/1.1.3 5.16 7 Apr 1994 V3.4 BIOS; 18C30 chip
+ 1.1.38 5.18 30 Jul 1994 36C70 chip (PCI version of 18C30)
+ 1.1.62 5.20 2 Nov 1994 V3.5 BIOS
+ 1.1.73 5.22 7 Dec 1994 Quantum ISA-200S board; V2.0 BIOS
+
+ 1.1.82 5.26 14 Jan 1995 V3.5 BIOS; TMC-1610M/MER/MEX board
+ 1.2.10 5.28 5 Jun 1995 Quantum ISA-250MG board; V2.0, V2.01 BIOS
+ 1.3.4 5.31 23 Jun 1995 PCI BIOS-32 detection (preliminary)
+ 1.3.7 5.33 4 Jul 1995 PCI BIOS-32 detection
+ 1.3.28 5.36 17 Sep 1995 V3.61 BIOS; LILO command-line support
+ 1.3.34 5.39 12 Oct 1995 V3.60 BIOS; /proc
+ 1.3.72 5.39 8 Feb 1996 Adaptec AHA-2920 board
+ 1.3.85 5.41 4 Apr 1996
+ 2.0.12 5.44 8 Aug 1996 Use ID 7 for all PCI cards
+
+
+
+ REFERENCES USED:
+
+ "TMC-1800 SCSI Chip Specification (FDC-1800T)", Future Domain Corporation,
+ 1990.
+
+ "Technical Reference Manual: 18C50 SCSI Host Adapter Chip", Future Domain
+ Corporation, January 1992.
+
+ "LXT SCSI Products: Specifications and OEM Technical Manual (Revision
+ B/September 1991)", Maxtor Corporation, 1991.
+
+ "7213S product Manual (Revision P3)", Maxtor Corporation, 1992.
+
+ "Draft Proposed American National Standard: Small Computer System
+ Interface - 2 (SCSI-2)", Global Engineering Documents. (X3T9.2/86-109,
+ revision 10h, October 17, 1991)
+
+ Private communications, Drew Eckhardt (drew@cs.colorado.edu) and Eric
+ Youngdale (ericy@cais.com), 1992.
+
+ Private communication, Tuong Le (Future Domain Engineering department),
+ 1994. (Disk geometry computations for Future Domain BIOS version 3.4, and
+ TMC-18C30 detection.)
+
+ Hogan, Thom. The Programmer's PC Sourcebook. Microsoft Press, 1988. Page
+ 60 (2.39: Disk Partition Table Layout).
+
+ "18C30 Technical Reference Manual", Future Domain Corporation, 1993, page
+ 6-1.
+
+
+
+ NOTES ON REFERENCES:
+
+ The Maxtor manuals were free. Maxtor telephone technical support is
+ great!
+
+ The Future Domain manuals were $25 and $35. They document the chip, not
+ the TMC-16x0 boards, so some information I had to guess at. In 1992,
+ Future Domain sold DOS BIOS source for $250 and the UN*X driver source was
+ $750, but these required a non-disclosure agreement, so even if I could
+ have afforded them, they would *not* have been useful for writing this
+ publically distributable driver. Future Domain technical support has
+ provided some information on the phone and have sent a few useful FAXs.
+ They have been much more helpful since they started to recognize that the
+ word "Linux" refers to an operating system :-).
+
+
+
+ ALPHA TESTERS:
+
+ There are many other alpha testers that come and go as the driver
+ develops. The people listed here were most helpful in times of greatest
+ need (mostly early on -- I've probably left out a few worthy people in
+ more recent times):
+
+ Todd Carrico (todd@wutc.wustl.edu), Dan Poirier (poirier@cs.unc.edu ), Ken
+ Corey (kenc@sol.acs.unt.edu), C. de Bruin (bruin@bruin@sterbbs.nl), Sakari
+ Aaltonen (sakaria@vipunen.hit.fi), John Rice (rice@xanth.cs.odu.edu), Brad
+ Yearwood (brad@optilink.com), and Ray Toy (toy@soho.crd.ge.com).
+
+ Special thanks to Tien-Wan Yang (twyang@cs.uh.edu), who graciously lent me
+ his 18C50-based card for debugging. He is the sole reason that this
+ driver works with the 18C50 chip.
+
+ Thanks to Dave Newman (dnewman@crl.com) for providing initial patches for
+ the version 3.4 BIOS.
+
+ Thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for providing
+ patches that support the TMC-3260, a PCI bus card with the 36C70 chip.
+ The 36C70 chip appears to be "completely compatible" with the 18C30 chip.
+
+ Thanks to Eric Kasten (tigger@petroglyph.cl.msu.edu) for providing the
+ patch for the version 3.5 BIOS.
+
+ Thanks for Stephen Henson (shenson@nyx10.cs.du.edu) for providing the
+ patch for the Quantum ISA-200S SCSI adapter.
+
+ Thanks to Adam Bowen for the signature to the 1610M/MER/MEX scsi cards, to
+ Martin Andrews (andrewm@ccfadm.eeg.ccf.org) for the signature to some
+ random TMC-1680 repackaged by IBM; and to Mintak Ng (mintak@panix.com) for
+ the version 3.61 BIOS signature.
+
+ Thanks for Mark Singer (elf@netcom.com) and Richard Simpson
+ (rsimpson@ewrcsdra.demon.co.uk) for more Quantum signatures and detective
+ work on the Quantum RAM layout.
+
+ Special thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for
+ providing patches for proper PCI BIOS32-mediated detection of the TMC-3260
+ card (a PCI bus card with the 36C70 chip). Please send James PCI-related
+ bug reports.
+
+ Thanks to Tom Cavin (tec@usa1.com) for preliminary command-line option
+ patches.
+
+ All of the alpha testers deserve much thanks.
+
+
+
+ NOTES ON USER DEFINABLE OPTIONS:
+
+ DEBUG: This turns on the printing of various debug information.
+
+ ENABLE_PARITY: This turns on SCSI parity checking. With the current
+ driver, all attached devices must support SCSI parity. If none of your
+ devices support parity, then you can probably get the driver to work by
+ turning this option off. I have no way of testing this, however, and it
+ would appear that no one ever uses this option.
+
+ FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
+ 18C30 chip have a 2k cache). When this many 512 byte blocks are filled by
+ the SCSI device, an interrupt will be raised. Therefore, this could be as
+ low as 0, or as high as 16. Note, however, that values which are too high
+ or too low seem to prevent any interrupts from occurring, and thereby lock
+ up the machine. I have found that 2 is a good number, but throughput may
+ be increased by changing this value to values which are close to 2.
+ Please let me know if you try any different values.
+
+ DO_DETECT: This activates some old scan code which was needed before the
+ high level drivers got fixed. If you are having trouble with the driver,
+ turning this on should not hurt, and might help. Please let me know if
+ this is the case, since this code will be removed from future drivers.
+
+ RESELECTION: This is no longer an option, since I gave up trying to
+ implement it in version 4.x of this driver. It did not improve
+ performance at all and made the driver unstable (because I never found one
+ of the two race conditions which were introduced by the multiple
+ outstanding command code). The instability seems a very high price to pay
+ just so that you don't have to wait for the tape to rewind. If you want
+ this feature implemented, send me patches. I'll be happy to send a copy
+ of my (broken) driver to anyone who would like to see a copy.
+
+ **************************************************************************/
+
+#ifdef PCMCIA
+#define MODULE
+#endif
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#ifdef PCMCIA
+#undef MODULE
+#endif
+
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "fdomain.h"
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/stat.h>
+
+#include <linux/config.h> /* for CONFIG_PCI */
+
+struct proc_dir_entry proc_scsi_fdomain = {
+ PROC_SCSI_FDOMAIN, 7, "fdomain",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#define VERSION "$Revision: 1.1 $"
+
+/* START OF USER DEFINABLE OPTIONS */
+
+#define DEBUG 1 /* Enable debugging output */
+#define ENABLE_PARITY 1 /* Enable SCSI Parity */
+#define FIFO_COUNT 2 /* Number of 512 byte blocks before INTR */
+#define DO_DETECT 0 /* Do device detection here (see scsi.c) */
+
+/* END OF USER DEFINABLE OPTIONS */
+
+#if DEBUG
+#define EVERY_ACCESS 0 /* Write a line on every scsi access */
+#define ERRORS_ONLY 1 /* Only write a line if there is an error */
+#define DEBUG_DETECT 0 /* Debug fdomain_16x0_detect() */
+#define DEBUG_MESSAGES 1 /* Debug MESSAGE IN phase */
+#define DEBUG_ABORT 1 /* Debug abort() routine */
+#define DEBUG_RESET 1 /* Debug reset() routine */
+#define DEBUG_RACE 1 /* Debug interrupt-driven race condition */
+#else
+#define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
+#define ERRORS_ONLY 0
+#define DEBUG_DETECT 0
+#define DEBUG_MESSAGES 0
+#define DEBUG_ABORT 0
+#define DEBUG_RESET 0
+#define DEBUG_RACE 0
+#endif
+
+/* Errors are reported on the line, so we don't need to report them again */
+#if EVERY_ACCESS
+#undef ERRORS_ONLY
+#define ERRORS_ONLY 0
+#endif
+
+#if ENABLE_PARITY
+#define PARITY_MASK 0x08
+#else
+#define PARITY_MASK 0x00
+#endif
+
+enum chip_type {
+ unknown = 0x00,
+ tmc1800 = 0x01,
+ tmc18c50 = 0x02,
+ tmc18c30 = 0x03,
+};
+
+enum {
+ in_arbitration = 0x02,
+ in_selection = 0x04,
+ in_other = 0x08,
+ disconnect = 0x10,
+ aborted = 0x20,
+ sent_ident = 0x40,
+};
+
+enum in_port_type {
+ Read_SCSI_Data = 0,
+ SCSI_Status = 1,
+ TMC_Status = 2,
+ FIFO_Status = 3, /* tmc18c50/tmc18c30 only */
+ Interrupt_Cond = 4, /* tmc18c50/tmc18c30 only */
+ LSB_ID_Code = 5,
+ MSB_ID_Code = 6,
+ Read_Loopback = 7,
+ SCSI_Data_NoACK = 8,
+ Interrupt_Status = 9,
+ Configuration1 = 10,
+ Configuration2 = 11, /* tmc18c50/tmc18c30 only */
+ Read_FIFO = 12,
+ FIFO_Data_Count = 14
+};
+
+enum out_port_type {
+ Write_SCSI_Data = 0,
+ SCSI_Cntl = 1,
+ Interrupt_Cntl = 2,
+ SCSI_Mode_Cntl = 3,
+ TMC_Cntl = 4,
+ Memory_Cntl = 5, /* tmc18c50/tmc18c30 only */
+ Write_Loopback = 7,
+ IO_Control = 11, /* tmc18c30 only */
+ Write_FIFO = 12
+};
+
+static int port_base = 0;
+static void *bios_base = NULL;
+static int bios_major = 0;
+static int bios_minor = 0;
+static int PCI_bus = 0;
+static int Quantum = 0; /* Quantum board variant */
+static int interrupt_level = 0;
+static volatile int in_command = 0;
+static Scsi_Cmnd *current_SC = NULL;
+static enum chip_type chip = unknown;
+static int adapter_mask = 0;
+static int this_id = 0;
+static int setup_called = 0;
+
+#if DEBUG_RACE
+static volatile int in_interrupt_flag = 0;
+#endif
+
+static int SCSI_Mode_Cntl_port;
+static int FIFO_Data_Count_port;
+static int Interrupt_Cntl_port;
+static int Interrupt_Status_port;
+static int Read_FIFO_port;
+static int Read_SCSI_Data_port;
+static int SCSI_Cntl_port;
+static int SCSI_Data_NoACK_port;
+static int SCSI_Status_port;
+static int TMC_Cntl_port;
+static int TMC_Status_port;
+static int Write_FIFO_port;
+static int Write_SCSI_Data_port;
+
+static int FIFO_Size = 0x2000; /* 8k FIFO for
+ pre-tmc18c30 chips */
+
+extern void fdomain_16x0_intr( int irq, void *dev_id, struct pt_regs * regs );
+
+static void *addresses[] = {
+ (void *)0xc8000,
+ (void *)0xca000,
+ (void *)0xce000,
+ (void *)0xde000,
+ (void *)0xcc000, /* Extra addresses for PCI boards */
+ (void *)0xd0000,
+ (void *)0xe0000,
+};
+#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned ))
+
+static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
+#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))
+
+static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
+
+/*
+
+ READ THIS BEFORE YOU ADD A SIGNATURE!
+
+ READING THIS SHORT NOTE CAN SAVE YOU LOTS OF TIME!
+
+ READ EVERY WORD, ESPECIALLY THE WORD *NOT*
+
+ This driver works *ONLY* for Future Domain cards using the TMC-1800,
+ TMC-18C50, or TMC-18C30 chip. This includes models TMC-1650, 1660, 1670,
+ and 1680.
+
+ The following BIOS signature signatures are for boards which do *NOT*
+ work with this driver (these TMC-8xx and TMC-9xx boards may work with the
+ Seagate driver):
+
+ FUTURE DOMAIN CORP. (C) 1986-1988 V4.0I 03/16/88
+ FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89
+ FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89
+ FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90
+ FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90
+ FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90
+ FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92
+
+*/
+
+struct signature {
+ const char *signature;
+ int sig_offset;
+ int sig_length;
+ int major_bios_version;
+ int minor_bios_version;
+ int flag; /* 1 == PCI_bus, 2 == ISA_200S, 3 == ISA_250MG, 4 == ISA_200S */
+} signatures[] = {
+ /* 1 2 3 4 5 6 */
+ /* 123456789012345678901234567890123456789012345678901234567890 */
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 5, 50, 2, 0, 0 },
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V1.07/28/89", 5, 50, 2, 0, 0 },
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 72, 50, 2, 0, 2 },
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0", 73, 43, 2, 0, 3 },
+ { "FUTURE DOMAIN CORP. (C) 1991 1800-V2.0.", 72, 39, 2, 0, 4 },
+ { "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92", 5, 44, 3, 0, 0 },
+ { "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93", 5, 44, 3, 2, 0 },
+ { "IBM F1 P2 BIOS v1.0104/29/93", 5, 28, 3, -1, 0 },
+ { "Future Domain Corp. V1.0008/18/93", 5, 33, 3, 4, 0 },
+ { "Future Domain Corp. V1.0008/18/93", 26, 33, 3, 4, 1 },
+ { "Adaptec AHA-2920 PCI-SCSI Card", 42, 31, 3, -1, 1 },
+ { "IBM F1 P264/32", 5, 14, 3, -1, 1 },
+ /* This next signature may not be a 3.5 bios */
+ { "Future Domain Corp. V2.0108/18/93", 5, 33, 3, 5, 0 },
+ { "FUTURE DOMAIN CORP. V3.5008/18/93", 5, 34, 3, 5, 0 },
+ { "FUTURE DOMAIN 18c30/18c50/1800 (C) 1994 V3.5", 5, 44, 3, 5, 0 },
+ { "FUTURE DOMAIN CORP. V3.6008/18/93", 5, 34, 3, 6, 0 },
+ { "FUTURE DOMAIN CORP. V3.6108/18/93", 5, 34, 3, 6, 0 },
+ { "FUTURE DOMAIN TMC-18XX", 5, 22, -1, -1, 0 },
+
+ /* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGNATURE
+ Also, fix the disk geometry code for your signature and send your
+ changes for faith@cs.unc.edu. Above all, do *NOT* change any old
+ signatures!
+
+ Note that the last line will match a "generic" 18XX bios. Because
+ Future Domain has changed the host SCSI ID and/or the location of the
+ geometry information in the on-board RAM area for each of the first
+ three BIOS's, it is still important to enter a fully qualified
+ signature in the table for any new BIOS's (after the host SCSI ID and
+ geometry location are verified). */
+};
+
+#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))
+
+static void print_banner( struct Scsi_Host *shpnt )
+{
+ if (!shpnt) return; /* This won't ever happen */
+
+ if (bios_major < 0 && bios_minor < 0) {
+ printk( "scsi%d <fdomain>: No BIOS; using scsi id %d\n",
+ shpnt->host_no, shpnt->this_id );
+ } else {
+ printk( "scsi%d <fdomain>: BIOS version ", shpnt->host_no );
+
+ if (bios_major >= 0) printk( "%d.", bios_major );
+ else printk( "?." );
+
+ if (bios_minor >= 0) printk( "%d", bios_minor );
+ else printk( "?." );
+
+ printk( " at 0x%x using scsi id %d\n",
+ (unsigned)bios_base, shpnt->this_id );
+ }
+
+ /* If this driver works for later FD PCI
+ boards, we will have to modify banner
+ for additional PCI cards, but for now if
+ it's PCI it's a TMC-3260 - JTM */
+ printk( "scsi%d <fdomain>: %s chip at 0x%x irq ",
+ shpnt->host_no,
+ chip == tmc1800 ? "TMC-1800"
+ : (chip == tmc18c50 ? "TMC-18C50"
+ : (chip == tmc18c30 ?
+ (PCI_bus ? "TMC-36C70 (PCI bus)" : "TMC-18C30")
+ : "Unknown")),
+ port_base );
+
+ if (interrupt_level) printk( "%d", interrupt_level );
+ else printk( "<none>" );
+
+ printk( "\n" );
+}
+
+void fdomain_setup( char *str, int *ints )
+{
+ if (setup_called++ || ints[0] < 2 || ints[0] > 3) {
+ printk( "fdomain: usage: fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]\n" );
+ printk( "fdomain: bad LILO parameters?\n" );
+ }
+
+ port_base = ints[0] >= 1 ? ints[1] : 0;
+ interrupt_level = ints[0] >= 2 ? ints[2] : 0;
+ this_id = ints[0] >= 3 ? ints[3] : 0;
+
+ bios_major = bios_minor = -1; /* Use geometry for BIOS version >= 3.4 */
+}
+
+
+static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */
+{
+ unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */
+
+ while (jiffies < the_time);
+}
+
+inline static void fdomain_make_bus_idle( void )
+{
+ outb( 0, SCSI_Cntl_port );
+ outb( 0, SCSI_Mode_Cntl_port );
+ if (chip == tmc18c50 || chip == tmc18c30)
+ outb( 0x21 | PARITY_MASK, TMC_Cntl_port ); /* Clear forced intr. */
+ else
+ outb( 0x01 | PARITY_MASK, TMC_Cntl_port );
+}
+
+static int fdomain_is_valid_port( int port )
+{
+#if DEBUG_DETECT
+ printk( " (%x%x),",
+ inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) );
+#endif
+
+ /* The MCA ID is a unique id for each MCA compatible board. We
+ are using ISA boards, but Future Domain provides the MCA ID
+ anyway. We can use this ID to ensure that this is a Future
+ Domain TMC-1660/TMC-1680.
+ */
+
+ if (inb( port + LSB_ID_Code ) != 0xe9) { /* test for 0x6127 id */
+ if (inb( port + LSB_ID_Code ) != 0x27) return 0;
+ if (inb( port + MSB_ID_Code ) != 0x61) return 0;
+ chip = tmc1800;
+ } else { /* test for 0xe960 id */
+ if (inb( port + MSB_ID_Code ) != 0x60) return 0;
+ chip = tmc18c50;
+
+#if 0
+
+ /* Try to toggle 32-bit mode. This only
+ works on an 18c30 chip. (User reports
+ say this works, so we should switch to
+ it in the near future.) */
+
+ outb( 0x80, port + IO_Control );
+ if ((inb( port + Configuration2 ) & 0x80) == 0x80) {
+ outb( 0x00, port + IO_Control );
+ if ((inb( port + Configuration2 ) & 0x80) == 0x00) {
+ chip = tmc18c30;
+ FIFO_Size = 0x800; /* 2k FIFO */
+ }
+ }
+#else
+
+ /* That should have worked, but appears to
+ have problems. Let's assume it is an
+ 18c30 if the RAM is disabled. */
+
+ if (inb( port + Configuration2 ) & 0x02) {
+ chip = tmc18c30;
+ FIFO_Size = 0x800; /* 2k FIFO */
+ }
+#endif
+ /* If that failed, we are an 18c50. */
+ }
+
+ return 1;
+}
+
+static int fdomain_test_loopback( void )
+{
+ int i;
+ int result;
+
+ for (i = 0; i < 255; i++) {
+ outb( i, port_base + Write_Loopback );
+ result = inb( port_base + Read_Loopback );
+ if (i != result)
+ return 1;
+ }
+ return 0;
+}
+
+/* fdomain_get_irq assumes that we have a valid MCA ID for a
+ TMC-1660/TMC-1680 Future Domain board. Now, check to be sure the
+ bios_base matches these ports. If someone was unlucky enough to have
+ purchased more than one Future Domain board, then they will have to
+ modify this code, as we only detect one board here. [The one with the
+ lowest bios_base.]
+
+ Note that this routine is only used for systems without a PCI BIOS32
+ (e.g., ISA bus). For PCI bus systems, this routine will likely fail
+ unless one of the IRQs listed in the ints array is used by the board.
+ Sometimes it is possible to use the computer's BIOS setup screen to
+ configure a PCI system so that one of these IRQs will be used by the
+ Future Domain card. */
+
+static int fdomain_get_irq( int base )
+{
+ int options = inb( base + Configuration1 );
+
+#if DEBUG_DETECT
+ printk( " Options = %x\n", options );
+#endif
+
+ /* Check for board with lowest bios_base --
+ this isn't valid for the 18c30 or for
+ boards on the PCI bus, so just assume we
+ have the right board. */
+
+ if (chip != tmc18c30
+ && !PCI_bus
+ && addresses[ (options & 0xc0) >> 6 ] != bios_base) return 0;
+
+ return ints[ (options & 0x0e) >> 1 ];
+}
+
+static int fdomain_isa_detect( int *irq, int *iobase )
+{
+ int i;
+ int base;
+ int flag = 0;
+
+ if (bios_major == 2) {
+ /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM.
+ Assuming the ROM is enabled (otherwise we wouldn't have been
+ able to read the ROM signature :-), then the ROM sets up the
+ RAM area with some magic numbers, such as a list of port
+ base addresses and a list of the disk "geometry" reported to
+ DOS (this geometry has nothing to do with physical geometry).
+ */
+
+ switch (Quantum) {
+ case 2: /* ISA_200S */
+ case 3: /* ISA_250MG */
+ base = *((char *)bios_base + 0x1fa2)
+ + (*((char *)bios_base + 0x1fa3) << 8);
+ break;
+ case 4: /* ISA_200S (another one) */
+ base = *((char *)bios_base + 0x1fa3)
+ + (*((char *)bios_base + 0x1fa4) << 8);
+ break;
+ default:
+ base = *((char *)bios_base + 0x1fcc)
+ + (*((char *)bios_base + 0x1fcd) << 8);
+ break;
+ }
+
+#if DEBUG_DETECT
+ printk( " %x,", base );
+#endif
+
+ for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) {
+ if (base == ports[i])
+ ++flag;
+ }
+
+ if (flag && fdomain_is_valid_port( base )) {
+ *irq = fdomain_get_irq( base );
+ *iobase = base;
+ return 1;
+ }
+
+ /* This is a bad sign. It usually means that someone patched the
+ BIOS signature list (the signatures variable) to contain a BIOS
+ signature for a board *OTHER THAN* the TMC-1660/TMC-1680. */
+
+#if DEBUG_DETECT
+ printk( " RAM FAILED, " );
+#endif
+ }
+
+ /* Anyway, the alternative to finding the address in the RAM is to just
+ search through every possible port address for one that is attached
+ to the Future Domain card. Don't panic, though, about reading all
+ these random port addresses -- there are rumors that the Future
+ Domain BIOS does something very similar.
+
+ Do not, however, check ports which the kernel knows are being used by
+ another driver. */
+
+ for (i = 0; i < PORT_COUNT; i++) {
+ base = ports[i];
+ if (check_region( base, 0x10 )) {
+#if DEBUG_DETECT
+ printk( " (%x inuse),", base );
+#endif
+ continue;
+ }
+#if DEBUG_DETECT
+ printk( " %x,", base );
+#endif
+ if ((flag = fdomain_is_valid_port( base ))) break;
+ }
+
+ if (!flag) return 0; /* iobase not found */
+
+ *irq = fdomain_get_irq( base );
+ *iobase = base;
+
+ return 1; /* success */
+}
+
+static int fdomain_pci_nobios_detect( int *irq, int *iobase )
+{
+ int i;
+ int flag = 0;
+
+ /* The proper way of doing this is to use ask the PCI bus for the device
+ IRQ and interrupt level. But we can't do that if PCI BIOS32 support
+ isn't compiled into the kernel, or if a PCI BIOS32 isn't present.
+
+ Instead, we scan down a bunch of addresses (Future Domain tech
+ support says we will probably find the address before we get to
+ 0xf800). This works fine on some systems -- other systems may have
+ to scan more addresses. If you have to modify this section for your
+ installation, please send mail to faith@cs.unc.edu. */
+
+ for (i = 0xfff8; i > 0xe000; i -= 8) {
+ if (check_region( i, 0x10 )) {
+#if DEBUG_DETECT
+ printk( " (%x inuse)," , i );
+#endif
+ continue;
+ }
+ if ((flag = fdomain_is_valid_port( i ))) break;
+ }
+
+ if (!flag) return 0; /* iobase not found */
+
+ *irq = fdomain_get_irq( i );
+ *iobase = i;
+
+ return 1; /* success */
+}
+
+/* PCI detection function: int fdomain_pci_bios_detect(int* irq, int*
+ iobase) This function gets the Interrupt Level and I/O base address from
+ the PCI configuration registers. The I/O base address is masked with
+ 0xfff8 since on my card the address read from the PCI config registers
+ is off by one from the actual I/O base address necessary for accessing
+ the status and control registers on the card (PCI config register gives
+ 0xf801, actual address is 0xf800). This is likely a bug in the FD
+ config code that writes to the PCI registers, however using a mask
+ should be safe since I think the scan done by the card to determine the
+ I/O base is done in increments of 8 (i.e., 0xf800, 0xf808, ...), at
+ least the old scan code we used to use to get the I/O base did... Also,
+ the device ID from the PCI config registers is 0x0 and should be 0x60e9
+ as it is in the status registers (offset 5 from I/O base). If this is
+ changed in future hardware/BIOS changes it will need to be fixed in this
+ detection function. Comments, bug reports, etc... on this function
+ should be sent to mckinley@msupa.pa.msu.edu - James T. McKinley. */
+
+#ifdef CONFIG_PCI
+static int fdomain_pci_bios_detect( int *irq, int *iobase )
+{
+ int error;
+ unsigned char pci_bus, pci_dev_fn; /* PCI bus & device function */
+ unsigned char pci_irq; /* PCI interrupt line */
+ unsigned int pci_base; /* PCI I/O base address */
+ unsigned short pci_vendor, pci_device; /* PCI vendor & device IDs */
+
+ /* If the PCI BIOS doesn't exist, use the old-style detection routines.
+ Otherwise, get the I/O base address and interrupt from the PCI config
+ registers. */
+
+ if (!pcibios_present()) return fdomain_pci_nobios_detect( irq, iobase );
+
+#if DEBUG_DETECT
+ /* Tell how to print a list of the known PCI devices from bios32 and
+ list vendor and device IDs being used if in debug mode. */
+
+ printk( "\nINFO: cat /proc/pci to see list of PCI devices from bios32\n" );
+ printk( "\nTMC-3260 detect:"
+ " Using PCI Vendor ID: 0x%x, PCI Device ID: 0x%x\n",
+ PCI_VENDOR_ID_FD,
+ PCI_DEVICE_ID_FD_36C70 );
+#endif
+
+ /* We will have to change this if more than 1 PCI bus is present and the
+ FD scsi host is not on the first bus (i.e., a PCI to PCI bridge,
+ which is not supported by bios32 right now anyway). This should
+ probably be done by a call to pcibios_find_device but I can't get it
+ to work... Also the device ID reported from the PCI config registers
+ does not match the device ID quoted in the tech manual or available
+ from offset 5 from the I/O base address. It should be 0x60E9, but it
+ is 0x0 if read from the PCI config registers. I guess the FD folks
+ neglected to write it to the PCI registers... This loop is necessary
+ to get the device function (at least until someone can get
+ pcibios_find_device to work, I cannot but 53c7,8xx.c uses it...). */
+
+ pci_bus = 0;
+
+ for (pci_dev_fn = 0x0; pci_dev_fn < 0xff; pci_dev_fn++) {
+ pcibios_read_config_word( pci_bus,
+ pci_dev_fn,
+ PCI_VENDOR_ID,
+ &pci_vendor );
+
+ if (pci_vendor == PCI_VENDOR_ID_FD) {
+ pcibios_read_config_word( pci_bus,
+ pci_dev_fn,
+ PCI_DEVICE_ID,
+ &pci_device );
+
+ if (pci_device == PCI_DEVICE_ID_FD_36C70) {
+ /* Break out once we have the correct device. If other FD
+ PCI devices are added to this driver we will need to add
+ an or of the other PCI_DEVICE_ID_FD_XXXXX's here. */
+ break;
+ } else {
+ /* If we can't find an FD scsi card we give up. */
+ return 0;
+ }
+ }
+ }
+
+#if DEBUG_DETECT
+ printk( "Future Domain 36C70 : at PCI bus %u, device %u, function %u\n",
+ pci_bus,
+ (pci_dev_fn & 0xf8) >> 3,
+ pci_dev_fn & 7 );
+#endif
+
+ /* We now have the appropriate device function for the FD board so we
+ just read the PCI config info from the registers. */
+
+ if ((error = pcibios_read_config_dword( pci_bus,
+ pci_dev_fn,
+ PCI_BASE_ADDRESS_0,
+ &pci_base ))
+ || (error = pcibios_read_config_byte( pci_bus,
+ pci_dev_fn,
+ PCI_INTERRUPT_LINE,
+ &pci_irq ))) {
+ printk ( "PCI ERROR: Future Domain 36C70 not initializing"
+ " due to error reading configuration space\n" );
+ return 0;
+ } else {
+#if DEBUG_DETECT
+ printk( "TMC-3260 PCI: IRQ = %u, I/O base = 0x%lx\n",
+ pci_irq, pci_base );
+#endif
+
+ /* Now we have the I/O base address and interrupt from the PCI
+ configuration registers. Unfortunately it seems that the I/O base
+ address is off by one on my card so I mask it with 0xfff8. This
+ must be some kind of goof in the FD code that does the autoconfig
+ and writes to the PCI registers (or maybe I just don't understand
+ something). If they fix it in later versions of the card or BIOS
+ we may have to adjust the address based on the signature or
+ something... */
+
+ *irq = pci_irq;
+ *iobase = (pci_base & 0xfff8);
+
+#if DEBUG_DETECT
+ printk( "TMC-3260 fix: Masking I/O base address with 0xff00.\n" );
+ printk( "TMC-3260: IRQ = %d, I/O base = 0x%x\n", *irq, *iobase );
+#endif
+
+ if (!fdomain_is_valid_port( *iobase )) return 0;
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
+{
+ int i, j;
+ int retcode;
+ struct Scsi_Host *shpnt;
+#if DO_DETECT
+ const int buflen = 255;
+ Scsi_Cmnd SCinit;
+ unsigned char do_inquiry[] = { INQUIRY, 0, 0, 0, buflen, 0 };
+ unsigned char do_request_sense[] = { REQUEST_SENSE, 0, 0, 0, buflen, 0 };
+ unsigned char do_read_capacity[] = { READ_CAPACITY,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ unsigned char buf[buflen];
+#endif
+
+#if DEBUG_DETECT
+ printk( "fdomain_16x0_detect()," );
+#endif
+ tpnt->proc_dir = &proc_scsi_fdomain;
+
+ if (setup_called) {
+#if DEBUG_DETECT
+ printk( "no BIOS, using port_base = 0x%x, irq = %d\n",
+ port_base, interrupt_level );
+#endif
+ if (!fdomain_is_valid_port( port_base )) {
+ printk( "fdomain: cannot locate chip at port base 0x%x\n",
+ port_base );
+ printk( "fdomain: bad LILO parameters?\n" );
+ return 0;
+ }
+ } else {
+ int flag = 0;
+
+ for (i = 0; !bios_base && i < ADDRESS_COUNT; i++) {
+#if DEBUG_DETECT
+ printk( " %x(%x),", (unsigned)addresses[i], (unsigned)bios_base );
+#endif
+ for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) {
+ if (!memcmp( ((char *)addresses[i] + signatures[j].sig_offset),
+ signatures[j].signature, signatures[j].sig_length )) {
+ bios_major = signatures[j].major_bios_version;
+ bios_minor = signatures[j].minor_bios_version;
+ PCI_bus = (signatures[j].flag == 1);
+ Quantum = (signatures[j].flag > 1) ? signatures[j].flag : 0;
+ bios_base = addresses[i];
+ }
+ }
+ }
+
+ if (!bios_base) {
+#if DEBUG_DETECT
+ printk( " FAILED: NO BIOS\n" );
+#endif
+ return 0;
+ }
+
+ if (!PCI_bus) {
+ flag = fdomain_isa_detect( &interrupt_level, &port_base );
+ } else {
+#ifdef CONFIG_PCI
+ flag = fdomain_pci_bios_detect( &interrupt_level, &port_base );
+#else
+ flag = fdomain_pci_nobios_detect( &interrupt_level, &port_base );
+#endif
+ }
+
+ if (!flag) {
+#if DEBUG_DETECT
+ printk( " FAILED: NO PORT\n" );
+#endif
+#ifdef CONFIG_PCI
+ printk( "\nTMC-3260 36C70 PCI scsi chip detection failed.\n" );
+ printk( "Send mail to mckinley@msupa.pa.msu.edu.\n" );
+#endif
+ return 0; /* Cannot find valid set of ports */
+ }
+ }
+
+ SCSI_Mode_Cntl_port = port_base + SCSI_Mode_Cntl;
+ FIFO_Data_Count_port = port_base + FIFO_Data_Count;
+ Interrupt_Cntl_port = port_base + Interrupt_Cntl;
+ Interrupt_Status_port = port_base + Interrupt_Status;
+ Read_FIFO_port = port_base + Read_FIFO;
+ Read_SCSI_Data_port = port_base + Read_SCSI_Data;
+ SCSI_Cntl_port = port_base + SCSI_Cntl;
+ SCSI_Data_NoACK_port = port_base + SCSI_Data_NoACK;
+ SCSI_Status_port = port_base + SCSI_Status;
+ TMC_Cntl_port = port_base + TMC_Cntl;
+ TMC_Status_port = port_base + TMC_Status;
+ Write_FIFO_port = port_base + Write_FIFO;
+ Write_SCSI_Data_port = port_base + Write_SCSI_Data;
+
+ fdomain_16x0_reset( NULL, 0 );
+
+ if (fdomain_test_loopback()) {
+#if DEBUG_DETECT
+ printk( "fdomain: LOOPBACK TEST FAILED, FAILING DETECT!\n" );
+#endif
+ if (setup_called) {
+ printk( "fdomain: loopback test failed at port base 0x%x\n",
+ port_base );
+ printk( "fdomain: bad LILO parameters?\n" );
+ }
+ return 0;
+ }
+
+ if (this_id) {
+ tpnt->this_id = (this_id & 0x07);
+ adapter_mask = (1 << tpnt->this_id);
+ } else {
+ if (PCI_bus || (bios_major == 3 && bios_minor >= 2) || bios_major < 0) {
+ tpnt->this_id = 7;
+ adapter_mask = 0x80;
+ } else {
+ tpnt->this_id = 6;
+ adapter_mask = 0x40;
+ }
+ }
+
+ /* Print out a banner here in case we can't
+ get resources. */
+
+ shpnt = scsi_register( tpnt, 0 );
+ shpnt->irq = interrupt_level;
+ shpnt->io_port = port_base;
+ shpnt->n_io_port = 0x10;
+ print_banner( shpnt );
+
+ /* Log IRQ with kernel */
+ if (!interrupt_level) {
+ panic( "fdomain: *NO* interrupt level selected!\n" );
+ } else {
+ /* Register the IRQ with the kernel */
+
+ retcode = request_irq( interrupt_level,
+ fdomain_16x0_intr, SA_INTERRUPT, "fdomain", NULL);
+
+ if (retcode < 0) {
+ if (retcode == -EINVAL) {
+ printk( "fdomain: IRQ %d is bad!\n", interrupt_level );
+ printk( " This shouldn't happen!\n" );
+ printk( " Send mail to faith@cs.unc.edu\n" );
+ } else if (retcode == -EBUSY) {
+ printk( "fdomain: IRQ %d is already in use!\n", interrupt_level );
+ printk( " Please use another IRQ!\n" );
+ } else {
+ printk( "fdomain: Error getting IRQ %d\n", interrupt_level );
+ printk( " This shouldn't happen!\n" );
+ printk( " Send mail to faith@cs.unc.edu\n" );
+ }
+ panic( "fdomain: Driver requires interruptions\n" );
+ }
+ }
+
+ /* Log I/O ports with kernel */
+ request_region( port_base, 0x10, "fdomain" );
+
+#if DO_DETECT
+
+ /* These routines are here because of the way the SCSI bus behaves after
+ a reset. This appropriate behavior was not handled correctly by the
+ higher level SCSI routines when I first wrote this driver. Now,
+ however, correct scan routines are part of scsi.c and these routines
+ are no longer needed. However, this code is still good for
+ debugging. */
+
+ SCinit.request_buffer = SCinit.buffer = buf;
+ SCinit.request_bufflen = SCinit.bufflen = sizeof(buf)-1;
+ SCinit.use_sg = 0;
+ SCinit.lun = 0;
+
+ printk( "fdomain: detection routine scanning for devices:\n" );
+ for (i = 0; i < 8; i++) {
+ SCinit.target = i;
+ if (i == tpnt->this_id) /* Skip host adapter */
+ continue;
+ memcpy(SCinit.cmnd, do_request_sense, sizeof(do_request_sense));
+ retcode = fdomain_16x0_command(&SCinit);
+ if (!retcode) {
+ memcpy(SCinit.cmnd, do_inquiry, sizeof(do_inquiry));
+ retcode = fdomain_16x0_command(&SCinit);
+ if (!retcode) {
+ printk( " SCSI ID %d: ", i );
+ for (j = 8; j < (buf[4] < 32 ? buf[4] : 32); j++)
+ printk( "%c", buf[j] >= 20 ? buf[j] : ' ' );
+ memcpy(SCinit.cmnd, do_read_capacity, sizeof(do_read_capacity));
+ retcode = fdomain_16x0_command(&SCinit);
+ if (!retcode) {
+ unsigned long blocks, size, capacity;
+
+ blocks = (buf[0] << 24) | (buf[1] << 16)
+ | (buf[2] << 8) | buf[3];
+ size = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ capacity = +( +(blocks / 1024L) * +(size * 10L)) / 1024L;
+
+ printk( "%lu MB (%lu byte blocks)",
+ ((capacity + 5L) / 10L), size );
+ } else {
+ memcpy(SCinit.cmnd, do_request_sense, sizeof(do_request_sense));
+ retcode = fdomain_16x0_command(&SCinit);
+ }
+ printk ("\n" );
+ } else {
+ memcpy(SCinit.cmnd, do_request_sense, sizeof(do_request_sense));
+ retcode = fdomain_16x0_command(&SCinit);
+ }
+ }
+ }
+#endif
+
+ return 1; /* Maximum of one adapter will be detected. */
+}
+
+const char *fdomain_16x0_info( struct Scsi_Host *ignore )
+{
+ static char buffer[80];
+ char *pt;
+
+ strcpy( buffer, "Future Domain TMC-16x0 SCSI driver, version" );
+ if (strchr( VERSION, ':')) { /* Assume VERSION is an RCS Revision string */
+ strcat( buffer, strchr( VERSION, ':' ) + 1 );
+ pt = strrchr( buffer, '$') - 1;
+ if (!pt) /* Stripped RCS Revision string? */
+ pt = buffer + strlen( buffer ) - 1;
+ if (*pt != ' ')
+ ++pt;
+ *pt = '\0';
+ } else { /* Assume VERSION is a number */
+ strcat( buffer, " " VERSION );
+ }
+
+ return buffer;
+}
+
+ /* First pass at /proc information routine. */
+/*
+ * inout : decides on the direction of the dataflow and the meaning of the
+ * variables
+ * buffer: If inout==FALSE data is being written to it else read from it
+ * *start: If inout==FALSE start of the valid data in the buffer
+ * offset: If inout==FALSE offset from the beginning of the imaginary file
+ * from which we start writing into the buffer
+ * length: If inout==FALSE max number of bytes to be written into the buffer
+ * else number of bytes in the buffer
+ */
+int fdomain_16x0_proc_info( char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout )
+{
+ const char *info = fdomain_16x0_info( NULL );
+ int len;
+ int pos;
+ int begin;
+
+ if (inout) return(-ENOSYS);
+
+ begin = 0;
+ strcpy( buffer, info );
+ strcat( buffer, "\n" );
+
+ pos = len = strlen( buffer );
+
+ if(pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin);
+ if(len > length) len = length;
+
+ return(len);
+}
+
+#if 0
+static int fdomain_arbitrate( void )
+{
+ int status = 0;
+ unsigned long timeout;
+
+#if EVERY_ACCESS
+ printk( "fdomain_arbitrate()\n" );
+#endif
+
+ outb( 0x00, SCSI_Cntl_port ); /* Disable data drivers */
+ outb( adapter_mask, port_base + SCSI_Data_NoACK ); /* Set our id bit */
+ outb( 0x04 | PARITY_MASK, TMC_Cntl_port ); /* Start arbitration */
+
+ timeout = jiffies + 50; /* 500 mS */
+ while (jiffies < timeout) {
+ status = inb( TMC_Status_port ); /* Read adapter status */
+ if (status & 0x02) /* Arbitration complete */
+ return 0;
+ }
+
+ /* Make bus idle */
+ fdomain_make_bus_idle();
+
+#if EVERY_ACCESS
+ printk( "Arbitration failed, status = %x\n", status );
+#endif
+#if ERRORS_ONLY
+ printk( "fdomain: Arbitration failed, status = %x\n", status );
+#endif
+ return 1;
+}
+#endif
+
+static int fdomain_select( int target )
+{
+ int status;
+ unsigned long timeout;
+ static int flag = 0;
+
+
+ outb( 0x82, SCSI_Cntl_port ); /* Bus Enable + Select */
+ outb( adapter_mask | (1 << target), SCSI_Data_NoACK_port );
+
+ /* Stop arbitration and enable parity */
+ outb( PARITY_MASK, TMC_Cntl_port );
+
+ timeout = jiffies + 35; /* 350mS -- because of timeouts
+ (was 250mS) */
+
+ while (jiffies < timeout) {
+ status = inb( SCSI_Status_port ); /* Read adapter status */
+ if (status & 1) { /* Busy asserted */
+ /* Enable SCSI Bus (on error, should make bus idle with 0) */
+ outb( 0x80, SCSI_Cntl_port );
+ return 0;
+ }
+ }
+ /* Make bus idle */
+ fdomain_make_bus_idle();
+#if EVERY_ACCESS
+ if (!target) printk( "Selection failed\n" );
+#endif
+#if ERRORS_ONLY
+ if (!target) {
+ if (!flag) /* Skip first failure for all chips. */
+ ++flag;
+ else
+ printk( "fdomain: Selection failed\n" );
+ }
+#endif
+ return 1;
+}
+
+void my_done( int error )
+{
+ if (in_command) {
+ in_command = 0;
+ outb( 0x00, Interrupt_Cntl_port );
+ fdomain_make_bus_idle();
+ current_SC->result = error;
+ if (current_SC->scsi_done)
+ current_SC->scsi_done( current_SC );
+ else panic( "fdomain: current_SC->scsi_done() == NULL" );
+ } else {
+ panic( "fdomain: my_done() called outside of command\n" );
+ }
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+}
+
+void fdomain_16x0_intr( int irq, void *dev_id, struct pt_regs * regs )
+{
+ int status;
+ int done = 0;
+ unsigned data_count;
+
+ /* The fdomain_16x0_intr is only called via
+ the interrupt handler. The goal of the
+ sti() here is to allow other
+ interruptions while this routine is
+ running. */
+
+ sti(); /* Yes, we really want sti() here */
+
+ outb( 0x00, Interrupt_Cntl_port );
+
+ /* We usually have one spurious interrupt after each command. Ignore it. */
+ if (!in_command || !current_SC) { /* Spurious interrupt */
+#if EVERY_ACCESS
+ printk( "Spurious interrupt, in_command = %d, current_SC = %x\n",
+ in_command, current_SC );
+#endif
+ return;
+ }
+
+ /* Abort calls my_done, so we do nothing here. */
+ if (current_SC->SCp.phase & aborted) {
+#if DEBUG_ABORT
+ printk( "Interrupt after abort, ignoring\n" );
+#endif
+ /*
+ return; */
+ }
+
+#if DEBUG_RACE
+ ++in_interrupt_flag;
+#endif
+
+ if (current_SC->SCp.phase & in_arbitration) {
+ status = inb( TMC_Status_port ); /* Read adapter status */
+ if (!(status & 0x02)) {
+#if EVERY_ACCESS
+ printk( " AFAIL " );
+#endif
+ my_done( DID_BUS_BUSY << 16 );
+ return;
+ }
+ current_SC->SCp.phase = in_selection;
+
+ outb( 0x40 | FIFO_COUNT, Interrupt_Cntl_port );
+
+ outb( 0x82, SCSI_Cntl_port ); /* Bus Enable + Select */
+ outb( adapter_mask | (1 << current_SC->target), SCSI_Data_NoACK_port );
+
+ /* Stop arbitration and enable parity */
+ outb( 0x10 | PARITY_MASK, TMC_Cntl_port );
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return;
+ } else if (current_SC->SCp.phase & in_selection) {
+ status = inb( SCSI_Status_port );
+ if (!(status & 0x01)) {
+ /* Try again, for slow devices */
+ if (fdomain_select( current_SC->target )) {
+#if EVERY_ACCESS
+ printk( " SFAIL " );
+#endif
+ my_done( DID_NO_CONNECT << 16 );
+ return;
+ } else {
+#if EVERY_ACCESS
+ printk( " AltSel " );
+#endif
+ /* Stop arbitration and enable parity */
+ outb( 0x10 | PARITY_MASK, TMC_Cntl_port );
+ }
+ }
+ current_SC->SCp.phase = in_other;
+ outb( 0x90 | FIFO_COUNT, Interrupt_Cntl_port );
+ outb( 0x80, SCSI_Cntl_port );
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return;
+ }
+
+ /* current_SC->SCp.phase == in_other: this is the body of the routine */
+
+ status = inb( SCSI_Status_port );
+
+ if (status & 0x10) { /* REQ */
+
+ switch (status & 0x0e) {
+
+ case 0x08: /* COMMAND OUT */
+ outb( current_SC->cmnd[current_SC->SCp.sent_command++],
+ Write_SCSI_Data_port );
+#if EVERY_ACCESS
+ printk( "CMD = %x,",
+ current_SC->cmnd[ current_SC->SCp.sent_command - 1] );
+#endif
+ break;
+ case 0x00: /* DATA OUT -- tmc18c50/tmc18c30 only */
+ if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
+ current_SC->SCp.have_data_in = -1;
+ outb( 0xd0 | PARITY_MASK, TMC_Cntl_port );
+ }
+ break;
+ case 0x04: /* DATA IN -- tmc18c50/tmc18c30 only */
+ if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
+ current_SC->SCp.have_data_in = 1;
+ outb( 0x90 | PARITY_MASK, TMC_Cntl_port );
+ }
+ break;
+ case 0x0c: /* STATUS IN */
+ current_SC->SCp.Status = inb( Read_SCSI_Data_port );
+#if EVERY_ACCESS
+ printk( "Status = %x, ", current_SC->SCp.Status );
+#endif
+#if ERRORS_ONLY
+ if (current_SC->SCp.Status
+ && current_SC->SCp.Status != 2
+ && current_SC->SCp.Status != 8) {
+ printk( "fdomain: target = %d, command = %x, status = %x\n",
+ current_SC->target,
+ current_SC->cmnd[0],
+ current_SC->SCp.Status );
+ }
+#endif
+ break;
+ case 0x0a: /* MESSAGE OUT */
+ outb( MESSAGE_REJECT, Write_SCSI_Data_port ); /* Reject */
+ break;
+ case 0x0e: /* MESSAGE IN */
+ current_SC->SCp.Message = inb( Read_SCSI_Data_port );
+#if EVERY_ACCESS
+ printk( "Message = %x, ", current_SC->SCp.Message );
+#endif
+ if (!current_SC->SCp.Message) ++done;
+#if DEBUG_MESSAGES || EVERY_ACCESS
+ if (current_SC->SCp.Message) {
+ printk( "fdomain: message = %x\n", current_SC->SCp.Message );
+ }
+#endif
+ break;
+ }
+ }
+
+ if (chip == tmc1800
+ && !current_SC->SCp.have_data_in
+ && (current_SC->SCp.sent_command
+ >= current_SC->cmd_len)) {
+ /* We have to get the FIFO direction
+ correct, so I've made a table based
+ on the SCSI Standard of which commands
+ appear to require a DATA OUT phase.
+ */
+ /*
+ p. 94: Command for all device types
+ CHANGE DEFINITION 40 DATA OUT
+ COMPARE 39 DATA OUT
+ COPY 18 DATA OUT
+ COPY AND VERIFY 3a DATA OUT
+ INQUIRY 12
+ LOG SELECT 4c DATA OUT
+ LOG SENSE 4d
+ MODE SELECT (6) 15 DATA OUT
+ MODE SELECT (10) 55 DATA OUT
+ MODE SENSE (6) 1a
+ MODE SENSE (10) 5a
+ READ BUFFER 3c
+ RECEIVE DIAGNOSTIC RESULTS 1c
+ REQUEST SENSE 03
+ SEND DIAGNOSTIC 1d DATA OUT
+ TEST UNIT READY 00
+ WRITE BUFFER 3b DATA OUT
+
+ p.178: Commands for direct-access devices (not listed on p. 94)
+ FORMAT UNIT 04 DATA OUT
+ LOCK-UNLOCK CACHE 36
+ PRE-FETCH 34
+ PREVENT-ALLOW MEDIUM REMOVAL 1e
+ READ (6)/RECEIVE 08
+ READ (10) 3c
+ READ CAPACITY 25
+ READ DEFECT DATA (10) 37
+ READ LONG 3e
+ REASSIGN BLOCKS 07 DATA OUT
+ RELEASE 17
+ RESERVE 16 DATA OUT
+ REZERO UNIT/REWIND 01
+ SEARCH DATA EQUAL (10) 31 DATA OUT
+ SEARCH DATA HIGH (10) 30 DATA OUT
+ SEARCH DATA LOW (10) 32 DATA OUT
+ SEEK (6) 0b
+ SEEK (10) 2b
+ SET LIMITS (10) 33
+ START STOP UNIT 1b
+ SYNCHRONIZE CACHE 35
+ VERIFY (10) 2f
+ WRITE (6)/PRINT/SEND 0a DATA OUT
+ WRITE (10)/SEND 2a DATA OUT
+ WRITE AND VERIFY (10) 2e DATA OUT
+ WRITE LONG 3f DATA OUT
+ WRITE SAME 41 DATA OUT ?
+
+ p. 261: Commands for sequential-access devices (not previously listed)
+ ERASE 19
+ LOAD UNLOAD 1b
+ LOCATE 2b
+ READ BLOCK LIMITS 05
+ READ POSITION 34
+ READ REVERSE 0f
+ RECOVER BUFFERED DATA 14
+ SPACE 11
+ WRITE FILEMARKS 10 ?
+
+ p. 298: Commands for printer devices (not previously listed)
+ ****** NOT SUPPORTED BY THIS DRIVER, since 0b is SEEK (6) *****
+ SLEW AND PRINT 0b DATA OUT -- same as seek
+ STOP PRINT 1b
+ SYNCHRONIZE BUFFER 10
+
+ p. 315: Commands for processor devices (not previously listed)
+
+ p. 321: Commands for write-once devices (not previously listed)
+ MEDIUM SCAN 38
+ READ (12) a8
+ SEARCH DATA EQUAL (12) b1 DATA OUT
+ SEARCH DATA HIGH (12) b0 DATA OUT
+ SEARCH DATA LOW (12) b2 DATA OUT
+ SET LIMITS (12) b3
+ VERIFY (12) af
+ WRITE (12) aa DATA OUT
+ WRITE AND VERIFY (12) ae DATA OUT
+
+ p. 332: Commands for CD-ROM devices (not previously listed)
+ PAUSE/RESUME 4b
+ PLAY AUDIO (10) 45
+ PLAY AUDIO (12) a5
+ PLAY AUDIO MSF 47
+ PLAY TRACK RELATIVE (10) 49
+ PLAY TRACK RELATIVE (12) a9
+ READ HEADER 44
+ READ SUB-CHANNEL 42
+ READ TOC 43
+
+ p. 370: Commands for scanner devices (not previously listed)
+ GET DATA BUFFER STATUS 34
+ GET WINDOW 25
+ OBJECT POSITION 31
+ SCAN 1b
+ SET WINDOW 24 DATA OUT
+
+ p. 391: Commands for optical memory devices (not listed)
+ ERASE (10) 2c
+ ERASE (12) ac
+ MEDIUM SCAN 38 DATA OUT
+ READ DEFECT DATA (12) b7
+ READ GENERATION 29
+ READ UPDATED BLOCK 2d
+ UPDATE BLOCK 3d DATA OUT
+
+ p. 419: Commands for medium changer devices (not listed)
+ EXCHANGE MEDIUM 46
+ INITIALIZE ELEMENT STATUS 07
+ MOVE MEDIUM a5
+ POSITION TO ELEMENT 2b
+ READ ELEMENT STATUS b8
+ REQUEST VOL. ELEMENT ADDRESS b5
+ SEND VOLUME TAG b6 DATA OUT
+
+ p. 454: Commands for communications devices (not listed previously)
+ GET MESSAGE (6) 08
+ GET MESSAGE (10) 28
+ GET MESSAGE (12) a8
+ */
+
+ switch (current_SC->cmnd[0]) {
+ case CHANGE_DEFINITION: case COMPARE: case COPY:
+ case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
+ case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
+
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case WRITE_6: case WRITE_10: case WRITE_VERIFY:
+ case 0x3f: case 0x41:
+
+ case 0xb1: case 0xb0: case 0xb2:
+ case 0xaa: case 0xae:
+
+ case 0x24:
+
+ case 0x38: case 0x3d:
+
+ case 0xb6:
+
+ case 0xea: /* alternate number for WRITE LONG */
+
+ current_SC->SCp.have_data_in = -1;
+ outb( 0xd0 | PARITY_MASK, TMC_Cntl_port );
+ break;
+
+ case 0x00:
+ default:
+
+ current_SC->SCp.have_data_in = 1;
+ outb( 0x90 | PARITY_MASK, TMC_Cntl_port );
+ break;
+ }
+ }
+
+ if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */
+ while ( (data_count = FIFO_Size - inw( FIFO_Data_Count_port )) > 512 ) {
+#if EVERY_ACCESS
+ printk( "DC=%d, ", data_count ) ;
+#endif
+ if (data_count > current_SC->SCp.this_residual)
+ data_count = current_SC->SCp.this_residual;
+ if (data_count > 0) {
+#if EVERY_ACCESS
+ printk( "%d OUT, ", data_count );
+#endif
+ if (data_count == 1) {
+ outb( *current_SC->SCp.ptr++, Write_FIFO_port );
+ --current_SC->SCp.this_residual;
+ } else {
+ data_count >>= 1;
+ outsw( Write_FIFO_port, current_SC->SCp.ptr, data_count );
+ current_SC->SCp.ptr += 2 * data_count;
+ current_SC->SCp.this_residual -= 2 * data_count;
+ }
+ }
+ if (!current_SC->SCp.this_residual) {
+ if (current_SC->SCp.buffers_residual) {
+ --current_SC->SCp.buffers_residual;
+ ++current_SC->SCp.buffer;
+ current_SC->SCp.ptr = current_SC->SCp.buffer->address;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ } else
+ break;
+ }
+ }
+ }
+
+ if (current_SC->SCp.have_data_in == 1) { /* DATA IN */
+ while ((data_count = inw( FIFO_Data_Count_port )) > 0) {
+#if EVERY_ACCESS
+ printk( "DC=%d, ", data_count );
+#endif
+ if (data_count > current_SC->SCp.this_residual)
+ data_count = current_SC->SCp.this_residual;
+ if (data_count) {
+#if EVERY_ACCESS
+ printk( "%d IN, ", data_count );
+#endif
+ if (data_count == 1) {
+ *current_SC->SCp.ptr++ = inb( Read_FIFO_port );
+ --current_SC->SCp.this_residual;
+ } else {
+ data_count >>= 1; /* Number of words */
+ insw( Read_FIFO_port, current_SC->SCp.ptr, data_count );
+ current_SC->SCp.ptr += 2 * data_count;
+ current_SC->SCp.this_residual -= 2 * data_count;
+ }
+ }
+ if (!current_SC->SCp.this_residual
+ && current_SC->SCp.buffers_residual) {
+ --current_SC->SCp.buffers_residual;
+ ++current_SC->SCp.buffer;
+ current_SC->SCp.ptr = current_SC->SCp.buffer->address;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ }
+ }
+ }
+
+ if (done) {
+#if EVERY_ACCESS
+ printk( " ** IN DONE %d ** ", current_SC->SCp.have_data_in );
+#endif
+
+#if ERRORS_ONLY
+ if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) {
+ if ((unsigned char)(*((char *)current_SC->request_buffer+2)) & 0x0f) {
+ unsigned char key;
+ unsigned char code;
+ unsigned char qualifier;
+
+ key = (unsigned char)(*((char *)current_SC->request_buffer + 2))
+ & 0x0f;
+ code = (unsigned char)(*((char *)current_SC->request_buffer + 12));
+ qualifier = (unsigned char)(*((char *)current_SC->request_buffer
+ + 13));
+
+ if (key != UNIT_ATTENTION
+ && !(key == NOT_READY
+ && code == 0x04
+ && (!qualifier || qualifier == 0x02 || qualifier == 0x01))
+ && !(key == ILLEGAL_REQUEST && (code == 0x25
+ || code == 0x24
+ || !code)))
+
+ printk( "fdomain: REQUEST SENSE "
+ "Key = %x, Code = %x, Qualifier = %x\n",
+ key, code, qualifier );
+ }
+ }
+#endif
+#if EVERY_ACCESS
+ printk( "BEFORE MY_DONE. . ." );
+#endif
+ my_done( (current_SC->SCp.Status & 0xff)
+ | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) );
+#if EVERY_ACCESS
+ printk( "RETURNING.\n" );
+#endif
+
+ } else {
+ if (current_SC->SCp.phase & disconnect) {
+ outb( 0xd0 | FIFO_COUNT, Interrupt_Cntl_port );
+ outb( 0x00, SCSI_Cntl_port );
+ } else {
+ outb( 0x90 | FIFO_COUNT, Interrupt_Cntl_port );
+ }
+ }
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return;
+}
+
+int fdomain_16x0_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ if (in_command) {
+ panic( "fdomain: fdomain_16x0_queue() NOT REENTRANT!\n" );
+ }
+#if EVERY_ACCESS
+ printk( "queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n",
+ SCpnt->target,
+ *(unsigned char *)SCpnt->cmnd,
+ SCpnt->use_sg,
+ SCpnt->request_bufflen );
+#endif
+
+ fdomain_make_bus_idle();
+
+ current_SC = SCpnt; /* Save this for the done function */
+ current_SC->scsi_done = done;
+
+ /* Initialize static data */
+
+ if (current_SC->use_sg) {
+ current_SC->SCp.buffer =
+ (struct scatterlist *)current_SC->request_buffer;
+ current_SC->SCp.ptr = current_SC->SCp.buffer->address;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ current_SC->SCp.buffers_residual = current_SC->use_sg - 1;
+ } else {
+ current_SC->SCp.ptr = (char *)current_SC->request_buffer;
+ current_SC->SCp.this_residual = current_SC->request_bufflen;
+ current_SC->SCp.buffer = NULL;
+ current_SC->SCp.buffers_residual = 0;
+ }
+
+
+ current_SC->SCp.Status = 0;
+ current_SC->SCp.Message = 0;
+ current_SC->SCp.have_data_in = 0;
+ current_SC->SCp.sent_command = 0;
+ current_SC->SCp.phase = in_arbitration;
+
+ /* Start arbitration */
+ outb( 0x00, Interrupt_Cntl_port );
+ outb( 0x00, SCSI_Cntl_port ); /* Disable data drivers */
+ outb( adapter_mask, SCSI_Data_NoACK_port ); /* Set our id bit */
+ ++in_command;
+ outb( 0x20, Interrupt_Cntl_port );
+ outb( 0x14 | PARITY_MASK, TMC_Cntl_port ); /* Start arbitration */
+
+ return 0;
+}
+
+/* The following code, which simulates the old-style command function, was
+ taken from Tommy Thorn's aha1542.c file. This code is Copyright (C)
+ 1992 Tommy Thorn. */
+
+static volatile int internal_done_flag = 0;
+static volatile int internal_done_errcode = 0;
+
+static void internal_done( Scsi_Cmnd *SCpnt )
+{
+ internal_done_errcode = SCpnt->result;
+ ++internal_done_flag;
+}
+
+int fdomain_16x0_command( Scsi_Cmnd *SCpnt )
+{
+ fdomain_16x0_queue( SCpnt, internal_done );
+
+ while (!internal_done_flag)
+ ;
+ internal_done_flag = 0;
+ return internal_done_errcode;
+}
+
+/* End of code derived from Tommy Thorn's work. */
+
+void print_info( Scsi_Cmnd *SCpnt )
+{
+ unsigned int imr;
+ unsigned int irr;
+ unsigned int isr;
+
+ if (!SCpnt || !SCpnt->host) {
+ printk( "fdomain: cannot provide detailed information\n" );
+ }
+
+ printk( "%s\n", fdomain_16x0_info( SCpnt->host ) );
+ print_banner( SCpnt->host );
+ switch (SCpnt->SCp.phase) {
+ case in_arbitration: printk( "arbitration " ); break;
+ case in_selection: printk( "selection " ); break;
+ case in_other: printk( "other " ); break;
+ default: printk( "unknown " ); break;
+ }
+
+ printk( "(%d), target = %d cmnd = 0x%02x pieces = %d size = %u\n",
+ SCpnt->SCp.phase,
+ SCpnt->target,
+ *(unsigned char *)SCpnt->cmnd,
+ SCpnt->use_sg,
+ SCpnt->request_bufflen );
+ printk( "sent_command = %d, have_data_in = %d, timeout = %d\n",
+ SCpnt->SCp.sent_command,
+ SCpnt->SCp.have_data_in,
+ SCpnt->timeout );
+#if DEBUG_RACE
+ printk( "in_interrupt_flag = %d\n", in_interrupt_flag );
+#endif
+
+ imr = (inb( 0x0a1 ) << 8) + inb( 0x21 );
+ outb( 0x0a, 0xa0 );
+ irr = inb( 0xa0 ) << 8;
+ outb( 0x0a, 0x20 );
+ irr += inb( 0x20 );
+ outb( 0x0b, 0xa0 );
+ isr = inb( 0xa0 ) << 8;
+ outb( 0x0b, 0x20 );
+ isr += inb( 0x20 );
+
+ /* Print out interesting information */
+ printk( "IMR = 0x%04x", imr );
+ if (imr & (1 << interrupt_level))
+ printk( " (masked)" );
+ printk( ", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr );
+
+ printk( "SCSI Status = 0x%02x\n", inb( SCSI_Status_port ) );
+ printk( "TMC Status = 0x%02x", inb( TMC_Status_port ) );
+ if (inb( TMC_Status_port & 1))
+ printk( " (interrupt)" );
+ printk( "\n" );
+ printk( "Interrupt Status = 0x%02x", inb( Interrupt_Status_port ) );
+ if (inb( Interrupt_Status_port ) & 0x08)
+ printk( " (enabled)" );
+ printk( "\n" );
+ if (chip == tmc18c50 || chip == tmc18c30) {
+ printk( "FIFO Status = 0x%02x\n", inb( port_base + FIFO_Status ) );
+ printk( "Int. Condition = 0x%02x\n",
+ inb( port_base + Interrupt_Cond ) );
+ }
+ printk( "Configuration 1 = 0x%02x\n", inb( port_base + Configuration1 ) );
+ if (chip == tmc18c50 || chip == tmc18c30)
+ printk( "Configuration 2 = 0x%02x\n",
+ inb( port_base + Configuration2 ) );
+}
+
+int fdomain_16x0_abort( Scsi_Cmnd *SCpnt)
+{
+ unsigned long flags;
+#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
+ printk( "fdomain: abort " );
+#endif
+
+ save_flags( flags );
+ cli();
+ if (!in_command) {
+#if EVERY_ACCESS || ERRORS_ONLY
+ printk( " (not in command)\n" );
+#endif
+ restore_flags( flags );
+ return SCSI_ABORT_NOT_RUNNING;
+ } else printk( "\n" );
+
+#if DEBUG_ABORT
+ print_info( SCpnt );
+#endif
+
+ fdomain_make_bus_idle();
+
+ current_SC->SCp.phase |= aborted;
+
+ current_SC->result = DID_ABORT << 16;
+
+ restore_flags( flags );
+
+ /* Aborts are not done well. . . */
+ my_done( DID_ABORT << 16 );
+
+ return SCSI_ABORT_SUCCESS;
+}
+
+int fdomain_16x0_reset( Scsi_Cmnd *SCpnt, unsigned int flags )
+{
+#if DEBUG_RESET
+ static int called_once = 0;
+#endif
+
+#if ERRORS_ONLY
+ if (SCpnt) printk( "fdomain: SCSI Bus Reset\n" );
+#endif
+
+#if DEBUG_RESET
+ if (called_once) print_info( current_SC );
+ called_once = 1;
+#endif
+
+ outb( 1, SCSI_Cntl_port );
+ do_pause( 2 );
+ outb( 0, SCSI_Cntl_port );
+ do_pause( 115 );
+ outb( 0, SCSI_Mode_Cntl_port );
+ outb( PARITY_MASK, TMC_Cntl_port );
+
+ /* Unless this is the very first call (i.e., SCPnt == NULL), everything
+ is probably hosed at this point. We will, however, try to keep
+ things going by informing the high-level code that we need help. */
+
+ return SCSI_RESET_WAKEUP;
+}
+
+#include "sd.h"
+#include <scsi/scsi_ioctl.h>
+
+int fdomain_16x0_biosparam( Scsi_Disk *disk, kdev_t dev, int *info_array )
+{
+ int drive;
+ unsigned char buf[512 + sizeof( int ) * 2];
+ int size = disk->capacity;
+ int *sizes = (int *)buf;
+ unsigned char *data = (unsigned char *)(sizes + 2);
+ unsigned char do_read[] = { READ_6, 0, 0, 0, 1, 0 };
+ int retcode;
+ struct drive_info {
+ unsigned short cylinders;
+ unsigned char heads;
+ unsigned char sectors;
+ } *i;
+
+ /* NOTES:
+ The RAM area starts at 0x1f00 from the bios_base address.
+
+ For BIOS Version 2.0:
+
+ The drive parameter table seems to start at 0x1f30.
+ The first byte's purpose is not known.
+ Next is the cylinder, head, and sector information.
+ The last 4 bytes appear to be the drive's size in sectors.
+ The other bytes in the drive parameter table are unknown.
+ If anyone figures them out, please send me mail, and I will
+ update these notes.
+
+ Tape drives do not get placed in this table.
+
+ There is another table at 0x1fea:
+ If the byte is 0x01, then the SCSI ID is not in use.
+ If the byte is 0x18 or 0x48, then the SCSI ID is in use,
+ although tapes don't seem to be in this table. I haven't
+ seen any other numbers (in a limited sample).
+
+ 0x1f2d is a drive count (i.e., not including tapes)
+
+ The table at 0x1fcc are I/O ports addresses for the various
+ operations. I calculate these by hand in this driver code.
+
+
+
+ For the ISA-200S version of BIOS Version 2.0:
+
+ The drive parameter table starts at 0x1f33.
+
+ WARNING: Assume that the table entry is 25 bytes long. Someone needs
+ to check this for the Quantum ISA-200S card.
+
+
+
+ For BIOS Version 3.2:
+
+ The drive parameter table starts at 0x1f70. Each entry is
+ 0x0a bytes long. Heads are one less than we need to report.
+ */
+
+ drive = MINOR(dev) / 16;
+
+ if (bios_major == 2) {
+ switch (Quantum) {
+ case 2: /* ISA_200S */
+ /* The value of 25 has never been verified.
+ It should probably be 15. */
+ i = (struct drive_info *)( (char *)bios_base + 0x1f33 + drive * 25 );
+ break;
+ case 3: /* ISA_250MG */
+ i = (struct drive_info *)( (char *)bios_base + 0x1f36 + drive * 15 );
+ break;
+ case 4: /* ISA_200S (another one) */
+ i = (struct drive_info *)( (char *)bios_base + 0x1f34 + drive * 15 );
+ break;
+ default:
+ i = (struct drive_info *)( (char *)bios_base + 0x1f31 + drive * 25 );
+ break;
+ }
+ info_array[0] = i->heads;
+ info_array[1] = i->sectors;
+ info_array[2] = i->cylinders;
+ } else if (bios_major == 3
+ && bios_minor >= 0
+ && bios_minor < 4) { /* 3.0 and 3.2 BIOS */
+ i = (struct drive_info *)( (char *)bios_base + 0x1f71 + drive * 10 );
+ info_array[0] = i->heads + 1;
+ info_array[1] = i->sectors;
+ info_array[2] = i->cylinders;
+ } else { /* 3.4 BIOS (and up?) */
+ /* This algorithm was provided by Future Domain (much thanks!). */
+
+ sizes[0] = 0; /* zero bytes out */
+ sizes[1] = 512; /* one sector in */
+ memcpy( data, do_read, sizeof( do_read ) );
+ retcode = kernel_scsi_ioctl( disk->device,
+ SCSI_IOCTL_SEND_COMMAND,
+ (void *)buf );
+ if (!retcode /* SCSI command ok */
+ && data[511] == 0xaa && data[510] == 0x55 /* Partition table valid */
+ && data[0x1c2]) { /* Partition type */
+
+ /* The partition table layout is as follows:
+
+ Start: 0x1b3h
+ Offset: 0 = partition status
+ 1 = starting head
+ 2 = starting sector and cylinder (word, encoded)
+ 4 = partition type
+ 5 = ending head
+ 6 = ending sector and cylinder (word, encoded)
+ 8 = starting absolute sector (double word)
+ c = number of sectors (double word)
+ Signature: 0x1fe = 0x55aa
+
+ So, this algorithm assumes:
+ 1) the first partition table is in use,
+ 2) the data in the first entry is correct, and
+ 3) partitions never divide cylinders
+
+ Note that (1) may be FALSE for NetBSD (and other BSD flavors),
+ as well as for Linux. Note also, that Linux doesn't pay any
+ attention to the fields that are used by this algorithm -- it
+ only uses the absolute sector data. Recent versions of Linux's
+ fdisk(1) will fill this data in correctly, and forthcoming
+ versions will check for consistency.
+
+ Checking for a non-zero partition type is not part of the
+ Future Domain algorithm, but it seemed to be a reasonable thing
+ to do, especially in the Linux and BSD worlds. */
+
+ info_array[0] = data[0x1c3] + 1; /* heads */
+ info_array[1] = data[0x1c4] & 0x3f; /* sectors */
+ } else {
+
+ /* Note that this new method guarantees that there will always be
+ less than 1024 cylinders on a platter. This is good for drives
+ up to approximately 7.85GB (where 1GB = 1024 * 1024 kB). */
+
+ if ((unsigned int)size >= 0x7e0000U) {
+ info_array[0] = 0xff; /* heads = 255 */
+ info_array[1] = 0x3f; /* sectors = 63 */
+ } else if ((unsigned int)size >= 0x200000U) {
+ info_array[0] = 0x80; /* heads = 128 */
+ info_array[1] = 0x3f; /* sectors = 63 */
+ } else {
+ info_array[0] = 0x40; /* heads = 64 */
+ info_array[1] = 0x20; /* sectors = 32 */
+ }
+ }
+ /* For both methods, compute the cylinders */
+ info_array[2] = (unsigned int)size / (info_array[0] * info_array[1] );
+ }
+
+ return 0;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = FDOMAIN_16X0;
+
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/fdomain.h b/linux/src/drivers/scsi/fdomain.h
new file mode 100644
index 00000000..bea9998b
--- /dev/null
+++ b/linux/src/drivers/scsi/fdomain.h
@@ -0,0 +1,61 @@
+/* fdomain.h -- Header for Future Domain TMC-16x0 driver
+ * Created: Sun May 3 18:47:33 1992 by faith@cs.unc.edu
+ * Revised: Thu Oct 12 13:21:35 1995 by r.faith@ieee.org
+ * Author: Rickard E. Faith, faith@cs.unc.edu
+ * Copyright 1992, 1993, 1994, 1995 Rickard E. Faith
+ *
+ * $Id: fdomain.h,v 1.1 1999/04/26 05:54:33 tb Exp $
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#ifndef _FDOMAIN_H
+#define _FDOMAIN_H
+
+int fdomain_16x0_detect( Scsi_Host_Template * );
+int fdomain_16x0_command( Scsi_Cmnd * );
+int fdomain_16x0_abort( Scsi_Cmnd * );
+const char *fdomain_16x0_info( struct Scsi_Host * );
+int fdomain_16x0_reset( Scsi_Cmnd *, unsigned int );
+int fdomain_16x0_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
+int fdomain_16x0_biosparam( Disk *, kdev_t, int * );
+int fdomain_16x0_proc_info( char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout );
+
+extern struct proc_dir_entry proc_scsi_fdomain;
+
+#define FDOMAIN_16X0 { NULL, \
+ NULL, \
+ NULL, \
+ fdomain_16x0_proc_info, \
+ NULL, \
+ fdomain_16x0_detect, \
+ NULL, \
+ fdomain_16x0_info, \
+ fdomain_16x0_command, \
+ fdomain_16x0_queue, \
+ fdomain_16x0_abort, \
+ fdomain_16x0_reset, \
+ NULL, \
+ fdomain_16x0_biosparam, \
+ 1, \
+ 6, \
+ 64, \
+ 1, \
+ 0, \
+ 0, \
+ DISABLE_CLUSTERING }
+#endif
diff --git a/linux/src/drivers/scsi/g_NCR5380.c b/linux/src/drivers/scsi/g_NCR5380.c
new file mode 100644
index 00000000..0d05c65a
--- /dev/null
+++ b/linux/src/drivers/scsi/g_NCR5380.c
@@ -0,0 +1,733 @@
+/*
+ * Generic Generic NCR5380 driver
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
+ * K.Lentin@cs.monash.edu.au
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * TODO : flesh out DMA support, find some one actually using this (I have
+ * a memory mapped Trantor board that works fine)
+ */
+
+/*
+ * Options :
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
+ *
+ * USLEEP - enable support for devices that don't disconnect. Untested.
+ *
+ * The card is detected and initialized in one of several ways :
+ * 1. With command line overrides - NCR5380=port,irq may be
+ * used on the LILO command line to override the defaults.
+ *
+ * 2. With the GENERIC_NCR5380_OVERRIDE compile time define. This is
+ * specified as an array of address, irq, dma, board tuples. Ie, for
+ * one board at 0x350, IRQ5, no dma, I could say
+ * -DGENERIC_NCR5380_OVERRIDE={{0xcc000, 5, DMA_NONE, BOARD_NCR5380}}
+ *
+ * -1 should be specified for no or DMA interrupt, -2 to autoprobe for an
+ * IRQ line if overridden on the command line.
+ *
+ * 3. When included as a module, with arguments passed on the command line:
+ * ncr_irq=xx the interrupt
+ * ncr_addr=xx the port or base address (for port or memory
+ * mapped, resp.)
+ * ncr_dma=xx the DMA
+ * ncr_5380=1 to set up for a NCR5380 board
+ * ncr_53c400=1 to set up for a NCR53C400 board
+ * e.g.
+ * modprobe g_NCR5380 ncr_irq=5 ncr_addr=0x350 ncr_5380=1
+ * for a port mapped NCR5380 board or
+ * modprobe g_NCR5380 ncr_irq=255 ncr_addr=0xc8000 ncr_53c400=1
+ * for a memory mapped NCR53C400 board with interrupts disabled.
+ *
+ * 255 should be specified for no or DMA interrupt, 254 to autoprobe for an
+ * IRQ line if overridden on the command line.
+ *
+ */
+
+/*
+ * $Log: generic_NCR5380.c,v $
+ */
+
+#define AUTOPROBE_IRQ
+#define AUTOSENSE
+
+#include <linux/config.h>
+
+#ifdef CONFIG_SCSI_GENERIC_NCR53C400
+#define NCR53C400_PSEUDO_DMA 1
+#define PSEUDO_DMA
+#define NCR53C400
+#define NCR5380_STATS
+#undef NCR5380_STAT_LIMIT
+#endif
+#if defined(CONFIG_SCSI_G_NCR5380_PORT) && defined(CONFIG_SCSI_G_NCR5380_MEM)
+#error You can not configure the Generic NCR 5380 SCSI Driver for memory mapped I/O and port mapped I/O at the same time (yet)
+#endif
+#if !defined(CONFIG_SCSI_G_NCR5380_PORT) && !defined(CONFIG_SCSI_G_NCR5380_MEM)
+#error You must configure the Generic NCR 5380 SCSI Driver for one of memory mapped I/O and port mapped I/O.
+#endif
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "g_NCR5380.h"
+#include "NCR5380.h"
+#include "constants.h"
+#include "sd.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_g_ncr5380 = {
+ PROC_SCSI_GENERIC_NCR5380, 9, "g_NCR5380",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#define NCR_NOT_SET 0
+static int ncr_irq=NCR_NOT_SET;
+static int ncr_dma=NCR_NOT_SET;
+static int ncr_addr=NCR_NOT_SET;
+static int ncr_5380=NCR_NOT_SET;
+static int ncr_53c400=NCR_NOT_SET;
+
+static struct override {
+ NCR5380_implementation_fields;
+ int irq;
+ int dma;
+ int board; /* Use NCR53c400, Ricoh, etc. extensions ? */
+} overrides
+#ifdef GENERIC_NCR5380_OVERRIDE
+ [] = GENERIC_NCR5380_OVERRIDE
+#else
+ [1] = {{0,},};
+#endif
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+/*
+ * Function : static internal_setup(int board, char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : board - either BOARD_NCR5380 for a normal NCR5380 board,
+ * or BOARD_NCR53C400 for a NCR53C400 board. str - unused, ints -
+ * array of integer parameters with ints[0] equal to the number of ints.
+ *
+ */
+
+static void internal_setup(int board, char *str, int *ints) {
+ static int commandline_current = 0;
+ switch (board) {
+ case BOARD_NCR5380:
+ if (ints[0] != 2 && ints[0] != 3) {
+ printk("generic_NCR5380_setup : usage ncr5380=" STRVAL(NCR5380_map_name) ",irq,dma\n");
+ return;
+ }
+ case BOARD_NCR53C400:
+ if (ints[0] != 2) {
+ printk("generic_NCR53C400_setup : usage ncr53c400=" STRVAL(NCR5380_map_name) ",irq\n");
+ return;
+ }
+ }
+
+ if (commandline_current < NO_OVERRIDES) {
+ overrides[commandline_current].NCR5380_map_name = (NCR5380_map_type)ints[1];
+ overrides[commandline_current].irq = ints[2];
+ if (ints[0] == 3)
+ overrides[commandline_current].dma = ints[3];
+ else
+ overrides[commandline_current].dma = DMA_NONE;
+ overrides[commandline_current].board = board;
+ ++commandline_current;
+ }
+}
+
+/*
+ * Function : generic_NCR5380_setup (char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ */
+
+void generic_NCR5380_setup (char *str, int *ints) {
+ internal_setup (BOARD_NCR5380, str, ints);
+}
+
+/*
+ * Function : generic_NCR53C400_setup (char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ */
+
+void generic_NCR53C400_setup (char *str, int *ints) {
+ internal_setup (BOARD_NCR53C400, str, ints);
+}
+
+/*
+ * Function : int generic_NCR5380_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes generic NCR5380 driver based on the
+ * command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int generic_NCR5380_detect(Scsi_Host_Template * tpnt) {
+ static int current_override = 0;
+ int count;
+ int flags = 0;
+ struct Scsi_Host *instance;
+
+ if (ncr_irq != NCR_NOT_SET)
+ overrides[0].irq=ncr_irq;
+ if (ncr_dma != NCR_NOT_SET)
+ overrides[0].dma=ncr_dma;
+ if (ncr_addr != NCR_NOT_SET)
+ overrides[0].NCR5380_map_name=(NCR5380_map_type)ncr_addr;
+ if (ncr_5380 != NCR_NOT_SET)
+ overrides[0].board=BOARD_NCR5380;
+ else if (ncr_53c400 != NCR_NOT_SET)
+ overrides[0].board=BOARD_NCR53C400;
+
+ tpnt->proc_dir = &proc_scsi_g_ncr5380;
+
+ for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+ if (!(overrides[current_override].NCR5380_map_name))
+ continue;
+
+ switch (overrides[current_override].board) {
+ case BOARD_NCR5380:
+ flags = FLAG_NO_PSEUDO_DMA;
+ break;
+ case BOARD_NCR53C400:
+ flags = FLAG_NCR53C400;
+ break;
+ }
+
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+ instance->NCR5380_instance_name = overrides[current_override].NCR5380_map_name;
+
+ NCR5380_init(instance, flags);
+
+ if (overrides[current_override].irq != IRQ_AUTO)
+ instance->irq = overrides[current_override].irq;
+ else
+ instance->irq = NCR5380_probe_irq(instance, 0xffff);
+
+ if (instance->irq != IRQ_NONE)
+ if (request_irq(instance->irq, generic_NCR5380_intr, SA_INTERRUPT, "NCR5380", NULL)) {
+ printk("scsi%d : IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = IRQ_NONE;
+ }
+
+ if (instance->irq == IRQ_NONE) {
+ printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+ printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ }
+
+ printk("scsi%d : at " STRVAL(NCR5380_map_name) " 0x%x", instance->host_no, (unsigned int)instance->NCR5380_instance_name);
+ if (instance->irq == IRQ_NONE)
+ printk (" interrupts disabled");
+ else
+ printk (" irq %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ CAN_QUEUE, CMD_PER_LUN, GENERIC_NCR5380_PUBLIC_RELEASE);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ ++current_override;
+ ++count;
+ }
+ return count;
+}
+
+const char * generic_NCR5380_info (struct Scsi_Host* host) {
+ static const char string[]="Generic NCR5380/53C400 Driver";
+ return string;
+}
+
+int generic_NCR5380_release_resources(struct Scsi_Host * instance)
+{
+ NCR5380_local_declare();
+
+ NCR5380_setup(instance);
+
+ if (instance->irq != IRQ_NONE)
+ free_irq(instance->irq, NULL);
+
+ return 0;
+}
+
+#ifdef BIOSPARAM
+/*
+ * Function : int generic_NCR5380_biosparam(Disk * disk, kdev_t dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for
+ * the specified device / size.
+ *
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ * major / minor, ip[] = {heads, sectors, cylinders}
+ *
+ * Returns : always 0 (success), initializes ip
+ *
+ */
+
+/*
+ * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+ */
+
+int generic_NCR5380_biosparam(Disk * disk, kdev_t dev, int *ip)
+{
+ int size = disk->capacity;
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ return 0;
+}
+#endif
+
+#if NCR53C400_PSEUDO_DMA
+static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst, int len)
+{
+ int blocks = len / 128;
+ int start = 0;
+ int i;
+ int bl;
+ NCR5380_local_declare();
+
+ NCR5380_setup(instance);
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: About to read %d blocks for %d bytes\n", blocks, len);
+#endif
+
+ NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE | CSR_TRANS_DIR);
+ NCR5380_write(C400_BLOCK_COUNTER_REG, blocks);
+ while (1) {
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: %d blocks left\n", blocks);
+#endif
+
+ if ((bl=NCR5380_read(C400_BLOCK_COUNTER_REG)) == 0) {
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ if (blocks)
+ printk("53C400r: blocks still == %d\n", blocks);
+ else
+ printk("53C400r: Exiting loop\n");
+#endif
+ break;
+ }
+
+#if 1
+ if (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ) {
+ printk("53C400r: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
+ return -1;
+ }
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: Waiting for buffer, bl=%d\n", bl);
+#endif
+
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ;
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: Transferring 128 bytes\n");
+#endif
+
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+ for (i=0; i<128; i++)
+ dst[start+i] = NCR5380_read(C400_HOST_BUFFER);
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ memmove(dst+start,NCR53C400_host_buffer+NCR5380_map_name,128);
+#endif
+ start+=128;
+ blocks--;
+ }
+
+ if (blocks) {
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: EXTRA: Waiting for buffer\n");
+#endif
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ;
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: Transferring EXTRA 128 bytes\n");
+#endif
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+ for (i=0; i<128; i++)
+ dst[start+i] = NCR5380_read(C400_HOST_BUFFER);
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ memmove(dst+start,NCR53C400_host_buffer+NCR5380_map_name,128);
+#endif
+ start+=128;
+ blocks--;
+ }
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ else
+ printk("53C400r: No EXTRA required\n");
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: Final values: blocks=%d start=%d\n", blocks, start);
+#endif
+
+ if (!(NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ))
+ printk("53C400r: no 53C80 gated irq after transfer");
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ else
+ printk("53C400r: Got 53C80 interrupt and tried to clear it\n");
+#endif
+
+/* DON'T DO THIS - THEY NEVER ARRIVE!
+ printk("53C400r: Waiting for 53C80 registers\n");
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_53C80_REG)
+ ;
+*/
+
+ if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER))
+ printk("53C400r: no end dma signal\n");
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ else
+ printk("53C400r: end dma as expected\n");
+#endif
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ return 0;
+}
+
+static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src, int len)
+{
+ int blocks = len / 128;
+ int start = 0;
+ int i;
+ int bl;
+ NCR5380_local_declare();
+
+ NCR5380_setup(instance);
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: About to write %d blocks for %d bytes\n", blocks, len);
+#endif
+
+ NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
+ NCR5380_write(C400_BLOCK_COUNTER_REG, blocks);
+ while (1) {
+ if (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ) {
+ printk("53C400w: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
+ return -1;
+ }
+
+ if ((bl=NCR5380_read(C400_BLOCK_COUNTER_REG)) == 0) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ if (blocks)
+ printk("53C400w: exiting loop, blocks still == %d\n", blocks);
+ else
+ printk("53C400w: exiting loop\n");
+#endif
+ break;
+ }
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: %d blocks left\n", blocks);
+
+ printk("53C400w: waiting for buffer, bl=%d\n", bl);
+#endif
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ;
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: transferring 128 bytes\n");
+#endif
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+ for (i=0; i<128; i++)
+ NCR5380_write(C400_HOST_BUFFER, src[start+i]);
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ memmove(NCR53C400_host_buffer+NCR5380_map_name,src+start,128);
+#endif
+ start+=128;
+ blocks--;
+ }
+ if (blocks) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: EXTRA waiting for buffer\n");
+#endif
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ;
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: transferring EXTRA 128 bytes\n");
+#endif
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+ for (i=0; i<128; i++)
+ NCR5380_write(C400_HOST_BUFFER, src[start+i]);
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ memmove(NCR53C400_host_buffer+NCR5380_map_name,src+start,128);
+#endif
+ start+=128;
+ blocks--;
+ }
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ else
+ printk("53C400w: No EXTRA required\n");
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: Final values: blocks=%d start=%d\n", blocks, start);
+#endif
+
+#if 0
+ printk("53C400w: waiting for registers to be available\n");
+ THEY NEVER DO!
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_53C80_REG)
+ ;
+ printk("53C400w: Got em\n");
+#endif
+
+ /* Let's wait for this instead - could be ugly */
+ /* All documentation says to check for this. Maybe my hardware is too
+ * fast. Waiting for it seems to work fine! KLL
+ */
+ while (!(i = NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ))
+ ;
+
+ /*
+ * I know. i is certainly != 0 here but the loop is new. See previous
+ * comment.
+ */
+ if (i) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: got 53C80 gated irq (last block)\n");
+#endif
+ if (!((i=NCR5380_read(BUS_AND_STATUS_REG)) & BASR_END_DMA_TRANSFER))
+ printk("53C400w: No END OF DMA bit - WHOOPS! BASR=%0x\n",i);
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ else
+ printk("53C400w: Got END OF DMA\n");
+#endif
+ }
+ else
+ printk("53C400w: no 53C80 gated irq after transfer (last block)\n");
+
+#if 0
+ if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER)) {
+ printk("53C400w: no end dma signal\n");
+ }
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: waiting for last byte...\n");
+#endif
+ while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT))
+ ;
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: got last byte.\n");
+ printk("53C400w: pwrite exiting with status 0, whoopee!\n");
+#endif
+ return 0;
+}
+#endif /* PSEUDO_DMA */
+
+#include "NCR5380.c"
+
+#define PRINTP(x) len += sprintf(buffer+len, x)
+#define ANDP ,
+
+static int sprint_opcode(char* buffer, int len, int opcode) {
+ int start = len;
+ PRINTP("0x%02x " ANDP opcode);
+ return len-start;
+}
+
+static int sprint_command (char* buffer, int len, unsigned char *command) {
+ int i,s,start=len;
+ len += sprint_opcode(buffer, len, command[0]);
+ for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+ PRINTP("%02x " ANDP command[i]);
+ PRINTP("\n");
+ return len-start;
+}
+
+static int sprint_Scsi_Cmnd (char* buffer, int len, Scsi_Cmnd *cmd) {
+ int start = len;
+ PRINTP("host number %d destination target %d, lun %d\n" ANDP
+ cmd->host->host_no ANDP
+ cmd->target ANDP
+ cmd->lun);
+ PRINTP(" command = ");
+ len += sprint_command (buffer, len, cmd->cmnd);
+ return len-start;
+}
+
+int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int length, int hostno, int inout)
+{
+ int len = 0;
+ NCR5380_local_declare();
+ unsigned char status;
+ int i;
+ struct Scsi_Host *scsi_ptr;
+ Scsi_Cmnd *ptr;
+ Scsi_Device *dev;
+ struct NCR5380_hostdata *hostdata;
+
+ cli();
+
+ for (scsi_ptr = first_instance; scsi_ptr; scsi_ptr=scsi_ptr->next)
+ if (scsi_ptr->host_no == hostno)
+ break;
+ NCR5380_setup(scsi_ptr);
+ hostdata = (struct NCR5380_hostdata *)scsi_ptr->hostdata;
+
+ PRINTP("SCSI host number %d : %s\n" ANDP scsi_ptr->host_no ANDP scsi_ptr->hostt->name);
+ PRINTP("Generic NCR5380 driver version %d\n" ANDP GENERIC_NCR5380_PUBLIC_RELEASE);
+ PRINTP("NCR5380 core version %d\n" ANDP NCR5380_PUBLIC_RELEASE);
+#ifdef NCR53C400
+ PRINTP("NCR53C400 extension version %d\n" ANDP NCR53C400_PUBLIC_RELEASE);
+ PRINTP("NCR53C400 card%s detected\n" ANDP (((struct NCR5380_hostdata *)scsi_ptr->hostdata)->flags & FLAG_NCR53C400)?"":" not");
+# if NCR53C400_PSEUDO_DMA
+ PRINTP("NCR53C400 pseudo DMA used\n");
+# endif
+#else
+ PRINTP("NO NCR53C400 driver extensions\n");
+#endif
+ PRINTP("Using %s mapping at %s 0x%x, " ANDP STRVAL(NCR5380_map_config) ANDP STRVAL(NCR5380_map_name) ANDP scsi_ptr->NCR5380_instance_name);
+ if (scsi_ptr->irq == IRQ_NONE)
+ PRINTP("no interrupt\n");
+ else
+ PRINTP("on interrupt %d\n" ANDP scsi_ptr->irq);
+
+#ifdef NCR5380_STATS
+ if (hostdata->connected || hostdata->issue_queue || hostdata->disconnected_queue)
+ PRINTP("There are commands pending, transfer rates may be crud\n");
+ if (hostdata->pendingr)
+ PRINTP(" %d pending reads" ANDP hostdata->pendingr);
+ if (hostdata->pendingw)
+ PRINTP(" %d pending writes" ANDP hostdata->pendingw);
+ if (hostdata->pendingr || hostdata->pendingw)
+ PRINTP("\n");
+ for (dev = scsi_devices; dev; dev=dev->next) {
+ if (dev->host == scsi_ptr) {
+ unsigned long br = hostdata->bytes_read[dev->id];
+ unsigned long bw = hostdata->bytes_write[dev->id];
+ long tr = hostdata->time_read[dev->id] / HZ;
+ long tw = hostdata->time_write[dev->id] / HZ;
+
+ PRINTP(" T:%d %s " ANDP dev->id ANDP (dev->type < MAX_SCSI_DEVICE_CODE) ? scsi_device_types[(int)dev->type] : "Unknown");
+ for (i=0; i<8; i++)
+ if (dev->vendor[i] >= 0x20)
+ *(buffer+(len++)) = dev->vendor[i];
+ *(buffer+(len++)) = ' ';
+ for (i=0; i<16; i++)
+ if (dev->model[i] >= 0x20)
+ *(buffer+(len++)) = dev->model[i];
+ *(buffer+(len++)) = ' ';
+ for (i=0; i<4; i++)
+ if (dev->rev[i] >= 0x20)
+ *(buffer+(len++)) = dev->rev[i];
+ *(buffer+(len++)) = ' ';
+
+ PRINTP("\n%10ld kb read in %5ld secs" ANDP br/1024 ANDP tr);
+ if (tr)
+ PRINTP(" @ %5ld bps" ANDP br / tr);
+
+ PRINTP("\n%10ld kb written in %5ld secs" ANDP bw/1024 ANDP tw);
+ if (tw)
+ PRINTP(" @ %5ld bps" ANDP bw / tw);
+ PRINTP("\n");
+ }
+ }
+#endif
+
+ status = NCR5380_read(STATUS_REG);
+ if (!(status & SR_REQ))
+ PRINTP("REQ not asserted, phase unknown.\n");
+ else {
+ for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
+ (phases[i].value != (status & PHASE_MASK)); ++i)
+ ;
+ PRINTP("Phase %s\n" ANDP phases[i].name);
+ }
+
+ if (!hostdata->connected) {
+ PRINTP("No currently connected command\n");
+ } else {
+ len += sprint_Scsi_Cmnd (buffer, len, (Scsi_Cmnd *) hostdata->connected);
+ }
+
+ PRINTP("issue_queue\n");
+
+ for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr;
+ ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ len += sprint_Scsi_Cmnd (buffer, len, ptr);
+
+ PRINTP("disconnected_queue\n");
+
+ for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
+ ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ len += sprint_Scsi_Cmnd (buffer, len, ptr);
+
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ sti();
+ return len;
+}
+
+#undef PRINTP
+#undef ANDP
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = GENERIC_NCR5380;
+
+#include <linux/module.h>
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/g_NCR5380.h b/linux/src/drivers/scsi/g_NCR5380.h
new file mode 100644
index 00000000..2730723e
--- /dev/null
+++ b/linux/src/drivers/scsi/g_NCR5380.h
@@ -0,0 +1,166 @@
+/*
+ * Generic Generic NCR5380 driver defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
+ * K.Lentin@cs.monash.edu.au
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: generic_NCR5380.h,v $
+ */
+
+#ifndef GENERIC_NCR5380_H
+#define GENERIC_NCR5380_H
+
+#include <linux/config.h>
+
+#define GENERIC_NCR5380_PUBLIC_RELEASE 1
+
+#ifdef NCR53C400
+#define BIOSPARAM
+#define NCR5380_BIOSPARAM generic_NCR5380_biosparam
+#else
+#define NCR5380_BIOSPARAM NULL
+#endif
+
+#ifndef ASM
+int generic_NCR5380_abort(Scsi_Cmnd *);
+int generic_NCR5380_detect(Scsi_Host_Template *);
+int generic_NCR5380_release_resources(struct Scsi_Host *);
+int generic_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int generic_NCR5380_reset(Scsi_Cmnd *, unsigned int);
+int notyet_generic_proc_info (char *buffer ,char **start, off_t offset,
+ int length, int hostno, int inout);
+const char* generic_NCR5380_info(struct Scsi_Host *);
+#ifdef BIOSPARAM
+int generic_NCR5380_biosparam(Disk *, kdev_t, int *);
+#endif
+
+int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#if defined(HOSTS_C) || defined(MODULE)
+
+#define GENERIC_NCR5380 {NULL, NULL, NULL, \
+ generic_NCR5380_proc_info, \
+ "Generic NCR5380/NCR53C400 Scsi Driver", \
+ generic_NCR5380_detect, generic_NCR5380_release_resources, \
+ (void *)generic_NCR5380_info, NULL, \
+ generic_NCR5380_queue_command, generic_NCR5380_abort, \
+ generic_NCR5380_reset, NULL, \
+ NCR5380_BIOSPARAM, \
+ /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \
+ /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
+
+#endif
+
+#ifndef HOSTS_C
+
+#define __STRVAL(x) #x
+#define STRVAL(x) __STRVAL(x)
+
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+
+#define NCR5380_map_config port
+
+#define NCR5380_map_type int
+
+#define NCR5380_map_name port
+
+#define NCR5380_instance_name io_port
+
+#define NCR53C400_register_offset 0
+
+#define NCR53C400_address_adjust 8
+
+#ifdef NCR53C400
+#define NCR5380_region_size 16
+#else
+#define NCR5380_region_size 8
+#endif
+
+#define NCR5380_read(reg) (inb(NCR5380_map_name + (reg)))
+#define NCR5380_write(reg, value) (outb((value), (NCR5380_map_name + (reg))))
+
+#else
+/* therefore CONFIG_SCSI_G_NCR5380_MEM */
+
+#define NCR5380_map_config memory
+
+#define NCR5380_map_type volatile unsigned char*
+
+#define NCR5380_map_name base
+
+#define NCR5380_instance_name base
+
+#define NCR53C400_register_offset 0x108
+
+#define NCR53C400_address_adjust 0
+
+#define NCR53C400_mem_base 0x3880
+
+#define NCR53C400_host_buffer 0x3900
+
+#define NCR5380_region_size 0x3a00
+
+
+#define NCR5380_read(reg) (*(NCR5380_map_name + NCR53C400_mem_base + (reg)))
+#define NCR5380_write(reg, value) (*(NCR5380_map_name + NCR53C400_mem_base + (reg)) = value)
+
+#endif
+
+#define NCR5380_implementation_fields \
+ NCR5380_map_type NCR5380_map_name
+
+#define NCR5380_local_declare() \
+ register NCR5380_implementation_fields
+
+#define NCR5380_setup(instance) \
+ NCR5380_map_name = (NCR5380_map_type)((instance)->NCR5380_instance_name)
+
+#define NCR5380_intr generic_NCR5380_intr
+#define NCR5380_queue_command generic_NCR5380_queue_command
+#define NCR5380_abort generic_NCR5380_abort
+#define NCR5380_reset generic_NCR5380_reset
+#define NCR5380_pread generic_NCR5380_pread
+#define NCR5380_pwrite generic_NCR5380_pwrite
+#define NCR5380_proc_info notyet_generic_proc_info
+
+#define BOARD_NCR5380 0
+#define BOARD_NCR53C400 1
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* GENERIC_NCR5380_H */
+
diff --git a/linux/src/drivers/scsi/gdth.c b/linux/src/drivers/scsi/gdth.c
new file mode 100644
index 00000000..a150b873
--- /dev/null
+++ b/linux/src/drivers/scsi/gdth.c
@@ -0,0 +1,3598 @@
+/************************************************************************
+ * GDT ISA/EISA/PCI Disk Array Controller driver for Linux *
+ * *
+ * gdth.c *
+ * Copyright (C) 1995-98 ICP vortex Computersysteme GmbH, Achim Leubner *
+ * *
+ * <achim@vortex.de> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published *
+ * by the Free Software Foundation; either version 2 of the License, *
+ * or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this kernel; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ * Tested with Linux 1.2.13, ..., 2.1.103 *
+ * *
+ * $Log: gdth.c,v $
+ * Revision 1.16 1998/09/28 16:08:46 achim
+ * GDT_PCIMPR: DPMEM remapping, if required
+ * mdelay() added
+ *
+ * Revision 1.15 1998/06/03 14:54:06 achim
+ * gdth_delay(), gdth_flush() implemented
+ * Bugfix: gdth_release() changed
+ *
+ * Revision 1.14 1998/05/22 10:01:17 achim
+ * mj: pcibios_strerror() removed
+ * Improved SMP support (if version >= 2.1.95)
+ * gdth_halt(): halt_called flag added (if version < 2.1)
+ *
+ * Revision 1.13 1998/04/16 09:14:57 achim
+ * Reserve drives (for raw service) implemented
+ * New error handling code enabled
+ * Get controller name from board_info() IOCTL
+ * Final round of PCI device driver patches by Martin Mares
+ *
+ * Revision 1.12 1998/03/03 09:32:37 achim
+ * Fibre channel controller support added
+ *
+ * Revision 1.11 1998/01/27 16:19:14 achim
+ * SA_SHIRQ added
+ * add_timer()/del_timer() instead of GDTH_TIMER
+ * scsi_add_timer()/scsi_del_timer() instead of SCSI_TIMER
+ * New error handling included
+ *
+ * Revision 1.10 1997/10/31 12:29:57 achim
+ * Read heads/sectors from host drive
+ *
+ * Revision 1.9 1997/09/04 10:07:25 achim
+ * IO-mapping with virt_to_bus(), readb(), writeb(), ...
+ * register_reboot_notifier() to get a notify on shutdown used
+ *
+ * Revision 1.8 1997/04/02 12:14:30 achim
+ * Version 1.00 (see gdth.h), tested with kernel 2.0.29
+ *
+ * Revision 1.7 1997/03/12 13:33:37 achim
+ * gdth_reset() changed, new async. events
+ *
+ * Revision 1.6 1997/03/04 14:01:11 achim
+ * Shutdown routine gdth_halt() implemented
+ *
+ * Revision 1.5 1997/02/21 09:08:36 achim
+ * New controller included (RP, RP1, RP2 series)
+ * IOCTL interface implemented
+ *
+ * Revision 1.4 1996/07/05 12:48:55 achim
+ * Function gdth_bios_param() implemented
+ * New constant GDTH_MAXC_P_L inserted
+ * GDT_WRITE_THR, GDT_EXT_INFO implemented
+ * Function gdth_reset() changed
+ *
+ * Revision 1.3 1996/05/10 09:04:41 achim
+ * Small changes for Linux 1.2.13
+ *
+ * Revision 1.2 1996/05/09 12:45:27 achim
+ * Loadable module support implemented
+ * /proc support corrections made
+ *
+ * Revision 1.1 1996/04/11 07:35:57 achim
+ * Initial revision
+ *
+ *
+ * $Id: gdth.c,v 1.1 1999/04/26 05:54:35 tb Exp $
+ ************************************************************************/
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/proc_fs.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#if LINUX_VERSION_CODE >= 0x020100
+#include <linux/reboot.h>
+#else
+#include <linux/bios32.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#if LINUX_VERSION_CODE >= 0x02015F
+#include <asm/spinlock.h>
+#endif
+
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/blk.h>
+#else
+#include "../block/blk.h"
+#endif
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#include "gdth.h"
+
+/****************************************************************************/
+
+/* LILO params: gdth=<IRQ>
+ *
+ * Where: <IRQ> is any of the valid IRQs for EISA controllers (10,11,12,14)
+ * Sets the IRQ of the GDT3000/3020 EISA controller to this value,
+ * if the IRQ can not automat. detect (controller BIOS disabled)
+ * See gdth_init_eisa()
+ *
+ * You can use the command line gdth=0 to disable the driver
+ */
+static unchar irqs[MAXHA] = {0xff};
+static unchar disable_gdth_scan = FALSE;
+
+/* Reserve drives for raw service: Fill the following structure with the
+ * appropriate values: Controller number, Channel, Target ID
+ */
+static gdth_reserve_str reserve_list[] = {
+ /* { 0, 1, 4 }, Example: Controller 0, Channel B, ID 4 */
+ { 0xff, 0xff, 0xff } /* end of list */
+};
+
+/****************************************************************************/
+
+#if LINUX_VERSION_CODE >= 0x02015F
+static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+static void do_gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+#elif LINUX_VERSION_CODE >= 0x010346
+static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+#else
+static void gdth_interrupt(int irq,struct pt_regs *regs);
+#endif
+static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp);
+static int gdth_async_event(int hanum,int service);
+
+static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority);
+static void gdth_next(int hanum);
+static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b);
+static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b);
+static gdth_evt_str *gdth_store_event(ushort source, ushort idx,
+ gdth_evt_data *evt);
+static int gdth_read_event(int handle, gdth_evt_str *estr);
+static void gdth_readapp_event(unchar application, gdth_evt_str *estr);
+static void gdth_clear_events(void);
+
+static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count);
+static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp,
+ unchar b,ulong *flags);
+static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive);
+
+static int gdth_search_eisa(ushort eisa_adr);
+static int gdth_search_isa(ulong bios_adr);
+static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr);
+static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha);
+static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha);
+static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha);
+
+static void gdth_enable_int(int hanum);
+static int gdth_get_status(unchar *pIStatus,int irq);
+static int gdth_test_busy(int hanum);
+static int gdth_get_cmd_index(int hanum);
+static void gdth_release_event(int hanum);
+static int gdth_wait(int hanum,int index,ulong time);
+static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1,
+ ulong p2,ulong p3);
+static int gdth_search_drives(int hanum);
+
+static void *gdth_mmap(ulong paddr, ulong size);
+static void gdth_munmap(void *addr);
+
+static const char *gdth_ctr_name(int hanum);
+
+static void gdth_flush(int hanum);
+#if LINUX_VERSION_CODE >= 0x020100
+static int gdth_halt(struct notifier_block *nb, ulong event, void *buf);
+#else
+static int halt_called = FALSE;
+void gdth_halt(void);
+#endif
+
+#ifdef DEBUG_GDTH
+static unchar DebugState = DEBUG_GDTH;
+extern int sys_syslog(int,char*,int);
+#define LOGEN sys_syslog(7,NULL,0)
+
+#ifdef __SERIAL__
+#define MAX_SERBUF 160
+static void ser_init(void);
+static void ser_puts(char *str);
+static void ser_putc(char c);
+static int ser_printk(const char *fmt, ...);
+static char strbuf[MAX_SERBUF+1];
+#ifdef __COM2__
+#define COM_BASE 0x2f8
+#else
+#define COM_BASE 0x3f8
+#endif
+static void ser_init()
+{
+ unsigned port=COM_BASE;
+
+ outb(0x80,port+3);
+ outb(0,port+1);
+ /* 19200 Baud, if 9600: outb(12,port) */
+ outb(6, port);
+ outb(3,port+3);
+ outb(0,port+1);
+ /*
+ ser_putc('I');
+ ser_putc(' ');
+ */
+}
+
+static void ser_puts(char *str)
+{
+ char *ptr;
+
+ ser_init();
+ for (ptr=str;*ptr;++ptr)
+ ser_putc(*ptr);
+}
+
+static void ser_putc(char c)
+{
+ unsigned port=COM_BASE;
+
+ while ((inb(port+5) & 0x20)==0);
+ outb(c,port);
+ if (c==0x0a)
+ {
+ while ((inb(port+5) & 0x20)==0);
+ outb(0x0d,port);
+ }
+}
+
+static int ser_printk(const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args,fmt);
+ i = vsprintf(strbuf,fmt,args);
+ ser_puts(strbuf);
+ va_end(args);
+ return i;
+}
+
+#define TRACE(a) {if (DebugState==1) {ser_printk a;}}
+#define TRACE2(a) {if (DebugState==1 || DebugState==2) {ser_printk a;}}
+#define TRACE3(a) {if (DebugState!=0) {ser_printk a;}}
+
+#else /* !__SERIAL__ */
+#define TRACE(a) {if (DebugState==1) {LOGEN;printk a;}}
+#define TRACE2(a) {if (DebugState==1 || DebugState==2) {LOGEN;printk a;}}
+#define TRACE3(a) {if (DebugState!=0) {LOGEN;printk a;}}
+#endif
+
+#else /* !DEBUG */
+#define TRACE(a)
+#define TRACE2(a)
+#define TRACE3(a)
+#endif
+
+#ifdef GDTH_STATISTICS
+static ulong max_rq=0, max_index=0, max_sg=0;
+static ulong act_ints=0, act_ios=0, act_stats=0, act_rq=0;
+static struct timer_list gdth_timer;
+#endif
+
+#define PTR2USHORT(a) (ushort)(ulong)(a)
+#define GDTOFFSOF(a,b) (size_t)&(((a*)0)->b)
+#define INDEX_OK(i,t) ((i)<sizeof(t)/sizeof((t)[0]))
+
+#define NUMDATA(a) ( (gdth_num_str *)((a)->hostdata))
+#define HADATA(a) (&((gdth_ext_str *)((a)->hostdata))->haext)
+#define CMDDATA(a) (&((gdth_ext_str *)((a)->hostdata))->cmdext)
+#define DMADATA(a) (&((gdth_ext_str *)((a)->hostdata))->dmaext)
+
+
+#if LINUX_VERSION_CODE < 0x010300
+static void *gdth_mmap(ulong paddr, ulong size)
+{
+ if (paddr >= high_memory)
+ return NULL;
+ else
+ return (void *)paddr;
+}
+static void gdth_munmap(void *addr)
+{
+}
+inline ulong virt_to_phys(volatile void *addr)
+{
+ return (ulong)addr;
+}
+inline void *phys_to_virt(ulong addr)
+{
+ return (void *)addr;
+}
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
+#define readb(addr) (*(volatile unchar *)(addr))
+#define readw(addr) (*(volatile ushort *)(addr))
+#define readl(addr) (*(volatile ulong *)(addr))
+#define writeb(b,addr) (*(volatile unchar *)(addr) = (b))
+#define writew(b,addr) (*(volatile ushort *)(addr) = (b))
+#define writel(b,addr) (*(volatile ulong *)(addr) = (b))
+#define memset_io(a,b,c) memset((void *)(a),(b),(c))
+#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
+#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
+
+#elif LINUX_VERSION_CODE < 0x020100
+static int remapped = FALSE;
+static void *gdth_mmap(ulong paddr, ulong size)
+{
+ if ( paddr >= high_memory) {
+ remapped = TRUE;
+ return vremap(paddr, size);
+ } else {
+ return (void *)paddr;
+ }
+}
+static void gdth_munmap(void *addr)
+{
+ if (remapped)
+ vfree(addr);
+ remapped = FALSE;
+}
+#else
+static void *gdth_mmap(ulong paddr, ulong size)
+{
+ return ioremap(paddr, size);
+}
+static void gdth_munmap(void *addr)
+{
+ return iounmap(addr);
+}
+#endif
+
+
+static unchar gdth_drq_tab[4] = {5,6,7,7}; /* DRQ table */
+static unchar gdth_irq_tab[6] = {0,10,11,12,14,0}; /* IRQ table */
+static unchar gdth_polling; /* polling if TRUE */
+static unchar gdth_from_wait = FALSE; /* gdth_wait() */
+static int wait_index,wait_hanum; /* gdth_wait() */
+static int gdth_ctr_count = 0; /* controller count */
+static int gdth_ctr_vcount = 0; /* virt. ctr. count */
+static int gdth_ctr_released = 0; /* gdth_release() */
+static struct Scsi_Host *gdth_ctr_tab[MAXHA]; /* controller table */
+static struct Scsi_Host *gdth_ctr_vtab[MAXHA*MAXBUS]; /* virt. ctr. table */
+static unchar gdth_write_through = FALSE; /* write through */
+static char *gdth_ioctl_tab[4][MAXHA]; /* ioctl buffer */
+static gdth_evt_str ebuffer[MAX_EVENTS]; /* event buffer */
+static int elastidx;
+static int eoldidx;
+
+static struct {
+ Scsi_Cmnd *cmnd; /* pending request */
+ ushort service; /* service */
+} gdth_cmd_tab[GDTH_MAXCMDS][MAXHA]; /* table of pend. requests */
+
+#define DIN 1 /* IN data direction */
+#define DOU 2 /* OUT data direction */
+#define DNO DIN /* no data transfer */
+#define DUN DIN /* unknown data direction */
+static unchar gdth_direction_tab[0x100] = {
+ DNO,DNO,DIN,DIN,DOU,DIN,DIN,DOU,DIN,DUN,DOU,DOU,DUN,DUN,DUN,DIN,
+ DNO,DIN,DIN,DOU,DIN,DOU,DNO,DNO,DOU,DNO,DIN,DNO,DIN,DOU,DNO,DUN,
+ DIN,DUN,DIN,DUN,DOU,DIN,DUN,DUN,DIN,DIN,DIN,DUN,DUN,DIN,DIN,DIN,
+ DIN,DIN,DIN,DNO,DIN,DNO,DNO,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN,
+ DIN,DIN,DIN,DIN,DIN,DNO,DUN,DNO,DNO,DNO,DUN,DNO,DIN,DIN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DNO,DNO,DUN,DIN,DNO,DIN,DUN,DNO,DUN,DIN,DIN,
+ DIN,DIN,DIN,DNO,DUN,DIN,DIN,DIN,DIN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN
+};
+
+/* __initfunc, __initdata macros */
+#if LINUX_VERSION_CODE >= 0x020126
+#include <linux/init.h>
+#else
+#define __initfunc(A) A
+#define __initdata
+#define __init
+#endif
+
+/* /proc support */
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/stat.h>
+struct proc_dir_entry proc_scsi_gdth = {
+ PROC_SCSI_GDTH, 4, "gdth",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#include "gdth_proc.h"
+#include "gdth_proc.c"
+#endif
+
+#if LINUX_VERSION_CODE >= 0x020100
+/* notifier block to get a notify on system shutdown/halt/reboot */
+static struct notifier_block gdth_notifier = {
+ gdth_halt, NULL, 0
+};
+#endif
+
+static void gdth_delay(int milliseconds)
+{
+ if (milliseconds == 0) {
+ udelay(1);
+ } else {
+#if LINUX_VERSION_CODE >= 0x020168
+ mdelay(milliseconds);
+#else
+ int i;
+ for (i = 0; i < milliseconds; ++i)
+ udelay(1000);
+#endif
+ }
+}
+
+/* controller search and initialization functions */
+
+__initfunc (static int gdth_search_eisa(ushort eisa_adr))
+{
+ ulong id;
+
+ TRACE(("gdth_search_eisa() adr. %x\n",eisa_adr));
+ id = inl(eisa_adr+ID0REG);
+ if (id == GDT3A_ID || id == GDT3B_ID) { /* GDT3000A or GDT3000B */
+ if ((inb(eisa_adr+EISAREG) & 8) == 0)
+ return 0; /* not EISA configured */
+ return 1;
+ }
+ if (id == GDT3_ID) /* GDT3000 */
+ return 1;
+
+ return 0;
+}
+
+
+__initfunc (static int gdth_search_isa(ulong bios_adr))
+{
+ void *addr;
+ ulong id;
+
+ TRACE(("gdth_search_isa() bios adr. %lx\n",bios_adr));
+ if ((addr = gdth_mmap(bios_adr+BIOS_ID_OFFS, sizeof(ulong))) != NULL) {
+ id = readl(addr);
+ gdth_munmap(addr);
+ if (id == GDT2_ID) /* GDT2000 */
+ return 1;
+ }
+ return 0;
+}
+
+
+__initfunc (static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr))
+{
+ int error;
+ ulong base0,base1,base2;
+
+ TRACE(("gdth_search_pci() device_id %d, index %d\n",
+ device_id,index));
+
+#if LINUX_VERSION_CODE >= 0x20155
+ if (!pci_present())
+ return 0;
+#else
+ if (!pcibios_present())
+ return 0;
+#endif
+
+ if (pcibios_find_device(PCI_VENDOR_ID_VORTEX,device_id,index,
+ &pcistr->bus,&pcistr->device_fn))
+ return 0;
+
+ /* GDT PCI controller found, now read resources from config space */
+#if LINUX_VERSION_CODE >= 0x20155
+ {
+ struct pci_dev *pdev = pci_find_slot(pcistr->bus, pcistr->device_fn);
+ base0 = pdev->base_address[0];
+ base1 = pdev->base_address[1];
+ base2 = pdev->base_address[2];
+ if ((error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_ROM_ADDRESS,
+ (int *) &pcistr->bios))) {
+ printk("GDT-PCI: error %d reading configuration space", error);
+ return -1;
+ }
+ pcistr->irq = pdev->irq;
+ }
+#else
+#if LINUX_VERSION_CODE >= 0x010300
+#define GDTH_BASEP (int *)
+#else
+#define GDTH_BASEP
+#endif
+ if ((error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_BASE_ADDRESS_0,
+ GDTH_BASEP&base0)) ||
+ (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_BASE_ADDRESS_1,
+ GDTH_BASEP&base1)) ||
+ (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_BASE_ADDRESS_2,
+ GDTH_BASEP&base2)) ||
+ (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_ROM_ADDRESS,
+ GDTH_BASEP&pcistr->bios)) ||
+ (error = pcibios_read_config_byte(pcistr->bus,pcistr->device_fn,
+ PCI_INTERRUPT_LINE,&pcistr->irq))) {
+ printk("GDT-PCI: error %d reading configuration space", error);
+ return -1;
+ }
+#endif
+
+ pcistr->device_id = device_id;
+ if (device_id <= PCI_DEVICE_ID_VORTEX_GDT6000B || /* GDT6000 or GDT6000B */
+ device_id >= PCI_DEVICE_ID_VORTEX_GDT6x17RP) { /* MPR */
+ if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY)
+ return -1;
+ pcistr->dpmem = base0 & PCI_BASE_ADDRESS_MEM_MASK;
+ } else { /* GDT6110, GDT6120, .. */
+ if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY ||
+ (base2 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY ||
+ (base1 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_IO)
+ return -1;
+ pcistr->dpmem = base2 & PCI_BASE_ADDRESS_MEM_MASK;
+ pcistr->io_mm = base0 & PCI_BASE_ADDRESS_MEM_MASK;
+ pcistr->io = base1 & PCI_BASE_ADDRESS_IO_MASK;
+ }
+ return 1;
+}
+
+
+__initfunc (static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha))
+{
+ ulong retries,id;
+ unchar prot_ver,eisacf,i,irq_found;
+
+ TRACE(("gdth_init_eisa() adr. %x\n",eisa_adr));
+
+ /* disable board interrupts, deinitialize services */
+ outb(0xff,eisa_adr+EDOORREG);
+ outb(0x00,eisa_adr+EDENABREG);
+ outb(0x00,eisa_adr+EINTENABREG);
+
+ outb(0xff,eisa_adr+LDOORREG);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (inb(eisa_adr+EDOORREG) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-EISA: Initialization error (DEINIT failed)\n");
+ return 0;
+ }
+ gdth_delay(1);
+ TRACE2(("wait for DEINIT: retries=%ld\n",retries));
+ }
+ prot_ver = inb(eisa_adr+MAILBOXREG);
+ outb(0xff,eisa_adr+EDOORREG);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-EISA: Illegal protocol version\n");
+ return 0;
+ }
+ ha->bmic = eisa_adr;
+ ha->brd_phys = (ulong)eisa_adr >> 12;
+
+ outl(0,eisa_adr+MAILBOXREG);
+ outl(0,eisa_adr+MAILBOXREG+4);
+ outl(0,eisa_adr+MAILBOXREG+8);
+ outl(0,eisa_adr+MAILBOXREG+12);
+
+ /* detect IRQ */
+ if ((id = inl(eisa_adr+ID0REG)) == GDT3_ID) {
+ ha->type = GDT_EISA;
+ ha->stype = id;
+ outl(1,eisa_adr+MAILBOXREG+8);
+ outb(0xfe,eisa_adr+LDOORREG);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (inb(eisa_adr+EDOORREG) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-EISA: Initialization error (get IRQ failed)\n");
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ ha->irq = inb(eisa_adr+MAILBOXREG);
+ outb(0xff,eisa_adr+EDOORREG);
+ TRACE2(("GDT3000/3020: IRQ=%d\n",ha->irq));
+ /* check the result */
+ if (ha->irq == 0) {
+ TRACE2(("Unknown IRQ, check IRQ table from cmd line !\n"));
+ for (i=0,irq_found=FALSE; i<MAXHA && irqs[i]!=0xff; ++i) {
+ if (irqs[i]!=0) {
+ irq_found=TRUE;
+ break;
+ }
+ }
+ if (irq_found) {
+ ha->irq = irqs[i];
+ irqs[i] = 0;
+ printk("GDT-EISA: Can not detect controller IRQ,\n");
+ printk("Use IRQ setting from command line (IRQ = %d)\n",
+ ha->irq);
+ } else {
+ printk("GDT-EISA: Initialization error (unknown IRQ), Enable\n");
+ printk("the controller BIOS or use command line parameters\n");
+ return 0;
+ }
+ }
+ } else {
+ eisacf = inb(eisa_adr+EISAREG) & 7;
+ if (eisacf > 4) /* level triggered */
+ eisacf -= 4;
+ ha->irq = gdth_irq_tab[eisacf];
+ ha->type = GDT_EISA;
+ ha->stype= id;
+ }
+ return 1;
+}
+
+
+__initfunc (static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha))
+{
+ register gdt2_dpram_str *dp2_ptr;
+ int i;
+ unchar irq_drq,prot_ver;
+ ulong retries;
+
+ TRACE(("gdth_init_isa() bios adr. %lx\n",bios_adr));
+
+ ha->brd = gdth_mmap(bios_adr, sizeof(gdt2_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-ISA: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ dp2_ptr = (gdt2_dpram_str *)ha->brd;
+ writeb(1, &dp2_ptr->io.memlock); /* switch off write protection */
+ /* reset interface area */
+ memset_io((char *)&dp2_ptr->u,0,sizeof(dp2_ptr->u));
+ if (readl(&dp2_ptr->u) != 0) {
+ printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ /* disable board interrupts, read DRQ and IRQ */
+ writeb(0xff, &dp2_ptr->io.irqdel);
+ writeb(0x00, &dp2_ptr->io.irqen);
+ writeb(0x00, &dp2_ptr->u.ic.S_Status);
+ writeb(0x00, &dp2_ptr->u.ic.Cmd_Index);
+
+ irq_drq = readb(&dp2_ptr->io.rq);
+ for (i=0; i<3; ++i) {
+ if ((irq_drq & 1)==0)
+ break;
+ irq_drq >>= 1;
+ }
+ ha->drq = gdth_drq_tab[i];
+
+ irq_drq = readb(&dp2_ptr->io.rq) >> 3;
+ for (i=1; i<5; ++i) {
+ if ((irq_drq & 1)==0)
+ break;
+ irq_drq >>= 1;
+ }
+ ha->irq = gdth_irq_tab[i];
+
+ /* deinitialize services */
+ writel(bios_adr, &dp2_ptr->u.ic.S_Info[0]);
+ writeb(0xff, &dp2_ptr->u.ic.S_Cmd_Indx);
+ writeb(0, &dp2_ptr->io.event);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (readb(&dp2_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-ISA: Initialization error (DEINIT failed)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ prot_ver = (unchar)readl(&dp2_ptr->u.ic.S_Info[0]);
+ writeb(0, &dp2_ptr->u.ic.Status);
+ writeb(0xff, &dp2_ptr->io.irqdel);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-ISA: Illegal protocol version\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_ISA;
+ ha->ic_all_size = sizeof(dp2_ptr->u);
+ ha->stype= GDT2_ID;
+ ha->brd_phys = bios_adr >> 4;
+
+ /* special request to controller BIOS */
+ writel(0x00, &dp2_ptr->u.ic.S_Info[0]);
+ writel(0x00, &dp2_ptr->u.ic.S_Info[1]);
+ writel(0x01, &dp2_ptr->u.ic.S_Info[2]);
+ writel(0x00, &dp2_ptr->u.ic.S_Info[3]);
+ writeb(0xfe, &dp2_ptr->u.ic.S_Cmd_Indx);
+ writeb(0, &dp2_ptr->io.event);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (readb(&dp2_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-ISA: Initialization error\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ writeb(0, &dp2_ptr->u.ic.Status);
+ writeb(0xff, &dp2_ptr->io.irqdel);
+ return 1;
+}
+
+
+__initfunc (static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha))
+{
+ register gdt6_dpram_str *dp6_ptr;
+ register gdt6c_dpram_str *dp6c_ptr;
+ register gdt6m_dpram_str *dp6m_ptr;
+ ulong retries;
+ unchar prot_ver;
+ int i, found = FALSE;
+
+ TRACE(("gdth_init_pci()\n"));
+
+ ha->brd_phys = (pcistr->bus << 8) | (pcistr->device_fn & 0xf8);
+ ha->stype = (ulong)pcistr->device_id;
+ ha->irq = pcistr->irq;
+
+ if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6000B) { /* GDT6000 or GDT6000B */
+ TRACE2(("init_pci() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
+ ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ dp6_ptr = (gdt6_dpram_str *)ha->brd;
+ /* reset interface area */
+ memset_io((char *)&dp6_ptr->u,0,sizeof(dp6_ptr->u));
+ if (readl(&dp6_ptr->u) != 0) {
+ printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ /* disable board interrupts, deinit services */
+ writeb(0xff, &dp6_ptr->io.irqdel);
+ writeb(0x00, &dp6_ptr->io.irqen);;
+ writeb(0x00, &dp6_ptr->u.ic.S_Status);
+ writeb(0x00, &dp6_ptr->u.ic.Cmd_Index);
+
+ writel(pcistr->dpmem, &dp6_ptr->u.ic.S_Info[0]);
+ writeb(0xff, &dp6_ptr->u.ic.S_Cmd_Indx);
+ writeb(0, &dp6_ptr->io.event);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (readb(&dp6_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ prot_ver = (unchar)readl(&dp6_ptr->u.ic.S_Info[0]);
+ writeb(0, &dp6_ptr->u.ic.S_Status);
+ writeb(0xff, &dp6_ptr->io.irqdel);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-PCI: Illegal protocol version\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_PCI;
+ ha->ic_all_size = sizeof(dp6_ptr->u);
+
+ /* special command to controller BIOS */
+ writel(0x00, &dp6_ptr->u.ic.S_Info[0]);
+ writel(0x00, &dp6_ptr->u.ic.S_Info[1]);
+ writel(0x01, &dp6_ptr->u.ic.S_Info[2]);
+ writel(0x00, &dp6_ptr->u.ic.S_Info[3]);
+ writeb(0xfe, &dp6_ptr->u.ic.S_Cmd_Indx);
+ writeb(0, &dp6_ptr->io.event);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (readb(&dp6_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ writeb(0, &dp6_ptr->u.ic.S_Status);
+ writeb(0xff, &dp6_ptr->io.irqdel);
+
+ } else if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6555) { /* GDT6110, GDT6120, .. */
+ ha->plx = (gdt6c_plx_regs *)pcistr->io;
+ TRACE2(("init_pci_new() dpmem %lx io %lx irq %d\n",
+ pcistr->dpmem,(ulong)ha->plx,ha->irq));
+ ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6c_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ dp6c_ptr = (gdt6c_dpram_str *)ha->brd;
+ /* reset interface area */
+ memset_io((char *)&dp6c_ptr->u,0,sizeof(dp6c_ptr->u));
+ if (readl(&dp6c_ptr->u) != 0) {
+ printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ /* disable board interrupts, deinit services */
+ outb(0x00,PTR2USHORT(&ha->plx->control1));
+ outb(0xff,PTR2USHORT(&ha->plx->edoor_reg));
+
+ writeb(0x00, &dp6c_ptr->u.ic.S_Status);
+ writeb(0x00, &dp6c_ptr->u.ic.Cmd_Index);
+
+ writel(pcistr->dpmem, &dp6c_ptr->u.ic.S_Info[0]);
+ writeb(0xff, &dp6c_ptr->u.ic.S_Cmd_Indx);
+
+ outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (readb(&dp6c_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ prot_ver = (unchar)readl(&dp6c_ptr->u.ic.S_Info[0]);
+ writeb(0, &dp6c_ptr->u.ic.Status);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-PCI: Illegal protocol version\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_PCINEW;
+ ha->ic_all_size = sizeof(dp6c_ptr->u);
+
+ /* special command to controller BIOS */
+ writel(0x00, &dp6c_ptr->u.ic.S_Info[0]);
+ writel(0x00, &dp6c_ptr->u.ic.S_Info[1]);
+ writel(0x01, &dp6c_ptr->u.ic.S_Info[2]);
+ writel(0x00, &dp6c_ptr->u.ic.S_Info[3]);
+ writeb(0xfe, &dp6c_ptr->u.ic.S_Cmd_Indx);
+
+ outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (readb(&dp6c_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ writeb(0, &dp6c_ptr->u.ic.S_Status);
+
+ } else { /* MPR */
+ TRACE2(("init_pci_mpr() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
+ ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6m_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+
+ /* check and reset interface area */
+ dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+ writel(DPMEM_MAGIC, &dp6m_ptr->u);
+ if (readl(&dp6m_ptr->u) != DPMEM_MAGIC) {
+ printk("GDT-PCI: Cannot access DPMEM at 0x%x (shadowed?)\n",
+ (int)ha->brd);
+ found = FALSE;
+ for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
+ pcibios_write_config_dword( pcistr->bus, pcistr->device_fn,
+ PCI_BASE_ADDRESS_0, i );
+ gdth_munmap( ha->brd );
+ ha->brd = gdth_mmap(i, sizeof(gdt6m_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+ writel(DPMEM_MAGIC, &dp6m_ptr->u);
+ if (readl(&dp6m_ptr->u) == DPMEM_MAGIC) {
+ printk("GDT-PCI: Use free address at 0x%x\n",
+ (int)ha->brd);
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ printk("GDT-PCI: No free address found!\n");
+ gdth_munmap( ha->brd );
+ return 0;
+ }
+ }
+ memset_io((char *)&dp6m_ptr->u,0,sizeof(dp6m_ptr->u));
+
+ /* disable board interrupts, deinit services */
+ writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) | 4,
+ &dp6m_ptr->i960r.edoor_en_reg);
+ writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ writeb(0x00, &dp6m_ptr->u.ic.S_Status);
+ writeb(0x00, &dp6m_ptr->u.ic.Cmd_Index);
+
+ writel(pcistr->dpmem, &dp6m_ptr->u.ic.S_Info[0]);
+ writeb(0xff, &dp6m_ptr->u.ic.S_Cmd_Indx);
+ writeb(1, &dp6m_ptr->i960r.ldoor_reg);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (readb(&dp6m_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ prot_ver = (unchar)readl(&dp6m_ptr->u.ic.S_Info[0]);
+ writeb(0, &dp6m_ptr->u.ic.S_Status);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-PCI: Illegal protocol version\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_PCIMPR;
+ ha->ic_all_size = sizeof(dp6m_ptr->u);
+
+ /* special command to controller BIOS */
+ writel(0x00, &dp6m_ptr->u.ic.S_Info[0]);
+ writel(0x00, &dp6m_ptr->u.ic.S_Info[1]);
+ writel(0x01, &dp6m_ptr->u.ic.S_Info[2]);
+ writel(0x00, &dp6m_ptr->u.ic.S_Info[3]);
+ writeb(0xfe, &dp6m_ptr->u.ic.S_Cmd_Indx);
+ writeb(1, &dp6m_ptr->i960r.ldoor_reg);
+ retries = INIT_RETRIES;
+ gdth_delay(20);
+ while (readb(&dp6m_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ gdth_delay(1);
+ }
+ writeb(0, &dp6m_ptr->u.ic.S_Status);
+ }
+
+ return 1;
+}
+
+
+/* controller protocol functions */
+
+__initfunc (static void gdth_enable_int(int hanum))
+{
+ gdth_ha_str *ha;
+ ulong flags;
+ gdt2_dpram_str *dp2_ptr;
+ gdt6_dpram_str *dp6_ptr;
+ gdt6m_dpram_str *dp6m_ptr;
+
+ TRACE(("gdth_enable_int() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ save_flags(flags);
+ cli();
+
+ if (ha->type == GDT_EISA) {
+ outb(0xff, ha->bmic + EDOORREG);
+ outb(0xff, ha->bmic + EDENABREG);
+ outb(0x01, ha->bmic + EINTENABREG);
+ } else if (ha->type == GDT_ISA) {
+ dp2_ptr = (gdt2_dpram_str *)ha->brd;
+ writeb(1, &dp2_ptr->io.irqdel);
+ writeb(0, &dp2_ptr->u.ic.Cmd_Index);
+ writeb(1, &dp2_ptr->io.irqen);
+ } else if (ha->type == GDT_PCI) {
+ dp6_ptr = (gdt6_dpram_str *)ha->brd;
+ writeb(1, &dp6_ptr->io.irqdel);
+ writeb(0, &dp6_ptr->u.ic.Cmd_Index);
+ writeb(1, &dp6_ptr->io.irqen);
+ } else if (ha->type == GDT_PCINEW) {
+ outb(0xff, PTR2USHORT(&ha->plx->edoor_reg));
+ outb(0x03, PTR2USHORT(&ha->plx->control1));
+ } else if (ha->type == GDT_PCIMPR) {
+ dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+ writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) & ~4,
+ &dp6m_ptr->i960r.edoor_en_reg);
+ }
+ restore_flags(flags);
+}
+
+
+static int gdth_get_status(unchar *pIStatus,int irq)
+{
+ register gdth_ha_str *ha;
+ int i;
+
+ TRACE(("gdth_get_status() irq %d ctr_count %d\n",
+ irq,gdth_ctr_count));
+
+ *pIStatus = 0;
+ for (i=0; i<gdth_ctr_count; ++i) {
+ ha = HADATA(gdth_ctr_tab[i]);
+ if (ha->irq != (unchar)irq) /* check IRQ */
+ continue;
+ if (ha->type == GDT_EISA)
+ *pIStatus = inb((ushort)ha->bmic + EDOORREG);
+ else if (ha->type == GDT_ISA)
+ *pIStatus = readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Cmd_Index);
+ else if (ha->type == GDT_PCI)
+ *pIStatus = readb(&((gdt6_dpram_str *)ha->brd)->u.ic.Cmd_Index);
+ else if (ha->type == GDT_PCINEW)
+ *pIStatus = inb(PTR2USHORT(&ha->plx->edoor_reg));
+ else if (ha->type == GDT_PCIMPR)
+ *pIStatus = readb(&((gdt6m_dpram_str *)ha->brd)->i960r.edoor_reg);
+
+ if (*pIStatus)
+ return i; /* board found */
+ }
+ return -1;
+}
+
+
+static int gdth_test_busy(int hanum)
+{
+ register gdth_ha_str *ha;
+ register int gdtsema0 = 0;
+
+ TRACE(("gdth_test_busy() hanum %d\n",hanum));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (ha->type == GDT_EISA)
+ gdtsema0 = (int)inb(ha->bmic + SEMA0REG);
+ else if (ha->type == GDT_ISA)
+ gdtsema0 = (int)readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCI)
+ gdtsema0 = (int)readb(&((gdt6_dpram_str *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCINEW)
+ gdtsema0 = (int)inb(PTR2USHORT(&ha->plx->sema0_reg));
+ else if (ha->type == GDT_PCIMPR)
+ gdtsema0 = (int)readb(&((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg);
+
+ return (gdtsema0 & 1);
+}
+
+
+static int gdth_get_cmd_index(int hanum)
+{
+ register gdth_ha_str *ha;
+ int i;
+
+ TRACE(("gdth_get_cmd_index() hanum %d\n",hanum));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ for (i=0; i<GDTH_MAXCMDS; ++i) {
+ if (gdth_cmd_tab[i][hanum].cmnd == UNUSED_CMND) {
+ gdth_cmd_tab[i][hanum].cmnd = ha->pccb->RequestBuffer;
+ gdth_cmd_tab[i][hanum].service = ha->pccb->Service;
+ ha->pccb->CommandIndex = (ulong)i+2;
+ return (i+2);
+ }
+ }
+ return 0;
+}
+
+
+static void gdth_set_sema0(int hanum)
+{
+ register gdth_ha_str *ha;
+
+ TRACE(("gdth_set_sema0() hanum %d\n",hanum));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (ha->type == GDT_EISA)
+ outb(1, ha->bmic + SEMA0REG);
+ else if (ha->type == GDT_ISA)
+ writeb(1, &((gdt2_dpram_str *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCI)
+ writeb(1, &((gdt6_dpram_str *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCINEW)
+ outb(1, PTR2USHORT(&ha->plx->sema0_reg));
+ else if (ha->type == GDT_PCIMPR)
+ writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg);
+
+}
+
+
+static void gdth_copy_command(int hanum)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmd_ptr;
+ register gdt6m_dpram_str *dp6m_ptr;
+ register gdt6c_dpram_str *dp6c_ptr;
+ gdt6_dpram_str *dp6_ptr;
+ gdt2_dpram_str *dp2_ptr;
+ ushort cp_count,dp_offset,cmd_no;
+
+ TRACE(("gdth_copy_command() hanum %d\n",hanum));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cp_count = ha->cmd_len;
+ dp_offset= ha->cmd_offs_dpmem;
+ cmd_no = ha->cmd_cnt;
+ cmd_ptr = ha->pccb;
+
+ ++ha->cmd_cnt;
+ if (ha->type == GDT_EISA)
+ return; /* no DPMEM, no copy */
+
+ /* set cpcount dword aligned */
+ if (cp_count & 3)
+ cp_count += (4 - (cp_count & 3));
+
+ ha->cmd_offs_dpmem += cp_count;
+
+ /* set offset and service, copy command to DPMEM */
+ if (ha->type == GDT_ISA) {
+ dp2_ptr = (gdt2_dpram_str *)ha->brd;
+ writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp2_ptr->u.ic.comm_queue[cmd_no].offset);
+ writew((ushort)cmd_ptr->Service,
+ &dp2_ptr->u.ic.comm_queue[cmd_no].serv_id);
+ memcpy_toio(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+ } else if (ha->type == GDT_PCI) {
+ dp6_ptr = (gdt6_dpram_str *)ha->brd;
+ writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp6_ptr->u.ic.comm_queue[cmd_no].offset);
+ writew((ushort)cmd_ptr->Service,
+ &dp6_ptr->u.ic.comm_queue[cmd_no].serv_id);
+ memcpy_toio(&dp6_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+ } else if (ha->type == GDT_PCINEW) {
+ dp6c_ptr = (gdt6c_dpram_str *)ha->brd;
+ writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp6c_ptr->u.ic.comm_queue[cmd_no].offset);
+ writew((ushort)cmd_ptr->Service,
+ &dp6c_ptr->u.ic.comm_queue[cmd_no].serv_id);
+ memcpy_toio(&dp6c_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+ } else if (ha->type == GDT_PCIMPR) {
+ dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+ writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp6m_ptr->u.ic.comm_queue[cmd_no].offset);
+ writew((ushort)cmd_ptr->Service,
+ &dp6m_ptr->u.ic.comm_queue[cmd_no].serv_id);
+ memcpy_toio(&dp6m_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+ }
+}
+
+
+static void gdth_release_event(int hanum)
+{
+ register gdth_ha_str *ha;
+
+#ifdef GDTH_STATISTICS
+ ulong i,j;
+ for (i=0,j=0; j<GDTH_MAXCMDS; ++j) {
+ if (gdth_cmd_tab[j][hanum].cmnd != UNUSED_CMND)
+ ++i;
+ }
+ if (max_index < i) {
+ max_index = i;
+ TRACE3(("GDT: max_index = %d\n",(ushort)i));
+ }
+#endif
+
+ TRACE(("gdth_release_event() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ if (ha->pccb->OpCode == GDT_INIT)
+ ha->pccb->Service |= 0x80;
+
+ if (ha->type == GDT_EISA) {
+ outb(ha->pccb->Service, ha->bmic + LDOORREG);
+ if (ha->pccb->OpCode == GDT_INIT) /* store DMA buffer */
+ outl((ulong)ha->pccb, ha->bmic + MAILBOXREG);
+ } else if (ha->type == GDT_ISA)
+ writeb(0, &((gdt2_dpram_str *)ha->brd)->io.event);
+ else if (ha->type == GDT_PCI)
+ writeb(0, &((gdt6_dpram_str *)ha->brd)->io.event);
+ else if (ha->type == GDT_PCINEW)
+ outb(1, PTR2USHORT(&ha->plx->ldoor_reg));
+ else if (ha->type == GDT_PCIMPR)
+ writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.ldoor_reg);
+}
+
+
+static int gdth_wait(int hanum,int index,ulong time)
+{
+ gdth_ha_str *ha;
+ int answer_found = FALSE;
+
+ TRACE(("gdth_wait() hanum %d index %d time %ld\n",hanum,index,time));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (index == 0)
+ return 1; /* no wait required */
+
+ gdth_from_wait = TRUE;
+ do {
+#if LINUX_VERSION_CODE >= 0x010346
+ gdth_interrupt((int)ha->irq,NULL,NULL);
+#else
+ gdth_interrupt((int)ha->irq,NULL);
+#endif
+ if (wait_hanum==hanum && wait_index==index) {
+ answer_found = TRUE;
+ break;
+ }
+ gdth_delay(1);
+ } while (--time);
+ gdth_from_wait = FALSE;
+
+ while (gdth_test_busy(hanum))
+ gdth_delay(0);
+
+ return (answer_found);
+}
+
+
+static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1,
+ ulong p2,ulong p3)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmd_ptr;
+ int retries,index;
+
+ TRACE2(("gdth_internal_cmd() service %d opcode %d\n",service,opcode));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmd_ptr = ha->pccb;
+ memset((char*)cmd_ptr,0,sizeof(gdth_cmd_str));
+
+ /* make command */
+ for (retries = INIT_RETRIES;;) {
+ cmd_ptr->Service = service;
+ cmd_ptr->RequestBuffer = INTERNAL_CMND;
+ if (!(index=gdth_get_cmd_index(hanum))) {
+ TRACE(("GDT: No free command index found\n"));
+ return 0;
+ }
+ gdth_set_sema0(hanum);
+ cmd_ptr->OpCode = opcode;
+ cmd_ptr->BoardNode = LOCALBOARD;
+ if (service == CACHESERVICE) {
+ if (opcode == GDT_IOCTL) {
+ cmd_ptr->u.ioctl.subfunc = p1;
+ cmd_ptr->u.ioctl.channel = p2;
+ cmd_ptr->u.ioctl.param_size = (ushort)p3;
+ cmd_ptr->u.ioctl.p_param = virt_to_bus(ha->pscratch);
+ } else {
+ cmd_ptr->u.cache.DeviceNo = (ushort)p1;
+ cmd_ptr->u.cache.BlockNo = p2;
+ }
+ } else if (service == SCSIRAWSERVICE) {
+ cmd_ptr->u.raw.direction = p1;
+ cmd_ptr->u.raw.bus = (unchar)p2;
+ cmd_ptr->u.raw.target = (unchar)p3;
+ cmd_ptr->u.raw.lun = 0;
+ }
+ ha->cmd_len = sizeof(gdth_cmd_str);
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ gdth_release_event(hanum);
+ gdth_delay(20);
+ if (!gdth_wait(hanum,index,INIT_TIMEOUT)) {
+ printk("GDT: Initialization error (timeout service %d)\n",service);
+ return 0;
+ }
+ if (ha->status != S_BSY || --retries == 0)
+ break;
+ gdth_delay(1);
+ }
+
+ return (ha->status != S_OK ? 0:1);
+}
+
+
+/* search for devices */
+
+__initfunc (static int gdth_search_drives(int hanum))
+{
+ register gdth_ha_str *ha;
+ ushort cdev_cnt,i;
+ unchar b,t,pos_found;
+ ulong drv_cyls, drv_hds, drv_secs;
+ ulong bus_no;
+ gdth_getch_str *chn;
+ gdth_iochan_str *ioc;
+
+ TRACE(("gdth_search_drives() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ /* initialize controller services, at first: screen service */
+ if (!gdth_internal_cmd(hanum,SCREENSERVICE,GDT_INIT,0,0,0)) {
+ printk("GDT: Initialization error screen service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n"));
+
+ /* initialize cache service */
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) {
+ printk("GDT: Initialization error cache service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n"));
+ cdev_cnt = (ushort)ha->info;
+
+ /* mount all cache devices */
+ gdth_internal_cmd(hanum,CACHESERVICE,GDT_MOUNT,0xffff,1,0);
+ TRACE2(("gdth_search_drives(): mountall CACHESERVICE OK\n"));
+
+ /* initialize cache service after mountall */
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) {
+ printk("GDT: Initialization error cache service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives() CACHES. init. after mountall\n"));
+ cdev_cnt = (ushort)ha->info;
+
+ /* detect number of SCSI buses - try new IOCTL */
+ ioc = (gdth_iochan_str *)DMADATA(gdth_ctr_tab[hanum]);
+ ioc->version = -1UL;
+ ioc->list_entries = MAXBUS;
+ ioc->first_chan = 0;
+ ioc->last_chan = MAXBUS-1;
+ ioc->list_offset = GDTOFFSOF(gdth_iochan_str, list[0]);
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,GET_IOCHAN_DESC,
+ INVALID_CHANNEL,sizeof(gdth_iochan_str))) {
+ TRACE2(("GET_IOCHAN_DESC supported!\n"));
+ ha->bus_cnt = ioc->chan_count;
+ for (bus_no = 0; bus_no < ha->bus_cnt; ++bus_no)
+ if (ioc->list[bus_no].proc_id < MAXID)
+ ha->id[bus_no][ioc->list[bus_no].proc_id].type = SIOP_DTYP;
+ } else {
+ /* old method */
+ chn = (gdth_getch_str *)DMADATA(gdth_ctr_tab[hanum]);
+ for (bus_no = 0; bus_no < MAXBUS; ++bus_no) {
+ chn->channel_no = bus_no;
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
+ SCSI_CHAN_CNT | L_CTRL_PATTERN,
+ IO_CHANNEL | INVALID_CHANNEL,
+ sizeof(gdth_getch_str))) {
+ if (bus_no == 0) {
+ printk("GDT: Error detecting SCSI channel count (0x%x)\n",
+ ha->status);
+ return 0;
+ }
+ break;
+ }
+ if (chn->siop_id < MAXID)
+ ha->id[bus_no][chn->siop_id].type = SIOP_DTYP;
+ }
+ ha->bus_cnt = (unchar)bus_no;
+ }
+ TRACE2(("gdth_search_drives() %d SCSI channels\n",ha->bus_cnt));
+
+ /* read cache configuration */
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,CACHE_INFO,
+ INVALID_CHANNEL,sizeof(gdth_cinfo_str))) {
+ printk("GDT: Initialization error cache service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ ha->cpar = ((gdth_cinfo_str *)DMADATA(gdth_ctr_tab[hanum]))->cpar;
+ TRACE2(("gdth_search_drives() cinfo: vs %lx sta %d str %d dw %d b %d\n",
+ ha->cpar.version,ha->cpar.state,ha->cpar.strategy,
+ ha->cpar.write_back,ha->cpar.block_size));
+
+ /* read board info, fill ctr_name[] */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,BOARD_INFO,
+ INVALID_CHANNEL,sizeof(gdth_binfo_str))) {
+ TRACE2(("BOARD_INFO supported!\n"));
+ strcpy(ha->ctr_name, ((gdth_binfo_str *)DMADATA(gdth_ctr_tab[hanum]))->type_string);
+ } else {
+ strcpy(ha->ctr_name, gdth_ctr_name(hanum));
+ }
+ TRACE2(("Controller name: %s\n",ha->ctr_name));
+
+ /* initialize raw service */
+ if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INIT,0,0,0)) {
+ printk("GDT: Initialization error raw service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives(): RAWSERVICE initialized\n"));
+
+ /* set/get features raw service (scatter/gather) */
+ ha->raw_feat = 0;
+ if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_SET_FEAT,SCATTER_GATHER,
+ 0,0)) {
+ TRACE2(("gdth_search_drives(): set features RAWSERVICE OK\n"));
+ if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_GET_FEAT,0,0,0)) {
+ TRACE2(("gdth_search_dr(): get feat RAWSERVICE %ld\n",
+ ha->info));
+ ha->raw_feat = (ushort)ha->info;
+ }
+ }
+
+ /* set/get features cache service (equal to raw service) */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_SET_FEAT,0,
+ SCATTER_GATHER,0)) {
+ TRACE2(("gdth_search_drives(): set features CACHESERVICE OK\n"));
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_GET_FEAT,0,0,0)) {
+ TRACE2(("gdth_search_dr(): get feat CACHESERV. %ld\n",
+ ha->info));
+ ha->cache_feat = (ushort)ha->info;
+ }
+ }
+
+ /* reserve drives for raw service */
+ for (i = 0; reserve_list[i].hanum != 0xff; ++i) {
+ if (reserve_list[i].hanum < MAXHA && reserve_list[i].hanum == hanum &&
+ reserve_list[i].bus < MAXBUS && reserve_list[i].id < MAXID) {
+ TRACE2(("gdth_search_drives(): reserve ha %d bus %d id %d\n",
+ reserve_list[i].hanum, reserve_list[i].bus,
+ reserve_list[i].id));
+ if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_RESERVE,0,
+ reserve_list[i].bus, reserve_list[i].id)) {
+ printk("GDT: Error raw service (RESERVE, code %d)\n",
+ ha->status);
+ }
+ }
+ }
+
+ /* scanning for raw devices */
+ for (b=0; b<ha->bus_cnt; ++b) {
+ for (t=0; t<MAXID; ++t) {
+ TRACE(("gdth_search_drives() rawd. bus %d id %d\n",b,t));
+ if (ha->id[b][t].type != SIOP_DTYP &&
+ gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INFO,0,b,t)) {
+ ha->id[b][t].type = RAW_DTYP;
+ }
+ }
+ }
+
+ /* scanning for cache devices */
+ for (i=0; i<cdev_cnt && i<MAX_HDRIVES; ++i) {
+ TRACE(("gdth_search_drives() cachedev. %d\n",i));
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_INFO,i,0,0)) {
+ /* dynamic relation between host drive number and Bus/ID */
+ /* search free position */
+ pos_found = FALSE;
+ for (b=0,t=0; b<ha->bus_cnt; ++b) {
+ for (t=0; t<MAXID; ++t) {
+ if (ha->id[b][t].type == EMPTY_DTYP) {
+ pos_found = TRUE;
+ break;
+ }
+ }
+ if (pos_found)
+ break;
+ }
+ TRACE(("gdth_search_dr() drive %d free pos at bus/id %d/%d\n",
+ i,b,t));
+
+ ha->id[b][t].type = CACHE_DTYP;
+ ha->id[b][t].devtype = 0;
+ ha->id[b][t].size = ha->info;
+ ha->id[b][t].hostdrive = i;
+
+ /* evaluate mapping (sectors per head, heads per cylinder) */
+ ha->id[b][t].size &= ~SECS32;
+ if (ha->info2 == 0) {
+ drv_cyls = ha->id[b][t].size /HEADS/SECS;
+ if (drv_cyls <= MAXCYLS) {
+ drv_hds = HEADS;
+ drv_secs= SECS;
+ } else { /* too high for 64*32 */
+ drv_cyls = ha->id[b][t].size /MEDHEADS/MEDSECS;
+ if (drv_cyls <= MAXCYLS) {
+ drv_hds = MEDHEADS;
+ drv_secs= MEDSECS;
+ } else { /* too high for 127*63 */
+ drv_cyls = ha->id[b][t].size /BIGHEADS/BIGSECS;
+ drv_hds = BIGHEADS;
+ drv_secs= BIGSECS;
+ }
+ }
+ } else {
+ drv_hds = ha->info2 & 0xff;
+ drv_secs = (ha->info2 >> 8) & 0xff;
+ drv_cyls = ha->id[b][t].size /drv_hds/drv_secs;
+ }
+ ha->id[b][t].heads = (unchar)drv_hds;
+ ha->id[b][t].secs = (unchar)drv_secs;
+ /* round size */
+ ha->id[b][t].size = drv_cyls * drv_hds * drv_secs;
+ TRACE2(("gdth_search_dr() cdr. %d size %ld hds %ld scs %ld\n",
+ i,ha->id[b][t].size,drv_hds,drv_secs));
+
+ /* get informations about device */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,i,
+ 0,0)) {
+ TRACE(("gdth_search_dr() cache drive %d devtype %ld\n",
+ i,ha->info));
+ ha->id[b][t].devtype = (ushort)ha->info;
+ }
+ }
+ }
+
+ TRACE(("gdth_search_drives() OK\n"));
+ return 1;
+}
+
+
+/* command queueing/sending functions */
+
+static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority)
+{
+ register gdth_ha_str *ha;
+ register Scsi_Cmnd *pscp;
+ register Scsi_Cmnd *nscp;
+ ulong flags;
+ unchar b, t;
+
+ TRACE(("gdth_putq() priority %d\n",priority));
+ save_flags(flags);
+ cli();
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ scp->SCp.this_residual = (int)priority;
+ gdth_update_timeout(hanum, scp, scp->timeout_per_command * 6);
+#if LINUX_VERSION_CODE >= 0x020000
+ b = scp->channel;
+#else
+ b = NUMDATA(nscp->host)->busnum;
+#endif
+ t = scp->target;
+#if LINUX_VERSION_CODE >= 0x010300
+ if (priority >= DEFAULT_PRI && ha->id[b][t].lock) {
+ TRACE2(("gdth_putq(): locked IO -> update_timeout()\n"));
+ scp->SCp.buffers_residual = gdth_update_timeout(hanum, scp, 0);
+ }
+#endif
+
+ if (ha->req_first==NULL) {
+ ha->req_first = scp; /* queue was empty */
+ scp->SCp.ptr = NULL;
+ } else { /* queue not empty */
+ pscp = ha->req_first;
+ nscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+ /* priority: 0-highest,..,0xff-lowest */
+ while (nscp && (unchar)nscp->SCp.this_residual <= priority) {
+ pscp = nscp;
+ nscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+ }
+ pscp->SCp.ptr = (char *)scp;
+ scp->SCp.ptr = (char *)nscp;
+ }
+ restore_flags(flags);
+
+#ifdef GDTH_STATISTICS
+ flags = 0;
+ for (nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
+ ++flags;
+ if (max_rq < flags) {
+ max_rq = flags;
+ TRACE3(("GDT: max_rq = %d\n",(ushort)max_rq));
+ }
+#endif
+}
+
+static void gdth_next(int hanum)
+{
+ register gdth_ha_str *ha;
+ register Scsi_Cmnd *pscp;
+ register Scsi_Cmnd *nscp;
+ unchar b, t, next_cmd, firsttime;
+ ushort hdrive;
+ ulong flags;
+ int cmd_index;
+
+ TRACE(("gdth_next() hanum %d\n",hanum));
+ save_flags(flags);
+ cli();
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ ha->cmd_cnt = ha->cmd_offs_dpmem = 0;
+ next_cmd = firsttime = TRUE;
+ cmd_index = 0;
+
+ for (nscp = pscp = ha->req_first; nscp; nscp = (Scsi_Cmnd *)nscp->SCp.ptr) {
+ if (nscp != pscp && nscp != (Scsi_Cmnd *)pscp->SCp.ptr)
+ pscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+#if LINUX_VERSION_CODE >= 0x020000
+ b = nscp->channel;
+#else
+ b = NUMDATA(nscp->host)->busnum;
+#endif
+ t = nscp->target;
+ if (nscp->SCp.this_residual < DEFAULT_PRI || !ha->id[b][t].lock) {
+
+ if (firsttime) {
+ if (gdth_test_busy(hanum)) { /* controller busy ? */
+ TRACE(("gdth_next() controller %d busy !\n",hanum));
+ if (!gdth_polling) {
+ restore_flags(flags);
+ return;
+ }
+ while (gdth_test_busy(hanum))
+ gdth_delay(1);
+ }
+ firsttime = FALSE;
+ }
+
+#if LINUX_VERSION_CODE >= 0x010300
+ if (nscp->done == gdth_scsi_done) {
+ if (!(cmd_index=gdth_special_cmd(hanum,nscp,b)))
+ next_cmd = FALSE;
+ } else
+#endif
+ if (ha->id[b][t].type != CACHE_DTYP) {
+ if (!(cmd_index=gdth_fill_raw_cmd(hanum,nscp,b)))
+ next_cmd = FALSE;
+ } else {
+ hdrive = ha->id[b][t].hostdrive;
+ switch (nscp->cmnd[0]) {
+ case TEST_UNIT_READY:
+ case INQUIRY:
+ case REQUEST_SENSE:
+ case READ_CAPACITY:
+ case VERIFY:
+ case START_STOP:
+ case MODE_SENSE:
+ TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+ nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+ nscp->cmnd[4],nscp->cmnd[5]));
+ gdth_internal_cache_cmd(hanum,nscp,b,&flags);
+ break;
+
+ case ALLOW_MEDIUM_REMOVAL:
+ TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+ nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+ nscp->cmnd[4],nscp->cmnd[5]));
+ if ( (nscp->cmnd[4]&1) && !(ha->id[b][t].devtype&1) ) {
+ TRACE2(("Prevent r. nonremov. drive->do nothing\n"));
+ nscp->result = DID_OK << 16;
+ restore_flags( flags );
+ if (!nscp->SCp.have_data_in)
+ nscp->SCp.have_data_in++;
+ else
+ nscp->scsi_done(nscp);
+ save_flags( flags );
+ cli();
+ } else {
+ nscp->cmnd[3] = (ha->id[b][t].devtype&1) ? 1:0;
+ TRACE2(("Prevent/allow r. %d rem. drive %d\n",
+ nscp->cmnd[4],nscp->cmnd[3]));
+ if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive)))
+ next_cmd = FALSE;
+ }
+ break;
+
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive)))
+ next_cmd = FALSE;
+ break;
+
+ default:
+ TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+ nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+ nscp->cmnd[4],nscp->cmnd[5]));
+ printk("GDT: Unknown SCSI command 0x%x to cache service !\n",
+ nscp->cmnd[0]);
+ nscp->result = DID_ABORT << 16;
+ restore_flags( flags );
+ if (!nscp->SCp.have_data_in)
+ nscp->SCp.have_data_in++;
+ else
+ nscp->scsi_done(nscp);
+ save_flags( flags );
+ cli();
+ break;
+ }
+ }
+
+ if (!next_cmd)
+ break;
+ if (nscp == ha->req_first)
+ ha->req_first = pscp = (Scsi_Cmnd *)nscp->SCp.ptr;
+ else
+ pscp->SCp.ptr = nscp->SCp.ptr;
+ if (gdth_polling)
+ break;
+ }
+ }
+
+ if (ha->cmd_cnt > 0) {
+ gdth_release_event(hanum);
+ }
+
+ restore_flags(flags);
+
+ if (gdth_polling && ha->cmd_cnt > 0) {
+ if (!gdth_wait(hanum,cmd_index,POLL_TIMEOUT))
+ printk("GDT: Controller %d: Command %d timed out !\n",
+ hanum,cmd_index);
+ }
+}
+
+static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count)
+{
+ ushort cpcount,i;
+ ushort cpsum,cpnow;
+ struct scatterlist *sl;
+
+ cpcount = count<=(ushort)scp->bufflen ? count:(ushort)scp->bufflen;
+ if (scp->use_sg) {
+ sl = (struct scatterlist *)scp->request_buffer;
+ for (i=0,cpsum=0; i<scp->use_sg; ++i,++sl) {
+ cpnow = (ushort)sl->length;
+ TRACE(("copy_internal() now %d sum %d count %d %d\n",
+ cpnow,cpsum,cpcount,(ushort)scp->bufflen));
+ if (cpsum+cpnow > cpcount)
+ cpnow = cpcount - cpsum;
+ cpsum += cpnow;
+ memcpy((char*)sl->address,buffer,cpnow);
+ if (cpsum == cpcount)
+ break;
+ buffer += cpnow;
+ }
+ } else {
+ TRACE(("copy_internal() count %d\n",cpcount));
+ memcpy((char*)scp->request_buffer,buffer,cpcount);
+ }
+}
+
+static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp,
+ unchar b,ulong *flags)
+{
+ register gdth_ha_str *ha;
+ ushort hdrive;
+ unchar t;
+ gdth_inq_data inq;
+ gdth_rdcap_data rdc;
+ gdth_sense_data sd;
+ gdth_modep_data mpd;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ t = scp->target;
+ hdrive = ha->id[b][t].hostdrive;
+ TRACE(("gdth_internal_cache_cmd() cmd 0x%x hdrive %d\n",
+ scp->cmnd[0],hdrive));
+
+ if (scp->lun !=0)
+ scp->result = DID_BAD_TARGET << 16;
+ else {
+ switch (scp->cmnd[0]) {
+ case TEST_UNIT_READY:
+ case VERIFY:
+ case START_STOP:
+ TRACE2(("Test/Verify/Start hdrive %d\n",hdrive));
+ break;
+
+ case INQUIRY:
+ TRACE2(("Inquiry hdrive %d devtype %d\n",
+ hdrive,ha->id[b][t].devtype));
+ inq.type_qual = (ha->id[b][t].devtype&4) ? TYPE_ROM:TYPE_DISK;
+ /* you can here set all disks to removable, if you want to do
+ a flush using the ALLOW_MEDIUM_REMOVAL command */
+ inq.modif_rmb = ha->id[b][t].devtype&1 ? 0x80:0x00;
+ inq.version = 2;
+ inq.resp_aenc = 2;
+ inq.add_length= 32;
+ strcpy(inq.vendor,"ICP ");
+ sprintf(inq.product,"Host Drive #%02d",hdrive);
+ strcpy(inq.revision," ");
+ gdth_copy_internal_data(scp,(char*)&inq,sizeof(gdth_inq_data));
+ break;
+
+ case REQUEST_SENSE:
+ TRACE2(("Request sense hdrive %d\n",hdrive));
+ sd.errorcode = 0x70;
+ sd.segno = 0x00;
+ sd.key = NO_SENSE;
+ sd.info = 0;
+ sd.add_length= 0;
+ gdth_copy_internal_data(scp,(char*)&sd,sizeof(gdth_sense_data));
+ break;
+
+ case MODE_SENSE:
+ TRACE2(("Mode sense hdrive %d\n",hdrive));
+ memset((char*)&mpd,0,sizeof(gdth_modep_data));
+ mpd.hd.data_length = sizeof(gdth_modep_data);
+ mpd.hd.dev_par = (ha->id[b][t].devtype&2) ? 0x80:0;
+ mpd.hd.bd_length = sizeof(mpd.bd);
+ mpd.bd.block_length[0] = (SECTOR_SIZE & 0x00ff0000) >> 16;
+ mpd.bd.block_length[1] = (SECTOR_SIZE & 0x0000ff00) >> 8;
+ mpd.bd.block_length[2] = (SECTOR_SIZE & 0x000000ff);
+ gdth_copy_internal_data(scp,(char*)&mpd,sizeof(gdth_modep_data));
+ break;
+
+ case READ_CAPACITY:
+ TRACE2(("Read capacity hdrive %d\n",hdrive));
+ rdc.last_block_no = ntohl(ha->id[b][t].size-1);
+ rdc.block_length = ntohl(SECTOR_SIZE);
+ gdth_copy_internal_data(scp,(char*)&rdc,sizeof(gdth_rdcap_data));
+ break;
+
+ default:
+ TRACE2(("Internal cache cmd 0x%x unknown\n",scp->cmnd[0]));
+ break;
+ }
+ scp->result = DID_OK << 16;
+ }
+
+ restore_flags(*flags);
+ if (!scp->SCp.have_data_in)
+ scp->SCp.have_data_in++;
+ else
+ scp->scsi_done(scp);
+ save_flags(*flags);
+ cli();
+ return 1;
+}
+
+static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmdp;
+ struct scatterlist *sl;
+ ushort i;
+ int cmd_index;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp = ha->pccb;
+ TRACE(("gdth_fill_cache_cmd() cmd 0x%x cmdsize %d hdrive %d\n",
+ scp->cmnd[0],scp->cmd_len,hdrive));
+
+ if (ha->type==GDT_EISA && ha->cmd_cnt>0)
+ return 0;
+
+ cmdp->Service = CACHESERVICE;
+ cmdp->RequestBuffer = scp;
+ /* search free command index */
+ if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+ TRACE(("GDT: No free command index found\n"));
+ return 0;
+ }
+ /* if it's the first command, set command semaphore */
+ if (ha->cmd_cnt == 0)
+ gdth_set_sema0(hanum);
+
+ /* fill command */
+ if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) {
+ if (scp->cmnd[4] & 1) /* prevent ? */
+ cmdp->OpCode = GDT_MOUNT;
+ else if (scp->cmnd[3] & 1) /* removable drive ? */
+ cmdp->OpCode = GDT_UNMOUNT;
+ else
+ cmdp->OpCode = GDT_FLUSH;
+ } else {
+ if (scp->cmnd[0]==WRITE_6 || scp->cmnd[0]==WRITE_10) {
+ if (gdth_write_through)
+ cmdp->OpCode = GDT_WRITE_THR;
+ else
+ cmdp->OpCode = GDT_WRITE;
+ } else {
+ cmdp->OpCode = GDT_READ;
+ }
+ }
+
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.cache.DeviceNo = hdrive;
+
+ if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) {
+ cmdp->u.cache.BlockNo = 1;
+ cmdp->u.cache.sg_canz = 0;
+ } else {
+ if (scp->cmd_len != 6) {
+ cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[2]);
+ cmdp->u.cache.BlockCnt= (ulong)ntohs(*(ushort*)&scp->cmnd[7]);
+ } else {
+ cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[0]) & 0x001fffffUL;
+ cmdp->u.cache.BlockCnt= scp->cmnd[4]==0 ? 0x100 : scp->cmnd[4];
+ }
+
+ if (scp->use_sg) {
+ cmdp->u.cache.DestAddr= -1UL;
+ sl = (struct scatterlist *)scp->request_buffer;
+ for (i=0; i<scp->use_sg; ++i,++sl) {
+ cmdp->u.cache.sg_lst[i].sg_ptr = virt_to_bus(sl->address);
+ cmdp->u.cache.sg_lst[i].sg_len = (ulong)sl->length;
+ }
+ cmdp->u.cache.sg_canz = (ulong)i;
+
+#ifdef GDTH_STATISTICS
+ if (max_sg < (ulong)i) {
+ max_sg = (ulong)i;
+ TRACE3(("GDT: max_sg = %d\n",i));
+ }
+#endif
+ if (i<GDTH_MAXSG)
+ cmdp->u.cache.sg_lst[i].sg_len = 0;
+ } else {
+ if (ha->cache_feat & SCATTER_GATHER) {
+ cmdp->u.cache.DestAddr = -1UL;
+ cmdp->u.cache.sg_canz = 1;
+ cmdp->u.cache.sg_lst[0].sg_ptr = virt_to_bus(scp->request_buffer);
+ cmdp->u.cache.sg_lst[0].sg_len = scp->request_bufflen;
+ cmdp->u.cache.sg_lst[1].sg_len = 0;
+ } else {
+ cmdp->u.cache.DestAddr = virt_to_bus(scp->request_buffer);
+ cmdp->u.cache.sg_canz= 0;
+ }
+ }
+ }
+ TRACE(("cache cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\n",
+ cmdp->u.cache.DestAddr,cmdp->u.cache.sg_canz,
+ cmdp->u.cache.sg_lst[0].sg_ptr,
+ cmdp->u.cache.sg_lst[0].sg_len));
+ TRACE(("cache cmd: cmd %d blockno. %ld, blockcnt %ld\n",
+ cmdp->OpCode,cmdp->u.cache.BlockNo,cmdp->u.cache.BlockCnt));
+
+ /* evaluate command size, check space */
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) +
+ (ushort)cmdp->u.cache.sg_canz * sizeof(gdth_sg_str);
+ if (ha->cmd_len & 3)
+ ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+ if (ha->cmd_cnt > 0) {
+ if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+ ha->ic_all_size) {
+ TRACE2(("gdth_fill_cache() DPMEM overflow\n"));
+ gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+ return 0;
+ }
+ }
+
+ /* copy command */
+ gdth_copy_command(hanum);
+ return cmd_index;
+}
+
+static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmdp;
+ struct scatterlist *sl;
+ ushort i;
+ int cmd_index;
+ unchar t,l;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ t = scp->target;
+ l = scp->lun;
+ cmdp = ha->pccb;
+ TRACE(("gdth_fill_raw_cmd() cmd 0x%x bus %d ID %d LUN %d\n",
+ scp->cmnd[0],b,t,l));
+
+ if (ha->type==GDT_EISA && ha->cmd_cnt>0)
+ return 0;
+
+ cmdp->Service = SCSIRAWSERVICE;
+ cmdp->RequestBuffer = scp;
+ /* search free command index */
+ if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+ TRACE(("GDT: No free command index found\n"));
+ return 0;
+ }
+ /* if it's the first command, set command semaphore */
+ if (ha->cmd_cnt == 0)
+ gdth_set_sema0(hanum);
+
+ /* fill command */
+ cmdp->OpCode = GDT_WRITE; /* always */
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.raw.reserved = 0;
+ cmdp->u.raw.mdisc_time = 0;
+ cmdp->u.raw.mcon_time = 0;
+ cmdp->u.raw.clen = scp->cmd_len;
+ cmdp->u.raw.target = t;
+ cmdp->u.raw.lun = l;
+ cmdp->u.raw.bus = b;
+ cmdp->u.raw.priority = 0;
+ cmdp->u.raw.link_p = NULL;
+ cmdp->u.raw.sdlen = scp->request_bufflen;
+ cmdp->u.raw.sense_len = 16;
+ cmdp->u.raw.sense_data = virt_to_bus(scp->sense_buffer);
+ cmdp->u.raw.direction =
+ gdth_direction_tab[scp->cmnd[0]]==DOU ? DATA_OUT : DATA_IN;
+ memcpy(cmdp->u.raw.cmd,scp->cmnd,12);
+
+ if (scp->use_sg) {
+ cmdp->u.raw.sdata = -1UL;
+ sl = (struct scatterlist *)scp->request_buffer;
+ for (i=0; i<scp->use_sg; ++i,++sl) {
+ cmdp->u.raw.sg_lst[i].sg_ptr = virt_to_bus(sl->address);
+ cmdp->u.raw.sg_lst[i].sg_len = (ulong)sl->length;
+ }
+ cmdp->u.raw.sg_ranz = (ulong)i;
+
+#ifdef GDTH_STATISTICS
+ if (max_sg < (ulong)i) {
+ max_sg = (ulong)i;
+ TRACE3(("GDT: max_sg = %d\n",i));
+ }
+#endif
+ if (i<GDTH_MAXSG)
+ cmdp->u.raw.sg_lst[i].sg_len = 0;
+ } else {
+ if (ha->raw_feat & SCATTER_GATHER) {
+ cmdp->u.raw.sdata = -1UL;
+ cmdp->u.raw.sg_ranz= 1;
+ cmdp->u.raw.sg_lst[0].sg_ptr = virt_to_bus(scp->request_buffer);
+ cmdp->u.raw.sg_lst[0].sg_len = scp->request_bufflen;
+ cmdp->u.raw.sg_lst[1].sg_len = 0;
+ } else {
+ cmdp->u.raw.sdata = virt_to_bus(scp->request_buffer);
+ cmdp->u.raw.sg_ranz= 0;
+ }
+ }
+ TRACE(("raw cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\n",
+ cmdp->u.raw.sdata,cmdp->u.raw.sg_ranz,
+ cmdp->u.raw.sg_lst[0].sg_ptr,
+ cmdp->u.raw.sg_lst[0].sg_len));
+
+ /* evaluate command size, check space */
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) +
+ (ushort)cmdp->u.raw.sg_ranz * sizeof(gdth_sg_str);
+ if (ha->cmd_len & 3)
+ ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+ if (ha->cmd_cnt > 0) {
+ if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+ ha->ic_all_size) {
+ TRACE2(("gdth_fill_raw() DPMEM overflow\n"));
+ gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+ return 0;
+ }
+ }
+
+ /* copy command */
+ gdth_copy_command(hanum);
+ return cmd_index;
+}
+
+static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b)
+{
+ register gdth_ha_str *ha;
+ register gdth_cmd_str *cmdp;
+ int cmd_index;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp= ha->pccb;
+ TRACE2(("gdth_special_cmd(): "));
+
+ if (ha->type==GDT_EISA && ha->cmd_cnt>0)
+ return 0;
+
+ memcpy( cmdp, scp->request_buffer, sizeof(gdth_cmd_str));
+ cmdp->RequestBuffer = scp;
+
+ /* search free command index */
+ if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+ TRACE(("GDT: No free command index found\n"));
+ return 0;
+ }
+
+ /* if it's the first command, set command semaphore */
+ if (ha->cmd_cnt == 0)
+ gdth_set_sema0(hanum);
+
+ /* evaluate command size, check space */
+ if (cmdp->OpCode == GDT_IOCTL) {
+ TRACE2(("IOCTL\n"));
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.ioctl.p_param) + sizeof(ulong);
+ } else if (cmdp->Service == CACHESERVICE) {
+ TRACE2(("cache command %d\n",cmdp->OpCode));
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) + sizeof(gdth_sg_str);
+ } else if (cmdp->Service == SCSIRAWSERVICE) {
+ TRACE2(("raw command %d/%d\n",cmdp->OpCode,cmdp->u.raw.cmd[0]));
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) + sizeof(gdth_sg_str);
+ }
+
+ if (ha->cmd_len & 3)
+ ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+ if (ha->cmd_cnt > 0) {
+ if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+ ha->ic_all_size) {
+ TRACE2(("gdth_special_cmd() DPMEM overflow\n"));
+ gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+ return 0;
+ }
+ }
+
+ /* copy command */
+ gdth_copy_command(hanum);
+ return cmd_index;
+}
+
+
+/* Controller event handling functions */
+static gdth_evt_str *gdth_store_event(ushort source, ushort idx,
+ gdth_evt_data *evt)
+{
+ gdth_evt_str *e;
+ ulong flags;
+ struct timeval tv;
+
+ TRACE2(("gdth_store_event() source %d idx %d\n", source, idx));
+ if (source == 0) /* no source -> no event */
+ return 0;
+
+ save_flags(flags);
+ cli();
+ if (ebuffer[elastidx].event_source == source &&
+ ebuffer[elastidx].event_idx == idx &&
+ !memcmp((char *)&ebuffer[elastidx].event_data.eu,
+ (char *)&evt->eu, evt->size)) {
+ e = &ebuffer[elastidx];
+ do_gettimeofday(&tv);
+ e->last_stamp = tv.tv_sec;
+ ++e->same_count;
+ } else {
+ if (ebuffer[elastidx].event_source != 0) { /* entry not free ? */
+ ++elastidx;
+ if (elastidx == MAX_EVENTS)
+ elastidx = 0;
+ if (elastidx == eoldidx) { /* reached mark ? */
+ ++eoldidx;
+ if (eoldidx == MAX_EVENTS)
+ eoldidx = 0;
+ }
+ }
+ e = &ebuffer[elastidx];
+ e->event_source = source;
+ e->event_idx = idx;
+ do_gettimeofday(&tv);
+ e->first_stamp = e->last_stamp = tv.tv_sec;
+ e->same_count = 1;
+ e->event_data = *evt;
+ }
+ restore_flags(flags);
+ return e;
+}
+
+static int gdth_read_event(int handle, gdth_evt_str *estr)
+{
+ gdth_evt_str *e;
+ int eindex;
+ ulong flags;
+
+ TRACE2(("gdth_read_event() handle %d\n", handle));
+ save_flags(flags);
+ cli();
+ if (handle == -1)
+ eindex = eoldidx;
+ else
+ eindex = handle;
+ estr->event_source = 0;
+
+ if (eindex >= MAX_EVENTS) {
+ restore_flags(flags);
+ return eindex;
+ }
+ e = &ebuffer[eindex];
+ if (e->event_source != 0) {
+ if (eindex != elastidx) {
+ if (++eindex == MAX_EVENTS)
+ eindex = 0;
+ } else {
+ eindex = -1;
+ }
+ memcpy(estr, e, sizeof(gdth_evt_str));
+ }
+ restore_flags(flags);
+ return eindex;
+}
+
+static void gdth_readapp_event(unchar application, gdth_evt_str *estr)
+{
+ gdth_evt_str *e;
+ int eindex;
+ ulong flags;
+ unchar found = FALSE;
+
+ TRACE2(("gdth_readapp_event() app. %d\n", application));
+ save_flags(flags);
+ cli();
+ eindex = eoldidx;
+ for (;;) {
+ e = &ebuffer[eindex];
+ if (e->event_source == 0)
+ break;
+ if ((e->application & application) == 0) {
+ e->application |= application;
+ found = TRUE;
+ break;
+ }
+ if (eindex == elastidx)
+ break;
+ if (++eindex == MAX_EVENTS)
+ eindex = 0;
+ }
+ if (found)
+ memcpy(estr, e, sizeof(gdth_evt_str));
+ else
+ estr->event_source = 0;
+ restore_flags(flags);
+}
+
+static void gdth_clear_events()
+{
+ ulong flags;
+
+ TRACE(("gdth_clear_events()"));
+ save_flags(flags);
+ cli();
+
+ eoldidx = elastidx = 0;
+ ebuffer[0].event_source = 0;
+ restore_flags(flags);
+}
+
+
+/* SCSI interface functions */
+
+#if LINUX_VERSION_CODE >= 0x02015F
+static void do_gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs)
+{
+ ulong flags;
+
+ spin_lock_irqsave(&io_request_lock, flags);
+ gdth_interrupt(irq, dev_id, regs);
+ spin_unlock_irqrestore(&io_request_lock, flags);
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x010346
+static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs)
+#else
+static void gdth_interrupt(int irq,struct pt_regs *regs)
+#endif
+{
+ register gdth_ha_str *ha;
+ gdt6m_dpram_str *dp6m_ptr;
+ gdt6_dpram_str *dp6_ptr;
+ gdt2_dpram_str *dp2_ptr;
+ Scsi_Cmnd *scp;
+ int hanum;
+ unchar IStatus;
+ ushort CmdStatus, Service = 0;
+ ulong InfoBytes, InfoBytes2 = 0;
+ gdth_evt_data dvr;
+
+ TRACE(("gdth_interrupt() IRQ %d\n",irq));
+
+ /* if polling and not from gdth_wait() -> return */
+ if (gdth_polling) {
+ if (!gdth_from_wait) {
+ return;
+ }
+ }
+
+ wait_index = 0;
+
+ /* search controller */
+ if ((hanum = gdth_get_status(&IStatus,irq)) == -1) {
+ /*
+ TRACE2(("gdth_interrupt(): Spurious interrupt received\n"));
+ */
+ return;
+ }
+
+#ifdef GDTH_STATISTICS
+ ++act_ints;
+#endif
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (ha->type == GDT_EISA) {
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = inw(ha->bmic + MAILBOXREG+8);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = inw(ha->bmic + MAILBOXREG+10);
+ InfoBytes2 = inl(ha->bmic + MAILBOXREG+4);
+ }
+ } else /* no error */
+ CmdStatus = S_OK;
+ InfoBytes = inl(ha->bmic + MAILBOXREG+12);
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = inl(ha->bmic + MAILBOXREG+4);
+ outb(0xff, ha->bmic + EDOORREG); /* acknowledge interrupt */
+ outb(0x00, ha->bmic + SEMA1REG); /* reset status semaphore */
+ } else if (ha->type == GDT_ISA) {
+ dp2_ptr = (gdt2_dpram_str *)ha->brd;
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = readw(&dp2_ptr->u.ic.Status);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = readw(&dp2_ptr->u.ic.Service);
+ InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]);
+ }
+ } else /* no error */
+ CmdStatus = S_OK;
+ InfoBytes = readl(&dp2_ptr->u.ic.Info[0]);
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]);
+ writeb(0xff, &dp2_ptr->io.irqdel); /* acknowledge interrupt */
+ writeb(0, &dp2_ptr->u.ic.Cmd_Index); /* reset command index */
+ writeb(0, &dp2_ptr->io.Sema1); /* reset status semaphore */
+ } else if (ha->type == GDT_PCI) {
+ dp6_ptr = (gdt6_dpram_str *)ha->brd;
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = readw(&dp6_ptr->u.ic.Status);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = readw(&dp6_ptr->u.ic.Service);
+ InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]);
+ }
+ } else /* no error */
+ CmdStatus = S_OK;
+ InfoBytes = readl(&dp6_ptr->u.ic.Info[0]);
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]);
+ writeb(0xff, &dp6_ptr->io.irqdel); /* acknowledge interrupt */
+ writeb(0, &dp6_ptr->u.ic.Cmd_Index); /* reset command index */
+ writeb(0, &dp6_ptr->io.Sema1); /* reset status semaphore */
+ } else if (ha->type == GDT_PCINEW) {
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = inw(PTR2USHORT(&ha->plx->status));
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = inw(PTR2USHORT(&ha->plx->service));
+ InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1]));
+ }
+ } else
+ CmdStatus = S_OK;
+
+ InfoBytes = inl(PTR2USHORT(&ha->plx->info[0]));
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1]));
+ outb(0xff, PTR2USHORT(&ha->plx->edoor_reg));
+ outb(0x00, PTR2USHORT(&ha->plx->sema1_reg));
+ } else if (ha->type == GDT_PCIMPR) {
+ dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = readw(&dp6m_ptr->i960r.status);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = readw(&dp6m_ptr->i960r.service);
+ InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]);
+ }
+ } else /* no error */
+ CmdStatus = S_OK;
+ InfoBytes = readl(&dp6m_ptr->i960r.info[0]);
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]);
+ writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ writeb(0, &dp6m_ptr->i960r.sema1_reg);
+ } else {
+ TRACE2(("gdth_interrupt() unknown controller type\n"));
+ return;
+ }
+
+ TRACE(("gdth_interrupt() index %d stat %d info %ld\n",
+ IStatus,CmdStatus,InfoBytes));
+ ha->status = CmdStatus;
+ ha->info = InfoBytes;
+ ha->info2 = InfoBytes2;
+
+ if (gdth_from_wait) {
+ wait_hanum = hanum;
+ wait_index = (int)IStatus;
+ }
+
+ if (IStatus == ASYNCINDEX) {
+ TRACE2(("gdth_interrupt() async. event\n"));
+ gdth_async_event(hanum,Service);
+ } else {
+ if (IStatus == SPEZINDEX) {
+ TRACE2(("Service unknown or not initialized !\n"));
+ dvr.size = sizeof(dvr.eu.driver);
+ dvr.eu.driver.ionode = hanum;
+ gdth_store_event(ES_DRIVER, 4, &dvr);
+ return;
+ }
+ scp = gdth_cmd_tab[IStatus-2][hanum].cmnd;
+ Service = gdth_cmd_tab[IStatus-2][hanum].service;
+ gdth_cmd_tab[IStatus-2][hanum].cmnd = UNUSED_CMND;
+ if (scp == UNUSED_CMND) {
+ TRACE2(("gdth_interrupt() index to unused command (%d)\n",IStatus));
+ dvr.size = sizeof(dvr.eu.driver);
+ dvr.eu.driver.ionode = hanum;
+ dvr.eu.driver.index = IStatus;
+ gdth_store_event(ES_DRIVER, 1, &dvr);
+ return;
+ }
+ if (scp == INTERNAL_CMND) {
+ TRACE(("gdth_interrupt() answer to internal command\n"));
+ return;
+ }
+ TRACE(("gdth_interrupt() sync. status\n"));
+ gdth_sync_event(hanum,Service,IStatus,scp);
+ }
+ gdth_next(hanum);
+}
+
+static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp)
+{
+ register gdth_ha_str *ha;
+ gdth_msg_str *msg;
+ gdth_cmd_str *cmdp;
+ char c='\r';
+ ushort i;
+ gdth_evt_data dvr;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp = ha->pccb;
+ TRACE(("gdth_sync_event() scp %lx serv %d status %d\n",
+ (ulong)scp,service,ha->status));
+
+ if (service == SCREENSERVICE) {
+ msg = (gdth_msg_str *)ha->pscratch;
+ TRACE(("len: %ld, answer: %d, ext: %d, alen: %ld\n",
+ msg->msg_len,msg->msg_answer,msg->msg_ext,msg->msg_alen));
+ if (msg->msg_len)
+ if (!(msg->msg_answer && msg->msg_ext)) {
+ msg->msg_text[msg->msg_len] = '\0';
+ printk("%s",msg->msg_text);
+ }
+
+ if (msg->msg_ext && !msg->msg_answer) {
+ while (gdth_test_busy(hanum))
+ gdth_delay(0);
+ cmdp->Service = SCREENSERVICE;
+ cmdp->RequestBuffer = SCREEN_CMND;
+ gdth_get_cmd_index(hanum);
+ gdth_set_sema0(hanum);
+ cmdp->OpCode = GDT_READ;
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.screen.reserved = 0;
+ cmdp->u.screen.msg_handle= msg->msg_handle;
+ cmdp->u.screen.msg_addr = (ulong)msg;
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr)
+ + sizeof(ulong);
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ gdth_release_event(hanum);
+ return 1;
+ }
+
+ if (msg->msg_answer && msg->msg_alen) {
+ for (i=0; i<msg->msg_alen && i<MSGLEN; ++i) {
+ /* getchar() ?? */
+ /* .. */
+ if (c == '\r')
+ break;
+ msg->msg_text[i] = c;
+ }
+ msg->msg_alen -= i;
+ if (c!='\r' && msg->msg_alen!=0) {
+ msg->msg_answer = 1;
+ msg->msg_ext = 1;
+ } else {
+ msg->msg_ext = 0;
+ msg->msg_answer = 0;
+ }
+ msg->msg_len = i;
+ while (gdth_test_busy(hanum))
+ gdth_delay(0);
+ cmdp->Service = SCREENSERVICE;
+ cmdp->RequestBuffer = SCREEN_CMND;
+ gdth_get_cmd_index(hanum);
+ gdth_set_sema0(hanum);
+ cmdp->OpCode = GDT_WRITE;
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.screen.reserved = 0;
+ cmdp->u.screen.msg_handle= msg->msg_handle;
+ cmdp->u.screen.msg_addr = (ulong)msg;
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr)
+ + sizeof(ulong);
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ gdth_release_event(hanum);
+ return 1;
+ }
+ printk("\n");
+
+ } else {
+ scp->SCp.Message = (int)ha->status;
+ /* cache or raw service */
+ if (ha->status == S_OK) {
+ scp->result = DID_OK << 16;
+ } else if (ha->status == S_BSY) {
+ TRACE2(("Controller busy -> retry !\n"));
+ gdth_putq(hanum,scp,scp->SCp.this_residual);
+ return 1;
+ } else {
+ if (service == CACHESERVICE) {
+ memset((char*)scp->sense_buffer,0,16);
+ scp->sense_buffer[0] = 0x70;
+ scp->sense_buffer[2] = NOT_READY;
+ scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+
+ if (scp->done != gdth_scsi_done) {
+ dvr.size = sizeof(dvr.eu.sync);
+ dvr.eu.sync.ionode = hanum;
+ dvr.eu.sync.service = service;
+ dvr.eu.sync.status = ha->status;
+ dvr.eu.sync.info = ha->info;
+ dvr.eu.sync.hostdrive =
+#if LINUX_VERSION_CODE >= 0x020000
+ ha->id[scp->channel][scp->target].hostdrive;
+#else
+ ha->id[NUMDATA(scp->host)->busnum][scp->target].hostdrive;
+#endif
+ if (ha->status >= 0x8000)
+ gdth_store_event(ES_SYNC, 0, &dvr);
+ else
+ gdth_store_event(ES_SYNC, service, &dvr);
+ }
+ } else {
+ if (ha->status!=S_RAW_SCSI || ha->status==S_RAW_ILL || ha->info>=0x100) {
+ scp->result = DID_BAD_TARGET << 16;
+ } else {
+ scp->result = (DID_OK << 16) | ha->info;
+ }
+ }
+ }
+ if (!scp->SCp.have_data_in)
+ scp->SCp.have_data_in++;
+ else
+ scp->scsi_done(scp);
+ }
+
+ return 1;
+}
+
+static char *async_cache_tab[] = {
+/* 0*/ "\011\000\002\002\002\004\002\006\004"
+ "GDT HA %u, service %u, async. status %u/%lu unknown",
+/* 1*/ "\011\000\002\002\002\004\002\006\004"
+ "GDT HA %u, service %u, async. status %u/%lu unknown",
+/* 2*/ "\005\000\002\006\004"
+ "GDT HA %u, Host Drive %lu not ready",
+/* 3*/ "\005\000\002\006\004"
+ "GDT HA %u, Host Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced",
+/* 4*/ "\005\000\002\006\004"
+ "GDT HA %u, mirror update on Host Drive %lu failed",
+/* 5*/ "\005\000\002\006\004"
+ "GDT HA %u, Mirror Drive %lu failed",
+/* 6*/ "\005\000\002\006\004"
+ "GDT HA %u, Mirror Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced",
+/* 7*/ "\005\000\002\006\004"
+ "GDT HA %u, Host Drive %lu write protected",
+/* 8*/ "\005\000\002\006\004"
+ "GDT HA %u, media changed in Host Drive %lu",
+/* 9*/ "\005\000\002\006\004"
+ "GDT HA %u, Host Drive %lu is offline",
+/*10*/ "\005\000\002\006\004"
+ "GDT HA %u, media change of Mirror Drive %lu",
+/*11*/ "\005\000\002\006\004"
+ "GDT HA %u, Mirror Drive %lu is write protected",
+/*12*/ "\005\000\002\006\004"
+ "GDT HA %u, general error on Host Drive %lu. Please check the devices of this drive!",
+/*13*/ "\007\000\002\006\002\010\002"
+ "GDT HA %u, Array Drive %u: Cache Drive %u failed",
+/*14*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: FAIL state entered",
+/*15*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: error",
+/*16*/ "\007\000\002\006\002\010\002"
+ "GDT HA %u, Array Drive %u: failed drive replaced by Cache Drive %u",
+/*17*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: parity build failed",
+/*18*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive rebuild failed",
+/*19*/ "\007\000\002\010\002"
+ "GDT HA %u, Test of Hot Fix %u failed",
+/*20*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive build finished successfully",
+/*21*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive rebuild finished successfully",
+/*22*/ "\007\000\002\006\002\010\002"
+ "GDT HA %u, Array Drive %u: Hot Fix %u activated",
+/*23*/ "\005\000\002\006\002"
+ "GDT HA %u, Host Drive %u: processing of i/o aborted due to serious drive error",
+/*24*/ "\005\000\002\010\002"
+ "GDT HA %u, mirror update on Cache Drive %u completed",
+/*25*/ "\005\000\002\010\002"
+ "GDT HA %u, mirror update on Cache Drive %lu failed",
+/*26*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive rebuild started",
+/*27*/ "\005\000\002\012\001"
+ "GDT HA %u, Fault bus %u: SHELF OK detected",
+/*28*/ "\005\000\002\012\001"
+ "GDT HA %u, Fault bus %u: SHELF not OK detected",
+/*29*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug started",
+/*30*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: new disk detected",
+/*31*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: old disk detected",
+/*32*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: plugging an active disk is illegal",
+/*33*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: illegal device detected",
+/*34*/ "\011\000\002\012\001\013\001\006\004"
+ "GDT HA %u, Fault bus %u, ID %u: insufficient disk capacity (%lu MB required)",
+/*35*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: disk write protected",
+/*36*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: disk not available",
+/*37*/ "\007\000\002\012\001\006\004"
+ "GDT HA %u, Fault bus %u: swap detected (%lu)",
+/*38*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug finished successfully",
+/*39*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted due to user Hot Plug",
+/*40*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted",
+/*41*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug for Hot Fix started",
+/*42*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: drive build started",
+/*43*/ "\003\000\002"
+ "GDT HA %u, DRAM parity error detected",
+/*44*/ "\005\000\002\006\002"
+ "GDT HA %u, Mirror Drive %u: update started",
+/*45*/ "\007\000\002\006\002\010\002"
+ "GDT HA %u, Mirror Drive %u: Hot Fix %u activated",
+/*46*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: no matching Pool Hot Fix Drive available",
+/*47*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: Pool Hot Fix Drive available",
+/*48*/ "\005\000\002\006\002"
+ "GDT HA %u, Mirror Drive %u: no matching Pool Hot Fix Drive available",
+/*49*/ "\005\000\002\006\002"
+ "GDT HA %u, Mirror Drive %u: Pool Hot Fix Drive available",
+/*50*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, SCSI bus %u, ID %u: IGNORE_WIDE_RESIDUE message received",
+/*51*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand started",
+/*52*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand finished successfully",
+/*53*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand failed",
+/*54*/ "\003\000\002"
+ "GDT HA %u, CPU temperature critical",
+/*55*/ "\003\000\002"
+ "GDT HA %u, CPU temperature OK",
+/*56*/ "\005\000\002\006\004"
+ "GDT HA %u, Host drive %lu created",
+/*57*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand restarted",
+/*58*/ "\005\000\002\006\002"
+ "GDT HA %u, Array Drive %u: expand stopped",
+};
+
+
+static int gdth_async_event(int hanum,int service)
+{
+ gdth_stackframe stack;
+ gdth_evt_data dvr;
+ char *f = NULL;
+ int i,j;
+ gdth_ha_str *ha;
+ gdth_msg_str *msg;
+ gdth_cmd_str *cmdp;
+ int cmd_index;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp= ha->pccb;
+ msg = (gdth_msg_str *)ha->pscratch;
+ TRACE2(("gdth_async_event() ha %d serv %d\n",
+ hanum,service));
+
+ if (service == SCREENSERVICE) {
+ if (ha->status == MSG_REQUEST) {
+ while (gdth_test_busy(hanum))
+ gdth_delay(0);
+ cmdp->Service = SCREENSERVICE;
+ cmdp->RequestBuffer = SCREEN_CMND;
+ cmd_index = gdth_get_cmd_index(hanum);
+ gdth_set_sema0(hanum);
+ cmdp->OpCode = GDT_READ;
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.screen.reserved = 0;
+ cmdp->u.screen.msg_handle= MSG_INV_HANDLE;
+ cmdp->u.screen.msg_addr = (ulong)msg;
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr)
+ + sizeof(ulong);
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ if (ha->type == GDT_EISA)
+ printk("[EISA slot %d] ",(ushort)ha->brd_phys);
+ else if (ha->type == GDT_ISA)
+ printk("[DPMEM 0x%4X] ",(ushort)ha->brd_phys);
+ else
+ printk("[PCI %d/%d] ",(ushort)(ha->brd_phys>>8),
+ (ushort)((ha->brd_phys>>3)&0x1f));
+ gdth_release_event(hanum);
+ }
+
+ } else {
+ dvr.size = sizeof(dvr.eu.async);
+ dvr.eu.async.ionode = hanum;
+ dvr.eu.async.service = service;
+ dvr.eu.async.status = ha->status;
+ dvr.eu.async.info = ha->info;
+ *(ulong *)dvr.eu.async.scsi_coord = ha->info2;
+ gdth_store_event(ES_ASYNC, service, &dvr);
+
+ if (service==CACHESERVICE && INDEX_OK(ha->status,async_cache_tab)) {
+ TRACE2(("GDT: Async. event cache service, event no.: %d\n",
+ ha->status));
+
+ f = async_cache_tab[ha->status];
+
+ /* i: parameter to push, j: stack element to fill */
+ for (j=0,i=1; i < f[0]; i+=2) {
+ switch (f[i+1]) {
+ case 4:
+ stack.b[j++] = *(ulong*)&dvr.eu.stream[(int)f[i]];
+ break;
+ case 2:
+ stack.b[j++] = *(ushort*)&dvr.eu.stream[(int)f[i]];
+ break;
+ case 1:
+ stack.b[j++] = *(unchar*)&dvr.eu.stream[(int)f[i]];
+ break;
+ default:
+ break;
+ }
+ }
+
+ printk(&f[f[0]],stack); printk("\n");
+
+ } else {
+ printk("GDT: Unknown async. event service %d event no. %d\n",
+ service,ha->status);
+ }
+ }
+ return 1;
+}
+
+
+#ifdef GDTH_STATISTICS
+void gdth_timeout(ulong data)
+{
+ ulong flags,i;
+ Scsi_Cmnd *nscp;
+ gdth_ha_str *ha;
+ int hanum = 0;
+
+ save_flags(flags);
+ cli();
+
+ for (act_stats=0,i=0; i<GDTH_MAXCMDS; ++i)
+ if (gdth_cmd_tab[i][hanum].cmnd != UNUSED_CMND)
+ ++act_stats;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ for (act_rq=0,nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
+ ++act_rq;
+
+ TRACE2(("gdth_to(): ints %ld, ios %ld, act_stats %ld, act_rq %ld\n",
+ act_ints, act_ios, act_stats, act_rq));
+ act_ints = act_ios = 0;
+
+ gdth_timer.expires = jiffies + 30 * HZ;
+ add_timer(&gdth_timer);
+ restore_flags(flags);
+}
+#endif
+
+
+__initfunc (int gdth_detect(Scsi_Host_Template *shtp))
+{
+ struct Scsi_Host *shp;
+ gdth_ha_str *ha;
+ unsigned long flags;
+ ulong isa_bios;
+ ushort eisa_slot,device_id,index;
+ gdth_pci_str pcistr;
+ int i,j,hanum;
+ unchar b;
+
+
+#ifdef DEBUG_GDTH
+ printk("GDT: This driver contains debugging information !! Trace level = %d\n",
+ DebugState);
+ printk(" Destination of debugging information: ");
+#ifdef __SERIAL__
+#ifdef __COM2__
+ printk("Serial port COM2\n");
+#else
+ printk("Serial port COM1\n");
+#endif
+#else
+ printk("Console\n");
+#endif
+ gdth_delay(3000);
+#endif
+
+ TRACE(("gdth_detect()\n"));
+
+ if (disable_gdth_scan) {
+ printk("GDT: Controller driver disabled from command line !\n");
+ return 0;
+ }
+
+ /* initializations */
+ gdth_polling = TRUE; b = 0;
+ for (i=0; i<GDTH_MAXCMDS; ++i)
+ for (j=0; j<MAXHA; ++j)
+ gdth_cmd_tab[i][j].cmnd = UNUSED_CMND;
+ for (i=0; i<4; ++i)
+ for (j=0; j<MAXHA; ++j)
+ gdth_ioctl_tab[i][j] = NULL;
+ gdth_clear_events();
+
+ /* scanning for controllers, at first: ISA controller */
+ for (isa_bios=0xc8000UL; isa_bios<=0xd8000UL; isa_bios+=0x8000UL) {
+ if (gdth_search_isa(isa_bios)) { /* controller found */
+ shp = scsi_register(shtp,sizeof(gdth_ext_str));
+ ha = HADATA(shp);
+ if (!gdth_init_isa(isa_bios,ha)) {
+ scsi_unregister(shp);
+ continue;
+ }
+ /* controller found and initialized */
+ printk("Configuring GDT-ISA HA at BIOS 0x%05lX IRQ %u DRQ %u\n",
+ isa_bios,ha->irq,ha->drq);
+
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x02015F
+ if (request_irq(ha->irq,do_gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#elif LINUX_VERSION_CODE >= 0x010346
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth"))
+#endif
+ {
+ printk("GDT-ISA: Unable to allocate IRQ\n");
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+ if (request_dma(ha->drq,"gdth")) {
+ printk("GDT-ISA: Unable to allocate DMA channel\n");
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(ha->irq,NULL);
+#else
+ free_irq(ha->irq);
+#endif
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+ set_dma_mode(ha->drq,DMA_MODE_CASCADE);
+ enable_dma(ha->drq);
+ shp->unchecked_isa_dma = 1;
+ shp->irq = ha->irq;
+ shp->dma_channel = ha->drq;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[0][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ hanum = gdth_ctr_count;
+ gdth_ctr_tab[gdth_ctr_count++] = shp;
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum= 0;
+
+ ha->pccb = CMDDATA(shp);
+ ha->pscratch = DMADATA(shp);
+ ha->req_first = NULL;
+ for (i=0; i<MAXBUS; ++i) {
+ for (j=0; j<MAXID; ++j) {
+ ha->id[i][j].type = EMPTY_DTYP;
+ ha->id[i][j].lock = 0;
+ ha->id[i][j].heads = 0;
+ }
+ }
+ restore_flags(flags);
+
+ if (!gdth_search_drives(hanum)) {
+ printk("GDT-ISA: Error during device scan\n");
+ --gdth_ctr_count;
+ --gdth_ctr_vcount;
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(ha->irq,NULL);
+#else
+ free_irq(ha->irq);
+#endif
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+
+#if LINUX_VERSION_CODE >= 0x020000
+ shp->max_id = 8;
+ shp->max_lun = MAXLUN;
+ shp->max_channel = ha->bus_cnt - 1;
+#else
+ /* register addit. SCSI channels as virtual controllers */
+ for (b=1; b<ha->bus_cnt; ++b) {
+ shp = scsi_register(shtp,sizeof(gdth_num_str));
+ shp->unchecked_isa_dma = 1;
+ shp->irq = ha->irq;
+ shp->dma_channel = ha->drq;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[b][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum = b;
+ }
+#endif
+
+ gdth_enable_int(hanum);
+ }
+ }
+
+ /* scanning for EISA controllers */
+ for (eisa_slot=0x1000; eisa_slot<=0x8000; eisa_slot+=0x1000) {
+ if (gdth_search_eisa(eisa_slot)) { /* controller found */
+ shp = scsi_register(shtp,sizeof(gdth_ext_str));
+ ha = HADATA(shp);
+ if (!gdth_init_eisa(eisa_slot,ha)) {
+ scsi_unregister(shp);
+ continue;
+ }
+ /* controller found and initialized */
+ printk("Configuring GDT-EISA HA at Slot %d IRQ %u\n",
+ eisa_slot>>12,ha->irq);
+
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x02015F
+ if (request_irq(ha->irq,do_gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#elif LINUX_VERSION_CODE >= 0x010346
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth"))
+#endif
+ {
+ printk("GDT-EISA: Unable to allocate IRQ\n");
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[0][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ hanum = gdth_ctr_count;
+ gdth_ctr_tab[gdth_ctr_count++] = shp;
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum= 0;
+ TRACE2(("EISA detect Bus 0: shp %lx hanum %d\n",
+ (ulong)shp,NUMDATA(shp)->hanum));
+
+ ha->pccb = CMDDATA(shp);
+ ha->pscratch = DMADATA(shp);
+ ha->req_first = NULL;
+ for (i=0; i<MAXBUS; ++i) {
+ for (j=0; j<MAXID; ++j) {
+ ha->id[i][j].type = EMPTY_DTYP;
+ ha->id[i][j].lock = 0;
+ ha->id[i][j].heads = 0;
+ }
+ }
+ restore_flags(flags);
+
+ if (!gdth_search_drives(hanum)) {
+ printk("GDT-EISA: Error during device scan\n");
+ --gdth_ctr_count;
+ --gdth_ctr_vcount;
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(ha->irq,NULL);
+#else
+ free_irq(ha->irq);
+#endif
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+
+#if LINUX_VERSION_CODE >= 0x020000
+ shp->max_id = 8;
+ shp->max_lun = MAXLUN;
+ shp->max_channel = ha->bus_cnt - 1;
+#else
+ /* register addit. SCSI channels as virtual controllers */
+ for (b=1; b<ha->bus_cnt; ++b) {
+ shp = scsi_register(shtp,sizeof(gdth_num_str));
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[b][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum = b;
+ TRACE2(("EISA detect Bus %d: shp %lx hanum %d\n",
+ NUMDATA(shp)->busnum,(ulong)shp,
+ NUMDATA(shp)->hanum));
+ }
+#endif
+
+ gdth_enable_int(hanum);
+ }
+ }
+
+ /* scanning for PCI controllers */
+ for (device_id = 0; device_id <= PCI_DEVICE_ID_VORTEX_GDTMAXRP; ++device_id) {
+ if (device_id > PCI_DEVICE_ID_VORTEX_GDT6555 &&
+ device_id < PCI_DEVICE_ID_VORTEX_GDT6x17RP)
+ continue;
+ for (index = 0; ; ++index) {
+ if (!gdth_search_pci(device_id,index,&pcistr))
+ break; /* next device_id */
+ shp = scsi_register(shtp,sizeof(gdth_ext_str));
+ ha = HADATA(shp);
+ if (!gdth_init_pci(&pcistr,ha)) {
+ scsi_unregister(shp);
+ continue;
+ }
+ /* controller found and initialized */
+ printk("Configuring GDT-PCI HA at %d/%d IRQ %u\n",
+ pcistr.bus,pcistr.device_fn>>3,ha->irq);
+
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x02015F
+ if (request_irq(ha->irq,do_gdth_interrupt,SA_INTERRUPT|SA_SHIRQ,"gdth",NULL))
+#elif LINUX_VERSION_CODE >= 0x010346
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT|SA_SHIRQ,"gdth",NULL))
+#else
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT|SA_SHIRQ,"gdth"))
+#endif
+ {
+ printk("GDT-PCI: Unable to allocate IRQ\n");
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[0][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ hanum = gdth_ctr_count;
+ gdth_ctr_tab[gdth_ctr_count++] = shp;
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum= 0;
+
+ ha->pccb = CMDDATA(shp);
+ ha->pscratch = DMADATA(shp);
+ ha->req_first = NULL;
+ for (i=0; i<MAXBUS; ++i) {
+ for (j=0; j<MAXID; ++j) {
+ ha->id[i][j].type = EMPTY_DTYP;
+ ha->id[i][j].lock = 0;
+ ha->id[i][j].heads = 0;
+ }
+ }
+ restore_flags(flags);
+
+ if (!gdth_search_drives(hanum)) {
+ printk("GDT-PCI: Error during device scan\n");
+ --gdth_ctr_count;
+ --gdth_ctr_vcount;
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(ha->irq,NULL);
+#else
+ free_irq(ha->irq);
+#endif
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+
+#if LINUX_VERSION_CODE >= 0x020000
+ shp->max_id = MAXID;
+ shp->max_lun = MAXLUN;
+ shp->max_channel = ha->bus_cnt - 1;
+#else
+ /* register addit. SCSI channels as virtual controllers */
+ for (b=1; b<ha->bus_cnt; ++b) {
+ shp = scsi_register(shtp,sizeof(gdth_num_str));
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[b][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum = b;
+ }
+#endif
+
+ gdth_enable_int(hanum);
+ }
+ }
+
+ TRACE2(("gdth_detect() %d controller detected\n",gdth_ctr_count));
+ if (gdth_ctr_count > 0) {
+#ifdef GDTH_STATISTICS
+ TRACE2(("gdth_detect(): Initializing timer !\n"));
+ init_timer(&gdth_timer);
+ gdth_timer.expires = jiffies + HZ;
+ gdth_timer.data = 0L;
+ gdth_timer.function = gdth_timeout;
+ add_timer(&gdth_timer);
+#endif
+#if LINUX_VERSION_CODE >= 0x020100
+ register_reboot_notifier(&gdth_notifier);
+#endif
+ }
+ gdth_polling = FALSE;
+ return gdth_ctr_vcount;
+}
+
+
+int gdth_release(struct Scsi_Host *shp)
+{
+ unsigned long flags;
+
+ TRACE2(("gdth_release()\n"));
+
+ if (NUMDATA(shp)->busnum == 0) {
+ gdth_flush(NUMDATA(shp)->hanum);
+
+ save_flags(flags);
+ cli();
+ if (shp->irq) {
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(shp->irq,NULL);
+#else
+ free_irq(shp->irq);
+#endif
+ }
+ if (shp->dma_channel != 0xff) {
+ free_dma(shp->dma_channel);
+ }
+ restore_flags(flags);
+ gdth_ctr_released++;
+ TRACE2(("gdth_release(): HA %d of %d\n",
+ gdth_ctr_released, gdth_ctr_count));
+
+ if (gdth_ctr_released == gdth_ctr_count) {
+#ifdef GDTH_STATISTICS
+ del_timer(&gdth_timer);
+#endif
+#if LINUX_VERSION_CODE >= 0x020100
+ unregister_reboot_notifier(&gdth_notifier);
+#endif
+ }
+ }
+
+ scsi_unregister(shp);
+ return 0;
+}
+
+
+static const char *gdth_ctr_name(int hanum)
+{
+ gdth_ha_str *ha;
+
+ TRACE2(("gdth_ctr_name()\n"));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ if (ha->type == GDT_EISA) {
+ switch (ha->stype) {
+ case GDT3_ID:
+ return("GDT3000/3020");
+ case GDT3A_ID:
+ return("GDT3000A/3020A/3050A");
+ case GDT3B_ID:
+ return("GDT3000B/3010A");
+ }
+ } else if (ha->type == GDT_ISA) {
+ return("GDT2000/2020");
+ } else if (ha->type == GDT_PCI) {
+ switch (ha->stype) {
+ case PCI_DEVICE_ID_VORTEX_GDT60x0:
+ return("GDT6000/6020/6050");
+ case PCI_DEVICE_ID_VORTEX_GDT6000B:
+ return("GDT6000B/6010");
+ }
+ }
+ /* new controllers (GDT_PCINEW, GDT_PCIMPR, ..) use board_info IOCTL! */
+
+ return("");
+}
+
+const char *gdth_info(struct Scsi_Host *shp)
+{
+ int hanum;
+ gdth_ha_str *ha;
+
+ TRACE2(("gdth_info()\n"));
+ hanum = NUMDATA(shp)->hanum;
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ return ((const char *)ha->ctr_name);
+}
+
+/* old error handling */
+int gdth_abort(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_abort() reason %d\n",scp->abort_reason));
+ return SCSI_ABORT_SNOOZE;
+}
+
+#if LINUX_VERSION_CODE >= 0x010346
+int gdth_reset(Scsi_Cmnd *scp, unsigned int reset_flags)
+#else
+int gdth_reset(Scsi_Cmnd *scp)
+#endif
+{
+ TRACE2(("gdth_reset()\n"));
+ return SCSI_RESET_PUNT;
+}
+
+#if LINUX_VERSION_CODE >= 0x02015F
+/* new error handling */
+int gdth_eh_abort(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_eh_abort()\n"));
+ return FAILED;
+}
+
+int gdth_eh_device_reset(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_eh_device_reset()\n"));
+ return FAILED;
+}
+
+int gdth_eh_bus_reset(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_eh_bus_reset()\n"));
+ return FAILED;
+}
+
+int gdth_eh_host_reset(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_eh_host_reset()\n"));
+ return FAILED;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x010300
+int gdth_bios_param(Disk *disk,kdev_t dev,int *ip)
+#else
+int gdth_bios_param(Disk *disk,int dev,int *ip)
+#endif
+{
+ unchar b, t;
+ int hanum;
+ gdth_ha_str *ha;
+ int drv_hds, drv_secs;
+
+ hanum = NUMDATA(disk->device->host)->hanum;
+ b = disk->device->channel;
+ t = disk->device->id;
+ TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", hanum, b, t));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ if (ha->id[b][t].heads == 0) {
+ /* raw device: evaluate mapping (sectors per head, heads per cylinder) */
+ if (disk->capacity /HEADS/SECS <= MAXCYLS) {
+ drv_hds = HEADS;
+ drv_secs= SECS;
+ } else if (disk->capacity /MEDHEADS/MEDSECS <= MAXCYLS) {
+ drv_hds = MEDHEADS;
+ drv_secs= MEDSECS;
+ } else {
+ drv_hds = BIGHEADS;
+ drv_secs= BIGSECS;
+ }
+ ha->id[b][t].heads = drv_hds;
+ ha->id[b][t].secs = drv_secs;
+ TRACE2(("gdth_bios_param(): raw device -> params evaluated\n"));
+ }
+
+ ip[0] = ha->id[b][t].heads;
+ ip[1] = ha->id[b][t].secs;
+ ip[2] = disk->capacity / ip[0] / ip[1];
+
+ TRACE2(("gdth_bios_param(): %d heads, %d secs, %d cyls\n",
+ ip[0],ip[1],ip[2]));
+ return 0;
+}
+
+
+static void internal_done(Scsi_Cmnd *scp)
+{
+ scp->SCp.sent_command++;
+}
+
+int gdth_command(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_command()\n"));
+
+ scp->SCp.sent_command = 0;
+ gdth_queuecommand(scp,internal_done);
+
+ while (!scp->SCp.sent_command)
+ barrier();
+ return scp->result;
+}
+
+
+int gdth_queuecommand(Scsi_Cmnd *scp,void (*done)(Scsi_Cmnd *))
+{
+ int hanum;
+ int priority;
+
+ TRACE(("gdth_queuecommand() cmd 0x%x id %d lun %d\n",
+ scp->cmnd[0],scp->target,scp->lun));
+
+ scp->scsi_done = (void *)done;
+ scp->SCp.have_data_in = 1;
+ hanum = NUMDATA(scp->host)->hanum;
+#ifdef GDTH_STATISTICS
+ ++act_ios;
+#endif
+
+ priority = DEFAULT_PRI;
+#if LINUX_VERSION_CODE >= 0x010300
+ if (scp->done == gdth_scsi_done)
+ priority = scp->SCp.this_residual;
+#endif
+ gdth_putq( hanum, scp, priority );
+ gdth_next( hanum );
+ return 0;
+}
+
+/* flush routine */
+static void gdth_flush(int hanum)
+{
+ int i, j;
+ gdth_ha_str *ha;
+ Scsi_Cmnd scp;
+ Scsi_Device sdev;
+ gdth_cmd_str gdtcmd;
+ char cmnd[12];
+
+ TRACE2(("gdth_flush() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ memset(&sdev,0,sizeof(Scsi_Device));
+ memset(&scp, 0,sizeof(Scsi_Cmnd));
+ sdev.host = gdth_ctr_tab[hanum];
+ sdev.id = sdev.host->this_id;
+ scp.cmd_len = 12;
+ scp.host = gdth_ctr_tab[hanum];
+ scp.target = sdev.host->this_id;
+ scp.device = &sdev;
+ scp.use_sg = 0;
+
+ for (i = 0; i < MAXBUS; ++i) {
+ for (j = 0; j < MAXID; ++j) {
+ if (ha->id[i][j].type == CACHE_DTYP) {
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_FLUSH;
+ gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive;
+ gdtcmd.u.cache.BlockNo = 1;
+ gdtcmd.u.cache.sg_canz = 0;
+ TRACE2(("gdth_flush(): flush ha %d drive %d\n",
+ hanum, ha->id[i][j].hostdrive));
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scp.SCp.this_residual = IOCTL_PRI;
+ scsi_do_cmd(&scp, cmnd, &gdtcmd,
+ sizeof(gdth_cmd_str), gdth_scsi_done,
+ 30*HZ, 1);
+ down(&sem);
+ }
+ }
+ }
+ }
+}
+
+/* shutdown routine */
+#if LINUX_VERSION_CODE >= 0x020100
+static int gdth_halt(struct notifier_block *nb, ulong event, void *buf)
+#else
+void gdth_halt(void)
+#endif
+{
+ int hanum;
+ Scsi_Cmnd scp;
+ Scsi_Device sdev;
+ gdth_cmd_str gdtcmd;
+ char cmnd[12];
+
+#if LINUX_VERSION_CODE >= 0x020100
+ TRACE2(("gdth_halt() event %d\n",event));
+ if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF)
+ return NOTIFY_DONE;
+#else
+ TRACE2(("gdth_halt()\n"));
+ if (halt_called) {
+ TRACE2(("already called\n"));
+ return;
+ }
+ halt_called = TRUE;
+#endif
+ printk("GDT: Flushing all host drives .. ");
+ for (hanum = 0; hanum < gdth_ctr_count; ++hanum) {
+ gdth_flush(hanum);
+
+ /* controller reset */
+ memset(&sdev,0,sizeof(Scsi_Device));
+ memset(&scp, 0,sizeof(Scsi_Cmnd));
+ sdev.host = gdth_ctr_tab[hanum];
+ sdev.id = sdev.host->this_id;
+ scp.cmd_len = 12;
+ scp.host = gdth_ctr_tab[hanum];
+ scp.target = sdev.host->this_id;
+ scp.device = &sdev;
+ scp.use_sg = 0;
+
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_RESET;
+ TRACE2(("gdth_halt(): reset controller %d\n", hanum));
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scp.SCp.this_residual = IOCTL_PRI;
+ scsi_do_cmd(&scp, cmnd, &gdtcmd,
+ sizeof(gdth_cmd_str), gdth_scsi_done,
+ 10*HZ, 1);
+ down(&sem);
+ }
+ }
+ printk("Done.\n");
+
+#ifdef GDTH_STATISTICS
+ del_timer(&gdth_timer);
+#endif
+#if LINUX_VERSION_CODE >= 0x020100
+ unregister_reboot_notifier(&gdth_notifier);
+ return NOTIFY_OK;
+#endif
+}
+
+
+/* called from init/main.c */
+__initfunc (void gdth_setup(char *str,int *ints))
+{
+ static size_t setup_idx = 0;
+
+ TRACE2(("gdth_setup() str %s ints[0] %d ints[1] %d\n",
+ str ? str:"NULL", ints[0],
+ ints[0] ? ints[1]:0));
+
+ if (setup_idx >= MAXHA) {
+ printk("GDT: gdth_setup() called too many times. Bad LILO params ?\n");
+ return;
+ }
+ if (ints[0] != 1) {
+ printk("GDT: Illegal command line !\n");
+ printk("Usage: gdth=<IRQ>\n");
+ printk("Where: <IRQ>: valid EISA controller IRQ (10,11,12,14)\n");
+ printk(" or 0 to disable controller driver\n");
+ return;
+ }
+ if (ints[1] == 10 || ints[1] == 11 || ints[1] == 12 || ints[1] == 14) {
+ irqs[setup_idx++] = ints[1];
+ irqs[setup_idx] = 0xff;
+ return;
+ }
+ if (ints[1] == 0) {
+ disable_gdth_scan = TRUE;
+ return;
+ }
+ printk("GDT: Invalid IRQ (%d) specified\n",ints[1]);
+}
+
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = GDTH;
+#include "scsi_module.c"
+#endif
+
diff --git a/linux/src/drivers/scsi/gdth.h b/linux/src/drivers/scsi/gdth.h
new file mode 100644
index 00000000..6eafd1f8
--- /dev/null
+++ b/linux/src/drivers/scsi/gdth.h
@@ -0,0 +1,819 @@
+#ifndef _GDTH_H
+#define _GDTH_H
+
+/*
+ * Header file for the GDT ISA/EISA/PCI Disk Array Controller driver for Linux
+ *
+ * gdth.h Copyright (C) 1995-98 ICP vortex Computersysteme GmbH, Achim Leubner
+ * See gdth.c for further informations and
+ * below for supported controller types
+ *
+ * <achim@vortex.de>
+ *
+ * $Id: gdth.h,v 1.1 1999/04/26 05:54:37 tb Exp $
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+
+#ifndef NULL
+#define NULL 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* defines, macros */
+
+/* driver version */
+#define GDTH_VERSION_STR "1.07"
+#define GDTH_VERSION 1
+#define GDTH_SUBVERSION 7
+
+/* protocol version */
+#define PROTOCOL_VERSION 1
+
+/* controller classes */
+#define GDT_ISA 0x01 /* ISA controller */
+#define GDT_EISA 0x02 /* EISA controller */
+#define GDT_PCI 0x03 /* PCI controller */
+#define GDT_PCINEW 0x04 /* new PCI controller */
+#define GDT_PCIMPR 0x05 /* PCI MPR controller */
+/* GDT_EISA, controller subtypes EISA */
+#define GDT3_ID 0x0130941c /* GDT3000/3020 */
+#define GDT3A_ID 0x0230941c /* GDT3000A/3020A/3050A */
+#define GDT3B_ID 0x0330941c /* GDT3000B/3010A */
+/* GDT_ISA */
+#define GDT2_ID 0x0120941c /* GDT2000/2020 */
+/* vendor ID, device IDs (PCI) */
+/* these defines should already exist in <linux/pci.h> */
+#ifndef PCI_VENDOR_ID_VORTEX
+#define PCI_VENDOR_ID_VORTEX 0x1119 /* PCI controller vendor ID */
+#endif
+#ifndef PCI_DEVICE_ID_VORTEX_GDT60x0
+/* GDT_PCI */
+#define PCI_DEVICE_ID_VORTEX_GDT60x0 0 /* GDT6000/6020/6050 */
+#define PCI_DEVICE_ID_VORTEX_GDT6000B 1 /* GDT6000B/6010 */
+/* GDT_PCINEW */
+#define PCI_DEVICE_ID_VORTEX_GDT6x10 2 /* GDT6110/6510 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x20 3 /* GDT6120/6520 */
+#define PCI_DEVICE_ID_VORTEX_GDT6530 4 /* GDT6530 */
+#define PCI_DEVICE_ID_VORTEX_GDT6550 5 /* GDT6550 */
+/* GDT_PCINEW, wide/ultra SCSI controllers */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17 6 /* GDT6117/6517 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27 7 /* GDT6127/6527 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537 8 /* GDT6537 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557 9 /* GDT6557/6557-ECC */
+/* GDT_PCINEW, wide SCSI controllers */
+#define PCI_DEVICE_ID_VORTEX_GDT6x15 10 /* GDT6115/6515 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x25 11 /* GDT6125/6525 */
+#define PCI_DEVICE_ID_VORTEX_GDT6535 12 /* GDT6535 */
+#define PCI_DEVICE_ID_VORTEX_GDT6555 13 /* GDT6555/6555-ECC */
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDT6x17RP
+/* GDT_MPR, RP series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x100 /* GDT6117RP/GDT6517RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x101 /* GDT6127RP/GDT6527RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x102 /* GDT6537RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x103 /* GDT6557RP */
+/* GDT_MPR, RP series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x104 /* GDT6111RP/GDT6511RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x105 /* GDT6121RP/GDT6521RP */
+/* GDT_MPR, RP1 series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP1 0x110 /* GDT6117RP1/GDT6517RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP1 0x111 /* GDT6127RP1/GDT6527RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP1 0x112 /* GDT6537RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP1 0x113 /* GDT6557RP1 */
+/* GDT_MPR, RP1 series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP1 0x114 /* GDT6111RP1/GDT6511RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP1 0x115 /* GDT6121RP1/GDT6521RP1 */
+/* GDT_MPR, RP2 series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP2 0x120 /* GDT6117RP2/GDT6517RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP2 0x121 /* GDT6127RP2/GDT6527RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP2 0x122 /* GDT6537RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP2 0x123 /* GDT6557RP2 */
+/* GDT_MPR, RP2 series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP2 0x124 /* GDT6111RP2/GDT6511RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP2 0x125 /* GDT6121RP2/GDT6521RP2 */
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDT6519RD
+/* GDT_MPR, Fibre Channel */
+#define PCI_DEVICE_ID_VORTEX_GDT6519RD 0x210 /* GDT6519RD */
+#define PCI_DEVICE_ID_VORTEX_GDT6529RD 0x211 /* GDT6529RD */
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDTMAXRP
+/* GDT_MPR, last device ID */
+#define PCI_DEVICE_ID_VORTEX_GDTMAXRP 0x2ff
+#endif
+
+/* limits */
+#define GDTH_SCRATCH 4096 /* 4KB scratch buffer */
+#define GDTH_MAXCMDS 124
+#define GDTH_MAXC_P_L 16 /* max. cmds per lun */
+#define MAXOFFSETS 128
+#define MAXHA 8
+#define MAXID 16
+#define MAXLUN 8
+#define MAXBUS 6
+#define MAX_HDRIVES 35 /* max. host drive count */
+#define MAX_EVENTS 100 /* event buffer count */
+#define MAXCYLS 1024
+#define HEADS 64
+#define SECS 32 /* mapping 64*32 */
+#define MEDHEADS 127
+#define MEDSECS 63 /* mapping 127*63 */
+#define BIGHEADS 255
+#define BIGSECS 63 /* mapping 255*63 */
+
+/* special command ptr. */
+#define UNUSED_CMND ((Scsi_Cmnd *)-1)
+#define INTERNAL_CMND ((Scsi_Cmnd *)-2)
+#define SCREEN_CMND ((Scsi_Cmnd *)-3)
+#define SPECIAL_SCP(p) (p==UNUSED_CMND || p==INTERNAL_CMND || p==SCREEN_CMND)
+
+/* device types */
+#define EMPTY_DTYP 0
+#define CACHE_DTYP 1
+#define RAW_DTYP 2
+#define SIOP_DTYP 3 /* the SCSI processor */
+
+/* controller services */
+#define SCSIRAWSERVICE 3
+#define CACHESERVICE 9
+#define SCREENSERVICE 11
+
+/* screenservice defines */
+#define MSG_INV_HANDLE -1 /* special message handle */
+#define MSGLEN 16 /* size of message text */
+#define MSG_SIZE 34 /* size of message structure */
+#define MSG_REQUEST 0 /* async. event: message */
+
+/* cacheservice defines */
+#define SECTOR_SIZE 0x200 /* always 512 bytes per sector */
+
+/* DPMEM constants */
+#define DPMEM_MAGIC 0xC0FFEE11
+#define IC_HEADER_BYTES 48
+#define IC_QUEUE_BYTES 4
+#define DPMEM_COMMAND_OFFSET IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS
+
+/* service commands */
+#define GDT_INIT 0 /* service initialization */
+#define GDT_READ 1 /* read command */
+#define GDT_WRITE 2 /* write command */
+#define GDT_INFO 3 /* information about devices */
+#define GDT_FLUSH 4 /* flush dirty cache buffers */
+#define GDT_IOCTL 5 /* ioctl command */
+#define GDT_DEVTYPE 9 /* additional information */
+#define GDT_MOUNT 10 /* mount cache device */
+#define GDT_UNMOUNT 11 /* unmount cache device */
+#define GDT_SET_FEAT 12 /* set feat. (scatter/gather) */
+#define GDT_GET_FEAT 13 /* get features */
+#define GDT_RESERVE 14 /* reserve dev. to raw service */
+#define GDT_WRITE_THR 16 /* write through */
+#define GDT_EXT_INFO 18 /* extended info */
+#define GDT_RESET 19 /* controller reset */
+
+/* IOCTL command defines */
+#define SCSI_CHAN_CNT 5 /* subfunctions */
+#define GET_IOCHAN_DESC 0x5e
+#define L_CTRL_PATTERN 0x20000000L
+#define CACHE_INFO 4
+#define CACHE_CONFIG 5
+#define BOARD_INFO 0x28
+#define IO_CHANNEL 0x00020000L /* channels */
+#define INVALID_CHANNEL 0x0000ffffL
+
+/* IOCTLs */
+#define GDTIOCTL_MASK ('J'<<8)
+#define GDTIOCTL_GENERAL (GDTIOCTL_MASK | 0) /* general IOCTL */
+#define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */
+#define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */
+#define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */
+#define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */
+#define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */
+#define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */
+
+/* service errors */
+#define S_OK 1 /* no error */
+#define S_BSY 7 /* controller busy */
+#define S_RAW_SCSI 12 /* raw serv.: target error */
+#define S_RAW_ILL 0xff /* raw serv.: illegal */
+
+/* timeout values */
+#define INIT_RETRIES 10000 /* 10000 * 1ms = 10s */
+#define INIT_TIMEOUT 100000 /* 1000 * 1ms = 1s */
+#define POLL_TIMEOUT 10000 /* 10000 * 1ms = 10s */
+
+/* priorities */
+#define DEFAULT_PRI 0x20
+#define IOCTL_PRI 0x10
+
+/* data directions */
+#define DATA_IN 0x01000000L /* data from target */
+#define DATA_OUT 0x00000000L /* data to target */
+
+/* BMIC registers (EISA controllers) */
+#define ID0REG 0x0c80 /* board ID */
+#define EINTENABREG 0x0c89 /* interrupt enable */
+#define SEMA0REG 0x0c8a /* command semaphore */
+#define SEMA1REG 0x0c8b /* status semaphore */
+#define LDOORREG 0x0c8d /* local doorbell */
+#define EDENABREG 0x0c8e /* EISA system doorbell enable */
+#define EDOORREG 0x0c8f /* EISA system doorbell */
+#define MAILBOXREG 0x0c90 /* mailbox reg. (16 bytes) */
+#define EISAREG 0x0cc0 /* EISA configuration */
+
+/* other defines */
+#define LINUX_OS 8 /* used for cache optim. */
+#define SCATTER_GATHER 1 /* s/g feature */
+#define GDTH_MAXSG 32 /* max. s/g elements */
+#define SECS32 0x1f /* round capacity */
+#define BIOS_ID_OFFS 0x10 /* offset contr. ID in ISABIOS */
+#define LOCALBOARD 0 /* board node always 0 */
+#define ASYNCINDEX 0 /* cmd index async. event */
+#define SPEZINDEX 1 /* cmd index unknown service */
+#define GDT_WR_THROUGH 0x100 /* WRITE_THROUGH supported */
+
+/* typedefs */
+
+#pragma pack(1)
+
+typedef struct {
+ char buffer[GDTH_SCRATCH]; /* scratch buffer */
+} gdth_scratch_str;
+
+/* screenservice message */
+typedef struct {
+ ulong msg_handle; /* message handle */
+ ulong msg_len; /* size of message */
+ ulong msg_alen; /* answer length */
+ unchar msg_answer; /* answer flag */
+ unchar msg_ext; /* more messages */
+ unchar msg_reserved[2];
+ char msg_text[MSGLEN+2]; /* the message text */
+} gdth_msg_str;
+
+/* get channel count IOCTL */
+typedef struct {
+ ulong channel_no; /* number of channel */
+ ulong drive_cnt; /* number of drives */
+ unchar siop_id; /* SCSI processor ID */
+ unchar siop_state; /* SCSI processor state */
+} gdth_getch_str;
+
+/* get raw channel count IOCTL (NEW!) */
+typedef struct {
+ ulong version; /* version of information (-1UL: newest) */
+ unchar list_entries; /* list entry count */
+ unchar first_chan; /* first channel number */
+ unchar last_chan; /* last channel number */
+ unchar chan_count; /* (R) channel count */
+ ulong list_offset; /* offset of list[0] */
+ struct {
+ unchar proc_id; /* processor id */
+ unchar proc_defect; /* defect ? */
+ unchar reserved[2];
+ } list[MAXBUS];
+} gdth_iochan_str;
+
+/* cache info/config IOCTL */
+typedef struct {
+ ulong version; /* firmware version */
+ ushort state; /* cache state (on/off) */
+ ushort strategy; /* cache strategy */
+ ushort write_back; /* write back state (on/off) */
+ ushort block_size; /* cache block size */
+} gdth_cpar_str;
+
+typedef struct {
+ ulong csize; /* cache size */
+ ulong read_cnt; /* read/write counter */
+ ulong write_cnt;
+ ulong tr_hits; /* hits */
+ ulong sec_hits;
+ ulong sec_miss; /* misses */
+} gdth_cstat_str;
+
+typedef struct {
+ gdth_cpar_str cpar;
+ gdth_cstat_str cstat;
+} gdth_cinfo_str;
+
+/* board info IOCTL */
+typedef struct {
+ ulong ser_no; /* serial no. */
+ unchar oem_id[2]; /* OEM ID */
+ ushort ep_flags; /* eprom flags */
+ ulong proc_id; /* processor ID */
+ ulong memsize; /* memory size (bytes) */
+ unchar mem_banks; /* memory banks */
+ unchar chan_type; /* channel type */
+ unchar chan_count; /* channel count */
+ unchar rdongle_pres; /* dongle present? */
+ ulong epr_fw_ver; /* (eprom) firmware version */
+ ulong upd_fw_ver; /* (update) firmware version */
+ ulong upd_revision; /* update revision */
+ char type_string[16]; /* controller name */
+ char raid_string[16]; /* RAID firmware name */
+ unchar update_pres; /* update present? */
+ unchar xor_pres; /* XOR engine present? */
+ unchar prom_type; /* ROM type (eprom/flash eprom) */
+ unchar prom_count; /* number of ROM devices */
+ ulong dup_pres; /* duplexing module present? */
+ ulong chan_pres; /* number of expansion channels */
+ ulong mem_pres; /* memory expansion installed? */
+ unchar ft_bus_system; /* fault bus supported? */
+ unchar subtype_valid; /* board_subtype valid? */
+ unchar board_subtype; /* controller subtype/hardware level */
+ unchar ramparity_pres; /* RAM parity check hardware present? */
+} gdth_binfo_str;
+
+/* scatter/gather element */
+typedef struct {
+ ulong sg_ptr; /* address */
+ ulong sg_len; /* length */
+} gdth_sg_str;
+
+/* command structure */
+typedef struct {
+ ulong BoardNode; /* board node (always 0) */
+ ulong CommandIndex; /* command number */
+ ushort OpCode; /* the command (READ,..) */
+ union {
+ struct {
+ ushort DeviceNo; /* number of cache drive */
+ ulong BlockNo; /* block number */
+ ulong BlockCnt; /* block count */
+ ulong DestAddr; /* dest. addr. (if s/g: -1) */
+ ulong sg_canz; /* s/g element count */
+ gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */
+ } cache; /* cache service cmd. str. */
+ struct {
+ ushort param_size; /* size of p_param buffer */
+ ulong subfunc; /* IOCTL function */
+ ulong channel; /* device */
+ ulong p_param; /* buffer */
+ } ioctl; /* IOCTL command structure */
+ struct {
+ ushort reserved;
+ ulong msg_handle; /* message handle */
+ ulong msg_addr; /* message buffer address */
+ } screen; /* screen service cmd. str. */
+ struct {
+ ushort reserved;
+ ulong direction; /* data direction */
+ ulong mdisc_time; /* disc. time (0: no timeout)*/
+ ulong mcon_time; /* connect time(0: no to.) */
+ ulong sdata; /* dest. addr. (if s/g: -1) */
+ ulong sdlen; /* data length (bytes) */
+ ulong clen; /* SCSI cmd. length(6,10,12) */
+ unchar cmd[12]; /* SCSI command */
+ unchar target; /* target ID */
+ unchar lun; /* LUN */
+ unchar bus; /* SCSI bus number */
+ unchar priority; /* only 0 used */
+ ulong sense_len; /* sense data length */
+ ulong sense_data; /* sense data addr. */
+ struct raw *link_p; /* linked cmds (not supp.) */
+ ulong sg_ranz; /* s/g element count */
+ gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */
+ } raw; /* raw service cmd. struct. */
+ } u;
+ /* additional variables */
+ unchar Service; /* controller service */
+ ushort Status; /* command result */
+ ulong Info; /* additional information */
+ Scsi_Cmnd *RequestBuffer; /* request buffer */
+} gdth_cmd_str;
+
+/* controller event structure */
+#define ES_ASYNC 1
+#define ES_DRIVER 2
+#define ES_TEST 3
+#define ES_SYNC 4
+typedef struct {
+ ushort size; /* size of structure */
+ union {
+ char stream[16];
+ struct {
+ ushort ionode;
+ ushort service;
+ ulong index;
+ } driver;
+ struct {
+ ushort ionode;
+ ushort service;
+ ushort status;
+ ulong info;
+ unchar scsi_coord[3];
+ } async;
+ struct {
+ ushort ionode;
+ ushort service;
+ ushort status;
+ ulong info;
+ ushort hostdrive;
+ unchar scsi_coord[3];
+ unchar sense_key;
+ } sync;
+ struct {
+ ulong l1, l2, l3, l4;
+ } test;
+ } eu;
+} gdth_evt_data;
+
+typedef struct {
+ ulong first_stamp;
+ ulong last_stamp;
+ ushort same_count;
+ ushort event_source;
+ ushort event_idx;
+ unchar application;
+ unchar reserved;
+ gdth_evt_data event_data;
+} gdth_evt_str;
+
+
+/* DPRAM structures */
+
+/* interface area ISA/PCI */
+typedef struct {
+ unchar S_Cmd_Indx; /* special command */
+ unchar volatile S_Status; /* status special command */
+ ushort reserved1;
+ ulong S_Info[4]; /* add. info special command */
+ unchar volatile Sema0; /* command semaphore */
+ unchar reserved2[3];
+ unchar Cmd_Index; /* command number */
+ unchar reserved3[3];
+ ushort volatile Status; /* command status */
+ ushort Service; /* service(for async.events) */
+ ulong Info[2]; /* additional info */
+ struct {
+ ushort offset; /* command offs. in the DPRAM*/
+ ushort serv_id; /* service */
+ } comm_queue[MAXOFFSETS]; /* command queue */
+ ulong bios_reserved[2];
+ unchar gdt_dpr_cmd[1]; /* commands */
+} gdt_dpr_if;
+
+/* SRAM structure PCI controllers */
+typedef struct {
+ ulong magic; /* controller ID from BIOS */
+ ushort need_deinit; /* switch betw. BIOS/driver */
+ unchar switch_support; /* see need_deinit */
+ unchar padding[9];
+ unchar os_used[16]; /* OS code per service */
+ unchar unused[28];
+ unchar fw_magic; /* contr. ID from firmware */
+} gdt_pci_sram;
+
+/* SRAM structure EISA controllers (but NOT GDT3000/3020) */
+typedef struct {
+ unchar os_used[16]; /* OS code per service */
+ ushort need_deinit; /* switch betw. BIOS/driver */
+ unchar switch_support; /* see need_deinit */
+ unchar padding;
+} gdt_eisa_sram;
+
+
+/* DPRAM ISA controllers */
+typedef struct {
+ union {
+ struct {
+ unchar bios_used[0x3c00-32]; /* 15KB - 32Bytes BIOS */
+ ulong magic; /* controller (EISA) ID */
+ ushort need_deinit; /* switch betw. BIOS/driver */
+ unchar switch_support; /* see need_deinit */
+ unchar padding[9];
+ unchar os_used[16]; /* OS code per service */
+ } dp_sram;
+ unchar bios_area[0x4000]; /* 16KB reserved for BIOS */
+ } bu;
+ union {
+ gdt_dpr_if ic; /* interface area */
+ unchar if_area[0x3000]; /* 12KB for interface */
+ } u;
+ struct {
+ unchar memlock; /* write protection DPRAM */
+ unchar event; /* release event */
+ unchar irqen; /* board interrupts enable */
+ unchar irqdel; /* acknowledge board int. */
+ unchar volatile Sema1; /* status semaphore */
+ unchar rq; /* IRQ/DRQ configuration */
+ } io;
+} gdt2_dpram_str;
+
+/* DPRAM PCI controllers */
+typedef struct {
+ union {
+ gdt_dpr_if ic; /* interface area */
+ unchar if_area[0xff0-sizeof(gdt_pci_sram)];
+ } u;
+ gdt_pci_sram gdt6sr; /* SRAM structure */
+ struct {
+ unchar unused0[1];
+ unchar volatile Sema1; /* command semaphore */
+ unchar unused1[3];
+ unchar irqen; /* board interrupts enable */
+ unchar unused2[2];
+ unchar event; /* release event */
+ unchar unused3[3];
+ unchar irqdel; /* acknowledge board int. */
+ unchar unused4[3];
+ } io;
+} gdt6_dpram_str;
+
+/* PLX register structure (new PCI controllers) */
+typedef struct {
+ unchar cfg_reg; /* DPRAM cfg.(2:below 1MB,0:anywhere)*/
+ unchar unused1[0x3f];
+ unchar volatile sema0_reg; /* command semaphore */
+ unchar volatile sema1_reg; /* status semaphore */
+ unchar unused2[2];
+ ushort volatile status; /* command status */
+ ushort service; /* service */
+ ulong info[2]; /* additional info */
+ unchar unused3[0x10];
+ unchar ldoor_reg; /* PCI to local doorbell */
+ unchar unused4[3];
+ unchar volatile edoor_reg; /* local to PCI doorbell */
+ unchar unused5[3];
+ unchar control0; /* control0 register(unused) */
+ unchar control1; /* board interrupts enable */
+ unchar unused6[0x16];
+} gdt6c_plx_regs;
+
+/* DPRAM new PCI controllers */
+typedef struct {
+ union {
+ gdt_dpr_if ic; /* interface area */
+ unchar if_area[0x4000-sizeof(gdt_pci_sram)];
+ } u;
+ gdt_pci_sram gdt6sr; /* SRAM structure */
+} gdt6c_dpram_str;
+
+/* i960 register structure (PCI MPR controllers) */
+typedef struct {
+ unchar unused1[16];
+ unchar volatile sema0_reg; /* command semaphore */
+ unchar unused2;
+ unchar volatile sema1_reg; /* status semaphore */
+ unchar unused3;
+ ushort volatile status; /* command status */
+ ushort service; /* service */
+ ulong info[2]; /* additional info */
+ unchar ldoor_reg; /* PCI to local doorbell */
+ unchar unused4[11];
+ unchar volatile edoor_reg; /* local to PCI doorbell */
+ unchar unused5[7];
+ unchar edoor_en_reg; /* board interrupts enable */
+ unchar unused6[27];
+ ulong unused7[1004]; /* size: 4 KB */
+} gdt6m_i960_regs;
+
+/* DPRAM PCI MPR controllers */
+typedef struct {
+ gdt6m_i960_regs i960r; /* 4KB i960 registers */
+ union {
+ gdt_dpr_if ic; /* interface area */
+ unchar if_area[0x3000-sizeof(gdt_pci_sram)];
+ } u;
+ gdt_pci_sram gdt6sr; /* SRAM structure */
+} gdt6m_dpram_str;
+
+
+/* PCI resources */
+typedef struct {
+ ushort device_id; /* device ID (0,..,9) */
+ unchar bus; /* PCI bus */
+ unchar device_fn; /* PCI device/function no. */
+ ulong dpmem; /* DPRAM address */
+ ulong io; /* IO address */
+ ulong io_mm; /* IO address mem. mapped */
+ ulong bios; /* BIOS address */
+ unchar irq; /* IRQ */
+} gdth_pci_str;
+
+
+/* controller information structure */
+typedef struct {
+ unchar bus_cnt; /* SCSI bus count */
+ unchar type; /* controller class */
+ ushort raw_feat; /* feat. raw service (s/g,..) */
+ ulong stype; /* controller subtype */
+ ushort cache_feat; /* feat. cache serv. (s/g,..) */
+ ushort bmic; /* BMIC address (EISA) */
+ void *brd; /* DPRAM address */
+ ulong brd_phys; /* slot number/BIOS address */
+ gdt6c_plx_regs *plx; /* PLX regs (new PCI contr.) */
+ gdth_cmd_str *pccb; /* address command structure */
+ gdth_scratch_str *pscratch;
+ unchar irq; /* IRQ */
+ unchar drq; /* DRQ (ISA controllers) */
+ ushort status; /* command status */
+ ulong info;
+ ulong info2; /* additional info */
+ Scsi_Cmnd *req_first; /* top of request queue */
+ struct {
+ unchar type; /* device type */
+ unchar heads; /* mapping */
+ unchar secs;
+ unchar lock; /* drive locked ? (hot plug) */
+ ushort hostdrive; /* host drive number */
+ ushort devtype; /* further information */
+ ulong size; /* capacity */
+ } id[MAXBUS][MAXID];
+ ushort cmd_cnt; /* command count in DPRAM */
+ ushort cmd_len; /* length of actual command */
+ ushort cmd_offs_dpmem; /* actual offset in DPRAM */
+ ushort ic_all_size; /* sizeof DPRAM interf. area */
+ unchar reserved;
+ unchar mode; /* information from /proc */
+ ushort param_size;
+ gdth_cpar_str cpar; /* controller cache par. */
+ char ctr_name[16]; /* controller name */
+} gdth_ha_str;
+
+/* structure for scsi_register(), SCSI bus != 0 */
+typedef struct {
+ ushort hanum;
+ ushort busnum;
+} gdth_num_str;
+
+/* structure for scsi_register() */
+typedef struct {
+ gdth_num_str numext; /* must be the first element */
+ gdth_ha_str haext;
+ gdth_cmd_str cmdext;
+ gdth_scratch_str dmaext;
+} gdth_ext_str;
+
+
+/* INQUIRY data format */
+typedef struct {
+ unchar type_qual;
+ unchar modif_rmb;
+ unchar version;
+ unchar resp_aenc;
+ unchar add_length;
+ unchar reserved1;
+ unchar reserved2;
+ unchar misc;
+ unchar vendor[8];
+ unchar product[16];
+ unchar revision[4];
+} gdth_inq_data;
+
+/* READ_CAPACITY data format */
+typedef struct {
+ ulong last_block_no;
+ ulong block_length;
+} gdth_rdcap_data;
+
+/* REQUEST_SENSE data format */
+typedef struct {
+ unchar errorcode;
+ unchar segno;
+ unchar key;
+ ulong info;
+ unchar add_length;
+ ulong cmd_info;
+ unchar adsc;
+ unchar adsq;
+ unchar fruc;
+ unchar key_spec[3];
+} gdth_sense_data;
+
+/* MODE_SENSE data format */
+typedef struct {
+ struct {
+ unchar data_length;
+ unchar med_type;
+ unchar dev_par;
+ unchar bd_length;
+ } hd;
+ struct {
+ unchar dens_code;
+ unchar block_count[3];
+ unchar reserved;
+ unchar block_length[3];
+ } bd;
+} gdth_modep_data;
+
+/* stack frame */
+typedef struct {
+ ulong b[10]; /* 32 bit compiler ! */
+} gdth_stackframe;
+
+#pragma pack()
+
+
+/* data structure for reserve drives */
+typedef struct {
+ unchar hanum;
+ unchar bus;
+ unchar id;
+} gdth_reserve_str;
+
+
+/* function prototyping */
+
+int gdth_detect(Scsi_Host_Template *);
+int gdth_release(struct Scsi_Host *);
+int gdth_command(Scsi_Cmnd *);
+int gdth_queuecommand(Scsi_Cmnd *,void (*done)(Scsi_Cmnd *));
+int gdth_abort(Scsi_Cmnd *);
+#if LINUX_VERSION_CODE >= 0x010346
+int gdth_reset(Scsi_Cmnd *, unsigned int reset_flags);
+#else
+int gdth_reset(Scsi_Cmnd *);
+#endif
+const char *gdth_info(struct Scsi_Host *);
+
+#if LINUX_VERSION_CODE >= 0x02015F
+int gdth_bios_param(Disk *,kdev_t,int *);
+extern struct proc_dir_entry proc_scsi_gdth;
+int gdth_proc_info(char *,char **,off_t,int,int,int);
+int gdth_eh_abort(Scsi_Cmnd *scp);
+int gdth_eh_device_reset(Scsi_Cmnd *scp);
+int gdth_eh_bus_reset(Scsi_Cmnd *scp);
+int gdth_eh_host_reset(Scsi_Cmnd *scp);
+#define GDTH { proc_dir: &proc_scsi_gdth, \
+ proc_info: gdth_proc_info, \
+ name: "GDT SCSI Disk Array Controller",\
+ detect: gdth_detect, \
+ release: gdth_release, \
+ info: gdth_info, \
+ command: gdth_command, \
+ queuecommand: gdth_queuecommand, \
+ eh_abort_handler: gdth_eh_abort, \
+ eh_device_reset_handler: gdth_eh_device_reset, \
+ eh_bus_reset_handler: gdth_eh_bus_reset, \
+ eh_host_reset_handler: gdth_eh_host_reset, \
+ abort: gdth_abort, \
+ reset: gdth_reset, \
+ bios_param: gdth_bios_param, \
+ can_queue: GDTH_MAXCMDS, \
+ this_id: -1, \
+ sg_tablesize: GDTH_MAXSG, \
+ cmd_per_lun: GDTH_MAXC_P_L, \
+ present: 0, \
+ unchecked_isa_dma: 1, \
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 1 /* use new error code */ }
+#elif LINUX_VERSION_CODE >= 0x010300
+int gdth_bios_param(Disk *,kdev_t,int *);
+extern struct proc_dir_entry proc_scsi_gdth;
+int gdth_proc_info(char *,char **,off_t,int,int,int);
+#define GDTH { NULL, NULL, \
+ &proc_scsi_gdth, \
+ gdth_proc_info, \
+ "GDT SCSI Disk Array Controller", \
+ gdth_detect, \
+ gdth_release, \
+ gdth_info, \
+ gdth_command, \
+ gdth_queuecommand, \
+ gdth_abort, \
+ gdth_reset, \
+ NULL, \
+ gdth_bios_param, \
+ GDTH_MAXCMDS, \
+ -1, \
+ GDTH_MAXSG, \
+ GDTH_MAXC_P_L, \
+ 0, \
+ 1, \
+ ENABLE_CLUSTERING}
+#else
+int gdth_bios_param(Disk *,int,int *);
+#define GDTH { NULL, NULL, \
+ "GDT SCSI Disk Array Controller", \
+ gdth_detect, \
+ gdth_release, \
+ gdth_info, \
+ gdth_command, \
+ gdth_queuecommand, \
+ gdth_abort, \
+ gdth_reset, \
+ NULL, \
+ gdth_bios_param, \
+ GDTH_MAXCMDS, \
+ -1, \
+ GDTH_MAXSG, \
+ GDTH_MAXC_P_L, \
+ 0, \
+ 1, \
+ ENABLE_CLUSTERING}
+#endif
+
+#endif
+
diff --git a/linux/src/drivers/scsi/gdth_ioctl.h b/linux/src/drivers/scsi/gdth_ioctl.h
new file mode 100644
index 00000000..bf155542
--- /dev/null
+++ b/linux/src/drivers/scsi/gdth_ioctl.h
@@ -0,0 +1,86 @@
+#ifndef _GDTH_IOCTL_H
+#define _GDTH_IOCTL_H
+
+/* gdth_ioctl.h
+ * $Id: gdth_ioctl.h,v 1.1 1999/04/26 05:54:37 tb Exp $
+ */
+
+/* IOCTLs */
+#define GDTIOCTL_MASK ('J'<<8)
+#define GDTIOCTL_GENERAL (GDTIOCTL_MASK | 0) /* general IOCTL */
+#define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */
+#define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */
+#define GDTIOCTL_OSVERS (GDTIOCTL_MASK | 3) /* get OS version */
+#define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */
+#define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */
+#define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */
+#define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */
+
+#define GDTIOCTL_MAGIC 0x06030f07UL
+
+
+/* IOCTL structure (write) */
+typedef struct {
+ ulong magic; /* IOCTL magic */
+ ushort ioctl; /* IOCTL */
+ ushort ionode; /* controller number */
+ ushort service; /* controller service */
+ ushort timeout; /* timeout */
+ union {
+ struct {
+ unchar command[512]; /* controller command */
+ unchar data[1]; /* add. data */
+ } general;
+ struct {
+ unchar lock; /* lock/unlock */
+ unchar drive_cnt; /* drive count */
+ ushort drives[35]; /* drives */
+ } lockdrv;
+ struct {
+ unchar lock; /* lock/unlock */
+ unchar channel; /* channel */
+ } lockchn;
+ struct {
+ int erase; /* erase event ? */
+ int handle;
+ } event;
+ } iu;
+} gdth_iowr_str;
+
+/* IOCTL structure (read) */
+typedef struct {
+ ulong size; /* buffer size */
+ ulong status; /* IOCTL error code */
+ union {
+ struct {
+ unchar data[1]; /* data */
+ } general;
+ struct {
+ ushort version; /* driver version */
+ } drvers;
+ struct {
+ unchar type; /* controller type */
+ ushort info; /* slot etc. */
+ ushort oem_id; /* OEM ID */
+ ushort bios_ver; /* not used */
+ ushort access; /* not used */
+ ushort ext_type; /* extended type */
+ } ctrtype;
+ struct {
+ unchar version; /* OS version */
+ unchar subversion; /* OS subversion */
+ ushort revision; /* revision */
+ } osvers;
+ struct {
+ ushort count; /* controller count */
+ } ctrcnt;
+ struct {
+ int handle;
+ unchar evt[32]; /* event structure */
+ } event;
+ } iu;
+} gdth_iord_str;
+
+
+#endif
+
diff --git a/linux/src/drivers/scsi/gdth_proc.c b/linux/src/drivers/scsi/gdth_proc.c
new file mode 100644
index 00000000..8764d558
--- /dev/null
+++ b/linux/src/drivers/scsi/gdth_proc.c
@@ -0,0 +1,656 @@
+/* gdth_proc.c
+ * $Id: gdth_proc.c,v 1.1 1999/04/26 05:54:38 tb Exp $
+ */
+
+#include "gdth_ioctl.h"
+
+int gdth_proc_info(char *buffer,char **start,off_t offset,int length,
+ int hostno,int inout)
+{
+ int hanum,busnum,i;
+
+ TRACE2(("gdth_proc_info() length %d ha %d offs %d inout %d\n",
+ length,hostno,(int)offset,inout));
+
+ for (i=0; i<gdth_ctr_vcount; ++i) {
+ if (gdth_ctr_vtab[i]->host_no == hostno)
+ break;
+ }
+ if (i==gdth_ctr_vcount)
+ return(-EINVAL);
+
+ hanum = NUMDATA(gdth_ctr_vtab[i])->hanum;
+ busnum= NUMDATA(gdth_ctr_vtab[i])->busnum;
+
+ if (inout)
+ return(gdth_set_info(buffer,length,i,hanum,busnum));
+ else
+ return(gdth_get_info(buffer,start,offset,length,i,hanum,busnum));
+}
+
+static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum)
+{
+ int ret_val;
+ Scsi_Cmnd scp;
+ Scsi_Device sdev;
+ gdth_iowr_str *piowr;
+
+ TRACE2(("gdth_set_info() ha %d bus %d\n",hanum,busnum));
+ piowr = (gdth_iowr_str *)buffer;
+
+ memset(&sdev,0,sizeof(Scsi_Device));
+ memset(&scp, 0,sizeof(Scsi_Cmnd));
+ sdev.host = gdth_ctr_vtab[vh];
+ sdev.id = sdev.host->this_id;
+ scp.cmd_len = 12;
+ scp.host = gdth_ctr_vtab[vh];
+ scp.target = sdev.host->this_id;
+ scp.device = &sdev;
+ scp.use_sg = 0;
+
+ if (length >= 4) {
+ if (strncmp(buffer,"gdth",4) == 0) {
+ buffer += 5;
+ length -= 5;
+ ret_val = gdth_set_asc_info( buffer, length, hanum, scp );
+ } else if (piowr->magic == GDTIOCTL_MAGIC) {
+ ret_val = gdth_set_bin_info( buffer, length, hanum, scp );
+ } else {
+ printk("GDT: Wrong signature: %6s\n",buffer);
+ ret_val = -EINVAL;
+ }
+ } else {
+ ret_val = -EINVAL;
+ }
+ return ret_val;
+}
+
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+{
+ int orig_length, drive, wb_mode;
+ char cmnd[12];
+ int i, j, found;
+ gdth_ha_str *ha;
+ gdth_cmd_str gdtcmd;
+ gdth_cpar_str *pcpar;
+
+ TRACE2(("gdth_set_asc_info() ha %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ memset(cmnd, 0,10);
+ orig_length = length + 5;
+ drive = -1;
+ wb_mode = 0;
+ found = FALSE;
+
+ if (length >= 5 && strncmp(buffer,"flush",5)==0) {
+ buffer += 6;
+ length -= 6;
+ if (length && *buffer>='0' && *buffer<='9') {
+ drive = (int)(*buffer-'0');
+ ++buffer; --length;
+ if (length && *buffer>='0' && *buffer<='9') {
+ drive = drive*10 + (int)(*buffer-'0');
+ ++buffer; --length;
+ }
+ printk("GDT: Flushing host drive %d .. ",drive);
+ } else {
+ printk("GDT: Flushing all host drives .. ");
+ }
+ for (i = 0; i < MAXBUS; ++i) {
+ for (j = 0; j < MAXID; ++j) {
+ if (ha->id[i][j].type == CACHE_DTYP) {
+ if (drive != -1 &&
+ ha->id[i][j].hostdrive != (ushort)drive)
+ continue;
+ found = TRUE;
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_FLUSH;
+ gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive;
+ gdtcmd.u.cache.BlockNo = 1;
+ gdtcmd.u.cache.sg_canz = 0;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scp.SCp.this_residual = IOCTL_PRI;
+ scsi_do_cmd(&scp, cmnd, &gdtcmd,
+ sizeof(gdth_cmd_str), gdth_scsi_done,
+ 30*HZ, 1);
+ down(&sem);
+ }
+ }
+ }
+ }
+ if (!found)
+ printk("\nNo host drive found !\n");
+ else
+ printk("Done.\n");
+ return(orig_length);
+ }
+
+ if (length >= 7 && strncmp(buffer,"wbp_off",7)==0) {
+ buffer += 8;
+ length -= 8;
+ printk("GDT: Disabling write back permanently .. ");
+ wb_mode = 1;
+ } else if (length >= 6 && strncmp(buffer,"wbp_on",6)==0) {
+ buffer += 7;
+ length -= 7;
+ printk("GDT: Enabling write back permanently .. ");
+ wb_mode = 2;
+ } else if (length >= 6 && strncmp(buffer,"wb_off",6)==0) {
+ buffer += 7;
+ length -= 7;
+ printk("GDT: Disabling write back commands .. ");
+ if (ha->cache_feat & GDT_WR_THROUGH) {
+ gdth_write_through = TRUE;
+ printk("Done.\n");
+ } else {
+ printk("Not supported !\n");
+ }
+ return(orig_length);
+ } else if (length >= 5 && strncmp(buffer,"wb_on",5)==0) {
+ buffer += 6;
+ length -= 6;
+ printk("GDT: Enabling write back commands .. ");
+ gdth_write_through = FALSE;
+ printk("Done.\n");
+ return(orig_length);
+ }
+
+ if (wb_mode) {
+ pcpar = (gdth_cpar_str *)kmalloc( sizeof(gdth_cpar_str),
+ GFP_ATOMIC | GFP_DMA );
+ if (pcpar == NULL) {
+ TRACE2(("gdth_set_info(): Unable to allocate memory.\n"));
+ printk("Unable to allocate memory.\n");
+ return(-EINVAL);
+ }
+ memcpy( pcpar, &ha->cpar, sizeof(gdth_cpar_str) );
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_IOCTL;
+ gdtcmd.u.ioctl.p_param = virt_to_bus(pcpar);
+ gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str);
+ gdtcmd.u.ioctl.subfunc = CACHE_CONFIG;
+ gdtcmd.u.ioctl.channel = INVALID_CHANNEL;
+ pcpar->write_back = wb_mode==1 ? 0:1;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scp.SCp.this_residual = IOCTL_PRI;
+ scsi_do_cmd(&scp, cmnd, &gdtcmd, sizeof(gdth_cmd_str),
+ gdth_scsi_done, 30*HZ, 1);
+ down(&sem);
+ }
+ kfree( pcpar );
+ printk("Done.\n");
+ return(orig_length);
+ }
+
+ printk("GDT: Unknown command: %s Length: %d\n",buffer,length);
+ return(-EINVAL);
+}
+
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+{
+ char cmnd[12];
+ int id;
+ unchar i, j, k, found;
+ gdth_ha_str *ha;
+ gdth_iowr_str *piowr;
+ gdth_iord_str *piord;
+ gdth_cmd_str *pcmd;
+ ulong *ppadd;
+ ulong add_size, flags;
+
+
+ TRACE2(("gdth_set_bin_info() ha %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ memset(cmnd, 0,10);
+ piowr = (gdth_iowr_str *)buffer;
+ piord = NULL;
+ pcmd = NULL;
+
+ if (length < GDTOFFSOF(gdth_iowr_str,iu))
+ return(-EINVAL);
+
+ switch (piowr->ioctl) {
+ case GDTIOCTL_GENERAL:
+ if (length < GDTOFFSOF(gdth_iowr_str,iu.general.data[0]))
+ return(-EINVAL);
+ pcmd = (gdth_cmd_str *)piowr->iu.general.command;
+ pcmd->Service = piowr->service;
+ if (pcmd->OpCode == GDT_IOCTL) {
+ ppadd = &pcmd->u.ioctl.p_param;
+ add_size = pcmd->u.ioctl.param_size;
+ } else if (piowr->service == CACHESERVICE) {
+ add_size = pcmd->u.cache.BlockCnt * SECTOR_SIZE;
+ if (ha->cache_feat & SCATTER_GATHER) {
+ ppadd = &pcmd->u.cache.sg_lst[0].sg_ptr;
+ pcmd->u.cache.DestAddr = -1UL;
+ pcmd->u.cache.sg_lst[0].sg_len = add_size;
+ pcmd->u.cache.sg_canz = 1;
+ } else {
+ ppadd = &pcmd->u.cache.DestAddr;
+ pcmd->u.cache.sg_canz = 0;
+ }
+ } else if (piowr->service == SCSIRAWSERVICE) {
+ add_size = pcmd->u.raw.sdlen;
+ if (ha->raw_feat & SCATTER_GATHER) {
+ ppadd = &pcmd->u.raw.sg_lst[0].sg_ptr;
+ pcmd->u.raw.sdata = -1UL;
+ pcmd->u.raw.sg_lst[0].sg_len = add_size;
+ pcmd->u.raw.sg_ranz = 1;
+ } else {
+ ppadd = &pcmd->u.raw.sdata;
+ pcmd->u.raw.sg_ranz = 0;
+ }
+ } else {
+ return(-EINVAL);
+ }
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) + add_size );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+
+ piord->size = sizeof(gdth_iord_str) + add_size;
+ if (add_size > 0) {
+ memcpy(piord->iu.general.data, piowr->iu.general.data, add_size);
+ *ppadd = virt_to_bus(piord->iu.general.data);
+ }
+ /* do IOCTL */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scp.SCp.this_residual = IOCTL_PRI;
+ scsi_do_cmd(&scp, cmnd, pcmd,
+ sizeof(gdth_cmd_str), gdth_scsi_done,
+ piowr->timeout*HZ, 1);
+ down(&sem);
+ piord->status = (ulong)scp.SCp.Message;
+ }
+ break;
+
+ case GDTIOCTL_DRVERS:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ piord->iu.drvers.version = (GDTH_VERSION<<8) | GDTH_SUBVERSION;
+ break;
+
+ case GDTIOCTL_CTRTYPE:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ if (ha->type == GDT_ISA || ha->type == GDT_EISA) {
+ piord->iu.ctrtype.type = (unchar)((ha->stype>>20) - 0x10);
+ } else if (ha->type != GDT_PCIMPR) {
+ piord->iu.ctrtype.type = (unchar)((ha->stype<<8) + 6);
+ } else {
+ piord->iu.ctrtype.type = 0xfe;
+ piord->iu.ctrtype.ext_type = 0x6000 | ha->stype;
+ }
+ piord->iu.ctrtype.info = ha->brd_phys;
+ piord->iu.ctrtype.oem_id = (ushort)GDT3_ID;
+ break;
+
+ case GDTIOCTL_CTRCNT:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ piord->iu.ctrcnt.count = (ushort)gdth_ctr_count;
+ break;
+
+ case GDTIOCTL_OSVERS:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ piord->iu.osvers.version = (unchar)(LINUX_VERSION_CODE >> 16);
+ piord->iu.osvers.subversion = (unchar)(LINUX_VERSION_CODE >> 8);
+ piord->iu.osvers.revision = (ushort)(LINUX_VERSION_CODE & 0xff);
+ break;
+
+ case GDTIOCTL_LOCKDRV:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ for (i = k = 0; i < piowr->iu.lockdrv.drive_cnt; ++i) {
+ found = FALSE;
+ for (j = 0; j < ha->bus_cnt; ++j) {
+ for (k = 0; k < MAXID; ++k) {
+ if (ha->id[j][k].type == CACHE_DTYP &&
+ ha->id[j][k].hostdrive == piowr->iu.lockdrv.drives[i]) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ if (!found)
+ continue;
+
+ if (piowr->iu.lockdrv.lock) {
+ save_flags( flags );
+ cli();
+ ha->id[j][k].lock = 1;
+ restore_flags( flags );
+ gdth_wait_completion( hanum, j, k );
+ gdth_stop_timeout( hanum, j, k );
+ } else {
+ save_flags( flags );
+ cli();
+ ha->id[j][k].lock = 0;
+ restore_flags( flags );
+ gdth_start_timeout( hanum, j, k );
+ gdth_next( hanum );
+ }
+ }
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ break;
+
+ case GDTIOCTL_LOCKCHN:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ for (k = 0, j = piowr->iu.lockchn.channel; k < MAXID; ++k) {
+ if (ha->id[j][k].type != RAW_DTYP)
+ continue;
+
+ if (piowr->iu.lockchn.lock) {
+ save_flags( flags );
+ cli();
+ ha->id[j][k].lock = 1;
+ restore_flags( flags );
+ gdth_wait_completion( hanum, j, k );
+ gdth_stop_timeout( hanum, j, k );
+ } else {
+ save_flags( flags );
+ cli();
+ ha->id[j][k].lock = 0;
+ restore_flags( flags );
+ gdth_start_timeout( hanum, j, k );
+ gdth_next( hanum );
+ }
+ }
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ break;
+
+ case GDTIOCTL_EVENT:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ if (piowr->iu.event.erase == 0) {
+ piord->iu.event.handle = gdth_read_event( piowr->iu.event.handle,
+ (gdth_evt_str *)piord->iu.event.evt );
+ } else {
+ piord->iu.event.handle = piowr->iu.event.handle;
+ gdth_readapp_event( (unchar)piowr->iu.event.erase,
+ (gdth_evt_str *)piord->iu.event.evt );
+ }
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ break;
+
+ default:
+ return(-EINVAL);
+ }
+ /* we return a buffer ID to detect the right buffer during READ-IOCTL */
+ return id;
+}
+
+static int gdth_get_info(char *buffer,char **start,off_t offset,
+ int length,int vh,int hanum,int busnum)
+{
+ int size = 0,len = 0;
+ off_t begin = 0,pos = 0;
+ gdth_ha_str *ha;
+ gdth_iord_str *piord;
+ int id;
+
+ TRACE2(("gdth_get_info() ha %d bus %d\n",hanum,busnum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ id = length;
+
+ /* look for buffer ID in length */
+ if (id > 4) {
+#if LINUX_VERSION_CODE >= 0x020000
+ size = sprintf(buffer+len,
+ "%s SCSI Disk Array Controller\n",
+ ha->ctr_name);
+#else
+ size = sprintf(buffer+len,
+ "%s SCSI Disk Array Controller (SCSI Bus %d)\n",
+ ha->ctr_name,busnum);
+#endif
+ len += size; pos = begin + len;
+ size = sprintf(buffer+len,
+ "Firmware Version: %d.%2d\tDriver Version: %s\n",
+ (unchar)(ha->cpar.version>>8),
+ (unchar)(ha->cpar.version),GDTH_VERSION_STR);
+ len += size; pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ } else {
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ if (piord == NULL)
+ goto stop_output;
+ length = piord->size;
+ memcpy(buffer+len, (char *)piord, length);
+ gdth_ioctl_free(hanum, id);
+ len += length; pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+
+stop_output:
+ *start = buffer +(offset-begin);
+ len -= (offset-begin);
+ if (len > length)
+ len = length;
+ TRACE2(("get_info() len %d pos %d begin %d offset %d length %d size %d\n",
+ len,(int)pos,(int)begin,(int)offset,length,size));
+ return(len);
+}
+
+
+void gdth_scsi_done(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_scsi_done()\n"));
+
+ scp->request.rq_status = RQ_SCSI_DONE;
+
+ if (scp->request.sem != NULL)
+ up(scp->request.sem);
+}
+
+static int gdth_ioctl_alloc(int hanum, ushort size)
+{
+ ulong flags;
+ int i;
+
+ if (size == 0)
+ return -1;
+
+ save_flags(flags);
+ cli();
+
+ for (i = 0; i < 4; ++i) {
+ if (gdth_ioctl_tab[i][hanum] == NULL) {
+ gdth_ioctl_tab[i][hanum] = kmalloc( size, GFP_ATOMIC | GFP_DMA );
+ break;
+ }
+ }
+
+ restore_flags(flags);
+ if (i == 4 || gdth_ioctl_tab[i][hanum] == NULL)
+ return -1;
+ return (i+1);
+}
+
+static void gdth_ioctl_free(int hanum, int idx)
+{
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+
+ kfree( gdth_ioctl_tab[idx-1][hanum] );
+ gdth_ioctl_tab[idx-1][hanum] = NULL;
+
+ restore_flags(flags);
+}
+
+static void gdth_wait_completion(int hanum, int busnum, int id)
+{
+ ulong flags;
+ int i;
+ Scsi_Cmnd *scp;
+
+ save_flags(flags);
+ cli();
+
+ for (i = 0; i < GDTH_MAXCMDS; ++i) {
+ scp = gdth_cmd_tab[i][hanum].cmnd;
+#if LINUX_VERSION_CODE >= 0x020000
+ if (!SPECIAL_SCP(scp) && scp->target == (unchar)id &&
+ scp->channel == (unchar)busnum)
+#else
+ if (!SPECIAL_SCP(scp) && scp->target == (unchar)id &&
+ NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+ {
+ scp->SCp.have_data_in = 0;
+ restore_flags(flags);
+ while (!scp->SCp.have_data_in)
+ barrier();
+ scp->scsi_done(scp);
+ save_flags(flags);
+ cli();
+ }
+ }
+ restore_flags(flags);
+}
+
+static void gdth_stop_timeout(int hanum, int busnum, int id)
+{
+ ulong flags;
+ Scsi_Cmnd *scp;
+ gdth_ha_str *ha;
+
+ save_flags(flags);
+ cli();
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+#if LINUX_VERSION_CODE >= 0x020000
+ if (scp->target == (unchar)id &&
+ scp->channel == (unchar)busnum)
+#else
+ if (scp->target == (unchar)id &&
+ NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+ {
+ TRACE2(("gdth_stop_timeout(): update_timeout()\n"));
+ scp->SCp.buffers_residual = gdth_update_timeout(hanum, scp, 0);
+ }
+ }
+ restore_flags(flags);
+}
+
+static void gdth_start_timeout(int hanum, int busnum, int id)
+{
+ ulong flags;
+ Scsi_Cmnd *scp;
+ gdth_ha_str *ha;
+
+ save_flags(flags);
+ cli();
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+#if LINUX_VERSION_CODE >= 0x020000
+ if (scp->target == (unchar)id &&
+ scp->channel == (unchar)busnum)
+#else
+ if (scp->target == (unchar)id &&
+ NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+ {
+ TRACE2(("gdth_start_timeout(): update_timeout()\n"));
+ gdth_update_timeout(hanum, scp, scp->SCp.buffers_residual);
+ }
+ }
+ restore_flags(flags);
+}
+
+static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout)
+{
+ ulong flags;
+ int oldto;
+
+ save_flags(flags);
+ cli();
+ oldto = scp->timeout_per_command;
+ scp->timeout_per_command = timeout;
+
+#if LINUX_VERSION_CODE >= 0x02014B
+ if (timeout == 0) {
+ del_timer(&scp->eh_timeout);
+ scp->eh_timeout.data = (unsigned long) NULL;
+ scp->eh_timeout.expires = 0;
+ } else {
+ if (scp->eh_timeout.data != (unsigned long) NULL)
+ del_timer(&scp->eh_timeout);
+ scp->eh_timeout.data = (unsigned long) scp;
+ scp->eh_timeout.expires = jiffies + timeout;
+ add_timer(&scp->eh_timeout);
+ }
+#else
+ if (timeout > 0) {
+ if (timer_table[SCSI_TIMER].expires == 0) {
+ timer_table[SCSI_TIMER].expires = jiffies + timeout;
+ timer_active |= 1 << SCSI_TIMER;
+ } else {
+ if (jiffies + timeout < timer_table[SCSI_TIMER].expires)
+ timer_table[SCSI_TIMER].expires = jiffies + timeout;
+ }
+ }
+#endif
+
+ restore_flags(flags);
+ return oldto;
+}
+
diff --git a/linux/src/drivers/scsi/gdth_proc.h b/linux/src/drivers/scsi/gdth_proc.h
new file mode 100644
index 00000000..708b077d
--- /dev/null
+++ b/linux/src/drivers/scsi/gdth_proc.h
@@ -0,0 +1,24 @@
+#ifndef _GDTH_PROC_H
+#define _GDTH_PROC_H
+
+/* gdth_proc.h
+ * $Id: gdth_proc.h,v 1.1 1999/04/26 05:54:39 tb Exp $
+ */
+
+static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum);
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+static int gdth_get_info(char *buffer,char **start,off_t offset,
+ int length,int vh,int hanum,int busnum);
+
+static int gdth_ioctl_alloc(int hanum, ushort size);
+static void gdth_ioctl_free(int hanum, int id);
+static void gdth_wait_completion(int hanum, int busnum, int id);
+static void gdth_stop_timeout(int hanum, int busnum, int id);
+static void gdth_start_timeout(int hanum, int busnum, int id);
+static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout);
+
+void gdth_scsi_done(Scsi_Cmnd *scp);
+
+#endif
+
diff --git a/linux/src/drivers/scsi/hosts.c b/linux/src/drivers/scsi/hosts.c
new file mode 100644
index 00000000..07646041
--- /dev/null
+++ b/linux/src/drivers/scsi/hosts.c
@@ -0,0 +1,545 @@
+/*
+ * hosts.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * mid to lowlevel SCSI driver interface
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ */
+
+
+/*
+ * This file contains the medium level SCSI
+ * host interface initialization, as well as the scsi_hosts array of SCSI
+ * hosts currently present in the system.
+ */
+
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+
+#include "scsi.h"
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+#define HOSTS_C
+
+#include "hosts.h"
+
+#ifdef CONFIG_A3000_SCSI
+#include "a3000.h"
+#endif
+
+#ifdef CONFIG_A2091_SCSI
+#include "a2091.h"
+#endif
+
+#ifdef CONFIG_GVP11_SCSI
+#include "gvp11.h"
+#endif
+
+#ifdef CONFIG_ATARI_SCSI
+#include "atari_scsi.h"
+#endif
+
+#ifdef CONFIG_SCSI_ADVANSYS
+#include "advansys.h"
+#endif
+
+#ifdef CONFIG_SCSI_AHA152X
+#include "aha152x.h"
+#endif
+
+#ifdef CONFIG_SCSI_AHA1542
+#include "aha1542.h"
+#endif
+
+#ifdef CONFIG_SCSI_AHA1740
+#include "aha1740.h"
+#endif
+
+#ifdef CONFIG_SCSI_AIC7XXX
+#include "aic7xxx.h"
+#endif
+
+#ifdef CONFIG_SCSI_BUSLOGIC
+#include "BusLogic.h"
+#endif
+
+#ifdef CONFIG_SCSI_EATA_DMA
+#include "eata_dma.h"
+#endif
+
+#ifdef CONFIG_SCSI_EATA_PIO
+#include "eata_pio.h"
+#endif
+
+#ifdef CONFIG_SCSI_U14_34F
+#include "u14-34f.h"
+#endif
+
+#ifdef CONFIG_SCSI_FUTURE_DOMAIN
+#include "fdomain.h"
+#endif
+
+#ifdef CONFIG_SCSI_GENERIC_NCR5380
+#include "g_NCR5380.h"
+#endif
+
+#ifdef CONFIG_SCSI_IN2000
+#include "in2000.h"
+#endif
+
+#ifdef CONFIG_SCSI_PAS16
+#include "pas16.h"
+#endif
+
+#ifdef CONFIG_SCSI_QLOGIC_FAS
+#include "qlogicfas.h"
+#endif
+
+#ifdef CONFIG_SCSI_QLOGIC_ISP
+#include "qlogicisp.h"
+#endif
+
+#ifdef CONFIG_SCSI_SEAGATE
+#include "seagate.h"
+#endif
+
+#ifdef CONFIG_SCSI_T128
+#include "t128.h"
+#endif
+
+#ifdef CONFIG_SCSI_DTC3280
+#include "dtc.h"
+#endif
+
+#ifdef CONFIG_SCSI_NCR53C7xx
+#include "53c7,8xx.h"
+#endif
+
+#ifdef CONFIG_SCSI_NCR53C8XX
+#include "ncr53c8xx.h"
+#endif
+
+#ifdef CONFIG_SCSI_ULTRASTOR
+#include "ultrastor.h"
+#endif
+
+#ifdef CONFIG_SCSI_7000FASST
+#include "wd7000.h"
+#endif
+
+#ifdef CONFIG_SCSI_EATA
+#include "eata.h"
+#endif
+
+#ifdef CONFIG_SCSI_NCR53C406A
+#include "NCR53c406a.h"
+#endif
+
+#ifdef CONFIG_SCSI_DC390T
+#include "dc390.h"
+#endif
+
+#ifdef CONFIG_SCSI_AM53C974
+#include "AM53C974.h"
+#endif
+
+#ifdef CONFIG_SCSI_MEGARAID
+#include "megaraid.h"
+#endif
+
+#ifdef CONFIG_SCSI_PPA
+#include "ppa.h"
+#endif
+
+#ifdef CONFIG_SCSI_SUNESP
+#include "esp.h"
+#endif
+
+#ifdef CONFIG_BLK_DEV_IDESCSI
+#include "ide-scsi.h"
+#endif
+
+#ifdef CONFIG_SCSI_GDTH
+#include "gdth.h"
+#endif
+
+#ifdef CONFIG_SCSI_DEBUG
+#include "scsi_debug.h"
+#endif
+
+
+/*
+static const char RCSid[] = "$Header: cvs/gnumach/linux/src/drivers/scsi/Attic/hosts.c,v 1.1 1999/04/26 05:54:40 tb Exp $";
+*/
+
+/*
+ * The scsi host entries should be in the order you wish the
+ * cards to be detected. A driver may appear more than once IFF
+ * it can deal with being detected (and therefore initialized)
+ * with more than one simultaneous host number, can handle being
+ * reentrant, etc.
+ *
+ * They may appear in any order, as each SCSI host is told which host
+ * number it is during detection.
+ */
+
+/* This is a placeholder for controllers that are not configured into
+ * the system - we do this to ensure that the controller numbering is
+ * always consistent, no matter how the kernel is configured. */
+
+#define NO_CONTROLLER {NULL, NULL, NULL, NULL, NULL, NULL, NULL, \
+ NULL, NULL, 0, 0, 0, 0, 0, 0}
+
+/*
+ * When figure is run, we don't want to link to any object code. Since
+ * the macro for each host will contain function pointers, we cannot
+ * use it and instead must use a "blank" that does no such
+ * idiocy.
+ */
+
+Scsi_Host_Template * scsi_hosts = NULL;
+
+static Scsi_Host_Template builtin_scsi_hosts[] =
+{
+#ifdef CONFIG_AMIGA
+#ifdef CONFIG_A3000_SCSI
+ A3000_SCSI,
+#endif
+#ifdef CONFIG_A2091_SCSI
+ A2091_SCSI,
+#endif
+#ifdef CONFIG_GVP11_SCSI
+ GVP11_SCSI,
+#endif
+#endif
+
+#ifdef CONFIG_ATARI
+#ifdef CONFIG_ATARI_SCSI
+ ATARI_SCSI,
+#endif
+#endif
+
+#ifdef CONFIG_SCSI_ADVANSYS
+ ADVANSYS,
+#endif
+/* BusLogic must come before aha1542.c */
+#ifdef CONFIG_SCSI_BUSLOGIC
+ BUSLOGIC,
+#endif
+#ifdef CONFIG_SCSI_U14_34F
+ ULTRASTOR_14_34F,
+#endif
+#ifdef CONFIG_SCSI_ULTRASTOR
+ ULTRASTOR_14F,
+#endif
+#ifdef CONFIG_SCSI_AHA152X
+ AHA152X,
+#endif
+#ifdef CONFIG_SCSI_AHA1542
+ AHA1542,
+#endif
+#ifdef CONFIG_SCSI_AHA1740
+ AHA1740,
+#endif
+#ifdef CONFIG_SCSI_AIC7XXX
+ AIC7XXX,
+#endif
+#ifdef CONFIG_SCSI_FUTURE_DOMAIN
+ FDOMAIN_16X0,
+#endif
+#ifdef CONFIG_SCSI_IN2000
+ IN2000,
+#endif
+#ifdef CONFIG_SCSI_GENERIC_NCR5380
+ GENERIC_NCR5380,
+#endif
+#ifdef CONFIG_SCSI_NCR53C406A /* 53C406A should come before QLOGIC */
+ NCR53c406a,
+#endif
+#ifdef CONFIG_SCSI_QLOGIC_FAS
+ QLOGICFAS,
+#endif
+#ifdef CONFIG_SCSI_QLOGIC_ISP
+ QLOGICISP,
+#endif
+#ifdef CONFIG_SCSI_PAS16
+ MV_PAS16,
+#endif
+#ifdef CONFIG_SCSI_SEAGATE
+ SEAGATE_ST0X,
+#endif
+#ifdef CONFIG_SCSI_T128
+ TRANTOR_T128,
+#endif
+#ifdef CONFIG_SCSI_DTC3280
+ DTC3x80,
+#endif
+#ifdef CONFIG_SCSI_DC390T
+ DC390_T,
+#endif
+#ifdef CONFIG_SCSI_NCR53C7xx
+ NCR53c7xx,
+#endif
+#ifdef CONFIG_SCSI_NCR53C8XX
+ NCR53C8XX,
+#endif
+#ifdef CONFIG_SCSI_EATA_DMA
+ EATA_DMA,
+#endif
+#ifdef CONFIG_SCSI_EATA_PIO
+ EATA_PIO,
+#endif
+#ifdef CONFIG_SCSI_7000FASST
+ WD7000,
+#endif
+#ifdef CONFIG_SCSI_EATA
+ EATA,
+#endif
+#ifdef CONFIG_SCSI_AM53C974
+ AM53C974,
+#endif
+#ifdef CONFIG_SCSI_MEGARAID
+ MEGARAID,
+#endif
+#ifdef CONFIG_SCSI_PPA
+ PPA,
+#endif
+#ifdef CONFIG_SCSI_SUNESP
+ SCSI_SPARC_ESP,
+#endif
+#ifdef CONFIG_SCSI_GDTH
+ GDTH,
+#endif
+#ifdef CONFIG_BLK_DEV_IDESCSI
+ IDESCSI,
+#endif
+#ifdef CONFIG_SCSI_DEBUG
+ SCSI_DEBUG,
+#endif
+};
+
+#define MAX_SCSI_HOSTS (sizeof(builtin_scsi_hosts) / sizeof(Scsi_Host_Template))
+
+
+/*
+ * Our semaphores and timeout counters, where size depends on
+ * MAX_SCSI_HOSTS here.
+ */
+
+struct Scsi_Host * scsi_hostlist = NULL;
+struct Scsi_Device_Template * scsi_devicelist = NULL;
+
+int max_scsi_hosts = 0;
+int next_scsi_host = 0;
+
+void
+scsi_unregister(struct Scsi_Host * sh){
+ struct Scsi_Host * shpnt;
+
+ if(scsi_hostlist == sh)
+ scsi_hostlist = sh->next;
+ else {
+ shpnt = scsi_hostlist;
+ while(shpnt->next != sh) shpnt = shpnt->next;
+ shpnt->next = shpnt->next->next;
+ }
+
+ /* If we are removing the last host registered, it is safe to reuse
+ * its host number (this avoids "holes" at boot time) (DB)
+ * It is also safe to reuse those of numbers directly below which have
+ * been released earlier (to avoid some holes in numbering).
+ */
+ if(sh->host_no == max_scsi_hosts - 1) {
+ while(--max_scsi_hosts >= next_scsi_host) {
+ shpnt = scsi_hostlist;
+ while(shpnt && shpnt->host_no != max_scsi_hosts - 1)
+ shpnt = shpnt->next;
+ if(shpnt)
+ break;
+ }
+ }
+ next_scsi_host--;
+ scsi_init_free((char *) sh, sizeof(struct Scsi_Host) + sh->extra_bytes);
+}
+
+/* We call this when we come across a new host adapter. We only do this
+ * once we are 100% sure that we want to use this host adapter - it is a
+ * pain to reverse this, so we try to avoid it
+ */
+
+struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
+ struct Scsi_Host * retval, *shpnt;
+ retval = (struct Scsi_Host *)scsi_init_malloc(sizeof(struct Scsi_Host) + j,
+ (tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC);
+ retval->host_busy = 0;
+ retval->block = NULL;
+ retval->wish_block = 0;
+ if(j > 0xffff) panic("Too many extra bytes requested\n");
+ retval->extra_bytes = j;
+ retval->loaded_as_module = scsi_loadable_module_flag;
+ retval->host_no = max_scsi_hosts++; /* never reuse host_no (DB) */
+ next_scsi_host++;
+ retval->host_queue = NULL;
+ retval->host_wait = NULL;
+ retval->last_reset = 0;
+ retval->irq = 0;
+ retval->dma_channel = 0xff;
+
+ /* These three are default values which can be overridden */
+ retval->max_channel = 0;
+ retval->max_id = 8;
+ retval->max_lun = 8;
+
+ retval->unique_id = 0;
+ retval->io_port = 0;
+ retval->hostt = tpnt;
+ retval->next = NULL;
+#ifdef DEBUG
+ printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j);
+#endif
+
+ /* The next six are the default values which can be overridden
+ * if need be */
+ retval->this_id = tpnt->this_id;
+ retval->can_queue = tpnt->can_queue;
+ retval->sg_tablesize = tpnt->sg_tablesize;
+ retval->cmd_per_lun = tpnt->cmd_per_lun;
+ retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
+ retval->use_clustering = tpnt->use_clustering;
+
+ retval->select_queue_depths = NULL;
+
+ if(!scsi_hostlist)
+ scsi_hostlist = retval;
+ else
+ {
+ shpnt = scsi_hostlist;
+ while(shpnt->next) shpnt = shpnt->next;
+ shpnt->next = retval;
+ }
+
+ return retval;
+}
+
+int
+scsi_register_device(struct Scsi_Device_Template * sdpnt)
+{
+ if(sdpnt->next) panic("Device already registered");
+ sdpnt->next = scsi_devicelist;
+ scsi_devicelist = sdpnt;
+ return 0;
+}
+
+unsigned int scsi_init()
+{
+ static int called = 0;
+ int i, pcount;
+ Scsi_Host_Template * tpnt;
+ struct Scsi_Host * shpnt;
+ const char * name;
+
+ if(called) return 0;
+
+ called = 1;
+ for (tpnt = &builtin_scsi_hosts[0], i = 0; i < MAX_SCSI_HOSTS; ++i, tpnt++)
+ {
+ /*
+ * Initialize our semaphores. -1 is interpreted to mean
+ * "inactive" - where as 0 will indicate a time out condition.
+ */
+
+ pcount = next_scsi_host;
+ if ((tpnt->detect) &&
+ (tpnt->present =
+ tpnt->detect(tpnt)))
+ {
+ /* The only time this should come up is when people use
+ * some kind of patched driver of some kind or another. */
+ if(pcount == next_scsi_host) {
+ if(tpnt->present > 1)
+ panic("Failure to register low-level scsi driver");
+ /* The low-level driver failed to register a driver. We
+ * can do this now. */
+ scsi_register(tpnt,0);
+ }
+ tpnt->next = scsi_hosts;
+ scsi_hosts = tpnt;
+
+ /* Add the driver to /proc/scsi */
+#if CONFIG_PROC_FS
+ build_proc_dir_entries(tpnt);
+#endif
+ }
+ }
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ if(shpnt->hostt->info)
+ name = shpnt->hostt->info(shpnt);
+ else
+ name = shpnt->hostt->name;
+ printk ("scsi%d : %s\n", /* And print a little message */
+ shpnt->host_no, name);
+ }
+
+ printk ("scsi : %d host%s.\n", next_scsi_host,
+ (next_scsi_host == 1) ? "" : "s");
+
+ scsi_make_blocked_list();
+
+ /* Now attach the high level drivers */
+#ifdef CONFIG_BLK_DEV_SD
+ scsi_register_device(&sd_template);
+#endif
+#ifdef CONFIG_BLK_DEV_SR
+ scsi_register_device(&sr_template);
+#endif
+#ifdef CONFIG_CHR_DEV_ST
+ scsi_register_device(&st_template);
+#endif
+#ifdef CONFIG_CHR_DEV_SG
+ scsi_register_device(&sg_template);
+#endif
+
+#if 0
+ max_scsi_hosts = next_scsi_host;
+#endif
+ return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/hosts.h b/linux/src/drivers/scsi/hosts.h
new file mode 100644
index 00000000..8f3f4e0e
--- /dev/null
+++ b/linux/src/drivers/scsi/hosts.h
@@ -0,0 +1,405 @@
+/*
+ * hosts.h Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * mid to low-level SCSI driver interface header
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale eric@aib.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Further modified by Eric Youngdale to support multiple host adapters
+ * of the same type.
+ */
+
+#ifndef _HOSTS_H
+#define _HOSTS_H
+
+/*
+ $Header: cvs/gnumach/linux/src/drivers/scsi/Attic/hosts.h,v 1.1 1999/04/26 05:54:41 tb Exp $
+*/
+
+#include <linux/proc_fs.h>
+
+/* It is senseless to set SG_ALL any higher than this - the performance
+ * does not get any better, and it wastes memory
+ */
+#define SG_NONE 0
+#define SG_ALL 0xff
+
+#define DISABLE_CLUSTERING 0
+#define ENABLE_CLUSTERING 1
+
+/* The various choices mean:
+ * NONE: Self evident. Host adapter is not capable of scatter-gather.
+ * ALL: Means that the host adapter module can do scatter-gather,
+ * and that there is no limit to the size of the table to which
+ * we scatter/gather data.
+ * Anything else: Indicates the maximum number of chains that can be
+ * used in one scatter-gather request.
+ */
+
+/*
+ * The Scsi_Host_Template type has all that is needed to interface with a SCSI
+ * host in a device independent matter. There is one entry for each different
+ * type of host adapter that is supported on the system.
+ */
+
+typedef struct scsi_disk Disk;
+
+typedef struct SHT
+{
+
+ /* Used with loadable modules so we can construct a linked list. */
+ struct SHT * next;
+
+ /* Used with loadable modules so that we know when it is safe to unload */
+ long * usage_count;
+
+ /* The pointer to the /proc/scsi directory entry */
+ struct proc_dir_entry *proc_dir;
+
+ /* proc-fs info function.
+ * Can be used to export driver statistics and other infos to the world
+ * outside the kernel ie. userspace and it also provides an interface
+ * to feed the driver with information. Check eata_dma_proc.c for reference
+ */
+ int (*proc_info)(char *, char **, off_t, int, int, int);
+
+ /*
+ * The name pointer is a pointer to the name of the SCSI
+ * device detected.
+ */
+ const char *name;
+
+ /*
+ * The detect function shall return non zero on detection,
+ * indicating the number of host adapters of this particular
+ * type were found. It should also
+ * initialize all data necessary for this particular
+ * SCSI driver. It is passed the host number, so this host
+ * knows where the first entry is in the scsi_hosts[] array.
+ *
+ * Note that the detect routine MUST not call any of the mid level
+ * functions to queue commands because things are not guaranteed
+ * to be set up yet. The detect routine can send commands to
+ * the host adapter as long as the program control will not be
+ * passed to scsi.c in the processing of the command. Note
+ * especially that scsi_malloc/scsi_free must not be called.
+ */
+ int (* detect)(struct SHT *);
+
+ /* Used with loadable modules to unload the host structures. Note:
+ * there is a default action built into the modules code which may
+ * be sufficient for most host adapters. Thus you may not have to supply
+ * this at all.
+ */
+ int (*release)(struct Scsi_Host *);
+
+ /*
+ * The info function will return whatever useful
+ * information the developer sees fit. If not provided, then
+ * the name field will be used instead.
+ */
+ const char *(* info)(struct Scsi_Host *);
+
+ /*
+ * The command function takes a target, a command (this is a SCSI
+ * command formatted as per the SCSI spec, nothing strange), a
+ * data buffer pointer, and data buffer length pointer. The return
+ * is a status int, bit fielded as follows :
+ * Byte What
+ * 0 SCSI status code
+ * 1 SCSI 1 byte message
+ * 2 host error return.
+ * 3 mid level error return
+ */
+ int (* command)(Scsi_Cmnd *);
+
+ /*
+ * The QueueCommand function works in a similar manner
+ * to the command function. It takes an additional parameter,
+ * void (* done)(int host, int code) which is passed the host
+ * # and exit result when the command is complete.
+ * Host number is the POSITION IN THE hosts array of THIS
+ * host adapter.
+ */
+ int (* queuecommand)(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+
+ /*
+ * Since the mid level driver handles time outs, etc, we want to
+ * be able to abort the current command. Abort returns 0 if the
+ * abortion was successful. The field SCpnt->abort reason
+ * can be filled in with the appropriate reason why we wanted
+ * the abort in the first place, and this will be used
+ * in the mid-level code instead of the host_byte().
+ * If non-zero, the code passed to it
+ * will be used as the return code, otherwise
+ * DID_ABORT should be returned.
+ *
+ * Note that the scsi driver should "clean up" after itself,
+ * resetting the bus, etc. if necessary.
+ */
+ int (* abort)(Scsi_Cmnd *);
+
+ /*
+ * The reset function will reset the SCSI bus. Any executing
+ * commands should fail with a DID_RESET in the host byte.
+ * The Scsi_Cmnd is passed so that the reset routine can figure
+ * out which host adapter should be reset, and also which command
+ * within the command block was responsible for the reset in
+ * the first place. Some hosts do not implement a reset function,
+ * and these hosts must call scsi_request_sense(SCpnt) to keep
+ * the command alive.
+ */
+ int (* reset)(Scsi_Cmnd *, unsigned int);
+
+ /*
+ * This function is used to select synchronous communications,
+ * which will result in a higher data throughput. Not implemented
+ * yet.
+ */
+ int (* slave_attach)(int, int);
+
+ /*
+ * This function determines the bios parameters for a given
+ * harddisk. These tend to be numbers that are made up by
+ * the host adapter. Parameters:
+ * size, device number, list (heads, sectors, cylinders)
+ */
+ int (* bios_param)(Disk *, kdev_t, int []);
+
+ /*
+ * This determines if we will use a non-interrupt driven
+ * or an interrupt driven scheme, It is set to the maximum number
+ * of simultaneous commands a given host adapter will accept.
+ */
+ int can_queue;
+
+ /*
+ * In many instances, especially where disconnect / reconnect are
+ * supported, our host also has an ID on the SCSI bus. If this is
+ * the case, then it must be reserved. Please set this_id to -1 if
+ * your setup is in single initiator mode, and the host lacks an
+ * ID.
+ */
+ int this_id;
+
+ /*
+ * This determines the degree to which the host adapter is capable
+ * of scatter-gather.
+ */
+ short unsigned int sg_tablesize;
+
+ /*
+ * True if this host adapter can make good use of linked commands.
+ * This will allow more than one command to be queued to a given
+ * unit on a given host. Set this to the maximum number of command
+ * blocks to be provided for each device. Set this to 1 for one
+ * command block per lun, 2 for two, etc. Do not set this to 0.
+ * You should make sure that the host adapter will do the right thing
+ * before you try setting this above 1.
+ */
+ short cmd_per_lun;
+
+ /*
+ * present contains counter indicating how many boards of this
+ * type were found when we did the scan.
+ */
+ unsigned char present;
+
+ /*
+ * true if this host adapter uses unchecked DMA onto an ISA bus.
+ */
+ unsigned unchecked_isa_dma:1;
+
+ /*
+ * true if this host adapter can make good use of clustering.
+ * I originally thought that if the tablesize was large that it
+ * was a waste of CPU cycles to prepare a cluster list, but
+ * it works out that the Buslogic is faster if you use a smaller
+ * number of segments (i.e. use clustering). I guess it is
+ * inefficient.
+ */
+ unsigned use_clustering:1;
+
+} Scsi_Host_Template;
+
+/*
+ * The scsi_hosts array is the array containing the data for all
+ * possible <supported> scsi hosts. This is similar to the
+ * Scsi_Host_Template, except that we have one entry for each
+ * actual physical host adapter on the system, stored as a linked
+ * list. Note that if there are 2 aha1542 boards, then there will
+ * be two Scsi_Host entries, but only 1 Scsi_Host_Template entry.
+ */
+
+struct Scsi_Host
+{
+ struct Scsi_Host * next;
+ unsigned short extra_bytes;
+ volatile unsigned char host_busy;
+ char host_no; /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */
+ unsigned long last_reset;
+ struct wait_queue *host_wait;
+ Scsi_Cmnd *host_queue;
+ Scsi_Host_Template * hostt;
+
+ /*
+ * These three parameters can be used to allow for wide scsi,
+ * and for host adapters that support multiple busses
+ * The first two should be set to 1 more than the actual max id
+ * or lun (i.e. 8 for normal systems).
+ */
+ unsigned int max_id;
+ unsigned int max_lun;
+ unsigned int max_channel;
+
+ /*
+ * Pointer to a circularly linked list - this indicates the hosts
+ * that should be locked out of performing I/O while we have an active
+ * command on this host.
+ */
+ struct Scsi_Host * block;
+ unsigned wish_block:1;
+
+ /* These parameters should be set by the detect routine */
+ unsigned char *base;
+ unsigned int io_port;
+ unsigned char n_io_port;
+ unsigned char irq;
+ unsigned char dma_channel;
+
+ /*
+ * This is a unique identifier that must be assigned so that we
+ * have some way of identifying each detected host adapter properly
+ * and uniquely. For hosts that do not support more than one card
+ * in the system at one time, this does not need to be set. It is
+ * initialized to 0 in scsi_register.
+ */
+ unsigned int unique_id;
+
+ /*
+ * The rest can be copied from the template, or specifically
+ * initialized, as required.
+ */
+
+ int this_id;
+ int can_queue;
+ short cmd_per_lun;
+ short unsigned int sg_tablesize;
+ unsigned unchecked_isa_dma:1;
+ unsigned use_clustering:1;
+ /*
+ * True if this host was loaded as a loadable module
+ */
+ unsigned loaded_as_module:1;
+
+ void (*select_queue_depths)(struct Scsi_Host *, Scsi_Device *);
+
+ unsigned long hostdata[0]; /* Used for storage of host specific stuff */
+};
+
+extern struct Scsi_Host * scsi_hostlist;
+extern struct Scsi_Device_Template * scsi_devicelist;
+
+extern Scsi_Host_Template * scsi_hosts;
+
+extern void build_proc_dir_entries(Scsi_Host_Template *);
+
+
+/*
+ * scsi_init initializes the scsi hosts.
+ */
+
+/*
+ * We use these goofy things because the MM is not set up when we init
+ * the scsi subsystem. By using these functions we can write code that
+ * looks normal. Also, it makes it possible to use the same code for a
+ * loadable module.
+ */
+
+extern void * scsi_init_malloc(unsigned int size, int priority);
+extern void scsi_init_free(char * ptr, unsigned int size);
+
+extern int next_scsi_host;
+
+extern int scsi_loadable_module_flag;
+unsigned int scsi_init(void);
+extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int j);
+extern void scsi_unregister(struct Scsi_Host * i);
+
+#define BLANK_HOST {"", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+struct Scsi_Device_Template
+{
+ struct Scsi_Device_Template * next;
+ const char * name;
+ const char * tag;
+ long * usage_count; /* Used for loadable modules */
+ unsigned char scsi_type;
+ unsigned char major;
+ unsigned char nr_dev; /* Number currently attached */
+ unsigned char dev_noticed; /* Number of devices detected. */
+ unsigned char dev_max; /* Current size of arrays */
+ unsigned blk:1; /* 0 if character device */
+ int (*detect)(Scsi_Device *); /* Returns 1 if we can attach this device */
+ int (*init)(void); /* Sizes arrays based upon number of devices
+ * detected */
+ void (*finish)(void); /* Perform initialization after attachment */
+ int (*attach)(Scsi_Device *); /* Attach devices to arrays */
+ void (*detach)(Scsi_Device *);
+};
+
+extern struct Scsi_Device_Template sd_template;
+extern struct Scsi_Device_Template st_template;
+extern struct Scsi_Device_Template sr_template;
+extern struct Scsi_Device_Template sg_template;
+
+int scsi_register_device(struct Scsi_Device_Template * sdpnt);
+
+/* These are used by loadable modules */
+extern int scsi_register_module(int, void *);
+extern void scsi_unregister_module(int, void *);
+
+/* The different types of modules that we can load and unload */
+#define MODULE_SCSI_HA 1
+#define MODULE_SCSI_CONST 2
+#define MODULE_SCSI_IOCTL 3
+#define MODULE_SCSI_DEV 4
+
+
+/*
+ * This is an ugly hack. If we expect to be able to load devices at run time,
+ * we need to leave extra room in some of the data structures. Doing a
+ * realloc to enlarge the structures would be riddled with race conditions,
+ * so until a better solution is discovered, we use this crude approach
+ */
+#define SD_EXTRA_DEVS 2
+#define ST_EXTRA_DEVS 2
+#define SR_EXTRA_DEVS 2
+#define SG_EXTRA_DEVS (SD_EXTRA_DEVS + SR_EXTRA_DEVS + ST_EXTRA_DEVS)
+
+#endif
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/in2000.c b/linux/src/drivers/scsi/in2000.c
new file mode 100644
index 00000000..04ecc151
--- /dev/null
+++ b/linux/src/drivers/scsi/in2000.c
@@ -0,0 +1,2376 @@
+/*
+ * in2000.c - Linux device driver for the
+ * Always IN2000 ISA SCSI card.
+ *
+ * Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ * john@geolog.com
+ * jshiffle@netcom.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * Drew Eckhardt's excellent 'Generic NCR5380' sources provided
+ * much of the inspiration and some of the code for this driver.
+ * The Linux IN2000 driver distributed in the Linux kernels through
+ * version 1.2.13 was an extremely valuable reference on the arcane
+ * (and still mysterious) workings of the IN2000's fifo. It also
+ * is where I lifted in2000_biosparam(), the gist of the card
+ * detection scheme, and other bits of code. Many thanks to the
+ * talented and courageous people who wrote, contributed to, and
+ * maintained that driver (including Brad McLean, Shaun Savage,
+ * Bill Earnest, Larry Doolittle, Roger Sunshine, John Luckey,
+ * Matt Postiff, Peter Lu, zerucha@shell.portal.com, and Eric
+ * Youngdale). I should also mention the driver written by
+ * Hamish Macdonald for the (GASP!) Amiga A2091 card, included
+ * in the Linux-m68k distribution; it gave me a good initial
+ * understanding of the proper way to run a WD33c93 chip, and I
+ * ended up stealing lots of code from it.
+ *
+ * _This_ driver is (I feel) an improvement over the old one in
+ * several respects:
+ * - All problems relating to the data size of a SCSI request are
+ * gone (as far as I know). The old driver couldn't handle
+ * swapping to partitions because that involved 4k blocks, nor
+ * could it deal with the st.c tape driver unmodified, because
+ * that usually involved 4k - 32k blocks. The old driver never
+ * quite got away from a morbid dependence on 2k block sizes -
+ * which of course is the size of the card's fifo.
+ *
+ * - Target Disconnection/Reconnection is now supported. Any
+ * system with more than one device active on the SCSI bus
+ * will benefit from this. The driver defaults to what I'm
+ * calling 'adaptive disconnect' - meaning that each command
+ * is evaluated individually as to whether or not it should
+ * be run with the option to disconnect/reselect (if the
+ * device chooses), or as a "SCSI-bus-hog".
+ *
+ * - Synchronous data transfers are now supported. Because there
+ * are a few devices (and many improperly terminated systems)
+ * that choke when doing sync, the default is sync DISABLED
+ * for all devices. This faster protocol can (and should!)
+ * be enabled on selected devices via the command-line.
+ *
+ * - Runtime operating parameters can now be specified through
+ * either the LILO or the 'insmod' command line. For LILO do:
+ * "in2000=blah,blah,blah"
+ * and with insmod go like:
+ * "insmod /usr/src/linux/modules/in2000.o setup_strings=blah,blah"
+ * The defaults should be good for most people. See the comment
+ * for 'setup_strings' below for more details.
+ *
+ * - The old driver relied exclusively on what the Western Digital
+ * docs call "Combination Level 2 Commands", which are a great
+ * idea in that the CPU is relieved of a lot of interrupt
+ * overhead. However, by accepting a certain (user-settable)
+ * amount of additional interrupts, this driver achieves
+ * better control over the SCSI bus, and data transfers are
+ * almost as fast while being much easier to define, track,
+ * and debug.
+ *
+ * - You can force detection of a card whose BIOS has been disabled.
+ *
+ * - Multiple IN2000 cards might almost be supported. I've tried to
+ * keep it in mind, but have no way to test...
+ *
+ *
+ * TODO:
+ * tagged queuing. multiple cards.
+ *
+ *
+ * NOTE:
+ * When using this or any other SCSI driver as a module, you'll
+ * find that with the stock kernel, at most _two_ SCSI hard
+ * drives will be linked into the device list (ie, usable).
+ * If your IN2000 card has more than 2 disks on its bus, you
+ * might want to change the define of 'SD_EXTRA_DEVS' in the
+ * 'hosts.h' file from 2 to whatever is appropriate. It took
+ * me a while to track down this surprisingly obscure and
+ * undocumented little "feature".
+ *
+ *
+ * People with bug reports, wish-lists, complaints, comments,
+ * or improvements are asked to pah-leeez email me (John Shifflett)
+ * at john@geolog.com or jshiffle@netcom.com! I'm anxious to get
+ * this thing into as good a shape as possible, and I'm positive
+ * there are lots of lurking bugs and "Stupid Places".
+ *
+ */
+
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+
+#include <linux/blk.h>
+#include <linux/stat.h>
+
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
+
+#define IN2000_VERSION "1.33"
+#define IN2000_DATE "26/August/1998"
+
+#include "in2000.h"
+
+
+/*
+ * 'setup_strings' is a single string used to pass operating parameters and
+ * settings from the kernel/module command-line to the driver. 'setup_args[]'
+ * is an array of strings that define the compile-time default values for
+ * these settings. If Linux boots with a LILO or insmod command-line, those
+ * settings are combined with 'setup_args[]'. Note that LILO command-lines
+ * are prefixed with "in2000=" while insmod uses a "setup_strings=" prefix.
+ * The driver recognizes the following keywords (lower case required) and
+ * arguments:
+ *
+ * - ioport:addr -Where addr is IO address of a (usually ROM-less) card.
+ * - noreset -No optional args. Prevents SCSI bus reset at boot time.
+ * - nosync:x -x is a bitmask where the 1st 7 bits correspond with
+ * the 7 possible SCSI devices (bit 0 for device #0, etc).
+ * Set a bit to PREVENT sync negotiation on that device.
+ * The driver default is sync DISABLED on all devices.
+ * - period:ns -ns is the minimum # of nanoseconds in a SCSI data transfer
+ * period. Default is 500; acceptable values are 250 - 1000.
+ * - disconnect:x -x = 0 to never allow disconnects, 2 to always allow them.
+ * x = 1 does 'adaptive' disconnects, which is the default
+ * and generally the best choice.
+ * - debug:x -If 'DEBUGGING_ON' is defined, x is a bitmask that causes
+ * various types of debug output to printed - see the DB_xxx
+ * defines in in2000.h
+ * - proc:x -If 'PROC_INTERFACE' is defined, x is a bitmask that
+ * determines how the /proc interface works and what it
+ * does - see the PR_xxx defines in in2000.h
+ *
+ * Syntax Notes:
+ * - Numeric arguments can be decimal or the '0x' form of hex notation. There
+ * _must_ be a colon between a keyword and its numeric argument, with no
+ * spaces.
+ * - Keywords are separated by commas, no spaces, in the standard kernel
+ * command-line manner.
+ * - A keyword in the 'nth' comma-separated command-line member will overwrite
+ * the 'nth' element of setup_args[]. A blank command-line member (in
+ * other words, a comma with no preceding keyword) will _not_ overwrite
+ * the corresponding setup_args[] element.
+ *
+ * A few LILO examples (for insmod, use 'setup_strings' instead of 'in2000'):
+ * - in2000=ioport:0x220,noreset
+ * - in2000=period:250,disconnect:2,nosync:0x03
+ * - in2000=debug:0x1e
+ * - in2000=proc:3
+ */
+
+/* Normally, no defaults are specified... */
+static char *setup_args[] =
+ {"","","","","","","","",""};
+
+/* filled in by 'insmod' */
+static char *setup_strings = 0;
+
+#ifdef MODULE_PARM
+MODULE_PARM(setup_strings, "s");
+#endif
+
+
+static struct Scsi_Host *instance_list = 0;
+
+
+
+static inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num)
+{
+ write1_io(reg_num,IO_WD_ADDR);
+ return read1_io(IO_WD_DATA);
+}
+
+
+#define READ_AUX_STAT() read1_io(IO_WD_ASR)
+
+
+static inline void write_3393(struct IN2000_hostdata *hostdata, uchar reg_num, uchar value)
+{
+ write1_io(reg_num,IO_WD_ADDR);
+ write1_io(value,IO_WD_DATA);
+}
+
+
+static inline void write_3393_cmd(struct IN2000_hostdata *hostdata, uchar cmd)
+{
+/* while (READ_AUX_STAT() & ASR_CIP)
+ printk("|");*/
+ write1_io(WD_COMMAND,IO_WD_ADDR);
+ write1_io(cmd,IO_WD_DATA);
+}
+
+
+static uchar read_1_byte(struct IN2000_hostdata *hostdata)
+{
+uchar asr, x = 0;
+
+ write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write_3393_cmd(hostdata,WD_CMD_TRANS_INFO|0x80);
+ do {
+ asr = READ_AUX_STAT();
+ if (asr & ASR_DBR)
+ x = read_3393(hostdata,WD_DATA);
+ } while (!(asr & ASR_INT));
+ return x;
+}
+
+
+static void write_3393_count(struct IN2000_hostdata *hostdata, unsigned long value)
+{
+ write1_io(WD_TRANSFER_COUNT_MSB,IO_WD_ADDR);
+ write1_io((value >> 16),IO_WD_DATA);
+ write1_io((value >> 8),IO_WD_DATA);
+ write1_io(value,IO_WD_DATA);
+}
+
+
+static unsigned long read_3393_count(struct IN2000_hostdata *hostdata)
+{
+unsigned long value;
+
+ write1_io(WD_TRANSFER_COUNT_MSB,IO_WD_ADDR);
+ value = read1_io(IO_WD_DATA) << 16;
+ value |= read1_io(IO_WD_DATA) << 8;
+ value |= read1_io(IO_WD_DATA);
+ return value;
+}
+
+
+/* The 33c93 needs to be told which direction a command transfers its
+ * data; we use this function to figure it out. Returns true if there
+ * will be a DATA_OUT phase with this command, false otherwise.
+ * (Thanks to Joerg Dorchain for the research and suggestion.)
+ */
+static int is_dir_out(Scsi_Cmnd *cmd)
+{
+ switch (cmd->cmnd[0]) {
+ case WRITE_6: case WRITE_10: case WRITE_12:
+ case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER:
+ case WRITE_VERIFY: case WRITE_VERIFY_12:
+ case COMPARE: case COPY: case COPY_VERIFY:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12:
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT:
+ case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK:
+ case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG:
+ case 0xea:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+
+static struct sx_period sx_table[] = {
+ { 1, 0x20},
+ {252, 0x20},
+ {376, 0x30},
+ {500, 0x40},
+ {624, 0x50},
+ {752, 0x60},
+ {876, 0x70},
+ {1000,0x00},
+ {0, 0} };
+
+static int round_period(unsigned int period)
+{
+int x;
+
+ for (x=1; sx_table[x].period_ns; x++) {
+ if ((period <= sx_table[x-0].period_ns) &&
+ (period > sx_table[x-1].period_ns)) {
+ return x;
+ }
+ }
+ return 7;
+}
+
+static uchar calc_sync_xfer(unsigned int period, unsigned int offset)
+{
+uchar result;
+
+ period *= 4; /* convert SDTR code to ns */
+ result = sx_table[round_period(period)].reg_value;
+ result |= (offset < OPTIMUM_SX_OFF)?offset:OPTIMUM_SX_OFF;
+ return result;
+}
+
+
+
+static void in2000_execute(struct Scsi_Host *instance);
+
+int in2000_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+struct IN2000_hostdata *hostdata;
+Scsi_Cmnd *tmp;
+unsigned long flags;
+
+ hostdata = (struct IN2000_hostdata *)cmd->host->hostdata;
+
+DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid))
+
+/* Set up a few fields in the Scsi_Cmnd structure for our own use:
+ * - host_scribble is the pointer to the next cmd in the input queue
+ * - scsi_done points to the routine we call when a cmd is finished
+ * - result is what you'd expect
+ */
+
+ cmd->host_scribble = NULL;
+ cmd->scsi_done = done;
+ cmd->result = 0;
+
+/* We use the Scsi_Pointer structure that's included with each command
+ * as a scratchpad (as it's intended to be used!). The handy thing about
+ * the SCp.xxx fields is that they're always associated with a given
+ * cmd, and are preserved across disconnect-reselect. This means we
+ * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages
+ * if we keep all the critical pointers and counters in SCp:
+ * - SCp.ptr is the pointer into the RAM buffer
+ * - SCp.this_residual is the size of that buffer
+ * - SCp.buffer points to the current scatter-gather buffer
+ * - SCp.buffers_residual tells us how many S.G. buffers there are
+ * - SCp.have_data_in helps keep track of >2048 byte transfers
+ * - SCp.sent_command is not used
+ * - SCp.phase records this command's SRCID_ER bit setting
+ */
+
+ if (cmd->use_sg) {
+ cmd->SCp.buffer = (struct scatterlist *)cmd->buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.ptr = (char *)cmd->SCp.buffer->address;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ }
+ else {
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *)cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ }
+ cmd->SCp.have_data_in = 0;
+
+/* We don't set SCp.phase here - that's done in in2000_execute() */
+
+/* WD docs state that at the conclusion of a "LEVEL2" command, the
+ * status byte can be retrieved from the LUN register. Apparently,
+ * this is the case only for *uninterrupted* LEVEL2 commands! If
+ * there are any unexpected phases entered, even if they are 100%
+ * legal (different devices may choose to do things differently),
+ * the LEVEL2 command sequence is exited. This often occurs prior
+ * to receiving the status byte, in which case the driver does a
+ * status phase interrupt and gets the status byte on its own.
+ * While such a command can then be "resumed" (ie restarted to
+ * finish up as a LEVEL2 command), the LUN register will NOT be
+ * a valid status byte at the command's conclusion, and we must
+ * use the byte obtained during the earlier interrupt. Here, we
+ * preset SCp.Status to an illegal value (0xff) so that when
+ * this command finally completes, we can tell where the actual
+ * status byte is stored.
+ */
+
+ cmd->SCp.Status = ILLEGAL_STATUS_BYTE;
+
+/* We need to disable interrupts before messing with the input
+ * queue and calling in2000_execute().
+ */
+
+ save_flags(flags);
+ cli();
+
+ /*
+ * Add the cmd to the end of 'input_Q'. Note that REQUEST_SENSE
+ * commands are added to the head of the queue so that the desired
+ * sense data is not lost before REQUEST_SENSE executes.
+ */
+
+ if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ cmd->host_scribble = (uchar *)hostdata->input_Q;
+ hostdata->input_Q = cmd;
+ }
+ else { /* find the end of the queue */
+ for (tmp=(Scsi_Cmnd *)hostdata->input_Q; tmp->host_scribble;
+ tmp=(Scsi_Cmnd *)tmp->host_scribble)
+ ;
+ tmp->host_scribble = (uchar *)cmd;
+ }
+
+/* We know that there's at least one command in 'input_Q' now.
+ * Go see if any of them are runnable!
+ */
+
+ in2000_execute(cmd->host);
+
+DB(DB_QUEUE_COMMAND,printk(")Q-%ld ",cmd->pid))
+
+ restore_flags(flags);
+ return 0;
+}
+
+
+
+/*
+ * This routine attempts to start a scsi command. If the host_card is
+ * already connected, we give up immediately. Otherwise, look through
+ * the input_Q, using the first command we find that's intended
+ * for a currently non-busy target/lun.
+ * Note that this function is always called with interrupts already
+ * disabled (either from in2000_queuecommand() or in2000_intr()).
+ */
+static void in2000_execute (struct Scsi_Host *instance)
+{
+struct IN2000_hostdata *hostdata;
+Scsi_Cmnd *cmd, *prev;
+int i;
+unsigned short *sp;
+unsigned short f;
+unsigned short flushbuf[16];
+
+
+ hostdata = (struct IN2000_hostdata *)instance->hostdata;
+
+DB(DB_EXECUTE,printk("EX("))
+
+ if (hostdata->selecting || hostdata->connected) {
+
+DB(DB_EXECUTE,printk(")EX-0 "))
+
+ return;
+ }
+
+ /*
+ * Search through the input_Q for a command destined
+ * for an idle target/lun.
+ */
+
+ cmd = (Scsi_Cmnd *)hostdata->input_Q;
+ prev = 0;
+ while (cmd) {
+ if (!(hostdata->busy[cmd->target] & (1 << cmd->lun)))
+ break;
+ prev = cmd;
+ cmd = (Scsi_Cmnd *)cmd->host_scribble;
+ }
+
+ /* quit if queue empty or all possible targets are busy */
+
+ if (!cmd) {
+
+DB(DB_EXECUTE,printk(")EX-1 "))
+
+ return;
+ }
+
+ /* remove command from queue */
+
+ if (prev)
+ prev->host_scribble = cmd->host_scribble;
+ else
+ hostdata->input_Q = (Scsi_Cmnd *)cmd->host_scribble;
+
+#ifdef PROC_STATISTICS
+ hostdata->cmd_cnt[cmd->target]++;
+#endif
+
+/*
+ * Start the selection process
+ */
+
+ if (is_dir_out(cmd))
+ write_3393(hostdata,WD_DESTINATION_ID, cmd->target);
+ else
+ write_3393(hostdata,WD_DESTINATION_ID, cmd->target | DSTID_DPD);
+
+/* Now we need to figure out whether or not this command is a good
+ * candidate for disconnect/reselect. We guess to the best of our
+ * ability, based on a set of hierarchical rules. When several
+ * devices are operating simultaneously, disconnects are usually
+ * an advantage. In a single device system, or if only 1 device
+ * is being accessed, transfers usually go faster if disconnects
+ * are not allowed:
+ *
+ * + Commands should NEVER disconnect if hostdata->disconnect =
+ * DIS_NEVER (this holds for tape drives also), and ALWAYS
+ * disconnect if hostdata->disconnect = DIS_ALWAYS.
+ * + Tape drive commands should always be allowed to disconnect.
+ * + Disconnect should be allowed if disconnected_Q isn't empty.
+ * + Commands should NOT disconnect if input_Q is empty.
+ * + Disconnect should be allowed if there are commands in input_Q
+ * for a different target/lun. In this case, the other commands
+ * should be made disconnect-able, if not already.
+ *
+ * I know, I know - this code would flunk me out of any
+ * "C Programming 101" class ever offered. But it's easy
+ * to change around and experiment with for now.
+ */
+
+ cmd->SCp.phase = 0; /* assume no disconnect */
+ if (hostdata->disconnect == DIS_NEVER)
+ goto no;
+ if (hostdata->disconnect == DIS_ALWAYS)
+ goto yes;
+ if (cmd->device->type == 1) /* tape drive? */
+ goto yes;
+ if (hostdata->disconnected_Q) /* other commands disconnected? */
+ goto yes;
+ if (!(hostdata->input_Q)) /* input_Q empty? */
+ goto no;
+ for (prev=(Scsi_Cmnd *)hostdata->input_Q; prev;
+ prev=(Scsi_Cmnd *)prev->host_scribble) {
+ if ((prev->target != cmd->target) || (prev->lun != cmd->lun)) {
+ for (prev=(Scsi_Cmnd *)hostdata->input_Q; prev;
+ prev=(Scsi_Cmnd *)prev->host_scribble)
+ prev->SCp.phase = 1;
+ goto yes;
+ }
+ }
+ goto no;
+
+yes:
+ cmd->SCp.phase = 1;
+
+#ifdef PROC_STATISTICS
+ hostdata->disc_allowed_cnt[cmd->target]++;
+#endif
+
+no:
+ write_3393(hostdata,WD_SOURCE_ID,((cmd->SCp.phase)?SRCID_ER:0));
+
+ write_3393(hostdata,WD_TARGET_LUN, cmd->lun);
+ write_3393(hostdata,WD_SYNCHRONOUS_TRANSFER,hostdata->sync_xfer[cmd->target]);
+ hostdata->busy[cmd->target] |= (1 << cmd->lun);
+
+ if ((hostdata->level2 <= L2_NONE) ||
+ (hostdata->sync_stat[cmd->target] == SS_UNSET)) {
+
+ /*
+ * Do a 'Select-With-ATN' command. This will end with
+ * one of the following interrupts:
+ * CSR_RESEL_AM: failure - can try again later.
+ * CSR_TIMEOUT: failure - give up.
+ * CSR_SELECT: success - proceed.
+ */
+
+ hostdata->selecting = cmd;
+
+/* Every target has its own synchronous transfer setting, kept in
+ * the sync_xfer array, and a corresponding status byte in sync_stat[].
+ * Each target's sync_stat[] entry is initialized to SS_UNSET, and its
+ * sync_xfer[] entry is initialized to the default/safe value. SS_UNSET
+ * means that the parameters are undetermined as yet, and that we
+ * need to send an SDTR message to this device after selection is
+ * complete. We set SS_FIRST to tell the interrupt routine to do so,
+ * unless we don't want to even _try_ synchronous transfers: In this
+ * case we set SS_SET to make the defaults final.
+ */
+ if (hostdata->sync_stat[cmd->target] == SS_UNSET) {
+ if (hostdata->sync_off & (1 << cmd->target))
+ hostdata->sync_stat[cmd->target] = SS_SET;
+ else
+ hostdata->sync_stat[cmd->target] = SS_FIRST;
+ }
+ hostdata->state = S_SELECTING;
+ write_3393_count(hostdata,0); /* this guarantees a DATA_PHASE interrupt */
+ write_3393_cmd(hostdata,WD_CMD_SEL_ATN);
+ }
+
+ else {
+
+ /*
+ * Do a 'Select-With-ATN-Xfer' command. This will end with
+ * one of the following interrupts:
+ * CSR_RESEL_AM: failure - can try again later.
+ * CSR_TIMEOUT: failure - give up.
+ * anything else: success - proceed.
+ */
+
+ hostdata->connected = cmd;
+ write_3393(hostdata,WD_COMMAND_PHASE, 0);
+
+ /* copy command_descriptor_block into WD chip
+ * (take advantage of auto-incrementing)
+ */
+
+ write1_io(WD_CDB_1, IO_WD_ADDR);
+ for (i=0; i<cmd->cmd_len; i++)
+ write1_io(cmd->cmnd[i], IO_WD_DATA);
+
+ /* The wd33c93 only knows about Group 0, 1, and 5 commands when
+ * it's doing a 'select-and-transfer'. To be safe, we write the
+ * size of the CDB into the OWN_ID register for every case. This
+ * way there won't be problems with vendor-unique, audio, etc.
+ */
+
+ write_3393(hostdata, WD_OWN_ID, cmd->cmd_len);
+
+ /* When doing a non-disconnect command, we can save ourselves a DATA
+ * phase interrupt later by setting everything up now. With writes we
+ * need to pre-fill the fifo; if there's room for the 32 flush bytes,
+ * put them in there too - that'll avoid a fifo interrupt. Reads are
+ * somewhat simpler.
+ * KLUDGE NOTE: It seems that you can't completely fill the fifo here:
+ * This results in the IO_FIFO_COUNT register rolling over to zero,
+ * and apparently the gate array logic sees this as empty, not full,
+ * so the 3393 chip is never signalled to start reading from the
+ * fifo. Or maybe it's seen as a permanent fifo interrupt condition.
+ * Regardless, we fix this by temporarily pretending that the fifo
+ * is 16 bytes smaller. (I see now that the old driver has a comment
+ * about "don't fill completely" in an analogous place - must be the
+ * same deal.) This results in CDROM, swap partitions, and tape drives
+ * needing an extra interrupt per write command - I think we can live
+ * with that!
+ */
+
+ if (!(cmd->SCp.phase)) {
+ write_3393_count(hostdata, cmd->SCp.this_residual);
+ write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS);
+ write1_io(0, IO_FIFO_WRITE); /* clear fifo counter, write mode */
+
+ if (is_dir_out(cmd)) {
+ hostdata->fifo = FI_FIFO_WRITING;
+ if ((i = cmd->SCp.this_residual) > (IN2000_FIFO_SIZE - 16) )
+ i = IN2000_FIFO_SIZE - 16;
+ cmd->SCp.have_data_in = i; /* this much data in fifo */
+ i >>= 1; /* Gulp. Assuming modulo 2. */
+ sp = (unsigned short *)cmd->SCp.ptr;
+ f = hostdata->io_base + IO_FIFO;
+
+#ifdef FAST_WRITE_IO
+
+ FAST_WRITE2_IO();
+#else
+ while (i--)
+ write2_io(*sp++,IO_FIFO);
+
+#endif
+
+ /* Is there room for the flush bytes? */
+
+ if (cmd->SCp.have_data_in <= ((IN2000_FIFO_SIZE - 16) - 32)) {
+ sp = flushbuf;
+ i = 16;
+
+#ifdef FAST_WRITE_IO
+
+ FAST_WRITE2_IO();
+#else
+ while (i--)
+ write2_io(0,IO_FIFO);
+
+#endif
+
+ }
+ }
+
+ else {
+ write1_io(0, IO_FIFO_READ); /* put fifo in read mode */
+ hostdata->fifo = FI_FIFO_READING;
+ cmd->SCp.have_data_in = 0; /* nothing transfered yet */
+ }
+
+ }
+ else {
+ write_3393_count(hostdata,0); /* this guarantees a DATA_PHASE interrupt */
+ }
+ hostdata->state = S_RUNNING_LEVEL2;
+ write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+ }
+
+ /*
+ * Since the SCSI bus can handle only 1 connection at a time,
+ * we get out of here now. If the selection fails, or when
+ * the command disconnects, we'll come back to this routine
+ * to search the input_Q again...
+ */
+
+DB(DB_EXECUTE,printk("%s%ld)EX-2 ",(cmd->SCp.phase)?"d:":"",cmd->pid))
+
+}
+
+
+
+static void transfer_pio(uchar *buf, int cnt,
+ int data_in_dir, struct IN2000_hostdata *hostdata)
+{
+uchar asr;
+
+DB(DB_TRANSFER,printk("(%p,%d,%s)",buf,cnt,data_in_dir?"in":"out"))
+
+ write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write_3393_count(hostdata,cnt);
+ write_3393_cmd(hostdata,WD_CMD_TRANS_INFO);
+ if (data_in_dir) {
+ do {
+ asr = READ_AUX_STAT();
+ if (asr & ASR_DBR)
+ *buf++ = read_3393(hostdata,WD_DATA);
+ } while (!(asr & ASR_INT));
+ }
+ else {
+ do {
+ asr = READ_AUX_STAT();
+ if (asr & ASR_DBR)
+ write_3393(hostdata,WD_DATA, *buf++);
+ } while (!(asr & ASR_INT));
+ }
+
+ /* Note: we are returning with the interrupt UN-cleared.
+ * Since (presumably) an entire I/O operation has
+ * completed, the bus phase is probably different, and
+ * the interrupt routine will discover this when it
+ * responds to the uncleared int.
+ */
+
+}
+
+
+
+static void transfer_bytes(Scsi_Cmnd *cmd, int data_in_dir)
+{
+struct IN2000_hostdata *hostdata;
+unsigned short *sp;
+unsigned short f;
+int i;
+
+ hostdata = (struct IN2000_hostdata *)cmd->host->hostdata;
+
+/* Normally, you'd expect 'this_residual' to be non-zero here.
+ * In a series of scatter-gather transfers, however, this
+ * routine will usually be called with 'this_residual' equal
+ * to 0 and 'buffers_residual' non-zero. This means that a
+ * previous transfer completed, clearing 'this_residual', and
+ * now we need to setup the next scatter-gather buffer as the
+ * source or destination for THIS transfer.
+ */
+ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+ ++cmd->SCp.buffer;
+ --cmd->SCp.buffers_residual;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = cmd->SCp.buffer->address;
+ }
+
+/* Set up hardware registers */
+
+ write_3393(hostdata,WD_SYNCHRONOUS_TRANSFER,hostdata->sync_xfer[cmd->target]);
+ write_3393_count(hostdata,cmd->SCp.this_residual);
+ write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS);
+ write1_io(0,IO_FIFO_WRITE); /* zero counter, assume write */
+
+/* Reading is easy. Just issue the command and return - we'll
+ * get an interrupt later when we have actual data to worry about.
+ */
+
+ if (data_in_dir) {
+ write1_io(0,IO_FIFO_READ);
+ if ((hostdata->level2 >= L2_DATA) ||
+ (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
+ write_3393(hostdata,WD_COMMAND_PHASE,0x45);
+ write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+ hostdata->state = S_RUNNING_LEVEL2;
+ }
+ else
+ write_3393_cmd(hostdata,WD_CMD_TRANS_INFO);
+ hostdata->fifo = FI_FIFO_READING;
+ cmd->SCp.have_data_in = 0;
+ return;
+ }
+
+/* Writing is more involved - we'll start the WD chip and write as
+ * much data to the fifo as we can right now. Later interrupts will
+ * write any bytes that don't make it at this stage.
+ */
+
+ if ((hostdata->level2 >= L2_DATA) ||
+ (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {
+ write_3393(hostdata,WD_COMMAND_PHASE,0x45);
+ write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+ hostdata->state = S_RUNNING_LEVEL2;
+ }
+ else
+ write_3393_cmd(hostdata,WD_CMD_TRANS_INFO);
+ hostdata->fifo = FI_FIFO_WRITING;
+ sp = (unsigned short *)cmd->SCp.ptr;
+
+ if ((i = cmd->SCp.this_residual) > IN2000_FIFO_SIZE)
+ i = IN2000_FIFO_SIZE;
+ cmd->SCp.have_data_in = i;
+ i >>= 1; /* Gulp. We assume this_residual is modulo 2 */
+ f = hostdata->io_base + IO_FIFO;
+
+#ifdef FAST_WRITE_IO
+
+ FAST_WRITE2_IO();
+#else
+ while (i--)
+ write2_io(*sp++,IO_FIFO);
+
+#endif
+
+}
+
+
+/* We need to use spin_lock_irqsave() & spin_unlock_irqrestore() in this
+ * function in order to work in an SMP environment. (I'd be surprised
+ * if the driver is ever used by anyone on a real multi-CPU motherboard,
+ * but it _does_ need to be able to compile and run in an SMP kernel.)
+ */
+
+static void in2000_intr (int irqnum, void * dev_id, struct pt_regs *ptregs)
+{
+struct Scsi_Host *instance;
+struct IN2000_hostdata *hostdata;
+Scsi_Cmnd *patch, *cmd;
+uchar asr, sr, phs, id, lun, *ucp, msg;
+int i,j;
+unsigned long length;
+unsigned short *sp;
+unsigned short f;
+unsigned long flags;
+
+ for (instance = instance_list; instance; instance = instance->next) {
+ if (instance->irq == irqnum)
+ break;
+ }
+ if (!instance) {
+ printk("*** Hmm... interrupts are screwed up! ***\n");
+ return;
+ }
+ hostdata = (struct IN2000_hostdata *)instance->hostdata;
+
+/* Get the spin_lock and disable further ints, for SMP */
+
+ CLISPIN_LOCK(flags);
+
+#ifdef PROC_STATISTICS
+ hostdata->int_cnt++;
+#endif
+
+/* The IN2000 card has 2 interrupt sources OR'ed onto its IRQ line - the
+ * WD3393 chip and the 2k fifo (which is actually a dual-port RAM combined
+ * with a big logic array, so it's a little different than what you might
+ * expect). As far as I know, there's no reason that BOTH can't be active
+ * at the same time, but there's a problem: while we can read the 3393
+ * to tell if _it_ wants an interrupt, I don't know of a way to ask the
+ * fifo the same question. The best we can do is check the 3393 and if
+ * it _isn't_ the source of the interrupt, then we can be pretty sure
+ * that the fifo is the culprit.
+ * UPDATE: I have it on good authority (Bill Earnest) that bit 0 of the
+ * IO_FIFO_COUNT register mirrors the fifo interrupt state. I
+ * assume that bit clear means interrupt active. As it turns
+ * out, the driver really doesn't need to check for this after
+ * all, so my remarks above about a 'problem' can safely be
+ * ignored. The way the logic is set up, there's no advantage
+ * (that I can see) to worrying about it.
+ *
+ * It seems that the fifo interrupt signal is negated when we extract
+ * bytes during read or write bytes during write.
+ * - fifo will interrupt when data is moving from it to the 3393, and
+ * there are 31 (or less?) bytes left to go. This is sort of short-
+ * sighted: what if you don't WANT to do more? In any case, our
+ * response is to push more into the fifo - either actual data or
+ * dummy bytes if need be. Note that we apparently have to write at
+ * least 32 additional bytes to the fifo after an interrupt in order
+ * to get it to release the ones it was holding on to - writing fewer
+ * than 32 will result in another fifo int.
+ * UPDATE: Again, info from Bill Earnest makes this more understandable:
+ * 32 bytes = two counts of the fifo counter register. He tells
+ * me that the fifo interrupt is a non-latching signal derived
+ * from a straightforward boolean interpretation of the 7
+ * highest bits of the fifo counter and the fifo-read/fifo-write
+ * state. Who'd a thought?
+ */
+
+ write1_io(0, IO_LED_ON);
+ asr = READ_AUX_STAT();
+ if (!(asr & ASR_INT)) { /* no WD33c93 interrupt? */
+
+/* Ok. This is definitely a FIFO-only interrupt.
+ *
+ * If FI_FIFO_READING is set, there are up to 2048 bytes waiting to be read,
+ * maybe more to come from the SCSI bus. Read as many as we can out of the
+ * fifo and into memory at the location of SCp.ptr[SCp.have_data_in], and
+ * update have_data_in afterwards.
+ *
+ * If we have FI_FIFO_WRITING, the FIFO has almost run out of bytes to move
+ * into the WD3393 chip (I think the interrupt happens when there are 31
+ * bytes left, but it may be fewer...). The 3393 is still waiting, so we
+ * shove some more into the fifo, which gets things moving again. If the
+ * original SCSI command specified more than 2048 bytes, there may still
+ * be some of that data left: fine - use it (from SCp.ptr[SCp.have_data_in]).
+ * Don't forget to update have_data_in. If we've already written out the
+ * entire buffer, feed 32 dummy bytes to the fifo - they're needed to
+ * push out the remaining real data.
+ * (Big thanks to Bill Earnest for getting me out of the mud in here.)
+ */
+
+ cmd = (Scsi_Cmnd *)hostdata->connected; /* assume we're connected */
+CHECK_NULL(cmd,"fifo_int")
+
+ if (hostdata->fifo == FI_FIFO_READING) {
+
+DB(DB_FIFO,printk("{R:%02x} ",read1_io(IO_FIFO_COUNT)))
+
+ sp = (unsigned short *)(cmd->SCp.ptr + cmd->SCp.have_data_in);
+ i = read1_io(IO_FIFO_COUNT) & 0xfe;
+ i <<= 2; /* # of words waiting in the fifo */
+ f = hostdata->io_base + IO_FIFO;
+
+#ifdef FAST_READ_IO
+
+ FAST_READ2_IO();
+#else
+ while (i--)
+ *sp++ = read2_io(IO_FIFO);
+
+#endif
+
+ i = sp - (unsigned short *)(cmd->SCp.ptr + cmd->SCp.have_data_in);
+ i <<= 1;
+ cmd->SCp.have_data_in += i;
+ }
+
+ else if (hostdata->fifo == FI_FIFO_WRITING) {
+
+DB(DB_FIFO,printk("{W:%02x} ",read1_io(IO_FIFO_COUNT)))
+
+/* If all bytes have been written to the fifo, flush out the stragglers.
+ * Note that while writing 16 dummy words seems arbitrary, we don't
+ * have another choice that I can see. What we really want is to read
+ * the 3393 transfer count register (that would tell us how many bytes
+ * needed flushing), but the TRANSFER_INFO command hasn't completed
+ * yet (not enough bytes!) and that register won't be accessible. So,
+ * we use 16 words - a number obtained through trial and error.
+ * UPDATE: Bill says this is exactly what Always does, so there.
+ * More thanks due him for help in this section.
+ */
+
+ if (cmd->SCp.this_residual == cmd->SCp.have_data_in) {
+ i = 16;
+ while (i--) /* write 32 dummy bytes */
+ write2_io(0,IO_FIFO);
+ }
+
+/* If there are still bytes left in the SCSI buffer, write as many as we
+ * can out to the fifo.
+ */
+
+ else {
+ sp = (unsigned short *)(cmd->SCp.ptr + cmd->SCp.have_data_in);
+ i = cmd->SCp.this_residual - cmd->SCp.have_data_in; /* bytes yet to go */
+ j = read1_io(IO_FIFO_COUNT) & 0xfe;
+ j <<= 2; /* how many words the fifo has room for */
+ if ((j << 1) > i)
+ j = (i >> 1);
+ while (j--)
+ write2_io(*sp++,IO_FIFO);
+
+ i = sp - (unsigned short *)(cmd->SCp.ptr + cmd->SCp.have_data_in);
+ i <<= 1;
+ cmd->SCp.have_data_in += i;
+ }
+ }
+
+ else {
+ printk("*** Spurious FIFO interrupt ***");
+ }
+
+ write1_io(0, IO_LED_OFF);
+
+/* release the SMP spin_lock and restore irq state */
+ CLISPIN_UNLOCK(flags);
+ return;
+ }
+
+/* This interrupt was triggered by the WD33c93 chip. The fifo interrupt
+ * may also be asserted, but we don't bother to check it: we get more
+ * detailed info from FIFO_READING and FIFO_WRITING (see below).
+ */
+
+ cmd = (Scsi_Cmnd *)hostdata->connected; /* assume we're connected */
+ sr = read_3393(hostdata,WD_SCSI_STATUS); /* clear the interrupt */
+ phs = read_3393(hostdata,WD_COMMAND_PHASE);
+
+ if (!cmd && (sr != CSR_RESEL_AM && sr != CSR_TIMEOUT && sr != CSR_SELECT)) {
+ printk("\nNR:wd-intr-1\n");
+ write1_io(0, IO_LED_OFF);
+
+/* release the SMP spin_lock and restore irq state */
+ CLISPIN_UNLOCK(flags);
+ return;
+ }
+
+DB(DB_INTR,printk("{%02x:%02x-",asr,sr))
+
+/* After starting a FIFO-based transfer, the next _WD3393_ interrupt is
+ * guaranteed to be in response to the completion of the transfer.
+ * If we were reading, there's probably data in the fifo that needs
+ * to be copied into RAM - do that here. Also, we have to update
+ * 'this_residual' and 'ptr' based on the contents of the
+ * TRANSFER_COUNT register, in case the device decided to do an
+ * intermediate disconnect (a device may do this if it has to
+ * do a seek, or just to be nice and let other devices have
+ * some bus time during long transfers).
+ * After doing whatever is necessary with the fifo, we go on and
+ * service the WD3393 interrupt normally.
+ */
+
+ if (hostdata->fifo == FI_FIFO_READING) {
+
+/* buffer index = start-of-buffer + #-of-bytes-already-read */
+
+ sp = (unsigned short *)(cmd->SCp.ptr + cmd->SCp.have_data_in);
+
+/* bytes remaining in fifo = (total-wanted - #-not-got) - #-already-read */
+
+ i = (cmd->SCp.this_residual - read_3393_count(hostdata)) - cmd->SCp.have_data_in;
+ i >>= 1; /* Gulp. We assume this will always be modulo 2 */
+ f = hostdata->io_base + IO_FIFO;
+
+#ifdef FAST_READ_IO
+
+ FAST_READ2_IO();
+#else
+ while (i--)
+ *sp++ = read2_io(IO_FIFO);
+
+#endif
+
+ hostdata->fifo = FI_FIFO_UNUSED;
+ length = cmd->SCp.this_residual;
+ cmd->SCp.this_residual = read_3393_count(hostdata);
+ cmd->SCp.ptr += (length - cmd->SCp.this_residual);
+
+DB(DB_TRANSFER,printk("(%p,%d)",cmd->SCp.ptr,cmd->SCp.this_residual))
+
+ }
+
+ else if (hostdata->fifo == FI_FIFO_WRITING) {
+ hostdata->fifo = FI_FIFO_UNUSED;
+ length = cmd->SCp.this_residual;
+ cmd->SCp.this_residual = read_3393_count(hostdata);
+ cmd->SCp.ptr += (length - cmd->SCp.this_residual);
+
+DB(DB_TRANSFER,printk("(%p,%d)",cmd->SCp.ptr,cmd->SCp.this_residual))
+
+ }
+
+/* Respond to the specific WD3393 interrupt - there are quite a few! */
+
+ switch (sr) {
+
+ case CSR_TIMEOUT:
+DB(DB_INTR,printk("TIMEOUT"))
+
+ if (hostdata->state == S_RUNNING_LEVEL2)
+ hostdata->connected = NULL;
+ else {
+ cmd = (Scsi_Cmnd *)hostdata->selecting; /* get a valid cmd */
+CHECK_NULL(cmd,"csr_timeout")
+ hostdata->selecting = NULL;
+ }
+
+ cmd->result = DID_NO_CONNECT << 16;
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+ hostdata->state = S_UNCONNECTED;
+ cmd->scsi_done(cmd);
+
+/* We are not connected to a target - check to see if there
+ * are commands waiting to be executed.
+ */
+
+ in2000_execute(instance);
+ break;
+
+
+/* Note: this interrupt should not occur in a LEVEL2 command */
+
+ case CSR_SELECT:
+DB(DB_INTR,printk("SELECT"))
+ hostdata->connected = cmd = (Scsi_Cmnd *)hostdata->selecting;
+CHECK_NULL(cmd,"csr_select")
+ hostdata->selecting = NULL;
+
+ /* construct an IDENTIFY message with correct disconnect bit */
+
+ hostdata->outgoing_msg[0] = (0x80 | 0x00 | cmd->lun);
+ if (cmd->SCp.phase)
+ hostdata->outgoing_msg[0] |= 0x40;
+
+ if (hostdata->sync_stat[cmd->target] == SS_FIRST) {
+#ifdef SYNC_DEBUG
+printk(" sending SDTR ");
+#endif
+
+ hostdata->sync_stat[cmd->target] = SS_WAITING;
+
+ /* tack on a 2nd message to ask about synchronous transfers */
+
+ hostdata->outgoing_msg[1] = EXTENDED_MESSAGE;
+ hostdata->outgoing_msg[2] = 3;
+ hostdata->outgoing_msg[3] = EXTENDED_SDTR;
+ hostdata->outgoing_msg[4] = OPTIMUM_SX_PER/4;
+ hostdata->outgoing_msg[5] = OPTIMUM_SX_OFF;
+ hostdata->outgoing_len = 6;
+ }
+ else
+ hostdata->outgoing_len = 1;
+
+ hostdata->state = S_CONNECTED;
+ break;
+
+
+ case CSR_XFER_DONE|PHS_DATA_IN:
+ case CSR_UNEXP |PHS_DATA_IN:
+ case CSR_SRV_REQ |PHS_DATA_IN:
+DB(DB_INTR,printk("IN-%d.%d",cmd->SCp.this_residual,cmd->SCp.buffers_residual))
+ transfer_bytes(cmd, DATA_IN_DIR);
+ if (hostdata->state != S_RUNNING_LEVEL2)
+ hostdata->state = S_CONNECTED;
+ break;
+
+
+ case CSR_XFER_DONE|PHS_DATA_OUT:
+ case CSR_UNEXP |PHS_DATA_OUT:
+ case CSR_SRV_REQ |PHS_DATA_OUT:
+DB(DB_INTR,printk("OUT-%d.%d",cmd->SCp.this_residual,cmd->SCp.buffers_residual))
+ transfer_bytes(cmd, DATA_OUT_DIR);
+ if (hostdata->state != S_RUNNING_LEVEL2)
+ hostdata->state = S_CONNECTED;
+ break;
+
+
+/* Note: this interrupt should not occur in a LEVEL2 command */
+
+ case CSR_XFER_DONE|PHS_COMMAND:
+ case CSR_UNEXP |PHS_COMMAND:
+ case CSR_SRV_REQ |PHS_COMMAND:
+DB(DB_INTR,printk("CMND-%02x,%ld",cmd->cmnd[0],cmd->pid))
+ transfer_pio(cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR, hostdata);
+ hostdata->state = S_CONNECTED;
+ break;
+
+
+ case CSR_XFER_DONE|PHS_STATUS:
+ case CSR_UNEXP |PHS_STATUS:
+ case CSR_SRV_REQ |PHS_STATUS:
+DB(DB_INTR,printk("STATUS="))
+
+ cmd->SCp.Status = read_1_byte(hostdata);
+DB(DB_INTR,printk("%02x",cmd->SCp.Status))
+ if (hostdata->level2 >= L2_BASIC) {
+ sr = read_3393(hostdata,WD_SCSI_STATUS); /* clear interrupt */
+ hostdata->state = S_RUNNING_LEVEL2;
+ write_3393(hostdata,WD_COMMAND_PHASE, 0x50);
+ write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+ }
+ else {
+ hostdata->state = S_CONNECTED;
+ }
+ break;
+
+
+ case CSR_XFER_DONE|PHS_MESS_IN:
+ case CSR_UNEXP |PHS_MESS_IN:
+ case CSR_SRV_REQ |PHS_MESS_IN:
+DB(DB_INTR,printk("MSG_IN="))
+
+ msg = read_1_byte(hostdata);
+ sr = read_3393(hostdata,WD_SCSI_STATUS); /* clear interrupt */
+
+ hostdata->incoming_msg[hostdata->incoming_ptr] = msg;
+ if (hostdata->incoming_msg[0] == EXTENDED_MESSAGE)
+ msg = EXTENDED_MESSAGE;
+ else
+ hostdata->incoming_ptr = 0;
+
+ cmd->SCp.Message = msg;
+ switch (msg) {
+
+ case COMMAND_COMPLETE:
+DB(DB_INTR,printk("CCMP-%ld",cmd->pid))
+ write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+ hostdata->state = S_PRE_CMP_DISC;
+ break;
+
+ case SAVE_POINTERS:
+DB(DB_INTR,printk("SDP"))
+ write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+
+ case RESTORE_POINTERS:
+DB(DB_INTR,printk("RDP"))
+ if (hostdata->level2 >= L2_BASIC) {
+ write_3393(hostdata,WD_COMMAND_PHASE, 0x45);
+ write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+ hostdata->state = S_RUNNING_LEVEL2;
+ }
+ else {
+ write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ }
+ break;
+
+ case DISCONNECT:
+DB(DB_INTR,printk("DIS"))
+ cmd->device->disconnect = 1;
+ write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+ hostdata->state = S_PRE_TMP_DISC;
+ break;
+
+ case MESSAGE_REJECT:
+DB(DB_INTR,printk("REJ"))
+#ifdef SYNC_DEBUG
+printk("-REJ-");
+#endif
+ if (hostdata->sync_stat[cmd->target] == SS_WAITING)
+ hostdata->sync_stat[cmd->target] = SS_SET;
+ write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+
+ case EXTENDED_MESSAGE:
+DB(DB_INTR,printk("EXT"))
+
+ ucp = hostdata->incoming_msg;
+
+#ifdef SYNC_DEBUG
+printk("%02x",ucp[hostdata->incoming_ptr]);
+#endif
+ /* Is this the last byte of the extended message? */
+
+ if ((hostdata->incoming_ptr >= 2) &&
+ (hostdata->incoming_ptr == (ucp[1] + 1))) {
+
+ switch (ucp[2]) { /* what's the EXTENDED code? */
+ case EXTENDED_SDTR:
+ id = calc_sync_xfer(ucp[3],ucp[4]);
+ if (hostdata->sync_stat[cmd->target] != SS_WAITING) {
+
+/* A device has sent an unsolicited SDTR message; rather than go
+ * through the effort of decoding it and then figuring out what
+ * our reply should be, we're just gonna say that we have a
+ * synchronous fifo depth of 0. This will result in asynchronous
+ * transfers - not ideal but so much easier.
+ * Actually, this is OK because it assures us that if we don't
+ * specifically ask for sync transfers, we won't do any.
+ */
+
+ write_3393_cmd(hostdata,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;
+ hostdata->outgoing_msg[1] = 3;
+ hostdata->outgoing_msg[2] = EXTENDED_SDTR;
+ hostdata->outgoing_msg[3] = hostdata->default_sx_per/4;
+ hostdata->outgoing_msg[4] = 0;
+ hostdata->outgoing_len = 5;
+ hostdata->sync_xfer[cmd->target] =
+ calc_sync_xfer(hostdata->default_sx_per/4,0);
+ }
+ else {
+ hostdata->sync_xfer[cmd->target] = id;
+ }
+#ifdef SYNC_DEBUG
+printk("sync_xfer=%02x",hostdata->sync_xfer[cmd->target]);
+#endif
+ hostdata->sync_stat[cmd->target] = SS_SET;
+ write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+ case EXTENDED_WDTR:
+ write_3393_cmd(hostdata,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ printk("sending WDTR ");
+ hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;
+ hostdata->outgoing_msg[1] = 2;
+ hostdata->outgoing_msg[2] = EXTENDED_WDTR;
+ hostdata->outgoing_msg[3] = 0; /* 8 bit transfer width */
+ hostdata->outgoing_len = 4;
+ write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+ default:
+ write_3393_cmd(hostdata,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ printk("Rejecting Unknown Extended Message(%02x). ",ucp[2]);
+ hostdata->outgoing_msg[0] = MESSAGE_REJECT;
+ hostdata->outgoing_len = 1;
+ write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+ }
+ hostdata->incoming_ptr = 0;
+ }
+
+ /* We need to read more MESS_IN bytes for the extended message */
+
+ else {
+ hostdata->incoming_ptr++;
+ write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ }
+ break;
+
+ default:
+ printk("Rejecting Unknown Message(%02x) ",msg);
+ write_3393_cmd(hostdata,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ hostdata->outgoing_msg[0] = MESSAGE_REJECT;
+ hostdata->outgoing_len = 1;
+ write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ }
+ break;
+
+
+/* Note: this interrupt will occur only after a LEVEL2 command */
+
+ case CSR_SEL_XFER_DONE:
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+ write_3393(hostdata,WD_SOURCE_ID, SRCID_ER);
+ if (phs == 0x60) {
+DB(DB_INTR,printk("SX-DONE-%ld",cmd->pid))
+ cmd->SCp.Message = COMMAND_COMPLETE;
+ lun = read_3393(hostdata,WD_TARGET_LUN);
+DB(DB_INTR,printk(":%d.%d",cmd->SCp.Status,lun))
+ hostdata->connected = NULL;
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+ hostdata->state = S_UNCONNECTED;
+ if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE)
+ cmd->SCp.Status = lun;
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ cmd->scsi_done(cmd);
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+
+ in2000_execute(instance);
+ }
+ else {
+ printk("%02x:%02x:%02x-%ld: Unknown SEL_XFER_DONE phase!!---",asr,sr,phs,cmd->pid);
+ }
+ break;
+
+
+/* Note: this interrupt will occur only after a LEVEL2 command */
+
+ case CSR_SDP:
+DB(DB_INTR,printk("SDP"))
+ hostdata->state = S_RUNNING_LEVEL2;
+ write_3393(hostdata,WD_COMMAND_PHASE, 0x41);
+ write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+ break;
+
+
+ case CSR_XFER_DONE|PHS_MESS_OUT:
+ case CSR_UNEXP |PHS_MESS_OUT:
+ case CSR_SRV_REQ |PHS_MESS_OUT:
+DB(DB_INTR,printk("MSG_OUT="))
+
+/* To get here, we've probably requested MESSAGE_OUT and have
+ * already put the correct bytes in outgoing_msg[] and filled
+ * in outgoing_len. We simply send them out to the SCSI bus.
+ * Sometimes we get MESSAGE_OUT phase when we're not expecting
+ * it - like when our SDTR message is rejected by a target. Some
+ * targets send the REJECT before receiving all of the extended
+ * message, and then seem to go back to MESSAGE_OUT for a byte
+ * or two. Not sure why, or if I'm doing something wrong to
+ * cause this to happen. Regardless, it seems that sending
+ * NOP messages in these situations results in no harm and
+ * makes everyone happy.
+ */
+
+ if (hostdata->outgoing_len == 0) {
+ hostdata->outgoing_len = 1;
+ hostdata->outgoing_msg[0] = NOP;
+ }
+ transfer_pio(hostdata->outgoing_msg, hostdata->outgoing_len,
+ DATA_OUT_DIR, hostdata);
+DB(DB_INTR,printk("%02x",hostdata->outgoing_msg[0]))
+ hostdata->outgoing_len = 0;
+ hostdata->state = S_CONNECTED;
+ break;
+
+
+ case CSR_UNEXP_DISC:
+
+/* I think I've seen this after a request-sense that was in response
+ * to an error condition, but not sure. We certainly need to do
+ * something when we get this interrupt - the question is 'what?'.
+ * Let's think positively, and assume some command has finished
+ * in a legal manner (like a command that provokes a request-sense),
+ * so we treat it as a normal command-complete-disconnect.
+ */
+
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+ write_3393(hostdata,WD_SOURCE_ID, SRCID_ER);
+ if (cmd == NULL) {
+ printk(" - Already disconnected! ");
+ hostdata->state = S_UNCONNECTED;
+
+/* release the SMP spin_lock and restore irq state */
+ CLISPIN_UNLOCK(flags);
+ return;
+ }
+DB(DB_INTR,printk("UNEXP_DISC-%ld",cmd->pid))
+ hostdata->connected = NULL;
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+ hostdata->state = S_UNCONNECTED;
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ cmd->scsi_done(cmd);
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+
+ in2000_execute(instance);
+ break;
+
+
+ case CSR_DISC:
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+ write_3393(hostdata,WD_SOURCE_ID, SRCID_ER);
+DB(DB_INTR,printk("DISC-%ld",cmd->pid))
+ if (cmd == NULL) {
+ printk(" - Already disconnected! ");
+ hostdata->state = S_UNCONNECTED;
+ }
+ switch (hostdata->state) {
+ case S_PRE_CMP_DISC:
+ hostdata->connected = NULL;
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+ hostdata->state = S_UNCONNECTED;
+DB(DB_INTR,printk(":%d",cmd->SCp.Status))
+ if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ else
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ cmd->scsi_done(cmd);
+ break;
+ case S_PRE_TMP_DISC:
+ case S_RUNNING_LEVEL2:
+ cmd->host_scribble = (uchar *)hostdata->disconnected_Q;
+ hostdata->disconnected_Q = cmd;
+ hostdata->connected = NULL;
+ hostdata->state = S_UNCONNECTED;
+
+#ifdef PROC_STATISTICS
+ hostdata->disc_done_cnt[cmd->target]++;
+#endif
+
+ break;
+ default:
+ printk("*** Unexpected DISCONNECT interrupt! ***");
+ hostdata->state = S_UNCONNECTED;
+ }
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+
+ in2000_execute(instance);
+ break;
+
+
+ case CSR_RESEL_AM:
+DB(DB_INTR,printk("RESEL"))
+
+ /* First we have to make sure this reselection didn't */
+ /* happen during Arbitration/Selection of some other device. */
+ /* If yes, put losing command back on top of input_Q. */
+
+ if (hostdata->level2 <= L2_NONE) {
+
+ if (hostdata->selecting) {
+ cmd = (Scsi_Cmnd *)hostdata->selecting;
+ hostdata->selecting = NULL;
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+ cmd->host_scribble = (uchar *)hostdata->input_Q;
+ hostdata->input_Q = cmd;
+ }
+ }
+
+ else {
+
+ if (cmd) {
+ if (phs == 0x00) {
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+ cmd->host_scribble = (uchar *)hostdata->input_Q;
+ hostdata->input_Q = cmd;
+ }
+ else {
+ printk("---%02x:%02x:%02x-TROUBLE: Intrusive ReSelect!---",asr,sr,phs);
+ while (1)
+ printk("\r");
+ }
+ }
+
+ }
+
+ /* OK - find out which device reselected us. */
+
+ id = read_3393(hostdata,WD_SOURCE_ID);
+ id &= SRCID_MASK;
+
+ /* and extract the lun from the ID message. (Note that we don't
+ * bother to check for a valid message here - I guess this is
+ * not the right way to go, but....)
+ */
+
+ lun = read_3393(hostdata,WD_DATA);
+ if (hostdata->level2 < L2_RESELECT)
+ write_3393_cmd(hostdata,WD_CMD_NEGATE_ACK);
+ lun &= 7;
+
+ /* Now we look for the command that's reconnecting. */
+
+ cmd = (Scsi_Cmnd *)hostdata->disconnected_Q;
+ patch = NULL;
+ while (cmd) {
+ if (id == cmd->target && lun == cmd->lun)
+ break;
+ patch = cmd;
+ cmd = (Scsi_Cmnd *)cmd->host_scribble;
+ }
+
+ /* Hmm. Couldn't find a valid command.... What to do? */
+
+ if (!cmd) {
+ printk("---TROUBLE: target %d.%d not in disconnect queue---",id,lun);
+ break;
+ }
+
+ /* Ok, found the command - now start it up again. */
+
+ if (patch)
+ patch->host_scribble = cmd->host_scribble;
+ else
+ hostdata->disconnected_Q = (Scsi_Cmnd *)cmd->host_scribble;
+ hostdata->connected = cmd;
+
+ /* We don't need to worry about 'initialize_SCp()' or 'hostdata->busy[]'
+ * because these things are preserved over a disconnect.
+ * But we DO need to fix the DPD bit so it's correct for this command.
+ */
+
+ if (is_dir_out(cmd))
+ write_3393(hostdata,WD_DESTINATION_ID,cmd->target);
+ else
+ write_3393(hostdata,WD_DESTINATION_ID,cmd->target | DSTID_DPD);
+ if (hostdata->level2 >= L2_RESELECT) {
+ write_3393_count(hostdata,0); /* we want a DATA_PHASE interrupt */
+ write_3393(hostdata,WD_COMMAND_PHASE, 0x45);
+ write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER);
+ hostdata->state = S_RUNNING_LEVEL2;
+ }
+ else
+ hostdata->state = S_CONNECTED;
+
+DB(DB_INTR,printk("-%ld",cmd->pid))
+ break;
+
+ default:
+ printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--",asr,sr,phs);
+ }
+
+ write1_io(0, IO_LED_OFF);
+
+DB(DB_INTR,printk("} "))
+
+/* release the SMP spin_lock and restore irq state */
+ CLISPIN_UNLOCK(flags);
+
+}
+
+
+
+#define RESET_CARD 0
+#define RESET_CARD_AND_BUS 1
+#define B_FLAG 0x80
+
+static int reset_hardware(struct Scsi_Host *instance, int type)
+{
+struct IN2000_hostdata *hostdata;
+int qt,x;
+unsigned long flags;
+
+ hostdata = (struct IN2000_hostdata *)instance->hostdata;
+
+ write1_io(0, IO_LED_ON);
+ if (type == RESET_CARD_AND_BUS) {
+ write1_io(0,IO_CARD_RESET);
+ x = read1_io(IO_HARDWARE);
+ }
+ x = read_3393(hostdata,WD_SCSI_STATUS); /* clear any WD intrpt */
+ write_3393(hostdata,WD_OWN_ID, instance->this_id |
+ OWNID_EAF | OWNID_RAF | OWNID_FS_8);
+ write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write_3393(hostdata,WD_SYNCHRONOUS_TRANSFER,
+ calc_sync_xfer(hostdata->default_sx_per/4,DEFAULT_SX_OFF));
+ save_flags(flags);
+ cli();
+ write1_io(0,IO_FIFO_WRITE); /* clear fifo counter */
+ write1_io(0,IO_FIFO_READ); /* start fifo out in read mode */
+ write_3393(hostdata,WD_COMMAND, WD_CMD_RESET);
+ while (!(READ_AUX_STAT() & ASR_INT))
+ ; /* wait for RESET to complete */
+
+ x = read_3393(hostdata,WD_SCSI_STATUS); /* clear interrupt */
+ restore_flags(flags);
+ write_3393(hostdata,WD_QUEUE_TAG,0xa5); /* any random number */
+ qt = read_3393(hostdata,WD_QUEUE_TAG);
+ if (qt == 0xa5) {
+ x |= B_FLAG;
+ write_3393(hostdata,WD_QUEUE_TAG,0);
+ }
+ write_3393(hostdata,WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE);
+ write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write1_io(0, IO_LED_OFF);
+ return x;
+}
+
+
+
+int in2000_reset(Scsi_Cmnd *cmd, unsigned int reset_flags)
+{
+unsigned long flags;
+struct Scsi_Host *instance;
+struct IN2000_hostdata *hostdata;
+int x;
+
+ instance = cmd->host;
+ hostdata = (struct IN2000_hostdata *)instance->hostdata;
+
+ printk("scsi%d: Reset. ", instance->host_no);
+ save_flags(flags);
+ cli();
+
+ /* do scsi-reset here */
+
+ reset_hardware(instance, RESET_CARD_AND_BUS);
+ for (x = 0; x < 8; x++) {
+ hostdata->busy[x] = 0;
+ hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF);
+ hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */
+ }
+ hostdata->input_Q = NULL;
+ hostdata->selecting = NULL;
+ hostdata->connected = NULL;
+ hostdata->disconnected_Q = NULL;
+ hostdata->state = S_UNCONNECTED;
+ hostdata->fifo = FI_FIFO_UNUSED;
+ hostdata->incoming_ptr = 0;
+ hostdata->outgoing_len = 0;
+
+ cmd->result = DID_RESET << 16;
+ restore_flags(flags);
+ return 0;
+}
+
+
+
+int in2000_abort (Scsi_Cmnd *cmd)
+{
+struct Scsi_Host *instance;
+struct IN2000_hostdata *hostdata;
+Scsi_Cmnd *tmp, *prev;
+unsigned long flags;
+uchar sr, asr;
+unsigned long timeout;
+
+ save_flags (flags);
+ cli();
+
+ instance = cmd->host;
+ hostdata = (struct IN2000_hostdata *)instance->hostdata;
+
+ printk ("scsi%d: Abort-", instance->host_no);
+ printk("(asr=%02x,count=%ld,resid=%d,buf_resid=%d,have_data=%d,FC=%02x)- ",
+ READ_AUX_STAT(),read_3393_count(hostdata),cmd->SCp.this_residual,cmd->SCp.buffers_residual,
+ cmd->SCp.have_data_in,read1_io(IO_FIFO_COUNT));
+
+/*
+ * Case 1 : If the command hasn't been issued yet, we simply remove it
+ * from the inout_Q.
+ */
+
+ tmp = (Scsi_Cmnd *)hostdata->input_Q;
+ prev = 0;
+ while (tmp) {
+ if (tmp == cmd) {
+ if (prev)
+ prev->host_scribble = cmd->host_scribble;
+ cmd->host_scribble = NULL;
+ cmd->result = DID_ABORT << 16;
+ printk("scsi%d: Abort - removing command %ld from input_Q. ",
+ instance->host_no, cmd->pid);
+ cmd->scsi_done(cmd);
+ restore_flags(flags);
+ return SCSI_ABORT_SUCCESS;
+ }
+ prev = tmp;
+ tmp = (Scsi_Cmnd *)tmp->host_scribble;
+ }
+
+/*
+ * Case 2 : If the command is connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or
+ * issue a reset.
+ *
+ * Timeouts, and therefore aborted commands, will be highly unlikely
+ * and handling them cleanly in this situation would make the common
+ * case of noresets less efficient, and would pollute our code. So,
+ * we fail.
+ */
+
+ if (hostdata->connected == cmd) {
+
+ printk("scsi%d: Aborting connected command %ld - ",
+ instance->host_no, cmd->pid);
+
+ printk("sending wd33c93 ABORT command - ");
+ write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
+ write_3393_cmd(hostdata, WD_CMD_ABORT);
+
+/* Now we have to attempt to flush out the FIFO... */
+
+ printk("flushing fifo - ");
+ timeout = 1000000;
+ do {
+ asr = READ_AUX_STAT();
+ if (asr & ASR_DBR)
+ read_3393(hostdata, WD_DATA);
+ } while (!(asr & ASR_INT) && timeout-- > 0);
+ sr = read_3393(hostdata, WD_SCSI_STATUS);
+ printk("asr=%02x, sr=%02x, %ld bytes un-transferred (timeout=%ld) - ",
+ asr, sr, read_3393_count(hostdata), timeout);
+
+ /*
+ * Abort command processed.
+ * Still connected.
+ * We must disconnect.
+ */
+
+ printk("sending wd33c93 DISCONNECT command - ");
+ write_3393_cmd(hostdata, WD_CMD_DISCONNECT);
+
+ timeout = 1000000;
+ asr = READ_AUX_STAT();
+ while ((asr & ASR_CIP) && timeout-- > 0)
+ asr = READ_AUX_STAT();
+ sr = read_3393(hostdata, WD_SCSI_STATUS);
+ printk("asr=%02x, sr=%02x.",asr,sr);
+
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+ hostdata->connected = NULL;
+ hostdata->state = S_UNCONNECTED;
+ cmd->result = DID_ABORT << 16;
+ cmd->scsi_done(cmd);
+
+ in2000_execute (instance);
+
+ restore_flags(flags);
+ return SCSI_ABORT_SUCCESS;
+ }
+
+/*
+ * Case 3: If the command is currently disconnected from the bus,
+ * we're not going to expend much effort here: Let's just return
+ * an ABORT_SNOOZE and hope for the best...
+ */
+
+ for (tmp=(Scsi_Cmnd *)hostdata->disconnected_Q; tmp;
+ tmp=(Scsi_Cmnd *)tmp->host_scribble)
+ if (cmd == tmp) {
+ restore_flags(flags);
+ printk("Sending ABORT_SNOOZE. ");
+ return SCSI_ABORT_SNOOZE;
+ }
+
+/*
+ * Case 4 : If we reached this point, the command was not found in any of
+ * the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke.
+ */
+
+ in2000_execute (instance);
+
+ restore_flags(flags);
+ printk("scsi%d: warning : SCSI command probably completed successfully"
+ " before abortion. ", instance->host_no);
+ return SCSI_ABORT_NOT_RUNNING;
+}
+
+
+
+#define MAX_IN2000_HOSTS 3
+#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *))
+#define SETUP_BUFFER_SIZE 200
+static char setup_buffer[SETUP_BUFFER_SIZE];
+static char setup_used[MAX_SETUP_ARGS];
+static int done_setup = 0;
+
+in2000__INITFUNC( void in2000_setup (char *str, int *ints) )
+{
+int i;
+char *p1,*p2;
+
+ strncpy(setup_buffer,str,SETUP_BUFFER_SIZE);
+ setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0';
+ p1 = setup_buffer;
+ i = 0;
+ while (*p1 && (i < MAX_SETUP_ARGS)) {
+ p2 = strchr(p1, ',');
+ if (p2) {
+ *p2 = '\0';
+ if (p1 != p2)
+ setup_args[i] = p1;
+ p1 = p2 + 1;
+ i++;
+ }
+ else {
+ setup_args[i] = p1;
+ break;
+ }
+ }
+ for (i=0; i<MAX_SETUP_ARGS; i++)
+ setup_used[i] = 0;
+ done_setup = 1;
+}
+
+
+/* check_setup_args() returns index if key found, 0 if not
+ */
+
+in2000__INITFUNC( static int check_setup_args(char *key, int *flags, int *val, char *buf) )
+{
+int x;
+char *cp;
+
+ for (x=0; x<MAX_SETUP_ARGS; x++) {
+ if (setup_used[x])
+ continue;
+ if (!strncmp(setup_args[x], key, strlen(key)))
+ break;
+ }
+ if (x == MAX_SETUP_ARGS)
+ return 0;
+ setup_used[x] = 1;
+ cp = setup_args[x] + strlen(key);
+ *val = -1;
+ if (*cp != ':')
+ return ++x;
+ cp++;
+ if ((*cp >= '0') && (*cp <= '9')) {
+ *val = simple_strtoul(cp,NULL,0);
+ }
+ return ++x;
+}
+
+
+
+/* The "correct" (ie portable) way to access memory-mapped hardware
+ * such as the IN2000 EPROM and dip switch is through the use of
+ * special macros declared in 'asm/io.h'. We use readb() and readl()
+ * when reading from the card's BIOS area in in2000_detect().
+ */
+static const unsigned int *bios_tab[] in2000__INITDATA = {
+ (unsigned int *)0xc8000,
+ (unsigned int *)0xd0000,
+ (unsigned int *)0xd8000,
+ 0
+ };
+
+static const unsigned short base_tab[] in2000__INITDATA = {
+ 0x220,
+ 0x200,
+ 0x110,
+ 0x100,
+ };
+
+static const int int_tab[] in2000__INITDATA = {
+ 15,
+ 14,
+ 11,
+ 10
+ };
+
+
+in2000__INITFUNC( int in2000_detect(Scsi_Host_Template * tpnt) )
+{
+struct Scsi_Host *instance;
+struct IN2000_hostdata *hostdata;
+int detect_count;
+int bios;
+int x;
+unsigned short base;
+uchar switches;
+uchar hrev;
+int flags;
+int val;
+char buf[32];
+
+/* Thanks to help from Bill Earnest, probing for IN2000 cards is a
+ * pretty straightforward and fool-proof operation. There are 3
+ * possible locations for the IN2000 EPROM in memory space - if we
+ * find a BIOS signature, we can read the dip switch settings from
+ * the byte at BIOS+32 (shadowed in by logic on the card). From 2
+ * of the switch bits we get the card's address in IO space. There's
+ * an image of the dip switch there, also, so we have a way to back-
+ * check that this really is an IN2000 card. Very nifty. Use the
+ * 'ioport:xx' command-line parameter if your BIOS EPROM is absent
+ * or disabled.
+ */
+
+ if (!done_setup && setup_strings)
+ in2000_setup(setup_strings,0);
+
+ detect_count = 0;
+ for (bios = 0; bios_tab[bios]; bios++) {
+ if (check_setup_args("ioport",&flags,&val,buf)) {
+ base = val;
+ switches = ~inb(base + IO_SWITCHES) & 0xff;
+ printk("Forcing IN2000 detection at IOport 0x%x ",base);
+ bios = 2;
+ }
+/*
+ * There have been a couple of BIOS versions with different layouts
+ * for the obvious ID strings. We look for the 2 most common ones and
+ * hope that they cover all the cases...
+ */
+ else if (readl(bios_tab[bios]+0x04) == 0x41564f4e ||
+ readl(bios_tab[bios]+0x0c) == 0x61776c41) {
+ printk("Found IN2000 BIOS at 0x%x ",(unsigned int)bios_tab[bios]);
+
+/* Read the switch image that's mapped into EPROM space */
+
+ switches = ~((readb(bios_tab[bios]+0x08) & 0xff));
+
+/* Find out where the IO space is */
+
+ x = switches & (SW_ADDR0 | SW_ADDR1);
+ base = base_tab[x];
+
+/* Check for the IN2000 signature in IO space. */
+
+ x = ~inb(base + IO_SWITCHES) & 0xff;
+ if (x != switches) {
+ printk("Bad IO signature: %02x vs %02x.\n",x,switches);
+ continue;
+ }
+ }
+ else
+ continue;
+
+/* OK. We have a base address for the IO ports - run a few safety checks */
+
+ if (!(switches & SW_BIT7)) { /* I _think_ all cards do this */
+ printk("There is no IN-2000 SCSI card at IOport 0x%03x!\n",base);
+ continue;
+ }
+
+/* Let's assume any hardware version will work, although the driver
+ * has only been tested on 0x21, 0x22, 0x25, 0x26, and 0x27. We'll
+ * print out the rev number for reference later, but accept them all.
+ */
+
+ hrev = inb(base + IO_HARDWARE);
+
+ /* Bit 2 tells us if interrupts are disabled */
+ if (switches & SW_DISINT) {
+ printk("The IN-2000 SCSI card at IOport 0x%03x ",base);
+ printk("is not configured for interrupt operation!\n");
+ printk("This driver requires an interrupt: cancelling detection.\n");
+ continue;
+ }
+
+/* Ok. We accept that there's an IN2000 at ioaddr 'base'. Now
+ * initialize it.
+ */
+
+ tpnt->proc_dir = &proc_scsi_in2000; /* done more than once? harmless. */
+ detect_count++;
+ instance = scsi_register(tpnt, sizeof(struct IN2000_hostdata));
+ if (!instance_list)
+ instance_list = instance;
+ hostdata = (struct IN2000_hostdata *)instance->hostdata;
+ instance->io_port = hostdata->io_base = base;
+ hostdata->dip_switch = switches;
+ hostdata->hrev = hrev;
+
+ write1_io(0,IO_FIFO_WRITE); /* clear fifo counter */
+ write1_io(0,IO_FIFO_READ); /* start fifo out in read mode */
+ write1_io(0,IO_INTR_MASK); /* allow all ints */
+ x = int_tab[(switches & (SW_INT0 | SW_INT1)) >> SW_INT_SHIFT];
+ if (request_irq(x, in2000_intr, SA_INTERRUPT, "in2000", NULL)) {
+ printk("in2000_detect: Unable to allocate IRQ.\n");
+ detect_count--;
+ continue;
+ }
+ instance->irq = x;
+ instance->n_io_port = 13;
+ request_region(base, 13, "in2000"); /* lock in this IO space for our use */
+
+ for (x = 0; x < 8; x++) {
+ hostdata->busy[x] = 0;
+ hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF);
+ hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */
+#ifdef PROC_STATISTICS
+ hostdata->cmd_cnt[x] = 0;
+ hostdata->disc_allowed_cnt[x] = 0;
+ hostdata->disc_done_cnt[x] = 0;
+#endif
+ }
+ hostdata->input_Q = NULL;
+ hostdata->selecting = NULL;
+ hostdata->connected = NULL;
+ hostdata->disconnected_Q = NULL;
+ hostdata->state = S_UNCONNECTED;
+ hostdata->fifo = FI_FIFO_UNUSED;
+ hostdata->level2 = L2_BASIC;
+ hostdata->disconnect = DIS_ADAPTIVE;
+ hostdata->args = DEBUG_DEFAULTS;
+ hostdata->incoming_ptr = 0;
+ hostdata->outgoing_len = 0;
+ hostdata->default_sx_per = DEFAULT_SX_PER;
+
+/* Older BIOS's had a 'sync on/off' switch - use its setting */
+
+ if (readl(bios_tab[bios]+0x04) == 0x41564f4e && (switches & SW_SYNC_DOS5))
+ hostdata->sync_off = 0x00; /* sync defaults to on */
+ else
+ hostdata->sync_off = 0xff; /* sync defaults to off */
+
+#ifdef PROC_INTERFACE
+ hostdata->proc = PR_VERSION|PR_INFO|PR_STATISTICS|
+ PR_CONNECTED|PR_INPUTQ|PR_DISCQ|
+ PR_STOP;
+#ifdef PROC_STATISTICS
+ hostdata->int_cnt = 0;
+#endif
+#endif
+
+ if (check_setup_args("nosync",&flags,&val,buf))
+ hostdata->sync_off = val;
+
+ if (check_setup_args("period",&flags,&val,buf))
+ hostdata->default_sx_per = sx_table[round_period((unsigned int)val)].period_ns;
+
+ if (check_setup_args("disconnect",&flags,&val,buf)) {
+ if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
+ hostdata->disconnect = val;
+ else
+ hostdata->disconnect = DIS_ADAPTIVE;
+ }
+
+ if (check_setup_args("noreset",&flags,&val,buf))
+ hostdata->args ^= A_NO_SCSI_RESET;
+
+ if (check_setup_args("level2",&flags,&val,buf))
+ hostdata->level2 = val;
+
+ if (check_setup_args("debug",&flags,&val,buf))
+ hostdata->args = (val & DB_MASK);
+
+#ifdef PROC_INTERFACE
+ if (check_setup_args("proc",&flags,&val,buf))
+ hostdata->proc = val;
+#endif
+
+
+ x = reset_hardware(instance,(hostdata->args & A_NO_SCSI_RESET)?RESET_CARD:RESET_CARD_AND_BUS);
+
+ hostdata->microcode = read_3393(hostdata,WD_CDB_1);
+ if (x & 0x01) {
+ if (x & B_FLAG)
+ hostdata->chip = C_WD33C93B;
+ else
+ hostdata->chip = C_WD33C93A;
+ }
+ else
+ hostdata->chip = C_WD33C93;
+
+ printk("dip_switch=%02x irq=%d ioport=%02x floppy=%s sync/DOS5=%s ",
+ (switches & 0x7f),
+ instance->irq,hostdata->io_base,
+ (switches & SW_FLOPPY)?"Yes":"No",
+ (switches & SW_SYNC_DOS5)?"Yes":"No");
+ printk("hardware_ver=%02x chip=%s microcode=%02x\n",
+ hrev,
+ (hostdata->chip==C_WD33C93)?"WD33c93":
+ (hostdata->chip==C_WD33C93A)?"WD33c93A":
+ (hostdata->chip==C_WD33C93B)?"WD33c93B":"unknown",
+ hostdata->microcode);
+#ifdef DEBUGGING_ON
+ printk("setup_args = ");
+ for (x=0; x<MAX_SETUP_ARGS; x++)
+ printk("%s,",setup_args[x]);
+ printk("\n");
+#endif
+ if (hostdata->sync_off == 0xff)
+ printk("Sync-transfer DISABLED on all devices: ENABLE from command-line\n");
+ printk("IN2000 driver version %s - %s\n",IN2000_VERSION,IN2000_DATE);
+ }
+
+ return detect_count;
+}
+
+
+/* NOTE: I lifted this function straight out of the old driver,
+ * and have not tested it. Presumably it does what it's
+ * supposed to do...
+ */
+
+int in2000_biosparam(Disk *disk, kdev_t dev, int *iinfo)
+{
+int size;
+
+ size = disk->capacity;
+ iinfo[0] = 64;
+ iinfo[1] = 32;
+ iinfo[2] = size >> 11;
+
+/* This should approximate the large drive handling that the DOS ASPI manager
+ uses. Drives very near the boundaries may not be handled correctly (i.e.
+ near 2.0 Gb and 4.0 Gb) */
+
+ if (iinfo[2] > 1024) {
+ iinfo[0] = 64;
+ iinfo[1] = 63;
+ iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
+ }
+ if (iinfo[2] > 1024) {
+ iinfo[0] = 128;
+ iinfo[1] = 63;
+ iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
+ }
+ if (iinfo[2] > 1024) {
+ iinfo[0] = 255;
+ iinfo[1] = 63;
+ iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
+ }
+ return 0;
+}
+
+
+
+struct proc_dir_entry proc_scsi_in2000 = {
+ PROC_SCSI_IN2000, 6, "in2000",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+ };
+
+
+int in2000_proc_info(char *buf, char **start, off_t off, int len, int hn, int in)
+{
+
+#ifdef PROC_INTERFACE
+
+char *bp;
+char tbuf[128];
+unsigned long flags;
+struct Scsi_Host *instance;
+struct IN2000_hostdata *hd;
+Scsi_Cmnd *cmd;
+int x,i;
+static int stop = 0;
+
+ for (instance=instance_list; instance; instance=instance->next) {
+ if (instance->host_no == hn)
+ break;
+ }
+ if (!instance) {
+ printk("*** Hmm... Can't find host #%d!\n",hn);
+ return (-ESRCH);
+ }
+ hd = (struct IN2000_hostdata *)instance->hostdata;
+
+/* If 'in' is TRUE we need to _read_ the proc file. We accept the following
+ * keywords (same format as command-line, but only ONE per read):
+ * debug
+ * disconnect
+ * period
+ * resync
+ * proc
+ */
+
+ if (in) {
+ buf[len] = '\0';
+ bp = buf;
+ if (!strncmp(bp,"debug:",6)) {
+ bp += 6;
+ hd->args = simple_strtoul(bp,NULL,0) & DB_MASK;
+ }
+ else if (!strncmp(bp,"disconnect:",11)) {
+ bp += 11;
+ x = simple_strtoul(bp,NULL,0);
+ if (x < DIS_NEVER || x > DIS_ALWAYS)
+ x = DIS_ADAPTIVE;
+ hd->disconnect = x;
+ }
+ else if (!strncmp(bp,"period:",7)) {
+ bp += 7;
+ x = simple_strtoul(bp,NULL,0);
+ hd->default_sx_per = sx_table[round_period((unsigned int)x)].period_ns;
+ }
+ else if (!strncmp(bp,"resync:",7)) {
+ bp += 7;
+ x = simple_strtoul(bp,NULL,0);
+ for (i=0; i<7; i++)
+ if (x & (1<<i))
+ hd->sync_stat[i] = SS_UNSET;
+ }
+ else if (!strncmp(bp,"proc:",5)) {
+ bp += 5;
+ hd->proc = simple_strtoul(bp,NULL,0);
+ }
+ else if (!strncmp(bp,"level2:",7)) {
+ bp += 7;
+ hd->level2 = simple_strtoul(bp,NULL,0);
+ }
+ return len;
+ }
+
+ save_flags(flags);
+ cli();
+ bp = buf;
+ *bp = '\0';
+ if (hd->proc & PR_VERSION) {
+ sprintf(tbuf,"\nVersion %s - %s. Compiled %s %s",
+ IN2000_VERSION,IN2000_DATE,__DATE__,__TIME__);
+ strcat(bp,tbuf);
+ }
+ if (hd->proc & PR_INFO) {
+ sprintf(tbuf,"\ndip_switch=%02x: irq=%d io=%02x floppy=%s sync/DOS5=%s",
+ (hd->dip_switch & 0x7f), instance->irq, hd->io_base,
+ (hd->dip_switch & 0x40)?"Yes":"No",
+ (hd->dip_switch & 0x20)?"Yes":"No");
+ strcat(bp,tbuf);
+ strcat(bp,"\nsync_xfer[] = ");
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%02x",hd->sync_xfer[x]);
+ strcat(bp,tbuf);
+ }
+ strcat(bp,"\nsync_stat[] = ");
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%02x",hd->sync_stat[x]);
+ strcat(bp,tbuf);
+ }
+ }
+#ifdef PROC_STATISTICS
+ if (hd->proc & PR_STATISTICS) {
+ strcat(bp,"\ncommands issued: ");
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%ld",hd->cmd_cnt[x]);
+ strcat(bp,tbuf);
+ }
+ strcat(bp,"\ndisconnects allowed:");
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%ld",hd->disc_allowed_cnt[x]);
+ strcat(bp,tbuf);
+ }
+ strcat(bp,"\ndisconnects done: ");
+ for (x=0; x<7; x++) {
+ sprintf(tbuf,"\t%ld",hd->disc_done_cnt[x]);
+ strcat(bp,tbuf);
+ }
+ sprintf(tbuf,"\ninterrupts: \t%ld",hd->int_cnt);
+ strcat(bp,tbuf);
+ }
+#endif
+ if (hd->proc & PR_CONNECTED) {
+ strcat(bp,"\nconnected: ");
+ if (hd->connected) {
+ cmd = (Scsi_Cmnd *)hd->connected;
+ sprintf(tbuf," %ld-%d:%d(%02x)",
+ cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+ strcat(bp,tbuf);
+ }
+ }
+ if (hd->proc & PR_INPUTQ) {
+ strcat(bp,"\ninput_Q: ");
+ cmd = (Scsi_Cmnd *)hd->input_Q;
+ while (cmd) {
+ sprintf(tbuf," %ld-%d:%d(%02x)",
+ cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+ strcat(bp,tbuf);
+ cmd = (Scsi_Cmnd *)cmd->host_scribble;
+ }
+ }
+ if (hd->proc & PR_DISCQ) {
+ strcat(bp,"\ndisconnected_Q:");
+ cmd = (Scsi_Cmnd *)hd->disconnected_Q;
+ while (cmd) {
+ sprintf(tbuf," %ld-%d:%d(%02x)",
+ cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+ strcat(bp,tbuf);
+ cmd = (Scsi_Cmnd *)cmd->host_scribble;
+ }
+ }
+ if (hd->proc & PR_TEST) {
+ ; /* insert your own custom function here */
+ }
+ strcat(bp,"\n");
+ restore_flags(flags);
+ *start = buf;
+ if (stop) {
+ stop = 0;
+ return 0; /* return 0 to signal end-of-file */
+ }
+ if (off > 0x40000) /* ALWAYS stop after 256k bytes have been read */
+ stop = 1;;
+ if (hd->proc & PR_STOP) /* stop every other time */
+ stop = 1;
+ return strlen(bp);
+
+#else /* PROC_INTERFACE */
+
+ return 0;
+
+#endif /* PROC_INTERFACE */
+
+}
+
+
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = IN2000;
+
+#include "scsi_module.c"
+
+#endif
+
diff --git a/linux/src/drivers/scsi/in2000.h b/linux/src/drivers/scsi/in2000.h
new file mode 100644
index 00000000..2a6ad294
--- /dev/null
+++ b/linux/src/drivers/scsi/in2000.h
@@ -0,0 +1,460 @@
+/*
+ * in2000.h - Linux device driver definitions for the
+ * Always IN2000 ISA SCSI card.
+ *
+ * IMPORTANT: This file is for version 1.33 - 26/Aug/1998
+ *
+ * Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ * john@geolog.com
+ * jshiffle@netcom.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef IN2000_H
+#define IN2000_H
+
+#include <asm/io.h>
+
+#define PROC_INTERFACE /* add code for /proc/scsi/in2000/xxx interface */
+#ifdef PROC_INTERFACE
+#define PROC_STATISTICS /* add code for keeping various real time stats */
+#endif
+
+#define SYNC_DEBUG /* extra info on sync negotiation printed */
+#define DEBUGGING_ON /* enable command-line debugging bitmask */
+#define DEBUG_DEFAULTS 0 /* default bitmask - change from command-line */
+
+#define FAST_READ_IO /* No problems with these on my machine */
+#define FAST_WRITE_IO
+
+#ifdef DEBUGGING_ON
+#define DB(f,a) if (hostdata->args & (f)) a;
+#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */
+#else
+#define DB(f,a)
+#define CHECK_NULL(p,s)
+#endif
+
+#define uchar unsigned char
+
+#define read1_io(a) (inb(hostdata->io_base+(a)))
+#define read2_io(a) (inw(hostdata->io_base+(a)))
+#define write1_io(b,a) (outb((b),hostdata->io_base+(a)))
+#define write2_io(w,a) (outw((w),hostdata->io_base+(a)))
+
+/* These inline assembly defines are derived from a patch
+ * sent to me by Bill Earnest. He's done a lot of very
+ * valuable thinking, testing, and coding during his effort
+ * to squeeze more speed out of this driver. I really think
+ * that we are doing IO at close to the maximum now with
+ * the fifo. (And yes, insw uses 'edi' while outsw uses
+ * 'esi'. Thanks Bill!)
+ */
+
+#define FAST_READ2_IO() \
+ __asm__ __volatile__ ("\n \
+ cld \n \
+ orl %%ecx, %%ecx \n \
+ jz 1f \n \
+ rep \n \
+ insw %%dx \n \
+1: " \
+ : "=D" (sp) /* output */ \
+ : "d" (f), "D" (sp), "c" (i) /* input */ \
+ : "edx", "ecx", "edi" ) /* trashed */
+
+#define FAST_WRITE2_IO() \
+ __asm__ __volatile__ ("\n \
+ cld \n \
+ orl %%ecx, %%ecx \n \
+ jz 1f \n \
+ rep \n \
+ outsw %%dx \n \
+1: " \
+ : "=S" (sp) /* output */ \
+ : "d" (f), "S" (sp), "c" (i) /* input */ \
+ : "edx", "ecx", "esi" ) /* trashed */
+
+
+/* IN2000 io_port offsets */
+#define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */
+#define ASR_INT 0x80
+#define ASR_LCI 0x40
+#define ASR_BSY 0x20
+#define ASR_CIP 0x10
+#define ASR_PE 0x02
+#define ASR_DBR 0x01
+#define IO_WD_ADDR 0x00 /* W - 3393 address reg */
+#define IO_WD_DATA 0x01 /* R/W - rest of 3393 regs */
+#define IO_FIFO 0x02 /* R/W - in2000 dual-port fifo (16 bits) */
+#define IN2000_FIFO_SIZE 2048 /* fifo capacity in bytes */
+#define IO_CARD_RESET 0x03 /* W - in2000 start master reset */
+#define IO_FIFO_COUNT 0x04 /* R - in2000 fifo counter */
+#define IO_FIFO_WRITE 0x05 /* W - clear fifo counter, start write */
+#define IO_FIFO_READ 0x07 /* W - start fifo read */
+#define IO_LED_OFF 0x08 /* W - turn off in2000 activity LED */
+#define IO_SWITCHES 0x08 /* R - read in2000 dip switch */
+#define SW_ADDR0 0x01 /* bit 0 = bit 0 of index to io addr */
+#define SW_ADDR1 0x02 /* bit 1 = bit 1 of index io addr */
+#define SW_DISINT 0x04 /* bit 2 true if ints disabled */
+#define SW_INT0 0x08 /* bit 3 = bit 0 of index to interrupt */
+#define SW_INT1 0x10 /* bit 4 = bit 1 of index to interrupt */
+#define SW_INT_SHIFT 3 /* shift right this amount to right justify int bits */
+#define SW_SYNC_DOS5 0x20 /* bit 5 used by Always BIOS */
+#define SW_FLOPPY 0x40 /* bit 6 true if floppy enabled */
+#define SW_BIT7 0x80 /* bit 7 hardwired true (ground) */
+#define IO_LED_ON 0x09 /* W - turn on in2000 activity LED */
+#define IO_HARDWARE 0x0a /* R - read in2000 hardware rev, stop reset */
+#define IO_INTR_MASK 0x0c /* W - in2000 interrupt mask reg */
+#define IMASK_WD 0x01 /* WD33c93 interrupt mask */
+#define IMASK_FIFO 0x02 /* FIFO interrupt mask */
+
+/* wd register names */
+#define WD_OWN_ID 0x00
+#define WD_CONTROL 0x01
+#define WD_TIMEOUT_PERIOD 0x02
+#define WD_CDB_1 0x03
+#define WD_CDB_2 0x04
+#define WD_CDB_3 0x05
+#define WD_CDB_4 0x06
+#define WD_CDB_5 0x07
+#define WD_CDB_6 0x08
+#define WD_CDB_7 0x09
+#define WD_CDB_8 0x0a
+#define WD_CDB_9 0x0b
+#define WD_CDB_10 0x0c
+#define WD_CDB_11 0x0d
+#define WD_CDB_12 0x0e
+#define WD_TARGET_LUN 0x0f
+#define WD_COMMAND_PHASE 0x10
+#define WD_SYNCHRONOUS_TRANSFER 0x11
+#define WD_TRANSFER_COUNT_MSB 0x12
+#define WD_TRANSFER_COUNT 0x13
+#define WD_TRANSFER_COUNT_LSB 0x14
+#define WD_DESTINATION_ID 0x15
+#define WD_SOURCE_ID 0x16
+#define WD_SCSI_STATUS 0x17
+#define WD_COMMAND 0x18
+#define WD_DATA 0x19
+#define WD_QUEUE_TAG 0x1a
+#define WD_AUXILIARY_STATUS 0x1f
+
+/* WD commands */
+#define WD_CMD_RESET 0x00
+#define WD_CMD_ABORT 0x01
+#define WD_CMD_ASSERT_ATN 0x02
+#define WD_CMD_NEGATE_ACK 0x03
+#define WD_CMD_DISCONNECT 0x04
+#define WD_CMD_RESELECT 0x05
+#define WD_CMD_SEL_ATN 0x06
+#define WD_CMD_SEL 0x07
+#define WD_CMD_SEL_ATN_XFER 0x08
+#define WD_CMD_SEL_XFER 0x09
+#define WD_CMD_RESEL_RECEIVE 0x0a
+#define WD_CMD_RESEL_SEND 0x0b
+#define WD_CMD_WAIT_SEL_RECEIVE 0x0c
+#define WD_CMD_TRANS_ADDR 0x18
+#define WD_CMD_TRANS_INFO 0x20
+#define WD_CMD_TRANSFER_PAD 0x21
+#define WD_CMD_SBT_MODE 0x80
+
+/* SCSI Bus Phases */
+#define PHS_DATA_OUT 0x00
+#define PHS_DATA_IN 0x01
+#define PHS_COMMAND 0x02
+#define PHS_STATUS 0x03
+#define PHS_MESS_OUT 0x06
+#define PHS_MESS_IN 0x07
+
+/* Command Status Register definitions */
+
+ /* reset state interrupts */
+#define CSR_RESET 0x00
+#define CSR_RESET_AF 0x01
+
+ /* successful completion interrupts */
+#define CSR_RESELECT 0x10
+#define CSR_SELECT 0x11
+#define CSR_SEL_XFER_DONE 0x16
+#define CSR_XFER_DONE 0x18
+
+ /* paused or aborted interrupts */
+#define CSR_MSGIN 0x20
+#define CSR_SDP 0x21
+#define CSR_SEL_ABORT 0x22
+#define CSR_RESEL_ABORT 0x25
+#define CSR_RESEL_ABORT_AM 0x27
+#define CSR_ABORT 0x28
+
+ /* terminated interrupts */
+#define CSR_INVALID 0x40
+#define CSR_UNEXP_DISC 0x41
+#define CSR_TIMEOUT 0x42
+#define CSR_PARITY 0x43
+#define CSR_PARITY_ATN 0x44
+#define CSR_BAD_STATUS 0x45
+#define CSR_UNEXP 0x48
+
+ /* service required interrupts */
+#define CSR_RESEL 0x80
+#define CSR_RESEL_AM 0x81
+#define CSR_DISC 0x85
+#define CSR_SRV_REQ 0x88
+
+ /* Own ID/CDB Size register */
+#define OWNID_EAF 0x08
+#define OWNID_EHP 0x10
+#define OWNID_RAF 0x20
+#define OWNID_FS_8 0x00
+#define OWNID_FS_12 0x40
+#define OWNID_FS_16 0x80
+
+ /* Control register */
+#define CTRL_HSP 0x01
+#define CTRL_HA 0x02
+#define CTRL_IDI 0x04
+#define CTRL_EDI 0x08
+#define CTRL_HHP 0x10
+#define CTRL_POLLED 0x00
+#define CTRL_BURST 0x20
+#define CTRL_BUS 0x40
+#define CTRL_DMA 0x80
+
+ /* Timeout Period register */
+#define TIMEOUT_PERIOD_VALUE 20 /* results in 200 ms. */
+
+ /* Synchronous Transfer Register */
+#define STR_FSS 0x80
+
+ /* Destination ID register */
+#define DSTID_DPD 0x40
+#define DATA_OUT_DIR 0
+#define DATA_IN_DIR 1
+#define DSTID_SCC 0x80
+
+ /* Source ID register */
+#define SRCID_MASK 0x07
+#define SRCID_SIV 0x08
+#define SRCID_DSP 0x20
+#define SRCID_ES 0x40
+#define SRCID_ER 0x80
+
+
+
+#define ILLEGAL_STATUS_BYTE 0xff
+
+
+#define DEFAULT_SX_PER 500 /* (ns) fairly safe */
+#define DEFAULT_SX_OFF 0 /* aka async */
+
+#define OPTIMUM_SX_PER 252 /* (ns) best we can do (mult-of-4) */
+#define OPTIMUM_SX_OFF 12 /* size of in2000 fifo */
+
+struct sx_period {
+ unsigned int period_ns;
+ uchar reg_value;
+ };
+
+
+struct IN2000_hostdata {
+ struct Scsi_Host *next;
+ uchar chip; /* what kind of wd33c93 chip? */
+ uchar microcode; /* microcode rev if 'B' */
+ unsigned short io_base; /* IO port base */
+ unsigned int dip_switch; /* dip switch settings */
+ unsigned int hrev; /* hardware revision of card */
+ volatile uchar busy[8]; /* index = target, bit = lun */
+ volatile Scsi_Cmnd *input_Q; /* commands waiting to be started */
+ volatile Scsi_Cmnd *selecting; /* trying to select this command */
+ volatile Scsi_Cmnd *connected; /* currently connected command */
+ volatile Scsi_Cmnd *disconnected_Q;/* commands waiting for reconnect */
+ uchar state; /* what we are currently doing */
+ uchar fifo; /* what the FIFO is up to */
+ uchar level2; /* extent to which Level-2 commands are used */
+ uchar disconnect; /* disconnect/reselect policy */
+ unsigned int args; /* set from command-line argument */
+ uchar incoming_msg[8]; /* filled during message_in phase */
+ int incoming_ptr; /* mainly used with EXTENDED messages */
+ uchar outgoing_msg[8]; /* send this during next message_out */
+ int outgoing_len; /* length of outgoing message */
+ unsigned int default_sx_per; /* default transfer period for SCSI bus */
+ uchar sync_xfer[8]; /* sync_xfer reg settings per target */
+ uchar sync_stat[8]; /* status of sync negotiation per target */
+ uchar sync_off; /* bit mask: don't use sync with these targets */
+#ifdef PROC_INTERFACE
+ uchar proc; /* bit mask: what's in proc output */
+#ifdef PROC_STATISTICS
+ unsigned long cmd_cnt[8]; /* # of commands issued per target */
+ unsigned long int_cnt; /* # of interrupts serviced */
+ unsigned long disc_allowed_cnt[8]; /* # of disconnects allowed per target */
+ unsigned long disc_done_cnt[8]; /* # of disconnects done per target*/
+#endif
+#endif
+ };
+
+
+/* defines for hostdata->chip */
+
+#define C_WD33C93 0
+#define C_WD33C93A 1
+#define C_WD33C93B 2
+#define C_UNKNOWN_CHIP 100
+
+/* defines for hostdata->state */
+
+#define S_UNCONNECTED 0
+#define S_SELECTING 1
+#define S_RUNNING_LEVEL2 2
+#define S_CONNECTED 3
+#define S_PRE_TMP_DISC 4
+#define S_PRE_CMP_DISC 5
+
+/* defines for hostdata->fifo */
+
+#define FI_FIFO_UNUSED 0
+#define FI_FIFO_READING 1
+#define FI_FIFO_WRITING 2
+
+/* defines for hostdata->level2 */
+/* NOTE: only the first 3 are trustworthy at this point -
+ * having trouble when more than 1 device is reading/writing
+ * at the same time...
+ */
+
+#define L2_NONE 0 /* no combination commands - we get lots of ints */
+#define L2_SELECT 1 /* start with SEL_ATN_XFER, but never resume it */
+#define L2_BASIC 2 /* resume after STATUS ints & RDP messages */
+#define L2_DATA 3 /* resume after DATA_IN/OUT ints */
+#define L2_MOST 4 /* resume after anything except a RESELECT int */
+#define L2_RESELECT 5 /* resume after everything, including RESELECT ints */
+#define L2_ALL 6 /* always resume */
+
+/* defines for hostdata->disconnect */
+
+#define DIS_NEVER 0
+#define DIS_ADAPTIVE 1
+#define DIS_ALWAYS 2
+
+/* defines for hostdata->args */
+
+#define DB_TEST 1<<0
+#define DB_FIFO 1<<1
+#define DB_QUEUE_COMMAND 1<<2
+#define DB_EXECUTE 1<<3
+#define DB_INTR 1<<4
+#define DB_TRANSFER 1<<5
+#define DB_MASK 0x3f
+
+#define A_NO_SCSI_RESET 1<<15
+
+
+/* defines for hostdata->sync_xfer[] */
+
+#define SS_UNSET 0
+#define SS_FIRST 1
+#define SS_WAITING 2
+#define SS_SET 3
+
+/* defines for hostdata->proc */
+
+#define PR_VERSION 1<<0
+#define PR_INFO 1<<1
+#define PR_STATISTICS 1<<2
+#define PR_CONNECTED 1<<3
+#define PR_INPUTQ 1<<4
+#define PR_DISCQ 1<<5
+#define PR_TEST 1<<6
+#define PR_STOP 1<<7
+
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < 0x020100 /* 2.0.xx */
+# define in2000__INITFUNC(function) function
+# define in2000__INIT
+# define in2000__INITDATA
+# define CLISPIN_LOCK(flags) do { save_flags(flags); cli(); } while(0)
+# define CLISPIN_UNLOCK(flags) restore_flags(flags)
+#else /* 2.1.xxx */
+# include <linux/init.h>
+# include <asm/spinlock.h>
+# define in2000__INITFUNC(function) __initfunc(function)
+# define in2000__INIT __init
+# define in2000__INITDATA __initdata
+# define CLISPIN_LOCK(flags) spin_lock_irqsave(&io_request_lock, flags)
+# define CLISPIN_UNLOCK(flags) spin_unlock_irqrestore(&io_request_lock, flags)
+#endif
+
+
+int in2000_detect(Scsi_Host_Template *) in2000__INIT;
+int in2000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int in2000_abort(Scsi_Cmnd *);
+void in2000_setup(char *, int *) in2000__INIT;
+int in2000_proc_info(char *, char **, off_t, int, int, int);
+struct proc_dir_entry proc_scsi_in2000;
+int in2000_biosparam(struct scsi_disk *, kdev_t, int *);
+int in2000_reset(Scsi_Cmnd *, unsigned int);
+
+
+#define IN2000_CAN_Q 16
+#define IN2000_SG SG_ALL
+#define IN2000_CPL 2
+#define IN2000_HOST_ID 7
+
+#if LINUX_VERSION_CODE < 0x020100 /* 2.0.xx */
+
+#define IN2000 { NULL, /* link pointer for modules */ \
+ NULL, /* usage_count for modules */ \
+ &proc_scsi_in2000, /* pointer to /proc/scsi directory entry */ \
+ in2000_proc_info, /* pointer to proc info function */ \
+ "Always IN2000", /* device name */ \
+ in2000_detect, /* returns number of in2000's found */ \
+ NULL, /* optional unload function for modules */ \
+ NULL, /* optional misc info function */ \
+ NULL, /* send scsi command, wait for completion */ \
+ in2000_queuecommand, /* queue scsi command, don't wait */ \
+ in2000_abort, /* abort current command */ \
+ in2000_reset, /* reset scsi bus */ \
+ NULL, /* slave_attach - unused */ \
+ in2000_biosparam, /* figures out BIOS parameters for lilo, etc */ \
+ IN2000_CAN_Q, /* max commands we can queue up */ \
+ IN2000_HOST_ID, /* host-adapter scsi id */ \
+ IN2000_SG, /* scatter-gather table size */ \
+ IN2000_CPL, /* commands per lun */ \
+ 0, /* board counter */ \
+ 0, /* unchecked dma */ \
+ DISABLE_CLUSTERING \
+ }
+
+#else /* 2.1.xxx */
+
+#define IN2000 { proc_dir: &proc_scsi_in2000, /* pointer to /proc/scsi directory entry */ \
+ proc_info: in2000_proc_info, /* pointer to proc info function */ \
+ name: "Always IN2000", /* device name */ \
+ detect: in2000_detect, /* returns number of in2000's found */ \
+ queuecommand: in2000_queuecommand, /* queue scsi command, don't wait */ \
+ abort: in2000_abort, /* abort current command */ \
+ reset: in2000_reset, /* reset scsi bus */ \
+ bios_param: in2000_biosparam, /* figures out BIOS parameters for lilo, etc */ \
+ can_queue: IN2000_CAN_Q, /* max commands we can queue up */ \
+ this_id: IN2000_HOST_ID, /* host-adapter scsi id */ \
+ sg_tablesize: IN2000_SG, /* scatter-gather table size */ \
+ cmd_per_lun: IN2000_CPL, /* commands per lun */ \
+ use_clustering: DISABLE_CLUSTERING, /* ENABLE_CLUSTERING may speed things up */ \
+ use_new_eh_code: 0 /* new error code - not using it yet */ \
+ }
+
+#endif
+
+
+#endif /* IN2000_H */
diff --git a/linux/src/drivers/scsi/ncr53c8xx.c b/linux/src/drivers/scsi/ncr53c8xx.c
new file mode 100644
index 00000000..1be3d9fe
--- /dev/null
+++ b/linux/src/drivers/scsi/ncr53c8xx.c
@@ -0,0 +1,10793 @@
+/******************************************************************************
+** Device driver for the PCI-SCSI NCR538XX controller family.
+**
+** Copyright (C) 1994 Wolfgang Stanglmeier
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+**-----------------------------------------------------------------------------
+**
+** This driver has been ported to Linux from the FreeBSD NCR53C8XX driver
+** and is currently maintained by
+**
+** Gerard Roudier <groudier@club-internet.fr>
+**
+** Being given that this driver originates from the FreeBSD version, and
+** in order to keep synergy on both, any suggested enhancements and corrections
+** received on Linux are automatically a potential candidate for the FreeBSD
+** version.
+**
+** The original driver has been written for 386bsd and FreeBSD by
+** Wolfgang Stanglmeier <wolf@cologne.de>
+** Stefan Esser <se@mi.Uni-Koeln.de>
+**
+** And has been ported to NetBSD by
+** Charles M. Hannum <mycroft@gnu.ai.mit.edu>
+**
+**-----------------------------------------------------------------------------
+**
+** Brief history
+**
+** December 10 1995 by Gerard Roudier:
+** Initial port to Linux.
+**
+** June 23 1996 by Gerard Roudier:
+** Support for 64 bits architectures (Alpha).
+**
+** November 30 1996 by Gerard Roudier:
+** Support for Fast-20 scsi.
+** Support for large DMA fifo and 128 dwords bursting.
+**
+** February 27 1997 by Gerard Roudier:
+** Support for Fast-40 scsi.
+** Support for on-Board RAM.
+**
+** May 3 1997 by Gerard Roudier:
+** Full support for scsi scripts instructions pre-fetching.
+**
+** May 19 1997 by Richard Waltham <dormouse@farsrobt.demon.co.uk>:
+** Support for NvRAM detection and reading.
+**
+** August 18 1997 by Cort <cort@cs.nmt.edu>:
+** Support for Power/PC (Big Endian).
+**
+*******************************************************************************
+*/
+
+/*
+** 30 January 1998, version 2.5f.1
+**
+** Supported SCSI-II features:
+** Synchronous negotiation
+** Wide negotiation (depends on the NCR Chip)
+** Enable disconnection
+** Tagged command queuing
+** Parity checking
+** Etc...
+**
+** Supported NCR chips:
+** 53C810 (8 bits, Fast SCSI-2, no rom BIOS)
+** 53C815 (8 bits, Fast SCSI-2, on board rom BIOS)
+** 53C820 (Wide, Fast SCSI-2, no rom BIOS)
+** 53C825 (Wide, Fast SCSI-2, on board rom BIOS)
+** 53C860 (8 bits, Fast 20, no rom BIOS)
+** 53C875 (Wide, Fast 20, on board rom BIOS)
+** 53C895 (Wide, Fast 40, on board rom BIOS)
+**
+** Other features:
+** Memory mapped IO (linux-1.3.X and above only)
+** Module
+** Shared IRQ (since linux-1.3.72)
+*/
+
+#define SCSI_NCR_DEBUG_FLAGS (0)
+
+#define NCR_GETCC_WITHMSG
+
+/*==========================================================
+**
+** Include files
+**
+**==========================================================
+*/
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/stat.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+#include <linux/blk.h>
+#else
+#include "../block/blk.h"
+#endif
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,35)
+#include <linux/init.h>
+#else
+#ifndef __initdata
+#define __initdata
+#endif
+#ifndef __initfunc
+#define __initfunc(__arginit) __arginit
+#endif
+#endif
+
+#include "scsi.h"
+#include "hosts.h"
+#include "constants.h"
+#include "sd.h"
+
+#include <linux/types.h>
+
+/*
+** Define the BSD style u_int32 type
+*/
+typedef u32 u_int32;
+
+#include "ncr53c8xx.h"
+
+/*==========================================================
+**
+** Configuration and Debugging
+**
+**==========================================================
+*/
+
+/*
+** SCSI address of this device.
+** The boot routines should have set it.
+** If not, use this.
+*/
+
+#ifndef SCSI_NCR_MYADDR
+#define SCSI_NCR_MYADDR (7)
+#endif
+
+/*
+** The maximum number of tags per logic unit.
+** Used only for disk devices that support tags.
+*/
+
+#ifndef SCSI_NCR_MAX_TAGS
+#define SCSI_NCR_MAX_TAGS (4)
+#endif
+
+/*
+** Number of targets supported by the driver.
+** n permits target numbers 0..n-1.
+** Default is 7, meaning targets #0..#6.
+** #7 .. is myself.
+*/
+
+#ifdef SCSI_NCR_MAX_TARGET
+#define MAX_TARGET (SCSI_NCR_MAX_TARGET)
+#else
+#define MAX_TARGET (16)
+#endif
+
+/*
+** Number of logic units supported by the driver.
+** n enables logic unit numbers 0..n-1.
+** The common SCSI devices require only
+** one lun, so take 1 as the default.
+*/
+
+#ifdef SCSI_NCR_MAX_LUN
+#define MAX_LUN SCSI_NCR_MAX_LUN
+#else
+#define MAX_LUN (1)
+#endif
+
+/*
+** Asynchronous pre-scaler (ns). Shall be 40
+*/
+
+#ifndef SCSI_NCR_MIN_ASYNC
+#define SCSI_NCR_MIN_ASYNC (40)
+#endif
+
+/*
+** The maximum number of jobs scheduled for starting.
+** There should be one slot per target, and one slot
+** for each tag of each target in use.
+** The calculation below is actually quite silly ...
+*/
+
+#ifdef SCSI_NCR_CAN_QUEUE
+#define MAX_START (SCSI_NCR_CAN_QUEUE + 4)
+#else
+#define MAX_START (MAX_TARGET + 7 * SCSI_NCR_MAX_TAGS)
+#endif
+
+/*
+** The maximum number of segments a transfer is split into.
+*/
+
+#define MAX_SCATTER (SCSI_NCR_MAX_SCATTER)
+
+/*
+** Io mapped or memory mapped.
+*/
+
+#if defined(SCSI_NCR_IOMAPPED)
+#define NCR_IOMAPPED
+#endif
+
+/*
+** other
+*/
+
+#define NCR_SNOOP_TIMEOUT (1000000)
+
+/*==========================================================
+**
+** Defines for Linux.
+**
+** Linux and Bsd kernel functions are quite different.
+** These defines allow a minimum change of the original
+** code.
+**
+**==========================================================
+*/
+
+ /*
+ ** Obvious definitions
+ */
+
+#define printf printk
+#define u_char unsigned char
+#define u_short unsigned short
+#define u_int unsigned int
+#define u_long unsigned long
+
+typedef u_long vm_offset_t;
+typedef int vm_size_t;
+
+#define bcopy(s, d, n) memcpy((d), (s), (n))
+#define bzero(d, n) memset((d), 0, (n))
+
+#ifndef offsetof
+#define offsetof(t, m) ((size_t) (&((t *)0)->m))
+#endif
+
+/*
+** Address translation
+**
+** On Linux 1.3.X, virt_to_bus() must be used to translate
+** virtual memory addresses of the kernel data segment into
+** IO bus adresses.
+** On i386 architecture, IO bus addresses match the physical
+** addresses. But on other architectures they can be different.
+** In the original Bsd driver, vtophys() is called to translate
+** data addresses to IO bus addresses. In order to minimize
+** change, I decide to define vtophys() as virt_to_bus().
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+#define vtophys(p) virt_to_bus(p)
+
+/*
+** Memory mapped IO
+**
+** Since linux-2.1, we must use ioremap() to map the io memory space.
+** iounmap() to unmap it. That allows portability.
+** Linux 1.3.X and 2.0.X allow to remap physical pages addresses greater
+** than the highest physical memory address to kernel virtual pages with
+** vremap() / vfree(). That was not portable but worked with i386
+** architecture.
+*/
+
+#ifndef NCR_IOMAPPED
+__initfunc(
+static vm_offset_t remap_pci_mem(u_long base, u_long size)
+)
+{
+ u_long page_base = ((u_long) base) & PAGE_MASK;
+ u_long page_offs = ((u_long) base) - page_base;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+ u_long page_remapped = (u_long) ioremap(page_base, page_offs+size);
+#else
+ u_long page_remapped = (u_long) vremap(page_base, page_offs+size);
+#endif
+
+ return (vm_offset_t) (page_remapped ? (page_remapped + page_offs) : 0UL);
+}
+
+__initfunc(
+static void unmap_pci_mem(vm_offset_t vaddr, u_long size)
+)
+{
+ if (vaddr)
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+ iounmap((void *) (vaddr & PAGE_MASK));
+#else
+ vfree((void *) (vaddr & PAGE_MASK));
+#endif
+}
+#endif /* !NCR_IOMAPPED */
+
+#else /* linux-1.2.13 */
+
+/*
+** Linux 1.2.X assumes that addresses (virtual, physical, bus)
+** are the same.
+**
+** I have not found how to do MMIO. It seems that only processes can
+** map high physical pages to virtual (Xservers can do MMIO).
+*/
+
+#define vtophys(p) ((u_long) (p))
+#endif
+
+/*
+** Insert a delay in micro-seconds.
+*/
+
+static void DELAY(long us)
+{
+ for (;us>1000;us-=1000) udelay(1000);
+ if (us) udelay(us);
+}
+
+/*
+** Internal data structure allocation.
+**
+** Linux scsi memory poor pool is adjusted for the need of
+** middle-level scsi driver.
+** We allocate our control blocks in the kernel memory pool
+** to avoid scsi pool shortage.
+** I notice that kmalloc() returns NULL during host attach under
+** Linux 1.2.13. But this ncr driver is reliable enough to
+** accomodate with this joke.
+**
+** kmalloc() only ensure 8 bytes boundary alignment.
+** The NCR need better alignment for cache line bursting.
+** The global header is moved betewen the NCB and CCBs and need
+** origin and destination addresses to have same lower four bits.
+**
+** We use 32 boundary alignment for NCB and CCBs and offset multiple
+** of 32 for global header fields. That's too much but at least enough.
+*/
+
+#define ALIGN_SIZE(shift) (1UL << shift)
+#define ALIGN_MASK(shift) (~(ALIGN_SIZE(shift)-1))
+
+#define NCB_ALIGN_SHIFT 5
+#define CCB_ALIGN_SHIFT 5
+#define LCB_ALIGN_SHIFT 5
+#define SCR_ALIGN_SHIFT 5
+
+#define NCB_ALIGN_SIZE ALIGN_SIZE(NCB_ALIGN_SHIFT)
+#define NCB_ALIGN_MASK ALIGN_MASK(NCB_ALIGN_SHIFT)
+#define CCB_ALIGN_SIZE ALIGN_SIZE(CCB_ALIGN_SHIFT)
+#define CCB_ALIGN_MASK ALIGN_MASK(CCB_ALIGN_SHIFT)
+#define SCR_ALIGN_SIZE ALIGN_SIZE(SCR_ALIGN_SHIFT)
+#define SCR_ALIGN_MASK ALIGN_MASK(SCR_ALIGN_SHIFT)
+
+static void *m_alloc(int size, int a_shift)
+{
+ u_long addr;
+ void *ptr;
+ u_long a_size, a_mask;
+
+ if (a_shift < 3)
+ a_shift = 3;
+
+ a_size = ALIGN_SIZE(a_shift);
+ a_mask = ALIGN_MASK(a_shift);
+
+ ptr = (void *) kmalloc(size + a_size, GFP_ATOMIC);
+ if (ptr) {
+ addr = (((u_long) ptr) + a_size) & a_mask;
+ *((void **) (addr - sizeof(void *))) = ptr;
+ ptr = (void *) addr;
+ }
+
+ return ptr;
+}
+
+#ifdef MODULE
+static void m_free(void *ptr, int size)
+{
+ u_long addr;
+
+ if (ptr) {
+ addr = (u_long) ptr;
+ ptr = *((void **) (addr - sizeof(void *)));
+
+ kfree(ptr);
+ }
+}
+#endif
+
+/*
+** Transfer direction
+**
+** Low-level scsi drivers under Linux do not receive the expected
+** data transfer direction from upper scsi drivers.
+** The driver will only check actual data direction for common
+** scsi opcodes. Other ones may cause problem, since they may
+** depend on device type or be vendor specific.
+** I would prefer to never trust the device for data direction,
+** but that is not possible.
+**
+** The original driver requires the expected direction to be known.
+** The Linux version of the driver has been enhanced in order to
+** be able to transfer data in the direction choosen by the target.
+*/
+
+#define XferNone 0
+#define XferIn 1
+#define XferOut 2
+#define XferBoth 3
+static int guess_xfer_direction(int opcode);
+
+/*
+** Head of list of NCR boards
+**
+** For kernel version < 1.3.70, host is retrieved by its irq level.
+** For later kernels, the internal host control block address
+** (struct ncb) is used as device id parameter of the irq stuff.
+*/
+
+static struct Scsi_Host *first_host = NULL;
+static Scsi_Host_Template *the_template = NULL;
+
+
+/*
+** /proc directory entry and proc_info function
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+struct proc_dir_entry proc_scsi_ncr53c8xx = {
+ PROC_SCSI_NCR53C8XX, 9, "ncr53c8xx",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+# ifdef SCSI_NCR_PROC_INFO_SUPPORT
+int ncr53c8xx_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int func);
+# endif
+#endif
+
+/*
+** Table of target capabilities.
+**
+** This bitmap is anded with the byte 7 of inquiry data on completion of
+** INQUIRY command.
+** The driver never see zeroed bits and will ignore the corresponding
+** capabilities of the target.
+*/
+
+static struct {
+ unsigned char and_map[MAX_TARGET];
+} target_capabilities[SCSI_NCR_MAX_HOST] = { NCR53C8XX_TARGET_CAPABILITIES };
+
+/*
+** Driver setup.
+**
+** This structure is initialized from linux config options.
+** It can be overridden at boot-up by the boot command line.
+*/
+struct ncr_driver_setup {
+ unsigned master_parity : 1;
+ unsigned scsi_parity : 1;
+ unsigned disconnection : 1;
+ unsigned special_features : 2;
+ unsigned ultra_scsi : 2;
+ unsigned force_sync_nego: 1;
+ unsigned reverse_probe: 1;
+ unsigned pci_fix_up: 4;
+ u_char use_nvram;
+ u_char verbose;
+ u_char default_tags;
+ u_short default_sync;
+ u_short debug;
+ u_char burst_max;
+ u_char led_pin;
+ u_char max_wide;
+ u_char settle_delay;
+ u_char diff_support;
+ u_char irqm;
+ u_char bus_check;
+};
+
+static struct ncr_driver_setup
+ driver_setup = SCSI_NCR_DRIVER_SETUP;
+
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+static struct ncr_driver_setup
+ driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP;
+#ifdef MODULE
+char *ncr53c8xx = 0; /* command line passed by insmod */
+#endif
+#endif
+
+/*
+** Other Linux definitions
+*/
+
+#define ScsiResult(host_code, scsi_code) (((host_code) << 16) + ((scsi_code) & 0x7f))
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
+static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_device *devlist);
+#endif
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
+static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs);
+#else
+static void ncr53c8xx_intr(int irq, struct pt_regs * regs);
+#endif
+
+static void ncr53c8xx_timeout(unsigned long np);
+
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+/*
+** Symbios NvRAM data format
+*/
+#define SYMBIOS_NVRAM_SIZE 368
+#define SYMBIOS_NVRAM_ADDRESS 0x100
+
+struct Symbios_nvram {
+/* Header 6 bytes */
+ u_short start_marker; /* 0x0000 */
+ u_short byte_count; /* excluding header/trailer */
+ u_short checksum;
+
+/* Controller set up 20 bytes */
+ u_short word0; /* 0x3000 */
+ u_short word2; /* 0x0000 */
+ u_short word4; /* 0x0000 */
+ u_short flags;
+#define SYMBIOS_SCAM_ENABLE (1)
+#define SYMBIOS_PARITY_ENABLE (1<<1)
+#define SYMBIOS_VERBOSE_MSGS (1<<2)
+ u_short flags1;
+#define SYMBIOS_SCAN_HI_LO (1)
+ u_short word10; /* 0x00 */
+ u_short flags3; /* 0x00 */
+#define SYMBIOS_REMOVABLE_FLAGS (3) /* 0=none, 1=bootable, 2=all */
+ u_char host_id;
+ u_char byte15; /* 0x04 */
+ u_short word16; /* 0x0410 */
+ u_short word18; /* 0x0000 */
+
+/* Boot order 14 bytes * 4 */
+ struct Symbios_host{
+ u_char word0; /* 0x0004:ok / 0x0000:nok */
+ u_short device_id; /* PCI device id */
+ u_short vendor_id; /* PCI vendor id */
+ u_char byte6; /* 0x00 */
+ u_char device_fn; /* PCI device/function number << 3*/
+ u_short word8;
+ u_short flags;
+#define SYMBIOS_INIT_SCAN_AT_BOOT (1)
+ u_short io_port; /* PCI io_port address */
+ } host[4];
+
+/* Targets 8 bytes * 16 */
+ struct Symbios_target {
+ u_short flags;
+#define SYMBIOS_DISCONNECT_ENABLE (1)
+#define SYMBIOS_SCAN_AT_BOOT_TIME (1<<1)
+#define SYMBIOS_SCAN_LUNS (1<<2)
+#define SYMBIOS_QUEUE_TAGS_ENABLED (1<<3)
+ u_char bus_width; /* 0x08/0x10 */
+ u_char sync_offset;
+ u_char sync_period; /* 4*period factor */
+ u_char byte6; /* 0x00 */
+ u_short timeout;
+ } target[16];
+ u_char spare_devices[19*8];
+ u_char trailer[6]; /* 0xfe 0xfe 0x00 0x00 0x00 0x00 */
+};
+typedef struct Symbios_nvram Symbios_nvram;
+typedef struct Symbios_host Symbios_host;
+typedef struct Symbios_target Symbios_target;
+
+/*
+** Tekram NvRAM data format.
+*/
+#define TEKRAM_NVRAM_SIZE 64
+#define TEKRAM_NVRAM_ADDRESS 0
+
+struct Tekram_nvram {
+ struct Tekram_target {
+ u_char flags;
+#define TEKRAM_PARITY_CHECK (1)
+#define TEKRAM_SYNC_NEGO (1<<1)
+#define TEKRAM_DISCONNECT_ENABLE (1<<2)
+#define TEKRAM_START_CMD (1<<3)
+#define TEKRAM_TAGGED_COMMANDS (1<<4)
+#define TEKRAM_WIDE_NEGO (1<<5)
+ u_char sync_index;
+ u_short word2;
+ } target[16];
+ u_char host_id;
+ u_char flags;
+#define TEKRAM_MORE_THAN_2_DRIVES (1)
+#define TEKRAM_DRIVES_SUP_1GB (1<<1)
+#define TEKRAM_RESET_ON_POWER_ON (1<<2)
+#define TEKRAM_ACTIVE_NEGATION (1<<3)
+#define TEKRAM_IMMEDIATE_SEEK (1<<4)
+#define TEKRAM_SCAN_LUNS (1<<5)
+#define TEKRAM_REMOVABLE_FLAGS (3<<6) /* 0: disable; 1: boot device; 2:all */
+ u_char boot_delay_index;
+ u_char max_tags_index;
+ u_short flags1;
+#define TEKRAM_F2_F6_ENABLED (1)
+ u_short spare[29];
+};
+typedef struct Tekram_nvram Tekram_nvram;
+typedef struct Tekram_target Tekram_target;
+
+static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21};
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+/*
+** Structures used by ncr53c8xx_detect/ncr53c8xx_pci_init to
+** transmit device configuration to the ncr_attach() function.
+*/
+typedef struct {
+ int bus;
+ u_char device_fn;
+ u_int base;
+ u_int base_2;
+ u_int io_port;
+ int irq;
+/* port and reg fields to use INB, OUTB macros */
+ u_int port;
+ volatile struct ncr_reg *reg;
+} ncr_slot;
+
+typedef struct {
+ int type;
+#define SCSI_NCR_SYMBIOS_NVRAM (1)
+#define SCSI_NCR_TEKRAM_NVRAM (2)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ union {
+ Symbios_nvram Symbios;
+ Tekram_nvram Tekram;
+ } data;
+#endif
+} ncr_nvram;
+
+/*
+** Structure used by ncr53c8xx_detect/ncr53c8xx_pci_init
+** to save data on each detected board for ncr_attach().
+*/
+typedef struct {
+ ncr_slot slot;
+ ncr_chip chip;
+ ncr_nvram *nvram;
+ int attach_done;
+} ncr_device;
+
+/*==========================================================
+**
+** Debugging tags
+**
+**==========================================================
+*/
+
+#define DEBUG_ALLOC (0x0001)
+#define DEBUG_PHASE (0x0002)
+#define DEBUG_POLL (0x0004)
+#define DEBUG_QUEUE (0x0008)
+#define DEBUG_RESULT (0x0010)
+#define DEBUG_SCATTER (0x0020)
+#define DEBUG_SCRIPT (0x0040)
+#define DEBUG_TINY (0x0080)
+#define DEBUG_TIMING (0x0100)
+#define DEBUG_NEGO (0x0200)
+#define DEBUG_TAGS (0x0400)
+#define DEBUG_FREEZE (0x0800)
+#define DEBUG_RESTART (0x1000)
+
+/*
+** Enable/Disable debug messages.
+** Can be changed at runtime too.
+*/
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+ #define DEBUG_FLAGS ncr_debug
+#else
+ #define DEBUG_FLAGS SCSI_NCR_DEBUG_FLAGS
+#endif
+
+
+
+/*==========================================================
+**
+** assert ()
+**
+**==========================================================
+**
+** modified copy from 386bsd:/usr/include/sys/assert.h
+**
+**----------------------------------------------------------
+*/
+
+#define assert(expression) { \
+ if (!(expression)) { \
+ (void)printf(\
+ "assertion \"%s\" failed: file \"%s\", line %d\n", \
+ #expression, \
+ __FILE__, __LINE__); \
+ } \
+}
+
+/*==========================================================
+**
+** Big/Little endian support.
+**
+**==========================================================
+*/
+
+/*
+** If the NCR uses big endian addressing mode over the
+** PCI, actual io register addresses for byte and word
+** accesses must be changed according to lane routing.
+** Btw, ncr_offb() and ncr_offw() macros only apply to
+** constants and so donnot generate bloated code.
+*/
+
+#if defined(SCSI_NCR_BIG_ENDIAN)
+
+#define ncr_offb(o) (((o)&~3)+((~((o)&3))&3))
+#define ncr_offw(o) (((o)&~3)+((~((o)&3))&2))
+
+#else
+
+#define ncr_offb(o) (o)
+#define ncr_offw(o) (o)
+
+#endif
+
+/*
+** If the CPU and the NCR use same endian-ness adressing,
+** no byte reordering is needed for script patching.
+** Macro cpu_to_scr() is to be used for script patching.
+** Macro scr_to_cpu() is to be used for getting a DWORD
+** from the script.
+*/
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_le32(dw)
+#define scr_to_cpu(dw) le32_to_cpu(dw)
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_be32(dw)
+#define scr_to_cpu(dw) be32_to_cpu(dw)
+
+#else
+
+#define cpu_to_scr(dw) (dw)
+#define scr_to_cpu(dw) (dw)
+
+#endif
+
+/*==========================================================
+**
+** Access to the controller chip.
+**
+** If NCR_IOMAPPED is defined, only IO are used by the driver.
+**
+**==========================================================
+*/
+
+/*
+** If the CPU and the NCR use same endian-ness adressing,
+** no byte reordering is needed for accessing chip io
+** registers. Functions suffixed by '_raw' are assumed
+** to access the chip over the PCI without doing byte
+** reordering. Functions suffixed by '_l2b' are
+** assumed to perform little-endian to big-endian byte
+** reordering, those suffixed by '_b2l' blah, blah,
+** blah, ...
+*/
+
+#if defined(NCR_IOMAPPED)
+
+/*
+** IO mapped only input / ouput
+*/
+
+#define INB_OFF(o) inb (np->port + ncr_offb(o))
+#define OUTB_OFF(o, val) outb ((val), np->port + ncr_offb(o))
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) inw_l2b (np->port + ncr_offw(o))
+#define INL_OFF(o) inl_l2b (np->port + (o))
+
+#define OUTW_OFF(o, val) outw_b2l ((val), np->port + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_b2l ((val), np->port + (o))
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) inw_b2l (np->port + ncr_offw(o))
+#define INL_OFF(o) inl_b2l (np->port + (o))
+
+#define OUTW_OFF(o, val) outw_l2b ((val), np->port + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_l2b ((val), np->port + (o))
+
+#else
+
+#define INW_OFF(o) inw_raw (np->port + ncr_offw(o))
+#define INL_OFF(o) inl_raw (np->port + (o))
+
+#define OUTW_OFF(o, val) outw_raw ((val), np->port + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_raw ((val), np->port + (o))
+
+#endif /* ENDIANs */
+
+#else /* defined NCR_IOMAPPED */
+
+/*
+** MEMORY mapped IO input / output
+*/
+
+#define INB_OFF(o) readb((char *)np->reg + ncr_offb(o))
+#define OUTB_OFF(o, val) writeb((val), (char *)np->reg + ncr_offb(o))
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) readw_l2b((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_l2b((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_b2l((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_b2l((val), (char *)np->reg + (o))
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) readw_b2l((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_b2l((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_l2b((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_l2b((val), (char *)np->reg + (o))
+
+#else
+
+#define INW_OFF(o) readw_raw((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_raw((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_raw((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_raw((val), (char *)np->reg + (o))
+
+#endif
+
+#endif /* defined NCR_IOMAPPED */
+
+#define INB(r) INB_OFF (offsetof(struct ncr_reg,r))
+#define INW(r) INW_OFF (offsetof(struct ncr_reg,r))
+#define INL(r) INL_OFF (offsetof(struct ncr_reg,r))
+
+#define OUTB(r, val) OUTB_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTW(r, val) OUTW_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTL(r, val) OUTL_OFF (offsetof(struct ncr_reg,r), (val))
+
+/*
+** Set bit field ON, OFF
+*/
+
+#define OUTONB(r, m) OUTB(r, INB(r) | (m))
+#define OUTOFFB(r, m) OUTB(r, INB(r) & ~(m))
+#define OUTONW(r, m) OUTW(r, INW(r) | (m))
+#define OUTOFFW(r, m) OUTW(r, INW(r) & ~(m))
+#define OUTONL(r, m) OUTL(r, INL(r) | (m))
+#define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m))
+
+
+/*==========================================================
+**
+** Command control block states.
+**
+**==========================================================
+*/
+
+#define HS_IDLE (0)
+#define HS_BUSY (1)
+#define HS_NEGOTIATE (2) /* sync/wide data transfer*/
+#define HS_DISCONNECT (3) /* Disconnected by target */
+
+#define HS_COMPLETE (4)
+#define HS_SEL_TIMEOUT (5) /* Selection timeout */
+#define HS_RESET (6) /* SCSI reset */
+#define HS_ABORTED (7) /* Transfer aborted */
+#define HS_TIMEOUT (8) /* Software timeout */
+#define HS_FAIL (9) /* SCSI or PCI bus errors */
+#define HS_UNEXPECTED (10) /* Unexpected disconnect */
+
+#define HS_DONEMASK (0xfc)
+
+/*==========================================================
+**
+** Software Interrupt Codes
+**
+**==========================================================
+*/
+
+#define SIR_SENSE_RESTART (1)
+#define SIR_SENSE_FAILED (2)
+#define SIR_STALL_RESTART (3)
+#define SIR_STALL_QUEUE (4)
+#define SIR_NEGO_SYNC (5)
+#define SIR_NEGO_WIDE (6)
+#define SIR_NEGO_FAILED (7)
+#define SIR_NEGO_PROTO (8)
+#define SIR_REJECT_RECEIVED (9)
+#define SIR_REJECT_SENT (10)
+#define SIR_IGN_RESIDUE (11)
+#define SIR_MISSING_SAVE (12)
+#define SIR_DATA_IO_IS_OUT (13)
+#define SIR_DATA_IO_IS_IN (14)
+#define SIR_MAX (14)
+
+/*==========================================================
+**
+** Extended error codes.
+** xerr_status field of struct ccb.
+**
+**==========================================================
+*/
+
+#define XE_OK (0)
+#define XE_EXTRA_DATA (1) /* unexpected data phase */
+#define XE_BAD_PHASE (2) /* illegal phase (4/5) */
+
+/*==========================================================
+**
+** Negotiation status.
+** nego_status field of struct ccb.
+**
+**==========================================================
+*/
+
+#define NS_SYNC (1)
+#define NS_WIDE (2)
+
+/*==========================================================
+**
+** "Special features" of targets.
+** quirks field of struct tcb.
+** actualquirks field of struct ccb.
+**
+**==========================================================
+*/
+
+#define QUIRK_AUTOSAVE (0x01)
+#define QUIRK_NOMSG (0x02)
+#define QUIRK_NOSYNC (0x10)
+#define QUIRK_NOWIDE16 (0x20)
+#define QUIRK_UPDATE (0x80)
+
+/*==========================================================
+**
+** Capability bits in Inquire response byte 7.
+**
+**==========================================================
+*/
+
+#define INQ7_QUEUE (0x02)
+#define INQ7_SYNC (0x10)
+#define INQ7_WIDE16 (0x20)
+
+/*==========================================================
+**
+** Misc.
+**
+**==========================================================
+*/
+
+#define CCB_MAGIC (0xf2691ad2)
+
+/*==========================================================
+**
+** Declaration of structs.
+**
+**==========================================================
+*/
+
+struct tcb;
+struct lcb;
+struct ccb;
+struct ncb;
+struct script;
+
+typedef struct ncb * ncb_p;
+typedef struct tcb * tcb_p;
+typedef struct lcb * lcb_p;
+typedef struct ccb * ccb_p;
+
+struct link {
+ ncrcmd l_cmd;
+ ncrcmd l_paddr;
+};
+
+struct usrcmd {
+ u_long target;
+ u_long lun;
+ u_long data;
+ u_long cmd;
+};
+
+#define UC_SETSYNC 10
+#define UC_SETTAGS 11
+#define UC_SETDEBUG 12
+#define UC_SETORDER 13
+#define UC_SETWIDE 14
+#define UC_SETFLAG 15
+#define UC_CLEARPROF 16
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+#define UC_DEBUG_ERROR_RECOVERY 17
+#endif
+
+#define UF_TRACE (0x01)
+#define UF_NODISC (0x02)
+#define UF_NOSCAN (0x04)
+
+/*---------------------------------------
+**
+** Timestamps for profiling
+**
+**---------------------------------------
+*/
+
+struct tstamp {
+ u_long start;
+ u_long end;
+ u_long select;
+ u_long command;
+ u_long status;
+ u_long disconnect;
+ u_long reselect;
+};
+
+/*
+** profiling data (per device)
+*/
+
+struct profile {
+ u_long num_trans;
+ u_long num_kbytes;
+ u_long rest_bytes;
+ u_long num_disc;
+ u_long num_break;
+ u_long num_int;
+ u_long num_fly;
+ u_long ms_setup;
+ u_long ms_data;
+ u_long ms_disc;
+ u_long ms_post;
+};
+
+/*==========================================================
+**
+** Declaration of structs: target control block
+**
+**==========================================================
+*/
+
+struct tcb {
+ /*
+ ** during reselection the ncr jumps to this point
+ ** with SFBR set to the encoded target number
+ ** with bit 7 set.
+ ** if it's not this target, jump to the next.
+ **
+ ** JUMP IF (SFBR != #target#)
+ ** @(next tcb)
+ */
+
+ struct link jump_tcb;
+
+ /*
+ ** load the actual values for the sxfer and the scntl3
+ ** register (sync/wide mode).
+ **
+ ** SCR_COPY (1);
+ ** @(sval field of this tcb)
+ ** @(sxfer register)
+ ** SCR_COPY (1);
+ ** @(wval field of this tcb)
+ ** @(scntl3 register)
+ */
+
+ ncrcmd getscr[6];
+
+ /*
+ ** if next message is "identify"
+ ** then load the message to SFBR,
+ ** else load 0 to SFBR.
+ **
+ ** CALL
+ ** <RESEL_LUN>
+ */
+
+ struct link call_lun;
+
+ /*
+ ** now look for the right lun.
+ **
+ ** JUMP
+ ** @(first ccb of this lun)
+ */
+
+ struct link jump_lcb;
+
+ /*
+ ** pointer to interrupted getcc ccb
+ */
+
+ ccb_p hold_cp;
+
+ /*
+ ** pointer to ccb used for negotiating.
+ ** Avoid to start a nego for all queued commands
+ ** when tagged command queuing is enabled.
+ */
+
+ ccb_p nego_cp;
+
+ /*
+ ** statistical data
+ */
+
+ u_long transfers;
+ u_long bytes;
+
+ /*
+ ** user settable limits for sync transfer
+ ** and tagged commands.
+ ** These limits are read from the NVRAM if present.
+ */
+
+ u_char usrsync;
+ u_char usrwide;
+ u_char usrtags;
+ u_char usrflag;
+
+ u_char numtags;
+ u_char maxtags;
+ u_short num_good;
+
+ /*
+ ** negotiation of wide and synch transfer.
+ ** device quirks.
+ */
+
+/*0*/ u_char minsync;
+/*1*/ u_char sval;
+/*2*/ u_short period;
+/*0*/ u_char maxoffs;
+
+/*1*/ u_char quirks;
+
+/*2*/ u_char widedone;
+/*3*/ u_char wval;
+ /*
+ ** inquire data
+ */
+#define MAX_INQUIRE 36
+ u_char inqdata[MAX_INQUIRE];
+
+ /*
+ ** the lcb's of this tcb
+ */
+
+ lcb_p lp[MAX_LUN];
+};
+
+/*==========================================================
+**
+** Declaration of structs: lun control block
+**
+**==========================================================
+*/
+
+struct lcb {
+ /*
+ ** during reselection the ncr jumps to this point
+ ** with SFBR set to the "Identify" message.
+ ** if it's not this lun, jump to the next.
+ **
+ ** JUMP IF (SFBR != #lun#)
+ ** @(next lcb of this target)
+ */
+
+ struct link jump_lcb;
+
+ /*
+ ** if next message is "simple tag",
+ ** then load the tag to SFBR,
+ ** else load 0 to SFBR.
+ **
+ ** CALL
+ ** <RESEL_TAG>
+ */
+
+ struct link call_tag;
+
+ /*
+ ** now look for the right ccb.
+ **
+ ** JUMP
+ ** @(first ccb of this lun)
+ */
+
+ struct link jump_ccb;
+
+ /*
+ ** start of the ccb chain
+ */
+
+ ccb_p next_ccb;
+
+ /*
+ ** Control of tagged queueing
+ */
+
+ u_char reqccbs;
+ u_char actccbs;
+ u_char reqlink;
+ u_char actlink;
+ u_char usetags;
+ u_char lasttag;
+
+ /*
+ ** Linux specific fields:
+ ** Number of active commands and current credit.
+ ** Should be managed by the generic scsi driver
+ */
+
+ u_char active;
+ u_char opennings;
+
+ /*-----------------------------------------------
+ ** Flag to force M_ORDERED_TAG on next command
+ ** in order to avoid spurious timeout when
+ ** M_SIMPLE_TAG is used for all operations.
+ **-----------------------------------------------
+ */
+ u_char force_ordered_tag;
+#define NCR_TIMEOUT_INCREASE (5*HZ)
+};
+
+/*==========================================================
+**
+** Declaration of structs: COMMAND control block
+**
+**==========================================================
+**
+** This substructure is copied from the ccb to a
+** global address after selection (or reselection)
+** and copied back before disconnect.
+**
+** These fields are accessible to the script processor.
+**
+**----------------------------------------------------------
+*/
+
+struct head {
+ /*
+ ** Execution of a ccb starts at this point.
+ ** It's a jump to the "SELECT" label
+ ** of the script.
+ **
+ ** After successful selection the script
+ ** processor overwrites it with a jump to
+ ** the IDLE label of the script.
+ */
+
+ struct link launch;
+
+ /*
+ ** Saved data pointer.
+ ** Points to the position in the script
+ ** responsible for the actual transfer
+ ** of data.
+ ** It's written after reception of a
+ ** "SAVE_DATA_POINTER" message.
+ ** The goalpointer points after
+ ** the last transfer command.
+ */
+
+ u_int32 savep;
+ u_int32 lastp;
+ u_int32 goalp;
+
+ /*
+ ** The virtual address of the ccb
+ ** containing this header.
+ */
+
+ ccb_p cp;
+
+ /*
+ ** space for some timestamps to gather
+ ** profiling data about devices and this driver.
+ */
+
+ struct tstamp stamp;
+
+ /*
+ ** status fields.
+ */
+
+ u_char scr_st[4]; /* script status */
+ u_char status[4]; /* host status. Must be the last */
+ /* DWORD of the CCB header */
+};
+
+/*
+** The status bytes are used by the host and the script processor.
+**
+** The byte corresponding to the host_status must be stored in the
+** last DWORD of the CCB header since it is used for command
+** completion (ncr_wakeup()). Doing so, we are sure that the header
+** has been entirely copied back to the CCB when the host_status is
+** seen complete by the CPU.
+**
+** The last four bytes (status[4]) are copied to the scratchb register
+** (declared as scr0..scr3 in ncr_reg.h) just after the select/reselect,
+** and copied back just after disconnecting.
+** Inside the script the XX_REG are used.
+**
+** The first four bytes (scr_st[4]) are used inside the script by
+** "COPY" commands.
+** Because source and destination must have the same alignment
+** in a DWORD, the fields HAVE to be at the choosen offsets.
+** xerr_st 0 (0x34) scratcha
+** sync_st 1 (0x05) sxfer
+** wide_st 3 (0x03) scntl3
+*/
+
+/*
+** Last four bytes (script)
+*/
+#define QU_REG scr0
+#define HS_REG scr1
+#define HS_PRT nc_scr1
+#define SS_REG scr2
+#define PS_REG scr3
+
+/*
+** Last four bytes (host)
+*/
+#define actualquirks phys.header.status[0]
+#define host_status phys.header.status[1]
+#define scsi_status phys.header.status[2]
+#define parity_status phys.header.status[3]
+
+/*
+** First four bytes (script)
+*/
+#define xerr_st header.scr_st[0]
+#define sync_st header.scr_st[1]
+#define nego_st header.scr_st[2]
+#define wide_st header.scr_st[3]
+
+/*
+** First four bytes (host)
+*/
+#define xerr_status phys.xerr_st
+#define sync_status phys.sync_st
+#define nego_status phys.nego_st
+#define wide_status phys.wide_st
+
+/*==========================================================
+**
+** Declaration of structs: Data structure block
+**
+**==========================================================
+**
+** During execution of a ccb by the script processor,
+** the DSA (data structure address) register points
+** to this substructure of the ccb.
+** This substructure contains the header with
+** the script-processor-changable data and
+** data blocks for the indirect move commands.
+**
+**----------------------------------------------------------
+*/
+
+struct dsb {
+
+ /*
+ ** Header.
+ ** Has to be the first entry,
+ ** because it's jumped to by the
+ ** script processor
+ */
+
+ struct head header;
+
+ /*
+ ** Table data for Script
+ */
+
+ struct scr_tblsel select;
+ struct scr_tblmove smsg ;
+ struct scr_tblmove smsg2 ;
+ struct scr_tblmove cmd ;
+ struct scr_tblmove scmd ;
+ struct scr_tblmove sense ;
+ struct scr_tblmove data [MAX_SCATTER];
+};
+
+/*==========================================================
+**
+** Declaration of structs: Command control block.
+**
+**==========================================================
+**
+** During execution of a ccb by the script processor,
+** the DSA (data structure address) register points
+** to this substructure of the ccb.
+** This substructure contains the header with
+** the script-processor-changable data and then
+** data blocks for the indirect move commands.
+**
+**----------------------------------------------------------
+*/
+
+
+struct ccb {
+ /*
+ ** This field forces 32 bytes alignement for phys.header,
+ ** in order to use cache line bursting when copying it
+ ** to the ncb.
+ */
+
+ struct link filler[2];
+
+ /*
+ ** during reselection the ncr jumps to this point.
+ ** If a "SIMPLE_TAG" message was received,
+ ** then SFBR is set to the tag.
+ ** else SFBR is set to 0
+ ** If looking for another tag, jump to the next ccb.
+ **
+ ** JUMP IF (SFBR != #TAG#)
+ ** @(next ccb of this lun)
+ */
+
+ struct link jump_ccb;
+
+ /*
+ ** After execution of this call, the return address
+ ** (in the TEMP register) points to the following
+ ** data structure block.
+ ** So copy it to the DSA register, and start
+ ** processing of this data structure.
+ **
+ ** CALL
+ ** <RESEL_TMP>
+ */
+
+ struct link call_tmp;
+
+ /*
+ ** This is the data structure which is
+ ** to be executed by the script processor.
+ */
+
+ struct dsb phys;
+
+ /*
+ ** If a data transfer phase is terminated too early
+ ** (after reception of a message (i.e. DISCONNECT)),
+ ** we have to prepare a mini script to transfer
+ ** the rest of the data.
+ */
+
+ ncrcmd patch[8];
+
+ /*
+ ** The general SCSI driver provides a
+ ** pointer to a control block.
+ */
+
+ Scsi_Cmnd *cmd;
+ int data_len;
+
+ /*
+ ** We prepare a message to be sent after selection,
+ ** and a second one to be sent after getcc selection.
+ ** Contents are IDENTIFY and SIMPLE_TAG.
+ ** While negotiating sync or wide transfer,
+ ** a SDTM or WDTM message is appended.
+ */
+
+ u_char scsi_smsg [8];
+ u_char scsi_smsg2[8];
+
+ /*
+ ** Lock this ccb.
+ ** Flag is used while looking for a free ccb.
+ */
+
+ u_long magic;
+
+ /*
+ ** Physical address of this instance of ccb
+ */
+
+ u_long p_ccb;
+
+ /*
+ ** Completion time out for this job.
+ ** It's set to time of start + allowed number of seconds.
+ */
+
+ u_long tlimit;
+
+ /*
+ ** All ccbs of one hostadapter are chained.
+ */
+
+ ccb_p link_ccb;
+
+ /*
+ ** All ccbs of one target/lun are chained.
+ */
+
+ ccb_p next_ccb;
+
+ /*
+ ** Sense command
+ */
+
+ u_char sensecmd[6];
+
+ /*
+ ** Tag for this transfer.
+ ** It's patched into jump_ccb.
+ ** If it's not zero, a SIMPLE_TAG
+ ** message is included in smsg.
+ */
+
+ u_char tag;
+
+ /*
+ ** Number of segments of the scatter list.
+ ** Used for recalculation of savep/goalp/lastp on
+ ** SIR_DATA_IO_IS_OUT interrupt.
+ */
+
+ u_char segments;
+};
+
+#define CCB_PHYS(cp,lbl) (cp->p_ccb + offsetof(struct ccb, lbl))
+
+/*==========================================================
+**
+** Declaration of structs: NCR device descriptor
+**
+**==========================================================
+*/
+
+struct ncb {
+ /*
+ ** The global header.
+ ** Accessible to both the host and the
+ ** script-processor.
+ ** Is 32 bytes aligned since ncb is, in order to
+ ** allow cache line bursting when copying it from or
+ ** to ccbs.
+ */
+ struct head header;
+
+ /*-----------------------------------------------
+ ** Specific Linux fields
+ **-----------------------------------------------
+ */
+ int unit; /* Unit number */
+ char chip_name[8]; /* Chip name */
+ char inst_name[16]; /* Instance name */
+ struct timer_list timer; /* Timer link header */
+ int ncr_cache; /* Cache test variable */
+ Scsi_Cmnd *waiting_list; /* Waiting list header for commands */
+ /* that we can't put into the squeue */
+ u_long settle_time; /* Reset in progess */
+ u_char release_stage; /* Synchronisation stage on release */
+ u_char verbose; /* Boot verbosity for this controller*/
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ u_char debug_error_recovery;
+ u_char stalling;
+ u_char assert_atn;
+#endif
+
+ /*-----------------------------------------------
+ ** Added field to support differences
+ ** between ncr chips.
+ ** sv_xxx are some io register bit value at start-up and
+ ** so assumed to have been set by the sdms bios.
+ ** rv_xxx are the bit fields of io register that will keep
+ ** the features used by the driver.
+ **-----------------------------------------------
+ */
+ u_short device_id;
+ u_char revision_id;
+
+ u_char sv_scntl0;
+ u_char sv_scntl3;
+ u_char sv_dmode;
+ u_char sv_dcntl;
+ u_char sv_ctest3;
+ u_char sv_ctest4;
+ u_char sv_ctest5;
+ u_char sv_gpcntl;
+ u_char sv_stest2;
+ u_char sv_stest4;
+
+ u_char rv_scntl0;
+ u_char rv_scntl3;
+ u_char rv_dmode;
+ u_char rv_dcntl;
+ u_char rv_ctest3;
+ u_char rv_ctest4;
+ u_char rv_ctest5;
+ u_char rv_stest2;
+
+ u_char scsi_mode;
+
+ /*-----------------------------------------------
+ ** Scripts ..
+ **-----------------------------------------------
+ **
+ ** During reselection the ncr jumps to this point.
+ ** The SFBR register is loaded with the encoded target id.
+ **
+ ** Jump to the first target.
+ **
+ ** JUMP
+ ** @(next tcb)
+ */
+ struct link jump_tcb;
+
+ /*-----------------------------------------------
+ ** Configuration ..
+ **-----------------------------------------------
+ **
+ ** virtual and physical addresses
+ ** of the 53c810 chip.
+ */
+ vm_offset_t vaddr;
+ vm_offset_t paddr;
+
+ vm_offset_t vaddr2;
+ vm_offset_t paddr2;
+
+ /*
+ ** pointer to the chip's registers.
+ */
+ volatile
+ struct ncr_reg* reg;
+
+ /*
+ ** A copy of the scripts, relocated for this ncb.
+ */
+ struct script *script0;
+ struct scripth *scripth0;
+
+ /*
+ ** Scripts instance virtual address.
+ */
+ struct script *script;
+ struct scripth *scripth;
+
+ /*
+ ** Scripts instance physical address.
+ */
+ u_long p_script;
+ u_long p_scripth;
+
+ /*
+ ** The SCSI address of the host adapter.
+ */
+ u_char myaddr;
+
+ /*
+ ** Max dwords burst supported by the adapter.
+ */
+ u_char maxburst; /* log base 2 of dwords burst */
+
+ /*
+ ** timing parameters
+ */
+ u_char minsync; /* Minimum sync period factor */
+ u_char maxsync; /* Maximum sync period factor */
+ u_char maxoffs; /* Max scsi offset */
+ u_char multiplier; /* Clock multiplier (1,2,4) */
+ u_char clock_divn; /* Number of clock divisors */
+ u_long clock_khz; /* SCSI clock frequency in KHz */
+ u_int features; /* Chip features map */
+
+
+ /*-----------------------------------------------
+ ** Link to the generic SCSI driver
+ **-----------------------------------------------
+ */
+
+ /* struct scsi_link sc_link; */
+
+ /*-----------------------------------------------
+ ** Job control
+ **-----------------------------------------------
+ **
+ ** Commands from user
+ */
+ struct usrcmd user;
+ u_char order;
+
+ /*
+ ** Target data
+ */
+ struct tcb target[MAX_TARGET];
+
+ /*
+ ** Start queue.
+ */
+ u_int32 squeue [MAX_START];
+ u_short squeueput;
+ u_short actccbs;
+
+ /*
+ ** Timeout handler
+ */
+#if 0
+ u_long heartbeat;
+ u_short ticks;
+ u_short latetime;
+#endif
+ u_long lasttime;
+
+ /*-----------------------------------------------
+ ** Debug and profiling
+ **-----------------------------------------------
+ **
+ ** register dump
+ */
+ struct ncr_reg regdump;
+ u_long regtime;
+
+ /*
+ ** Profiling data
+ */
+ struct profile profile;
+ u_long disc_phys;
+ u_long disc_ref;
+
+ /*
+ ** The global control block.
+ ** It's used only during the configuration phase.
+ ** A target control block will be created
+ ** after the first successful transfer.
+ */
+ struct ccb *ccb;
+
+ /*
+ ** message buffers.
+ ** Should be longword aligned,
+ ** because they're written with a
+ ** COPY script command.
+ */
+ u_char msgout[8];
+ u_char msgin [8];
+ u_int32 lastmsg;
+
+ /*
+ ** Buffer for STATUS_IN phase.
+ */
+ u_char scratch;
+
+ /*
+ ** controller chip dependent maximal transfer width.
+ */
+ u_char maxwide;
+
+ /*
+ ** option for M_IDENTIFY message: enables disconnecting
+ */
+ u_char disc;
+
+ /*
+ ** address of the ncr control registers in io space
+ */
+ u_int port;
+
+ /*
+ ** irq level
+ */
+ u_short irq;
+};
+
+#define NCB_SCRIPT_PHYS(np,lbl) (np->p_script + offsetof (struct script, lbl))
+#define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth, lbl))
+
+/*==========================================================
+**
+**
+** Script for NCR-Processor.
+**
+** Use ncr_script_fill() to create the variable parts.
+** Use ncr_script_copy_and_bind() to make a copy and
+** bind to physical addresses.
+**
+**
+**==========================================================
+**
+** We have to know the offsets of all labels before
+** we reach them (for forward jumps).
+** Therefore we declare a struct here.
+** If you make changes inside the script,
+** DON'T FORGET TO CHANGE THE LENGTHS HERE!
+**
+**----------------------------------------------------------
+*/
+
+/*
+** Script fragments which are loaded into the on-board RAM
+** of 825A, 875 and 895 chips.
+*/
+struct script {
+ ncrcmd start [ 4];
+ ncrcmd start0 [ 2];
+ ncrcmd start1 [ 3];
+ ncrcmd startpos [ 1];
+ ncrcmd trysel [ 8];
+ ncrcmd skip [ 8];
+ ncrcmd skip2 [ 3];
+ ncrcmd idle [ 2];
+ ncrcmd select [ 22];
+ ncrcmd prepare [ 4];
+ ncrcmd loadpos [ 14];
+ ncrcmd prepare2 [ 24];
+ ncrcmd setmsg [ 5];
+ ncrcmd clrack [ 2];
+ ncrcmd dispatch [ 38];
+ ncrcmd no_data [ 17];
+ ncrcmd checkatn [ 10];
+ ncrcmd command [ 15];
+ ncrcmd status [ 27];
+ ncrcmd msg_in [ 26];
+ ncrcmd msg_bad [ 6];
+ ncrcmd complete [ 13];
+ ncrcmd cleanup [ 12];
+ ncrcmd cleanup0 [ 11];
+ ncrcmd signal [ 10];
+ ncrcmd save_dp [ 5];
+ ncrcmd restore_dp [ 5];
+ ncrcmd disconnect [ 12];
+ ncrcmd disconnect0 [ 5];
+ ncrcmd disconnect1 [ 23];
+ ncrcmd msg_out [ 9];
+ ncrcmd msg_out_done [ 7];
+ ncrcmd badgetcc [ 6];
+ ncrcmd reselect [ 8];
+ ncrcmd reselect1 [ 8];
+ ncrcmd reselect2 [ 8];
+ ncrcmd resel_tmp [ 5];
+ ncrcmd resel_lun [ 18];
+ ncrcmd resel_tag [ 24];
+ ncrcmd data_io [ 6];
+ ncrcmd data_in [MAX_SCATTER * 4 + 4];
+};
+
+/*
+** Script fragments which stay in main memory for all chips.
+*/
+struct scripth {
+ ncrcmd tryloop [MAX_START*5+2];
+ ncrcmd msg_parity [ 6];
+ ncrcmd msg_reject [ 8];
+ ncrcmd msg_ign_residue [ 32];
+ ncrcmd msg_extended [ 18];
+ ncrcmd msg_ext_2 [ 18];
+ ncrcmd msg_wdtr [ 27];
+ ncrcmd msg_ext_3 [ 18];
+ ncrcmd msg_sdtr [ 27];
+ ncrcmd msg_out_abort [ 10];
+ ncrcmd getcc [ 4];
+ ncrcmd getcc1 [ 5];
+#ifdef NCR_GETCC_WITHMSG
+ ncrcmd getcc2 [ 33];
+#else
+ ncrcmd getcc2 [ 14];
+#endif
+ ncrcmd getcc3 [ 10];
+ ncrcmd data_out [MAX_SCATTER * 4 + 4];
+ ncrcmd aborttag [ 4];
+ ncrcmd abort [ 22];
+ ncrcmd snooptest [ 9];
+ ncrcmd snoopend [ 2];
+};
+
+/*==========================================================
+**
+**
+** Function headers.
+**
+**
+**==========================================================
+*/
+
+static void ncr_alloc_ccb (ncb_p np, u_long t, u_long l);
+static void ncr_complete (ncb_p np, ccb_p cp);
+static void ncr_exception (ncb_p np);
+static void ncr_free_ccb (ncb_p np, ccb_p cp, u_long t, u_long l);
+static void ncr_getclock (ncb_p np, int mult);
+static void ncr_selectclock (ncb_p np, u_char scntl3);
+static ccb_p ncr_get_ccb (ncb_p np, u_long t,u_long l);
+static void ncr_init (ncb_p np, int reset, char * msg, u_long code);
+static int ncr_int_sbmc (ncb_p np);
+static int ncr_int_par (ncb_p np);
+static void ncr_int_ma (ncb_p np);
+static void ncr_int_sir (ncb_p np);
+static void ncr_int_sto (ncb_p np);
+static u_long ncr_lookup (char* id);
+static void ncr_negotiate (struct ncb* np, struct tcb* tp);
+static void ncr_opennings (ncb_p np, lcb_p lp, Scsi_Cmnd * xp);
+
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+static void ncb_profile (ncb_p np, ccb_p cp);
+#endif
+
+static void ncr_script_copy_and_bind
+ (ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
+static void ncr_script_fill (struct script * scr, struct scripth * scripth);
+static int ncr_scatter (ccb_p cp, Scsi_Cmnd *cmd);
+static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags);
+static void ncr_getsync (ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p);
+static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
+static void ncr_settags (tcb_p tp, lcb_p lp);
+static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack);
+static int ncr_show_msg (u_char * msg);
+static int ncr_snooptest (ncb_p np);
+static void ncr_timeout (ncb_p np);
+static void ncr_wakeup (ncb_p np, u_long code);
+static void ncr_start_reset (ncb_p np, int settle_delay);
+static int ncr_reset_scsi_bus (ncb_p np, int enab_int, int settle_delay);
+
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
+static void ncr_usercmd (ncb_p np);
+#endif
+
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device);
+
+static void insert_into_waiting_list(ncb_p np, Scsi_Cmnd *cmd);
+static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd);
+static void process_waiting_list(ncb_p np, int sts);
+
+#define remove_from_waiting_list(np, cmd) \
+ retrieve_from_waiting_list(1, (np), (cmd))
+#define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
+#define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram);
+static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram);
+#endif
+
+/*==========================================================
+**
+**
+** Global static data.
+**
+**
+**==========================================================
+*/
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
+#endif
+
+static inline char *ncr_name (ncb_p np)
+{
+ return np->inst_name;
+}
+
+
+/*==========================================================
+**
+**
+** Scripts for NCR-Processor.
+**
+** Use ncr_script_bind for binding to physical addresses.
+**
+**
+**==========================================================
+**
+** NADDR generates a reference to a field of the controller data.
+** PADDR generates a reference to another part of the script.
+** RADDR generates a reference to a script processor register.
+** FADDR generates a reference to a script processor register
+** with offset.
+**
+**----------------------------------------------------------
+*/
+
+#define RELOC_SOFTC 0x40000000
+#define RELOC_LABEL 0x50000000
+#define RELOC_REGISTER 0x60000000
+#define RELOC_KVAR 0x70000000
+#define RELOC_LABELH 0x80000000
+#define RELOC_MASK 0xf0000000
+
+#define NADDR(label) (RELOC_SOFTC | offsetof(struct ncb, label))
+#define PADDR(label) (RELOC_LABEL | offsetof(struct script, label))
+#define PADDRH(label) (RELOC_LABELH | offsetof(struct scripth, label))
+#define RADDR(label) (RELOC_REGISTER | REG(label))
+#define FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs)))
+#define KVAR(which) (RELOC_KVAR | (which))
+
+#define SCRIPT_KVAR_JIFFIES (0)
+
+#define SCRIPT_KVAR_FIRST SCRIPT_KVAR_JIFFIES
+#define SCRIPT_KVAR_LAST SCRIPT_KVAR_JIFFIES
+
+/*
+ * Kernel variables referenced in the scripts.
+ * THESE MUST ALL BE ALIGNED TO A 4-BYTE BOUNDARY.
+ */
+static void *script_kvars[] __initdata =
+ { (void *)&jiffies };
+
+static struct script script0 __initdata = {
+/*--------------------------< START >-----------------------*/ {
+#if 0
+ /*
+ ** Claim to be still alive ...
+ */
+ SCR_COPY (sizeof (((struct ncb *)0)->heartbeat)),
+ KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (heartbeat),
+#endif
+ /*
+ ** Make data structure address invalid.
+ ** clear SIGP.
+ */
+ SCR_LOAD_REG (dsa, 0xff),
+ 0,
+ SCR_FROM_REG (ctest2),
+ 0,
+}/*-------------------------< START0 >----------------------*/,{
+ /*
+ ** Hook for interrupted GetConditionCode.
+ ** Will be patched to ... IFTRUE by
+ ** the interrupt handler.
+ */
+ SCR_INT ^ IFFALSE (0),
+ SIR_SENSE_RESTART,
+
+}/*-------------------------< START1 >----------------------*/,{
+ /*
+ ** Hook for stalled start queue.
+ ** Will be patched to IFTRUE by the interrupt handler.
+ */
+ SCR_INT ^ IFFALSE (0),
+ SIR_STALL_RESTART,
+ /*
+ ** Then jump to a certain point in tryloop.
+ ** Due to the lack of indirect addressing the code
+ ** is self modifying here.
+ */
+ SCR_JUMP,
+}/*-------------------------< STARTPOS >--------------------*/,{
+ PADDRH(tryloop),
+}/*-------------------------< TRYSEL >----------------------*/,{
+ /*
+ ** Now:
+ ** DSA: Address of a Data Structure
+ ** or Address of the IDLE-Label.
+ **
+ ** TEMP: Address of a script, which tries to
+ ** start the NEXT entry.
+ **
+ ** Save the TEMP register into the SCRATCHA register.
+ ** Then copy the DSA to TEMP and RETURN.
+ ** This is kind of an indirect jump.
+ ** (The script processor has NO stack, so the
+ ** CALL is actually a jump and link, and the
+ ** RETURN is an indirect jump.)
+ **
+ ** If the slot was empty, DSA contains the address
+ ** of the IDLE part of this script. The processor
+ ** jumps to IDLE and waits for a reselect.
+ ** It will wake up and try the same slot again
+ ** after the SIGP bit becomes set by the host.
+ **
+ ** If the slot was not empty, DSA contains
+ ** the address of the phys-part of a ccb.
+ ** The processor jumps to this address.
+ ** phys starts with head,
+ ** head starts with launch,
+ ** so actually the processor jumps to
+ ** the lauch part.
+ ** If the entry is scheduled for execution,
+ ** then launch contains a jump to SELECT.
+ ** If it's not scheduled, it contains a jump to IDLE.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ RADDR (scratcha),
+ SCR_COPY (4),
+ RADDR (dsa),
+ RADDR (temp),
+ SCR_RETURN,
+ 0
+
+}/*-------------------------< SKIP >------------------------*/,{
+ /*
+ ** This entry has been canceled.
+ ** Next time use the next slot.
+ */
+ SCR_COPY (4),
+ RADDR (scratcha),
+ PADDR (startpos),
+ /*
+ ** patch the launch field.
+ ** should look like an idle process.
+ */
+ SCR_COPY_F (4),
+ RADDR (dsa),
+ PADDR (skip2),
+ SCR_COPY (8),
+ PADDR (idle),
+}/*-------------------------< SKIP2 >-----------------------*/,{
+ 0,
+ SCR_JUMP,
+ PADDR(start),
+}/*-------------------------< IDLE >------------------------*/,{
+ /*
+ ** Nothing to do?
+ ** Wait for reselect.
+ */
+ SCR_JUMP,
+ PADDR(reselect),
+
+}/*-------------------------< SELECT >----------------------*/,{
+ /*
+ ** DSA contains the address of a scheduled
+ ** data structure.
+ **
+ ** SCRATCHA contains the address of the script,
+ ** which starts the next entry.
+ **
+ ** Set Initiator mode.
+ **
+ ** (Target mode is left as an exercise for the reader)
+ */
+
+ SCR_CLR (SCR_TRG),
+ 0,
+ SCR_LOAD_REG (HS_REG, 0xff),
+ 0,
+
+ /*
+ ** And try to select this target.
+ */
+ SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select),
+ PADDR (reselect),
+
+ /*
+ ** Now there are 4 possibilities:
+ **
+ ** (1) The ncr looses arbitration.
+ ** This is ok, because it will try again,
+ ** when the bus becomes idle.
+ ** (But beware of the timeout function!)
+ **
+ ** (2) The ncr is reselected.
+ ** Then the script processor takes the jump
+ ** to the RESELECT label.
+ **
+ ** (3) The ncr completes the selection.
+ ** Then it will execute the next statement.
+ **
+ ** (4) There is a selection timeout.
+ ** Then the ncr should interrupt the host and stop.
+ ** Unfortunately, it seems to continue execution
+ ** of the script. But it will fail with an
+ ** IID-interrupt on the next WHEN.
+ */
+
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ 0,
+
+ /*
+ ** Save target id to ctest0 register
+ */
+
+ SCR_FROM_REG (sdid),
+ 0,
+ SCR_TO_REG (ctest0),
+ 0,
+ /*
+ ** Send the IDENTIFY and SIMPLE_TAG messages
+ ** (and the M_X_SYNC_REQ message)
+ */
+ SCR_MOVE_TBL ^ SCR_MSG_OUT,
+ offsetof (struct dsb, smsg),
+#ifdef undef /* XXX better fail than try to deal with this ... */
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ -16,
+#endif
+ SCR_CLR (SCR_ATN),
+ 0,
+ SCR_COPY (1),
+ RADDR (sfbr),
+ NADDR (lastmsg),
+ /*
+ ** Selection complete.
+ ** Next time use the next slot.
+ */
+ SCR_COPY (4),
+ RADDR (scratcha),
+ PADDR (startpos),
+}/*-------------------------< PREPARE >----------------------*/,{
+ /*
+ ** The ncr doesn't have an indirect load
+ ** or store command. So we have to
+ ** copy part of the control block to a
+ ** fixed place, where we can access it.
+ **
+ ** We patch the address part of a
+ ** COPY command with the DSA-register.
+ */
+ SCR_COPY_F (4),
+ RADDR (dsa),
+ PADDR (loadpos),
+ /*
+ ** then we do the actual copy.
+ */
+ SCR_COPY (sizeof (struct head)),
+ /*
+ ** continued after the next label ...
+ */
+
+}/*-------------------------< LOADPOS >---------------------*/,{
+ 0,
+ NADDR (header),
+ /*
+ ** Mark this ccb as not scheduled.
+ */
+ SCR_COPY (8),
+ PADDR (idle),
+ NADDR (header.launch),
+ /*
+ ** Set a time stamp for this selection
+ */
+ SCR_COPY (sizeof (u_long)),
+ KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (header.stamp.select),
+ /*
+ ** load the savep (saved pointer) into
+ ** the TEMP register (actual pointer)
+ */
+ SCR_COPY (4),
+ NADDR (header.savep),
+ RADDR (temp),
+ /*
+ ** Initialize the status registers
+ */
+ SCR_COPY (4),
+ NADDR (header.status),
+ RADDR (scr0),
+
+}/*-------------------------< PREPARE2 >---------------------*/,{
+ /*
+ ** Load the synchronous mode register
+ */
+ SCR_COPY (1),
+ NADDR (sync_st),
+ RADDR (sxfer),
+ /*
+ ** Load the wide mode and timing register
+ */
+ SCR_COPY (1),
+ NADDR (wide_st),
+ RADDR (scntl3),
+ /*
+ ** Initialize the msgout buffer with a NOOP message.
+ */
+ SCR_LOAD_REG (scratcha, M_NOOP),
+ 0,
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (msgout),
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (msgin),
+ /*
+ ** Message in phase ?
+ */
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** Extended or reject message ?
+ */
+ SCR_FROM_REG (sbdl),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
+ PADDR (msg_in),
+ SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
+ PADDRH (msg_reject),
+ /*
+ ** normal processing
+ */
+ SCR_JUMP,
+ PADDR (dispatch),
+}/*-------------------------< SETMSG >----------------------*/,{
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (msgout),
+ SCR_SET (SCR_ATN),
+ 0,
+}/*-------------------------< CLRACK >----------------------*/,{
+ /*
+ ** Terminate possible pending message phase.
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+
+}/*-----------------------< DISPATCH >----------------------*/,{
+ SCR_FROM_REG (HS_REG),
+ 0,
+ SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
+ SIR_NEGO_FAILED,
+ /*
+ ** remove bogus output signals
+ */
+ SCR_REG_REG (socl, SCR_AND, CACK|CATN),
+ 0,
+ SCR_RETURN ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+ 0,
+ /*
+ ** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 4.
+ ** Possible data corruption during Memory Write and Invalidate.
+ ** This work-around resets the addressing logic prior to the
+ ** start of the first MOVE of a DATA IN phase.
+ ** (See README.ncr53c8xx for more information)
+ */
+ SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)),
+ 20,
+ SCR_COPY (4),
+ RADDR (scratcha),
+ RADDR (scratcha),
+ SCR_RETURN,
+ 0,
+
+ SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
+ PADDR (msg_out),
+ SCR_JUMP ^ IFTRUE (IF (SCR_MSG_IN)),
+ PADDR (msg_in),
+ SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND)),
+ PADDR (command),
+ SCR_JUMP ^ IFTRUE (IF (SCR_STATUS)),
+ PADDR (status),
+ /*
+ ** Discard one illegal phase byte, if required.
+ */
+ SCR_LOAD_REG (scratcha, XE_BAD_PHASE),
+ 0,
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (xerr_st),
+ SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_OUT)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_ILG_OUT,
+ NADDR (scratch),
+ SCR_JUMPR ^ IFFALSE (IF (SCR_ILG_IN)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_ILG_IN,
+ NADDR (scratch),
+ SCR_JUMP,
+ PADDR (dispatch),
+
+}/*-------------------------< NO_DATA >--------------------*/,{
+ /*
+ ** The target wants to tranfer too much data
+ ** or in the wrong direction.
+ ** Remember that in extended error.
+ */
+ SCR_LOAD_REG (scratcha, XE_EXTRA_DATA),
+ 0,
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (xerr_st),
+ /*
+ ** Discard one data byte, if required.
+ */
+ SCR_JUMPR ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_DATA_OUT,
+ NADDR (scratch),
+ SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)),
+ 8,
+ SCR_MOVE_ABS (1) ^ SCR_DATA_IN,
+ NADDR (scratch),
+ /*
+ ** .. and repeat as required.
+ */
+ SCR_CALL,
+ PADDR (dispatch),
+ SCR_JUMP,
+ PADDR (no_data),
+}/*-------------------------< CHECKATN >--------------------*/,{
+ /*
+ ** If AAP (bit 1 of scntl0 register) is set
+ ** and a parity error is detected,
+ ** the script processor asserts ATN.
+ **
+ ** The target should switch to a MSG_OUT phase
+ ** to get the message.
+ */
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFFALSE (MASK (CATN, CATN)),
+ PADDR (dispatch),
+ /*
+ ** count it
+ */
+ SCR_REG_REG (PS_REG, SCR_ADD, 1),
+ 0,
+ /*
+ ** Prepare a M_ID_ERROR message
+ ** (initiator detected error).
+ ** The target should retry the transfer.
+ */
+ SCR_LOAD_REG (scratcha, M_ID_ERROR),
+ 0,
+ SCR_JUMP,
+ PADDR (setmsg),
+
+}/*-------------------------< COMMAND >--------------------*/,{
+ /*
+ ** If this is not a GETCC transfer ...
+ */
+ SCR_FROM_REG (SS_REG),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFTRUE (DATA (S_CHECK_COND)),
+ 28,
+ /*
+ ** ... set a timestamp ...
+ */
+ SCR_COPY (sizeof (u_long)),
+ KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (header.stamp.command),
+ /*
+ ** ... and send the command
+ */
+ SCR_MOVE_TBL ^ SCR_COMMAND,
+ offsetof (struct dsb, cmd),
+ SCR_JUMP,
+ PADDR (dispatch),
+ /*
+ ** Send the GETCC command
+ */
+/*>>>*/ SCR_MOVE_TBL ^ SCR_COMMAND,
+ offsetof (struct dsb, scmd),
+ SCR_JUMP,
+ PADDR (dispatch),
+
+}/*-------------------------< STATUS >--------------------*/,{
+ /*
+ ** set the timestamp.
+ */
+ SCR_COPY (sizeof (u_long)),
+ KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (header.stamp.status),
+ /*
+ ** If this is a GETCC transfer,
+ */
+ SCR_FROM_REG (SS_REG),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (S_CHECK_COND)),
+ 40,
+ /*
+ ** get the status
+ */
+ SCR_MOVE_ABS (1) ^ SCR_STATUS,
+ NADDR (scratch),
+ /*
+ ** Save status to scsi_status.
+ ** Mark as complete.
+ ** And wait for disconnect.
+ */
+ SCR_TO_REG (SS_REG),
+ 0,
+ SCR_REG_REG (SS_REG, SCR_OR, S_SENSE),
+ 0,
+ SCR_LOAD_REG (HS_REG, HS_COMPLETE),
+ 0,
+ SCR_JUMP,
+ PADDR (checkatn),
+ /*
+ ** If it was no GETCC transfer,
+ ** save the status to scsi_status.
+ */
+/*>>>*/ SCR_MOVE_ABS (1) ^ SCR_STATUS,
+ NADDR (scratch),
+ SCR_TO_REG (SS_REG),
+ 0,
+ /*
+ ** if it was no check condition ...
+ */
+ SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
+ PADDR (checkatn),
+ /*
+ ** ... mark as complete.
+ */
+ SCR_LOAD_REG (HS_REG, HS_COMPLETE),
+ 0,
+ SCR_JUMP,
+ PADDR (checkatn),
+
+}/*-------------------------< MSG_IN >--------------------*/,{
+ /*
+ ** Get the first byte of the message
+ ** and save it to SCRATCHA.
+ **
+ ** The script processor doesn't negate the
+ ** ACK signal after this transfer.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[0]),
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ SCR_FROM_REG (scratcha),
+ 0,
+ /*
+ ** Parity was ok, handle this message.
+ */
+ SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)),
+ PADDR (complete),
+ SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP)),
+ PADDR (save_dp),
+ SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)),
+ PADDR (restore_dp),
+ SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
+ PADDR (disconnect),
+ SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
+ PADDRH (msg_extended),
+ SCR_JUMP ^ IFTRUE (DATA (M_NOOP)),
+ PADDR (clrack),
+ SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
+ PADDRH (msg_reject),
+ SCR_JUMP ^ IFTRUE (DATA (M_IGN_RESIDUE)),
+ PADDRH (msg_ign_residue),
+ /*
+ ** Rest of the messages left as
+ ** an exercise ...
+ **
+ ** Unimplemented messages:
+ ** fall through to MSG_BAD.
+ */
+}/*-------------------------< MSG_BAD >------------------*/,{
+ /*
+ ** unimplemented message - reject it.
+ */
+ SCR_INT,
+ SIR_REJECT_SENT,
+ SCR_LOAD_REG (scratcha, M_REJECT),
+ 0,
+ SCR_JUMP,
+ PADDR (setmsg),
+
+}/*-------------------------< COMPLETE >-----------------*/,{
+ /*
+ ** Complete message.
+ **
+ ** If it's not the get condition code,
+ ** copy TEMP register to LASTP in header.
+ */
+ SCR_FROM_REG (SS_REG),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFTRUE (MASK (S_SENSE, S_SENSE)),
+ 12,
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR (header.lastp),
+/*>>>*/ /*
+ ** When we terminate the cycle by clearing ACK,
+ ** the target may disconnect immediately.
+ **
+ ** We don't want to be told of an
+ ** "unexpected disconnect",
+ ** so we disable this feature.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ /*
+ ** Terminate cycle ...
+ */
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ /*
+ ** ... and wait for the disconnect.
+ */
+ SCR_WAIT_DISC,
+ 0,
+}/*-------------------------< CLEANUP >-------------------*/,{
+ /*
+ ** dsa: Pointer to ccb
+ ** or xxxxxxFF (no ccb)
+ **
+ ** HS_REG: Host-Status (<>0!)
+ */
+ SCR_FROM_REG (dsa),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (0xff)),
+ PADDR (signal),
+ /*
+ ** dsa is valid.
+ ** save the status registers
+ */
+ SCR_COPY (4),
+ RADDR (scr0),
+ NADDR (header.status),
+ /*
+ ** and copy back the header to the ccb.
+ */
+ SCR_COPY_F (4),
+ RADDR (dsa),
+ PADDR (cleanup0),
+ SCR_COPY (sizeof (struct head)),
+ NADDR (header),
+}/*-------------------------< CLEANUP0 >--------------------*/,{
+ 0,
+
+ /*
+ ** If command resulted in "check condition"
+ ** status and is not yet completed,
+ ** try to get the condition code.
+ */
+ SCR_FROM_REG (HS_REG),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)),
+ 16,
+ SCR_FROM_REG (SS_REG),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
+ PADDRH(getcc2),
+ /*
+ ** And make the DSA register invalid.
+ */
+/*>>>*/ SCR_LOAD_REG (dsa, 0xff), /* invalid */
+ 0,
+}/*-------------------------< SIGNAL >----------------------*/,{
+ /*
+ ** if status = queue full,
+ ** reinsert in startqueue and stall queue.
+ */
+ SCR_FROM_REG (SS_REG),
+ 0,
+ SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
+ SIR_STALL_QUEUE,
+ /*
+ ** if job completed ...
+ */
+ SCR_FROM_REG (HS_REG),
+ 0,
+ /*
+ ** ... signal completion to the host
+ */
+ SCR_INT_FLY ^ IFFALSE (MASK (0, HS_DONEMASK)),
+ 0,
+ /*
+ ** Auf zu neuen Schandtaten!
+ */
+ SCR_JUMP,
+ PADDR(start),
+
+}/*-------------------------< SAVE_DP >------------------*/,{
+ /*
+ ** SAVE_DP message:
+ ** Copy TEMP register to SAVEP in header.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR (header.savep),
+ SCR_JUMP,
+ PADDR (clrack),
+}/*-------------------------< RESTORE_DP >---------------*/,{
+ /*
+ ** RESTORE_DP message:
+ ** Copy SAVEP in header to TEMP register.
+ */
+ SCR_COPY (4),
+ NADDR (header.savep),
+ RADDR (temp),
+ SCR_JUMP,
+ PADDR (clrack),
+
+}/*-------------------------< DISCONNECT >---------------*/,{
+ /*
+ ** If QUIRK_AUTOSAVE is set,
+ ** do an "save pointer" operation.
+ */
+ SCR_FROM_REG (QU_REG),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (QUIRK_AUTOSAVE, QUIRK_AUTOSAVE)),
+ 12,
+ /*
+ ** like SAVE_DP message:
+ ** Copy TEMP register to SAVEP in header.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR (header.savep),
+/*>>>*/ /*
+ ** Check if temp==savep or temp==goalp:
+ ** if not, log a missing save pointer message.
+ ** In fact, it's a comparison mod 256.
+ **
+ ** Hmmm, I hadn't thought that I would be urged to
+ ** write this kind of ugly self modifying code.
+ **
+ ** It's unbelievable, but the ncr53c8xx isn't able
+ ** to subtract one register from another.
+ */
+ SCR_FROM_REG (temp),
+ 0,
+ /*
+ ** You are not expected to understand this ..
+ **
+ ** CAUTION: only little endian architectures supported! XXX
+ */
+ SCR_COPY_F (1),
+ NADDR (header.savep),
+ PADDR (disconnect0),
+}/*-------------------------< DISCONNECT0 >--------------*/,{
+/*<<<*/ SCR_JUMPR ^ IFTRUE (DATA (1)),
+ 20,
+ /*
+ ** neither this
+ */
+ SCR_COPY_F (1),
+ NADDR (header.goalp),
+ PADDR (disconnect1),
+}/*-------------------------< DISCONNECT1 >--------------*/,{
+ SCR_INT ^ IFFALSE (DATA (1)),
+ SIR_MISSING_SAVE,
+/*>>>*/
+
+ /*
+ ** DISCONNECTing ...
+ **
+ ** disable the "unexpected disconnect" feature,
+ ** and remove the ACK signal.
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ /*
+ ** Wait for the disconnect.
+ */
+ SCR_WAIT_DISC,
+ 0,
+ /*
+ ** Profiling:
+ ** Set a time stamp,
+ ** and count the disconnects.
+ */
+ SCR_COPY (sizeof (u_long)),
+ KVAR(SCRIPT_KVAR_JIFFIES),
+ NADDR (header.stamp.disconnect),
+ SCR_COPY (4),
+ NADDR (disc_phys),
+ RADDR (temp),
+ SCR_REG_REG (temp, SCR_ADD, 0x01),
+ 0,
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR (disc_phys),
+ /*
+ ** Status is: DISCONNECTED.
+ */
+ SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
+ 0,
+ SCR_JUMP,
+ PADDR (cleanup),
+
+}/*-------------------------< MSG_OUT >-------------------*/,{
+ /*
+ ** The target requests a message.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+ NADDR (msgout),
+ SCR_COPY (1),
+ RADDR (sfbr),
+ NADDR (lastmsg),
+ /*
+ ** If it was no ABORT message ...
+ */
+ SCR_JUMP ^ IFTRUE (DATA (M_ABORT)),
+ PADDRH (msg_out_abort),
+ /*
+ ** ... wait for the next phase
+ ** if it's a message out, send it again, ...
+ */
+ SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+ PADDR (msg_out),
+}/*-------------------------< MSG_OUT_DONE >--------------*/,{
+ /*
+ ** ... else clear the message ...
+ */
+ SCR_LOAD_REG (scratcha, M_NOOP),
+ 0,
+ SCR_COPY (4),
+ RADDR (scratcha),
+ NADDR (msgout),
+ /*
+ ** ... and process the next phase
+ */
+ SCR_JUMP,
+ PADDR (dispatch),
+}/*------------------------< BADGETCC >---------------------*/,{
+ /*
+ ** If SIGP was set, clear it and try again.
+ */
+ SCR_FROM_REG (ctest2),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
+ PADDRH (getcc2),
+ SCR_INT,
+ SIR_SENSE_FAILED,
+}/*-------------------------< RESELECT >--------------------*/,{
+ /*
+ ** This NOP will be patched with LED OFF
+ ** SCR_REG_REG (gpreg, SCR_OR, 0x01)
+ */
+ SCR_NO_OP,
+ 0,
+ /*
+ ** make the DSA invalid.
+ */
+ SCR_LOAD_REG (dsa, 0xff),
+ 0,
+ SCR_CLR (SCR_TRG),
+ 0,
+ /*
+ ** Sleep waiting for a reselection.
+ ** If SIGP is set, special treatment.
+ **
+ ** Zu allem bereit ..
+ */
+ SCR_WAIT_RESEL,
+ PADDR(reselect2),
+}/*-------------------------< RESELECT1 >--------------------*/,{
+ /*
+ ** This NOP will be patched with LED ON
+ ** SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+ */
+ SCR_NO_OP,
+ 0,
+ /*
+ ** ... zu nichts zu gebrauchen ?
+ **
+ ** load the target id into the SFBR
+ ** and jump to the control block.
+ **
+ ** Look at the declarations of
+ ** - struct ncb
+ ** - struct tcb
+ ** - struct lcb
+ ** - struct ccb
+ ** to understand what's going on.
+ */
+ SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
+ 0,
+ SCR_TO_REG (ctest0),
+ 0,
+ SCR_JUMP,
+ NADDR (jump_tcb),
+}/*-------------------------< RESELECT2 >-------------------*/,{
+ /*
+ ** This NOP will be patched with LED ON
+ ** SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+ */
+ SCR_NO_OP,
+ 0,
+ /*
+ ** If it's not connected :(
+ ** -> interrupted by SIGP bit.
+ ** Jump to start.
+ */
+ SCR_FROM_REG (ctest2),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
+ PADDR (start),
+ SCR_JUMP,
+ PADDR (reselect),
+
+}/*-------------------------< RESEL_TMP >-------------------*/,{
+ /*
+ ** The return address in TEMP
+ ** is in fact the data structure address,
+ ** so copy it to the DSA register.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ RADDR (dsa),
+ SCR_JUMP,
+ PADDR (prepare),
+
+}/*-------------------------< RESEL_LUN >-------------------*/,{
+ /*
+ ** come back to this point
+ ** to get an IDENTIFY message
+ ** Wait for a msg_in phase.
+ */
+/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ 48,
+ /*
+ ** message phase
+ ** It's not a sony, it's a trick:
+ ** read the data without acknowledging it.
+ */
+ SCR_FROM_REG (sbdl),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (M_IDENTIFY, 0x98)),
+ 32,
+ /*
+ ** It WAS an Identify message.
+ ** get it and ack it!
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin),
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ ** Mask out the lun.
+ */
+ SCR_REG_REG (sfbr, SCR_AND, 0x07),
+ 0,
+ SCR_RETURN,
+ 0,
+ /*
+ ** No message phase or no IDENTIFY message:
+ ** return 0.
+ */
+/*>>>*/ SCR_LOAD_SFBR (0),
+ 0,
+ SCR_RETURN,
+ 0,
+
+}/*-------------------------< RESEL_TAG >-------------------*/,{
+ /*
+ ** come back to this point
+ ** to get a SIMPLE_TAG message
+ ** Wait for a MSG_IN phase.
+ */
+/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ 64,
+ /*
+ ** message phase
+ ** It's a trick - read the data
+ ** without acknowledging it.
+ */
+ SCR_FROM_REG (sbdl),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)),
+ 48,
+ /*
+ ** It WAS a SIMPLE_TAG message.
+ ** get it and ack it!
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin),
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ ** Wait for the second byte (the tag)
+ */
+/*<<<*/ SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ 24,
+ /*
+ ** Get it and ack it!
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin),
+ SCR_CLR (SCR_ACK|SCR_CARRY),
+ 0,
+ SCR_RETURN,
+ 0,
+ /*
+ ** No message phase or no SIMPLE_TAG message
+ ** or no second byte: return 0.
+ */
+/*>>>*/ SCR_LOAD_SFBR (0),
+ 0,
+ SCR_SET (SCR_CARRY),
+ 0,
+ SCR_RETURN,
+ 0,
+
+}/*-------------------------< DATA_IO >--------------------*/,{
+/*
+** Because Linux does not provide xfer data direction
+** to low-level scsi drivers, we must trust the target
+** for actual data direction when we cannot guess it.
+** The programmed interrupt patches savep, lastp, goalp,
+** etc.., and restarts the scsi script at data_out/in.
+*/
+ SCR_INT ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+ SIR_DATA_IO_IS_OUT,
+ SCR_INT ^ IFTRUE (WHEN (SCR_DATA_IN)),
+ SIR_DATA_IO_IS_IN,
+ SCR_JUMP,
+ PADDR (no_data),
+
+}/*-------------------------< DATA_IN >--------------------*/,{
+/*
+** Because the size depends on the
+** #define MAX_SCATTER parameter,
+** it is filled in at runtime.
+**
+** ##===========< i=0; i<MAX_SCATTER >=========
+** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
+** || PADDR (checkatn),
+** || SCR_MOVE_TBL ^ SCR_DATA_IN,
+** || offsetof (struct dsb, data[ i]),
+** ##==========================================
+**
+** SCR_CALL,
+** PADDR (checkatn),
+** SCR_JUMP,
+** PADDR (no_data),
+*/
+0
+}/*--------------------------------------------------------*/
+};
+
+static struct scripth scripth0 __initdata = {
+/*-------------------------< TRYLOOP >---------------------*/{
+/*
+** Load an entry of the start queue into dsa
+** and try to start it by jumping to TRYSEL.
+**
+** Because the size depends on the
+** #define MAX_START parameter, it is filled
+** in at runtime.
+**
+**-----------------------------------------------------------
+**
+** ##===========< I=0; i<MAX_START >===========
+** || SCR_COPY (4),
+** || NADDR (squeue[i]),
+** || RADDR (dsa),
+** || SCR_CALL,
+** || PADDR (trysel),
+** ##==========================================
+**
+** SCR_JUMP,
+** PADDRH(tryloop),
+**
+**-----------------------------------------------------------
+*/
+0
+},/*-------------------------< MSG_PARITY >---------------*/{
+ /*
+ ** count it
+ */
+ SCR_REG_REG (PS_REG, SCR_ADD, 0x01),
+ 0,
+ /*
+ ** send a "message parity error" message.
+ */
+ SCR_LOAD_REG (scratcha, M_PARITY),
+ 0,
+ SCR_JUMP,
+ PADDR (setmsg),
+}/*-------------------------< MSG_REJECT >---------------*/,{
+ /*
+ ** If a negotiation was in progress,
+ ** negotiation failed.
+ */
+ SCR_FROM_REG (HS_REG),
+ 0,
+ SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
+ SIR_NEGO_FAILED,
+ /*
+ ** else make host log this message
+ */
+ SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)),
+ SIR_REJECT_RECEIVED,
+ SCR_JUMP,
+ PADDR (clrack),
+
+}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{
+ /*
+ ** Terminate cycle
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get residue size.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[1]),
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ SCR_FROM_REG (scratcha),
+ 0,
+ /*
+ ** Size is 0 .. ignore message.
+ */
+ SCR_JUMP ^ IFTRUE (DATA (0)),
+ PADDR (clrack),
+ /*
+ ** Size is not 1 .. have to interrupt.
+ */
+/*<<<*/ SCR_JUMPR ^ IFFALSE (DATA (1)),
+ 40,
+ /*
+ ** Check for residue byte in swide register
+ */
+ SCR_FROM_REG (scntl2),
+ 0,
+/*<<<*/ SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
+ 16,
+ /*
+ ** There IS data in the swide register.
+ ** Discard it.
+ */
+ SCR_REG_REG (scntl2, SCR_OR, WSR),
+ 0,
+ SCR_JUMP,
+ PADDR (clrack),
+ /*
+ ** Load again the size to the sfbr register.
+ */
+/*>>>*/ SCR_FROM_REG (scratcha),
+ 0,
+/*>>>*/ SCR_INT,
+ SIR_IGN_RESIDUE,
+ SCR_JUMP,
+ PADDR (clrack),
+
+}/*-------------------------< MSG_EXTENDED >-------------*/,{
+ /*
+ ** Terminate cycle
+ */
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get length.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[1]),
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ SCR_FROM_REG (scratcha),
+ 0,
+ /*
+ */
+ SCR_JUMP ^ IFTRUE (DATA (3)),
+ PADDRH (msg_ext_3),
+ SCR_JUMP ^ IFFALSE (DATA (2)),
+ PADDR (msg_bad),
+}/*-------------------------< MSG_EXT_2 >----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get extended message code.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[2]),
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ SCR_FROM_REG (scratcha),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)),
+ PADDRH (msg_wdtr),
+ /*
+ ** unknown extended message
+ */
+ SCR_JUMP,
+ PADDR (msg_bad)
+}/*-------------------------< MSG_WDTR >-----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get data bus width
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[3]),
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ /*
+ ** let the host do the real work.
+ */
+ SCR_INT,
+ SIR_NEGO_WIDE,
+ /*
+ ** let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+
+ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ SIR_NEGO_PROTO,
+ /*
+ ** Send the M_X_WIDE_REQ
+ */
+ SCR_MOVE_ABS (4) ^ SCR_MSG_OUT,
+ NADDR (msgout),
+ SCR_CLR (SCR_ATN),
+ 0,
+ SCR_COPY (1),
+ RADDR (sfbr),
+ NADDR (lastmsg),
+ SCR_JUMP,
+ PADDR (msg_out_done),
+
+}/*-------------------------< MSG_EXT_3 >----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get extended message code.
+ */
+ SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+ NADDR (msgin[2]),
+ /*
+ ** Check for message parity error.
+ */
+ SCR_TO_REG (scratcha),
+ 0,
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ SCR_FROM_REG (scratcha),
+ 0,
+ SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)),
+ PADDRH (msg_sdtr),
+ /*
+ ** unknown extended message
+ */
+ SCR_JUMP,
+ PADDR (msg_bad)
+
+}/*-------------------------< MSG_SDTR >-----------------*/,{
+ SCR_CLR (SCR_ACK),
+ 0,
+ SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+ PADDR (dispatch),
+ /*
+ ** get period and offset
+ */
+ SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+ NADDR (msgin[3]),
+ SCR_FROM_REG (socl),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+ PADDRH (msg_parity),
+ /*
+ ** let the host do the real work.
+ */
+ SCR_INT,
+ SIR_NEGO_SYNC,
+ /*
+ ** let the target fetch our answer.
+ */
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+
+ SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+ SIR_NEGO_PROTO,
+ /*
+ ** Send the M_X_SYNC_REQ
+ */
+ SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
+ NADDR (msgout),
+ SCR_CLR (SCR_ATN),
+ 0,
+ SCR_COPY (1),
+ RADDR (sfbr),
+ NADDR (lastmsg),
+ SCR_JUMP,
+ PADDR (msg_out_done),
+
+}/*-------------------------< MSG_OUT_ABORT >-------------*/,{
+ /*
+ ** After ABORT message,
+ **
+ ** expect an immediate disconnect, ...
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ SCR_WAIT_DISC,
+ 0,
+ /*
+ ** ... and set the status to "ABORTED"
+ */
+ SCR_LOAD_REG (HS_REG, HS_ABORTED),
+ 0,
+ SCR_JUMP,
+ PADDR (cleanup),
+
+}/*-------------------------< GETCC >-----------------------*/,{
+ /*
+ ** The ncr doesn't have an indirect load
+ ** or store command. So we have to
+ ** copy part of the control block to a
+ ** fixed place, where we can modify it.
+ **
+ ** We patch the address part of a COPY command
+ ** with the address of the dsa register ...
+ */
+ SCR_COPY_F (4),
+ RADDR (dsa),
+ PADDRH (getcc1),
+ /*
+ ** ... then we do the actual copy.
+ */
+ SCR_COPY (sizeof (struct head)),
+}/*-------------------------< GETCC1 >----------------------*/,{
+ 0,
+ NADDR (header),
+ /*
+ ** Initialize the status registers
+ */
+ SCR_COPY (4),
+ NADDR (header.status),
+ RADDR (scr0),
+}/*-------------------------< GETCC2 >----------------------*/,{
+ /*
+ ** Get the condition code from a target.
+ **
+ ** DSA points to a data structure.
+ ** Set TEMP to the script location
+ ** that receives the condition code.
+ **
+ ** Because there is no script command
+ ** to load a longword into a register,
+ ** we use a CALL command.
+ */
+/*<<<*/ SCR_CALLR,
+ 24,
+ /*
+ ** Get the condition code.
+ */
+ SCR_MOVE_TBL ^ SCR_DATA_IN,
+ offsetof (struct dsb, sense),
+ /*
+ ** No data phase may follow!
+ */
+ SCR_CALL,
+ PADDR (checkatn),
+ SCR_JUMP,
+ PADDR (no_data),
+/*>>>*/
+
+ /*
+ ** The CALL jumps to this point.
+ ** Prepare for a RESTORE_POINTER message.
+ ** Save the TEMP register into the saved pointer.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR (header.savep),
+ /*
+ ** Load scratcha, because in case of a selection timeout,
+ ** the host will expect a new value for startpos in
+ ** the scratcha register.
+ */
+ SCR_COPY (4),
+ PADDR (startpos),
+ RADDR (scratcha),
+#ifdef NCR_GETCC_WITHMSG
+ /*
+ ** If QUIRK_NOMSG is set, select without ATN.
+ ** and don't send a message.
+ */
+ SCR_FROM_REG (QU_REG),
+ 0,
+ SCR_JUMP ^ IFTRUE (MASK (QUIRK_NOMSG, QUIRK_NOMSG)),
+ PADDRH(getcc3),
+ /*
+ ** Then try to connect to the target.
+ ** If we are reselected, special treatment
+ ** of the current job is required before
+ ** accepting the reselection.
+ */
+ SCR_SEL_TBL_ATN ^ offsetof (struct dsb, select),
+ PADDR(badgetcc),
+ /*
+ ** save target id.
+ */
+ SCR_FROM_REG (sdid),
+ 0,
+ SCR_TO_REG (ctest0),
+ 0,
+ /*
+ ** Send the IDENTIFY message.
+ ** In case of short transfer, remove ATN.
+ */
+ SCR_MOVE_TBL ^ SCR_MSG_OUT,
+ offsetof (struct dsb, smsg2),
+ SCR_CLR (SCR_ATN),
+ 0,
+ /*
+ ** save the first byte of the message.
+ */
+ SCR_COPY (1),
+ RADDR (sfbr),
+ NADDR (lastmsg),
+ SCR_JUMP,
+ PADDR (prepare2),
+
+#endif
+}/*-------------------------< GETCC3 >----------------------*/,{
+ /*
+ ** Try to connect to the target.
+ ** If we are reselected, special treatment
+ ** of the current job is required before
+ ** accepting the reselection.
+ **
+ ** Silly target won't accept a message.
+ ** Select without ATN.
+ */
+ SCR_SEL_TBL ^ offsetof (struct dsb, select),
+ PADDR(badgetcc),
+ /*
+ ** save target id.
+ */
+ SCR_FROM_REG (sdid),
+ 0,
+ SCR_TO_REG (ctest0),
+ 0,
+ /*
+ ** Force error if selection timeout
+ */
+ SCR_JUMPR ^ IFTRUE (WHEN (SCR_MSG_IN)),
+ 0,
+ /*
+ ** don't negotiate.
+ */
+ SCR_JUMP,
+ PADDR (prepare2),
+
+}/*-------------------------< DATA_OUT >-------------------*/,{
+/*
+** Because the size depends on the
+** #define MAX_SCATTER parameter,
+** it is filled in at runtime.
+**
+** ##===========< i=0; i<MAX_SCATTER >=========
+** || SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT)),
+** || PADDR (dispatch),
+** || SCR_MOVE_TBL ^ SCR_DATA_OUT,
+** || offsetof (struct dsb, data[ i]),
+** ##==========================================
+**
+** SCR_CALL,
+** PADDR (dispatch),
+** SCR_JUMP,
+** PADDR (no_data),
+**
+**---------------------------------------------------------
+*/
+0
+}/*-------------------------< ABORTTAG >-------------------*/,{
+ /*
+ ** Abort a bad reselection.
+ ** Set the message to ABORT vs. ABORT_TAG
+ */
+ SCR_LOAD_REG (scratcha, M_ABORT_TAG),
+ 0,
+ SCR_JUMPR ^ IFFALSE (CARRYSET),
+ 8,
+}/*-------------------------< ABORT >----------------------*/,{
+ SCR_LOAD_REG (scratcha, M_ABORT),
+ 0,
+ SCR_COPY (1),
+ RADDR (scratcha),
+ NADDR (msgout),
+ SCR_SET (SCR_ATN),
+ 0,
+ SCR_CLR (SCR_ACK),
+ 0,
+ /*
+ ** and send it.
+ ** we expect an immediate disconnect
+ */
+ SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+ 0,
+ SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+ NADDR (msgout),
+ SCR_COPY (1),
+ RADDR (sfbr),
+ NADDR (lastmsg),
+ SCR_CLR (SCR_ACK|SCR_ATN),
+ 0,
+ SCR_WAIT_DISC,
+ 0,
+ SCR_JUMP,
+ PADDR (start),
+}/*-------------------------< SNOOPTEST >-------------------*/,{
+ /*
+ ** Read the variable.
+ */
+ SCR_COPY (4),
+ NADDR(ncr_cache),
+ RADDR (scratcha),
+ /*
+ ** Write the variable.
+ */
+ SCR_COPY (4),
+ RADDR (temp),
+ NADDR(ncr_cache),
+ /*
+ ** Read back the variable.
+ */
+ SCR_COPY (4),
+ NADDR(ncr_cache),
+ RADDR (temp),
+}/*-------------------------< SNOOPEND >-------------------*/,{
+ /*
+ ** And stop.
+ */
+ SCR_INT,
+ 99,
+}/*--------------------------------------------------------*/
+};
+
+/*==========================================================
+**
+**
+** Fill in #define dependent parts of the script
+**
+**
+**==========================================================
+*/
+
+__initfunc(
+void ncr_script_fill (struct script * scr, struct scripth * scrh)
+)
+{
+ int i;
+ ncrcmd *p;
+
+ p = scrh->tryloop;
+ for (i=0; i<MAX_START; i++) {
+ *p++ =SCR_COPY (4);
+ *p++ =NADDR (squeue[i]);
+ *p++ =RADDR (dsa);
+ *p++ =SCR_CALL;
+ *p++ =PADDR (trysel);
+ };
+ *p++ =SCR_JUMP;
+ *p++ =PADDRH(tryloop);
+
+ assert ((u_long)p == (u_long)&scrh->tryloop + sizeof (scrh->tryloop));
+
+ p = scr->data_in;
+
+ for (i=0; i<MAX_SCATTER; i++) {
+ *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN));
+ *p++ =PADDR (checkatn);
+ *p++ =SCR_MOVE_TBL ^ SCR_DATA_IN;
+ *p++ =offsetof (struct dsb, data[i]);
+ };
+
+ *p++ =SCR_CALL;
+ *p++ =PADDR (checkatn);
+ *p++ =SCR_JUMP;
+ *p++ =PADDR (no_data);
+
+ assert ((u_long)p == (u_long)&scr->data_in + sizeof (scr->data_in));
+
+ p = scrh->data_out;
+
+ for (i=0; i<MAX_SCATTER; i++) {
+ *p++ =SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_OUT));
+ *p++ =PADDR (dispatch);
+ *p++ =SCR_MOVE_TBL ^ SCR_DATA_OUT;
+ *p++ =offsetof (struct dsb, data[i]);
+ };
+
+ *p++ =SCR_CALL;
+ *p++ =PADDR (dispatch);
+ *p++ =SCR_JUMP;
+ *p++ =PADDR (no_data);
+
+ assert ((u_long)p == (u_long)&scrh->data_out + sizeof (scrh->data_out));
+}
+
+/*==========================================================
+**
+**
+** Copy and rebind a script.
+**
+**
+**==========================================================
+*/
+
+__initfunc(
+static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
+)
+{
+ ncrcmd opcode, new, old, tmp1, tmp2;
+ ncrcmd *start, *end;
+ int relocs;
+ int opchanged = 0;
+
+ start = src;
+ end = src + len/4;
+
+ while (src < end) {
+
+ opcode = *src++;
+ *dst++ = cpu_to_scr(opcode);
+
+ /*
+ ** If we forget to change the length
+ ** in struct script, a field will be
+ ** padded with 0. This is an illegal
+ ** command.
+ */
+
+ if (opcode == 0) {
+ printf ("%s: ERROR0 IN SCRIPT at %d.\n",
+ ncr_name(np), (int) (src-start-1));
+ DELAY (1000000);
+ };
+
+ if (DEBUG_FLAGS & DEBUG_SCRIPT)
+ printf ("%p: <%x>\n",
+ (src-1), (unsigned)opcode);
+
+ /*
+ ** We don't have to decode ALL commands
+ */
+ switch (opcode >> 28) {
+
+ case 0xc:
+ /*
+ ** COPY has TWO arguments.
+ */
+ relocs = 2;
+ tmp1 = src[0];
+ if ((tmp1 & RELOC_MASK) == RELOC_KVAR)
+ tmp1 = 0;
+ tmp2 = src[1];
+ if ((tmp2 & RELOC_MASK) == RELOC_KVAR)
+ tmp2 = 0;
+ if ((tmp1 ^ tmp2) & 3) {
+ printf ("%s: ERROR1 IN SCRIPT at %d.\n",
+ ncr_name(np), (int) (src-start-1));
+ DELAY (1000000);
+ }
+ /*
+ ** If PREFETCH feature not enabled, remove
+ ** the NO FLUSH bit if present.
+ */
+ if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) {
+ dst[-1] = cpu_to_scr(opcode & ~SCR_NO_FLUSH);
+ ++opchanged;
+ }
+ break;
+
+ case 0x0:
+ /*
+ ** MOVE (absolute address)
+ */
+ relocs = 1;
+ break;
+
+ case 0x8:
+ /*
+ ** JUMP / CALL
+ ** don't relocate if relative :-)
+ */
+ if (opcode & 0x00800000)
+ relocs = 0;
+ else
+ relocs = 1;
+ break;
+
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ relocs = 1;
+ break;
+
+ default:
+ relocs = 0;
+ break;
+ };
+
+ if (relocs) {
+ while (relocs--) {
+ old = *src++;
+
+ switch (old & RELOC_MASK) {
+ case RELOC_REGISTER:
+ new = (old & ~RELOC_MASK) + np->paddr;
+ break;
+ case RELOC_LABEL:
+ new = (old & ~RELOC_MASK) + np->p_script;
+ break;
+ case RELOC_LABELH:
+ new = (old & ~RELOC_MASK) + np->p_scripth;
+ break;
+ case RELOC_SOFTC:
+ new = (old & ~RELOC_MASK) + vtophys(np);
+ break;
+ case RELOC_KVAR:
+ if (((old & ~RELOC_MASK) <
+ SCRIPT_KVAR_FIRST) ||
+ ((old & ~RELOC_MASK) >
+ SCRIPT_KVAR_LAST))
+ panic("ncr KVAR out of range");
+ new = vtophys(script_kvars[old &
+ ~RELOC_MASK]);
+ break;
+ case 0:
+ /* Don't relocate a 0 address. */
+ if (old == 0) {
+ new = old;
+ break;
+ }
+ /* fall through */
+ default:
+ panic("ncr_script_copy_and_bind: weird relocation %x\n", old);
+ break;
+ }
+
+ *dst++ = cpu_to_scr(new);
+ }
+ } else
+ *dst++ = cpu_to_scr(*src++);
+
+ };
+ if (bootverbose > 1 && opchanged)
+ printf("%s: NO FLUSH bit removed from %d script instructions\n",
+ ncr_name(np), opchanged);
+}
+
+/*==========================================================
+**
+**
+** Auto configuration: attach and init a host adapter.
+**
+**
+**==========================================================
+*/
+
+/*
+** Linux host data structure
+**
+** The script area is allocated in the host data structure
+** because kmalloc() returns NULL during scsi initialisations
+** with Linux 1.2.X
+*/
+
+struct host_data {
+ struct ncb *ncb;
+
+ char ncb_align[NCB_ALIGN_SIZE-1]; /* Filler for alignment */
+ struct ncb _ncb_data;
+
+ char ccb_align[CCB_ALIGN_SIZE-1]; /* Filler for alignment */
+ struct ccb _ccb_data;
+
+ char scr_align[SCR_ALIGN_SIZE-1]; /* Filler for alignment */
+ struct script script_data;
+
+ struct scripth scripth_data;
+};
+
+/*
+** Print something which allow to retrieve the controler type, unit,
+** target, lun concerned by a kernel message.
+*/
+
+#define PRINT_LUN(np, target, lun) \
+printf(KERN_INFO "%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
+
+static void PRINT_ADDR(Scsi_Cmnd *cmd)
+{
+ struct host_data *host_data = (struct host_data *) cmd->host->hostdata;
+ ncb_p np = host_data->ncb;
+ if (np) PRINT_LUN(np, cmd->target, cmd->lun);
+}
+
+/*==========================================================
+**
+** NCR chip clock divisor table.
+** Divisors are multiplied by 10,000,000 in order to make
+** calculations more simple.
+**
+**==========================================================
+*/
+
+#define _5M 5000000
+static u_long div_10M[] =
+ {2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M};
+
+
+/*===============================================================
+**
+** Prepare io register values used by ncr_init() according
+** to selected and supported features.
+**
+** NCR chips allow burst lengths of 2, 4, 8, 16, 32, 64, 128
+** transfers. 32,64,128 are only supported by 875 and 895 chips.
+** We use log base 2 (burst length) as internal code, with
+** value 0 meaning "burst disabled".
+**
+**===============================================================
+*/
+
+/*
+ * Burst length from burst code.
+ */
+#define burst_length(bc) (!(bc))? 0 : 1 << (bc)
+
+/*
+ * Burst code from io register bits.
+ */
+#define burst_code(dmode, ctest4, ctest5) \
+ (ctest4) & 0x80? 0 : (((dmode) & 0xc0) >> 6) + ((ctest5) & 0x04) + 1
+
+/*
+ * Set initial io register bits from burst code.
+ */
+static inline void ncr_init_burst(ncb_p np, u_char bc)
+{
+ np->rv_ctest4 &= ~0x80;
+ np->rv_dmode &= ~(0x3 << 6);
+ np->rv_ctest5 &= ~0x4;
+
+ if (!bc) {
+ np->rv_ctest4 |= 0x80;
+ }
+ else {
+ --bc;
+ np->rv_dmode |= ((bc & 0x3) << 6);
+ np->rv_ctest5 |= (bc & 0x4);
+ }
+}
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/*
+** Get target set-up from Symbios format NVRAM.
+*/
+
+__initfunc(
+static void
+ ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram)
+)
+{
+ tcb_p tp = &np->target[target];
+ Symbios_target *tn = &nvram->target[target];
+
+ tp->usrsync = tn->sync_period ? (tn->sync_period + 3) / 4 : 255;
+ tp->usrwide = tn->bus_width == 0x10 ? 1 : 0;
+ tp->usrtags =
+ (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SCSI_NCR_MAX_TAGS : 0;
+
+ if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE))
+ tp->usrflag |= UF_NODISC;
+ if (!(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME))
+ tp->usrflag |= UF_NOSCAN;
+}
+
+/*
+** Get target set-up from Tekram format NVRAM.
+*/
+
+__initfunc(
+static void
+ ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram)
+)
+{
+ tcb_p tp = &np->target[target];
+ struct Tekram_target *tn = &nvram->target[target];
+ int i;
+
+ if (tn->flags & TEKRAM_SYNC_NEGO) {
+ i = tn->sync_index & 0xf;
+ tp->usrsync = i < 12 ? Tekram_sync[i] : 255;
+ }
+
+ tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0;
+
+ if (tn->flags & TEKRAM_TAGGED_COMMANDS) {
+ tp->usrtags = 2 << nvram->max_tags_index;
+ if (tp->usrtags > SCSI_NCR_MAX_TAGS)
+ tp->usrtags = SCSI_NCR_MAX_TAGS;
+ }
+
+ if (!(tn->flags & TEKRAM_DISCONNECT_ENABLE))
+ tp->usrflag = UF_NODISC;
+
+ /* If any device does not support parity, we will not use this option */
+ if (!(tn->flags & TEKRAM_PARITY_CHECK))
+ np->rv_scntl0 &= ~0x0a; /* SCSI parity checking disabled */
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+__initfunc(
+static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
+)
+{
+ u_char burst_max;
+ u_long period;
+ int i;
+
+ /*
+ ** Save assumed BIOS setting
+ */
+
+ np->sv_scntl0 = INB(nc_scntl0) & 0x0a;
+ np->sv_scntl3 = INB(nc_scntl3) & 0x07;
+ np->sv_dmode = INB(nc_dmode) & 0xce;
+ np->sv_dcntl = INB(nc_dcntl) & 0xa8;
+ np->sv_ctest3 = INB(nc_ctest3) & 0x01;
+ np->sv_ctest4 = INB(nc_ctest4) & 0x80;
+ np->sv_ctest5 = INB(nc_ctest5) & 0x24;
+ np->sv_gpcntl = INB(nc_gpcntl);
+ np->sv_stest2 = INB(nc_stest2) & 0x20;
+ np->sv_stest4 = INB(nc_stest4);
+
+ /*
+ ** Wide ?
+ */
+
+ np->maxwide = (np->features & FE_WIDE)? 1 : 0;
+
+ /*
+ ** Get the frequency of the chip's clock.
+ ** Find the right value for scntl3.
+ */
+
+ if (np->features & FE_QUAD)
+ np->multiplier = 4;
+ else if (np->features & FE_DBLR)
+ np->multiplier = 2;
+ else
+ np->multiplier = 1;
+
+ np->clock_khz = (np->features & FE_CLK80)? 80000 : 40000;
+ np->clock_khz *= np->multiplier;
+
+ if (np->clock_khz != 40000)
+ ncr_getclock(np, np->multiplier);
+
+ /*
+ * Divisor to be used for async (timer pre-scaler).
+ */
+ i = np->clock_divn - 1;
+ while (i >= 0) {
+ --i;
+ if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) {
+ ++i;
+ break;
+ }
+ }
+ np->rv_scntl3 = i+1;
+
+ /*
+ * Minimum synchronous period factor supported by the chip.
+ * Btw, 'period' is in tenths of nanoseconds.
+ */
+
+ period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz;
+ if (period <= 250) np->minsync = 10;
+ else if (period <= 303) np->minsync = 11;
+ else if (period <= 500) np->minsync = 12;
+ else np->minsync = (period + 40 - 1) / 40;
+
+ /*
+ * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2).
+ */
+
+ if (np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2)))
+ np->minsync = 25;
+ else if (np->minsync < 12 && !(np->features & FE_ULTRA2))
+ np->minsync = 12;
+
+ /*
+ * Maximum synchronous period factor supported by the chip.
+ */
+
+ period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz);
+ np->maxsync = period > 2540 ? 254 : period / 10;
+
+ /*
+ ** Prepare initial value of other IO registers
+ */
+#if defined SCSI_NCR_TRUST_BIOS_SETTING
+ np->rv_scntl0 = np->sv_scntl0;
+ np->rv_dmode = np->sv_dmode;
+ np->rv_dcntl = np->sv_dcntl;
+ np->rv_ctest3 = np->sv_ctest3;
+ np->rv_ctest4 = np->sv_ctest4;
+ np->rv_ctest5 = np->sv_ctest5;
+ burst_max = burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
+#else
+
+ /*
+ ** Select burst length (dwords)
+ */
+ burst_max = driver_setup.burst_max;
+ if (burst_max == 255)
+ burst_max = burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
+ if (burst_max > 7)
+ burst_max = 7;
+ if (burst_max > np->maxburst)
+ burst_max = np->maxburst;
+
+ /*
+ ** Select all supported special features
+ */
+ if (np->features & FE_ERL)
+ np->rv_dmode |= ERL; /* Enable Read Line */
+ if (np->features & FE_BOF)
+ np->rv_dmode |= BOF; /* Burst Opcode Fetch */
+ if (np->features & FE_ERMP)
+ np->rv_dmode |= ERMP; /* Enable Read Multiple */
+ if (np->features & FE_PFEN)
+ np->rv_dcntl |= PFEN; /* Prefetch Enable */
+ if (np->features & FE_CLSE)
+ np->rv_dcntl |= CLSE; /* Cache Line Size Enable */
+ if (np->features & FE_WRIE)
+ np->rv_ctest3 |= WRIE; /* Write and Invalidate */
+ if (np->features & FE_DFS)
+ np->rv_ctest5 |= DFS; /* Dma Fifo Size */
+
+ /*
+ ** Select some other
+ */
+ if (driver_setup.master_parity)
+ np->rv_ctest4 |= MPEE; /* Master parity checking */
+ if (driver_setup.scsi_parity)
+ np->rv_scntl0 |= 0x0a; /* full arb., ena parity, par->ATN */
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ /*
+ ** Get parity checking, host ID and verbose mode from NVRAM
+ **/
+ if (nvram) {
+ switch(nvram->type) {
+ case SCSI_NCR_TEKRAM_NVRAM:
+ np->myaddr = nvram->data.Tekram.host_id & 0x0f;
+ break;
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ if (!(nvram->data.Symbios.flags & SYMBIOS_PARITY_ENABLE))
+ np->rv_scntl0 &= ~0x0a;
+ np->myaddr = nvram->data.Symbios.host_id & 0x0f;
+ if (nvram->data.Symbios.flags & SYMBIOS_VERBOSE_MSGS)
+ np->verbose += 1;
+ break;
+ }
+ }
+#endif
+ /*
+ ** Get SCSI addr of host adapter (set by bios?).
+ */
+ if (!np->myaddr) np->myaddr = INB(nc_scid) & 0x07;
+ if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
+
+
+#endif /* SCSI_NCR_TRUST_BIOS_SETTING */
+
+ /*
+ * Prepare initial io register bits for burst length
+ */
+ ncr_init_burst(np, burst_max);
+
+ /*
+ ** Set differential mode and LED support.
+ ** Ignore these features for boards known to use a
+ ** specific GPIO wiring (Tekram only for now).
+ ** Probe initial setting of GPREG and GPCNTL for
+ ** other ones.
+ */
+ if (!nvram || nvram->type != SCSI_NCR_TEKRAM_NVRAM) {
+ switch(driver_setup.diff_support) {
+ case 3:
+ if (INB(nc_gpreg) & 0x08)
+ break;
+ case 2:
+ np->rv_stest2 |= 0x20;
+ break;
+ case 1:
+ np->rv_stest2 |= (np->sv_stest2 & 0x20);
+ break;
+ default:
+ break;
+ }
+ }
+ if ((driver_setup.led_pin ||
+ (nvram && nvram->type == SCSI_NCR_SYMBIOS_NVRAM)) &&
+ !(np->sv_gpcntl & 0x01))
+ np->features |= FE_LED0;
+
+ /*
+ ** Set irq mode.
+ */
+ switch(driver_setup.irqm) {
+ case 2:
+ np->rv_dcntl |= IRQM;
+ break;
+ case 1:
+ np->rv_dcntl |= (np->sv_dcntl & IRQM);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ ** Configure targets according to driver setup.
+ ** If NVRAM present get targets setup from NVRAM.
+ ** Allow to override sync, wide and NOSCAN from
+ ** boot command line.
+ */
+ for (i = 0 ; i < MAX_TARGET ; i++) {
+ tcb_p tp = &np->target[i];
+
+ tp->usrsync = 255;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (nvram) {
+ switch(nvram->type) {
+ case SCSI_NCR_TEKRAM_NVRAM:
+ ncr_Tekram_setup_target(np, i, &nvram->data.Tekram);
+ break;
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ ncr_Symbios_setup_target(np, i, &nvram->data.Symbios);
+ break;
+ }
+ if (driver_setup.use_nvram & 0x2)
+ tp->usrsync = driver_setup.default_sync;
+ if (driver_setup.use_nvram & 0x4)
+ tp->usrwide = driver_setup.max_wide;
+ if (driver_setup.use_nvram & 0x8)
+ tp->usrflag &= ~UF_NOSCAN;
+ }
+ else {
+#else
+ if (1) {
+#endif
+ tp->usrsync = driver_setup.default_sync;
+ tp->usrwide = driver_setup.max_wide;
+ tp->usrtags = driver_setup.default_tags;
+ if (!driver_setup.disconnection)
+ np->target[i].usrflag = UF_NODISC;
+ }
+ }
+
+ /*
+ ** Announce all that stuff to user.
+ */
+
+ i = nvram ? nvram->type : 0;
+ printf(KERN_INFO "%s: %sID %d, Fast-%d%s%s\n", ncr_name(np),
+ i == SCSI_NCR_SYMBIOS_NVRAM ? "Symbios format NVRAM, " :
+ (i == SCSI_NCR_TEKRAM_NVRAM ? "Tekram format NVRAM, " : ""),
+ np->myaddr,
+ np->minsync < 12 ? 40 : (np->minsync < 25 ? 20 : 10),
+ (np->rv_scntl0 & 0xa) ? ", Parity Checking" : ", NO Parity",
+ (np->rv_stest2 & 0x20) ? ", Differential" : "");
+
+ if (bootverbose > 1) {
+ printf ("%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+ "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+ ncr_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl,
+ np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
+
+ printf ("%s: final SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+ "(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+ ncr_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl,
+ np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
+ }
+
+ if (bootverbose && np->paddr2)
+ printf (KERN_INFO "%s: on-board RAM at 0x%lx\n",
+ ncr_name(np), np->paddr2);
+
+ return 0;
+}
+
+
+#ifdef SCSI_NCR_DEBUG_NVRAM
+
+__initfunc(
+void ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
+)
+{
+ int i;
+
+ /* display Symbios nvram host data */
+ printf("%s: HOST ID=%d%s%s%s%s\n",
+ ncr_name(np), nvram->host_id & 0x0f,
+ (nvram->flags & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & SYMBIOS_PARITY_ENABLE) ? " PARITY" :"",
+ (nvram->flags & SYMBIOS_VERBOSE_MSGS) ? " VERSBOSE" :"",
+ (nvram->flags1 & SYMBIOS_SCAN_HI_LO) ? " HI_LO" :"");
+
+ /* display Symbios nvram drive data */
+ for (i = 0 ; i < 15 ; i++) {
+ struct Symbios_target *tn = &nvram->target[i];
+ printf("%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+ ncr_name(np), i,
+ (tn->flags & SYMBIOS_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME) ? " SCAN_BOOT" : "",
+ (tn->flags & SYMBIOS_SCAN_LUNS) ? " SCAN_LUNS" : "",
+ (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ" : "",
+ tn->bus_width,
+ tn->sync_period / 4,
+ tn->timeout);
+ }
+}
+
+static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
+
+__initfunc(
+void ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
+)
+{
+ int i, tags, boot_delay;
+ char *rem;
+
+ /* display Tekram nvram host data */
+ tags = 2 << nvram->max_tags_index;
+ boot_delay = 0;
+ if (nvram->boot_delay_index < 6)
+ boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+ switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+ default:
+ case 0: rem = ""; break;
+ case 1: rem = " REMOVABLE=boot device"; break;
+ case 2: rem = " REMOVABLE=all"; break;
+ }
+
+ printf("%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+ ncr_name(np), nvram->host_id & 0x0f,
+ (nvram->flags1 & SYMBIOS_SCAM_ENABLE) ? " SCAM" :"",
+ (nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES" :"",
+ (nvram->flags & TEKRAM_DRIVES_SUP_1GB) ? " >1GB" :"",
+ (nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET" :"",
+ (nvram->flags & TEKRAM_ACTIVE_NEGATION) ? " ACT_NEG" :"",
+ (nvram->flags & TEKRAM_IMMEDIATE_SEEK) ? " IMM_SEEK" :"",
+ (nvram->flags & TEKRAM_SCAN_LUNS) ? " SCAN_LUNS" :"",
+ (nvram->flags1 & TEKRAM_F2_F6_ENABLED) ? " F2_F6" :"",
+ rem, boot_delay, tags);
+
+ /* display Tekram nvram drive data */
+ for (i = 0; i <= 15; i++) {
+ int sync, j;
+ struct Tekram_target *tn = &nvram->target[i];
+ j = tn->sync_index & 0xf;
+ sync = j < 12 ? Tekram_sync[j] : 255;
+ printf("%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
+ ncr_name(np), i,
+ (tn->flags & TEKRAM_PARITY_CHECK) ? " PARITY" : "",
+ (tn->flags & TEKRAM_SYNC_NEGO) ? " SYNC" : "",
+ (tn->flags & TEKRAM_DISCONNECT_ENABLE) ? " DISC" : "",
+ (tn->flags & TEKRAM_START_CMD) ? " START" : "",
+ (tn->flags & TEKRAM_TAGGED_COMMANDS) ? " TCQ" : "",
+ (tn->flags & TEKRAM_WIDE_NEGO) ? " WIDE" : "",
+ sync);
+ }
+}
+#endif /* SCSI_NCR_DEBUG_NVRAM */
+
+/*
+** Host attach and initialisations.
+**
+** Allocate host data and ncb structure.
+** Request IO region and remap MMIO region.
+** Do chip initialization.
+** If all is OK, install interrupt handling and
+** start the timer daemon.
+*/
+
+__initfunc(
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
+)
+{
+ struct host_data *host_data;
+ ncb_p np;
+ struct Scsi_Host *instance = 0;
+ u_long flags = 0;
+ ncr_nvram *nvram = device->nvram;
+
+printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
+ device->chip.name, unit, device->chip.revision_id, device->slot.base,
+ device->slot.io_port, device->slot.irq);
+
+ /*
+ ** Allocate host_data structure
+ */
+ if (!(instance = scsi_register(tpnt, sizeof(*host_data))))
+ goto attach_error;
+
+ /*
+ ** Initialize structure.
+ */
+ host_data = (struct host_data *) instance->hostdata;
+
+ /*
+ ** Align np and first ccb to 32 boundary for cache line
+ ** bursting when copying the global header.
+ */
+ np = (ncb_p) (((u_long) &host_data->_ncb_data) & NCB_ALIGN_MASK);
+ host_data->ncb = np;
+ bzero (np, sizeof (*np));
+
+ np->ccb = (ccb_p) (((u_long) &host_data->_ccb_data) & CCB_ALIGN_MASK);
+ bzero (np->ccb, sizeof (*np->ccb));
+
+ /*
+ ** Store input informations in the host data structure.
+ */
+ strncpy(np->chip_name, device->chip.name, sizeof(np->chip_name) - 1);
+ np->unit = unit;
+ np->verbose = driver_setup.verbose;
+ sprintf(np->inst_name, "ncr53c%s-%d", np->chip_name, np->unit);
+ np->device_id = device->chip.device_id;
+ np->revision_id = device->chip.revision_id;
+ np->features = device->chip.features;
+ np->clock_divn = device->chip.nr_divisor;
+ np->maxoffs = device->chip.offset_max;
+ np->maxburst = device->chip.burst_max;
+
+ np->script0 =
+ (struct script *) (((u_long) &host_data->script_data) & SCR_ALIGN_MASK);
+ np->scripth0 = &host_data->scripth_data;
+
+ /*
+ ** Initialize timer structure
+ **
+ */
+ init_timer(&np->timer);
+ np->timer.data = (unsigned long) np;
+ np->timer.function = ncr53c8xx_timeout;
+
+ /*
+ ** Try to map the controller chip to
+ ** virtual and physical memory.
+ */
+
+ np->paddr = device->slot.base;
+ np->paddr2 = (np->features & FE_RAM)? device->slot.base_2 : 0;
+
+#ifndef NCR_IOMAPPED
+ np->vaddr = remap_pci_mem((u_long) np->paddr, (u_long) 128);
+ if (!np->vaddr) {
+ printf("%s: can't map memory mapped IO region\n", ncr_name(np));
+ goto attach_error;
+ }
+ else
+ if (bootverbose > 1)
+ printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
+
+ /*
+ ** Make the controller's registers available.
+ ** Now the INB INW INL OUTB OUTW OUTL macros
+ ** can be used safely.
+ */
+
+ np->reg = (struct ncr_reg*) np->vaddr;
+
+#endif /* !defined NCR_IOMAPPED */
+
+ /*
+ ** Try to map the controller chip into iospace.
+ */
+
+ request_region(device->slot.io_port, 128, "ncr53c8xx");
+ np->port = device->slot.io_port;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (nvram) {
+ switch(nvram->type) {
+ case SCSI_NCR_SYMBIOS_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ ncr_display_Symbios_nvram(np, &nvram->data.Symbios);
+#endif
+ break;
+ case SCSI_NCR_TEKRAM_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ ncr_display_Tekram_nvram(np, &nvram->data.Tekram);
+#endif
+ break;
+ default:
+ nvram = 0;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ printf("%s: NVRAM: None or invalid data.\n", ncr_name(np));
+#endif
+ }
+ }
+#endif
+
+ /*
+ ** Do chip dependent initialization.
+ */
+ (void)ncr_prepare_setting(np, nvram);
+
+#ifndef NCR_IOMAPPED
+ if (np->paddr2 && sizeof(struct script) <= 4096) {
+ np->vaddr2 = remap_pci_mem((u_long) np->paddr2, (u_long) 4096);
+ if (!np->vaddr2) {
+ printf("%s: can't map memory mapped IO region\n", ncr_name(np));
+ goto attach_error;
+ }
+ else
+ if (bootverbose > 1)
+ printf("%s: on-board ram mapped at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr2);
+ }
+#endif /* !defined NCR_IOMAPPED */
+
+ /*
+ ** Fill Linux host instance structure
+ */
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+ instance->max_channel = 0;
+ instance->max_id = np->maxwide ? 16 : 8;
+ instance->max_lun = SCSI_NCR_MAX_LUN;
+#endif
+#ifndef NCR_IOMAPPED
+ instance->base = (char *) np->reg;
+#endif
+ instance->irq = device->slot.irq;
+ instance->io_port = device->slot.io_port;
+ instance->n_io_port = 128;
+ instance->dma_channel = 0;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
+ instance->select_queue_depths = ncr53c8xx_select_queue_depths;
+#endif
+
+ /*
+ ** Patch script to physical addresses
+ */
+ ncr_script_fill (&script0, &scripth0);
+
+ np->scripth = np->scripth0;
+ np->p_scripth = vtophys(np->scripth);
+
+ np->script = (np->vaddr2) ? (struct script *) np->vaddr2 : np->script0;
+ np->p_script = (np->vaddr2) ? np->paddr2 : vtophys(np->script0);
+
+ ncr_script_copy_and_bind (np, (ncrcmd *) &script0, (ncrcmd *) np->script0, sizeof(struct script));
+ ncr_script_copy_and_bind (np, (ncrcmd *) &scripth0, (ncrcmd *) np->scripth0, sizeof(struct scripth));
+ np->ccb->p_ccb = vtophys (np->ccb);
+
+ /*
+ ** Patch the script for LED support.
+ */
+
+ if (np->features & FE_LED0) {
+ np->script0->reselect[0] =
+ cpu_to_scr(SCR_REG_REG(gpreg, SCR_OR, 0x01));
+ np->script0->reselect1[0] =
+ cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe));
+ np->script0->reselect2[0] =
+ cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe));
+ }
+
+ /*
+ ** init data structure
+ */
+
+ np->jump_tcb.l_cmd = cpu_to_scr(SCR_JUMP);
+ np->jump_tcb.l_paddr = cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
+
+ /*
+ ** Reset chip.
+ */
+
+ OUTB (nc_istat, SRST);
+ DELAY (1000);
+ OUTB (nc_istat, 0 );
+
+ /*
+ ** Now check the cache handling of the pci chipset.
+ */
+
+ if (ncr_snooptest (np)) {
+ printf ("CACHE INCORRECTLY CONFIGURED.\n");
+ goto attach_error;
+ };
+
+ /*
+ ** Install the interrupt handler.
+ */
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
+#ifdef SCSI_NCR_SHARE_IRQ
+ if (bootverbose > 1)
+ printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
+ ncr_name(np), device->slot.irq, (u_long) np);
+ if (request_irq(device->slot.irq, ncr53c8xx_intr,
+ SA_INTERRUPT|SA_SHIRQ, "ncr53c8xx", np)) {
+#else
+ if (request_irq(device->slot.irq, ncr53c8xx_intr,
+ SA_INTERRUPT, "ncr53c8xx", np)) {
+#endif
+#else
+ if (request_irq(device->slot.irq, ncr53c8xx_intr,
+ SA_INTERRUPT, "ncr53c8xx")) {
+#endif
+ printf("%s: request irq %d failure\n", ncr_name(np), device->slot.irq);
+ goto attach_error;
+ }
+ np->irq = device->slot.irq;
+
+ /*
+ ** After SCSI devices have been opened, we cannot
+ ** reset the bus safely, so we do it here.
+ ** Interrupt handler does the real work.
+ ** Process the reset exception,
+ ** if interrupts are not enabled yet.
+ ** Then enable disconnects.
+ */
+ save_flags(flags); cli();
+ if (ncr_reset_scsi_bus(np, 0, driver_setup.settle_delay) != 0) {
+ printf("%s: FATAL ERROR: CHECK SCSI BUS - CABLES, TERMINATION, DEVICE POWER etc.!\n", ncr_name(np));
+ restore_flags(flags);
+ goto attach_error;
+ }
+ ncr_exception (np);
+ restore_flags(flags);
+
+ np->disc = 1;
+
+ /*
+ ** The middle-level SCSI driver does not
+ ** wait devices to settle.
+ ** Wait synchronously if more than 2 seconds.
+ */
+ if (driver_setup.settle_delay > 2) {
+ printf("%s: waiting %d seconds for scsi devices to settle...\n",
+ ncr_name(np), driver_setup.settle_delay);
+ DELAY(1000000UL * driver_setup.settle_delay);
+ }
+
+ /*
+ ** Now let the generic SCSI driver
+ ** look for the SCSI devices on the bus ..
+ */
+
+ /*
+ ** start the timeout daemon
+ */
+ np->lasttime=0;
+ ncr_timeout (np);
+
+ /*
+ ** use SIMPLE TAG messages by default
+ */
+#ifdef SCSI_NCR_ALWAYS_SIMPLE_TAG
+ np->order = M_SIMPLE_TAG;
+#endif
+
+ /*
+ ** Done.
+ */
+ if (!the_template) {
+ the_template = instance->hostt;
+ first_host = instance;
+ }
+
+ return 0;
+
+attach_error:
+ if (!instance) return -1;
+ printf("%s: detaching...\n", ncr_name(np));
+#ifndef NCR_IOMAPPED
+ if (np->vaddr) {
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
+#endif
+ unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128);
+ }
+ if (np->vaddr2) {
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr2, 4096);
+#endif
+ unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096);
+ }
+#endif
+ if (np->port) {
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
+#endif
+ release_region(np->port, 128);
+ }
+ if (np->irq) {
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: freeing irq %d\n", ncr_name(np), np->irq);
+#endif
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
+ free_irq(np->irq, np);
+#else
+ free_irq(np->irq);
+#endif
+ }
+ scsi_unregister(instance);
+
+ return -1;
+ }
+
+/*==========================================================
+**
+**
+** Start execution of a SCSI command.
+** This is called from the generic SCSI driver.
+**
+**
+**==========================================================
+*/
+int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
+{
+ struct Scsi_Host *host = cmd->host;
+/* Scsi_Device *device = cmd->device; */
+ struct host_data *host_data = (struct host_data *) host->hostdata;
+ ncb_p np = host_data->ncb;
+ tcb_p tp = &np->target[cmd->target];
+
+ ccb_p cp;
+ lcb_p lp;
+
+ int segments;
+ u_char qidx, nego, idmsg, *msgptr;
+ u_int msglen, msglen2;
+ u_long flags;
+ int xfer_direction;
+
+ cmd->scsi_done = done;
+ cmd->host_scribble = NULL;
+ cmd->SCp.ptr = NULL;
+ cmd->SCp.buffer = NULL;
+
+ /*---------------------------------------------
+ **
+ ** Some shortcuts ...
+ **
+ **---------------------------------------------
+ */
+ if ((cmd->target == np->myaddr ) ||
+ (cmd->target >= MAX_TARGET) ||
+ (cmd->lun >= MAX_LUN )) {
+ return(DID_BAD_TARGET);
+ }
+
+ /*---------------------------------------------
+ **
+ ** Complete the 1st TEST UNIT READY command
+ ** with error condition if the device is
+ ** flagged NOSCAN, in order to speed up
+ ** the boot.
+ **
+ **---------------------------------------------
+ */
+ if (cmd->cmnd[0] == 0 && (tp->usrflag & UF_NOSCAN)) {
+ tp->usrflag &= ~UF_NOSCAN;
+ return DID_BAD_TARGET;
+ }
+
+ if (DEBUG_FLAGS & DEBUG_TINY) {
+ PRINT_ADDR(cmd);
+ printf ("CMD=%x ", cmd->cmnd[0]);
+ }
+
+ /*---------------------------------------------------
+ **
+ ** Assign a ccb / bind cmd.
+ ** If resetting, shorten settle_time if necessary
+ ** in order to avoid spurious timeouts.
+ ** If resetting or no free ccb,
+ ** insert cmd into the waiting list.
+ **
+ **----------------------------------------------------
+ */
+ save_flags(flags); cli();
+
+ if (np->settle_time && cmd->timeout_per_command >= HZ &&
+ np->settle_time > jiffies + cmd->timeout_per_command - HZ) {
+ np->settle_time = jiffies + cmd->timeout_per_command - HZ;
+ }
+
+ if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
+ insert_into_waiting_list(np, cmd);
+ restore_flags(flags);
+ return(DID_OK);
+ }
+ cp->cmd = cmd;
+
+ /*---------------------------------------------------
+ **
+ ** Enable tagged queue if asked by scsi ioctl
+ **
+ **----------------------------------------------------
+ */
+ if (!tp->usrtags && cmd->device && cmd->device->tagged_queue) {
+ tp->usrtags = SCSI_NCR_MAX_TAGS;
+ ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
+ }
+
+ /*---------------------------------------------------
+ **
+ ** timestamp
+ **
+ **----------------------------------------------------
+ */
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+ bzero (&cp->phys.header.stamp, sizeof (struct tstamp));
+ cp->phys.header.stamp.start = jiffies;
+#endif
+
+ /*----------------------------------------------------
+ **
+ ** Get device quirks from a speciality table.
+ **
+ ** @GENSCSI@
+ ** This should be a part of the device table
+ ** in "scsi_conf.c".
+ **
+ **----------------------------------------------------
+ */
+ if (tp->quirks & QUIRK_UPDATE) {
+ tp->quirks = ncr_lookup ((char*) &tp->inqdata[0]);
+#ifndef NCR_GETCC_WITHMSG
+ if (tp->quirks) {
+ PRINT_ADDR(cmd);
+ printf ("quirks=%x.\n", tp->quirks);
+ }
+#endif
+ }
+
+ /*---------------------------------------------------
+ **
+ ** negotiation required?
+ **
+ ** Only SCSI-II devices.
+ ** To negotiate with SCSI-I devices is dangerous, since
+ ** Synchronous Negotiation protocol is optional, and
+ ** INQUIRY data do not contains capabilities in byte 7.
+ **----------------------------------------------------
+ */
+
+ nego = 0;
+
+ if (cmd->lun == 0 && !tp->nego_cp &&
+ (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
+ /*
+ ** negotiate wide transfers ?
+ */
+
+ if (!tp->widedone) {
+ if (tp->inqdata[7] & INQ7_WIDE16) {
+ nego = NS_WIDE;
+ } else
+ tp->widedone=1;
+ };
+
+ /*
+ ** negotiate synchronous transfers?
+ */
+
+ if (!nego && !tp->period) {
+ if ( 1
+#if defined (CDROM_ASYNC)
+ && ((tp->inqdata[0] & 0x1f) != 5)
+#endif
+ && (tp->inqdata[7] & INQ7_SYNC)) {
+ nego = NS_SYNC;
+ } else {
+ tp->period =0xffff;
+ tp->sval = 0xe0;
+ PRINT_ADDR(cmd);
+ printf ("asynchronous.\n");
+ };
+ };
+
+ /*
+ ** remember nego is pending for the target.
+ ** Avoid to start a nego for all queued commands
+ ** when tagged command queuing is enabled.
+ */
+
+ if (nego)
+ tp->nego_cp = cp;
+ };
+
+ /*---------------------------------------------------
+ **
+ ** choose a new tag ...
+ **
+ **----------------------------------------------------
+ */
+
+ if ((lp = tp->lp[cmd->lun]) && (lp->usetags)) {
+ /*
+ ** assign a tag to this ccb!
+ */
+ while (!cp->tag) {
+ ccb_p cp2 = lp->next_ccb;
+ lp->lasttag = lp->lasttag % 255 + 1;
+ while (cp2 && cp2->tag != lp->lasttag)
+ cp2 = cp2->next_ccb;
+ if (cp2) continue;
+ cp->tag=lp->lasttag;
+ if (DEBUG_FLAGS & DEBUG_TAGS) {
+ PRINT_ADDR(cmd);
+ printf ("using tag #%d.\n", cp->tag);
+ }
+ }
+ } else {
+ cp->tag=0;
+ }
+
+ /*----------------------------------------------------
+ **
+ ** Build the identify / tag / sdtr message
+ **
+ **----------------------------------------------------
+ */
+
+ idmsg = M_IDENTIFY | cmd->lun;
+
+ if (cp != np->ccb && ((np->disc && !(tp->usrflag & UF_NODISC)) || cp->tag))
+ idmsg |= 0x40;
+
+ msgptr = cp->scsi_smsg;
+ msglen = 0;
+ msgptr[msglen++] = idmsg;
+
+ if (cp->tag) {
+ char tag;
+
+ tag = np->order;
+ if (tag == 0) {
+ /*
+ ** Ordered write ops, unordered read ops.
+ */
+ switch (cmd->cmnd[0]) {
+ case 0x08: /* READ_SMALL (6) */
+ case 0x28: /* READ_BIG (10) */
+ case 0xa8: /* READ_HUGE (12) */
+ tag = M_SIMPLE_TAG;
+ break;
+ default:
+ tag = M_ORDERED_TAG;
+ }
+ }
+ /*
+ ** Have to force ordered tag to avoid timeouts
+ */
+ if ((lp = tp->lp[cmd->lun]) && (lp->force_ordered_tag)) {
+ tag = M_ORDERED_TAG;
+ lp->force_ordered_tag = 0;
+ if (DEBUG_FLAGS & DEBUG_TAGS) {
+ PRINT_ADDR(cmd);
+ printf ("Ordered Queue Tag forced\n");
+ }
+ }
+ msgptr[msglen++] = tag;
+ msgptr[msglen++] = cp -> tag;
+ }
+
+ switch (nego) {
+ case NS_SYNC:
+ msgptr[msglen++] = M_EXTENDED;
+ msgptr[msglen++] = 3;
+ msgptr[msglen++] = M_X_SYNC_REQ;
+ msgptr[msglen++] = tp->maxoffs ? tp->minsync : 0;
+ msgptr[msglen++] = tp->maxoffs;
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ PRINT_ADDR(cp->cmd);
+ printf ("sync msgout: ");
+ ncr_show_msg (&cp->scsi_smsg [msglen-5]);
+ printf (".\n");
+ };
+ break;
+ case NS_WIDE:
+ msgptr[msglen++] = M_EXTENDED;
+ msgptr[msglen++] = 2;
+ msgptr[msglen++] = M_X_WIDE_REQ;
+ msgptr[msglen++] = tp->usrwide;
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ PRINT_ADDR(cp->cmd);
+ printf ("wide msgout: ");
+ ncr_show_msg (&cp->scsi_smsg [msglen-4]);
+ printf (".\n");
+ };
+ break;
+ };
+
+ /*----------------------------------------------------
+ **
+ ** Build the identify message for getcc.
+ **
+ **----------------------------------------------------
+ */
+
+ cp -> scsi_smsg2 [0] = idmsg;
+ msglen2 = 1;
+
+ /*----------------------------------------------------
+ **
+ ** Build the data descriptors
+ **
+ **----------------------------------------------------
+ */
+
+ segments = ncr_scatter (cp, cp->cmd);
+
+ if (segments < 0) {
+ ncr_free_ccb(np, cp, cmd->target, cmd->lun);
+ restore_flags(flags);
+ return(DID_ERROR);
+ }
+
+ /*----------------------------------------------------
+ **
+ ** Guess xfer direction.
+ ** Spare some CPU by testing here frequently opcode.
+ **
+ **----------------------------------------------------
+ */
+ switch((int) cmd->cmnd[0]) {
+ case 0x08: /* READ(6) 08 */
+ case 0x28: /* READ(10) 28 */
+ case 0xA8: /* READ(12) A8 */
+ xfer_direction = XferIn;
+ break;
+ case 0x0A: /* WRITE(6) 0A */
+ case 0x2A: /* WRITE(10) 2A */
+ case 0xAA: /* WRITE(12) AA */
+ xfer_direction = XferOut;
+ break;
+ default:
+ xfer_direction = guess_xfer_direction((int) cmd->cmnd[0]);
+ break;
+ }
+
+ /*----------------------------------------------------
+ **
+ ** Set the SAVED_POINTER.
+ **
+ **----------------------------------------------------
+ */
+
+ cp->segments = segments;
+ if (!cp->data_len)
+ xfer_direction = XferNone;
+
+ switch (xfer_direction) {
+ u_long endp;
+ default:
+ case XferBoth:
+ cp->phys.header.savep =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, data_io));
+ cp->phys.header.goalp = cp->phys.header.savep;
+ break;
+ case XferIn:
+ endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16;
+ cp->phys.header.goalp = cpu_to_scr(endp + 8);
+ cp->phys.header.savep = cpu_to_scr(endp - segments*16);
+ break;
+ case XferOut:
+ endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
+ cp->phys.header.goalp = cpu_to_scr(endp + 8);
+ cp->phys.header.savep = cpu_to_scr(endp - segments*16);
+ break;
+ case XferNone:
+ cp->phys.header.savep =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, no_data));
+ cp->phys.header.goalp = cp->phys.header.savep;
+ break;
+ }
+
+ cp->phys.header.lastp = cp->phys.header.savep;
+
+ /*----------------------------------------------------
+ **
+ ** fill in ccb
+ **
+ **----------------------------------------------------
+ **
+ **
+ ** physical -> virtual backlink
+ ** Generic SCSI command
+ */
+ cp->phys.header.cp = cp;
+ /*
+ ** Startqueue
+ */
+ cp->phys.header.launch.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
+ cp->phys.header.launch.l_cmd = cpu_to_scr(SCR_JUMP);
+ /*
+ ** select
+ */
+ cp->phys.select.sel_id = cmd->target;
+ cp->phys.select.sel_scntl3 = tp->wval;
+ cp->phys.select.sel_sxfer = tp->sval;
+ /*
+ ** message
+ */
+ cp->phys.smsg.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg));
+ cp->phys.smsg.size = cpu_to_scr(msglen);
+
+ cp->phys.smsg2.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg2));
+ cp->phys.smsg2.size = cpu_to_scr(msglen2);
+ /*
+ ** command
+ */
+ cp->phys.cmd.addr = cpu_to_scr(vtophys (&cmd->cmnd[0]));
+ cp->phys.cmd.size = cpu_to_scr(cmd->cmd_len);
+ /*
+ ** sense command
+ */
+ cp->phys.scmd.addr = cpu_to_scr(CCB_PHYS (cp, sensecmd));
+ cp->phys.scmd.size = cpu_to_scr(6);
+ /*
+ ** patch requested size into sense command
+ */
+ cp->sensecmd[0] = 0x03;
+ cp->sensecmd[1] = cmd->lun << 5;
+ cp->sensecmd[4] = sizeof(cmd->sense_buffer);
+ /*
+ ** sense data
+ */
+ cp->phys.sense.addr =
+ cpu_to_scr(vtophys (&cmd->sense_buffer[0]));
+ cp->phys.sense.size = cpu_to_scr(sizeof(cmd->sense_buffer));
+ /*
+ ** status
+ */
+ cp->actualquirks = tp->quirks;
+ cp->host_status = nego ? HS_NEGOTIATE : HS_BUSY;
+ cp->scsi_status = S_ILLEGAL;
+ cp->parity_status = 0;
+
+ cp->xerr_status = XE_OK;
+ cp->sync_status = tp->sval;
+ cp->nego_status = nego;
+ cp->wide_status = tp->wval;
+
+ /*----------------------------------------------------
+ **
+ ** Critical region: start this job.
+ **
+ **----------------------------------------------------
+ */
+
+ /*
+ ** reselect pattern and activate this job.
+ */
+
+ cp->jump_ccb.l_cmd =
+ cpu_to_scr((SCR_JUMP ^ IFFALSE (DATA (cp->tag))));
+
+ /* Compute a time limit greater than the middle-level driver one */
+ if (cmd->timeout_per_command > 0)
+ cp->tlimit = jiffies + cmd->timeout_per_command + NCR_TIMEOUT_INCREASE;
+ else
+ cp->tlimit = jiffies + 3600 * HZ; /* No timeout=one hour */
+ cp->magic = CCB_MAGIC;
+
+ /*
+ ** insert into start queue.
+ */
+
+ qidx = np->squeueput + 1;
+ if (qidx >= MAX_START) qidx=0;
+ np->squeue [qidx ] = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+ np->squeue [np->squeueput] = cpu_to_scr(CCB_PHYS (cp, phys));
+ np->squeueput = qidx;
+
+ if(DEBUG_FLAGS & DEBUG_QUEUE)
+ printf ("%s: queuepos=%d tryoffset=%d.\n", ncr_name (np),
+ np->squeueput,
+ (unsigned)(scr_to_cpu(np->script->startpos[0]) -
+ (NCB_SCRIPTH_PHYS (np, tryloop))));
+
+ /*
+ ** Script processor may be waiting for reselect.
+ ** Wake it up.
+ */
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if (!np->stalling)
+#endif
+ OUTB (nc_istat, SIGP);
+
+ /*
+ ** and reenable interrupts
+ */
+ restore_flags(flags);
+
+ /*
+ ** Command is successfully queued.
+ */
+
+ return(DID_OK);
+}
+
+/*==========================================================
+**
+**
+** Start reset process.
+** If reset in progress do nothing.
+** The interrupt handler will reinitialize the chip.
+** The timeout handler will wait for settle_time before
+** clearing it and so resuming command processing.
+**
+**
+**==========================================================
+*/
+static void ncr_start_reset(ncb_p np, int settle_delay)
+{
+ u_long flags;
+
+ save_flags(flags); cli();
+
+ if (!np->settle_time) {
+ (void) ncr_reset_scsi_bus(np, 1, settle_delay);
+ }
+ restore_flags(flags);
+}
+
+static int ncr_reset_scsi_bus(ncb_p np, int enab_int, int settle_delay)
+{
+ u_int32 term;
+ int retv = 0;
+
+ np->settle_time = jiffies + settle_delay * HZ;
+
+ if (bootverbose > 1)
+ printf("%s: resetting, "
+ "command processing suspended for %d seconds\n",
+ ncr_name(np), settle_delay);
+
+ OUTB (nc_istat, SRST);
+ DELAY (1000);
+ OUTB (nc_istat, 0);
+ if (enab_int)
+ OUTW (nc_sien, RST);
+ /*
+ ** Enable Tolerant, reset IRQD if present and
+ ** properly set IRQ mode, prior to resetting the bus.
+ */
+ OUTB (nc_stest3, TE);
+ OUTB (nc_dcntl, (np->rv_dcntl & IRQM));
+ OUTB (nc_scntl1, CRST);
+ DELAY (100);
+
+ if (!driver_setup.bus_check)
+ goto out;
+ /*
+ ** Check for no terminators or SCSI bus shorts to ground.
+ ** Read SCSI data bus, data parity bits and control signals.
+ ** We are expecting RESET to be TRUE and other signals to be
+ ** FALSE.
+ */
+ term = INB(nc_sstat0); /* rst, sdp0 */
+ term = ((term & 2) << 7) + ((term & 1) << 16);
+ term |= ((INB(nc_sstat2) & 0x01) << 25) | /* sdp1 */
+ (INW(nc_sbdl) << 9) | /* d15-0 */
+ INB(nc_sbcl); /* req, ack, bsy, sel, atn, msg, cd, io */
+
+ if (!(np->features & FE_WIDE))
+ term &= 0x3ffff;
+
+ if (term != (2<<7)) {
+ printf("%s: suspicious SCSI data while resetting the BUS.\n",
+ ncr_name(np));
+ printf("%s: %sdp0,d7-0,rst,req,ack,bsy,sel,atn,msg,c/d,i/o = "
+ "0x%lx, expecting 0x%lx\n",
+ ncr_name(np),
+ (np->features & FE_WIDE) ? "dp1,d15-8," : "",
+ (u_long)term, (u_long)(2<<7));
+ if (driver_setup.bus_check == 1)
+ retv = 1;
+ }
+out:
+ OUTB (nc_scntl1, 0);
+ return retv;
+}
+
+/*==========================================================
+**
+**
+** Reset the SCSI BUS.
+** This is called from the generic SCSI driver.
+**
+**
+**==========================================================
+*/
+int ncr_reset_bus (Scsi_Cmnd *cmd, int sync_reset)
+{
+ struct Scsi_Host *host = cmd->host;
+/* Scsi_Device *device = cmd->device; */
+ struct host_data *host_data = (struct host_data *) host->hostdata;
+ ncb_p np = host_data->ncb;
+ ccb_p cp;
+ u_long flags;
+ int found;
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if (np->stalling)
+ np->stalling = 0;
+#endif
+
+ save_flags(flags); cli();
+/*
+ * Return immediately if reset is in progress.
+ */
+ if (np->settle_time) {
+ restore_flags(flags);
+ return SCSI_RESET_PUNT;
+ }
+/*
+ * Start the reset process.
+ * The script processor is then assumed to be stopped.
+ * Commands will now be queued in the waiting list until a settle
+ * delay of 2 seconds will be completed.
+ */
+ ncr_start_reset(np, driver_setup.settle_delay);
+/*
+ * First, look in the wakeup list
+ */
+ for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
+ /*
+ ** look for the ccb of this command.
+ */
+ if (cp->host_status == HS_IDLE) continue;
+ if (cp->cmd == cmd) {
+ found = 1;
+ break;
+ }
+ }
+/*
+ * Then, look in the waiting list
+ */
+ if (!found && retrieve_from_waiting_list(0, np, cmd))
+ found = 1;
+/*
+ * Wake-up all awaiting commands with DID_RESET.
+ */
+ reset_waiting_list(np);
+/*
+ * Wake-up all pending commands with HS_RESET -> DID_RESET.
+ */
+ ncr_wakeup(np, HS_RESET);
+/*
+ * If the involved command was not in a driver queue, and the
+ * scsi driver told us reset is synchronous, and the command is not
+ * currently in the waiting list, complete it with DID_RESET status,
+ * in order to keep it alive.
+ */
+ if (!found && sync_reset && !retrieve_from_waiting_list(0, np, cmd)) {
+ cmd->result = ScsiResult(DID_RESET, 0);
+ cmd->scsi_done(cmd);
+ }
+
+ restore_flags(flags);
+
+ return SCSI_RESET_SUCCESS;
+}
+
+/*==========================================================
+**
+**
+** Abort an SCSI command.
+** This is called from the generic SCSI driver.
+**
+**
+**==========================================================
+*/
+static int ncr_abort_command (Scsi_Cmnd *cmd)
+{
+ struct Scsi_Host *host = cmd->host;
+/* Scsi_Device *device = cmd->device; */
+ struct host_data *host_data = (struct host_data *) host->hostdata;
+ ncb_p np = host_data->ncb;
+ ccb_p cp;
+ u_long flags;
+ int found;
+ int retv;
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if (np->stalling == 2)
+ np->stalling = 0;
+#endif
+
+ save_flags(flags); cli();
+/*
+ * First, look for the scsi command in the waiting list
+ */
+ if (remove_from_waiting_list(np, cmd)) {
+ cmd->result = ScsiResult(DID_ABORT, 0);
+ cmd->scsi_done(cmd);
+ restore_flags(flags);
+ return SCSI_ABORT_SUCCESS;
+ }
+
+/*
+ * Then, look in the wakeup list
+ */
+ for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
+ /*
+ ** look for the ccb of this command.
+ */
+ if (cp->host_status == HS_IDLE) continue;
+ if (cp->cmd == cmd) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ restore_flags(flags);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ if (np->settle_time) {
+ restore_flags(flags);
+ return SCSI_ABORT_SNOOZE;
+ }
+
+ /*
+ ** Disable reselect.
+ ** Remove it from startqueue.
+ ** Set cp->tlimit to 0. The ncr_timeout() handler will use
+ ** this condition in order to complete the canceled command
+ ** after the script skipped the ccb, if necessary.
+ */
+ cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
+ if (cp->phys.header.launch.l_paddr ==
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, select))) {
+ printf ("%s: abort ccb=%p (skip)\n", ncr_name (np), cp);
+ cp->phys.header.launch.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, skip));
+ }
+
+ cp->tlimit = 0;
+ retv = SCSI_ABORT_PENDING;
+
+ /*
+ ** If there are no requests, the script
+ ** processor will sleep on SEL_WAIT_RESEL.
+ ** Let's wake it up, since it may have to work.
+ */
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if (!np->stalling)
+#endif
+ OUTB (nc_istat, SIGP);
+
+ restore_flags(flags);
+
+ return retv;
+}
+
+/*==========================================================
+**
+** Linux release module stuff.
+**
+** Called before unloading the module
+** Detach the host.
+** We have to free resources and halt the NCR chip
+**
+**==========================================================
+*/
+
+#ifdef MODULE
+static int ncr_detach(ncb_p np)
+{
+ ccb_p cp;
+ tcb_p tp;
+ lcb_p lp;
+ int target, lun;
+ int i;
+
+ printf("%s: releasing host resources\n", ncr_name(np));
+
+/*
+** Stop the ncr_timeout process
+** Set release_stage to 1 and wait that ncr_timeout() set it to 2.
+*/
+
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: stopping the timer\n", ncr_name(np));
+#endif
+ np->release_stage = 1;
+ for (i = 50 ; i && np->release_stage != 2 ; i--) DELAY(100000);
+ if (np->release_stage != 2)
+ printf("%s: the timer seems to be already stopped\n", ncr_name(np));
+ else np->release_stage = 2;
+
+/*
+** Disable chip interrupts
+*/
+
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: disabling chip interrupts\n", ncr_name(np));
+#endif
+ OUTW (nc_sien , 0);
+ OUTB (nc_dien , 0);
+
+/*
+** Free irq
+*/
+
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: freeing irq %d\n", ncr_name(np), np->irq);
+#endif
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
+ free_irq(np->irq, np);
+#else
+ free_irq(np->irq);
+#endif
+
+ /*
+ ** Reset NCR chip
+ ** Restore bios setting for automatic clock detection.
+ */
+
+ printf("%s: resetting chip\n", ncr_name(np));
+ OUTB (nc_istat, SRST);
+ DELAY (1000);
+ OUTB (nc_istat, 0 );
+
+ OUTB(nc_dmode, np->sv_dmode);
+ OUTB(nc_dcntl, np->sv_dcntl);
+ OUTB(nc_ctest3, np->sv_ctest3);
+ OUTB(nc_ctest4, np->sv_ctest4);
+ OUTB(nc_ctest5, np->sv_ctest5);
+ OUTB(nc_gpcntl, np->sv_gpcntl);
+ OUTB(nc_stest2, np->sv_stest2);
+
+ ncr_selectclock(np, np->sv_scntl3);
+
+ /*
+ ** Release Memory mapped IO region and IO mapped region
+ */
+
+#ifndef NCR_IOMAPPED
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
+#endif
+ unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128);
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr2, 4096);
+#endif
+ unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096);
+#endif
+
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
+#endif
+ release_region(np->port, 128);
+
+ /*
+ ** Free allocated ccb(s)
+ */
+
+ while ((cp=np->ccb->link_ccb) != NULL) {
+ np->ccb->link_ccb = cp->link_ccb;
+ if (cp->host_status) {
+ printf("%s: shall free an active ccb (host_status=%d)\n",
+ ncr_name(np), cp->host_status);
+ }
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: freeing ccb (%lx)\n", ncr_name(np), (u_long) cp);
+#endif
+ m_free(cp, sizeof(*cp));
+ }
+
+ /*
+ ** Free allocated tp(s)
+ */
+
+ for (target = 0; target < MAX_TARGET ; target++) {
+ tp=&np->target[target];
+ for (lun = 0 ; lun < MAX_LUN ; lun++) {
+ lp = tp->lp[lun];
+ if (lp) {
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: freeing lp (%lx)\n", ncr_name(np), (u_long) lp);
+#endif
+ m_free(lp, sizeof(*lp));
+ }
+ }
+ }
+
+ printf("%s: host resources successfully released\n", ncr_name(np));
+
+ return 1;
+}
+#endif
+
+/*==========================================================
+**
+**
+** Complete execution of a SCSI command.
+** Signal completion to the generic SCSI driver.
+**
+**
+**==========================================================
+*/
+
+void ncr_complete (ncb_p np, ccb_p cp)
+{
+ Scsi_Cmnd *cmd;
+ tcb_p tp;
+ lcb_p lp;
+
+ /*
+ ** Sanity check
+ */
+
+ if (!cp || (cp->magic!=CCB_MAGIC) || !cp->cmd) return;
+ cp->magic = 1;
+ cp->tlimit= 0;
+ cmd = cp->cmd;
+
+ /*
+ ** No Reselect anymore.
+ */
+ cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
+
+ /*
+ ** No starting.
+ */
+ cp->phys.header.launch.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+
+ /*
+ ** timestamp
+ ** Optional, spare some CPU time
+ */
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+ ncb_profile (np, cp);
+#endif
+
+ if (DEBUG_FLAGS & DEBUG_TINY)
+ printf ("CCB=%lx STAT=%x/%x\n", (unsigned long)cp & 0xfff,
+ cp->host_status,cp->scsi_status);
+
+ cmd = cp->cmd;
+ cp->cmd = NULL;
+ tp = &np->target[cmd->target];
+ lp = tp->lp[cmd->lun];
+
+ /*
+ ** We donnot queue more than 1 ccb per target
+ ** with negotiation at any time. If this ccb was
+ ** used for negotiation, clear this info in the tcb.
+ */
+
+ if (cp == tp->nego_cp)
+ tp->nego_cp = 0;
+
+ /*
+ ** Check for parity errors.
+ */
+
+ if (cp->parity_status) {
+ PRINT_ADDR(cmd);
+ printf ("%d parity error(s), fallback.\n", cp->parity_status);
+ /*
+ ** fallback to asynch transfer.
+ */
+ tp->usrsync=255;
+ tp->period = 0;
+ }
+
+ /*
+ ** Check for extended errors.
+ */
+
+ if (cp->xerr_status != XE_OK) {
+ PRINT_ADDR(cmd);
+ switch (cp->xerr_status) {
+ case XE_EXTRA_DATA:
+ printf ("extraneous data discarded.\n");
+ break;
+ case XE_BAD_PHASE:
+ printf ("illegal scsi phase (4/5).\n");
+ break;
+ default:
+ printf ("extended error %d.\n", cp->xerr_status);
+ break;
+ }
+ if (cp->host_status==HS_COMPLETE)
+ cp->host_status = HS_FAIL;
+ }
+
+ /*
+ ** Check the status.
+ */
+ if ( (cp->host_status == HS_COMPLETE)
+ && (cp->scsi_status == S_GOOD ||
+ cp->scsi_status == S_COND_MET)) {
+ /*
+ ** All went well (GOOD status).
+ ** CONDITION MET status is returned on
+ ** `Pre-Fetch' or `Search data' success.
+ */
+ cmd->result = ScsiResult(DID_OK, cp->scsi_status);
+
+ /*
+ ** if (cp->phys.header.lastp != cp->phys.header.goalp)...
+ **
+ ** @RESID@
+ ** Could dig out the correct value for resid,
+ ** but it would be quite complicated.
+ **
+ ** The ah1542.c driver sets it to 0 too ...
+ */
+
+ /*
+ ** Try to assign a ccb to this nexus
+ */
+ ncr_alloc_ccb (np, cmd->target, cmd->lun);
+
+ /*
+ ** On inquire cmd (0x12) save some data.
+ ** Clear questionnable capacities.
+ */
+ if (cmd->lun == 0 && cmd->cmnd[0] == 0x12) {
+ if (np->unit < SCSI_NCR_MAX_HOST) {
+ if (driver_setup.force_sync_nego)
+ ((char *) cmd->request_buffer)[7] |= INQ7_SYNC;
+ else
+ ((char *) cmd->request_buffer)[7] &=
+ (target_capabilities[np->unit].and_map[cmd->target]);
+ }
+ bcopy ( cmd->request_buffer,
+ &tp->inqdata,
+ sizeof (tp->inqdata));
+
+ /*
+ ** set number of tags
+ */
+ ncr_setmaxtags (np, tp, driver_setup.default_tags);
+ /*
+ ** prepare negotiation of synch and wide.
+ */
+ ncr_negotiate (np, tp);
+
+ /*
+ ** force quirks update before next command start
+ */
+ tp->quirks |= QUIRK_UPDATE;
+ }
+
+ /*
+ ** Announce changes to the generic driver.
+ */
+ if (lp) {
+ ncr_settags (tp, lp);
+ if (lp->reqlink != lp->actlink)
+ ncr_opennings (np, lp, cmd);
+ };
+
+ tp->bytes += cp->data_len;
+ tp->transfers ++;
+
+ /*
+ ** If tags was reduced due to queue full,
+ ** increase tags if 100 good status received.
+ */
+ if (tp->numtags < tp->maxtags) {
+ ++tp->num_good;
+ if (tp->num_good >= 100) {
+ tp->num_good = 0;
+ ++tp->numtags;
+ if (tp->numtags == 1) {
+ PRINT_ADDR(cmd);
+ printf("tagged command queueing resumed\n");
+ }
+ }
+ }
+ } else if ((cp->host_status == HS_COMPLETE)
+ && (cp->scsi_status == (S_SENSE|S_GOOD) ||
+ cp->scsi_status == (S_SENSE|S_CHECK_COND))) {
+
+ /*
+ ** Check condition code
+ */
+ cmd->result = ScsiResult(DID_OK, S_CHECK_COND);
+
+ if (DEBUG_FLAGS & (DEBUG_RESULT|DEBUG_TINY)) {
+ u_char * p = (u_char*) & cmd->sense_buffer;
+ int i;
+ printf ("\n%s: sense data:", ncr_name (np));
+ for (i=0; i<14; i++) printf (" %x", *p++);
+ printf (".\n");
+ }
+
+ } else if ((cp->host_status == HS_COMPLETE)
+ && (cp->scsi_status == S_BUSY ||
+ cp->scsi_status == S_CONFLICT)) {
+
+ /*
+ ** Target is busy.
+ */
+ cmd->result = ScsiResult(DID_OK, cp->scsi_status);
+
+ } else if ((cp->host_status == HS_COMPLETE)
+ && (cp->scsi_status == S_QUEUE_FULL)) {
+
+ /*
+ ** Target is stuffed.
+ */
+ cmd->result = ScsiResult(DID_OK, cp->scsi_status);
+
+ /*
+ ** Suspend tagged queuing and start good status counter.
+ ** Announce changes to the generic driver.
+ */
+ if (tp->numtags) {
+ PRINT_ADDR(cmd);
+ printf("QUEUE FULL! suspending tagged command queueing\n");
+ tp->numtags = 0;
+ tp->num_good = 0;
+ if (lp) {
+ ncr_settags (tp, lp);
+ if (lp->reqlink != lp->actlink)
+ ncr_opennings (np, lp, cmd);
+ };
+ }
+ } else if ((cp->host_status == HS_SEL_TIMEOUT)
+ || (cp->host_status == HS_TIMEOUT)) {
+
+ /*
+ ** No response
+ */
+ cmd->result = ScsiResult(DID_TIME_OUT, cp->scsi_status);
+
+ } else if (cp->host_status == HS_RESET) {
+
+ /*
+ ** SCSI bus reset
+ */
+ cmd->result = ScsiResult(DID_RESET, cp->scsi_status);
+
+ } else if (cp->host_status == HS_ABORTED) {
+
+ /*
+ ** Transfer aborted
+ */
+ cmd->result = ScsiResult(DID_ABORT, cp->scsi_status);
+
+ } else {
+
+ /*
+ ** Other protocol messes
+ */
+ PRINT_ADDR(cmd);
+ printf ("COMMAND FAILED (%x %x) @%p.\n",
+ cp->host_status, cp->scsi_status, cp);
+
+ cmd->result = ScsiResult(DID_ERROR, cp->scsi_status);
+ }
+
+ /*
+ ** trace output
+ */
+
+ if (tp->usrflag & UF_TRACE) {
+ u_char * p;
+ int i;
+ PRINT_ADDR(cmd);
+ printf (" CMD:");
+ p = (u_char*) &cmd->cmnd[0];
+ for (i=0; i<cmd->cmd_len; i++) printf (" %x", *p++);
+
+ if (cp->host_status==HS_COMPLETE) {
+ switch (cp->scsi_status) {
+ case S_GOOD:
+ printf (" GOOD");
+ break;
+ case S_CHECK_COND:
+ printf (" SENSE:");
+ p = (u_char*) &cmd->sense_buffer;
+ for (i=0; i<14; i++)
+ printf (" %x", *p++);
+ break;
+ default:
+ printf (" STAT: %x\n", cp->scsi_status);
+ break;
+ }
+ } else printf (" HOSTERROR: %x", cp->host_status);
+ printf ("\n");
+ }
+
+ /*
+ ** Free this ccb
+ */
+ ncr_free_ccb (np, cp, cmd->target, cmd->lun);
+
+ /*
+ ** requeue awaiting scsi commands
+ */
+ if (np->waiting_list) requeue_waiting_list(np);
+
+ /*
+ ** signal completion to generic driver.
+ */
+ cmd->scsi_done (cmd);
+}
+
+/*==========================================================
+**
+**
+** Signal all (or one) control block done.
+**
+**
+**==========================================================
+*/
+
+void ncr_wakeup (ncb_p np, u_long code)
+{
+ /*
+ ** Starting at the default ccb and following
+ ** the links, complete all jobs with a
+ ** host_status greater than "disconnect".
+ **
+ ** If the "code" parameter is not zero,
+ ** complete all jobs that are not IDLE.
+ */
+
+ ccb_p cp = np->ccb;
+ while (cp) {
+ switch (cp->host_status) {
+
+ case HS_IDLE:
+ break;
+
+ case HS_DISCONNECT:
+ if(DEBUG_FLAGS & DEBUG_TINY) printf ("D");
+ /* fall through */
+
+ case HS_BUSY:
+ case HS_NEGOTIATE:
+ if (!code) break;
+ cp->host_status = code;
+
+ /* fall through */
+
+ default:
+ ncr_complete (np, cp);
+ break;
+ };
+ cp = cp -> link_ccb;
+ };
+}
+
+/*==========================================================
+**
+**
+** Start NCR chip.
+**
+**
+**==========================================================
+*/
+
+void ncr_init (ncb_p np, int reset, char * msg, u_long code)
+{
+ int i;
+
+ /*
+ ** Reset chip if asked, otherwise just clear fifos.
+ */
+ if (reset) {
+ OUTB (nc_istat, SRST);
+ DELAY (10000);
+ }
+ else {
+ OUTB (nc_stest3, TE|CSF);
+ OUTONB (nc_ctest3, CLF);
+ }
+
+ /*
+ ** Message.
+ */
+
+ if (msg) printf (KERN_INFO "%s: restart (%s).\n", ncr_name (np), msg);
+
+ /*
+ ** Clear Start Queue
+ */
+ for (i=0;i<MAX_START;i++)
+ np -> squeue [i] = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+
+ /*
+ ** Start at first entry.
+ */
+ np->squeueput = 0;
+ np->script0->startpos[0] = cpu_to_scr(NCB_SCRIPTH_PHYS (np, tryloop));
+ np->script0->start0 [0] = cpu_to_scr(SCR_INT ^ IFFALSE (0));
+
+ /*
+ ** Wakeup all pending jobs.
+ */
+ ncr_wakeup (np, code);
+
+ /*
+ ** Init chip.
+ */
+
+ OUTB (nc_istat, 0x00 ); /* Remove Reset, abort */
+ OUTB (nc_scntl0, np->rv_scntl0 | 0xc0);
+ /* full arb., ena parity, par->ATN */
+ OUTB (nc_scntl1, 0x00); /* odd parity, and remove CRST!! */
+
+ ncr_selectclock(np, np->rv_scntl3); /* Select SCSI clock */
+
+ OUTB (nc_scid , RRE|np->myaddr); /* Adapter SCSI address */
+ OUTW (nc_respid, 1ul<<np->myaddr); /* Id to respond to */
+ OUTB (nc_istat , SIGP ); /* Signal Process */
+ OUTB (nc_dmode , np->rv_dmode); /* Burst length, dma mode */
+ OUTB (nc_ctest5, np->rv_ctest5); /* Large fifo + large burst */
+
+ OUTB (nc_dcntl , NOCOM|np->rv_dcntl); /* Protect SFBR */
+ OUTB (nc_ctest3, np->rv_ctest3); /* Write and invalidate */
+ OUTB (nc_ctest4, np->rv_ctest4); /* Master parity checking */
+
+ OUTB (nc_stest2, EXT|np->rv_stest2); /* Extended Sreq/Sack filtering */
+ OUTB (nc_stest3, TE); /* TolerANT enable */
+ OUTB (nc_stime0, 0x0d ); /* HTH disabled STO 0.4 sec. */
+
+ /*
+ ** Disable disconnects.
+ */
+
+ np->disc = 0;
+
+ /*
+ ** Enable GPIO0 pin for writing if LED support.
+ */
+
+ if (np->features & FE_LED0) {
+ OUTOFFB (nc_gpcntl, 0x01);
+ }
+
+ /*
+ ** Upload the script into on-board RAM
+ */
+ if (np->vaddr2) {
+ if (bootverbose)
+ printf ("%s: copying script fragments into the on-board RAM ...\n", ncr_name(np));
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
+ memcpy_toio(np->script, np->script0, sizeof(struct script));
+#else
+ memcpy(np->script, np->script0, sizeof(struct script));
+#endif
+ }
+
+ /*
+ ** enable ints
+ */
+
+ OUTW (nc_sien , STO|HTH|MA|SGE|UDC|RST);
+ OUTB (nc_dien , MDPE|BF|ABRT|SSI|SIR|IID);
+
+ /*
+ ** For 895/6 enable SBMC interrupt and save current SCSI bus mode.
+ */
+ if (np->features & FE_ULTRA2) {
+ OUTONW (nc_sien, SBMC);
+ np->scsi_mode = INB (nc_stest4) & SMODE;
+ }
+
+ /*
+ ** Fill in target structure.
+ ** Reinitialize usrsync.
+ ** Reinitialize usrwide.
+ ** Prepare sync negotiation according to actual SCSI bus mode.
+ */
+
+ for (i=0;i<MAX_TARGET;i++) {
+ tcb_p tp = &np->target[i];
+
+ tp->sval = 0;
+ tp->wval = np->rv_scntl3;
+
+ if (tp->usrsync != 255) {
+ if (tp->usrsync <= np->maxsync) {
+ if (tp->usrsync < np->minsync) {
+ tp->usrsync = np->minsync;
+ }
+ }
+ else
+ tp->usrsync = 255;
+ };
+
+ if (tp->usrwide > np->maxwide)
+ tp->usrwide = np->maxwide;
+
+ ncr_negotiate (np, tp);
+ }
+
+ /*
+ ** Start script processor.
+ */
+
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
+}
+
+/*==========================================================
+**
+** Prepare the negotiation values for wide and
+** synchronous transfers.
+**
+**==========================================================
+*/
+
+static void ncr_negotiate (struct ncb* np, struct tcb* tp)
+{
+ /*
+ ** minsync unit is 4ns !
+ */
+
+ u_long minsync = tp->usrsync;
+
+ /*
+ ** SCSI bus mode limit
+ */
+
+ if (np->scsi_mode && np->scsi_mode == SMODE_SE) {
+ if (minsync < 12) minsync = 12;
+ }
+
+ /*
+ ** if not scsi 2
+ ** don't believe FAST!
+ */
+
+ if ((minsync < 50) && (tp->inqdata[2] & 0x0f) < 2)
+ minsync=50;
+
+ /*
+ ** our limit ..
+ */
+
+ if (minsync < np->minsync)
+ minsync = np->minsync;
+
+ /*
+ ** divider limit
+ */
+
+ if (minsync > np->maxsync)
+ minsync = 255;
+
+ tp->minsync = minsync;
+ tp->maxoffs = (minsync<255 ? np->maxoffs : 0);
+
+ /*
+ ** period=0: has to negotiate sync transfer
+ */
+
+ tp->period=0;
+
+ /*
+ ** widedone=0: has to negotiate wide transfer
+ */
+ tp->widedone=0;
+}
+
+/*==========================================================
+**
+** Get clock factor and sync divisor for a given
+** synchronous factor period.
+** Returns the clock factor (in sxfer) and scntl3
+** synchronous divisor field.
+**
+**==========================================================
+*/
+
+static void ncr_getsync(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p)
+{
+ u_long clk = np->clock_khz; /* SCSI clock frequency in kHz */
+ int div = np->clock_divn; /* Number of divisors supported */
+ u_long fak; /* Sync factor in sxfer */
+ u_long per; /* Period in tenths of ns */
+ u_long kpc; /* (per * clk) */
+
+ /*
+ ** Compute the synchronous period in tenths of nano-seconds
+ */
+ if (sfac <= 10) per = 250;
+ else if (sfac == 11) per = 303;
+ else if (sfac == 12) per = 500;
+ else per = 40 * sfac;
+
+ /*
+ ** Look for the greatest clock divisor that allows an
+ ** input speed faster than the period.
+ */
+ kpc = per * clk;
+ while (--div >= 0)
+ if (kpc >= (div_10M[div] << 2)) break;
+
+ /*
+ ** Calculate the lowest clock factor that allows an output
+ ** speed not faster than the period.
+ */
+ fak = (kpc - 1) / div_10M[div] + 1;
+
+#if 0 /* This optimization does not seem very usefull */
+
+ per = (fak * div_10M[div]) / clk;
+
+ /*
+ ** Why not to try the immediate lower divisor and to choose
+ ** the one that allows the fastest output speed ?
+ ** We don't want input speed too much greater than output speed.
+ */
+ if (div >= 1 && fak < 8) {
+ u_long fak2, per2;
+ fak2 = (kpc - 1) / div_10M[div-1] + 1;
+ per2 = (fak2 * div_10M[div-1]) / clk;
+ if (per2 < per && fak2 <= 8) {
+ fak = fak2;
+ per = per2;
+ --div;
+ }
+ }
+#endif
+
+ if (fak < 4) fak = 4; /* Should never happen, too bad ... */
+
+ /*
+ ** Compute and return sync parameters for the ncr
+ */
+ *fakp = fak - 4;
+ *scntl3p = ((div+1) << 4) + (sfac < 25 ? 0x80 : 0);
+}
+
+
+/*==========================================================
+**
+** Set actual values, sync status and patch all ccbs of
+** a target according to new sync/wide agreement.
+**
+**==========================================================
+*/
+
+static void ncr_set_sync_wide_status (ncb_p np, u_char target)
+{
+ ccb_p cp;
+ tcb_p tp = &np->target[target];
+
+ /*
+ ** set actual value and sync_status
+ */
+ OUTB (nc_sxfer, tp->sval);
+ np->sync_st = tp->sval;
+ OUTB (nc_scntl3, tp->wval);
+ np->wide_st = tp->wval;
+
+ /*
+ ** patch ALL ccbs of this target.
+ */
+ for (cp = np->ccb; cp; cp = cp->link_ccb) {
+ if (!cp->cmd) continue;
+ if (cp->cmd->target != target) continue;
+ cp->sync_status = tp->sval;
+ cp->wide_status = tp->wval;
+ };
+}
+
+/*==========================================================
+**
+** Switch sync mode for current job and it's target
+**
+**==========================================================
+*/
+
+static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
+{
+ Scsi_Cmnd *cmd;
+ tcb_p tp;
+ u_char target = INB (nc_ctest0) & 0x0f;
+ u_char idiv;
+
+ assert (cp);
+ if (!cp) return;
+
+ cmd = cp->cmd;
+ assert (cmd);
+ if (!cmd) return;
+ assert (target == (cmd->target & 0xf));
+
+ tp = &np->target[target];
+
+ if (!scntl3 || !(sxfer & 0x1f))
+ scntl3 = np->rv_scntl3;
+ scntl3 = (scntl3 & 0xf0) | (tp->wval & EWS) | (np->rv_scntl3 & 0x07);
+
+ /*
+ ** Deduce the value of controller sync period from scntl3.
+ ** period is in tenths of nano-seconds.
+ */
+
+ idiv = ((scntl3 >> 4) & 0x7);
+ if ((sxfer & 0x1f) && idiv)
+ tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz;
+ else
+ tp->period = 0xffff;
+
+ /*
+ ** Stop there if sync parameters are unchanged
+ */
+ if (tp->sval == sxfer && tp->wval == scntl3) return;
+ tp->sval = sxfer;
+ tp->wval = scntl3;
+
+ /*
+ ** Bells and whistles ;-)
+ */
+ PRINT_ADDR(cmd);
+ if (sxfer & 0x01f) {
+ unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0);
+ unsigned mb10 = (f10 + tp->period/2) / tp->period;
+ char *scsi;
+
+ /*
+ ** Disable extended Sreq/Sack filtering
+ */
+ if (tp->period <= 2000) OUTOFFB (nc_stest2, EXT);
+
+ /*
+ ** Bells and whistles ;-)
+ */
+ if (tp->period < 500) scsi = "FAST-40";
+ else if (tp->period < 1000) scsi = "FAST-20";
+ else if (tp->period < 2000) scsi = "FAST-10";
+ else scsi = "FAST-5";
+
+ printf ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
+ tp->widedone > 1 ? "WIDE " : "",
+ mb10 / 10, mb10 % 10, tp->period / 10, sxfer & 0x1f);
+ } else
+ printf ("%sasynchronous.\n", tp->widedone > 1 ? "wide " : "");
+
+ /*
+ ** set actual value and sync_status
+ ** patch ALL ccbs of this target.
+ */
+ ncr_set_sync_wide_status(np, target);
+}
+
+/*==========================================================
+**
+** Switch wide mode for current job and it's target
+** SCSI specs say: a SCSI device that accepts a WDTR
+** message shall reset the synchronous agreement to
+** asynchronous mode.
+**
+**==========================================================
+*/
+
+static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack)
+{
+ Scsi_Cmnd *cmd;
+ u_short target = INB (nc_ctest0) & 0x0f;
+ tcb_p tp;
+ u_char scntl3;
+ u_char sxfer;
+
+ assert (cp);
+ if (!cp) return;
+
+ cmd = cp->cmd;
+ assert (cmd);
+ if (!cmd) return;
+ assert (target == (cmd->target & 0xf));
+
+ tp = &np->target[target];
+ tp->widedone = wide+1;
+ scntl3 = (tp->wval & (~EWS)) | (wide ? EWS : 0);
+
+ sxfer = ack ? 0 : tp->sval;
+
+ /*
+ ** Stop there if sync/wide parameters are unchanged
+ */
+ if (tp->sval == sxfer && tp->wval == scntl3) return;
+ tp->sval = sxfer;
+ tp->wval = scntl3;
+
+ /*
+ ** Bells and whistles ;-)
+ */
+ if (bootverbose >= 2) {
+ PRINT_ADDR(cmd);
+ if (scntl3 & EWS)
+ printf ("WIDE SCSI (16 bit) enabled.\n");
+ else
+ printf ("WIDE SCSI disabled.\n");
+ }
+
+ /*
+ ** set actual value and sync_status
+ ** patch ALL ccbs of this target.
+ */
+ ncr_set_sync_wide_status(np, target);
+}
+
+/*==========================================================
+**
+** Switch tagged mode for a target.
+**
+**==========================================================
+*/
+
+static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags)
+{
+ int l;
+ if (numtags > tp->usrtags)
+ numtags = tp->usrtags;
+ tp->numtags = numtags;
+ tp->maxtags = numtags;
+
+ for (l=0; l<MAX_LUN; l++) {
+ lcb_p lp;
+ u_char wastags;
+
+ if (!tp) break;
+ lp=tp->lp[l];
+ if (!lp) continue;
+
+ wastags = lp->usetags;
+ ncr_settags (tp, lp);
+
+ if (numtags > 1 && lp->reqccbs > 1) {
+ PRINT_LUN(np, tp - np->target, l);
+ printf("using tagged command queueing, up to %ld cmds/lun\n", numtags);
+ }
+ else if (numtags <= 1 && wastags) {
+ PRINT_LUN(np, tp - np->target, l);
+ printf("disabling tagged command queueing\n");
+ }
+ };
+}
+
+static void ncr_settags (tcb_p tp, lcb_p lp)
+{
+ u_char reqtags, tmp;
+
+ if ((!tp) || (!lp)) return;
+
+ /*
+ ** only devices conformant to ANSI Version >= 2
+ ** only devices capable of tagges commands
+ ** only disk devices
+ ** only if enabled by user ..
+ */
+ if (( tp->inqdata[2] & 0x7) >= 2 &&
+ ( tp->inqdata[7] & INQ7_QUEUE) && ((tp->inqdata[0] & 0x1f)==0x00)
+ && tp->numtags > 1) {
+ reqtags = tp->numtags;
+ if (lp->actlink <= 1)
+ lp->usetags=reqtags;
+ } else {
+ reqtags = 1;
+ if (lp->actlink <= 1)
+ lp->usetags=0;
+ };
+
+ /*
+ ** don't announce more than available.
+ */
+ tmp = lp->actccbs;
+ if (tmp > reqtags) tmp = reqtags;
+ lp->reqlink = tmp;
+
+ /*
+ ** don't discard if announced.
+ */
+ tmp = lp->actlink;
+ if (tmp < reqtags) tmp = reqtags;
+ lp->reqccbs = tmp;
+}
+
+/*----------------------------------------------------
+**
+** handle user commands
+**
+**----------------------------------------------------
+*/
+
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
+
+static void ncr_usercmd (ncb_p np)
+{
+ u_char t;
+ tcb_p tp;
+
+ switch (np->user.cmd) {
+
+ case 0: return;
+
+ case UC_SETSYNC:
+ for (t=0; t<MAX_TARGET; t++) {
+ if (!((np->user.target>>t)&1)) continue;
+ tp = &np->target[t];
+ tp->usrsync = np->user.data;
+ ncr_negotiate (np, tp);
+ };
+ break;
+
+ case UC_SETTAGS:
+ if (np->user.data > SCSI_NCR_MAX_TAGS)
+ np->user.data = SCSI_NCR_MAX_TAGS;
+ for (t=0; t<MAX_TARGET; t++) {
+ if (!((np->user.target>>t)&1)) continue;
+ np->target[t].usrtags = np->user.data;
+ ncr_setmaxtags (np, &np->target[t], np->user.data);
+ };
+ break;
+
+ case UC_SETDEBUG:
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+ ncr_debug = np->user.data;
+#endif
+ break;
+
+ case UC_SETORDER:
+ np->order = np->user.data;
+ break;
+
+ case UC_SETWIDE:
+ for (t=0; t<MAX_TARGET; t++) {
+ u_long size;
+ if (!((np->user.target>>t)&1)) continue;
+ tp = &np->target[t];
+ size = np->user.data;
+ if (size > np->maxwide) size=np->maxwide;
+ tp->usrwide = size;
+ ncr_negotiate (np, tp);
+ };
+ break;
+
+ case UC_SETFLAG:
+ for (t=0; t<MAX_TARGET; t++) {
+ if (!((np->user.target>>t)&1)) continue;
+ tp = &np->target[t];
+ tp->usrflag = np->user.data;
+ };
+ break;
+
+ case UC_CLEARPROF:
+ bzero(&np->profile, sizeof(np->profile));
+ break;
+#ifdef UC_DEBUG_ERROR_RECOVERY
+ case UC_DEBUG_ERROR_RECOVERY:
+ np->debug_error_recovery = np->user.data;
+ break;
+#endif
+ }
+ np->user.cmd=0;
+}
+#endif
+
+
+/*=====================================================================
+**
+** Embedded error recovery debugging code.
+**
+**=====================================================================
+**
+** This code is conditionned by SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT.
+** It only can be enabled after boot-up with a control command.
+**
+** Every 30 seconds the timer handler of the driver decides to
+** change the behaviour of the driver in order to trigger errors.
+**
+** If last command was "debug_error_recovery sge", the driver
+** sets sync offset of all targets that use sync transfers to 2,
+** and so hopes a SCSI gross error at the next read operation.
+**
+** If last command was "debug_error_recovery abort", the driver
+** does not signal new scsi commands to the script processor, until
+** it is asked to abort or reset a command by the mid-level driver.
+**
+** If last command was "debug_error_recovery reset", the driver
+** does not signal new scsi commands to the script processor, until
+** it is asked to reset a command by the mid-level driver.
+**
+** If last command was "debug_error_recovery parity", the driver
+** will assert ATN on the next DATA IN phase mismatch, and so will
+** behave as if a parity error had been detected.
+**
+** The command "debug_error_recovery none" makes the driver behave
+** normaly.
+**
+**=====================================================================
+*/
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+static void ncr_trigger_errors (ncb_p np)
+{
+ /*
+ ** If np->debug_error_recovery is not zero, we want to
+ ** simulate common errors in order to test error recovery.
+ */
+ do {
+ static u_long last = 0l;
+
+ if (!np->debug_error_recovery)
+ break;
+ if (!last)
+ last = jiffies;
+ else if (jiffies < last + 30*HZ)
+ break;
+ last = jiffies;
+ /*
+ * This one triggers SCSI gross errors.
+ */
+ if (np->debug_error_recovery == 1) {
+ int i;
+ printf("%s: testing error recovery from SCSI gross error...\n", ncr_name(np));
+ for (i = 0 ; i < MAX_TARGET ; i++) {
+ if (np->target[i].sval & 0x1f) {
+ np->target[i].sval &= ~0x1f;
+ np->target[i].sval += 2;
+ }
+ }
+ }
+ /*
+ * This one triggers abort from the mid-level driver.
+ */
+ else if (np->debug_error_recovery == 2) {
+ printf("%s: testing error recovery from mid-level driver abort()...\n", ncr_name(np));
+ np->stalling = 2;
+ }
+ /*
+ * This one triggers reset from the mid-level driver.
+ */
+ else if (np->debug_error_recovery == 3) {
+ printf("%s: testing error recovery from mid-level driver reset()...\n", ncr_name(np));
+ np->stalling = 3;
+ }
+ /*
+ * This one set ATN on phase mismatch in DATA IN phase and so
+ * will behave as on scsi parity error detected.
+ */
+ else if (np->debug_error_recovery == 4) {
+ printf("%s: testing data in parity error...\n", ncr_name(np));
+ np->assert_atn = 1;
+ }
+ } while (0);
+}
+#endif
+
+/*==========================================================
+**
+**
+** ncr timeout handler.
+**
+**
+**==========================================================
+**
+** Misused to keep the driver running when
+** interrupts are not configured correctly.
+**
+**----------------------------------------------------------
+*/
+
+static void ncr_timeout (ncb_p np)
+{
+ u_long thistime = jiffies;
+ u_long count = 0;
+ ccb_p cp;
+ u_long flags;
+
+ /*
+ ** If release process in progress, let's go
+ ** Set the release stage from 1 to 2 to synchronize
+ ** with the release process.
+ */
+
+ if (np->release_stage) {
+ if (np->release_stage == 1) np->release_stage = 2;
+ return;
+ }
+
+ np->timer.expires =
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+ jiffies +
+#endif
+ SCSI_NCR_TIMER_INTERVAL;
+
+ add_timer(&np->timer);
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ ncr_trigger_errors (np);
+#endif
+
+ /*
+ ** If we are resetting the ncr, wait for settle_time before
+ ** clearing it. Then command processing will be resumed.
+ */
+ if (np->settle_time) {
+ if (np->settle_time <= thistime) {
+ if (bootverbose > 1)
+ printf("%s: command processing resumed\n", ncr_name(np));
+ save_flags(flags); cli();
+ np->settle_time = 0;
+ np->disc = 1;
+ requeue_waiting_list(np);
+ restore_flags(flags);
+ }
+ return;
+ }
+
+ /*
+ ** Since the generic scsi driver only allows us 0.5 second
+ ** to perform abort of a command, we must look at ccbs about
+ ** every 0.25 second.
+ */
+ if (np->lasttime + (HZ>>2) <= thistime) {
+ /*
+ ** block ncr interrupts
+ */
+ save_flags(flags); cli();
+
+ np->lasttime = thistime;
+
+ /*
+ ** Reset profile data to avoid ugly overflow
+ ** (Limited to 1024 GB for 32 bit architecture)
+ */
+ if (np->profile.num_kbytes > (~0UL >> 2))
+ bzero(&np->profile, sizeof(np->profile));
+
+ /*----------------------------------------------------
+ **
+ ** handle ncr chip timeouts
+ **
+ ** Assumption:
+ ** We have a chance to arbitrate for the
+ ** SCSI bus at least every 10 seconds.
+ **
+ **----------------------------------------------------
+ */
+#if 0
+ if (thistime < np->heartbeat + HZ + HZ)
+ np->latetime = 0;
+ else
+ np->latetime++;
+#endif
+
+ /*----------------------------------------------------
+ **
+ ** handle ccb timeouts
+ **
+ **----------------------------------------------------
+ */
+
+ for (cp=np->ccb; cp; cp=cp->link_ccb) {
+ /*
+ ** look for timed out ccbs.
+ */
+ if (!cp->host_status) continue;
+ count++;
+ /*
+ ** Have to force ordered tag to avoid timeouts
+ */
+ if (cp->cmd && cp->tlimit && cp->tlimit <=
+ thistime + NCR_TIMEOUT_INCREASE + SCSI_NCR_TIMEOUT_ALERT) {
+ lcb_p lp;
+ lp = np->target[cp->cmd->target].lp[cp->cmd->lun];
+ if (lp && !lp->force_ordered_tag) {
+ lp->force_ordered_tag = 1;
+ }
+ }
+ /*
+ ** ncr_abort_command() cannot complete canceled
+ ** commands immediately. It sets tlimit to zero
+ ** and ask the script to skip the scsi process if
+ ** necessary. We have to complete this work here.
+ */
+
+ if (cp->tlimit) continue;
+
+ switch (cp->host_status) {
+
+ case HS_BUSY:
+ case HS_NEGOTIATE:
+ /*
+ ** still in start queue ?
+ */
+ if (cp->phys.header.launch.l_paddr ==
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, skip)))
+ continue;
+
+ /* fall through */
+ case HS_DISCONNECT:
+ cp->host_status=HS_ABORTED;
+ };
+ cp->tag = 0;
+
+ /*
+ ** wakeup this ccb.
+ */
+ ncr_complete (np, cp);
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if (!np->stalling)
+#endif
+ OUTB (nc_istat, SIGP);
+ }
+ restore_flags(flags);
+ }
+
+#ifdef SCSI_NCR_BROKEN_INTR
+ if (INB(nc_istat) & (INTF|SIP|DIP)) {
+
+ /*
+ ** Process pending interrupts.
+ */
+ save_flags(flags); cli();
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("{");
+ ncr_exception (np);
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("}");
+ restore_flags(flags);
+ }
+#endif /* SCSI_NCR_BROKEN_INTR */
+}
+
+/*==========================================================
+**
+** log message for real hard errors
+**
+** "ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ name (dsp:dbc)."
+** " reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
+**
+** exception register:
+** ds: dstat
+** si: sist
+**
+** SCSI bus lines:
+** so: control lines as driver by NCR.
+** si: control lines as seen by NCR.
+** sd: scsi data lines as seen by NCR.
+**
+** wide/fastmode:
+** sxfer: (see the manual)
+** scntl3: (see the manual)
+**
+** current script command:
+** dsp: script adress (relative to start of script).
+** dbc: first word of script command.
+**
+** First 16 register of the chip:
+** r0..rf
+**
+**==========================================================
+*/
+
+static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat)
+{
+ u_int32 dsp;
+ int script_ofs;
+ int script_size;
+ char *script_name;
+ u_char *script_base;
+ int i;
+
+ dsp = INL (nc_dsp);
+
+ if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
+ script_ofs = dsp - np->p_script;
+ script_size = sizeof(struct script);
+ script_base = (u_char *) np->script;
+ script_name = "script";
+ }
+ else if (np->p_scripth < dsp &&
+ dsp <= np->p_scripth + sizeof(struct scripth)) {
+ script_ofs = dsp - np->p_scripth;
+ script_size = sizeof(struct scripth);
+ script_base = (u_char *) np->scripth;
+ script_name = "scripth";
+ } else {
+ script_ofs = dsp;
+ script_size = 0;
+ script_base = 0;
+ script_name = "mem";
+ }
+
+ printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%s %x:%08x).\n",
+ ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
+ (unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
+ (unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs,
+ (unsigned)INL (nc_dbc));
+
+ if (((script_ofs & 3) == 0) &&
+ (unsigned)script_ofs < script_size) {
+ printf ("%s: script cmd = %08x\n", ncr_name(np),
+ (int) *(ncrcmd *)(script_base + script_ofs));
+ }
+
+ printf ("%s: regdump:", ncr_name(np));
+ for (i=0; i<16;i++)
+ printf (" %02x", (unsigned)INB_OFF(i));
+ printf (".\n");
+}
+
+/*============================================================
+**
+** ncr chip exception handler.
+**
+**============================================================
+**
+** In normal cases, interrupt conditions occur one at a
+** time. The ncr is able to stack in some extra registers
+** other interrupts that will occurs after the first one.
+** But severall interrupts may occur at the same time.
+**
+** We probably should only try to deal with the normal
+** case, but it seems that multiple interrupts occur in
+** some cases that are not abnormal at all.
+**
+** The most frequent interrupt condition is Phase Mismatch.
+** We should want to service this interrupt quickly.
+** A SCSI parity error may be delivered at the same time.
+** The SIR interrupt is not very frequent in this driver,
+** since the INTFLY is likely used for command completion
+** signaling.
+** The Selection Timeout interrupt may be triggered with
+** IID and/or UDC.
+** The SBMC interrupt (SCSI Bus Mode Change) may probably
+** occur at any time.
+**
+** This handler try to deal as cleverly as possible with all
+** the above.
+**
+**============================================================
+*/
+
+void ncr_exception (ncb_p np)
+{
+ u_char istat, dstat;
+ u_short sist;
+ int i;
+
+ /*
+ ** interrupt on the fly ?
+ ** Since the global header may be copied back to a CCB
+ ** using a posted PCI memory write, the last operation on
+ ** the istat register is a READ in order to flush posted
+ ** PCI commands (Btw, the 'do' loop is probably useless).
+ */
+ istat = INB (nc_istat);
+ if (istat & INTF) {
+ do {
+ OUTB (nc_istat, (istat & SIGP) | INTF);
+ istat = INB (nc_istat);
+ } while (istat & INTF);
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("F ");
+ np->profile.num_fly++;
+ ncr_wakeup (np, 0);
+ };
+
+ if (!(istat & (SIP|DIP)))
+ return;
+
+ np->profile.num_int++;
+
+ if (istat & CABRT)
+ OUTB (nc_istat, CABRT);
+
+ /*
+ ** Steinbach's Guideline for Systems Programming:
+ ** Never test for an error condition you don't know how to handle.
+ */
+
+ sist = (istat & SIP) ? INW (nc_sist) : 0;
+ dstat = (istat & DIP) ? INB (nc_dstat) : 0;
+
+ if (DEBUG_FLAGS & DEBUG_TINY)
+ printf ("<%d|%x:%x|%x:%x>",
+ (int)INB(nc_scr0),
+ dstat,sist,
+ (unsigned)INL(nc_dsp),
+ (unsigned)INL(nc_dbc));
+
+ /*========================================================
+ ** First, interrupts we want to service cleanly.
+ **
+ ** Phase mismatch is the most frequent interrupt, and
+ ** so we have to service it as quickly and as cleanly
+ ** as possible.
+ ** Programmed interrupts are rarely used in this driver,
+ ** but we must handle them cleanly anyway.
+ ** We try to deal with PAR and SBMC combined with
+ ** some other interrupt(s).
+ **=========================================================
+ */
+
+ if (!(sist & (STO|GEN|HTH|SGE|UDC|RST)) &&
+ !(dstat & (MDPE|BF|ABRT|IID))) {
+ if ((sist & SBMC) && ncr_int_sbmc (np))
+ return;
+ if ((sist & PAR) && ncr_int_par (np))
+ return;
+ if (sist & MA) {
+ ncr_int_ma (np);
+ return;
+ }
+ if (dstat & SIR) {
+ ncr_int_sir (np);
+ return;
+ }
+ /*
+ ** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 2.
+ */
+ if (!(sist & (SBMC|PAR)) && !(dstat & SSI)) {
+ printf( "%s: unknown interrupt(s) ignored, "
+ "ISTAT=%x DSTAT=%x SIST=%x\n",
+ ncr_name(np), istat, dstat, sist);
+ return;
+ }
+
+ OUTONB (nc_dcntl, (STD|NOCOM));
+ return;
+ };
+
+ /*========================================================
+ ** Now, interrupts that need some fixing up.
+ ** Order and multiple interrupts is so less important.
+ **
+ ** If SRST has been asserted, we just reset the chip.
+ **
+ ** Selection is intirely handled by the chip. If the
+ ** chip says STO, we trust it. Seems some other
+ ** interrupts may occur at the same time (UDC, IID), so
+ ** we ignore them. In any case we do enough fix-up
+ ** in the service routine.
+ ** We just exclude some fatal dma errors.
+ **=========================================================
+ */
+
+ if (sist & RST) {
+ ncr_init (np, 1, bootverbose ? "scsi reset" : NULL, HS_RESET);
+ return;
+ };
+
+ if ((sist & STO) &&
+ !(dstat & (MDPE|BF|ABRT))) {
+ /*
+ ** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 1.
+ */
+ OUTONB (nc_ctest3, CLF);
+
+ ncr_int_sto (np);
+ return;
+ };
+
+ /*=========================================================
+ ** Now, interrupts we are not able to recover cleanly.
+ ** (At least for the moment).
+ **
+ ** Do the register dump.
+ ** Log message for real hard errors.
+ ** Clear all fifos.
+ ** For MDPE, BF, ABORT, IID, SGE and HTH we reset the
+ ** BUS and the chip.
+ ** We are more soft for UDC.
+ **=========================================================
+ */
+ if (jiffies - np->regtime > 10*HZ) {
+ np->regtime = jiffies;
+ for (i = 0; i<sizeof(np->regdump); i++)
+ ((char*)&np->regdump)[i] = INB_OFF(i);
+ np->regdump.nc_dstat = dstat;
+ np->regdump.nc_sist = sist;
+ };
+
+ ncr_log_hard_error(np, sist, dstat);
+
+ printf ("%s: have to clear fifos.\n", ncr_name (np));
+ OUTB (nc_stest3, TE|CSF);
+ OUTONB (nc_ctest3, CLF);
+
+ if ((sist & (SGE)) ||
+ (dstat & (MDPE|BF|ABORT|IID))) {
+ ncr_start_reset(np, driver_setup.settle_delay);
+ return;
+ };
+
+ if (sist & HTH) {
+ printf ("%s: handshake timeout\n", ncr_name(np));
+ ncr_start_reset(np, driver_setup.settle_delay);
+ return;
+ };
+
+ if (sist & UDC) {
+ printf ("%s: unexpected disconnect\n", ncr_name(np));
+ if (INB (nc_scr1) != 0xff) {
+ OUTB (nc_scr1, HS_UNEXPECTED);
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
+ };
+ ncr_start_reset(np, driver_setup.settle_delay);
+ return;
+ };
+
+ /*=========================================================
+ ** We just miss the cause of the interrupt. :(
+ ** Print a message. The timeout will do the real work.
+ **=========================================================
+ */
+ printf ("%s: unknown interrupt\n", ncr_name(np));
+}
+
+/*==========================================================
+**
+** ncr chip exception handler for selection timeout
+**
+**==========================================================
+**
+** There seems to be a bug in the 53c810.
+** Although a STO-Interrupt is pending,
+** it continues executing script commands.
+** But it will fail and interrupt (IID) on
+** the next instruction where it's looking
+** for a valid phase.
+**
+**----------------------------------------------------------
+*/
+
+void ncr_int_sto (ncb_p np)
+{
+ u_long dsa, scratcha, diff;
+ ccb_p cp;
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("T");
+
+ /*
+ ** look for ccb and set the status.
+ */
+
+ dsa = INL (nc_dsa);
+ cp = np->ccb;
+ while (cp && (CCB_PHYS (cp, phys) != dsa))
+ cp = cp->link_ccb;
+
+ if (cp) {
+ cp-> host_status = HS_SEL_TIMEOUT;
+ ncr_complete (np, cp);
+ };
+
+ /*
+ ** repair start queue
+ */
+
+ scratcha = INL (nc_scratcha);
+ diff = scratcha - NCB_SCRIPTH_PHYS (np, tryloop);
+
+/* assert ((diff <= MAX_START * 20) && !(diff % 20));*/
+
+ if ((diff <= MAX_START * 20) && !(diff % 20)) {
+ np->script->startpos[0] = cpu_to_scr(scratcha);
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
+ return;
+ };
+ ncr_init (np, 1, "selection timeout", HS_FAIL);
+ np->disc = 1;
+}
+
+/*==========================================================
+**
+** ncr chip exception handler for SCSI bus mode change
+**
+**==========================================================
+**
+** spi2-r12 11.2.3 says a transceiver mode change must
+** generate a reset event and a device that detects a reset
+** event shall initiate a hard reset. It says also that a
+** device that detects a mode change shall set data transfer
+** mode to eight bit asynchronous, etc...
+** So, just resetting should be enough.
+**
+**
+**----------------------------------------------------------
+*/
+
+static int ncr_int_sbmc (ncb_p np)
+{
+ u_char scsi_mode = INB (nc_stest4) & SMODE;
+
+ printf("%s: SCSI bus mode change from %x to %x.\n",
+ ncr_name(np), np->scsi_mode, scsi_mode);
+
+ np->scsi_mode = scsi_mode;
+
+ /*
+ ** Suspend command processing for 1 second and
+ ** reinitialize all except the chip.
+ */
+ np->settle_time = jiffies + HZ;
+ ncr_init (np, 0, bootverbose ? "scsi mode change" : NULL, HS_RESET);
+
+ return 1;
+}
+
+/*==========================================================
+**
+** ncr chip exception handler for SCSI parity error.
+**
+**==========================================================
+**
+** SCSI parity errors are handled by the SCSI script.
+** So, we just print some message.
+**
+**----------------------------------------------------------
+*/
+
+static int ncr_int_par (ncb_p np)
+{
+ printf("%s: SCSI parity error detected\n", ncr_name(np));
+ return 0;
+}
+
+/*==========================================================
+**
+**
+** ncr chip exception handler for phase errors.
+**
+**
+**==========================================================
+**
+** We have to construct a new transfer descriptor,
+** to transfer the rest of the current block.
+**
+**----------------------------------------------------------
+*/
+
+static void ncr_int_ma (ncb_p np)
+{
+ u_int32 dbc;
+ u_int32 rest;
+ u_int32 dsp;
+ u_int32 dsa;
+ u_int32 nxtdsp;
+ u_int32 *vdsp;
+ u_int32 oadr, olen;
+ u_int32 *tblp;
+ ncrcmd *newcmd;
+ u_char cmd, sbcl;
+ ccb_p cp;
+
+ dsp = INL (nc_dsp);
+ dbc = INL (nc_dbc);
+ sbcl = INB (nc_sbcl);
+
+ cmd = dbc >> 24;
+ rest = dbc & 0xffffff;
+
+ /*
+ ** Take into account dma fifo and various buffers and latches,
+ ** only if the interrupted phase is an OUTPUT phase.
+ */
+
+ if ((cmd & 1) == 0) {
+ u_char ctest5, ss0, ss2;
+ u_short delta;
+
+ ctest5 = (np->rv_ctest5 & DFS) ? INB (nc_ctest5) : 0;
+ if (ctest5 & DFS)
+ delta=(((ctest5 << 8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff;
+ else
+ delta=(INB (nc_dfifo) - rest) & 0x7f;
+
+ /*
+ ** The data in the dma fifo has not been transfered to
+ ** the target -> add the amount to the rest
+ ** and clear the data.
+ ** Check the sstat2 register in case of wide transfer.
+ */
+
+ rest += delta;
+ ss0 = INB (nc_sstat0);
+ if (ss0 & OLF) rest++;
+ if (ss0 & ORF) rest++;
+ if (INB(nc_scntl3) & EWS) {
+ ss2 = INB (nc_sstat2);
+ if (ss2 & OLF1) rest++;
+ if (ss2 & ORF1) rest++;
+ };
+
+ OUTONB (nc_ctest3, CLF ); /* clear dma fifo */
+ OUTB (nc_stest3, TE|CSF); /* clear scsi fifo */
+
+ if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
+ printf ("P%x%x RL=%d D=%d SS0=%x ", cmd&7, sbcl&7,
+ (unsigned) rest, (unsigned) delta, ss0);
+
+ } else {
+ if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
+ printf ("P%x%x RL=%d ", cmd&7, sbcl&7, rest);
+ if ((cmd & 7) != 1) {
+ OUTONB (nc_ctest3, CLF );
+ OUTB (nc_stest3, TE|CSF);
+ }
+ }
+
+ /*
+ ** locate matching cp
+ */
+ dsa = INL (nc_dsa);
+ cp = np->ccb;
+ while (cp && (CCB_PHYS (cp, phys) != dsa))
+ cp = cp->link_ccb;
+
+ if (!cp) {
+ printf ("%s: SCSI phase error fixup: CCB already dequeued (0x%08lx)\n",
+ ncr_name (np), (u_long) np->header.cp);
+ return;
+ }
+ if (cp != np->header.cp) {
+ printf ("%s: SCSI phase error fixup: CCB address mismatch (0x%08lx != 0x%08lx)\n",
+ ncr_name (np), (u_long) cp, (u_long) np->header.cp);
+/* return;*/
+ }
+
+ /*
+ ** find the interrupted script command,
+ ** and the address at which to continue.
+ */
+
+ if (dsp == vtophys (&cp->patch[2])) {
+ vdsp = &cp->patch[0];
+ nxtdsp = vdsp[3];
+ } else if (dsp == vtophys (&cp->patch[6])) {
+ vdsp = &cp->patch[4];
+ nxtdsp = vdsp[3];
+ } else if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
+ vdsp = (u_int32 *) ((char*)np->script - np->p_script + dsp -8);
+ nxtdsp = dsp;
+ } else {
+ vdsp = (u_int32 *) ((char*)np->scripth - np->p_scripth + dsp -8);
+ nxtdsp = dsp;
+ };
+
+ /*
+ ** log the information
+ */
+
+ if (DEBUG_FLAGS & DEBUG_PHASE) {
+ printf ("\nCP=%p CP2=%p DSP=%x NXT=%x VDSP=%p CMD=%x ",
+ cp, np->header.cp,
+ (unsigned)dsp,
+ (unsigned)nxtdsp, vdsp, cmd);
+ };
+
+ /*
+ ** get old startaddress and old length.
+ */
+
+ oadr = scr_to_cpu(vdsp[1]);
+
+ if (cmd & 0x10) { /* Table indirect */
+ tblp = (u_int32 *) ((char*) &cp->phys + oadr);
+ olen = scr_to_cpu(tblp[0]);
+ oadr = scr_to_cpu(tblp[1]);
+ } else {
+ tblp = (u_int32 *) 0;
+ olen = scr_to_cpu(vdsp[0]) & 0xffffff;
+ };
+
+ if (DEBUG_FLAGS & DEBUG_PHASE) {
+ printf ("OCMD=%x\nTBLP=%p OLEN=%x OADR=%x\n",
+ (unsigned) (scr_to_cpu(vdsp[0]) >> 24),
+ tblp,
+ (unsigned) olen,
+ (unsigned) oadr);
+ };
+
+ /*
+ ** check cmd against assumed interrupted script command.
+ */
+
+ if (cmd != (scr_to_cpu(vdsp[0]) >> 24)) {
+ PRINT_ADDR(cp->cmd);
+ printf ("internal error: cmd=%02x != %02x=(vdsp[0] >> 24)\n",
+ (unsigned)cmd, (unsigned)scr_to_cpu(vdsp[0]) >> 24);
+
+ return;
+ }
+
+#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
+ if ((cmd & 7) == 1 && np->assert_atn) {
+ np->assert_atn = 0;
+ OUTONB(nc_socl, CATN);
+ }
+#endif
+
+ /*
+ ** if old phase not dataphase, leave here.
+ */
+
+ if (cmd & 0x06) {
+ PRINT_ADDR(cp->cmd);
+ printf ("phase change %x-%x %d@%08x resid=%d.\n",
+ cmd&7, sbcl&7, (unsigned)olen,
+ (unsigned)oadr, (unsigned)rest);
+
+ OUTONB (nc_dcntl, (STD|NOCOM));
+ return;
+ };
+
+ /*
+ ** choose the correct patch area.
+ ** if savep points to one, choose the other.
+ */
+
+ newcmd = cp->patch;
+ if (cp->phys.header.savep == cpu_to_scr(vtophys (newcmd))) newcmd+=4;
+
+ /*
+ ** fillin the commands
+ */
+
+ newcmd[0] = cpu_to_scr(((cmd & 0x0f) << 24) | rest);
+ newcmd[1] = cpu_to_scr(oadr + olen - rest);
+ newcmd[2] = cpu_to_scr(SCR_JUMP);
+ newcmd[3] = cpu_to_scr(nxtdsp);
+
+ if (DEBUG_FLAGS & DEBUG_PHASE) {
+ PRINT_ADDR(cp->cmd);
+ printf ("newcmd[%d] %x %x %x %x.\n",
+ (int) (newcmd - cp->patch),
+ (unsigned)scr_to_cpu(newcmd[0]),
+ (unsigned)scr_to_cpu(newcmd[1]),
+ (unsigned)scr_to_cpu(newcmd[2]),
+ (unsigned)scr_to_cpu(newcmd[3]));
+ }
+ /*
+ ** fake the return address (to the patch).
+ ** and restart script processor at dispatcher.
+ */
+ np->profile.num_break++;
+ OUTL (nc_temp, vtophys (newcmd));
+ if ((cmd & 7) == 0)
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
+ else
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, checkatn));
+}
+
+/*==========================================================
+**
+**
+** ncr chip exception handler for programmed interrupts.
+**
+**
+**==========================================================
+*/
+
+static int ncr_show_msg (u_char * msg)
+{
+ u_char i;
+ printf ("%x",*msg);
+ if (*msg==M_EXTENDED) {
+ for (i=1;i<8;i++) {
+ if (i-1>msg[1]) break;
+ printf ("-%x",msg[i]);
+ };
+ return (i+1);
+ } else if ((*msg & 0xf0) == 0x20) {
+ printf ("-%x",msg[1]);
+ return (2);
+ };
+ return (1);
+}
+
+void ncr_int_sir (ncb_p np)
+{
+ u_char scntl3;
+ u_char chg, ofs, per, fak, wide;
+ u_char num = INB (nc_dsps);
+ ccb_p cp=0;
+ u_long dsa;
+ u_char target = INB (nc_ctest0) & 0x0f;
+ tcb_p tp = &np->target[target];
+ int i;
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("I#%d", num);
+
+ switch (num) {
+ case SIR_SENSE_RESTART:
+ case SIR_STALL_RESTART:
+ break;
+ case SIR_STALL_QUEUE: /* Ignore, just restart the script */
+ goto out;
+
+ default:
+ /*
+ ** lookup the ccb
+ */
+ dsa = INL (nc_dsa);
+ cp = np->ccb;
+ while (cp && (CCB_PHYS (cp, phys) != dsa))
+ cp = cp->link_ccb;
+
+ assert (cp);
+ if (!cp)
+ goto out;
+ assert (cp == np->header.cp);
+ if (cp != np->header.cp)
+ goto out;
+ }
+
+ switch (num) {
+ u_long endp;
+ case SIR_DATA_IO_IS_OUT:
+ case SIR_DATA_IO_IS_IN:
+/*
+** We did not guess the direction of transfer. We have to wait for
+** actual data direction driven by the target before setting
+** pointers. We must patch the global header too.
+*/
+ if (num == SIR_DATA_IO_IS_OUT) {
+ endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
+ cp->phys.header.goalp = cpu_to_scr(endp + 8);
+ cp->phys.header.savep =
+ cpu_to_scr(endp - cp->segments*16);
+ } else {
+ endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16;
+ cp->phys.header.goalp = cpu_to_scr(endp + 8);
+ cp->phys.header.savep =
+ cpu_to_scr(endp - cp->segments*16);
+ }
+
+ cp->phys.header.lastp = cp->phys.header.savep;
+ np->header.savep = cp->phys.header.savep;
+ np->header.goalp = cp->phys.header.goalp;
+ np->header.lastp = cp->phys.header.lastp;
+
+ OUTL (nc_temp, scr_to_cpu(np->header.savep));
+ OUTL (nc_dsp, scr_to_cpu(np->header.savep));
+ return;
+ /* break; */
+
+/*--------------------------------------------------------------------
+**
+** Processing of interrupted getcc selects
+**
+**--------------------------------------------------------------------
+*/
+
+ case SIR_SENSE_RESTART:
+ /*------------------------------------------
+ ** Script processor is idle.
+ ** Look for interrupted "check cond"
+ **------------------------------------------
+ */
+
+ if (DEBUG_FLAGS & DEBUG_RESTART)
+ printf ("%s: int#%d",ncr_name (np),num);
+ cp = (ccb_p) 0;
+ for (i=0; i<MAX_TARGET; i++) {
+ if (DEBUG_FLAGS & DEBUG_RESTART) printf (" t%d", i);
+ tp = &np->target[i];
+ if (DEBUG_FLAGS & DEBUG_RESTART) printf ("+");
+ cp = tp->hold_cp;
+ if (!cp) continue;
+ if (DEBUG_FLAGS & DEBUG_RESTART) printf ("+");
+ if ((cp->host_status==HS_BUSY) &&
+ (cp->scsi_status==S_CHECK_COND))
+ break;
+ if (DEBUG_FLAGS & DEBUG_RESTART) printf ("- (remove)");
+ tp->hold_cp = cp = (ccb_p) 0;
+ };
+
+ if (cp) {
+ if (DEBUG_FLAGS & DEBUG_RESTART)
+ printf ("+ restart job ..\n");
+ OUTL (nc_dsa, CCB_PHYS (cp, phys));
+ OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, getcc));
+ return;
+ };
+
+ /*
+ ** no job, resume normal processing
+ */
+ if (DEBUG_FLAGS & DEBUG_RESTART) printf (" -- remove trap\n");
+ np->script->start0[0] = cpu_to_scr(SCR_INT ^ IFFALSE (0));
+ break;
+
+ case SIR_SENSE_FAILED:
+ /*-------------------------------------------
+ ** While trying to select for
+ ** getting the condition code,
+ ** a target reselected us.
+ **-------------------------------------------
+ */
+ if (DEBUG_FLAGS & DEBUG_RESTART) {
+ PRINT_ADDR(cp->cmd);
+ printf ("in getcc reselect by t%d.\n",
+ (int)INB(nc_ssid) & 0x0f);
+ }
+
+ /*
+ ** Mark this job
+ */
+ cp->host_status = HS_BUSY;
+ cp->scsi_status = S_CHECK_COND;
+ np->target[cp->cmd->target].hold_cp = cp;
+
+ /*
+ ** And patch code to restart it.
+ */
+ np->script->start0[0] = cpu_to_scr(SCR_INT);
+ break;
+
+/*-----------------------------------------------------------------------------
+**
+** Was Sie schon immer ueber transfermode negotiation wissen wollten ...
+**
+** We try to negotiate sync and wide transfer only after
+** a successfull inquire command. We look at byte 7 of the
+** inquire data to determine the capabilities of the target.
+**
+** When we try to negotiate, we append the negotiation message
+** to the identify and (maybe) simple tag message.
+** The host status field is set to HS_NEGOTIATE to mark this
+** situation.
+**
+** If the target doesn't answer this message immidiately
+** (as required by the standard), the SIR_NEGO_FAIL interrupt
+** will be raised eventually.
+** The handler removes the HS_NEGOTIATE status, and sets the
+** negotiated value to the default (async / nowide).
+**
+** If we receive a matching answer immediately, we check it
+** for validity, and set the values.
+**
+** If we receive a Reject message immediately, we assume the
+** negotiation has failed, and fall back to standard values.
+**
+** If we receive a negotiation message while not in HS_NEGOTIATE
+** state, it's a target initiated negotiation. We prepare a
+** (hopefully) valid answer, set our parameters, and send back
+** this answer to the target.
+**
+** If the target doesn't fetch the answer (no message out phase),
+** we assume the negotiation has failed, and fall back to default
+** settings.
+**
+** When we set the values, we adjust them in all ccbs belonging
+** to this target, in the controller's register, and in the "phys"
+** field of the controller's struct ncb.
+**
+** Possible cases: hs sir msg_in value send goto
+** We try to negotiate:
+** -> target doesnt't msgin NEG FAIL noop defa. - dispatch
+** -> target rejected our msg NEG FAIL reject defa. - dispatch
+** -> target answered (ok) NEG SYNC sdtr set - clrack
+** -> target answered (!ok) NEG SYNC sdtr defa. REJ--->msg_bad
+** -> target answered (ok) NEG WIDE wdtr set - clrack
+** -> target answered (!ok) NEG WIDE wdtr defa. REJ--->msg_bad
+** -> any other msgin NEG FAIL noop defa. - dispatch
+**
+** Target tries to negotiate:
+** -> incoming message --- SYNC sdtr set SDTR -
+** -> incoming message --- WIDE wdtr set WDTR -
+** We sent our answer:
+** -> target doesn't msgout --- PROTO ? defa. - dispatch
+**
+**-----------------------------------------------------------------------------
+*/
+
+ case SIR_NEGO_FAILED:
+ /*-------------------------------------------------------
+ **
+ ** Negotiation failed.
+ ** Target doesn't send an answer message,
+ ** or target rejected our message.
+ **
+ ** Remove negotiation request.
+ **
+ **-------------------------------------------------------
+ */
+ OUTB (HS_PRT, HS_BUSY);
+
+ /* fall through */
+
+ case SIR_NEGO_PROTO:
+ /*-------------------------------------------------------
+ **
+ ** Negotiation failed.
+ ** Target doesn't fetch the answer message.
+ **
+ **-------------------------------------------------------
+ */
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ PRINT_ADDR(cp->cmd);
+ printf ("negotiation failed sir=%x status=%x.\n",
+ num, cp->nego_status);
+ };
+
+ /*
+ ** any error in negotiation:
+ ** fall back to default mode.
+ */
+ switch (cp->nego_status) {
+
+ case NS_SYNC:
+ ncr_setsync (np, cp, 0, 0xe0);
+ break;
+
+ case NS_WIDE:
+ ncr_setwide (np, cp, 0, 0);
+ break;
+
+ };
+ np->msgin [0] = M_NOOP;
+ np->msgout[0] = M_NOOP;
+ cp->nego_status = 0;
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, dispatch));
+ break;
+
+ case SIR_NEGO_SYNC:
+ /*
+ ** Synchronous request message received.
+ */
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ PRINT_ADDR(cp->cmd);
+ printf ("sync msgin: ");
+ (void) ncr_show_msg (np->msgin);
+ printf (".\n");
+ };
+
+ /*
+ ** get requested values.
+ */
+
+ chg = 0;
+ per = np->msgin[3];
+ ofs = np->msgin[4];
+ if (ofs==0) per=255;
+
+ /*
+ ** if target sends SDTR message,
+ ** it CAN transfer synch.
+ */
+
+ if (ofs)
+ tp->inqdata[7] |= INQ7_SYNC;
+
+ /*
+ ** check values against driver limits.
+ */
+
+ if (per < np->minsync)
+ {chg = 1; per = np->minsync;}
+ if (per < tp->minsync)
+ {chg = 1; per = tp->minsync;}
+ if (ofs > tp->maxoffs)
+ {chg = 1; ofs = tp->maxoffs;}
+
+ /*
+ ** Check against controller limits.
+ */
+ fak = 7;
+ scntl3 = 0;
+ if (ofs != 0) {
+ ncr_getsync(np, per, &fak, &scntl3);
+ if (fak > 7) {
+ chg = 1;
+ ofs = 0;
+ }
+ }
+ if (ofs == 0) {
+ fak = 7;
+ per = 0;
+ scntl3 = 0;
+ tp->minsync = 0;
+ }
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ PRINT_ADDR(cp->cmd);
+ printf ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n",
+ per, scntl3, ofs, fak, chg);
+ }
+
+ if (INB (HS_PRT) == HS_NEGOTIATE) {
+ OUTB (HS_PRT, HS_BUSY);
+ switch (cp->nego_status) {
+
+ case NS_SYNC:
+ /*
+ ** This was an answer message
+ */
+ if (chg) {
+ /*
+ ** Answer wasn't acceptable.
+ */
+ ncr_setsync (np, cp, 0, 0xe0);
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
+ } else {
+ /*
+ ** Answer is ok.
+ */
+ ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
+ };
+ return;
+
+ case NS_WIDE:
+ ncr_setwide (np, cp, 0, 0);
+ break;
+ };
+ };
+
+ /*
+ ** It was a request.
+ ** Check against the table of target capabilities.
+ ** If target not capable force M_REJECT and asynchronous.
+ */
+ if (np->unit < SCSI_NCR_MAX_HOST) {
+ tp->inqdata[7] &=
+ (target_capabilities[np->unit].and_map[target]);
+ if (!(tp->inqdata[7] & INQ7_SYNC)) {
+ ofs = 0;
+ fak = 7;
+ }
+ }
+
+ /*
+ ** It was a request. Set value and
+ ** prepare an answer message
+ */
+
+ ncr_setsync (np, cp, scntl3, (fak<<5)|ofs);
+
+ np->msgout[0] = M_EXTENDED;
+ np->msgout[1] = 3;
+ np->msgout[2] = M_X_SYNC_REQ;
+ np->msgout[3] = per;
+ np->msgout[4] = ofs;
+
+ cp->nego_status = NS_SYNC;
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ PRINT_ADDR(cp->cmd);
+ printf ("sync msgout: ");
+ (void) ncr_show_msg (np->msgout);
+ printf (".\n");
+ }
+
+ if (!ofs) {
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
+ return;
+ }
+ np->msgin [0] = M_NOOP;
+
+ break;
+
+ case SIR_NEGO_WIDE:
+ /*
+ ** Wide request message received.
+ */
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ PRINT_ADDR(cp->cmd);
+ printf ("wide msgin: ");
+ (void) ncr_show_msg (np->msgin);
+ printf (".\n");
+ };
+
+ /*
+ ** get requested values.
+ */
+
+ chg = 0;
+ wide = np->msgin[3];
+
+ /*
+ ** if target sends WDTR message,
+ ** it CAN transfer wide.
+ */
+
+ if (wide)
+ tp->inqdata[7] |= INQ7_WIDE16;
+
+ /*
+ ** check values against driver limits.
+ */
+
+ if (wide > tp->usrwide)
+ {chg = 1; wide = tp->usrwide;}
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ PRINT_ADDR(cp->cmd);
+ printf ("wide: wide=%d chg=%d.\n", wide, chg);
+ }
+
+ if (INB (HS_PRT) == HS_NEGOTIATE) {
+ OUTB (HS_PRT, HS_BUSY);
+ switch (cp->nego_status) {
+
+ case NS_WIDE:
+ /*
+ ** This was an answer message
+ */
+ if (chg) {
+ /*
+ ** Answer wasn't acceptable.
+ */
+ ncr_setwide (np, cp, 0, 1);
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
+ } else {
+ /*
+ ** Answer is ok.
+ */
+ ncr_setwide (np, cp, wide, 1);
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
+ };
+ return;
+
+ case NS_SYNC:
+ ncr_setsync (np, cp, 0, 0xe0);
+ break;
+ };
+ };
+
+ /*
+ ** It was a request, set value and
+ ** prepare an answer message
+ */
+
+ ncr_setwide (np, cp, wide, 1);
+
+ np->msgout[0] = M_EXTENDED;
+ np->msgout[1] = 2;
+ np->msgout[2] = M_X_WIDE_REQ;
+ np->msgout[3] = wide;
+
+ np->msgin [0] = M_NOOP;
+
+ cp->nego_status = NS_WIDE;
+
+ if (DEBUG_FLAGS & DEBUG_NEGO) {
+ PRINT_ADDR(cp->cmd);
+ printf ("wide msgout: ");
+ (void) ncr_show_msg (np->msgin);
+ printf (".\n");
+ }
+ break;
+
+/*--------------------------------------------------------------------
+**
+** Processing of special messages
+**
+**--------------------------------------------------------------------
+*/
+
+ case SIR_REJECT_RECEIVED:
+ /*-----------------------------------------------
+ **
+ ** We received a M_REJECT message.
+ **
+ **-----------------------------------------------
+ */
+
+ PRINT_ADDR(cp->cmd);
+ printf ("M_REJECT received (%x:%x).\n",
+ (unsigned)scr_to_cpu(np->lastmsg), np->msgout[0]);
+ break;
+
+ case SIR_REJECT_SENT:
+ /*-----------------------------------------------
+ **
+ ** We received an unknown message
+ **
+ **-----------------------------------------------
+ */
+
+ PRINT_ADDR(cp->cmd);
+ printf ("M_REJECT sent for ");
+ (void) ncr_show_msg (np->msgin);
+ printf (".\n");
+ break;
+
+/*--------------------------------------------------------------------
+**
+** Processing of special messages
+**
+**--------------------------------------------------------------------
+*/
+
+ case SIR_IGN_RESIDUE:
+ /*-----------------------------------------------
+ **
+ ** We received an IGNORE RESIDUE message,
+ ** which couldn't be handled by the script.
+ **
+ **-----------------------------------------------
+ */
+
+ PRINT_ADDR(cp->cmd);
+ printf ("M_IGN_RESIDUE received, but not yet implemented.\n");
+ break;
+
+ case SIR_MISSING_SAVE:
+ /*-----------------------------------------------
+ **
+ ** We received an DISCONNECT message,
+ ** but the datapointer wasn't saved before.
+ **
+ **-----------------------------------------------
+ */
+
+ PRINT_ADDR(cp->cmd);
+ printf ("M_DISCONNECT received, but datapointer not saved: "
+ "data=%x save=%x goal=%x.\n",
+ (unsigned) INL (nc_temp),
+ (unsigned) scr_to_cpu(np->header.savep),
+ (unsigned) scr_to_cpu(np->header.goalp));
+ break;
+
+#if 0 /* This stuff does not work */
+/*--------------------------------------------------------------------
+**
+** Processing of a "S_QUEUE_FULL" status.
+**
+** The current command has been rejected,
+** because there are too many in the command queue.
+** We have started too many commands for that target.
+**
+** If possible, reinsert at head of queue.
+** Stall queue until there are no disconnected jobs
+** (ncr is REALLY idle). Then restart processing.
+**
+** We should restart the current job after the controller
+** has become idle. But this is not yet implemented.
+**
+**--------------------------------------------------------------------
+*/
+ case SIR_STALL_QUEUE:
+ /*-----------------------------------------------
+ **
+ ** Stall the start queue.
+ **
+ **-----------------------------------------------
+ */
+ PRINT_ADDR(cp->cmd);
+ printf ("queue full.\n");
+
+ np->script->start1[0] = cpu_to_scr(SCR_INT);
+
+ /*
+ ** Try to disable tagged transfers.
+ */
+ ncr_setmaxtags (np, &np->target[target], 0);
+
+ /*
+ ** @QUEUE@
+ **
+ ** Should update the launch field of the
+ ** current job to be able to restart it.
+ ** Then prepend it to the start queue.
+ */
+
+ /* fall through */
+
+ case SIR_STALL_RESTART:
+ /*-----------------------------------------------
+ **
+ ** Enable selecting again,
+ ** if NO disconnected jobs.
+ **
+ **-----------------------------------------------
+ */
+ /*
+ ** Look for a disconnected job.
+ */
+ cp = np->ccb;
+ while (cp && cp->host_status != HS_DISCONNECT)
+ cp = cp->link_ccb;
+
+ /*
+ ** if there is one, ...
+ */
+ if (cp) {
+ /*
+ ** wait for reselection
+ */
+ OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, reselect));
+ return;
+ };
+
+ /*
+ ** else remove the interrupt.
+ */
+
+ printf ("%s: queue empty.\n", ncr_name (np));
+ np->script->start1[0] = cpu_to_scr(SCR_INT ^ IFFALSE (0));
+ break;
+#endif /* This stuff does not work */
+ };
+
+out:
+ OUTONB (nc_dcntl, (STD|NOCOM));
+}
+
+/*==========================================================
+**
+**
+** Aquire a control block
+**
+**
+**==========================================================
+*/
+
+static ccb_p ncr_get_ccb
+ (ncb_p np, u_long target, u_long lun)
+{
+ lcb_p lp;
+ ccb_p cp = (ccb_p) 0;
+
+ /*
+ ** Lun structure available ?
+ */
+
+ lp = np->target[target].lp[lun];
+
+ if (lp && lp->opennings && (!lp->active || lp->active < lp->reqlink)) {
+
+ cp = lp->next_ccb;
+
+ /*
+ ** Look for free CCB
+ */
+
+ while (cp && cp->magic) cp = cp->next_ccb;
+
+ /*
+ ** Increment active commands and decrement credit.
+ */
+
+ if (cp) {
+ ++lp->active;
+ --lp->opennings;
+ }
+ }
+
+ /*
+ ** if nothing available, take the default.
+ ** DANGEROUS, because this ccb is not suitable for
+ ** reselection.
+ ** If lp->actccbs > 0 wait for a suitable ccb to be free.
+ */
+ if ((!cp) && lp && lp->actccbs > 0)
+ return ((ccb_p) 0);
+
+ if (!cp) cp = np->ccb;
+
+ /*
+ ** Wait until available.
+ */
+#if 0
+ while (cp->magic) {
+ if (flags & SCSI_NOSLEEP) break;
+ if (tsleep ((caddr_t)cp, PRIBIO|PCATCH, "ncr", 0))
+ break;
+ };
+#endif
+
+ if (cp->magic)
+ return ((ccb_p) 0);
+
+ cp->magic = 1;
+ return (cp);
+}
+
+/*==========================================================
+**
+**
+** Release one control block
+**
+**
+**==========================================================
+*/
+
+void ncr_free_ccb (ncb_p np, ccb_p cp, u_long target, u_long lun)
+{
+ lcb_p lp;
+
+ /*
+ ** sanity
+ */
+
+ assert (cp != NULL);
+
+ /*
+ ** Decrement active commands and increment credit.
+ */
+
+ lp = np->target[target].lp[lun];
+ if (lp) {
+ --lp->active;
+ ++lp->opennings;
+ }
+
+ cp -> host_status = HS_IDLE;
+ cp -> magic = 0;
+#if 0
+ if (cp == np->ccb)
+ wakeup ((caddr_t) cp);
+#endif
+}
+
+/*==========================================================
+**
+**
+** Allocation of resources for Targets/Luns/Tags.
+**
+**
+**==========================================================
+*/
+
+static void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
+{
+ tcb_p tp;
+ lcb_p lp;
+ ccb_p cp;
+
+ assert (np != NULL);
+
+ if (target>=MAX_TARGET) return;
+ if (lun >=MAX_LUN ) return;
+
+ tp=&np->target[target];
+
+ if (!tp->jump_tcb.l_cmd) {
+ /*
+ ** initialize it.
+ */
+ tp->jump_tcb.l_cmd =
+ cpu_to_scr((SCR_JUMP^IFFALSE (DATA (0x80 + target))));
+ tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr;
+
+ tp->getscr[0] = (np->features & FE_PFEN) ?
+ cpu_to_scr(SCR_COPY(1)):cpu_to_scr(SCR_COPY_F(1));
+ tp->getscr[1] = cpu_to_scr(vtophys (&tp->sval));
+ tp->getscr[2] =
+ cpu_to_scr(np->paddr + offsetof (struct ncr_reg, nc_sxfer));
+
+ tp->getscr[3] = (np->features & FE_PFEN) ?
+ cpu_to_scr(SCR_COPY(1)):cpu_to_scr(SCR_COPY_F(1));
+ tp->getscr[4] = cpu_to_scr(vtophys (&tp->wval));
+ tp->getscr[5] =
+ cpu_to_scr(np->paddr + offsetof (struct ncr_reg, nc_scntl3));
+
+ assert (( (offsetof(struct ncr_reg, nc_sxfer) ^
+ offsetof(struct tcb , sval )) &3) == 0);
+ assert (( (offsetof(struct ncr_reg, nc_scntl3) ^
+ offsetof(struct tcb , wval )) &3) == 0);
+
+ tp->call_lun.l_cmd = cpu_to_scr(SCR_CALL);
+ tp->call_lun.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_lun));
+
+ tp->jump_lcb.l_cmd = cpu_to_scr(SCR_JUMP);
+ tp->jump_lcb.l_paddr = cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
+ np->jump_tcb.l_paddr = cpu_to_scr(vtophys (&tp->jump_tcb));
+ }
+
+ /*
+ ** Logic unit control block
+ */
+ lp = tp->lp[lun];
+ if (!lp) {
+ /*
+ ** Allocate a lcb
+ */
+ lp = (lcb_p) m_alloc (sizeof (struct lcb), LCB_ALIGN_SHIFT);
+ if (!lp) return;
+
+ if (DEBUG_FLAGS & DEBUG_ALLOC) {
+ PRINT_LUN(np, target, lun);
+ printf ("new lcb @%p.\n", lp);
+ }
+
+ /*
+ ** Initialize it
+ */
+ bzero (lp, sizeof (*lp));
+ lp->jump_lcb.l_cmd =
+ cpu_to_scr(SCR_JUMP ^ IFFALSE (DATA (lun)));
+ lp->jump_lcb.l_paddr = tp->jump_lcb.l_paddr;
+
+ lp->call_tag.l_cmd = cpu_to_scr(SCR_CALL);
+ lp->call_tag.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_tag));
+
+ lp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
+ lp->jump_ccb.l_paddr =
+ cpu_to_scr(NCB_SCRIPTH_PHYS (np, aborttag));
+
+ lp->actlink = 1;
+
+ lp->active = 1;
+
+ /*
+ ** Chain into LUN list
+ */
+ tp->jump_lcb.l_paddr = cpu_to_scr(vtophys (&lp->jump_lcb));
+ tp->lp[lun] = lp;
+
+ ncr_setmaxtags (np, tp, driver_setup.default_tags);
+ }
+
+ /*
+ ** Allocate ccbs up to lp->reqccbs.
+ */
+
+ /*
+ ** Limit possible number of ccbs.
+ **
+ ** If tagged command queueing is enabled,
+ ** can use more than one ccb.
+ */
+ if (np->actccbs >= MAX_START-2) return;
+ if (lp->actccbs && (lp->actccbs >= lp->reqccbs))
+ return;
+
+ /*
+ ** Allocate a ccb
+ */
+ cp = (ccb_p) m_alloc (sizeof (struct ccb), CCB_ALIGN_SHIFT);
+ if (!cp)
+ return;
+
+ if (DEBUG_FLAGS & DEBUG_ALLOC) {
+ PRINT_LUN(np, target, lun);
+ printf ("new ccb @%p.\n", cp);
+ }
+
+ /*
+ ** Count it
+ */
+ lp->actccbs++;
+ np->actccbs++;
+
+ /*
+ ** Initialize it
+ */
+ bzero (cp, sizeof (*cp));
+
+ /*
+ ** Fill in physical addresses
+ */
+
+ cp->p_ccb = vtophys (cp);
+
+ /*
+ ** Chain into reselect list
+ */
+ cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
+ cp->jump_ccb.l_paddr = lp->jump_ccb.l_paddr;
+ lp->jump_ccb.l_paddr = cpu_to_scr(CCB_PHYS (cp, jump_ccb));
+ cp->call_tmp.l_cmd = cpu_to_scr(SCR_CALL);
+ cp->call_tmp.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_tmp));
+
+ /*
+ ** Chain into wakeup list
+ */
+ cp->link_ccb = np->ccb->link_ccb;
+ np->ccb->link_ccb = cp;
+
+ /*
+ ** Chain into CCB list
+ */
+ cp->next_ccb = lp->next_ccb;
+ lp->next_ccb = cp;
+}
+
+/*==========================================================
+**
+**
+** Announce the number of ccbs/tags to the scsi driver.
+**
+**
+**==========================================================
+*/
+
+static void ncr_opennings (ncb_p np, lcb_p lp, Scsi_Cmnd * cmd)
+{
+ /*
+ ** want to reduce the number ...
+ */
+ if (lp->actlink > lp->reqlink) {
+
+ /*
+ ** Try to reduce the count.
+ ** We assume to run at splbio ..
+ */
+ u_char diff = lp->actlink - lp->reqlink;
+
+ if (!diff) return;
+
+ if (diff > lp->opennings)
+ diff = lp->opennings;
+
+ lp->opennings -= diff;
+
+ lp->actlink -= diff;
+ if (DEBUG_FLAGS & DEBUG_TAGS)
+ printf ("%s: actlink: diff=%d, new=%d, req=%d\n",
+ ncr_name(np), diff, lp->actlink, lp->reqlink);
+ return;
+ };
+
+ /*
+ ** want to increase the number ?
+ */
+ if (lp->reqlink > lp->actlink) {
+ u_char diff = lp->reqlink - lp->actlink;
+
+ lp->opennings += diff;
+
+ lp->actlink += diff;
+#if 0
+ wakeup ((caddr_t) xp->sc_link);
+#endif
+ if (DEBUG_FLAGS & DEBUG_TAGS)
+ printf ("%s: actlink: diff=%d, new=%d, req=%d\n",
+ ncr_name(np), diff, lp->actlink, lp->reqlink);
+ };
+}
+
+/*==========================================================
+**
+**
+** Build Scatter Gather Block
+**
+**
+**==========================================================
+**
+** The transfer area may be scattered among
+** several non adjacent physical pages.
+**
+** We may use MAX_SCATTER blocks.
+**
+**----------------------------------------------------------
+*/
+
+/*
+** We try to reduce the number of interrupts caused
+** by unexpected phase changes due to disconnects.
+** A typical harddisk may disconnect before ANY block.
+** If we wanted to avoid unexpected phase changes at all
+** we had to use a break point every 512 bytes.
+** Of course the number of scatter/gather blocks is
+** limited.
+** Under Linux, the scatter/gatter blocks are provided by
+** the generic driver. We just have to copy addresses and
+** sizes to the data segment array.
+*/
+
+static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd)
+{
+ struct scr_tblmove *data;
+ int segment = 0;
+ int use_sg = (int) cmd->use_sg;
+
+#if 0
+ bzero (cp->phys.data, sizeof (cp->phys.data));
+#endif
+ data = cp->phys.data;
+ cp->data_len = 0;
+
+ if (!use_sg) {
+ if (cmd->request_bufflen) {
+ data = &data[MAX_SCATTER - 1];
+ data[0].addr = cpu_to_scr(vtophys(cmd->request_buffer));
+ data[0].size = cpu_to_scr(cmd->request_bufflen);
+ cp->data_len = cmd->request_bufflen;
+ segment = 1;
+ }
+ }
+ else if (use_sg <= MAX_SCATTER) {
+ struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
+
+ data = &data[MAX_SCATTER - use_sg];
+ while (segment < use_sg) {
+ data[segment].addr =
+ cpu_to_scr(vtophys(scatter[segment].address));
+ data[segment].size =
+ cpu_to_scr(scatter[segment].length);
+ cp->data_len += scatter[segment].length;
+ ++segment;
+ }
+ }
+ else {
+ return -1;
+ }
+
+ return segment;
+}
+
+/*==========================================================
+**
+**
+** Test the pci bus snoop logic :-(
+**
+** Has to be called with interrupts disabled.
+**
+**
+**==========================================================
+*/
+
+#ifndef NCR_IOMAPPED
+__initfunc(
+static int ncr_regtest (struct ncb* np)
+)
+{
+ register volatile u_long data;
+ /*
+ ** ncr registers may NOT be cached.
+ ** write 0xffffffff to a read only register area,
+ ** and try to read it back.
+ */
+ data = 0xffffffff;
+ OUTL_OFF(offsetof(struct ncr_reg, nc_dstat), data);
+ data = INL_OFF(offsetof(struct ncr_reg, nc_dstat));
+#if 1
+ if (data == 0xffffffff) {
+#else
+ if ((data & 0xe2f0fffd) != 0x02000080) {
+#endif
+ printf ("CACHE TEST FAILED: reg dstat-sstat2 readback %x.\n",
+ (unsigned) data);
+ return (0x10);
+ };
+ return (0);
+}
+#endif
+
+__initfunc(
+static int ncr_snooptest (struct ncb* np)
+)
+{
+ u_long ncr_rd, ncr_wr, ncr_bk, host_rd, host_wr, pc, err=0;
+ int i;
+#ifndef NCR_IOMAPPED
+ if (np->reg) {
+ err |= ncr_regtest (np);
+ if (err) return (err);
+ }
+#endif
+ /*
+ ** init
+ */
+ pc = NCB_SCRIPTH_PHYS (np, snooptest);
+ host_wr = 1;
+ ncr_wr = 2;
+ /*
+ ** Set memory and register.
+ */
+ np->ncr_cache = cpu_to_scr(host_wr);
+ OUTL (nc_temp, ncr_wr);
+ /*
+ ** Start script (exchange values)
+ */
+ OUTL (nc_dsp, pc);
+ /*
+ ** Wait 'til done (with timeout)
+ */
+ for (i=0; i<NCR_SNOOP_TIMEOUT; i++)
+ if (INB(nc_istat) & (INTF|SIP|DIP))
+ break;
+ /*
+ ** Save termination position.
+ */
+ pc = INL (nc_dsp);
+ /*
+ ** Read memory and register.
+ */
+ host_rd = scr_to_cpu(np->ncr_cache);
+ ncr_rd = INL (nc_scratcha);
+ ncr_bk = INL (nc_temp);
+ /*
+ ** Reset ncr chip
+ */
+ OUTB (nc_istat, SRST);
+ DELAY (1000);
+ OUTB (nc_istat, 0 );
+ /*
+ ** check for timeout
+ */
+ if (i>=NCR_SNOOP_TIMEOUT) {
+ printf ("CACHE TEST FAILED: timeout.\n");
+ return (0x20);
+ };
+ /*
+ ** Check termination position.
+ */
+ if (pc != NCB_SCRIPTH_PHYS (np, snoopend)+8) {
+ printf ("CACHE TEST FAILED: script execution failed.\n");
+ printf ("start=%08lx, pc=%08lx, end=%08lx\n",
+ (u_long) NCB_SCRIPTH_PHYS (np, snooptest), pc,
+ (u_long) NCB_SCRIPTH_PHYS (np, snoopend) +8);
+ return (0x40);
+ };
+ /*
+ ** Show results.
+ */
+ if (host_wr != ncr_rd) {
+ printf ("CACHE TEST FAILED: host wrote %d, ncr read %d.\n",
+ (int) host_wr, (int) ncr_rd);
+ err |= 1;
+ };
+ if (host_rd != ncr_wr) {
+ printf ("CACHE TEST FAILED: ncr wrote %d, host read %d.\n",
+ (int) ncr_wr, (int) host_rd);
+ err |= 2;
+ };
+ if (ncr_bk != ncr_wr) {
+ printf ("CACHE TEST FAILED: ncr wrote %d, read back %d.\n",
+ (int) ncr_wr, (int) ncr_bk);
+ err |= 4;
+ };
+ return (err);
+}
+
+/*==========================================================
+**
+**
+** Profiling the drivers and targets performance.
+**
+**
+**==========================================================
+*/
+
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+
+/*
+** Compute the difference in jiffies ticks.
+*/
+
+#define ncr_delta(from, to) \
+ ( ((to) && (from))? (to) - (from) : -1 )
+
+#define PROFILE cp->phys.header.stamp
+static void ncb_profile (ncb_p np, ccb_p cp)
+{
+ int co, st, en, di, se, post,work,disc;
+ u_long diff;
+
+ PROFILE.end = jiffies;
+
+ st = ncr_delta (PROFILE.start,PROFILE.status);
+ if (st<0) return; /* status not reached */
+
+ co = ncr_delta (PROFILE.start,PROFILE.command);
+ if (co<0) return; /* command not executed */
+
+ en = ncr_delta (PROFILE.start,PROFILE.end),
+ di = ncr_delta (PROFILE.start,PROFILE.disconnect),
+ se = ncr_delta (PROFILE.start,PROFILE.select);
+ post = en - st;
+
+ /*
+ ** @PROFILE@ Disconnect time invalid if multiple disconnects
+ */
+
+ if (di>=0) disc = se-di; else disc = 0;
+
+ work = (st - co) - disc;
+
+ diff = (np->disc_phys - np->disc_ref) & 0xff;
+ np->disc_ref += diff;
+
+ np->profile.num_trans += 1;
+ if (cp->cmd) {
+ np->profile.num_kbytes += (cp->cmd->request_bufflen >> 10);
+ np->profile.rest_bytes += (cp->cmd->request_bufflen & (0x400-1));
+ if (np->profile.rest_bytes >= 0x400) {
+ ++np->profile.num_kbytes;
+ np->profile.rest_bytes -= 0x400;
+ }
+ }
+ np->profile.num_disc += diff;
+ np->profile.ms_setup += co;
+ np->profile.ms_data += work;
+ np->profile.ms_disc += disc;
+ np->profile.ms_post += post;
+}
+#undef PROFILE
+
+#endif /* SCSI_NCR_PROFILE_SUPPORT */
+
+/*==========================================================
+**
+**
+** Device lookup.
+**
+** @GENSCSI@ should be integrated to scsiconf.c
+**
+**
+**==========================================================
+*/
+
+struct table_entry {
+ char * manufacturer;
+ char * model;
+ char * version;
+ u_long info;
+};
+
+static struct table_entry device_tab[] =
+{
+#ifdef NCR_GETCC_WITHMSG
+ {"", "", "", QUIRK_NOMSG},
+ {"SONY", "SDT-5000", "3.17", QUIRK_NOMSG},
+ {"WangDAT", "Model 2600", "01.7", QUIRK_NOMSG},
+ {"WangDAT", "Model 3200", "02.2", QUIRK_NOMSG},
+ {"WangDAT", "Model 1300", "02.4", QUIRK_NOMSG},
+#endif
+ {"", "", "", 0} /* catch all: must be last entry. */
+};
+
+static u_long ncr_lookup(char * id)
+{
+ struct table_entry * p = device_tab;
+ char *d, *r, c;
+
+ for (;;p++) {
+
+ d = id+8;
+ r = p->manufacturer;
+ while ((c=*r++)) if (c!=*d++) break;
+ if (c) continue;
+
+ d = id+16;
+ r = p->model;
+ while ((c=*r++)) if (c!=*d++) break;
+ if (c) continue;
+
+ d = id+32;
+ r = p->version;
+ while ((c=*r++)) if (c!=*d++) break;
+ if (c) continue;
+
+ return (p->info);
+ }
+}
+
+/*==========================================================
+**
+** Determine the ncr's clock frequency.
+** This is essential for the negotiation
+** of the synchronous transfer rate.
+**
+**==========================================================
+**
+** Note: we have to return the correct value.
+** THERE IS NO SAVE DEFAULT VALUE.
+**
+** Most NCR/SYMBIOS boards are delivered with a 40 Mhz clock.
+** 53C860 and 53C875 rev. 1 support fast20 transfers but
+** do not have a clock doubler and so are provided with a
+** 80 MHz clock. All other fast20 boards incorporate a doubler
+** and so should be delivered with a 40 MHz clock.
+** The future fast40 chips (895/895) use a 40 Mhz base clock
+** and provide a clock quadrupler (160 Mhz). The code below
+** tries to deal as cleverly as possible with all this stuff.
+**
+**----------------------------------------------------------
+*/
+
+/*
+ * Select NCR SCSI clock frequency
+ */
+static void ncr_selectclock(ncb_p np, u_char scntl3)
+{
+ if (np->multiplier < 2) {
+ OUTB(nc_scntl3, scntl3);
+ return;
+ }
+
+ if (bootverbose >= 2)
+ printf ("%s: enabling clock multiplier\n", ncr_name(np));
+
+ OUTB(nc_stest1, DBLEN); /* Enable clock multiplier */
+ if (np->multiplier > 2) { /* Poll bit 5 of stest4 for quadrupler */
+ int i = 20;
+ while (!(INB(nc_stest4) & LCKFRQ) && --i > 0)
+ DELAY(20);
+ if (!i)
+ printf("%s: the chip cannot lock the frequency\n", ncr_name(np));
+ } else /* Wait 20 micro-seconds for doubler */
+ DELAY(20);
+ OUTB(nc_stest3, HSC); /* Halt the scsi clock */
+ OUTB(nc_scntl3, scntl3);
+ OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier */
+ OUTB(nc_stest3, 0x00|TE); /* Restart scsi clock */
+}
+
+
+/*
+ * calculate NCR SCSI clock frequency (in KHz)
+ */
+__initfunc(
+static unsigned ncrgetfreq (ncb_p np, int gen)
+)
+{
+ unsigned ms = 0;
+
+ /*
+ * Measure GEN timer delay in order
+ * to calculate SCSI clock frequency
+ *
+ * This code will never execute too
+ * many loop iterations (if DELAY is
+ * reasonably correct). It could get
+ * too low a delay (too high a freq.)
+ * if the CPU is slow executing the
+ * loop for some reason (an NMI, for
+ * example). For this reason we will
+ * if multiple measurements are to be
+ * performed trust the higher delay
+ * (lower frequency returned).
+ */
+ OUTB (nc_stest1, 0); /* make sure clock doubler is OFF */
+ OUTW (nc_sien , 0); /* mask all scsi interrupts */
+ (void) INW (nc_sist); /* clear pending scsi interrupt */
+ OUTB (nc_dien , 0); /* mask all dma interrupts */
+ (void) INW (nc_sist); /* another one, just to be sure :) */
+ OUTB (nc_scntl3, 4); /* set pre-scaler to divide by 3 */
+ OUTB (nc_stime1, 0); /* disable general purpose timer */
+ OUTB (nc_stime1, gen); /* set to nominal delay of 1<<gen * 125us */
+ while (!(INW(nc_sist) & GEN) && ms++ < 100000)
+ DELAY(1000); /* count ms */
+ OUTB (nc_stime1, 0); /* disable general purpose timer */
+ /*
+ * set prescaler to divide by whatever 0 means
+ * 0 ought to choose divide by 2, but appears
+ * to set divide by 3.5 mode in my 53c810 ...
+ */
+ OUTB (nc_scntl3, 0);
+
+ if (bootverbose >= 2)
+ printf ("%s: Delay (GEN=%d): %u msec\n", ncr_name(np), gen, ms);
+ /*
+ * adjust for prescaler, and convert into KHz
+ */
+ return ms ? ((1 << gen) * 4340) / ms : 0;
+}
+
+/*
+ * Get/probe NCR SCSI clock frequency
+ */
+__initfunc(
+static void ncr_getclock (ncb_p np, int mult)
+)
+{
+ unsigned char scntl3 = INB(nc_scntl3);
+ unsigned char stest1 = INB(nc_stest1);
+ unsigned f1;
+
+ np->multiplier = 1;
+ f1 = 40000;
+
+ /*
+ ** True with 875 or 895 with clock multiplier selected
+ */
+ if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
+ if (bootverbose >= 2)
+ printf ("%s: clock multiplier found\n", ncr_name(np));
+ np->multiplier = mult;
+ }
+
+ /*
+ ** If multiplier not found or scntl3 not 7,5,3,
+ ** reset chip and get frequency from general purpose timer.
+ ** Otherwise trust scntl3 BIOS setting.
+ */
+ if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) {
+ unsigned f2;
+
+ OUTB(nc_istat, SRST); DELAY(5); OUTB(nc_istat, 0);
+
+ (void) ncrgetfreq (np, 11); /* throw away first result */
+ f1 = ncrgetfreq (np, 11);
+ f2 = ncrgetfreq (np, 11);
+
+ if (bootverbose)
+ printf ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2);
+
+ if (f1 > f2) f1 = f2; /* trust lower result */
+
+ if (f1 < 45000) f1 = 40000;
+ else if (f1 < 55000) f1 = 50000;
+ else f1 = 80000;
+
+ if (f1 < 80000 && mult > 1) {
+ if (bootverbose >= 2)
+ printf ("%s: clock multiplier assumed\n", ncr_name(np));
+ np->multiplier = mult;
+ }
+ } else {
+ if ((scntl3 & 7) == 3) f1 = 40000;
+ else if ((scntl3 & 7) == 5) f1 = 80000;
+ else f1 = 160000;
+
+ f1 /= np->multiplier;
+ }
+
+ /*
+ ** Compute controller synchronous parameters.
+ */
+ f1 *= np->multiplier;
+ np->clock_khz = f1;
+}
+
+/*===================== LINUX ENTRY POINTS SECTION ==========================*/
+
+#ifndef uchar
+#define uchar unsigned char
+#endif
+
+#ifndef ushort
+#define ushort unsigned short
+#endif
+
+#ifndef ulong
+#define ulong unsigned long
+#endif
+
+/* ---------------------------------------------------------------------
+**
+** Driver setup from the boot command line
+**
+** ---------------------------------------------------------------------
+*/
+
+__initfunc(
+void ncr53c8xx_setup(char *str, int *ints)
+)
+{
+#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+ char *cur = str;
+ char *pc, *pv;
+ int val;
+ int base;
+ int c;
+
+ while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
+ val = 0;
+ pv = pc;
+ c = *++pv;
+ if (c == 'n')
+ val = 0;
+ else if (c == 'y')
+ val = 1;
+ else {
+ base = 0;
+#if 0
+ if (c == '0') {
+ c = *pv++;
+ base = 8;
+ }
+ if (c == 'x') {
+ ++pv;
+ base = 16;
+ }
+ else if (c >= '0' && c <= '9')
+ base = 10;
+ else
+ break;
+#endif
+ val = (int) simple_strtoul(pv, NULL, base);
+ }
+
+ if (!strncmp(cur, "mpar:", 5))
+ driver_setup.master_parity = val;
+ else if (!strncmp(cur, "spar:", 5))
+ driver_setup.scsi_parity = val;
+ else if (!strncmp(cur, "disc:", 5))
+ driver_setup.disconnection = val;
+ else if (!strncmp(cur, "specf:", 6))
+ driver_setup.special_features = val;
+ else if (!strncmp(cur, "ultra:", 6))
+ driver_setup.ultra_scsi = val;
+ else if (!strncmp(cur, "fsn:", 4))
+ driver_setup.force_sync_nego = val;
+ else if (!strncmp(cur, "revprob:", 8))
+ driver_setup.reverse_probe = val;
+ else if (!strncmp(cur, "tags:", 5)) {
+ if (val > SCSI_NCR_MAX_TAGS)
+ val = SCSI_NCR_MAX_TAGS;
+ driver_setup.default_tags = val;
+ }
+ else if (!strncmp(cur, "sync:", 5))
+ driver_setup.default_sync = val;
+ else if (!strncmp(cur, "verb:", 5))
+ driver_setup.verbose = val;
+ else if (!strncmp(cur, "debug:", 6))
+ driver_setup.debug = val;
+ else if (!strncmp(cur, "burst:", 6))
+ driver_setup.burst_max = val;
+ else if (!strncmp(cur, "led:", 4))
+ driver_setup.led_pin = val;
+ else if (!strncmp(cur, "wide:", 5))
+ driver_setup.max_wide = val? 1:0;
+ else if (!strncmp(cur, "settle:", 7))
+ driver_setup.settle_delay= val;
+ else if (!strncmp(cur, "diff:", 5))
+ driver_setup.diff_support= val;
+ else if (!strncmp(cur, "irqm:", 5))
+ driver_setup.irqm = val;
+ else if (!strncmp(cur, "pcifix:", 7))
+ driver_setup.pci_fix_up = val;
+ else if (!strncmp(cur, "buschk:", 7))
+ driver_setup.bus_check = val;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ else if (!strncmp(cur, "nvram:", 6))
+ driver_setup.use_nvram = val;
+#endif
+
+ else if (!strncmp(cur, "safe:", 5) && val)
+ memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup));
+ else
+ printf("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+
+#ifdef MODULE
+ if ((cur = strchr(cur, ' ')) != NULL)
+#else
+ if ((cur = strchr(cur, ',')) != NULL)
+#endif
+ ++cur;
+ }
+#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
+}
+
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+ uchar bus, uchar device_fn, ncr_device *device);
+
+/*
+** Linux entry point for NCR53C8XX devices detection routine.
+**
+** Called by the middle-level scsi drivers at initialization time,
+** or at module installation.
+**
+** Read the PCI configuration and try to attach each
+** detected NCR board.
+**
+** If NVRAM is present, try to attach boards according to
+** the used defined boot order.
+**
+** Returns the number of boards successfully attached.
+*/
+
+__initfunc(
+static void ncr_print_driver_setup(void)
+)
+{
+#define YesNo(y) y ? 'y' : 'n'
+ printk("ncr53c8xx: setup=disc:%c,specf:%d,ultra:%c,tags:%d,sync:%d,burst:%d,wide:%c,diff:%d\n",
+ YesNo(driver_setup.disconnection),
+ driver_setup.special_features,
+ YesNo(driver_setup.ultra_scsi),
+ driver_setup.default_tags,
+ driver_setup.default_sync,
+ driver_setup.burst_max,
+ YesNo(driver_setup.max_wide),
+ driver_setup.diff_support);
+ printk("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,led:%c,settle:%d,irqm:%d\n",
+ YesNo(driver_setup.master_parity),
+ YesNo(driver_setup.scsi_parity),
+ YesNo(driver_setup.force_sync_nego),
+ driver_setup.verbose,
+ driver_setup.debug,
+ YesNo(driver_setup.led_pin),
+ driver_setup.settle_delay,
+ driver_setup.irqm);
+#undef YesNo
+}
+
+/*
+** NCR53C8XX devices description table and chip ids list.
+*/
+
+static ncr_chip ncr_chip_table[] __initdata = SCSI_NCR_CHIP_TABLE;
+static ushort ncr_chip_ids[] __initdata = SCSI_NCR_CHIP_IDS;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+__initfunc(
+static int
+ncr_attach_using_nvram(Scsi_Host_Template *tpnt, int nvram_index, int count, ncr_device device[])
+)
+{
+ int i, j;
+ int attach_count = 0;
+ ncr_nvram *nvram;
+ ncr_device *devp;
+
+ if (!nvram_index)
+ return 0;
+
+ /* find first Symbios NVRAM if there is one as we need to check it for host boot order */
+ for (i = 0, nvram_index = -1; i < count; i++) {
+ devp = &device[i];
+ nvram = devp->nvram;
+ if (!nvram)
+ continue;
+ if (nvram->type == SCSI_NCR_SYMBIOS_NVRAM) {
+ if (nvram_index == -1)
+ nvram_index = i;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+ printf("ncr53c8xx: NVRAM: Symbios format Boot Block, 53c%s, PCI bus %d, device %d, function %d\n",
+ devp->chip.name, devp->slot.bus,
+ (int) (devp->slot.device_fn & 0xf8) >> 3,
+ (int) devp->slot.device_fn & 7);
+ for (j = 0 ; j < 4 ; j++) {
+ Symbios_host *h = &nvram->data.Symbios.host[j];
+ printf("ncr53c8xx: BOOT[%d] device_id=%04x vendor_id=%04x device_fn=%02x io_port=%04x %s\n",
+ j, h->device_id, h->vendor_id,
+ h->device_fn, h->io_port,
+ (h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) ? "SCAN AT BOOT" : "");
+ }
+ }
+ else if (nvram->type == SCSI_NCR_TEKRAM_NVRAM) {
+ /* display Tekram nvram data */
+ printf("ncr53c8xx: NVRAM: Tekram format data, 53c%s, PCI bus %d, device %d, function %d\n",
+ devp->chip.name, devp->slot.bus,
+ (int) (devp->slot.device_fn & 0xf8) >> 3,
+ (int) devp->slot.device_fn & 7);
+#endif
+ }
+ }
+
+ if (nvram_index >= 0 && nvram_index < count)
+ nvram = device[nvram_index].nvram;
+ else
+ nvram = 0;
+
+ if (!nvram)
+ goto out;
+
+ /*
+ ** check devices in the boot record against devices detected.
+ ** attach devices if we find a match. boot table records that
+ ** do not match any detected devices will be ignored.
+ ** devices that do not match any boot table will not be attached
+ ** here but will attempt to be attached during the device table
+ ** rescan.
+ */
+ for (i = 0; i < 4; i++) {
+ Symbios_host *h = &nvram->data.Symbios.host[i];
+ for (j = 0 ; j < count ; j++) {
+ devp = &device[j];
+ if (h->device_fn == devp->slot.device_fn &&
+#if 0 /* bus number location in nvram ? */
+ h->bus == devp->slot.bus &&
+#endif
+ h->device_id == devp->chip.device_id)
+ break;
+ }
+ if (j < count && !devp->attach_done) {
+ if (!ncr_attach (tpnt, attach_count, devp))
+ attach_count++;
+ devp->attach_done = 1;
+ }
+ }
+
+out:
+ return attach_count;
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+__initfunc(
+int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
+)
+{
+ int i, j;
+ int chips;
+ int count = 0;
+ uchar bus, device_fn;
+ short index;
+ int attach_count = 0;
+ ncr_device device[8];
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_nvram nvram[4];
+ int k, nvrams;
+#endif
+ int hosts;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ int nvram_index = 0;
+#endif
+ if (initverbose >= 2)
+ ncr_print_driver_setup();
+
+#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
+ ncr_debug = driver_setup.debug;
+#endif
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+ tpnt->proc_dir = &proc_scsi_ncr53c8xx;
+# ifdef SCSI_NCR_PROC_INFO_SUPPORT
+ tpnt->proc_info = ncr53c8xx_proc_info;
+# endif
+#endif
+
+#if defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
+if (ncr53c8xx)
+ ncr53c8xx_setup(ncr53c8xx, (int *) 0);
+#endif
+
+ /*
+ ** Detect all 53c8xx hosts and then attach them.
+ **
+ ** If we are using NVRAM, once all hosts are detected, we need to check
+ ** any NVRAM for boot order in case detect and boot order differ and
+ ** attach them using the order in the NVRAM.
+ **
+ ** If no NVRAM is found or data appears invalid attach boards in the
+ ** the order they are detected.
+ */
+
+ if (!pcibios_present())
+ return 0;
+
+ chips = sizeof(ncr_chip_ids) / sizeof(ncr_chip_ids[0]);
+ hosts = sizeof(device) / sizeof(device[0]);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ k = 0;
+ if (driver_setup.use_nvram & 0x1)
+ nvrams = sizeof(nvram) / sizeof(nvram[0]);
+ else
+ nvrams = 0;
+#endif
+
+ for (j = 0; j < chips ; ++j) {
+ i = driver_setup.reverse_probe ? chips-1 - j : j;
+ for (index = 0; ; index++) {
+ char *msg = "";
+ if ((pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+ index, &bus, &device_fn)) ||
+ (count == hosts))
+ break;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ device[count].nvram = k < nvrams ? &nvram[k] : 0;
+#else
+ device[count].nvram = 0;
+#endif
+ if (ncr53c8xx_pci_init(tpnt, bus, device_fn, &device[count])) {
+ device[count].nvram = 0;
+ continue;
+ }
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (device[count].nvram) {
+ ++k;
+ nvram_index |= device[count].nvram->type;
+ switch (device[count].nvram->type) {
+ case SCSI_NCR_TEKRAM_NVRAM:
+ msg = "with Tekram NVRAM";
+ break;
+ case SCSI_NCR_SYMBIOS_NVRAM:
+ msg = "with Symbios NVRAM";
+ break;
+ default:
+ msg = "";
+ device[count].nvram = 0;
+ --k;
+ }
+ }
+#endif
+ printf(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
+ device[count].chip.name, msg);
+ ++count;
+ }
+ }
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ attach_count = ncr_attach_using_nvram(tpnt, nvram_index, count, device);
+#endif
+ /*
+ ** rescan device list to make sure all boards attached.
+ ** devices without boot records will not be attached yet
+ ** so try to attach them here.
+ */
+ for (i= 0; i < count; i++) {
+ if (!device[i].attach_done &&
+ !ncr_attach (tpnt, attach_count, &device[i])) {
+ attach_count++;
+ }
+ }
+
+ return attach_count;
+}
+
+/*
+** Read and check the PCI configuration for any detected NCR
+** boards and save data for attaching after all boards have
+** been detected.
+*/
+
+__initfunc(
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+ uchar bus, uchar device_fn, ncr_device *device)
+)
+{
+ ushort vendor_id, device_id, command;
+ uchar cache_line_size, latency_timer;
+ uchar irq, revision;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+ uint base, base_2, io_port;
+#else
+ ulong base, base_2;
+#endif
+ int i;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ ncr_nvram *nvram = device->nvram;
+#endif
+ ncr_chip *chip;
+
+ printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
+ bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
+ /*
+ * Read info from the PCI config space.
+ * pcibios_read_config_xxx() functions are assumed to be used for
+ * successfully detected PCI devices.
+ * Expecting error conditions from them is just paranoia,
+ * thus void cast.
+ */
+ (void) pcibios_read_config_word(bus, device_fn,
+ PCI_VENDOR_ID, &vendor_id);
+ (void) pcibios_read_config_word(bus, device_fn,
+ PCI_DEVICE_ID, &device_id);
+ (void) pcibios_read_config_word(bus, device_fn,
+ PCI_COMMAND, &command);
+ (void) pcibios_read_config_dword(bus, device_fn,
+ PCI_BASE_ADDRESS_0, &io_port);
+ (void) pcibios_read_config_dword(bus, device_fn,
+ PCI_BASE_ADDRESS_1, &base);
+ (void) pcibios_read_config_dword(bus, device_fn,
+ PCI_BASE_ADDRESS_2, &base_2);
+ (void) pcibios_read_config_byte(bus, device_fn,
+ PCI_CLASS_REVISION,&revision);
+ (void) pcibios_read_config_byte(bus, device_fn,
+ PCI_INTERRUPT_LINE, &irq);
+ (void) pcibios_read_config_byte(bus, device_fn,
+ PCI_CACHE_LINE_SIZE, &cache_line_size);
+ (void) pcibios_read_config_byte(bus, device_fn,
+ PCI_LATENCY_TIMER, &latency_timer);
+
+ /*
+ * Check if the chip is supported
+ */
+ chip = 0;
+ for (i = 0; i < sizeof(ncr_chip_table)/sizeof(ncr_chip_table[0]); i++) {
+ if (device_id != ncr_chip_table[i].device_id)
+ continue;
+ if (revision > ncr_chip_table[i].revision_id)
+ continue;
+ chip = &device->chip;
+ memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
+ chip->revision_id = revision;
+ break;
+ }
+ if (!chip) {
+ printk("ncr53c8xx: not initializing, device not supported\n");
+ return -1;
+ }
+
+#ifdef __powerpc__
+ /*
+ * Severall fix-up for power/pc.
+ * Should not be performed by the driver.
+ */
+ if ((command &
+ (PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) !=
+ (PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) {
+ printk("ncr53c8xx : setting PCI master/io/command bit\n");
+ command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY;
+ pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+ }
+ if (io_port >= 0x10000000) {
+ io_port = (io_port & 0x00FFFFFF) | 0x01000000;
+ pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, io_port);
+ }
+ if (base >= 0x10000000) {
+ base = (base & 0x00FFFFFF) | 0x01000000;
+ pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, base);
+ }
+#endif
+
+ /*
+ * Check availability of IO space, memory space and master capability.
+ */
+ if (command & PCI_COMMAND_IO) {
+ if ((io_port & 3) != 1) {
+ printk("ncr53c8xx: disabling I/O mapping since base address 0 (0x%x)\n"
+ " bits 0..1 indicate a non-IO mapping\n", (int) io_port);
+ io_port = 0;
+ }
+ else
+ io_port &= PCI_BASE_ADDRESS_IO_MASK;
+ }
+ else
+ io_port = 0;
+
+ if (command & PCI_COMMAND_MEMORY) {
+ if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+ printk("ncr53c8xx: disabling memory mapping since base address 1\n"
+ " contains a non-memory mapping\n");
+ base = 0;
+ }
+ else
+ base &= PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ else
+ base = 0;
+
+ if (!io_port && !base) {
+ printk("ncr53c8xx: not initializing, both I/O and memory mappings disabled\n");
+ return -1;
+ }
+
+ base_2 &= PCI_BASE_ADDRESS_MEM_MASK;
+
+ if (io_port && check_region (io_port, 128)) {
+ printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
+ (int) io_port, (int) (io_port + 127));
+ return -1;
+ }
+
+ if (!(command & PCI_COMMAND_MASTER)) {
+ printk("ncr53c8xx: not initializing, BUS MASTERING was disabled\n");
+ return -1;
+ }
+
+ /*
+ * Fix some features according to driver setup.
+ */
+ if (!(driver_setup.special_features & 1))
+ chip->features &= ~FE_SPECIAL_SET;
+ else {
+ if (driver_setup.special_features & 2)
+ chip->features &= ~FE_WRIE;
+ }
+ if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
+ chip->features |= FE_ULTRA;
+ chip->features &= ~FE_ULTRA2;
+ }
+ if (driver_setup.ultra_scsi < 1)
+ chip->features &= ~FE_ULTRA;
+ if (!driver_setup.max_wide)
+ chip->features &= ~FE_WIDE;
+
+
+#ifdef SCSI_NCR_PCI_FIX_UP_SUPPORT
+
+ /*
+ * Try to fix up PCI config according to wished features.
+ */
+#if defined(__i386) && !defined(MODULE)
+ if ((driver_setup.pci_fix_up & 1) &&
+ (chip->features & FE_CLSE) && cache_line_size == 0) {
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
+ extern char x86;
+ switch(x86) {
+#else
+ switch(boot_cpu_data.x86) {
+#endif
+ case 4: cache_line_size = 4; break;
+ case 5: cache_line_size = 8; break;
+ }
+ if (cache_line_size)
+ (void) pcibios_write_config_byte(bus, device_fn,
+ PCI_CACHE_LINE_SIZE, cache_line_size);
+ if (initverbose)
+ printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
+ }
+
+ if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
+ (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+ command |= PCI_COMMAND_INVALIDATE;
+ (void) pcibios_write_config_word(bus, device_fn,
+ PCI_COMMAND, command);
+ if (initverbose)
+ printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
+ }
+#endif
+ /*
+ * Fix up for old chips that support READ LINE but not CACHE LINE SIZE.
+ * - If CACHE LINE SIZE is unknown, set burst max to 32 bytes = 8 dwords
+ * and donnot enable READ LINE.
+ * - Otherwise set it to the CACHE LINE SIZE (power of 2 assumed).
+ */
+
+ if (!(chip->features & FE_CLSE)) {
+ int burst_max = chip->burst_max;
+ if (cache_line_size == 0) {
+ chip->features &= ~FE_ERL;
+ if (burst_max > 3)
+ burst_max = 3;
+ }
+ else {
+ while (cache_line_size < (1 << burst_max))
+ --burst_max;
+ }
+ chip->burst_max = burst_max;
+ }
+
+ /*
+ * Tune PCI LATENCY TIMER according to burst max length transfer.
+ * (latency timer >= burst length + 6, we add 10 to be quite sure)
+ * If current value is zero, the device has probably been configured
+ * for no bursting due to some broken hardware.
+ */
+
+ if (latency_timer == 0 && chip->burst_max)
+ printk("ncr53c8xx: PCI_LATENCY_TIMER=0, bursting should'nt be allowed.\n");
+
+ if ((driver_setup.pci_fix_up & 4) && chip->burst_max) {
+ uchar lt = (1 << chip->burst_max) + 6 + 10;
+ if (latency_timer < lt) {
+ latency_timer = lt;
+ if (initverbose)
+ printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer);
+ (void) pcibios_write_config_byte(bus, device_fn,
+ PCI_LATENCY_TIMER, latency_timer);
+ }
+ }
+
+ /*
+ * Fix up for recent chips that support CACHE LINE SIZE.
+ * If PCI config space is not OK, remove features that shall not be
+ * used by the chip. No need to trigger possible chip bugs.
+ */
+
+ if ((chip->features & FE_CLSE) && cache_line_size == 0) {
+ chip->features &= ~FE_CACHE_SET;
+ printk("ncr53c8xx: PCI_CACHE_LINE_SIZE not set, features based on CACHE LINE SIZE not used.\n");
+ }
+
+ if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+ chip->features &= ~FE_WRIE;
+ printk("ncr53c8xx: PCI_COMMAND_INVALIDATE not set, WRITE AND INVALIDATE not used\n");
+ }
+
+#endif /* SCSI_NCR_PCI_FIX_UP_SUPPORT */
+
+ /* initialise ncr_device structure with items required by ncr_attach */
+ device->slot.bus = bus;
+ device->slot.device_fn = device_fn;
+ device->slot.base = base;
+ device->slot.base_2 = base_2;
+ device->slot.io_port = io_port;
+ device->slot.irq = irq;
+ device->attach_done = 0;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+ if (!nvram)
+ goto out;
+
+ /*
+ ** Get access to chip IO registers
+ */
+#ifdef NCR_IOMAPPED
+ request_region(io_port, 128, "ncr53c8xx");
+ device->slot.port = io_port;
+#else
+ device->slot.reg = (struct ncr_reg *) remap_pci_mem((ulong) base, 128);
+ if (!device->slot.reg)
+ goto out;
+#endif
+
+ /*
+ ** Try to read SYMBIOS nvram.
+ ** Data can be used to order booting of boards.
+ **
+ ** Data is saved in ncr_device structure if NVRAM found. This
+ ** is then used to find drive boot order for ncr_attach().
+ **
+ ** NVRAM data is passed to Scsi_Host_Template later during ncr_attach()
+ ** for any device set up.
+ **
+ ** Try to read TEKRAM nvram if Symbios nvram not found.
+ */
+
+ if (!ncr_get_Symbios_nvram(&device->slot, &nvram->data.Symbios))
+ nvram->type = SCSI_NCR_SYMBIOS_NVRAM;
+ else if (!ncr_get_Tekram_nvram(&device->slot, &nvram->data.Tekram))
+ nvram->type = SCSI_NCR_TEKRAM_NVRAM;
+ else
+ nvram->type = 0;
+out:
+ /*
+ ** Release access to chip IO registers
+ */
+#ifdef NCR_IOMAPPED
+ release_region(device->slot.port, 128);
+#else
+ unmap_pci_mem((vm_offset_t) device->slot.reg, (u_long) 128);
+#endif
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
+/*
+** Linux select queue depths function
+*/
+static void ncr53c8xx_select_queue_depths(struct Scsi_Host *host, struct scsi_device *devlist)
+{
+ struct scsi_device *device;
+
+ for (device = devlist; device; device = device->next) {
+ if (device->host == host) {
+#if SCSI_NCR_MAX_TAGS > 1
+ if (device->tagged_supported) {
+ device->queue_depth = SCSI_NCR_MAX_TAGS;
+ }
+ else {
+ device->queue_depth = 2;
+ }
+#else
+ device->queue_depth = 1;
+#endif
+
+#ifdef DEBUG_NCR53C8XX
+printk("ncr53c8xx_select_queue_depth: id=%d, lun=%d, queue_depth=%d\n",
+ device->id, device->lun, device->queue_depth);
+#endif
+ }
+ }
+}
+#endif
+
+/*
+** Linux entry point of queuecommand() function
+*/
+
+int ncr53c8xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
+{
+ int sts;
+#ifdef DEBUG_NCR53C8XX
+printk("ncr53c8xx_queue_command\n");
+#endif
+
+ if ((sts = ncr_queue_command(cmd, done)) != DID_OK) {
+ cmd->result = ScsiResult(sts, 0);
+ done(cmd);
+#ifdef DEBUG_NCR53C8XX
+printk("ncr53c8xx : command not queued - result=%d\n", sts);
+#endif
+ return sts;
+ }
+#ifdef DEBUG_NCR53C8XX
+printk("ncr53c8xx : command successfully queued\n");
+#endif
+ return sts;
+}
+
+/*
+** Linux entry point of the interrupt handler.
+** Fort linux versions > 1.3.70, we trust the kernel for
+** passing the internal host descriptor as 'dev_id'.
+** Otherwise, we scan the host list and call the interrupt
+** routine for each host that uses this IRQ.
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
+static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
+{
+#ifdef DEBUG_NCR53C8XX
+ printk("ncr53c8xx : interrupt received\n");
+#endif
+
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+ ncr_exception((ncb_p) dev_id);
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
+}
+
+#else
+static void ncr53c8xx_intr(int irq, struct pt_regs * regs)
+{
+ struct Scsi_Host *host;
+ struct host_data *host_data;
+
+ for (host = first_host; host; host = host->next) {
+ if (host->hostt == the_template && host->irq == irq) {
+ host_data = (struct host_data *) host->hostdata;
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+ ncr_exception(host_data->ncb);
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
+ }
+ }
+}
+#endif
+
+/*
+** Linux entry point of the timer handler
+*/
+
+static void ncr53c8xx_timeout(unsigned long np)
+{
+ ncr_timeout((ncb_p) np);
+}
+
+/*
+** Linux entry point of reset() function
+*/
+
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
+
+int ncr53c8xx_reset(Scsi_Cmnd *cmd, unsigned int reset_flags)
+{
+ int sts;
+ unsigned long flags;
+
+ printk("ncr53c8xx_reset: pid=%lu reset_flags=%x serial_number=%ld serial_number_at_timeout=%ld\n",
+ cmd->pid, reset_flags, cmd->serial_number, cmd->serial_number_at_timeout);
+
+ save_flags(flags); cli();
+
+ /*
+ * We have to just ignore reset requests in some situations.
+ */
+#if defined SCSI_RESET_NOT_RUNNING
+ if (cmd->serial_number != cmd->serial_number_at_timeout) {
+ sts = SCSI_RESET_NOT_RUNNING;
+ goto out;
+ }
+#endif
+ /*
+ * If the mid-level driver told us reset is synchronous, it seems
+ * that we must call the done() callback for the involved command,
+ * even if this command was not queued to the low-level driver,
+ * before returning SCSI_RESET_SUCCESS.
+ */
+
+ sts = ncr_reset_bus(cmd,
+ (reset_flags & (SCSI_RESET_SYNCHRONOUS | SCSI_RESET_ASYNCHRONOUS)) == SCSI_RESET_SYNCHRONOUS);
+ /*
+ * Since we always reset the controller, when we return success,
+ * we add this information to the return code.
+ */
+#if defined SCSI_RESET_HOST_RESET
+ if (sts == SCSI_RESET_SUCCESS)
+ sts |= SCSI_RESET_HOST_RESET;
+#endif
+
+out:
+ restore_flags(flags);
+ return sts;
+}
+#else
+int ncr53c8xx_reset(Scsi_Cmnd *cmd)
+{
+ printk("ncr53c8xx_reset: command pid %lu\n", cmd->pid);
+ return ncr_reset_bus(cmd, 1);
+}
+#endif
+
+/*
+** Linux entry point of abort() function
+*/
+
+#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS
+
+int ncr53c8xx_abort(Scsi_Cmnd *cmd)
+{
+ int sts;
+ unsigned long flags;
+
+ printk("ncr53c8xx_abort: pid=%lu serial_number=%ld serial_number_at_timeout=%ld\n",
+ cmd->pid, cmd->serial_number, cmd->serial_number_at_timeout);
+
+ save_flags(flags); cli();
+
+ /*
+ * We have to just ignore abort requests in some situations.
+ */
+ if (cmd->serial_number != cmd->serial_number_at_timeout) {
+ sts = SCSI_ABORT_NOT_RUNNING;
+ goto out;
+ }
+
+ sts = ncr_abort_command(cmd);
+out:
+ restore_flags(flags);
+ return sts;
+}
+#else
+int ncr53c8xx_abort(Scsi_Cmnd *cmd)
+{
+ printk("ncr53c8xx_abort: command pid %lu\n", cmd->pid);
+ return ncr_abort_command(cmd);
+}
+#endif
+
+#ifdef MODULE
+int ncr53c8xx_release(struct Scsi_Host *host)
+{
+#ifdef DEBUG_NCR53C8XX
+printk("ncr53c8xx : release\n");
+#endif
+ ncr_detach(((struct host_data *) host->hostdata)->ncb);
+
+ return 1;
+}
+#endif
+
+
+/*
+** Scsi command waiting list management.
+**
+** It may happen that we cannot insert a scsi command into the start queue,
+** in the following circumstances.
+** Too few preallocated ccb(s),
+** maxtags < cmd_per_lun of the Linux host control block,
+** etc...
+** Such scsi commands are inserted into a waiting list.
+** When a scsi command complete, we try to requeue the commands of the
+** waiting list.
+*/
+
+#define next_wcmd host_scribble
+
+static void insert_into_waiting_list(ncb_p np, Scsi_Cmnd *cmd)
+{
+ Scsi_Cmnd *wcmd;
+
+#ifdef DEBUG_WAITING_LIST
+ printf("%s: cmd %lx inserted into waiting list\n", ncr_name(np), (u_long) cmd);
+#endif
+ cmd->next_wcmd = 0;
+ if (!(wcmd = np->waiting_list)) np->waiting_list = cmd;
+ else {
+ while ((wcmd->next_wcmd) != 0)
+ wcmd = (Scsi_Cmnd *) wcmd->next_wcmd;
+ wcmd->next_wcmd = (char *) cmd;
+ }
+}
+
+static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd)
+{
+ Scsi_Cmnd *wcmd;
+
+ if (!(wcmd = np->waiting_list)) return 0;
+ while (wcmd->next_wcmd) {
+ if (cmd == (Scsi_Cmnd *) wcmd->next_wcmd) {
+ if (to_remove) {
+ wcmd->next_wcmd = cmd->next_wcmd;
+ cmd->next_wcmd = 0;
+ }
+#ifdef DEBUG_WAITING_LIST
+ printf("%s: cmd %lx retrieved from waiting list\n", ncr_name(np), (u_long) cmd);
+#endif
+ return cmd;
+ }
+ }
+ return 0;
+}
+
+static void process_waiting_list(ncb_p np, int sts)
+{
+ Scsi_Cmnd *waiting_list, *wcmd;
+
+ waiting_list = np->waiting_list;
+ np->waiting_list = 0;
+
+#ifdef DEBUG_WAITING_LIST
+ if (waiting_list) printf("%s: waiting_list=%lx processing sts=%d\n", ncr_name(np), (u_long) waiting_list, sts);
+#endif
+ while ((wcmd = waiting_list) != 0) {
+ waiting_list = (Scsi_Cmnd *) wcmd->next_wcmd;
+ wcmd->next_wcmd = 0;
+ if (sts == DID_OK) {
+#ifdef DEBUG_WAITING_LIST
+ printf("%s: cmd %lx trying to requeue\n", ncr_name(np), (u_long) wcmd);
+#endif
+ sts = ncr_queue_command(wcmd, wcmd->scsi_done);
+ }
+ if (sts != DID_OK) {
+#ifdef DEBUG_WAITING_LIST
+ printf("%s: cmd %lx done forced sts=%d\n", ncr_name(np), (u_long) wcmd, sts);
+#endif
+ wcmd->result = ScsiResult(sts, 0);
+ wcmd->scsi_done(wcmd);
+ }
+ }
+}
+
+#undef next_wcmd
+
+/*
+** Returns data transfer direction for common op-codes.
+*/
+
+static int guess_xfer_direction(int opcode)
+{
+ int d;
+
+ switch(opcode) {
+ case 0x12: /* INQUIRY 12 */
+ case 0x4D: /* LOG SENSE 4D */
+ case 0x5A: /* MODE SENSE(10) 5A */
+ case 0x1A: /* MODE SENSE(6) 1A */
+ case 0x3C: /* READ BUFFER 3C */
+ case 0x1C: /* RECEIVE DIAGNOSTIC RESULTS 1C */
+ case 0x03: /* REQUEST SENSE 03 */
+ d = XferIn;
+ break;
+ case 0x39: /* COMPARE 39 */
+ case 0x3A: /* COPY AND VERIFY 3A */
+ case 0x18: /* COPY 18 */
+ case 0x4C: /* LOG SELECT 4C */
+ case 0x55: /* MODE SELECT(10) 55 */
+ case 0x3B: /* WRITE BUFFER 3B */
+ case 0x1D: /* SEND DIAGNOSTIC 1D */
+ case 0x40: /* CHANGE DEFINITION 40 */
+ case 0x15: /* MODE SELECT(6) 15 */
+ d = XferOut;
+ break;
+ case 0x00: /* TEST UNIT READY 00 */
+ d = XferNone;
+ break;
+ default:
+ d = XferBoth;
+ break;
+ }
+
+ return d;
+}
+
+
+#ifdef SCSI_NCR_PROC_INFO_SUPPORT
+
+/*=========================================================================
+** Proc file system stuff
+**
+** A read operation returns profile information.
+** A write operation is a control command.
+** The string is parsed in the driver code and the command is passed
+** to the ncr_usercmd() function.
+**=========================================================================
+*/
+
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
+
+#define is_digit(c) ((c) >= '0' && (c) <= '9')
+#define digit_to_bin(c) ((c) - '0')
+#define is_space(c) ((c) == ' ' || (c) == '\t')
+
+static int skip_spaces(char *ptr, int len)
+{
+ int cnt, c;
+
+ for (cnt = len; cnt > 0 && (c = *ptr++) && is_space(c); cnt--);
+
+ return (len - cnt);
+}
+
+static int get_int_arg(char *ptr, int len, u_long *pv)
+{
+ int cnt, c;
+ u_long v;
+
+ for (v = 0, cnt = len; cnt > 0 && (c = *ptr++) && is_digit(c); cnt--) {
+ v = (v * 10) + digit_to_bin(c);
+ }
+
+ if (pv)
+ *pv = v;
+
+ return (len - cnt);
+}
+
+static int is_keyword(char *ptr, int len, char *verb)
+{
+ int verb_len = strlen(verb);
+
+ if (len >= strlen(verb) && !memcmp(verb, ptr, verb_len))
+ return verb_len;
+ else
+ return 0;
+
+}
+
+#define SKIP_SPACES(min_spaces) \
+ if ((arg_len = skip_spaces(ptr, len)) < (min_spaces)) \
+ return -EINVAL; \
+ ptr += arg_len; len -= arg_len;
+
+#define GET_INT_ARG(v) \
+ if (!(arg_len = get_int_arg(ptr, len, &(v)))) \
+ return -EINVAL; \
+ ptr += arg_len; len -= arg_len;
+
+
+/*
+** Parse a control command
+*/
+
+static int ncr_user_command(ncb_p np, char *buffer, int length)
+{
+ char *ptr = buffer;
+ int len = length;
+ struct usrcmd *uc = &np->user;
+ int arg_len;
+ u_long target;
+
+ bzero(uc, sizeof(*uc));
+
+ if (len > 0 && ptr[len-1] == '\n')
+ --len;
+
+ if ((arg_len = is_keyword(ptr, len, "setsync")) != 0)
+ uc->cmd = UC_SETSYNC;
+ else if ((arg_len = is_keyword(ptr, len, "settags")) != 0)
+ uc->cmd = UC_SETTAGS;
+ else if ((arg_len = is_keyword(ptr, len, "setorder")) != 0)
+ uc->cmd = UC_SETORDER;
+ else if ((arg_len = is_keyword(ptr, len, "setwide")) != 0)
+ uc->cmd = UC_SETWIDE;
+ else if ((arg_len = is_keyword(ptr, len, "setdebug")) != 0)
+ uc->cmd = UC_SETDEBUG;
+ else if ((arg_len = is_keyword(ptr, len, "setflag")) != 0)
+ uc->cmd = UC_SETFLAG;
+ else if ((arg_len = is_keyword(ptr, len, "clearprof")) != 0)
+ uc->cmd = UC_CLEARPROF;
+#ifdef UC_DEBUG_ERROR_RECOVERY
+ else if ((arg_len = is_keyword(ptr, len, "debug_error_recovery")) != 0)
+ uc->cmd = UC_DEBUG_ERROR_RECOVERY;
+#endif
+ else
+ arg_len = 0;
+
+#ifdef DEBUG_PROC_INFO
+printf("ncr_user_command: arg_len=%d, cmd=%ld\n", arg_len, uc->cmd);
+#endif
+
+ if (!arg_len)
+ return -EINVAL;
+ ptr += arg_len; len -= arg_len;
+
+ switch(uc->cmd) {
+ case UC_SETSYNC:
+ case UC_SETTAGS:
+ case UC_SETWIDE:
+ case UC_SETFLAG:
+ SKIP_SPACES(1);
+ if ((arg_len = is_keyword(ptr, len, "all")) != 0) {
+ ptr += arg_len; len -= arg_len;
+ uc->target = ~0;
+ } else {
+ GET_INT_ARG(target);
+ uc->target = (1<<target);
+#ifdef DEBUG_PROC_INFO
+printf("ncr_user_command: target=%ld\n", target);
+#endif
+ }
+ break;
+ }
+
+ switch(uc->cmd) {
+ case UC_SETSYNC:
+ case UC_SETTAGS:
+ case UC_SETWIDE:
+ SKIP_SPACES(1);
+ GET_INT_ARG(uc->data);
+#ifdef DEBUG_PROC_INFO
+printf("ncr_user_command: data=%ld\n", uc->data);
+#endif
+ break;
+ case UC_SETORDER:
+ SKIP_SPACES(1);
+ if ((arg_len = is_keyword(ptr, len, "simple")))
+ uc->data = M_SIMPLE_TAG;
+ else if ((arg_len = is_keyword(ptr, len, "ordered")))
+ uc->data = M_ORDERED_TAG;
+ else if ((arg_len = is_keyword(ptr, len, "default")))
+ uc->data = 0;
+ else
+ return -EINVAL;
+ break;
+ case UC_SETDEBUG:
+ while (len > 0) {
+ SKIP_SPACES(1);
+ if ((arg_len = is_keyword(ptr, len, "alloc")))
+ uc->data |= DEBUG_ALLOC;
+ else if ((arg_len = is_keyword(ptr, len, "phase")))
+ uc->data |= DEBUG_PHASE;
+ else if ((arg_len = is_keyword(ptr, len, "poll")))
+ uc->data |= DEBUG_POLL;
+ else if ((arg_len = is_keyword(ptr, len, "queue")))
+ uc->data |= DEBUG_QUEUE;
+ else if ((arg_len = is_keyword(ptr, len, "result")))
+ uc->data |= DEBUG_RESULT;
+ else if ((arg_len = is_keyword(ptr, len, "scatter")))
+ uc->data |= DEBUG_SCATTER;
+ else if ((arg_len = is_keyword(ptr, len, "script")))
+ uc->data |= DEBUG_SCRIPT;
+ else if ((arg_len = is_keyword(ptr, len, "tiny")))
+ uc->data |= DEBUG_TINY;
+ else if ((arg_len = is_keyword(ptr, len, "timing")))
+ uc->data |= DEBUG_TIMING;
+ else if ((arg_len = is_keyword(ptr, len, "nego")))
+ uc->data |= DEBUG_NEGO;
+ else if ((arg_len = is_keyword(ptr, len, "tags")))
+ uc->data |= DEBUG_TAGS;
+ else if ((arg_len = is_keyword(ptr, len, "freeze")))
+ uc->data |= DEBUG_FREEZE;
+ else if ((arg_len = is_keyword(ptr, len, "restart")))
+ uc->data |= DEBUG_RESTART;
+ else
+ return -EINVAL;
+ ptr += arg_len; len -= arg_len;
+ }
+#ifdef DEBUG_PROC_INFO
+printf("ncr_user_command: data=%ld\n", uc->data);
+#endif
+ break;
+ case UC_SETFLAG:
+ while (len > 0) {
+ SKIP_SPACES(1);
+ if ((arg_len = is_keyword(ptr, len, "trace")))
+ uc->data |= UF_TRACE;
+ else if ((arg_len = is_keyword(ptr, len, "no_disc")))
+ uc->data |= UF_NODISC;
+ else
+ return -EINVAL;
+ ptr += arg_len; len -= arg_len;
+ }
+ break;
+#ifdef UC_DEBUG_ERROR_RECOVERY
+ case UC_DEBUG_ERROR_RECOVERY:
+ SKIP_SPACES(1);
+ if ((arg_len = is_keyword(ptr, len, "sge")))
+ uc->data = 1;
+ else if ((arg_len = is_keyword(ptr, len, "abort")))
+ uc->data = 2;
+ else if ((arg_len = is_keyword(ptr, len, "reset")))
+ uc->data = 3;
+ else if ((arg_len = is_keyword(ptr, len, "parity")))
+ uc->data = 4;
+ else if ((arg_len = is_keyword(ptr, len, "none")))
+ uc->data = 0;
+ else
+ return -EINVAL;
+ ptr += arg_len; len -= arg_len;
+ break;
+#endif
+ default:
+ break;
+ }
+
+ if (len)
+ return -EINVAL;
+ else {
+ long flags;
+
+ save_flags(flags); cli();
+ ncr_usercmd (np);
+ restore_flags(flags);
+ }
+ return length;
+}
+
+#endif /* SCSI_NCR_USER_COMMAND_SUPPORT */
+
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
+
+struct info_str
+{
+ char *buffer;
+ int length;
+ int offset;
+ int pos;
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->length)
+ len = info->length - info->pos;
+
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
+ }
+ if (info->pos < info->offset) {
+ data += (info->offset - info->pos);
+ len -= (info->offset - info->pos);
+ }
+
+ if (len > 0) {
+ memcpy(info->buffer + info->pos, data, len);
+ info->pos += len;
+ }
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[81];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return len;
+}
+
+/*
+** Copy formatted profile information into the input buffer.
+*/
+
+#define to_ms(t) ((t) * 1000 / HZ)
+
+static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len)
+{
+ struct info_str info;
+
+ info.buffer = ptr;
+ info.length = len;
+ info.offset = offset;
+ info.pos = 0;
+
+ copy_info(&info, "General information:\n");
+ copy_info(&info, " Chip NCR53C%s, ", np->chip_name);
+ copy_info(&info, "device id 0x%x, ", np->device_id);
+ copy_info(&info, "revision id 0x%x\n", np->revision_id);
+
+ copy_info(&info, " IO port address 0x%lx, ", (u_long) np->port);
+ copy_info(&info, "IRQ number %d\n", (int) np->irq);
+
+#ifndef NCR_IOMAPPED
+ if (np->reg)
+ copy_info(&info, " Using memory mapped IO at virtual address 0x%lx\n",
+ (u_long) np->reg);
+#endif
+ copy_info(&info, " Synchronous period factor %d, ", (int) np->minsync);
+ copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS);
+
+ if (driver_setup.debug || driver_setup.verbose > 1) {
+ copy_info(&info, " Debug flags 0x%x, ", driver_setup.debug);
+ copy_info(&info, "verbosity level %d\n", driver_setup.verbose);
+ }
+
+#ifdef SCSI_NCR_PROFILE_SUPPORT
+ copy_info(&info, "Profiling information:\n");
+ copy_info(&info, " %-12s = %lu\n", "num_trans",np->profile.num_trans);
+ copy_info(&info, " %-12s = %lu\n", "num_kbytes",np->profile.num_kbytes);
+ copy_info(&info, " %-12s = %lu\n", "num_disc", np->profile.num_disc);
+ copy_info(&info, " %-12s = %lu\n", "num_break",np->profile.num_break);
+ copy_info(&info, " %-12s = %lu\n", "num_int", np->profile.num_int);
+ copy_info(&info, " %-12s = %lu\n", "num_fly", np->profile.num_fly);
+ copy_info(&info, " %-12s = %lu\n", "ms_setup", to_ms(np->profile.ms_setup));
+ copy_info(&info, " %-12s = %lu\n", "ms_data", to_ms(np->profile.ms_data));
+ copy_info(&info, " %-12s = %lu\n", "ms_disc", to_ms(np->profile.ms_disc));
+ copy_info(&info, " %-12s = %lu\n", "ms_post", to_ms(np->profile.ms_post));
+#endif
+
+ return info.pos > info.offset? info.pos - info.offset : 0;
+}
+
+#endif /* SCSI_NCR_USER_INFO_SUPPORT */
+
+/*
+** Entry point of the scsi proc fs of the driver.
+** - func = 0 means read (returns profile data)
+** - func = 1 means write (parse user control command)
+*/
+
+int ncr53c8xx_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int func)
+{
+ struct Scsi_Host *host;
+ struct host_data *host_data;
+ ncb_p ncb = 0;
+ int retv;
+
+#ifdef DEBUG_PROC_INFO
+printf("ncr53c8xx_proc_info: hostno=%d, func=%d\n", hostno, func);
+#endif
+
+ for (host = first_host; host; host = host->next) {
+ if (host->hostt == the_template && host->host_no == hostno) {
+ host_data = (struct host_data *) host->hostdata;
+ ncb = host_data->ncb;
+ break;
+ }
+ }
+
+ if (!ncb)
+ return -EINVAL;
+
+ if (func) {
+#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
+ retv = ncr_user_command(ncb, buffer, length);
+#else
+ retv = -EINVAL;
+#endif
+ }
+ else {
+ if (start)
+ *start = buffer;
+#ifdef SCSI_NCR_USER_INFO_SUPPORT
+ retv = ncr_host_info(ncb, buffer, offset, length);
+#else
+ retv = -EINVAL;
+#endif
+ }
+
+ return retv;
+}
+
+
+/*=========================================================================
+** End of proc file system stuff
+**=========================================================================
+*/
+#endif
+
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/* ---------------------------------------------------------------------
+**
+** Try reading Symbios format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in/data out
+** GPIO1 - clock
+**
+** return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl);
+static void nvram_start(ncr_slot *np, u_char *gpreg);
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg);
+static void nvram_stop(ncr_slot *np, u_char *gpreg);
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode);
+
+__initfunc(
+static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
+)
+{
+ static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ u_short csum;
+ u_char ack_data;
+ int retv = 1;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB (nc_gpreg);
+ old_gpcntl = INB (nc_gpcntl);
+ gpcntl = old_gpcntl & 0xfc;
+
+ /* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+ OUTB (nc_gpreg, old_gpreg);
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* this is to set NVRAM into a known state with GPIO0/1 both low */
+ gpreg = old_gpreg;
+ nvram_setBit(np, 0, &gpreg, CLR_CLK);
+ nvram_setBit(np, 0, &gpreg, CLR_BIT);
+
+ /* now set NVRAM inactive with GPIO0/1 both high */
+ nvram_stop(np, &gpreg);
+
+ /* activate NVRAM */
+ nvram_start(np, &gpreg);
+
+ /* write device code and random address MSB */
+ nvram_write_byte(np, &ack_data,
+ 0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* write random address LSB */
+ nvram_write_byte(np, &ack_data,
+ (SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* regenerate START state to set up for reading */
+ nvram_start(np, &gpreg);
+
+ /* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+ nvram_write_byte(np, &ack_data,
+ 0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+ if (ack_data & 0x01)
+ goto out;
+
+ /* now set up GPIO0 for inputting data */
+ gpcntl |= 0x01;
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* input all active data - only part of total NVRAM */
+ csum = nvram_read_data(np,
+ (u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl);
+
+ /* finally put NVRAM back in inactive mode */
+ gpcntl &= 0xfe;
+ OUTB (nc_gpcntl, gpcntl);
+ nvram_stop(np, &gpreg);
+
+#ifdef SCSI_NCR_DEBUG_NVRAM
+printf("ncr53c8xx: NvRAM marker=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
+ nvram->start_marker,
+ nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
+ nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
+ nvram->byte_count, sizeof(*nvram) - 12,
+ nvram->checksum, csum);
+#endif
+
+ /* check valid NVRAM signature, verify byte count and checksum */
+ if (nvram->start_marker == 0 &&
+ !memcmp(nvram->trailer, Symbios_trailer, 6) &&
+ nvram->byte_count == sizeof(*nvram) - 12 &&
+ csum == nvram->checksum)
+ retv = 0;
+out:
+ /* return GPIO0/1 to original states after having accessed NVRAM */
+ OUTB (nc_gpcntl, old_gpcntl);
+ OUTB (nc_gpreg, old_gpreg);
+
+ return retv;
+}
+
+/*
+ * Read Symbios NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
+)
+{
+ int x;
+ u_short csum;
+
+ for (x = 0; x < len; x++)
+ nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl);
+
+ for (x = 6, csum = 0; x < len - 6; x++)
+ csum += data[x];
+
+ return csum;
+}
+
+/*
+ * Send START condition to NVRAM to wake it up.
+ */
+__initfunc(
+static void nvram_start(ncr_slot *np, u_char *gpreg)
+)
+{
+ nvram_setBit(np, 1, gpreg, SET_BIT);
+ nvram_setBit(np, 0, gpreg, SET_CLK);
+ nvram_setBit(np, 0, gpreg, CLR_BIT);
+ nvram_setBit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ * GPIO0 must already be set as an output
+ */
+__initfunc(
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+ int x;
+
+ for (x = 0; x < 8; x++)
+ nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+
+ nvram_readAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * READ a byte from the NVRAM and then send an ACK to say we have got it,
+ * GPIO0 must already be set as an input
+ */
+__initfunc(
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+ int x;
+ u_char read_bit;
+
+ *read_data = 0;
+ for (x = 0; x < 8; x++) {
+ nvram_doBit(np, &read_bit, 1, gpreg);
+ *read_data |= ((read_bit & 0x01) << (7 - x));
+ }
+
+ nvram_writeAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * Output an ACK to the NVRAM after reading,
+ * change GPIO0 to output and when done back to an input
+ */
+__initfunc(
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+ OUTB (nc_gpcntl, *gpcntl & 0xfe);
+ nvram_doBit(np, 0, write_bit, gpreg);
+ OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Input an ACK from NVRAM after writing,
+ * change GPIO0 to input and when done back to an output
+ */
+__initfunc(
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+ OUTB (nc_gpcntl, *gpcntl | 0x01);
+ nvram_doBit(np, read_bit, 1, gpreg);
+ OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Read or write a bit to the NVRAM,
+ * read if GPIO0 input else write if GPIO0 output
+ */
+__initfunc(
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
+)
+{
+ nvram_setBit(np, write_bit, gpreg, SET_BIT);
+ nvram_setBit(np, 0, gpreg, SET_CLK);
+ if (read_bit)
+ *read_bit = INB (nc_gpreg);
+ nvram_setBit(np, 0, gpreg, CLR_CLK);
+ nvram_setBit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+__initfunc(
+static void nvram_stop(ncr_slot *np, u_char *gpreg)
+)
+{
+ nvram_setBit(np, 0, gpreg, SET_CLK);
+ nvram_setBit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ * Set/clear data/clock bit in GPIO0
+ */
+__initfunc(
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+)
+{
+ DELAY(5);
+ switch (bit_mode){
+ case SET_BIT:
+ *gpreg |= write_bit;
+ break;
+ case CLR_BIT:
+ *gpreg &= 0xfe;
+ break;
+ case SET_CLK:
+ *gpreg |= 0x02;
+ break;
+ case CLR_CLK:
+ *gpreg &= 0xfd;
+ break;
+
+ }
+ OUTB (nc_gpreg, *gpreg);
+ DELAY(5);
+}
+
+#undef SET_BIT 0
+#undef CLR_BIT 1
+#undef SET_CLK 2
+#undef CLR_CLK 3
+
+
+/* ---------------------------------------------------------------------
+**
+** Try reading Tekram format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in
+** GPIO1 - data out
+** GPIO2 - clock
+** GPIO4 - chip select
+**
+** return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg);
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg);
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg);
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg);
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg);
+
+__initfunc(
+static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
+)
+{
+ u_char gpcntl, gpreg;
+ u_char old_gpcntl, old_gpreg;
+ u_short csum;
+
+ /* save current state of GPCNTL and GPREG */
+ old_gpreg = INB (nc_gpreg);
+ old_gpcntl = INB (nc_gpcntl);
+
+ /* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+ 1/2/4 out */
+ gpreg = old_gpreg & 0xe9;
+ OUTB (nc_gpreg, gpreg);
+ gpcntl = (old_gpcntl & 0xe9) | 0x09;
+ OUTB (nc_gpcntl, gpcntl);
+
+ /* input all of NVRAM, 64 words */
+ csum = Tnvram_read_data(np, (u_short *) nvram,
+ sizeof(*nvram) / sizeof(short), &gpreg);
+
+ /* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+ OUTB (nc_gpcntl, old_gpcntl);
+ OUTB (nc_gpreg, old_gpreg);
+
+ /* check data valid */
+ if (csum != 0x1234)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Read Tekram NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
+)
+{
+ u_char read_bit;
+ u_short csum;
+ int x;
+
+ for (x = 0, csum = 0; x < len; x++) {
+
+ /* output read command and address */
+ Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+ if (read_bit & 0x01)
+ return 0; /* Force bad checksum */
+
+ Tnvram_Read_Word(np, &data[x], gpreg);
+ csum += data[x];
+
+ Tnvram_Stop(np, gpreg);
+ }
+
+ return csum;
+}
+
+/*
+ * Send read command and address to NVRAM
+ */
+__initfunc(
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
+)
+{
+ int x;
+
+ /* send 9 bits, start bit (1), command (2), address (6) */
+ for (x = 0; x < 9; x++)
+ Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+ *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * READ a byte from the NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
+)
+{
+ int x;
+ u_char read_bit;
+
+ *nvram_data = 0;
+ for (x = 0; x < 16; x++) {
+ Tnvram_Read_Bit(np, &read_bit, gpreg);
+
+ if (read_bit & 0x01)
+ *nvram_data |= (0x01 << (15 - x));
+ else
+ *nvram_data &= ~(0x01 << (15 - x));
+ }
+}
+
+/*
+ * Read bit from NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
+)
+{
+ DELAY(2);
+ Tnvram_Clk(np, gpreg);
+ *read_bit = INB (nc_gpreg);
+}
+
+/*
+ * Write bit to GPIO0
+ */
+__initfunc(
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+)
+{
+ if (write_bit & 0x01)
+ *gpreg |= 0x02;
+ else
+ *gpreg &= 0xfd;
+
+ *gpreg |= 0x10;
+
+ OUTB (nc_gpreg, *gpreg);
+ DELAY(2);
+
+ Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+__initfunc(
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg)
+)
+{
+ *gpreg &= 0xef;
+ OUTB (nc_gpreg, *gpreg);
+ DELAY(2);
+
+ Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Pulse clock bit in GPIO0
+ */
+__initfunc(
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg)
+)
+{
+ OUTB (nc_gpreg, *gpreg | 0x04);
+ DELAY(2);
+ OUTB (nc_gpreg, *gpreg);
+}
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+/*
+** Module stuff
+*/
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = NCR53C8XX;
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/ncr53c8xx.h b/linux/src/drivers/scsi/ncr53c8xx.h
new file mode 100644
index 00000000..cc009ca6
--- /dev/null
+++ b/linux/src/drivers/scsi/ncr53c8xx.h
@@ -0,0 +1,1220 @@
+/******************************************************************************
+** Device driver for the PCI-SCSI NCR538XX controller family.
+**
+** Copyright (C) 1994 Wolfgang Stanglmeier
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+**-----------------------------------------------------------------------------
+**
+** This driver has been ported to Linux from the FreeBSD NCR53C8XX driver
+** and is currently maintained by
+**
+** Gerard Roudier <groudier@club-internet.fr>
+**
+** Being given that this driver originates from the FreeBSD version, and
+** in order to keep synergy on both, any suggested enhancements and corrections
+** received on Linux are automatically a potential candidate for the FreeBSD
+** version.
+**
+** The original driver has been written for 386bsd and FreeBSD by
+** Wolfgang Stanglmeier <wolf@cologne.de>
+** Stefan Esser <se@mi.Uni-Koeln.de>
+**
+** And has been ported to NetBSD by
+** Charles M. Hannum <mycroft@gnu.ai.mit.edu>
+**
+*******************************************************************************
+*/
+
+#ifndef NCR53C8XX_H
+#define NCR53C8XX_H
+
+/*
+** Name and revision of the driver
+*/
+#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 2.5f.1"
+
+/*
+** Check supported Linux versions
+*/
+
+#if !defined(LINUX_VERSION_CODE)
+#include <linux/version.h>
+#endif
+#include <linux/config.h>
+
+/*
+** During make dep of linux-1.2.13, LINUX_VERSION_CODE is undefined
+** Under linux-1.3.X, all seems to be OK.
+** So, we have only to define it under 1.2.13
+*/
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#if !defined(LINUX_VERSION_CODE)
+#define LINUX_VERSION_CODE LinuxVersionCode(1,2,13)
+#endif
+
+/*
+** Normal IO or memory mapped IO.
+**
+** Memory mapped IO only works with linux-1.3.X
+** If your motherboard does not work with memory mapped IO,
+** define SCSI_NCR_IOMAPPED for PATCHLEVEL 3 too.
+*/
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(1,3,0)
+# define SCSI_NCR_IOMAPPED
+#endif
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+# define SCSI_NCR_PROC_INFO_SUPPORT
+#endif
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,72)
+# define SCSI_NCR_SHARE_IRQ
+#endif
+
+/*
+** If you want a driver as small as possible, donnot define the
+** following options.
+*/
+
+#define SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
+#define SCSI_NCR_DEBUG_INFO_SUPPORT
+#define SCSI_NCR_PCI_FIX_UP_SUPPORT
+#ifdef SCSI_NCR_PROC_INFO_SUPPORT
+# define SCSI_NCR_PROFILE_SUPPORT
+# define SCSI_NCR_USER_COMMAND_SUPPORT
+# define SCSI_NCR_USER_INFO_SUPPORT
+/* # define SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT */
+#endif
+
+/*==========================================================
+**
+** nvram settings - #define SCSI_NCR_NVRAM_SUPPORT to enable
+**
+**==========================================================
+*/
+
+#ifdef CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+#define SCSI_NCR_NVRAM_SUPPORT
+/* #define SCSI_NCR_DEBUG_NVRAM */
+#endif
+
+/* ---------------------------------------------------------------------
+** Take into account kernel configured parameters.
+** Most of these options can be overridden at startup by a command line.
+** ---------------------------------------------------------------------
+*/
+
+/*
+ * For Ultra2 SCSI support option, use special features and allow 40Mhz
+ * synchronous data transfers.
+ */
+#define SCSI_NCR_SETUP_SPECIAL_FEATURES (3)
+#define SCSI_NCR_SETUP_ULTRA_SCSI (2)
+#define SCSI_NCR_MAX_SYNC (40)
+
+/*
+ * Allow tags from 2 to 12, default 4
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_MAX_TAGS
+#if CONFIG_SCSI_NCR53C8XX_MAX_TAGS < 2
+#define SCSI_NCR_MAX_TAGS (2)
+#elif CONFIG_SCSI_NCR53C8XX_MAX_TAGS > 12
+#define SCSI_NCR_MAX_TAGS (12)
+#else
+#define SCSI_NCR_MAX_TAGS CONFIG_SCSI_NCR53C8XX_MAX_TAGS
+#endif
+#else
+#define SCSI_NCR_MAX_TAGS (4)
+#endif
+
+/*
+ * Allow tagged command queuing support if configured with default number
+ * of tags set to max (see above).
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
+#define SCSI_NCR_SETUP_DEFAULT_TAGS SCSI_NCR_MAX_TAGS
+#else
+#define SCSI_NCR_SETUP_DEFAULT_TAGS (0)
+#endif
+
+/*
+ * Use normal IO if configured. Forced for alpha and ppc.
+ */
+#if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED)
+#define SCSI_NCR_IOMAPPED
+#elif defined(__alpha__) || defined(__powerpc__)
+#define SCSI_NCR_IOMAPPED
+#endif
+
+/*
+ * Sync transfer frequency at startup.
+ * Allow from 5Mhz to 40Mhz default 10 Mhz.
+ */
+#ifndef CONFIG_SCSI_NCR53C8XX_SYNC
+#define CONFIG_SCSI_NCR53C8XX_SYNC (5)
+#elif CONFIG_SCSI_NCR53C8XX_SYNC > SCSI_NCR_MAX_SYNC
+#define SCSI_NCR_SETUP_DEFAULT_SYNC SCSI_NCR_MAX_SYNC
+#endif
+
+#if CONFIG_SCSI_NCR53C8XX_SYNC == 0
+#define SCSI_NCR_SETUP_DEFAULT_SYNC (255)
+#elif CONFIG_SCSI_NCR53C8XX_SYNC <= 5
+#define SCSI_NCR_SETUP_DEFAULT_SYNC (50)
+#elif CONFIG_SCSI_NCR53C8XX_SYNC <= 20
+#define SCSI_NCR_SETUP_DEFAULT_SYNC (250/(CONFIG_SCSI_NCR53C8XX_SYNC))
+#elif CONFIG_SCSI_NCR53C8XX_SYNC <= 33
+#define SCSI_NCR_SETUP_DEFAULT_SYNC (11)
+#else
+#define SCSI_NCR_SETUP_DEFAULT_SYNC (10)
+#endif
+
+/*
+ * Disallow disconnections at boot-up
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
+#define SCSI_NCR_SETUP_DISCONNECTION (0)
+#else
+#define SCSI_NCR_SETUP_DISCONNECTION (1)
+#endif
+
+/*
+ * Force synchronous negotiation for all targets
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO
+#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO (1)
+#else
+#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO (0)
+#endif
+
+/*
+ * Disable master parity checking (flawed hardwares need that)
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK
+#define SCSI_NCR_SETUP_MASTER_PARITY (0)
+#else
+#define SCSI_NCR_SETUP_MASTER_PARITY (1)
+#endif
+
+/*
+ * Disable scsi parity checking (flawed devices may need that)
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
+#define SCSI_NCR_SETUP_SCSI_PARITY (0)
+#else
+#define SCSI_NCR_SETUP_SCSI_PARITY (1)
+#endif
+
+/*
+ * Vendor specific stuff
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+#define SCSI_NCR_SETUP_LED_PIN (1)
+#define SCSI_NCR_SETUP_DIFF_SUPPORT (3)
+#else
+#define SCSI_NCR_SETUP_LED_PIN (0)
+#define SCSI_NCR_SETUP_DIFF_SUPPORT (0)
+#endif
+
+/*
+ * Settle time after reset at boot-up
+ */
+#define SCSI_NCR_SETUP_SETTLE_TIME (2)
+
+/*
+** Other parameters not configurable with "make config"
+** Avoid to change these constants, unless you know what you are doing.
+*/
+
+#define SCSI_NCR_ALWAYS_SIMPLE_TAG
+#define SCSI_NCR_MAX_SCATTER (127)
+#define SCSI_NCR_MAX_TARGET (16)
+#define SCSI_NCR_MAX_HOST (2)
+#define SCSI_NCR_TIMEOUT_ALERT (3*HZ)
+
+#define SCSI_NCR_CAN_QUEUE (7*SCSI_NCR_MAX_TAGS)
+#define SCSI_NCR_CMD_PER_LUN (SCSI_NCR_MAX_TAGS)
+#define SCSI_NCR_SG_TABLESIZE (SCSI_NCR_MAX_SCATTER)
+
+#define SCSI_NCR_TIMER_INTERVAL ((HZ+5-1)/5)
+
+#if 1 /* defined CONFIG_SCSI_MULTI_LUN */
+#define SCSI_NCR_MAX_LUN (8)
+#else
+#define SCSI_NCR_MAX_LUN (1)
+#endif
+
+/*
+** Define Scsi_Host_Template parameters
+**
+** Used by hosts.c and ncr53c8xx.c with module configuration.
+*/
+
+#if defined(HOSTS_C) || defined(MODULE)
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,98)
+#include <scsi/scsicam.h>
+#else
+#include <linux/scsicam.h>
+#endif
+
+int ncr53c8xx_abort(Scsi_Cmnd *);
+int ncr53c8xx_detect(Scsi_Host_Template *tpnt);
+int ncr53c8xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,98)
+int ncr53c8xx_reset(Scsi_Cmnd *, unsigned int);
+#else
+int ncr53c8xx_reset(Scsi_Cmnd *);
+#endif
+
+#ifdef MODULE
+int ncr53c8xx_release(struct Scsi_Host *);
+#else
+#define ncr53c8xx_release NULL
+#endif
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,75)
+
+#define NCR53C8XX { name: SCSI_NCR_DRIVER_NAME, \
+ detect: ncr53c8xx_detect, \
+ release: ncr53c8xx_release, \
+ queuecommand: ncr53c8xx_queue_command,\
+ abort: ncr53c8xx_abort, \
+ reset: ncr53c8xx_reset, \
+ bios_param: scsicam_bios_param, \
+ can_queue: SCSI_NCR_CAN_QUEUE, \
+ this_id: 7, \
+ sg_tablesize: SCSI_NCR_SG_TABLESIZE, \
+ cmd_per_lun: SCSI_NCR_CMD_PER_LUN, \
+ use_clustering: DISABLE_CLUSTERING}
+
+#elif LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+
+#define NCR53C8XX { NULL, NULL, NULL, NULL, \
+ SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect, \
+ ncr53c8xx_release, NULL, NULL, \
+ ncr53c8xx_queue_command,ncr53c8xx_abort, \
+ ncr53c8xx_reset, NULL, scsicam_bios_param, \
+ SCSI_NCR_CAN_QUEUE, 7, \
+ SCSI_NCR_SG_TABLESIZE, SCSI_NCR_CMD_PER_LUN, \
+ 0, 0, DISABLE_CLUSTERING}
+
+#else
+
+#define NCR53C8XX { NULL, NULL, \
+ SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect, \
+ ncr53c8xx_release, NULL, NULL, \
+ ncr53c8xx_queue_command,ncr53c8xx_abort, \
+ ncr53c8xx_reset, NULL, scsicam_bios_param, \
+ SCSI_NCR_CAN_QUEUE, 7, \
+ SCSI_NCR_SG_TABLESIZE, SCSI_NCR_CMD_PER_LUN, \
+ 0, 0, DISABLE_CLUSTERING}
+
+#endif /* LINUX_VERSION_CODE */
+
+#endif /* defined(HOSTS_C) || defined(MODULE) */
+
+
+#ifndef HOSTS_C
+
+/*
+** IO functions definition for big/little endian support.
+** For now, the NCR is only supported in little endian addressing mode,
+** and big endian byte ordering is only supported for the PPC.
+** MMIO is not used on PPC.
+*/
+
+#ifdef __BIG_ENDIAN
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
+#error "BIG ENDIAN byte ordering needs kernel version >= 2.1.0"
+#endif
+
+#ifdef __powerpc__
+#define inw_l2b inw
+#define inl_l2b inl
+#define outw_b2l outw
+#define outl_b2l outl
+#else
+#error "Support for BIG ENDIAN is only available for the PowerPC"
+#endif
+
+#else /* Assumed x86 or alpha */
+
+#define inw_raw inw
+#define inl_raw inl
+#define outw_raw outw
+#define outl_raw outl
+#define readw_raw readw
+#define readl_raw readl
+#define writew_raw writew
+#define writel_raw writel
+
+#endif
+
+#ifdef SCSI_NCR_BIG_ENDIAN
+#error "The NCR in BIG ENDIAN adressing mode is not (yet) supported"
+#endif
+
+/*
+** NCR53C8XX Device Ids
+*/
+
+#ifndef PCI_DEVICE_ID_NCR_53C810
+#define PCI_DEVICE_ID_NCR_53C810 1
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C810AP
+#define PCI_DEVICE_ID_NCR_53C810AP 5
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C815
+#define PCI_DEVICE_ID_NCR_53C815 4
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C820
+#define PCI_DEVICE_ID_NCR_53C820 2
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C825
+#define PCI_DEVICE_ID_NCR_53C825 3
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C860
+#define PCI_DEVICE_ID_NCR_53C860 6
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C875
+#define PCI_DEVICE_ID_NCR_53C875 0xf
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C875J
+#define PCI_DEVICE_ID_NCR_53C875J 0x8f
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C885
+#define PCI_DEVICE_ID_NCR_53C885 0xd
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C895
+#define PCI_DEVICE_ID_NCR_53C895 0xc
+#endif
+
+#ifndef PCI_DEVICE_ID_NCR_53C896
+#define PCI_DEVICE_ID_NCR_53C896 0xb
+#endif
+
+/*
+** NCR53C8XX devices features table.
+*/
+typedef struct {
+ unsigned short device_id;
+ unsigned short revision_id;
+ char *name;
+ unsigned char burst_max;
+ unsigned char offset_max;
+ unsigned char nr_divisor;
+ unsigned int features;
+#define FE_LED0 (1<<0)
+#define FE_WIDE (1<<1)
+#define FE_ULTRA (1<<2)
+#define FE_ULTRA2 (1<<3)
+#define FE_DBLR (1<<4)
+#define FE_QUAD (1<<5)
+#define FE_ERL (1<<6)
+#define FE_CLSE (1<<7)
+#define FE_WRIE (1<<8)
+#define FE_ERMP (1<<9)
+#define FE_BOF (1<<10)
+#define FE_DFS (1<<11)
+#define FE_PFEN (1<<12)
+#define FE_LDSTR (1<<13)
+#define FE_RAM (1<<14)
+#define FE_CLK80 (1<<15)
+#define FE_CACHE_SET (FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP)
+#define FE_SCSI_SET (FE_WIDE|FE_ULTRA|FE_ULTRA2|FE_DBLR|FE_QUAD|F_CLK80)
+#define FE_SPECIAL_SET (FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM)
+} ncr_chip;
+
+/*
+** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 3.
+** Memory Read transaction terminated by a retry followed by
+** Memory Read Line command.
+*/
+#define FE_CACHE0_SET (FE_CACHE_SET & ~FE_ERL)
+
+/*
+** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 5.
+** On paper, this errata is harmless. But it is a good reason for
+** using a shorter programmed burst length (64 DWORDS instead of 128).
+*/
+
+#define SCSI_NCR_CHIP_TABLE \
+{ \
+ {PCI_DEVICE_ID_NCR_53C810, 0x0f, "810", 4, 8, 4, \
+ FE_ERL} \
+ , \
+ {PCI_DEVICE_ID_NCR_53C810, 0xff, "810a", 4, 8, 4, \
+ FE_CACHE_SET|FE_LDSTR|FE_PFEN|FE_BOF} \
+ , \
+ {PCI_DEVICE_ID_NCR_53C815, 0xff, "815", 4, 8, 4, \
+ FE_ERL|FE_BOF} \
+ , \
+ {PCI_DEVICE_ID_NCR_53C820, 0xff, "820", 4, 8, 4, \
+ FE_WIDE|FE_ERL} \
+ , \
+ {PCI_DEVICE_ID_NCR_53C825, 0x0f, "825", 4, 8, 4, \
+ FE_WIDE|FE_ERL|FE_BOF} \
+ , \
+ {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 6, 8, 4, \
+ FE_WIDE|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} \
+ , \
+ {PCI_DEVICE_ID_NCR_53C860, 0xff, "860", 4, 8, 5, \
+ FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN} \
+ , \
+ {PCI_DEVICE_ID_NCR_53C875, 0x01, "875", 6, 16, 5, \
+ FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ , \
+ {PCI_DEVICE_ID_NCR_53C875, 0xff, "875", 6, 16, 5, \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ , \
+ {PCI_DEVICE_ID_NCR_53C875J,0xff, "875J", 6, 16, 5, \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ , \
+ {PCI_DEVICE_ID_NCR_53C885, 0xff, "885", 6, 16, 5, \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ , \
+ {PCI_DEVICE_ID_NCR_53C895, 0xff, "895", 7, 31, 7, \
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ , \
+ {PCI_DEVICE_ID_NCR_53C896, 0xff, "896", 7, 31, 7, \
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+}
+
+/*
+ * List of supported NCR chip ids
+ */
+#define SCSI_NCR_CHIP_IDS \
+{ \
+ PCI_DEVICE_ID_NCR_53C810, \
+ PCI_DEVICE_ID_NCR_53C815, \
+ PCI_DEVICE_ID_NCR_53C820, \
+ PCI_DEVICE_ID_NCR_53C825, \
+ PCI_DEVICE_ID_NCR_53C860, \
+ PCI_DEVICE_ID_NCR_53C875, \
+ PCI_DEVICE_ID_NCR_53C875J, \
+ PCI_DEVICE_ID_NCR_53C885, \
+ PCI_DEVICE_ID_NCR_53C895, \
+ PCI_DEVICE_ID_NCR_53C896 \
+}
+
+/*
+** Initial setup.
+** Can be overriden at startup by a command line.
+*/
+#define SCSI_NCR_DRIVER_SETUP \
+{ \
+ SCSI_NCR_SETUP_MASTER_PARITY, \
+ SCSI_NCR_SETUP_SCSI_PARITY, \
+ SCSI_NCR_SETUP_DISCONNECTION, \
+ SCSI_NCR_SETUP_SPECIAL_FEATURES, \
+ SCSI_NCR_SETUP_ULTRA_SCSI, \
+ SCSI_NCR_SETUP_FORCE_SYNC_NEGO, \
+ 0, \
+ 0, \
+ 1, \
+ 1, \
+ SCSI_NCR_SETUP_DEFAULT_TAGS, \
+ SCSI_NCR_SETUP_DEFAULT_SYNC, \
+ 0x00, \
+ 7, \
+ SCSI_NCR_SETUP_LED_PIN, \
+ 1, \
+ SCSI_NCR_SETUP_SETTLE_TIME, \
+ SCSI_NCR_SETUP_DIFF_SUPPORT, \
+ 0, \
+ 1 \
+}
+
+/*
+** Boot fail safe setup.
+** Override initial setup from boot command line:
+** ncr53c8xx=safe:y
+*/
+#define SCSI_NCR_DRIVER_SAFE_SETUP \
+{ \
+ 0, \
+ 1, \
+ 0, \
+ 0, \
+ 0, \
+ 0, \
+ 0, \
+ 0, \
+ 1, \
+ 2, \
+ 0, \
+ 255, \
+ 0x00, \
+ 255, \
+ 0, \
+ 0, \
+ 10, \
+ 1, \
+ 1, \
+ 1 \
+}
+
+/*
+** Define the table of target capabilities by host and target
+**
+** If you have problems with a scsi device, note the host unit and the
+** corresponding target number.
+**
+** Edit the corresponding entry of the table below and try successively:
+** NQ7_Questionnable
+** NQ7_IdeLike
+**
+** This bitmap is anded with the byte 7 of inquiry data on completion of
+** INQUIRY command.
+** The driver never see the zeroed bits and will ignore the corresponding
+** capabilities of the target.
+*/
+
+#define INQ7_SftRe 1
+#define INQ7_CmdQueue (1<<1) /* Tagged Command */
+#define INQ7_Reserved (1<<2)
+#define INQ7_Linked (1<<3)
+#define INQ7_Sync (1<<4) /* Synchronous Negotiation */
+#define INQ7_WBus16 (1<<5)
+#define INQ7_WBus32 (1<<6)
+#define INQ7_RelAdr (1<<7)
+
+#define INQ7_IdeLike 0
+#define INQ7_Scsi1Like INQ7_IdeLike
+#define INQ7_Perfect 0xff
+#define INQ7_Questionnable ~(INQ7_CmdQueue|INQ7_Sync)
+#define INQ7_VeryQuestionnable \
+ ~(INQ7_CmdQueue|INQ7_Sync|INQ7_WBus16|INQ7_WBus32)
+
+#define INQ7_Default INQ7_Perfect
+
+#define NCR53C8XX_TARGET_CAPABILITIES \
+/* Host 0 */ \
+{ \
+ { \
+ /* Target 0 */ INQ7_Default, \
+ /* Target 1 */ INQ7_Default, \
+ /* Target 2 */ INQ7_Default, \
+ /* Target 3 */ INQ7_Default, \
+ /* Target 4 */ INQ7_Default, \
+ /* Target 5 */ INQ7_Default, \
+ /* Target 6 */ INQ7_Default, \
+ /* Target 7 */ INQ7_Default, \
+ /* Target 8 */ INQ7_Default, \
+ /* Target 9 */ INQ7_Default, \
+ /* Target 10 */ INQ7_Default, \
+ /* Target 11 */ INQ7_Default, \
+ /* Target 12 */ INQ7_Default, \
+ /* Target 13 */ INQ7_Default, \
+ /* Target 14 */ INQ7_Default, \
+ /* Target 15 */ INQ7_Default, \
+ } \
+}, \
+/* Host 1 */ \
+{ \
+ { \
+ /* Target 0 */ INQ7_Default, \
+ /* Target 1 */ INQ7_Default, \
+ /* Target 2 */ INQ7_Default, \
+ /* Target 3 */ INQ7_Default, \
+ /* Target 4 */ INQ7_Default, \
+ /* Target 5 */ INQ7_Default, \
+ /* Target 6 */ INQ7_Default, \
+ /* Target 7 */ INQ7_Default, \
+ /* Target 8 */ INQ7_Default, \
+ /* Target 9 */ INQ7_Default, \
+ /* Target 10 */ INQ7_Default, \
+ /* Target 11 */ INQ7_Default, \
+ /* Target 12 */ INQ7_Default, \
+ /* Target 13 */ INQ7_Default, \
+ /* Target 14 */ INQ7_Default, \
+ /* Target 15 */ INQ7_Default, \
+ } \
+}
+
+/*
+** Replace the proc_dir_entry of the standard ncr driver.
+*/
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+#if defined(CONFIG_SCSI_NCR53C7xx) || !defined(CONFIG_SCSI_NCR53C8XX)
+#define PROC_SCSI_NCR53C8XX PROC_SCSI_NCR53C7xx
+#endif
+#endif
+
+/**************** ORIGINAL CONTENT of ncrreg.h from FreeBSD ******************/
+
+/*-----------------------------------------------------------------
+**
+** The ncr 53c810 register structure.
+**
+**-----------------------------------------------------------------
+*/
+
+struct ncr_reg {
+/*00*/ u_char nc_scntl0; /* full arb., ena parity, par->ATN */
+
+/*01*/ u_char nc_scntl1; /* no reset */
+ #define ISCON 0x10 /* connected to scsi */
+ #define CRST 0x08 /* force reset */
+
+/*02*/ u_char nc_scntl2; /* no disconnect expected */
+ #define SDU 0x80 /* cmd: disconnect will raise error */
+ #define CHM 0x40 /* sta: chained mode */
+ #define WSS 0x08 /* sta: wide scsi send [W]*/
+ #define WSR 0x01 /* sta: wide scsi received [W]*/
+
+/*03*/ u_char nc_scntl3; /* cnf system clock dependent */
+ #define EWS 0x08 /* cmd: enable wide scsi [W]*/
+ #define ULTRA 0x80 /* cmd: ULTRA enable */
+
+/*04*/ u_char nc_scid; /* cnf host adapter scsi address */
+ #define RRE 0x40 /* r/w:e enable response to resel. */
+ #define SRE 0x20 /* r/w:e enable response to select */
+
+/*05*/ u_char nc_sxfer; /* ### Sync speed and count */
+
+/*06*/ u_char nc_sdid; /* ### Destination-ID */
+
+/*07*/ u_char nc_gpreg; /* ??? IO-Pins */
+
+/*08*/ u_char nc_sfbr; /* ### First byte in phase */
+
+/*09*/ u_char nc_socl;
+ #define CREQ 0x80 /* r/w: SCSI-REQ */
+ #define CACK 0x40 /* r/w: SCSI-ACK */
+ #define CBSY 0x20 /* r/w: SCSI-BSY */
+ #define CSEL 0x10 /* r/w: SCSI-SEL */
+ #define CATN 0x08 /* r/w: SCSI-ATN */
+ #define CMSG 0x04 /* r/w: SCSI-MSG */
+ #define CC_D 0x02 /* r/w: SCSI-C_D */
+ #define CI_O 0x01 /* r/w: SCSI-I_O */
+
+/*0a*/ u_char nc_ssid;
+
+/*0b*/ u_char nc_sbcl;
+
+/*0c*/ u_char nc_dstat;
+ #define DFE 0x80 /* sta: dma fifo empty */
+ #define MDPE 0x40 /* int: master data parity error */
+ #define BF 0x20 /* int: script: bus fault */
+ #define ABRT 0x10 /* int: script: command aborted */
+ #define SSI 0x08 /* int: script: single step */
+ #define SIR 0x04 /* int: script: interrupt instruct. */
+ #define IID 0x01 /* int: script: illegal instruct. */
+
+/*0d*/ u_char nc_sstat0;
+ #define ILF 0x80 /* sta: data in SIDL register lsb */
+ #define ORF 0x40 /* sta: data in SODR register lsb */
+ #define OLF 0x20 /* sta: data in SODL register lsb */
+ #define AIP 0x10 /* sta: arbitration in progress */
+ #define LOA 0x08 /* sta: arbitration lost */
+ #define WOA 0x04 /* sta: arbitration won */
+ #define IRST 0x02 /* sta: scsi reset signal */
+ #define SDP 0x01 /* sta: scsi parity signal */
+
+/*0e*/ u_char nc_sstat1;
+ #define FF3210 0xf0 /* sta: bytes in the scsi fifo */
+
+/*0f*/ u_char nc_sstat2;
+ #define ILF1 0x80 /* sta: data in SIDL register msb[W]*/
+ #define ORF1 0x40 /* sta: data in SODR register msb[W]*/
+ #define OLF1 0x20 /* sta: data in SODL register msb[W]*/
+ #define DM 0x04 /* sta: DIFFSENS mismatch (895/6 only) */
+ #define LDSC 0x02 /* sta: disconnect & reconnect */
+
+/*10*/ u_int32 nc_dsa; /* --> Base page */
+
+/*14*/ u_char nc_istat; /* --> Main Command and status */
+ #define CABRT 0x80 /* cmd: abort current operation */
+ #define SRST 0x40 /* mod: reset chip */
+ #define SIGP 0x20 /* r/w: message from host to ncr */
+ #define SEM 0x10 /* r/w: message between host + ncr */
+ #define CON 0x08 /* sta: connected to scsi */
+ #define INTF 0x04 /* sta: int on the fly (reset by wr)*/
+ #define SIP 0x02 /* sta: scsi-interrupt */
+ #define DIP 0x01 /* sta: host/script interrupt */
+
+/*15*/ u_char nc_15_;
+/*16*/ u_char nc_16_;
+/*17*/ u_char nc_17_;
+
+/*18*/ u_char nc_ctest0;
+/*19*/ u_char nc_ctest1;
+
+/*1a*/ u_char nc_ctest2;
+ #define CSIGP 0x40
+
+/*1b*/ u_char nc_ctest3;
+ #define FLF 0x08 /* cmd: flush dma fifo */
+ #define CLF 0x04 /* cmd: clear dma fifo */
+ #define FM 0x02 /* mod: fetch pin mode */
+ #define WRIE 0x01 /* mod: write and invalidate enable */
+
+/*1c*/ u_int32 nc_temp; /* ### Temporary stack */
+
+/*20*/ u_char nc_dfifo;
+/*21*/ u_char nc_ctest4;
+ #define BDIS 0x80 /* mod: burst disable */
+ #define MPEE 0x08 /* mod: master parity error enable */
+
+/*22*/ u_char nc_ctest5;
+ #define DFS 0x20 /* mod: dma fifo size */
+/*23*/ u_char nc_ctest6;
+
+/*24*/ u_int32 nc_dbc; /* ### Byte count and command */
+/*28*/ u_int32 nc_dnad; /* ### Next command register */
+/*2c*/ u_int32 nc_dsp; /* --> Script Pointer */
+/*30*/ u_int32 nc_dsps; /* --> Script pointer save/opcode#2 */
+/*34*/ u_int32 nc_scratcha; /* ??? Temporary register a */
+
+/*38*/ u_char nc_dmode;
+ #define BL_2 0x80 /* mod: burst length shift value +2 */
+ #define BL_1 0x40 /* mod: burst length shift value +1 */
+ #define ERL 0x08 /* mod: enable read line */
+ #define ERMP 0x04 /* mod: enable read multiple */
+ #define BOF 0x02 /* mod: burst op code fetch */
+
+/*39*/ u_char nc_dien;
+/*3a*/ u_char nc_dwt;
+
+/*3b*/ u_char nc_dcntl; /* --> Script execution control */
+
+ #define CLSE 0x80 /* mod: cache line size enable */
+ #define PFF 0x40 /* cmd: pre-fetch flush */
+ #define PFEN 0x20 /* mod: pre-fetch enable */
+ #define SSM 0x10 /* mod: single step mode */
+ #define IRQM 0x08 /* mod: irq mode (1 = totem pole !) */
+ #define STD 0x04 /* cmd: start dma mode */
+ #define IRQD 0x02 /* mod: irq disable */
+ #define NOCOM 0x01 /* cmd: protect sfbr while reselect */
+
+/*3c*/ u_int32 nc_adder;
+
+/*40*/ u_short nc_sien; /* -->: interrupt enable */
+/*42*/ u_short nc_sist; /* <--: interrupt status */
+ #define SBMC 0x1000/* sta: SCSI Bus Mode Change (895/6 only) */
+ #define STO 0x0400/* sta: timeout (select) */
+ #define GEN 0x0200/* sta: timeout (general) */
+ #define HTH 0x0100/* sta: timeout (handshake) */
+ #define MA 0x80 /* sta: phase mismatch */
+ #define CMP 0x40 /* sta: arbitration complete */
+ #define SEL 0x20 /* sta: selected by another device */
+ #define RSL 0x10 /* sta: reselected by another device*/
+ #define SGE 0x08 /* sta: gross error (over/underflow)*/
+ #define UDC 0x04 /* sta: unexpected disconnect */
+ #define RST 0x02 /* sta: scsi bus reset detected */
+ #define PAR 0x01 /* sta: scsi parity error */
+
+/*44*/ u_char nc_slpar;
+/*45*/ u_char nc_swide;
+/*46*/ u_char nc_macntl;
+/*47*/ u_char nc_gpcntl;
+/*48*/ u_char nc_stime0; /* cmd: timeout for select&handshake*/
+/*49*/ u_char nc_stime1; /* cmd: timeout user defined */
+/*4a*/ u_short nc_respid; /* sta: Reselect-IDs */
+
+/*4c*/ u_char nc_stest0;
+
+/*4d*/ u_char nc_stest1;
+ #define DBLEN 0x08 /* clock doubler running */
+ #define DBLSEL 0x04 /* clock doubler selected */
+
+
+/*4e*/ u_char nc_stest2;
+ #define ROF 0x40 /* reset scsi offset (after gross error!) */
+ #define EXT 0x02 /* extended filtering */
+
+/*4f*/ u_char nc_stest3;
+ #define TE 0x80 /* c: tolerAnt enable */
+ #define HSC 0x20 /* c: Halt SCSI Clock */
+ #define CSF 0x02 /* c: clear scsi fifo */
+
+/*50*/ u_short nc_sidl; /* Lowlevel: latched from scsi data */
+/*52*/ u_char nc_stest4;
+ #define SMODE 0xc0 /* SCSI bus mode (895/6 only) */
+ #define SMODE_HVD 0x40 /* High Voltage Differential */
+ #define SMODE_SE 0x80 /* Single Ended */
+ #define SMODE_LVD 0xc0 /* Low Voltage Differential */
+ #define LCKFRQ 0x20 /* Frequency Lock (895/6 only) */
+
+/*53*/ u_char nc_53_;
+/*54*/ u_short nc_sodl; /* Lowlevel: data out to scsi data */
+/*56*/ u_short nc_56_;
+/*58*/ u_short nc_sbdl; /* Lowlevel: data from scsi data */
+/*5a*/ u_short nc_5a_;
+/*5c*/ u_char nc_scr0; /* Working register B */
+/*5d*/ u_char nc_scr1; /* */
+/*5e*/ u_char nc_scr2; /* */
+/*5f*/ u_char nc_scr3; /* */
+/*60*/
+};
+
+/*-----------------------------------------------------------
+**
+** Utility macros for the script.
+**
+**-----------------------------------------------------------
+*/
+
+#define REGJ(p,r) (offsetof(struct ncr_reg, p ## r))
+#define REG(r) REGJ (nc_, r)
+
+#ifndef TARGET_MODE
+#define TARGET_MODE 0
+#endif
+
+typedef u_int32 ncrcmd;
+
+/*-----------------------------------------------------------
+**
+** SCSI phases
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_DATA_OUT 0x00000000
+#define SCR_DATA_IN 0x01000000
+#define SCR_COMMAND 0x02000000
+#define SCR_STATUS 0x03000000
+#define SCR_ILG_OUT 0x04000000
+#define SCR_ILG_IN 0x05000000
+#define SCR_MSG_OUT 0x06000000
+#define SCR_MSG_IN 0x07000000
+
+/*-----------------------------------------------------------
+**
+** Data transfer via SCSI.
+**
+**-----------------------------------------------------------
+**
+** MOVE_ABS (LEN)
+** <<start address>>
+**
+** MOVE_IND (LEN)
+** <<dnad_offset>>
+**
+** MOVE_TBL
+** <<dnad_offset>>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_MOVE_ABS(l) ((0x08000000 ^ (TARGET_MODE << 1ul)) | (l))
+#define SCR_MOVE_IND(l) ((0x28000000 ^ (TARGET_MODE << 1ul)) | (l))
+#define SCR_MOVE_TBL (0x18000000 ^ (TARGET_MODE << 1ul))
+
+struct scr_tblmove {
+ u_int32 size;
+ u_int32 addr;
+};
+
+/*-----------------------------------------------------------
+**
+** Selection
+**
+**-----------------------------------------------------------
+**
+** SEL_ABS | SCR_ID (0..7) [ | REL_JMP]
+** <<alternate_address>>
+**
+** SEL_TBL | << dnad_offset>> [ | REL_JMP]
+** <<alternate_address>>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_SEL_ABS 0x40000000
+#define SCR_SEL_ABS_ATN 0x41000000
+#define SCR_SEL_TBL 0x42000000
+#define SCR_SEL_TBL_ATN 0x43000000
+
+struct scr_tblsel {
+ u_char sel_0;
+ u_char sel_sxfer;
+ u_char sel_id;
+ u_char sel_scntl3;
+};
+
+#define SCR_JMP_REL 0x04000000
+#define SCR_ID(id) (((u_int32)(id)) << 16)
+
+/*-----------------------------------------------------------
+**
+** Waiting for Disconnect or Reselect
+**
+**-----------------------------------------------------------
+**
+** WAIT_DISC
+** dummy: <<alternate_address>>
+**
+** WAIT_RESEL
+** <<alternate_address>>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_WAIT_DISC 0x48000000
+#define SCR_WAIT_RESEL 0x50000000
+
+/*-----------------------------------------------------------
+**
+** Bit Set / Reset
+**
+**-----------------------------------------------------------
+**
+** SET (flags {|.. })
+**
+** CLR (flags {|.. })
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_SET(f) (0x58000000 | (f))
+#define SCR_CLR(f) (0x60000000 | (f))
+
+#define SCR_CARRY 0x00000400
+#define SCR_TRG 0x00000200
+#define SCR_ACK 0x00000040
+#define SCR_ATN 0x00000008
+
+
+
+
+/*-----------------------------------------------------------
+**
+** Memory to memory move
+**
+**-----------------------------------------------------------
+**
+** COPY (bytecount)
+** << source_address >>
+** << destination_address >>
+**
+** SCR_COPY sets the NO FLUSH option by default.
+** SCR_COPY_F does not set this option.
+**
+** For chips which do not support this option,
+** ncr_copy_and_bind() will remove this bit.
+**-----------------------------------------------------------
+*/
+
+#define SCR_NO_FLUSH 0x01000000
+
+#define SCR_COPY(n) (0xc0000000 | SCR_NO_FLUSH | (n))
+#define SCR_COPY_F(n) (0xc0000000 | (n))
+
+/*-----------------------------------------------------------
+**
+** Register move and binary operations
+**
+**-----------------------------------------------------------
+**
+** SFBR_REG (reg, op, data) reg = SFBR op data
+** << 0 >>
+**
+** REG_SFBR (reg, op, data) SFBR = reg op data
+** << 0 >>
+**
+** REG_REG (reg, op, data) reg = reg op data
+** << 0 >>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_REG_OFS(ofs) ((ofs) << 16ul)
+
+#define SCR_SFBR_REG(reg,op,data) \
+ (0x68000000 | (SCR_REG_OFS(REG(reg))) | (op) | ((data)<<8ul))
+
+#define SCR_REG_SFBR(reg,op,data) \
+ (0x70000000 | (SCR_REG_OFS(REG(reg))) | (op) | ((data)<<8ul))
+
+#define SCR_REG_REG(reg,op,data) \
+ (0x78000000 | (SCR_REG_OFS(REG(reg))) | (op) | ((data)<<8ul))
+
+
+#define SCR_LOAD 0x00000000
+#define SCR_SHL 0x01000000
+#define SCR_OR 0x02000000
+#define SCR_XOR 0x03000000
+#define SCR_AND 0x04000000
+#define SCR_SHR 0x05000000
+#define SCR_ADD 0x06000000
+#define SCR_ADDC 0x07000000
+
+/*-----------------------------------------------------------
+**
+** FROM_REG (reg) reg = SFBR
+** << 0 >>
+**
+** TO_REG (reg) SFBR = reg
+** << 0 >>
+**
+** LOAD_REG (reg, data) reg = <data>
+** << 0 >>
+**
+** LOAD_SFBR(data) SFBR = <data>
+** << 0 >>
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_FROM_REG(reg) \
+ SCR_REG_SFBR(reg,SCR_OR,0)
+
+#define SCR_TO_REG(reg) \
+ SCR_SFBR_REG(reg,SCR_OR,0)
+
+#define SCR_LOAD_REG(reg,data) \
+ SCR_REG_REG(reg,SCR_LOAD,data)
+
+#define SCR_LOAD_SFBR(data) \
+ (SCR_REG_SFBR (gpreg, SCR_LOAD, data))
+
+/*-----------------------------------------------------------
+**
+** Waiting for Disconnect or Reselect
+**
+**-----------------------------------------------------------
+**
+** JUMP [ | IFTRUE/IFFALSE ( ... ) ]
+** <<address>>
+**
+** JUMPR [ | IFTRUE/IFFALSE ( ... ) ]
+** <<distance>>
+**
+** CALL [ | IFTRUE/IFFALSE ( ... ) ]
+** <<address>>
+**
+** CALLR [ | IFTRUE/IFFALSE ( ... ) ]
+** <<distance>>
+**
+** RETURN [ | IFTRUE/IFFALSE ( ... ) ]
+** <<dummy>>
+**
+** INT [ | IFTRUE/IFFALSE ( ... ) ]
+** <<ident>>
+**
+** INT_FLY [ | IFTRUE/IFFALSE ( ... ) ]
+** <<ident>>
+**
+** Conditions:
+** WHEN (phase)
+** IF (phase)
+** CARRY
+** DATA (data, mask)
+**
+**-----------------------------------------------------------
+*/
+
+#define SCR_NO_OP 0x80000000
+#define SCR_JUMP 0x80080000
+#define SCR_JUMPR 0x80880000
+#define SCR_CALL 0x88080000
+#define SCR_CALLR 0x88880000
+#define SCR_RETURN 0x90080000
+#define SCR_INT 0x98080000
+#define SCR_INT_FLY 0x98180000
+
+#define IFFALSE(arg) (0x00080000 | (arg))
+#define IFTRUE(arg) (0x00000000 | (arg))
+
+#define WHEN(phase) (0x00030000 | (phase))
+#define IF(phase) (0x00020000 | (phase))
+
+#define DATA(D) (0x00040000 | ((D) & 0xff))
+#define MASK(D,M) (0x00040000 | (((M ^ 0xff) & 0xff) << 8ul)|((D) & 0xff))
+
+#define CARRYSET (0x00200000)
+
+/*-----------------------------------------------------------
+**
+** SCSI constants.
+**
+**-----------------------------------------------------------
+*/
+
+/*
+** Messages
+*/
+
+#define M_COMPLETE (0x00)
+#define M_EXTENDED (0x01)
+#define M_SAVE_DP (0x02)
+#define M_RESTORE_DP (0x03)
+#define M_DISCONNECT (0x04)
+#define M_ID_ERROR (0x05)
+#define M_ABORT (0x06)
+#define M_REJECT (0x07)
+#define M_NOOP (0x08)
+#define M_PARITY (0x09)
+#define M_LCOMPLETE (0x0a)
+#define M_FCOMPLETE (0x0b)
+#define M_RESET (0x0c)
+#define M_ABORT_TAG (0x0d)
+#define M_CLEAR_QUEUE (0x0e)
+#define M_INIT_REC (0x0f)
+#define M_REL_REC (0x10)
+#define M_TERMINATE (0x11)
+#define M_SIMPLE_TAG (0x20)
+#define M_HEAD_TAG (0x21)
+#define M_ORDERED_TAG (0x22)
+#define M_IGN_RESIDUE (0x23)
+#define M_IDENTIFY (0x80)
+
+#define M_X_MODIFY_DP (0x00)
+#define M_X_SYNC_REQ (0x01)
+#define M_X_WIDE_REQ (0x03)
+
+/*
+** Status
+*/
+
+#define S_GOOD (0x00)
+#define S_CHECK_COND (0x02)
+#define S_COND_MET (0x04)
+#define S_BUSY (0x08)
+#define S_INT (0x10)
+#define S_INT_COND_MET (0x14)
+#define S_CONFLICT (0x18)
+#define S_TERMINATED (0x20)
+#define S_QUEUE_FULL (0x28)
+#define S_ILLEGAL (0xff)
+#define S_SENSE (0x80)
+
+/*
+ * End of ncrreg from FreeBSD
+ */
+
+#endif /* !defined HOSTS_C */
+
+#endif /* defined NCR53C8XX_H */
diff --git a/linux/src/drivers/scsi/pas16.c b/linux/src/drivers/scsi/pas16.c
new file mode 100644
index 00000000..bd964204
--- /dev/null
+++ b/linux/src/drivers/scsi/pas16.c
@@ -0,0 +1,576 @@
+#define AUTOSENSE
+#define PSEUDO_DMA
+#define FOO
+#define UNSAFE /* Not unsafe for PAS16 -- use it */
+
+/*
+ * This driver adapted from Drew Eckhardt's Trantor T128 driver
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * ( Based on T128 - DISTRIBUTION RELEASE 3. )
+ *
+ * Modified to work with the Pro Audio Spectrum/Studio 16
+ * by John Weidman.
+ *
+ *
+ * For more information, please consult
+ *
+ * Media Vision
+ * (510) 770-8600
+ * (800) 348-7116
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * Options :
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512
+ * bytes at a time. Since interrupts are disabled by default during
+ * these transfers, we might need this to give reasonable interrupt
+ * service time if the transfer size gets too large.
+ *
+ * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
+ * increase compared to polled I/O.
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. This
+ * parameter comes from the NCR5380 code. It is NOT unsafe with
+ * the PAS16 and you should use it. If you don't you will have
+ * a problem with dropped characters during high speed
+ * communications during SCSI transfers. If you really don't
+ * want to use UNSAFE you can try defining LIMIT_TRANSFERSIZE or
+ * twiddle with the transfer size in the high level code.
+ *
+ * USLEEP - enable support for devices that don't disconnect. Untested.
+ *
+ * The card is detected and initialized in one of several ways :
+ * 1. Autoprobe (default) - There are many different models of
+ * the Pro Audio Spectrum/Studio 16, and I only have one of
+ * them, so this may require a little tweaking. An interrupt
+ * is triggered to autoprobe for the interrupt line. Note:
+ * with the newer model boards, the interrupt is set via
+ * software after reset using the default_irq for the
+ * current board number.
+ *
+ *
+ * 2. With command line overrides - pas16=port,irq may be
+ * used on the LILO command line to override the defaults.
+ *
+ * 3. With the PAS16_OVERRIDE compile time define. This is
+ * specified as an array of address, irq tuples. Ie, for
+ * one board at the default 0x388 address, IRQ10, I could say
+ * -DPAS16_OVERRIDE={{0x388, 10}}
+ * NOTE: Untested.
+ *
+ * Note that if the override methods are used, place holders must
+ * be specified for other boards in the system.
+ *
+ *
+ * Configuration notes :
+ * The current driver does not support interrupt sharing with the
+ * sound portion of the card. If you use the same irq for the
+ * scsi port and sound you will have problems. Either use
+ * a different irq for the scsi port or don't use interrupts
+ * for the scsi port.
+ *
+ * If you have problems with your card not being recognized, use
+ * the LILO command line override. Try to get it recognized without
+ * interrupts. Ie, for a board at the default 0x388 base port,
+ * boot: linux pas16=0x388,255
+ *
+ * (255 is the IRQ_NONE constant in NCR5380.h)
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <asm/system.h>
+#include <linux/signal.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "pas16.h"
+#define AUTOPROBE_IRQ
+#include "NCR5380.h"
+#include "constants.h"
+#include "sd.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_pas16 = {
+ PROC_SCSI_PAS16, 5, "pas16",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+static int pas_maxi = 0;
+static int pas_wmaxi = 0;
+
+
+int scsi_irq_translate[] =
+ { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 };
+
+/* The default_irqs array contains values used to set the irq into the
+ * board via software (as must be done on newer model boards without
+ * irq jumpers on the board). The first value in the array will be
+ * assigned to logical board 0, the next to board 1, etc.
+ */
+int default_irqs[] = { PAS16_DEFAULT_BOARD_1_IRQ,
+ PAS16_DEFAULT_BOARD_2_IRQ,
+ PAS16_DEFAULT_BOARD_3_IRQ,
+ PAS16_DEFAULT_BOARD_4_IRQ
+ };
+
+static struct override {
+ unsigned short io_port;
+ int irq;
+} overrides
+#ifdef PAS16_OVERRIDE
+ [] = PAS16_OVERRIDE;
+#else
+ [4] = {{0,IRQ_AUTO}, {0,IRQ_AUTO}, {0,IRQ_AUTO},
+ {0,IRQ_AUTO}};
+#endif
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+static struct base {
+ unsigned short io_port;
+ int noauto;
+} bases[] = { {PAS16_DEFAULT_BASE_1, 0},
+ {PAS16_DEFAULT_BASE_2, 0},
+ {PAS16_DEFAULT_BASE_3, 0},
+ {PAS16_DEFAULT_BASE_4, 0}
+ };
+
+#define NO_BASES (sizeof (bases) / sizeof (struct base))
+
+unsigned short pas16_offset[ 8 ] =
+ {
+ 0x1c00, /* OUTPUT_DATA_REG */
+ 0x1c01, /* INITIATOR_COMMAND_REG */
+ 0x1c02, /* MODE_REG */
+ 0x1c03, /* TARGET_COMMAND_REG */
+ 0x3c00, /* STATUS_REG ro, SELECT_ENABLE_REG wo */
+ 0x3c01, /* BUS_AND_STATUS_REG ro, START_DMA_SEND_REG wo */
+ 0x3c02, /* INPUT_DATA_REGISTER ro, (N/A on PAS16 ?)
+ * START_DMA_TARGET_RECEIVE_REG wo
+ */
+ 0x3c03, /* RESET_PARITY_INTERRUPT_REG ro,
+ * START_DMA_INITIATOR_RECEIVE_REG wo
+ */
+ };
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at */
+/* 1 = blue
+ 2 = green
+ 3 = cyan
+ 4 = red
+ 5 = magenta
+ 6 = yellow
+ 7 = white
+*/
+#if 1
+#define rtrc(i) {inb(0x3da); outb(0x31, 0x3c0); outb((i), 0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+
+
+/*
+ * Function : enable_board( int board_num, unsigned short port )
+ *
+ * Purpose : set address in new model board
+ *
+ * Inputs : board_num - logical board number 0-3, port - base address
+ *
+ */
+
+void enable_board( int board_num, unsigned short port )
+{
+ outb( 0xbc + board_num, MASTER_ADDRESS_PTR );
+ outb( port >> 2, MASTER_ADDRESS_PTR );
+}
+
+
+
+/*
+ * Function : init_board( unsigned short port, int irq )
+ *
+ * Purpose : Set the board up to handle the SCSI interface
+ *
+ * Inputs : port - base address of the board,
+ * irq - irq to assign to the SCSI port
+ * force_irq - set it even if it conflicts with sound driver
+ *
+ */
+
+void init_board( unsigned short io_port, int irq, int force_irq )
+{
+ unsigned int tmp;
+ unsigned int pas_irq_code;
+
+ /* Initialize the SCSI part of the board */
+
+ outb( 0x30, io_port + P_TIMEOUT_COUNTER_REG ); /* Timeout counter */
+ outb( 0x01, io_port + P_TIMEOUT_STATUS_REG_OFFSET ); /* Reset TC */
+ outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */
+
+ NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+
+ /* Set the SCSI interrupt pointer without mucking up the sound
+ * interrupt pointer in the same byte.
+ */
+ pas_irq_code = ( irq < 16 ) ? scsi_irq_translate[irq] : 0;
+ tmp = inb( io_port + IO_CONFIG_3 );
+
+ if( (( tmp & 0x0f ) == pas_irq_code) && pas_irq_code > 0
+ && !force_irq )
+ {
+ printk( "pas16: WARNING: Can't use same irq as sound "
+ "driver -- interrupts disabled\n" );
+ /* Set up the drive parameters, disable 5380 interrupts */
+ outb( 0x4d, io_port + SYS_CONFIG_4 );
+ }
+ else
+ {
+ tmp = ( tmp & 0x0f ) | ( pas_irq_code << 4 );
+ outb( tmp, io_port + IO_CONFIG_3 );
+
+ /* Set up the drive parameters and enable 5380 interrupts */
+ outb( 0x6d, io_port + SYS_CONFIG_4 );
+ }
+}
+
+
+/*
+ * Function : pas16_hw_detect( unsigned short board_num )
+ *
+ * Purpose : determine if a pas16 board is present
+ *
+ * Inputs : board_num - logical board number ( 0 - 3 )
+ *
+ * Returns : 0 if board not found, 1 if found.
+ */
+
+int pas16_hw_detect( unsigned short board_num )
+{
+ unsigned char board_rev, tmp;
+ unsigned short io_port = bases[ board_num ].io_port;
+
+ /* See if we can find a PAS16 board at the address associated
+ * with this logical board number.
+ */
+
+ /* First, attempt to take a newer model board out of reset and
+ * give it a base address. This shouldn't affect older boards.
+ */
+ enable_board( board_num, io_port );
+
+ /* Now see if it looks like a PAS16 board */
+ board_rev = inb( io_port + PCB_CONFIG );
+
+ if( board_rev == 0xff )
+ return 0;
+
+ tmp = board_rev ^ 0xe0;
+
+ outb( tmp, io_port + PCB_CONFIG );
+ tmp = inb( io_port + PCB_CONFIG );
+ outb( board_rev, io_port + PCB_CONFIG );
+
+ if( board_rev != tmp ) /* Not a PAS-16 */
+ return 0;
+
+ if( ( inb( io_port + OPERATION_MODE_1 ) & 0x03 ) != 0x03 )
+ return 0; /* return if no SCSI interface found */
+
+ /* Mediavision has some new model boards that return ID bits
+ * that indicate a SCSI interface, but they're not (LMS). We'll
+ * put in an additional test to try to weed them out.
+ */
+
+ outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */
+ NCR5380_write( MODE_REG, 0x20 ); /* Is it really SCSI? */
+ if( NCR5380_read( MODE_REG ) != 0x20 ) /* Write to a reg. */
+ return 0; /* and try to read */
+ NCR5380_write( MODE_REG, 0x00 ); /* it back. */
+ if( NCR5380_read( MODE_REG ) != 0x00 )
+ return 0;
+
+ return 1;
+}
+
+
+/*
+ * Function : pas16_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ *
+ */
+
+void pas16_setup(char *str, int *ints) {
+ static int commandline_current = 0;
+ int i;
+ if (ints[0] != 2)
+ printk("pas16_setup : usage pas16=io_port,irq\n");
+ else
+ if (commandline_current < NO_OVERRIDES) {
+ overrides[commandline_current].io_port = (unsigned short) ints[1];
+ overrides[commandline_current].irq = ints[2];
+ for (i = 0; i < NO_BASES; ++i)
+ if (bases[i].io_port == (unsigned short) ints[1]) {
+ bases[i].noauto = 1;
+ break;
+ }
+ ++commandline_current;
+ }
+}
+
+/*
+ * Function : int pas16_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : detects and initializes PAS16 controllers
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int pas16_detect(Scsi_Host_Template * tpnt) {
+ static int current_override = 0;
+ static unsigned short current_base = 0;
+ struct Scsi_Host *instance;
+ unsigned short io_port;
+ int count;
+
+ tpnt->proc_dir = &proc_scsi_pas16;
+ tpnt->proc_info = &pas16_proc_info;
+
+ for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+ io_port = 0;
+
+ if (overrides[current_override].io_port)
+ {
+ io_port = overrides[current_override].io_port;
+ enable_board( current_override, io_port );
+ init_board( io_port, overrides[current_override].irq, 1 );
+ }
+ else
+ for (; !io_port && (current_base < NO_BASES); ++current_base) {
+#if (PDEBUG & PDEBUG_INIT)
+ printk("scsi-pas16 : probing io_port %04x\n", (unsigned int) bases[current_base].io_port);
+#endif
+ if ( !bases[current_base].noauto &&
+ pas16_hw_detect( current_base ) ){
+ io_port = bases[current_base].io_port;
+ init_board( io_port, default_irqs[ current_base ], 0 );
+#if (PDEBUG & PDEBUG_INIT)
+ printk("scsi-pas16 : detected board.\n");
+#endif
+ }
+ }
+
+
+#if defined(PDEBUG) && (PDEBUG & PDEBUG_INIT)
+ printk("scsi-pas16 : io_port = %04x\n", (unsigned int) io_port);
+#endif
+
+ if (!io_port)
+ break;
+
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+ instance->io_port = io_port;
+
+ NCR5380_init(instance, 0);
+
+ if (overrides[current_override].irq != IRQ_AUTO)
+ instance->irq = overrides[current_override].irq;
+ else
+ instance->irq = NCR5380_probe_irq(instance, PAS16_IRQS);
+
+ if (instance->irq != IRQ_NONE)
+ if (request_irq(instance->irq, pas16_intr, SA_INTERRUPT, "pas16", NULL)) {
+ printk("scsi%d : IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = IRQ_NONE;
+ }
+
+ if (instance->irq == IRQ_NONE) {
+ printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+ printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ /* Disable 5380 interrupts, leave drive params the same */
+ outb( 0x4d, io_port + SYS_CONFIG_4 );
+ outb( (inb(io_port + IO_CONFIG_3) & 0x0f), io_port + IO_CONFIG_3 );
+ }
+
+#if defined(PDEBUG) && (PDEBUG & PDEBUG_INIT)
+ printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
+#endif
+
+ printk("scsi%d : at 0x%04x", instance->host_no, (int)
+ instance->io_port);
+ if (instance->irq == IRQ_NONE)
+ printk (" interrupts disabled");
+ else
+ printk (" irq %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ CAN_QUEUE, CMD_PER_LUN, PAS16_PUBLIC_RELEASE);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ ++current_override;
+ ++count;
+ }
+ return count;
+}
+
+/*
+ * Function : int pas16_biosparam(Disk *disk, kdev_t dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for
+ * the specified device / size.
+ *
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ * major / minor, ip[] = {heads, sectors, cylinders}
+ *
+ * Returns : always 0 (success), initializes ip
+ *
+ */
+
+/*
+ * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+ */
+
+int pas16_biosparam(Disk * disk, kdev_t dev, int * ip)
+{
+ int size = disk->capacity;
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11; /* I think I have it as /(32*64) */
+ if( ip[2] > 1024 ) { /* yes, >, not >= */
+ ip[0]=255;
+ ip[1]=63;
+ ip[2]=size/(63*255);
+ if( ip[2] > 1023 ) /* yes >1023... */
+ ip[2] = 1023;
+ }
+
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_pread (struct Scsi_Host *instance,
+ * unsigned char *dst, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to
+ * dst
+ *
+ * Inputs : dst = destination, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+ */
+
+static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,
+ int len) {
+ register unsigned char *d = dst;
+ register unsigned short reg = (unsigned short) (instance->io_port +
+ P_DATA_REG_OFFSET);
+ register int i = len;
+ int ii = 0;
+
+ while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) )
+ ++ii;
+
+ insb( reg, d, i );
+
+ if ( inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) {
+ outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET);
+ printk("scsi%d : watchdog timer fired in NCR5380_pread()\n",
+ instance->host_no);
+ return -1;
+ }
+ if (ii > pas_maxi)
+ pas_maxi = ii;
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_pwrite (struct Scsi_Host *instance,
+ * unsigned char *src, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from
+ * src
+ *
+ * Inputs : src = source, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+ */
+
+static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src,
+ int len) {
+ register unsigned char *s = src;
+ register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET);
+ register int i = len;
+ int ii = 0;
+
+ while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) )
+ ++ii;
+
+ outsb( reg, s, i );
+
+ if (inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) {
+ outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET);
+ printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n",
+ instance->host_no);
+ return -1;
+ }
+ if (ii > pas_maxi)
+ pas_wmaxi = ii;
+ return 0;
+}
+
+#include "NCR5380.c"
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = MV_PAS16;
+
+#include <linux/module.h>
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/pas16.h b/linux/src/drivers/scsi/pas16.h
new file mode 100644
index 00000000..a1bda1fa
--- /dev/null
+++ b/linux/src/drivers/scsi/pas16.h
@@ -0,0 +1,196 @@
+/*
+ * This driver adapted from Drew Eckhardt's Trantor T128 driver
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * ( Based on T128 - DISTRIBUTION RELEASE 3. )
+ *
+ * Modified to work with the Pro Audio Spectrum/Studio 16
+ * by John Weidman.
+ *
+ *
+ * For more information, please consult
+ *
+ * Media Vision
+ * (510) 770-8600
+ * (800) 348-7116
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+
+#ifndef PAS16_H
+#define PAS16_H
+
+#define PAS16_PUBLIC_RELEASE 3
+
+#define PDEBUG_INIT 0x1
+#define PDEBUG_TRANSFER 0x2
+
+#define PAS16_DEFAULT_BASE_1 0x388
+#define PAS16_DEFAULT_BASE_2 0x384
+#define PAS16_DEFAULT_BASE_3 0x38c
+#define PAS16_DEFAULT_BASE_4 0x288
+
+#define PAS16_DEFAULT_BOARD_1_IRQ 10
+#define PAS16_DEFAULT_BOARD_2_IRQ 12
+#define PAS16_DEFAULT_BOARD_3_IRQ 14
+#define PAS16_DEFAULT_BOARD_4_IRQ 15
+
+
+/*
+ * The Pro Audio Spectrum boards are I/O mapped. They use a Zilog 5380
+ * SCSI controller, which is the equivalent of NCR's 5380. "Pseudo-DMA"
+ * architecture is used, where a PAL drives the DMA signals on the 5380
+ * allowing fast, blind transfers with proper handshaking.
+ */
+
+
+/* The Time-out Counter register is used to safe-guard against a stuck
+ * bus (in the case of RDY driven handshake) or a stuck byte (if 16-Bit
+ * DMA conversion is used). The counter uses a 28.224MHz clock
+ * divided by 14 as its clock source. In the case of a stuck byte in
+ * the holding register, an interrupt is generated (and mixed with the
+ * one with the drive) using the CD-ROM interrupt pointer.
+ */
+
+#define P_TIMEOUT_COUNTER_REG 0x4000
+#define P_TC_DISABLE 0x80 /* Set to 0 to enable timeout int. */
+ /* Bits D6-D0 contain timeout count */
+
+
+#define P_TIMEOUT_STATUS_REG_OFFSET 0x4001
+#define P_TS_TIM 0x80 /* check timeout status */
+ /* Bits D6-D4 N/U */
+#define P_TS_ARM_DRQ_INT 0x08 /* Arm DRQ Int. When set high,
+ * the next rising edge will
+ * cause a CD-ROM interrupt.
+ * When set low, the interrupt
+ * will be cleared. There is
+ * no status available for
+ * this interrupt.
+ */
+#define P_TS_ENABLE_TO_ERR_INTERRUPT /* Enable timeout error int. */
+#define P_TS_ENABLE_WAIT /* Enable Wait */
+
+#define P_TS_CT 0x01 /* clear timeout. Note: writing
+ * to this register clears the
+ * timeout error int. or status
+ */
+
+
+/*
+ * The data register reads/writes to/from the 5380 in pseudo-DMA mode
+ */
+
+#define P_DATA_REG_OFFSET 0x5c00 /* rw */
+
+#define P_STATUS_REG_OFFSET 0x5c01 /* ro */
+#define P_ST_RDY 0x80 /* 5380 DDRQ Status */
+
+#define P_IRQ_STATUS 0x5c03
+#define P_IS_IRQ 0x80 /* DIRQ status */
+
+#define PCB_CONFIG 0x803
+#define MASTER_ADDRESS_PTR 0x9a01 /* Fixed position - no relo */
+#define SYS_CONFIG_4 0x8003
+#define WAIT_STATE 0xbc00
+#define OPERATION_MODE_1 0xec03
+#define IO_CONFIG_3 0xf002
+
+
+#ifndef ASM
+int pas16_abort(Scsi_Cmnd *);
+int pas16_biosparam(Disk *, kdev_t, int*);
+int pas16_detect(Scsi_Host_Template *);
+int pas16_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int pas16_reset(Scsi_Cmnd *, unsigned int);
+int pas16_proc_info (char *buffer ,char **start, off_t offset,
+ int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 32
+#endif
+
+/*
+ * I hadn't thought of this with the earlier drivers - but to prevent
+ * macro definition conflicts, we shouldn't define all of the internal
+ * macros when this is being used solely for the host stub.
+ */
+
+#if defined(HOSTS_C) || defined(MODULE)
+
+#define MV_PAS16 {NULL, NULL, NULL, NULL, \
+ "Pro Audio Spectrum-16 SCSI", \
+ pas16_detect, NULL, NULL, \
+ NULL, pas16_queue_command, pas16_abort, pas16_reset, NULL, \
+ pas16_biosparam, \
+ /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \
+ /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
+
+#endif
+#ifndef HOSTS_C
+
+#define NCR5380_implementation_fields \
+ volatile unsigned short io_port
+
+#define NCR5380_local_declare() \
+ volatile unsigned short io_port
+
+#define NCR5380_setup(instance) \
+ io_port = (instance)->io_port
+
+#define PAS16_io_port(reg) ( io_port + pas16_offset[(reg)] )
+
+#if !(PDEBUG & PDEBUG_TRANSFER)
+#define NCR5380_read(reg) ( inb(PAS16_io_port(reg)) )
+#define NCR5380_write(reg, value) ( outb((value),PAS16_io_port(reg)) )
+#else
+#define NCR5380_read(reg) \
+ (((unsigned char) printk("scsi%d : read register %d at io_port %04x\n"\
+ , instance->hostno, (reg), PAS16_io_port(reg))), inb( PAS16_io_port(reg)) )
+
+#define NCR5380_write(reg, value) \
+ (printk("scsi%d : write %02x to register %d at io_port %04x\n", \
+ instance->hostno, (value), (reg), PAS16_io_port(reg)), \
+ outb( (value),PAS16_io_port(reg) ) )
+
+#endif
+
+
+#define NCR5380_intr pas16_intr
+#define NCR5380_queue_command pas16_queue_command
+#define NCR5380_abort pas16_abort
+#define NCR5380_reset pas16_reset
+#define NCR5380_proc_info pas16_proc_info
+
+/* 15 14 12 10 7 5 3
+ 1101 0100 1010 1000 */
+
+#define PAS16_IRQS 0xd4a8
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* PAS16_H */
diff --git a/linux/src/drivers/scsi/ppa.c b/linux/src/drivers/scsi/ppa.c
new file mode 100644
index 00000000..98749dd7
--- /dev/null
+++ b/linux/src/drivers/scsi/ppa.c
@@ -0,0 +1,1550 @@
+/* ppa.c -- low level driver for the IOMEGA PPA3
+ * parallel port SCSI host adapter.
+ *
+ * (The PPA3 is the embedded controller in the ZIP drive.)
+ *
+ * (c) 1995,1996 Grant R. Guenther, grant@torque.net,
+ * under the terms of the GNU Public License.
+ *
+ * Current Maintainer: David Campbell (Perth, Western Australia)
+ * campbell@gear.torque.net
+ * dcampbel@p01.as17.honeywell.com.au
+ *
+ * My unoffical company acronym list is 21 pages long:
+ * FLA: Four letter acronym with built in facility for
+ * future expansion to five letters.
+ */
+
+#include <linux/config.h>
+
+/* The following #define is to avoid a clash with hosts.c */
+#define PPA_CODE 1
+#ifndef HAVE_PC87332
+#define HAVE_PC87332 0
+#endif
+#define PPA_PROBE_SPP 0x0001
+#define PPA_PROBE_PS2 0x0002
+#define PPA_PROBE_ECR 0x0010
+#define PPA_PROBE_EPP17 0x0100
+#define PPA_PROBE_EPP19 0x0200
+int port_probe(unsigned short);
+
+#include <linux/blk.h>
+#include "sd.h"
+#include "hosts.h"
+typedef struct {
+ int base; /* Actual port address */
+ int mode; /* Transfer mode */
+ int host; /* Host number (for proc) */
+ Scsi_Cmnd *cur_cmd; /* Current queued command */
+ struct tq_struct ppa_tq; /* Polling interupt stuff */
+ unsigned long jstart; /* Jiffies at start */
+ unsigned failed:1; /* Failure flag */
+} ppa_struct;
+
+#define PPA_EMPTY \
+{-1, /* base */ \
+PPA_AUTODETECT, /* mode */ \
+-1, /* host */ \
+NULL, /* cur_cmd */ \
+{0, 0, ppa_interrupt, NULL}, \
+0, /* jstart */ \
+0 /* failed */ \
+}
+
+#include "ppa.h"
+#undef CONFIG_PARPORT
+#define NO_HOSTS 4
+static ppa_struct ppa_hosts[NO_HOSTS] =
+{PPA_EMPTY, PPA_EMPTY, PPA_EMPTY, PPA_EMPTY};
+
+#define PPA_BASE(x) ppa_hosts[(x)].base
+
+int base[NO_HOSTS] =
+{0x03bc, 0x0378, 0x0278, 0x0000};
+#define parbus_base base
+#define parbus_no NO_HOSTS
+
+static inline int ppa_pb_claim(int host_no)
+{
+ if (ppa_hosts[host_no].cur_cmd)
+ ppa_hosts[host_no].cur_cmd->SCp.phase++;
+ return 0;
+}
+
+/***************************************************************************
+ * Parallel port probing routines *
+ ***************************************************************************/
+
+#ifndef MODULE
+/*
+ * Command line parameters (for built-in driver):
+ *
+ * Syntax: ppa=base[,mode[,use_sg]]
+ *
+ * For example: ppa=0x378 or ppa=0x378,0,3
+ *
+ */
+
+void ppa_setup(char *str, int *ints)
+{
+ static int x = 0;
+
+ if (x == 0) { /* Disable ALL known ports */
+ int i;
+
+ for (i = 0; i < NO_HOSTS; i++)
+ parbus_base[i] = 0x0000;
+ }
+ switch (ints[0]) {
+ case 3:
+ ppa_sg = ints[3];
+ case 2:
+ ppa_hosts[x].mode = ints[2];
+ parbus_base[x] = ints[1];
+ break;
+ default:
+ printk("PPA: I only use between 2 to 3 parameters.\n");
+ break;
+ }
+ x++;
+ }
+#else
+Scsi_Host_Template driver_template = PPA;
+#include "scsi_module.c"
+#endif
+
+/*
+ * Start of Chipset kludges
+ */
+
+#if HAVE_PC87332 > 0
+#warning PC87332 Kludge code included
+static inline int pc87332_port(int host_no)
+{
+ /* A routine to detect and kludge pc87332 chipsets into the
+ * "optimum" mode for parallel port data transfer.
+ * This assumes EPP is better than ECP...
+ * (Which it is for disk drives but not printers and scanners)
+ */
+ int base = ppa_hosts[host_no].base;
+
+ /* This is where an pc87332 can hide */
+ unsigned short index_addr[4] =
+ {
+ 0x0398, 0x026e, 0x015c, 0x002e
+ };
+
+ /* Bits 0&1 of FAR (Function Address Register) which specify where
+ * the LPT port will show up at.
+ */
+ unsigned short port_ref[4] =
+ {
+ 0x378, 0x3bc, 0x278, 0xffff
+ };
+
+ unsigned char a;
+ int loop;
+
+ for (loop = 0; loop < 4; loop++) {
+ /* Clear the "wax" out of the pc87332, only needed after hard
+ * reset.
+ */
+ inb(index_addr[loop]);
+ inb(index_addr[loop]);
+ inb(index_addr[loop]);
+ inb(index_addr[loop]);
+
+ /* Anyone home ?? */
+ outb(0xff, index_addr[loop]);
+ a = inb(index_addr[loop]);
+ switch (a) {
+ case (0x0f): /* PC87732 */
+ break;
+ case (0x1f): /* PC87306 */
+ break;
+ case (0x7f): /* PC87??? */
+ break;
+ default:
+ continue;
+ } /* Is this pc87332 on the desired port */
+ outb(0x01, index_addr[loop]);
+ a = inb(index_addr[loop] + 1);
+ if (port_ref[a & 0x03] != base)
+ continue;
+
+ /* Found a pc87332 */
+ printk("NatSemi PC87332 (or variant) at 0x%04x\n", base);
+
+ /* Try to enable EPP modes
+ * with hardware data direction
+ */
+ if (base != 0x3bc) {
+ /* EPP 1.9 */
+ outb(0x04, index_addr[loop]);
+ a = inb(index_addr[loop] + 1);
+ printk("Old reg1 = %02x\n", a);
+ /* 0x01 for EPP 1.7, 0x03 for EPP 1.9, 0x0c for ECP */
+ a = (a & 0xf0) | 0x03;
+ outb(a, index_addr[loop] + 1);
+ outb(a, index_addr[loop] + 1);
+
+ /* Software data direction selection */
+ outb(0x02, index_addr[loop]);
+ a = inb(index_addr[loop] + 1);
+ printk("Old reg2 = %02x\n", a);
+ /* 0x80 for software, 0x00 for hardware */
+ a = (a & 0x7f) | 0x80;
+ outb(a, index_addr[loop] + 1);
+ outb(a, index_addr[loop] + 1);
+ ppa_hosts[host_no].mode = PPA_EPP_32;
+ } else {
+ /* There is not enough address space for the 0x3bc port
+ * to have EPP registers so we will kludge it into an
+ * ECP
+ * port to allow bi-directional byte mode...
+ */
+ /* ECP */
+ outb(0x04, index_addr[loop]);
+ a = inb(index_addr[loop] + 1);
+ a = (a & 0xfb) | 0x06;
+ outb(a, index_addr[loop] + 1);
+ outb(a, index_addr[loop] + 1);
+ ppa_hosts[host_no].mode = PPA_PS2;
+ }
+
+ outb(0x04, index_addr[loop]);
+ a = inb(index_addr[loop] + 1);
+ return ppa_hosts[host_no].mode;
+ }
+ return 0;
+ }
+#else
+#define pc87332_port(x)
+#endif /* HAVE_PC87332 */
+
+static inline int generic_port(int host_no)
+{
+ /* Generic parallel port detection
+ * This will try to discover if the port is
+ * EPP, ECP, PS/2 or NIBBLE (In that order, approx....)
+ */
+ unsigned int save_ctr, save_ecr, r;
+ int ppb = PPA_BASE(host_no);
+
+ save_ctr = r_ctr(ppb);
+ save_ecr = r_ecr(ppb);
+ r = port_probe(ppb);
+ w_ecr(ppb, save_ecr);
+ w_ctr(ppb, save_ctr);
+
+ if (r & PPA_PROBE_SPP)
+ ppa_hosts[host_no].mode = PPA_NIBBLE;
+
+ if (r & PPA_PROBE_PS2) {
+ ppa_hosts[host_no].mode = PPA_PS2;
+ if (r & PPA_PROBE_ECR)
+ w_ecr(ppb, 0x20);
+ }
+ if ((r & PPA_PROBE_EPP17) || (r & PPA_PROBE_EPP19)) {
+ /* ppa_hosts[host_no].mode = PPA_EPP_32; */
+ if (r & PPA_PROBE_ECR)
+ w_ecr(ppb, 0x80);
+ }
+ return ppa_hosts[host_no].mode;
+}
+
+int ppa_detect(Scsi_Host_Template * host)
+{
+ struct Scsi_Host *hreg;
+ int ports;
+ int i, nhosts;
+ unsigned short ppb;
+
+ printk("ppa: Version %s\n", PPA_VERSION);
+ nhosts = 0;
+
+ for (i = 0; i < parbus_no; i++) {
+ if (parbus_base[i] == 0x0000)
+ continue;
+ ppb = ppa_hosts[i].base = parbus_base[i];
+
+ /* sanity checks */
+ if (check_region(parbus_base[i],
+ (parbus_base[i] == 0x03bc) ? 3 : 8))
+ continue;
+
+ pc87332_port(i);
+ if (!generic_port(i))
+ continue;
+
+ if (ppa_init(i))
+ continue;
+
+ /* now the glue ... */
+ switch (ppa_hosts[i].mode) {
+ case PPA_NIBBLE:
+ case PPA_PS2:
+ ports = 3;
+ break;
+ case PPA_EPP_8:
+ case PPA_EPP_16:
+ case PPA_EPP_32:
+ ports = 8;
+ break;
+ default: /* Never gets here */
+ continue;
+ }
+ request_region(ppa_hosts[i].base, ports, "ppa");
+ host->can_queue = PPA_CAN_QUEUE;
+ host->sg_tablesize = ppa_sg;
+ hreg = scsi_register(host, 0);
+ hreg->io_port = ppa_hosts[i].base;
+ hreg->n_io_port = ports;
+ hreg->dma_channel = -1;
+ hreg->unique_id = i;
+ ppa_hosts[i].host = hreg->host_no;
+ nhosts++;
+ }
+ if (nhosts == 0)
+ return 0;
+ else
+ return 1; /* return number of hosts detected */
+}
+
+/* This is to give the ppa driver a way to modify the timings (and other
+ * parameters) by writing to the /proc/scsi/ppa/0 file.
+ * Very simple method really... (To simple, no error checking :( )
+ * Reason: Kernel hackers HATE having to unload and reload modules for
+ * testing...
+ * Also gives a method to use a script to obtain optimum timings (TODO)
+ */
+
+static inline int ppa_strncmp(const char *a, const char *b, int len)
+{
+ int loop;
+ for (loop = 0; loop < len; loop++)
+ if (a[loop] != b[loop])
+ return 1;
+
+ return 0;
+}
+static inline int ppa_proc_write(int hostno, char *buffer, int length)
+{
+ unsigned long x;
+
+ if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) {
+ x = simple_strtoul(buffer + 5, NULL, 0);
+ ppa_hosts[hostno].mode = x;
+ return length;
+ }
+ printk("ppa /proc: invalid variable\n");
+ return (-EINVAL);
+}
+
+int ppa_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
+{
+ int i;
+ int len = 0;
+
+ for (i = 0; i < 4; i++)
+ if (ppa_hosts[i].host == hostno)
+ break;
+
+ if (inout)
+ return ppa_proc_write(i, buffer, length);
+
+ len += sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
+ len += sprintf(buffer + len, "Port : 0x%04x\n", ppa_hosts[i].base);
+ len += sprintf(buffer + len, "Mode : %s\n", PPA_MODE_STRING[ppa_hosts[i].mode]);
+
+ /* Request for beyond end of buffer */
+ if (offset > len)
+ return 0;
+
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ return len;
+} /* end of ppa.c */
+static int device_check(int host_no);
+
+#if PPA_DEBUG > 0
+#define ppa_fail(x,y) printk("ppa: ppa_fail(%i) from %s at line %d\n",\
+ y, __FUNCTION__, __LINE__); ppa_fail_func(x,y);
+static inline void ppa_fail_func(int host_no, int error_code)
+#else
+static inline void ppa_fail(int host_no, int error_code)
+ #endif
+{
+ /* If we fail a device then we trash status / message bytes */
+ if (ppa_hosts[host_no].cur_cmd) {
+ ppa_hosts[host_no].cur_cmd->result = error_code << 16;
+ ppa_hosts[host_no].failed = 1;
+ }
+}
+
+/*
+ * Wait for the high bit to be set.
+ *
+ * In principle, this could be tied to an interrupt, but the adapter
+ * doesn't appear to be designed to support interrupts. We spin on
+ * the 0x80 ready bit.
+ */
+static unsigned char ppa_wait(int host_no)
+{
+ int k;
+ unsigned short ppb = PPA_BASE(host_no);
+ unsigned char r;
+
+ k = PPA_SPIN_TMO;
+ do {
+ r = r_str(ppb);
+ k--;
+ udelay(1);
+ }
+ while (!(r & 0x80) && (k));
+
+ /*
+ * return some status information.
+ * Semantics: 0xc0 = ZIP wants more data
+ * 0xd0 = ZIP wants to send more data
+ * 0xe0 = ZIP is expecting SCSI command data
+ * 0xf0 = end of transfer, ZIP is sending status
+ */
+ if (k)
+ return (r & 0xf0);
+
+ /* Counter expired - Time out occurred */
+ ppa_fail(host_no, DID_TIME_OUT);
+ printk("ppa timeout in ppa_wait\n");
+ return 0; /* command timed out */
+}
+
+/*
+ * output a string, in whatever mode is available, according to the
+ * PPA protocol.
+ */
+static inline void epp_reset(unsigned short ppb)
+{
+ int i;
+
+ i = r_str(ppb);
+ w_str(ppb, i);
+ w_str(ppb, i & 0xfe);
+}
+
+static inline void ecp_sync(unsigned short ppb)
+{
+ int i;
+
+ if ((r_ecr(ppb) & 0xe0) != 0x80)
+ return;
+
+ for (i = 0; i < 100; i++) {
+ if (r_ecr(ppb) & 0x01)
+ return;
+ udelay(5);
+ }
+ printk("ppa: ECP sync failed as data still present in FIFO.\n");
+}
+
+/*
+ * Here is the asm code for the SPP/PS2 protocols for the i386.
+ * This has been optimised for speed on 386/486 machines. There will
+ * be very little improvement on the current 586+ machines as it is the
+ * IO statements which will limit throughput.
+ */
+#ifdef __i386__
+#define BYTE_OUT(reg) \
+ " movb " #reg ",%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " addl $2,%%edx\n" \
+ " movb $0x0e,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " movb $0x0c,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " subl $2,%%edx\n"
+
+static inline int ppa_byte_out(unsigned short base, char *buffer, unsigned int len)
+{
+ /*
+ * %eax scratch
+ * %ebx Data to transfer
+ * %ecx Counter (Don't touch!!)
+ * %edx Port
+ * %esi Source buffer (mem pointer)
+ *
+ * In case you are wondering what the last line of the asm does...
+ * <output allocation> : <input allocation> : <trashed registers>
+ */
+ asm("shr $2,%%ecx\n" \
+ " jz .no_more_bulk_bo\n" \
+ " .align 4\n" \
+ ".loop_bulk_bo:\n" \
+ " movl (%%esi),%%ebx\n" \
+ BYTE_OUT(%%bl) \
+ BYTE_OUT(%%bh) \
+ " rorl $16,%%ebx\n" \
+ BYTE_OUT(%%bl) \
+ BYTE_OUT(%%bh) \
+ " addl $4,%%esi\n" \
+ " loop .loop_bulk_bo\n" \
+ " .align 4\n" \
+ ".no_more_bulk_bo:" \
+ : "=S"(buffer): "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+
+ asm("andl $3,%%ecx\n" \
+ " jz .no_more_loose_bo\n" \
+ " .align 4\n" \
+ ".loop_loose_bo:\n" \
+ BYTE_OUT((%%esi)) \
+ " incl %%esi\n" \
+ " loop .loop_loose_bo\n" \
+ ".no_more_loose_bo:\n" \
+ : /* no output */ : "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+ return 1; /* All went well - we hope! */
+}
+
+#define BYTE_IN(reg) \
+ " inb (%%dx),%%al\n" \
+ " movb %%al," #reg "\n" \
+ " addl $2,%%edx\n" \
+ " movb $0x27,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " movb $0x25,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " subl $2,%%edx\n"
+
+static inline int ppa_byte_in(unsigned short base, char *buffer, int len)
+{
+ /*
+ * %eax scratch
+ * %ebx Data to transfer
+ * %ecx Counter (Don't touch!!)
+ * %edx Port
+ * %esi Source buffer (mem pointer)
+ *
+ * In case you are wondering what the last line of the asm does...
+ * <output allocation> : <input allocation> : <trashed registers>
+ */
+ asm("shr $2,%%ecx\n" \
+ " jz .no_more_bulk_bi\n" \
+ " .align 4\n" \
+ ".loop_bulk_bi:\n" \
+ BYTE_IN(%%bl) \
+ BYTE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ BYTE_IN(%%bl) \
+ BYTE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ " movl %%ebx,(%%esi)\n" \
+ " addl $4,%%esi\n" \
+ " loop .loop_bulk_bi\n" \
+ " .align 4\n" \
+ ".no_more_bulk_bi:" \
+ : "=S"(buffer): "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+
+ asm("andl $3,%%ecx\n" \
+ " jz .no_more_loose_bi\n" \
+ " .align 4\n" \
+ ".loop_loose_bi:\n" \
+ BYTE_IN((%%esi)) \
+ " incl %%esi\n" \
+ " loop .loop_loose_bi\n" \
+ ".no_more_loose_bi:\n" \
+ : /* no output */ : "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+ return 1; /* All went well - we hope! */
+}
+
+#define NIBBLE_IN(reg) \
+ " incl %%edx\n" \
+ " movb $0x04,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " decl %%edx\n" \
+ " inb (%%dx),%%al\n" \
+ " andb $0xf0,%%al\n" \
+ " movb %%al," #reg "\n" \
+ " incl %%edx\n" \
+ " movb $0x06,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " decl %%edx\n" \
+ " inb (%%dx),%%al\n" \
+ " shrb $4,%%al\n" \
+ " orb %%al," #reg "\n"
+
+static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len)
+{
+ /*
+ * %eax scratch
+ * %ebx Data to transfer
+ * %ecx Counter (Don't touch!!)
+ * %edx Port
+ * %esi Source buffer (mem pointer)
+ *
+ * In case you are wondering what the last line of the asm does...
+ * <output allocation> : <input allocation> : <trashed registers>
+ */
+ asm("shr $2,%%ecx\n" \
+ " jz .no_more_bulk_ni\n" \
+ " .align 4\n" \
+ ".loop_bulk_ni:\n" \
+ NIBBLE_IN(%%bl) \
+ NIBBLE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ NIBBLE_IN(%%bl) \
+ NIBBLE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ " movl %%ebx,(%%esi)\n" \
+ " addl $4,%%esi\n" \
+ " loop .loop_bulk_ni\n" \
+ " .align 4\n" \
+ ".no_more_bulk_ni:" \
+ : "=S"(buffer): "c"(len), "d"(str_p), "S"(buffer):"eax", "ebx", "ecx");
+
+ asm("andl $3,%%ecx\n" \
+ " jz .no_more_loose_ni\n" \
+ " .align 4\n" \
+ ".loop_loose_ni:\n" \
+ NIBBLE_IN((%%esi)) \
+ " incl %%esi\n" \
+ " loop .loop_loose_ni\n" \
+ ".no_more_loose_ni:\n" \
+ : /* no output */ : "c"(len), "d"(str_p), "S"(buffer):"eax", "ebx", "ecx");
+ return 1; /* All went well - we hope! */
+}
+#else /* Old style C routines */
+
+static inline int ppa_byte_out(unsigned short base, const char *buffer, int len)
+{
+ unsigned short ctr_p = base + 2;
+ int i;
+
+ for (i = len; i; i--) {
+ outb(*buffer++, base);
+ outb(0xe, ctr_p);
+ outb(0xc, ctr_p);
+ }
+ return 1; /* All went well - we hope! */
+}
+
+static inline int ppa_byte_in(unsigned short base, char *buffer, int len)
+{
+ unsigned short ctr_p = base + 2;
+ int i;
+
+ for (i = len; i; i--) {
+ *buffer++ = inb(base);
+ outb(0x27, ctr_p);
+ outb(0x25, ctr_p);
+ }
+ return 1; /* All went well - we hope! */
+}
+
+static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len)
+{
+ unsigned short ctr_p = str_p + 1;
+ unsigned char h, l;
+ int i;
+
+ for (i = len; i; i--) {
+ outb(0x4, ctr_p);
+ h = inb(str_p);
+ outb(0x6, ctr_p);
+ l = inb(str_p);
+ *buffer++ = (h & 0xf0) | ((l & 0xf0) >> 4);
+ }
+ return 1; /* All went well - we hope! */
+ }
+ #endif
+
+static inline int ppa_epp_out(unsigned short epp_p, unsigned short str_p, const char *buffer, int len)
+{
+ int i;
+ for (i = len; i; i--) {
+ outb(*buffer++, epp_p);
+#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC
+ if (inb(str_p) & 0x01)
+ return 0;
+ #endif
+ }
+ return 1;
+ }
+
+static int ppa_out(int host_no, char *buffer, int len)
+{
+ int r;
+ unsigned short ppb = PPA_BASE(host_no);
+
+ r = ppa_wait(host_no);
+
+ if ((r & 0x50) != 0x40) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
+ }
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_NIBBLE:
+ case PPA_PS2:
+ /* 8 bit output, with a loop */
+ r = ppa_byte_out(ppb, buffer, len);
+ break;
+
+ case PPA_EPP_32:
+ case PPA_EPP_16:
+ case PPA_EPP_8:
+ epp_reset(ppb);
+ w_ctr(ppb, 0x4);
+#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC
+ r = ppa_epp_out(ppb + 4, ppb + 1, buffer, len);
+#else
+ if (!(((long) buffer | len) & 0x03))
+ outsl(ppb + 4, buffer, len >> 2);
+ else
+ outsb(ppb + 4, buffer, len);
+ w_ctr(ppb, 0xc);
+ r = !(r_str(ppb) & 0x01);
+#endif
+ w_ctr(ppb, 0xc);
+ ecp_sync(ppb);
+ break;
+
+ default:
+ printk("PPA: bug in ppa_out()\n");
+ r = 0;
+ }
+ return r;
+}
+
+static inline int ppa_epp_in(int epp_p, int str_p, char *buffer, int len)
+{
+ int i;
+ for (i = len; i; i--) {
+ *buffer++ = inb(epp_p);
+#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC
+ if (inb(str_p) & 0x01)
+ return 0;
+#endif
+ }
+ return 1;
+ }
+
+static int ppa_in(int host_no, char *buffer, int len)
+{
+ int r;
+ unsigned short ppb = PPA_BASE(host_no);
+
+ r = ppa_wait(host_no);
+
+ if ((r & 0x50) != 0x50) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
+ }
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_NIBBLE:
+ /* 4 bit input, with a loop */
+ r = ppa_nibble_in(ppb + 1, buffer, len);
+ w_ctr(ppb, 0xc);
+ break;
+
+ case PPA_PS2:
+ /* 8 bit input, with a loop */
+ w_ctr(ppb, 0x25);
+ r = ppa_byte_in(ppb, buffer, len);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
+ break;
+
+ case PPA_EPP_32:
+ case PPA_EPP_16:
+ case PPA_EPP_8:
+ epp_reset(ppb);
+ w_ctr(ppb, 0x24);
+#ifdef CONFIG_SCSI_PPA_HAVE_PEDANTIC
+ r = ppa_epp_in(ppb + 4, ppb + 1, buffer, len);
+ #else
+ if (!(((long) buffer | len) & 0x03))
+ insl(ppb + 4, buffer, len >> 2);
+ else
+ insb(ppb + 4, buffer, len);
+ w_ctr(ppb, 0x2c);
+ r = !(r_str(ppb) & 0x01);
+#endif
+ w_ctr(ppb, 0x2c);
+ ecp_sync(ppb);
+ break;
+
+ default:
+ printk("PPA: bug in ppa_ins()\n");
+ r = 0;
+ break;
+ }
+ return r;
+}
+
+/* end of ppa_io.h */
+static inline void ppa_d_pulse(unsigned short ppb, unsigned char b)
+{
+ w_dtr(ppb, b);
+ w_ctr(ppb, 0xc);
+ w_ctr(ppb, 0xe);
+ w_ctr(ppb, 0xc);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
+}
+
+static void ppa_disconnect(int host_no)
+{
+ unsigned short ppb = PPA_BASE(host_no);
+
+ ppa_d_pulse(ppb, 0);
+ ppa_d_pulse(ppb, 0x3c);
+ ppa_d_pulse(ppb, 0x20);
+ ppa_d_pulse(ppb, 0xf);
+}
+
+static inline void ppa_c_pulse(unsigned short ppb, unsigned char b)
+{
+ w_dtr(ppb, b);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0x6);
+ w_ctr(ppb, 0x4);
+ w_ctr(ppb, 0xc);
+}
+
+static inline void ppa_connect(int host_no, int flag)
+{
+ unsigned short ppb = PPA_BASE(host_no);
+
+ ppa_c_pulse(ppb, 0);
+ ppa_c_pulse(ppb, 0x3c);
+ ppa_c_pulse(ppb, 0x20);
+ if ((flag == CONNECT_EPP_MAYBE) &&
+ IN_EPP_MODE(ppa_hosts[host_no].mode))
+ ppa_c_pulse(ppb, 0xcf);
+ else
+ ppa_c_pulse(ppb, 0x8f);
+}
+
+static int ppa_select(int host_no, int target)
+{
+ int k;
+ unsigned short ppb = PPA_BASE(host_no);
+
+ /*
+ * Bit 6 (0x40) is the device selected bit.
+ * First we must wait till the current device goes off line...
+ */
+ k = PPA_SELECT_TMO;
+ do {
+ k--;
+ } while ((r_str(ppb) & 0x40) && (k));
+ if (!k)
+ return 0;
+
+ w_dtr(ppb, (1 << target));
+ w_ctr(ppb, 0xe);
+ w_ctr(ppb, 0xc);
+ w_dtr(ppb, 0x80); /* This is NOT the initator */
+ w_ctr(ppb, 0x8);
+
+ k = PPA_SELECT_TMO;
+ do {
+ k--;
+ }
+ while (!(r_str(ppb) & 0x40) && (k));
+ if (!k)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * This is based on a trace of what the Iomega DOS 'guest' driver does.
+ * I've tried several different kinds of parallel ports with guest and
+ * coded this to react in the same ways that it does.
+ *
+ * The return value from this function is just a hint about where the
+ * handshaking failed.
+ *
+ */
+static int ppa_init(int host_no)
+{
+ int retv;
+ unsigned short ppb = PPA_BASE(host_no);
+
+ ppa_disconnect(host_no);
+ ppa_connect(host_no, CONNECT_NORMAL);
+
+ retv = 2; /* Failed */
+
+ w_ctr(ppb, 0xe);
+ if ((r_str(ppb) & 0x08) == 0x08)
+ retv--;
+
+ w_ctr(ppb, 0xc);
+ if ((r_str(ppb) & 0x08) == 0x00)
+ retv--;
+
+ /* This is a SCSI BUS reset signal */
+ if (!retv) {
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000); /* Allow devices to settle down */
+ }
+ ppa_disconnect(host_no);
+ udelay(1000); /* Another delay to allow devices to settle */
+
+ if (!retv)
+ retv = device_check(host_no);
+
+ return retv;
+}
+
+static inline int ppa_send_command(Scsi_Cmnd * cmd)
+{
+ int host_no = cmd->host->unique_id;
+ int k;
+
+ w_ctr(PPA_BASE(host_no), 0x0c);
+
+ for (k = 0; k < cmd->cmd_len; k++)
+ if (!ppa_out(host_no, &cmd->cmnd[k], 1))
+ return 0;
+ return 1;
+}
+
+/*
+ * The bulk flag enables some optimisations in the data transfer loops,
+ * it should be true for any command that transfers data in integral
+ * numbers of sectors.
+ *
+ * The driver appears to remain stable if we speed up the parallel port
+ * i/o in this function, but not elsewhere.
+ */
+static int ppa_completion(Scsi_Cmnd * cmd)
+{
+ /* Return codes:
+ * -1 Error
+ * 0 Told to schedule
+ * 1 Finished data transfer
+ */
+ int host_no = cmd->host->unique_id;
+ unsigned short ppb = PPA_BASE(host_no);
+ unsigned long start_jiffies = jiffies;
+
+ unsigned char r, v;
+ int fast, bulk, status;
+
+ v = cmd->cmnd[0];
+ bulk = ((v == READ_6) ||
+ (v == READ_10) ||
+ (v == WRITE_6) ||
+ (v == WRITE_10));
+
+ /*
+ * We only get here if the drive is ready to comunicate,
+ * hence no need for a full ppa_wait.
+ */
+ r = (r_str(ppb) & 0xf0);
+
+ while (r != (unsigned char) 0xf0) {
+ /*
+ * If we have been running for more than a full timer tick
+ * then take a rest.
+ */
+ if (jiffies > start_jiffies + 1)
+ return 0;
+
+ if (((r & 0xc0) != 0xc0) || (cmd->SCp.this_residual <= 0)) {
+ ppa_fail(host_no, DID_ERROR);
+ return -1; /* ERROR_RETURN */
+ }
+ /* determine if we should use burst I/O */ fast = (bulk && (cmd->SCp.this_residual >= PPA_BURST_SIZE))
+ ? PPA_BURST_SIZE : 1;
+
+ if (r == (unsigned char) 0xc0)
+ status = ppa_out(host_no, cmd->SCp.ptr, fast);
+ else
+ status = ppa_in(host_no, cmd->SCp.ptr, fast);
+
+ cmd->SCp.ptr += fast;
+ cmd->SCp.this_residual -= fast;
+
+ if (!status) {
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return -1; /* ERROR_RETURN */
+ }
+ if (cmd->SCp.buffer && !cmd->SCp.this_residual) {
+ /* if scatter/gather, advance to the next segment */
+ if (cmd->SCp.buffers_residual--) {
+ cmd->SCp.buffer++;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = cmd->SCp.buffer->address;
+ }
+ }
+ /* Now check to see if the drive is ready to comunicate */
+ r = (r_str(ppb) & 0xf0);
+ /* If not, drop back down to the scheduler and wait a timer tick */
+ if (!(r & 0x80))
+ return 0;
+ }
+ return 1; /* FINISH_RETURN */
+}
+
+/*
+ * Since the PPA itself doesn't generate interrupts, we use
+ * the scheduler's task queue to generate a stream of call-backs and
+ * complete the request when the drive is ready.
+ */
+static void ppa_interrupt(void *data)
+{
+ ppa_struct *tmp = (ppa_struct *) data;
+ Scsi_Cmnd *cmd = tmp->cur_cmd;
+
+ if (!cmd) {
+ printk("PPA: bug in ppa_interrupt\n");
+ return;
+ }
+ if (ppa_engine(tmp, cmd)) {
+ tmp->ppa_tq.data = (void *) tmp;
+ tmp->ppa_tq.sync = 0;
+ queue_task(&tmp->ppa_tq, &tq_timer);
+ return;
+ }
+ /* Command must of completed hence it is safe to let go... */
+#if PPA_DEBUG > 0
+ switch ((cmd->result >> 16) & 0xff) {
+ case DID_OK:
+ break;
+ case DID_NO_CONNECT:
+ printk("ppa: no device at SCSI ID %i\n", cmd->target);
+ break;
+ case DID_BUS_BUSY:
+ printk("ppa: BUS BUSY - EPP timeout detected\n");
+ break;
+ case DID_TIME_OUT:
+ printk("ppa: unknown timeout\n");
+ break;
+ case DID_ABORT:
+ printk("ppa: told to abort\n");
+ break;
+ case DID_PARITY:
+ printk("ppa: parity error (???)\n");
+ break;
+ case DID_ERROR:
+ printk("ppa: internal driver error\n");
+ break;
+ case DID_RESET:
+ printk("ppa: told to reset device\n");
+ break;
+ case DID_BAD_INTR:
+ printk("ppa: bad interrupt (???)\n");
+ break;
+ default:
+ printk("ppa: bad return code (%02x)\n", (cmd->result >> 16) & 0xff);
+ }
+ #endif
+
+ if (cmd->SCp.phase > 1)
+ ppa_disconnect(cmd->host->unique_id);
+
+ tmp->cur_cmd = 0;
+ cmd->scsi_done(cmd);
+ return;
+}
+
+static int ppa_engine(ppa_struct * tmp, Scsi_Cmnd * cmd)
+{
+ int host_no = cmd->host->unique_id;
+ unsigned short ppb = PPA_BASE(host_no);
+ unsigned char l = 0, h = 0;
+ int retv;
+
+ /* First check for any errors that may have occurred
+ * Here we check for internal errors
+ */
+ if (tmp->failed)
+ return 0;
+
+ switch (cmd->SCp.phase) {
+ case 0: /* Phase 0 - Waiting for parport */
+ if ((jiffies - tmp->jstart) > HZ) {
+ /*
+ * We waited more than a second
+ * for parport to call us
+ */
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return 0;
+ }
+ return 1; /* wait until ppa_wakeup claims parport */
+ case 1: /* Phase 1 - Connected */
+ { /* Perform a sanity check for cable unplugged */
+ int retv = 2; /* Failed */
+
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+
+ w_ctr(ppb, 0xe);
+ if ((r_str(ppb) & 0x08) == 0x08)
+ retv--;
+
+ w_ctr(ppb, 0xc);
+ if ((r_str(ppb) & 0x08) == 0x00)
+ retv--;
+
+ if (retv)
+ if ((jiffies - tmp->jstart) > (1 * HZ)) {
+ printk("ppa: Parallel port cable is unplugged!!\n");
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return 0;
+ } else {
+ ppa_disconnect(host_no);
+ return 1; /* Try again in a jiffy */
+ }
+ cmd->SCp.phase++;
+ }
+
+ case 2: /* Phase 2 - We are now talking to the scsi bus */
+ if (!ppa_select(host_no, cmd->target)) {
+ ppa_fail(host_no, DID_NO_CONNECT);
+ return 0;
+ }
+ cmd->SCp.phase++;
+
+ case 3: /* Phase 3 - Ready to accept a command */
+ w_ctr(ppb, 0x0c);
+ if (!(r_str(ppb) & 0x80))
+ return 1;
+
+ if (!ppa_send_command(cmd))
+ return 0;
+ cmd->SCp.phase++;
+
+ case 4: /* Phase 4 - Setup scatter/gather buffers */
+ if (cmd->use_sg) {
+ /* if many buffers are available, start filling the first */
+ cmd->SCp.buffer = (struct scatterlist *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = cmd->SCp.buffer->address;
+ } else {
+ /* else fill the only available buffer */
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ cmd->SCp.ptr = cmd->request_buffer;
+ }
+ cmd->SCp.buffers_residual = cmd->use_sg;
+ cmd->SCp.phase++;
+
+ case 5: /* Phase 5 - Data transfer stage */
+ w_ctr(ppb, 0x0c);
+ if (!(r_str(ppb) & 0x80))
+ return 1;
+
+ retv = ppa_completion(cmd);
+ if (retv == -1)
+ return 0;
+ if (retv == 0)
+ return 1;
+ cmd->SCp.phase++;
+
+ case 6: /* Phase 6 - Read status/message */
+ cmd->result = DID_OK << 16;
+ /* Check for data overrun */
+ if (ppa_wait(host_no) != (unsigned char) 0xf0) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
+ }
+ if (ppa_in(host_no, &l, 1)) { /* read status byte */
+ /* Check for optional message byte */
+ if (ppa_wait(host_no) == (unsigned char) 0xf0)
+ ppa_in(host_no, &h, 1);
+ cmd->result = (DID_OK << 16) + (h << 8) + (l & STATUS_MASK);
+ }
+ return 0; /* Finished */
+ break;
+
+ default:
+ printk("ppa: Invalid scsi phase\n");
+ }
+ return 0;
+}
+
+int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ int host_no = cmd->host->unique_id;
+
+ if (ppa_hosts[host_no].cur_cmd) {
+ printk("PPA: bug in ppa_queuecommand\n");
+ return 0;
+ }
+ ppa_hosts[host_no].failed = 0;
+ ppa_hosts[host_no].jstart = jiffies;
+ ppa_hosts[host_no].cur_cmd = cmd;
+ cmd->scsi_done = done;
+ cmd->result = DID_ERROR << 16; /* default return code */
+ cmd->SCp.phase = 0; /* bus free */
+
+ ppa_pb_claim(host_no);
+
+ ppa_hosts[host_no].ppa_tq.data = ppa_hosts + host_no;
+ ppa_hosts[host_no].ppa_tq.sync = 0;
+ queue_task(&ppa_hosts[host_no].ppa_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+
+ return 0;
+}
+
+/*
+ * Apparently the disk->capacity attribute is off by 1 sector
+ * for all disk drives. We add the one here, but it should really
+ * be done in sd.c. Even if it gets fixed there, this will still
+ * work.
+ */
+int ppa_biosparam(Disk * disk, kdev_t dev, int ip[])
+{
+ ip[0] = 0x40;
+ ip[1] = 0x20;
+ ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+ if (ip[2] > 1024) {
+ ip[0] = 0xff;
+ ip[1] = 0x3f;
+ ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+ if (ip[2] > 1023)
+ ip[2] = 1023;
+ }
+ return 0;
+}
+
+int ppa_abort(Scsi_Cmnd * cmd)
+{
+ /*
+ * There is no method for aborting commands since Iomega
+ * have tied the SCSI_MESSAGE line high in the interface
+ */
+
+ switch (cmd->SCp.phase) {
+ case 0: /* Do not have access to parport */
+ case 1: /* Have not connected to interface */
+ cmd->result = DID_ABORT;
+ cmd->done(cmd);
+ return SCSI_ABORT_SUCCESS;
+ break;
+ default: /* SCSI command sent, can not abort */
+ return SCSI_ABORT_BUSY;
+ break;
+ }
+}
+
+int ppa_reset(Scsi_Cmnd * cmd, unsigned int x)
+{
+ int host_no = cmd->host->unique_id;
+ int ppb = PPA_BASE(host_no);
+
+ /*
+ * PHASE1:
+ * Bring the interface crashing down on whatever is running
+ * hopefully this will kill the request.
+ * Bring back up the interface, reset the drive (and anything
+ * attached for that manner)
+ */
+ if (cmd)
+ if (cmd->SCp.phase)
+ ppa_disconnect(cmd->host->unique_id);
+
+ ppa_connect(host_no, CONNECT_NORMAL);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x8);
+ udelay(30);
+ w_ctr(ppb, 0xc);
+ udelay(1000); /* delay for devices to settle down */
+ ppa_disconnect(host_no);
+ udelay(1000); /* Additional delay to allow devices to settle down */
+
+ /*
+ * PHASE2:
+ * Sanity check for the sake of mid-level driver
+ */
+ if (!cmd) {
+ printk("ppa bus reset called for invalid command.\n");
+ return SCSI_RESET_NOT_RUNNING;
+ }
+ /*
+ * PHASE3:
+ * Flag the current command as having died due to reset
+ */
+ ppa_connect(host_no, CONNECT_NORMAL);
+ ppa_fail(host_no, DID_RESET);
+
+ /* Since the command was already on the timer queue ppa_interrupt
+ * will be called shortly.
+ */
+ return SCSI_RESET_PENDING;
+}
+
+static int device_check(int host_no)
+{
+ /* This routine looks for a device and then attempts to use EPP
+ to send a command. If all goes as planned then EPP is available. */
+
+ static char cmd[6] =
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ int loop, old_mode, status, k, ppb = PPA_BASE(host_no);
+ unsigned char l;
+
+ old_mode = ppa_hosts[host_no].mode;
+ for (loop = 0; loop < 8; loop++) {
+ /* Attempt to use EPP for Test Unit Ready */
+ if ((ppb & 0x0007) == 0x0000)
+ ppa_hosts[host_no].mode = PPA_EPP_32;
+
+ second_pass:
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+ /* Select SCSI device */
+ if (!ppa_select(host_no, loop)) {
+ ppa_disconnect(host_no);
+ continue;
+ }
+ printk("ppa: Found device at ID %i, Attempting to use %s\n", loop,
+ PPA_MODE_STRING[ppa_hosts[host_no].mode]);
+
+ /* Send SCSI command */
+ status = 1;
+ w_ctr(ppb, 0x0c);
+ for (l = 0; (l < 6) && (status); l++)
+ status = ppa_out(host_no, cmd, 1);
+
+ if (!status) {
+ ppa_disconnect(host_no);
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000);
+ ppa_disconnect(host_no);
+ udelay(1000);
+ if (ppa_hosts[host_no].mode == PPA_EPP_32) {
+ ppa_hosts[host_no].mode = old_mode;
+ goto second_pass;
+ }
+ printk("ppa: Unable to establish communication, aborting driver load.\n");
+ return 1;
+ }
+ w_ctr(ppb, 0x0c);
+ k = 1000000; /* 1 Second */
+ do {
+ l = r_str(ppb);
+ k--;
+ udelay(1);
+ } while (!(l & 0x80) && (k));
+
+ l &= 0xf0;
+
+ if (l != 0xf0) {
+ ppa_disconnect(host_no);
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000);
+ ppa_disconnect(host_no);
+ udelay(1000);
+ if (ppa_hosts[host_no].mode == PPA_EPP_32) {
+ ppa_hosts[host_no].mode = old_mode;
+ goto second_pass;
+ }
+ printk("ppa: Unable to establish communication, aborting driver load.\n");
+ return 1;
+ }
+ ppa_disconnect(host_no);
+ printk("ppa: Communication established with ID %i using %s\n", loop,
+ PPA_MODE_STRING[ppa_hosts[host_no].mode]);
+ return 0;
+ }
+ printk("ppa: No devices found, aborting driver load.\n");
+ return 1;
+}
+
+#define PPA_ID "ppa: "
+
+int port_probe(unsigned short port)
+{
+ int retv = 0;
+ unsigned char a, b, c;
+ unsigned int i, j;
+
+
+ printk(PPA_ID "Probing port %04x\n", port);
+
+/* ##### ###### ######
+ * # # # # # #
+ * # # # # #
+ * ##### ###### ######
+ * # # #
+ * # # # #
+ * ##### # #
+ */
+
+ outb(0x0c, port + 0x402);
+ outb(0x0c, port + 0x002);
+ outb(0x55, port);
+ a = inb(port);
+ if (a != 0x55)
+ return retv;
+ printk(PPA_ID " SPP port present\n");
+
+ retv += PPA_PROBE_SPP;
+
+/* ####### ##### ######
+ * # # # # #
+ * # # # #
+ * ##### # ######
+ * # # #
+ * # # # #
+ * ####### ##### #
+ */
+
+ for (i = 1024; i > 0; i--) { /* clear at most 1k of data from FIFO */
+ a = inb(port + 0x402);
+ if ((a & 0x03) == 0x03)
+ goto no_ecp;
+ if (a & 0x01)
+ break;
+ inb(port + 0x400); /* Remove byte from FIFO */
+ }
+
+ if (i <= 0)
+ goto no_ecp;
+
+ b = a ^ 3;
+ outb(b, port + 0x402);
+ c = inb(port + 0x402);
+
+ if (a == c) {
+ outb(0xc0, port + 0x402); /* FIFO test */
+ j = 0;
+ while (!(inb(port + 0x402) & 0x01) && (j < 1024)) {
+ inb(port + 0x400);
+ j++;
+ }
+ if (j >= 1024)
+ goto no_ecp;
+ i = 0;
+ j = 0;
+ while (!(inb(port + 0x402) & 0x02) && (j < 1024)) {
+ outb(0x00, port + 0x400);
+ i++;
+ j++;
+ }
+ if (j >= 1024)
+ goto no_ecp;
+ j = 0;
+ while (!(inb(port + 0x402) & 0x01) && (j < 1024)) {
+ inb(port + 0x400);
+ j++;
+ }
+ if (j >= 1024)
+ goto no_ecp;
+ printk(PPA_ID " ECP with a %i byte FIFO present\n", i);
+
+ retv += PPA_PROBE_ECR;
+ }
+/* ###### ##### #####
+ * # # # # # #
+ * # # # #
+ * ###### ##### #####
+ * # # #
+ * # # # #
+ * # ##### #######
+ */
+
+ no_ecp:
+ if (retv & PPA_PROBE_ECR)
+ outb(0x20, port + 0x402);
+
+ outb(0x55, port);
+ outb(0x0c, port + 2);
+ a = inb(port);
+ outb(0x55, port);
+ outb(0x2c, port + 2);
+ b = inb(port);
+ if (a != b) {
+ printk(PPA_ID " PS/2 bidirectional port present\n");
+ retv += PPA_PROBE_PS2;
+ }
+/* ####### ###### ######
+ * # # # # #
+ * # # # # #
+ * ##### ###### ######
+ * # # #
+ * # # #
+ * ####### # #
+ */
+
+ if (port & 0x007) {
+ printk(PPA_ID " EPP not supported at this address\n");
+ return retv;
+ }
+ if (retv & PPA_PROBE_ECR) {
+ for (i = 0x00; i < 0x80; i += 0x20) {
+ outb(i, port + 0x402);
+
+ a = inb(port + 1);
+ outb(a, port + 1);
+ outb(a & 0xfe, port + 1);
+ a = inb(port + 1);
+ if (!(a & 0x01)) {
+ printk(PPA_ID " Failed Intel bug check. (Phony EPP in ECP)\n");
+ return retv;
+ }
+ }
+ printk(PPA_ID " Passed Intel bug check.\n");
+ outb(0x80, port + 0x402);
+ }
+ a = inb(port + 1);
+ outb(a, port + 1);
+ outb(a & 0xfe, port + 1);
+ a = inb(port + 1);
+
+ if (a & 0x01) {
+ outb(0x0c, port + 0x402);
+ outb(0x0c, port + 0x002);
+ return retv;
+ }
+
+ outb(0x04, port + 2);
+ inb(port + 4);
+ a = inb(port + 1);
+ outb(a, port + 1);
+ outb(a & 0xfe, port + 1);
+
+ if (a & 0x01) {
+ printk(PPA_ID " EPP 1.9 with hardware direction protocol\n");
+ retv += PPA_PROBE_EPP19;
+ } else {
+ /* The EPP timeout bit was not set, this could either be:
+ * EPP 1.7
+ * EPP 1.9 with software direction
+ */
+ outb(0x24, port + 2);
+ inb(port + 4);
+ a = inb(port + 1);
+ outb(a, port + 1);
+ outb(a & 0xfe, port + 1);
+ if (a & 0x01) {
+ printk(PPA_ID " EPP 1.9 with software direction protocol\n");
+ retv += PPA_PROBE_EPP19;
+ } else {
+ printk(PPA_ID " EPP 1.7\n");
+ retv += PPA_PROBE_EPP17;
+ }
+ }
+
+ outb(0x0c, port + 0x402);
+ outb(0x0c, port + 0x002);
+ return retv;
+}
diff --git a/linux/src/drivers/scsi/ppa.h b/linux/src/drivers/scsi/ppa.h
new file mode 100644
index 00000000..1497c208
--- /dev/null
+++ b/linux/src/drivers/scsi/ppa.h
@@ -0,0 +1,176 @@
+/* Driver for the PPA3 parallel port SCSI HBA embedded in
+ * the Iomega ZIP drive
+ *
+ * (c) 1996 Grant R. Guenther grant@torque.net
+ * David Campbell campbell@torque.net
+ *
+ * All comments to David.
+ */
+
+#include <linux/config.h> /* CONFIG_SCSI_PPA_HAVE_PEDANTIC */
+#ifndef _PPA_H
+#define _PPA_H
+
+#define PPA_VERSION "1.42"
+
+#if 0
+/* Use the following to enable certain chipset support
+ * Default is PEDANTIC = 3
+ */
+#ifndef CONFIG_SCSI_PPA_HAVE_PEDANTIC
+#define CONFIG_SCSI_PPA_HAVE_PEDANTIC 3
+#endif
+#endif
+
+/*
+ * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
+ * to support EPP and scatter-gather. [0.26-athena]
+ *
+ * additional hacks by David Campbell
+ * in response to this driver "mis-behaving" on his machine.
+ * Fixed EPP to handle "software" changing of EPP port data direction.
+ * Chased down EPP timeouts
+ * Made this driver "kernel version friendly" [0.28-athena]
+ *
+ * [ Stuff removed ]
+ *
+ * Compiled against 2.1.53.
+ * Rebuilt ppa_abort() function, should handle unplugged cable.
+ * [1.35s]
+ *
+ * PPA now auto probes for EPP on base address which are aligned on
+ * 8 byte boundaries (0x278 & 0x378) using the attached devices.
+ * This hopefully avoids the nasty problem of trying to detect EPP.
+ * Tested on 2.1.53 [1.36]
+ *
+ * The id_probe utility no longer performs read/write tests.
+ * Additional code included for checking the Intel ECP bug
+ * (Bit 0 of STR stuck low which fools the EPP detection routine)
+ * [1.37]
+ *
+ * Oops! Got the bit sign mixed up for the Intel bug check.
+ * Found that an additional delay is required during SCSI resets
+ * to allow devices to settle down.
+ * [1.38]
+ *
+ * Fixed all problems in the parport sharing scheme. Now ppa can be safe
+ * used with lp or other parport devices on the same parallel port.
+ * 1997 by Andrea Arcangeli
+ * [1.39]
+ *
+ * Little fix in ppa engine to ensure that ppa don' t release parport
+ * or disconnect in wrong cases.
+ * 1997 by Andrea Arcangeli
+ * [1.40]
+ *
+ * Corrected ppa.h for 2.1.x kernels (>=2.1.85)
+ * Modified "Nat Semi Kludge" for extended chipsets
+ * [1.41]
+ *
+ * Fixed id_probe for EPP 1.9 chipsets (misdetected as EPP 1.7)
+ * [1.42]
+ */
+/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
+
+#ifdef PPA_CODE
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/tqueue.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/blk.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include "sd.h"
+#include "hosts.h"
+/* batteries not included :-) */
+
+/*
+ * modes in which the driver can operate
+ */
+#define PPA_AUTODETECT 0 /* Autodetect mode */
+#define PPA_NIBBLE 1 /* work in standard 4 bit mode */
+#define PPA_PS2 2 /* PS/2 byte mode */
+#define PPA_EPP_8 3 /* EPP mode, 8 bit */
+#define PPA_EPP_16 4 /* EPP mode, 16 bit */
+#define PPA_EPP_32 5 /* EPP mode, 32 bit */
+#define PPA_UNKNOWN 6 /* Just in case... */
+
+static char *PPA_MODE_STRING[] =
+{
+ "Autodetect",
+ "SPP",
+ "PS/2",
+ "EPP 8 bit",
+ "EPP 16 bit",
+ "EPP 32 bit",
+ "Unknown"};
+
+/* This is a global option */
+int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */
+
+/* other options */
+#define PPA_CAN_QUEUE 1 /* use "queueing" interface */
+#define PPA_BURST_SIZE 512 /* data burst size */
+#define PPA_SELECT_TMO 5000 /* how long to wait for target ? */
+#define PPA_SPIN_TMO 50000 /* ppa_wait loop limiter */
+#define PPA_DEBUG 0 /* debuging option */
+#define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32)
+
+/* args to ppa_connect */
+#define CONNECT_EPP_MAYBE 1
+#define CONNECT_NORMAL 0
+
+#define r_dtr(x) (unsigned char)inb((x))
+#define r_str(x) (unsigned char)inb((x)+1)
+#define r_ctr(x) (unsigned char)inb((x)+2)
+#define r_epp(x) (unsigned char)inb((x)+4)
+#define r_fifo(x) (unsigned char)inb((x)+0x400)
+#define r_ecr(x) (unsigned char)inb((x)+0x402)
+
+#define w_dtr(x,y) outb(y, (x))
+#define w_str(x,y) outb(y, (x)+1)
+#define w_ctr(x,y) outb(y, (x)+2)
+#define w_epp(x,y) outb(y, (x)+4)
+#define w_fifo(x,y) outb(y, (x)+0x400)
+#define w_ecr(x,y) outb(y, (x)+0x402)
+
+static int ppa_engine(ppa_struct *, Scsi_Cmnd *);
+static int ppa_in(int, char *, int);
+static int ppa_init(int);
+static void ppa_interrupt(void *);
+static int ppa_out(int, char *, int);
+
+struct proc_dir_entry proc_scsi_ppa =
+{PROC_SCSI_PPA, 3, "ppa", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+#else
+extern struct proc_dir_entry proc_scsi_ppa;
+#endif
+
+int ppa_detect(Scsi_Host_Template *);
+const char *ppa_info(struct Scsi_Host *);
+int ppa_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
+int ppa_abort(Scsi_Cmnd *);
+int ppa_reset(Scsi_Cmnd *, unsigned int);
+int ppa_proc_info(char *, char **, off_t, int, int, int);
+int ppa_biosparam(Disk *, kdev_t, int *);
+
+#define PPA { proc_dir: &proc_scsi_ppa, \
+ proc_info: ppa_proc_info, \
+ name: "Iomega parport ZIP drive", \
+ detect: ppa_detect, \
+ queuecommand: ppa_queuecommand, \
+ abort: ppa_abort, \
+ reset: ppa_reset, \
+ bios_param: ppa_biosparam, \
+ this_id: -1, \
+ sg_tablesize: SG_ALL, \
+ cmd_per_lun: 1, \
+ use_clustering: ENABLE_CLUSTERING \
+}
+#endif /* _PPA_H */
diff --git a/linux/src/drivers/scsi/qlogicfas.c b/linux/src/drivers/scsi/qlogicfas.c
new file mode 100644
index 00000000..b5cb9dd0
--- /dev/null
+++ b/linux/src/drivers/scsi/qlogicfas.c
@@ -0,0 +1,679 @@
+/*----------------------------------------------------------------*/
+/*
+ Qlogic linux driver - work in progress. No Warranty express or implied.
+ Use at your own risk. Support Tort Reform so you won't have to read all
+ these silly disclaimers.
+
+ Copyright 1994, Tom Zerucha.
+ zerucha@shell.portal.com
+
+ Additional Code, and much appreciated help by
+ Michael A. Griffith
+ grif@cs.ucr.edu
+
+ Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
+ help respectively, and for suffering through my foolishness during the
+ debugging process.
+
+ Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
+ (you can reference it, but it is incomplete and inaccurate in places)
+
+ Version 0.45 6/9/96 - kernel 1.2.0+
+
+ Functions as standalone, loadable, and PCMCIA driver, the latter from
+ Dave Hind's PCMCIA package.
+
+ Redistributable under terms of the GNU Public License
+
+*/
+/*----------------------------------------------------------------*/
+/* Configuration */
+
+/* Set the following to 2 to use normal interrupt (active high/totempole-
+ tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
+ drain */
+#define QL_INT_ACTIVE_HIGH 2
+
+/* Set the following to 1 to enable the use of interrupts. Note that 0 tends
+ to be more stable, but slower (or ties up the system more) */
+#define QL_USE_IRQ 1
+
+/* Set the following to max out the speed of the PIO PseudoDMA transfers,
+ again, 0 tends to be slower, but more stable. */
+#define QL_TURBO_PDMA 1
+
+/* This should be 1 to enable parity detection */
+#define QL_ENABLE_PARITY 1
+
+/* This will reset all devices when the driver is initialized (during bootup).
+ The other linux drivers don't do this, but the DOS drivers do, and after
+ using DOS or some kind of crash or lockup this will bring things back
+ without requiring a cold boot. It does take some time to recover from a
+ reset, so it is slower, and I have seen timeouts so that devices weren't
+ recognized when this was set. */
+#define QL_RESET_AT_START 0
+
+/* crystal frequency in megahertz (for offset 5 and 9)
+ Please set this for your card. Most Qlogic cards are 40 Mhz. The
+ Control Concepts ISA (not VLB) is 24 Mhz */
+#define XTALFREQ 40
+
+/**********/
+/* DANGER! modify these at your own risk */
+/* SLOWCABLE can usually be reset to zero if you have a clean setup and
+ proper termination. The rest are for synchronous transfers and other
+ advanced features if your device can transfer faster than 5Mb/sec.
+ If you are really curious, email me for a quick howto until I have
+ something official */
+/**********/
+
+/*****/
+/* config register 1 (offset 8) options */
+/* This needs to be set to 1 if your cabling is long or noisy */
+#define SLOWCABLE 1
+
+/*****/
+/* offset 0xc */
+/* This will set fast (10Mhz) synchronous timing when set to 1
+ For this to have an effect, FASTCLK must also be 1 */
+#define FASTSCSI 0
+
+/* This when set to 1 will set a faster sync transfer rate */
+#define FASTCLK 0
+/*(XTALFREQ>25?1:0)*/
+
+/*****/
+/* offset 6 */
+/* This is the sync transfer divisor, XTALFREQ/X will be the maximum
+ achievable data rate (assuming the rest of the system is capable
+ and set properly) */
+#define SYNCXFRPD 5
+/*(XTALFREQ/5)*/
+
+/*****/
+/* offset 7 */
+/* This is the count of how many synchronous transfers can take place
+ i.e. how many reqs can occur before an ack is given.
+ The maximum value for this is 15, the upper bits can modify
+ REQ/ACK assertion and deassertion during synchronous transfers
+ If this is 0, the bus will only transfer asynchronously */
+#define SYNCOFFST 0
+/* for the curious, bits 7&6 control the deassertion delay in 1/2 cycles
+ of the 40Mhz clock. If FASTCLK is 1, specifying 01 (1/2) will
+ cause the deassertion to be early by 1/2 clock. Bits 5&4 control
+ the assertion delay, also in 1/2 clocks (FASTCLK is ignored here). */
+
+/*----------------------------------------------------------------*/
+#ifdef PCMCIA
+#undef QL_INT_ACTIVE_HIGH
+#define QL_INT_ACTIVE_HIGH 0
+#define MODULE
+#endif
+
+#include <linux/module.h>
+
+#ifdef PCMCIA
+#undef MODULE
+#endif
+
+#include <linux/blk.h> /* to get disk capacity */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "sd.h"
+#include "hosts.h"
+#include "qlogicfas.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_qlogicfas = {
+ PROC_SCSI_QLOGICFAS, 6, "qlogicfas",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/*----------------------------------------------------------------*/
+/* driver state info, local to driver */
+static int qbase = 0; /* Port */
+static int qinitid; /* initiator ID */
+static int qabort; /* Flag to cause an abort */
+static int qlirq = -1; /* IRQ being used */
+static char qinfo[80]; /* description */
+static Scsi_Cmnd *qlcmd; /* current command being processed */
+
+static int qlcfg5 = ( XTALFREQ << 5 ); /* 15625/512 */
+static int qlcfg6 = SYNCXFRPD;
+static int qlcfg7 = SYNCOFFST;
+static int qlcfg8 = ( SLOWCABLE << 7 ) | ( QL_ENABLE_PARITY << 4 );
+static int qlcfg9 = ( ( XTALFREQ + 4 ) / 5 );
+static int qlcfgc = ( FASTCLK << 3 ) | ( FASTSCSI << 4 );
+
+/*----------------------------------------------------------------*/
+/* The qlogic card uses two register maps - These macros select which one */
+#define REG0 ( outb( inb( qbase + 0xd ) & 0x7f , qbase + 0xd ), outb( 4 , qbase + 0xd ))
+#define REG1 ( outb( inb( qbase + 0xd ) | 0x80 , qbase + 0xd ), outb( 0xb4 | QL_INT_ACTIVE_HIGH , qbase + 0xd ))
+
+/* following is watchdog timeout in microseconds */
+#define WATCHDOG 5000000
+
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at and as a simple profiler) */
+
+#if 0
+#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+
+/*----------------------------------------------------------------*/
+/* local functions */
+/*----------------------------------------------------------------*/
+static void ql_zap(void);
+/* error recovery - reset everything */
+void ql_zap()
+{
+int x;
+unsigned long flags;
+ save_flags( flags );
+ cli();
+ x = inb(qbase + 0xd);
+ REG0;
+ outb(3, qbase + 3); /* reset SCSI */
+ outb(2, qbase + 3); /* reset chip */
+ if (x & 0x80)
+ REG1;
+ restore_flags( flags );
+}
+
+/*----------------------------------------------------------------*/
+/* do pseudo-dma */
+static int ql_pdma(int phase, char *request, int reqlen)
+{
+int j;
+ j = 0;
+ if (phase & 1) { /* in */
+#if QL_TURBO_PDMA
+rtrc(4)
+ /* empty fifo in large chunks */
+ if( reqlen >= 128 && (inb( qbase + 8 ) & 2) ) { /* full */
+ insl( qbase + 4, request, 32 );
+ reqlen -= 128;
+ request += 128;
+ }
+ while( reqlen >= 84 && !( j & 0xc0 ) ) /* 2/3 */
+ if( (j=inb( qbase + 8 )) & 4 ) {
+ insl( qbase + 4, request, 21 );
+ reqlen -= 84;
+ request += 84;
+ }
+ if( reqlen >= 44 && (inb( qbase + 8 ) & 8) ) { /* 1/3 */
+ insl( qbase + 4, request, 11 );
+ reqlen -= 44;
+ request += 44;
+ }
+#endif
+ /* until both empty and int (or until reclen is 0) */
+rtrc(7)
+ j = 0;
+ while( reqlen && !( (j & 0x10) && (j & 0xc0) ) ) {
+ /* while bytes to receive and not empty */
+ j &= 0xc0;
+ while ( reqlen && !( (j=inb(qbase + 8)) & 0x10 ) ) {
+ *request++ = inb(qbase + 4);
+ reqlen--;
+ }
+ if( j & 0x10 )
+ j = inb(qbase+8);
+
+ }
+ }
+ else { /* out */
+#if QL_TURBO_PDMA
+rtrc(4)
+ if( reqlen >= 128 && inb( qbase + 8 ) & 0x10 ) { /* empty */
+ outsl(qbase + 4, request, 32 );
+ reqlen -= 128;
+ request += 128;
+ }
+ while( reqlen >= 84 && !( j & 0xc0 ) ) /* 1/3 */
+ if( !((j=inb( qbase + 8 )) & 8) ) {
+ outsl( qbase + 4, request, 21 );
+ reqlen -= 84;
+ request += 84;
+ }
+ if( reqlen >= 40 && !(inb( qbase + 8 ) & 4 ) ) { /* 2/3 */
+ outsl( qbase + 4, request, 10 );
+ reqlen -= 40;
+ request += 40;
+ }
+#endif
+ /* until full and int (or until reclen is 0) */
+rtrc(7)
+ j = 0;
+ while( reqlen && !( (j & 2) && (j & 0xc0) ) ) {
+ /* while bytes to send and not full */
+ while ( reqlen && !( (j=inb(qbase + 8)) & 2 ) ) {
+ outb(*request++, qbase + 4);
+ reqlen--;
+ }
+ if( j & 2 )
+ j = inb(qbase+8);
+ }
+ }
+/* maybe return reqlen */
+ return inb( qbase + 8 ) & 0xc0;
+}
+
+/*----------------------------------------------------------------*/
+/* wait for interrupt flag (polled - not real hardware interrupt) */
+static int ql_wai(void)
+{
+int i,k;
+ k = 0;
+ i = jiffies + WATCHDOG;
+ while ( i > jiffies && !qabort && !((k = inb(qbase + 4)) & 0xe0))
+ barrier();
+ if (i <= jiffies)
+ return (DID_TIME_OUT);
+ if (qabort)
+ return (qabort == 1 ? DID_ABORT : DID_RESET);
+ if (k & 0x60)
+ ql_zap();
+ if (k & 0x20)
+ return (DID_PARITY);
+ if (k & 0x40)
+ return (DID_ERROR);
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+/* initiate scsi command - queueing handler */
+static void ql_icmd(Scsi_Cmnd * cmd)
+{
+unsigned int i;
+unsigned long flags;
+
+ qabort = 0;
+
+ save_flags( flags );
+ cli();
+ REG0;
+/* clearing of interrupts and the fifo is needed */
+ inb(qbase + 5); /* clear interrupts */
+ if (inb(qbase + 5)) /* if still interrupting */
+ outb(2, qbase + 3); /* reset chip */
+ else if (inb(qbase + 7) & 0x1f)
+ outb(1, qbase + 3); /* clear fifo */
+ while (inb(qbase + 5)); /* clear ints */
+ REG1;
+ outb(1, qbase + 8); /* set for PIO pseudo DMA */
+ outb(0, qbase + 0xb); /* disable ints */
+ inb(qbase + 8); /* clear int bits */
+ REG0;
+ outb(0x40, qbase + 0xb); /* enable features */
+
+/* configurables */
+ outb( qlcfgc , qbase + 0xc);
+/* config: no reset interrupt, (initiator) bus id */
+ outb( 0x40 | qlcfg8 | qinitid, qbase + 8);
+ outb( qlcfg7 , qbase + 7 );
+ outb( qlcfg6 , qbase + 6 );
+/**/
+ outb(qlcfg5, qbase + 5); /* select timer */
+ outb(qlcfg9 & 7, qbase + 9); /* prescaler */
+/* outb(0x99, qbase + 5); */
+ outb(cmd->target, qbase + 4);
+
+ for (i = 0; i < cmd->cmd_len; i++)
+ outb(cmd->cmnd[i], qbase + 2);
+ qlcmd = cmd;
+ outb(0x41, qbase + 3); /* select and send command */
+ restore_flags( flags );
+}
+/*----------------------------------------------------------------*/
+/* process scsi command - usually after interrupt */
+static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
+{
+unsigned int i, j, k;
+unsigned int result; /* ultimate return result */
+unsigned int status; /* scsi returned status */
+unsigned int message; /* scsi returned message */
+unsigned int phase; /* recorded scsi phase */
+unsigned int reqlen; /* total length of transfer */
+struct scatterlist *sglist; /* scatter-gather list pointer */
+unsigned int sgcount; /* sg counter */
+
+rtrc(1)
+ j = inb(qbase + 6);
+ i = inb(qbase + 5);
+ if (i == 0x20) {
+ return (DID_NO_CONNECT << 16);
+ }
+ i |= inb(qbase + 5); /* the 0x10 bit can be set after the 0x08 */
+ if (i != 0x18) {
+ printk("Ql:Bad Interrupt status:%02x\n", i);
+ ql_zap();
+ return (DID_BAD_INTR << 16);
+ }
+ j &= 7; /* j = inb( qbase + 7 ) >> 5; */
+/* correct status is supposed to be step 4 */
+/* it sometimes returns step 3 but with 0 bytes left to send */
+/* We can try stuffing the FIFO with the max each time, but we will get a
+ sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
+ if(j != 3 && j != 4) {
+ printk("Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n", j, i, inb( qbase+7 ) & 0x1f );
+ ql_zap();
+ return (DID_ERROR << 16);
+ }
+ result = DID_OK;
+ if (inb(qbase + 7) & 0x1f) /* if some bytes in fifo */
+ outb(1, qbase + 3); /* clear fifo */
+/* note that request_bufflen is the total xfer size when sg is used */
+ reqlen = cmd->request_bufflen;
+/* note that it won't work if transfers > 16M are requested */
+ if (reqlen && !((phase = inb(qbase + 4)) & 6)) { /* data phase */
+rtrc(2)
+ outb(reqlen, qbase); /* low-mid xfer cnt */
+ outb(reqlen >> 8, qbase+1); /* low-mid xfer cnt */
+ outb(reqlen >> 16, qbase + 0xe); /* high xfer cnt */
+ outb(0x90, qbase + 3); /* command do xfer */
+/* PIO pseudo DMA to buffer or sglist */
+ REG1;
+ if (!cmd->use_sg)
+ ql_pdma(phase, cmd->request_buffer, cmd->request_bufflen);
+ else {
+ sgcount = cmd->use_sg;
+ sglist = cmd->request_buffer;
+ while (sgcount--) {
+ if (qabort) {
+ REG0;
+ return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+ }
+ if (ql_pdma(phase, sglist->address, sglist->length))
+ break;
+ sglist++;
+ }
+ }
+ REG0;
+rtrc(2)
+/* wait for irq (split into second state of irq handler if this can take time) */
+ if ((k = ql_wai()))
+ return (k << 16);
+ k = inb(qbase + 5); /* should be 0x10, bus service */
+ }
+/*** Enter Status (and Message In) Phase ***/
+ k = jiffies + WATCHDOG;
+ while ( k > jiffies && !qabort && !(inb(qbase + 4) & 6)); /* wait for status phase */
+ if ( k <= jiffies ) {
+ ql_zap();
+ return (DID_TIME_OUT << 16);
+ }
+ while (inb(qbase + 5)); /* clear pending ints */
+ if (qabort)
+ return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+ outb(0x11, qbase + 3); /* get status and message */
+ if ((k = ql_wai()))
+ return (k << 16);
+ i = inb(qbase + 5); /* get chip irq stat */
+ j = inb(qbase + 7) & 0x1f; /* and bytes rec'd */
+ status = inb(qbase + 2);
+ message = inb(qbase + 2);
+/* should get function complete int if Status and message, else bus serv if only status */
+ if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
+ printk("Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
+ result = DID_ERROR;
+ }
+ outb(0x12, qbase + 3); /* done, disconnect */
+rtrc(1)
+ if ((k = ql_wai()))
+ return (k << 16);
+/* should get bus service interrupt and disconnect interrupt */
+ i = inb(qbase + 5); /* should be bus service */
+ while (!qabort && ((i & 0x20) != 0x20)) {
+ barrier();
+ i |= inb(qbase + 5);
+ }
+rtrc(0)
+ if (qabort)
+ return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+ return (result << 16) | (message << 8) | (status & STATUS_MASK);
+}
+
+#if QL_USE_IRQ
+/*----------------------------------------------------------------*/
+/* interrupt handler */
+static void ql_ihandl(int irq, void *dev_id, struct pt_regs * regs)
+{
+Scsi_Cmnd *icmd;
+ REG0;
+ if (!(inb(qbase + 4) & 0x80)) /* false alarm? */
+ return;
+ if (qlcmd == NULL) { /* no command to process? */
+ int i;
+ i = 16;
+ while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */
+ return;
+ }
+ icmd = qlcmd;
+ icmd->result = ql_pcmd(icmd);
+ qlcmd = NULL;
+/* if result is CHECK CONDITION done calls qcommand to request sense */
+ (icmd->scsi_done) (icmd);
+}
+#endif
+
+/*----------------------------------------------------------------*/
+/* global functions */
+/*----------------------------------------------------------------*/
+/* non queued command */
+#if QL_USE_IRQ
+static void qlidone(Scsi_Cmnd * cmd) {}; /* null function */
+#endif
+
+/* command process */
+int qlogicfas_command(Scsi_Cmnd * cmd)
+{
+int k;
+#if QL_USE_IRQ
+ if (qlirq >= 0) {
+ qlogicfas_queuecommand(cmd, qlidone);
+ while (qlcmd != NULL);
+ return cmd->result;
+ }
+#endif
+/* non-irq version */
+ if (cmd->target == qinitid)
+ return (DID_BAD_TARGET << 16);
+ ql_icmd(cmd);
+ if ((k = ql_wai()))
+ return (k << 16);
+ return ql_pcmd(cmd);
+
+}
+
+#if QL_USE_IRQ
+/*----------------------------------------------------------------*/
+/* queued command */
+int qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ if(cmd->target == qinitid) {
+ cmd->result = DID_BAD_TARGET << 16;
+ done(cmd);
+ return 0;
+ }
+
+ cmd->scsi_done = done;
+/* wait for the last command's interrupt to finish */
+ while (qlcmd != NULL)
+ barrier();
+ ql_icmd(cmd);
+ return 0;
+}
+#else
+int qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ return 1;
+}
+#endif
+
+#ifdef PCMCIA
+/*----------------------------------------------------------------*/
+/* allow PCMCIA code to preset the port */
+/* port should be 0 and irq to -1 respectively for autoprobing */
+void qlogicfas_preset(int port, int irq)
+{
+ qbase=port;
+ qlirq=irq;
+}
+#endif
+
+/*----------------------------------------------------------------*/
+/* look for qlogic card and init if found */
+int qlogicfas_detect(Scsi_Host_Template * host)
+{
+int i, j; /* these are only used by IRQ detect */
+int qltyp; /* type of chip */
+struct Scsi_Host *hreg; /* registered host structure */
+unsigned long flags;
+
+host->proc_dir = &proc_scsi_qlogicfas;
+
+/* Qlogic Cards only exist at 0x230 or 0x330 (the chip itself decodes the
+ address - I check 230 first since MIDI cards are typically at 330
+
+ Theoretically, two Qlogic cards can coexist in the same system. This
+ should work by simply using this as a loadable module for the second
+ card, but I haven't tested this.
+*/
+
+ if( !qbase ) {
+ for (qbase = 0x230; qbase < 0x430; qbase += 0x100) {
+ if( check_region( qbase , 0x10 ) )
+ continue;
+ REG1;
+ if ( ( (inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7 )
+ && ( (inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7 ) )
+ break;
+ }
+ if (qbase == 0x430)
+ return 0;
+ }
+ else
+ printk( "Ql: Using preset base address of %03x\n", qbase );
+
+ qltyp = inb(qbase + 0xe) & 0xf8;
+ qinitid = host->this_id;
+ if (qinitid < 0)
+ qinitid = 7; /* if no ID, use 7 */
+ outb(1, qbase + 8); /* set for PIO pseudo DMA */
+ REG0;
+ outb(0x40 | qlcfg8 | qinitid, qbase + 8); /* (ini) bus id, disable scsi rst */
+ outb(qlcfg5, qbase + 5); /* select timer */
+ outb(qlcfg9, qbase + 9); /* prescaler */
+#if QL_RESET_AT_START
+ outb( 3 , qbase + 3 );
+ REG1;
+ while( inb( qbase + 0xf ) & 4 );
+ REG0;
+#endif
+#if QL_USE_IRQ
+/* IRQ probe - toggle pin and check request pending */
+
+ if( qlirq == -1 ) {
+ save_flags( flags );
+ cli();
+ i = 0xffff;
+ j = 3;
+ outb(0x90, qbase + 3); /* illegal command - cause interrupt */
+ REG1;
+ outb(10, 0x20); /* access pending interrupt map */
+ outb(10, 0xa0);
+ while (j--) {
+ outb(0xb0 | QL_INT_ACTIVE_HIGH , qbase + 0xd); /* int pin off */
+ i &= ~(inb(0x20) | (inb(0xa0) << 8)); /* find IRQ off */
+ outb(0xb4 | QL_INT_ACTIVE_HIGH , qbase + 0xd); /* int pin on */
+ i &= inb(0x20) | (inb(0xa0) << 8); /* find IRQ on */
+ }
+ REG0;
+ while (inb(qbase + 5)); /* purge int */
+ j = -1;
+ while (i) /* find on bit */
+ i >>= 1, j++; /* should check for exactly 1 on */
+ qlirq = j;
+ restore_flags( flags );
+ }
+ else
+ printk( "Ql: Using preset IRQ %d\n", qlirq );
+
+ if (qlirq >= 0 && !request_irq(qlirq, ql_ihandl, 0, "qlogicfas", NULL))
+ host->can_queue = 1;
+#endif
+ request_region( qbase , 0x10 ,"qlogicfas");
+ hreg = scsi_register( host , 0 ); /* no host data */
+ hreg->io_port = qbase;
+ hreg->n_io_port = 16;
+ hreg->dma_channel = -1;
+ if( qlirq != -1 )
+ hreg->irq = qlirq;
+
+ sprintf(qinfo, "Qlogicfas Driver version 0.45, chip %02X at %03X, IRQ %d, TPdma:%d",
+ qltyp, qbase, qlirq, QL_TURBO_PDMA );
+ host->name = qinfo;
+
+ return 1;
+}
+
+/*----------------------------------------------------------------*/
+/* return bios parameters */
+int qlogicfas_biosparam(Disk * disk, kdev_t dev, int ip[])
+{
+/* This should mimic the DOS Qlogic driver's behavior exactly */
+ ip[0] = 0x40;
+ ip[1] = 0x20;
+ ip[2] = disk->capacity / (ip[0] * ip[1]);
+ if (ip[2] > 1024) {
+ ip[0] = 0xff;
+ ip[1] = 0x3f;
+ ip[2] = disk->capacity / (ip[0] * ip[1]);
+ if (ip[2] > 1023)
+ ip[2] = 1023;
+ }
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+/* abort command in progress */
+int qlogicfas_abort(Scsi_Cmnd * cmd)
+{
+ qabort = 1;
+ ql_zap();
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+/* reset SCSI bus */
+int qlogicfas_reset(Scsi_Cmnd * cmd, unsigned int flags)
+{
+ qabort = 2;
+ ql_zap();
+ return 1;
+}
+
+/*----------------------------------------------------------------*/
+/* return info string */
+const char *qlogicfas_info(struct Scsi_Host * host)
+{
+ return qinfo;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = QLOGICFAS;
+
+#include "scsi_module.c"
+#endif
+
diff --git a/linux/src/drivers/scsi/qlogicfas.h b/linux/src/drivers/scsi/qlogicfas.h
new file mode 100644
index 00000000..5a1dfdbf
--- /dev/null
+++ b/linux/src/drivers/scsi/qlogicfas.h
@@ -0,0 +1,43 @@
+#ifndef _QLOGICFAS_H
+#define _QLOGICFAS_H
+
+int qlogicfas_detect(Scsi_Host_Template * );
+const char * qlogicfas_info(struct Scsi_Host *);
+int qlogicfas_command(Scsi_Cmnd *);
+int qlogicfas_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int qlogicfas_abort(Scsi_Cmnd *);
+int qlogicfas_reset(Scsi_Cmnd *, unsigned int flags);
+int qlogicfas_biosparam(Disk *, kdev_t, int[]);
+
+#ifndef NULL
+#define NULL (0)
+#endif
+
+#define QLOGICFAS { \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ qlogicfas_detect, \
+ NULL, \
+ qlogicfas_info, \
+ qlogicfas_command, \
+ qlogicfas_queuecommand, \
+ qlogicfas_abort, \
+ qlogicfas_reset, \
+ NULL, \
+ qlogicfas_biosparam, \
+ 0, \
+ -1, \
+ SG_ALL, \
+ 1, \
+ 0, \
+ 0, \
+ DISABLE_CLUSTERING \
+}
+
+#endif /* _QLOGICFAS_H */
+
+
+
diff --git a/linux/src/drivers/scsi/qlogicisp.c b/linux/src/drivers/scsi/qlogicisp.c
new file mode 100644
index 00000000..72aa5431
--- /dev/null
+++ b/linux/src/drivers/scsi/qlogicisp.c
@@ -0,0 +1,1768 @@
+/*
+ * QLogic ISP1020 Intelligent SCSI Processor Driver (PCI)
+ * Written by Erik H. Moe, ehm@cris.com
+ * Copyright 1995, Erik H. Moe
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+/* Renamed and updated to 1.3.x by Michael Griffith <grif@cs.ucr.edu> */
+
+/*
+ * $Date: 1999/04/26 05:54:52 $
+ * $Revision: 1.1 $
+ *
+ * $Log: isp1020.c,v $
+ * Revision 0.5 1995/09/22 02:23:15 root
+ * do auto request sense
+ *
+ * Revision 0.4 1995/08/07 04:44:33 root
+ * supply firmware with driver.
+ * numerous bug fixes/general cleanup of code.
+ *
+ * Revision 0.3 1995/07/16 16:15:39 root
+ * added reset/abort code.
+ *
+ * Revision 0.2 1995/06/29 03:14:19 root
+ * fixed biosparam.
+ * added queue protocol.
+ *
+ * Revision 0.1 1995/06/25 01:55:45 root
+ * Initial release.
+ *
+ */
+
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/unistd.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "sd.h"
+#include "hosts.h"
+#include "qlogicisp.h"
+
+/* Configuration section *****************************************************/
+
+/* Set the following macro to 1 to reload the ISP1020's firmware. This is
+ the latest firmware provided by QLogic. This may be an earlier/later
+ revision than supplied by your board. */
+
+#define RELOAD_FIRMWARE 1
+
+/* Set the following macro to 1 to reload the ISP1020's defaults from nvram.
+ If you are not sure of your settings, leave this alone, the driver will
+ use a set of 'safe' defaults */
+
+#define USE_NVRAM_DEFAULTS 0
+
+/* Macros used for debugging */
+
+#define DEBUG_ISP1020 0
+#define DEBUG_ISP1020_INT 0
+#define DEBUG_ISP1020_SETUP 0
+#define TRACE_ISP 0
+
+#define DEFAULT_LOOP_COUNT 1000000
+
+/* End Configuration section *************************************************/
+
+#include <linux/module.h>
+
+#if TRACE_ISP
+
+# define TRACE_BUF_LEN (32*1024)
+
+struct {
+ u_long next;
+ struct {
+ u_long time;
+ u_int index;
+ u_int addr;
+ u_char * name;
+ } buf[TRACE_BUF_LEN];
+} trace;
+
+#define TRACE(w, i, a) \
+{ \
+ unsigned long flags; \
+ \
+ save_flags(flags); \
+ cli(); \
+ trace.buf[trace.next].name = (w); \
+ trace.buf[trace.next].time = jiffies; \
+ trace.buf[trace.next].index = (i); \
+ trace.buf[trace.next].addr = (long) (a); \
+ trace.next = (trace.next + 1) & (TRACE_BUF_LEN - 1); \
+ restore_flags(flags); \
+}
+
+#else
+# define TRACE(w, i, a)
+#endif
+
+#if DEBUG_ISP1020
+#define ENTER(x) printk("isp1020 : entering %s()\n", x);
+#define LEAVE(x) printk("isp1020 : leaving %s()\n", x);
+#define DEBUG(x) x
+#else
+#define ENTER(x)
+#define LEAVE(x)
+#define DEBUG(x)
+#endif /* DEBUG_ISP1020 */
+
+#if DEBUG_ISP1020_INTR
+#define ENTER_INTR(x) printk("isp1020 : entering %s()\n", x);
+#define LEAVE_INTR(x) printk("isp1020 : leaving %s()\n", x);
+#define DEBUG_INTR(x) x
+#else
+#define ENTER_INTR(x)
+#define LEAVE_INTR(x)
+#define DEBUG_INTR(x)
+#endif /* DEBUG ISP1020_INTR */
+
+#define ISP1020_REV_ID 1
+
+#define MAX_TARGETS 16
+#define MAX_LUNS 8
+
+/* host configuration and control registers */
+#define HOST_HCCR 0xc0 /* host command and control */
+
+/* pci bus interface registers */
+#define PCI_ID_LOW 0x00 /* vendor id */
+#define PCI_ID_HIGH 0x02 /* device id */
+#define ISP_CFG0 0x04 /* configuration register #0 */
+#define ISP_CFG1 0x06 /* configuration register #1 */
+#define PCI_INTF_CTL 0x08 /* pci interface control */
+#define PCI_INTF_STS 0x0a /* pci interface status */
+#define PCI_SEMAPHORE 0x0c /* pci semaphore */
+#define PCI_NVRAM 0x0e /* pci nvram interface */
+
+/* mailbox registers */
+#define MBOX0 0x70 /* mailbox 0 */
+#define MBOX1 0x72 /* mailbox 1 */
+#define MBOX2 0x74 /* mailbox 2 */
+#define MBOX3 0x76 /* mailbox 3 */
+#define MBOX4 0x78 /* mailbox 4 */
+#define MBOX5 0x7a /* mailbox 5 */
+
+/* mailbox command complete status codes */
+#define MBOX_COMMAND_COMPLETE 0x4000
+#define INVALID_COMMAND 0x4001
+#define HOST_INTERFACE_ERROR 0x4002
+#define TEST_FAILED 0x4003
+#define COMMAND_ERROR 0x4005
+#define COMMAND_PARAM_ERROR 0x4006
+
+/* async event status codes */
+#define ASYNC_SCSI_BUS_RESET 0x8001
+#define SYSTEM_ERROR 0x8002
+#define REQUEST_TRANSFER_ERROR 0x8003
+#define RESPONSE_TRANSFER_ERROR 0x8004
+#define REQUEST_QUEUE_WAKEUP 0x8005
+#define EXECUTION_TIMEOUT_RESET 0x8006
+
+struct Entry_header {
+ u_char entry_type;
+ u_char entry_cnt;
+ u_char sys_def_1;
+ u_char flags;
+};
+
+/* entry header type commands */
+#define ENTRY_COMMAND 1
+#define ENTRY_CONTINUATION 2
+#define ENTRY_STATUS 3
+#define ENTRY_MARKER 4
+#define ENTRY_EXTENDED_COMMAND 5
+
+/* entry header flag definitions */
+#define EFLAG_CONTINUATION 1
+#define EFLAG_BUSY 2
+#define EFLAG_BAD_HEADER 4
+#define EFLAG_BAD_PAYLOAD 8
+
+struct dataseg {
+ u_int d_base;
+ u_int d_count;
+};
+
+struct Command_Entry {
+ struct Entry_header hdr;
+ u_int handle;
+ u_char target_lun;
+ u_char target_id;
+ u_short cdb_length;
+ u_short control_flags;
+ u_short rsvd;
+ u_short time_out;
+ u_short segment_cnt;
+ u_char cdb[12];
+ struct dataseg dataseg[4];
+};
+
+/* command entry control flag definitions */
+#define CFLAG_NODISC 0x01
+#define CFLAG_HEAD_TAG 0x02
+#define CFLAG_ORDERED_TAG 0x04
+#define CFLAG_SIMPLE_TAG 0x08
+#define CFLAG_TAR_RTN 0x10
+#define CFLAG_READ 0x20
+#define CFLAG_WRITE 0x40
+
+struct Ext_Command_Entry {
+ struct Entry_header hdr;
+ u_int handle;
+ u_char target_lun;
+ u_char target_id;
+ u_short cdb_length;
+ u_short control_flags;
+ u_short rsvd;
+ u_short time_out;
+ u_short segment_cnt;
+ u_char cdb[44];
+};
+
+struct Continuation_Entry {
+ struct Entry_header hdr;
+ u_int reserved;
+ struct dataseg dataseg[7];
+};
+
+struct Marker_Entry {
+ struct Entry_header hdr;
+ u_int reserved;
+ u_char target_lun;
+ u_char target_id;
+ u_char modifier;
+ u_char rsvd;
+ u_char rsvds[52];
+};
+
+/* marker entry modifier definitions */
+#define SYNC_DEVICE 0
+#define SYNC_TARGET 1
+#define SYNC_ALL 2
+
+struct Status_Entry {
+ struct Entry_header hdr;
+ u_int handle;
+ u_short scsi_status;
+ u_short completion_status;
+ u_short state_flags;
+ u_short status_flags;
+ u_short time;
+ u_short req_sense_len;
+ u_int residual;
+ u_char rsvd[8];
+ u_char req_sense_data[32];
+};
+
+/* status entry completion status definitions */
+#define CS_COMPLETE 0x0000
+#define CS_INCOMPLETE 0x0001
+#define CS_DMA_ERROR 0x0002
+#define CS_TRANSPORT_ERROR 0x0003
+#define CS_RESET_OCCURRED 0x0004
+#define CS_ABORTED 0x0005
+#define CS_TIMEOUT 0x0006
+#define CS_DATA_OVERRUN 0x0007
+#define CS_COMMAND_OVERRUN 0x0008
+#define CS_STATUS_OVERRUN 0x0009
+#define CS_BAD_MESSAGE 0x000a
+#define CS_NO_MESSAGE_OUT 0x000b
+#define CS_EXT_ID_FAILED 0x000c
+#define CS_IDE_MSG_FAILED 0x000d
+#define CS_ABORT_MSG_FAILED 0x000e
+#define CS_REJECT_MSG_FAILED 0x000f
+#define CS_NOP_MSG_FAILED 0x0010
+#define CS_PARITY_ERROR_MSG_FAILED 0x0011
+#define CS_DEVICE_RESET_MSG_FAILED 0x0012
+#define CS_ID_MSG_FAILED 0x0013
+#define CS_UNEXP_BUS_FREE 0x0014
+/* as per app note #83120-514-06a: */
+#define CS_DATA_UNDERRUN 0x0015
+#define CS_INVALID_ENTRY_TYPE 0x001b
+#define CS_DEVICE_QUEUE_FULL 0x001c
+#define CS_SCSI_PHASE_SKIPPED 0x001d
+#define CS_ARS_FAILED 0x001e /* auto Req. Sense failed */
+
+/* status entry state flag definitions */
+#define SF_GOT_BUS 0x0100
+#define SF_GOT_TARGET 0x0200
+#define SF_SENT_CDB 0x0400
+#define SF_TRANSFERRED_DATA 0x0800
+#define SF_GOT_STATUS 0x1000
+#define SF_GOT_SENSE 0x2000
+
+/* status entry status flag definitions */
+#define STF_DISCONNECT 0x0001
+#define STF_SYNCHRONOUS 0x0002
+#define STF_PARITY_ERROR 0x0004
+#define STF_BUS_RESET 0x0008
+#define STF_DEVICE_RESET 0x0010
+#define STF_ABORTED 0x0020
+#define STF_TIMEOUT 0x0040
+#define STF_NEGOTIATION 0x0080
+
+/* interface control commands */
+#define ISP_RESET 0x0001
+#define ISP_EN_INT 0x0002
+#define ISP_EN_RISC 0x0004
+
+/* host control commands */
+#define HCCR_NOP 0x0000
+#define HCCR_RESET 0x1000
+#define HCCR_PAUSE 0x2000
+#define HCCR_RELEASE 0x3000
+#define HCCR_SINGLE_STEP 0x4000
+#define HCCR_SET_HOST_INTR 0x5000
+#define HCCR_CLEAR_HOST_INTR 0x6000
+#define HCCR_CLEAR_RISC_INTR 0x7000
+#define HCCR_BP_ENABLE 0x8000
+#define HCCR_BIOS_DISABLE 0x9000
+#define HCCR_TEST_MODE 0xf000
+
+#define RISC_BUSY 0x0004
+
+/* mailbox commands */
+#define MBOX_NO_OP 0x0000
+#define MBOX_LOAD_RAM 0x0001
+#define MBOX_EXEC_FIRMWARE 0x0002
+#define MBOX_DUMP_RAM 0x0003
+#define MBOX_WRITE_RAM_WORD 0x0004
+#define MBOX_READ_RAM_WORD 0x0005
+#define MBOX_MAILBOX_REG_TEST 0x0006
+#define MBOX_VERIFY_CHECKSUM 0x0007
+#define MBOX_ABOUT_FIRMWARE 0x0008
+#define MBOX_CHECK_FIRMWARE 0x000e
+#define MBOX_INIT_REQ_QUEUE 0x0010
+#define MBOX_INIT_RES_QUEUE 0x0011
+#define MBOX_EXECUTE_IOCB 0x0012
+#define MBOX_WAKE_UP 0x0013
+#define MBOX_STOP_FIRMWARE 0x0014
+#define MBOX_ABORT 0x0015
+#define MBOX_ABORT_DEVICE 0x0016
+#define MBOX_ABORT_TARGET 0x0017
+#define MBOX_BUS_RESET 0x0018
+#define MBOX_STOP_QUEUE 0x0019
+#define MBOX_START_QUEUE 0x001a
+#define MBOX_SINGLE_STEP_QUEUE 0x001b
+#define MBOX_ABORT_QUEUE 0x001c
+#define MBOX_GET_DEV_QUEUE_STATUS 0x001d
+#define MBOX_GET_FIRMWARE_STATUS 0x001f
+#define MBOX_GET_INIT_SCSI_ID 0x0020
+#define MBOX_GET_SELECT_TIMEOUT 0x0021
+#define MBOX_GET_RETRY_COUNT 0x0022
+#define MBOX_GET_TAG_AGE_LIMIT 0x0023
+#define MBOX_GET_CLOCK_RATE 0x0024
+#define MBOX_GET_ACT_NEG_STATE 0x0025
+#define MBOX_GET_ASYNC_DATA_SETUP_TIME 0x0026
+#define MBOX_GET_PCI_PARAMS 0x0027
+#define MBOX_GET_TARGET_PARAMS 0x0028
+#define MBOX_GET_DEV_QUEUE_PARAMS 0x0029
+#define MBOX_SET_INIT_SCSI_ID 0x0030
+#define MBOX_SET_SELECT_TIMEOUT 0x0031
+#define MBOX_SET_RETRY_COUNT 0x0032
+#define MBOX_SET_TAG_AGE_LIMIT 0x0033
+#define MBOX_SET_CLOCK_RATE 0x0034
+#define MBOX_SET_ACTIVE_NEG_STATE 0x0035
+#define MBOX_SET_ASYNC_DATA_SETUP_TIME 0x0036
+#define MBOX_SET_PCI_CONTROL_PARAMS 0x0037
+#define MBOX_SET_TARGET_PARAMS 0x0038
+#define MBOX_SET_DEV_QUEUE_PARAMS 0x0039
+#define MBOX_RETURN_BIOS_BLOCK_ADDR 0x0040
+#define MBOX_WRITE_FOUR_RAM_WORDS 0x0041
+#define MBOX_EXEC_BIOS_IOCB 0x0042
+
+#include "qlogicisp_asm.c"
+
+#define PACKB(a, b) (((a)<<4)|(b))
+
+const u_char mbox_param[] = {
+ PACKB(1, 1), /* MBOX_NO_OP */
+ PACKB(5, 5), /* MBOX_LOAD_RAM */
+ PACKB(2, 0), /* MBOX_EXEC_FIRMWARE */
+ PACKB(5, 5), /* MBOX_DUMP_RAM */
+ PACKB(3, 3), /* MBOX_WRITE_RAM_WORD */
+ PACKB(2, 3), /* MBOX_READ_RAM_WORD */
+ PACKB(6, 6), /* MBOX_MAILBOX_REG_TEST */
+ PACKB(2, 3), /* MBOX_VERIFY_CHECKSUM */
+ PACKB(1, 3), /* MBOX_ABOUT_FIRMWARE */
+ PACKB(0, 0), /* 0x0009 */
+ PACKB(0, 0), /* 0x000a */
+ PACKB(0, 0), /* 0x000b */
+ PACKB(0, 0), /* 0x000c */
+ PACKB(0, 0), /* 0x000d */
+ PACKB(1, 2), /* MBOX_CHECK_FIRMWARE */
+ PACKB(0, 0), /* 0x000f */
+ PACKB(5, 5), /* MBOX_INIT_REQ_QUEUE */
+ PACKB(6, 6), /* MBOX_INIT_RES_QUEUE */
+ PACKB(4, 4), /* MBOX_EXECUTE_IOCB */
+ PACKB(2, 2), /* MBOX_WAKE_UP */
+ PACKB(1, 6), /* MBOX_STOP_FIRMWARE */
+ PACKB(4, 4), /* MBOX_ABORT */
+ PACKB(2, 2), /* MBOX_ABORT_DEVICE */
+ PACKB(3, 3), /* MBOX_ABORT_TARGET */
+ PACKB(2, 2), /* MBOX_BUS_RESET */
+ PACKB(2, 3), /* MBOX_STOP_QUEUE */
+ PACKB(2, 3), /* MBOX_START_QUEUE */
+ PACKB(2, 3), /* MBOX_SINGLE_STEP_QUEUE */
+ PACKB(2, 3), /* MBOX_ABORT_QUEUE */
+ PACKB(2, 4), /* MBOX_GET_DEV_QUEUE_STATUS */
+ PACKB(0, 0), /* 0x001e */
+ PACKB(1, 3), /* MBOX_GET_FIRMWARE_STATUS */
+ PACKB(1, 2), /* MBOX_GET_INIT_SCSI_ID */
+ PACKB(1, 2), /* MBOX_GET_SELECT_TIMEOUT */
+ PACKB(1, 3), /* MBOX_GET_RETRY_COUNT */
+ PACKB(1, 2), /* MBOX_GET_TAG_AGE_LIMIT */
+ PACKB(1, 2), /* MBOX_GET_CLOCK_RATE */
+ PACKB(1, 2), /* MBOX_GET_ACT_NEG_STATE */
+ PACKB(1, 2), /* MBOX_GET_ASYNC_DATA_SETUP_TIME */
+ PACKB(1, 3), /* MBOX_GET_PCI_PARAMS */
+ PACKB(2, 4), /* MBOX_GET_TARGET_PARAMS */
+ PACKB(2, 4), /* MBOX_GET_DEV_QUEUE_PARAMS */
+ PACKB(0, 0), /* 0x002a */
+ PACKB(0, 0), /* 0x002b */
+ PACKB(0, 0), /* 0x002c */
+ PACKB(0, 0), /* 0x002d */
+ PACKB(0, 0), /* 0x002e */
+ PACKB(0, 0), /* 0x002f */
+ PACKB(2, 2), /* MBOX_SET_INIT_SCSI_ID */
+ PACKB(2, 2), /* MBOX_SET_SELECT_TIMEOUT */
+ PACKB(3, 3), /* MBOX_SET_RETRY_COUNT */
+ PACKB(2, 2), /* MBOX_SET_TAG_AGE_LIMIT */
+ PACKB(2, 2), /* MBOX_SET_CLOCK_RATE */
+ PACKB(2, 2), /* MBOX_SET_ACTIVE_NEG_STATE */
+ PACKB(2, 2), /* MBOX_SET_ASYNC_DATA_SETUP_TIME */
+ PACKB(3, 3), /* MBOX_SET_PCI_CONTROL_PARAMS */
+ PACKB(4, 4), /* MBOX_SET_TARGET_PARAMS */
+ PACKB(4, 4), /* MBOX_SET_DEV_QUEUE_PARAMS */
+ PACKB(0, 0), /* 0x003a */
+ PACKB(0, 0), /* 0x003b */
+ PACKB(0, 0), /* 0x003c */
+ PACKB(0, 0), /* 0x003d */
+ PACKB(0, 0), /* 0x003e */
+ PACKB(0, 0), /* 0x003f */
+ PACKB(1, 2), /* MBOX_RETURN_BIOS_BLOCK_ADDR */
+ PACKB(6, 1), /* MBOX_WRITE_FOUR_RAM_WORDS */
+ PACKB(2, 3) /* MBOX_EXEC_BIOS_IOCB */
+};
+
+#define MAX_MBOX_COMMAND (sizeof(mbox_param)/sizeof(u_short))
+
+struct host_param {
+ u_short fifo_threshold;
+ u_short host_adapter_enable;
+ u_short initiator_scsi_id;
+ u_short bus_reset_delay;
+ u_short retry_count;
+ u_short retry_delay;
+ u_short async_data_setup_time;
+ u_short req_ack_active_negation;
+ u_short data_line_active_negation;
+ u_short data_dma_burst_enable;
+ u_short command_dma_burst_enable;
+ u_short tag_aging;
+ u_short selection_timeout;
+ u_short max_queue_depth;
+};
+
+/*
+ * Device Flags:
+ *
+ * Bit Name
+ * ---------
+ * 7 Disconnect Privilege
+ * 6 Parity Checking
+ * 5 Wide Data Transfers
+ * 4 Synchronous Data Transfers
+ * 3 Tagged Queuing
+ * 2 Automatic Request Sense
+ * 1 Stop Queue on Check Condition
+ * 0 Renegotiate on Error
+ */
+
+struct dev_param {
+ u_short device_flags;
+ u_short execution_throttle;
+ u_short synchronous_period;
+ u_short synchronous_offset;
+ u_short device_enable;
+ u_short reserved; /* pad */
+};
+
+/*
+ * The result queue can be quite a bit smaller since continuation entries
+ * do not show up there:
+ */
+#define RES_QUEUE_LEN ((QLOGICISP_REQ_QUEUE_LEN + 1) / 8 - 1)
+#define QUEUE_ENTRY_LEN 64
+
+struct isp1020_hostdata {
+ u_char bus;
+ u_char revision;
+ u_char device_fn;
+ struct host_param host_param;
+ struct dev_param dev_param[MAX_TARGETS];
+
+ /* result and request queues (shared with isp1020): */
+ u_int req_in_ptr; /* index of next request slot */
+ u_int res_out_ptr; /* index of next result slot */
+
+ /* this is here so the queues are nicely aligned */
+ long send_marker; /* do we need to send a marker? */
+
+ char res[RES_QUEUE_LEN+1][QUEUE_ENTRY_LEN];
+ char req[QLOGICISP_REQ_QUEUE_LEN+1][QUEUE_ENTRY_LEN];
+};
+
+/* queue length's _must_ be power of two: */
+#define QUEUE_DEPTH(in, out, ql) ((in - out) & (ql))
+#define REQ_QUEUE_DEPTH(in, out) QUEUE_DEPTH(in, out, \
+ QLOGICISP_REQ_QUEUE_LEN)
+#define RES_QUEUE_DEPTH(in, out) QUEUE_DEPTH(in, out, RES_QUEUE_LEN)
+
+struct Scsi_Host *irq2host[NR_IRQS];
+
+static void isp1020_enable_irqs(struct Scsi_Host *);
+static void isp1020_disable_irqs(struct Scsi_Host *);
+static int isp1020_init(struct Scsi_Host *);
+static int isp1020_reset_hardware(struct Scsi_Host *);
+static int isp1020_set_defaults(struct Scsi_Host *);
+static int isp1020_load_parameters(struct Scsi_Host *);
+static int isp1020_mbox_command(struct Scsi_Host *, u_short []);
+static int isp1020_return_status(struct Status_Entry *);
+static void isp1020_intr_handler(int, void *, struct pt_regs *);
+
+#if USE_NVRAM_DEFAULTS
+static int isp1020_get_defaults(struct Scsi_Host *);
+static int isp1020_verify_nvram(struct Scsi_Host *);
+static u_short isp1020_read_nvram_word(struct Scsi_Host *, u_short);
+#endif
+
+#if DEBUG_ISP1020
+static void isp1020_print_scsi_cmd(Scsi_Cmnd *);
+#endif
+#if DEBUG_ISP1020_INTR
+static void isp1020_print_status_entry(struct Status_Entry *);
+#endif
+
+static struct proc_dir_entry proc_scsi_isp1020 = {
+ PROC_SCSI_QLOGICISP, 7, "isp1020",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+
+static inline void isp1020_enable_irqs(struct Scsi_Host *host)
+{
+ outw(ISP_EN_INT|ISP_EN_RISC, host->io_port + PCI_INTF_CTL);
+}
+
+
+static inline void isp1020_disable_irqs(struct Scsi_Host *host)
+{
+ outw(0x0, host->io_port + PCI_INTF_CTL);
+}
+
+
+int isp1020_detect(Scsi_Host_Template *tmpt)
+{
+ int hosts = 0;
+ u_short index;
+ u_char bus, device_fn;
+ struct Scsi_Host *host;
+ struct isp1020_hostdata *hostdata;
+
+ ENTER("isp1020_detect");
+
+ tmpt->proc_dir = &proc_scsi_isp1020;
+
+ if (pcibios_present() == 0) {
+ printk("qlogicisp : PCI bios not present\n");
+ return 0;
+ }
+
+ memset(irq2host, 0, sizeof(irq2host));
+
+ for (index = 0; pcibios_find_device(PCI_VENDOR_ID_QLOGIC,
+ PCI_DEVICE_ID_QLOGIC_ISP1020,
+ index, &bus, &device_fn) == 0;
+ index++)
+ {
+ host = scsi_register(tmpt, sizeof(struct isp1020_hostdata));
+ hostdata = (struct isp1020_hostdata *) host->hostdata;
+
+ memset(hostdata, 0, sizeof(struct isp1020_hostdata));
+ hostdata->bus = bus;
+ hostdata->device_fn = device_fn;
+
+ if (isp1020_init(host) || isp1020_reset_hardware(host)
+#if USE_NVRAM_DEFAULTS
+ || isp1020_get_defaults(host)
+#else
+ || isp1020_set_defaults(host)
+#endif /* USE_NVRAM_DEFAULTS */
+ || isp1020_load_parameters(host)) {
+ scsi_unregister(host);
+ continue;
+ }
+
+ host->this_id = hostdata->host_param.initiator_scsi_id;
+
+ if (request_irq(host->irq, isp1020_intr_handler, SA_INTERRUPT,
+ "qlogicisp", NULL))
+ {
+ printk("qlogicisp : interrupt %d already in use\n",
+ host->irq);
+ scsi_unregister(host);
+ continue;
+ }
+
+ if (check_region(host->io_port, 0xff)) {
+ printk("qlogicisp : i/o region 0x%04x-0x%04x already "
+ "in use\n",
+ host->io_port, host->io_port + 0xff);
+ free_irq(host->irq, NULL);
+ scsi_unregister(host);
+ continue;
+ }
+
+ request_region(host->io_port, 0xff, "qlogicisp");
+ irq2host[host->irq] = host;
+
+ outw(0x0, host->io_port + PCI_SEMAPHORE);
+ outw(HCCR_CLEAR_RISC_INTR, host->io_port + HOST_HCCR);
+ isp1020_enable_irqs(host);
+
+ hosts++;
+ }
+
+ LEAVE("isp1020_detect");
+
+ return hosts;
+}
+
+
+int isp1020_release(struct Scsi_Host *host)
+{
+ struct isp1020_hostdata *hostdata;
+
+ ENTER("isp1020_release");
+
+ hostdata = (struct isp1020_hostdata *) host->hostdata;
+
+ outw(0x0, host->io_port + PCI_INTF_CTL);
+ free_irq(host->irq, NULL);
+
+ release_region(host->io_port, 0xff);
+
+ LEAVE("isp1020_release");
+
+ return 0;
+}
+
+
+const char *isp1020_info(struct Scsi_Host *host)
+{
+ static char buf[80];
+ struct isp1020_hostdata *hostdata;
+
+ ENTER("isp1020_info");
+
+ hostdata = (struct isp1020_hostdata *) host->hostdata;
+ sprintf(buf,
+ "QLogic ISP1020 SCSI on PCI bus %d device %d irq %d base 0x%x",
+ hostdata->bus, (hostdata->device_fn & 0xf8) >> 3, host->irq,
+ host->io_port);
+
+ LEAVE("isp1020_info");
+
+ return buf;
+}
+
+
+/*
+ * The middle SCSI layer ensures that queuecommand never gets invoked
+ * concurrently with itself or the interrupt handler (though the
+ * interrupt handler may call this routine as part of
+ * request-completion handling).
+ */
+int isp1020_queuecommand(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *))
+{
+ int i, sg_count, n, num_free;
+ u_int in_ptr, out_ptr;
+ struct dataseg * ds;
+ struct scatterlist *sg;
+ struct Command_Entry *cmd;
+ struct Continuation_Entry *cont;
+ struct Scsi_Host *host;
+ struct isp1020_hostdata *hostdata;
+
+ ENTER("isp1020_queuecommand");
+
+ host = Cmnd->host;
+ hostdata = (struct isp1020_hostdata *) host->hostdata;
+ Cmnd->scsi_done = done;
+
+ DEBUG(isp1020_print_scsi_cmd(Cmnd));
+
+ out_ptr = inw(host->io_port + MBOX4);
+ in_ptr = hostdata->req_in_ptr;
+
+ DEBUG(printk("qlogicisp : request queue depth %d\n",
+ REQ_QUEUE_DEPTH(in_ptr, out_ptr)));
+
+ cmd = (struct Command_Entry *) &hostdata->req[in_ptr][0];
+ in_ptr = (in_ptr + 1) & QLOGICISP_REQ_QUEUE_LEN;
+ if (in_ptr == out_ptr) {
+ printk("qlogicisp : request queue overflow\n");
+ return 1;
+ }
+
+ if (hostdata->send_marker) {
+ struct Marker_Entry *marker;
+
+ TRACE("queue marker", in_ptr, 0);
+
+ DEBUG(printk("qlogicisp : adding marker entry\n"));
+ marker = (struct Marker_Entry *) cmd;
+ memset(marker, 0, sizeof(struct Marker_Entry));
+
+ marker->hdr.entry_type = ENTRY_MARKER;
+ marker->hdr.entry_cnt = 1;
+ marker->modifier = SYNC_ALL;
+
+ hostdata->send_marker = 0;
+
+ if (((in_ptr + 1) & QLOGICISP_REQ_QUEUE_LEN) == out_ptr) {
+ outw(in_ptr, host->io_port + MBOX4);
+ hostdata->req_in_ptr = in_ptr;
+ printk("qlogicisp : request queue overflow\n");
+ return 1;
+ }
+ cmd = (struct Command_Entry *) &hostdata->req[in_ptr][0];
+ in_ptr = (in_ptr + 1) & QLOGICISP_REQ_QUEUE_LEN;
+ }
+
+ TRACE("queue command", in_ptr, Cmnd);
+
+ memset(cmd, 0, sizeof(struct Command_Entry));
+
+ cmd->hdr.entry_type = ENTRY_COMMAND;
+ cmd->hdr.entry_cnt = 1;
+
+ cmd->handle = (u_int) virt_to_bus(Cmnd);
+ cmd->target_lun = Cmnd->lun;
+ cmd->target_id = Cmnd->target;
+ cmd->cdb_length = Cmnd->cmd_len;
+ cmd->control_flags = CFLAG_READ | CFLAG_WRITE;
+ cmd->time_out = 30;
+
+ memcpy(cmd->cdb, Cmnd->cmnd, Cmnd->cmd_len);
+
+ if (Cmnd->use_sg) {
+ cmd->segment_cnt = sg_count = Cmnd->use_sg;
+ sg = (struct scatterlist *) Cmnd->request_buffer;
+ ds = cmd->dataseg;
+
+ /* fill in first four sg entries: */
+ n = sg_count;
+ if (n > 4)
+ n = 4;
+ for (i = 0; i < n; i++) {
+ ds[i].d_base = (u_int) virt_to_bus(sg->address);
+ ds[i].d_count = sg->length;
+ ++sg;
+ }
+ sg_count -= 4;
+
+ while (sg_count > 0) {
+ ++cmd->hdr.entry_cnt;
+ cont = (struct Continuation_Entry *)
+ &hostdata->req[in_ptr][0];
+ in_ptr = (in_ptr + 1) & QLOGICISP_REQ_QUEUE_LEN;
+ if (in_ptr == out_ptr) {
+ printk("isp1020: unexpected request queue "
+ "overflow\n");
+ return 1;
+ }
+ TRACE("queue continuation", in_ptr, 0);
+ cont->hdr.entry_type = ENTRY_CONTINUATION;
+ cont->hdr.entry_cnt = 0;
+ cont->hdr.sys_def_1 = 0;
+ cont->hdr.flags = 0;
+ cont->reserved = 0;
+ ds = cont->dataseg;
+ n = sg_count;
+ if (n > 7)
+ n = 7;
+ for (i = 0; i < n; ++i) {
+ ds[i].d_base = (u_int)virt_to_bus(sg->address);
+ ds[i].d_count = sg->length;
+ ++sg;
+ }
+ sg_count -= n;
+ }
+ } else {
+ cmd->dataseg[0].d_base =
+ (u_int) virt_to_bus(Cmnd->request_buffer);
+ cmd->dataseg[0].d_count =
+ (u_int) Cmnd->request_bufflen;
+ cmd->segment_cnt = 1;
+ }
+
+ outw(in_ptr, host->io_port + MBOX4);
+ hostdata->req_in_ptr = in_ptr;
+
+ num_free = QLOGICISP_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr);
+ host->can_queue = host->host_busy + num_free;
+ host->sg_tablesize = QLOGICISP_MAX_SG(num_free);
+
+ LEAVE("isp1020_queuecommand");
+
+ return 0;
+}
+
+
+#define ASYNC_EVENT_INTERRUPT 0x01
+
+void isp1020_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ Scsi_Cmnd *Cmnd;
+ struct Status_Entry *sts;
+ struct Scsi_Host *host;
+ struct isp1020_hostdata *hostdata;
+ u_int in_ptr, out_ptr;
+ u_short status;
+
+ ENTER_INTR("isp1020_intr_handler");
+
+ host = irq2host[irq];
+ if (!host) {
+ printk("qlogicisp : unexpected interrupt on line %d\n", irq);
+ return;
+ }
+ hostdata = (struct isp1020_hostdata *) host->hostdata;
+
+ DEBUG_INTR(printk("qlogicisp : interrupt on line %d\n", irq));
+
+ if (!(inw(host->io_port + PCI_INTF_STS) & 0x04)) {
+ /* spurious interrupts can happen legally */
+ DEBUG_INTR(printk("qlogicisp: got spurious interrupt\n"));
+ return;
+ }
+ in_ptr = inw(host->io_port + MBOX5);
+ outw(HCCR_CLEAR_RISC_INTR, host->io_port + HOST_HCCR);
+
+ if ((inw(host->io_port + PCI_SEMAPHORE) & ASYNC_EVENT_INTERRUPT)) {
+ status = inw(host->io_port + MBOX0);
+
+ DEBUG_INTR(printk("qlogicisp : mbox completion status: %x\n",
+ status));
+
+ switch (status) {
+ case ASYNC_SCSI_BUS_RESET:
+ case EXECUTION_TIMEOUT_RESET:
+ hostdata->send_marker = 1;
+ break;
+ case INVALID_COMMAND:
+ case HOST_INTERFACE_ERROR:
+ case COMMAND_ERROR:
+ case COMMAND_PARAM_ERROR:
+ printk("qlogicisp : bad mailbox return status\n");
+ break;
+ }
+ outw(0x0, host->io_port + PCI_SEMAPHORE);
+ }
+ out_ptr = hostdata->res_out_ptr;
+
+ DEBUG_INTR(printk("qlogicisp : response queue update\n"));
+ DEBUG_INTR(printk("qlogicisp : response queue depth %d\n",
+ QUEUE_DEPTH(in_ptr, out_ptr)));
+
+ while (out_ptr != in_ptr) {
+ sts = (struct Status_Entry *) &hostdata->res[out_ptr][0];
+ out_ptr = (out_ptr + 1) & RES_QUEUE_LEN;
+
+ Cmnd = (Scsi_Cmnd *) bus_to_virt(sts->handle);
+
+ TRACE("done", out_ptr, Cmnd);
+
+ if (sts->completion_status == CS_RESET_OCCURRED
+ || sts->completion_status == CS_ABORTED
+ || (sts->status_flags & STF_BUS_RESET))
+ hostdata->send_marker = 1;
+
+ if (sts->state_flags & SF_GOT_SENSE)
+ memcpy(Cmnd->sense_buffer, sts->req_sense_data,
+ sizeof(Cmnd->sense_buffer));
+
+ DEBUG_INTR(isp1020_print_status_entry(sts));
+
+ if (sts->hdr.entry_type == ENTRY_STATUS)
+ Cmnd->result = isp1020_return_status(sts);
+ else
+ Cmnd->result = DID_ERROR << 16;
+
+ outw(out_ptr, host->io_port + MBOX5);
+ (*Cmnd->scsi_done)(Cmnd);
+ }
+ hostdata->res_out_ptr = out_ptr;
+
+ LEAVE_INTR("isp1020_intr_handler");
+}
+
+
+static int isp1020_return_status(struct Status_Entry *sts)
+{
+ int host_status = DID_ERROR;
+#if DEBUG_ISP1020_INTR
+ static char *reason[] = {
+ "DID_OK",
+ "DID_NO_CONNECT",
+ "DID_BUS_BUSY",
+ "DID_TIME_OUT",
+ "DID_BAD_TARGET",
+ "DID_ABORT",
+ "DID_PARITY",
+ "DID_ERROR",
+ "DID_RESET",
+ "DID_BAD_INTR"
+ };
+#endif /* DEBUG_ISP1020_INTR */
+
+ ENTER("isp1020_return_status");
+
+ DEBUG(printk("qlogicisp : completion status = 0x%04x\n",
+ sts->completion_status));
+
+ switch(sts->completion_status) {
+ case CS_COMPLETE:
+ host_status = DID_OK;
+ break;
+ case CS_INCOMPLETE:
+ if (!(sts->state_flags & SF_GOT_BUS))
+ host_status = DID_NO_CONNECT;
+ else if (!(sts->state_flags & SF_GOT_TARGET))
+ host_status = DID_BAD_TARGET;
+ else if (!(sts->state_flags & SF_SENT_CDB))
+ host_status = DID_ERROR;
+ else if (!(sts->state_flags & SF_TRANSFERRED_DATA))
+ host_status = DID_ERROR;
+ else if (!(sts->state_flags & SF_GOT_STATUS))
+ host_status = DID_ERROR;
+ else if (!(sts->state_flags & SF_GOT_SENSE))
+ host_status = DID_ERROR;
+ break;
+ case CS_DMA_ERROR:
+ case CS_TRANSPORT_ERROR:
+ host_status = DID_ERROR;
+ break;
+ case CS_RESET_OCCURRED:
+ host_status = DID_RESET;
+ break;
+ case CS_ABORTED:
+ host_status = DID_ABORT;
+ break;
+ case CS_TIMEOUT:
+ host_status = DID_TIME_OUT;
+ break;
+ case CS_DATA_OVERRUN:
+ case CS_COMMAND_OVERRUN:
+ case CS_STATUS_OVERRUN:
+ case CS_BAD_MESSAGE:
+ case CS_NO_MESSAGE_OUT:
+ case CS_EXT_ID_FAILED:
+ case CS_IDE_MSG_FAILED:
+ case CS_ABORT_MSG_FAILED:
+ case CS_NOP_MSG_FAILED:
+ case CS_PARITY_ERROR_MSG_FAILED:
+ case CS_DEVICE_RESET_MSG_FAILED:
+ case CS_ID_MSG_FAILED:
+ case CS_UNEXP_BUS_FREE:
+ case CS_INVALID_ENTRY_TYPE:
+ case CS_DEVICE_QUEUE_FULL:
+ case CS_SCSI_PHASE_SKIPPED:
+ case CS_ARS_FAILED:
+ host_status = DID_ERROR;
+ break;
+ case CS_DATA_UNDERRUN:
+ host_status = DID_OK;
+ break;
+ default:
+ printk("qlogicisp : unknown completion status 0x%04x\n",
+ sts->completion_status);
+ host_status = DID_ERROR;
+ break;
+ }
+
+ DEBUG_INTR(printk("qlogicisp : host status (%s) scsi status %x\n",
+ reason[host_status], sts->scsi_status));
+
+ LEAVE("isp1020_return_status");
+
+ return (sts->scsi_status & STATUS_MASK) | (host_status << 16);
+}
+
+
+int isp1020_abort(Scsi_Cmnd *Cmnd)
+{
+ u_short param[6];
+ struct Scsi_Host *host;
+ struct isp1020_hostdata *hostdata;
+ int return_status = SCSI_ABORT_SUCCESS;
+ u_int cmdaddr = virt_to_bus(Cmnd);
+
+ ENTER("isp1020_abort");
+
+ host = Cmnd->host;
+ hostdata = (struct isp1020_hostdata *) host->hostdata;
+
+ isp1020_disable_irqs(host);
+
+ param[0] = MBOX_ABORT;
+ param[1] = (((u_short) Cmnd->target) << 8) | Cmnd->lun;
+ param[2] = cmdaddr >> 16;
+ param[3] = cmdaddr & 0xffff;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : scsi abort failure: %x\n", param[0]);
+ return_status = SCSI_ABORT_ERROR;
+ }
+
+ isp1020_enable_irqs(host);
+
+ LEAVE("isp1020_abort");
+
+ return return_status;
+}
+
+
+int isp1020_reset(Scsi_Cmnd *Cmnd, unsigned int reset_flags)
+{
+ u_short param[6];
+ struct Scsi_Host *host;
+ struct isp1020_hostdata *hostdata;
+ int return_status = SCSI_RESET_SUCCESS;
+
+ ENTER("isp1020_reset");
+
+ host = Cmnd->host;
+ hostdata = (struct isp1020_hostdata *) host->hostdata;
+
+ param[0] = MBOX_BUS_RESET;
+ param[1] = hostdata->host_param.bus_reset_delay;
+
+ isp1020_disable_irqs(host);
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : scsi bus reset failure: %x\n", param[0]);
+ return_status = SCSI_RESET_ERROR;
+ }
+
+ isp1020_enable_irqs(host);
+
+ LEAVE("isp1020_reset");
+
+ return return_status;;
+}
+
+
+int isp1020_biosparam(Disk *disk, kdev_t n, int ip[])
+{
+ int size = disk->capacity;
+
+ ENTER("isp1020_biosparam");
+
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ if (ip[2] > 1024) {
+ ip[0] = 255;
+ ip[1] = 63;
+ ip[2] = size / (ip[0] * ip[1]);
+ if (ip[2] > 1023)
+ ip[2] = 1023;
+ }
+
+ LEAVE("isp1020_biosparam");
+
+ return 0;
+}
+
+
+static int isp1020_reset_hardware(struct Scsi_Host *host)
+{
+ u_short param[6];
+ int loop_count;
+
+ ENTER("isp1020_reset_hardware");
+
+ outw(ISP_RESET, host->io_port + PCI_INTF_CTL);
+ outw(HCCR_RESET, host->io_port + HOST_HCCR);
+ outw(HCCR_RELEASE, host->io_port + HOST_HCCR);
+ outw(HCCR_BIOS_DISABLE, host->io_port + HOST_HCCR);
+
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && inw(host->io_port + HOST_HCCR) == RISC_BUSY)
+ barrier();
+ if (!loop_count)
+ printk("qlogicisp: reset_hardware loop timeout\n");
+
+ outw(0, host->io_port + ISP_CFG1);
+
+#if DEBUG_ISP1020
+ printk("qlogicisp : mbox 0 0x%04x \n", inw(host->io_port + MBOX0));
+ printk("qlogicisp : mbox 1 0x%04x \n", inw(host->io_port + MBOX1));
+ printk("qlogicisp : mbox 2 0x%04x \n", inw(host->io_port + MBOX2));
+ printk("qlogicisp : mbox 3 0x%04x \n", inw(host->io_port + MBOX3));
+ printk("qlogicisp : mbox 4 0x%04x \n", inw(host->io_port + MBOX4));
+ printk("qlogicisp : mbox 5 0x%04x \n", inw(host->io_port + MBOX5));
+#endif /* DEBUG_ISP1020 */
+
+ DEBUG(printk("qlogicisp : loading risc ram\n"));
+
+#if RELOAD_FIRMWARE
+ {
+ int i;
+ for (i = 0; i < risc_code_length01; i++) {
+ param[0] = MBOX_WRITE_RAM_WORD;
+ param[1] = risc_code_addr01 + i;
+ param[2] = risc_code01[i];
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : firmware load failure\n");
+ return 1;
+ }
+ }
+ }
+#endif /* RELOAD_FIRMWARE */
+
+ DEBUG(printk("qlogicisp : verifying checksum\n"));
+
+ param[0] = MBOX_VERIFY_CHECKSUM;
+ param[1] = risc_code_addr01;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : ram checksum failure\n");
+ return 1;
+ }
+
+ DEBUG(printk("qlogicisp : executing firmware\n"));
+
+ param[0] = MBOX_EXEC_FIRMWARE;
+ param[1] = risc_code_addr01;
+
+ isp1020_mbox_command(host, param);
+
+ param[0] = MBOX_ABOUT_FIRMWARE;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ printk("qlogicisp : about firmware failure\n");
+ return 1;
+ }
+
+ DEBUG(printk("qlogicisp : firmware major revision %d\n", param[1]));
+ DEBUG(printk("qlogicisp : firmware minor revision %d\n", param[2]));
+
+ LEAVE("isp1020_reset_hardware");
+
+ return 0;
+}
+
+
+static int isp1020_init(struct Scsi_Host *sh)
+{
+ u_int io_base;
+ struct isp1020_hostdata *hostdata;
+ u_char bus, device_fn, revision, irq;
+ u_short vendor_id, device_id, command;
+
+ ENTER("isp1020_init");
+
+ hostdata = (struct isp1020_hostdata *) sh->hostdata;
+ bus = hostdata->bus;
+ device_fn = hostdata->device_fn;
+
+ if (pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id)
+ || pcibios_read_config_word(bus, device_fn,
+ PCI_DEVICE_ID, &device_id)
+ || pcibios_read_config_word(bus, device_fn,
+ PCI_COMMAND, &command)
+ || pcibios_read_config_dword(bus, device_fn,
+ PCI_BASE_ADDRESS_0, &io_base)
+ || pcibios_read_config_byte(bus, device_fn,
+ PCI_CLASS_REVISION, &revision)
+ || pcibios_read_config_byte(bus, device_fn,
+ PCI_INTERRUPT_LINE, &irq))
+ {
+ printk("qlogicisp : error reading PCI configuration\n");
+ return 1;
+ }
+
+ if (vendor_id != PCI_VENDOR_ID_QLOGIC) {
+ printk("qlogicisp : 0x%04x is not QLogic vendor ID\n",
+ vendor_id);
+ return 1;
+ }
+
+ if (device_id != PCI_DEVICE_ID_QLOGIC_ISP1020) {
+ printk("qlogicisp : 0x%04x does not match ISP1020 device id\n",
+ device_id);
+ return 1;
+ }
+
+ if (command & PCI_COMMAND_IO && (io_base & 3) == 1)
+ io_base &= PCI_BASE_ADDRESS_IO_MASK;
+ else {
+ printk("qlogicisp : i/o mapping is disabled\n");
+ return 1;
+ }
+
+ if (!(command & PCI_COMMAND_MASTER)) {
+ printk("qlogicisp : bus mastering is disabled\n");
+ return 1;
+ }
+
+ if (revision != ISP1020_REV_ID)
+ printk("qlogicisp : new isp1020 revision ID (%d)\n", revision);
+
+ if (inw(io_base + PCI_ID_LOW) != PCI_VENDOR_ID_QLOGIC
+ || inw(io_base + PCI_ID_HIGH) != PCI_DEVICE_ID_QLOGIC_ISP1020)
+ {
+ printk("qlogicisp : can't decode i/o address space at 0x%x\n",
+ io_base);
+ return 1;
+ }
+
+ hostdata->revision = revision;
+
+ sh->irq = irq;
+ sh->io_port = io_base;
+
+ LEAVE("isp1020_init");
+
+ return 0;
+}
+
+
+#if USE_NVRAM_DEFAULTS
+
+static int isp1020_get_defaults(struct Scsi_Host *host)
+{
+ int i;
+ u_short value;
+ struct isp1020_hostdata *hostdata =
+ (struct isp1020_hostdata *) host->hostdata;
+
+ ENTER("isp1020_get_defaults");
+
+ if (!isp1020_verify_nvram(host)) {
+ printk("qlogicisp : nvram checksum failure\n");
+ printk("qlogicisp : attempting to use default parameters\n");
+ return isp1020_set_defaults(host);
+ }
+
+ value = isp1020_read_nvram_word(host, 2);
+ hostdata->host_param.fifo_threshold = (value >> 8) & 0x03;
+ hostdata->host_param.host_adapter_enable = (value >> 11) & 0x01;
+ hostdata->host_param.initiator_scsi_id = (value >> 12) & 0x0f;
+
+ value = isp1020_read_nvram_word(host, 3);
+ hostdata->host_param.bus_reset_delay = value & 0xff;
+ hostdata->host_param.retry_count = value >> 8;
+
+ value = isp1020_read_nvram_word(host, 4);
+ hostdata->host_param.retry_delay = value & 0xff;
+ hostdata->host_param.async_data_setup_time = (value >> 8) & 0x0f;
+ hostdata->host_param.req_ack_active_negation = (value >> 12) & 0x01;
+ hostdata->host_param.data_line_active_negation = (value >> 13) & 0x01;
+ hostdata->host_param.data_dma_burst_enable = (value >> 14) & 0x01;
+ hostdata->host_param.command_dma_burst_enable = (value >> 15);
+
+ value = isp1020_read_nvram_word(host, 5);
+ hostdata->host_param.tag_aging = value & 0xff;
+
+ value = isp1020_read_nvram_word(host, 6);
+ hostdata->host_param.selection_timeout = value & 0xffff;
+
+ value = isp1020_read_nvram_word(host, 7);
+ hostdata->host_param.max_queue_depth = value & 0xffff;
+
+#if DEBUG_ISP1020_SETUP
+ printk("qlogicisp : fifo threshold=%d\n",
+ hostdata->host_param.fifo_threshold);
+ printk("qlogicisp : initiator scsi id=%d\n",
+ hostdata->host_param.initiator_scsi_id);
+ printk("qlogicisp : bus reset delay=%d\n",
+ hostdata->host_param.bus_reset_delay);
+ printk("qlogicisp : retry count=%d\n",
+ hostdata->host_param.retry_count);
+ printk("qlogicisp : retry delay=%d\n",
+ hostdata->host_param.retry_delay);
+ printk("qlogicisp : async data setup time=%d\n",
+ hostdata->host_param.async_data_setup_time);
+ printk("qlogicisp : req/ack active negation=%d\n",
+ hostdata->host_param.req_ack_active_negation);
+ printk("qlogicisp : data line active negation=%d\n",
+ hostdata->host_param.data_line_active_negation);
+ printk("qlogicisp : data DMA burst enable=%d\n",
+ hostdata->host_param.data_dma_burst_enable);
+ printk("qlogicisp : command DMA burst enable=%d\n",
+ hostdata->host_param.command_dma_burst_enable);
+ printk("qlogicisp : tag age limit=%d\n",
+ hostdata->host_param.tag_aging);
+ printk("qlogicisp : selection timeout limit=%d\n",
+ hostdata->host_param.selection_timeout);
+ printk("qlogicisp : max queue depth=%d\n",
+ hostdata->host_param.max_queue_depth);
+#endif /* DEBUG_ISP1020_SETUP */
+
+ for (i = 0; i < MAX_TARGETS; i++) {
+
+ value = isp1020_read_nvram_word(host, 14 + i * 3);
+ hostdata->dev_param[i].device_flags = value & 0xff;
+ hostdata->dev_param[i].execution_throttle = value >> 8;
+
+ value = isp1020_read_nvram_word(host, 15 + i * 3);
+ hostdata->dev_param[i].synchronous_period = value & 0xff;
+ hostdata->dev_param[i].synchronous_offset = (value >> 8) & 0x0f;
+ hostdata->dev_param[i].device_enable = (value >> 12) & 0x01;
+
+#if DEBUG_ISP1020_SETUP
+ printk("qlogicisp : target 0x%02x\n", i);
+ printk("qlogicisp : device flags=0x%02x\n",
+ hostdata->dev_param[i].device_flags);
+ printk("qlogicisp : execution throttle=%d\n",
+ hostdata->dev_param[i].execution_throttle);
+ printk("qlogicisp : synchronous period=%d\n",
+ hostdata->dev_param[i].synchronous_period);
+ printk("qlogicisp : synchronous offset=%d\n",
+ hostdata->dev_param[i].synchronous_offset);
+ printk("qlogicisp : device enable=%d\n",
+ hostdata->dev_param[i].device_enable);
+#endif /* DEBUG_ISP1020_SETUP */
+ }
+
+ LEAVE("isp1020_get_defaults");
+
+ return 0;
+}
+
+
+#define ISP1020_NVRAM_LEN 0x40
+#define ISP1020_NVRAM_SIG1 0x5349
+#define ISP1020_NVRAM_SIG2 0x2050
+
+static int isp1020_verify_nvram(struct Scsi_Host *host)
+{
+ int i;
+ u_short value;
+ u_char checksum = 0;
+
+ for (i = 0; i < ISP1020_NVRAM_LEN; i++) {
+ value = isp1020_read_nvram_word(host, i);
+
+ switch (i) {
+ case 0:
+ if (value != ISP1020_NVRAM_SIG1) return 0;
+ break;
+ case 1:
+ if (value != ISP1020_NVRAM_SIG2) return 0;
+ break;
+ case 2:
+ if ((value & 0xff) != 0x02) return 0;
+ break;
+ }
+ checksum += value & 0xff;
+ checksum += value >> 8;
+ }
+
+ return (checksum == 0);
+}
+
+#define NVRAM_DELAY() udelay(2) /* 2 microsecond delay */
+
+
+u_short isp1020_read_nvram_word(struct Scsi_Host *host, u_short byte)
+{
+ int i;
+ u_short value, output, input;
+
+ byte &= 0x3f; byte |= 0x0180;
+
+ for (i = 8; i >= 0; i--) {
+ output = ((byte >> i) & 0x1) ? 0x4 : 0x0;
+ outw(output | 0x2, host->io_port + PCI_NVRAM); NVRAM_DELAY();
+ outw(output | 0x3, host->io_port + PCI_NVRAM); NVRAM_DELAY();
+ outw(output | 0x2, host->io_port + PCI_NVRAM); NVRAM_DELAY();
+ }
+
+ for (i = 0xf, value = 0; i >= 0; i--) {
+ value <<= 1;
+ outw(0x3, host->io_port + PCI_NVRAM); NVRAM_DELAY();
+ input = inw(host->io_port + PCI_NVRAM); NVRAM_DELAY();
+ outw(0x2, host->io_port + PCI_NVRAM); NVRAM_DELAY();
+ if (input & 0x8) value |= 1;
+ }
+
+ outw(0x0, host->io_port + PCI_NVRAM); NVRAM_DELAY();
+
+ return value;
+}
+
+#endif /* USE_NVRAM_DEFAULTS */
+
+
+static int isp1020_set_defaults(struct Scsi_Host *host)
+{
+ struct isp1020_hostdata *hostdata =
+ (struct isp1020_hostdata *) host->hostdata;
+ int i;
+
+ ENTER("isp1020_set_defaults");
+
+ hostdata->host_param.fifo_threshold = 2;
+ hostdata->host_param.host_adapter_enable = 1;
+ hostdata->host_param.initiator_scsi_id = 7;
+ hostdata->host_param.bus_reset_delay = 3;
+ hostdata->host_param.retry_count = 0;
+ hostdata->host_param.retry_delay = 1;
+ hostdata->host_param.async_data_setup_time = 6;
+ hostdata->host_param.req_ack_active_negation = 1;
+ hostdata->host_param.data_line_active_negation = 1;
+ hostdata->host_param.data_dma_burst_enable = 1;
+ hostdata->host_param.command_dma_burst_enable = 1;
+ hostdata->host_param.tag_aging = 8;
+ hostdata->host_param.selection_timeout = 250;
+ hostdata->host_param.max_queue_depth = 256;
+
+ for (i = 0; i < MAX_TARGETS; i++) {
+ hostdata->dev_param[i].device_flags = 0xfd;
+ hostdata->dev_param[i].execution_throttle = 16;
+ hostdata->dev_param[i].synchronous_period = 25;
+ hostdata->dev_param[i].synchronous_offset = 12;
+ hostdata->dev_param[i].device_enable = 1;
+ }
+
+ LEAVE("isp1020_set_defaults");
+
+ return 0;
+}
+
+
+static int isp1020_load_parameters(struct Scsi_Host *host)
+{
+ int i, k;
+ u_int queue_addr;
+ u_short param[6];
+ u_short isp_cfg1;
+ unsigned long flags;
+ struct isp1020_hostdata *hostdata =
+ (struct isp1020_hostdata *) host->hostdata;
+
+ ENTER("isp1020_load_parameters");
+
+ save_flags(flags);
+ cli();
+
+ outw(hostdata->host_param.fifo_threshold, host->io_port + ISP_CFG1);
+
+ param[0] = MBOX_SET_INIT_SCSI_ID;
+ param[1] = hostdata->host_param.initiator_scsi_id;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ restore_flags(flags);
+ printk("qlogicisp : set initiator id failure\n");
+ return 1;
+ }
+
+ param[0] = MBOX_SET_RETRY_COUNT;
+ param[1] = hostdata->host_param.retry_count;
+ param[2] = hostdata->host_param.retry_delay;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ restore_flags(flags);
+ printk("qlogicisp : set retry count failure\n");
+ return 1;
+ }
+
+ param[0] = MBOX_SET_ASYNC_DATA_SETUP_TIME;
+ param[1] = hostdata->host_param.async_data_setup_time;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ restore_flags(flags);
+ printk("qlogicisp : async data setup time failure\n");
+ return 1;
+ }
+
+ param[0] = MBOX_SET_ACTIVE_NEG_STATE;
+ param[1] = (hostdata->host_param.req_ack_active_negation << 4)
+ | (hostdata->host_param.data_line_active_negation << 5);
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ restore_flags(flags);
+ printk("qlogicisp : set active negation state failure\n");
+ return 1;
+ }
+
+ param[0] = MBOX_SET_PCI_CONTROL_PARAMS;
+ param[1] = hostdata->host_param.data_dma_burst_enable << 1;
+ param[2] = hostdata->host_param.command_dma_burst_enable << 1;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ restore_flags(flags);
+ printk("qlogicisp : set pci control parameter failure\n");
+ return 1;
+ }
+
+ isp_cfg1 = inw(host->io_port + ISP_CFG1);
+
+ if (hostdata->host_param.data_dma_burst_enable
+ || hostdata->host_param.command_dma_burst_enable)
+ isp_cfg1 |= 0x0004;
+ else
+ isp_cfg1 &= 0xfffb;
+
+ outw(isp_cfg1, host->io_port + ISP_CFG1);
+
+ param[0] = MBOX_SET_TAG_AGE_LIMIT;
+ param[1] = hostdata->host_param.tag_aging;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ restore_flags(flags);
+ printk("qlogicisp : set tag age limit failure\n");
+ return 1;
+ }
+
+ param[0] = MBOX_SET_SELECT_TIMEOUT;
+ param[1] = hostdata->host_param.selection_timeout;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ restore_flags(flags);
+ printk("qlogicisp : set selection timeout failure\n");
+ return 1;
+ }
+
+ for (i = 0; i < MAX_TARGETS; i++) {
+
+ if (!hostdata->dev_param[i].device_enable)
+ continue;
+
+ param[0] = MBOX_SET_TARGET_PARAMS;
+ param[1] = i << 8;
+ param[2] = hostdata->dev_param[i].device_flags << 8;
+ param[3] = (hostdata->dev_param[i].synchronous_offset << 8)
+ | hostdata->dev_param[i].synchronous_period;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ restore_flags(flags);
+ printk("qlogicisp : set target parameter failure\n");
+ return 1;
+ }
+
+ for (k = 0; k < MAX_LUNS; k++) {
+
+ param[0] = MBOX_SET_DEV_QUEUE_PARAMS;
+ param[1] = (i << 8) | k;
+ param[2] = hostdata->host_param.max_queue_depth;
+ param[3] = hostdata->dev_param[i].execution_throttle;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ restore_flags(flags);
+ printk("qlogicisp : set device queue "
+ "parameter failure\n");
+ return 1;
+ }
+ }
+ }
+
+ queue_addr = (u_int) virt_to_bus(&hostdata->res[0][0]);
+
+ param[0] = MBOX_INIT_RES_QUEUE;
+ param[1] = RES_QUEUE_LEN + 1;
+ param[2] = (u_short) (queue_addr >> 16);
+ param[3] = (u_short) (queue_addr & 0xffff);
+ param[4] = 0;
+ param[5] = 0;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ restore_flags(flags);
+ printk("qlogicisp : set response queue failure\n");
+ return 1;
+ }
+
+ queue_addr = (u_int) virt_to_bus(&hostdata->req[0][0]);
+
+ param[0] = MBOX_INIT_REQ_QUEUE;
+ param[1] = QLOGICISP_REQ_QUEUE_LEN + 1;
+ param[2] = (u_short) (queue_addr >> 16);
+ param[3] = (u_short) (queue_addr & 0xffff);
+ param[4] = 0;
+
+ isp1020_mbox_command(host, param);
+
+ if (param[0] != MBOX_COMMAND_COMPLETE) {
+ restore_flags(flags);
+ printk("qlogicisp : set request queue failure\n");
+ return 1;
+ }
+
+ restore_flags(flags);
+
+ LEAVE("isp1020_load_parameters");
+
+ return 0;
+}
+
+
+/*
+ * currently, this is only called during initialization or abort/reset,
+ * at which times interrupts are disabled, so polling is OK, I guess...
+ */
+static int isp1020_mbox_command(struct Scsi_Host *host, u_short param[])
+{
+ int loop_count;
+
+ if (mbox_param[param[0]] == 0)
+ return 1;
+
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && inw(host->io_port + HOST_HCCR) & 0x0080)
+ barrier();
+ if (!loop_count)
+ printk("qlogicisp: mbox_command loop timeout #1\n");
+
+ switch(mbox_param[param[0]] >> 4) {
+ case 6: outw(param[5], host->io_port + MBOX5);
+ case 5: outw(param[4], host->io_port + MBOX4);
+ case 4: outw(param[3], host->io_port + MBOX3);
+ case 3: outw(param[2], host->io_port + MBOX2);
+ case 2: outw(param[1], host->io_port + MBOX1);
+ case 1: outw(param[0], host->io_port + MBOX0);
+ }
+
+ outw(0x0, host->io_port + PCI_SEMAPHORE);
+ outw(HCCR_CLEAR_RISC_INTR, host->io_port + HOST_HCCR);
+ outw(HCCR_SET_HOST_INTR, host->io_port + HOST_HCCR);
+
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && !(inw(host->io_port + PCI_INTF_STS) & 0x04))
+ barrier();
+ if (!loop_count)
+ printk("qlogicisp: mbox_command loop timeout #2\n");
+
+ loop_count = DEFAULT_LOOP_COUNT;
+ while (--loop_count && inw(host->io_port + MBOX0) == 0x04)
+ barrier();
+ if (!loop_count)
+ printk("qlogicisp: mbox_command loop timeout #3\n");
+
+ switch(mbox_param[param[0]] & 0xf) {
+ case 6: param[5] = inw(host->io_port + MBOX5);
+ case 5: param[4] = inw(host->io_port + MBOX4);
+ case 4: param[3] = inw(host->io_port + MBOX3);
+ case 3: param[2] = inw(host->io_port + MBOX2);
+ case 2: param[1] = inw(host->io_port + MBOX1);
+ case 1: param[0] = inw(host->io_port + MBOX0);
+ }
+
+ outw(0x0, host->io_port + PCI_SEMAPHORE);
+ outw(HCCR_CLEAR_RISC_INTR, host->io_port + HOST_HCCR);
+
+ return 0;
+}
+
+
+#if DEBUG_ISP1020_INTR
+
+void isp1020_print_status_entry(struct Status_Entry *status)
+{
+ int i;
+
+ printk("qlogicisp : entry count = 0x%02x, type = 0x%02x, flags = 0x%02x\n",
+ status->hdr.entry_cnt, status->hdr.entry_type, status->hdr.flags);
+ printk("qlogicisp : scsi status = 0x%04x, completion status = 0x%04x\n",
+ status->scsi_status, status->completion_status);
+ printk("qlogicisp : state flags = 0x%04x, status flags = 0x%04x\n",
+ status->state_flags, status->status_flags);
+ printk("qlogicisp : time = 0x%04x, request sense length = 0x%04x\n",
+ status->time, status->req_sense_len);
+ printk("qlogicisp : residual transfer length = 0x%08x\n", status->residual);
+
+ for (i = 0; i < status->req_sense_len; i++)
+ printk("qlogicisp : sense data = 0x%02x\n", status->req_sense_data[i]);
+}
+
+#endif /* DEBUG_ISP1020_INTR */
+
+
+#if DEBUG_ISP1020
+
+void isp1020_print_scsi_cmd(Scsi_Cmnd *cmd)
+{
+ int i;
+
+ printk("qlogicisp : target = 0x%02x, lun = 0x%02x, cmd_len = 0x%02x\n",
+ cmd->target, cmd->lun, cmd->cmd_len);
+ printk("qlogicisp : command = ");
+ for (i = 0; i < cmd->cmd_len; i++)
+ printk("0x%02x ", cmd->cmnd[i]);
+ printk("\n");
+}
+
+#endif /* DEBUG_ISP1020 */
+
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = QLOGICISP;
+
+#include "scsi_module.c"
+#endif /* MODULE */
diff --git a/linux/src/drivers/scsi/qlogicisp.h b/linux/src/drivers/scsi/qlogicisp.h
new file mode 100644
index 00000000..e5da4a2c
--- /dev/null
+++ b/linux/src/drivers/scsi/qlogicisp.h
@@ -0,0 +1,99 @@
+/*
+ * QLogic ISP1020 Intelligent SCSI Processor Driver (PCI)
+ * Written by Erik H. Moe, ehm@cris.com
+ * Copyright 1995, Erik H. Moe
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+/* Renamed and updated to 1.3.x by Michael Griffith <grif@cs.ucr.edu> */
+
+/*
+ * $Date: 1999/04/26 05:54:53 $
+ * $Revision: 1.1 $
+ *
+ * $Log: isp1020.h,v $
+ * Revision 0.5 1995/09/22 02:32:56 root
+ * do auto request sense
+ *
+ * Revision 0.4 1995/08/07 04:48:28 root
+ * supply firmware with driver.
+ * numerous bug fixes/general cleanup of code.
+ *
+ * Revision 0.3 1995/07/16 16:17:16 root
+ * added reset/abort code.
+ *
+ * Revision 0.2 1995/06/29 03:19:43 root
+ * fixed biosparam.
+ * added queue protocol.
+ *
+ * Revision 0.1 1995/06/25 01:56:13 root
+ * Initial release.
+ *
+ */
+
+#ifndef _QLOGICISP_H
+#define _QLOGICISP_H
+
+/*
+ * With the qlogic interface, every queue slot can hold a SCSI
+ * command with up to 4 scatter/gather entries. If we need more
+ * than 4 entries, continuation entries can be used that hold
+ * another 7 entries each. Unlike for other drivers, this means
+ * that the maximum number of scatter/gather entries we can
+ * support at any given time is a function of the number of queue
+ * slots available. That is, host->can_queue and host->sg_tablesize
+ * are dynamic and _not_ independent. This all works fine because
+ * requests are queued serially and the scatter/gather limit is
+ * determined for each queue request anew.
+ */
+#define QLOGICISP_REQ_QUEUE_LEN 63 /* must be power of two - 1 */
+#define QLOGICISP_MAX_SG(ql) (4 + ((ql) > 0) ? 7*((ql) - 1) : 0)
+
+int isp1020_detect(Scsi_Host_Template *);
+int isp1020_release(struct Scsi_Host *);
+const char * isp1020_info(struct Scsi_Host *);
+int isp1020_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int isp1020_abort(Scsi_Cmnd *);
+int isp1020_reset(Scsi_Cmnd *, unsigned int);
+int isp1020_biosparam(Disk *, kdev_t, int[]);
+
+#ifndef NULL
+#define NULL (0)
+#endif
+
+extern struct proc_dir_entry proc_scsi_isp1020;
+
+#define QLOGICISP { \
+ /* next */ NULL, \
+ /* usage_count */ NULL, \
+ /* proc dir */ NULL, \
+ /* procfs info */ NULL, \
+ /* name */ NULL, \
+ /* detect */ isp1020_detect, \
+ /* release */ isp1020_release, \
+ /* info */ isp1020_info, \
+ /* command */ NULL, \
+ /* queuecommand */ isp1020_queuecommand, \
+ /* abort */ isp1020_abort, \
+ /* reset */ isp1020_reset, \
+ /* slave_attach */ NULL, \
+ /* bios_param */ isp1020_biosparam, \
+ /* can_queue */ QLOGICISP_REQ_QUEUE_LEN, \
+ /* this_id */ -1, \
+ /* sg_tablesize */ QLOGICISP_MAX_SG(QLOGICISP_REQ_QUEUE_LEN), \
+ /* cmd_per_lun */ 1, \
+ /* present */ 0, \
+ /* unchecked_isa_dma */ 0, \
+ /* use_clustering */ DISABLE_CLUSTERING \
+}
+
+#endif /* _QLOGICISP_H */
diff --git a/linux/src/drivers/scsi/qlogicisp_asm.c b/linux/src/drivers/scsi/qlogicisp_asm.c
new file mode 100644
index 00000000..1fafc7ec
--- /dev/null
+++ b/linux/src/drivers/scsi/qlogicisp_asm.c
@@ -0,0 +1,1304 @@
+/*
+ * Version 2.10 Initiator Firmware (16:13 Oct 18, 1995)
+ */
+
+unsigned short risc_code_version = 2*1024+10;
+
+unsigned short risc_code_addr01 = 0x1000 ;
+
+#if RELOAD_FIRMWARE
+
+unsigned short risc_code01[] = {
+ 0x0078, 0x1041, 0x0000, 0x283a, 0x0000, 0x2043, 0x4f50, 0x5952,
+ 0x4947, 0x4854, 0x2031, 0x3939, 0x312c, 0x3139, 0x3932, 0x2c31,
+ 0x3939, 0x332c, 0x3139, 0x3934, 0x2051, 0x4c4f, 0x4749, 0x4320,
+ 0x434f, 0x5250, 0x4f52, 0x4154, 0x494f, 0x4e00, 0x2049, 0x5350,
+ 0x3130, 0x3230, 0x2046, 0x6972, 0x6d77, 0x6172, 0x6520, 0x2056,
+ 0x6572, 0x7369, 0x6f6e, 0x2030, 0x322e, 0x3130, 0x2020, 0x2043,
+ 0x7573, 0x746f, 0x6d65, 0x7220, 0x4e6f, 0x2e20, 0x3030, 0x2050,
+ 0x726f, 0x6475, 0x6374, 0x204e, 0x6f2e, 0x2020, 0x3030, 0x2020,
+ 0x2400, 0x20b9, 0x1212, 0x2071, 0x0010, 0x70c3, 0x0004, 0x20c9,
+ 0x43ff, 0x2089, 0x115b, 0x70c7, 0x4953, 0x70cb, 0x5020, 0x70cf,
+ 0x2020, 0x70d3, 0x0002, 0x3f00, 0x70d6, 0x20c1, 0x0008, 0x2019,
+ 0x0000, 0x2009, 0xfeff, 0x2100, 0x200b, 0xa5a5, 0xa1ec, 0x7fff,
+ 0x2d64, 0x206b, 0x0a0a, 0xaddc, 0x3fff, 0x2b54, 0x205b, 0x5050,
+ 0x2114, 0xa286, 0xa5a5, 0x0040, 0x10b3, 0xa386, 0x000f, 0x0040,
+ 0x1079, 0x2c6a, 0x2a5a, 0x20c1, 0x0000, 0x2019, 0x000f, 0x0078,
+ 0x1059, 0x2c6a, 0x2a5a, 0x20c1, 0x0008, 0x2009, 0x7fff, 0x2148,
+ 0x2944, 0x204b, 0x0a0a, 0xa9bc, 0x3fff, 0x2734, 0x203b, 0x5050,
+ 0x2114, 0xa286, 0x0a0a, 0x0040, 0x109d, 0x284a, 0x263a, 0x20c1,
+ 0x0004, 0x2009, 0x3fff, 0x2134, 0x200b, 0x5050, 0x2114, 0xa286,
+ 0x5050, 0x0040, 0x109e, 0x0078, 0x1163, 0x284a, 0x263a, 0x98c0,
+ 0xa188, 0x1000, 0x212c, 0x200b, 0xa5a5, 0x2114, 0xa286, 0xa5a5,
+ 0x0040, 0x10b0, 0x250a, 0xa18a, 0x1000, 0x98c1, 0x0078, 0x10b5,
+ 0x250a, 0x0078, 0x10b5, 0x2c6a, 0x2a5a, 0x2130, 0xa18a, 0x0040,
+ 0x2128, 0xa1a2, 0x3900, 0x8424, 0x8424, 0x8424, 0x8424, 0x8424,
+ 0x8424, 0xa192, 0x4400, 0x2009, 0x0000, 0x2001, 0x002f, 0x1078,
+ 0x1a70, 0x2218, 0x2079, 0x3900, 0x2fa0, 0x2408, 0x2011, 0x0000,
+ 0x20a9, 0x0040, 0x42a4, 0x8109, 0x00c0, 0x10d0, 0x7eea, 0x7dde,
+ 0x8528, 0x7dda, 0x7ce2, 0x7be6, 0x787b, 0x0000, 0x2031, 0x0030,
+ 0x78c3, 0x0101, 0x780b, 0x0002, 0x780f, 0x0002, 0x784f, 0x0003,
+ 0x2069, 0x3940, 0x681b, 0x0028, 0x6807, 0x0007, 0x680b, 0x00fa,
+ 0x680f, 0x0008, 0x6813, 0x0005, 0x681f, 0x0000, 0x6823, 0x0006,
+ 0x6817, 0x0008, 0x6827, 0x0000, 0x2069, 0x3a00, 0x2011, 0x0020,
+ 0x2009, 0x0010, 0x680b, 0x0c19, 0x680f, 0x0019, 0x6803, 0xfd00,
+ 0x6807, 0x0018, 0x6a1a, 0x2d00, 0xa0e8, 0x0008, 0xa290, 0x0004,
+ 0x8109, 0x00c0, 0x1102, 0x2069, 0x3a80, 0x20a9, 0x0080, 0x680b,
+ 0x0040, 0x7be8, 0xa386, 0xfeff, 0x00c0, 0x1124, 0x6817, 0x0100,
+ 0x681f, 0x0064, 0x0078, 0x1128, 0x6817, 0x0064, 0x681f, 0x0002,
+ 0xade8, 0x0010, 0x0070, 0x112e, 0x0078, 0x1117, 0x1078, 0x1d15,
+ 0x1078, 0x3366, 0x1078, 0x18a4, 0x1078, 0x37fc, 0x3200, 0xa085,
+ 0x000d, 0x2090, 0x70c3, 0x0000, 0x0090, 0x1145, 0x70c0, 0xa086,
+ 0x0002, 0x00c0, 0x1145, 0x1078, 0x1274, 0x1078, 0x1186, 0x78c0,
+ 0xa005, 0x00c0, 0x1151, 0x1078, 0x1a99, 0x0068, 0x1155, 0x1078,
+ 0x1c6f, 0x0068, 0x1155, 0x1078, 0x1997, 0x00e0, 0x1145, 0x1078,
+ 0x369a, 0x0078, 0x1145, 0x1163, 0x1165, 0x1ebb, 0x1ebb, 0x33d7,
+ 0x33d7, 0x1ebb, 0x1ebb, 0x0078, 0x1163, 0x0078, 0x1165, 0x0078,
+ 0x1167, 0x0078, 0x1169, 0x2009, 0x0022, 0x2104, 0xa086, 0x4000,
+ 0x0040, 0x1181, 0x7008, 0x800b, 0x00c8, 0x1181, 0x7007, 0x0002,
+ 0xa08c, 0x0060, 0x00c0, 0x1182, 0xa084, 0x0008, 0x0040, 0x1181,
+ 0x087a, 0x097a, 0x70c3, 0x4002, 0x0078, 0x1277, 0x0068, 0x11f1,
+ 0x2061, 0x0000, 0x6018, 0xa084, 0x0001, 0x00c0, 0x11f1, 0x7814,
+ 0xa005, 0x00c0, 0x1197, 0x0010, 0x11f2, 0x0078, 0x11f1, 0x2009,
+ 0x3968, 0x2104, 0xa005, 0x00c0, 0x11f1, 0x2009, 0x3971, 0x200b,
+ 0x0000, 0x7914, 0xa186, 0x0042, 0x00c0, 0x11bc, 0x7816, 0x2009,
+ 0x396f, 0x2164, 0x200b, 0x0000, 0x6018, 0x70c6, 0x6014, 0x70ca,
+ 0x611c, 0xa18c, 0xff00, 0x6020, 0xa084, 0x00ff, 0xa105, 0x70ce,
+ 0x1078, 0x1896, 0x0078, 0x11ef, 0x7814, 0xa086, 0x0018, 0x00c0,
+ 0x11c3, 0x1078, 0x1622, 0x7817, 0x0000, 0x2009, 0x396f, 0x2104,
+ 0xa065, 0x0040, 0x11df, 0x0c7e, 0x609c, 0x2060, 0x1078, 0x18f6,
+ 0x0c7f, 0x609f, 0x0000, 0x1078, 0x16e9, 0x2009, 0x001c, 0x6087,
+ 0x0103, 0x1078, 0x181d, 0x00c0, 0x11eb, 0x1078, 0x1896, 0x2009,
+ 0x396f, 0x200b, 0x0000, 0x2009, 0x3969, 0x2104, 0x200b, 0x0000,
+ 0xa005, 0x0040, 0x11ef, 0x2001, 0x4005, 0x0078, 0x1276, 0x0078,
+ 0x1274, 0x007c, 0x70c3, 0x0000, 0x70c7, 0x0000, 0x70cb, 0x0000,
+ 0x70cf, 0x0000, 0x70c0, 0xa0bc, 0xffc0, 0x00c0, 0x1242, 0x2038,
+ 0x0079, 0x1202, 0x1274, 0x12cf, 0x1293, 0x12cf, 0x1338, 0x1338,
+ 0x128a, 0x16fd, 0x1343, 0x1282, 0x1297, 0x1299, 0x129b, 0x129d,
+ 0x1702, 0x1282, 0x1355, 0x1380, 0x163a, 0x16f7, 0x129f, 0x1569,
+ 0x158b, 0x15a1, 0x15be, 0x1526, 0x1534, 0x1548, 0x155c, 0x13f3,
+ 0x1282, 0x13a1, 0x13a7, 0x13ac, 0x13b1, 0x13b7, 0x13bc, 0x13c1,
+ 0x13c6, 0x13cb, 0x13cf, 0x13e4, 0x13f0, 0x1282, 0x1282, 0x1282,
+ 0x1282, 0x13ff, 0x1408, 0x1417, 0x143d, 0x1447, 0x144e, 0x1474,
+ 0x1483, 0x1492, 0x14a4, 0x1506, 0x1516, 0x1282, 0x1282, 0x1282,
+ 0x1282, 0x151b, 0xa0bc, 0xffa0, 0x00c0, 0x1282, 0x2038, 0xa084,
+ 0x001f, 0x0079, 0x124b, 0x1719, 0x171c, 0x172c, 0x17a8, 0x17e1,
+ 0x1282, 0x1282, 0x1282, 0x1282, 0x1282, 0x1282, 0x1282, 0x1282,
+ 0x1282, 0x1282, 0x1282, 0x12c5, 0x132e, 0x134b, 0x1376, 0x1630,
+ 0x1282, 0x1282, 0x1282, 0x1282, 0x1282, 0x17f9, 0x1803, 0x1807,
+ 0x1815, 0x1282, 0x1282, 0x72ca, 0x71c6, 0x2001, 0x4006, 0x0078,
+ 0x1276, 0x73ce, 0x72ca, 0x71c6, 0x2001, 0x4000, 0x70c2, 0x0068,
+ 0x1277, 0x2061, 0x0000, 0x601b, 0x0001, 0x2091, 0x5000, 0x2091,
+ 0x4080, 0x007c, 0x70c3, 0x4001, 0x0078, 0x1277, 0x70c3, 0x4006,
+ 0x0078, 0x1277, 0x2099, 0x0041, 0x20a1, 0x0041, 0x20a9, 0x0005,
+ 0x53a3, 0x0078, 0x1274, 0x70c4, 0x70c3, 0x0004, 0x007a, 0x0078,
+ 0x1274, 0x0078, 0x1274, 0x0078, 0x1274, 0x0078, 0x1274, 0x2091,
+ 0x8000, 0x70c3, 0x0000, 0x70c7, 0x4953, 0x70cb, 0x5020, 0x70cf,
+ 0x2020, 0x70d3, 0x0002, 0x3f00, 0x70d6, 0x2079, 0x0000, 0x781b,
+ 0x0001, 0x2031, 0x0030, 0x2059, 0x1000, 0x2029, 0x0457, 0x2051,
+ 0x0470, 0x2061, 0x0472, 0x20b9, 0xffff, 0x20c1, 0x0000, 0x2091,
+ 0x5000, 0x2091, 0x4080, 0x0078, 0x0455, 0x1078, 0x1a04, 0x00c0,
+ 0x1286, 0x75d8, 0x74dc, 0x75da, 0x74de, 0x0078, 0x12d2, 0x2029,
+ 0x0000, 0x2520, 0x71d0, 0x72c8, 0x73cc, 0x70c4, 0x20a0, 0x2098,
+ 0x2031, 0x0030, 0x81ff, 0x0040, 0x1274, 0x7007, 0x0004, 0x731a,
+ 0x721e, 0x7422, 0x7526, 0x2051, 0x0012, 0x2049, 0x130d, 0x2041,
+ 0x1274, 0x7003, 0x0002, 0xa786, 0x0001, 0x0040, 0x12f5, 0xa786,
+ 0x0050, 0x0040, 0x12f5, 0x0078, 0x12fb, 0x2049, 0x131a, 0x2041,
+ 0x1326, 0x7003, 0x0003, 0x7017, 0x0000, 0x810b, 0x7112, 0x00c8,
+ 0x1303, 0x7017, 0x0001, 0x7007, 0x0001, 0xa786, 0x0001, 0x0040,
+ 0x131a, 0xa786, 0x0050, 0x0040, 0x131a, 0x700c, 0xa084, 0x007f,
+ 0x2009, 0x0040, 0xa102, 0x8004, 0x094a, 0x20a8, 0x26a0, 0x53a6,
+ 0x0078, 0x116b, 0x700c, 0xa084, 0x007f, 0x0040, 0x131a, 0x80ac,
+ 0x0048, 0x131a, 0x2698, 0x53a5, 0x0078, 0x116b, 0x700c, 0xa084,
+ 0x007f, 0x80ac, 0x2698, 0x53a5, 0x0078, 0x1274, 0x1078, 0x1a04,
+ 0x00c0, 0x1286, 0x75d8, 0x74dc, 0x75da, 0x74de, 0x0078, 0x12d2,
+ 0x71c4, 0x70c8, 0x2114, 0xa79e, 0x0004, 0x00c0, 0x1340, 0x200a,
+ 0x72ca, 0x0078, 0x1273, 0x70c7, 0x0002, 0x70cb, 0x000a, 0x70cf,
+ 0x0000, 0x0078, 0x1274, 0x1078, 0x1a04, 0x00c0, 0x1286, 0x75d8,
+ 0x76dc, 0x75da, 0x76de, 0x0078, 0x1358, 0x2029, 0x0000, 0x2530,
+ 0x70c4, 0x72c8, 0x73cc, 0x74d0, 0x70c6, 0x72ca, 0x73ce, 0x74d2,
+ 0xa005, 0x0040, 0x1370, 0x8001, 0x788a, 0x7a92, 0x7b96, 0x7d9a,
+ 0x7e9e, 0x7c8e, 0x78c0, 0xa084, 0xfffc, 0x78c2, 0x0078, 0x1374,
+ 0x78c0, 0xa085, 0x0001, 0x78c2, 0x0078, 0x1274, 0x1078, 0x1a04,
+ 0x00c0, 0x1286, 0x75d8, 0x76dc, 0x75da, 0x76de, 0x0078, 0x1383,
+ 0x2029, 0x0000, 0x2530, 0x70c4, 0x72c8, 0x73cc, 0x74d4, 0x70c6,
+ 0x72ca, 0x73ce, 0x74d6, 0xa005, 0x0040, 0x139b, 0x8001, 0x78a6,
+ 0x7aae, 0x7bb2, 0x7db6, 0x7eba, 0x7caa, 0x78c0, 0xa084, 0xfcff,
+ 0x78c2, 0x0078, 0x139f, 0x78c0, 0xa085, 0x0100, 0x78c2, 0x0078,
+ 0x1274, 0x2009, 0x395f, 0x210c, 0x7ae4, 0x0078, 0x1272, 0x2009,
+ 0x3941, 0x210c, 0x0078, 0x1273, 0x2009, 0x3942, 0x210c, 0x0078,
+ 0x1273, 0x2061, 0x3940, 0x610c, 0x6210, 0x0078, 0x1272, 0x2009,
+ 0x3945, 0x210c, 0x0078, 0x1273, 0x2009, 0x3946, 0x210c, 0x0078,
+ 0x1273, 0x2009, 0x3947, 0x210c, 0x0078, 0x1273, 0x2009, 0x3948,
+ 0x210c, 0x0078, 0x1273, 0x7908, 0x7a0c, 0x0078, 0x1272, 0x71c4,
+ 0x8107, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e8, 0x3a00,
+ 0x6a00, 0x6804, 0xa084, 0x0008, 0x0040, 0x13e1, 0x6b08, 0x0078,
+ 0x13e2, 0x6b0c, 0x0078, 0x1271, 0x77c4, 0x1078, 0x18b4, 0x2091,
+ 0x8000, 0x6b1c, 0x6a14, 0x2091, 0x8001, 0x2708, 0x0078, 0x1271,
+ 0x794c, 0x0078, 0x1273, 0x77c4, 0x1078, 0x18b4, 0x2091, 0x8000,
+ 0x6908, 0x6a18, 0x6b10, 0x2091, 0x8001, 0x0078, 0x1271, 0x71c4,
+ 0xa182, 0x0010, 0x00c8, 0x126c, 0x1078, 0x1d9b, 0x0078, 0x1271,
+ 0x71c4, 0xa182, 0x0010, 0x00c8, 0x126c, 0x2011, 0x3941, 0x2204,
+ 0x007e, 0x2112, 0x1078, 0x1d54, 0x017f, 0x0078, 0x1273, 0x71c4,
+ 0x2011, 0x1435, 0x20a9, 0x0008, 0x2204, 0xa106, 0x0040, 0x1427,
+ 0x8210, 0x0070, 0x1425, 0x0078, 0x141c, 0x0078, 0x126c, 0xa292,
+ 0x1435, 0x027e, 0x2011, 0x3942, 0x2204, 0x2112, 0x017f, 0x007e,
+ 0x1078, 0x1d60, 0x017f, 0x0078, 0x1273, 0x03e8, 0x00fa, 0x01f4,
+ 0x02ee, 0x0064, 0x0019, 0x0032, 0x004b, 0x2061, 0x3940, 0x610c,
+ 0x6210, 0x70c4, 0x600e, 0x70c8, 0x6012, 0x0078, 0x1272, 0x2061,
+ 0x3940, 0x6114, 0x70c4, 0x6016, 0x0078, 0x1273, 0x71c4, 0x2011,
+ 0x0004, 0x2019, 0x1212, 0xa186, 0x0028, 0x0040, 0x1467, 0x2011,
+ 0x0005, 0x2019, 0x1212, 0xa186, 0x0032, 0x0040, 0x1467, 0x2011,
+ 0x0006, 0x2019, 0x2323, 0xa186, 0x003c, 0x00c0, 0x126c, 0x2061,
+ 0x3940, 0x6018, 0x007e, 0x611a, 0x23b8, 0x1078, 0x1d71, 0x1078,
+ 0x37fc, 0x017f, 0x0078, 0x1273, 0x71c4, 0xa184, 0xffcf, 0x00c0,
+ 0x126c, 0x2011, 0x3947, 0x2204, 0x2112, 0x007e, 0x1078, 0x1d93,
+ 0x017f, 0x0078, 0x1273, 0x71c4, 0xa182, 0x0010, 0x00c8, 0x126c,
+ 0x2011, 0x3948, 0x2204, 0x007e, 0x2112, 0x1078, 0x1d82, 0x017f,
+ 0x0078, 0x1273, 0x71c4, 0x72c8, 0xa184, 0xfffd, 0x00c0, 0x126b,
+ 0xa284, 0xfffd, 0x00c0, 0x126b, 0x2100, 0x7908, 0x780a, 0x2200,
+ 0x7a0c, 0x780e, 0x0078, 0x1272, 0x71c4, 0x8107, 0xa084, 0x000f,
+ 0x8003, 0x8003, 0x8003, 0xa0e8, 0x3a00, 0x2019, 0x0000, 0x72c8,
+ 0x6800, 0x007e, 0xa226, 0x0040, 0x14d3, 0x6a02, 0xa484, 0x2000,
+ 0x0040, 0x14bc, 0xa39d, 0x0010, 0xa484, 0x1000, 0x0040, 0x14c2,
+ 0xa39d, 0x0008, 0xa484, 0x4000, 0x0040, 0x14d3, 0x810f, 0xa284,
+ 0x4000, 0x0040, 0x14cf, 0x1078, 0x1db5, 0x0078, 0x14d3, 0x1078,
+ 0x1da7, 0x0078, 0x14d3, 0x72cc, 0x82ff, 0x0040, 0x14ff, 0x6808,
+ 0xa206, 0x0040, 0x14ff, 0xa2a4, 0x00ff, 0x2061, 0x3940, 0x6118,
+ 0xa186, 0x0028, 0x0040, 0x14ec, 0xa186, 0x0032, 0x0040, 0x14f2,
+ 0xa186, 0x003c, 0x0040, 0x14f8, 0xa482, 0x0064, 0x00c8, 0x126d,
+ 0x0078, 0x14fc, 0xa482, 0x0050, 0x00c8, 0x126d, 0x0078, 0x14fc,
+ 0xa482, 0x0043, 0x00c8, 0x126d, 0x6a0a, 0xa39d, 0x000a, 0x6804,
+ 0xa305, 0x6806, 0x027f, 0x6b0c, 0x0078, 0x1271, 0x77c4, 0x1078,
+ 0x18b4, 0x2091, 0x8000, 0x6a14, 0x6b1c, 0x2091, 0x8001, 0x70c8,
+ 0x6816, 0x70cc, 0x681e, 0x2708, 0x0078, 0x1271, 0x70c4, 0x794c,
+ 0x784e, 0x0078, 0x1273, 0x71c4, 0x72c8, 0x73cc, 0xa182, 0x0010,
+ 0x00c8, 0x126c, 0x1078, 0x1dc3, 0x0078, 0x1271, 0x77c4, 0x1078,
+ 0x18b4, 0x2091, 0x8000, 0x6a08, 0xa295, 0x0002, 0x6a0a, 0x2091,
+ 0x8001, 0x2708, 0x0078, 0x1272, 0x77c4, 0x1078, 0x18b4, 0x2091,
+ 0x8000, 0x6a08, 0xa294, 0xfff9, 0x6a0a, 0x6804, 0xa005, 0x0040,
+ 0x1543, 0x1078, 0x1cf6, 0x2091, 0x8001, 0x2708, 0x0078, 0x1272,
+ 0x77c4, 0x1078, 0x18b4, 0x2091, 0x8000, 0x6a08, 0xa295, 0x0004,
+ 0x6a0a, 0x6804, 0xa005, 0x0040, 0x1557, 0x1078, 0x1cf6, 0x2091,
+ 0x8001, 0x2708, 0x0078, 0x1272, 0x77c4, 0x2041, 0x0001, 0x2049,
+ 0x0005, 0x2051, 0x0020, 0x1078, 0x18c1, 0x2708, 0x6a08, 0x0078,
+ 0x1272, 0x77c4, 0x73c8, 0x72cc, 0x77c6, 0x73ca, 0x72ce, 0x1078,
+ 0x193c, 0x00c0, 0x1587, 0x6818, 0xa005, 0x0040, 0x1581, 0x2708,
+ 0x1078, 0x1dd3, 0x00c0, 0x1581, 0x7817, 0x0015, 0x2091, 0x8001,
+ 0x007c, 0x2091, 0x8001, 0x2001, 0x4005, 0x0078, 0x1276, 0x2091,
+ 0x8001, 0x0078, 0x1274, 0x77c4, 0x77c6, 0x2061, 0x3940, 0x60a3,
+ 0x0003, 0x67b6, 0x60c7, 0x000f, 0x2041, 0x0021, 0x2049, 0x0005,
+ 0x2051, 0x0020, 0x1078, 0x18c1, 0x7817, 0x0016, 0x1078, 0x1cf6,
+ 0x007c, 0x77c4, 0x77c6, 0xa7bc, 0xff00, 0x2061, 0x3940, 0x60a3,
+ 0x0002, 0x67b6, 0x60c7, 0x000f, 0x7817, 0x0017, 0x1078, 0x1cf6,
+ 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0010, 0x1078, 0x18c1,
+ 0x8738, 0xa784, 0x0007, 0x00c0, 0x15b6, 0x007c, 0x78c0, 0xa084,
+ 0x0003, 0x00c0, 0x15e2, 0x2039, 0x0000, 0x2041, 0x0021, 0x2049,
+ 0x0004, 0x2051, 0x0008, 0x1078, 0x18b4, 0x2091, 0x8000, 0x6808,
+ 0xa80d, 0x690a, 0x2091, 0x8001, 0x8738, 0xa784, 0x0007, 0x00c0,
+ 0x15cb, 0xa7bc, 0xff00, 0x873f, 0x8738, 0x873f, 0xa784, 0x0f00,
+ 0x00c0, 0x15cb, 0x2091, 0x8000, 0x2069, 0x0100, 0x6830, 0xa084,
+ 0x0040, 0x0040, 0x160b, 0x684b, 0x0004, 0x20a9, 0x0014, 0x6848,
+ 0xa084, 0x0004, 0x0040, 0x15f8, 0x0070, 0x15f8, 0x0078, 0x15ef,
+ 0x684b, 0x0009, 0x20a9, 0x0014, 0x6848, 0xa084, 0x0001, 0x0040,
+ 0x1605, 0x0070, 0x1605, 0x0078, 0x15fc, 0x20a9, 0x00fa, 0x0070,
+ 0x160b, 0x0078, 0x1607, 0x2079, 0x3900, 0x7817, 0x0018, 0x2061,
+ 0x3940, 0x60a3, 0x0001, 0x60c7, 0x000f, 0x78c0, 0xa085, 0x0002,
+ 0x78c2, 0x6808, 0xa084, 0xfffd, 0x680a, 0x681b, 0x0047, 0x2091,
+ 0x8001, 0x007c, 0x78c0, 0xa084, 0xfffd, 0x78c2, 0xa084, 0x0001,
+ 0x00c0, 0x162c, 0x1078, 0x197e, 0x71c4, 0x71c6, 0x794a, 0x007c,
+ 0x1078, 0x1a04, 0x00c0, 0x1286, 0x75d8, 0x74dc, 0x75da, 0x74de,
+ 0x0078, 0x163d, 0x2029, 0x0000, 0x2520, 0x71c4, 0x73c8, 0x72cc,
+ 0x71c6, 0x73ca, 0x72ce, 0x2079, 0x3900, 0x1078, 0x188d, 0x0040,
+ 0x16e5, 0x20a9, 0x0005, 0x20a1, 0x3916, 0x41a1, 0x2009, 0x0040,
+ 0x1078, 0x1857, 0x0040, 0x1658, 0x1078, 0x1896, 0x0078, 0x16e5,
+ 0x6004, 0xa084, 0xff00, 0x8007, 0x8009, 0x0040, 0x16b9, 0x0c7e,
+ 0x2c68, 0x1078, 0x188d, 0x0040, 0x1688, 0x2c00, 0x689e, 0x8109,
+ 0x00c0, 0x1660, 0x609f, 0x0000, 0x0c7f, 0x0c7e, 0x7218, 0x731c,
+ 0x7420, 0x7524, 0x2c68, 0x689c, 0xa065, 0x0040, 0x16b8, 0x2009,
+ 0x0040, 0x1078, 0x1857, 0x00c0, 0x16a1, 0x6004, 0xa084, 0x00ff,
+ 0xa086, 0x0002, 0x00c0, 0x1688, 0x2d00, 0x6002, 0x0078, 0x166e,
+ 0x0c7f, 0x0c7e, 0x609c, 0x2060, 0x1078, 0x18f6, 0x0c7f, 0x609f,
+ 0x0000, 0x1078, 0x16e9, 0x2009, 0x001c, 0x6008, 0xa085, 0x0200,
+ 0x600a, 0x6004, 0x6086, 0x1078, 0x181d, 0x1078, 0x1896, 0x0078,
+ 0x16e5, 0x0c7f, 0x0c7e, 0x609c, 0x2060, 0x1078, 0x18f6, 0x0c7f,
+ 0x609f, 0x0000, 0x1078, 0x16e9, 0x2009, 0x001c, 0x6087, 0x0103,
+ 0x601b, 0x0003, 0x1078, 0x181d, 0x1078, 0x1896, 0x0078, 0x16e5,
+ 0x0c7f, 0x74c4, 0x73c8, 0x72cc, 0x6014, 0x7817, 0x0012, 0x0e7e,
+ 0x2071, 0x3940, 0x70a3, 0x0005, 0x70a7, 0x0000, 0x73aa, 0x72ae,
+ 0x74b2, 0x70b6, 0x70bb, 0x0000, 0x2c00, 0x70be, 0x70c3, 0x0000,
+ 0xa02e, 0x2530, 0x611c, 0xa184, 0x0060, 0x0040, 0x16d9, 0x1078,
+ 0x330a, 0x0e7f, 0x6596, 0x65a6, 0x669a, 0x669a, 0x60af, 0x0000,
+ 0x60b3, 0x0000, 0x1078, 0x1cf6, 0x007c, 0x70c3, 0x4005, 0x0078,
+ 0x1277, 0x20a9, 0x0005, 0x2099, 0x3916, 0x530a, 0x2100, 0xa210,
+ 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x007c, 0x71c4,
+ 0x70c7, 0x0000, 0x7906, 0x0078, 0x1274, 0x71c4, 0x71c6, 0x2168,
+ 0x0078, 0x1704, 0x2069, 0x1000, 0x690c, 0xa016, 0x2d04, 0xa210,
+ 0x8d68, 0x8109, 0x00c0, 0x1706, 0xa285, 0x0000, 0x00c0, 0x1714,
+ 0x70c3, 0x4000, 0x0078, 0x1716, 0x70c3, 0x4003, 0x70ca, 0x0078,
+ 0x1277, 0x79d8, 0x0078, 0x1273, 0x71c4, 0x71c6, 0x2198, 0x20a1,
+ 0x0042, 0x20a9, 0x0004, 0x53a3, 0x21a0, 0x2099, 0x0042, 0x20a9,
+ 0x0004, 0x53a3, 0x0078, 0x1274, 0x70c4, 0x2068, 0x2079, 0x3900,
+ 0x1078, 0x188d, 0x0040, 0x17a4, 0x6007, 0x0001, 0x600b, 0x0000,
+ 0x602b, 0x0000, 0x601b, 0x0006, 0x6a10, 0xa28c, 0x0007, 0xa284,
+ 0x00f0, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0x6016, 0xa284,
+ 0x0800, 0x0040, 0x174f, 0x601b, 0x000a, 0x0078, 0x1755, 0xa284,
+ 0x1000, 0x0040, 0x1755, 0x601b, 0x000c, 0xa284, 0x0300, 0x0040,
+ 0x175e, 0x602b, 0x0001, 0x8004, 0x8004, 0x8004, 0xa085, 0x0001,
+ 0x601e, 0x6023, 0x0000, 0x6027, 0x000a, 0xa284, 0x0400, 0x0040,
+ 0x176b, 0x602b, 0x0000, 0x20a9, 0x0006, 0xac80, 0x000b, 0x20a0,
+ 0xad80, 0x0005, 0x2098, 0x53a3, 0xa284, 0x0300, 0x00c0, 0x1780,
+ 0x6046, 0x604a, 0x604e, 0x6052, 0x6096, 0x609a, 0x0078, 0x178a,
+ 0x6800, 0x6046, 0x6804, 0x604a, 0x6e08, 0x664e, 0x6d0c, 0x6552,
+ 0x6596, 0x669a, 0x6014, 0x7817, 0x0042, 0x2c08, 0x2061, 0x3940,
+ 0x60a3, 0x0005, 0x60a7, 0x0000, 0x60ab, 0x0000, 0x60af, 0x0000,
+ 0x60b3, 0x0000, 0x60b6, 0x61be, 0xa284, 0x0400, 0x60c2, 0x1078,
+ 0x32f5, 0x1078, 0x1cf6, 0x007c, 0x70c3, 0x4005, 0x0078, 0x1277,
+ 0x78f0, 0xa005, 0x0040, 0x1282, 0x2091, 0x8000, 0x70c4, 0x800a,
+ 0x2011, 0x0010, 0x810c, 0x0048, 0x17ba, 0x3a00, 0xa084, 0xfff7,
+ 0x0078, 0x17bd, 0x3a00, 0xa085, 0x0008, 0x20d0, 0x0005, 0x0005,
+ 0xa084, 0xfffb, 0x20d0, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
+ 0x0005, 0x0005, 0x0005, 0xa085, 0x0004, 0x20d0, 0x0005, 0x0005,
+ 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x8211, 0x00c0,
+ 0x17b2, 0x3a00, 0xa085, 0x0008, 0x20d0, 0x2091, 0x8001, 0x0078,
+ 0x1274, 0x2011, 0x04fd, 0x2204, 0xa082, 0x0004, 0x0048, 0x17f5,
+ 0x78f3, 0x0001, 0x2009, 0xff01, 0x200a, 0x2001, 0x000c, 0x20d8,
+ 0x2001, 0x000c, 0x20d0, 0x0078, 0x1274, 0x2001, 0x4005, 0x0078,
+ 0x1276, 0x7978, 0x71c6, 0x71c4, 0xa182, 0x0003, 0x00c8, 0x126c,
+ 0x797a, 0x0078, 0x1274, 0x7978, 0x71c6, 0x0078, 0x1274, 0x796c,
+ 0x71c6, 0x71c4, 0x796e, 0x7970, 0x71ca, 0x71c8, 0x7972, 0x7974,
+ 0x71ce, 0x71cc, 0x7976, 0x0078, 0x1274, 0x796c, 0x71c6, 0x7970,
+ 0x71ca, 0x7974, 0x71ce, 0x0078, 0x1274, 0x700c, 0xa084, 0x00ff,
+ 0x0040, 0x1829, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0,
+ 0x1824, 0x7017, 0x0000, 0x7112, 0x721a, 0x731e, 0x7422, 0x7526,
+ 0xac80, 0x0001, 0x8108, 0x810c, 0x81a9, 0x8098, 0x20a1, 0x0030,
+ 0x6084, 0x20a2, 0x53a6, 0x780c, 0xa085, 0x0000, 0x7002, 0x7007,
+ 0x0001, 0x2009, 0x0022, 0x2104, 0xa084, 0x4000, 0x00c0, 0x1841,
+ 0x7108, 0x8103, 0x00c8, 0x1841, 0x7014, 0xa005, 0x0040, 0x1841,
+ 0x7007, 0x0002, 0xa184, 0x0060, 0x7003, 0x0000, 0x007c, 0x700c,
+ 0xa084, 0x00ff, 0x0040, 0x1863, 0x7007, 0x0004, 0x7004, 0xa084,
+ 0x0004, 0x00c0, 0x185e, 0x7017, 0x0000, 0x7112, 0x721a, 0x7422,
+ 0x7526, 0x731e, 0x2099, 0x0030, 0x8108, 0x81ac, 0x780c, 0xa085,
+ 0x0001, 0x7002, 0x7007, 0x0001, 0x2009, 0x0022, 0x2104, 0xa084,
+ 0x4000, 0x00c0, 0x1874, 0x7008, 0x800b, 0x00c8, 0x1874, 0x7007,
+ 0x0002, 0xa08c, 0x0060, 0x00c0, 0x188a, 0xac80, 0x0001, 0x20a0,
+ 0x53a5, 0xa006, 0x7003, 0x0000, 0x007c, 0x7850, 0xa065, 0x0040,
+ 0x1895, 0x2c04, 0x7852, 0x2063, 0x0000, 0x007c, 0x0f7e, 0x2079,
+ 0x3900, 0x7850, 0x2062, 0x2c00, 0xa005, 0x00c0, 0x18a1, 0x1078,
+ 0x1eac, 0x7852, 0x0f7f, 0x007c, 0x2011, 0x4400, 0x7a52, 0x7be4,
+ 0x8319, 0x0040, 0x18b1, 0xa280, 0x002f, 0x2012, 0x2010, 0x0078,
+ 0x18a8, 0x2013, 0x0000, 0x007c, 0xa784, 0x0f00, 0x800c, 0xa784,
+ 0x0007, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0xa0e8, 0x3a80,
+ 0x007c, 0x1078, 0x18b4, 0x2900, 0x682a, 0x2a00, 0x682e, 0x6808,
+ 0xa084, 0xffef, 0xa80d, 0x690a, 0x2091, 0x8000, 0x2009, 0x394f,
+ 0x210c, 0x6804, 0xa005, 0x0040, 0x18de, 0xa116, 0x00c0, 0x18de,
+ 0x2060, 0x6000, 0x6806, 0x017e, 0x0078, 0x18e1, 0x2009, 0x0000,
+ 0x017e, 0x6804, 0xa065, 0x0040, 0x18f0, 0x6000, 0x6806, 0x1078,
+ 0x1903, 0x1078, 0x1a14, 0x6810, 0x8001, 0x6812, 0x00c0, 0x18e1,
+ 0x017f, 0x6902, 0x6906, 0x2091, 0x8001, 0x007c, 0xa065, 0x0040,
+ 0x1902, 0x609c, 0x609f, 0x0000, 0x2008, 0x1078, 0x1896, 0x2100,
+ 0x0078, 0x18f6, 0x007c, 0x6007, 0x0103, 0x20a9, 0x001c, 0xac80,
+ 0x0005, 0x20a0, 0x2001, 0x0000, 0x40a4, 0x6828, 0x601a, 0x682c,
+ 0x6022, 0x007c, 0x0e7e, 0x2071, 0x3940, 0x7040, 0xa08c, 0x0080,
+ 0x00c0, 0x1920, 0xa088, 0x3980, 0x2d0a, 0x8000, 0x7042, 0xa006,
+ 0x0e7f, 0x007c, 0x0e7e, 0x2071, 0x3940, 0x2009, 0x3980, 0x7240,
+ 0x8221, 0x8211, 0x0048, 0x193a, 0x2104, 0x8108, 0xad06, 0x00c0,
+ 0x1929, 0x8119, 0x211e, 0x8108, 0x8318, 0x8211, 0x00c8, 0x1932,
+ 0x7442, 0xa006, 0x0e7f, 0x007c, 0x1078, 0x18b4, 0x2091, 0x8000,
+ 0x6804, 0x781e, 0xa065, 0x0040, 0x197d, 0x0078, 0x194d, 0x2c00,
+ 0x781e, 0x6000, 0xa065, 0x0040, 0x197d, 0x6010, 0xa306, 0x00c0,
+ 0x1947, 0x600c, 0xa206, 0x00c0, 0x1947, 0x2c28, 0x6804, 0xac06,
+ 0x00c0, 0x1964, 0x6000, 0x2060, 0x6806, 0xa005, 0x00c0, 0x1964,
+ 0x6803, 0x0000, 0x0078, 0x196e, 0x6400, 0x781c, 0x2060, 0x6402,
+ 0xa486, 0x0000, 0x00c0, 0x196e, 0x2c00, 0x6802, 0x2560, 0x1078,
+ 0x1903, 0x601b, 0x0005, 0x6023, 0x0020, 0x1078, 0x1a14, 0x6810,
+ 0x8001, 0x6812, 0x2001, 0xffff, 0xa005, 0x007c, 0x2039, 0x0000,
+ 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0008, 0x1078, 0x18c1,
+ 0x8738, 0xa784, 0x0007, 0x00c0, 0x1986, 0xa7bc, 0xff00, 0x873f,
+ 0x8738, 0x873f, 0xa784, 0x0f00, 0x00c0, 0x1986, 0x007c, 0x2061,
+ 0x0000, 0x6018, 0xa084, 0x0001, 0x00c0, 0x19a8, 0x2091, 0x8000,
+ 0x78d4, 0x78d7, 0x0000, 0x2091, 0x8001, 0xa005, 0x00c0, 0x19a9,
+ 0x007c, 0xa08c, 0xfff0, 0x0040, 0x19af, 0x1078, 0x1eac, 0x0079,
+ 0x19b1, 0x19c1, 0x19c3, 0x19c9, 0x19cd, 0x19c1, 0x19d1, 0x19c1,
+ 0x19d8, 0x19dc, 0x19e0, 0x1a0a, 0x1a0e, 0x19c1, 0x19c1, 0x19c1,
+ 0x19c1, 0x1078, 0x1eac, 0x1078, 0x197e, 0x2001, 0x8001, 0x0078,
+ 0x1276, 0x2001, 0x8003, 0x0078, 0x1276, 0x2001, 0x8004, 0x0078,
+ 0x1276, 0x1078, 0x197e, 0x2001, 0x8006, 0x007c, 0x0078, 0x1276,
+ 0x2001, 0x8008, 0x0078, 0x1276, 0x2001, 0x8009, 0x0078, 0x1276,
+ 0x2091, 0x8000, 0x2069, 0x3940, 0x6800, 0xa086, 0x0000, 0x0040,
+ 0x19ee, 0x2091, 0x8001, 0x78d7, 0x0009, 0x007c, 0x68b4, 0xa0bc,
+ 0xff00, 0x2091, 0x8000, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051,
+ 0x0010, 0x1078, 0x18c1, 0x8738, 0xa784, 0x0007, 0x00c0, 0x19f9,
+ 0x2001, 0x800a, 0x0078, 0x1276, 0x2001, 0x04fd, 0x2004, 0xa086,
+ 0x0004, 0x007c, 0x2001, 0x800c, 0x0078, 0x1276, 0x1078, 0x197e,
+ 0x2001, 0x800d, 0x0078, 0x1276, 0x6004, 0x6086, 0x2c08, 0x2063,
+ 0x0000, 0x787c, 0x8000, 0x787e, 0x7880, 0xa005, 0x7982, 0x0040,
+ 0x1a24, 0x2c02, 0x0078, 0x1a25, 0x7986, 0x007c, 0x0c7e, 0x2061,
+ 0x3900, 0x6887, 0x0103, 0x2d08, 0x206b, 0x0000, 0x607c, 0x8000,
+ 0x607e, 0x6080, 0xa005, 0x6182, 0x0040, 0x1a39, 0x2d02, 0x0078,
+ 0x1a3a, 0x6186, 0x0c7f, 0x007c, 0x1078, 0x1a4d, 0x0040, 0x1a4c,
+ 0x0c7e, 0x609c, 0xa065, 0x0040, 0x1a47, 0x1078, 0x18f6, 0x0c7f,
+ 0x609f, 0x0000, 0x1078, 0x1896, 0x007c, 0x7884, 0xa065, 0x0040,
+ 0x1a5f, 0x2091, 0x8000, 0x787c, 0x8001, 0x787e, 0x2c04, 0x7886,
+ 0xa005, 0x00c0, 0x1a5d, 0x7882, 0x8000, 0x2091, 0x8001, 0x007c,
+ 0x20a9, 0x0010, 0xa006, 0x8004, 0x8086, 0x818e, 0x00c8, 0x1a69,
+ 0xa200, 0x0070, 0x1a6d, 0x0078, 0x1a64, 0x8086, 0x818e, 0x007c,
+ 0x157e, 0x20a9, 0x0010, 0xa005, 0x0040, 0x1a93, 0xa11a, 0x00c8,
+ 0x1a93, 0x8213, 0x818d, 0x0048, 0x1a84, 0xa11a, 0x00c8, 0x1a85,
+ 0x0070, 0x1a8b, 0x0078, 0x1a79, 0xa11a, 0x2308, 0x8210, 0x0070,
+ 0x1a8b, 0x0078, 0x1a79, 0x007e, 0x3200, 0xa084, 0xf7ff, 0x2080,
+ 0x007f, 0x157f, 0x007c, 0x007e, 0x3200, 0xa085, 0x0800, 0x0078,
+ 0x1a8f, 0x798c, 0x70d0, 0x007e, 0x007f, 0xa106, 0x0040, 0x1ae9,
+ 0x2091, 0x8000, 0x2071, 0x0020, 0x7004, 0xa005, 0x00c0, 0x1ae9,
+ 0x7008, 0x7208, 0xa206, 0x00c0, 0x1ae9, 0xa286, 0x0008, 0x00c0,
+ 0x1ae9, 0x2071, 0x0010, 0x1078, 0x188d, 0x0040, 0x1ae9, 0x7a94,
+ 0x7b90, 0x7c9c, 0x7d98, 0x8107, 0x8004, 0x8004, 0xa210, 0xa399,
+ 0x0000, 0x2009, 0x0040, 0x1078, 0x1857, 0x2091, 0x8001, 0x0040,
+ 0x1ae0, 0x1078, 0x1896, 0x78a0, 0x8000, 0x78a2, 0xa086, 0x0002,
+ 0x00c0, 0x1ae9, 0x2091, 0x8000, 0x78d7, 0x0002, 0x78a3, 0x0000,
+ 0x78c0, 0xa085, 0x0003, 0x78c2, 0x2091, 0x8001, 0x0078, 0x1ae9,
+ 0x78a3, 0x0000, 0x1078, 0x1c38, 0x6004, 0xa084, 0x000f, 0x0079,
+ 0x1aee, 0x2071, 0x0010, 0x2091, 0x8001, 0x007c, 0x1afe, 0x1b20,
+ 0x1b46, 0x1afe, 0x1b58, 0x1b0d, 0x1afe, 0x1afe, 0x1afe, 0x1b1a,
+ 0x1b40, 0x1afe, 0x1afe, 0x1afe, 0x1afe, 0x1afe, 0x2039, 0x0400,
+ 0x78d0, 0xa705, 0x78d2, 0x6008, 0xa705, 0x600a, 0x1078, 0x1b96,
+ 0x609c, 0x78ce, 0x1078, 0x1c20, 0x007c, 0x78d0, 0xa084, 0x0100,
+ 0x0040, 0x1b14, 0x0078, 0x1afe, 0x601c, 0xa085, 0x0080, 0x601e,
+ 0x0078, 0x1b27, 0x1078, 0x1a04, 0x00c0, 0x1afe, 0x1078, 0x1c52,
+ 0x78d0, 0xa084, 0x0100, 0x0040, 0x1b27, 0x0078, 0x1afe, 0x78d3,
+ 0x0000, 0x6004, 0x8007, 0xa084, 0x00ff, 0x78c6, 0x8001, 0x609f,
+ 0x0000, 0x0040, 0x1b3d, 0x1078, 0x1b96, 0x0040, 0x1b3d, 0x78d0,
+ 0xa085, 0x0100, 0x78d2, 0x0078, 0x1b3f, 0x1078, 0x1bba, 0x007c,
+ 0x1078, 0x1a04, 0x00c0, 0x1afe, 0x1078, 0x1c4e, 0x78d0, 0xa08c,
+ 0x0e00, 0x00c0, 0x1b4f, 0xa084, 0x0100, 0x00c0, 0x1b51, 0x0078,
+ 0x1afe, 0x1078, 0x1b96, 0x00c0, 0x1b57, 0x1078, 0x1bba, 0x007c,
+ 0x78d0, 0xa084, 0x0100, 0x0040, 0x1b5f, 0x0078, 0x1afe, 0x78d3,
+ 0x0000, 0x6714, 0x20a9, 0x0001, 0x6018, 0xa005, 0x0040, 0x1b7a,
+ 0xa7bc, 0xff00, 0x20a9, 0x0008, 0xa08e, 0x0001, 0x0040, 0x1b7a,
+ 0x2039, 0x0000, 0x20a9, 0x0080, 0xa08e, 0x0002, 0x0040, 0x1b7a,
+ 0x0078, 0x1b93, 0x1078, 0x18b4, 0x2d00, 0xa088, 0x0002, 0x2091,
+ 0x8000, 0x2168, 0x682b, 0x0000, 0x682f, 0x0000, 0x2104, 0xa084,
+ 0xffde, 0x200a, 0x2100, 0xa088, 0x0010, 0x2091, 0x8001, 0x0070,
+ 0x1b93, 0x0078, 0x1b7f, 0x1078, 0x1896, 0x007c, 0x78c8, 0xa06d,
+ 0x00c0, 0x1ba1, 0x2c00, 0x78ca, 0x78ce, 0x609f, 0x0000, 0x0078,
+ 0x1bad, 0x2c00, 0x689e, 0x609f, 0x0000, 0x78ca, 0x2d00, 0x6002,
+ 0x78cc, 0xad06, 0x00c0, 0x1bad, 0x6002, 0x78c4, 0x8001, 0x78c6,
+ 0x00c0, 0x1bb9, 0x78d0, 0xa084, 0x0000, 0x78d2, 0x78cc, 0x2060,
+ 0xa006, 0x007c, 0xa02e, 0x2530, 0x611c, 0x61a2, 0xa184, 0xc1ff,
+ 0x601e, 0xa184, 0x0060, 0x0040, 0x1bc9, 0x0e7e, 0x1078, 0x330a,
+ 0x0e7f, 0x6596, 0x669a, 0x6714, 0x1078, 0x18b4, 0x2091, 0x8000,
+ 0x6808, 0xa084, 0x0001, 0x0040, 0x1be5, 0x2091, 0x8001, 0x1078,
+ 0x1903, 0x2091, 0x8000, 0x1078, 0x1a14, 0x2091, 0x8001, 0x78cb,
+ 0x0000, 0x78cf, 0x0000, 0x0078, 0x1c1f, 0x6024, 0xa096, 0x0001,
+ 0x00c0, 0x1bec, 0x8000, 0x6026, 0x6a10, 0x6814, 0x2091, 0x8001,
+ 0xa202, 0x0048, 0x1bfb, 0x0040, 0x1bfb, 0x2039, 0x0200, 0x1078,
+ 0x1c20, 0x0078, 0x1c1f, 0x2c08, 0x2091, 0x8000, 0x6800, 0xa065,
+ 0x0040, 0x1c03, 0x6102, 0x6902, 0x00c0, 0x1c07, 0x6906, 0x2160,
+ 0x6003, 0x0000, 0x6810, 0x8000, 0x6812, 0x2091, 0x8001, 0x6808,
+ 0xa08c, 0x0040, 0x0040, 0x1c19, 0xa086, 0x0040, 0x680a, 0x1078,
+ 0x1912, 0x1078, 0x1cf6, 0x78cf, 0x0000, 0x78cb, 0x0000, 0x007c,
+ 0x6008, 0xa705, 0x600a, 0x2091, 0x8000, 0x1078, 0x1a14, 0x2091,
+ 0x8001, 0x78cc, 0xa065, 0x0040, 0x1c33, 0x609c, 0x78ce, 0x609f,
+ 0x0000, 0x0078, 0x1c23, 0x78cb, 0x0000, 0x78cf, 0x0000, 0x007c,
+ 0x7988, 0x788c, 0x8000, 0xa10a, 0x00c8, 0x1c3f, 0xa006, 0x788e,
+ 0x70d2, 0x7804, 0xa005, 0x0040, 0x1c4d, 0x8001, 0x7806, 0x00c0,
+ 0x1c4d, 0x0068, 0x1c4d, 0x2091, 0x4080, 0x007c, 0x2039, 0x1c66,
+ 0x0078, 0x1c54, 0x2039, 0x1c6c, 0x2704, 0xa005, 0x0040, 0x1c65,
+ 0xac00, 0x2068, 0x6b08, 0x6c0c, 0x6910, 0x6a14, 0x690a, 0x6a0e,
+ 0x6b12, 0x6c16, 0x8738, 0x0078, 0x1c54, 0x007c, 0x0003, 0x0009,
+ 0x000f, 0x0015, 0x001b, 0x0000, 0x0015, 0x001b, 0x0000, 0x0068,
+ 0x1c87, 0x2029, 0x0000, 0x7884, 0xa065, 0x0040, 0x1c82, 0x1078,
+ 0x1c88, 0x0040, 0x1c82, 0x1078, 0x1c99, 0x00c0, 0x1c82, 0x8528,
+ 0x0078, 0x1c73, 0x85ff, 0x0040, 0x1c87, 0x2091, 0x4080, 0x007c,
+ 0x7ba4, 0x79a8, 0x70d4, 0x007e, 0x007f, 0xa102, 0x00c0, 0x1c93,
+ 0x2300, 0xa005, 0x007c, 0x0048, 0x1c97, 0xa302, 0x007c, 0x8002,
+ 0x007c, 0x2091, 0x8000, 0x2071, 0x0020, 0x7004, 0xa005, 0x00c0,
+ 0x1cdd, 0x7008, 0x7208, 0xa206, 0x00c0, 0x1cdd, 0xa286, 0x0008,
+ 0x00c0, 0x1cdd, 0x2071, 0x0010, 0x1078, 0x1ce2, 0x2009, 0x001c,
+ 0x6028, 0xa005, 0x0040, 0x1cb6, 0x2009, 0x0040, 0x1078, 0x181d,
+ 0x0040, 0x1ccf, 0x78bc, 0x8000, 0x78be, 0xa086, 0x0002, 0x00c0,
+ 0x1cdd, 0x2091, 0x8000, 0x78d7, 0x0003, 0x78bf, 0x0000, 0x78c0,
+ 0xa085, 0x0300, 0x78c2, 0x2091, 0x8001, 0x0078, 0x1cdd, 0x78bf,
+ 0x0000, 0x1078, 0x1a3c, 0x79a4, 0x78a8, 0x8000, 0xa10a, 0x00c8,
+ 0x1cda, 0xa006, 0x78aa, 0x70d6, 0xa006, 0x2071, 0x0010, 0x2091,
+ 0x8001, 0x007c, 0x8107, 0x8004, 0x8004, 0x7ab0, 0x7bac, 0x7cb8,
+ 0x7db4, 0xa210, 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9, 0x0000,
+ 0x007c, 0x2009, 0x3968, 0x2091, 0x8000, 0x200a, 0x0f7e, 0x2079,
+ 0x0100, 0x2009, 0x3940, 0x2091, 0x8000, 0x2104, 0xa086, 0x0000,
+ 0x00c0, 0x1d11, 0x2009, 0x3912, 0x2104, 0xa005, 0x00c0, 0x1d11,
+ 0x7830, 0xa084, 0x00c0, 0x00c0, 0x1d11, 0x0018, 0x1d11, 0x781b,
+ 0x0045, 0x2091, 0x8001, 0x0f7f, 0x007c, 0x127e, 0x2091, 0x2300,
+ 0x2071, 0x3940, 0x2079, 0x0100, 0x784b, 0x000f, 0x2019, 0x3205,
+ 0x20a1, 0x012b, 0x2304, 0xa005, 0x0040, 0x1d2f, 0x789a, 0x8318,
+ 0x23ac, 0x8318, 0x2398, 0x53a6, 0x3318, 0x0078, 0x1d22, 0x789b,
+ 0x0020, 0x20a9, 0x0010, 0x78af, 0x0000, 0x78af, 0x0020, 0x0070,
+ 0x1d3b, 0x0078, 0x1d33, 0x7003, 0x0000, 0x1078, 0x1e40, 0x7004,
+ 0xa084, 0x000f, 0xa085, 0x6280, 0x7806, 0x780f, 0x9200, 0x7843,
+ 0x00d8, 0x7853, 0x0080, 0x780b, 0x0008, 0x7047, 0x397f, 0x7043,
+ 0x0000, 0x127f, 0x2000, 0x007c, 0xa18c, 0x000f, 0x2011, 0x0101,
+ 0x2204, 0xa084, 0xfff0, 0xa105, 0x2012, 0x1078, 0x1e40, 0x007c,
+ 0x2011, 0x0101, 0x20a9, 0x0009, 0x810b, 0x0070, 0x1d69, 0x0078,
+ 0x1d64, 0xa18c, 0x0e00, 0x2204, 0xa084, 0xf1ff, 0xa105, 0x2012,
+ 0x007c, 0x2009, 0x0101, 0x20a9, 0x0005, 0x8213, 0x0070, 0x1d7a,
+ 0x0078, 0x1d75, 0xa294, 0x00e0, 0x2104, 0xa084, 0xff1f, 0xa205,
+ 0x200a, 0x007c, 0x2011, 0x0101, 0x20a9, 0x000c, 0x810b, 0x0070,
+ 0x1d8b, 0x0078, 0x1d86, 0xa18c, 0xf000, 0x2204, 0xa084, 0x0fff,
+ 0xa105, 0x2012, 0x007c, 0x2011, 0x0102, 0x2204, 0xa084, 0xffcf,
+ 0xa105, 0x2012, 0x007c, 0x8103, 0x8003, 0xa080, 0x0020, 0x0c7e,
+ 0x2061, 0x0100, 0x609a, 0x62ac, 0x63ac, 0x0c7f, 0x007c, 0x8103,
+ 0x8003, 0xa080, 0x0022, 0x0c7e, 0x2061, 0x0100, 0x609a, 0x60a4,
+ 0xa084, 0xffdf, 0x60ae, 0x0c7f, 0x007c, 0x8103, 0x8003, 0xa080,
+ 0x0022, 0x0c7e, 0x2061, 0x0100, 0x609a, 0x60a4, 0xa085, 0x0020,
+ 0x60ae, 0x0c7f, 0x007c, 0x8103, 0x8003, 0xa080, 0x0020, 0x0c7e,
+ 0x2061, 0x0100, 0x609a, 0x60a4, 0x62ae, 0x2010, 0x60a4, 0x63ae,
+ 0x2018, 0x0c7f, 0x007c, 0x0c7e, 0x0e7e, 0x6818, 0xa005, 0x0040,
+ 0x1e1c, 0x2061, 0x4380, 0x1078, 0x1e22, 0x0040, 0x1e06, 0x20a9,
+ 0x0000, 0x2061, 0x4280, 0x0c7e, 0x1078, 0x1e22, 0x0040, 0x1df0,
+ 0x0c7f, 0x8c60, 0x0070, 0x1dee, 0x0078, 0x1de3, 0x0078, 0x1e1c,
+ 0x007f, 0xa082, 0x4280, 0x2071, 0x3940, 0x70ba, 0x6020, 0xa085,
+ 0x0800, 0x6022, 0x2091, 0x8001, 0x71b6, 0x2001, 0x0004, 0x70a2,
+ 0x70c7, 0x000f, 0x1078, 0x1cf1, 0x0078, 0x1e18, 0x2071, 0x3940,
+ 0x6020, 0xa085, 0x0800, 0x6022, 0x2091, 0x8001, 0x71b6, 0x2c00,
+ 0x70be, 0x2001, 0x0006, 0x70a2, 0x70c7, 0x000f, 0x1078, 0x1cf1,
+ 0x2001, 0x0000, 0x0078, 0x1e1e, 0x2001, 0x0001, 0xa005, 0x0e7f,
+ 0x0c7f, 0x007c, 0x2091, 0x8000, 0x2c04, 0xa005, 0x0040, 0x1e3b,
+ 0x2060, 0x6010, 0xa306, 0x00c0, 0x1e38, 0x600c, 0xa206, 0x00c0,
+ 0x1e38, 0x6014, 0xa106, 0x00c0, 0x1e38, 0xa006, 0x0078, 0x1e3f,
+ 0x6000, 0x0078, 0x1e25, 0xa085, 0x0001, 0x2091, 0x8001, 0x007c,
+ 0x2011, 0x3941, 0x220c, 0xa18c, 0x000f, 0x2011, 0x013b, 0x2204,
+ 0xa084, 0x0100, 0x0040, 0x1e56, 0x2021, 0xff04, 0x2122, 0x810b,
+ 0x810b, 0x810b, 0x810b, 0xa18d, 0x0f00, 0x2104, 0x007c, 0x0e7e,
+ 0x68e4, 0xa08c, 0x0020, 0x0040, 0x1eaa, 0xa084, 0x0006, 0x00c0,
+ 0x1eaa, 0x6014, 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003,
+ 0xa0f0, 0x3a00, 0x7004, 0xa084, 0x000a, 0x00c0, 0x1eaa, 0x7108,
+ 0xa194, 0xff00, 0x0040, 0x1eaa, 0xa18c, 0x00ff, 0x2001, 0x000c,
+ 0xa106, 0x0040, 0x1e91, 0x2001, 0x0012, 0xa106, 0x0040, 0x1e95,
+ 0x2001, 0x0014, 0xa106, 0x0040, 0x1e99, 0x2001, 0x0019, 0xa106,
+ 0x0040, 0x1e9d, 0x2001, 0x0032, 0xa106, 0x0040, 0x1ea1, 0x0078,
+ 0x1ea5, 0x2009, 0x0012, 0x0078, 0x1ea7, 0x2009, 0x0014, 0x0078,
+ 0x1ea7, 0x2009, 0x0019, 0x0078, 0x1ea7, 0x2009, 0x0020, 0x0078,
+ 0x1ea7, 0x2009, 0x003f, 0x0078, 0x1ea7, 0x2011, 0x0000, 0x2100,
+ 0xa205, 0x700a, 0x0e7f, 0x007c, 0x2071, 0x0010, 0x70ca, 0x007f,
+ 0x70c6, 0x70c3, 0x8002, 0x2071, 0x0000, 0x701b, 0x0001, 0x2091,
+ 0x4080, 0x0078, 0x1eb9, 0x107e, 0x007e, 0x127e, 0x2091, 0x2300,
+ 0x7f3c, 0x7e58, 0x7c30, 0x7d38, 0x2009, 0x3974, 0x78a0, 0x200a,
+ 0x8108, 0x250a, 0x8108, 0x240a, 0x8108, 0x260a, 0x8108, 0x270a,
+ 0xa594, 0x003f, 0xa484, 0x4000, 0x0040, 0x1edc, 0xa784, 0x007c,
+ 0x00c0, 0x318f, 0x1078, 0x1eac, 0xa49c, 0x000f, 0xa382, 0x0004,
+ 0x0050, 0x1ee4, 0x1078, 0x1eac, 0x8507, 0xa084, 0x000f, 0x0079,
+ 0x1ee9, 0x236e, 0x240d, 0x242e, 0x2699, 0x28dd, 0x293b, 0x2984,
+ 0x29f0, 0x2a8d, 0x2b1a, 0x1f11, 0x1ef9, 0x21c3, 0x2288, 0x28bc,
+ 0x1ef9, 0x1078, 0x1eac, 0x0018, 0x1ec0, 0x127f, 0x2091, 0x8001,
+ 0x007f, 0x107f, 0x007c, 0x7003, 0x0000, 0x703f, 0x0000, 0x7030,
+ 0xa005, 0x0040, 0x1f0d, 0x7033, 0x0000, 0x1078, 0x316a, 0x0018,
+ 0x1ec0, 0x2009, 0x390f, 0x200b, 0x0000, 0x705c, 0xa005, 0x00c0,
+ 0x1fe2, 0x70a0, 0xa084, 0x0007, 0x0079, 0x1f1e, 0x2005, 0x1f26,
+ 0x1f34, 0x1f51, 0x1f73, 0x1fc0, 0x1f99, 0x1f26, 0x7808, 0xa084,
+ 0xfffd, 0x780a, 0x2009, 0x0047, 0x1078, 0x27c1, 0x00c0, 0x1f32,
+ 0x7003, 0x0004, 0x0078, 0x1efb, 0x1078, 0x3151, 0x00c0, 0x1f4f,
+ 0x70b4, 0x8007, 0x7882, 0x789b, 0x0010, 0x78ab, 0x000c, 0x789b,
+ 0x0060, 0x78ab, 0x0001, 0x785b, 0x0004, 0x2009, 0x00fb, 0x1078,
+ 0x27bf, 0x00c0, 0x1f4f, 0x7003, 0x0004, 0x70c7, 0x000f, 0x0078,
+ 0x1efb, 0x1078, 0x3151, 0x00c0, 0x1f71, 0x71b4, 0x8107, 0x7882,
+ 0x789b, 0x0010, 0xa18c, 0x0007, 0xa18d, 0x00c0, 0x79aa, 0x78ab,
+ 0x0006, 0x789b, 0x0060, 0x78ab, 0x0002, 0x785b, 0x0004, 0x2009,
+ 0x00fb, 0x1078, 0x27bf, 0x00c0, 0x1f71, 0x7003, 0x0004, 0x70c7,
+ 0x000f, 0x0078, 0x1efb, 0x1078, 0x3151, 0x00c0, 0x1f97, 0x71b4,
+ 0x8107, 0x7882, 0x789b, 0x0010, 0xa18c, 0x0007, 0xa18d, 0x00c0,
+ 0x79aa, 0x78ab, 0x0020, 0x71b8, 0x79aa, 0x78ab, 0x000d, 0x789b,
+ 0x0060, 0x78ab, 0x0004, 0x785b, 0x0004, 0x2009, 0x00fb, 0x1078,
+ 0x27bf, 0x00c0, 0x1f97, 0x7003, 0x0004, 0x70c7, 0x000f, 0x0078,
+ 0x1efb, 0x1078, 0x3151, 0x00c0, 0x1fbe, 0x71b4, 0x8107, 0x7882,
+ 0x789b, 0x0010, 0xa18c, 0x0007, 0xa18d, 0x00c0, 0x79aa, 0x78ab,
+ 0x0006, 0x789b, 0x0060, 0x78ab, 0x0002, 0x785b, 0x0004, 0x2009,
+ 0x00fb, 0x1078, 0x27bf, 0x00c0, 0x1fbe, 0x70bc, 0x70bf, 0x0000,
+ 0x2068, 0x703e, 0x7003, 0x0002, 0x70c7, 0x000f, 0x0078, 0x1efb,
+ 0x1078, 0x3151, 0x00c0, 0x1efb, 0x70bc, 0x2068, 0x1078, 0x31f3,
+ 0x789b, 0x0010, 0x6814, 0xa084, 0x0007, 0xa085, 0x0080, 0x007e,
+ 0x007f, 0x78aa, 0x6e1c, 0x067e, 0x067f, 0x2041, 0x0001, 0x70c0,
+ 0xa084, 0x0400, 0x2001, 0x0004, 0x0040, 0x1fe0, 0x2001, 0x0006,
+ 0x0078, 0x20e1, 0x1078, 0x3151, 0x00c0, 0x1efb, 0x789b, 0x0010,
+ 0x705c, 0x2068, 0x1078, 0x31f3, 0x6f14, 0x1078, 0x3099, 0x6008,
+ 0xa085, 0x0010, 0x600a, 0xad80, 0x0009, 0x2003, 0x0005, 0x6814,
+ 0xa084, 0x0007, 0xa085, 0x0080, 0x78aa, 0x2031, 0x0020, 0x2041,
+ 0x0001, 0x2001, 0x0003, 0x0078, 0x20e1, 0x0018, 0x1ec0, 0x7440,
+ 0xa485, 0x0000, 0x0040, 0x201f, 0xa080, 0x3980, 0x2030, 0x7144,
+ 0x8108, 0xa12a, 0x0048, 0x2016, 0x2009, 0x3980, 0x2164, 0x6504,
+ 0x85ff, 0x00c0, 0x202c, 0x8421, 0x00c0, 0x2010, 0x7146, 0x7003,
+ 0x0000, 0x703f, 0x0000, 0x0078, 0x1efb, 0x7640, 0xa6b0, 0x3980,
+ 0x7144, 0x2600, 0x0078, 0x201b, 0x7146, 0x2568, 0x2558, 0x753e,
+ 0x2c50, 0x6708, 0x7736, 0xa784, 0x013f, 0x0040, 0x2059, 0xa784,
+ 0x0021, 0x00c0, 0x2029, 0xa784, 0x0002, 0x0040, 0x2046, 0xa784,
+ 0x0004, 0x0040, 0x2029, 0xa7bc, 0xfffb, 0x670a, 0xa784, 0x0008,
+ 0x00c0, 0x2029, 0xa784, 0x0010, 0x00c0, 0x2029, 0xa784, 0x0100,
+ 0x0040, 0x2059, 0x6018, 0xa005, 0x00c0, 0x2029, 0xa7bc, 0xfeff,
+ 0x670a, 0x6823, 0x0000, 0x6e1c, 0xa684, 0x000e, 0x6118, 0x0040,
+ 0x2069, 0x601c, 0xa102, 0x0048, 0x206c, 0x0040, 0x206c, 0x0078,
+ 0x2025, 0x81ff, 0x00c0, 0x2025, 0xa784, 0x0080, 0x00c0, 0x2072,
+ 0x700c, 0x6022, 0x1078, 0x31f3, 0x0018, 0x1ec0, 0x789b, 0x0010,
+ 0xa046, 0x1078, 0x3151, 0x00c0, 0x1efb, 0x6b14, 0xa39c, 0x0007,
+ 0xa39d, 0x00c0, 0x704c, 0xa084, 0x8000, 0x0040, 0x208b, 0xa684,
+ 0x0001, 0x0040, 0x208d, 0xa39c, 0xffbf, 0xa684, 0x0010, 0x0040,
+ 0x2093, 0xa39d, 0x0020, 0x7baa, 0x8840, 0xa684, 0x000e, 0x00c0,
+ 0x209e, 0xa7bd, 0x0010, 0x670a, 0x0078, 0x20df, 0x714c, 0xa18c,
+ 0x0800, 0x0040, 0x2cfc, 0x2011, 0x0021, 0x8004, 0x8004, 0x0048,
+ 0x20b5, 0x2011, 0x0022, 0x8004, 0x0048, 0x20b5, 0x2011, 0x0020,
+ 0x8004, 0x0048, 0x20b5, 0x0040, 0x20df, 0x7aaa, 0x8840, 0x1078,
+ 0x316a, 0x6a14, 0x610c, 0x8108, 0xa18c, 0x00ff, 0xa1e0, 0x4280,
+ 0x2c64, 0x8cff, 0x0040, 0x20d6, 0x6014, 0xa206, 0x00c0, 0x20c0,
+ 0x60b8, 0x8001, 0x60ba, 0x00c0, 0x20bb, 0x0c7e, 0x2a60, 0x6008,
+ 0xa085, 0x0100, 0x600a, 0x0c7f, 0x0078, 0x2005, 0x1078, 0x3151,
+ 0x00c0, 0x1efb, 0x2a60, 0x610e, 0x79aa, 0x8840, 0x712e, 0x2001,
+ 0x0001, 0x007e, 0x7150, 0xa184, 0x0018, 0x0040, 0x20fc, 0xa184,
+ 0x0010, 0x0040, 0x20ef, 0x1078, 0x2ee3, 0x00c0, 0x211f, 0xa184,
+ 0x0008, 0x0040, 0x20fc, 0x69a0, 0xa184, 0x0600, 0x00c0, 0x20fc,
+ 0x1078, 0x2ddf, 0x0078, 0x211f, 0x69a0, 0xa184, 0x0800, 0x0040,
+ 0x2113, 0x0c7e, 0x027e, 0x2960, 0x6000, 0xa085, 0x2000, 0x6002,
+ 0x6104, 0xa18d, 0x0010, 0x6106, 0x027f, 0x0c7f, 0x1078, 0x2ee3,
+ 0x00c0, 0x211f, 0x69a0, 0xa184, 0x0200, 0x0040, 0x211b, 0x1078,
+ 0x2e2e, 0x0078, 0x211f, 0xa184, 0x0400, 0x00c0, 0x20f8, 0x69a0,
+ 0xa184, 0x1000, 0x0040, 0x212a, 0x6914, 0xa18c, 0xff00, 0x810f,
+ 0x1078, 0x1da7, 0x007f, 0x7002, 0xa68c, 0x00e0, 0xa684, 0x0060,
+ 0x0040, 0x2138, 0xa086, 0x0060, 0x00c0, 0x2138, 0xa18d, 0x4000,
+ 0x88ff, 0x0040, 0x213d, 0xa18d, 0x0004, 0x795a, 0x69b6, 0x789b,
+ 0x0060, 0x2800, 0x78aa, 0x789b, 0x0061, 0x6818, 0xa08d, 0x8000,
+ 0xa084, 0x7fff, 0x691a, 0xa68c, 0x0080, 0x0040, 0x215c, 0x70cb,
+ 0x0000, 0xa08a, 0x000d, 0x0050, 0x215a, 0xa08a, 0x000c, 0x71ca,
+ 0x2001, 0x000c, 0x800c, 0x71ce, 0x78aa, 0x8008, 0x810c, 0x0040,
+ 0x2d07, 0xa18c, 0x00f8, 0x00c0, 0x2d07, 0x157e, 0x137e, 0x147e,
+ 0x20a1, 0x012b, 0x789b, 0x0000, 0x8000, 0x80ac, 0xad80, 0x000b,
+ 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f, 0x6814, 0x8007, 0x7882,
+ 0x6d94, 0x7dd6, 0x7dde, 0x6e98, 0x7ed2, 0x7eda, 0x7830, 0xa084,
+ 0x00c0, 0x00c0, 0x2185, 0x0098, 0x218d, 0x6008, 0xa084, 0xffef,
+ 0x600a, 0x1078, 0x316a, 0x0078, 0x1f03, 0x7200, 0xa284, 0x0007,
+ 0xa086, 0x0001, 0x00c0, 0x219a, 0x781b, 0x004a, 0x1078, 0x316a,
+ 0x0078, 0x21ab, 0x6ab4, 0xa295, 0x2000, 0x7a5a, 0x781b, 0x004a,
+ 0x1078, 0x316a, 0x7200, 0x2500, 0xa605, 0x0040, 0x21ab, 0xa284,
+ 0x0007, 0x1079, 0x21b9, 0xa284, 0x0007, 0xa086, 0x0001, 0x00c0,
+ 0x1efb, 0x6018, 0x8000, 0x601a, 0xad80, 0x0009, 0x7032, 0x0078,
+ 0x1efb, 0x21c1, 0x3571, 0x3571, 0x3560, 0x3571, 0x21c1, 0x3560,
+ 0x21c1, 0x1078, 0x1eac, 0x7808, 0xa084, 0xfffd, 0x780a, 0x0f7e,
+ 0x2079, 0x3900, 0x78c0, 0x0f7f, 0xa084, 0x0001, 0x0040, 0x21e9,
+ 0x70a0, 0xa086, 0x0001, 0x00c0, 0x21d8, 0x70a2, 0x0078, 0x226c,
+ 0x70a0, 0xa086, 0x0005, 0x00c0, 0x21e7, 0x70bc, 0x2068, 0x681b,
+ 0x0004, 0x6817, 0x0000, 0x6820, 0xa085, 0x0008, 0x6822, 0x70a3,
+ 0x0000, 0x157e, 0x2011, 0x0004, 0x71a0, 0xa186, 0x0001, 0x0040,
+ 0x2207, 0xa186, 0x0007, 0x00c0, 0x21fb, 0x2009, 0x3935, 0x200b,
+ 0x0005, 0x0078, 0x2207, 0x2009, 0x3913, 0x2104, 0x2009, 0x3912,
+ 0x200a, 0x2009, 0x3935, 0x200b, 0x0001, 0x0078, 0x2209, 0x70a3,
+ 0x0000, 0x1078, 0x32f5, 0x20a9, 0x0010, 0x2039, 0x0000, 0x1078,
+ 0x2f9a, 0xa7b8, 0x0100, 0x0070, 0x2217, 0x0078, 0x220f, 0x7000,
+ 0x0079, 0x221a, 0x2248, 0x2231, 0x2231, 0x2224, 0x2248, 0x2248,
+ 0x2248, 0x2222, 0x1078, 0x1eac, 0x2021, 0x3957, 0x2404, 0xa005,
+ 0x0040, 0x2248, 0xad06, 0x00c0, 0x2231, 0x6800, 0x2022, 0x0078,
+ 0x2241, 0x6820, 0xa084, 0x0001, 0x00c0, 0x223d, 0x6f14, 0x1078,
+ 0x3099, 0x1078, 0x2cc9, 0x0078, 0x2241, 0x7054, 0x2060, 0x6800,
+ 0x6002, 0x6a1a, 0x6820, 0xa085, 0x0008, 0x6822, 0x1078, 0x1a26,
+ 0x2021, 0x4380, 0x1078, 0x2272, 0x2021, 0x3957, 0x1078, 0x2272,
+ 0x20a9, 0x0000, 0x2021, 0x4280, 0x1078, 0x2272, 0x8420, 0x0070,
+ 0x225b, 0x0078, 0x2254, 0x20a9, 0x0080, 0x2061, 0x3a80, 0x6018,
+ 0x6110, 0xa102, 0x6012, 0x601b, 0x0000, 0xace0, 0x0010, 0x0070,
+ 0x226b, 0x0078, 0x225f, 0x157f, 0x7003, 0x0000, 0x703f, 0x0000,
+ 0x0078, 0x1efb, 0x047e, 0x2404, 0xa005, 0x0040, 0x2284, 0x2068,
+ 0x6800, 0x007e, 0x6a1a, 0x6820, 0xa085, 0x0008, 0x6822, 0x1078,
+ 0x1a26, 0x007f, 0x0078, 0x2274, 0x047f, 0x2023, 0x0000, 0x007c,
+ 0xa282, 0x0003, 0x0050, 0x228e, 0x1078, 0x1eac, 0x2300, 0x0079,
+ 0x2291, 0x2294, 0x2311, 0x232e, 0xa282, 0x0002, 0x0040, 0x229a,
+ 0x1078, 0x1eac, 0x70a0, 0x70a3, 0x0000, 0x70c7, 0x0000, 0x0079,
+ 0x22a1, 0x22a9, 0x22a9, 0x22ab, 0x22e9, 0x2d0d, 0x22a9, 0x22e9,
+ 0x22a9, 0x1078, 0x1eac, 0x77b4, 0x1078, 0x2f9a, 0x77b4, 0xa7bc,
+ 0x0f00, 0x1078, 0x3099, 0x6018, 0xa005, 0x0040, 0x22e0, 0x2021,
+ 0x4380, 0x2009, 0x0004, 0x2011, 0x0010, 0x1078, 0x2349, 0x0040,
+ 0x22e0, 0x157e, 0x20a9, 0x0000, 0x2021, 0x4280, 0x047e, 0x2009,
+ 0x0004, 0x2011, 0x0010, 0x1078, 0x2349, 0x047f, 0x0040, 0x22d5,
+ 0x8420, 0x0070, 0x22d5, 0x0078, 0x22c6, 0x157f, 0x2021, 0x3957,
+ 0x2009, 0x0004, 0x2011, 0x0010, 0x1078, 0x2349, 0x0040, 0x22e0,
+ 0x8738, 0xa784, 0x0007, 0x00c0, 0x22b1, 0x0078, 0x1f03, 0x0078,
+ 0x1f03, 0x77b4, 0x1078, 0x3099, 0x6018, 0xa005, 0x0040, 0x230f,
+ 0x2021, 0x4380, 0x2009, 0x0005, 0x2011, 0x0020, 0x1078, 0x2349,
+ 0x0040, 0x230f, 0x157e, 0x20a9, 0x0000, 0x2021, 0x4280, 0x047e,
+ 0x2009, 0x0005, 0x2011, 0x0020, 0x1078, 0x2349, 0x047f, 0x0040,
+ 0x230e, 0x8420, 0x0070, 0x230e, 0x0078, 0x22ff, 0x157f, 0x0078,
+ 0x1f03, 0x2200, 0x0079, 0x2314, 0x2317, 0x2319, 0x2319, 0x1078,
+ 0x1eac, 0x2009, 0x0012, 0x70a0, 0xa086, 0x0002, 0x0040, 0x2322,
+ 0x2009, 0x000e, 0x6818, 0xa084, 0x8000, 0x0040, 0x2328, 0x691a,
+ 0x70a3, 0x0000, 0x70a7, 0x0001, 0x0078, 0x311c, 0x2200, 0x0079,
+ 0x2331, 0x2336, 0x2319, 0x2334, 0x1078, 0x1eac, 0x1078, 0x27ce,
+ 0x7000, 0xa086, 0x0001, 0x00c0, 0x2c9f, 0x1078, 0x2cdf, 0x6008,
+ 0xa084, 0xffef, 0x600a, 0x1078, 0x2c92, 0x0040, 0x2c9f, 0x0078,
+ 0x2005, 0x2404, 0xa005, 0x0040, 0x236a, 0x2068, 0x2d04, 0x007e,
+ 0x6814, 0xa706, 0x0040, 0x2358, 0x2d20, 0x007f, 0x0078, 0x234a,
+ 0x007f, 0x2022, 0x691a, 0x6820, 0xa205, 0x6822, 0x1078, 0x1a26,
+ 0x6010, 0x8001, 0x6012, 0x6008, 0xa084, 0xffef, 0x600a, 0x1078,
+ 0x2cdf, 0x007c, 0xa085, 0x0001, 0x0078, 0x2369, 0x2300, 0x0079,
+ 0x2371, 0x2376, 0x2374, 0x23c2, 0x1078, 0x1eac, 0x78e4, 0xa005,
+ 0x00d0, 0x238a, 0x0018, 0x238a, 0xa084, 0x0007, 0x0079, 0x2380,
+ 0x239b, 0x23a8, 0x238e, 0x2388, 0x3144, 0x3144, 0x2388, 0x23b5,
+ 0x1078, 0x1eac, 0x2001, 0x0003, 0x0078, 0x26ad, 0x6818, 0xa084,
+ 0x8000, 0x0040, 0x2395, 0x681b, 0x001d, 0x1078, 0x2f7d, 0x781b,
+ 0x0053, 0x0078, 0x1efb, 0x6818, 0xa084, 0x8000, 0x0040, 0x23a2,
+ 0x681b, 0x001d, 0x1078, 0x2f7d, 0x781b, 0x00de, 0x0078, 0x1efb,
+ 0x6818, 0xa084, 0x8000, 0x0040, 0x23af, 0x681b, 0x001d, 0x1078,
+ 0x2f7d, 0x781b, 0x00e5, 0x0078, 0x1efb, 0x6818, 0xa084, 0x8000,
+ 0x0040, 0x23bc, 0x681b, 0x001d, 0x1078, 0x2f7d, 0x781b, 0x009c,
+ 0x0078, 0x1efb, 0xa584, 0x000f, 0x00c0, 0x23e1, 0x1078, 0x27ce,
+ 0x7000, 0x0079, 0x23cb, 0x23d3, 0x23d5, 0x23d3, 0x2c9f, 0x2c9f,
+ 0x2c9f, 0x2c9f, 0x23d3, 0x1078, 0x1eac, 0x1078, 0x2cdf, 0x6008,
+ 0xa084, 0xffef, 0x600a, 0x1078, 0x2c92, 0x0040, 0x2c9f, 0x0078,
+ 0x2005, 0x79e4, 0xa005, 0x00d0, 0x238a, 0x0018, 0x238a, 0xa184,
+ 0x0007, 0x0079, 0x23eb, 0x23fb, 0x2401, 0x23f5, 0x23f3, 0x3144,
+ 0x3144, 0x23f3, 0x313c, 0x1078, 0x1eac, 0x1078, 0x2f85, 0x781b,
+ 0x0053, 0x0078, 0x1efb, 0x1078, 0x2f85, 0x781b, 0x00de, 0x0078,
+ 0x1efb, 0x1078, 0x2f85, 0x781b, 0x00e5, 0x0078, 0x1efb, 0x1078,
+ 0x2f85, 0x781b, 0x009c, 0x0078, 0x1efb, 0x2300, 0x0079, 0x2410,
+ 0x2415, 0x2413, 0x2417, 0x1078, 0x1eac, 0x0078, 0x29f0, 0x681b,
+ 0x0008, 0x78a3, 0x0000, 0x79e4, 0xa184, 0x0030, 0x0040, 0x29f0,
+ 0xa184, 0x0007, 0x0079, 0x2424, 0x242c, 0x2401, 0x238e, 0x311c,
+ 0x3144, 0x3144, 0x242c, 0x313c, 0x1078, 0x1eac, 0xa282, 0x0005,
+ 0x0050, 0x2434, 0x1078, 0x1eac, 0x2300, 0x0079, 0x2437, 0x243a,
+ 0x265e, 0x266a, 0x2200, 0x0079, 0x243d, 0x2457, 0x2444, 0x2457,
+ 0x2442, 0x2643, 0x1078, 0x1eac, 0x789b, 0x0018, 0x78a8, 0xa084,
+ 0x00ff, 0xa082, 0x0020, 0x0048, 0x2f69, 0xa08a, 0x0004, 0x00c8,
+ 0x2f69, 0x0079, 0x2453, 0x2f69, 0x2f69, 0x2f69, 0x2f23, 0x789b,
+ 0x0018, 0x79a8, 0xa184, 0x0080, 0x0040, 0x246c, 0xa184, 0x0018,
+ 0x0040, 0x2468, 0x0078, 0x2f69, 0x7000, 0xa005, 0x00c0, 0x2462,
+ 0x2011, 0x0004, 0x0078, 0x2b28, 0xa184, 0x00ff, 0xa08a, 0x0010,
+ 0x00c8, 0x2f69, 0x0079, 0x2474, 0x2486, 0x2484, 0x249e, 0x24a2,
+ 0x255a, 0x2f69, 0x2f69, 0x255c, 0x2f69, 0x2f69, 0x263f, 0x263f,
+ 0x2f69, 0x2f69, 0x2f69, 0x2641, 0x1078, 0x1eac, 0xa684, 0x1000,
+ 0x0040, 0x2493, 0x2001, 0x0300, 0x8000, 0x8000, 0x783a, 0x781b,
+ 0x0099, 0x0078, 0x1efb, 0x6818, 0xa084, 0x8000, 0x0040, 0x249c,
+ 0x681b, 0x001d, 0x0078, 0x248a, 0x0078, 0x311c, 0x681b, 0x001d,
+ 0x0078, 0x2f75, 0x6920, 0xa184, 0x8000, 0x00c0, 0x24ae, 0x68af,
+ 0x0000, 0x68b3, 0x0000, 0xa18d, 0x8000, 0x6922, 0xa684, 0x1800,
+ 0x00c0, 0x24ed, 0x6820, 0xa084, 0x0001, 0x00c0, 0x24f3, 0x6818,
+ 0xa086, 0x0008, 0x00c0, 0x24be, 0x681b, 0x0000, 0xa684, 0x0400,
+ 0x0040, 0x2556, 0xa684, 0x0080, 0x0040, 0x24e9, 0x70cb, 0x0000,
+ 0x6818, 0xa084, 0x003f, 0xa08a, 0x000d, 0x0050, 0x24e9, 0xa08a,
+ 0x000c, 0x71ca, 0x2001, 0x000c, 0x800c, 0x71ce, 0x789b, 0x0061,
+ 0x78aa, 0x157e, 0x137e, 0x147e, 0x20a1, 0x012b, 0x789b, 0x0000,
+ 0x8000, 0x80ac, 0xad80, 0x000b, 0x2098, 0x53a6, 0x147f, 0x137f,
+ 0x157f, 0x781b, 0x0056, 0x0078, 0x1efb, 0xa684, 0x1000, 0x0040,
+ 0x24f3, 0x0078, 0x1efb, 0xa684, 0x0060, 0x0040, 0x2552, 0xa684,
+ 0x0800, 0x0040, 0x2552, 0xa684, 0x8000, 0x00c0, 0x2503, 0x69b0,
+ 0x6aac, 0x0078, 0x251d, 0xa6b4, 0x7fff, 0x7e5a, 0x6eb6, 0x789b,
+ 0x0074, 0x7aac, 0x79ac, 0x78ac, 0x801b, 0x00c8, 0x2510, 0x8000,
+ 0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x6b98, 0x2100, 0xa302,
+ 0x68b2, 0x6b94, 0x2200, 0xa303, 0x68ae, 0xa684, 0x4000, 0x0040,
+ 0x2525, 0xa6b4, 0xbfff, 0x7e5a, 0x6eb6, 0xa006, 0x1078, 0x362f,
+ 0x6ab0, 0x69ac, 0x6c98, 0x6b94, 0x2200, 0xa105, 0x0040, 0x2534,
+ 0x2200, 0xa422, 0x2100, 0xa31b, 0x6caa, 0x7cd2, 0x6ba6, 0x7bd6,
+ 0x2300, 0xa405, 0x00c0, 0x2544, 0xa6b5, 0x4000, 0x7e5a, 0x6eb6,
+ 0x781b, 0x0065, 0x0078, 0x1efb, 0x781b, 0x0065, 0x2200, 0xa115,
+ 0x00c0, 0x254e, 0x1078, 0x3571, 0x0078, 0x1efb, 0x1078, 0x35a6,
+ 0x0078, 0x1efb, 0x781b, 0x0068, 0x0078, 0x1efb, 0x781b, 0x0056,
+ 0x0078, 0x1efb, 0x1078, 0x1eac, 0x0078, 0x25b1, 0x6920, 0xa184,
+ 0x0100, 0x0040, 0x2570, 0xa18c, 0xfeff, 0x6922, 0x0c7e, 0x7048,
+ 0x2060, 0x6004, 0xa084, 0xfff5, 0x6006, 0x0c7f, 0x0078, 0x25a0,
+ 0xa184, 0x0200, 0x0040, 0x25a0, 0xa18c, 0xfdff, 0x6922, 0x0c7e,
+ 0x7048, 0x2060, 0x6004, 0xa084, 0xffef, 0x6006, 0x2008, 0x2c48,
+ 0x0c7f, 0xa184, 0x0008, 0x0040, 0x25a0, 0x1078, 0x3095, 0x1078,
+ 0x2ddf, 0x88ff, 0x0040, 0x25a0, 0x789b, 0x0060, 0x2800, 0x78aa,
+ 0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x259c,
+ 0x781b, 0x0053, 0x0078, 0x1efb, 0x781b, 0x0067, 0x0078, 0x1efb,
+ 0x7e58, 0xa684, 0x0400, 0x00c0, 0x25a9, 0x781b, 0x0056, 0x0078,
+ 0x1efb, 0x781b, 0x0068, 0x0078, 0x1efb, 0x0078, 0x2f6f, 0x0078,
+ 0x2f6f, 0x2019, 0x0000, 0x7990, 0xa18c, 0x0007, 0x0040, 0x25af,
+ 0x789b, 0x0010, 0x78a8, 0xa094, 0x00ff, 0xa286, 0x0001, 0x00c0,
+ 0x25d4, 0x2300, 0x7ca8, 0xa400, 0x2018, 0xa102, 0x0040, 0x25cc,
+ 0x0048, 0x25cc, 0x0078, 0x25ce, 0x0078, 0x255e, 0x24a8, 0x7aa8,
+ 0x00f0, 0x25ce, 0x0078, 0x25ba, 0xa284, 0x00f0, 0xa086, 0x0020,
+ 0x00c0, 0x2630, 0x8318, 0x8318, 0x2300, 0xa102, 0x0040, 0x25e4,
+ 0x0048, 0x25e4, 0x0078, 0x262d, 0xa286, 0x0023, 0x0040, 0x25af,
+ 0x681c, 0xa084, 0xfff1, 0x681e, 0x7e58, 0xa684, 0xfff1, 0xa085,
+ 0x0010, 0x2030, 0x7e5a, 0x6008, 0xa085, 0x0010, 0x600a, 0x0c7e,
+ 0x7048, 0x2060, 0x6004, 0x2008, 0x2c48, 0x0c7f, 0xa184, 0x0010,
+ 0x0040, 0x2608, 0x1078, 0x3095, 0x1078, 0x2ee3, 0x0078, 0x2617,
+ 0x0c7e, 0x7048, 0x2060, 0x6004, 0x2008, 0x2c48, 0x0c7f, 0xa184,
+ 0x0008, 0x0040, 0x25a0, 0x1078, 0x3095, 0x1078, 0x2ddf, 0x88ff,
+ 0x0040, 0x25a0, 0x789b, 0x0060, 0x2800, 0x78aa, 0xa6b5, 0x0004,
+ 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x2629, 0x781b, 0x0053, 0x0078,
+ 0x1efb, 0x781b, 0x0067, 0x0078, 0x1efb, 0x7aa8, 0x0078, 0x25ba,
+ 0x8318, 0x2300, 0xa102, 0x0040, 0x2639, 0x0048, 0x2639, 0x0078,
+ 0x25ba, 0xa284, 0x0080, 0x00c0, 0x2f75, 0x0078, 0x2f6f, 0x0078,
+ 0x2f75, 0x0078, 0x2f69, 0x789b, 0x0018, 0x78a8, 0xa084, 0x00ff,
+ 0xa08e, 0x0001, 0x0040, 0x264e, 0x1078, 0x1eac, 0x7aa8, 0xa294,
+ 0x00ff, 0x78a8, 0xa084, 0x00ff, 0xa08a, 0x0004, 0x00c8, 0x2f69,
+ 0x0079, 0x265a, 0x2f69, 0x2d32, 0x2f69, 0x2e7e, 0xa282, 0x0000,
+ 0x00c0, 0x2664, 0x1078, 0x1eac, 0x1078, 0x2f7d, 0x781b, 0x0067,
+ 0x0078, 0x1efb, 0xa282, 0x0003, 0x00c0, 0x2670, 0x1078, 0x1eac,
+ 0xa484, 0x8000, 0x00c0, 0x2693, 0x70a0, 0xa005, 0x0040, 0x267a,
+ 0x1078, 0x1eac, 0x6f14, 0x77b6, 0xa7bc, 0x0f00, 0x1078, 0x3099,
+ 0x6008, 0xa085, 0x0021, 0x600a, 0x8738, 0xa784, 0x0007, 0x00c0,
+ 0x267e, 0x1078, 0x2f81, 0x70a3, 0x0002, 0x2009, 0x3935, 0x200b,
+ 0x0009, 0x0078, 0x2695, 0x1078, 0x2f8d, 0x781b, 0x0067, 0x0078,
+ 0x1efb, 0xa282, 0x0004, 0x0050, 0x269f, 0x1078, 0x1eac, 0x2300,
+ 0x0079, 0x26a2, 0x26a5, 0x2781, 0x27a9, 0xa286, 0x0003, 0x0040,
+ 0x26ab, 0x1078, 0x1eac, 0x2001, 0x0000, 0x703a, 0x7000, 0xa084,
+ 0x0007, 0x0079, 0x26b3, 0x26bb, 0x26bd, 0x26bd, 0x2871, 0x28a2,
+ 0x1f03, 0x28a2, 0x26bb, 0x1078, 0x1eac, 0xa684, 0x1000, 0x00c0,
+ 0x26c5, 0x1078, 0x32f5, 0x0040, 0x275b, 0x7868, 0xa08c, 0x00ff,
+ 0x0040, 0x270d, 0xa186, 0x0008, 0x00c0, 0x26dc, 0x1078, 0x2cdf,
+ 0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x2c92, 0x0040, 0x270d,
+ 0x1078, 0x32f5, 0x0078, 0x26f4, 0xa186, 0x0028, 0x00c0, 0x270d,
+ 0x1078, 0x32f5, 0x6008, 0xa084, 0xffef, 0x600a, 0x6018, 0xa005,
+ 0x0040, 0x26f4, 0x8001, 0x601a, 0xa005, 0x0040, 0x26f4, 0x8001,
+ 0xa005, 0x0040, 0x26f4, 0x601e, 0x6820, 0xa084, 0x0001, 0x0040,
+ 0x1f03, 0x6820, 0xa084, 0xfffe, 0x6822, 0x7054, 0x0c7e, 0x2060,
+ 0x6800, 0x6002, 0x0c7f, 0x6004, 0x6802, 0xa005, 0x2d00, 0x00c0,
+ 0x270a, 0x6002, 0x6006, 0x0078, 0x1f03, 0x017e, 0x1078, 0x27ce,
+ 0x017f, 0xa684, 0xdf00, 0x681e, 0x682b, 0x0000, 0x6f14, 0x81ff,
+ 0x0040, 0x275b, 0xa186, 0x0002, 0x00c0, 0x2753, 0xa684, 0x0800,
+ 0x00c0, 0x272a, 0xa684, 0x0060, 0x0040, 0x272a, 0x78d8, 0x7adc,
+ 0x682e, 0x6a32, 0x6820, 0xa084, 0x0800, 0x00c0, 0x275b, 0x8717,
+ 0xa294, 0x000f, 0x8213, 0x8213, 0x8213, 0xa290, 0x3a00, 0xa290,
+ 0x0000, 0x221c, 0x8210, 0x2204, 0xa085, 0x0018, 0x2012, 0x8211,
+ 0xa384, 0x0400, 0x0040, 0x274d, 0x68a0, 0xa084, 0x0100, 0x00c0,
+ 0x274d, 0x1078, 0x2830, 0x0078, 0x1f03, 0x6008, 0xa085, 0x0002,
+ 0x600a, 0x0078, 0x275b, 0xa186, 0x0018, 0x0040, 0x275b, 0xa186,
+ 0x0014, 0x0040, 0x1f03, 0x6916, 0x6818, 0xa084, 0x8000, 0x0040,
+ 0x2763, 0x7038, 0x681a, 0xa68c, 0xdf00, 0x691e, 0x1078, 0x2cd0,
+ 0x1078, 0x2cdf, 0x00c0, 0x2770, 0x6008, 0xa084, 0xffef, 0x600a,
+ 0x6820, 0xa084, 0x0001, 0x00c0, 0x2779, 0x1078, 0x2cc9, 0x0078,
+ 0x277d, 0x7054, 0x2060, 0x6800, 0x6002, 0x1078, 0x1a26, 0x0078,
+ 0x1f03, 0xa282, 0x0004, 0x0048, 0x2787, 0x1078, 0x1eac, 0x2200,
+ 0x0079, 0x278a, 0x2785, 0x278e, 0x2794, 0x278e, 0x1078, 0x2f7d,
+ 0x781b, 0x0067, 0x0078, 0x1efb, 0x7890, 0x8007, 0x8001, 0xa084,
+ 0x0007, 0xa080, 0x0018, 0x789a, 0x79a8, 0xa18c, 0x00ff, 0xa186,
+ 0x0003, 0x0040, 0x27a5, 0x0078, 0x2f69, 0x781b, 0x0068, 0x0078,
+ 0x1efb, 0x6820, 0xa085, 0x0004, 0x6822, 0x82ff, 0x00c0, 0x27b4,
+ 0x1078, 0x2f7d, 0x0078, 0x27bb, 0x8211, 0x0040, 0x27b9, 0x1078,
+ 0x1eac, 0x1078, 0x2f8d, 0x781b, 0x0067, 0x0078, 0x1efb, 0x1078,
+ 0x316a, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x27cb, 0x0018, 0x27cb,
+ 0x791a, 0xa006, 0x007c, 0xa085, 0x0001, 0x007c, 0xa684, 0x0060,
+ 0x00c0, 0x27d8, 0x682f, 0x0000, 0x6833, 0x0000, 0x0078, 0x282f,
+ 0xa684, 0x0800, 0x00c0, 0x27e8, 0x6998, 0x6a94, 0x692e, 0x6a32,
+ 0x7000, 0xa086, 0x0006, 0x0040, 0x27e7, 0x1078, 0x32f5, 0x007c,
+ 0xa684, 0x0020, 0x0040, 0x2802, 0xa684, 0x4000, 0x0040, 0x27f6,
+ 0x682f, 0x0000, 0x6833, 0x0000, 0x0078, 0x27e0, 0x7038, 0xa005,
+ 0x00c0, 0x27fc, 0x703b, 0x0015, 0x79d8, 0x7adc, 0x692e, 0x6a32,
+ 0x0078, 0x27e0, 0xa684, 0x4000, 0x0040, 0x280c, 0x682f, 0x0000,
+ 0x6833, 0x0000, 0x0078, 0x27e0, 0x7038, 0xa005, 0x00c0, 0x2812,
+ 0x703b, 0x0015, 0x79d8, 0x7adc, 0x78d0, 0x80fb, 0x00c8, 0x2819,
+ 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x692e, 0x6a32,
+ 0x2100, 0xa205, 0x00c0, 0x2826, 0x0078, 0x27e0, 0x7000, 0xa086,
+ 0x0006, 0x0040, 0x282f, 0x1078, 0x362f, 0x0078, 0x27e0, 0x007c,
+ 0xa384, 0x0200, 0x0040, 0x2838, 0x6008, 0xa085, 0x0002, 0x600a,
+ 0x681b, 0x0006, 0x6a30, 0x692c, 0x6a3e, 0x6942, 0x682f, 0x0003,
+ 0x6833, 0x0000, 0x6837, 0x0020, 0x6897, 0x0000, 0x689b, 0x0020,
+ 0x7000, 0x0079, 0x284b, 0x2853, 0x2855, 0x285e, 0x2853, 0x2853,
+ 0x2853, 0x2853, 0x2853, 0x1078, 0x1eac, 0x6820, 0xa084, 0x0001,
+ 0x00c0, 0x285e, 0x1078, 0x2cc9, 0x0078, 0x2864, 0x7054, 0x2c50,
+ 0x2060, 0x6800, 0x6002, 0x2a60, 0x2021, 0x3957, 0x2404, 0xa005,
+ 0x0040, 0x286d, 0x2020, 0x0078, 0x2866, 0x2d22, 0x206b, 0x0000,
+ 0x007c, 0x1078, 0x2cd0, 0x1078, 0x2cdf, 0x682b, 0x0000, 0x789b,
+ 0x000e, 0x6f14, 0x6817, 0x0002, 0x1078, 0x366b, 0xa684, 0x0800,
+ 0x0040, 0x2886, 0x691c, 0xa18d, 0x2000, 0x691e, 0x6818, 0xa084,
+ 0x8000, 0x0040, 0x2896, 0x7868, 0xa08c, 0x00ff, 0x0040, 0x2894,
+ 0x681b, 0x001e, 0x0078, 0x2896, 0x681b, 0x0000, 0x2021, 0x3957,
+ 0x6800, 0x2022, 0x6a3c, 0x6940, 0x6a32, 0x692e, 0x1078, 0x1a26,
+ 0x0078, 0x1f03, 0x1078, 0x27ce, 0x682b, 0x0000, 0x789b, 0x000e,
+ 0x6f14, 0x1078, 0x316f, 0xa08c, 0x00ff, 0x6916, 0x6818, 0xa084,
+ 0x8000, 0x0040, 0x28b5, 0x7038, 0x681a, 0xa68c, 0xdf00, 0x691e,
+ 0x70a3, 0x0000, 0x0078, 0x1f03, 0xa006, 0x1078, 0x32f5, 0x6817,
+ 0x0000, 0x681b, 0x0001, 0xa68c, 0xdf00, 0x691e, 0x682b, 0x0000,
+ 0x7000, 0x0079, 0x28cb, 0x28d3, 0x28d5, 0x28d5, 0x28d7, 0x28d7,
+ 0x28d7, 0x28d7, 0x28d3, 0x1078, 0x1eac, 0x1078, 0x2cdf, 0x6008,
+ 0xa084, 0xffef, 0x600a, 0x0078, 0x2caa, 0x2300, 0x0079, 0x28e0,
+ 0x28e3, 0x28e5, 0x2939, 0x1078, 0x1eac, 0x7000, 0x0079, 0x28e8,
+ 0x28f0, 0x28f2, 0x28f2, 0x290d, 0x28f2, 0x291a, 0x290d, 0x28f0,
+ 0x1078, 0x1eac, 0xa684, 0x0060, 0xa086, 0x0060, 0x00c0, 0x2909,
+ 0xa6b4, 0xffdf, 0xa6b4, 0xbfff, 0xa6b5, 0x2000, 0x7e5a, 0x681c,
+ 0xa084, 0xffdf, 0x681e, 0x1078, 0x32f5, 0x1078, 0x3571, 0x0078,
+ 0x311c, 0xa684, 0x2000, 0x0040, 0x28fc, 0x6818, 0xa084, 0x8000,
+ 0x0040, 0x291a, 0x681b, 0x0015, 0xa684, 0x4000, 0x0040, 0x291a,
+ 0x681b, 0x0007, 0x2009, 0x391e, 0x210c, 0xa186, 0x0000, 0x0040,
+ 0x292f, 0xa186, 0x0001, 0x0040, 0x2933, 0x2009, 0x3935, 0x200b,
+ 0x000b, 0x70a3, 0x0001, 0x781b, 0x0047, 0x0078, 0x1efb, 0x781b,
+ 0x00df, 0x0078, 0x1efb, 0x2009, 0x3935, 0x200b, 0x000a, 0x0078,
+ 0x1efb, 0x1078, 0x1eac, 0x2300, 0x0079, 0x293e, 0x2941, 0x2943,
+ 0x2976, 0x1078, 0x1eac, 0x7000, 0x0079, 0x2946, 0x294e, 0x2950,
+ 0x2950, 0x296b, 0x2950, 0x2972, 0x296b, 0x294e, 0x1078, 0x1eac,
+ 0xa684, 0x0060, 0xa086, 0x0060, 0x00c0, 0x2967, 0xa6b4, 0xffbf,
+ 0xa6b4, 0xbfff, 0xa6b5, 0x2000, 0x7e5a, 0x681c, 0xa084, 0xffbf,
+ 0x681e, 0x1078, 0x32f5, 0x1078, 0x3571, 0x0078, 0x311c, 0xa684,
+ 0x2000, 0x0040, 0x295a, 0x6818, 0xa084, 0x8000, 0x0040, 0x2972,
+ 0x681b, 0x0007, 0x781b, 0x00e6, 0x0078, 0x1efb, 0x6820, 0xa085,
+ 0x0004, 0x6822, 0x1078, 0x30e7, 0xa6b5, 0x0800, 0x1078, 0x2f7d,
+ 0x781b, 0x0067, 0x0078, 0x1efb, 0x2300, 0x0079, 0x2987, 0x298a,
+ 0x298c, 0x298e, 0x1078, 0x1eac, 0x1078, 0x1eac, 0xa684, 0x0400,
+ 0x00c0, 0x29ae, 0x782b, 0x3009, 0x6920, 0xa18c, 0xfdff, 0xa18c,
+ 0xfeff, 0x6922, 0x789b, 0x0060, 0x78ab, 0x0000, 0xa684, 0xfffb,
+ 0x785a, 0x79e4, 0xa184, 0x0020, 0x00c0, 0x29aa, 0x2001, 0x0014,
+ 0x0078, 0x26ad, 0xa184, 0x0007, 0x0079, 0x29e6, 0x7a90, 0xa294,
+ 0x0007, 0x789b, 0x0060, 0x79a8, 0x81ff, 0x0040, 0x29e4, 0x789b,
+ 0x0010, 0x7ba8, 0xa384, 0x0001, 0x00c0, 0x29d5, 0x7ba8, 0x7ba8,
+ 0xa386, 0x0001, 0x00c0, 0x29c8, 0x2009, 0xfff7, 0x0078, 0x29ce,
+ 0xa386, 0x0003, 0x00c0, 0x29d5, 0x2009, 0xffef, 0x0c7e, 0x7048,
+ 0x2060, 0x6004, 0xa104, 0x6006, 0x0c7f, 0x789b, 0x0060, 0x78ab,
+ 0x0000, 0xa684, 0xfffb, 0x785a, 0x782b, 0x3009, 0x6920, 0xa18c,
+ 0xfdff, 0xa18c, 0xfeff, 0x6922, 0x0078, 0x311c, 0x239b, 0x23a8,
+ 0x3124, 0x3124, 0x29ee, 0x29ee, 0x29ee, 0x3124, 0x1078, 0x1eac,
+ 0x79e4, 0xa184, 0x0030, 0x00c0, 0x2a06, 0x70a0, 0xa086, 0x0002,
+ 0x00c0, 0x29fe, 0x2011, 0x0002, 0x0078, 0x2288, 0x6818, 0xa085,
+ 0x8000, 0x681a, 0x2001, 0x0014, 0x0078, 0x26ad, 0xa184, 0x0007,
+ 0x0079, 0x2a0a, 0x311c, 0x311c, 0x2a12, 0x311c, 0x3144, 0x3144,
+ 0x311c, 0x311c, 0xa684, 0x0080, 0x0040, 0x2a41, 0x71c8, 0x81ff,
+ 0x0040, 0x2a41, 0xa182, 0x000d, 0x00d0, 0x2a22, 0x70cb, 0x0000,
+ 0x0078, 0x2a27, 0xa182, 0x000c, 0x70ca, 0x2009, 0x000c, 0x789b,
+ 0x0061, 0x79aa, 0x157e, 0x137e, 0x147e, 0x70cc, 0x8114, 0xa210,
+ 0x72ce, 0xa080, 0x000b, 0xad00, 0x2098, 0x20a1, 0x012b, 0x789b,
+ 0x0000, 0x8108, 0x81ac, 0x53a6, 0x147f, 0x137f, 0x157f, 0x0078,
+ 0x3124, 0xa684, 0x0400, 0x00c0, 0x2a82, 0x6820, 0xa084, 0x0001,
+ 0x0040, 0x3124, 0xa68c, 0x0060, 0xa684, 0x0060, 0x0040, 0x2a56,
+ 0xa086, 0x0060, 0x00c0, 0x2a56, 0xa18d, 0x4000, 0xa18c, 0xfffb,
+ 0x795a, 0x69b6, 0x789b, 0x0060, 0x78ab, 0x0000, 0x789b, 0x0061,
+ 0x6818, 0xa085, 0x8000, 0x681a, 0x78aa, 0x8008, 0x810c, 0x0040,
+ 0x2d07, 0xa18c, 0x00f8, 0x00c0, 0x2d07, 0x157e, 0x137e, 0x147e,
+ 0x20a1, 0x012b, 0x789b, 0x0000, 0x8000, 0x80ac, 0xad80, 0x000b,
+ 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f, 0x6814, 0x8007, 0x7882,
+ 0x0078, 0x3124, 0x6818, 0xa084, 0x8000, 0x0040, 0x2a89, 0x681b,
+ 0x0008, 0x781b, 0x00da, 0x0078, 0x1efb, 0x2300, 0x0079, 0x2a90,
+ 0x2a95, 0x2b18, 0x2a93, 0x1078, 0x1eac, 0x7000, 0xa084, 0x0007,
+ 0x0079, 0x2a9a, 0x2aa2, 0x2aa4, 0x2ac0, 0x2aa2, 0x2aa2, 0x1f03,
+ 0x2aa2, 0x2aa2, 0x1078, 0x1eac, 0x6920, 0xa18d, 0x0001, 0x6922,
+ 0x6800, 0x6006, 0xa005, 0x00c0, 0x2aae, 0x6002, 0x681c, 0xa084,
+ 0x000e, 0x0040, 0x2aba, 0x7014, 0x68ba, 0x712c, 0xa188, 0x4280,
+ 0x0078, 0x2abc, 0x2009, 0x4380, 0x2104, 0x6802, 0x2d0a, 0x7156,
+ 0x6920, 0xa184, 0x8000, 0x00c0, 0x2acc, 0x68af, 0x0000, 0x68b3,
+ 0x0000, 0xa18d, 0x8000, 0x6922, 0x6eb6, 0xa684, 0x0060, 0x0040,
+ 0x2b16, 0xa684, 0x0800, 0x00c0, 0x2add, 0x6894, 0x68a6, 0x6898,
+ 0x68aa, 0x1078, 0x32f5, 0x0078, 0x2b16, 0xa684, 0x0020, 0x0040,
+ 0x2aea, 0xa006, 0x1078, 0x362f, 0x79d8, 0x7adc, 0x69aa, 0x6aa6,
+ 0x0078, 0x2af0, 0x1078, 0x30a6, 0x69aa, 0x6aa6, 0x1078, 0x362f,
+ 0xa684, 0x8000, 0x0040, 0x2b16, 0xa684, 0x7fff, 0x68b6, 0x789b,
+ 0x0074, 0x1078, 0x316f, 0x2010, 0x1078, 0x316f, 0x2008, 0xa684,
+ 0x0020, 0x00c0, 0x2b0e, 0x1078, 0x316f, 0x801b, 0x00c8, 0x2b09,
+ 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000, 0x6b98, 0x2100,
+ 0xa302, 0x68b2, 0x6b94, 0x2200, 0xa303, 0x68ae, 0x0078, 0x1f03,
+ 0x0078, 0x2f75, 0x7033, 0x0000, 0xa282, 0x0006, 0x0050, 0x2b22,
+ 0x1078, 0x1eac, 0x2300, 0x0079, 0x2b25, 0x2b28, 0x2b4e, 0x2b72,
+ 0x2200, 0x0079, 0x2b2b, 0x2b31, 0x2f75, 0x2b33, 0x2b31, 0x2b9c,
+ 0x2bed, 0x1078, 0x1eac, 0x7003, 0x0005, 0x2001, 0x4390, 0x2068,
+ 0x703e, 0x157e, 0x20a9, 0x002f, 0x2003, 0x0000, 0x8000, 0x0070,
+ 0x2b43, 0x0078, 0x2b3c, 0x157f, 0x6817, 0x0000, 0x68b7, 0x0700,
+ 0x6823, 0x0800, 0x6827, 0x0003, 0x0078, 0x2f69, 0x7000, 0xa086,
+ 0x0001, 0x00c0, 0x2b5b, 0x1078, 0x2cdf, 0x1078, 0x32f5, 0x7034,
+ 0x600a, 0x0078, 0x2b60, 0x7000, 0xa086, 0x0003, 0x0040, 0x2b55,
+ 0x7003, 0x0005, 0x2001, 0x4390, 0x2068, 0x703e, 0x7032, 0x2200,
+ 0x0079, 0x2b6a, 0x2f75, 0x2b70, 0x2b70, 0x2b9c, 0x2b70, 0x2f75,
+ 0x1078, 0x1eac, 0x7000, 0xa086, 0x0001, 0x00c0, 0x2b7f, 0x1078,
+ 0x2cdf, 0x1078, 0x32f5, 0x7034, 0x600a, 0x0078, 0x2b84, 0x7000,
+ 0xa086, 0x0003, 0x0040, 0x2b79, 0x7003, 0x0005, 0x2001, 0x4390,
+ 0x2068, 0x703e, 0x7032, 0x2200, 0x0079, 0x2b8e, 0x2b96, 0x2b94,
+ 0x2b94, 0x2b96, 0x2b94, 0x2b96, 0x1078, 0x1eac, 0x1078, 0x2f8d,
+ 0x781b, 0x0067, 0x0078, 0x1efb, 0x7000, 0xa086, 0x0001, 0x00c0,
+ 0x2ba9, 0x1078, 0x2cdf, 0x1078, 0x32f5, 0x7034, 0x600a, 0x0078,
+ 0x2bae, 0x7000, 0xa086, 0x0003, 0x0040, 0x2ba3, 0x7003, 0x0002,
+ 0x7a80, 0xa294, 0x0f00, 0x789b, 0x0018, 0x7ca8, 0xa484, 0x0007,
+ 0xa215, 0x2069, 0x4380, 0x2d04, 0x2d08, 0x7156, 0x2068, 0xa005,
+ 0x0040, 0x2bc9, 0x6814, 0xa206, 0x0040, 0x2be2, 0x6800, 0x0078,
+ 0x2bbc, 0x7003, 0x0005, 0x2001, 0x4390, 0x2068, 0x703e, 0x7032,
+ 0x157e, 0x20a9, 0x002f, 0x2003, 0x0000, 0x8000, 0x0070, 0x2bda,
+ 0x0078, 0x2bd3, 0x157f, 0x6a16, 0x68b7, 0x0700, 0x6823, 0x0800,
+ 0x6827, 0x0003, 0x6eb4, 0x7e5a, 0x6820, 0xa084, 0x0c00, 0x0040,
+ 0x2c4b, 0x1078, 0x2f85, 0x0078, 0x2c4b, 0x7000, 0xa086, 0x0001,
+ 0x00c0, 0x2bfa, 0x1078, 0x2cdf, 0x1078, 0x32f5, 0x7034, 0x600a,
+ 0x0078, 0x2bff, 0x7000, 0xa086, 0x0003, 0x0040, 0x2bf4, 0x7003,
+ 0x0002, 0x7a80, 0xa294, 0x0f00, 0x789b, 0x0018, 0x7ca8, 0xa484,
+ 0x0007, 0xa215, 0x79a8, 0x79a8, 0xa18c, 0x00ff, 0xa1e8, 0x4280,
+ 0x2d04, 0x2d08, 0x7156, 0x2068, 0xa005, 0x0040, 0x2c1e, 0x6814,
+ 0xa206, 0x0040, 0x2c36, 0x6800, 0x0078, 0x2c11, 0x7003, 0x0005,
+ 0x2001, 0x4390, 0x2068, 0x703e, 0x157e, 0x20a9, 0x002f, 0x2003,
+ 0x0000, 0x8000, 0x0070, 0x2c2e, 0x0078, 0x2c27, 0x157f, 0x6a16,
+ 0x68b7, 0x0700, 0x6823, 0x0800, 0x6827, 0x0003, 0x6eb4, 0x7e5a,
+ 0x6820, 0xa084, 0x0c00, 0x0040, 0x2c4b, 0xa084, 0x0800, 0x0040,
+ 0x2c45, 0x1078, 0x2f89, 0x0078, 0x2c4b, 0x1078, 0x2f85, 0x70bf,
+ 0x0000, 0x0078, 0x2c4b, 0x027e, 0x8207, 0xa084, 0x000f, 0x8003,
+ 0x8003, 0x8003, 0xa080, 0x3a00, 0x2060, 0x704a, 0x6000, 0x704e,
+ 0x6004, 0x7052, 0xa684, 0x0060, 0x0040, 0x2c64, 0x68a8, 0x78d2,
+ 0x78da, 0x68a4, 0x78d6, 0x78de, 0x077f, 0x1078, 0x3099, 0x2009,
+ 0x0068, 0xa684, 0x0008, 0x0040, 0x2c6f, 0x2009, 0x0067, 0xa6b5,
+ 0x2000, 0x7e5a, 0x791a, 0xa684, 0x0060, 0x0040, 0x2c85, 0xa684,
+ 0x0800, 0x00c0, 0x2c7f, 0x1078, 0x3571, 0x0078, 0x2c85, 0xa684,
+ 0x4000, 0x00c0, 0x2c85, 0x1078, 0x3502, 0x2d00, 0x703e, 0x8207,
+ 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa080, 0x3a00, 0x2048,
+ 0x0078, 0x1efb, 0x6020, 0xa005, 0x0040, 0x2c9e, 0x8001, 0x6022,
+ 0x6008, 0xa085, 0x0008, 0x600a, 0x7010, 0x6026, 0x007c, 0xa006,
+ 0x1078, 0x32f5, 0x6817, 0x0000, 0x681b, 0x0001, 0x6823, 0x0040,
+ 0x681f, 0x0100, 0x7000, 0xa084, 0x0007, 0x0079, 0x2caf, 0x2cb7,
+ 0x2cb9, 0x2cb9, 0x2cc5, 0x2cc1, 0x2cb7, 0x2cc1, 0x2cb7, 0x1078,
+ 0x1eac, 0x1078, 0x2cd0, 0x1078, 0x2cc9, 0x1078, 0x1a26, 0x0078,
+ 0x1f03, 0x70a3, 0x0000, 0x0078, 0x1f03, 0x681b, 0x0000, 0x0078,
+ 0x2871, 0x6800, 0xa005, 0x00c0, 0x2cce, 0x6002, 0x6006, 0x007c,
+ 0x6010, 0xa005, 0x0040, 0x2cd9, 0x8001, 0x00d0, 0x2cd9, 0x1078,
+ 0x1eac, 0x6012, 0x6008, 0xa084, 0xffef, 0x600a, 0x007c, 0x6018,
+ 0xa005, 0x0040, 0x2ce5, 0x8001, 0x601a, 0x007c, 0x1078, 0x316a,
+ 0x6818, 0xa084, 0x8000, 0x0040, 0x2cef, 0x681b, 0x0018, 0x0078,
+ 0x2d26, 0x1078, 0x316a, 0x6818, 0xa084, 0x8000, 0x0040, 0x2cfa,
+ 0x681b, 0x0019, 0x0078, 0x2d26, 0x1078, 0x316a, 0x6818, 0xa084,
+ 0x8000, 0x0040, 0x2d05, 0x681b, 0x001a, 0x0078, 0x2d26, 0x1078,
+ 0x316a, 0x681b, 0x0003, 0x0078, 0x2d26, 0x71b8, 0xa18c, 0x00ff,
+ 0xa1e8, 0x4280, 0x2d04, 0x2d08, 0x2068, 0xa005, 0x00c0, 0x2d1a,
+ 0x0078, 0x1f03, 0x6814, 0x72b4, 0xa206, 0x0040, 0x2d22, 0x6800,
+ 0x0078, 0x2d13, 0x6800, 0x200a, 0x681b, 0x0005, 0x681f, 0x0000,
+ 0x6823, 0x0020, 0x1078, 0x2cd0, 0x1078, 0x2cc9, 0x1078, 0x1a26,
+ 0x0078, 0x1f03, 0xa282, 0x0003, 0x00c0, 0x2f69, 0x7da8, 0xa5ac,
+ 0x00ff, 0x7ea8, 0xa6b4, 0x00ff, 0x6920, 0xa18d, 0x0080, 0x6922,
+ 0xa184, 0x0100, 0x0040, 0x2d92, 0xa18c, 0xfeff, 0x6922, 0xa6b4,
+ 0x00ff, 0x0040, 0x2d7c, 0xa682, 0x000c, 0x0048, 0x2d53, 0x0040,
+ 0x2d53, 0x2031, 0x000c, 0x852b, 0x852b, 0x1078, 0x3018, 0x0040,
+ 0x2d5d, 0x1078, 0x2e4a, 0x0078, 0x2d85, 0x1078, 0x2fd3, 0x0c7e,
+ 0x2960, 0x6004, 0xa084, 0xfff5, 0x6006, 0x1078, 0x2e6e, 0x0c7f,
+ 0x6920, 0xa18d, 0x0100, 0x6922, 0x7e58, 0xa6b5, 0x0004, 0x7e5a,
+ 0xa684, 0x0400, 0x00c0, 0x2d78, 0x781b, 0x0053, 0x0078, 0x1efb,
+ 0x781b, 0x0067, 0x0078, 0x1efb, 0x0c7e, 0x2960, 0x6004, 0xa084,
+ 0xfff5, 0x6006, 0x1078, 0x2e6e, 0x0c7f, 0x7e58, 0xa684, 0x0400,
+ 0x00c0, 0x2d8e, 0x781b, 0x0056, 0x0078, 0x1efb, 0x781b, 0x0068,
+ 0x0078, 0x1efb, 0x0c7e, 0x7048, 0x2060, 0x6100, 0xa18c, 0x1000,
+ 0x0040, 0x2dd2, 0x6208, 0x8217, 0xa294, 0x00ff, 0xa282, 0x000c,
+ 0x0048, 0x2da6, 0x0040, 0x2da6, 0x2011, 0x000c, 0x2600, 0xa202,
+ 0x00c8, 0x2dab, 0x2230, 0x6208, 0xa294, 0x00ff, 0x7018, 0xa086,
+ 0x0028, 0x00c0, 0x2dbb, 0xa282, 0x0019, 0x00c8, 0x2dc1, 0x2011,
+ 0x0019, 0x0078, 0x2dc1, 0xa282, 0x000c, 0x00c8, 0x2dc1, 0x2011,
+ 0x000c, 0x2200, 0xa502, 0x00c8, 0x2dc6, 0x2228, 0x1078, 0x2fd7,
+ 0x852b, 0x852b, 0x1078, 0x3018, 0x0040, 0x2dd2, 0x1078, 0x2e4a,
+ 0x0078, 0x2dd6, 0x1078, 0x2fd3, 0x1078, 0x2e6e, 0x7858, 0xa085,
+ 0x0004, 0x785a, 0x0c7f, 0x781b, 0x0067, 0x0078, 0x1efb, 0x0c7e,
+ 0x2960, 0x6000, 0xa084, 0x1000, 0x00c0, 0x2df7, 0x6010, 0xa084,
+ 0x000f, 0x00c0, 0x2df1, 0x6104, 0xa18c, 0xfff5, 0x6106, 0x0c7f,
+ 0x007c, 0x2011, 0x0032, 0x2019, 0x0000, 0x0078, 0x2e1e, 0x68a0,
+ 0xa084, 0x0200, 0x00c0, 0x2df1, 0x6208, 0xa294, 0x00ff, 0x7018,
+ 0xa086, 0x0028, 0x00c0, 0x2e0c, 0xa282, 0x0019, 0x00c8, 0x2e12,
+ 0x2011, 0x0019, 0x0078, 0x2e12, 0xa282, 0x000c, 0x00c8, 0x2e12,
+ 0x2011, 0x000c, 0x6308, 0x831f, 0xa39c, 0x00ff, 0xa382, 0x000c,
+ 0x0048, 0x2e1e, 0x0040, 0x2e1e, 0x2019, 0x000c, 0x78ab, 0x0001,
+ 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7aaa, 0x7baa, 0xa8c0, 0x0005,
+ 0x6820, 0xa085, 0x0100, 0x6822, 0x0c7f, 0x007c, 0x0c7e, 0x2960,
+ 0x6104, 0xa18c, 0xfff5, 0x6106, 0x2011, 0x0032, 0x2019, 0x0000,
+ 0x0078, 0x2e3a, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001,
+ 0x7aaa, 0x7baa, 0xa8c0, 0x0005, 0x6820, 0xa085, 0x0100, 0x6822,
+ 0x0c7f, 0x007c, 0x0c7e, 0x7148, 0x2160, 0x2008, 0xa084, 0xfff0,
+ 0xa635, 0x7e86, 0x6018, 0x789a, 0x7eae, 0x6612, 0x78a4, 0xa084,
+ 0xfff8, 0xa18c, 0x0007, 0xa105, 0x78a6, 0x6016, 0x788a, 0xa6b4,
+ 0x000f, 0x8637, 0x8204, 0x8004, 0xa084, 0x00ff, 0xa605, 0x600e,
+ 0x6004, 0xa084, 0xfff5, 0x6006, 0x0c7f, 0x007c, 0x0c7e, 0x7048,
+ 0x2060, 0x6018, 0x789a, 0x78a4, 0xa084, 0xfff0, 0x78a6, 0x6012,
+ 0x7884, 0xa084, 0xfff0, 0x7886, 0x0c7f, 0x007c, 0xa282, 0x0002,
+ 0x00c0, 0x2f69, 0x7aa8, 0x6920, 0xa18d, 0x0080, 0x6922, 0xa184,
+ 0x0200, 0x0040, 0x2ec3, 0xa18c, 0xfdff, 0x6922, 0xa294, 0x00ff,
+ 0xa282, 0x0002, 0x00c8, 0x2f69, 0x1078, 0x2f0a, 0x1078, 0x2e6e,
+ 0xa980, 0x0001, 0x200c, 0x1078, 0x3095, 0x1078, 0x2ddf, 0x88ff,
+ 0x0040, 0x2eb6, 0x789b, 0x0060, 0x2800, 0x78aa, 0x7e58, 0xa6b5,
+ 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x2eb2, 0x781b, 0x0053,
+ 0x0078, 0x1efb, 0x781b, 0x0067, 0x0078, 0x1efb, 0x7e58, 0xa684,
+ 0x0400, 0x00c0, 0x2ebf, 0x781b, 0x0056, 0x0078, 0x1efb, 0x781b,
+ 0x0068, 0x0078, 0x1efb, 0xa282, 0x0002, 0x00c8, 0x2ecb, 0xa284,
+ 0x0001, 0x0040, 0x2ed5, 0x7148, 0xa188, 0x0000, 0x210c, 0xa18c,
+ 0x2000, 0x00c0, 0x2ed5, 0x2011, 0x0000, 0x1078, 0x2fc5, 0x1078,
+ 0x2f0a, 0x1078, 0x2e6e, 0x7858, 0xa085, 0x0004, 0x785a, 0x781b,
+ 0x0067, 0x0078, 0x1efb, 0x0c7e, 0x027e, 0x2960, 0x6000, 0x2011,
+ 0x0001, 0xa084, 0x2000, 0x00c0, 0x2efa, 0x6014, 0xa084, 0x0040,
+ 0x00c0, 0x2ef8, 0xa18c, 0xffef, 0x6106, 0xa006, 0x0078, 0x2f07,
+ 0x2011, 0x0000, 0x78ab, 0x0001, 0x78ab, 0x0002, 0x78ab, 0x0003,
+ 0x7aaa, 0xa8c0, 0x0004, 0x6820, 0xa085, 0x0200, 0x6822, 0x027f,
+ 0x0c7f, 0x007c, 0x0c7e, 0x7048, 0x2060, 0x82ff, 0x0040, 0x2f12,
+ 0x2011, 0x0040, 0x6018, 0xa080, 0x0002, 0x789a, 0x78a4, 0xa084,
+ 0xffbf, 0xa205, 0x78a6, 0x6016, 0x788a, 0x6004, 0xa084, 0xffef,
+ 0x6006, 0x0c7f, 0x007c, 0xa684, 0x0020, 0x0040, 0x2f65, 0x7888,
+ 0xa084, 0x0040, 0x0040, 0x2f65, 0x7bb8, 0xa384, 0x003f, 0x831b,
+ 0x00c8, 0x2f33, 0x8000, 0xa005, 0x0040, 0x2f4c, 0x831b, 0x00c8,
+ 0x2f3c, 0x8001, 0x0040, 0x2f61, 0xa684, 0x4000, 0x0040, 0x2f4c,
+ 0x78b8, 0x801b, 0x00c8, 0x2f45, 0x8000, 0xa084, 0x003f, 0x00c0,
+ 0x2f61, 0xa6b4, 0xbfff, 0x7e5a, 0x79d8, 0x7adc, 0x2001, 0x0001,
+ 0xa108, 0x00c8, 0x2f55, 0xa291, 0x0000, 0x79d2, 0x79da, 0x7ad6,
+ 0x7ade, 0x1078, 0x362f, 0x781b, 0x0065, 0x1078, 0x3502, 0x0078,
+ 0x1efb, 0x781b, 0x0065, 0x0078, 0x1efb, 0x781b, 0x0068, 0x0078,
+ 0x1efb, 0x1078, 0x2f91, 0x781b, 0x0067, 0x0078, 0x1efb, 0x1078,
+ 0x2f7d, 0x781b, 0x0067, 0x0078, 0x1efb, 0x6827, 0x0002, 0x1078,
+ 0x2f85, 0x781b, 0x0067, 0x0078, 0x1efb, 0x2001, 0x0005, 0x0078,
+ 0x2f93, 0x2001, 0x000c, 0x0078, 0x2f93, 0x2001, 0x0006, 0x0078,
+ 0x2f93, 0x2001, 0x000d, 0x0078, 0x2f93, 0x2001, 0x0009, 0x0078,
+ 0x2f93, 0x2001, 0x0007, 0x789b, 0x007e, 0x78aa, 0xa6b5, 0x0008,
+ 0x7e5a, 0x007c, 0x077e, 0x873f, 0xa7bc, 0x000f, 0x873b, 0x873b,
+ 0x8703, 0xa0e0, 0x3a00, 0xa7b8, 0x0020, 0x7f9a, 0x79a4, 0xa184,
+ 0x000f, 0x0040, 0x2fb3, 0xa184, 0xfff0, 0x78a6, 0x6012, 0x6004,
+ 0xa085, 0x0008, 0x6006, 0x8738, 0x8738, 0x7f9a, 0x79a4, 0xa184,
+ 0x0040, 0x0040, 0x2fc3, 0xa184, 0xffbf, 0x78a6, 0x6016, 0x6004,
+ 0xa085, 0x0010, 0x6006, 0x077f, 0x007c, 0x789b, 0x0010, 0x78ab,
+ 0x0001, 0x78ab, 0x0002, 0x78ab, 0x0003, 0x7aaa, 0x789b, 0x0060,
+ 0x78ab, 0x0004, 0x007c, 0x2031, 0x0000, 0x2029, 0x0032, 0x789b,
+ 0x0010, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7daa,
+ 0x7eaa, 0x789b, 0x0060, 0x78ab, 0x0005, 0x007c, 0x157e, 0x8007,
+ 0xa084, 0x00ff, 0x8003, 0x8003, 0xa080, 0x0020, 0x789a, 0x79a4,
+ 0xa18c, 0xfff0, 0x2001, 0x3946, 0x2004, 0xa082, 0x0028, 0x0040,
+ 0x3001, 0x2021, 0x307c, 0x2019, 0x0014, 0x20a9, 0x000c, 0x0078,
+ 0x3007, 0x2021, 0x3088, 0x2019, 0x0019, 0x20a9, 0x000d, 0x2011,
+ 0x0064, 0x2404, 0xa084, 0xfff0, 0xa106, 0x0040, 0x3016, 0x8420,
+ 0x2300, 0xa210, 0x0070, 0x3016, 0x0078, 0x3009, 0x157f, 0x007c,
+ 0x157e, 0x2011, 0x3946, 0x2214, 0xa282, 0x0032, 0x0048, 0x302c,
+ 0x0040, 0x3030, 0x2021, 0x306e, 0x2019, 0x0011, 0x20a9, 0x000e,
+ 0x2011, 0x0032, 0x0078, 0x3042, 0xa282, 0x0028, 0x0040, 0x303a,
+ 0x2021, 0x307c, 0x2019, 0x0014, 0x20a9, 0x000c, 0x2011, 0x0064,
+ 0x0078, 0x3042, 0x2021, 0x3088, 0x2019, 0x0019, 0x20a9, 0x000d,
+ 0x2011, 0x0064, 0x2200, 0xa502, 0x0040, 0x3052, 0x0048, 0x3052,
+ 0x8420, 0x2300, 0xa210, 0x0070, 0x304f, 0x0078, 0x3042, 0x157f,
+ 0xa006, 0x007c, 0x157f, 0x7a08, 0xa582, 0x0064, 0x00c8, 0x305d,
+ 0xa285, 0x0040, 0x780a, 0x0078, 0x305d, 0x78ec, 0xa084, 0x0300,
+ 0x0040, 0x306b, 0x2404, 0xa09e, 0x2002, 0x00c0, 0x306b, 0x2001,
+ 0x2101, 0x0078, 0x306c, 0x2404, 0xa015, 0x007c, 0x2002, 0x3002,
+ 0x3202, 0x4203, 0x4403, 0x5404, 0x5604, 0x6605, 0x6805, 0x7806,
+ 0x7a06, 0x0a07, 0x0c07, 0x0e07, 0x3202, 0x4202, 0x5202, 0x6202,
+ 0x7202, 0x6605, 0x7605, 0x7805, 0x7a05, 0x7c05, 0x7e05, 0x7f05,
+ 0x2202, 0x3202, 0x4202, 0x5202, 0x5404, 0x6404, 0x7404, 0x7604,
+ 0x7804, 0x7a04, 0x7c04, 0x7e04, 0x7f04, 0x789b, 0x0010, 0xa046,
+ 0x007c, 0xa784, 0x0f00, 0x800c, 0xa784, 0x0007, 0x8003, 0x8003,
+ 0x8003, 0x8003, 0xa105, 0xa0e0, 0x3a80, 0x007c, 0x79d8, 0x7adc,
+ 0x78d0, 0x801b, 0x00c8, 0x30ad, 0x8000, 0xa084, 0x003f, 0xa108,
+ 0xa291, 0x0000, 0x007c, 0x0f7e, 0x2079, 0x0100, 0x2009, 0x3940,
+ 0x2091, 0x8000, 0x2104, 0x0079, 0x30bd, 0x30e3, 0x30c7, 0x30c7,
+ 0x30c7, 0x30c7, 0x30c7, 0x30c7, 0x30c5, 0x1078, 0x1eac, 0x784b,
+ 0x0004, 0x68b4, 0xa085, 0x4000, 0x68b6, 0x7858, 0xa085, 0x4000,
+ 0x785a, 0x7830, 0xa084, 0x0080, 0x00c0, 0x30e3, 0x0018, 0x30e3,
+ 0x681c, 0xa084, 0x0020, 0x00c0, 0x30e1, 0x781b, 0x00df, 0x0078,
+ 0x30e3, 0x781b, 0x00e6, 0x2091, 0x8001, 0x0f7f, 0x007c, 0x0c7e,
+ 0x6814, 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e0,
+ 0x3a00, 0x6004, 0xa084, 0x000a, 0x00c0, 0x311a, 0x6108, 0xa194,
+ 0xff00, 0x0040, 0x311a, 0xa18c, 0x00ff, 0x2001, 0x0019, 0xa106,
+ 0x0040, 0x3109, 0x2001, 0x0032, 0xa106, 0x0040, 0x310d, 0x0078,
+ 0x3111, 0x2009, 0x0020, 0x0078, 0x3113, 0x2009, 0x003f, 0x0078,
+ 0x3113, 0x2011, 0x0000, 0x2100, 0xa205, 0x600a, 0x6004, 0xa085,
+ 0x0002, 0x6006, 0x0c7f, 0x007c, 0x781b, 0x0068, 0x0078, 0x1efb,
+ 0x781b, 0x0067, 0x0078, 0x1efb, 0x781b, 0x0056, 0x0078, 0x1efb,
+ 0x781b, 0x0053, 0x0078, 0x1efb, 0x781b, 0x00df, 0x0078, 0x1efb,
+ 0x781b, 0x00de, 0x0078, 0x1efb, 0x781b, 0x00e6, 0x0078, 0x1efb,
+ 0x781b, 0x00e5, 0x0078, 0x1efb, 0x781b, 0x009d, 0x0078, 0x1efb,
+ 0x781b, 0x009c, 0x0078, 0x1efb, 0x6818, 0xa084, 0x8000, 0x0040,
+ 0x314b, 0x681b, 0x001d, 0x70a3, 0x0001, 0x781b, 0x0047, 0x0078,
+ 0x1efb, 0x007e, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x3168, 0x7808,
+ 0xa084, 0xfffd, 0x780a, 0x0005, 0x0005, 0x0005, 0x0005, 0x78ec,
+ 0xa084, 0x0021, 0x0040, 0x3168, 0x7808, 0xa085, 0x0002, 0x780a,
+ 0x007f, 0x007c, 0x7808, 0xa085, 0x0002, 0x780a, 0x007c, 0x7830,
+ 0xa084, 0x0040, 0x00c0, 0x316f, 0x0098, 0x3178, 0x78ac, 0x007c,
+ 0x7808, 0xa084, 0xfffd, 0x780a, 0x0005, 0x0005, 0x0005, 0x0005,
+ 0x78ec, 0xa084, 0x0021, 0x0040, 0x3187, 0x0098, 0x3185, 0x78ac,
+ 0x007e, 0x7808, 0xa085, 0x0002, 0x780a, 0x007f, 0x007c, 0xa784,
+ 0x0070, 0x0040, 0x319b, 0x0c7e, 0x2d60, 0x2f68, 0x1078, 0x1e57,
+ 0x2d78, 0x2c68, 0x0c7f, 0xa784, 0x0008, 0x0040, 0x31a8, 0x784b,
+ 0x0008, 0x78ec, 0xa084, 0x0003, 0x0040, 0x1f03, 0x0078, 0x311c,
+ 0xa784, 0x0004, 0x0040, 0x31db, 0x78b8, 0xa084, 0x4001, 0x0040,
+ 0x31db, 0x784b, 0x0008, 0x78ec, 0xa084, 0x0003, 0x0040, 0x1f03,
+ 0x78e4, 0xa084, 0x0007, 0xa086, 0x0001, 0x00c0, 0x31db, 0x78c0,
+ 0xa685, 0x4800, 0x2030, 0x7e5a, 0x781b, 0x00e6, 0x0078, 0x1efb,
+ 0x784b, 0x0008, 0x6818, 0xa084, 0x8000, 0x0040, 0x31d7, 0x681b,
+ 0x0015, 0xa684, 0x4000, 0x0040, 0x31d7, 0x681b, 0x0007, 0x781b,
+ 0x00df, 0x0078, 0x1efb, 0x681b, 0x0003, 0x7858, 0xa084, 0x3f00,
+ 0x681e, 0x682f, 0x0000, 0x6833, 0x0000, 0x784b, 0x0008, 0x78e4,
+ 0xa005, 0x00d0, 0x238a, 0xa084, 0x0020, 0x0040, 0x238a, 0x0018,
+ 0x238a, 0x0078, 0x2f6f, 0x6b14, 0x8307, 0xa084, 0x000f, 0x8003,
+ 0x8003, 0x8003, 0xa080, 0x3a00, 0x2060, 0x2048, 0x704a, 0x6000,
+ 0x704e, 0x6004, 0x7052, 0x2a60, 0x007c, 0x0020, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+ 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0062,
+ 0x0009, 0x0014, 0x0014, 0x9848, 0x0014, 0x0014, 0x98f9, 0x98e9,
+ 0x0014, 0x0014, 0x0080, 0x00c0, 0x0100, 0x0402, 0x2008, 0xf880,
+ 0x0018, 0xa20a, 0x0014, 0x300b, 0xa20c, 0x0014, 0xa200, 0x8838,
+ 0x3806, 0x8839, 0x28c2, 0x9cc2, 0xa805, 0x0864, 0xa83d, 0x3008,
+ 0x28c1, 0x9cc2, 0xa201, 0x300c, 0x2847, 0x8161, 0x846a, 0x8000,
+ 0x84a4, 0x1856, 0x883a, 0xa808, 0x28e2, 0x9c9f, 0xa8f3, 0x0864,
+ 0xa82b, 0x300c, 0xa801, 0x3008, 0x28e1, 0x9c9f, 0x280d, 0xa204,
+ 0x64c0, 0x67a0, 0x6fc0, 0x1814, 0x883b, 0x7023, 0x8576, 0x8677,
+ 0xa80f, 0x786e, 0x883e, 0xa80c, 0x282b, 0xa205, 0x64a0, 0x67a0,
+ 0x6fc0, 0x1814, 0x883b, 0x7023, 0x8576, 0x8677, 0xa801, 0x883e,
+ 0x206b, 0x28c1, 0x9cc2, 0x2044, 0x2103, 0x20a2, 0x2081, 0xa8dc,
+ 0xa207, 0x2901, 0xa80a, 0x0014, 0xa203, 0x8000, 0x85a4, 0x1872,
+ 0x879a, 0x883c, 0x1fe2, 0xf601, 0xa208, 0x856e, 0x866f, 0x0704,
+ 0x3008, 0x9c9f, 0x0014, 0xa202, 0x8000, 0x85a4, 0x3009, 0x84a8,
+ 0x19e2, 0xf848, 0x8174, 0x86eb, 0x85eb, 0x872e, 0x87a9, 0x883f,
+ 0x08e6, 0xa8f1, 0xf861, 0xa8e8, 0xf801, 0x0014, 0xf881, 0x0016,
+ 0x85b2, 0x80f0, 0x9532, 0xfaa2, 0x1de2, 0x0014, 0x8532, 0xf221,
+ 0x0014, 0x1de2, 0x84a8, 0xd6e0, 0x1fe6, 0x0014, 0xa206, 0x6865,
+ 0x817e, 0x842a, 0x1dc1, 0x8823, 0x0016, 0x6042, 0x8008, 0xa8fa,
+ 0x8000, 0x84a4, 0x8160, 0x842a, 0xf021, 0x3008, 0x84a8, 0x11d6,
+ 0x7042, 0x20dd, 0x0011, 0x20d4, 0x8822, 0x0016, 0x8000, 0x2848,
+ 0x1011, 0xa8fc, 0x3008, 0x8000, 0xa000, 0x2802, 0x1011, 0xa8fd,
+ 0xa883, 0x3008, 0x283d, 0x1011, 0xa8fd, 0xa209, 0x0017, 0x300c,
+ 0x8000, 0x85a4, 0x1de2, 0xdac1, 0x0014, 0xd301, 0x0014, 0x26e0,
+ 0x873a, 0xfaa2, 0x19f2, 0x1fe2, 0x0014, 0xa20b, 0x0014, 0xa20d,
+ 0x3806, 0x0210, 0x9ccc, 0x0704, 0x0000, 0x127e, 0x2091, 0x2200,
+ 0x2049, 0x32f5, 0x7000, 0x7204, 0xa205, 0x720c, 0xa215, 0x7008,
+ 0xa084, 0xfff7, 0xa205, 0x0040, 0x3307, 0x1078, 0x3380, 0x127f,
+ 0x2000, 0x007c, 0x6428, 0x84ff, 0x0040, 0x3336, 0x2c70, 0x7004,
+ 0xa0bc, 0x000f, 0xa7b8, 0x3346, 0x273c, 0x87fb, 0x00c0, 0x3324,
+ 0x0048, 0x331c, 0x1078, 0x1eac, 0x609c, 0xa075, 0x0040, 0x3336,
+ 0x0078, 0x330f, 0x2039, 0x333b, 0x2704, 0xae68, 0x6808, 0xa630,
+ 0x680c, 0xa529, 0x8421, 0x0040, 0x3336, 0x8738, 0x2704, 0xa005,
+ 0x00c0, 0x3325, 0x709c, 0xa075, 0x00c0, 0x330f, 0x007c, 0x0000,
+ 0x0005, 0x0009, 0x000d, 0x0011, 0x0015, 0x0019, 0x001d, 0x0000,
+ 0x0003, 0x0009, 0x000f, 0x0015, 0x001b, 0x0000, 0x0000, 0x333b,
+ 0x3338, 0x0000, 0x0000, 0x8000, 0x0000, 0x333b, 0x0000, 0x3343,
+ 0x3340, 0x0000, 0x0000, 0x0000, 0x0000, 0x3343, 0x0000, 0x333e,
+ 0x333e, 0x0000, 0x0000, 0x8000, 0x0000, 0x333e, 0x0000, 0x3344,
+ 0x3344, 0x0000, 0x0000, 0x0000, 0x0000, 0x3344, 0x127e, 0x2091,
+ 0x2200, 0x2079, 0x3900, 0x2071, 0x0010, 0x7007, 0x000a, 0x7007,
+ 0x0002, 0x7003, 0x0000, 0x2071, 0x0020, 0x7007, 0x000a, 0x7007,
+ 0x0002, 0x7003, 0x0000, 0x2049, 0x0000, 0x127f, 0x2000, 0x007c,
+ 0x2049, 0x3380, 0x2019, 0x0000, 0x7004, 0x8004, 0x00c8, 0x33b2,
+ 0x7007, 0x0012, 0x7108, 0x7008, 0xa106, 0x00c0, 0x338a, 0xa184,
+ 0x01e0, 0x0040, 0x3395, 0x1078, 0x1eac, 0xa184, 0x4000, 0x00c0,
+ 0x338a, 0xa19c, 0x300c, 0xa386, 0x2004, 0x0040, 0x33a7, 0xa386,
+ 0x0008, 0x0040, 0x33b2, 0xa386, 0x200c, 0x00c0, 0x338a, 0x7200,
+ 0x8204, 0x0048, 0x33b2, 0x730c, 0xa384, 0x00ff, 0x0040, 0x33b2,
+ 0x1078, 0x1eac, 0x7007, 0x0012, 0x7000, 0xa084, 0x0001, 0x00c0,
+ 0x33c3, 0x7310, 0x7014, 0xa305, 0x0040, 0x33c3, 0x700c, 0xa084,
+ 0x00ff, 0x00c0, 0x3380, 0x7007, 0x0012, 0x7007, 0x0008, 0x7004,
+ 0xa084, 0x0008, 0x00c0, 0x33c7, 0x7007, 0x0012, 0x7108, 0x8103,
+ 0x0048, 0x33cc, 0x7003, 0x0000, 0x2049, 0x0000, 0x007c, 0x107e,
+ 0x007e, 0x127e, 0x157e, 0x2091, 0x2200, 0x7108, 0x1078, 0x33e7,
+ 0x157f, 0x127f, 0x2091, 0x8001, 0x007f, 0x107f, 0x007c, 0x7204,
+ 0x7500, 0x730c, 0xa384, 0x0300, 0x00c0, 0x3426, 0xa184, 0x0060,
+ 0x00c0, 0x3442, 0x7008, 0x7108, 0xa106, 0x00c0, 0x33f2, 0xa184,
+ 0x01e0, 0x00c0, 0x3442, 0xa184, 0x4000, 0x00c0, 0x33f2, 0xa986,
+ 0x362f, 0x00c0, 0x341a, 0xa19c, 0x300c, 0xa386, 0x2004, 0x0040,
+ 0x3411, 0xa386, 0x0008, 0x0040, 0x341a, 0xa386, 0x200c, 0x00c0,
+ 0x33f2, 0x7200, 0x8204, 0x0048, 0x341a, 0x730c, 0xa384, 0x00ff,
+ 0x00c0, 0x3426, 0xa184, 0x0007, 0x0079, 0x341e, 0x3428, 0x3436,
+ 0x3426, 0x3436, 0x3426, 0x348f, 0x3426, 0x348d, 0x1078, 0x1eac,
+ 0x7007, 0x0002, 0x8aff, 0x00c0, 0x3431, 0x2049, 0x0000, 0x0078,
+ 0x3435, 0x1078, 0x3606, 0x00c0, 0x3431, 0x007c, 0x7007, 0x0002,
+ 0x8aff, 0x00c0, 0x343d, 0x0078, 0x3441, 0x1078, 0x3606, 0x00c0,
+ 0x343d, 0x007c, 0x7108, 0x7008, 0xa106, 0x00c0, 0x3442, 0xa184,
+ 0x4000, 0x00c0, 0x3442, 0x7007, 0x0012, 0x7108, 0x7008, 0xa106,
+ 0x00c0, 0x344d, 0xa184, 0x4000, 0x00c0, 0x344d, 0x00e0, 0x3456,
+ 0x2091, 0x6000, 0x00e0, 0x345a, 0x2091, 0x6000, 0x7007, 0x0012,
+ 0x7007, 0x0008, 0x7004, 0xa084, 0x0008, 0x00c0, 0x3462, 0x7007,
+ 0x0012, 0x7108, 0x8103, 0x0048, 0x3467, 0x7003, 0x0000, 0x7000,
+ 0xa005, 0x00c0, 0x347b, 0x7004, 0xa005, 0x00c0, 0x347b, 0x700c,
+ 0xa005, 0x0040, 0x347d, 0x0078, 0x345e, 0x2049, 0x0000, 0x1078,
+ 0x30b3, 0x6818, 0xa084, 0x8000, 0x0040, 0x3488, 0x681b, 0x0002,
+ 0x007c, 0x1078, 0x1eac, 0x1078, 0x1eac, 0x1078, 0x34ed, 0x7210,
+ 0x7114, 0x700c, 0xa09c, 0x00ff, 0x2800, 0xa300, 0xa211, 0xa189,
+ 0x0000, 0x1078, 0x34ed, 0x2704, 0x2c58, 0xac60, 0x6308, 0x2200,
+ 0xa322, 0x630c, 0x2100, 0xa31b, 0x2400, 0xa305, 0x0040, 0x34b2,
+ 0x00c8, 0x34b2, 0x8412, 0x8210, 0x830a, 0xa189, 0x0000, 0x2b60,
+ 0x0078, 0x3499, 0x2b60, 0x8a07, 0x007e, 0x6004, 0xa084, 0x0008,
+ 0x0040, 0x34be, 0xa7ba, 0x3340, 0x0078, 0x34c0, 0xa7ba, 0x3338,
+ 0x007f, 0xa73d, 0x2c00, 0x6886, 0x6f8a, 0x6c92, 0x6b8e, 0x1078,
+ 0x3380, 0x007c, 0x8738, 0x2704, 0xa005, 0x00c0, 0x34dd, 0x609c,
+ 0xa005, 0x0040, 0x34ea, 0x2060, 0x6004, 0xa084, 0x000f, 0xa080,
+ 0x3346, 0x203c, 0x87fb, 0x1040, 0x1eac, 0x8a51, 0x0040, 0x34e9,
+ 0x7008, 0x7508, 0xa52e, 0x00c0, 0x34e0, 0xa084, 0x0003, 0xa086,
+ 0x0003, 0x007c, 0x2051, 0x0000, 0x007c, 0x8a50, 0x8739, 0x2704,
+ 0xa004, 0x00c0, 0x3501, 0x6000, 0xa064, 0x00c0, 0x34f8, 0x2d60,
+ 0x6004, 0xa084, 0x000f, 0xa080, 0x3356, 0x203c, 0x87fb, 0x1040,
+ 0x1eac, 0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x6884,
+ 0x2060, 0x6888, 0x6b8c, 0x6c90, 0x8057, 0xaad4, 0x00ff, 0xa084,
+ 0x00ff, 0x007e, 0x6804, 0xa084, 0x0008, 0x007f, 0x0040, 0x351c,
+ 0xa0b8, 0x3340, 0x0078, 0x351e, 0xa0b8, 0x3338, 0x7e08, 0xa6b5,
+ 0x000c, 0x681c, 0xa084, 0x0040, 0x0040, 0x3528, 0xa6b5, 0x0001,
+ 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x352a, 0x2400,
+ 0xa305, 0x00c0, 0x3535, 0x0078, 0x3559, 0x2c58, 0x2704, 0x6104,
+ 0xac60, 0x6000, 0xa400, 0x701a, 0x6004, 0xa301, 0x701e, 0xa184,
+ 0x0008, 0x0040, 0x3549, 0x6010, 0xa001, 0x7022, 0x6014, 0xa001,
+ 0x7026, 0x6208, 0x2400, 0xa202, 0x7012, 0x620c, 0x2300, 0xa203,
+ 0x7016, 0x7602, 0x7007, 0x0001, 0x2b60, 0x1078, 0x34ca, 0x0078,
+ 0x355b, 0x1078, 0x3606, 0x00c0, 0x3559, 0x127f, 0x2000, 0x007c,
+ 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x7007, 0x0004, 0x7004,
+ 0xa084, 0x0004, 0x00c0, 0x3567, 0x7003, 0x0008, 0x127f, 0x2000,
+ 0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x2049, 0x3571,
+ 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x357a, 0x7e08,
+ 0xa6b5, 0x000c, 0x681c, 0xa084, 0x0020, 0x00c0, 0x3589, 0xa6b5,
+ 0x0001, 0x6828, 0x2050, 0x2d60, 0x6004, 0xa0bc, 0x000f, 0xa7b8,
+ 0x3346, 0x273c, 0x87fb, 0x00c0, 0x359f, 0x0048, 0x3599, 0x1078,
+ 0x1eac, 0x689c, 0xa065, 0x0040, 0x35a3, 0x0078, 0x358c, 0x1078,
+ 0x3606, 0x00c0, 0x359f, 0x127f, 0x2000, 0x007c, 0x127e, 0x007e,
+ 0x017e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x037f, 0x047f, 0x7e08,
+ 0xa6b5, 0x000c, 0x681c, 0xa084, 0x0040, 0x0040, 0x35b9, 0xa6b5,
+ 0x0001, 0x2049, 0x35a6, 0x6828, 0xa055, 0x0040, 0x3603, 0x2d70,
+ 0x2e60, 0x7004, 0xa0bc, 0x000f, 0xa7b8, 0x3346, 0x273c, 0x87fb,
+ 0x00c0, 0x35d5, 0x0048, 0x35ce, 0x1078, 0x1eac, 0x709c, 0xa075,
+ 0x2060, 0x0040, 0x3603, 0x0078, 0x35c1, 0x2704, 0xae68, 0x6808,
+ 0xa422, 0x680c, 0xa31b, 0x0048, 0x35f0, 0x8a51, 0x00c0, 0x35e2,
+ 0x1078, 0x1eac, 0x8738, 0x2704, 0xa005, 0x00c0, 0x35d6, 0x709c,
+ 0xa075, 0x2060, 0x0040, 0x3603, 0x2039, 0x3338, 0x0078, 0x35c1,
+ 0x8422, 0x8420, 0x831a, 0xa399, 0x0000, 0x6908, 0x2400, 0xa122,
+ 0x690c, 0x2300, 0xa11b, 0x00c8, 0x35ff, 0x1078, 0x1eac, 0x2071,
+ 0x0020, 0x0078, 0x3528, 0x127f, 0x2000, 0x007c, 0x7008, 0x7508,
+ 0xa52e, 0x00c0, 0x3606, 0xa084, 0x0003, 0xa086, 0x0003, 0x0040,
+ 0x362e, 0x2704, 0xac08, 0x2104, 0x701a, 0x8108, 0x2104, 0x701e,
+ 0x8108, 0x2104, 0x7012, 0x8108, 0x2104, 0x7016, 0x6004, 0xa084,
+ 0x0008, 0x0040, 0x3629, 0x8108, 0x2104, 0x7022, 0x8108, 0x2104,
+ 0x7026, 0x7602, 0x7007, 0x0001, 0x1078, 0x34ca, 0x007c, 0x127e,
+ 0x007e, 0x0d7e, 0x2091, 0x2200, 0x2049, 0x362f, 0x0d7f, 0x087f,
+ 0x7108, 0x7008, 0xa106, 0x00c0, 0x3638, 0xa184, 0x4000, 0x00c0,
+ 0x3638, 0xa184, 0x0003, 0x00c0, 0x364f, 0x6828, 0xa005, 0x0040,
+ 0x365d, 0x0020, 0x364f, 0x1078, 0x348f, 0x0078, 0x365d, 0x00a0,
+ 0x3656, 0x7108, 0x1078, 0x33e7, 0x0078, 0x3638, 0x7007, 0x0010,
+ 0x00a0, 0x3658, 0x7108, 0x1078, 0x33e7, 0x7008, 0xa086, 0x0008,
+ 0x00c0, 0x3638, 0x7000, 0xa005, 0x00c0, 0x3638, 0x2049, 0x0000,
+ 0x127f, 0x2000, 0x007c, 0x127e, 0x147e, 0x137e, 0x157e, 0x0d7e,
+ 0x2091, 0x2200, 0x0d7f, 0x2049, 0x366b, 0xad80, 0x0011, 0x20a0,
+ 0x2099, 0x0031, 0x700c, 0xa084, 0x00ff, 0x682a, 0x7007, 0x0008,
+ 0x7007, 0x0002, 0x7003, 0x0001, 0x0040, 0x3689, 0x8000, 0x80ac,
+ 0x53a5, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x368b,
+ 0x2049, 0x0000, 0x7003, 0x0000, 0x157f, 0x137f, 0x147f, 0x127f,
+ 0x2000, 0x007c, 0x2091, 0x6000, 0x78c0, 0xa005, 0x0040, 0x36af,
+ 0x798c, 0x70d0, 0xa106, 0x00c0, 0x36af, 0x7804, 0xa005, 0x0040,
+ 0x36af, 0x7807, 0x0000, 0x0068, 0x36af, 0x2091, 0x4080, 0x7820,
+ 0x8001, 0x7822, 0x00c0, 0x370f, 0x7824, 0x7822, 0x2091, 0x8000,
+ 0x78f0, 0xa005, 0x0040, 0x36dc, 0x78d4, 0xa005, 0x00c0, 0x36dc,
+ 0x3a10, 0xa284, 0x0002, 0x00c0, 0x36cc, 0x78d7, 0x0007, 0x2009,
+ 0xff01, 0x200a, 0x0078, 0x36dc, 0xa284, 0x0001, 0x00c0, 0x36d4,
+ 0x78ef, 0x0000, 0x0078, 0x36dc, 0x78ec, 0xa005, 0x00c0, 0x36dc,
+ 0x78d7, 0x0008, 0x78ef, 0x0001, 0x2069, 0x3940, 0x6800, 0xa084,
+ 0x0007, 0x0040, 0x36f3, 0xa086, 0x0002, 0x0040, 0x36f3, 0x6830,
+ 0xa00d, 0x0040, 0x36f3, 0x2104, 0xa005, 0x0040, 0x36f3, 0x8001,
+ 0x200a, 0x0040, 0x37b1, 0x7848, 0xa005, 0x0040, 0x3703, 0x8001,
+ 0x784a, 0x00c0, 0x3703, 0x0f7e, 0x2079, 0x0100, 0x1078, 0x316a,
+ 0x0f7f, 0x1078, 0x1cf6, 0x68c4, 0xa005, 0x0040, 0x370f, 0x8001,
+ 0x68c6, 0x00c0, 0x370f, 0x68a3, 0x0000, 0x68a7, 0x0001, 0x1078,
+ 0x3716, 0x1078, 0x373b, 0x2091, 0x8001, 0x007c, 0x7834, 0x8001,
+ 0x7836, 0x00c0, 0x373a, 0x7838, 0x7836, 0x2091, 0x8000, 0x7844,
+ 0xa005, 0x00c0, 0x3725, 0x2001, 0x0101, 0x8001, 0x7846, 0xa080,
+ 0x4280, 0x2040, 0x2004, 0xa065, 0x0040, 0x373a, 0x6024, 0xa005,
+ 0x0040, 0x3736, 0x8001, 0x6026, 0x0040, 0x376a, 0x6000, 0x2c40,
+ 0x0078, 0x372b, 0x007c, 0x7828, 0x8001, 0x782a, 0x00c0, 0x3769,
+ 0x782c, 0x782a, 0x7830, 0xa005, 0x00c0, 0x3748, 0x2001, 0x0080,
+ 0x8001, 0x7832, 0x8003, 0x8003, 0x8003, 0x8003, 0xa090, 0x3a80,
+ 0xa298, 0x0002, 0x2304, 0xa084, 0x0008, 0x0040, 0x3769, 0xa290,
+ 0x0009, 0x2204, 0xa005, 0x0040, 0x3761, 0x8001, 0x2012, 0x00c0,
+ 0x3769, 0x2304, 0xa084, 0xfff7, 0xa085, 0x0080, 0x201a, 0x1078,
+ 0x1cf6, 0x007c, 0x2069, 0x3940, 0x6800, 0xa005, 0x0040, 0x3774,
+ 0x683c, 0xac06, 0x0040, 0x37b1, 0x601b, 0x0006, 0x60b4, 0xa084,
+ 0x3f00, 0x601e, 0x6020, 0xa084, 0x00ff, 0xa085, 0x0060, 0x6022,
+ 0x6000, 0x2042, 0x6714, 0x6fb6, 0x1078, 0x18b4, 0x6818, 0xa005,
+ 0x0040, 0x378c, 0x8001, 0x681a, 0x6808, 0xa084, 0xffef, 0x680a,
+ 0x6810, 0x8001, 0x00d0, 0x3796, 0x1078, 0x1eac, 0x6812, 0x602f,
+ 0x0000, 0x6033, 0x0000, 0x2c68, 0x1078, 0x1a26, 0x2069, 0x3940,
+ 0x2001, 0x0006, 0x68a2, 0x7944, 0xa184, 0x0100, 0x00c0, 0x37ac,
+ 0x69ba, 0x2001, 0x0004, 0x68a2, 0x1078, 0x1cf1, 0x2091, 0x8001,
+ 0x007c, 0x2009, 0x394f, 0x2164, 0x2069, 0x0100, 0x1078, 0x1e57,
+ 0x601b, 0x0006, 0x6858, 0xa084, 0x3f00, 0x601e, 0x6020, 0xa084,
+ 0x00ff, 0xa085, 0x0048, 0x6022, 0x602f, 0x0000, 0x6033, 0x0000,
+ 0x6830, 0xa084, 0x0040, 0x0040, 0x37ed, 0x684b, 0x0004, 0x20a9,
+ 0x0014, 0x6848, 0xa084, 0x0004, 0x0040, 0x37da, 0x0070, 0x37da,
+ 0x0078, 0x37d1, 0x684b, 0x0009, 0x20a9, 0x0014, 0x6848, 0xa084,
+ 0x0001, 0x0040, 0x37e7, 0x0070, 0x37e7, 0x0078, 0x37de, 0x20a9,
+ 0x00fa, 0x0070, 0x37ed, 0x0078, 0x37e9, 0x6808, 0xa084, 0xfffd,
+ 0x680a, 0x681b, 0x0047, 0x2009, 0x3968, 0x200b, 0x0007, 0x784c,
+ 0x784a, 0x2091, 0x8001, 0x007c, 0x2079, 0x3900, 0x1078, 0x3827,
+ 0x1078, 0x380b, 0x1078, 0x3819, 0x7833, 0x0000, 0x7847, 0x0000,
+ 0x784b, 0x0000, 0x007c, 0x2019, 0x000c, 0x2011, 0x3946, 0x2204,
+ 0xa086, 0x003c, 0x0040, 0x3816, 0x2019, 0x0008, 0x7b2a, 0x7b2e,
+ 0x007c, 0x2019, 0x0039, 0x2011, 0x3946, 0x2204, 0xa086, 0x003c,
+ 0x0040, 0x3824, 0x2019, 0x0027, 0x7b36, 0x7b3a, 0x007c, 0x2019,
+ 0x3971, 0x2011, 0x3946, 0x2204, 0xa086, 0x003c, 0x0040, 0x3832,
+ 0x2019, 0x2626, 0x7b22, 0x7b26, 0x783f, 0x0000, 0x7843, 0x000a,
+ 0x007c, 0x8e59
+};
+
+#endif /* RELOAD_FIRMWARE */
+
+unsigned short risc_code_length01 = 0x283a;
diff --git a/linux/src/drivers/scsi/scripts.h b/linux/src/drivers/scsi/scripts.h
new file mode 100644
index 00000000..482b0c24
--- /dev/null
+++ b/linux/src/drivers/scsi/scripts.h
@@ -0,0 +1,1357 @@
+/***********************************************************************
+;* File Name : SCRIPTS.H *
+;* Description:SCRIPT language for NCR53c825A,875 SCRIPT processor*
+;* *
+;***********************************************************************
+
+;==========================================================
+; NCR 53C810,53C815,53C820,53C825,53C825A,53C860,53C875
+; Script language definition for assembly programming
+;==========================================================
+
+;==========================================================
+; DMA Command
+;==========================================================
+*/
+#define DCMD_BLOCK_MOVE 0
+#define DCMD_IO 0x040000000 /*;BIT30 */
+#define DCMD_RD_WRT 0x040000000 /*;BIT30 */
+#define DCMD_XFER_CTRL 0x080000000 /*;BIT31 */
+#define DCMD_MEM_MOVE 0x0C0000000 /*;(BIT31+BIT30) */
+#define DCMD_LOAD_STORE 0x0E0000000 /*;(BIT31+BIT30+BIT29) */
+/*;==========================================================*/
+#define INDIRECT_ADDR 0x20000000 /*;BIT29 */
+#define TABLE_INDIRECT 0x10000000 /*;BIT28 */
+#define BLOCK_MOVE 0x08000000 /*;BIT27 */
+#define CHAIN_MOVE 0
+/*; SCSI phase definition */
+#define DATA_OUT_ 0x00000000 /*;data out phase */
+#define DATA_IN_ 0x01000000 /*;BIT24 ; data in phase */
+#define COMMAND_ 0x02000000 /*;BIT25 ; command phase */
+#define STATUS_ 0x03000000 /*;(BIT25+BIT24) ; status phase */
+#define RESERVED_OUT 0x04000000 /*;BIT26 */
+#define RESERVED_IN 0x05000000 /*;(BIT26+BIT24) */
+#define MSG_OUT_ 0x06000000 /*;(BIT26+BIT25) ; message in phase */
+#define MSG_IN_ 0x07000000 /*;(BIT26+BIT25+BIT24);message out phase */
+/*;----------------------------------------------------------*/
+#define DCMD_SELECT 0x40000000 /*;DCMD_IO+0 */
+#define DCMD_SELECT_ATN 0x41000000 /*;(DCMD_IO+BIT24) */
+#define DCMD_WAIT_DISC 0x48000000 /*;(DCMD_IO+BIT27) */
+#define DCMD_WAIT_RESEL 0x50000000 /*;(DCMD_IO+BIT28) */
+#define DCMD_SET_CARRY 0x58000400 /*;(DCMD_IO+BIT28+BIT27+BIT10) */
+#define DCMD_SET_ACK 0x58000040 /*;(DCMD_IO+BIT28+BIT27+BIT6) */
+#define DCMD_SET_ATN 0x58000008 /*;(DCMD_IO+BIT28+BIT27+BIT3) */
+#define DCMD_CLR_CARRY 0x60000400 /*;(DCMD_IO+BIT29+BIT10) */
+#define DCMD_CLR_ACK 0x60000040 /*;(DCMD_IO+BIT29+BIT6) */
+#define DCMD_CLR_ATN 0x60000008 /*;(DCMD_IO+BIT29+BIT3) */
+#define RELATIVE_ADDR 0x04000000 /*;BIT26 */
+#define IO_TABLE_INDIR 0x02000000 /*;BIT25 */
+/*;----------------------------------------------------------*/
+#define MOVE_FROM_SFBR 0x68000000 /*;(DCMD_RD_WRT+BIT29+BIT27) */
+#define MOVE_TO_SFBR 0x70000000 /*;(DCMD_RD_WRT+BIT29+BIT28) */
+#define RD_MODIFY_WRT 0x78000000 /*;(DCMD_RD_WRT+BIT29+BIT28+BIT27) */
+#define OP_MOVE_DATA 0
+#define OP_SHIFT_LEFT_C 0x01000000 /*;BIT24 */
+#define OP_OR 0x02000000 /*;BIT25 */
+#define OP_XOR 0x03000000 /*;(BIT25+BIT24) */
+#define OP_AND 0x04000000 /*;BIT26 */
+#define OP_SHIFT_RIGHT_C 0x05000000 /*;(BIT26+BIT24) */
+#define OP_ADD_DATA 0x06000000 /*;(BIT26+BIT25) */
+#define OP_ADD_DATA_C 0x07000000 /*;(BIT26+BIT25+BIT24) */
+#define USE_SFBR 0x00800000 /*;BIT23 */
+/*;----------------------------------------------------------*/
+#define DCMD_JUMP 0x80000000 /*;DCMD_XFER_CTRL+0 */
+#define DCMD_CALL 0x88000000 /*;(DCMD_XFER_CTRL+BIT27) */
+#define DCMD_RETURN 0x90000000 /*;(DCMD_XFER_CTRL+BIT28) */
+#define DCMD_INT 0x98000000 /*;(DCMD_XFER_CTRL+BIT28+BIT27) */
+#define RELATIVE_ 0x00800000 /*;BIT23 */
+#define IF_CARRY 0x00200000 /*;BIT21 */
+#define INT_ON_FLY_ 0x00100000 /*;BIT20 */
+#define IF_TRUE 0x00080000 /*;BIT19 */
+#define IF_NOT 0
+#define DATA_CMP 0x00040000 /*;BIT18 */
+#define PHASE_CMP 0x00020000 /*;BIT17 */
+#define WAIT_PHASE_VALID 0x00010000 /*;BIT16 */
+/*;----------------------------------------------------------*/
+#define DSA_RELATIVE 0x10000000 /*;BIT28 */
+#define FLUSH_PREFETCH 0x02000000 /*;BIT25 */
+#define DCMD_LOAD 0x0E1000000 /*;(DCMD_LOAD_STORE+BIT24) */
+#define DCMD_STORE 0x0E0000000 /*;DCMD_LOAD_STORE */
+/*
+;==========================================================
+; SCSI message EQUATES
+;==========================================================
+*/
+#define CMD_COMPLETE 0
+#define EXT_MSG 1
+#define SAVE_PTR 2
+#define RESTORE_PTR 3
+#define DISCONNECTMSG 4
+#define INITIATOR_ERR 5
+#define ABORTMSG 6
+#define MSG_REJECT 7
+#define NOPMSG 8
+#define MSG_PARITY 9
+#define LINK_CMD_CPL 0x0a
+#define LINK_CMD_FLAG 0x0b
+#define RESET_DEVICE 0x0c
+#define IDENTIFYMSG 0x80
+#define SIMPLE_TAG 0x20
+#define IGNORE_WIDE_RES 0x23
+/*
+;==========================================================
+; Operation assumption
+; 1. If phase mismatch during Xfer PAD ==> do nothing
+; Else compute FIXUP needed
+; 2. After phase mismatch ==> Set to Xfer PAD
+; 3. At disconnection ==> Modify return address
+; 4. 1st restore ptr after reselection is ignored
+; 5. If Xfer PAD is done ==> Error
+;==========================================================
+*/
+/* static start_script
+ static reselected
+ static reselecttag
+ static select0
+ static select1
+ static check_phase
+ static status1_phase
+ static command_phase
+ static jump_table0
+ static jump_tableB
+ static din_phase
+ static din_phaseB
+ static din_pad_0
+ static din_pad_addrB
+ static dout_phase
+ static dout_phaseB
+ static dout_pad_0
+ static dout_pad_addrB
+ static jump_tablew
+ static jump_tableW
+ static din_phase1
+ static din_phaseW
+ static din_pad_1
+ static din_pad_addrW
+ static dout_phase1
+ static dout_phaseW
+ static dout_pad_1
+ static dout_pad_addrW
+ static mout_phase
+ static status_phase
+ static min_phase
+ static set_atn
+ static clr_atn
+ static end_script
+ static start_mov
+ static SrcPhysAddr
+ static DesPhysAddr
+*/
+ULONG start_script[]={
+/*
+;==========================================================
+; Wait for reselection
+;==========================================================
+*/
+ DCMD_WAIT_RESEL
+ };
+ULONG jmp_select0[]={
+ 0 /* offset select0 */
+ };
+ULONG reselected[]={
+ RD_MODIFY_WRT+OP_OR+0x200+0x340000, /* (2 shl 8) or (__scratcha shl 16) */
+ 0,
+
+ DCMD_INT+WAIT_PHASE_VALID+IF_NOT+PHASE_CMP+MSG_IN_,
+ __RESELECTED,
+
+ BLOCK_MOVE+MSG_IN_+1 /* ;move in ID byte */
+ };
+ULONG ACB_msgin123_1[]={
+ 0, /* offset ACB.msgin123,*/
+
+ DCMD_INT+IF_TRUE,
+ __RESELECTED1
+ };
+ULONG reselecttag[]={
+ DCMD_CLR_ACK,
+ 0,
+
+ BLOCK_MOVE+MSG_IN_+2 /* ;move 2 msg bytes */
+ };
+ULONG ACB_msgin123_2[]={
+ 0, /* offset ACB.msgin123,*/
+
+ DCMD_INT+IF_TRUE,
+ __RESELECTEDT
+ };
+/*
+;==========================================================
+; Select
+; Case 1 : Only identify message is to be sent
+; Case 2 : Synchronous negotiation is requested
+;==========================================================
+*/
+ULONG select0[]={
+ DCMD_INT+IF_TRUE,
+ __SIGNAL
+ };
+ULONG select1[]={ /* ; Select with ATN */
+
+ DCMD_SELECT_ATN+IO_TABLE_INDIR /* +offset SRB.__select ;4200h or 0100H */
+ };
+ULONG jmp_reselected[]={
+ 0, /* offset reselected, */
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_NOT+PHASE_CMP+MSG_OUT_
+ };
+ULONG jmp_check_phase[]={
+ 0, /* offset check_phase, */
+
+ TABLE_INDIRECT+BLOCK_MOVE+MSG_OUT_
+ };
+ULONG SRB_msgout0[]={
+ 0 /* offset SRB.__msgout0 */
+ };
+ULONG check_phase[]={
+ DCMD_RETURN+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0,
+
+ DCMD_RETURN+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0
+ };
+ULONG status1_phase[]={
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+STATUS_
+ };
+ULONG jmp_status_phase[]={
+ 0, /* offset status_phase,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+COMMAND_
+ };
+ULONG jmp_command_phase[]={
+ 0, /* offset command_phase, */
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+MSG_IN_
+ };
+ULONG jmp_min_phase[]={
+ 0, /* offset min_phase,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+MSG_OUT_
+ };
+ULONG jmp_mout_phase[]={
+ 0, /* offset mout_phase,*/
+
+ DCMD_INT+IF_TRUE,
+ __FATALERROR
+ };
+/*
+;==========================================================
+; Command phase
+;==========================================================
+*/
+ULONG command_phase[]={
+ DCMD_CLR_ATN,
+ 0,
+ TABLE_INDIRECT+BLOCK_MOVE+COMMAND_
+ };
+ULONG SRB_command[]={
+ 0, /* offset SRB.__command,*/
+
+ DCMD_JUMP+IF_TRUE
+ };
+ULONG jmp_check_phase1[]={
+ 0 /* offset check_phase */
+ };
+/*
+;==========================================================
+; Data phase jump table for 8 bit operation
+;==========================================================
+*/
+ULONG jmp_dio_phaseB[]={
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 0,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 0,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 8,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 8,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 16,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 16,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 24,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 24,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 32,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 32,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 40,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 40,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 48,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 48,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 56,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 56,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 64,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 64,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 72,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 72,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 80,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 80,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 88,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 88,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 96,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 96,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 104,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 104,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 112,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 112,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 120,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 120,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 0,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 0,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 8,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 8,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 16,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 16,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 24,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 24,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 32,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 32,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 40,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 40,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 48,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 48,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 56,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 56,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 64,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 64,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 72,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 72,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 80,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 80,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 88,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 88,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 96,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 96,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 104,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 104,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 112,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 112,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 120,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseB+ 120,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseB+ 128,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0 /* offset dout_phaseB+ 128 */
+ };
+ULONG jump_table0[]={
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_
+ };
+ULONG jmp_din_pad_0[]={
+ 0, /* offset din_pad_0,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_
+ };
+ULONG jmp_dout_pad_0[]={
+ 0 /* offset dout_pad_0 */
+ };
+
+#define jump_tableB jump_table0
+/*
+;==========================================================
+; Data in phase
+;==========================================================
+*/
+ULONG din_phaseB[]={
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment0,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment1,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment2,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment3,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment4,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment5,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment6,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment7,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment8,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment9,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment10,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment11,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment12,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment13,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment14,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment15,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment0,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment1,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment2,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment3,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment4,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment5,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment6,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment7,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment8,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment9,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment10,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment11,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment12,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment13,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment14,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment15,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment16,*/
+
+ RD_MODIFY_WRT+OP_OR+0x100+0x340000, /*;(1 shl 8) or (__scratcha shl 16)*/
+ 0,
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_NOT+PHASE_CMP+DATA_IN_
+ };
+ULONG jmp_status1_phase[]={
+ 0 /* offset status1_phase */
+ };
+
+#define din_phase din_phaseB
+
+ULONG din_pad_0[]={
+ RD_MODIFY_WRT+OP_OR+0x340000+0x400, /*;(4 shl 8) or (__scratcha shl 16)*/
+ 0
+ };
+ULONG din_pad_addrB[]={
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_IN_
+ };
+ULONG SRB_SegmentPad[]={
+ 0, /* offset SRB.SegmentPad,*/
+
+ DCMD_JUMP+IF_TRUE
+ };
+ULONG jmp_din_pad_addrB[]={
+ 0 /* offset din_pad_addrB */
+ };
+/*
+;==========================================================
+; Data out phase
+;==========================================================
+*/
+ULONG dout_phaseB[]={
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment0,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment1,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment2,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment3,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment4,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment5,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment6,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment7,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment8,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment9,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment10,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment11,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment12,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment13,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment14,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment15,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment0,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment1,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment2,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment3,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment4,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment5,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment6,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment7,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment8,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment9,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment10,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment11,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment12,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment13,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment14,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment15,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment16,*/
+
+ RD_MODIFY_WRT+OP_OR+0x100+0x340000, /*;(1 shl 8) or (__scratcha shl 16)*/
+ 0,
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_NOT+PHASE_CMP+DATA_OUT_
+ };
+ULONG jmp_status1_phase1[]={
+ 0 /* offset status1_phase */
+ };
+
+#define dout_phase dout_phaseB
+
+ULONG dout_pad_0[]={
+ RD_MODIFY_WRT+OP_OR+0x340000+0x400, /*;(4 shl 8) or (__scratcha shl 16)*/
+ 0
+ };
+ULONG dout_pad_addrB[]={
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_
+ };
+ULONG SRB_SegmentPad1[]={
+ 0, /* offset SRB.SegmentPad,*/
+
+ DCMD_JUMP+IF_TRUE
+ };
+ULONG jmp_dout_pad_addrB[]={
+ 0 /* offset dout_pad_addrB */
+ };
+/*
+;==========================================================
+; Data phase jump table for WIDE SCSI operation
+;==========================================================
+*/
+ULONG jmp_dio_phaseW[]={
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 0,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 0,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 8,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 8,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 16,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 16,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 24,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 24,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 32,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 32,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 40,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 40,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 48,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 48,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 56,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 56,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 64,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 64,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 72,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 72,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 80,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 80,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 88,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 88,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 96,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 96,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 104,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 104,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 112,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 112,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 120,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 120,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 0,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 0,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 8,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 8,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 16,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 16,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 24,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 24,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 32,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 32,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 40,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 40,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 48,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 48,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 56,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 56,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 64,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 64,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 72,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 72,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 80,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 80,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 88,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 88,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 96,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 96,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 104,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 104,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 112,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 112,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 120,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0, /* offset dout_phaseW+ 120,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_,
+ 0, /* offset din_phaseW+ 128,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_,
+ 0 /* offset dout_phaseW+ 128 */
+ };
+ULONG jump_tablew[]={
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_IN_
+ };
+ULONG jmp_din_pad_1[]={
+ 0, /* offset din_pad_1,*/
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_TRUE+PHASE_CMP+DATA_OUT_
+ };
+ULONG jmp_dout_pad_1[]={
+ 0 /* offset dout_pad_1 */
+ };
+
+#define jump_tableW jump_tablew
+/*
+;==========================================================
+; Data in phase
+;==========================================================
+*/
+ULONG din_phaseW[]={
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment0,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment1,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment2,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment3,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment4,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment5,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment6,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment7,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment8,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment9,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment10,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment11,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment12,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment13,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment14,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment15,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment0,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment1,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment2,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment3,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment4,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment5,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment6,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment7,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment8,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment9,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment10,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment11,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment12,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment13,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment14,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment15,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_,
+ 0, /* offset SRB.Segment16,*/
+
+ RD_MODIFY_WRT+OP_OR+0x340000+0x100, /*;(1 shl 8) or (__scratcha shl 16)*/
+ 0,
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_NOT+PHASE_CMP+DATA_IN_
+ };
+ULONG jmp_status1_phase2[]={
+ 0 /* offset status1_phase */
+ };
+
+#define din_phase1 din_phaseW
+
+ULONG din_pad_1[]={
+ RD_MODIFY_WRT+OP_OR+0x340000+0x400, /*;(4 shl 8) or (__scratcha shl 16)*/
+ 0
+ };
+ULONG din_pad_addrW[]={
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_IN_
+ };
+ULONG SRB_SegmentPad2[]={
+ 0, /* offset SRB.SegmentPad,*/
+
+ DCMD_JUMP+IF_TRUE
+ };
+ULONG jmp_din_pad_addrW[]={
+ 0 /* offset din_pad_addrW */
+ };
+/*
+;==========================================================
+; Data out phase
+;==========================================================
+*/
+ULONG dout_phaseW[]={
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment0,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment1,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment2,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment3,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment4,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment5,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment6,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment7,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment8,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment9,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment10,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment11,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment12,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment13,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment14,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment15,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment0,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment1,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment2,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment3,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment4,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment5,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment6,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment7,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment8,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment9,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment10,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment11,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment12,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment13,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment14,*/
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+CHAIN_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment15,*/
+/*; 18000000h or DATA_OUT_ */
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_,
+ 0, /* offset SRB.Segment16,*/
+
+ RD_MODIFY_WRT+OP_OR+0x340000+0x100, /*;(1 shl 8) or (__scratcha shl 16)*/
+ 0,
+
+ DCMD_JUMP+WAIT_PHASE_VALID+IF_NOT+PHASE_CMP+DATA_OUT_
+ };
+ULONG jmp_status1_phase3[]={
+ 0 /* offset status1_phase */
+ };
+
+#define dout_phase1 dout_phaseW
+
+ULONG dout_pad_1[]={
+ RD_MODIFY_WRT+OP_OR+0x340000+0x400, /*;(4 shl 8) or (__scratcha shl 16)*/
+ 0
+ };
+ULONG dout_pad_addrW[]={
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+DATA_OUT_
+ };
+ULONG SRB_SegmentPad3[]={
+ 0, /* offset SRB.SegmentPad,*/
+
+ DCMD_JUMP+IF_TRUE
+ };
+ULONG jmp_dout_pad_addrW[]={
+ 0 /* offset dout_pad_addrW */
+ };
+/*
+;==========================================================
+; message out phase
+;==========================================================
+*/
+ULONG mout_phase[]={
+ DCMD_SET_ATN,
+ 0,
+
+ DCMD_BLOCK_MOVE+TABLE_INDIRECT+BLOCK_MOVE+MSG_OUT_
+ };
+ULONG SRB_msgout01[]={
+ 0, /* offset SRB.__msgout0,*/
+
+ DCMD_JUMP+IF_TRUE
+ };
+ULONG jmp_check_phase2[]={
+ 0 /* offset check_phase */
+ };
+/*
+;==========================================================
+; Status phase process
+;==========================================================
+*/
+ULONG status_phase[]={
+ DCMD_BLOCK_MOVE+BLOCK_MOVE+STATUS_+1
+ };
+ULONG ACB_status[]={
+ 0 /* offset ACB.status */
+ };
+/*
+;==========================================================
+; message in phase
+;==========================================================
+*/
+ULONG min_phase[]={
+ DCMD_BLOCK_MOVE+BLOCK_MOVE+MSG_IN_+1
+ };
+ULONG ACB_msgin123_3[]={
+ 0, /* offset ACB.msgin123,*/
+
+ DCMD_JUMP+IF_NOT+DATA_CMP+CMD_COMPLETE
+ };
+ULONG jmp_jump_msgok[]={
+ 0 /* offset jump_msgok */
+ };
+/*
+;==========================================================
+; command complete message
+;==========================================================
+*/
+ULONG msg__0[]={
+ RD_MODIFY_WRT+OP_AND+0x20000+0x7F00, /*;(7FH shl 8) or (__scntl2 shl 16)*/
+ 0,
+
+ DCMD_CLR_ACK,
+ 0,
+
+ DCMD_WAIT_DISC,
+ 0,
+
+ DCMD_INT+IF_TRUE,
+ __COMPLETE
+ };
+/*
+;==========================================================
+; Other message
+;==========================================================
+*/
+ULONG jump_msgok[]={
+ DCMD_JUMP+IF_TRUE+DATA_CMP+SAVE_PTR
+ };
+ULONG jmp_msg__a[]={
+ 0, /* offset msg__a,*/
+
+ DCMD_JUMP+IF_TRUE+DATA_CMP+RESTORE_PTR
+ };
+ULONG jmp_msg__3[]={
+ 0, /* offset msg__3,*/
+
+ DCMD_JUMP+IF_TRUE+DATA_CMP+DISCONNECTMSG
+ };
+ULONG jmp_msg__4[]={
+ 0, /* offset msg__4,*/
+
+ DCMD_JUMP+IF_TRUE+DATA_CMP+EXT_MSG
+ };
+ULONG jmp_msg__1[]={
+ 0, /* offset msg__1,*/
+
+ DCMD_INT+IF_TRUE+DATA_CMP+MSG_REJECT,
+ __MSGREJECT,
+
+ DCMD_JUMP+IF_TRUE+DATA_CMP+LINK_CMD_CPL
+ };
+ULONG jmp_msg__a1[]={
+ 0, /* offset msg__a,*/
+
+ DCMD_JUMP+IF_TRUE+DATA_CMP+LINK_CMD_FLAG
+ };
+ULONG jmp_msg__a2[]={
+ 0, /* offset msg__a,*/
+
+ DCMD_JUMP+IF_TRUE+DATA_CMP+IGNORE_WIDE_RES
+ };
+ULONG jmp_msg__23[]={
+ 0, /* offset msg__23,*/
+
+ DCMD_INT+IF_TRUE,
+ __MSGUNKNOWN
+ };
+/*
+;==========================================================
+; Extended message
+;==========================================================
+*/
+ULONG msg__1[]={
+ DCMD_CLR_ACK,
+ 0,
+
+ DCMD_BLOCK_MOVE+BLOCK_MOVE+MSG_IN_+1 /* ;ext msg len */
+ };
+ULONG ACB_msgin123_4[]={
+ 0, /* offset ACB.msgin123,*/
+
+ DCMD_JUMP+IF_TRUE+DATA_CMP+3
+ };
+ULONG jmp_msg___3[]={
+ 0, /* offset msg___3,*/
+
+ DCMD_JUMP+IF_TRUE+DATA_CMP+2
+ };
+ULONG jmp_msg___2[]={
+ 0, /* offset msg___2,*/
+
+ DCMD_INT+IF_TRUE,
+ __MSGEXTEND
+ };
+
+ULONG msg___3[]={
+ DCMD_CLR_ACK,
+ 0,
+
+ DCMD_BLOCK_MOVE+BLOCK_MOVE+MSG_IN_+3
+ };
+ULONG ACB_msgin123_5[]={
+ 0, /* offset ACB.msgin123,*/
+
+ DCMD_INT+IF_TRUE,
+ __MSGSYNC
+ };
+
+ULONG msg___2[]={
+ DCMD_CLR_ACK,
+ 0,
+
+ DCMD_BLOCK_MOVE+BLOCK_MOVE+MSG_IN_+2
+ };
+ULONG ACB_msgin123_6[]={
+ 0, /* offset ACB.msgin123,*/
+
+ DCMD_INT+IF_TRUE,
+ __MSGWIDE
+ };
+/*
+;############################################################
+; for synchronous negotiation
+; 1. Active ==> INT3, restart at data__1_2
+; 2. Passive ==> INT3, prepare message out, restart at data__1_1
+; 3. Disable ==> INT3, prepare message out, restart at data__1_1
+;############################################################
+*/
+ULONG set_atn[]={
+ DCMD_SET_ATN,
+ 0
+ };
+ULONG msg__a[]={
+ DCMD_CLR_ACK,
+ 0,
+
+ DCMD_JUMP+IF_TRUE
+ };
+ULONG jmp_check_phase3[]={
+ 0 /* offset check_phase */
+ };
+
+ULONG msg__23[]={ /* ; ignore wide residue */
+ DCMD_CLR_ACK,
+ 0,
+
+ DCMD_BLOCK_MOVE+BLOCK_MOVE+MSG_IN_+1
+ };
+ULONG ACB_msgin123_7[]={
+ 0, /* offset ACB.msgin123,*/
+
+ DCMD_CLR_ACK,
+ 0,
+
+ DCMD_JUMP+IF_TRUE
+ };
+ULONG jmp_check_phase4[]={
+ 0 /* offset check_phase */
+ };
+
+ULONG msg__3[]={
+ DCMD_CLR_ACK,
+ 0,
+
+ DCMD_JUMP+IF_TRUE
+ };
+ULONG jmp_check_phase5[]={
+ 0 /* offset check_phase */
+ };
+
+ULONG msg__4[]={ /* ; disconnect */
+ RD_MODIFY_WRT+OP_AND+0x20000+0x7F00, /*;(7FH shl 8) or (__scntl2 shl 16)*/
+ 0,
+
+ DCMD_CLR_ACK,
+ 0,
+
+ DCMD_WAIT_DISC,
+ 0,
+
+ DCMD_INT+IF_TRUE,
+ __DISCONNECTED
+ };
+
+ULONG clr_atn[]={
+ DCMD_CLR_ATN,
+ 0,
+
+ DCMD_JUMP+IF_TRUE
+ };
+ULONG jmp_check_phase6[]={
+ 0 /* offset check_phase */
+ };
+/*
+;==========================================================
+; Used for script operation
+;==========================================================
+*/
+ULONG start_mov[]={
+/*; DCMD_MEM_MOVE+(OFFSET DGROUP:end_script - OFFSET DGROUP:start_script) ;Memory move SCRIPTS instruction*/
+ DCMD_MEM_MOVE+0x1000 /*;Memory move SCRIPTS instruction ( 4K )*/
+ };
+ULONG SrcPhysAddr[]={
+ 0 /*; source */
+ };
+ULONG DesPhysAddr[]={
+ 0, /*; destination */
+
+ DCMD_INT+IF_TRUE, /*; script interrupt, */
+ 0,
+
+ DCMD_INT+IF_NOT, /*; script interrupt */
+ 0
+ };
+ULONG end_script[]={0};
+/***********************************************************************/
+
diff --git a/linux/src/drivers/scsi/scsi.c b/linux/src/drivers/scsi/scsi.c
new file mode 100644
index 00000000..62c4b10c
--- /dev/null
+++ b/linux/src/drivers/scsi/scsi.c
@@ -0,0 +1,3585 @@
+/*
+ * scsi.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * generic mid-level SCSI driver
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Bug correction thanks go to :
+ * Rik Faith <faith@cs.unc.edu>
+ * Tommy Thorn <tthorn>
+ * Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de>
+ *
+ * Modified by Eric Youngdale eric@aib.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Native multichannel, wide scsi, /proc/scsi and hot plugging
+ * support added by Michael Neuffer <mike@i-connect.net>
+ *
+ * Added request_module("scsi_hostadapter") for kerneld:
+ * (Put an "alias scsi_hostadapter your_hostadapter" in /etc/conf.modules)
+ * Bjorn Ekwall <bj0rn@blox.se>
+ *
+ * Major improvements to the timeout, abort, and reset processing,
+ * as well as performance modifications for large queue depths by
+ * Leonard N. Zubkoff <lnz@dandelion.com>
+ */
+
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/blk.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "constants.h"
+
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#undef USE_STATIC_SCSI_MEMORY
+
+/*
+static const char RCSid[] = "$Header: cvs/gnumach/linux/src/drivers/scsi/Attic/scsi.c,v 1.1 1999/04/26 05:54:57 tb Exp $";
+*/
+
+
+/* Command groups 3 and 4 are reserved and should never be used. */
+const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, 12, 12, 10, 10 };
+
+#define INTERNAL_ERROR (panic ("Internal error in file %s, line %d.\n", __FILE__, __LINE__))
+
+/*
+ * PAGE_SIZE must be a multiple of the sector size (512). True
+ * for all reasonably recent architectures (even the VAX...).
+ */
+#define SECTOR_SIZE 512
+#define SECTORS_PER_PAGE (PAGE_SIZE/SECTOR_SIZE)
+
+#if SECTORS_PER_PAGE <= 8
+ typedef unsigned char FreeSectorBitmap;
+#elif SECTORS_PER_PAGE <= 32
+ typedef unsigned int FreeSectorBitmap;
+#else
+# error You lose.
+#endif
+
+static void scsi_done (Scsi_Cmnd *SCpnt);
+static int update_timeout (Scsi_Cmnd *, int);
+static void print_inquiry(unsigned char *data);
+static void scsi_times_out (Scsi_Cmnd * SCpnt);
+static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev ,
+ int * sparse_lun, Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt,
+ struct Scsi_Host *shpnt, char * scsi_result);
+void scsi_build_commandblocks(Scsi_Device * SDpnt);
+
+#ifdef CONFIG_MODULES
+extern struct symbol_table scsi_symbol_table;
+#endif
+
+static FreeSectorBitmap * dma_malloc_freelist = NULL;
+static int scsi_need_isa_bounce_buffers;
+static unsigned int dma_sectors = 0;
+unsigned int dma_free_sectors = 0;
+unsigned int need_isa_buffer = 0;
+static unsigned char ** dma_malloc_pages = NULL;
+
+static int time_start;
+static int time_elapsed;
+static volatile struct Scsi_Host * host_active = NULL;
+#define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \
+ || (HOST->can_queue && HOST->host_busy >= HOST->can_queue))
+
+const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
+{
+ "Direct-Access ",
+ "Sequential-Access",
+ "Printer ",
+ "Processor ",
+ "WORM ",
+ "CD-ROM ",
+ "Scanner ",
+ "Optical Device ",
+ "Medium Changer ",
+ "Communications "
+};
+
+
+/*
+ * global variables :
+ * scsi_devices an array of these specifying the address for each
+ * (host, id, LUN)
+ */
+
+Scsi_Device * scsi_devices = NULL;
+
+/* Process ID of SCSI commands */
+unsigned long scsi_pid = 0;
+
+static unsigned long serial_number = 0;
+
+static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0};
+static void resize_dma_pool(void);
+
+/* This variable is merely a hook so that we can debug the kernel with gdb. */
+Scsi_Cmnd * last_cmnd = NULL;
+
+/* This is the pointer to the /proc/scsi code.
+ * It is only initialized to !=0 if the scsi code is present
+ */
+#if CONFIG_PROC_FS
+extern int (* dispatch_scsi_info_ptr)(int ino, char *buffer, char **start,
+ off_t offset, int length, int inout);
+extern int dispatch_scsi_info(int ino, char *buffer, char **start,
+ off_t offset, int length, int inout);
+
+struct proc_dir_entry proc_scsi_scsi = {
+ PROC_SCSI_SCSI, 4, "scsi",
+ S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, 0,
+ NULL,
+ NULL, NULL,
+ NULL, NULL, NULL
+};
+#endif
+
+/*
+ * This is the number of clock ticks we should wait before we time out
+ * and abort the command. This is for where the scsi.c module generates
+ * the command, not where it originates from a higher level, in which
+ * case the timeout is specified there.
+ *
+ * ABORT_TIMEOUT and RESET_TIMEOUT are the timeouts for RESET and ABORT
+ * respectively.
+ */
+
+#ifdef DEBUG_TIMEOUT
+static void scsi_dump_status(void);
+#endif
+
+
+#ifdef DEBUG
+ #define SCSI_TIMEOUT (5*HZ)
+#else
+ #define SCSI_TIMEOUT (2*HZ)
+#endif
+
+#ifdef DEBUG
+ #define SENSE_TIMEOUT SCSI_TIMEOUT
+ #define ABORT_TIMEOUT SCSI_TIMEOUT
+ #define RESET_TIMEOUT SCSI_TIMEOUT
+#else
+ #define SENSE_TIMEOUT (5*HZ/10)
+ #define RESET_TIMEOUT (5*HZ/10)
+ #define ABORT_TIMEOUT (5*HZ/10)
+#endif
+
+#define MIN_RESET_DELAY (2*HZ)
+
+/* Do not call reset on error if we just did a reset within 15 sec. */
+#define MIN_RESET_PERIOD (15*HZ)
+
+/* The following devices are known not to tolerate a lun != 0 scan for
+ * one reason or another. Some will respond to all luns, others will
+ * lock up.
+ */
+
+#define BLIST_NOLUN 0x01
+#define BLIST_FORCELUN 0x02
+#define BLIST_BORKEN 0x04
+#define BLIST_KEY 0x08
+#define BLIST_SINGLELUN 0x10
+#define BLIST_NOTQ 0x20
+#define BLIST_SPARSELUN 0x40
+#define BLIST_MAX5LUN 0x80
+
+struct dev_info{
+ const char * vendor;
+ const char * model;
+ const char * revision; /* Latest revision known to be bad. Not used yet */
+ unsigned flags;
+};
+
+/*
+ * This is what was previously known as the blacklist. The concept
+ * has been expanded so that we can specify other types of things we
+ * need to be aware of.
+ */
+static struct dev_info device_list[] =
+{
+{"TEAC","CD-R55S","1.0H", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"CHINON","CD-ROM CDS-431","H42", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"CHINON","CD-ROM CDS-535","Q14", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"DENON","DRD-25X","V", BLIST_NOLUN}, /* Locks up if probed for lun != 0 */
+{"HITACHI","DK312C","CM81", BLIST_NOLUN}, /* Responds to all lun - dtg */
+{"HITACHI","DK314C","CR21" , BLIST_NOLUN}, /* responds to all lun */
+{"IMS", "CDD521/10","2.06", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+{"MAXTOR","XT-3280","PR02", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+{"MAXTOR","XT-4380S","B3C", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+{"MAXTOR","MXT-1240S","I1.2", BLIST_NOLUN}, /* Locks up when LUN>0 polled */
+{"MAXTOR","XT-4170S","B5A", BLIST_NOLUN}, /* Locks-up sometimes when LUN>0 polled. */
+{"MAXTOR","XT-8760S","B7B", BLIST_NOLUN}, /* guess what? */
+{"MEDIAVIS","RENO CD-ROMX2A","2.03",BLIST_NOLUN},/*Responds to all lun */
+{"MICROP", "4110", "*", BLIST_NOTQ}, /* Buggy Tagged Queuing */
+{"NEC","CD-ROM DRIVE:841","1.0", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+{"RODIME","RO3000S","2.33", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"SANYO", "CRD-250S", "1.20", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for aha152x controller, which causes
+ * SCSI code to reset bus.*/
+{"SEAGATE", "ST157N", "\004|j", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for aha152x controller, which causes
+ * SCSI code to reset bus.*/
+{"SEAGATE", "ST296","921", BLIST_NOLUN}, /* Responds to all lun */
+{"SEAGATE","ST1581","6538",BLIST_NOLUN}, /* Responds to all lun */
+{"SONY","CD-ROM CDU-541","4.3d", BLIST_NOLUN},
+{"SONY","CD-ROM CDU-55S","1.0i", BLIST_NOLUN},
+{"SONY","CD-ROM CDU-561","1.7x", BLIST_NOLUN},
+{"TANDBERG","TDC 3600","U07", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"TEAC","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for seagate controller, which causes
+ * SCSI code to reset bus.*/
+{"TEXEL","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for seagate controller, which causes
+ * SCSI code to reset bus.*/
+{"QUANTUM","LPS525S","3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */
+{"QUANTUM","PD1225S","3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */
+{"MEDIAVIS","CDR-H93MV","1.31", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"SANKYO", "CP525","6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */
+{"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */
+{"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */
+{"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */
+
+/*
+ * Other types of devices that have special flags.
+ */
+{"SONY","CD-ROM CDU-8001","*", BLIST_BORKEN},
+{"TEXEL","CD-ROM","1.06", BLIST_BORKEN},
+{"IOMEGA","Io20S *F","*", BLIST_KEY},
+{"INSITE","Floptical F*8I","*", BLIST_KEY},
+{"INSITE","I325VM","*", BLIST_KEY},
+{"NRC","MBR-7","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"NRC","MBR-7.4","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"REGAL","CDC-4X","*", BLIST_MAX5LUN | BLIST_SINGLELUN},
+{"NAKAMICH","MJ-4.8S","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"NAKAMICH","MJ-5.16S","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"PIONEER","CD-ROM DRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"EMULEX","MD21/S2 ESDI","*", BLIST_SINGLELUN},
+{"CANON","IPUBJD","*", BLIST_SPARSELUN},
+{"MATSHITA","PD","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"YAMAHA","CDR100","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"YAMAHA","CDR102","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"nCipher","Fastness Crypto","*", BLIST_FORCELUN},
+/*
+ * Must be at end of list...
+ */
+{NULL, NULL, NULL}
+};
+
+static int get_device_flags(unsigned char * response_data){
+ int i = 0;
+ unsigned char * pnt;
+ for(i=0; 1; i++){
+ if(device_list[i].vendor == NULL) return 0;
+ pnt = &response_data[8];
+ while(*pnt && *pnt == ' ') pnt++;
+ if(memcmp(device_list[i].vendor, pnt,
+ strlen(device_list[i].vendor))) continue;
+ pnt = &response_data[16];
+ while(*pnt && *pnt == ' ') pnt++;
+ if(memcmp(device_list[i].model, pnt,
+ strlen(device_list[i].model))) continue;
+ return device_list[i].flags;
+ }
+ return 0;
+}
+
+void scsi_make_blocked_list(void) {
+ int block_count = 0, index;
+ unsigned long flags;
+ struct Scsi_Host * sh[128], * shpnt;
+
+ /*
+ * Create a circular linked list from the scsi hosts which have
+ * the "wish_block" field in the Scsi_Host structure set.
+ * The blocked list should include all the scsi hosts using ISA DMA.
+ * In some systems, using two dma channels simultaneously causes
+ * unpredictable results.
+ * Among the scsi hosts in the blocked list, only one host at a time
+ * is allowed to have active commands queued. The transition from
+ * one active host to the next one is allowed only when host_busy == 0
+ * for the active host (which implies host_busy == 0 for all the hosts
+ * in the list). Moreover for block devices the transition to a new
+ * active host is allowed only when a request is completed, since a
+ * block device request can be divided into multiple scsi commands
+ * (when there are few sg lists or clustering is disabled).
+ *
+ * (DB, 4 Feb 1995)
+ */
+
+ save_flags(flags);
+ cli();
+ host_active = NULL;
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) {
+
+#if 0
+ /*
+ * Is this is a candidate for the blocked list?
+ * Useful to put into the blocked list all the hosts whose driver
+ * does not know about the host->block feature.
+ */
+ if (shpnt->unchecked_isa_dma) shpnt->wish_block = 1;
+#endif
+
+ if (shpnt->wish_block) sh[block_count++] = shpnt;
+ }
+
+ if (block_count == 1) sh[0]->block = NULL;
+
+ else if (block_count > 1) {
+
+ for(index = 0; index < block_count - 1; index++) {
+ sh[index]->block = sh[index + 1];
+ printk("scsi%d : added to blocked host list.\n",
+ sh[index]->host_no);
+ }
+
+ sh[block_count - 1]->block = sh[0];
+ printk("scsi%d : added to blocked host list.\n",
+ sh[index]->host_no);
+ }
+
+ restore_flags(flags);
+}
+
+static void scan_scsis_done (Scsi_Cmnd * SCpnt)
+{
+
+#ifdef DEBUG
+ printk ("scan_scsis_done(%p, %06x)\n", SCpnt->host, SCpnt->result);
+#endif
+ SCpnt->request.rq_status = RQ_SCSI_DONE;
+
+ if (SCpnt->request.sem != NULL)
+ up(SCpnt->request.sem);
+}
+
+#ifdef CONFIG_SCSI_MULTI_LUN
+static int max_scsi_luns = 8;
+#else
+static int max_scsi_luns = 1;
+#endif
+
+void scsi_luns_setup(char *str, int *ints) {
+ if (ints[0] != 1)
+ printk("scsi_luns_setup : usage max_scsi_luns=n (n should be between 1 and 8)\n");
+ else
+ max_scsi_luns = ints[1];
+}
+
+/*
+ * Detecting SCSI devices :
+ * We scan all present host adapter's busses, from ID 0 to ID (max_id).
+ * We use the INQUIRY command, determine device type, and pass the ID /
+ * lun address of all sequential devices to the tape driver, all random
+ * devices to the disk driver.
+ */
+static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
+ unchar hchannel, unchar hid, unchar hlun)
+{
+ int dev, lun, channel;
+ unsigned char scsi_result0[256];
+ unsigned char *scsi_result;
+ Scsi_Device *SDpnt;
+ int max_dev_lun, sparse_lun;
+ Scsi_Cmnd *SCpnt;
+
+ SCpnt = (Scsi_Cmnd *) scsi_init_malloc (sizeof (Scsi_Cmnd), GFP_ATOMIC | GFP_DMA);
+ SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
+ memset (SCpnt, 0, sizeof (Scsi_Cmnd));
+
+
+ /* Make sure we have something that is valid for DMA purposes */
+ scsi_result = ( ( !shpnt->unchecked_isa_dma )
+ ? &scsi_result0[0] : scsi_init_malloc (512, GFP_DMA));
+
+ if (scsi_result == NULL) {
+ printk ("Unable to obtain scsi_result buffer\n");
+ goto leave;
+ }
+
+ /* We must chain ourself in the host_queue, so commands can time out */
+ if(shpnt->host_queue)
+ shpnt->host_queue->prev = SCpnt;
+ SCpnt->next = shpnt->host_queue;
+ SCpnt->prev = NULL;
+ shpnt->host_queue = SCpnt;
+
+
+ if (hardcoded == 1) {
+ Scsi_Device *oldSDpnt=SDpnt;
+ struct Scsi_Device_Template * sdtpnt;
+ channel = hchannel;
+ if(channel > shpnt->max_channel) goto leave;
+ dev = hid;
+ if(dev >= shpnt->max_id) goto leave;
+ lun = hlun;
+ if(lun >= shpnt->max_lun) goto leave;
+ scan_scsis_single (channel, dev, lun, &max_dev_lun, &sparse_lun,
+ &SDpnt, SCpnt, shpnt, scsi_result);
+ if(SDpnt!=oldSDpnt) {
+
+ /* it could happen the blockdevice hasn't yet been inited */
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+
+ oldSDpnt->scsi_request_fn = NULL;
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->attach) {
+ (*sdtpnt->attach)(oldSDpnt);
+ if(oldSDpnt->attached) scsi_build_commandblocks(oldSDpnt);}
+ resize_dma_pool();
+
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) {
+ if(sdtpnt->finish && sdtpnt->nr_dev)
+ {(*sdtpnt->finish)();}
+ }
+ }
+
+ }
+ else {
+ for (channel = 0; channel <= shpnt->max_channel; channel++) {
+ for (dev = 0; dev < shpnt->max_id; ++dev) {
+ if (shpnt->this_id != dev) {
+
+ /*
+ * We need the for so our continue, etc. work fine. We put this in
+ * a variable so that we can override it during the scan if we
+ * detect a device *KNOWN* to have multiple logical units.
+ */
+ max_dev_lun = (max_scsi_luns < shpnt->max_lun ?
+ max_scsi_luns : shpnt->max_lun);
+ sparse_lun = 0;
+ for (lun = 0; lun < max_dev_lun; ++lun) {
+ if (!scan_scsis_single (channel, dev, lun, &max_dev_lun,
+ &sparse_lun, &SDpnt, SCpnt, shpnt,
+ scsi_result)
+ && !sparse_lun)
+ break; /* break means don't probe further for luns!=0 */
+ } /* for lun ends */
+ } /* if this_id != id ends */
+ } /* for dev ends */
+ } /* for channel ends */
+ } /* if/else hardcoded */
+
+ leave:
+
+ {/* Unchain SCpnt from host_queue */
+ Scsi_Cmnd *prev, *next, *hqptr;
+ for(hqptr = shpnt->host_queue; hqptr != SCpnt; hqptr = hqptr->next) ;
+ if(hqptr) {
+ prev = hqptr->prev;
+ next = hqptr->next;
+ if(prev)
+ prev->next = next;
+ else
+ shpnt->host_queue = next;
+ if(next) next->prev = prev;
+ }
+ }
+
+ /* Last device block does not exist. Free memory. */
+ if (SDpnt != NULL)
+ scsi_init_free ((char *) SDpnt, sizeof (Scsi_Device));
+
+ if (SCpnt != NULL)
+ scsi_init_free ((char *) SCpnt, sizeof (Scsi_Cmnd));
+
+ /* If we allocated a buffer so we could do DMA, free it now */
+ if (scsi_result != &scsi_result0[0] && scsi_result != NULL)
+ scsi_init_free (scsi_result, 512);
+
+}
+
+/*
+ * The worker for scan_scsis.
+ * Returning 0 means Please don't ask further for lun!=0, 1 means OK go on.
+ * Global variables used : scsi_devices(linked list)
+ */
+int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
+ int *sparse_lun, Scsi_Device **SDpnt2, Scsi_Cmnd * SCpnt,
+ struct Scsi_Host * shpnt, char *scsi_result)
+{
+ unsigned char scsi_cmd[12];
+ struct Scsi_Device_Template *sdtpnt;
+ Scsi_Device * SDtail, *SDpnt=*SDpnt2;
+ int bflags, type=-1;
+
+ SDtail = scsi_devices;
+ if (scsi_devices)
+ while (SDtail->next)
+ SDtail = SDtail->next;
+
+ memset (SDpnt, 0, sizeof (Scsi_Device));
+ SDpnt->host = shpnt;
+ SDpnt->id = dev;
+ SDpnt->lun = lun;
+ SDpnt->channel = channel;
+
+ /* Some low level driver could use device->type (DB) */
+ SDpnt->type = -1;
+
+ /*
+ * Assume that the device will have handshaking problems, and then fix this
+ * field later if it turns out it doesn't
+ */
+ SDpnt->borken = 1;
+ SDpnt->was_reset = 0;
+ SDpnt->expecting_cc_ua = 0;
+
+ scsi_cmd[0] = TEST_UNIT_READY;
+ scsi_cmd[1] = lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
+
+ SCpnt->host = SDpnt->host;
+ SCpnt->device = SDpnt;
+ SCpnt->target = SDpnt->id;
+ SCpnt->lun = SDpnt->lun;
+ SCpnt->channel = SDpnt->channel;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) scsi_result,
+ 256, scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
+ down (&sem);
+ }
+
+#if defined(DEBUG) || defined(DEBUG_INIT)
+ printk ("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
+ dev, lun, SCpnt->result);
+ print_driverbyte(SCpnt->result); print_hostbyte(SCpnt->result);
+ printk("\n");
+#endif
+
+ if (SCpnt->result) {
+ if (((driver_byte (SCpnt->result) & DRIVER_SENSE) ||
+ (status_byte (SCpnt->result) & CHECK_CONDITION)) &&
+ ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
+ if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
+ ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
+ ((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0))
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+#if defined (DEBUG) || defined(DEBUG_INIT)
+ printk ("scsi: performing INQUIRY\n");
+#endif
+ /*
+ * Build an INQUIRY command block.
+ */
+ scsi_cmd[0] = INQUIRY;
+ scsi_cmd[1] = (lun << 5) & 0xe0;
+ scsi_cmd[2] = 0;
+ scsi_cmd[3] = 0;
+ scsi_cmd[4] = 255;
+ scsi_cmd[5] = 0;
+ SCpnt->cmd_len = 0;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) scsi_result,
+ 256, scan_scsis_done, SCSI_TIMEOUT, 3);
+ down (&sem);
+ }
+
+#if defined(DEBUG) || defined(DEBUG_INIT)
+ printk ("scsi: INQUIRY %s with code 0x%x\n",
+ SCpnt->result ? "failed" : "successful", SCpnt->result);
+#endif
+
+ if (SCpnt->result)
+ return 0; /* assume no peripheral if any sort of error */
+
+ /*
+ * Check the peripheral qualifier field - this tells us whether LUNS
+ * are supported here or not.
+ */
+ if( (scsi_result[0] >> 5) == 3 )
+ {
+ return 0; /* assume no peripheral if any sort of error */
+ }
+
+ /*
+ * It would seem some TOSHIBA CDROM gets things wrong
+ */
+ if (!strncmp (scsi_result + 8, "TOSHIBA", 7) &&
+ !strncmp (scsi_result + 16, "CD-ROM", 6) &&
+ scsi_result[0] == TYPE_DISK) {
+ scsi_result[0] = TYPE_ROM;
+ scsi_result[1] |= 0x80; /* removable */
+ }
+
+ if (!strncmp (scsi_result + 8, "NEC", 3)) {
+ if (!strncmp (scsi_result + 16, "CD-ROM DRIVE:84 ", 16) ||
+ !strncmp (scsi_result + 16, "CD-ROM DRIVE:25", 15))
+ SDpnt->manufacturer = SCSI_MAN_NEC_OLDCDR;
+ else
+ SDpnt->manufacturer = SCSI_MAN_NEC;
+ }
+ else if (!strncmp (scsi_result + 8, "TOSHIBA", 7))
+ SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
+ else if (!strncmp (scsi_result + 8, "SONY", 4))
+ SDpnt->manufacturer = SCSI_MAN_SONY;
+ else if (!strncmp (scsi_result + 8, "PIONEER", 7))
+ SDpnt->manufacturer = SCSI_MAN_PIONEER;
+ else
+ SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
+
+ memcpy (SDpnt->vendor, scsi_result + 8, 8);
+ memcpy (SDpnt->model, scsi_result + 16, 16);
+ memcpy (SDpnt->rev, scsi_result + 32, 4);
+
+ SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
+ SDpnt->lockable = SDpnt->removable;
+ SDpnt->changed = 0;
+ SDpnt->access_count = 0;
+ SDpnt->busy = 0;
+ SDpnt->has_cmdblocks = 0;
+ /*
+ * Currently, all sequential devices are assumed to be tapes, all random
+ * devices disk, with the appropriate read only flags set for ROM / WORM
+ * treated as RO.
+ */
+ switch (type = (scsi_result[0] & 0x1f)) {
+ case TYPE_TAPE:
+ case TYPE_DISK:
+ case TYPE_MOD:
+ case TYPE_PROCESSOR:
+ case TYPE_SCANNER:
+ case TYPE_MEDIUM_CHANGER:
+ SDpnt->writeable = 1;
+ break;
+ case TYPE_WORM:
+ case TYPE_ROM:
+ SDpnt->writeable = 0;
+ break;
+ default:
+ printk ("scsi: unknown type %d\n", type);
+ }
+
+ SDpnt->single_lun = 0;
+ SDpnt->soft_reset =
+ (scsi_result[7] & 1) && ((scsi_result[3] & 7) == 2);
+ SDpnt->random = (type == TYPE_TAPE) ? 0 : 1;
+ SDpnt->type = (type & 0x1f);
+
+ print_inquiry (scsi_result);
+
+ for (sdtpnt = scsi_devicelist; sdtpnt;
+ sdtpnt = sdtpnt->next)
+ if (sdtpnt->detect)
+ SDpnt->attached +=
+ (*sdtpnt->detect) (SDpnt);
+
+ SDpnt->scsi_level = scsi_result[2] & 0x07;
+ if (SDpnt->scsi_level >= 2 ||
+ (SDpnt->scsi_level == 1 &&
+ (scsi_result[3] & 0x0f) == 1))
+ SDpnt->scsi_level++;
+
+ /*
+ * Accommodate drivers that want to sleep when they should be in a polling
+ * loop.
+ */
+ SDpnt->disconnect = 0;
+
+ /*
+ * Get any flags for this device.
+ */
+ bflags = get_device_flags (scsi_result);
+
+ /*
+ * Set the tagged_queue flag for SCSI-II devices that purport to support
+ * tagged queuing in the INQUIRY data.
+ */
+ SDpnt->tagged_queue = 0;
+ if ((SDpnt->scsi_level >= SCSI_2) &&
+ (scsi_result[7] & 2) &&
+ !(bflags & BLIST_NOTQ)) {
+ SDpnt->tagged_supported = 1;
+ SDpnt->current_tag = 0;
+ }
+
+ /*
+ * Some revisions of the Texel CD ROM drives have handshaking problems when
+ * used with the Seagate controllers. Before we know what type of device
+ * we're talking to, we assume it's borken and then change it here if it
+ * turns out that it isn't a TEXEL drive.
+ */
+ if ((bflags & BLIST_BORKEN) == 0)
+ SDpnt->borken = 0;
+
+ /*
+ * If we want to only allow I/O to one of the luns attached to this device
+ * at a time, then we set this flag.
+ */
+ if (bflags & BLIST_SINGLELUN)
+ SDpnt->single_lun = 1;
+
+ /*
+ * These devices need this "key" to unlock the devices so we can use it
+ */
+ if ((bflags & BLIST_KEY) != 0) {
+ printk ("Unlocked floptical drive.\n");
+ SDpnt->lockable = 0;
+ scsi_cmd[0] = MODE_SENSE;
+ scsi_cmd[1] = (lun << 5) & 0xe0;
+ scsi_cmd[2] = 0x2e;
+ scsi_cmd[3] = 0;
+ scsi_cmd[4] = 0x2a;
+ scsi_cmd[5] = 0;
+ SCpnt->cmd_len = 0;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) scsi_result, 0x2a,
+ scan_scsis_done, SCSI_TIMEOUT, 3);
+ down (&sem);
+ }
+ }
+ /* Add this device to the linked list at the end */
+ if (SDtail)
+ SDtail->next = SDpnt;
+ else
+ scsi_devices = SDpnt;
+ SDtail = SDpnt;
+
+ SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
+ *SDpnt2=SDpnt;
+ if (!SDpnt)
+ printk ("scsi: scan_scsis_single: Cannot malloc\n");
+
+
+ /*
+ * Some scsi devices cannot be polled for lun != 0 due to firmware bugs
+ */
+ if (bflags & BLIST_NOLUN)
+ return 0; /* break; */
+
+ /*
+ * If this device is known to support sparse multiple units, override the
+ * other settings, and scan all of them.
+ */
+ if (bflags & BLIST_SPARSELUN) {
+ *max_dev_lun = 8;
+ *sparse_lun = 1;
+ return 1;
+ }
+
+ /*
+ * If this device is known to support multiple units, override the other
+ * settings, and scan all of them.
+ */
+ if (bflags & BLIST_FORCELUN) {
+ *max_dev_lun = 8;
+ return 1;
+ }
+
+ /*
+ * REGAL CDC-4X: avoid hang after LUN 4
+ */
+ if (bflags & BLIST_MAX5LUN) {
+ *max_dev_lun = 5;
+ return 1;
+ }
+
+ /*
+ * We assume the device can't handle lun!=0 if: - it reports scsi-0 (ANSI
+ * SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it reports scsi-1
+ * (ANSI SCSI Revision 1) and Response Data Format 0
+ */
+ if (((scsi_result[2] & 0x07) == 0)
+ ||
+ ((scsi_result[2] & 0x07) == 1 &&
+ (scsi_result[3] & 0x0f) == 0))
+ return 0;
+ return 1;
+}
+
+/*
+ * Flag bits for the internal_timeout array
+ */
+#define NORMAL_TIMEOUT 0
+#define IN_ABORT 1
+#define IN_RESET 2
+#define IN_RESET2 4
+#define IN_RESET3 8
+
+/*
+ * This is our time out function, called when the timer expires for a
+ * given host adapter. It will attempt to abort the currently executing
+ * command, that failing perform a kernel panic.
+ */
+
+static void scsi_times_out (Scsi_Cmnd * SCpnt)
+{
+
+ switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET | IN_RESET2 | IN_RESET3))
+ {
+ case NORMAL_TIMEOUT:
+ {
+#ifdef DEBUG_TIMEOUT
+ scsi_dump_status();
+#endif
+ }
+
+ if (!scsi_abort (SCpnt, DID_TIME_OUT))
+ return;
+ case IN_ABORT:
+ printk("SCSI host %d abort (pid %ld) timed out - resetting\n",
+ SCpnt->host->host_no, SCpnt->pid);
+ if (!scsi_reset (SCpnt, SCSI_RESET_ASYNCHRONOUS))
+ return;
+ case IN_RESET:
+ case (IN_ABORT | IN_RESET):
+ /* This might be controversial, but if there is a bus hang,
+ * you might conceivably want the machine up and running
+ * esp if you have an ide disk.
+ */
+ printk("SCSI host %d channel %d reset (pid %ld) timed out - "
+ "trying harder\n",
+ SCpnt->host->host_no, SCpnt->channel, SCpnt->pid);
+ SCpnt->internal_timeout &= ~IN_RESET;
+ SCpnt->internal_timeout |= IN_RESET2;
+ scsi_reset (SCpnt,
+ SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_BUS_RESET);
+ return;
+ case IN_RESET2:
+ case (IN_ABORT | IN_RESET2):
+ /* Obviously the bus reset didn't work.
+ * Let's try even harder and call for an HBA reset.
+ * Maybe the HBA itself crashed and this will shake it loose.
+ */
+ printk("SCSI host %d reset (pid %ld) timed out - trying to shake it loose\n",
+ SCpnt->host->host_no, SCpnt->pid);
+ SCpnt->internal_timeout &= ~(IN_RESET | IN_RESET2);
+ SCpnt->internal_timeout |= IN_RESET3;
+ scsi_reset (SCpnt,
+ SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_HOST_RESET);
+ return;
+
+ default:
+ printk("SCSI host %d reset (pid %ld) timed out again -\n",
+ SCpnt->host->host_no, SCpnt->pid);
+ printk("probably an unrecoverable SCSI bus or device hang.\n");
+ return;
+
+ }
+
+}
+
+
+/* This function takes a quick look at a request, and decides if it
+ * can be queued now, or if there would be a stall while waiting for
+ * something else to finish. This routine assumes that interrupts are
+ * turned off when entering the routine. It is the responsibility
+ * of the calling code to ensure that this is the case.
+ */
+
+Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
+{
+ Scsi_Cmnd * SCpnt = NULL;
+ int tablesize;
+ Scsi_Cmnd * found = NULL;
+ struct buffer_head * bh, *bhp;
+
+ if (!device)
+ panic ("No device passed to request_queueable().\n");
+
+ if (req && req->rq_status == RQ_INACTIVE)
+ panic("Inactive in request_queueable");
+
+ /*
+ * Look for a free command block. If we have been instructed not to queue
+ * multiple commands to multi-lun devices, then check to see what else is
+ * going for this device first.
+ */
+
+ if (!device->single_lun) {
+ SCpnt = device->device_queue;
+ while(SCpnt){
+ if(SCpnt->request.rq_status == RQ_INACTIVE) break;
+ SCpnt = SCpnt->device_next;
+ }
+ } else {
+ SCpnt = device->host->host_queue;
+ while(SCpnt){
+ if(SCpnt->channel == device->channel
+ && SCpnt->target == device->id) {
+ if (SCpnt->lun == device->lun) {
+ if(found == NULL
+ && SCpnt->request.rq_status == RQ_INACTIVE)
+ {
+ found=SCpnt;
+ }
+ }
+ if(SCpnt->request.rq_status != RQ_INACTIVE) {
+ /*
+ * I think that we should really limit things to one
+ * outstanding command per device - this is what tends
+ * to trip up buggy firmware.
+ */
+ return NULL;
+ }
+ }
+ SCpnt = SCpnt->next;
+ }
+ SCpnt = found;
+ }
+
+ if (!SCpnt) return NULL;
+
+ if (SCSI_BLOCK(device->host)) return NULL;
+
+ if (req) {
+ memcpy(&SCpnt->request, req, sizeof(struct request));
+ tablesize = device->host->sg_tablesize;
+ bhp = bh = req->bh;
+ if(!tablesize) bh = NULL;
+ /* Take a quick look through the table to see how big it is.
+ * We already have our copy of req, so we can mess with that
+ * if we want to.
+ */
+ while(req->nr_sectors && bh){
+ bhp = bhp->b_reqnext;
+ if(!bhp || !CONTIGUOUS_BUFFERS(bh,bhp)) tablesize--;
+ req->nr_sectors -= bh->b_size >> 9;
+ req->sector += bh->b_size >> 9;
+ if(!tablesize) break;
+ bh = bhp;
+ }
+ if(req->nr_sectors && bh && bh->b_reqnext){ /* Any leftovers? */
+ SCpnt->request.bhtail = bh;
+ req->bh = bh->b_reqnext; /* Divide request */
+ bh->b_reqnext = NULL;
+ bh = req->bh;
+
+ /* Now reset things so that req looks OK */
+ SCpnt->request.nr_sectors -= req->nr_sectors;
+ req->current_nr_sectors = bh->b_size >> 9;
+ req->buffer = bh->b_data;
+ SCpnt->request.sem = NULL; /* Wait until whole thing done */
+ } else {
+ req->rq_status = RQ_INACTIVE;
+ wake_up(&wait_for_request);
+ }
+ } else {
+ SCpnt->request.rq_status = RQ_SCSI_BUSY; /* Busy, but no request */
+ SCpnt->request.sem = NULL; /* And no one is waiting for the device
+ * either */
+ }
+
+ SCpnt->use_sg = 0; /* Reset the scatter-gather flag */
+ SCpnt->old_use_sg = 0;
+ SCpnt->transfersize = 0;
+ SCpnt->underflow = 0;
+ SCpnt->cmd_len = 0;
+
+/* Since not everyone seems to set the device info correctly
+ * before Scsi_Cmnd gets send out to scsi_do_command, we do it here.
+ */
+ SCpnt->channel = device->channel;
+ SCpnt->lun = device->lun;
+ SCpnt->target = device->id;
+
+ return SCpnt;
+}
+
+/* This function returns a structure pointer that will be valid for
+ * the device. The wait parameter tells us whether we should wait for
+ * the unit to become free or not. We are also able to tell this routine
+ * not to return a descriptor if the host is unable to accept any more
+ * commands for the time being. We need to keep in mind that there is no
+ * guarantee that the host remain not busy. Keep in mind the
+ * request_queueable function also knows the internal allocation scheme
+ * of the packets for each device
+ */
+
+Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
+ int wait)
+{
+ kdev_t dev;
+ struct request * req = NULL;
+ int tablesize;
+ unsigned long flags;
+ struct buffer_head * bh, *bhp;
+ struct Scsi_Host * host;
+ Scsi_Cmnd * SCpnt = NULL;
+ Scsi_Cmnd * SCwait = NULL;
+ Scsi_Cmnd * found = NULL;
+
+ if (!device)
+ panic ("No device passed to allocate_device().\n");
+
+ if (reqp) req = *reqp;
+
+ /* See if this request has already been queued by an interrupt routine */
+ if (req) {
+ if(req->rq_status == RQ_INACTIVE) return NULL;
+ dev = req->rq_dev;
+ } else
+ dev = 0; /* unused */
+
+ host = device->host;
+
+ if (intr_count && SCSI_BLOCK(host)) return NULL;
+
+ while (1==1){
+ if (!device->single_lun) {
+ SCpnt = device->device_queue;
+ while(SCpnt){
+ SCwait = SCpnt;
+ if(SCpnt->request.rq_status == RQ_INACTIVE) break;
+ SCpnt = SCpnt->device_next;
+ }
+ } else {
+ SCpnt = device->host->host_queue;
+ while(SCpnt){
+ if(SCpnt->channel == device->channel
+ && SCpnt->target == device->id) {
+ if (SCpnt->lun == device->lun) {
+ SCwait = SCpnt;
+ if(found == NULL
+ && SCpnt->request.rq_status == RQ_INACTIVE)
+ {
+ found=SCpnt;
+ }
+ }
+ if(SCpnt->request.rq_status != RQ_INACTIVE) {
+ /*
+ * I think that we should really limit things to one
+ * outstanding command per device - this is what tends
+ * to trip up buggy firmware.
+ */
+ found = NULL;
+ break;
+ }
+ }
+ SCpnt = SCpnt->next;
+ }
+ SCpnt = found;
+ }
+
+ save_flags(flags);
+ cli();
+ /* See if this request has already been queued by an interrupt routine
+ */
+ if (req && (req->rq_status == RQ_INACTIVE || req->rq_dev != dev)) {
+ restore_flags(flags);
+ return NULL;
+ }
+ if (!SCpnt || SCpnt->request.rq_status != RQ_INACTIVE) /* Might have changed */
+ {
+#if 1 /* NEW CODE */
+ if (wait && SCwait && SCwait->request.rq_status != RQ_INACTIVE){
+ sleep_on(&device->device_wait);
+ restore_flags(flags);
+ } else {
+ restore_flags(flags);
+ if (!wait) return NULL;
+ if (!SCwait) {
+ printk("Attempt to allocate device channel %d,"
+ " target %d, lun %d\n", device->channel,
+ device->id, device->lun);
+ panic("No device found in allocate_device\n");
+ }
+ }
+#else /* ORIGINAL CODE */
+ restore_flags(flags);
+ if(!wait) return NULL;
+ if (!SCwait) {
+ printk("Attempt to allocate device channel %d, target"
+ " %d, lun %d\n", device->channel, device->id,
+ device->lun);
+ panic("No device found in allocate_device\n");
+ }
+ SCSI_SLEEP(&device->device_wait,
+ (SCwait->request.rq_status != RQ_INACTIVE));
+#endif
+ } else {
+ if (req) {
+ memcpy(&SCpnt->request, req, sizeof(struct request));
+ tablesize = device->host->sg_tablesize;
+ bhp = bh = req->bh;
+ if(!tablesize) bh = NULL;
+ /* Take a quick look through the table to see how big it is.
+ * We already have our copy of req, so we can mess with that
+ * if we want to.
+ */
+ while(req->nr_sectors && bh){
+ bhp = bhp->b_reqnext;
+ if(!bhp || !CONTIGUOUS_BUFFERS(bh,bhp)) tablesize--;
+ req->nr_sectors -= bh->b_size >> 9;
+ req->sector += bh->b_size >> 9;
+ if(!tablesize) break;
+ bh = bhp;
+ }
+ if(req->nr_sectors && bh && bh->b_reqnext){/* Any leftovers? */
+ SCpnt->request.bhtail = bh;
+ req->bh = bh->b_reqnext; /* Divide request */
+ bh->b_reqnext = NULL;
+ bh = req->bh;
+ /* Now reset things so that req looks OK */
+ SCpnt->request.nr_sectors -= req->nr_sectors;
+ req->current_nr_sectors = bh->b_size >> 9;
+ req->buffer = bh->b_data;
+ SCpnt->request.sem = NULL; /* Wait until whole thing done*/
+ }
+ else
+ {
+ req->rq_status = RQ_INACTIVE;
+ *reqp = req->next;
+ wake_up(&wait_for_request);
+ }
+ } else {
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = NULL; /* And no one is waiting for this
+ * to complete */
+ }
+ restore_flags(flags);
+ break;
+ }
+ }
+
+ SCpnt->use_sg = 0; /* Reset the scatter-gather flag */
+ SCpnt->old_use_sg = 0;
+ SCpnt->transfersize = 0; /* No default transfer size */
+ SCpnt->cmd_len = 0;
+
+ SCpnt->underflow = 0; /* Do not flag underflow conditions */
+
+ /* Since not everyone seems to set the device info correctly
+ * before Scsi_Cmnd gets send out to scsi_do_command, we do it here.
+ */
+ SCpnt->channel = device->channel;
+ SCpnt->lun = device->lun;
+ SCpnt->target = device->id;
+
+ return SCpnt;
+}
+
+/*
+ * This is inline because we have stack problemes if we recurse to deeply.
+ */
+
+inline void internal_cmnd (Scsi_Cmnd * SCpnt)
+{
+ unsigned long flags, timeout;
+ struct Scsi_Host * host;
+#ifdef DEBUG_DELAY
+ unsigned long clock;
+#endif
+
+#if DEBUG
+ unsigned long *ret = 0;
+#ifdef __mips__
+ __asm__ __volatile__ ("move\t%0,$31":"=r"(ret));
+#else
+ ret = __builtin_return_address(0);
+#endif
+#endif
+
+ host = SCpnt->host;
+
+ save_flags(flags);
+ cli();
+ /* Assign a unique nonzero serial_number. */
+ if (++serial_number == 0) serial_number = 1;
+ SCpnt->serial_number = serial_number;
+
+ /*
+ * We will wait MIN_RESET_DELAY clock ticks after the last reset so
+ * we can avoid the drive not being ready.
+ */
+ timeout = host->last_reset + MIN_RESET_DELAY;
+ if (jiffies < timeout) {
+ int ticks_remaining = timeout - jiffies;
+ /*
+ * NOTE: This may be executed from within an interrupt
+ * handler! This is bad, but for now, it'll do. The irq
+ * level of the interrupt handler has been masked out by the
+ * platform dependent interrupt handling code already, so the
+ * sti() here will not cause another call to the SCSI host's
+ * interrupt handler (assuming there is one irq-level per
+ * host).
+ */
+ sti();
+ while (--ticks_remaining >= 0) udelay(1000000/HZ);
+ host->last_reset = jiffies - MIN_RESET_DELAY;
+ }
+ restore_flags(flags);
+
+ update_timeout(SCpnt, SCpnt->timeout_per_command);
+
+ /*
+ * We will use a queued command if possible, otherwise we will emulate the
+ * queuing and calling of completion function ourselves.
+ */
+#ifdef DEBUG
+ printk("internal_cmnd (host = %d, channel = %d, target = %d, "
+ "command = %p, buffer = %p, \nbufflen = %d, done = %p)\n",
+ SCpnt->host->host_no, SCpnt->channel, SCpnt->target, SCpnt->cmnd,
+ SCpnt->buffer, SCpnt->bufflen, SCpnt->done);
+#endif
+
+ if (host->can_queue)
+ {
+#ifdef DEBUG
+ printk("queuecommand : routine at %p\n",
+ host->hostt->queuecommand);
+#endif
+ /* This locking tries to prevent all sorts of races between
+ * queuecommand and the interrupt code. In effect,
+ * we are only allowed to be in queuecommand once at
+ * any given time, and we can only be in the interrupt
+ * handler and the queuecommand function at the same time
+ * when queuecommand is called while servicing the
+ * interrupt.
+ */
+
+ if(!intr_count && SCpnt->host->irq)
+ disable_irq(SCpnt->host->irq);
+
+ host->hostt->queuecommand (SCpnt, scsi_done);
+
+ if(!intr_count && SCpnt->host->irq)
+ enable_irq(SCpnt->host->irq);
+ }
+ else
+ {
+ int temp;
+
+#ifdef DEBUG
+ printk("command() : routine at %p\n", host->hostt->command);
+#endif
+ temp = host->hostt->command (SCpnt);
+ SCpnt->result = temp;
+#ifdef DEBUG_DELAY
+ clock = jiffies + 4 * HZ;
+ while (jiffies < clock) barrier();
+ printk("done(host = %d, result = %04x) : routine at %p\n",
+ host->host_no, temp, host->hostt->command);
+#endif
+ scsi_done(SCpnt);
+ }
+#ifdef DEBUG
+ printk("leaving internal_cmnd()\n");
+#endif
+}
+
+static void scsi_request_sense (Scsi_Cmnd * SCpnt)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ SCpnt->flags |= WAS_SENSE | ASKED_FOR_SENSE;
+ update_timeout(SCpnt, SENSE_TIMEOUT);
+ restore_flags(flags);
+
+
+ memcpy ((void *) SCpnt->cmnd , (void *) generic_sense,
+ sizeof(generic_sense));
+
+ SCpnt->cmnd[1] = SCpnt->lun << 5;
+ SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer);
+
+ SCpnt->request_buffer = &SCpnt->sense_buffer;
+ SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer);
+ SCpnt->use_sg = 0;
+ SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
+ internal_cmnd (SCpnt);
+}
+
+
+
+/*
+ * scsi_do_cmd sends all the commands out to the low-level driver. It
+ * handles the specifics required for each low level driver - ie queued
+ * or non queued. It also prevents conflicts when different high level
+ * drivers go for the same host at the same time.
+ */
+
+void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
+ void *buffer, unsigned bufflen, void (*done)(Scsi_Cmnd *),
+ int timeout, int retries)
+{
+ unsigned long flags;
+ struct Scsi_Host * host = SCpnt->host;
+
+#ifdef DEBUG
+ {
+ int i;
+ int target = SCpnt->target;
+ printk ("scsi_do_cmd (host = %d, channel = %d target = %d, "
+ "buffer =%p, bufflen = %d, done = %p, timeout = %d, "
+ "retries = %d)\n"
+ "command : " , host->host_no, SCpnt->channel, target, buffer,
+ bufflen, done, timeout, retries);
+ for (i = 0; i < 10; ++i)
+ printk ("%02x ", ((unsigned char *) cmnd)[i]);
+ printk("\n");
+ }
+#endif
+
+ if (!host)
+ {
+ panic ("Invalid or not present host.\n");
+ }
+
+
+ /*
+ * We must prevent reentrancy to the lowlevel host driver. This prevents
+ * it - we enter a loop until the host we want to talk to is not busy.
+ * Race conditions are prevented, as interrupts are disabled in between the
+ * time we check for the host being not busy, and the time we mark it busy
+ * ourselves.
+ */
+
+ save_flags(flags);
+ cli();
+ SCpnt->pid = scsi_pid++;
+
+ while (SCSI_BLOCK(host)) {
+ restore_flags(flags);
+ SCSI_SLEEP(&host->host_wait, SCSI_BLOCK(host));
+ cli();
+ }
+
+ if (host->block) host_active = host;
+
+ host->host_busy++;
+ restore_flags(flags);
+
+ /*
+ * Our own function scsi_done (which marks the host as not busy, disables
+ * the timeout counter, etc) will be called by us or by the
+ * scsi_hosts[host].queuecommand() function needs to also call
+ * the completion function for the high level driver.
+ */
+
+ memcpy ((void *) SCpnt->data_cmnd , (const void *) cmnd, 12);
+#if 0
+ SCpnt->host = host;
+ SCpnt->channel = channel;
+ SCpnt->target = target;
+ SCpnt->lun = (SCpnt->data_cmnd[1] >> 5);
+#endif
+ SCpnt->reset_chain = NULL;
+ SCpnt->serial_number = 0;
+ SCpnt->bufflen = bufflen;
+ SCpnt->buffer = buffer;
+ SCpnt->flags = 0;
+ SCpnt->retries = 0;
+ SCpnt->allowed = retries;
+ SCpnt->done = done;
+ SCpnt->timeout_per_command = timeout;
+
+ memcpy ((void *) SCpnt->cmnd , (const void *) cmnd, 12);
+ /* Zero the sense buffer. Some host adapters automatically request
+ * sense on error. 0 is not a valid sense code.
+ */
+ memset ((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer);
+ SCpnt->request_buffer = buffer;
+ SCpnt->request_bufflen = bufflen;
+ SCpnt->old_use_sg = SCpnt->use_sg;
+ if (SCpnt->cmd_len == 0)
+ SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
+ SCpnt->old_cmd_len = SCpnt->cmd_len;
+
+ /* Start the timer ticking. */
+
+ SCpnt->internal_timeout = NORMAL_TIMEOUT;
+ SCpnt->abort_reason = 0;
+ internal_cmnd (SCpnt);
+
+#ifdef DEBUG
+ printk ("Leaving scsi_do_cmd()\n");
+#endif
+}
+
+static int check_sense (Scsi_Cmnd * SCpnt)
+{
+ /* If there is no sense information, request it. If we have already
+ * requested it, there is no point in asking again - the firmware must
+ * be confused.
+ */
+ if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) {
+ if(!(SCpnt->flags & ASKED_FOR_SENSE))
+ return SUGGEST_SENSE;
+ else
+ return SUGGEST_RETRY;
+ }
+
+ SCpnt->flags &= ~ASKED_FOR_SENSE;
+
+#ifdef DEBUG_INIT
+ printk("scsi%d, channel%d : ", SCpnt->host->host_no, SCpnt->channel);
+ print_sense("", SCpnt);
+ printk("\n");
+#endif
+ if (SCpnt->sense_buffer[2] & 0xe0)
+ return SUGGEST_ABORT;
+
+ switch (SCpnt->sense_buffer[2] & 0xf)
+ {
+ case NO_SENSE:
+ return 0;
+ case RECOVERED_ERROR:
+ return SUGGEST_IS_OK;
+
+ case ABORTED_COMMAND:
+ return SUGGEST_RETRY;
+ case NOT_READY:
+ case UNIT_ATTENTION:
+ /*
+ * If we are expecting a CC/UA because of a bus reset that we
+ * performed, treat this just as a retry. Otherwise this is
+ * information that we should pass up to the upper-level driver
+ * so that we can deal with it there.
+ */
+ if( SCpnt->device->expecting_cc_ua )
+ {
+ SCpnt->device->expecting_cc_ua = 0;
+ return SUGGEST_RETRY;
+ }
+ return SUGGEST_ABORT;
+
+ /* these three are not supported */
+ case COPY_ABORTED:
+ case VOLUME_OVERFLOW:
+ case MISCOMPARE:
+
+ case MEDIUM_ERROR:
+ return SUGGEST_REMAP;
+ case BLANK_CHECK:
+ case DATA_PROTECT:
+ case HARDWARE_ERROR:
+ case ILLEGAL_REQUEST:
+ default:
+ return SUGGEST_ABORT;
+ }
+}
+
+/* This function is the mid-level interrupt routine, which decides how
+ * to handle error conditions. Each invocation of this function must
+ * do one and *only* one of the following:
+ *
+ * (1) Call last_cmnd[host].done. This is done for fatal errors and
+ * normal completion, and indicates that the handling for this
+ * request is complete.
+ * (2) Call internal_cmnd to requeue the command. This will result in
+ * scsi_done being called again when the retry is complete.
+ * (3) Call scsi_request_sense. This asks the host adapter/drive for
+ * more information about the error condition. When the information
+ * is available, scsi_done will be called again.
+ * (4) Call reset(). This is sort of a last resort, and the idea is that
+ * this may kick things loose and get the drive working again. reset()
+ * automatically calls scsi_request_sense, and thus scsi_done will be
+ * called again once the reset is complete.
+ *
+ * If none of the above actions are taken, the drive in question
+ * will hang. If more than one of the above actions are taken by
+ * scsi_done, then unpredictable behavior will result.
+ */
+static void scsi_done (Scsi_Cmnd * SCpnt)
+{
+ int status=0;
+ int exit=0;
+ int checked;
+ int oldto;
+ struct Scsi_Host * host = SCpnt->host;
+ int result = SCpnt->result;
+ SCpnt->serial_number = 0;
+ oldto = update_timeout(SCpnt, 0);
+
+#ifdef DEBUG_TIMEOUT
+ if(result) printk("Non-zero result in scsi_done %x %d:%d\n",
+ result, SCpnt->target, SCpnt->lun);
+#endif
+
+ /* If we requested an abort, (and we got it) then fix up the return
+ * status to say why
+ */
+ if(host_byte(result) == DID_ABORT && SCpnt->abort_reason)
+ SCpnt->result = result = (result & 0xff00ffff) |
+ (SCpnt->abort_reason << 16);
+
+
+#define FINISHED 0
+#define MAYREDO 1
+#define REDO 3
+#define PENDING 4
+
+#ifdef DEBUG
+ printk("In scsi_done(host = %d, result = %06x)\n", host->host_no, result);
+#endif
+
+ if(SCpnt->flags & WAS_SENSE)
+ {
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ }
+
+ switch (host_byte(result))
+ {
+ case DID_OK:
+ if (status_byte(result) && (SCpnt->flags & WAS_SENSE))
+ /* Failed to obtain sense information */
+ {
+ SCpnt->flags &= ~WAS_SENSE;
+#if 0 /* This cannot possibly be correct. */
+ SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
+#endif
+
+ if (!(SCpnt->flags & WAS_RESET))
+ {
+ printk("scsi%d : channel %d target %d lun %d request sense"
+ " failed, performing reset.\n",
+ SCpnt->host->host_no, SCpnt->channel, SCpnt->target,
+ SCpnt->lun);
+ scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
+ return;
+ }
+ else
+ {
+ exit = (DRIVER_HARD | SUGGEST_ABORT);
+ status = FINISHED;
+ }
+ }
+ else switch(msg_byte(result))
+ {
+ case COMMAND_COMPLETE:
+ switch (status_byte(result))
+ {
+ case GOOD:
+ if (SCpnt->flags & WAS_SENSE)
+ {
+#ifdef DEBUG
+ printk ("In scsi_done, GOOD status, COMMAND COMPLETE, "
+ "parsing sense information.\n");
+#endif
+ SCpnt->flags &= ~WAS_SENSE;
+#if 0 /* This cannot possibly be correct. */
+ SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
+#endif
+
+ switch (checked = check_sense(SCpnt))
+ {
+ case SUGGEST_SENSE:
+ case 0:
+#ifdef DEBUG
+ printk("NO SENSE. status = REDO\n");
+#endif
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+ case SUGGEST_IS_OK:
+ break;
+ case SUGGEST_REMAP:
+#ifdef DEBUG
+ printk("SENSE SUGGEST REMAP - status = FINISHED\n");
+#endif
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ case SUGGEST_RETRY:
+#ifdef DEBUG
+ printk("SENSE SUGGEST RETRY - status = MAYREDO\n");
+#endif
+ status = MAYREDO;
+ exit = DRIVER_SENSE | SUGGEST_RETRY;
+ break;
+ case SUGGEST_ABORT:
+#ifdef DEBUG
+ printk("SENSE SUGGEST ABORT - status = FINISHED");
+#endif
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ default:
+ printk ("Internal error %s %d \n", __FILE__,
+ __LINE__);
+ }
+ } /* end WAS_SENSE */
+ else
+ {
+#ifdef DEBUG
+ printk("COMMAND COMPLETE message returned, "
+ "status = FINISHED. \n");
+#endif
+ exit = DRIVER_OK;
+ status = FINISHED;
+ }
+ break;
+
+ case CHECK_CONDITION:
+ case COMMAND_TERMINATED:
+ switch (check_sense(SCpnt))
+ {
+ case 0:
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+ case SUGGEST_REMAP:
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ case SUGGEST_RETRY:
+ status = MAYREDO;
+ exit = DRIVER_SENSE | SUGGEST_RETRY;
+ break;
+ case SUGGEST_ABORT:
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ case SUGGEST_SENSE:
+ scsi_request_sense (SCpnt);
+ status = PENDING;
+ break;
+ }
+ break;
+
+ case CONDITION_GOOD:
+ case INTERMEDIATE_GOOD:
+ case INTERMEDIATE_C_GOOD:
+ break;
+
+ case BUSY:
+ case QUEUE_FULL:
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+
+ case RESERVATION_CONFLICT:
+ printk("scsi%d, channel %d : RESERVATION CONFLICT performing"
+ " reset.\n", SCpnt->host->host_no, SCpnt->channel);
+ scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
+ return;
+#if 0
+ exit = DRIVER_SOFT | SUGGEST_ABORT;
+ status = MAYREDO;
+ break;
+#endif
+ default:
+ printk ("Internal error %s %d \n"
+ "status byte = %d \n", __FILE__,
+ __LINE__, status_byte(result));
+
+ }
+ break;
+ default:
+ panic("scsi: unsupported message byte %d received\n",
+ msg_byte(result));
+ }
+ break;
+ case DID_TIME_OUT:
+#ifdef DEBUG
+ printk("Host returned DID_TIME_OUT - ");
+#endif
+
+ if (SCpnt->flags & WAS_TIMEDOUT)
+ {
+#ifdef DEBUG
+ printk("Aborting\n");
+#endif
+ /*
+ Allow TEST_UNIT_READY and INQUIRY commands to timeout early
+ without causing resets. All other commands should be retried.
+ */
+ if (SCpnt->cmnd[0] != TEST_UNIT_READY &&
+ SCpnt->cmnd[0] != INQUIRY)
+ status = MAYREDO;
+ exit = (DRIVER_TIMEOUT | SUGGEST_ABORT);
+ }
+ else
+ {
+#ifdef DEBUG
+ printk ("Retrying.\n");
+#endif
+ SCpnt->flags |= WAS_TIMEDOUT;
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ status = REDO;
+ }
+ break;
+ case DID_BUS_BUSY:
+ case DID_PARITY:
+ status = REDO;
+ break;
+ case DID_NO_CONNECT:
+#ifdef DEBUG
+ printk("Couldn't connect.\n");
+#endif
+ exit = (DRIVER_HARD | SUGGEST_ABORT);
+ break;
+ case DID_ERROR:
+ status = MAYREDO;
+ exit = (DRIVER_HARD | SUGGEST_ABORT);
+ break;
+ case DID_BAD_TARGET:
+ case DID_ABORT:
+ exit = (DRIVER_INVALID | SUGGEST_ABORT);
+ break;
+ case DID_RESET:
+ if (SCpnt->flags & IS_RESETTING)
+ {
+ SCpnt->flags &= ~IS_RESETTING;
+ status = REDO;
+ break;
+ }
+
+ if(msg_byte(result) == GOOD &&
+ status_byte(result) == CHECK_CONDITION) {
+ switch (check_sense(SCpnt)) {
+ case 0:
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+ case SUGGEST_REMAP:
+ case SUGGEST_RETRY:
+ status = MAYREDO;
+ exit = DRIVER_SENSE | SUGGEST_RETRY;
+ break;
+ case SUGGEST_ABORT:
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ case SUGGEST_SENSE:
+ scsi_request_sense (SCpnt);
+ status = PENDING;
+ break;
+ }
+ } else {
+ status=REDO;
+ exit = SUGGEST_RETRY;
+ }
+ break;
+ default :
+ exit = (DRIVER_ERROR | SUGGEST_DIE);
+ }
+
+ switch (status)
+ {
+ case FINISHED:
+ case PENDING:
+ break;
+ case MAYREDO:
+#ifdef DEBUG
+ printk("In MAYREDO, allowing %d retries, have %d\n",
+ SCpnt->allowed, SCpnt->retries);
+#endif
+ if ((++SCpnt->retries) < SCpnt->allowed)
+ {
+ if ((SCpnt->retries >= (SCpnt->allowed >> 1))
+ && !(SCpnt->host->last_reset > 0 &&
+ jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD)
+ && !(SCpnt->flags & WAS_RESET))
+ {
+ printk("scsi%d channel %d : resetting for second half of retries.\n",
+ SCpnt->host->host_no, SCpnt->channel);
+ scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
+ break;
+ }
+
+ }
+ else
+ {
+ status = FINISHED;
+ break;
+ }
+ /* fall through to REDO */
+
+ case REDO:
+
+ if (SCpnt->flags & WAS_SENSE)
+ scsi_request_sense(SCpnt);
+ else
+ {
+ memcpy ((void *) SCpnt->cmnd,
+ (void*) SCpnt->data_cmnd,
+ sizeof(SCpnt->data_cmnd));
+ SCpnt->request_buffer = SCpnt->buffer;
+ SCpnt->request_bufflen = SCpnt->bufflen;
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ internal_cmnd (SCpnt);
+ }
+ break;
+ default:
+ INTERNAL_ERROR;
+ }
+
+ if (status == FINISHED) {
+#ifdef DEBUG
+ printk("Calling done function - at address %p\n", SCpnt->done);
+#endif
+ host->host_busy--; /* Indicate that we are free */
+
+ if (host->block && host->host_busy == 0) {
+ host_active = NULL;
+
+ /* For block devices "wake_up" is done in end_scsi_request */
+ if (MAJOR(SCpnt->request.rq_dev) != SCSI_DISK_MAJOR &&
+ MAJOR(SCpnt->request.rq_dev) != SCSI_CDROM_MAJOR) {
+ struct Scsi_Host * next;
+
+ for (next = host->block; next != host; next = next->block)
+ wake_up(&next->host_wait);
+ }
+
+ }
+
+ wake_up(&host->host_wait);
+ SCpnt->result = result | ((exit & 0xff) << 24);
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ SCpnt->done (SCpnt);
+ }
+
+#undef FINISHED
+#undef REDO
+#undef MAYREDO
+#undef PENDING
+}
+
+/*
+ * The scsi_abort function interfaces with the abort() function of the host
+ * we are aborting, and causes the current command to not complete. The
+ * caller should deal with any error messages or status returned on the
+ * next call.
+ *
+ * This will not be called reentrantly for a given host.
+ */
+
+/*
+ * Since we're nice guys and specified that abort() and reset()
+ * can be non-reentrant. The internal_timeout flags are used for
+ * this.
+ */
+
+
+int scsi_abort (Scsi_Cmnd * SCpnt, int why)
+{
+ int oldto;
+ unsigned long flags;
+ struct Scsi_Host * host = SCpnt->host;
+
+ while(1)
+ {
+ save_flags(flags);
+ cli();
+
+ /*
+ * Protect against races here. If the command is done, or we are
+ * on a different command forget it.
+ */
+ if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) {
+ restore_flags(flags);
+ return 0;
+ }
+
+ if (SCpnt->internal_timeout & IN_ABORT)
+ {
+ restore_flags(flags);
+ while (SCpnt->internal_timeout & IN_ABORT)
+ barrier();
+ }
+ else
+ {
+ SCpnt->internal_timeout |= IN_ABORT;
+ oldto = update_timeout(SCpnt, ABORT_TIMEOUT);
+
+ if ((SCpnt->flags & IS_RESETTING) && SCpnt->device->soft_reset) {
+ /* OK, this command must have died when we did the
+ * reset. The device itself must have lied.
+ */
+ printk("Stale command on %d %d:%d appears to have died when"
+ " the bus was reset\n",
+ SCpnt->channel, SCpnt->target, SCpnt->lun);
+ }
+
+ restore_flags(flags);
+ if (!host->host_busy) {
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ update_timeout(SCpnt, oldto);
+ return 0;
+ }
+ printk("scsi : aborting command due to timeout : pid %lu, scsi%d,"
+ " channel %d, id %d, lun %d ",
+ SCpnt->pid, SCpnt->host->host_no, (int) SCpnt->channel,
+ (int) SCpnt->target, (int) SCpnt->lun);
+ print_command (SCpnt->cmnd);
+ if (SCpnt->serial_number != SCpnt->serial_number_at_timeout)
+ return 0;
+ SCpnt->abort_reason = why;
+ switch(host->hostt->abort(SCpnt)) {
+ /* We do not know how to abort. Try waiting another
+ * time increment and see if this helps. Set the
+ * WAS_TIMEDOUT flag set so we do not try this twice
+ */
+ case SCSI_ABORT_BUSY: /* Tough call - returning 1 from
+ * this is too severe
+ */
+ case SCSI_ABORT_SNOOZE:
+ if(why == DID_TIME_OUT) {
+ save_flags(flags);
+ cli();
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ if(SCpnt->flags & WAS_TIMEDOUT) {
+ restore_flags(flags);
+ return 1; /* Indicate we cannot handle this.
+ * We drop down into the reset handler
+ * and try again
+ */
+ } else {
+ SCpnt->flags |= WAS_TIMEDOUT;
+ oldto = SCpnt->timeout_per_command;
+ update_timeout(SCpnt, oldto);
+ }
+ restore_flags(flags);
+ }
+ return 0;
+ case SCSI_ABORT_PENDING:
+ if(why != DID_TIME_OUT) {
+ save_flags(flags);
+ cli();
+ update_timeout(SCpnt, oldto);
+ restore_flags(flags);
+ }
+ return 0;
+ case SCSI_ABORT_SUCCESS:
+ /* We should have already aborted this one. No
+ * need to adjust timeout
+ */
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ return 0;
+ case SCSI_ABORT_NOT_RUNNING:
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ update_timeout(SCpnt, 0);
+ return 0;
+ case SCSI_ABORT_ERROR:
+ default:
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ return 1;
+ }
+ }
+ }
+}
+
+
+/* Mark a single SCSI Device as having been reset. */
+
+static inline void scsi_mark_device_reset(Scsi_Device *Device)
+{
+ Device->was_reset = 1;
+ Device->expecting_cc_ua = 1;
+}
+
+
+/* Mark all SCSI Devices on a specific Host as having been reset. */
+
+void scsi_mark_host_reset(struct Scsi_Host *Host)
+{
+ Scsi_Cmnd *SCpnt;
+ for (SCpnt = Host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ scsi_mark_device_reset(SCpnt->device);
+}
+
+
+/* Mark all SCSI Devices on a specific Host Bus as having been reset. */
+
+void scsi_mark_bus_reset(struct Scsi_Host *Host, int channel)
+{
+ Scsi_Cmnd *SCpnt;
+ for (SCpnt = Host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if (SCpnt->channel == channel)
+ scsi_mark_device_reset(SCpnt->device);
+}
+
+
+int scsi_reset (Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+{
+ int temp;
+ unsigned long flags;
+ Scsi_Cmnd * SCpnt1;
+ struct Scsi_Host * host = SCpnt->host;
+
+ printk("SCSI bus is being reset for host %d channel %d.\n",
+ host->host_no, SCpnt->channel);
+
+#if 0
+ /*
+ * First of all, we need to make a recommendation to the low-level
+ * driver as to whether a BUS_DEVICE_RESET should be performed,
+ * or whether we should do a full BUS_RESET. There is no simple
+ * algorithm here - we basically use a series of heuristics
+ * to determine what we should do.
+ */
+ SCpnt->host->suggest_bus_reset = FALSE;
+
+ /*
+ * First see if all of the active devices on the bus have
+ * been jammed up so that we are attempting resets. If so,
+ * then suggest a bus reset. Forcing a bus reset could
+ * result in some race conditions, but no more than
+ * you would usually get with timeouts. We will cross
+ * that bridge when we come to it.
+ *
+ * This is actually a pretty bad idea, since a sequence of
+ * commands will often timeout together and this will cause a
+ * Bus Device Reset followed immediately by a SCSI Bus Reset.
+ * If all of the active devices really are jammed up, the
+ * Bus Device Reset will quickly timeout and scsi_times_out
+ * will follow up with a SCSI Bus Reset anyway.
+ */
+ SCpnt1 = host->host_queue;
+ while(SCpnt1) {
+ if( SCpnt1->request.rq_status != RQ_INACTIVE
+ && (SCpnt1->flags & (WAS_RESET | IS_RESETTING)) == 0 )
+ break;
+ SCpnt1 = SCpnt1->next;
+ }
+ if( SCpnt1 == NULL ) {
+ reset_flags |= SCSI_RESET_SUGGEST_BUS_RESET;
+ }
+
+ /*
+ * If the code that called us is suggesting a hard reset, then
+ * definitely request it. This usually occurs because a
+ * BUS_DEVICE_RESET times out.
+ *
+ * Passing reset_flags along takes care of this automatically.
+ */
+ if( reset_flags & SCSI_RESET_SUGGEST_BUS_RESET ) {
+ SCpnt->host->suggest_bus_reset = TRUE;
+ }
+#endif
+
+ while (1) {
+ save_flags(flags);
+ cli();
+
+ /*
+ * Protect against races here. If the command is done, or we are
+ * on a different command forget it.
+ */
+ if (reset_flags & SCSI_RESET_ASYNCHRONOUS)
+ if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) {
+ restore_flags(flags);
+ return 0;
+ }
+
+ if (SCpnt->internal_timeout & IN_RESET)
+ {
+ restore_flags(flags);
+ while (SCpnt->internal_timeout & IN_RESET)
+ barrier();
+ }
+ else
+ {
+ SCpnt->internal_timeout |= IN_RESET;
+ update_timeout(SCpnt, RESET_TIMEOUT);
+
+ if (host->host_busy)
+ {
+ restore_flags(flags);
+ SCpnt1 = host->host_queue;
+ while(SCpnt1) {
+ if (SCpnt1->request.rq_status != RQ_INACTIVE) {
+#if 0
+ if (!(SCpnt1->flags & IS_RESETTING) &&
+ !(SCpnt1->internal_timeout & IN_ABORT))
+ scsi_abort(SCpnt1, DID_RESET);
+#endif
+ SCpnt1->flags |= (WAS_RESET | IS_RESETTING);
+ }
+ SCpnt1 = SCpnt1->next;
+ }
+
+ host->last_reset = jiffies;
+ temp = host->hostt->reset(SCpnt, reset_flags);
+ /*
+ This test allows the driver to introduce an additional bus
+ settle time delay by setting last_reset up to 20 seconds in
+ the future. In the normal case where the driver does not
+ modify last_reset, it must be assumed that the actual bus
+ reset occurred immediately prior to the return to this code,
+ and so last_reset must be updated to the current time, so
+ that the delay in internal_cmnd will guarantee at least a
+ MIN_RESET_DELAY bus settle time.
+ */
+ if ((host->last_reset < jiffies) ||
+ (host->last_reset > (jiffies + 20 * HZ)))
+ host->last_reset = jiffies;
+ }
+ else
+ {
+ if (!host->block) host->host_busy++;
+ restore_flags(flags);
+ host->last_reset = jiffies;
+ SCpnt->flags |= (WAS_RESET | IS_RESETTING);
+ temp = host->hostt->reset(SCpnt, reset_flags);
+ if ((host->last_reset < jiffies) ||
+ (host->last_reset > (jiffies + 20 * HZ)))
+ host->last_reset = jiffies;
+ if (!host->block) host->host_busy--;
+ }
+
+#ifdef DEBUG
+ printk("scsi reset function returned %d\n", temp);
+#endif
+
+ /*
+ * Now figure out what we need to do, based upon
+ * what the low level driver said that it did.
+ * If the result is SCSI_RESET_SUCCESS, SCSI_RESET_PENDING,
+ * or SCSI_RESET_WAKEUP, then the low level driver did a
+ * bus device reset or bus reset, so we should go through
+ * and mark one or all of the devices on that bus
+ * as having been reset.
+ */
+ switch(temp & SCSI_RESET_ACTION) {
+ case SCSI_RESET_SUCCESS:
+ if (temp & SCSI_RESET_HOST_RESET)
+ scsi_mark_host_reset(host);
+ else if (temp & SCSI_RESET_BUS_RESET)
+ scsi_mark_bus_reset(host, SCpnt->channel);
+ else scsi_mark_device_reset(SCpnt->device);
+ save_flags(flags);
+ cli();
+ SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
+ restore_flags(flags);
+ return 0;
+ case SCSI_RESET_PENDING:
+ if (temp & SCSI_RESET_HOST_RESET)
+ scsi_mark_host_reset(host);
+ else if (temp & SCSI_RESET_BUS_RESET)
+ scsi_mark_bus_reset(host, SCpnt->channel);
+ else scsi_mark_device_reset(SCpnt->device);
+ case SCSI_RESET_NOT_RUNNING:
+ return 0;
+ case SCSI_RESET_PUNT:
+ SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
+ scsi_request_sense (SCpnt);
+ return 0;
+ case SCSI_RESET_WAKEUP:
+ if (temp & SCSI_RESET_HOST_RESET)
+ scsi_mark_host_reset(host);
+ else if (temp & SCSI_RESET_BUS_RESET)
+ scsi_mark_bus_reset(host, SCpnt->channel);
+ else scsi_mark_device_reset(SCpnt->device);
+ SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
+ scsi_request_sense (SCpnt);
+ /*
+ * If a bus reset was performed, we
+ * need to wake up each and every command
+ * that was active on the bus or if it was a HBA
+ * reset all active commands on all channels
+ */
+ if( temp & SCSI_RESET_HOST_RESET )
+ {
+ SCpnt1 = host->host_queue;
+ while(SCpnt1) {
+ if (SCpnt1->request.rq_status != RQ_INACTIVE
+ && SCpnt1 != SCpnt)
+ scsi_request_sense (SCpnt1);
+ SCpnt1 = SCpnt1->next;
+ }
+ } else if( temp & SCSI_RESET_BUS_RESET ) {
+ SCpnt1 = host->host_queue;
+ while(SCpnt1) {
+ if(SCpnt1->request.rq_status != RQ_INACTIVE
+ && SCpnt1 != SCpnt
+ && SCpnt1->channel == SCpnt->channel)
+ scsi_request_sense (SCpnt);
+ SCpnt1 = SCpnt1->next;
+ }
+ }
+ return 0;
+ case SCSI_RESET_SNOOZE:
+ /* In this case, we set the timeout field to 0
+ * so that this command does not time out any more,
+ * and we return 1 so that we get a message on the
+ * screen.
+ */
+ save_flags(flags);
+ cli();
+ SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
+ update_timeout(SCpnt, 0);
+ restore_flags(flags);
+ /* If you snooze, you lose... */
+ case SCSI_RESET_ERROR:
+ default:
+ return 1;
+ }
+
+ return temp;
+ }
+ }
+}
+
+
+static void scsi_main_timeout(void)
+{
+ /*
+ * We must not enter update_timeout with a timeout condition still pending.
+ */
+
+ int timed_out;
+ unsigned long flags;
+ struct Scsi_Host * host;
+ Scsi_Cmnd * SCpnt = NULL;
+
+ save_flags(flags);
+ cli();
+
+ update_timeout(NULL, 0);
+
+ /*
+ * Find all timers such that they have 0 or negative (shouldn't happen)
+ * time remaining on them.
+ */
+ timed_out = 0;
+ for (host = scsi_hostlist; host; host = host->next) {
+ for (SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if (SCpnt->timeout == -1)
+ {
+ SCpnt->timeout = 0;
+ SCpnt->serial_number_at_timeout = SCpnt->serial_number;
+ ++timed_out;
+ }
+ }
+ if (timed_out > 0) {
+ for (host = scsi_hostlist; host; host = host->next) {
+ for (SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if (SCpnt->serial_number_at_timeout > 0 &&
+ SCpnt->serial_number_at_timeout == SCpnt->serial_number)
+ {
+ restore_flags(flags);
+ scsi_times_out(SCpnt);
+ SCpnt->serial_number_at_timeout = 0;
+ cli();
+ }
+ }
+ }
+ restore_flags(flags);
+}
+
+/*
+ * The strategy is to cause the timer code to call scsi_times_out()
+ * when the soonest timeout is pending.
+ * The arguments are used when we are queueing a new command, because
+ * we do not want to subtract the time used from this time, but when we
+ * set the timer, we want to take this value into account.
+ */
+
+static int update_timeout(Scsi_Cmnd * SCset, int timeout)
+{
+ unsigned int least, used;
+ unsigned int oldto;
+ unsigned long flags;
+ struct Scsi_Host * host;
+ Scsi_Cmnd * SCpnt = NULL;
+
+ save_flags(flags);
+ cli();
+
+ oldto = 0;
+
+ /*
+ * This routine can be a performance bottleneck under high loads, since
+ * it is called twice per SCSI operation: once when internal_cmnd is
+ * called, and again when scsi_done completes the command. To limit
+ * the load this routine can cause, we shortcut processing if no clock
+ * ticks have occurred since the last time it was called.
+ */
+
+ if (jiffies == time_start && timer_table[SCSI_TIMER].expires > 0) {
+ if(SCset){
+ oldto = SCset->timeout;
+ SCset->timeout = timeout;
+ if (timeout > 0 &&
+ jiffies + timeout < timer_table[SCSI_TIMER].expires)
+ timer_table[SCSI_TIMER].expires = jiffies + timeout;
+ }
+ restore_flags(flags);
+ return oldto;
+ }
+
+ /*
+ * Figure out how much time has passed since the last time the timeouts
+ * were updated
+ */
+ used = (time_start) ? (jiffies - time_start) : 0;
+
+ /*
+ * Find out what is due to timeout soonest, and adjust all timeouts for
+ * the amount of time that has passed since the last time we called
+ * update_timeout.
+ */
+
+ oldto = 0;
+
+ if(SCset){
+ oldto = SCset->timeout - used;
+ SCset->timeout = timeout;
+ }
+
+ least = 0xffffffff;
+
+ for(host = scsi_hostlist; host; host = host->next)
+ for(SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if (SCpnt->timeout > 0) {
+ if (SCpnt != SCset)
+ SCpnt->timeout -= used;
+ if(SCpnt->timeout <= 0) SCpnt->timeout = -1;
+ if(SCpnt->timeout > 0 && SCpnt->timeout < least)
+ least = SCpnt->timeout;
+ }
+
+ /*
+ * If something is due to timeout again, then we will set the next timeout
+ * interrupt to occur. Otherwise, timeouts are disabled.
+ */
+
+ if (least != 0xffffffff)
+ {
+ time_start = jiffies;
+ timer_table[SCSI_TIMER].expires = (time_elapsed = least) + jiffies;
+ timer_active |= 1 << SCSI_TIMER;
+ }
+ else
+ {
+ timer_table[SCSI_TIMER].expires = time_start = time_elapsed = 0;
+ timer_active &= ~(1 << SCSI_TIMER);
+ }
+ restore_flags(flags);
+ return oldto;
+}
+
+#ifdef CONFIG_MODULES
+static int scsi_register_host(Scsi_Host_Template *);
+static void scsi_unregister_host(Scsi_Host_Template *);
+#endif
+
+void *scsi_malloc(unsigned int len)
+{
+ unsigned int nbits, mask;
+ unsigned long flags;
+ int i, j;
+ if(len % SECTOR_SIZE != 0 || len > PAGE_SIZE)
+ return NULL;
+
+ save_flags(flags);
+ cli();
+ nbits = len >> 9;
+ mask = (1 << nbits) - 1;
+
+ for(i=0;i < dma_sectors / SECTORS_PER_PAGE; i++)
+ for(j=0; j<=SECTORS_PER_PAGE - nbits; j++){
+ if ((dma_malloc_freelist[i] & (mask << j)) == 0){
+ dma_malloc_freelist[i] |= (mask << j);
+ restore_flags(flags);
+ dma_free_sectors -= nbits;
+#ifdef DEBUG
+ printk("SMalloc: %d %p\n",len, dma_malloc_pages[i] + (j << 9));
+#endif
+ return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9));
+ }
+ }
+ restore_flags(flags);
+ return NULL; /* Nope. No more */
+}
+
+int scsi_free(void *obj, unsigned int len)
+{
+ unsigned int page, sector, nbits, mask;
+ unsigned long flags;
+
+#ifdef DEBUG
+ unsigned long ret = 0;
+
+#ifdef __mips__
+ __asm__ __volatile__ ("move\t%0,$31":"=r"(ret));
+#else
+ ret = __builtin_return_address(0);
+#endif
+ printk("scsi_free %p %d\n",obj, len);
+#endif
+
+ for (page = 0; page < dma_sectors / SECTORS_PER_PAGE; page++) {
+ unsigned long page_addr = (unsigned long) dma_malloc_pages[page];
+ if ((unsigned long) obj >= page_addr &&
+ (unsigned long) obj < page_addr + PAGE_SIZE)
+ {
+ sector = (((unsigned long) obj) - page_addr) >> 9;
+
+ nbits = len >> 9;
+ mask = (1 << nbits) - 1;
+
+ if ((mask << sector) >= (1 << SECTORS_PER_PAGE))
+ panic ("scsi_free:Bad memory alignment");
+
+ save_flags(flags);
+ cli();
+ if((dma_malloc_freelist[page] &
+ (mask << sector)) != (mask<<sector)){
+#ifdef DEBUG
+ printk("scsi_free(obj=%p, len=%d) called from %08lx\n",
+ obj, len, ret);
+#endif
+ panic("scsi_free:Trying to free unused memory");
+ }
+ dma_free_sectors += nbits;
+ dma_malloc_freelist[page] &= ~(mask << sector);
+ restore_flags(flags);
+ return 0;
+ }
+ }
+ panic("scsi_free:Bad offset");
+}
+
+
+int scsi_loadable_module_flag; /* Set after we scan builtin drivers */
+
+void * scsi_init_malloc(unsigned int size, int priority)
+{
+ void * retval;
+
+ /*
+ * For buffers used by the DMA pool, we assume page aligned
+ * structures.
+ */
+ if ((size % PAGE_SIZE) == 0) {
+ int order, a_size;
+ for (order = 0, a_size = PAGE_SIZE;
+ a_size < size; order++, a_size <<= 1)
+ ;
+ retval = (void *) __get_dma_pages(priority & GFP_LEVEL_MASK,
+ order);
+ } else
+ retval = kmalloc(size, priority);
+
+ if (retval)
+ memset(retval, 0, size);
+ return retval;
+}
+
+
+void scsi_init_free(char * ptr, unsigned int size)
+{
+ /*
+ * We need this special code here because the DMA pool assumes
+ * page aligned data. Besides, it is wasteful to allocate
+ * page sized chunks with kmalloc.
+ */
+ if ((size % PAGE_SIZE) == 0) {
+ int order, a_size;
+
+ for (order = 0, a_size = PAGE_SIZE;
+ a_size < size; order++, a_size <<= 1)
+ ;
+ free_pages((unsigned long)ptr, order);
+ } else
+ kfree(ptr);
+}
+
+void scsi_build_commandblocks(Scsi_Device * SDpnt)
+{
+ struct Scsi_Host *host = SDpnt->host;
+ int j;
+ Scsi_Cmnd * SCpnt;
+
+ if (SDpnt->queue_depth == 0)
+ SDpnt->queue_depth = host->cmd_per_lun;
+ SDpnt->device_queue = NULL;
+
+ for(j=0;j<SDpnt->queue_depth;j++){
+ SCpnt = (Scsi_Cmnd *)
+ scsi_init_malloc(sizeof(Scsi_Cmnd),
+ GFP_ATOMIC |
+ (host->unchecked_isa_dma ? GFP_DMA : 0));
+ SCpnt->host = host;
+ SCpnt->device = SDpnt;
+ SCpnt->target = SDpnt->id;
+ SCpnt->lun = SDpnt->lun;
+ SCpnt->channel = SDpnt->channel;
+ SCpnt->request.rq_status = RQ_INACTIVE;
+ SCpnt->use_sg = 0;
+ SCpnt->old_use_sg = 0;
+ SCpnt->old_cmd_len = 0;
+ SCpnt->timeout = 0;
+ SCpnt->underflow = 0;
+ SCpnt->transfersize = 0;
+ SCpnt->serial_number = 0;
+ SCpnt->serial_number_at_timeout = 0;
+ SCpnt->host_scribble = NULL;
+ if(host->host_queue)
+ host->host_queue->prev = SCpnt;
+ SCpnt->next = host->host_queue;
+ SCpnt->prev = NULL;
+ host->host_queue = SCpnt;
+ SCpnt->device_next = SDpnt->device_queue;
+ SDpnt->device_queue = SCpnt;
+ }
+ SDpnt->has_cmdblocks = 1;
+}
+
+/*
+ * scsi_dev_init() is our initialization routine, which in turn calls host
+ * initialization, bus scanning, and sd/st initialization routines.
+ */
+
+int scsi_dev_init(void)
+{
+ Scsi_Device * SDpnt;
+ struct Scsi_Host * shpnt;
+ struct Scsi_Device_Template * sdtpnt;
+#ifdef FOO_ON_YOU
+ return;
+#endif
+
+ /* Yes we're here... */
+#if CONFIG_PROC_FS
+ dispatch_scsi_info_ptr = dispatch_scsi_info;
+#endif
+
+ /* Init a few things so we can "malloc" memory. */
+ scsi_loadable_module_flag = 0;
+
+ timer_table[SCSI_TIMER].fn = scsi_main_timeout;
+ timer_table[SCSI_TIMER].expires = 0;
+
+#ifdef CONFIG_MODULES
+ register_symtab(&scsi_symbol_table);
+#endif
+
+ /* Register the /proc/scsi/scsi entry */
+#if CONFIG_PROC_FS
+ proc_scsi_register(0, &proc_scsi_scsi);
+#endif
+
+ /* initialize all hosts */
+ scsi_init();
+
+ scsi_devices = (Scsi_Device *) NULL;
+
+ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
+ scan_scsis(shpnt,0,0,0,0); /* scan for scsi devices */
+ if (shpnt->select_queue_depths != NULL)
+ (shpnt->select_queue_depths)(shpnt, scsi_devices);
+ }
+
+ printk("scsi : detected ");
+ for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if (sdtpnt->dev_noticed && sdtpnt->name)
+ printk("%d SCSI %s%s ", sdtpnt->dev_noticed, sdtpnt->name,
+ (sdtpnt->dev_noticed != 1) ? "s" : "");
+ printk("total.\n");
+
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+
+ for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
+ SDpnt->scsi_request_fn = NULL;
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+ if(SDpnt->attached) scsi_build_commandblocks(SDpnt);
+ }
+
+
+ /*
+ * This should build the DMA pool.
+ */
+ resize_dma_pool();
+
+ /*
+ * OK, now we finish the initialization by doing spin-up, read
+ * capacity, etc, etc
+ */
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->finish && sdtpnt->nr_dev)
+ (*sdtpnt->finish)();
+
+ scsi_loadable_module_flag = 1;
+
+ return 0;
+}
+
+static void print_inquiry(unsigned char *data)
+{
+ int i;
+
+ printk(" Vendor: ");
+ for (i = 8; i < 16; i++)
+ {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
+
+ printk(" Model: ");
+ for (i = 16; i < 32; i++)
+ {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
+
+ printk(" Rev: ");
+ for (i = 32; i < 36; i++)
+ {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
+
+ printk("\n");
+
+ i = data[0] & 0x1f;
+
+ printk(" Type: %s ",
+ i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : "Unknown " );
+ printk(" ANSI SCSI revision: %02x", data[2] & 0x07);
+ if ((data[2] & 0x07) == 1 && (data[3] & 0x0f) == 1)
+ printk(" CCS\n");
+ else
+ printk("\n");
+}
+
+
+#ifdef CONFIG_PROC_FS
+int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+ Scsi_Cmnd *SCpnt;
+ struct Scsi_Device_Template *SDTpnt;
+ Scsi_Device *scd, *scd_h = NULL;
+ struct Scsi_Host *HBA_ptr;
+ char *p;
+ int host, channel, id, lun;
+ int size, len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+
+ scd = scsi_devices;
+ HBA_ptr = scsi_hostlist;
+
+ if(inout == 0) {
+ size = sprintf(buffer+len,"Attached devices: %s\n", (scd)?"":"none");
+ len += size;
+ pos = begin + len;
+ while (HBA_ptr) {
+#if 0
+ size += sprintf(buffer+len,"scsi%2d: %s\n", (int) HBA_ptr->host_no,
+ HBA_ptr->hostt->procname);
+ len += size;
+ pos = begin + len;
+#endif
+ scd = scsi_devices;
+ while (scd) {
+ if (scd->host == HBA_ptr) {
+ proc_print_scsidevice(scd, buffer, &size, len);
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+ scd = scd->next;
+ }
+ HBA_ptr = HBA_ptr->next;
+ }
+
+ stop_output:
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len = length; /* Ending slop */
+ return (len);
+ }
+
+ if(!buffer || length < 25 || strncmp("scsi", buffer, 4))
+ return(-EINVAL);
+
+ /*
+ * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi
+ * with "0 1 2 3" replaced by your "Host Channel Id Lun".
+ * Consider this feature BETA.
+ * CAUTION: This is not for hotplugging your peripherals. As
+ * SCSI was not designed for this you could damage your
+ * hardware !
+ * However perhaps it is legal to switch on an
+ * already connected device. It is perhaps not
+ * guaranteed this device doesn't corrupt an ongoing data transfer.
+ */
+ if(!strncmp("add-single-device", buffer + 5, 17)) {
+ p = buffer + 23;
+
+ host = simple_strtoul(p, &p, 0);
+ channel = simple_strtoul(p+1, &p, 0);
+ id = simple_strtoul(p+1, &p, 0);
+ lun = simple_strtoul(p+1, &p, 0);
+
+ printk("scsi singledevice %d %d %d %d\n", host, channel,
+ id, lun);
+
+ while(scd && (scd->host->host_no != host
+ || scd->channel != channel
+ || scd->id != id
+ || scd->lun != lun)) {
+ scd = scd->next;
+ }
+ if(scd)
+ return(-ENOSYS); /* We do not yet support unplugging */
+ while(HBA_ptr && HBA_ptr->host_no != host)
+ HBA_ptr = HBA_ptr->next;
+
+ if(!HBA_ptr)
+ return(-ENXIO);
+
+ scan_scsis (HBA_ptr, 1, channel, id, lun);
+ return(length);
+
+ }
+
+ /*
+ * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi
+ * with "0 1 2 3" replaced by your "Host Channel Id Lun".
+ *
+ * Consider this feature pre-BETA.
+ *
+ * CAUTION: This is not for hotplugging your peripherals. As
+ * SCSI was not designed for this you could damage your
+ * hardware and thoroughly confuse the SCSI subsystem.
+ *
+ */
+ else if(!strncmp("remove-single-device", buffer + 5, 20)) {
+ p = buffer + 26;
+
+ host = simple_strtoul(p, &p, 0);
+ channel = simple_strtoul(p+1, &p, 0);
+ id = simple_strtoul(p+1, &p, 0);
+ lun = simple_strtoul(p+1, &p, 0);
+
+ while(scd != NULL) {
+ if(scd->host->host_no == host
+ && scd->channel == channel
+ && scd->id == id
+ && scd->lun == lun){
+ break;
+ }
+ scd_h = scd;
+ scd = scd->next;
+ }
+
+ if(scd == NULL)
+ return(-ENODEV); /* there is no such device attached */
+
+ if(scd->access_count)
+ return(-EBUSY);
+
+ SDTpnt = scsi_devicelist;
+ while(SDTpnt != NULL) {
+ if(SDTpnt->detach) (*SDTpnt->detach)(scd);
+ SDTpnt = SDTpnt->next;
+ }
+
+ if(scd->attached == 0) {
+ /*
+ * Nobody is using this device any more.
+ * Free all of the command structures.
+ */
+ for(SCpnt=scd->host->host_queue; SCpnt; SCpnt = SCpnt->next){
+ if(SCpnt->device == scd) {
+ if(SCpnt->prev != NULL)
+ SCpnt->prev->next = SCpnt->next;
+ if(SCpnt->next != NULL)
+ SCpnt->next->prev = SCpnt->prev;
+ if(SCpnt == scd->host->host_queue)
+ scd->host->host_queue = SCpnt->next;
+ scsi_init_free((char *) SCpnt, sizeof(*SCpnt));
+ }
+ }
+ /* Now we can remove the device structure */
+ if(scd_h != NULL) {
+ scd_h->next = scd->next;
+ } else if (scsi_devices == scd) {
+ /* We had a hit on the first entry of the device list */
+ scsi_devices = scd->next;
+ }
+ scsi_init_free((char *) scd, sizeof(Scsi_Device));
+ } else {
+ return(-EBUSY);
+ }
+ return(0);
+ }
+ return(-EINVAL);
+}
+#endif
+
+/*
+ * Go through the device list and recompute the most appropriate size
+ * for the dma pool. Then grab more memory (as required).
+ */
+static void resize_dma_pool(void)
+{
+ int i;
+ unsigned long size;
+ struct Scsi_Host * shpnt;
+ struct Scsi_Host * host = NULL;
+ Scsi_Device * SDpnt;
+ unsigned long flags;
+ FreeSectorBitmap * new_dma_malloc_freelist = NULL;
+ unsigned int new_dma_sectors = 0;
+ unsigned int new_need_isa_buffer = 0;
+ unsigned char ** new_dma_malloc_pages = NULL;
+
+ if( !scsi_devices )
+ {
+ /*
+ * Free up the DMA pool.
+ */
+ if( dma_free_sectors != dma_sectors )
+ panic("SCSI DMA pool memory leak %d %d\n",dma_free_sectors,dma_sectors);
+
+ for(i=0; i < dma_sectors / SECTORS_PER_PAGE; i++)
+ scsi_init_free(dma_malloc_pages[i], PAGE_SIZE);
+ if (dma_malloc_pages)
+ scsi_init_free((char *) dma_malloc_pages,
+ (dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_pages));
+ dma_malloc_pages = NULL;
+ if (dma_malloc_freelist)
+ scsi_init_free((char *) dma_malloc_freelist,
+ (dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_freelist));
+ dma_malloc_freelist = NULL;
+ dma_sectors = 0;
+ dma_free_sectors = 0;
+ return;
+ }
+ /* Next, check to see if we need to extend the DMA buffer pool */
+
+ new_dma_sectors = 2*SECTORS_PER_PAGE; /* Base value we use */
+
+ if (high_memory-1 > ISA_DMA_THRESHOLD)
+ scsi_need_isa_bounce_buffers = 1;
+ else
+ scsi_need_isa_bounce_buffers = 0;
+
+ if (scsi_devicelist)
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ new_dma_sectors += SECTORS_PER_PAGE; /* Increment for each host */
+
+ for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
+ host = SDpnt->host;
+
+ /*
+ * sd and sr drivers allocate scatterlists.
+ * sr drivers may allocate for each command 1x2048 or 2x1024 extra
+ * buffers for 2k sector size and 1k fs.
+ * sg driver allocates buffers < 4k.
+ * st driver does not need buffers from the dma pool.
+ * estimate 4k buffer/command for devices of unknown type (should panic).
+ */
+ if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM ||
+ SDpnt->type == TYPE_DISK || SDpnt->type == TYPE_MOD) {
+ new_dma_sectors += ((host->sg_tablesize *
+ sizeof(struct scatterlist) + 511) >> 9) *
+ SDpnt->queue_depth;
+ if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM)
+ new_dma_sectors += (2048 >> 9) * SDpnt->queue_depth;
+ }
+ else if (SDpnt->type == TYPE_SCANNER ||
+ SDpnt->type == TYPE_PROCESSOR ||
+ SDpnt->type == TYPE_MEDIUM_CHANGER) {
+ new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
+ }
+ else {
+ if (SDpnt->type != TYPE_TAPE) {
+ printk("resize_dma_pool: unknown device type %d\n", SDpnt->type);
+ new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
+ }
+ }
+
+ if(host->unchecked_isa_dma &&
+ scsi_need_isa_bounce_buffers &&
+ SDpnt->type != TYPE_TAPE) {
+ new_dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
+ SDpnt->queue_depth;
+ new_need_isa_buffer++;
+ }
+ }
+
+#ifdef DEBUG_INIT
+ printk("resize_dma_pool: needed dma sectors = %d\n", new_dma_sectors);
+#endif
+
+ /* limit DMA memory to 32MB: */
+ new_dma_sectors = (new_dma_sectors + 15) & 0xfff0;
+
+ /*
+ * We never shrink the buffers - this leads to
+ * race conditions that I would rather not even think
+ * about right now.
+ */
+ if( new_dma_sectors < dma_sectors )
+ new_dma_sectors = dma_sectors;
+
+ if (new_dma_sectors)
+ {
+ size = (new_dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap);
+ new_dma_malloc_freelist = (FreeSectorBitmap *) scsi_init_malloc(size, GFP_ATOMIC);
+ memset(new_dma_malloc_freelist, 0, size);
+
+ size = (new_dma_sectors / SECTORS_PER_PAGE)*sizeof(*new_dma_malloc_pages);
+ new_dma_malloc_pages = (unsigned char **) scsi_init_malloc(size, GFP_ATOMIC);
+ memset(new_dma_malloc_pages, 0, size);
+ }
+
+ /*
+ * If we need more buffers, expand the list.
+ */
+ if( new_dma_sectors > dma_sectors ) {
+ for(i=dma_sectors / SECTORS_PER_PAGE; i< new_dma_sectors / SECTORS_PER_PAGE; i++)
+ new_dma_malloc_pages[i] = (unsigned char *)
+ scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
+ }
+
+ /* When we dick with the actual DMA list, we need to
+ * protect things
+ */
+ save_flags(flags);
+ cli();
+ if (dma_malloc_freelist)
+ {
+ size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap);
+ memcpy(new_dma_malloc_freelist, dma_malloc_freelist, size);
+ scsi_init_free((char *) dma_malloc_freelist, size);
+ }
+ dma_malloc_freelist = new_dma_malloc_freelist;
+
+ if (dma_malloc_pages)
+ {
+ size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_pages);
+ memcpy(new_dma_malloc_pages, dma_malloc_pages, size);
+ scsi_init_free((char *) dma_malloc_pages, size);
+ }
+
+ dma_free_sectors += new_dma_sectors - dma_sectors;
+ dma_malloc_pages = new_dma_malloc_pages;
+ dma_sectors = new_dma_sectors;
+ need_isa_buffer = new_need_isa_buffer;
+ restore_flags(flags);
+
+#ifdef DEBUG_INIT
+ printk("resize_dma_pool: dma free sectors = %d\n", dma_free_sectors);
+ printk("resize_dma_pool: dma sectors = %d\n", dma_sectors);
+ printk("resize_dma_pool: need isa buffers = %d\n", need_isa_buffer);
+#endif
+}
+
+#ifdef CONFIG_MODULES /* a big #ifdef block... */
+
+/*
+ * This entry point should be called by a loadable module if it is trying
+ * add a low level scsi driver to the system.
+ */
+static int scsi_register_host(Scsi_Host_Template * tpnt)
+{
+ int pcount;
+ struct Scsi_Host * shpnt;
+ Scsi_Device * SDpnt;
+ struct Scsi_Device_Template * sdtpnt;
+ const char * name;
+
+ if (tpnt->next || !tpnt->detect) return 1;/* Must be already loaded, or
+ * no detect routine available
+ */
+ pcount = next_scsi_host;
+ if ((tpnt->present = tpnt->detect(tpnt)))
+ {
+ if(pcount == next_scsi_host) {
+ if(tpnt->present > 1) {
+ printk("Failure to register low-level scsi driver");
+ scsi_unregister_host(tpnt);
+ return 1;
+ }
+ /* The low-level driver failed to register a driver. We
+ * can do this now.
+ */
+ scsi_register(tpnt,0);
+ }
+ tpnt->next = scsi_hosts; /* Add to the linked list */
+ scsi_hosts = tpnt;
+
+ /* Add the new driver to /proc/scsi */
+#if CONFIG_PROC_FS
+ build_proc_dir_entries(tpnt);
+#endif
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ if(shpnt->hostt == tpnt)
+ {
+ if(tpnt->info)
+ name = tpnt->info(shpnt);
+ else
+ name = tpnt->name;
+ printk ("scsi%d : %s\n", /* And print a little message */
+ shpnt->host_no, name);
+ }
+
+ printk ("scsi : %d host%s.\n", next_scsi_host,
+ (next_scsi_host == 1) ? "" : "s");
+
+ scsi_make_blocked_list();
+
+ /* The next step is to call scan_scsis here. This generates the
+ * Scsi_Devices entries
+ */
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ if(shpnt->hostt == tpnt) {
+ scan_scsis(shpnt,0,0,0,0);
+ if (shpnt->select_queue_depths != NULL)
+ (shpnt->select_queue_depths)(shpnt, scsi_devices);
+ }
+
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+
+ /* Next we create the Scsi_Cmnd structures for this host */
+
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ if(SDpnt->host->hostt == tpnt)
+ {
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+ if(SDpnt->attached) scsi_build_commandblocks(SDpnt);
+ }
+
+ /*
+ * Now that we have all of the devices, resize the DMA pool,
+ * as required. */
+ resize_dma_pool();
+
+
+ /* This does any final handling that is required. */
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->finish && sdtpnt->nr_dev)
+ (*sdtpnt->finish)();
+ }
+
+#if defined(USE_STATIC_SCSI_MEMORY)
+ printk ("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n",
+ (scsi_memory_upper_value - scsi_memory_lower_value) / 1024,
+ (scsi_init_memory_start - scsi_memory_lower_value) / 1024,
+ (scsi_memory_upper_value - scsi_init_memory_start) / 1024);
+#endif
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Similarly, this entry point should be called by a loadable module if it
+ * is trying to remove a low level scsi driver from the system.
+ */
+static void scsi_unregister_host(Scsi_Host_Template * tpnt)
+{
+ Scsi_Host_Template * SHT, *SHTp;
+ Scsi_Device *sdpnt, * sdppnt, * sdpnt1;
+ Scsi_Cmnd * SCpnt;
+ unsigned long flags;
+ struct Scsi_Device_Template * sdtpnt;
+ struct Scsi_Host * shpnt, *sh1;
+ int pcount;
+
+ /* First verify that this host adapter is completely free with no pending
+ * commands */
+
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+ if(sdpnt->host->hostt == tpnt && sdpnt->host->hostt->usage_count
+ && *sdpnt->host->hostt->usage_count) return;
+
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ if (shpnt->hostt != tpnt) continue;
+ for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ save_flags(flags);
+ cli();
+ if(SCpnt->request.rq_status != RQ_INACTIVE) {
+ restore_flags(flags);
+ for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if(SCpnt->request.rq_status == RQ_SCSI_DISCONNECTING)
+ SCpnt->request.rq_status = RQ_INACTIVE;
+ printk("Device busy???\n");
+ return;
+ }
+ SCpnt->request.rq_status = RQ_SCSI_DISCONNECTING; /* Mark as busy */
+ restore_flags(flags);
+ }
+ }
+ /* Next we detach the high level drivers from the Scsi_Device structures */
+
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+ if(sdpnt->host->hostt == tpnt)
+ {
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->detach) (*sdtpnt->detach)(sdpnt);
+ /* If something still attached, punt */
+ if (sdpnt->attached) {
+ printk("Attached usage count = %d\n", sdpnt->attached);
+ return;
+ }
+ }
+
+ /* Next we free up the Scsi_Cmnd structures for this host */
+
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+ if(sdpnt->host->hostt == tpnt)
+ while (sdpnt->host->host_queue) {
+ SCpnt = sdpnt->host->host_queue->next;
+ scsi_init_free((char *) sdpnt->host->host_queue, sizeof(Scsi_Cmnd));
+ sdpnt->host->host_queue = SCpnt;
+ if (SCpnt) SCpnt->prev = NULL;
+ sdpnt->has_cmdblocks = 0;
+ }
+
+ /* Next free up the Scsi_Device structures for this host */
+
+ sdppnt = NULL;
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt1)
+ {
+ sdpnt1 = sdpnt->next;
+ if (sdpnt->host->hostt == tpnt) {
+ if (sdppnt)
+ sdppnt->next = sdpnt->next;
+ else
+ scsi_devices = sdpnt->next;
+ scsi_init_free((char *) sdpnt, sizeof (Scsi_Device));
+ } else
+ sdppnt = sdpnt;
+ }
+
+ /* Next we go through and remove the instances of the individual hosts
+ * that were detected */
+
+ shpnt = scsi_hostlist;
+ while(shpnt) {
+ sh1 = shpnt->next;
+ if(shpnt->hostt == tpnt) {
+ if(shpnt->loaded_as_module) {
+ pcount = next_scsi_host;
+ /* Remove the /proc/scsi directory entry */
+#if CONFIG_PROC_FS
+ proc_scsi_unregister(tpnt->proc_dir,
+ shpnt->host_no + PROC_SCSI_FILE);
+#endif
+ if(tpnt->release)
+ (*tpnt->release)(shpnt);
+ else {
+ /* This is the default case for the release function.
+ * It should do the right thing for most correctly
+ * written host adapters.
+ */
+ if (shpnt->irq) free_irq(shpnt->irq, NULL);
+ if (shpnt->dma_channel != 0xff) free_dma(shpnt->dma_channel);
+ if (shpnt->io_port && shpnt->n_io_port)
+ release_region(shpnt->io_port, shpnt->n_io_port);
+ }
+ if(pcount == next_scsi_host) scsi_unregister(shpnt);
+ tpnt->present--;
+ }
+ }
+ shpnt = sh1;
+ }
+
+ /*
+ * If there are absolutely no more hosts left, it is safe
+ * to completely nuke the DMA pool. The resize operation will
+ * do the right thing and free everything.
+ */
+ if( !scsi_devices )
+ resize_dma_pool();
+
+ printk ("scsi : %d host%s.\n", next_scsi_host,
+ (next_scsi_host == 1) ? "" : "s");
+
+#if defined(USE_STATIC_SCSI_MEMORY)
+ printk ("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n",
+ (scsi_memory_upper_value - scsi_memory_lower_value) / 1024,
+ (scsi_init_memory_start - scsi_memory_lower_value) / 1024,
+ (scsi_memory_upper_value - scsi_init_memory_start) / 1024);
+#endif
+
+ scsi_make_blocked_list();
+
+ /* There were some hosts that were loaded at boot time, so we cannot
+ do any more than this */
+ if (tpnt->present) return;
+
+ /* OK, this is the very last step. Remove this host adapter from the
+ linked list. */
+ for(SHTp=NULL, SHT=scsi_hosts; SHT; SHTp=SHT, SHT=SHT->next)
+ if(SHT == tpnt) {
+ if(SHTp)
+ SHTp->next = SHT->next;
+ else
+ scsi_hosts = SHT->next;
+ SHT->next = NULL;
+ break;
+ }
+
+ /* Rebuild the /proc/scsi directory entries */
+#if CONFIG_PROC_FS
+ proc_scsi_unregister(tpnt->proc_dir, tpnt->proc_dir->low_ino);
+#endif
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * This entry point should be called by a loadable module if it is trying
+ * add a high level scsi driver to the system.
+ */
+static int scsi_register_device_module(struct Scsi_Device_Template * tpnt)
+{
+ Scsi_Device * SDpnt;
+
+ if (tpnt->next) return 1;
+
+ scsi_register_device(tpnt);
+ /*
+ * First scan the devices that we know about, and see if we notice them.
+ */
+
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ if(tpnt->detect) SDpnt->attached += (*tpnt->detect)(SDpnt);
+
+ /*
+ * If any of the devices would match this driver, then perform the
+ * init function.
+ */
+ if(tpnt->init && tpnt->dev_noticed)
+ if ((*tpnt->init)()) return 1;
+
+ /*
+ * Now actually connect the devices to the new driver.
+ */
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ {
+ if(tpnt->attach) (*tpnt->attach)(SDpnt);
+ /*
+ * If this driver attached to the device, and we no longer
+ * have anything attached, release the scsi command blocks.
+ */
+ if(SDpnt->attached && SDpnt->has_cmdblocks == 0)
+ scsi_build_commandblocks(SDpnt);
+ }
+
+ /*
+ * This does any final handling that is required.
+ */
+ if(tpnt->finish && tpnt->nr_dev) (*tpnt->finish)();
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int scsi_unregister_device(struct Scsi_Device_Template * tpnt)
+{
+ Scsi_Device * SDpnt;
+ Scsi_Cmnd * SCpnt;
+ struct Scsi_Device_Template * spnt;
+ struct Scsi_Device_Template * prev_spnt;
+
+ /*
+ * If we are busy, this is not going to fly.
+ */
+ if( *tpnt->usage_count != 0) return 0;
+ /*
+ * Next, detach the devices from the driver.
+ */
+
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ {
+ if(tpnt->detach) (*tpnt->detach)(SDpnt);
+ if(SDpnt->attached == 0)
+ {
+ /*
+ * Nobody is using this device any more. Free all of the
+ * command structures.
+ */
+ for(SCpnt = SDpnt->host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ if(SCpnt->device == SDpnt)
+ {
+ if(SCpnt->prev != NULL)
+ SCpnt->prev->next = SCpnt->next;
+ if(SCpnt->next != NULL)
+ SCpnt->next->prev = SCpnt->prev;
+ if(SCpnt == SDpnt->host->host_queue)
+ SDpnt->host->host_queue = SCpnt->next;
+ scsi_init_free((char *) SCpnt, sizeof(*SCpnt));
+ }
+ }
+ SDpnt->has_cmdblocks = 0;
+ }
+ }
+ /*
+ * Extract the template from the linked list.
+ */
+ spnt = scsi_devicelist;
+ prev_spnt = NULL;
+ while(spnt != tpnt)
+ {
+ prev_spnt = spnt;
+ spnt = spnt->next;
+ }
+ if(prev_spnt == NULL)
+ scsi_devicelist = tpnt->next;
+ else
+ prev_spnt->next = spnt->next;
+
+ MOD_DEC_USE_COUNT;
+ /*
+ * Final cleanup for the driver is done in the driver sources in the
+ * cleanup function.
+ */
+ return 0;
+}
+
+
+int scsi_register_module(int module_type, void * ptr)
+{
+ switch(module_type){
+ case MODULE_SCSI_HA:
+ return scsi_register_host((Scsi_Host_Template *) ptr);
+
+ /* Load upper level device handler of some kind */
+ case MODULE_SCSI_DEV:
+#ifdef CONFIG_KERNELD
+ if (scsi_hosts == NULL)
+ request_module("scsi_hostadapter");
+#endif
+ return scsi_register_device_module((struct Scsi_Device_Template *) ptr);
+ /* The rest of these are not yet implemented */
+
+ /* Load constants.o */
+ case MODULE_SCSI_CONST:
+
+ /* Load specialized ioctl handler for some device. Intended for
+ * cdroms that have non-SCSI2 audio command sets. */
+ case MODULE_SCSI_IOCTL:
+
+ default:
+ return 1;
+ }
+}
+
+void scsi_unregister_module(int module_type, void * ptr)
+{
+ switch(module_type) {
+ case MODULE_SCSI_HA:
+ scsi_unregister_host((Scsi_Host_Template *) ptr);
+ break;
+ case MODULE_SCSI_DEV:
+ scsi_unregister_device((struct Scsi_Device_Template *) ptr);
+ break;
+ /* The rest of these are not yet implemented. */
+ case MODULE_SCSI_CONST:
+ case MODULE_SCSI_IOCTL:
+ break;
+ default:
+ }
+ return;
+}
+
+#endif /* CONFIG_MODULES */
+
+#ifdef DEBUG_TIMEOUT
+static void
+scsi_dump_status(void)
+{
+ int i;
+ struct Scsi_Host * shpnt;
+ Scsi_Cmnd * SCpnt;
+ printk("Dump of scsi parameters:\n");
+ i = 0;
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ for(SCpnt=shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ /* (0) 0:0:0:0 (802 123434 8 8 0) (3 3 2) (%d %d %d) %d %x */
+ printk("(%d) %d:%d:%d:%d (%s %ld %ld %ld %d) (%d %d %x) (%d %d %d) %x %x %x\n",
+ i++, SCpnt->host->host_no,
+ SCpnt->channel,
+ SCpnt->target,
+ SCpnt->lun,
+ kdevname(SCpnt->request.rq_dev),
+ SCpnt->request.sector,
+ SCpnt->request.nr_sectors,
+ SCpnt->request.current_nr_sectors,
+ SCpnt->use_sg,
+ SCpnt->retries,
+ SCpnt->allowed,
+ SCpnt->flags,
+ SCpnt->timeout_per_command,
+ SCpnt->timeout,
+ SCpnt->internal_timeout,
+ SCpnt->cmnd[0],
+ SCpnt->sense_buffer[2],
+ SCpnt->result);
+ }
+ printk("wait_for_request = %p\n", wait_for_request);
+ /* Now dump the request lists for each block device */
+ printk("Dump of pending block device requests\n");
+ for(i=0; i<MAX_BLKDEV; i++)
+ if(blk_dev[i].current_request)
+ {
+ struct request * req;
+ printk("%d: ", i);
+ req = blk_dev[i].current_request;
+ while(req) {
+ printk("(%s %d %ld %ld %ld) ",
+ kdevname(req->rq_dev),
+ req->cmd,
+ req->sector,
+ req->nr_sectors,
+ req->current_nr_sectors);
+ req = req->next;
+ }
+ printk("\n");
+ }
+}
+#endif
+
+#ifdef MODULE
+
+int init_module(void) {
+ unsigned long size;
+
+ /*
+ * This makes /proc/scsi visible.
+ */
+#if CONFIG_PROC_FS
+ dispatch_scsi_info_ptr = dispatch_scsi_info;
+#endif
+
+ timer_table[SCSI_TIMER].fn = scsi_main_timeout;
+ timer_table[SCSI_TIMER].expires = 0;
+ register_symtab(&scsi_symbol_table);
+ scsi_loadable_module_flag = 1;
+
+ /* Register the /proc/scsi/scsi entry */
+#if CONFIG_PROC_FS
+ proc_scsi_register(0, &proc_scsi_scsi);
+#endif
+
+
+ dma_sectors = PAGE_SIZE / SECTOR_SIZE;
+ dma_free_sectors= dma_sectors;
+ /*
+ * Set up a minimal DMA buffer list - this will be used during scan_scsis
+ * in some cases.
+ */
+
+ /* One bit per sector to indicate free/busy */
+ size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap);
+ dma_malloc_freelist = (unsigned char *) scsi_init_malloc(size, GFP_ATOMIC);
+ memset(dma_malloc_freelist, 0, size);
+
+ /* One pointer per page for the page list */
+ dma_malloc_pages = (unsigned char **)
+ scsi_init_malloc((dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_pages), GFP_ATOMIC);
+ dma_malloc_pages[0] = (unsigned char *)
+ scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
+ return 0;
+}
+
+void cleanup_module( void)
+{
+#if CONFIG_PROC_FS
+ proc_scsi_unregister(0, PROC_SCSI_SCSI);
+
+ /* No, we're not here anymore. Don't show the /proc/scsi files. */
+ dispatch_scsi_info_ptr = 0L;
+#endif
+
+ /*
+ * Free up the DMA pool.
+ */
+ resize_dma_pool();
+
+ timer_table[SCSI_TIMER].fn = NULL;
+ timer_table[SCSI_TIMER].expires = 0;
+}
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/scsi.h b/linux/src/drivers/scsi/scsi.h
new file mode 100644
index 00000000..c3dfcd45
--- /dev/null
+++ b/linux/src/drivers/scsi/scsi.h
@@ -0,0 +1,632 @@
+/*
+ * scsi.h Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ * generic SCSI package header file by
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale eric@aib.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ */
+
+#ifndef _SCSI_H
+#define _SCSI_H
+
+/*
+ * Some of the public constants are being moved to this file.
+ * We include it here so that what came from where is transparent.
+ */
+#include <scsi/scsi.h>
+
+#include <linux/random.h>
+
+
+/*
+ * Some defs, in case these are not defined elsewhere.
+ */
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+
+extern void scsi_make_blocked_list(void);
+extern volatile int in_scan_scsis;
+extern const unsigned char scsi_command_size[8];
+#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7]
+#define IDENTIFY_BASE 0x80
+#define IDENTIFY(can_disconnect, lun) (IDENTIFY_BASE |\
+ ((can_disconnect) ? 0x40 : 0) |\
+ ((lun) & 0x07))
+#define MAX_SCSI_DEVICE_CODE 10
+extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
+
+
+
+/*
+ * the return of the status word will be in the following format :
+ * The low byte is the status returned by the SCSI command,
+ * with vendor specific bits masked.
+ *
+ * The next byte is the message which followed the SCSI status.
+ * This allows a stos to be used, since the Intel is a little
+ * endian machine.
+ *
+ * The final byte is a host return code, which is one of the following.
+ *
+ * IE
+ * lsb msb
+ * status msg host code
+ *
+ * Our errors returned by OUR driver, NOT SCSI message. Or'd with
+ * SCSI message passed back to driver <IF any>.
+ */
+
+
+#define DID_OK 0x00 /* NO error */
+#define DID_NO_CONNECT 0x01 /* Couldn't connect before timeout period */
+#define DID_BUS_BUSY 0x02 /* BUS stayed busy through time out period */
+#define DID_TIME_OUT 0x03 /* TIMED OUT for other reason */
+#define DID_BAD_TARGET 0x04 /* BAD target. */
+#define DID_ABORT 0x05 /* Told to abort for some other reason */
+#define DID_PARITY 0x06 /* Parity error */
+#define DID_ERROR 0x07 /* Internal error */
+#define DID_RESET 0x08 /* Reset by somebody. */
+#define DID_BAD_INTR 0x09 /* Got an interrupt we weren't expecting. */
+#define DRIVER_OK 0x00 /* Driver status */
+
+/*
+ * These indicate the error that occurred, and what is available.
+ */
+
+#define DRIVER_BUSY 0x01
+#define DRIVER_SOFT 0x02
+#define DRIVER_MEDIA 0x03
+#define DRIVER_ERROR 0x04
+
+#define DRIVER_INVALID 0x05
+#define DRIVER_TIMEOUT 0x06
+#define DRIVER_HARD 0x07
+#define DRIVER_SENSE 0x08
+
+#define SUGGEST_RETRY 0x10
+#define SUGGEST_ABORT 0x20
+#define SUGGEST_REMAP 0x30
+#define SUGGEST_DIE 0x40
+#define SUGGEST_SENSE 0x80
+#define SUGGEST_IS_OK 0xff
+
+#define DRIVER_MASK 0x0f
+#define SUGGEST_MASK 0xf0
+
+#define MAX_COMMAND_SIZE 12
+
+/*
+ * SCSI command sets
+ */
+
+#define SCSI_UNKNOWN 0
+#define SCSI_1 1
+#define SCSI_1_CCS 2
+#define SCSI_2 3
+
+/*
+ * Every SCSI command starts with a one byte OP-code.
+ * The next byte's high three bits are the LUN of the
+ * device. Any multi-byte quantities are stored high byte
+ * first, and may have a 5 bit MSB in the same byte
+ * as the LUN.
+ */
+
+/*
+ * Manufacturers list
+ */
+
+#define SCSI_MAN_UNKNOWN 0
+#define SCSI_MAN_NEC 1
+#define SCSI_MAN_TOSHIBA 2
+#define SCSI_MAN_NEC_OLDCDR 3
+#define SCSI_MAN_SONY 4
+#define SCSI_MAN_PIONEER 5
+
+/*
+ * As the scsi do command functions are intelligent, and may need to
+ * redo a command, we need to keep track of the last command
+ * executed on each one.
+ */
+
+#define WAS_RESET 0x01
+#define WAS_TIMEDOUT 0x02
+#define WAS_SENSE 0x04
+#define IS_RESETTING 0x08
+#define IS_ABORTING 0x10
+#define ASKED_FOR_SENSE 0x20
+
+/*
+ * The scsi_device struct contains what we know about each given scsi
+ * device.
+ */
+
+typedef struct scsi_device {
+ struct scsi_device * next; /* Used for linked list */
+
+ unsigned char id, lun, channel;
+
+ unsigned int manufacturer; /* Manufacturer of device, for using
+ * vendor-specific cmd's */
+ int attached; /* # of high level drivers attached to
+ * this */
+ int access_count; /* Count of open channels/mounts */
+ struct wait_queue * device_wait;/* Used to wait if device is busy */
+ struct Scsi_Host * host;
+ void (*scsi_request_fn)(void); /* Used to jumpstart things after an
+ * ioctl */
+ struct scsi_cmnd *device_queue; /* queue of SCSI Command structures */
+ void *hostdata; /* available to low-level driver */
+ char type;
+ char scsi_level;
+ char vendor[8], model[16], rev[4];
+ unsigned char current_tag; /* current tag */
+ unsigned char sync_min_period; /* Not less than this period */
+ unsigned char sync_max_offset; /* Not greater than this offset */
+ unsigned char queue_depth; /* How deep a queue to use */
+
+ unsigned writeable:1;
+ unsigned removable:1;
+ unsigned random:1;
+ unsigned has_cmdblocks:1;
+ unsigned changed:1; /* Data invalid due to media change */
+ unsigned busy:1; /* Used to prevent races */
+ unsigned lockable:1; /* Able to prevent media removal */
+ unsigned borken:1; /* Tell the Seagate driver to be
+ * painfully slow on this device */
+ unsigned tagged_supported:1; /* Supports SCSI-II tagged queuing */
+ unsigned tagged_queue:1; /* SCSI-II tagged queuing enabled */
+ unsigned disconnect:1; /* can disconnect */
+ unsigned soft_reset:1; /* Uses soft reset option */
+ unsigned sync:1; /* Negotiate for sync transfers */
+ unsigned single_lun:1; /* Indicates we should only allow I/O to
+ * one of the luns for the device at a
+ * time. */
+ unsigned was_reset:1; /* There was a bus reset on the bus for
+ * this device */
+ unsigned expecting_cc_ua:1; /* Expecting a CHECK_CONDITION/UNIT_ATTN
+ * because we did a bus reset. */
+} Scsi_Device;
+
+/*
+ * Use these to separate status msg and our bytes
+ */
+
+#define status_byte(result) (((result) >> 1) & 0x1f)
+#define msg_byte(result) (((result) >> 8) & 0xff)
+#define host_byte(result) (((result) >> 16) & 0xff)
+#define driver_byte(result) (((result) >> 24) & 0xff)
+#define suggestion(result) (driver_byte(result) & SUGGEST_MASK)
+
+#define sense_class(sense) (((sense) >> 4) & 0x7)
+#define sense_error(sense) ((sense) & 0xf)
+#define sense_valid(sense) ((sense) & 0x80);
+
+/*
+ * These are the SCSI devices available on the system.
+ */
+
+extern Scsi_Device * scsi_devices;
+
+extern struct hd_struct * sd;
+
+#if defined(MAJOR_NR) && (MAJOR_NR == SCSI_DISK_MAJOR)
+extern struct hd_struct * sd;
+#endif
+
+/*
+ * Initializes all SCSI devices. This scans all scsi busses.
+ */
+
+extern int scsi_dev_init (void);
+
+struct scatterlist {
+ char * address; /* Location data is to be transferred to */
+ char * alt_address; /* Location of actual if address is a
+ * dma indirect buffer. NULL otherwise */
+ unsigned int length;
+};
+
+#ifdef __alpha__
+# define ISA_DMA_THRESHOLD (~0UL)
+#else
+# define ISA_DMA_THRESHOLD (0x00ffffff)
+#endif
+#define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data)
+
+
+/*
+ * These are the return codes for the abort and reset functions. The mid-level
+ * code uses these to decide what to do next. Each of the low level abort
+ * and reset functions must correctly indicate what it has done.
+ * The descriptions are written from the point of view of the mid-level code,
+ * so that the return code is telling the mid-level drivers exactly what
+ * the low level driver has already done, and what remains to be done.
+ */
+
+/* We did not do anything.
+ * Wait some more for this command to complete, and if this does not work,
+ * try something more serious. */
+#define SCSI_ABORT_SNOOZE 0
+
+/* This means that we were able to abort the command. We have already
+ * called the mid-level done function, and do not expect an interrupt that
+ * will lead to another call to the mid-level done function for this command */
+#define SCSI_ABORT_SUCCESS 1
+
+/* We called for an abort of this command, and we should get an interrupt
+ * when this succeeds. Thus we should not restore the timer for this
+ * command in the mid-level abort function. */
+#define SCSI_ABORT_PENDING 2
+
+/* Unable to abort - command is currently on the bus. Grin and bear it. */
+#define SCSI_ABORT_BUSY 3
+
+/* The command is not active in the low level code. Command probably
+ * finished. */
+#define SCSI_ABORT_NOT_RUNNING 4
+
+/* Something went wrong. The low level driver will indicate the correct
+ * error condition when it calls scsi_done, so the mid-level abort function
+ * can simply wait until this comes through */
+#define SCSI_ABORT_ERROR 5
+
+/* We do not know how to reset the bus, or we do not want to. Bummer.
+ * Anyway, just wait a little more for the command in question, and hope that
+ * it eventually finishes. If it never finishes, the SCSI device could
+ * hang, so use this with caution. */
+#define SCSI_RESET_SNOOZE 0
+
+/* We do not know how to reset the bus, or we do not want to. Bummer.
+ * We have given up on this ever completing. The mid-level code will
+ * request sense information to decide how to proceed from here. */
+#define SCSI_RESET_PUNT 1
+
+/* This means that we were able to reset the bus. We have restarted all of
+ * the commands that should be restarted, and we should be able to continue
+ * on normally from here. We do not expect any interrupts that will return
+ * DID_RESET to any of the other commands in the host_queue, and the mid-level
+ * code does not need to do anything special to keep the commands alive.
+ * If a hard reset was performed then all outstanding commands on the
+ * bus have been restarted. */
+#define SCSI_RESET_SUCCESS 2
+
+/* We called for a reset of this bus, and we should get an interrupt
+ * when this succeeds. Each command should get its own status
+ * passed up to scsi_done, but this has not happened yet.
+ * If a hard reset was performed, then we expect an interrupt
+ * for *each* of the outstanding commands that will have the
+ * effect of restarting the commands.
+ */
+#define SCSI_RESET_PENDING 3
+
+/* We did a reset, but do not expect an interrupt to signal DID_RESET.
+ * This tells the upper level code to request the sense info, and this
+ * should keep the command alive. */
+#define SCSI_RESET_WAKEUP 4
+
+/* The command is not active in the low level code. Command probably
+ finished. */
+#define SCSI_RESET_NOT_RUNNING 5
+
+/* Something went wrong, and we do not know how to fix it. */
+#define SCSI_RESET_ERROR 6
+
+#define SCSI_RESET_SYNCHRONOUS 0x01
+#define SCSI_RESET_ASYNCHRONOUS 0x02
+#define SCSI_RESET_SUGGEST_BUS_RESET 0x04
+#define SCSI_RESET_SUGGEST_HOST_RESET 0x08
+/*
+ * This is a bitmask that is ored with one of the above codes.
+ * It tells the mid-level code that we did a hard reset.
+ */
+#define SCSI_RESET_BUS_RESET 0x100
+/*
+ * This is a bitmask that is ored with one of the above codes.
+ * It tells the mid-level code that we did a host adapter reset.
+ */
+#define SCSI_RESET_HOST_RESET 0x200
+/*
+ * Used to mask off bits and to obtain the basic action that was
+ * performed.
+ */
+#define SCSI_RESET_ACTION 0xff
+
+void * scsi_malloc(unsigned int);
+int scsi_free(void *, unsigned int);
+extern unsigned int dma_free_sectors; /* How much room do we have left */
+extern unsigned int need_isa_buffer; /* True if some devices need indirection
+ * buffers */
+
+/*
+ * The Scsi_Cmnd structure is used by scsi.c internally, and for communication
+ * with low level drivers that support multiple outstanding commands.
+ */
+typedef struct scsi_pointer {
+ char * ptr; /* data pointer */
+ int this_residual; /* left in this buffer */
+ struct scatterlist *buffer; /* which buffer */
+ int buffers_residual; /* how many buffers left */
+
+ volatile int Status;
+ volatile int Message;
+ volatile int have_data_in;
+ volatile int sent_command;
+ volatile int phase;
+} Scsi_Pointer;
+
+typedef struct scsi_cmnd {
+ struct Scsi_Host * host;
+ Scsi_Device * device;
+ unsigned char target, lun, channel;
+ unsigned char cmd_len;
+ unsigned char old_cmd_len;
+ struct scsi_cmnd *next, *prev, *device_next, *reset_chain;
+
+ /* These elements define the operation we are about to perform */
+ unsigned char cmnd[12];
+ unsigned request_bufflen; /* Actual request size */
+
+ void * request_buffer; /* Actual requested buffer */
+
+ /* These elements define the operation we ultimately want to perform */
+ unsigned char data_cmnd[12];
+ unsigned short old_use_sg; /* We save use_sg here when requesting
+ * sense info */
+ unsigned short use_sg; /* Number of pieces of scatter-gather */
+ unsigned short sglist_len; /* size of malloc'd scatter-gather list */
+ unsigned short abort_reason;/* If the mid-level code requests an
+ * abort, this is the reason. */
+ unsigned bufflen; /* Size of data buffer */
+ void *buffer; /* Data buffer */
+
+ unsigned underflow; /* Return error if less than this amount is
+ * transfered */
+
+ unsigned transfersize; /* How much we are guaranteed to transfer with
+ * each SCSI transfer (ie, between disconnect /
+ * reconnects. Probably == sector size */
+
+
+ struct request request; /* A copy of the command we are working on */
+
+ unsigned char sense_buffer[16]; /* Sense for this command, if needed */
+
+ /*
+ A SCSI Command is assigned a nonzero serial_number when internal_cmnd
+ passes it to the driver's queue command function. The serial_number
+ is cleared when scsi_done is entered indicating that the command has
+ been completed. If a timeout occurs, the serial number at the moment
+ of timeout is copied into serial_number_at_timeout. By subsequently
+ comparing the serial_number and serial_number_at_timeout fields
+ during abort or reset processing, we can detect whether the command
+ has already completed. This also detects cases where the command has
+ completed and the SCSI Command structure has already being reused
+ for another command, so that we can avoid incorrectly aborting or
+ resetting the new command.
+ */
+
+ unsigned long serial_number;
+ unsigned long serial_number_at_timeout;
+
+ int retries;
+ int allowed;
+ int timeout_per_command, timeout_total, timeout;
+
+ /*
+ * We handle the timeout differently if it happens when a reset,
+ * abort, etc are in process.
+ */
+ unsigned volatile char internal_timeout;
+
+ unsigned flags;
+
+ /* These variables are for the cdrom only. Once we have variable size
+ * buffers in the buffer cache, they will go away. */
+ int this_count;
+ /* End of special cdrom variables */
+
+ /* Low-level done function - can be used by low-level driver to point
+ * to completion function. Not used by mid/upper level code. */
+ void (*scsi_done)(struct scsi_cmnd *);
+ void (*done)(struct scsi_cmnd *); /* Mid-level done function */
+
+ /*
+ * The following fields can be written to by the host specific code.
+ * Everything else should be left alone.
+ */
+
+ Scsi_Pointer SCp; /* Scratchpad used by some host adapters */
+
+ unsigned char * host_scribble; /* The host adapter is allowed to
+ * call scsi_malloc and get some memory
+ * and hang it here. The host adapter
+ * is also expected to call scsi_free
+ * to release this memory. (The memory
+ * obtained by scsi_malloc is guaranteed
+ * to be at an address < 16Mb). */
+
+ int result; /* Status code from lower level driver */
+
+ unsigned char tag; /* SCSI-II queued command tag */
+ unsigned long pid; /* Process ID, starts at 0 */
+} Scsi_Cmnd;
+
+/*
+ * scsi_abort aborts the current command that is executing on host host.
+ * The error code, if non zero is returned in the host byte, otherwise
+ * DID_ABORT is returned in the hostbyte.
+ */
+
+extern int scsi_abort (Scsi_Cmnd *, int code);
+
+extern void scsi_do_cmd (Scsi_Cmnd *, const void *cmnd ,
+ void *buffer, unsigned bufflen,
+ void (*done)(struct scsi_cmnd *),
+ int timeout, int retries);
+
+
+extern Scsi_Cmnd * allocate_device(struct request **, Scsi_Device *, int);
+
+extern Scsi_Cmnd * request_queueable(struct request *, Scsi_Device *);
+extern int scsi_reset (Scsi_Cmnd *, unsigned int);
+
+extern int max_scsi_hosts;
+
+extern void proc_print_scsidevice(Scsi_Device *, char *, int *, int);
+
+extern void print_command(unsigned char *);
+extern void print_sense(const char *, Scsi_Cmnd *);
+extern void print_driverbyte(int scsiresult);
+extern void print_hostbyte(int scsiresult);
+
+extern void scsi_mark_host_reset(struct Scsi_Host *Host);
+extern void scsi_mark_bus_reset(struct Scsi_Host *Host, int channel);
+
+#if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR)
+#include "hosts.h"
+
+static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors)
+{
+ struct request * req;
+ struct buffer_head * bh;
+
+ req = &SCpnt->request;
+ req->errors = 0;
+ if (!uptodate) {
+#if defined(MAJOR_NR) && (MAJOR_NR == SCSI_DISK_MAJOR)
+ printk(DEVICE_NAME " I/O error: dev %s, sector %lu, absolute sector %lu\n",
+ kdevname(req->rq_dev), req->sector,
+ req->sector + sd[MINOR(SCpnt->request.rq_dev)].start_sect);
+#else
+ printk(DEVICE_NAME " I/O error: dev %s, sector %lu\n",
+ kdevname(req->rq_dev), req->sector);
+#endif
+ }
+
+ do {
+ if ((bh = req->bh) != NULL) {
+ req->bh = bh->b_reqnext;
+ req->nr_sectors -= bh->b_size >> 9;
+ req->sector += bh->b_size >> 9;
+ bh->b_reqnext = NULL;
+ /*
+ * This is our 'MD IO has finished' event handler.
+ * note that b_state should be cached in a register
+ * anyways, so the overhead if this checking is almost
+ * zero. But anyways .. we never get OO for free :)
+ */
+ if (test_bit(BH_MD, &bh->b_state)) {
+ struct md_personality * pers=(struct md_personality *)bh->personality;
+ pers->end_request(bh,uptodate);
+ }
+ /*
+ * the normal (nonmirrored and no RAID5) case:
+ */
+ else {
+ mark_buffer_uptodate(bh, uptodate);
+ unlock_buffer(bh);
+ }
+ sectors -= bh->b_size >> 9;
+ if ((bh = req->bh) != NULL) {
+ req->current_nr_sectors = bh->b_size >> 9;
+ if (req->nr_sectors < req->current_nr_sectors) {
+ req->nr_sectors = req->current_nr_sectors;
+ printk("end_scsi_request: buffer-list destroyed\n");
+ }
+ }
+ }
+ } while(sectors && bh);
+ if (req->bh){
+ req->buffer = bh->b_data;
+ return SCpnt;
+ }
+ DEVICE_OFF(req->rq_dev);
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+ add_blkdev_randomness(MAJOR(req->rq_dev));
+
+ if (SCpnt->host->block) {
+ struct Scsi_Host * next;
+
+ for (next = SCpnt->host->block; next != SCpnt->host;
+ next = next->block)
+ wake_up(&next->host_wait);
+ }
+
+ req->rq_status = RQ_INACTIVE;
+ wake_up(&wait_for_request);
+ wake_up(&SCpnt->device->device_wait);
+ return NULL;
+}
+
+
+/* This is just like INIT_REQUEST, but we need to be aware of the fact
+ * that an interrupt may start another request, so we run this with interrupts
+ * turned off
+ */
+#define INIT_SCSI_REQUEST \
+ if (!CURRENT) { \
+ CLEAR_INTR; \
+ restore_flags(flags); \
+ return; \
+ } \
+ if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) \
+ panic(DEVICE_NAME ": request list destroyed");\
+ if (CURRENT->bh) { \
+ if (!buffer_locked(CURRENT->bh)) \
+ panic(DEVICE_NAME ": block not locked"); \
+ }
+#endif
+
+#define SCSI_SLEEP(QUEUE, CONDITION) { \
+ if (CONDITION) { \
+ struct wait_queue wait = { current, NULL}; \
+ add_wait_queue(QUEUE, &wait); \
+ for(;;) { \
+ current->state = TASK_UNINTERRUPTIBLE; \
+ if (CONDITION) { \
+ if (intr_count) \
+ panic("scsi: trying to call schedule() in interrupt" \
+ ", file %s, line %d.\n", __FILE__, __LINE__); \
+ schedule(); \
+ } \
+ else \
+ break; \
+ } \
+ remove_wait_queue(QUEUE, &wait);\
+ current->state = TASK_RUNNING; \
+ }; }
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/scsi_ioctl.c b/linux/src/drivers/scsi/scsi_ioctl.c
new file mode 100644
index 00000000..76918595
--- /dev/null
+++ b/linux/src/drivers/scsi/scsi_ioctl.c
@@ -0,0 +1,452 @@
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/page.h>
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include <scsi/scsi_ioctl.h>
+
+#define NORMAL_RETRIES 5
+#define NORMAL_TIMEOUT (10 * HZ)
+#define FORMAT_UNIT_TIMEOUT (2 * 60 * 60 * HZ)
+#define START_STOP_TIMEOUT (60 * HZ)
+#define MOVE_MEDIUM_TIMEOUT (5 * 60 * HZ)
+#define READ_ELEMENT_STATUS_TIMEOUT (5 * 60 * HZ)
+
+#define MAX_BUF PAGE_SIZE
+
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+/*
+ * If we are told to probe a host, we will return 0 if the host is not
+ * present, 1 if the host is present, and will return an identifying
+ * string at *arg, if arg is non null, filling to the length stored at
+ * (int *) arg
+ */
+
+static int ioctl_probe(struct Scsi_Host * host, void *buffer)
+{
+ int temp, result;
+ unsigned int len,slen;
+ const char * string;
+
+ if ((temp = host->hostt->present) && buffer) {
+ result = verify_area(VERIFY_READ, buffer, sizeof(long));
+ if (result) return result;
+
+ len = get_user ((unsigned int *) buffer);
+ if(host->hostt->info)
+ string = host->hostt->info(host);
+ else
+ string = host->hostt->name;
+ if(string) {
+ slen = strlen(string);
+ if (len > slen)
+ len = slen + 1;
+ result = verify_area(VERIFY_WRITE, buffer, len);
+ if (result) return result;
+
+ memcpy_tofs (buffer, string, len);
+ }
+ }
+ return temp;
+}
+
+/*
+ *
+ * The SCSI_IOCTL_SEND_COMMAND ioctl sends a command out to the SCSI host.
+ * The NORMAL_TIMEOUT and NORMAL_RETRIES variables are used.
+ *
+ * dev is the SCSI device struct ptr, *(int *) arg is the length of the
+ * input data, if any, not including the command string & counts,
+ * *((int *)arg + 1) is the output buffer size in bytes.
+ *
+ * *(char *) ((int *) arg)[2] the actual command byte.
+ *
+ * Note that if more than MAX_BUF bytes are requested to be transfered,
+ * the ioctl will fail with error EINVAL. MAX_BUF can be increased in
+ * the future by increasing the size that scsi_malloc will accept.
+ *
+ * This size *does not* include the initial lengths that were passed.
+ *
+ * The SCSI command is read from the memory location immediately after the
+ * length words, and the input data is right after the command. The SCSI
+ * routines know the command size based on the opcode decode.
+ *
+ * The output area is then filled in starting from the command byte.
+ */
+
+static void scsi_ioctl_done (Scsi_Cmnd * SCpnt)
+{
+ struct request * req;
+
+ req = &SCpnt->request;
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+}
+
+static int ioctl_internal_command(Scsi_Device *dev, char * cmd,
+ int timeout, int retries)
+{
+ int result;
+ Scsi_Cmnd * SCpnt;
+
+ SCpnt = allocate_device(NULL, dev, 1);
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd(SCpnt, cmd, NULL, 0, scsi_ioctl_done, timeout, retries);
+ down(&sem);
+ }
+
+ if(driver_byte(SCpnt->result) != 0)
+ switch(SCpnt->sense_buffer[2] & 0xf) {
+ case ILLEGAL_REQUEST:
+ if(cmd[0] == ALLOW_MEDIUM_REMOVAL) dev->lockable = 0;
+ else printk("SCSI device (ioctl) reports ILLEGAL REQUEST.\n");
+ break;
+ case NOT_READY: /* This happens if there is no disc in drive */
+ if(dev->removable){
+ printk(KERN_INFO "Device not ready. Make sure there is a disc in the drive.\n");
+ break;
+ };
+ case UNIT_ATTENTION:
+ if (dev->removable){
+ dev->changed = 1;
+ SCpnt->result = 0; /* This is no longer considered an error */
+ printk(KERN_INFO "Disc change detected.\n");
+ break;
+ };
+ default: /* Fall through for non-removable media */
+ printk("SCSI error: host %d id %d lun %d return code = %x\n",
+ dev->host->host_no,
+ dev->id,
+ dev->lun,
+ SCpnt->result);
+ printk("\tSense class %x, sense error %x, extended sense %x\n",
+ sense_class(SCpnt->sense_buffer[0]),
+ sense_error(SCpnt->sense_buffer[0]),
+ SCpnt->sense_buffer[2] & 0xf);
+
+ };
+
+ result = SCpnt->result;
+ SCpnt->request.rq_status = RQ_INACTIVE;
+
+ if (!SCpnt->device->was_reset && SCpnt->device->scsi_request_fn)
+ (*SCpnt->device->scsi_request_fn)();
+
+ wake_up(&SCpnt->device->device_wait);
+ return result;
+}
+
+/*
+ * This interface is depreciated - users should use the scsi generics
+ * interface instead, as this is a more flexible approach to performing
+ * generic SCSI commands on a device.
+ */
+int scsi_ioctl_send_command(Scsi_Device *dev, void *buffer)
+{
+ char * buf;
+ unsigned char cmd[12];
+ char * cmd_in;
+ Scsi_Cmnd * SCpnt;
+ unsigned char opcode;
+ int inlen, outlen, cmdlen;
+ int needed, buf_needed;
+ int timeout, retries, result;
+
+ if (!buffer)
+ return -EINVAL;
+
+
+ /*
+ * Verify that we can read at least this much.
+ */
+ result = verify_area(VERIFY_READ, buffer, 2*sizeof(long) + 1);
+ if (result) return result;
+
+ /*
+ * The structure that we are passed should look like:
+ *
+ * struct sdata {
+ * unsigned int inlen;
+ * unsigned int outlen;
+ * unsigned char cmd[]; # However many bytes are used for cmd.
+ * unsigned char data[];
+ * };
+ */
+ inlen = get_user((unsigned int *) buffer);
+ outlen = get_user( ((unsigned int *) buffer) + 1);
+
+ /*
+ * We do not transfer more than MAX_BUF with this interface.
+ * If the user needs to transfer more data than this, they
+ * should use scsi_generics instead.
+ */
+ if( inlen > MAX_BUF ) return -EINVAL;
+ if( outlen > MAX_BUF ) return -EINVAL;
+
+ cmd_in = (char *) ( ((int *)buffer) + 2);
+ opcode = get_user(cmd_in);
+
+ needed = buf_needed = (inlen > outlen ? inlen : outlen);
+ if(buf_needed){
+ buf_needed = (buf_needed + 511) & ~511;
+ if (buf_needed > MAX_BUF) buf_needed = MAX_BUF;
+ buf = (char *) scsi_malloc(buf_needed);
+ if (!buf) return -ENOMEM;
+ memset(buf, 0, buf_needed);
+ } else
+ buf = NULL;
+
+ /*
+ * Obtain the command from the user's address space.
+ */
+ cmdlen = COMMAND_SIZE(opcode);
+
+ result = verify_area(VERIFY_READ, cmd_in,
+ cmdlen + inlen > MAX_BUF ? MAX_BUF : inlen);
+ if (result) return result;
+
+ memcpy_fromfs ((void *) cmd, cmd_in, cmdlen);
+
+ /*
+ * Obtain the data to be sent to the device (if any).
+ */
+ memcpy_fromfs ((void *) buf,
+ (void *) (cmd_in + cmdlen),
+ inlen);
+
+ /*
+ * Set the lun field to the correct value.
+ */
+ cmd[1] = ( cmd[1] & 0x1f ) | (dev->lun << 5);
+
+ switch (opcode)
+ {
+ case FORMAT_UNIT:
+ timeout = FORMAT_UNIT_TIMEOUT;
+ retries = 1;
+ break;
+ case START_STOP:
+ timeout = START_STOP_TIMEOUT;
+ retries = NORMAL_RETRIES;
+ break;
+ case MOVE_MEDIUM:
+ timeout = MOVE_MEDIUM_TIMEOUT;
+ retries = NORMAL_RETRIES;
+ break;
+ case READ_ELEMENT_STATUS:
+ timeout = READ_ELEMENT_STATUS_TIMEOUT;
+ retries = NORMAL_RETRIES;
+ break;
+ default:
+ timeout = NORMAL_TIMEOUT;
+ retries = NORMAL_RETRIES;
+ break;
+ }
+
+#ifndef DEBUG_NO_CMD
+
+ SCpnt = allocate_device(NULL, dev, 1);
+
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd(SCpnt, cmd, buf, needed, scsi_ioctl_done,
+ timeout, retries);
+ down(&sem);
+ }
+
+ /*
+ * If there was an error condition, pass the info back to the user.
+ */
+ if(SCpnt->result) {
+ result = verify_area(VERIFY_WRITE,
+ cmd_in,
+ sizeof(SCpnt->sense_buffer));
+ if (result) return result;
+ memcpy_tofs((void *) cmd_in,
+ SCpnt->sense_buffer,
+ sizeof(SCpnt->sense_buffer));
+ } else {
+ result = verify_area(VERIFY_WRITE, cmd_in, outlen);
+ if (result) return result;
+ memcpy_tofs ((void *) cmd_in, buf, outlen);
+ }
+ result = SCpnt->result;
+
+ SCpnt->request.rq_status = RQ_INACTIVE;
+
+ if (buf) scsi_free(buf, buf_needed);
+
+ if(SCpnt->device->scsi_request_fn)
+ (*SCpnt->device->scsi_request_fn)();
+
+ wake_up(&SCpnt->device->device_wait);
+ return result;
+#else
+ {
+ int i;
+ printk("scsi_ioctl : device %d. command = ", dev->id);
+ for (i = 0; i < 12; ++i)
+ printk("%02x ", cmd[i]);
+ printk("\nbuffer =");
+ for (i = 0; i < 20; ++i)
+ printk("%02x ", buf[i]);
+ printk("\n");
+ printk("inlen = %d, outlen = %d, cmdlen = %d\n",
+ inlen, outlen, cmdlen);
+ printk("buffer = %d, cmd_in = %d\n", buffer, cmd_in);
+ }
+ return 0;
+#endif
+}
+
+/*
+ * the scsi_ioctl() function differs from most ioctls in that it does
+ * not take a major/minor number as the dev field. Rather, it takes
+ * a pointer to a scsi_devices[] element, a structure.
+ */
+int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg)
+{
+ int result;
+ char scsi_cmd[12];
+
+ /* No idea how this happens.... */
+ if (!dev) return -ENXIO;
+
+ switch (cmd) {
+ case SCSI_IOCTL_GET_IDLUN:
+ result = verify_area(VERIFY_WRITE, (void *) arg, 2*sizeof(long));
+ if (result) return result;
+
+ put_user(dev->id
+ + (dev->lun << 8)
+ + (dev->channel << 16)
+ + ((dev->host->hostt->proc_dir->low_ino & 0xff) << 24),
+ (unsigned long *) arg);
+ put_user( dev->host->unique_id, (unsigned long *) arg+1);
+ return 0;
+ case SCSI_IOCTL_GET_BUS_NUMBER:
+ result = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
+ if (result) return result;
+ put_user( dev->host->host_no, (int *) arg);
+ return 0;
+ case SCSI_IOCTL_TAGGED_ENABLE:
+ if(!suser()) return -EACCES;
+ if(!dev->tagged_supported) return -EINVAL;
+ dev->tagged_queue = 1;
+ dev->current_tag = 1;
+ return 0;
+ case SCSI_IOCTL_TAGGED_DISABLE:
+ if(!suser()) return -EACCES;
+ if(!dev->tagged_supported) return -EINVAL;
+ dev->tagged_queue = 0;
+ dev->current_tag = 0;
+ return 0;
+ case SCSI_IOCTL_PROBE_HOST:
+ return ioctl_probe(dev->host, arg);
+ case SCSI_IOCTL_SEND_COMMAND:
+ if(!suser() || securelevel > 0) return -EACCES;
+ return scsi_ioctl_send_command((Scsi_Device *) dev, arg);
+ case SCSI_IOCTL_DOORLOCK:
+ if (!dev->removable || !dev->lockable) return 0;
+ scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
+ scsi_cmd[1] = dev->lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = SCSI_REMOVAL_PREVENT;
+ return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+ NORMAL_TIMEOUT, NORMAL_RETRIES);
+ break;
+ case SCSI_IOCTL_DOORUNLOCK:
+ if (!dev->removable || !dev->lockable) return 0;
+ scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
+ scsi_cmd[1] = dev->lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = SCSI_REMOVAL_ALLOW;
+ return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+ NORMAL_TIMEOUT, NORMAL_RETRIES);
+ case SCSI_IOCTL_TEST_UNIT_READY:
+ scsi_cmd[0] = TEST_UNIT_READY;
+ scsi_cmd[1] = dev->lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = 0;
+ return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+ NORMAL_TIMEOUT, NORMAL_RETRIES);
+ break;
+ case SCSI_IOCTL_START_UNIT:
+ scsi_cmd[0] = START_STOP;
+ scsi_cmd[1] = dev->lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = 1;
+ return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+ START_STOP_TIMEOUT, NORMAL_RETRIES);
+ break;
+ case SCSI_IOCTL_STOP_UNIT:
+ scsi_cmd[0] = START_STOP;
+ scsi_cmd[1] = dev->lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = 0;
+ return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+ START_STOP_TIMEOUT, NORMAL_RETRIES);
+ break;
+ default :
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Just like scsi_ioctl, only callable from kernel space with no
+ * fs segment fiddling.
+ */
+
+int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg) {
+ unsigned long oldfs;
+ int tmp;
+ oldfs = get_fs();
+ set_fs(get_ds());
+ tmp = scsi_ioctl (dev, cmd, arg);
+ set_fs(oldfs);
+ return tmp;
+}
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/scsi_proc.c b/linux/src/drivers/scsi/scsi_proc.c
new file mode 100644
index 00000000..d1fa28d0
--- /dev/null
+++ b/linux/src/drivers/scsi/scsi_proc.c
@@ -0,0 +1,302 @@
+/*
+ * linux/drivers/scsi/scsi_proc.c
+ *
+ * The functions in this file provide an interface between
+ * the PROC file system and the SCSI device drivers
+ * It is mainly used for debugging, statistics and to pass
+ * information directly to the lowlevel driver.
+ *
+ * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
+ * Version: 0.99.8 last change: 95/09/13
+ *
+ * generic command parser provided by:
+ * Andreas Heilwagen <crashcar@informatik.uni-koblenz.de>
+ */
+
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+extern int scsi_proc_info(char *, char **, off_t, int, int, int);
+
+struct scsi_dir {
+ struct proc_dir_entry entry;
+ char name[4];
+};
+
+
+/* generic_proc_info
+ * Used if the driver currently has no own support for /proc/scsi
+ */
+int generic_proc_info(char *buffer, char **start, off_t offset,
+ int length, int inode, int inout)
+{
+ int len, pos, begin;
+
+ if(inout == TRUE)
+ return(-ENOSYS); /* This is a no-op */
+
+ begin = 0;
+ pos = len = sprintf(buffer,
+ "The driver does not yet support the proc-fs\n");
+ if(pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin);
+ if(len > length)
+ len = length;
+
+ return(len);
+}
+
+/* dispatch_scsi_info is the central dispatcher
+ * It is the interface between the proc-fs and the SCSI subsystem code
+ */
+extern int dispatch_scsi_info(int ino, char *buffer, char **start,
+ off_t offset, int length, int func)
+{
+ struct Scsi_Host *hpnt = scsi_hostlist;
+
+ if(ino == PROC_SCSI_SCSI) {
+ /*
+ * This is for the scsi core, rather than any specific
+ * lowlevel driver.
+ */
+ return(scsi_proc_info(buffer, start, offset, length, 0, func));
+ }
+
+ while(hpnt) {
+ if (ino == (hpnt->host_no + PROC_SCSI_FILE)) {
+ if(hpnt->hostt->proc_info == NULL)
+ return generic_proc_info(buffer, start, offset, length,
+ hpnt->host_no, func);
+ else
+ return(hpnt->hostt->proc_info(buffer, start, offset,
+ length, hpnt->host_no, func));
+ }
+ hpnt = hpnt->next;
+ }
+ return(-EBADF);
+}
+
+void build_proc_dir_entries(Scsi_Host_Template *tpnt)
+{
+ struct Scsi_Host *hpnt;
+
+ struct scsi_dir *scsi_hba_dir;
+
+ proc_scsi_register(0, tpnt->proc_dir);
+
+ hpnt = scsi_hostlist;
+ while (hpnt) {
+ if (tpnt == hpnt->hostt) {
+ scsi_hba_dir = scsi_init_malloc(sizeof(struct scsi_dir), GFP_KERNEL);
+ if(scsi_hba_dir == NULL)
+ panic("Not enough memory to register SCSI HBA in /proc/scsi !\n");
+ memset(scsi_hba_dir, 0, sizeof(struct scsi_dir));
+ scsi_hba_dir->entry.low_ino = PROC_SCSI_FILE + hpnt->host_no;
+ scsi_hba_dir->entry.namelen = sprintf(scsi_hba_dir->name,"%d",
+ hpnt->host_no);
+ scsi_hba_dir->entry.name = scsi_hba_dir->name;
+ scsi_hba_dir->entry.mode = S_IFREG | S_IRUGO | S_IWUSR;
+ proc_scsi_register(tpnt->proc_dir, &scsi_hba_dir->entry);
+ }
+ hpnt = hpnt->next;
+ }
+}
+
+/*
+ * parseHandle *parseInit(char *buf, char *cmdList, int cmdNum);
+ * gets a pointer to a null terminated data buffer
+ * and a list of commands with blanks as delimiter
+ * in between.
+ * The commands have to be alphanumerically sorted.
+ * cmdNum has to contain the number of commands.
+ * On success, a pointer to a handle structure
+ * is returned, NULL on failure
+ *
+ * int parseOpt(parseHandle *handle, char **param);
+ * processes the next parameter. On success, the
+ * index of the appropriate command in the cmdList
+ * is returned, starting with zero.
+ * param points to the null terminated parameter string.
+ * On failure, -1 is returned.
+ *
+ * The databuffer buf may only contain pairs of commands
+ * options, separated by blanks:
+ * <Command> <Parameter> [<Command> <Parameter>]*
+ */
+
+typedef struct
+{
+ char *buf, /* command buffer */
+ *cmdList, /* command list */
+ *bufPos, /* actual position */
+ **cmdPos, /* cmdList index */
+ cmdNum; /* cmd number */
+} parseHandle;
+
+
+inline int parseFree (parseHandle *handle) /* free memory */
+{
+ kfree (handle->cmdPos);
+ kfree (handle);
+
+ return(-1);
+}
+
+
+parseHandle *parseInit(char *buf, char *cmdList, int cmdNum)
+{
+ char *ptr; /* temp pointer */
+ parseHandle *handle; /* new handle */
+
+ if (!buf || !cmdList) /* bad input ? */
+ return(NULL);
+ if ((handle = (parseHandle*) kmalloc(sizeof(parseHandle), GFP_KERNEL)) == 0)
+ return(NULL); /* out of memory */
+ if ((handle->cmdPos = (char**) kmalloc(sizeof(int) * cmdNum, GFP_KERNEL)) == 0) {
+ kfree(handle);
+ return(NULL); /* out of memory */
+ }
+
+ handle->buf = handle->bufPos = buf; /* init handle */
+ handle->cmdList = cmdList;
+ handle->cmdNum = cmdNum;
+
+ handle->cmdPos[cmdNum = 0] = cmdList;
+ for (ptr = cmdList; *ptr; ptr++) { /* scan command string */
+ if(*ptr == ' ') { /* and insert zeroes */
+ *ptr++ = 0;
+ handle->cmdPos[++cmdNum] = ptr++;
+ }
+ }
+ return(handle);
+}
+
+
+int parseOpt(parseHandle *handle, char **param)
+{
+ int cmdIndex = 0,
+ cmdLen = 0;
+ char *startPos;
+
+ if (!handle) /* invalid handle */
+ return(parseFree(handle));
+ /* skip spaces */
+ for (; *(handle->bufPos) && *(handle->bufPos) == ' '; handle->bufPos++);
+ if (!*(handle->bufPos))
+ return(parseFree(handle)); /* end of data */
+
+ startPos = handle->bufPos; /* store cmd start */
+ for (; handle->cmdPos[cmdIndex][cmdLen] && *(handle->bufPos); handle->bufPos++)
+ { /* no string end? */
+ for (;;)
+ {
+ if (*(handle->bufPos) == handle->cmdPos[cmdIndex][cmdLen])
+ break; /* char matches ? */
+ else
+ if (memcmp(startPos, (char*)(handle->cmdPos[++cmdIndex]), cmdLen))
+ return(parseFree(handle)); /* unknown command */
+
+ if (cmdIndex >= handle->cmdNum)
+ return(parseFree(handle)); /* unknown command */
+ }
+
+ cmdLen++; /* next char */
+ }
+
+ /* Get param. First skip all blanks, then insert zero after param */
+
+ for (; *(handle->bufPos) && *(handle->bufPos) == ' '; handle->bufPos++);
+ *param = handle->bufPos;
+
+ for (; *(handle->bufPos) && *(handle->bufPos) != ' '; handle->bufPos++);
+ *(handle->bufPos++) = 0;
+
+ return(cmdIndex);
+}
+
+void proc_print_scsidevice(Scsi_Device *scd, char *buffer, int *size, int len)
+{
+ int x, y = *size;
+
+ y = sprintf(buffer + len,
+ "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n Vendor: ",
+ scd->host->host_no, scd->channel, scd->id, scd->lun);
+ for (x = 0; x < 8; x++) {
+ if (scd->vendor[x] >= 0x20)
+ y += sprintf(buffer + len + y, "%c", scd->vendor[x]);
+ else
+ y += sprintf(buffer + len + y," ");
+ }
+ y += sprintf(buffer + len + y, " Model: ");
+ for (x = 0; x < 16; x++) {
+ if (scd->model[x] >= 0x20)
+ y += sprintf(buffer + len + y, "%c", scd->model[x]);
+ else
+ y += sprintf(buffer + len + y, " ");
+ }
+ y += sprintf(buffer + len + y, " Rev: ");
+ for (x = 0; x < 4; x++) {
+ if (scd->rev[x] >= 0x20)
+ y += sprintf(buffer + len + y, "%c", scd->rev[x]);
+ else
+ y += sprintf(buffer + len + y, " ");
+ }
+ y += sprintf(buffer + len + y, "\n");
+
+ y += sprintf(buffer + len + y, " Type: %s ",
+ scd->type < MAX_SCSI_DEVICE_CODE ?
+ scsi_device_types[(int)scd->type] : "Unknown " );
+ y += sprintf(buffer + len + y, " ANSI"
+ " SCSI revision: %02x", (scd->scsi_level < 3)?1:2);
+ if (scd->scsi_level == 2)
+ y += sprintf(buffer + len + y, " CCS\n");
+ else
+ y += sprintf(buffer + len + y, "\n");
+
+ *size = y;
+ return;
+}
+
+/*
+ * Overrides for Emacs so that we get a uniform tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/scsicam.c b/linux/src/drivers/scsi/scsicam.c
new file mode 100644
index 00000000..2c6f6377
--- /dev/null
+++ b/linux/src/drivers/scsi/scsicam.c
@@ -0,0 +1,230 @@
+/*
+ * scsicam.c - SCSI CAM support functions, use for HDIO_GETGEO, etc.
+ *
+ * Copyright 1993, 1994 Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@Colorado.EDU
+ * +1 (303) 786-7975
+ *
+ * For more information, please consult the SCSI-CAM draft.
+ */
+
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/blk.h>
+#include <asm/unaligned.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+static int partsize(struct buffer_head *bh, unsigned long capacity,
+ unsigned int *cyls, unsigned int *hds, unsigned int *secs);
+static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
+ unsigned int *secs);
+
+/*
+ * Function : int scsicam_bios_param (Disk *disk, int dev, int *ip)
+ *
+ * Purpose : to determine the BIOS mapping used for a drive in a
+ * SCSI-CAM system, storing the results in ip as required
+ * by the HDIO_GETGEO ioctl().
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ */
+
+int scsicam_bios_param (Disk *disk, /* SCSI disk */
+ kdev_t dev, /* Device major, minor */
+ int *ip /* Heads, sectors, cylinders in that order */) {
+
+ struct buffer_head *bh;
+ int ret_code;
+ int size = disk->capacity;
+ unsigned long temp_cyl;
+
+ if (!(bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024)))
+ return -1;
+
+ /* try to infer mapping from partition table */
+ ret_code = partsize (bh, (unsigned long) size, (unsigned int *) ip + 2,
+ (unsigned int *) ip + 0, (unsigned int *) ip + 1);
+ brelse (bh);
+
+ if (ret_code == -1) {
+ /* pick some standard mapping with at most 1024 cylinders,
+ and at most 62 sectors per track - this works up to
+ 7905 MB */
+ ret_code = setsize ((unsigned long) size, (unsigned int *) ip + 2,
+ (unsigned int *) ip + 0, (unsigned int *) ip + 1);
+ }
+
+ /* if something went wrong, then apparently we have to return
+ a geometry with more than 1024 cylinders */
+ if (ret_code || ip[0] > 255 || ip[1] > 63) {
+ ip[0] = 64;
+ ip[1] = 32;
+ temp_cyl = size / (ip[0] * ip[1]);
+ if (temp_cyl > 65534) {
+ ip[0] = 255;
+ ip[1] = 63;
+ }
+ ip[2] = size / (ip[0] * ip[1]);
+ }
+
+ return 0;
+}
+
+/*
+ * Function : static int partsize(struct buffer_head *bh, unsigned long
+ * capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs);
+ *
+ * Purpose : to determine the BIOS mapping used to create the partition
+ * table, storing the results in *cyls, *hds, and *secs
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ */
+
+static int partsize(struct buffer_head *bh, unsigned long capacity,
+ unsigned int *cyls, unsigned int *hds, unsigned int *secs) {
+ struct partition *p, *largest = NULL;
+ int i, largest_cyl;
+ int cyl, ext_cyl, end_head, end_cyl, end_sector;
+ unsigned int logical_end, physical_end, ext_physical_end;
+
+
+ if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
+ for (largest_cyl = -1, p = (struct partition *)
+ (0x1BE + bh->b_data), i = 0; i < 4; ++i, ++p) {
+ if (!p->sys_ind)
+ continue;
+#ifdef DEBUG
+ printk ("scsicam_bios_param : partition %d has system \n",
+ i);
+#endif
+ cyl = p->cyl + ((p->sector & 0xc0) << 2);
+ if (cyl > largest_cyl) {
+ largest_cyl = cyl;
+ largest = p;
+ }
+ }
+ }
+
+ if (largest) {
+ end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
+ end_head = largest->end_head;
+ end_sector = largest->end_sector & 0x3f;
+
+ if( end_head + 1 == 0 || end_sector == 0 ) return -1;
+
+#ifdef DEBUG
+ printk ("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
+ end_head, end_cyl, end_sector);
+#endif
+
+ physical_end = end_cyl * (end_head + 1) * end_sector +
+ end_head * end_sector + end_sector;
+
+ /* This is the actual _sector_ number at the end */
+ logical_end = get_unaligned(&largest->start_sect)
+ + get_unaligned(&largest->nr_sects);
+
+ /* This is for >1023 cylinders */
+ ext_cyl= (logical_end-(end_head * end_sector + end_sector))
+ /(end_head + 1) / end_sector;
+ ext_physical_end = ext_cyl * (end_head + 1) * end_sector +
+ end_head * end_sector + end_sector;
+
+#ifdef DEBUG
+ printk("scsicam_bios_param : logical_end=%d physical_end=%d ext_physical_end=%d ext_cyl=%d\n"
+ ,logical_end,physical_end,ext_physical_end,ext_cyl);
+#endif
+
+ if ((logical_end == physical_end) ||
+ (end_cyl==1023 && ext_physical_end==logical_end)) {
+ *secs = end_sector;
+ *hds = end_head + 1;
+ *cyls = capacity / ((end_head + 1) * end_sector);
+ return 0;
+ }
+
+#ifdef DEBUG
+ printk ("scsicam_bios_param : logical (%u) != physical (%u)\n",
+ logical_end, physical_end);
+#endif
+ }
+ return -1;
+}
+
+/*
+ * Function : static int setsize(unsigned long capacity,unsigned int *cyls,
+ * unsigned int *hds, unsigned int *secs);
+ *
+ * Purpose : to determine a near-optimal int 0x13 mapping for a
+ * SCSI disk in terms of lost space of size capacity, storing
+ * the results in *cyls, *hds, and *secs.
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ * Extracted from
+ *
+ * WORKING X3T9.2
+ * DRAFT 792D
+ *
+ *
+ * Revision 6
+ * 10-MAR-94
+ * Information technology -
+ * SCSI-2 Common access method
+ * transport and SCSI interface module
+ *
+ * ANNEX A :
+ *
+ * setsize() converts a read capacity value to int 13h
+ * head-cylinder-sector requirements. It minimizes the value for
+ * number of heads and maximizes the number of cylinders. This
+ * will support rather large disks before the number of heads
+ * will not fit in 4 bits (or 6 bits). This algorithm also
+ * minimizes the number of sectors that will be unused at the end
+ * of the disk while allowing for very large disks to be
+ * accommodated. This algorithm does not use physical geometry.
+ */
+
+static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
+ unsigned int *secs) {
+ unsigned int rv = 0;
+ unsigned long heads, sectors, cylinders, temp;
+
+ cylinders = 1024L; /* Set number of cylinders to max */
+ sectors = 62L; /* Maximize sectors per track */
+
+ temp = cylinders * sectors; /* Compute divisor for heads */
+ heads = capacity / temp; /* Compute value for number of heads */
+ if (capacity % temp) { /* If no remainder, done! */
+ heads++; /* Else, increment number of heads */
+ temp = cylinders * heads; /* Compute divisor for sectors */
+ sectors = capacity / temp; /* Compute value for sectors per
+ track */
+ if (capacity % temp) { /* If no remainder, done! */
+ sectors++; /* Else, increment number of sectors */
+ temp = heads * sectors; /* Compute divisor for cylinders */
+ cylinders = capacity / temp;/* Compute number of cylinders */
+ }
+ }
+ if (cylinders == 0) rv=(unsigned)-1;/* Give error if 0 cylinders */
+
+ *cyls = (unsigned int) cylinders; /* Stuff return values */
+ *secs = (unsigned int) sectors;
+ *hds = (unsigned int) heads;
+ return(rv);
+}
diff --git a/linux/src/drivers/scsi/scsiio.c b/linux/src/drivers/scsi/scsiio.c
new file mode 100644
index 00000000..cea68b8d
--- /dev/null
+++ b/linux/src/drivers/scsi/scsiio.c
@@ -0,0 +1,1537 @@
+/***********************************************************************
+ * FILE NAME : SCSIIO.C *
+ * BY : C.L. Huang *
+ * Description: Device Driver for Tekram DC-390W/U/F (T) PCI SCSI *
+ * Bus Master Host Adapter *
+ ***********************************************************************/
+
+
+static void
+PrepareSG( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+ ULONG retAddr,wlval;
+ USHORT wval,i;
+ PSGL psgl;
+ PSGE psge;
+
+
+ retAddr = pACB->jmp_table8;
+ if(pDCB->DCBscntl3 & EN_WIDE_SCSI)
+ retAddr += jmp_table16;
+ wval = (USHORT)(pSRB->SGcount);
+ wval <<= 4; /* 16 bytes per entry, datain=8, dataout=8 */
+ /* (4 bytes for count, 4 bytes for addr) */
+ retAddr -= (ULONG)wval;
+ pSRB->ReturnAddr = retAddr; /* return address for SCRIPT */
+ if(wval)
+ {
+ wval >>= 1;
+ wlval = (ULONG) pSRB->SegmentPad;
+ wlval -= (ULONG)wval;
+ wval >>= 3;
+ psge = (PSGE) wlval;
+ psgl = pSRB->pSegmentList;
+ for(i=0; i<wval; i++)
+ {
+#ifndef VERSION_ELF_1_2_13
+ psge->SGXPtr = virt_to_phys( psgl->address );
+#else
+ psge->SGXPtr = (ULONG) psgl->address;
+#endif
+ psge->SGXLen = psgl->length;
+ psge++;
+ psgl++;
+ }
+ }
+}
+
+
+static void
+DC390W_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+ USHORT ioport;
+ UCHAR bval;
+
+ pSRB->TagNumber = 31;
+ ioport = pACB->IOPortBase;
+ bval = SIGNAL_PROC;
+ outb(bval,ioport+ISTAT);
+ pACB->pActiveDCB = pDCB;
+ pDCB->pActiveSRB = pSRB;
+ return;
+}
+
+
+#ifndef VERSION_ELF_1_2_13
+static void
+DC390W_Interrupt( int irq, void *dev_id, struct pt_regs *regs)
+#else
+static void
+DC390W_Interrupt( int irq, struct pt_regs *regs)
+#endif
+{
+ PACB pACB;
+ PDCB pDCB;
+ ULONG wlval;
+ USHORT ioport = 0;
+ USHORT wval, i;
+ void (*stateV)( PACB );
+ UCHAR istat = 0;
+ UCHAR bval;
+
+ pACB = pACB_start;
+ if( pACB == NULL )
+ return;
+ for( i=0; i < adapterCnt; i++ )
+ {
+ if( pACB->IRQLevel == (UCHAR) irq )
+ {
+ ioport = pACB->IOPortBase;
+ istat = inb( ioport+ISTAT );
+ if( istat & (ABORT_OP+SCSI_INT_PENDING+DMA_INT_PENDING) )
+ break;
+ else
+ pACB = pACB->pNextACB;
+ }
+ else
+ {
+ pACB = pACB->pNextACB;
+ }
+ }
+
+ if( pACB == (PACB )-1 )
+ {
+ printk("DC390W_intr: Spurious interrupt detected!\n");
+ return;
+ }
+
+
+#ifdef DC390W_DEBUG1
+ printk("Istate=%2x,",istat);
+#endif
+ /* if Abort operation occurred, reset abort bit before reading DMA status
+ to prevent further aborted interrupt. */
+
+ if(istat & ABORT_OP)
+ {
+ istat &= ~ABORT_OP;
+ outb(istat,ioport+ISTAT);
+ }
+
+ pDCB = pACB->pActiveDCB;
+ bval = inb(ioport+CTEST2); /* Clear Signal Bit */
+
+ /* If Scsi Interrupt, then clear Interrupt Status by reading
+ Scsi interrupt status register 0. */
+
+ wlval = 0;
+ if(istat & SCSI_INT_PENDING)
+ {
+ wlval = (ULONG)inw( ioport+SIST0 );
+ wlval <<= 8;
+ }
+
+ /* If DMA Interrupt, then read the DMA status register to see what happen */
+
+ if(istat & DMA_INT_PENDING)
+ {
+ bval = inb(ioport+DSTAT);
+ wlval |= (ULONG) bval;
+ }
+
+#ifdef DC390W_DEBUG1
+ printk("IDstate=%8x,",(UINT) wlval);
+#endif
+ if(wlval & ( (SEL_TIMEOUT << 16)+
+ ((SCSI_GERROR+UNEXPECT_DISC+SCSI_RESET) << 8)+
+ ILLEGAL_INSTRUC+ABORT_) )
+ {
+ ExceptionHandler( wlval, pACB, pDCB );
+ }
+ else if( wlval & SCRIPTS_INT )
+ {
+ wval = inw( ioport+DSPS );
+ stateV = (void *) IntVector[wval];
+ stateV( pACB );
+ }
+ else if( wlval & ( PARITY_ERROR << 8) )
+ ParityError( pACB, pDCB );
+ else if( wlval & ( PHASE_MISMATCH << 8) )
+ PhaseMismatch( pACB );
+ return;
+}
+
+
+static void
+ExceptionHandler(ULONG wlval, PACB pACB, PDCB pDCB)
+{
+ PSRB pSRB;
+ UCHAR bval;
+ USHORT ioport;
+
+/* disconnect/scsi reset/illegal instruction */
+
+ ioport = pACB->IOPortBase;
+ if(wlval & ( (SCSI_RESET+SCSI_GERROR) << 8) )
+ DC390W_ScsiRstDetect( pACB );
+ else if(wlval & ABORT_)
+ {
+#ifdef DC390W_DEBUG0
+ printk("AboRst,");
+#endif
+ if( !InitialTime )
+ DC390W_ResetSCSIBus2( pACB );
+ }
+ else if(wlval & (SEL_TIMEOUT << 16) )
+ {
+ pACB->status = SCSI_STAT_SEL_TIMEOUT;
+#ifdef DC390W_DEBUG1
+ printk("Selto,");
+#endif
+ DC390W_CmdCompleted( pACB );
+ }
+ else if(wlval & (UNEXPECT_DISC << 8) )
+ {
+ bval = inb(ioport+STEST3);
+ bval |= CLR_SCSI_FIFO;
+ outb(bval,ioport+STEST3);
+ bval = CLR_DMA_FIFO;
+ outb(bval,ioport+CTEST3);
+ pSRB = pDCB->pActiveSRB;
+ if( pSRB->SRBState & DO_SYNC_NEGO )
+ {
+ pDCB->DevMode &= ~SYNC_NEGO_;
+ pACB->status = SCSI_STAT_CHECKCOND;
+ DC390W_CmdCompleted( pACB );
+ }
+ else if( pSRB->SRBState & DO_WIDE_NEGO )
+ {
+ pDCB->DevMode &= ~WIDE_NEGO_;
+ pACB->status = SCSI_STAT_CHECKCOND;
+ DC390W_CmdCompleted( pACB );
+ }
+ else
+ {
+ pACB->status = SCSI_STAT_UNEXP_BUS_F;
+ DC390W_CmdCompleted( pACB );
+ }
+#ifdef DC390W_DEBUG0
+ printk("Uxpbf,");
+#endif
+ }
+ else
+ {
+#ifdef DC390W_DEBUG0
+ printk("Except,");
+#endif
+ DC390W_ResetSCSIBus( pACB );
+ }
+}
+
+
+static void
+ParityError( PACB pACB, PDCB pDCB )
+{
+ ULONG ioport;
+ UCHAR bval,msg;
+ ULONG wlval;
+ PSRB pSRB;
+
+ ioport = pACB->IOPortBase;
+ bval = inb(ioport+SCRATCHA);
+ if(bval & RE_SELECTED_)
+ {
+#ifdef DC390W_DEBUG0
+ printk("ParityErr,");
+#endif
+ DC390W_ResetSCSIBus( pACB );
+ return;
+ }
+ else
+ {
+ pSRB = pDCB->pActiveSRB;
+ bval = inb(ioport+STEST3);
+ bval |= CLR_SCSI_FIFO;
+ outb(bval,ioport+STEST3);
+ bval = CLR_DMA_FIFO;
+ outb(bval,ioport+CTEST3);
+
+ bval = inb(ioport+DCMD);
+ bval &= 0x07; /* get phase bits */
+ if(bval == 0x07) /* message in phase */
+ {
+ msg = MSG_PARITY_ERROR;
+ wlval = pACB->jmp_clear_ack;
+ }
+ else
+ {
+ msg = MSG_INITIATOR_ERROR;
+ wlval = pACB->jmp_next;
+ }
+ pSRB->__msgout0[0] = 1;
+ pSRB->MsgOutBuf[0] = msg;
+ outl(wlval,(ioport+DSP));
+ return;
+ }
+}
+
+
+static void
+DC390W_Signal( PACB pACB )
+{
+ PDCB pDCB;
+ PSRB pSRB;
+ USHORT ioport;
+ ULONG wlval, flags;
+ UCHAR bval,msgcnt,tagnum;
+
+ save_flags(flags);
+ cli();
+ ioport = pACB->IOPortBase;
+ pDCB = pACB->pActiveDCB;
+ pSRB = pDCB->pActiveSRB;
+#ifdef DC390W_DEBUG0
+ printk("Signal,Cmd=%2x", pSRB->CmdBlock[0]);
+#endif
+ wlval = pSRB->PhysSRB;
+ outl(wlval,(ioport+DSA));
+ wlval = pSRB->ReturnAddr;
+ outl(wlval,(ioport+TEMP));
+ msgcnt = 1;
+ bval = pDCB->IdentifyMsg;
+ pSRB->MsgOutBuf[0] = bval;
+ if( (pSRB->CmdBlock[0] != INQUIRY) &&
+ (pSRB->CmdBlock[0] != REQUEST_SENSE) )
+ {
+ if(pDCB->MaxCommand > 1)
+ {
+ wlval = 1;
+ tagnum = 0;
+ while( wlval & pDCB->TagMask )
+ {
+ wlval = wlval << 1;
+ tagnum++;
+ }
+ pDCB->TagMask |= wlval;
+ pSRB->TagNumber = tagnum;
+ pSRB->MsgOutBuf[1] = MSG_SIMPLE_QTAG;
+ pSRB->MsgOutBuf[2] = tagnum;
+ msgcnt = 3;
+ }
+ }
+ else
+ {
+ pSRB->MsgOutBuf[0] &= 0xBF; /* Diable Disconnected */
+ if(pSRB->CmdBlock[0] == INQUIRY)
+ {
+ if(bval & 0x07)
+ goto type_6_3;
+ }
+ if(pDCB->DevMode & WIDE_NEGO_)
+ {
+ msgcnt = 5;
+ *((PULONG) &(pSRB->MsgOutBuf[1])) = 0x01030201;
+ pSRB->SRBState |= DO_WIDE_NEGO;
+ }
+ else if(pDCB->DevMode & SYNC_NEGO_)
+ {
+ msgcnt = 6;
+ *((PULONG) &(pSRB->MsgOutBuf[1])) = 0x00010301;
+ pSRB->MsgOutBuf[4] = pDCB->NegoPeriod;
+ pSRB->MsgOutBuf[5] = SYNC_NEGO_OFFSET;
+ pSRB->SRBState |= DO_SYNC_NEGO;
+ }
+ }
+type_6_3:
+ pSRB->__msgout0[0] = (ULONG) msgcnt;
+ wlval = 0;
+ outl(wlval,(ioport+SCRATCHA));
+ bval = pDCB->DCBscntl0;
+ outb(bval,ioport+SCNTL0);
+ pSRB->__select = *((PULONG) &(pDCB->DCBselect));
+#ifdef DC390W_DEBUG0
+ printk("__sel=%8x,", (UINT)(pSRB->__select));
+#endif
+ wlval = pACB->jmp_select;
+ outl(wlval,(ioport+DSP));
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+DC390W_MessageWide( PACB pACB )
+{
+ PDCB pDCB;
+ PSRB pSRB;
+ PUCHAR msgoutPtr;
+ USHORT ioport;
+ ULONG wlval;
+ UCHAR bval,msgcnt;
+
+
+#ifdef DC390W_DEBUG0
+ printk("MsgWide,");
+#endif
+ ioport = pACB->IOPortBase;
+ pDCB = pACB->pActiveDCB;
+ pSRB = pDCB->pActiveSRB;
+ msgcnt = 0;
+ pDCB->DCBscntl3 &= ~EN_WIDE_SCSI;
+ msgoutPtr = pSRB->MsgOutBuf;
+ if( pSRB->SRBState & DO_WIDE_NEGO )
+ {
+ pSRB->SRBState &= ~DO_WIDE_NEGO;
+ if( pACB->msgin123[0] == 3 )
+ {
+ bval = pACB->msgin123[1];
+ if(bval == 1)
+ {
+ pDCB->DCBscntl3 |= EN_WIDE_SCSI;
+ goto x5;
+ }
+ if(bval < 1)
+ goto x5;
+ }
+ }
+
+/*type_11_1:*/
+ msgcnt = 1;
+ *msgoutPtr = MSG_REJECT_;
+ msgoutPtr++;
+x5:
+ bval = pDCB->DCBscntl3;
+ outb(bval,ioport+SCNTL3);
+ AdjustTemp(pACB,pDCB,pSRB);
+ SetXferRate(pACB,pDCB);
+ if( pDCB->DevMode & SYNC_NEGO_ )
+ {
+ *((PULONG)msgoutPtr) = 0x00010301;
+ *(msgoutPtr + 3) = pDCB->NegoPeriod;
+ *(msgoutPtr + 4) = SYNC_NEGO_OFFSET;
+ msgcnt += 5;
+ pSRB->SRBState |= DO_SYNC_NEGO;
+ }
+
+ pSRB->__msgout0[0] = (ULONG) msgcnt;
+ wlval = pACB->jmp_clear_ack;
+ if(msgcnt)
+ wlval = pACB->jmp_set_atn;
+ outl(wlval,(ioport+DSP));
+ return;
+}
+
+
+static void
+DC390W_MessageSync( PACB pACB )
+{
+ PDCB pDCB;
+ PSRB pSRB;
+ USHORT ioport;
+ ULONG wlval;
+ USHORT wval,wval1;
+ UCHAR bval,bval1;
+
+#ifdef DC390W_DEBUG0
+ printk("MsgSync,");
+#endif
+ ioport = pACB->IOPortBase;
+ pDCB = pACB->pActiveDCB;
+ pSRB = pDCB->pActiveSRB;
+ if( !(pSRB->SRBState & DO_SYNC_NEGO) )
+ goto MessageExtnd;
+ pSRB->SRBState &= ~DO_SYNC_NEGO;
+ if(pACB->msgin123[0] != 1)
+ {
+MessageExtnd:
+ pSRB->__msgout0[0] = 1;
+ pSRB->MsgOutBuf[0] = MSG_REJECT_;
+ wlval = pACB->jmp_set_atn;
+ outl(wlval,(ioport+DSP));
+ return;
+ }
+ bval = pACB->msgin123[2]; /* offset */
+asyncx:
+ pDCB->DCBsxfer = bval;
+ if(bval == 0) /* if offset or period == 0, async */
+ {
+ if( pACB->AdaptType == DC390W )
+ bval = SYNC_CLK_F2+ASYNC_CLK_F2;
+ else
+ bval = SYNC_CLK_F4+ASYNC_CLK_F4;
+ pDCB->DCBscntl3 = bval;
+ }
+ else
+ {
+ bval = pACB->msgin123[1];
+ if(bval == 0)
+ goto asyncx;
+ pDCB->SyncPeriod = bval;
+ wval = (USHORT)bval;
+ wval <<= 3;
+ bval = pDCB->DCBscntl3;
+ bval &= 0x0f;
+ if(wval < 200) /* < 100 ns ==> Fast-20 */
+ {
+ bval |= 0x90; /* Fast-20 and div 1 */
+ bval1 = 25; /* 12.5 ns */
+ }
+ else if(wval < 400)
+ {
+ bval |= 0x30; /* 1 cycle = 25ns */
+ bval1 = 50;
+ }
+ else /* Non Fast */
+ {
+ bval |= 0x50; /* 1 cycle = 50ns */
+ bval1 = 100;
+ }
+ if( pACB->AdaptType == DC390W )
+ bval -= 0x20; /* turn down to 40Mhz scsi clock */
+ /* assume 390W will not receive fast-20 */
+ wval1 = wval;
+ wval /= bval1;
+ if(wval * bval1 < wval1)
+ wval++;
+ /* XFERP TP2 TP1 TP0 */
+ wval -= 4; /* 4 0 0 0 */
+ /* 5 0 0 1 */
+ wval <<= 5;
+ pDCB->DCBsxfer |= (UCHAR)wval;
+ pDCB->DCBscntl3 = bval;
+ }
+/*sync_2:*/
+ SetXferRate( pACB,pDCB );
+ wlval = pACB->jmp_clear_ack;
+/*sync_3:*/
+ bval = pDCB->DCBscntl3;
+ outb(bval,ioport+SCNTL3);
+ bval = pDCB->DCBsxfer;
+ outb(bval,ioport+SXFER);
+ outl(wlval,(ioport+DSP));
+ return;
+}
+
+
+static void
+DC390W_MsgReject( PACB pACB )
+{
+ PDCB pDCB;
+ PSRB pSRB;
+ ULONG wlval;
+ USHORT ioport;
+ UCHAR bval;
+
+#ifdef DC390W_DEBUG0
+ printk("Msgrjt,");
+#endif
+ pDCB = pACB->pActiveDCB;
+ pSRB = pDCB->pActiveSRB;
+ wlval = pACB->jmp_clear_ack;
+ if(pSRB->SRBState & DO_WIDE_NEGO)
+ {
+ pSRB->SRBState &= ~DO_WIDE_NEGO;
+ pDCB->DCBscntl3 &= ~EN_WIDE_SCSI;
+ AdjustTemp( pACB, pDCB, pSRB );
+ SetXferRate( pACB, pDCB );
+ if( pDCB->DevMode & SYNC_NEGO_ )
+ {
+ *((PULONG) &(pSRB->MsgOutBuf[0])) = 0x00010301;
+ pSRB->MsgOutBuf[3] = pDCB->NegoPeriod;
+ pSRB->MsgOutBuf[4] = SYNC_NEGO_OFFSET;
+ pSRB->__msgout0[0] = 5;
+ pSRB->SRBState |= DO_SYNC_NEGO;
+ wlval = pACB->jmp_set_atn;
+ }
+ }
+ else
+ {
+ if(pSRB->SRBState & DO_SYNC_NEGO)
+ {
+ pSRB->SRBState &= ~DO_SYNC_NEGO;
+ pDCB->DCBsxfer = 0; /* reject sync msg, set aync */
+ if( pACB->AdaptType == DC390W )
+ bval = SYNC_CLK_F2+ASYNC_CLK_F2;
+ else
+ bval = SYNC_CLK_F4+ASYNC_CLK_F4;
+ pDCB->DCBscntl3 = bval;
+ SetXferRate(pACB,pDCB);
+ wlval = pACB->jmp_clear_ack;
+ }
+ }
+ ioport = pACB->IOPortBase;
+ bval = pDCB->DCBscntl3;
+ outb(bval,ioport+SCNTL3);
+ bval = pDCB->DCBsxfer;
+ outb(bval,ioport+SXFER);
+ outl(wlval,(ioport+DSP));
+ return;
+}
+
+
+static void
+AdjustTemp( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+ USHORT ioport;
+ ULONG wlval;
+
+ wlval = pSRB->ReturnAddr;
+ if(wlval <= pACB->jmp_table8)
+ {
+ if(pDCB->DCBscntl3 & EN_WIDE_SCSI)
+ wlval += jmp_table16;
+ }
+ else
+ {
+ if((pDCB->DCBscntl3 & EN_WIDE_SCSI) == 0)
+ wlval -= jmp_table16;
+ }
+ pSRB->ReturnAddr = wlval;
+ ioport = pACB->IOPortBase;
+ outl(wlval,(ioport+TEMP));
+ return;
+}
+
+
+static void
+SetXferRate( PACB pACB, PDCB pDCB )
+{
+ UCHAR bval;
+ USHORT cnt, i;
+ PDCB ptr;
+
+ if( !(pDCB->IdentifyMsg & 0x07) )
+ {
+ if( pACB->scan_devices )
+ {
+ CurrDCBscntl3 = pDCB->DCBscntl3;
+ }
+ else
+ {
+ ptr = pACB->pLinkDCB;
+ cnt = pACB->DeviceCnt;
+ bval = pDCB->UnitSCSIID;
+ for(i=0; i<cnt; i++)
+ {
+ if( ptr->UnitSCSIID == bval )
+ {
+ ptr->DCBsxfer = pDCB->DCBsxfer;
+ ptr->DCBscntl3 = pDCB->DCBscntl3;
+ }
+ ptr = ptr->pNextDCB;
+ }
+ }
+ }
+ return;
+}
+
+
+static void
+DC390W_UnknownMsg( PACB pACB )
+{
+ PSRB pSRB;
+ ULONG wlval;
+ USHORT ioport;
+
+ pSRB = pACB->pActiveDCB->pActiveSRB;
+ pSRB->__msgout0[0] = 1;
+ pSRB->MsgOutBuf[0] = MSG_REJECT_;
+ wlval = pACB->jmp_set_atn;
+ ioport = pACB->IOPortBase;
+ outl(wlval,(ioport+DSP));
+ return;
+}
+
+
+static void
+DC390W_MessageExtnd( PACB pACB )
+{
+ DC390W_UnknownMsg( pACB );
+}
+
+
+static void
+DC390W_Disconnected( PACB pACB )
+{
+ PDCB pDCB;
+ PSRB pSRB;
+ ULONG wlval, flags;
+ USHORT ioport;
+ UCHAR bval;
+
+#ifdef DC390W_DEBUG0
+ printk("Discnet,");
+#endif
+ save_flags(flags);
+ cli();
+ pDCB = pACB->pActiveDCB;
+ if (! pDCB)
+ {
+#ifdef DC390W_DEBUG0
+ printk("ACB:%08lx->ActiveDCB:%08lx !,", (ULONG)pACB, (ULONG)pDCB);
+#endif
+ restore_flags(flags); return;
+ }
+
+ pSRB = pDCB->pActiveSRB;
+
+ ioport = pACB->IOPortBase;
+ bval = inb(ioport+SCRATCHA);
+ pSRB->ScratchABuf = bval;
+ pSRB->SRBState |= SRB_DISCONNECT; /* 1.02 */
+ wlval = pACB->jmp_reselect;
+ outl(wlval,(ioport+DSP));
+ pACB->pActiveDCB = 0;
+ DoWaitingSRB( pACB );
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+DC390W_Reselected( PACB pACB )
+{
+#ifdef DC390W_DEBUG0
+ printk("Rsel,");
+#endif
+ pACB->msgin123[0] = 0x80; /* set identify byte 80h */
+ DC390W_Reselected1(pACB);
+ return;
+}
+
+
+static void
+DC390W_Reselected1( PACB pACB )
+{
+ PDCB pDCB;
+ PSRB pSRB;
+ USHORT ioport, wval;
+ ULONG wlval, flags;
+ UCHAR bval;
+
+
+#ifdef DC390W_DEBUG0
+ printk("Rsel1,");
+#endif
+ ioport = pACB->IOPortBase;
+ pDCB = pACB->pActiveDCB;
+ if(pDCB)
+ {
+ pSRB = pDCB->pActiveSRB;
+ RewaitSRB( pDCB, pSRB );
+ }
+
+ wval = (USHORT) (pACB->msgin123[0]);
+ wval = (wval & 7) << 8; /* get LUN */
+ wval |= (USHORT) (inb(ioport+SSID) & 0x0f); /* get ID */
+ pDCB = pACB->pLinkDCB;
+ while( *((PUSHORT) &pDCB->UnitSCSIID) != wval )
+ pDCB = pDCB->pNextDCB;
+ pACB->pActiveDCB = pDCB;
+ bval = pDCB->DCBscntl3;
+ outb(bval,ioport+SCNTL3);
+ bval = pDCB->DCBsxfer;
+ outb(bval,ioport+SXFER);
+ bval = pDCB->DCBscntl0;
+ outb(bval,ioport+SCNTL0);
+ if(pDCB->MaxCommand > 1)
+ {
+ wlval = pACB->jmp_reselecttag;
+ outl(wlval,(ioport+DSP));
+ }
+ else
+ {
+ pSRB = pDCB->pActiveSRB;
+ if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) )
+ {
+ save_flags(flags);
+ cli();
+ pSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = pSRB->pNextSRB;
+ restore_flags(flags);
+ pSRB->SRBState = SRB_UNEXPECT_RESEL;
+ pDCB->pActiveSRB = pSRB;
+ pSRB->MsgOutBuf[0] = MSG_ABORT;
+ pSRB->__msgout0[0] = 1;
+ }
+ pSRB->SRBState &= ~SRB_DISCONNECT;
+ wlval = pSRB->PhysSRB;
+ outl(wlval,(ioport+DSA));
+ wlval = pSRB->ReturnAddr;
+ outl(wlval,(ioport+TEMP));
+ bval = pSRB->ScratchABuf;
+ outb(bval,ioport+SCRATCHA);
+ if( pSRB->SRBState & SRB_UNEXPECT_RESEL )
+ wlval = pACB->jmp_set_atn;
+ else
+ wlval = pACB->jmp_clear_ack;
+ outl(wlval,(ioport+DSP));
+ }
+ return;
+}
+
+
+static void
+DC390W_ReselectedT( PACB pACB )
+{
+ PDCB pDCB;
+ PSRB pSRB, psrb1;
+ USHORT ioport;
+ ULONG wlval, flags;
+ UCHAR bval;
+
+#ifdef DC390W_DEBUG0
+ printk("RselT,");
+#endif
+ ioport = pACB->IOPortBase;
+ bval = pACB->msgin123[1];
+ pDCB = pACB->pActiveDCB;
+ pSRB = pDCB->pGoingSRB;
+ psrb1 = pDCB->pGoingLast;
+ if( !pSRB )
+ goto UXP_RSL;
+ for(;;)
+ {
+ if(pSRB->TagNumber != bval)
+ {
+ if( pSRB != psrb1 )
+ pSRB = pSRB->pNextSRB;
+ else
+ goto UXP_RSL;
+ }
+ else
+ break;
+ }
+ if( !(pSRB->SRBState & SRB_DISCONNECT) )
+ {
+UXP_RSL:
+ save_flags(flags);
+ cli();
+ pSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = pSRB->pNextSRB;
+ restore_flags(flags);
+ pSRB->SRBState = SRB_UNEXPECT_RESEL;
+ pDCB->pActiveSRB = pSRB;
+ pSRB->MsgOutBuf[0] = MSG_ABORT_TAG;
+ pSRB->__msgout0[0] = 1;
+ }
+ else
+ {
+ pSRB->SRBState &= ~SRB_DISCONNECT;
+ pDCB->pActiveSRB = pSRB;
+ }
+ wlval = pSRB->PhysSRB;
+ outl(wlval,(ioport+DSA));
+ wlval = pSRB->ReturnAddr;
+ outl(wlval,(ioport+TEMP));
+ bval = pSRB->ScratchABuf;
+ outb(bval,ioport+SCRATCHA);
+ if( pSRB->SRBState & SRB_UNEXPECT_RESEL )
+ wlval = pACB->jmp_set_atn;
+ else
+ wlval = pACB->jmp_clear_ack;
+ outl(wlval,(ioport+DSP));
+ return;
+}
+
+
+static void
+DC390W_RestorePtr( PACB pACB )
+{
+ PSRB pSRB;
+ USHORT ioport;
+ ULONG wlval;
+
+ pSRB = pACB->pActiveDCB->pActiveSRB;
+ wlval = pSRB->ReturnAddr;
+ ioport = pACB->IOPortBase;
+ outl(wlval,(ioport+TEMP));
+ wlval = inl(ioport+DSP);
+ outl(wlval,(ioport+DSP));
+ return;
+}
+
+
+static void
+PhaseMismatch( PACB pACB )
+{
+ USHORT ioport;
+ ULONG wlval,swlval;
+ USHORT wval;
+ UCHAR bval,phase;
+ PDCB pDCB;
+
+#ifdef DC390W_DEBUG0
+ printk("Mismatch,");
+#endif
+ ioport = pACB->IOPortBase;
+ bval = inb(ioport+SCRATCHA);
+ if(bval & OVER_RUN_) /* xfer PAD */
+ {
+ bval = inb(ioport+STEST3);
+ bval |= CLR_SCSI_FIFO;
+ outb(bval,ioport+STEST3);
+ bval = CLR_DMA_FIFO;
+ outb(bval,ioport+CTEST3);
+ wlval = pACB->jmp_next; /* check phase */
+ outl(wlval,(ioport+DSP));
+ return;
+ }
+ pDCB = pACB->pActiveDCB;
+ wlval = inl(ioport+DBC);
+ phase = (UCHAR)((wlval & 0x07000000) >> 24);
+ wlval &= 0xffffff; /* bytes not xferred */
+ if( phase == SCSI_DATA_IN )
+ {
+ swlval = pACB->jmp_din8;
+ if( pDCB->DCBscntl3 & EN_WIDE_SCSI )
+ swlval += jmp_din16;
+ DataIOcommon(pACB,swlval,wlval);
+ }
+ else if( phase == SCSI_DATA_OUT )
+ {
+ wval = (USHORT)inb(ioport+CTEST5);
+ wval <<= 8;
+ bval = inb(ioport+DFIFO);
+ wval |= (USHORT) bval;
+ wval -= ((USHORT)(wlval & 0xffff));
+ wval &= 0x3ff;
+ wlval += (ULONG)wval; /* # of bytes remains in FIFO */
+ bval = inb(ioport+SSTAT0);
+ if(bval & SODR_LSB_FULL)
+ wlval++; /* data left in Scsi Output Data Buffer */
+ if(bval & SODL_LSB_FULL)
+ wlval++; /* data left in Scsi Output Data Latch */
+ swlval = pACB->jmp_dout8;
+ if(pDCB->DCBscntl3 & EN_WIDE_SCSI)
+ {
+ swlval += jmp_dout16;
+ bval = inb(ioport+SSTAT2);
+ if(bval & SODR_MSB_FULL)
+ wlval++;
+ if(bval & SODL_MSB_FULL)
+ wlval++;
+ }
+ bval = inb(ioport+STEST3);
+ bval |= CLR_SCSI_FIFO;
+ outb(bval,ioport+STEST3);
+ bval = CLR_DMA_FIFO;
+ outb(bval,ioport+CTEST3);
+ DataIOcommon(pACB,swlval,wlval);
+ }
+ else
+ {
+ bval = inb(ioport+STEST3);
+ bval |= CLR_SCSI_FIFO;
+ outb(bval,ioport+STEST3);
+ bval = CLR_DMA_FIFO;
+ outb(bval,ioport+CTEST3);
+ if(phase == SCSI_MSG_OUT)
+ wlval = pACB->jmp_clear_atn;
+ else
+ wlval = pACB->jmp_next; /* check phase */
+ outl(wlval,(ioport+DSP));
+ }
+ return;
+}
+
+
+static void
+DataIOcommon( PACB pACB, ULONG Swlval, ULONG Cwlval )
+{
+ /* Swlval - script address */
+ /* Cwlval - bytes not xferred */
+ PDCB pDCB;
+ PSRB pSRB;
+ PSGE Segptr;
+ USHORT ioport;
+ ULONG wlval,swlval,dataXferCnt;
+ UCHAR bval,bvald;
+
+ ioport = pACB->IOPortBase;
+ wlval = inl((ioport+DSP));
+ pDCB = pACB->pActiveDCB;
+ pSRB = pDCB->pActiveSRB;
+ wlval -= Swlval;
+ bval = inb(ioport+SBCL);
+ bval &= 0x07;
+ if(bval == SCSI_MSG_IN)
+ {
+ bval = pDCB->DCBscntl3;
+ bval &= ~EN_WIDE_SCSI;
+ outb(bval,ioport+SCNTL3);
+ bval = inb(ioport+SBDL);
+ bvald = pDCB->DCBscntl3; /* enable WIDE SCSI */
+ outb(bvald,ioport+SCNTL3);
+ if(bval == MSG_DISCONNECT || bval == MSG_SAVE_PTR)
+ {
+ Segptr = (PSGE)((ULONG) &(pSRB->Segment0[0][0]) + wlval);
+ dataXferCnt = Segptr->SGXLen - Cwlval;
+ Segptr->SGXLen = Cwlval; /* modified count */
+ Segptr->SGXPtr += dataXferCnt; /* modified address */
+ swlval = pACB->jmp_table8;
+ if(pDCB->DCBscntl3 & EN_WIDE_SCSI)
+ swlval += jmp_table16;
+ wlval <<= 1;
+ swlval += wlval;
+ swlval = swlval - ((MAX_SG_LIST_BUF+1) * 16);
+ pSRB->ReturnAddr = swlval;
+ }
+ }
+ else if( Cwlval ) /* Remaining not xferred -- UNDER_RUN */
+ {
+ Segptr = (PSGE)((ULONG) &(pSRB->Segment0[0][0]) + wlval);
+ dataXferCnt = Segptr->SGXLen - Cwlval;
+ Segptr->SGXLen = Cwlval; /* modified count */
+ Segptr->SGXPtr += dataXferCnt; /* modified address */
+ swlval = pACB->jmp_table8;
+ if(pDCB->DCBscntl3 & EN_WIDE_SCSI)
+ swlval += jmp_table16;
+ wlval <<= 1;
+ swlval += wlval;
+ swlval = swlval - ((MAX_SG_LIST_BUF+1) * 16);
+ pSRB->RemainSegPtr = swlval;
+ }
+/* pm__1: */
+ wlval = pSRB->ReturnAddr;
+ outl(wlval,(ioport+TEMP));
+ wlval = pACB->jmp_next;
+ outl(wlval,(ioport+DSP));
+ return;
+}
+
+
+static void
+DC390W_CmdCompleted( PACB pACB )
+{
+ PDCB pDCB;
+ PSRB pSRB;
+ USHORT ioport;
+ ULONG wlval, flags;
+ UCHAR bval;
+
+#ifdef DC390W_DEBUG0
+ printk("Cmplete,");
+#endif
+ save_flags(flags);
+ cli();
+ pDCB = pACB->pActiveDCB;
+ pSRB = pDCB->pActiveSRB;
+ pDCB->pActiveSRB = NULL;
+ ioport = pACB->IOPortBase;
+
+ bval = inb(ioport+SCRATCHA);
+ pSRB->ScratchABuf = bval; /* save status */
+ bval = pSRB->TagNumber;
+ if(pDCB->MaxCommand > 1)
+ pDCB->TagMask &= (~(1 << bval)); /* free tag mask */
+ pACB->pActiveDCB = NULL; /* no active device */
+ wlval = pACB->jmp_reselect; /* enable reselection */
+ outl(wlval,(ioport+DSP));
+ SRBdone( pACB, pDCB, pSRB);
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+ PSRB psrb;
+ UCHAR bval, bval1, i, j, status;
+ PSCSICMD pcmd;
+ PSCSI_INQDATA ptr;
+ USHORT disable_tag;
+ ULONG flags;
+ PSGE ptr1;
+ PSGL ptr2;
+ ULONG wlval,swlval;
+
+ pcmd = pSRB->pcmd;
+ status = pACB->status;
+ if(pSRB->SRBFlag & AUTO_REQSENSE)
+ {
+ pSRB->SRBFlag &= ~AUTO_REQSENSE;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = SCSI_STAT_CHECKCOND;
+ if(status == SCSI_STAT_CHECKCOND)
+ {
+ pcmd->result = DID_BAD_TARGET << 16;
+ goto ckc_e;
+ }
+ if(pSRB->RetryCnt == 0)
+ {
+ *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0][0];
+ pSRB->XferredLen = pSRB->Segment0[2][1];
+ if( (pSRB->XferredLen) &&
+ (pSRB->XferredLen >= pcmd->underflow) )
+ {
+ pcmd->result |= (DID_OK << 16);
+ }
+ else
+ pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) |
+ SCSI_STAT_CHECKCOND;
+ goto ckc_e;
+ }
+ else
+ {
+ pSRB->RetryCnt--;
+ pSRB->TargetStatus = 0;
+ *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0][0];
+ *((PULONG) &(pSRB->CmdBlock[4])) = pSRB->Segment0[0][1];
+ *((PULONG) &(pSRB->CmdBlock[8])) = pSRB->Segment0[1][0];
+ pSRB->__command[0] = pSRB->Segment0[1][1] & 0xff;
+ pSRB->SGcount = (UCHAR) (pSRB->Segment0[1][1] >> 8);
+ *((PULONG) &(pSRB->pSegmentList))= pSRB->Segment0[2][0];
+ if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
+ {
+ pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) |
+ SCSI_STAT_CHECKCOND;
+ goto ckc_e;
+ }
+ pcmd->result |= (DRIVER_SENSE << 24);
+ PrepareSG(pACB,pDCB,pSRB);
+ pSRB->XferredLen = 0;
+ DC390W_StartSCSI( pACB, pDCB, pSRB );
+ return;
+ }
+ }
+ if( status )
+ {
+ if( status == SCSI_STAT_CHECKCOND)
+ {
+ if( !(pSRB->ScratchABuf & SRB_OK) && (pSRB->SGcount) && (pSRB->RemainSegPtr) )
+ {
+ wlval = pSRB->RemainSegPtr;
+ swlval = pACB->jmp_table8;
+ if(pDCB->DCBscntl3 & EN_WIDE_SCSI)
+ swlval += jmp_table16;
+ swlval -= wlval;
+ swlval >>= 4;
+ bval = (UCHAR) swlval;
+ wlval = 0;
+ ptr1 = (PSGE) &pSRB->Segment0[MAX_SG_LIST_BUF+1][0];
+ for( i=0; i< bval; i++)
+ {
+ wlval += ptr1->SGXLen;
+ ptr1--;
+ }
+
+ bval = pSRB->SGcount;
+ swlval = 0;
+ ptr2 = pSRB->pSegmentList;
+ for( i=0; i< bval; i++)
+ {
+ swlval += ptr2->length;
+ ptr2++;
+ }
+ pSRB->XferredLen = swlval - wlval;
+ pSRB->RemainSegPtr = 0;
+#ifdef DC390W_DEBUG0
+ printk("XferredLen=%8x,NotXferLen=%8x,",(UINT) pSRB->XferredLen,(UINT) wlval);
+#endif
+ }
+ RequestSense( pACB, pDCB, pSRB );
+ return;
+ }
+ else if( status == SCSI_STAT_QUEUEFULL )
+ {
+ bval = (UCHAR) pDCB->GoingSRBCnt;
+ bval--;
+ pDCB->MaxCommand = bval;
+ RewaitSRB( pDCB, pSRB );
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ return;
+ }
+ else if(status == SCSI_STAT_SEL_TIMEOUT)
+ {
+ pSRB->AdaptStatus = H_SEL_TIMEOUT;
+ pSRB->TargetStatus = 0;
+ pcmd->result = DID_BAD_TARGET << 16;
+ }
+ else if(status == SCSI_STAT_UNEXP_BUS_F)
+ {
+ pSRB->AdaptStatus = H_UNEXP_BUS_FREE;
+ pSRB->TargetStatus = 0;
+ pcmd->result |= DID_NO_CONNECT << 16;
+ }
+ else if(status == SCSI_STAT_BUS_RST_DETECT )
+ {
+ pSRB->AdaptStatus = H_ABORT;
+ pSRB->TargetStatus = 0;
+ pcmd->result = DID_RESET << 16;
+ }
+ else
+ {
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = status;
+ if( pSRB->RetryCnt )
+ {
+ pSRB->RetryCnt--;
+ pSRB->TargetStatus = 0;
+ PrepareSG(pACB,pDCB,pSRB);
+ pSRB->XferredLen = 0;
+ DC390W_StartSCSI( pACB, pDCB, pSRB );
+ return;
+ }
+ else
+ {
+ pcmd->result |= (DID_ERROR << 16) | (ULONG) (pACB->msgin123[0] << 8) |
+ (ULONG) status;
+ }
+ }
+ }
+ else
+ {
+ status = pSRB->ScratchABuf;
+ if(status & OVER_RUN_)
+ {
+ pSRB->AdaptStatus = H_OVER_UNDER_RUN;
+ pSRB->TargetStatus = 0;
+ pcmd->result |= (DID_OK << 16) | (pACB->msgin123[0] << 8);
+ }
+ else /* No error */
+ {
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ pcmd->result |= (DID_OK << 16);
+ }
+ }
+ckc_e:
+
+ if( pACB->scan_devices )
+ {
+ if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
+ {
+ if(pcmd->result != (DID_OK << 16))
+ {
+ if( pcmd->result & SCSI_STAT_CHECKCOND )
+ {
+ goto RTN_OK;
+ }
+ else
+ {
+ pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun);
+ pPrevDCB->pNextDCB = pACB->pLinkDCB;
+ if( (pcmd->target == pACB->max_id) &&
+ ((pcmd->lun == 0) || (pcmd->lun == pACB->max_lun)) )
+ {
+ pACB->scan_devices = 0;
+ }
+ }
+ }
+ else
+ {
+RTN_OK:
+ pPrevDCB->pNextDCB = pDCB;
+ pDCB->pNextDCB = pACB->pLinkDCB;
+ if( (pcmd->target == pACB->max_id) && (pcmd->lun == pACB->max_lun) )
+ pACB->scan_devices = END_SCAN;
+ }
+ }
+ else if( pSRB->CmdBlock[0] == INQUIRY )
+ {
+ if( (pcmd->target == pACB->max_id) &&
+ (pcmd->lun == pACB->max_lun) )
+ {
+ pACB->scan_devices = 0;
+ }
+ ptr = (PSCSI_INQDATA) (pcmd->request_buffer);
+ if( pcmd->use_sg )
+ ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address);
+ bval1 = ptr->DevType & SCSI_DEVTYPE;
+ if(bval1 == SCSI_NODEV)
+ {
+ pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun);
+ pPrevDCB->pNextDCB = pACB->pLinkDCB;
+ }
+ else
+ {
+ pACB->DeviceCnt++;
+ pPrevDCB = pDCB;
+ pACB->pDCB_free = (PDCB) ((ULONG) (pACB->pDCB_free) + sizeof( DC390W_DCB ));
+ pDCB->DevType = bval1;
+ if(bval1 == TYPE_DISK || bval1 == TYPE_MOD)
+ {
+ if( (((ptr->Vers & 0x07) >= 2) || ((ptr->RDF & 0x0F) == 2)) &&
+ (ptr->Flags & SCSI_INQ_CMDQUEUE) &&
+ (pDCB->DevMode & TAG_QUEUING_) &&
+ (pDCB->DevMode & EN_DISCONNECT_) )
+ {
+ disable_tag = 0;
+ for(i=0; i<BADDEVCNT; i++)
+ {
+ for(j=0; j<28; j++)
+ {
+ if( ((PUCHAR)ptr)[8+j] != baddevname[i][j])
+ break;
+ }
+ if(j == 28)
+ {
+ disable_tag = 1;
+ break;
+ }
+ }
+
+ if( !disable_tag )
+ {
+ pDCB->MaxCommand = pACB->TagMaxNum;
+ pDCB->TagMask = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ save_flags( flags );
+ cli();
+/* ReleaseSRB( pDCB, pSRB ); */
+
+ if(pSRB == pDCB->pGoingSRB )
+ {
+ pDCB->pGoingSRB = pSRB->pNextSRB;
+ }
+ else
+ {
+ psrb = pDCB->pGoingSRB;
+ while( psrb->pNextSRB != pSRB )
+ psrb = psrb->pNextSRB;
+ psrb->pNextSRB = pSRB->pNextSRB;
+ if( pSRB == pDCB->pGoingLast )
+ pDCB->pGoingLast = psrb;
+ }
+ pSRB->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = pSRB;
+ pDCB->GoingSRBCnt--;
+
+ DoWaitingSRB( pACB );
+ restore_flags(flags);
+
+/* Notify cmd done */
+ pcmd->scsi_done( pcmd );
+
+ if( pDCB->QIORBCnt )
+ DoNextCmd( pACB, pDCB );
+ return;
+}
+
+
+static void
+DoingSRB_Done( PACB pACB )
+{
+ PDCB pDCB, pdcb;
+ PSRB psrb, psrb2;
+ USHORT cnt, i;
+ PSCSICMD pcmd;
+
+ pDCB = pACB->pLinkDCB;
+ pdcb = pDCB;
+ do
+ {
+ cnt = pdcb->GoingSRBCnt;
+ psrb = pdcb->pGoingSRB;
+ for( i=0; i<cnt; i++)
+ {
+ psrb2 = psrb->pNextSRB;
+ pcmd = psrb->pcmd;
+ pcmd->result = DID_RESET << 16;
+
+/* ReleaseSRB( pDCB, pSRB ); */
+
+ psrb->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = psrb;
+
+ pcmd->scsi_done( pcmd );
+ psrb = psrb2;
+ }
+ pdcb->GoingSRBCnt = 0;;
+ pdcb->pGoingSRB = NULL;
+ pdcb->TagMask = 0;
+ pdcb = pdcb->pNextDCB;
+ }
+ while( pdcb != pDCB );
+}
+
+
+static void
+DC390W_ResetSCSIBus( PACB pACB )
+{
+ USHORT ioport;
+ UCHAR bval;
+ ULONG flags;
+
+ save_flags(flags);
+ cli();
+ pACB->ACBFlag |= RESET_DEV;
+ ioport = pACB->IOPortBase;
+ bval = ABORT_OP;
+ outb(bval,ioport+ISTAT);
+ udelay(25);
+ bval = 0;
+ outb(bval,ioport+ISTAT);
+
+ bval = ASSERT_RST;
+ outb(bval,ioport+SCNTL1);
+ udelay(25); /* 25 us */
+ bval = 0;
+ outb(bval,ioport+SCNTL1);
+ restore_flags(flags);
+ return;
+}
+
+
+
+static void
+DC390W_ResetSCSIBus2( PACB pACB )
+{
+ USHORT ioport;
+ UCHAR bval;
+ ULONG flags;
+
+ save_flags(flags);
+ cli();
+ ioport = pACB->IOPortBase;
+ bval = ASSERT_RST;
+ outb(bval,ioport+SCNTL1);
+ udelay(25); /* 25 us */
+ bval = 0;
+ outb(bval,ioport+SCNTL1);
+ restore_flags(flags);
+ return;
+}
+
+
+
+static void
+DC390W_ScsiRstDetect( PACB pACB )
+{
+ ULONG wlval, flags;
+ USHORT ioport;
+ UCHAR bval;
+
+ save_flags(flags);
+ sti();
+#ifdef DC390W_DEBUG0
+ printk("Reset_Detect0,");
+#endif
+/* delay 1 sec */
+ wlval = jiffies + HZ;
+ while( jiffies < wlval );
+/* USHORT i;
+ for( i=0; i<1000; i++ )
+ udelay(1000); */
+
+ cli();
+ ioport = pACB->IOPortBase;
+ bval = inb(ioport+STEST3);
+ bval |= CLR_SCSI_FIFO;
+ outb(bval,ioport+STEST3);
+ bval = CLR_DMA_FIFO;
+ outb(bval,ioport+CTEST3);
+
+ if( pACB->ACBFlag & RESET_DEV )
+ pACB->ACBFlag |= RESET_DONE;
+ else
+ {
+ pACB->ACBFlag |= RESET_DETECT;
+
+ ResetDevParam( pACB );
+/* DoingSRB_Done( pACB ); ???? */
+ RecoverSRB( pACB );
+ pACB->pActiveDCB = NULL;
+ wlval = pACB->jmp_reselect;
+ outl(wlval,(ioport+DSP));
+ pACB->ACBFlag = 0;
+ DoWaitingSRB( pACB );
+ }
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+ PSCSICMD pcmd;
+
+ pSRB->SRBFlag |= AUTO_REQSENSE;
+ pSRB->Segment0[0][0] = *((PULONG) &(pSRB->CmdBlock[0]));
+ pSRB->Segment0[0][1] = *((PULONG) &(pSRB->CmdBlock[4]));
+ pSRB->Segment0[1][0] = *((PULONG) &(pSRB->CmdBlock[8]));
+ pSRB->Segment0[1][1] = pSRB->__command[0] | (pSRB->SGcount << 8);
+ pSRB->Segment0[2][0] = *((PULONG) &(pSRB->pSegmentList));
+ pSRB->Segment0[2][1] = pSRB->XferredLen;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+
+ pcmd = pSRB->pcmd;
+
+ pSRB->Segmentx.address = (PUCHAR) &(pcmd->sense_buffer);
+ pSRB->Segmentx.length = sizeof(pcmd->sense_buffer);
+ pSRB->pSegmentList = &pSRB->Segmentx;
+ pSRB->SGcount = 1;
+
+ *((PULONG) &(pSRB->CmdBlock[0])) = 0x00000003;
+ pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5;
+ *((PUSHORT) &(pSRB->CmdBlock[4])) = sizeof(pcmd->sense_buffer);
+ pSRB->__command[0] = 6;
+ PrepareSG( pACB, pDCB, pSRB );
+ pSRB->XferredLen = 0;
+ DC390W_StartSCSI( pACB, pDCB, pSRB );
+ return;
+}
+
+
+static void
+DC390W_MessageOut( PACB pACB )
+{
+ DC390W_FatalError( pACB );
+}
+
+
+static void
+DC390W_FatalError( PACB pACB )
+{
+ PSRB pSRB;
+ PDCB pDCB;
+ ULONG flags;
+
+#ifdef DC390W_DEBUG0
+ printk("DC390W: Fatal Error!!\n");
+#endif
+
+ pDCB = pACB->pActiveDCB;
+ pSRB = pDCB->pActiveSRB;
+ if( pSRB->SRBState & SRB_UNEXPECT_RESEL )
+ {
+ save_flags(flags);
+ cli();
+ pSRB->SRBState &= ~SRB_UNEXPECT_RESEL;
+ pSRB->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = pSRB;
+ pACB->pActiveDCB = NULL;
+ pDCB->pActiveSRB = NULL;
+ restore_flags(flags);
+ DoWaitingSRB( pACB );
+ }
+ else
+ DC390W_ResetSCSIBus(pACB);
+ return;
+}
+
+
+static void
+DC390W_Debug( PACB pACB )
+{
+ ULONG wlval;
+ USHORT ioport;
+
+ ioport = pACB->IOPortBase;
+ wlval = inl(ioport+DSP);
+ outl(wlval,(ioport+DSP));
+ return;
+}
+
+
diff --git a/linux/src/drivers/scsi/scsiiom.c b/linux/src/drivers/scsi/scsiiom.c
new file mode 100644
index 00000000..97801d75
--- /dev/null
+++ b/linux/src/drivers/scsi/scsiiom.c
@@ -0,0 +1,1540 @@
+/***********************************************************************
+ * FILE NAME : SCSIIOM.C *
+ * BY : C.L. Huang, ching@tekram.com.tw *
+ * Description: Device Driver for Tekram DC-390 (T) PCI SCSI *
+ * Bus Master Host Adapter *
+ ***********************************************************************/
+
+
+static USHORT
+DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+ USHORT ioport, rc;
+ UCHAR bval, bval1, i, cnt;
+ PUCHAR ptr;
+ ULONG wlval;
+
+ pSRB->TagNumber = 31;
+ ioport = pACB->IOPortBase;
+ bval = pDCB->UnitSCSIID;
+ outb(bval,ioport+Scsi_Dest_ID);
+ bval = pDCB->SyncPeriod;
+ outb(bval,ioport+Sync_Period);
+ bval = pDCB->SyncOffset;
+ outb(bval,ioport+Sync_Offset);
+ bval = pDCB->CtrlR1;
+ outb(bval,ioport+CtrlReg1);
+ bval = pDCB->CtrlR3;
+ outb(bval,ioport+CtrlReg3);
+ bval = pDCB->CtrlR4;
+ outb(bval,ioport+CtrlReg4);
+ bval = CLEAR_FIFO_CMD; /* Flush FIFO */
+ outb(bval,ioport+ScsiCmd);
+
+ pSRB->ScsiPhase = SCSI_NOP0;
+ bval = pDCB->IdentifyMsg;
+ if( !(pDCB->SyncMode & EN_ATN_STOP) )
+ {
+ if( (pSRB->CmdBlock[0] == INQUIRY) ||
+ (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
+ (pSRB->SRBFlag & AUTO_REQSENSE) )
+ {
+ bval &= 0xBF; /* NO disconnection */
+ outb(bval,ioport+ScsiFifo);
+ bval1 = SELECT_W_ATN;
+ pSRB->SRBState = SRB_START_;
+ if( pDCB->SyncMode & SYNC_ENABLE )
+ {
+ if( !(pDCB->IdentifyMsg & 7) ||
+ (pSRB->CmdBlock[0] != INQUIRY) )
+ {
+ bval1 = SEL_W_ATN_STOP;
+ pSRB->SRBState = SRB_MSGOUT;
+ }
+ }
+ }
+ else
+ {
+ if(pDCB->SyncMode & EN_TAG_QUEUING)
+ {
+ outb(bval,ioport+ScsiFifo);
+ bval = MSG_SIMPLE_QTAG;
+ outb(bval,ioport+ScsiFifo);
+ wlval = 1;
+ bval = 0;
+ while( wlval & pDCB->TagMask )
+ {
+ wlval = wlval << 1;
+ bval++;
+ }
+ outb(bval,ioport+ScsiFifo);
+ pDCB->TagMask |= wlval;
+ pSRB->TagNumber = bval;
+ bval1 = SEL_W_ATN2;
+ pSRB->SRBState = SRB_START_;
+ }
+ else
+ {
+ outb(bval,ioport+ScsiFifo);
+ bval1 = SELECT_W_ATN;
+ pSRB->SRBState = SRB_START_;
+ }
+ }
+
+ if( pSRB->SRBFlag & AUTO_REQSENSE )
+ {
+ bval = REQUEST_SENSE;
+ outb(bval,ioport+ScsiFifo);
+ bval = pDCB->IdentifyMsg << 5;
+ outb(bval,ioport+ScsiFifo);
+ bval = 0;
+ outb(bval,ioport+ScsiFifo);
+ outb(bval,ioport+ScsiFifo);
+ bval = sizeof(pSRB->pcmd->sense_buffer);
+ outb(bval,ioport+ScsiFifo);
+ bval = 0;
+ outb(bval,ioport+ScsiFifo);
+ }
+ else
+ {
+ cnt = pSRB->ScsiCmdLen;
+ ptr = (PUCHAR) pSRB->CmdBlock;
+ for(i=0; i<cnt; i++)
+ {
+ bval = *ptr++;
+ outb(bval,ioport+ScsiFifo);
+ }
+ }
+ }
+ else /* ATN_STOP */
+ {
+ if( (pSRB->CmdBlock[0] == INQUIRY) ||
+ (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
+ (pSRB->SRBFlag & AUTO_REQSENSE) )
+ {
+ bval &= 0xBF;
+ outb(bval,ioport+ScsiFifo);
+ bval1 = SELECT_W_ATN;
+ pSRB->SRBState = SRB_START_;
+ if( pDCB->SyncMode & SYNC_ENABLE )
+ {
+ if( !(pDCB->IdentifyMsg & 7) ||
+ (pSRB->CmdBlock[0] != INQUIRY) )
+ {
+ bval1 = SEL_W_ATN_STOP;
+ pSRB->SRBState = SRB_MSGOUT;
+ }
+ }
+ }
+ else
+ {
+ if(pDCB->SyncMode & EN_TAG_QUEUING)
+ {
+ outb(bval,ioport+ScsiFifo);
+ pSRB->MsgOutBuf[0] = MSG_SIMPLE_QTAG;
+ wlval = 1;
+ bval = 0;
+ while( wlval & pDCB->TagMask )
+ {
+ wlval = wlval << 1;
+ bval++;
+ }
+ pDCB->TagMask |= wlval;
+ pSRB->TagNumber = bval;
+ pSRB->MsgOutBuf[1] = bval;
+ pSRB->MsgCnt = 2;
+ bval1 = SEL_W_ATN_STOP;
+ pSRB->SRBState = SRB_START_;
+ }
+ else
+ {
+ outb(bval,ioport+ScsiFifo);
+ pSRB->MsgOutBuf[0] = MSG_NOP;
+ pSRB->MsgCnt = 1;
+ pSRB->SRBState = SRB_START_;
+ bval1 = SEL_W_ATN_STOP;
+ }
+ }
+ }
+ bval = inb( ioport+Scsi_Status );
+ if( bval & INTERRUPT )
+ {
+ pSRB->SRBState = SRB_READY;
+ pDCB->TagMask &= ~( 1 << pSRB->TagNumber );
+ rc = 1;
+ }
+ else
+ {
+ pSRB->ScsiPhase = SCSI_NOP1;
+ pACB->pActiveDCB = pDCB;
+ pDCB->pActiveSRB = pSRB;
+ rc = 0;
+ outb(bval1,ioport+ScsiCmd);
+ }
+ return( rc );
+}
+
+
+#ifndef VERSION_ELF_1_2_13
+static void
+DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs)
+#else
+static void
+DC390_Interrupt( int irq, struct pt_regs *regs)
+#endif
+{
+ PACB pACB;
+ PDCB pDCB;
+ PSRB pSRB;
+ USHORT ioport = 0;
+ USHORT phase, i;
+ void (*stateV)( PACB, PSRB, PUCHAR );
+ UCHAR istate = 0;
+ UCHAR sstatus=0, istatus;
+
+ pACB = pACB_start;
+ if( pACB == NULL )
+ return;
+ for( i=0; i < adapterCnt; i++ )
+ {
+ if( pACB->IRQLevel == (UCHAR) irq )
+ {
+ ioport = pACB->IOPortBase;
+ sstatus = inb( ioport+Scsi_Status );
+ if( sstatus & INTERRUPT )
+ break;
+ else
+ pACB = pACB->pNextACB;
+ }
+ else
+ {
+ pACB = pACB->pNextACB;
+ }
+ }
+
+#ifdef DC390_DEBUG1
+ printk("sstatus=%2x,",sstatus);
+#endif
+
+ if( pACB == (PACB )-1 )
+ {
+ printk("DC390: Spurious interrupt detected!\n");
+ return;
+ }
+
+ istate = inb( ioport+Intern_State );
+ istatus = inb( ioport+INT_Status );
+
+#ifdef DC390_DEBUG1
+ printk("Istatus=%2x,",istatus);
+#endif
+
+ if(istatus & DISCONNECTED)
+ {
+ DC390_Disconnect( pACB );
+ return;
+ }
+
+ if(istatus & RESELECTED)
+ {
+ DC390_Reselect( pACB );
+ return;
+ }
+
+ if(istatus & INVALID_CMD)
+ {
+ DC390_InvalidCmd( pACB );
+ return;
+ }
+
+ if(istatus & SCSI_RESET)
+ {
+ DC390_ScsiRstDetect( pACB );
+ return;
+ }
+
+ if( istatus & (SUCCESSFUL_OP+SERVICE_REQUEST) )
+ {
+ pDCB = pACB->pActiveDCB;
+ pSRB = pDCB->pActiveSRB;
+ if( pDCB )
+ {
+ if( pDCB->DCBFlag & ABORT_DEV_ )
+ EnableMsgOut( pACB, pSRB );
+ }
+
+ phase = (USHORT) pSRB->ScsiPhase;
+ stateV = (void *) DC390_phase0[phase];
+ stateV( pACB, pSRB, &sstatus );
+
+ pSRB->ScsiPhase = sstatus & 7;
+ phase = (USHORT) sstatus & 7;
+ stateV = (void *) DC390_phase1[phase];
+ stateV( pACB, pSRB, &sstatus );
+ }
+}
+
+
+static void
+DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR sstatus, bval;
+ USHORT ioport;
+ PSGL psgl;
+ ULONG ResidCnt, xferCnt;
+
+ ioport = pACB->IOPortBase;
+ sstatus = *psstatus;
+
+ if( !(pSRB->SRBState & SRB_XFERPAD) )
+ {
+ if( sstatus & PARITY_ERR )
+ pSRB->SRBStatus |= PARITY_ERROR;
+
+ if( sstatus & COUNT_2_ZERO )
+ {
+ bval = inb(ioport+DMA_Status);
+ while( !(bval & DMA_XFER_DONE) )
+ bval = inb(ioport+DMA_Status);
+ pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
+ pSRB->SGIndex++;
+ if( pSRB->SGIndex < pSRB->SGcount )
+ {
+ pSRB->pSegmentList++;
+ psgl = pSRB->pSegmentList;
+
+#ifndef VERSION_ELF_1_2_13
+ pSRB->SGPhysAddr = virt_to_phys( psgl->address );
+#else
+ pSRB->SGPhysAddr = (ULONG) psgl->address;
+#endif
+ pSRB->SGToBeXferLen = (ULONG) psgl->length;
+ }
+ else
+ pSRB->SGToBeXferLen = 0;
+ }
+ else
+ {
+ bval = inb( ioport+Current_Fifo );
+ bval &= 0x1f;
+ ResidCnt = (ULONG) inb(ioport+CtcReg_High);
+ ResidCnt = ResidCnt << 8;
+ ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid);
+ ResidCnt = ResidCnt << 8;
+ ResidCnt |= (ULONG) inb(ioport+CtcReg_Low);
+ ResidCnt += (ULONG) bval;
+
+ xferCnt = pSRB->SGToBeXferLen - ResidCnt;
+ pSRB->SGPhysAddr += xferCnt;
+ pSRB->TotalXferredLen += xferCnt;
+ pSRB->SGToBeXferLen = ResidCnt;
+ }
+ }
+ bval = WRITE_DIRECTION+DMA_IDLE_CMD;
+ outb( bval, ioport+DMA_Cmd);
+}
+
+static void
+DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR sstatus, bval;
+ USHORT i, ioport, residual;
+ PSGL psgl;
+ ULONG ResidCnt, xferCnt;
+ PUCHAR ptr;
+
+
+ ioport = pACB->IOPortBase;
+ sstatus = *psstatus;
+
+ if( !(pSRB->SRBState & SRB_XFERPAD) )
+ {
+ if( sstatus & PARITY_ERR )
+ pSRB->SRBStatus |= PARITY_ERROR;
+
+ if( sstatus & COUNT_2_ZERO )
+ {
+ bval = inb(ioport+DMA_Status);
+ while( !(bval & DMA_XFER_DONE) )
+ bval = inb(ioport+DMA_Status);
+
+ bval = READ_DIRECTION+DMA_IDLE_CMD;
+ outb( bval, ioport+DMA_Cmd);
+
+ pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
+ pSRB->SGIndex++;
+ if( pSRB->SGIndex < pSRB->SGcount )
+ {
+ pSRB->pSegmentList++;
+ psgl = pSRB->pSegmentList;
+
+#ifndef VERSION_ELF_1_2_13
+ pSRB->SGPhysAddr = virt_to_phys( psgl->address );
+#else
+ pSRB->SGPhysAddr = (ULONG) psgl->address;
+#endif
+ pSRB->SGToBeXferLen = (ULONG) psgl->length;
+ }
+ else
+ pSRB->SGToBeXferLen = 0;
+ }
+ else /* phase changed */
+ {
+ residual = 0;
+ bval = inb(ioport+Current_Fifo);
+ while( bval & 0x1f )
+ {
+ if( (bval & 0x1f) == 1 )
+ {
+ for(i=0; i< 0x100; i++)
+ {
+ bval = inb(ioport+Current_Fifo);
+ if( !(bval & 0x1f) )
+ goto din_1;
+ else if( i == 0x0ff )
+ {
+ residual = 1; /* ;1 residual byte */
+ goto din_1;
+ }
+ }
+ }
+ else
+ bval = inb(ioport+Current_Fifo);
+ }
+din_1:
+ bval = READ_DIRECTION+DMA_BLAST_CMD;
+ outb(bval, ioport+DMA_Cmd);
+ for(i=0; i<0x8000; i++)
+ {
+ bval = inb(ioport+DMA_Status);
+ if(bval & BLAST_COMPLETE)
+ break;
+ }
+ bval = READ_DIRECTION+DMA_IDLE_CMD;
+ outb(bval, ioport+DMA_Cmd);
+
+ ResidCnt = (ULONG) inb(ioport+CtcReg_High);
+ ResidCnt = ResidCnt << 8;
+ ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid);
+ ResidCnt = ResidCnt << 8;
+ ResidCnt |= (ULONG) inb(ioport+CtcReg_Low);
+
+ xferCnt = pSRB->SGToBeXferLen - ResidCnt;
+ pSRB->SGPhysAddr += xferCnt;
+ pSRB->TotalXferredLen += xferCnt;
+ pSRB->SGToBeXferLen = ResidCnt;
+
+ if( residual )
+ {
+ bval = inb(ioport+ScsiFifo); /* get residual byte */
+#ifndef VERSION_ELF_1_2_13
+ ptr = (PUCHAR) phys_to_virt( pSRB->SGPhysAddr );
+#else
+ ptr = (PUCHAR) pSRB->SGPhysAddr;
+#endif
+ *ptr = bval;
+ pSRB->SGPhysAddr++;
+ pSRB->TotalXferredLen++;
+ pSRB->SGToBeXferLen--;
+ }
+ }
+ }
+}
+
+static void
+DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+}
+
+static void
+DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR bval;
+ USHORT ioport;
+
+ ioport = pACB->IOPortBase;
+ bval = inb(ioport+ScsiFifo);
+ pSRB->TargetStatus = bval;
+ bval++;
+ bval = inb(ioport+ScsiFifo); /* get message */
+ pSRB->EndMessage = bval;
+
+ *psstatus = SCSI_NOP0;
+ pSRB->SRBState = SRB_COMPLETED;
+ bval = MSG_ACCEPTED_CMD;
+ outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) )
+ *psstatus = SCSI_NOP0;
+}
+
+static void
+DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR bval;
+ USHORT ioport, wval, wval1;
+ PDCB pDCB;
+ PSRB psrb;
+
+ ioport = pACB->IOPortBase;
+ pDCB = pACB->pActiveDCB;
+
+ bval = inb( ioport+ScsiFifo );
+ if( !(pSRB->SRBState & SRB_MSGIN_MULTI) )
+ {
+ if(bval == MSG_DISCONNECT)
+ {
+ pSRB->SRBState = SRB_DISCONNECT;
+ }
+ else if( bval == MSG_SAVE_PTR )
+ goto min6;
+ else if( (bval == MSG_EXTENDED) || ((bval >= MSG_SIMPLE_QTAG) &&
+ (bval <= MSG_ORDER_QTAG)) )
+ {
+ pSRB->SRBState |= SRB_MSGIN_MULTI;
+ pSRB->MsgInBuf[0] = bval;
+ pSRB->MsgCnt = 1;
+ pSRB->pMsgPtr = &pSRB->MsgInBuf[1];
+ }
+ else if(bval == MSG_REJECT_)
+ {
+ bval = RESET_ATN_CMD;
+ outb(bval, ioport+ScsiCmd);
+ if( pSRB->SRBState & DO_SYNC_NEGO)
+ goto set_async;
+ }
+ else if( bval == MSG_RESTORE_PTR)
+ goto min6;
+ else
+ goto min6;
+ }
+ else
+ { /* minx: */
+
+ *pSRB->pMsgPtr = bval;
+ pSRB->MsgCnt++;
+ pSRB->pMsgPtr++;
+ if( (pSRB->MsgInBuf[0] >= MSG_SIMPLE_QTAG) &&
+ (pSRB->MsgInBuf[0] <= MSG_ORDER_QTAG) )
+ {
+ if( pSRB->MsgCnt == 2)
+ {
+ pSRB->SRBState = 0;
+ bval = pSRB->MsgInBuf[1];
+ pSRB = pDCB->pGoingSRB;
+ psrb = pDCB->pGoingLast;
+ if( pSRB )
+ {
+ for( ;; )
+ {
+ if(pSRB->TagNumber != bval)
+ {
+ if( pSRB == psrb )
+ goto mingx0;
+ pSRB = pSRB->pNextSRB;
+ }
+ else
+ break;
+ }
+ if( pDCB->DCBFlag & ABORT_DEV_ )
+ {
+ pSRB->SRBState = SRB_ABORT_SENT;
+ EnableMsgOut( pACB, pSRB );
+ }
+ if( !(pSRB->SRBState & SRB_DISCONNECT) )
+ goto mingx0;
+ pDCB->pActiveSRB = pSRB;
+ pSRB->SRBState = SRB_DATA_XFER;
+ }
+ else
+ {
+mingx0:
+ pSRB = pACB->pTmpSRB;
+ pSRB->SRBState = SRB_UNEXPECT_RESEL;
+ pDCB->pActiveSRB = pSRB;
+ pSRB->MsgOutBuf[0] = MSG_ABORT_TAG;
+ EnableMsgOut2( pACB, pSRB );
+ }
+ }
+ }
+ else if( (pSRB->MsgInBuf[0] == MSG_EXTENDED) && (pSRB->MsgCnt == 5) )
+ {
+ pSRB->SRBState &= ~(SRB_MSGIN_MULTI+DO_SYNC_NEGO);
+ if( (pSRB->MsgInBuf[1] != 3) || (pSRB->MsgInBuf[2] != 1) )
+ { /* reject_msg: */
+ pSRB->MsgCnt = 1;
+ pSRB->MsgInBuf[0] = MSG_REJECT_;
+ bval = SET_ATN_CMD;
+ outb(bval, ioport+ScsiCmd);
+ }
+ else if( !(pSRB->MsgInBuf[3]) || !(pSRB->MsgInBuf[4]) )
+ {
+set_async:
+ pDCB = pSRB->pSRBDCB;
+ pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE);
+ pDCB->SyncPeriod = 0;
+ pDCB->SyncOffset = 0;
+ pDCB->CtrlR3 = FAST_CLK; /* ;non_fast */
+ pDCB->CtrlR4 &= 0x3f;
+ pDCB->CtrlR4 |= EATER_25NS; /* ; 25ns glitch eater */
+ goto re_prog;
+ }
+ else
+ { /* set_sync: */
+
+ pDCB = pSRB->pSRBDCB;
+ pDCB->SyncMode |= SYNC_ENABLE+SYNC_NEGO_DONE;
+ pDCB->SyncOffset &= 0x0f0;
+ pDCB->SyncOffset |= pSRB->MsgInBuf[4];
+ pDCB->NegoPeriod = pSRB->MsgInBuf[3];
+ wval = (USHORT) pSRB->MsgInBuf[3];
+ wval = wval << 2;
+ wval--;
+ wval1 = wval / 25;
+ if( (wval1 * 25) != wval)
+ wval1++;
+ bval = FAST_CLK+FAST_SCSI;
+ pDCB->CtrlR4 &= 0x3f;
+ if(wval1 >= 8)
+ {
+ wval1--;
+ bval = FAST_CLK; /* ;fast clock/normal scsi */
+ pDCB->CtrlR4 |= EATER_25NS; /* ;25 ns glitch eater */
+ }
+ pDCB->CtrlR3 = bval;
+ pDCB->SyncPeriod = (UCHAR)wval1;
+re_prog:
+ bval = pDCB->SyncPeriod;
+ outb(bval, ioport+Sync_Period);
+ bval = pDCB->SyncOffset;
+ outb(bval, ioport+Sync_Offset);
+ bval = pDCB->CtrlR3;
+ outb(bval, ioport+CtrlReg3);
+ bval = pDCB->CtrlR4;
+ outb(bval, ioport+CtrlReg4);
+ SetXferRate( pACB, pDCB);
+ }
+ }
+ }
+min6:
+ *psstatus = SCSI_NOP0;
+ bval = MSG_ACCEPTED_CMD;
+ outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DataIO_Comm( PACB pACB, PSRB pSRB, UCHAR ioDir)
+{
+ PSGL psgl;
+ UCHAR bval;
+ USHORT ioport;
+ ULONG lval;
+
+
+ ioport = pACB->IOPortBase;
+ if( pSRB->SGIndex < pSRB->SGcount )
+ {
+ bval = DMA_IDLE_CMD | ioDir; /* ;+EN_DMA_INT */
+ outb( bval, ioport+DMA_Cmd);
+ if( !pSRB->SGToBeXferLen )
+ {
+ psgl = pSRB->pSegmentList;
+#ifndef VERSION_ELF_1_2_13
+ pSRB->SGPhysAddr = virt_to_phys( psgl->address );
+#else
+ pSRB->SGPhysAddr = (ULONG) psgl->address;
+#endif
+ pSRB->SGToBeXferLen = (ULONG) psgl->length;
+ }
+ lval = pSRB->SGToBeXferLen;
+ bval = (UCHAR) lval;
+ outb(bval,ioport+CtcReg_Low);
+ lval = lval >> 8;
+ bval = (UCHAR) lval;
+ outb(bval,ioport+CtcReg_Mid);
+ lval = lval >> 8;
+ bval = (UCHAR) lval;
+ outb(bval,ioport+CtcReg_High);
+
+ lval = pSRB->SGToBeXferLen;
+ outl(lval, ioport+DMA_XferCnt);
+
+ lval = pSRB->SGPhysAddr;
+ outl( lval, ioport+DMA_XferAddr);
+
+ bval = DMA_COMMAND+INFO_XFER_CMD;
+ outb(bval, ioport+ScsiCmd);
+
+ pSRB->SRBState = SRB_DATA_XFER;
+
+ bval = DMA_IDLE_CMD | ioDir; /* ;+EN_DMA_INT */
+ outb(bval, ioport+DMA_Cmd);
+
+ bval = DMA_START_CMD | ioDir; /* ;+EN_DMA_INT */
+ outb(bval, ioport+DMA_Cmd);
+ }
+ else /* xfer pad */
+ {
+ if( pSRB->SGcount )
+ {
+ pSRB->AdaptStatus = H_OVER_UNDER_RUN;
+ pSRB->SRBStatus |= OVER_RUN;
+ }
+ bval = 0;
+ outb(bval,ioport+CtcReg_Low);
+ outb(bval,ioport+CtcReg_Mid);
+ outb(bval,ioport+CtcReg_High);
+
+ pSRB->SRBState |= SRB_XFERPAD;
+ bval = DMA_COMMAND+XFER_PAD_BYTE;
+ outb(bval, ioport+ScsiCmd);
+/*
+ bval = DMA_IDLE_CMD | ioDir; ;+EN_DMA_INT
+ outb(bval, ioport+DMA_Cmd);
+ bval = DMA_START_CMD | ioDir; ;+EN_DMA_INT
+ outb(bval, ioport+DMA_Cmd);
+*/
+ }
+}
+
+
+static void
+DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR ioDir;
+
+ ioDir = WRITE_DIRECTION;
+ DataIO_Comm( pACB, pSRB, ioDir);
+}
+
+static void
+DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR ioDir;
+
+ ioDir = READ_DIRECTION;
+ DataIO_Comm( pACB, pSRB, ioDir);
+}
+
+static void
+DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ PDCB pDCB;
+ UCHAR bval;
+ PUCHAR ptr;
+ USHORT ioport, i, cnt;
+
+
+ ioport = pACB->IOPortBase;
+ bval = RESET_ATN_CMD;
+ outb(bval, ioport+ScsiCmd);
+ bval = CLEAR_FIFO_CMD;
+ outb(bval, ioport+ScsiCmd);
+ if( !(pSRB->SRBFlag & AUTO_REQSENSE) )
+ {
+ cnt = (USHORT) pSRB->ScsiCmdLen;
+ ptr = (PUCHAR) pSRB->CmdBlock;
+ for(i=0; i < cnt; i++)
+ {
+ outb(*ptr, ioport+ScsiFifo);
+ ptr++;
+ }
+ }
+ else
+ {
+ bval = REQUEST_SENSE;
+ outb(bval, ioport+ScsiFifo);
+ pDCB = pACB->pActiveDCB;
+ bval = pDCB->IdentifyMsg << 5;
+ outb(bval, ioport+ScsiFifo);
+ bval = 0;
+ outb(bval, ioport+ScsiFifo);
+ outb(bval, ioport+ScsiFifo);
+ bval = sizeof(pSRB->pcmd->sense_buffer);
+ outb(bval, ioport+ScsiFifo);
+ bval = 0;
+ outb(bval, ioport+ScsiFifo);
+ }
+ pSRB->SRBState = SRB_COMMAND;
+ bval = INFO_XFER_CMD;
+ outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR bval;
+ USHORT ioport;
+
+ ioport = pACB->IOPortBase;
+ bval = CLEAR_FIFO_CMD;
+ outb(bval, ioport+ScsiCmd);
+ pSRB->SRBState = SRB_STATUS;
+ bval = INITIATOR_CMD_CMPLTE;
+ outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR bval;
+ USHORT ioport, i, cnt;
+ PUCHAR ptr;
+ PDCB pDCB;
+
+ ioport = pACB->IOPortBase;
+ bval = CLEAR_FIFO_CMD;
+ outb(bval, ioport+ScsiCmd);
+ pDCB = pACB->pActiveDCB;
+ if( !(pSRB->SRBState & SRB_MSGOUT) )
+ {
+ cnt = pSRB->MsgCnt;
+ if( cnt )
+ {
+ ptr = (PUCHAR) pSRB->MsgOutBuf;
+ for(i=0; i < cnt; i++)
+ {
+ outb(*ptr, ioport+ScsiFifo);
+ ptr++;
+ }
+ pSRB->MsgCnt = 0;
+ if( (pDCB->DCBFlag & ABORT_DEV_) &&
+ (pSRB->MsgOutBuf[0] == MSG_ABORT) )
+ pSRB->SRBState = SRB_ABORT_SENT;
+ }
+ else
+ {
+ bval = MSG_ABORT; /* ??? MSG_NOP */
+ if( (pSRB->CmdBlock[0] == INQUIRY ) ||
+ (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
+ (pSRB->SRBFlag & AUTO_REQSENSE) )
+ {
+ if( pDCB->SyncMode & SYNC_ENABLE )
+ goto mop1;
+ }
+ outb(bval, ioport+ScsiFifo);
+ }
+ bval = INFO_XFER_CMD;
+ outb( bval, ioport+ScsiCmd);
+ }
+ else
+ {
+mop1:
+ bval = MSG_EXTENDED;
+ outb(bval, ioport+ScsiFifo);
+ bval = 3; /* ;length of extended msg */
+ outb(bval, ioport+ScsiFifo);
+ bval = 1; /* ; sync nego */
+ outb(bval, ioport+ScsiFifo);
+ bval = pDCB->NegoPeriod;
+ outb(bval, ioport+ScsiFifo);
+ bval = SYNC_NEGO_OFFSET;
+ outb(bval, ioport+ScsiFifo);
+ pSRB->SRBState |= DO_SYNC_NEGO;
+ bval = INFO_XFER_CMD;
+ outb(bval, ioport+ScsiCmd);
+ }
+}
+
+static void
+DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+ UCHAR bval;
+ USHORT ioport;
+
+ ioport = pACB->IOPortBase;
+ bval = CLEAR_FIFO_CMD;
+ outb(bval, ioport+ScsiCmd);
+ if( !(pSRB->SRBState & SRB_MSGIN) )
+ {
+ pSRB->SRBState &= SRB_DISCONNECT;
+ pSRB->SRBState |= SRB_MSGIN;
+ }
+ bval = INFO_XFER_CMD;
+ outb(bval, ioport+ScsiCmd);
+}
+
+static void
+DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+}
+
+static void
+DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus)
+{
+}
+
+
+static void
+SetXferRate( PACB pACB, PDCB pDCB )
+{
+ UCHAR bval;
+ USHORT cnt, i;
+ PDCB ptr;
+
+ if( !(pDCB->IdentifyMsg & 0x07) )
+ {
+ if( pACB->scan_devices )
+ {
+ CurrSyncOffset = pDCB->SyncOffset;
+ }
+ else
+ {
+ ptr = pACB->pLinkDCB;
+ cnt = pACB->DeviceCnt;
+ bval = pDCB->UnitSCSIID;
+ for(i=0; i<cnt; i++)
+ {
+ if( ptr->UnitSCSIID == bval )
+ {
+ ptr->SyncPeriod = pDCB->SyncPeriod;
+ ptr->SyncOffset = pDCB->SyncOffset;
+ ptr->CtrlR3 = pDCB->CtrlR3;
+ ptr->CtrlR4 = pDCB->CtrlR4;
+ ptr->SyncMode = pDCB->SyncMode;
+ }
+ ptr = ptr->pNextDCB;
+ }
+ }
+ }
+ return;
+}
+
+
+static void
+DC390_Disconnect( PACB pACB )
+{
+ PDCB pDCB;
+ PSRB pSRB, psrb;
+ ULONG flags;
+ USHORT ioport, i, cnt;
+ UCHAR bval;
+
+#ifdef DC390_DEBUG0
+ printk("DISC,");
+#endif
+
+ save_flags(flags);
+ cli();
+ ioport = pACB->IOPortBase;
+ pDCB = pACB->pActiveDCB;
+ if (!pDCB)
+ {
+#ifdef DC390_DEBUG0
+ printk("ACB:%08lx->ActiveDCB:%08lx !,",(ULONG)pACB,(ULONG)pDCB);
+#endif
+ restore_flags(flags); return;
+ }
+ pSRB = pDCB->pActiveSRB;
+ pACB->pActiveDCB = 0;
+ pSRB->ScsiPhase = SCSI_NOP0;
+ bval = EN_SEL_RESEL;
+ outb(bval, ioport+ScsiCmd);
+ if( pSRB->SRBState & SRB_UNEXPECT_RESEL )
+ {
+ pSRB->SRBState = 0;
+ DoWaitingSRB( pACB );
+ }
+ else if( pSRB->SRBState & SRB_ABORT_SENT )
+ {
+ pDCB->TagMask = 0;
+ pDCB->DCBFlag = 0;
+ cnt = pDCB->GoingSRBCnt;
+ pDCB->GoingSRBCnt = 0;
+ pSRB = pDCB->pGoingSRB;
+ for( i=0; i < cnt; i++)
+ {
+ psrb = pSRB->pNextSRB;
+ pSRB->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = pSRB;
+ pSRB = psrb;
+ }
+ pDCB->pGoingSRB = 0;
+ DoWaitingSRB( pACB );
+ }
+ else
+ {
+ if( (pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) ||
+ !(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED)) )
+ { /* Selection time out */
+ if( !(pACB->scan_devices) )
+ {
+ pSRB->SRBState = SRB_READY;
+ RewaitSRB( pDCB, pSRB);
+ }
+ else
+ {
+ pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT;
+ goto disc1;
+ }
+ }
+ else if( pSRB->SRBState & SRB_DISCONNECT )
+ {
+ DoWaitingSRB( pACB );
+ }
+ else if( pSRB->SRBState & SRB_COMPLETED )
+ {
+disc1:
+ if(pDCB->MaxCommand > 1)
+ {
+ bval = pSRB->TagNumber;
+ pDCB->TagMask &= (~(1 << bval)); /* free tag mask */
+ }
+ pDCB->pActiveSRB = 0;
+ pSRB->SRBState = SRB_FREE;
+ SRBdone( pACB, pDCB, pSRB);
+ }
+ }
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+DC390_Reselect( PACB pACB )
+{
+ PDCB pDCB, pdcb;
+ PSRB pSRB;
+ USHORT ioport, wval;
+ UCHAR bval, bval1;
+
+
+#ifdef DC390_DEBUG0
+ printk("RSEL,");
+#endif
+ ioport = pACB->IOPortBase;
+ pDCB = pACB->pActiveDCB;
+ if( pDCB )
+ { /* Arbitration lost but Reselection win */
+ pSRB = pDCB->pActiveSRB;
+ if( !( pACB->scan_devices ) )
+ {
+ pSRB->SRBState = SRB_READY;
+ RewaitSRB( pDCB, pSRB);
+ }
+ }
+ bval = inb(ioport+ScsiFifo); /* get ID */
+ bval = bval ^ pACB->HostID_Bit;
+ wval = 0;
+ bval1 = 1;
+ for(;;)
+ {
+ if( !(bval & bval1) )
+ {
+ bval1 = bval1 << 1;
+ wval++;
+ }
+ else
+ break;
+ }
+ wval |= ( (USHORT) inb(ioport+ScsiFifo) & 7) << 8; /* get LUN */
+ pDCB = pACB->pLinkDCB;
+ pdcb = pDCB;
+ while( wval != *((PUSHORT) &pDCB->UnitSCSIID) )
+ {
+ pDCB = pDCB->pNextDCB;
+ if( pDCB == pdcb )
+ return;
+ }
+ pACB->pActiveDCB = pDCB;
+ if( pDCB->SyncMode & EN_TAG_QUEUING )
+ {
+ pSRB = pACB->pTmpSRB;
+ pDCB->pActiveSRB = pSRB;
+ }
+ else
+ {
+ pSRB = pDCB->pActiveSRB;
+ if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) )
+ {
+ pSRB= pACB->pTmpSRB;
+ pSRB->SRBState = SRB_UNEXPECT_RESEL;
+ pDCB->pActiveSRB = pSRB;
+ EnableMsgOut( pACB, pSRB );
+ }
+ else
+ {
+ if( pDCB->DCBFlag & ABORT_DEV_ )
+ {
+ pSRB->SRBState = SRB_ABORT_SENT;
+ EnableMsgOut( pACB, pSRB );
+ }
+ else
+ pSRB->SRBState = SRB_DATA_XFER;
+ }
+ }
+ pSRB->ScsiPhase = SCSI_NOP0;
+ bval = pDCB->UnitSCSIID;
+ outb( bval, ioport+Scsi_Dest_ID);
+ bval = pDCB->SyncPeriod;
+ outb(bval, ioport+Sync_Period);
+ bval = pDCB->SyncOffset;
+ outb( bval, ioport+Sync_Offset);
+ bval = pDCB->CtrlR1;
+ outb(bval, ioport+CtrlReg1);
+ bval = pDCB->CtrlR3;
+ outb(bval, ioport+CtrlReg3);
+ bval = pDCB->CtrlR4; /* ; Glitch eater */
+ outb(bval, ioport+CtrlReg4);
+ bval = MSG_ACCEPTED_CMD; /* ;to rls the /ACK signal */
+ outb(bval, ioport+ScsiCmd);
+}
+
+
+static void
+SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+ PSRB psrb;
+ UCHAR bval, bval1, i, j, status;
+ PSCSICMD pcmd;
+ PSCSI_INQDATA ptr;
+ USHORT disable_tag;
+ ULONG flags;
+ PSGL ptr2;
+ ULONG swlval;
+
+ pcmd = pSRB->pcmd;
+ status = pSRB->TargetStatus;
+ if(pSRB->SRBFlag & AUTO_REQSENSE)
+ {
+ pSRB->SRBFlag &= ~AUTO_REQSENSE;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = SCSI_STAT_CHECKCOND;
+ if(status == SCSI_STAT_CHECKCOND)
+ {
+ pcmd->result = DID_BAD_TARGET << 16;
+ goto ckc_e;
+ }
+ if(pSRB->RetryCnt == 0)
+ {
+ *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
+ pSRB->TotalXferredLen = pSRB->Segment1[1];
+ if( (pSRB->TotalXferredLen) &&
+ (pSRB->TotalXferredLen >= pcmd->underflow) )
+ pcmd->result |= (DID_OK << 16);
+ else
+ pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) |
+ SCSI_STAT_CHECKCOND;
+#ifdef DC390_DEBUG0
+ printk("Cmd=%2x,Result=%8x,XferL=%8x,",pSRB->CmdBlock[0],
+ (UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);
+#endif
+ goto ckc_e;
+ }
+ else
+ {
+ pSRB->RetryCnt--;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
+ *((PULONG) &(pSRB->CmdBlock[4])) = pSRB->Segment0[1];
+ if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
+ {
+ pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) |
+ SCSI_STAT_CHECKCOND;
+ goto ckc_e;
+ }
+ pcmd->result |= (DRIVER_SENSE << 24);
+ pSRB->SGcount = (UCHAR) pSRB->Segment1[0];
+ pSRB->ScsiCmdLen = (UCHAR) (pSRB->Segment1[0] >> 8);
+ pSRB->SGIndex = 0;
+ pSRB->TotalXferredLen = 0;
+ pSRB->SGToBeXferLen = 0;
+ if( pcmd->use_sg )
+ pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+ else if( pcmd->request_buffer )
+ {
+ pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+ pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+ pSRB->Segmentx.length = pcmd->request_bufflen;
+ }
+ if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
+ RewaitSRB( pDCB, pSRB );
+ return;
+ }
+ }
+ if( status )
+ {
+ if( status == SCSI_STAT_CHECKCOND)
+ {
+ if( (pSRB->SGIndex < pSRB->SGcount) && (pSRB->SGcount) && (pSRB->SGToBeXferLen) )
+ {
+ bval = pSRB->SGcount;
+ swlval = 0;
+ ptr2 = pSRB->pSegmentList;
+ for( i=pSRB->SGIndex; i < bval; i++)
+ {
+ swlval += ptr2->length;
+ ptr2++;
+ }
+#ifdef DC390_DEBUG0
+ printk("XferredLen=%8x,NotXferLen=%8x,",
+ (UINT) pSRB->TotalXferredLen, (UINT) swlval);
+#endif
+ }
+ RequestSense( pACB, pDCB, pSRB );
+ return;
+ }
+ else if( status == SCSI_STAT_QUEUEFULL )
+ {
+ bval = (UCHAR) pDCB->GoingSRBCnt;
+ bval--;
+ pDCB->MaxCommand = bval;
+ RewaitSRB( pDCB, pSRB );
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ return;
+ }
+ else if(status == SCSI_STAT_SEL_TIMEOUT)
+ {
+ pSRB->AdaptStatus = H_SEL_TIMEOUT;
+ pSRB->TargetStatus = 0;
+ pcmd->result = DID_BAD_TARGET << 16;
+ }
+ else
+ {
+ pSRB->AdaptStatus = 0;
+ if( pSRB->RetryCnt )
+ {
+ pSRB->RetryCnt--;
+ pSRB->TargetStatus = 0;
+ pSRB->SGIndex = 0;
+ pSRB->TotalXferredLen = 0;
+ pSRB->SGToBeXferLen = 0;
+ if( pcmd->use_sg )
+ pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+ else if( pcmd->request_buffer )
+ {
+ pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+ pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+ pSRB->Segmentx.length = pcmd->request_bufflen;
+ }
+ if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
+ RewaitSRB( pDCB, pSRB );
+ return;
+ }
+ else
+ {
+ pcmd->result |= (DID_ERROR << 16) | (ULONG) (pSRB->EndMessage << 8) |
+ (ULONG) status;
+ }
+ }
+ }
+ else
+ {
+ status = pSRB->AdaptStatus;
+ if(status & H_OVER_UNDER_RUN)
+ {
+ pSRB->TargetStatus = 0;
+ pcmd->result |= (DID_OK << 16) | (pSRB->EndMessage << 8);
+ }
+ else if( pSRB->SRBStatus & PARITY_ERROR)
+ {
+ pcmd->result |= (DID_PARITY << 16) | (pSRB->EndMessage << 8);
+ }
+ else /* No error */
+ {
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ pcmd->result |= (DID_OK << 16);
+ }
+ }
+
+ckc_e:
+ if( pACB->scan_devices )
+ {
+ if( pSRB->CmdBlock[0] == TEST_UNIT_READY )
+ {
+ if(pcmd->result != (DID_OK << 16))
+ {
+ if( pcmd->result & SCSI_STAT_CHECKCOND )
+ {
+ goto RTN_OK;
+ }
+ else
+ {
+ pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun);
+ pPrevDCB->pNextDCB = pACB->pLinkDCB;
+ if( (pcmd->target == pACB->max_id) &&
+ ((pcmd->lun == 0) || (pcmd->lun == pACB->max_lun)) )
+ {
+ pACB->scan_devices = 0;
+ }
+ }
+ }
+ else
+ {
+RTN_OK:
+ pPrevDCB->pNextDCB = pDCB;
+ pDCB->pNextDCB = pACB->pLinkDCB;
+ if( (pcmd->target == pACB->max_id) && (pcmd->lun == pACB->max_lun) )
+ pACB->scan_devices = END_SCAN;
+ }
+ }
+ else if( pSRB->CmdBlock[0] == INQUIRY )
+ {
+ if( (pcmd->target == pACB->max_id) &&
+ (pcmd->lun == pACB->max_lun) )
+ {
+ pACB->scan_devices = 0;
+ }
+ ptr = (PSCSI_INQDATA) (pcmd->request_buffer);
+ if( pcmd->use_sg )
+ ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address);
+ bval1 = ptr->DevType & SCSI_DEVTYPE;
+ if(bval1 == SCSI_NODEV)
+ {
+ pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun);
+ pPrevDCB->pNextDCB = pACB->pLinkDCB;
+ }
+ else
+ {
+ pACB->DeviceCnt++;
+ pPrevDCB = pDCB;
+ pACB->pDCB_free = (PDCB) ((ULONG) (pACB->pDCB_free) + sizeof( DC390_DCB ));
+ pDCB->DevType = bval1;
+ if(bval1 == TYPE_DISK || bval1 == TYPE_MOD)
+ {
+ if( (((ptr->Vers & 0x07) >= 2) || ((ptr->RDF & 0x0F) == 2)) &&
+ (ptr->Flags & SCSI_INQ_CMDQUEUE) &&
+ (pDCB->DevMode & TAG_QUEUING_) &&
+ (pDCB->DevMode & EN_DISCONNECT_) )
+ {
+ disable_tag = 0;
+ for(i=0; i<BADDEVCNT; i++)
+ {
+ for(j=0; j<28; j++)
+ {
+ if( ((PUCHAR)ptr)[8+j] != baddevname1[i][j])
+ break;
+ }
+ if(j == 28)
+ {
+ disable_tag = 1;
+ break;
+ }
+ }
+
+ if( !disable_tag )
+ {
+ pDCB->MaxCommand = pACB->TagMaxNum;
+ pDCB->SyncMode |= EN_TAG_QUEUING;
+ pDCB->TagMask = 0;
+ }
+ else
+ {
+ pDCB->SyncMode |= EN_ATN_STOP;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ save_flags( flags );
+ cli();
+/* ReleaseSRB( pDCB, pSRB ); */
+
+ if(pSRB == pDCB->pGoingSRB )
+ {
+ pDCB->pGoingSRB = pSRB->pNextSRB;
+ }
+ else
+ {
+ psrb = pDCB->pGoingSRB;
+ while( psrb->pNextSRB != pSRB )
+ psrb = psrb->pNextSRB;
+ psrb->pNextSRB = pSRB->pNextSRB;
+ if( pSRB == pDCB->pGoingLast )
+ pDCB->pGoingLast = psrb;
+ }
+ pSRB->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = pSRB;
+ pDCB->GoingSRBCnt--;
+
+ DoWaitingSRB( pACB );
+ restore_flags(flags);
+
+/* Notify cmd done */
+ pcmd->scsi_done( pcmd );
+
+ if( pDCB->QIORBCnt )
+ DoNextCmd( pACB, pDCB );
+ return;
+}
+
+
+static void
+DoingSRB_Done( PACB pACB )
+{
+ PDCB pDCB, pdcb;
+ PSRB psrb, psrb2;
+ USHORT cnt, i;
+ PSCSICMD pcmd;
+
+ pDCB = pACB->pLinkDCB;
+ pdcb = pDCB;
+ do
+ {
+ cnt = pdcb->GoingSRBCnt;
+ psrb = pdcb->pGoingSRB;
+ for( i=0; i<cnt; i++)
+ {
+ psrb2 = psrb->pNextSRB;
+ pcmd = psrb->pcmd;
+ pcmd->result = DID_RESET << 16;
+
+/* ReleaseSRB( pDCB, pSRB ); */
+
+ psrb->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = psrb;
+
+ pcmd->scsi_done( pcmd );
+ psrb = psrb2;
+ }
+ pdcb->GoingSRBCnt = 0;;
+ pdcb->pGoingSRB = NULL;
+ pdcb->TagMask = 0;
+ pdcb = pdcb->pNextDCB;
+ }
+ while( pdcb != pDCB );
+}
+
+
+static void
+DC390_ResetSCSIBus( PACB pACB )
+{
+ USHORT ioport;
+ UCHAR bval;
+ ULONG flags;
+
+ save_flags(flags);
+ cli();
+ pACB->ACBFlag |= RESET_DEV;
+ ioport = pACB->IOPortBase;
+
+ bval = DMA_IDLE_CMD;
+ outb(bval,ioport+DMA_Cmd);
+
+ bval = RST_SCSI_BUS_CMD;
+ outb(bval,ioport+ScsiCmd);
+
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+DC390_ScsiRstDetect( PACB pACB )
+{
+ ULONG wlval, flags;
+ USHORT ioport;
+ UCHAR bval;
+
+#ifdef DC390_DEBUG0
+ printk("RST_DETEC");
+#endif
+ save_flags(flags);
+ sti();
+ wlval = jiffies + HZ;
+ while( jiffies < wlval ); /* delay 1 sec */
+
+ cli();
+ ioport = pACB->IOPortBase;
+ bval = DMA_IDLE_CMD;
+ outb(bval,ioport+DMA_Cmd);
+ bval = CLEAR_FIFO_CMD;
+ outb(bval,ioport+ScsiCmd);
+
+ if( pACB->ACBFlag & RESET_DEV )
+ pACB->ACBFlag |= RESET_DONE;
+ else
+ {
+ pACB->ACBFlag |= RESET_DETECT;
+
+ ResetDevParam( pACB );
+/* DoingSRB_Done( pACB ); ???? */
+ RecoverSRB( pACB );
+ pACB->pActiveDCB = NULL;
+ pACB->ACBFlag = 0;
+ DoWaitingSRB( pACB );
+ }
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB )
+{
+ PSCSICMD pcmd;
+
+ pSRB->SRBFlag |= AUTO_REQSENSE;
+ pSRB->Segment0[0] = *((PULONG) &(pSRB->CmdBlock[0]));
+ pSRB->Segment0[1] = *((PULONG) &(pSRB->CmdBlock[4]));
+ pSRB->Segment1[0] = (ULONG) ((pSRB->ScsiCmdLen << 8) + pSRB->SGcount);
+ pSRB->Segment1[1] = pSRB->TotalXferredLen;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+
+ pcmd = pSRB->pcmd;
+
+ pSRB->Segmentx.address = (PUCHAR) &(pcmd->sense_buffer);
+ pSRB->Segmentx.length = sizeof(pcmd->sense_buffer);
+ pSRB->pSegmentList = &pSRB->Segmentx;
+ pSRB->SGcount = 1;
+ pSRB->SGIndex = 0;
+
+ *((PULONG) &(pSRB->CmdBlock[0])) = 0x00000003;
+ pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5;
+ *((PUSHORT) &(pSRB->CmdBlock[4])) = sizeof(pcmd->sense_buffer);
+ pSRB->ScsiCmdLen = 6;
+
+ pSRB->TotalXferredLen = 0;
+ pSRB->SGToBeXferLen = 0;
+ if( DC390_StartSCSI( pACB, pDCB, pSRB ) )
+ RewaitSRB( pDCB, pSRB );
+}
+
+
+static void
+EnableMsgOut2( PACB pACB, PSRB pSRB )
+{
+ USHORT ioport;
+ UCHAR bval;
+
+ ioport = pACB->IOPortBase;
+ pSRB->MsgCnt = 1;
+ bval = SET_ATN_CMD;
+ outb(bval, ioport+ScsiCmd);
+}
+
+
+static void
+EnableMsgOut( PACB pACB, PSRB pSRB )
+{
+ pSRB->MsgOutBuf[0] = MSG_ABORT;
+ EnableMsgOut2( pACB, pSRB );
+}
+
+
+static void
+DC390_InvalidCmd( PACB pACB )
+{
+ UCHAR bval;
+ USHORT ioport;
+ PSRB pSRB;
+
+ pSRB = pACB->pActiveDCB->pActiveSRB;
+ if( pSRB->SRBState & (SRB_START_+SRB_MSGOUT) )
+ {
+ ioport = pACB->IOPortBase;
+ bval = CLEAR_FIFO_CMD;
+ outb(bval,(ioport+ScsiCmd));
+ }
+}
+
diff --git a/linux/src/drivers/scsi/sd.c b/linux/src/drivers/scsi/sd.c
new file mode 100644
index 00000000..c647ab44
--- /dev/null
+++ b/linux/src/drivers/scsi/sd.c
@@ -0,0 +1,1635 @@
+/*
+ * sd.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * Linux scsi disk driver
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale ericy@cais.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Modified by Eric Youngdale eric@aib.com to support loadable
+ * low-level scsi drivers.
+ */
+
+#include <linux/module.h>
+#ifdef MODULE
+/*
+ * This is a variable in scsi.c that is set when we are processing something
+ * after boot time. By definition, this is true when we are a loadable module
+ * ourselves.
+ */
+#define MODULE_FLAG 1
+#else
+#define MODULE_FLAG scsi_loadable_module_flag
+#endif /* MODULE */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+
+#include <asm/system.h>
+
+#define MAJOR_NR SCSI_DISK_MAJOR
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include <scsi/scsi_ioctl.h>
+#include "constants.h"
+
+#include <linux/genhd.h>
+
+/*
+ * static const char RCSid[] = "$Header:";
+ */
+
+#define MAX_RETRIES 5
+
+/*
+ * Time out in seconds for disks and Magneto-opticals (which are slower).
+ */
+
+#define SD_TIMEOUT (20 * HZ)
+#define SD_MOD_TIMEOUT (25 * HZ)
+
+#define CLUSTERABLE_DEVICE(SC) (SC->host->use_clustering && \
+ SC->device->type != TYPE_MOD)
+
+struct hd_struct * sd;
+
+Scsi_Disk * rscsi_disks = NULL;
+static int * sd_sizes;
+static int * sd_blocksizes;
+static int * sd_hardsizes; /* Hardware sector size */
+
+extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+static int check_scsidisk_media_change(kdev_t);
+static int fop_revalidate_scsidisk(kdev_t);
+
+static int sd_init_onedisk(int);
+
+static void requeue_sd_request (Scsi_Cmnd * SCpnt);
+
+static int sd_init(void);
+static void sd_finish(void);
+static int sd_attach(Scsi_Device *);
+static int sd_detect(Scsi_Device *);
+static void sd_detach(Scsi_Device *);
+
+struct Scsi_Device_Template sd_template =
+{ NULL, "disk", "sd", NULL, TYPE_DISK,
+ SCSI_DISK_MAJOR, 0, 0, 0, 1,
+ sd_detect, sd_init,
+ sd_finish, sd_attach, sd_detach
+};
+
+static int sd_open(struct inode * inode, struct file * filp)
+{
+ int target;
+ target = DEVICE_NR(inode->i_rdev);
+
+ if(target >= sd_template.dev_max || !rscsi_disks[target].device)
+ return -ENXIO; /* No such device */
+
+ /*
+ * Make sure that only one process can do a check_change_disk at one time.
+ * This is also used to lock out further access when the partition table
+ * is being re-read.
+ */
+
+ while (rscsi_disks[target].device->busy)
+ barrier();
+ if(rscsi_disks[target].device->removable) {
+ check_disk_change(inode->i_rdev);
+
+ /*
+ * If the drive is empty, just let the open fail.
+ */
+ if ( !rscsi_disks[target].ready )
+ return -ENXIO;
+
+ /*
+ * Similarly, if the device has the write protect tab set,
+ * have the open fail if the user expects to be able to write
+ * to the thing.
+ */
+ if ( (rscsi_disks[target].write_prot) && (filp->f_mode & 2) )
+ return -EROFS;
+ }
+
+ /*
+ * See if we are requesting a non-existent partition. Do this
+ * after checking for disk change.
+ */
+ if(sd_sizes[MINOR(inode->i_rdev)] == 0)
+ return -ENXIO;
+
+ if(rscsi_disks[target].device->removable)
+ if(!rscsi_disks[target].device->access_count)
+ sd_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+
+ rscsi_disks[target].device->access_count++;
+ if (rscsi_disks[target].device->host->hostt->usage_count)
+ (*rscsi_disks[target].device->host->hostt->usage_count)++;
+ if(sd_template.usage_count) (*sd_template.usage_count)++;
+ return 0;
+}
+
+static void sd_release(struct inode * inode, struct file * file)
+{
+ int target;
+ fsync_dev(inode->i_rdev);
+
+ target = DEVICE_NR(inode->i_rdev);
+
+ rscsi_disks[target].device->access_count--;
+ if (rscsi_disks[target].device->host->hostt->usage_count)
+ (*rscsi_disks[target].device->host->hostt->usage_count)--;
+ if(sd_template.usage_count) (*sd_template.usage_count)--;
+
+ if(rscsi_disks[target].device->removable) {
+ if(!rscsi_disks[target].device->access_count)
+ sd_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ }
+}
+
+static void sd_geninit(struct gendisk *);
+
+static struct file_operations sd_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ sd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ sd_open, /* open code */
+ sd_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ check_scsidisk_media_change, /* Disk change */
+ fop_revalidate_scsidisk /* revalidate */
+};
+
+static struct gendisk sd_gendisk = {
+ MAJOR_NR, /* Major number */
+ "sd", /* Major name */
+ 4, /* Bits to shift to get real from partition */
+ 1 << 4, /* Number of partitions per real */
+ 0, /* maximum number of real */
+ sd_geninit, /* init function */
+ NULL, /* hd struct */
+ NULL, /* block sizes */
+ 0, /* number */
+ NULL, /* internal */
+ NULL /* next */
+};
+
+static void sd_geninit (struct gendisk *ignored)
+{
+ int i;
+
+ for (i = 0; i < sd_template.dev_max; ++i)
+ if(rscsi_disks[i].device)
+ sd[i << 4].nr_sects = rscsi_disks[i].capacity;
+#if 0
+ /* No longer needed - we keep track of this as we attach/detach */
+ sd_gendisk.nr_real = sd_template.dev_max;
+#endif
+}
+
+/*
+ * rw_intr is the interrupt routine for the device driver.
+ * It will be notified on the end of a SCSI read / write, and
+ * will take one of several actions based on success or failure.
+ */
+
+static void rw_intr (Scsi_Cmnd *SCpnt)
+{
+ int result = SCpnt->result;
+ int this_count = SCpnt->bufflen >> 9;
+ int good_sectors = (result == 0 ? this_count : 0);
+ int block_sectors = 1;
+
+#ifdef DEBUG
+ printk("sd%c : rw_intr(%d, %d)\n", 'a' + MINOR(SCpnt->request.rq_dev),
+ SCpnt->host->host_no, result);
+#endif
+
+ /*
+ Handle MEDIUM ERRORs that indicate partial success. Since this is a
+ relatively rare error condition, no care is taken to avoid unnecessary
+ additional work such as memcpy's that could be avoided.
+ */
+
+ if (driver_byte(result) != 0 && /* An error occurred */
+ SCpnt->sense_buffer[0] == 0xF0 && /* Sense data is valid */
+ SCpnt->sense_buffer[2] == MEDIUM_ERROR)
+ {
+ long error_sector = (SCpnt->sense_buffer[3] << 24) |
+ (SCpnt->sense_buffer[4] << 16) |
+ (SCpnt->sense_buffer[5] << 8) |
+ SCpnt->sense_buffer[6];
+ int sector_size =
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].sector_size;
+ if (SCpnt->request.bh != NULL)
+ block_sectors = SCpnt->request.bh->b_size >> 9;
+ if (sector_size == 1024)
+ {
+ error_sector <<= 1;
+ if (block_sectors < 2) block_sectors = 2;
+ }
+ else if (sector_size == 256)
+ error_sector >>= 1;
+ error_sector -= sd[MINOR(SCpnt->request.rq_dev)].start_sect;
+ error_sector &= ~ (block_sectors - 1);
+ good_sectors = error_sector - SCpnt->request.sector;
+ if (good_sectors < 0 || good_sectors >= this_count)
+ good_sectors = 0;
+ }
+
+ /*
+ * Handle RECOVERED ERRORs that indicate success after recovery action
+ * by the target device.
+ */
+
+ if (SCpnt->sense_buffer[0] == 0xF0 && /* Sense data is valid */
+ SCpnt->sense_buffer[2] == RECOVERED_ERROR)
+ {
+ printk("scsidisk recovered I/O error: dev %s, sector %lu, absolute sector %lu\n",
+ kdevname(SCpnt->request.rq_dev), SCpnt->request.sector,
+ SCpnt->request.sector + sd[MINOR(SCpnt->request.rq_dev)].start_sect);
+ good_sectors = this_count;
+ result = 0;
+ }
+
+ /*
+ * First case : we assume that the command succeeded. One of two things
+ * will happen here. Either we will be finished, or there will be more
+ * sectors that we were unable to read last time.
+ */
+
+ if (good_sectors > 0) {
+
+#ifdef DEBUG
+ printk("sd%c : %d sectors remain.\n", 'a' + MINOR(SCpnt->request.rq_dev),
+ SCpnt->request.nr_sectors);
+ printk("use_sg is %d\n ",SCpnt->use_sg);
+#endif
+ if (SCpnt->use_sg) {
+ struct scatterlist * sgpnt;
+ int i;
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+#ifdef DEBUG
+ printk(":%x %x %d\n",sgpnt[i].alt_address, sgpnt[i].address,
+ sgpnt[i].length);
+#endif
+ if (sgpnt[i].alt_address) {
+ if (SCpnt->request.cmd == READ)
+ memcpy(sgpnt[i].alt_address, sgpnt[i].address,
+ sgpnt[i].length);
+ scsi_free(sgpnt[i].address, sgpnt[i].length);
+ }
+ }
+
+ /* Free list of scatter-gather pointers */
+ scsi_free(SCpnt->buffer, SCpnt->sglist_len);
+ } else {
+ if (SCpnt->buffer != SCpnt->request.buffer) {
+#ifdef DEBUG
+ printk("nosg: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+#endif
+ if (SCpnt->request.cmd == READ)
+ memcpy(SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+ scsi_free(SCpnt->buffer, SCpnt->bufflen);
+ }
+ }
+ /*
+ * If multiple sectors are requested in one buffer, then
+ * they will have been finished off by the first command.
+ * If not, then we have a multi-buffer command.
+ */
+ if (SCpnt->request.nr_sectors > this_count)
+ {
+ SCpnt->request.errors = 0;
+
+ if (!SCpnt->request.bh)
+ {
+#ifdef DEBUG
+ printk("sd%c : handling page request, no buffer\n",
+ 'a' + MINOR(SCpnt->request.rq_dev));
+#endif
+ /*
+ * The SCpnt->request.nr_sectors field is always done in
+ * 512 byte sectors, even if this really isn't the case.
+ */
+ panic("sd.c: linked page request (%lx %x)",
+ SCpnt->request.sector, this_count);
+ }
+ }
+ SCpnt = end_scsi_request(SCpnt, 1, good_sectors);
+ if (result == 0)
+ {
+ requeue_sd_request(SCpnt);
+ return;
+ }
+ }
+
+ if (good_sectors == 0) {
+
+ /* Free up any indirection buffers we allocated for DMA purposes. */
+ if (SCpnt->use_sg) {
+ struct scatterlist * sgpnt;
+ int i;
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+#ifdef DEBUG
+ printk("err: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+#endif
+ if (sgpnt[i].alt_address) {
+ scsi_free(sgpnt[i].address, sgpnt[i].length);
+ }
+ }
+ scsi_free(SCpnt->buffer, SCpnt->sglist_len); /* Free list of scatter-gather pointers */
+ } else {
+#ifdef DEBUG
+ printk("nosgerr: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+#endif
+ if (SCpnt->buffer != SCpnt->request.buffer)
+ scsi_free(SCpnt->buffer, SCpnt->bufflen);
+ }
+ }
+
+ /*
+ * Now, if we were good little boys and girls, Santa left us a request
+ * sense buffer. We can extract information from this, so we
+ * can choose a block to remap, etc.
+ */
+
+ if (driver_byte(result) != 0) {
+ if (suggestion(result) == SUGGEST_REMAP) {
+#ifdef REMAP
+ /*
+ * Not yet implemented. A read will fail after being remapped,
+ * a write will call the strategy routine again.
+ */
+ if rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].remap
+ {
+ result = 0;
+ }
+ else
+#endif
+ }
+
+ if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) {
+ if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
+ if(rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->removable) {
+ /* detected disc change. set a bit and quietly refuse
+ * further access.
+ */
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->changed = 1;
+ SCpnt = end_scsi_request(SCpnt, 0, this_count);
+ requeue_sd_request(SCpnt);
+ return;
+ }
+ else
+ {
+ /*
+ * Must have been a power glitch, or a bus reset.
+ * Could not have been a media change, so we just retry
+ * the request and see what happens.
+ */
+ requeue_sd_request(SCpnt);
+ return;
+ }
+ }
+ }
+
+
+ /* If we had an ILLEGAL REQUEST returned, then we may have
+ * performed an unsupported command. The only thing this should be
+ * would be a ten byte read where only a six byte read was supported.
+ * Also, on a system where READ CAPACITY failed, we have read past
+ * the end of the disk.
+ */
+
+ if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
+ if (rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].ten) {
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].ten = 0;
+ requeue_sd_request(SCpnt);
+ result = 0;
+ } else {
+ /* ???? */
+ }
+ }
+
+ if (SCpnt->sense_buffer[2] == MEDIUM_ERROR) {
+ printk("scsi%d: MEDIUM ERROR on channel %d, id %d, lun %d, CDB: ",
+ SCpnt->host->host_no, (int) SCpnt->channel,
+ (int) SCpnt->target, (int) SCpnt->lun);
+ print_command(SCpnt->cmnd);
+ print_sense("sd", SCpnt);
+ SCpnt = end_scsi_request(SCpnt, 0, block_sectors);
+ requeue_sd_request(SCpnt);
+ return;
+ }
+ } /* driver byte != 0 */
+ if (result) {
+ printk("SCSI disk error : host %d channel %d id %d lun %d return code = %x\n",
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->host->host_no,
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->channel,
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->id,
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->lun, result);
+
+ if (driver_byte(result) & DRIVER_SENSE)
+ print_sense("sd", SCpnt);
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
+ requeue_sd_request(SCpnt);
+ return;
+ }
+}
+
+/*
+ * requeue_sd_request() is the request handler function for the sd driver.
+ * Its function in life is to take block device requests, and translate
+ * them to SCSI commands.
+ */
+
+static void do_sd_request (void)
+{
+ Scsi_Cmnd * SCpnt = NULL;
+ Scsi_Device * SDev;
+ struct request * req = NULL;
+ unsigned long flags;
+ int flag = 0;
+
+ save_flags(flags);
+ while (1==1){
+ cli();
+ if (CURRENT != NULL && CURRENT->rq_status == RQ_INACTIVE) {
+ restore_flags(flags);
+ return;
+ }
+
+ INIT_SCSI_REQUEST;
+ SDev = rscsi_disks[DEVICE_NR(CURRENT->rq_dev)].device;
+
+ /*
+ * I am not sure where the best place to do this is. We need
+ * to hook in a place where we are likely to come if in user
+ * space.
+ */
+ if( SDev->was_reset )
+ {
+ /*
+ * We need to relock the door, but we might
+ * be in an interrupt handler. Only do this
+ * from user space, since we do not want to
+ * sleep from an interrupt.
+ */
+ if( SDev->removable && !intr_count )
+ {
+ scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
+ /* scsi_ioctl may allow CURRENT to change, so start over. */
+ SDev->was_reset = 0;
+ continue;
+ }
+ SDev->was_reset = 0;
+ }
+
+ /* We have to be careful here. allocate_device will get a free pointer,
+ * but there is no guarantee that it is queueable. In normal usage,
+ * we want to call this, because other types of devices may have the
+ * host all tied up, and we want to make sure that we have at least
+ * one request pending for this type of device. We can also come
+ * through here while servicing an interrupt, because of the need to
+ * start another command. If we call allocate_device more than once,
+ * then the system can wedge if the command is not queueable. The
+ * request_queueable function is safe because it checks to make sure
+ * that the host is able to take another command before it returns
+ * a pointer.
+ */
+
+ if (flag++ == 0)
+ SCpnt = allocate_device(&CURRENT,
+ rscsi_disks[DEVICE_NR(CURRENT->rq_dev)].device, 0);
+ else SCpnt = NULL;
+
+ /*
+ * The following restore_flags leads to latency problems. FIXME.
+ * Using a "sti()" gets rid of the latency problems but causes
+ * race conditions and crashes.
+ */
+ restore_flags(flags);
+
+ /* This is a performance enhancement. We dig down into the request
+ * list and try to find a queueable request (i.e. device not busy,
+ * and host able to accept another command. If we find one, then we
+ * queue it. This can make a big difference on systems with more than
+ * one disk drive. We want to have the interrupts off when monkeying
+ * with the request list, because otherwise the kernel might try to
+ * slip in a request in between somewhere.
+ */
+
+ if (!SCpnt && sd_template.nr_dev > 1){
+ struct request *req1;
+ req1 = NULL;
+ cli();
+ req = CURRENT;
+ while(req){
+ SCpnt = request_queueable(req,
+ rscsi_disks[DEVICE_NR(req->rq_dev)].device);
+ if(SCpnt) break;
+ req1 = req;
+ req = req->next;
+ }
+ if (SCpnt && req->rq_status == RQ_INACTIVE) {
+ if (req == CURRENT)
+ CURRENT = CURRENT->next;
+ else
+ req1->next = req->next;
+ }
+ restore_flags(flags);
+ }
+
+ if (!SCpnt) return; /* Could not find anything to do */
+
+ /* Queue command */
+ requeue_sd_request(SCpnt);
+ } /* While */
+}
+
+static void requeue_sd_request (Scsi_Cmnd * SCpnt)
+{
+ int dev, devm, block, this_count;
+ unsigned char cmd[10];
+ int bounce_size, contiguous;
+ int max_sg;
+ struct buffer_head * bh, *bhp;
+ char * buff, *bounce_buffer;
+
+ repeat:
+
+ if(!SCpnt || SCpnt->request.rq_status == RQ_INACTIVE) {
+ do_sd_request();
+ return;
+ }
+
+ devm = MINOR(SCpnt->request.rq_dev);
+ dev = DEVICE_NR(SCpnt->request.rq_dev);
+
+ block = SCpnt->request.sector;
+ this_count = 0;
+
+#ifdef DEBUG
+ printk("Doing sd request, dev = %d, block = %d\n", devm, block);
+#endif
+
+ if (devm >= (sd_template.dev_max << 4) ||
+ !rscsi_disks[dev].device ||
+ block + SCpnt->request.nr_sectors > sd[devm].nr_sects)
+ {
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ }
+
+ block += sd[devm].start_sect;
+
+ if (rscsi_disks[dev].device->changed)
+ {
+ /*
+ * quietly refuse to do anything to a changed disc until the changed
+ * bit has been reset
+ */
+ /* printk("SCSI disk has been changed. Prohibiting further I/O.\n"); */
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ }
+
+#ifdef DEBUG
+ printk("sd%c : real dev = /dev/sd%c, block = %d\n",
+ 'a' + devm, dev, block);
+#endif
+
+ /*
+ * If we have a 1K hardware sectorsize, prevent access to single
+ * 512 byte sectors. In theory we could handle this - in fact
+ * the scsi cdrom driver must be able to handle this because
+ * we typically use 1K blocksizes, and cdroms typically have
+ * 2K hardware sectorsizes. Of course, things are simpler
+ * with the cdrom, since it is read-only. For performance
+ * reasons, the filesystems should be able to handle this
+ * and not force the scsi disk driver to use bounce buffers
+ * for this.
+ */
+ if (rscsi_disks[dev].sector_size == 1024)
+ if((block & 1) || (SCpnt->request.nr_sectors & 1)) {
+ printk("sd.c:Bad block number requested");
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ }
+
+ switch (SCpnt->request.cmd)
+ {
+ case WRITE :
+ if (!rscsi_disks[dev].device->writeable)
+ {
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ }
+ cmd[0] = WRITE_6;
+ break;
+ case READ :
+ cmd[0] = READ_6;
+ break;
+ default :
+ panic ("Unknown sd command %d\n", SCpnt->request.cmd);
+ }
+
+ SCpnt->this_count = 0;
+
+ /* If the host adapter can deal with very large scatter-gather
+ * requests, it is a waste of time to cluster
+ */
+ contiguous = (!CLUSTERABLE_DEVICE(SCpnt) ? 0 :1);
+ bounce_buffer = NULL;
+ bounce_size = (SCpnt->request.nr_sectors << 9);
+
+ /* First see if we need a bounce buffer for this request. If we do, make
+ * sure that we can allocate a buffer. Do not waste space by allocating
+ * a bounce buffer if we are straddling the 16Mb line
+ */
+ if (contiguous && SCpnt->request.bh &&
+ ((long) SCpnt->request.bh->b_data)
+ + (SCpnt->request.nr_sectors << 9) - 1 > ISA_DMA_THRESHOLD
+ && SCpnt->host->unchecked_isa_dma) {
+ if(((long) SCpnt->request.bh->b_data) > ISA_DMA_THRESHOLD)
+ bounce_buffer = (char *) scsi_malloc(bounce_size);
+ if(!bounce_buffer) contiguous = 0;
+ }
+
+ if(contiguous && SCpnt->request.bh && SCpnt->request.bh->b_reqnext)
+ for(bh = SCpnt->request.bh, bhp = bh->b_reqnext; bhp; bh = bhp,
+ bhp = bhp->b_reqnext) {
+ if(!CONTIGUOUS_BUFFERS(bh,bhp)) {
+ if(bounce_buffer) scsi_free(bounce_buffer, bounce_size);
+ contiguous = 0;
+ break;
+ }
+ }
+ if (!SCpnt->request.bh || contiguous) {
+
+ /* case of page request (i.e. raw device), or unlinked buffer */
+ this_count = SCpnt->request.nr_sectors;
+ buff = SCpnt->request.buffer;
+ SCpnt->use_sg = 0;
+
+ } else if (SCpnt->host->sg_tablesize == 0 ||
+ (need_isa_buffer && dma_free_sectors <= 10)) {
+
+ /* Case of host adapter that cannot scatter-gather. We also
+ * come here if we are running low on DMA buffer memory. We set
+ * a threshold higher than that we would need for this request so
+ * we leave room for other requests. Even though we would not need
+ * it all, we need to be conservative, because if we run low enough
+ * we have no choice but to panic.
+ */
+ if (SCpnt->host->sg_tablesize != 0 &&
+ need_isa_buffer &&
+ dma_free_sectors <= 10)
+ printk("Warning: SCSI DMA buffer space running low. Using non scatter-gather I/O.\n");
+
+ this_count = SCpnt->request.current_nr_sectors;
+ buff = SCpnt->request.buffer;
+ SCpnt->use_sg = 0;
+
+ } else {
+
+ /* Scatter-gather capable host adapter */
+ struct scatterlist * sgpnt;
+ int count, this_count_max;
+ int counted;
+
+ bh = SCpnt->request.bh;
+ this_count = 0;
+ this_count_max = (rscsi_disks[dev].ten ? 0xffff : 0xff);
+ count = 0;
+ bhp = NULL;
+ while(bh) {
+ if ((this_count + (bh->b_size >> 9)) > this_count_max) break;
+ if(!bhp || !CONTIGUOUS_BUFFERS(bhp,bh) ||
+ !CLUSTERABLE_DEVICE(SCpnt) ||
+ (SCpnt->host->unchecked_isa_dma &&
+ ((unsigned long) bh->b_data-1) == ISA_DMA_THRESHOLD)) {
+ if (count < SCpnt->host->sg_tablesize) count++;
+ else break;
+ }
+ this_count += (bh->b_size >> 9);
+ bhp = bh;
+ bh = bh->b_reqnext;
+ }
+#if 0
+ if(SCpnt->host->unchecked_isa_dma &&
+ ((unsigned int) SCpnt->request.bh->b_data-1) == ISA_DMA_THRESHOLD) count--;
+#endif
+ SCpnt->use_sg = count; /* Number of chains */
+ /* scsi_malloc can only allocate in chunks of 512 bytes */
+ count = (SCpnt->use_sg * sizeof(struct scatterlist) + 511) & ~511;
+
+ SCpnt->sglist_len = count;
+ max_sg = count / sizeof(struct scatterlist);
+ if(SCpnt->host->sg_tablesize < max_sg)
+ max_sg = SCpnt->host->sg_tablesize;
+ sgpnt = (struct scatterlist * ) scsi_malloc(count);
+ if (!sgpnt) {
+ printk("Warning - running *really* short on DMA buffers\n");
+ SCpnt->use_sg = 0; /* No memory left - bail out */
+ this_count = SCpnt->request.current_nr_sectors;
+ buff = SCpnt->request.buffer;
+ } else {
+ memset(sgpnt, 0, count); /* Zero so it is easy to fill, but only
+ * if memory is available
+ */
+ buff = (char *) sgpnt;
+ counted = 0;
+ for(count = 0, bh = SCpnt->request.bh, bhp = bh->b_reqnext;
+ count < SCpnt->use_sg && bh;
+ count++, bh = bhp) {
+
+ bhp = bh->b_reqnext;
+
+ if(!sgpnt[count].address) sgpnt[count].address = bh->b_data;
+ sgpnt[count].length += bh->b_size;
+ counted += bh->b_size >> 9;
+
+ if (((long) sgpnt[count].address) + sgpnt[count].length - 1 >
+ ISA_DMA_THRESHOLD && (SCpnt->host->unchecked_isa_dma) &&
+ !sgpnt[count].alt_address) {
+ sgpnt[count].alt_address = sgpnt[count].address;
+ /* We try to avoid exhausting the DMA pool, since it is
+ * easier to control usage here. In other places we might
+ * have a more pressing need, and we would be screwed if
+ * we ran out */
+ if(dma_free_sectors < (sgpnt[count].length >> 9) + 10) {
+ sgpnt[count].address = NULL;
+ } else {
+ sgpnt[count].address =
+ (char *) scsi_malloc(sgpnt[count].length);
+ }
+ /* If we start running low on DMA buffers, we abort the
+ * scatter-gather operation, and free all of the memory
+ * we have allocated. We want to ensure that all scsi
+ * operations are able to do at least a non-scatter/gather
+ * operation */
+ if(sgpnt[count].address == NULL){ /* Out of dma memory */
+#if 0
+ printk("Warning: Running low on SCSI DMA buffers");
+ /* Try switching back to a non s-g operation. */
+ while(--count >= 0){
+ if(sgpnt[count].alt_address)
+ scsi_free(sgpnt[count].address,
+ sgpnt[count].length);
+ }
+ this_count = SCpnt->request.current_nr_sectors;
+ buff = SCpnt->request.buffer;
+ SCpnt->use_sg = 0;
+ scsi_free(sgpnt, SCpnt->sglist_len);
+#endif
+ SCpnt->use_sg = count;
+ this_count = counted -= bh->b_size >> 9;
+ break;
+ }
+ }
+
+ /* Only cluster buffers if we know that we can supply DMA
+ * buffers large enough to satisfy the request. Do not cluster
+ * a new request if this would mean that we suddenly need to
+ * start using DMA bounce buffers */
+ if(bhp && CONTIGUOUS_BUFFERS(bh,bhp)
+ && CLUSTERABLE_DEVICE(SCpnt)) {
+ char * tmp;
+
+ if (((long) sgpnt[count].address) + sgpnt[count].length +
+ bhp->b_size - 1 > ISA_DMA_THRESHOLD &&
+ (SCpnt->host->unchecked_isa_dma) &&
+ !sgpnt[count].alt_address) continue;
+
+ if(!sgpnt[count].alt_address) {count--; continue; }
+ if(dma_free_sectors > 10)
+ tmp = (char *) scsi_malloc(sgpnt[count].length
+ + bhp->b_size);
+ else {
+ tmp = NULL;
+ max_sg = SCpnt->use_sg;
+ }
+ if(tmp){
+ scsi_free(sgpnt[count].address, sgpnt[count].length);
+ sgpnt[count].address = tmp;
+ count--;
+ continue;
+ }
+
+ /* If we are allowed another sg chain, then increment
+ * counter so we can insert it. Otherwise we will end
+ up truncating */
+
+ if (SCpnt->use_sg < max_sg) SCpnt->use_sg++;
+ } /* contiguous buffers */
+ } /* for loop */
+
+ /* This is actually how many we are going to transfer */
+ this_count = counted;
+
+ if(count < SCpnt->use_sg || SCpnt->use_sg
+ > SCpnt->host->sg_tablesize){
+ bh = SCpnt->request.bh;
+ printk("Use sg, count %d %x %d\n",
+ SCpnt->use_sg, count, dma_free_sectors);
+ printk("maxsg = %x, counted = %d this_count = %d\n",
+ max_sg, counted, this_count);
+ while(bh){
+ printk("[%p %lx] ", bh->b_data, bh->b_size);
+ bh = bh->b_reqnext;
+ }
+ if(SCpnt->use_sg < 16)
+ for(count=0; count<SCpnt->use_sg; count++)
+ printk("{%d:%p %p %d} ", count,
+ sgpnt[count].address,
+ sgpnt[count].alt_address,
+ sgpnt[count].length);
+ panic("Ooops");
+ }
+
+ if (SCpnt->request.cmd == WRITE)
+ for(count=0; count<SCpnt->use_sg; count++)
+ if(sgpnt[count].alt_address)
+ memcpy(sgpnt[count].address, sgpnt[count].alt_address,
+ sgpnt[count].length);
+ } /* Able to malloc sgpnt */
+ } /* Host adapter capable of scatter-gather */
+
+ /* Now handle the possibility of DMA to addresses > 16Mb */
+
+ if(SCpnt->use_sg == 0){
+ if (((long) buff) + (this_count << 9) - 1 > ISA_DMA_THRESHOLD &&
+ (SCpnt->host->unchecked_isa_dma)) {
+ if(bounce_buffer)
+ buff = bounce_buffer;
+ else
+ buff = (char *) scsi_malloc(this_count << 9);
+ if(buff == NULL) { /* Try backing off a bit if we are low on mem*/
+ this_count = SCpnt->request.current_nr_sectors;
+ buff = (char *) scsi_malloc(this_count << 9);
+ if(!buff) panic("Ran out of DMA buffers.");
+ }
+ if (SCpnt->request.cmd == WRITE)
+ memcpy(buff, (char *)SCpnt->request.buffer, this_count << 9);
+ }
+ }
+#ifdef DEBUG
+ printk("sd%c : %s %d/%d 512 byte blocks.\n",
+ 'a' + devm,
+ (SCpnt->request.cmd == WRITE) ? "writing" : "reading",
+ this_count, SCpnt->request.nr_sectors);
+#endif
+
+ cmd[1] = (SCpnt->lun << 5) & 0xe0;
+
+ if (rscsi_disks[dev].sector_size == 1024){
+ if(block & 1) panic("sd.c:Bad block number requested");
+ if(this_count & 1) panic("sd.c:Bad block number requested");
+ block = block >> 1;
+ this_count = this_count >> 1;
+ }
+
+ if (rscsi_disks[dev].sector_size == 256){
+ block = block << 1;
+ this_count = this_count << 1;
+ }
+
+ if (((this_count > 0xff) || (block > 0x1fffff)) && rscsi_disks[dev].ten)
+ {
+ if (this_count > 0xffff)
+ this_count = 0xffff;
+
+ cmd[0] += READ_10 - READ_6 ;
+ cmd[2] = (unsigned char) (block >> 24) & 0xff;
+ cmd[3] = (unsigned char) (block >> 16) & 0xff;
+ cmd[4] = (unsigned char) (block >> 8) & 0xff;
+ cmd[5] = (unsigned char) block & 0xff;
+ cmd[6] = cmd[9] = 0;
+ cmd[7] = (unsigned char) (this_count >> 8) & 0xff;
+ cmd[8] = (unsigned char) this_count & 0xff;
+ }
+ else
+ {
+ if (this_count > 0xff)
+ this_count = 0xff;
+
+ cmd[1] |= (unsigned char) ((block >> 16) & 0x1f);
+ cmd[2] = (unsigned char) ((block >> 8) & 0xff);
+ cmd[3] = (unsigned char) block & 0xff;
+ cmd[4] = (unsigned char) this_count;
+ cmd[5] = 0;
+ }
+
+ /*
+ * We shouldn't disconnect in the middle of a sector, so with a dumb
+ * host adapter, it's safe to assume that we can at least transfer
+ * this many bytes between each connect / disconnect.
+ */
+
+ SCpnt->transfersize = rscsi_disks[dev].sector_size;
+ SCpnt->underflow = this_count << 9;
+ scsi_do_cmd (SCpnt, (void *) cmd, buff,
+ this_count * rscsi_disks[dev].sector_size,
+ rw_intr,
+ (SCpnt->device->type == TYPE_DISK ?
+ SD_TIMEOUT : SD_MOD_TIMEOUT),
+ MAX_RETRIES);
+}
+
+static int check_scsidisk_media_change(kdev_t full_dev){
+ int retval;
+ int target;
+ struct inode inode;
+ int flag = 0;
+
+ target = DEVICE_NR(full_dev);
+
+ if (target >= sd_template.dev_max ||
+ !rscsi_disks[target].device) {
+ printk("SCSI disk request error: invalid device.\n");
+ return 0;
+ }
+
+ if(!rscsi_disks[target].device->removable) return 0;
+
+ inode.i_rdev = full_dev; /* This is all we really need here */
+
+ /* Using Start/Stop enables differentiation between drive with
+ * no cartridge loaded - NOT READY, drive with changed cartridge -
+ * UNIT ATTENTION, or with same cartridge - GOOD STATUS.
+ * This also handles drives that auto spin down. eg iomega jaz 1GB
+ * as this will spin up the drive.
+ */
+ retval = sd_ioctl(&inode, NULL, SCSI_IOCTL_START_UNIT, 0);
+
+ if(retval){ /* Unable to test, unit probably not ready. This usually
+ * means there is no disc in the drive. Mark as changed,
+ * and we will figure it out later once the drive is
+ * available again. */
+
+ rscsi_disks[target].ready = 0;
+ rscsi_disks[target].device->changed = 1;
+ return 1; /* This will force a flush, if called from
+ * check_disk_change */
+ }
+
+ /*
+ * for removable scsi disk ( FLOPTICAL ) we have to recognise the
+ * presence of disk in the drive. This is kept in the Scsi_Disk
+ * struct and tested at open ! Daniel Roche ( dan@lectra.fr )
+ */
+
+ rscsi_disks[target].ready = 1; /* FLOPTICAL */
+
+ retval = rscsi_disks[target].device->changed;
+ if(!flag) rscsi_disks[target].device->changed = 0;
+ return retval;
+}
+
+static void sd_init_done (Scsi_Cmnd * SCpnt)
+{
+ struct request * req;
+
+ req = &SCpnt->request;
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+}
+
+static int sd_init_onedisk(int i)
+{
+ unsigned char cmd[10];
+ unsigned char *buffer;
+ unsigned long spintime;
+ int the_result, retries;
+ Scsi_Cmnd * SCpnt;
+
+ /* We need to retry the READ_CAPACITY because a UNIT_ATTENTION is
+ * considered a fatal error, and many devices report such an error
+ * just after a scsi bus reset.
+ */
+
+ SCpnt = allocate_device(NULL, rscsi_disks[i].device, 1);
+ buffer = (unsigned char *) scsi_malloc(512);
+
+ spintime = 0;
+
+ /* Spin up drives, as required. Only do this at boot time */
+ /* Spinup needs to be done for module loads too. */
+ do{
+ retries = 0;
+ while(retries < 3)
+ {
+ cmd[0] = TEST_UNIT_READY;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ memset ((void *) &cmd[2], 0, 8);
+ SCpnt->cmd_len = 0;
+ SCpnt->sense_buffer[0] = 0;
+ SCpnt->sense_buffer[2] = 0;
+
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ /* Mark as really busy again */
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sd_init_done, SD_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ the_result = SCpnt->result;
+ retries++;
+ if( the_result == 0
+ || SCpnt->sense_buffer[2] != UNIT_ATTENTION)
+ break;
+ }
+
+ /* Look for non-removable devices that return NOT_READY.
+ * Issue command to spin up drive for these cases. */
+ if(the_result && !rscsi_disks[i].device->removable &&
+ SCpnt->sense_buffer[2] == NOT_READY) {
+ unsigned long time1;
+ if(!spintime){
+ printk( "sd%c: Spinning up disk...", 'a' + i );
+ cmd[0] = START_STOP;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ cmd[1] |= 1; /* Return immediately */
+ memset ((void *) &cmd[2], 0, 8);
+ cmd[4] = 1; /* Start spin cycle */
+ SCpnt->cmd_len = 0;
+ SCpnt->sense_buffer[0] = 0;
+ SCpnt->sense_buffer[2] = 0;
+
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ /* Mark as really busy again */
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sd_init_done, SD_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ spintime = jiffies;
+ }
+
+ time1 = jiffies + HZ;
+ while(jiffies < time1); /* Wait 1 second for next try */
+ printk( "." );
+ }
+ } while(the_result && spintime && spintime+100*HZ > jiffies);
+ if (spintime) {
+ if (the_result)
+ printk( "not responding...\n" );
+ else
+ printk( "ready\n" );
+ }
+
+ retries = 3;
+ do {
+ cmd[0] = READ_CAPACITY;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ memset ((void *) &cmd[2], 0, 8);
+ memset ((void *) buffer, 0, 8);
+ SCpnt->cmd_len = 0;
+ SCpnt->sense_buffer[0] = 0;
+ SCpnt->sense_buffer[2] = 0;
+
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ /* Mark as really busy again */
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 8, sd_init_done, SD_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem); /* sleep until it is ready */
+ }
+
+ the_result = SCpnt->result;
+ retries--;
+
+ } while(the_result && retries);
+
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
+
+ wake_up(&SCpnt->device->device_wait);
+
+ /* Wake up a process waiting for device */
+
+ /*
+ * The SCSI standard says:
+ * "READ CAPACITY is necessary for self configuring software"
+ * While not mandatory, support of READ CAPACITY is strongly encouraged.
+ * We used to die if we couldn't successfully do a READ CAPACITY.
+ * But, now we go on about our way. The side effects of this are
+ *
+ * 1. We can't know block size with certainty. I have said "512 bytes
+ * is it" as this is most common.
+ *
+ * 2. Recovery from when some one attempts to read past the end of the
+ * raw device will be slower.
+ */
+
+ if (the_result)
+ {
+ printk ("sd%c : READ CAPACITY failed.\n"
+ "sd%c : status = %x, message = %02x, host = %d, driver = %02x \n",
+ 'a' + i, 'a' + i,
+ status_byte(the_result),
+ msg_byte(the_result),
+ host_byte(the_result),
+ driver_byte(the_result)
+ );
+ if (driver_byte(the_result) & DRIVER_SENSE)
+ printk("sd%c : extended sense code = %1x \n",
+ 'a' + i, SCpnt->sense_buffer[2] & 0xf);
+ else
+ printk("sd%c : sense not available. \n", 'a' + i);
+
+ printk("sd%c : block size assumed to be 512 bytes, disk size 1GB. \n",
+ 'a' + i);
+ rscsi_disks[i].capacity = 0x1fffff;
+ rscsi_disks[i].sector_size = 512;
+
+ /* Set dirty bit for removable devices if not ready - sometimes drives
+ * will not report this properly. */
+ if(rscsi_disks[i].device->removable &&
+ SCpnt->sense_buffer[2] == NOT_READY)
+ rscsi_disks[i].device->changed = 1;
+
+ }
+ else
+ {
+ /*
+ * FLOPTICAL , if read_capa is ok , drive is assumed to be ready
+ */
+ rscsi_disks[i].ready = 1;
+
+ rscsi_disks[i].capacity = 1 + ((buffer[0] << 24) |
+ (buffer[1] << 16) |
+ (buffer[2] << 8) |
+ buffer[3]);
+
+ rscsi_disks[i].sector_size = (buffer[4] << 24) |
+ (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
+
+ if (rscsi_disks[i].sector_size == 0) {
+ rscsi_disks[i].sector_size = 512;
+ printk("sd%c : sector size 0 reported, assuming 512.\n", 'a' + i);
+ }
+
+
+ if (rscsi_disks[i].sector_size != 512 &&
+ rscsi_disks[i].sector_size != 1024 &&
+ rscsi_disks[i].sector_size != 256)
+ {
+ printk ("sd%c : unsupported sector size %d.\n",
+ 'a' + i, rscsi_disks[i].sector_size);
+ if(rscsi_disks[i].device->removable){
+ rscsi_disks[i].capacity = 0;
+ } else {
+ printk ("scsi : deleting disk entry.\n");
+ rscsi_disks[i].device = NULL;
+ sd_template.nr_dev--;
+ sd_gendisk.nr_real--;
+ return i;
+ }
+ }
+ {
+ /*
+ * The msdos fs needs to know the hardware sector size
+ * So I have created this table. See ll_rw_blk.c
+ * Jacques Gelinas (Jacques@solucorp.qc.ca)
+ */
+ int m, mb;
+ int sz_quot, sz_rem;
+ int hard_sector = rscsi_disks[i].sector_size;
+ /* There are 16 minors allocated for each major device */
+ for (m=i<<4; m<((i+1)<<4); m++){
+ sd_hardsizes[m] = hard_sector;
+ }
+ mb = rscsi_disks[i].capacity / 1024 * hard_sector / 1024;
+ /* sz = div(m/100, 10); this seems to not be in the libr */
+ m = (mb + 50) / 100;
+ sz_quot = m / 10;
+ sz_rem = m - (10 * sz_quot);
+ printk ("SCSI device sd%c: hdwr sector= %d bytes."
+ " Sectors= %d [%d MB] [%d.%1d GB]\n",
+ i+'a', hard_sector, rscsi_disks[i].capacity,
+ mb, sz_quot, sz_rem);
+ }
+ if(rscsi_disks[i].sector_size == 1024)
+ rscsi_disks[i].capacity <<= 1; /* Change into 512 byte sectors */
+ if(rscsi_disks[i].sector_size == 256)
+ rscsi_disks[i].capacity >>= 1; /* Change into 512 byte sectors */
+ }
+
+
+ /*
+ * Unless otherwise specified, this is not write protected.
+ */
+ rscsi_disks[i].write_prot = 0;
+ if ( rscsi_disks[i].device->removable && rscsi_disks[i].ready ) {
+ /* FLOPTICAL */
+
+ /*
+ * for removable scsi disk ( FLOPTICAL ) we have to recognise
+ * the Write Protect Flag. This flag is kept in the Scsi_Disk struct
+ * and tested at open !
+ * Daniel Roche ( dan@lectra.fr )
+ */
+
+ memset ((void *) &cmd[0], 0, 8);
+ cmd[0] = MODE_SENSE;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ cmd[2] = 1; /* page code 1 ?? */
+ cmd[4] = 12;
+ SCpnt->cmd_len = 0;
+ SCpnt->sense_buffer[0] = 0;
+ SCpnt->sense_buffer[2] = 0;
+
+ /* same code as READCAPA !! */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY; /* Mark as really busy again */
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sd_init_done, SD_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ the_result = SCpnt->result;
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
+ wake_up(&SCpnt->device->device_wait);
+
+ if ( the_result ) {
+ printk ("sd%c: test WP failed, assume Write Protected\n",i+'a');
+ rscsi_disks[i].write_prot = 1;
+ } else {
+ rscsi_disks[i].write_prot = ((buffer[2] & 0x80) != 0);
+ printk ("sd%c: Write Protect is %s\n",i+'a',
+ rscsi_disks[i].write_prot ? "on" : "off");
+ }
+
+ } /* check for write protect */
+
+ rscsi_disks[i].ten = 1;
+ rscsi_disks[i].remap = 1;
+ scsi_free(buffer, 512);
+ return i;
+}
+
+/*
+ * The sd_init() function looks at all SCSI drives present, determines
+ * their size, and reads partition table entries for them.
+ */
+
+static int sd_registered = 0;
+
+static int sd_init()
+{
+ int i;
+
+ if (sd_template.dev_noticed == 0) return 0;
+
+ if(!sd_registered) {
+ if (register_blkdev(MAJOR_NR,"sd",&sd_fops)) {
+ printk("Unable to get major %d for SCSI disk\n",MAJOR_NR);
+ return 1;
+ }
+ sd_registered++;
+ }
+
+ /* We do not support attaching loadable devices yet. */
+ if(rscsi_disks) return 0;
+
+ sd_template.dev_max = sd_template.dev_noticed + SD_EXTRA_DEVS;
+
+ rscsi_disks = (Scsi_Disk *)
+ scsi_init_malloc(sd_template.dev_max * sizeof(Scsi_Disk), GFP_ATOMIC);
+ memset(rscsi_disks, 0, sd_template.dev_max * sizeof(Scsi_Disk));
+
+ sd_sizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(int), GFP_ATOMIC);
+ memset(sd_sizes, 0, (sd_template.dev_max << 4) * sizeof(int));
+
+ sd_blocksizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(int), GFP_ATOMIC);
+
+ sd_hardsizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(int), GFP_ATOMIC);
+
+ for(i=0;i<(sd_template.dev_max << 4);i++){
+ sd_blocksizes[i] = 1024;
+ sd_hardsizes[i] = 512;
+ }
+ blksize_size[MAJOR_NR] = sd_blocksizes;
+ hardsect_size[MAJOR_NR] = sd_hardsizes;
+ sd = (struct hd_struct *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(struct hd_struct),
+ GFP_ATOMIC);
+
+
+ sd_gendisk.max_nr = sd_template.dev_max;
+ sd_gendisk.part = sd;
+ sd_gendisk.sizes = sd_sizes;
+ sd_gendisk.real_devices = (void *) rscsi_disks;
+ return 0;
+}
+
+static void sd_finish(void)
+{
+ struct gendisk *gendisk;
+ int i;
+
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+
+ for (gendisk = gendisk_head; gendisk != NULL; gendisk = gendisk->next)
+ if (gendisk == &sd_gendisk)
+ break;
+ if (gendisk == NULL)
+ {
+ sd_gendisk.next = gendisk_head;
+ gendisk_head = &sd_gendisk;
+ }
+
+ for (i = 0; i < sd_template.dev_max; ++i)
+ if (!rscsi_disks[i].capacity &&
+ rscsi_disks[i].device)
+ {
+ if (MODULE_FLAG
+ && !rscsi_disks[i].has_part_table) {
+ sd_sizes[i << 4] = rscsi_disks[i].capacity;
+ /* revalidate does sd_init_onedisk via MAYBE_REINIT*/
+ revalidate_scsidisk(MKDEV(MAJOR_NR, i << 4), 0);
+ }
+ else
+ i=sd_init_onedisk(i);
+ rscsi_disks[i].has_part_table = 1;
+ }
+
+ /* If our host adapter is capable of scatter-gather, then we increase
+ * the read-ahead to 16 blocks (32 sectors). If not, we use
+ * a two block (4 sector) read ahead.
+ */
+ if(rscsi_disks[0].device && rscsi_disks[0].device->host->sg_tablesize)
+ read_ahead[MAJOR_NR] = 120; /* 120 sector read-ahead */
+ else
+ read_ahead[MAJOR_NR] = 4; /* 4 sector read-ahead */
+
+ return;
+}
+
+static int sd_detect(Scsi_Device * SDp){
+ if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0;
+
+ printk("Detected scsi %sdisk sd%c at scsi%d, channel %d, id %d, lun %d\n",
+ SDp->removable ? "removable " : "",
+ 'a'+ (sd_template.dev_noticed++),
+ SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+
+ return 1;
+}
+
+static int sd_attach(Scsi_Device * SDp){
+ Scsi_Disk * dpnt;
+ int i;
+
+ if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0;
+
+ if(sd_template.nr_dev >= sd_template.dev_max) {
+ SDp->attached--;
+ return 1;
+ }
+
+ for(dpnt = rscsi_disks, i=0; i<sd_template.dev_max; i++, dpnt++)
+ if(!dpnt->device) break;
+
+ if(i >= sd_template.dev_max) panic ("scsi_devices corrupt (sd)");
+
+ SDp->scsi_request_fn = do_sd_request;
+ rscsi_disks[i].device = SDp;
+ rscsi_disks[i].has_part_table = 0;
+ sd_template.nr_dev++;
+ sd_gendisk.nr_real++;
+ return 0;
+}
+
+#define DEVICE_BUSY rscsi_disks[target].device->busy
+#define USAGE rscsi_disks[target].device->access_count
+#define CAPACITY rscsi_disks[target].capacity
+#define MAYBE_REINIT sd_init_onedisk(target)
+#define GENDISK_STRUCT sd_gendisk
+
+/* This routine is called to flush all partitions and partition tables
+ * for a changed scsi disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0. If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+int revalidate_scsidisk(kdev_t dev, int maxusage){
+ int target;
+ struct gendisk * gdev;
+ unsigned long flags;
+ int max_p;
+ int start;
+ int i;
+
+ target = DEVICE_NR(dev);
+ gdev = &GENDISK_STRUCT;
+
+ save_flags(flags);
+ cli();
+ if (DEVICE_BUSY || USAGE > maxusage) {
+ restore_flags(flags);
+ printk("Device busy for revalidation (usage=%d)\n", USAGE);
+ return -EBUSY;
+ }
+ DEVICE_BUSY = 1;
+ restore_flags(flags);
+
+ max_p = gdev->max_p;
+ start = target << gdev->minor_shift;
+
+ for (i=max_p - 1; i >=0 ; i--) {
+ int minor = start+i;
+ kdev_t devi = MKDEV(MAJOR_NR, minor);
+ sync_dev(devi);
+ invalidate_inodes(devi);
+ invalidate_buffers(devi);
+ gdev->part[minor].start_sect = 0;
+ gdev->part[minor].nr_sects = 0;
+ /*
+ * Reset the blocksize for everything so that we can read
+ * the partition table.
+ */
+ blksize_size[MAJOR_NR][minor] = 1024;
+ }
+
+#ifdef MAYBE_REINIT
+ MAYBE_REINIT;
+#endif
+
+ gdev->part[start].nr_sects = CAPACITY;
+ resetup_one_dev(gdev, target);
+
+ DEVICE_BUSY = 0;
+ return 0;
+}
+
+static int fop_revalidate_scsidisk(kdev_t dev){
+ return revalidate_scsidisk(dev, 0);
+}
+
+
+static void sd_detach(Scsi_Device * SDp)
+{
+ Scsi_Disk * dpnt;
+ int i;
+ int max_p;
+ int start;
+
+ for(dpnt = rscsi_disks, i=0; i<sd_template.dev_max; i++, dpnt++)
+ if(dpnt->device == SDp) {
+
+ /* If we are disconnecting a disk driver, sync and invalidate
+ * everything */
+ max_p = sd_gendisk.max_p;
+ start = i << sd_gendisk.minor_shift;
+
+ for (i=max_p - 1; i >=0 ; i--) {
+ int minor = start+i;
+ kdev_t devi = MKDEV(MAJOR_NR, minor);
+ sync_dev(devi);
+ invalidate_inodes(devi);
+ invalidate_buffers(devi);
+ sd_gendisk.part[minor].start_sect = 0;
+ sd_gendisk.part[minor].nr_sects = 0;
+ sd_sizes[minor] = 0;
+ }
+
+ dpnt->has_part_table = 0;
+ dpnt->device = NULL;
+ dpnt->capacity = 0;
+ SDp->attached--;
+ sd_template.dev_noticed--;
+ sd_template.nr_dev--;
+ sd_gendisk.nr_real--;
+ return;
+ }
+ return;
+}
+
+#ifdef MODULE
+
+int init_module(void) {
+ sd_template.usage_count = &mod_use_count_;
+ return scsi_register_module(MODULE_SCSI_DEV, &sd_template);
+}
+
+void cleanup_module( void)
+{
+ struct gendisk * prev_sdgd;
+ struct gendisk * sdgd;
+
+ scsi_unregister_module(MODULE_SCSI_DEV, &sd_template);
+ unregister_blkdev(SCSI_DISK_MAJOR, "sd");
+ sd_registered--;
+ if( rscsi_disks != NULL )
+ {
+ scsi_init_free((char *) rscsi_disks,
+ (sd_template.dev_noticed + SD_EXTRA_DEVS)
+ * sizeof(Scsi_Disk));
+
+ scsi_init_free((char *) sd_sizes, sd_template.dev_max * sizeof(int));
+ scsi_init_free((char *) sd_blocksizes, sd_template.dev_max * sizeof(int));
+ scsi_init_free((char *) sd_hardsizes, sd_template.dev_max * sizeof(int));
+ scsi_init_free((char *) sd,
+ (sd_template.dev_max << 4) * sizeof(struct hd_struct));
+ /*
+ * Now remove sd_gendisk from the linked list
+ */
+ sdgd = gendisk_head;
+ prev_sdgd = NULL;
+ while(sdgd != &sd_gendisk)
+ {
+ prev_sdgd = sdgd;
+ sdgd = sdgd->next;
+ }
+
+ if(sdgd != &sd_gendisk)
+ printk("sd_gendisk not in disk chain.\n");
+ else {
+ if(prev_sdgd != NULL)
+ prev_sdgd->next = sdgd->next;
+ else
+ gendisk_head = sdgd->next;
+ }
+ }
+
+ blksize_size[MAJOR_NR] = NULL;
+ blk_dev[MAJOR_NR].request_fn = NULL;
+ blk_size[MAJOR_NR] = NULL;
+ hardsect_size[MAJOR_NR] = NULL;
+ read_ahead[MAJOR_NR] = 0;
+ sd_template.dev_max = 0;
+}
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/sd.h b/linux/src/drivers/scsi/sd.h
new file mode 100644
index 00000000..02b3437d
--- /dev/null
+++ b/linux/src/drivers/scsi/sd.h
@@ -0,0 +1,65 @@
+/*
+ * sd.h Copyright (C) 1992 Drew Eckhardt
+ * SCSI disk driver header file by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale eric@aib.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ */
+#ifndef _SD_H
+#define _SD_H
+/*
+ $Header: cvs/gnumach/linux/src/drivers/scsi/Attic/sd.h,v 1.1 1999/04/26 05:55:03 tb Exp $
+*/
+
+#ifndef _SCSI_H
+#include "scsi.h"
+#endif
+
+#ifndef _GENDISK_H
+#include <linux/genhd.h>
+#endif
+
+extern struct hd_struct * sd;
+
+typedef struct scsi_disk {
+ unsigned capacity; /* size in blocks */
+ unsigned sector_size; /* size in bytes */
+ Scsi_Device *device;
+ unsigned char ready; /* flag ready for FLOPTICAL */
+ unsigned char write_prot; /* flag write_protect for rmvable dev */
+ unsigned char sector_bit_size; /* sector_size = 2 to the bit size power */
+ unsigned char sector_bit_shift; /* power of 2 sectors per FS block */
+ unsigned ten:1; /* support ten byte read / write */
+ unsigned remap:1; /* support remapping */
+ unsigned has_part_table:1; /* has partition table */
+} Scsi_Disk;
+
+extern Scsi_Disk * rscsi_disks;
+
+extern int revalidate_scsidisk(kdev_t dev, int maxusage);
+
+#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
+
diff --git a/linux/src/drivers/scsi/sd_ioctl.c b/linux/src/drivers/scsi/sd_ioctl.c
new file mode 100644
index 00000000..52fd754d
--- /dev/null
+++ b/linux/src/drivers/scsi/sd_ioctl.c
@@ -0,0 +1,119 @@
+/*
+ * drivers/scsi/sd_ioctl.c
+ *
+ * ioctl handling for SCSI disks
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/hdreg.h>
+#include <linux/errno.h>
+
+#include <asm/segment.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include <scsi/scsi_ioctl.h>
+#include "hosts.h"
+#include "sd.h"
+#include <scsi/scsicam.h> /* must follow "hosts.h" */
+
+int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
+{
+ kdev_t dev = inode->i_rdev;
+ int error;
+ struct Scsi_Host * host;
+ int diskinfo[4];
+ struct hd_geometry *loc = (struct hd_geometry *) arg;
+
+ switch (cmd) {
+ case HDIO_GETGEO: /* Return BIOS disk parameters */
+ if (!loc) return -EINVAL;
+ error = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+ if (error)
+ return error;
+ host = rscsi_disks[MINOR(dev) >> 4].device->host;
+
+/* default to most commonly used values */
+
+ diskinfo[0] = 0x40;
+ diskinfo[1] = 0x20;
+ diskinfo[2] = rscsi_disks[MINOR(dev) >> 4].capacity >> 11;
+
+/* override with calculated, extended default, or driver values */
+
+ if(host->hostt->bios_param != NULL)
+ host->hostt->bios_param(&rscsi_disks[MINOR(dev) >> 4],
+ dev,
+ &diskinfo[0]);
+ else scsicam_bios_param(&rscsi_disks[MINOR(dev) >> 4],
+ dev, &diskinfo[0]);
+
+ put_user(diskinfo[0], &loc->heads);
+ put_user(diskinfo[1], &loc->sectors);
+ put_user(diskinfo[2], &loc->cylinders);
+ put_user(sd[MINOR(inode->i_rdev)].start_sect, &loc->start);
+ return 0;
+ case BLKGETSIZE: /* Return device size */
+ if (!arg) return -EINVAL;
+ error = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+ if (error)
+ return error;
+ put_user(sd[MINOR(inode->i_rdev)].nr_sects,
+ (long *) arg);
+ return 0;
+
+ case BLKRASET:
+ if (!suser())
+ return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+
+ case BLKRAGET:
+ if (!arg)
+ return -EINVAL;
+ error = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
+ if (error)
+ return error;
+ put_user(read_ahead[MAJOR(inode->i_rdev)], (int *) arg);
+ return 0;
+
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
+ case BLKRRPART: /* Re-read partition tables */
+ return revalidate_scsidisk(dev, 1);
+
+ RO_IOCTLS(dev, arg);
+
+ default:
+ return scsi_ioctl(rscsi_disks[MINOR(dev) >> 4].device , cmd, (void *) arg);
+ }
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/seagate.c b/linux/src/drivers/scsi/seagate.c
new file mode 100644
index 00000000..9e942746
--- /dev/null
+++ b/linux/src/drivers/scsi/seagate.c
@@ -0,0 +1,1755 @@
+/*
+ * seagate.c Copyright (C) 1992, 1993 Drew Eckhardt
+ * low level scsi driver for ST01/ST02, Future Domain TMC-885,
+ * TMC-950 by
+ *
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ *
+ * Note : TMC-880 boards don't work because they have two bits in
+ * the status register flipped, I'll fix this "RSN"
+ *
+ * This card does all the I/O via memory mapped I/O, so there is no need
+ * to check or allocate a region of the I/O address space.
+ */
+
+/*
+ * Configuration :
+ * To use without BIOS -DOVERRIDE=base_address -DCONTROLLER=FD or SEAGATE
+ * -DIRQ will override the default of 5.
+ * Note: You can now set these options from the kernel's "command line".
+ * The syntax is:
+ *
+ * st0x=ADDRESS,IRQ (for a Seagate controller)
+ * or:
+ * tmc8xx=ADDRESS,IRQ (for a TMC-8xx or TMC-950 controller)
+ * eg:
+ * tmc8xx=0xC8000,15
+ *
+ * will configure the driver for a TMC-8xx style controller using IRQ 15
+ * with a base address of 0xC8000.
+ *
+ * -DFAST or -DFAST32 will use blind transfers where possible
+ *
+ * -DARBITRATE will cause the host adapter to arbitrate for the
+ * bus for better SCSI-II compatibility, rather than just
+ * waiting for BUS FREE and then doing its thing. Should
+ * let us do one command per Lun when I integrate my
+ * reorganization changes into the distribution sources.
+ *
+ * -DSLOW_HANDSHAKE will allow compatibility with broken devices that don't
+ * handshake fast enough (ie, some CD ROM's) for the Seagate
+ * code.
+ *
+ * -DSLOW_RATE=x, x some number will let you specify a default
+ * transfer rate if handshaking isn't working correctly.
+ */
+
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/proc_fs.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "seagate.h"
+#include "constants.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_seagate = {
+ PROC_SCSI_SEAGATE, 7, "seagate",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+
+#ifndef IRQ
+#define IRQ 5
+#endif
+
+#if (defined(FAST32) && !defined(FAST))
+#define FAST
+#endif
+
+#if defined(SLOW_RATE) && !defined(SLOW_HANDSHAKE)
+#define SLOW_HANDSHAKE
+#endif
+
+#if defined(SLOW_HANDSHAKE) && !defined(SLOW_RATE)
+#define SLOW_RATE 50
+#endif
+
+
+#if defined(LINKED)
+#undef LINKED /* Linked commands are currently broken ! */
+#endif
+
+static int internal_command(unsigned char target, unsigned char lun,
+ const void *cmnd,
+ void *buff, int bufflen, int reselect);
+
+static int incommand; /*
+ set if arbitration has finished and we are
+ in some command phase.
+ */
+
+static const void *base_address = NULL; /*
+ Where the card ROM starts,
+ used to calculate memory mapped
+ register location.
+ */
+#ifdef notyet
+static volatile int abort_confirm = 0;
+#endif
+
+static volatile void *st0x_cr_sr; /*
+ control register write,
+ status register read.
+ 256 bytes in length.
+
+ Read is status of SCSI BUS,
+ as per STAT masks.
+
+ */
+
+
+static volatile void *st0x_dr; /*
+ data register, read write
+ 256 bytes in length.
+ */
+
+
+static volatile int st0x_aborted=0; /*
+ set when we are aborted, ie by a time out, etc.
+ */
+
+static unsigned char controller_type = 0; /* set to SEAGATE for ST0x boards or FD for TMC-8xx boards */
+static unsigned char irq = IRQ;
+
+#define retcode(result) (((result) << 16) | (message << 8) | status)
+#define STATUS (*(volatile unsigned char *) st0x_cr_sr)
+#define CONTROL STATUS
+#define DATA (*(volatile unsigned char *) st0x_dr)
+
+void st0x_setup (char *str, int *ints) {
+ controller_type = SEAGATE;
+ base_address = (void *) ints[1];
+ irq = ints[2];
+}
+
+void tmc8xx_setup (char *str, int *ints) {
+ controller_type = FD;
+ base_address = (void *) ints[1];
+ irq = ints[2];
+}
+
+
+#ifndef OVERRIDE
+static const char * seagate_bases[] = {
+ (char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000,
+ (char *) 0xce000, (char *) 0xdc000, (char *) 0xde000
+};
+
+typedef struct {
+ const char *signature ;
+ unsigned offset;
+ unsigned length;
+ unsigned char type;
+} Signature;
+
+static const Signature signatures[] = {
+#ifdef CONFIG_SCSI_SEAGATE
+{"ST01 v1.7 (C) Copyright 1987 Seagate", 15, 37, SEAGATE},
+{"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40, SEAGATE},
+
+/*
+ * The following two lines are NOT mistakes. One detects ROM revision
+ * 3.0.0, the other 3.2. Since seagate has only one type of SCSI adapter,
+ * and this is not going to change, the "SEAGATE" and "SCSI" together
+ * are probably "good enough"
+ */
+
+{"SEAGATE SCSI BIOS ",16, 17, SEAGATE},
+{"SEAGATE SCSI BIOS ",17, 17, SEAGATE},
+
+/*
+ * However, future domain makes several incompatible SCSI boards, so specific
+ * signatures must be used.
+ */
+
+{"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 46, FD},
+{"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FD},
+{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90",5, 47, FD},
+{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90",5, 47, FD},
+{"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD},
+{"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD},
+{"IBM F1 BIOS V1.1004/30/92", 5, 25, FD},
+{"FUTURE DOMAIN TMC-950", 5, 21, FD},
+#endif /* CONFIG_SCSI_SEAGATE */
+}
+;
+
+#define NUM_SIGNATURES (sizeof(signatures) / sizeof(Signature))
+#endif /* n OVERRIDE */
+
+/*
+ * hostno stores the hostnumber, as told to us by the init routine.
+ */
+
+static int hostno = -1;
+static void seagate_reconnect_intr(int, void *, struct pt_regs *);
+
+#ifdef FAST
+static int fast = 1;
+#endif
+
+#ifdef SLOW_HANDSHAKE
+/*
+ * Support for broken devices :
+ * The Seagate board has a handshaking problem. Namely, a lack
+ * thereof for slow devices. You can blast 600K/second through
+ * it if you are polling for each byte, more if you do a blind
+ * transfer. In the first case, with a fast device, REQ will
+ * transition high-low or high-low-high before your loop restarts
+ * and you'll have no problems. In the second case, the board
+ * will insert wait states for up to 13.2 usecs for REQ to
+ * transition low->high, and everything will work.
+ *
+ * However, there's nothing in the state machine that says
+ * you *HAVE* to see a high-low-high set of transitions before
+ * sending the next byte, and slow things like the Trantor CD ROMS
+ * will break because of this.
+ *
+ * So, we need to slow things down, which isn't as simple as it
+ * seems. We can't slow things down period, because then people
+ * who don't recompile their kernels will shoot me for ruining
+ * their performance. We need to do it on a case per case basis.
+ *
+ * The best for performance will be to, only for borken devices
+ * (this is stored on a per-target basis in the scsi_devices array)
+ *
+ * Wait for a low->high transition before continuing with that
+ * transfer. If we timeout, continue anyways. We don't need
+ * a long timeout, because REQ should only be asserted until the
+ * corresponding ACK is received and processed.
+ *
+ * Note that we can't use the system timer for this, because of
+ * resolution, and we *really* can't use the timer chip since
+ * gettimeofday() and the beeper routines use that. So,
+ * the best thing for us to do will be to calibrate a timing
+ * loop in the initialization code using the timer chip before
+ * gettimeofday() can screw with it.
+ */
+
+static int borken_calibration = 0;
+static void borken_init (void) {
+ register int count = 0, start = jiffies + 1, stop = start + 25;
+
+ while (jiffies < start);
+ for (;jiffies < stop; ++count);
+
+/*
+ * Ok, we now have a count for .25 seconds. Convert to a
+ * count per second and divide by transfer rate in K.
+ */
+
+ borken_calibration = (count * 4) / (SLOW_RATE*1024);
+
+ if (borken_calibration < 1)
+ borken_calibration = 1;
+#if (DEBUG & DEBUG_BORKEN)
+ printk("scsi%d : borken calibrated to %dK/sec, %d cycles per transfer\n",
+ hostno, BORKEN_RATE, borken_calibration);
+#endif
+}
+
+static inline void borken_wait(void) {
+ register int count;
+ for (count = borken_calibration; count && (STATUS & STAT_REQ);
+ --count);
+#if (DEBUG & DEBUG_BORKEN)
+ if (count)
+ printk("scsi%d : borken timeout\n", hostno);
+#endif
+}
+
+#endif /* def SLOW_HANDSHAKE */
+
+int seagate_st0x_detect (Scsi_Host_Template * tpnt)
+ {
+ struct Scsi_Host *instance;
+#ifndef OVERRIDE
+ int i,j;
+#endif
+
+ tpnt->proc_dir = &proc_scsi_seagate;
+/*
+ * First, we try for the manual override.
+ */
+#ifdef DEBUG
+ printk("Autodetecting ST0x / TMC-8xx\n");
+#endif
+
+ if (hostno != -1)
+ {
+ printk ("ERROR : seagate_st0x_detect() called twice.\n");
+ return 0;
+ }
+
+ /* If the user specified the controller type from the command line,
+ controller_type will be non-zero, so don't try to detect one */
+
+ if (!controller_type) {
+#ifdef OVERRIDE
+ base_address = (void *) OVERRIDE;
+
+/* CONTROLLER is used to override controller (SEAGATE or FD). PM: 07/01/93 */
+#ifdef CONTROLLER
+ controller_type = CONTROLLER;
+#else
+#error Please use -DCONTROLLER=SEAGATE or -DCONTROLLER=FD to override controller type
+#endif /* CONTROLLER */
+#ifdef DEBUG
+ printk("Base address overridden to %x, controller type is %s\n",
+ base_address,controller_type == SEAGATE ? "SEAGATE" : "FD");
+#endif
+#else /* OVERRIDE */
+/*
+ * To detect this card, we simply look for the signature
+ * from the BIOS version notice in all the possible locations
+ * of the ROM's. This has a nice side effect of not trashing
+ * any register locations that might be used by something else.
+ *
+ * XXX - note that we probably should be probing the address
+ * space for the on-board RAM instead.
+ */
+
+ for (i = 0; i < (sizeof (seagate_bases) / sizeof (char * )); ++i)
+ for (j = 0; !base_address && j < NUM_SIGNATURES; ++j)
+ if (!memcmp ((const void *) (seagate_bases[i] +
+ signatures[j].offset), (const void *) signatures[j].signature,
+ signatures[j].length)) {
+ base_address = (const void *) seagate_bases[i];
+ controller_type = signatures[j].type;
+ }
+#endif /* OVERRIDE */
+ } /* (! controller_type) */
+
+ tpnt->this_id = (controller_type == SEAGATE) ? 7 : 6;
+ tpnt->name = (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR;
+
+ if (base_address)
+ {
+ st0x_cr_sr =(void *) (((const unsigned char *) base_address) + (controller_type == SEAGATE ? 0x1a00 : 0x1c00));
+ st0x_dr = (void *) (((const unsigned char *) base_address ) + (controller_type == SEAGATE ? 0x1c00 : 0x1e00));
+#ifdef DEBUG
+ printk("%s detected. Base address = %x, cr = %x, dr = %x\n", tpnt->name, base_address, st0x_cr_sr, st0x_dr);
+#endif
+/*
+ * At all times, we will use IRQ 5. Should also check for IRQ3 if we
+ * loose our first interrupt.
+ */
+ instance = scsi_register(tpnt, 0);
+ hostno = instance->host_no;
+ if (request_irq((int) irq, seagate_reconnect_intr, SA_INTERRUPT,
+ (controller_type == SEAGATE) ? "seagate" : "tmc-8xx", NULL)) {
+ printk("scsi%d : unable to allocate IRQ%d\n",
+ hostno, (int) irq);
+ return 0;
+ }
+ instance->irq = irq;
+ instance->io_port = (unsigned int) base_address;
+#ifdef SLOW_HANDSHAKE
+ borken_init();
+#endif
+
+ printk("%s options:"
+#ifdef ARBITRATE
+ " ARBITRATE"
+#endif
+#ifdef SLOW_HANDSHAKE
+ " SLOW_HANDSHAKE"
+#endif
+#ifdef FAST
+#ifdef FAST32
+ " FAST32"
+#else
+ " FAST"
+#endif
+#endif
+#ifdef LINKED
+ " LINKED"
+#endif
+ "\n", tpnt->name);
+ return 1;
+ }
+ else
+ {
+#ifdef DEBUG
+ printk("ST0x / TMC-8xx not detected.\n");
+#endif
+ return 0;
+ }
+ }
+
+const char *seagate_st0x_info(struct Scsi_Host * shpnt) {
+ static char buffer[64];
+ sprintf(buffer, "%s at irq %d, address 0x%05X",
+ (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR,
+ irq, (unsigned int)base_address);
+ return buffer;
+}
+
+int seagate_st0x_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
+{
+ const char *info = seagate_st0x_info(NULL);
+ int len;
+ int pos;
+ int begin;
+
+ if (inout) return(-ENOSYS);
+
+ begin = 0;
+ strcpy(buffer,info);
+ strcat(buffer,"\n");
+
+ pos = len = strlen(buffer);
+
+ if (pos<offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+ if ( len > length ) len = length;
+ return(len);
+}
+
+/*
+ * These are our saved pointers for the outstanding command that is
+ * waiting for a reconnect
+ */
+
+static unsigned char current_target, current_lun;
+static unsigned char *current_cmnd, *current_data;
+static int current_nobuffs;
+static struct scatterlist *current_buffer;
+static int current_bufflen;
+
+#ifdef LINKED
+
+/*
+ * linked_connected indicates whether or not we are currently connected to
+ * linked_target, linked_lun and in an INFORMATION TRANSFER phase,
+ * using linked commands.
+ */
+
+static int linked_connected = 0;
+static unsigned char linked_target, linked_lun;
+#endif
+
+
+static void (*done_fn)(Scsi_Cmnd *) = NULL;
+static Scsi_Cmnd * SCint = NULL;
+
+/*
+ * These control whether or not disconnect / reconnect will be attempted,
+ * or are being attempted.
+ */
+
+#define NO_RECONNECT 0
+#define RECONNECT_NOW 1
+#define CAN_RECONNECT 2
+
+#ifdef LINKED
+
+/*
+ * LINKED_RIGHT indicates that we are currently connected to the correct target
+ * for this command, LINKED_WRONG indicates that we are connected to the wrong
+ * target. Note that these imply CAN_RECONNECT.
+ */
+
+#define LINKED_RIGHT 3
+#define LINKED_WRONG 4
+#endif
+
+/*
+ * This determines if we are expecting to reconnect or not.
+ */
+
+static int should_reconnect = 0;
+
+/*
+ * The seagate_reconnect_intr routine is called when a target reselects the
+ * host adapter. This occurs on the interrupt triggered by the target
+ * asserting SEL.
+ */
+
+static void seagate_reconnect_intr(int irq, void *dev_id, struct pt_regs *regs)
+ {
+ int temp;
+ Scsi_Cmnd * SCtmp;
+
+/* enable all other interrupts. */
+ sti();
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : seagate_reconnect_intr() called\n", hostno);
+#endif
+
+ if (!should_reconnect)
+ printk("scsi%d: unexpected interrupt.\n", hostno);
+ else {
+ should_reconnect = 0;
+
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : internal_command("
+ "%d, %08x, %08x, %d, RECONNECT_NOW\n", hostno,
+ current_target, current_data, current_bufflen);
+#endif
+
+ temp = internal_command (current_target, current_lun,
+ current_cmnd, current_data, current_bufflen,
+ RECONNECT_NOW);
+
+ if (msg_byte(temp) != DISCONNECT) {
+ if (done_fn) {
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : done_fn(%d,%08x)", hostno,
+ hostno, temp);
+#endif
+ if(!SCint) panic("SCint == NULL in seagate");
+ SCtmp = SCint;
+ SCint = NULL;
+ SCtmp->result = temp;
+ done_fn (SCtmp);
+ } else
+ printk("done_fn() not defined.\n");
+ }
+ }
+ }
+
+/*
+ * The seagate_st0x_queue_command() function provides a queued interface
+ * to the seagate SCSI driver. Basically, it just passes control onto the
+ * seagate_command() function, after fixing it so that the done_fn()
+ * is set to the one passed to the function. We have to be very careful,
+ * because there are some commands on some devices that do not disconnect,
+ * and if we simply call the done_fn when the command is done then another
+ * command is started and queue_command is called again... We end up
+ * overflowing the kernel stack, and this tends not to be such a good idea.
+ */
+
+static int recursion_depth = 0;
+
+int seagate_st0x_queue_command (Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+ {
+ int result, reconnect;
+ Scsi_Cmnd * SCtmp;
+
+ done_fn = done;
+ current_target = SCpnt->target;
+ current_lun = SCpnt->lun;
+ (const void *) current_cmnd = SCpnt->cmnd;
+ current_data = (unsigned char *) SCpnt->request_buffer;
+ current_bufflen = SCpnt->request_bufflen;
+ SCint = SCpnt;
+ if(recursion_depth) {
+ return 0;
+ };
+ recursion_depth++;
+ do{
+#ifdef LINKED
+/*
+ * Set linked command bit in control field of SCSI command.
+ */
+
+ current_cmnd[SCpnt->cmd_len] |= 0x01;
+ if (linked_connected) {
+#if (DEBUG & DEBUG_LINKED)
+ printk("scsi%d : using linked commands, current I_T_L nexus is ",
+ hostno);
+#endif
+ if ((linked_target == current_target) &&
+ (linked_lun == current_lun)) {
+#if (DEBUG & DEBUG_LINKED)
+ printk("correct\n");
+#endif
+ reconnect = LINKED_RIGHT;
+ } else {
+#if (DEBUG & DEBUG_LINKED)
+ printk("incorrect\n");
+#endif
+ reconnect = LINKED_WRONG;
+ }
+ } else
+#endif /* LINKED */
+ reconnect = CAN_RECONNECT;
+
+
+
+
+
+ result = internal_command (SCint->target, SCint->lun, SCint->cmnd, SCint->request_buffer,
+ SCint->request_bufflen,
+ reconnect);
+ if (msg_byte(result) == DISCONNECT) break;
+ SCtmp = SCint;
+ SCint = NULL;
+ SCtmp->result = result;
+ done_fn (SCtmp);
+ } while(SCint);
+ recursion_depth--;
+ return 0;
+ }
+
+int seagate_st0x_command (Scsi_Cmnd * SCpnt) {
+ return internal_command (SCpnt->target, SCpnt->lun, SCpnt->cmnd, SCpnt->request_buffer,
+ SCpnt->request_bufflen,
+ (int) NO_RECONNECT);
+}
+
+static int internal_command(unsigned char target, unsigned char lun, const void *cmnd,
+ void *buff, int bufflen, int reselect) {
+ int len = 0;
+ unsigned char *data = NULL;
+ struct scatterlist *buffer = NULL;
+ int nobuffs = 0;
+ int clock;
+ int temp;
+#ifdef SLOW_HANDSHAKE
+ int borken; /* Does the current target require Very Slow I/O ? */
+#endif
+
+
+#if (DEBUG & PHASE_DATAIN) || (DEBUG & PHASE_DATOUT)
+ int transfered = 0;
+#endif
+
+#if (((DEBUG & PHASE_ETC) == PHASE_ETC) || (DEBUG & PRINT_COMMAND) || \
+ (DEBUG & PHASE_EXIT))
+ int i;
+#endif
+
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
+ int phase=0, newphase;
+#endif
+
+ int done = 0;
+ unsigned char status = 0;
+ unsigned char message = 0;
+ register unsigned char status_read;
+
+ unsigned transfersize = 0, underflow = 0;
+
+ incommand = 0;
+ st0x_aborted = 0;
+
+#ifdef SLOW_HANDSHAKE
+ borken = (int) SCint->device->borken;
+#endif
+
+#if (DEBUG & PRINT_COMMAND)
+ printk ("scsi%d : target = %d, command = ", hostno, target);
+ print_command((unsigned char *) cmnd);
+ printk("\n");
+#endif
+
+#if (DEBUG & PHASE_RESELECT)
+ switch (reselect) {
+ case RECONNECT_NOW :
+ printk("scsi%d : reconnecting\n", hostno);
+ break;
+#ifdef LINKED
+ case LINKED_RIGHT :
+ printk("scsi%d : connected, can reconnect\n", hostno);
+ break;
+ case LINKED_WRONG :
+ printk("scsi%d : connected to wrong target, can reconnect\n",
+ hostno);
+ break;
+#endif
+ case CAN_RECONNECT :
+ printk("scsi%d : allowed to reconnect\n", hostno);
+ break;
+ default :
+ printk("scsi%d : not allowed to reconnect\n", hostno);
+ }
+#endif
+
+
+ if (target == (controller_type == SEAGATE ? 7 : 6))
+ return DID_BAD_TARGET;
+
+/*
+ * We work it differently depending on if this is "the first time,"
+ * or a reconnect. If this is a reselect phase, then SEL will
+ * be asserted, and we must skip selection / arbitration phases.
+ */
+
+ switch (reselect) {
+ case RECONNECT_NOW:
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : phase RESELECT \n", hostno);
+#endif
+
+/*
+ * At this point, we should find the logical or of our ID and the original
+ * target's ID on the BUS, with BSY, SEL, and I/O signals asserted.
+ *
+ * After ARBITRATION phase is completed, only SEL, BSY, and the
+ * target ID are asserted. A valid initiator ID is not on the bus
+ * until IO is asserted, so we must wait for that.
+ */
+ clock = jiffies + 10;
+ for (;;) {
+ temp = STATUS;
+ if ((temp & STAT_IO) && !(temp & STAT_BSY))
+ break;
+
+ if (jiffies > clock) {
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : RESELECT timed out while waiting for IO .\n",
+ hostno);
+#endif
+ return (DID_BAD_INTR << 16);
+ }
+ }
+
+/*
+ * After I/O is asserted by the target, we can read our ID and its
+ * ID off of the BUS.
+ */
+
+ if (!((temp = DATA) & (controller_type == SEAGATE ? 0x80 : 0x40)))
+ {
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : detected reconnect request to different target.\n"
+ "\tData bus = %d\n", hostno, temp);
+#endif
+ return (DID_BAD_INTR << 16);
+ }
+
+ if (!(temp & (1 << current_target)))
+ {
+ printk("scsi%d : Unexpected reselect interrupt. Data bus = %d\n",
+ hostno, temp);
+ return (DID_BAD_INTR << 16);
+ }
+
+ buffer=current_buffer;
+ cmnd=current_cmnd; /* WDE add */
+ data=current_data; /* WDE add */
+ len=current_bufflen; /* WDE add */
+ nobuffs=current_nobuffs;
+
+/*
+ * We have determined that we have been selected. At this point,
+ * we must respond to the reselection by asserting BSY ourselves
+ */
+
+#if 1
+ CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY);
+#else
+ CONTROL = (BASE_CMD | CMD_BSY);
+#endif
+
+/*
+ * The target will drop SEL, and raise BSY, at which time we must drop
+ * BSY.
+ */
+
+ for (clock = jiffies + 10; (jiffies < clock) && (STATUS & STAT_SEL););
+
+ if (jiffies >= clock)
+ {
+ CONTROL = (BASE_CMD | CMD_INTR);
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : RESELECT timed out while waiting for SEL.\n",
+ hostno);
+#endif
+ return (DID_BAD_INTR << 16);
+ }
+
+ CONTROL = BASE_CMD;
+
+/*
+ * At this point, we have connected with the target and can get
+ * on with our lives.
+ */
+ break;
+ case CAN_RECONNECT:
+
+#ifdef LINKED
+/*
+ * This is a bletcherous hack, just as bad as the Unix #! interpreter stuff.
+ * If it turns out we are using the wrong I_T_L nexus, the easiest way to deal
+ * with it is to go into our INFORMATION TRANSFER PHASE code, send a ABORT
+ * message on MESSAGE OUT phase, and then loop back to here.
+ */
+
+connect_loop :
+
+#endif
+
+#if (DEBUG & PHASE_BUS_FREE)
+ printk ("scsi%d : phase = BUS FREE \n", hostno);
+#endif
+
+/*
+ * BUS FREE PHASE
+ *
+ * On entry, we make sure that the BUS is in a BUS FREE
+ * phase, by insuring that both BSY and SEL are low for
+ * at least one bus settle delay. Several reads help
+ * eliminate wire glitch.
+ */
+
+ clock = jiffies + ST0X_BUS_FREE_DELAY;
+
+#if !defined (ARBITRATE)
+ while (((STATUS | STATUS | STATUS) &
+ (STAT_BSY | STAT_SEL)) &&
+ (!st0x_aborted) && (jiffies < clock));
+
+ if (jiffies > clock)
+ return retcode(DID_BUS_BUSY);
+ else if (st0x_aborted)
+ return retcode(st0x_aborted);
+#endif
+
+#if (DEBUG & PHASE_SELECTION)
+ printk("scsi%d : phase = SELECTION\n", hostno);
+#endif
+
+ clock = jiffies + ST0X_SELECTION_DELAY;
+
+/*
+ * Arbitration/selection procedure :
+ * 1. Disable drivers
+ * 2. Write HOST adapter address bit
+ * 3. Set start arbitration.
+ * 4. We get either ARBITRATION COMPLETE or SELECT at this
+ * point.
+ * 5. OR our ID and targets on bus.
+ * 6. Enable SCSI drivers and asserted SEL and ATTN
+ */
+
+#if defined(ARBITRATE)
+ cli();
+ CONTROL = 0;
+ DATA = (controller_type == SEAGATE) ? 0x80 : 0x40;
+ CONTROL = CMD_START_ARB;
+ sti();
+ while (!((status_read = STATUS) & (STAT_ARB_CMPL | STAT_SEL)) &&
+ (jiffies < clock) && !st0x_aborted);
+
+ if (!(status_read & STAT_ARB_CMPL)) {
+#if (DEBUG & PHASE_SELECTION)
+ if (status_read & STAT_SEL)
+ printk("scsi%d : arbitration lost\n", hostno);
+ else
+ printk("scsi%d : arbitration timeout.\n", hostno);
+#endif
+ CONTROL = BASE_CMD;
+ return retcode(DID_NO_CONNECT);
+ };
+
+#if (DEBUG & PHASE_SELECTION)
+ printk("scsi%d : arbitration complete\n", hostno);
+#endif
+#endif
+
+
+/*
+ * When the SCSI device decides that we're gawking at it, it will
+ * respond by asserting BUSY on the bus.
+ *
+ * Note : the Seagate ST-01/02 product manual says that we should
+ * twiddle the DATA register before the control register. However,
+ * this does not work reliably so we do it the other way around.
+ *
+ * Probably could be a problem with arbitration too, we really should
+ * try this with a SCSI protocol or logic analyzer to see what is
+ * going on.
+ */
+ cli();
+ DATA = (unsigned char) ((1 << target) | (controller_type == SEAGATE ? 0x80 : 0x40));
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL |
+ (reselect ? CMD_ATTN : 0);
+ sti();
+ while (!((status_read = STATUS) & STAT_BSY) &&
+ (jiffies < clock) && !st0x_aborted)
+
+#if 0 && (DEBUG & PHASE_SELECTION)
+ {
+ temp = clock - jiffies;
+
+ if (!(jiffies % 5))
+ printk("seagate_st0x_timeout : %d \r",temp);
+
+ }
+ printk("Done. \n");
+ printk("scsi%d : status = %02x, seagate_st0x_timeout = %d, aborted = %02x \n",
+ hostno, status_read, temp, st0x_aborted);
+#else
+ ;
+#endif
+
+
+ if ((jiffies >= clock) && !(status_read & STAT_BSY))
+ {
+#if (DEBUG & PHASE_SELECTION)
+ printk ("scsi%d : NO CONNECT with target %d, status = %x \n",
+ hostno, target, STATUS);
+#endif
+ return retcode(DID_NO_CONNECT);
+ }
+
+/*
+ * If we have been aborted, and we have a command in progress, IE the
+ * target still has BSY asserted, then we will reset the bus, and
+ * notify the midlevel driver to expect sense.
+ */
+
+ if (st0x_aborted) {
+ CONTROL = BASE_CMD;
+ if (STATUS & STAT_BSY) {
+ printk("scsi%d : BST asserted after we've been aborted.\n",
+ hostno);
+ seagate_st0x_reset(NULL, 0);
+ return retcode(DID_RESET);
+ }
+ return retcode(st0x_aborted);
+ }
+
+/* Establish current pointers. Take into account scatter / gather */
+
+ if ((nobuffs = SCint->use_sg)) {
+#if (DEBUG & DEBUG_SG)
+ {
+ int i;
+ printk("scsi%d : scatter gather requested, using %d buffers.\n",
+ hostno, nobuffs);
+ for (i = 0; i < nobuffs; ++i)
+ printk("scsi%d : buffer %d address = %08x length = %d\n",
+ hostno, i, buffer[i].address, buffer[i].length);
+ }
+#endif
+
+ buffer = (struct scatterlist *) SCint->buffer;
+ len = buffer->length;
+ data = (unsigned char *) buffer->address;
+ } else {
+#if (DEBUG & DEBUG_SG)
+ printk("scsi%d : scatter gather not requested.\n", hostno);
+#endif
+ buffer = NULL;
+ len = SCint->request_bufflen;
+ data = (unsigned char *) SCint->request_buffer;
+ }
+
+#if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT))
+ printk("scsi%d : len = %d\n", hostno, len);
+#endif
+
+ break;
+#ifdef LINKED
+ case LINKED_RIGHT:
+ break;
+ case LINKED_WRONG:
+ break;
+#endif
+ }
+
+/*
+ * There are several conditions under which we wish to send a message :
+ * 1. When we are allowing disconnect / reconnect, and need to establish
+ * the I_T_L nexus via an IDENTIFY with the DiscPriv bit set.
+ *
+ * 2. When we are doing linked commands, are have the wrong I_T_L nexus
+ * established and want to send an ABORT message.
+ */
+
+
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE |
+ (((reselect == CAN_RECONNECT)
+#ifdef LINKED
+ || (reselect == LINKED_WRONG)
+#endif
+ ) ? CMD_ATTN : 0) ;
+
+/*
+ * INFORMATION TRANSFER PHASE
+ *
+ * The nasty looking read / write inline assembler loops we use for
+ * DATAIN and DATAOUT phases are approximately 4-5 times as fast as
+ * the 'C' versions - since we're moving 1024 bytes of data, this
+ * really adds up.
+ */
+
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
+ printk("scsi%d : phase = INFORMATION TRANSFER\n", hostno);
+#endif
+
+ incommand = 1;
+ transfersize = SCint->transfersize;
+ underflow = SCint->underflow;
+
+
+/*
+ * Now, we poll the device for status information,
+ * and handle any requests it makes. Note that since we are unsure of
+ * how much data will be flowing across the system, etc and cannot
+ * make reasonable timeouts, that we will instead have the midlevel
+ * driver handle any timeouts that occur in this phase.
+ */
+
+ while (((status_read = STATUS) & STAT_BSY) && !st0x_aborted && !done)
+ {
+#ifdef PARITY
+ if (status_read & STAT_PARITY)
+ {
+ printk("scsi%d : got parity error\n", hostno);
+ st0x_aborted = DID_PARITY;
+ }
+#endif
+
+ if (status_read & STAT_REQ)
+ {
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
+ if ((newphase = (status_read & REQ_MASK)) != phase)
+ {
+ phase = newphase;
+ switch (phase)
+ {
+ case REQ_DATAOUT:
+ printk("scsi%d : phase = DATA OUT\n",
+ hostno);
+ break;
+ case REQ_DATAIN :
+ printk("scsi%d : phase = DATA IN\n",
+ hostno);
+ break;
+ case REQ_CMDOUT :
+ printk("scsi%d : phase = COMMAND OUT\n",
+ hostno);
+ break;
+ case REQ_STATIN :
+ printk("scsi%d : phase = STATUS IN\n",
+ hostno);
+ break;
+ case REQ_MSGOUT :
+ printk("scsi%d : phase = MESSAGE OUT\n",
+ hostno);
+ break;
+ case REQ_MSGIN :
+ printk("scsi%d : phase = MESSAGE IN\n",
+ hostno);
+ break;
+ default :
+ printk("scsi%d : phase = UNKNOWN\n",
+ hostno);
+ st0x_aborted = DID_ERROR;
+ }
+ }
+#endif
+ switch (status_read & REQ_MASK)
+ {
+ case REQ_DATAOUT :
+/*
+ * If we are in fast mode, then we simply splat the data out
+ * in word-sized chunks as fast as we can.
+ */
+
+#ifdef FAST
+if (!len) {
+#if 0
+ printk("scsi%d: underflow to target %d lun %d \n",
+ hostno, target, lun);
+ st0x_aborted = DID_ERROR;
+ fast = 0;
+#endif
+ break;
+}
+
+if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
+#ifdef FAST32
+ && !(transfersize % 4)
+#endif
+ ) {
+#if (DEBUG & DEBUG_FAST)
+ printk("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n"
+ " len = %d, data = %08x\n", hostno, SCint->underflow,
+ SCint->transfersize, len, data);
+#endif
+
+ __asm__("
+ cld;
+"
+#ifdef FAST32
+" shr $2, %%ecx;
+1: lodsl;
+ movl %%eax, (%%edi);
+"
+#else
+"1: lodsb;
+ movb %%al, (%%edi);
+"
+#endif
+" loop 1b;" : :
+ /* input */
+ "D" (st0x_dr), "S" (data), "c" (SCint->transfersize) :
+ /* clobbered */
+ "eax", "ecx", "esi" );
+
+ len -= transfersize;
+ data += transfersize;
+
+#if (DEBUG & DEBUG_FAST)
+ printk("scsi%d : FAST transfer complete len = %d data = %08x\n",
+ hostno, len, data);
+#endif
+
+
+} else
+#endif
+
+{
+/*
+ * We loop as long as we are in a data out phase, there is data to send,
+ * and BSY is still active.
+ */
+ __asm__ (
+
+/*
+ Local variables :
+ len = ecx
+ data = esi
+ st0x_cr_sr = ebx
+ st0x_dr = edi
+
+ Test for any data here at all.
+*/
+ "\torl %%ecx, %%ecx
+ jz 2f
+
+ cld
+
+ movl " SYMBOL_NAME_STR(st0x_cr_sr) ", %%ebx
+ movl " SYMBOL_NAME_STR(st0x_dr) ", %%edi
+
+1: movb (%%ebx), %%al\n"
+/*
+ Test for BSY
+*/
+
+ "\ttest $1, %%al
+ jz 2f\n"
+
+/*
+ Test for data out phase - STATUS & REQ_MASK should be REQ_DATAOUT, which is 0.
+*/
+ "\ttest $0xe, %%al
+ jnz 2f \n"
+/*
+ Test for REQ
+*/
+ "\ttest $0x10, %%al
+ jz 1b
+ lodsb
+ movb %%al, (%%edi)
+ loop 1b
+
+2:
+ ":
+/* output */
+"=S" (data), "=c" (len) :
+/* input */
+"0" (data), "1" (len) :
+/* clobbered */
+"eax", "ebx", "edi");
+}
+
+ if (!len && nobuffs) {
+ --nobuffs;
+ ++buffer;
+ len = buffer->length;
+ data = (unsigned char *) buffer->address;
+#if (DEBUG & DEBUG_SG)
+ printk("scsi%d : next scatter-gather buffer len = %d address = %08x\n",
+ hostno, len, data);
+#endif
+ }
+ break;
+
+ case REQ_DATAIN :
+#ifdef SLOW_HANDSHAKE
+ if (borken) {
+#if (DEBUG & (PHASE_DATAIN))
+ transfered += len;
+#endif
+ for (; len && (STATUS & (REQ_MASK | STAT_REQ)) == (REQ_DATAIN |
+ STAT_REQ); --len) {
+ *data++ = DATA;
+ borken_wait();
+}
+#if (DEBUG & (PHASE_DATAIN))
+ transfered -= len;
+#endif
+ } else
+#endif
+#ifdef FAST
+if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
+#ifdef FAST32
+ && !(transfersize % 4)
+#endif
+ ) {
+#if (DEBUG & DEBUG_FAST)
+ printk("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n"
+ " len = %d, data = %08x\n", hostno, SCint->underflow,
+ SCint->transfersize, len, data);
+#endif
+ __asm__("
+ cld;
+"
+#ifdef FAST32
+" shr $2, %%ecx;
+1: movl (%%esi), %%eax;
+ stosl;
+"
+#else
+"1: movb (%%esi), %%al;
+ stosb;
+"
+#endif
+
+" loop 1b;" : :
+ /* input */
+ "S" (st0x_dr), "D" (data), "c" (SCint->transfersize) :
+ /* clobbered */
+ "eax", "ecx", "edi");
+
+ len -= transfersize;
+ data += transfersize;
+
+#if (DEBUG & PHASE_DATAIN)
+ printk("scsi%d: transfered += %d\n", hostno, transfersize);
+ transfered += transfersize;
+#endif
+
+#if (DEBUG & DEBUG_FAST)
+ printk("scsi%d : FAST transfer complete len = %d data = %08x\n",
+ hostno, len, data);
+#endif
+
+} else
+#endif
+{
+
+#if (DEBUG & PHASE_DATAIN)
+ printk("scsi%d: transfered += %d\n", hostno, len);
+ transfered += len; /* Assume we'll transfer it all, then
+ subtract what we *didn't* transfer */
+#endif
+
+/*
+ * We loop as long as we are in a data in phase, there is room to read,
+ * and BSY is still active
+ */
+
+ __asm__ (
+/*
+ Local variables :
+ ecx = len
+ edi = data
+ esi = st0x_cr_sr
+ ebx = st0x_dr
+
+ Test for room to read
+*/
+ "\torl %%ecx, %%ecx
+ jz 2f
+
+ cld
+ movl " SYMBOL_NAME_STR(st0x_cr_sr) ", %%esi
+ movl " SYMBOL_NAME_STR(st0x_dr) ", %%ebx
+
+1: movb (%%esi), %%al\n"
+/*
+ Test for BSY
+*/
+
+ "\ttest $1, %%al
+ jz 2f\n"
+
+/*
+ Test for data in phase - STATUS & REQ_MASK should be REQ_DATAIN, = STAT_IO, which is 4.
+*/
+ "\tmovb $0xe, %%ah
+ andb %%al, %%ah
+ cmpb $0x04, %%ah
+ jne 2f\n"
+
+/*
+ Test for REQ
+*/
+ "\ttest $0x10, %%al
+ jz 1b
+
+ movb (%%ebx), %%al
+ stosb
+ loop 1b\n"
+
+"2:\n"
+ :
+/* output */
+"=D" (data), "=c" (len) :
+/* input */
+"0" (data), "1" (len) :
+/* clobbered */
+"eax","ebx", "esi");
+
+#if (DEBUG & PHASE_DATAIN)
+ printk("scsi%d: transfered -= %d\n", hostno, len);
+ transfered -= len; /* Since we assumed all of Len got
+ * transfered, correct our mistake */
+#endif
+}
+
+ if (!len && nobuffs) {
+ --nobuffs;
+ ++buffer;
+ len = buffer->length;
+ data = (unsigned char *) buffer->address;
+#if (DEBUG & DEBUG_SG)
+ printk("scsi%d : next scatter-gather buffer len = %d address = %08x\n",
+ hostno, len, data);
+#endif
+ }
+
+ break;
+
+ case REQ_CMDOUT :
+ while (((status_read = STATUS) & STAT_BSY) &&
+ ((status_read & REQ_MASK) == REQ_CMDOUT))
+ if (status_read & STAT_REQ) {
+ DATA = *(const unsigned char *) cmnd;
+ cmnd = 1+(const unsigned char *) cmnd;
+#ifdef SLOW_HANDSHAKE
+ if (borken)
+ borken_wait();
+#endif
+ }
+ break;
+
+ case REQ_STATIN :
+ status = DATA;
+ break;
+
+ case REQ_MSGOUT :
+/*
+ * We can only have sent a MSG OUT if we requested to do this
+ * by raising ATTN. So, we must drop ATTN.
+ */
+
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
+/*
+ * If we are reconnecting, then we must send an IDENTIFY message in
+ * response to MSGOUT.
+ */
+ switch (reselect) {
+ case CAN_RECONNECT:
+ DATA = IDENTIFY(1, lun);
+
+#if (DEBUG & (PHASE_RESELECT | PHASE_MSGOUT))
+ printk("scsi%d : sent IDENTIFY message.\n", hostno);
+#endif
+ break;
+#ifdef LINKED
+ case LINKED_WRONG:
+ DATA = ABORT;
+ linked_connected = 0;
+ reselect = CAN_RECONNECT;
+ goto connect_loop;
+#if (DEBUG & (PHASE_MSGOUT | DEBUG_LINKED))
+ printk("scsi%d : sent ABORT message to cancel incorrect I_T_L nexus.\n", hostno);
+#endif
+#endif /* LINKED */
+#if (DEBUG & DEBUG_LINKED)
+ printk("correct\n");
+#endif
+ default:
+ DATA = NOP;
+ printk("scsi%d : target %d requested MSGOUT, sent NOP message.\n", hostno, target);
+ }
+ break;
+
+ case REQ_MSGIN :
+ switch (message = DATA) {
+ case DISCONNECT :
+ should_reconnect = 1;
+ current_data = data; /* WDE add */
+ current_buffer = buffer;
+ current_bufflen = len; /* WDE add */
+ current_nobuffs = nobuffs;
+#ifdef LINKED
+ linked_connected = 0;
+#endif
+ done=1;
+#if (DEBUG & (PHASE_RESELECT | PHASE_MSGIN))
+ printk("scsi%d : disconnected.\n", hostno);
+#endif
+ break;
+
+#ifdef LINKED
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+#endif
+ case COMMAND_COMPLETE :
+/*
+ * Note : we should check for underflow here.
+ */
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : command complete.\n", hostno);
+#endif
+ done = 1;
+ break;
+ case ABORT :
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : abort message.\n", hostno);
+#endif
+ done=1;
+ break;
+ case SAVE_POINTERS :
+ current_buffer = buffer;
+ current_bufflen = len; /* WDE add */
+ current_data = data; /* WDE mod */
+ current_nobuffs = nobuffs;
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : pointers saved.\n", hostno);
+#endif
+ break;
+ case RESTORE_POINTERS:
+ buffer=current_buffer;
+ cmnd=current_cmnd;
+ data=current_data; /* WDE mod */
+ len=current_bufflen;
+ nobuffs=current_nobuffs;
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : pointers restored.\n", hostno);
+#endif
+ break;
+ default:
+
+/*
+ * IDENTIFY distinguishes itself from the other messages by setting the
+ * high byte.
+ *
+ * Note : we need to handle at least one outstanding command per LUN,
+ * and need to hash the SCSI command for that I_T_L nexus based on the
+ * known ID (at this point) and LUN.
+ */
+
+ if (message & 0x80) {
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : IDENTIFY message received from id %d, lun %d.\n",
+ hostno, target, message & 7);
+#endif
+ } else {
+
+/*
+ * We should go into a MESSAGE OUT phase, and send a MESSAGE_REJECT
+ * if we run into a message that we don't like. The seagate driver
+ * needs some serious restructuring first though.
+ */
+
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : unknown message %d from target %d.\n",
+ hostno, message, target);
+#endif
+ }
+ }
+ break;
+
+ default :
+ printk("scsi%d : unknown phase.\n", hostno);
+ st0x_aborted = DID_ERROR;
+ }
+
+#ifdef SLOW_HANDSHAKE
+/*
+ * I really don't care to deal with borken devices in each single
+ * byte transfer case (ie, message in, message out, status), so
+ * I'll do the wait here if necessary.
+ */
+ if (borken)
+ borken_wait();
+#endif
+
+ } /* if ends */
+ } /* while ends */
+
+#if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT | PHASE_EXIT))
+ printk("scsi%d : Transfered %d bytes\n", hostno, transfered);
+#endif
+
+#if (DEBUG & PHASE_EXIT)
+#if 0 /* Doesn't work for scatter / gather */
+ printk("Buffer : \n");
+ for (i = 0; i < 20; ++i)
+ printk ("%02x ", ((unsigned char *) data)[i]); /* WDE mod */
+ printk("\n");
+#endif
+ printk("scsi%d : status = ", hostno);
+ print_status(status);
+ printk("message = %02x\n", message);
+#endif
+
+
+/* We shouldn't reach this until *after* BSY has been deasserted */
+#ifdef notyet
+ if (st0x_aborted) {
+ if (STATUS & STAT_BSY) {
+ seagate_st0x_reset(NULL);
+ st0x_aborted = DID_RESET;
+ }
+ abort_confirm = 1;
+ }
+#endif
+
+#ifdef LINKED
+else {
+/*
+ * Fix the message byte so that unsuspecting high level drivers don't
+ * puke when they see a LINKED COMMAND message in place of the COMMAND
+ * COMPLETE they may be expecting. Shouldn't be necessary, but it's
+ * better to be on the safe side.
+ *
+ * A non LINKED* message byte will indicate that the command completed,
+ * and we are now disconnected.
+ */
+
+ switch (message) {
+ case LINKED_CMD_COMPLETE :
+ case LINKED_FLG_CMD_COMPLETE :
+ message = COMMAND_COMPLETE;
+ linked_target = current_target;
+ linked_lun = current_lun;
+ linked_connected = 1;
+#if (DEBUG & DEBUG_LINKED)
+ printk("scsi%d : keeping I_T_L nexus established for linked command.\n",
+ hostno);
+#endif
+/*
+ * We also will need to adjust status to accommodate intermediate conditions.
+ */
+ if ((status == INTERMEDIATE_GOOD) ||
+ (status == INTERMEDIATE_C_GOOD))
+ status = GOOD;
+
+ break;
+/*
+ * We should also handle what are "normal" termination messages
+ * here (ABORT, BUS_DEVICE_RESET?, and COMMAND_COMPLETE individually,
+ * and flake if things aren't right.
+ */
+
+ default :
+#if (DEBUG & DEBUG_LINKED)
+ printk("scsi%d : closing I_T_L nexus.\n", hostno);
+#endif
+ linked_connected = 0;
+ }
+ }
+#endif /* LINKED */
+
+
+
+
+ if (should_reconnect) {
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : exiting seagate_st0x_queue_command() with reconnect enabled.\n",
+ hostno);
+#endif
+ CONTROL = BASE_CMD | CMD_INTR ;
+ } else
+ CONTROL = BASE_CMD;
+
+ return retcode (st0x_aborted);
+ }
+
+int seagate_st0x_abort (Scsi_Cmnd * SCpnt)
+ {
+ st0x_aborted = DID_ABORT;
+
+ return SCSI_ABORT_PENDING;
+ }
+
+/*
+ the seagate_st0x_reset function resets the SCSI bus
+*/
+
+int seagate_st0x_reset (Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+ {
+ unsigned clock;
+ /*
+ No timeouts - this command is going to fail because
+ it was reset.
+ */
+
+#ifdef DEBUG
+ printk("In seagate_st0x_reset()\n");
+#endif
+
+
+ /* assert RESET signal on SCSI bus. */
+
+ CONTROL = BASE_CMD | CMD_RST;
+ clock=jiffies+2;
+
+
+ /* Wait. */
+
+ while (jiffies < clock);
+
+ CONTROL = BASE_CMD;
+
+ st0x_aborted = DID_RESET;
+
+#ifdef DEBUG
+ printk("SCSI bus reset.\n");
+#endif
+ return SCSI_RESET_WAKEUP;
+ }
+
+#include <asm/segment.h>
+#include "sd.h"
+#include <scsi/scsi_ioctl.h>
+
+int seagate_st0x_biosparam(Disk * disk, kdev_t dev, int* ip) {
+ unsigned char buf[256 + sizeof(int) * 2], cmd[6], *data, *page;
+ int *sizes, result, formatted_sectors, total_sectors;
+ int cylinders, heads, sectors;
+ int capacity;
+
+/*
+ * Only SCSI-I CCS drives and later implement the necessary mode sense
+ * pages.
+ */
+
+ if (disk->device->scsi_level < 2)
+ return -1;
+
+ sizes = (int *) buf;
+ data = (unsigned char *) (sizes + 2);
+
+ cmd[0] = MODE_SENSE;
+ cmd[1] = (disk->device->lun << 5) & 0xe5;
+ cmd[2] = 0x04; /* Read page 4, rigid disk geometry page current values */
+ cmd[3] = 0;
+ cmd[4] = 255;
+ cmd[5] = 0;
+
+/*
+ * We are transferring 0 bytes in the out direction, and expect to get back
+ * 24 bytes for each mode page.
+ */
+
+ sizes[0] = 0;
+ sizes[1] = 256;
+
+ memcpy (data, cmd, 6);
+
+ if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
+/*
+ * The mode page lies beyond the MODE SENSE header, with length 4, and
+ * the BLOCK DESCRIPTOR, with length header[3].
+ */
+
+ page = data + 4 + data[3];
+ heads = (int) page[5];
+ cylinders = (page[2] << 16) | (page[3] << 8) | page[4];
+
+ cmd[2] = 0x03; /* Read page 3, format page current values */
+ memcpy (data, cmd, 6);
+
+ if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
+ page = data + 4 + data[3];
+ sectors = (page[10] << 8) | page[11];
+
+
+/*
+ * Get the total number of formatted sectors from the block descriptor,
+ * so we can tell how many are being used for alternates.
+ */
+
+ formatted_sectors = (data[4 + 1] << 16) | (data[4 + 2] << 8) |
+ data[4 + 3] ;
+
+ total_sectors = (heads * cylinders * sectors);
+
+/*
+ * Adjust the real geometry by subtracting
+ * (spare sectors / (heads * tracks)) cylinders from the number of cylinders.
+ *
+ * It appears that the CE cylinder CAN be a partial cylinder.
+ */
+
+
+printk("scsi%d : heads = %d cylinders = %d sectors = %d total = %d formatted = %d\n",
+ hostno, heads, cylinders, sectors, total_sectors, formatted_sectors);
+
+ if (!heads || !sectors || !cylinders)
+ result = -1;
+ else
+ cylinders -= ((total_sectors - formatted_sectors) / (heads * sectors));
+
+/*
+ * Now, we need to do a sanity check on the geometry to see if it is
+ * BIOS compatible. The maximum BIOS geometry is 1024 cylinders *
+ * 256 heads * 64 sectors.
+ */
+
+ if ((cylinders > 1024) || (sectors > 64)) {
+ /* The Seagate's seem to have some mapping
+ * Multiple heads * sectors * cyl to get capacity
+ * Then start rounding down. */
+ capacity = heads * sectors * cylinders;
+ sectors = 17; /* Old MFM Drives use this, so does the Seagate */
+ heads = 2;
+ capacity = capacity / sectors;
+ while (cylinders > 1024)
+ {
+ heads *= 2; /* For some reason, they go in multiples */
+ cylinders = capacity / heads;
+ }
+ }
+ ip[0] = heads;
+ ip[1] = sectors;
+ ip[2] = cylinders;
+
+/*
+ * There should be an alternate mapping for things the seagate doesn't
+ * understand, but I couldn't say what it is with reasonable certainty.
+ */
+
+ }
+ }
+
+ return result;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = SEAGATE_ST0X;
+
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/seagate.h b/linux/src/drivers/scsi/seagate.h
new file mode 100644
index 00000000..da18dbea
--- /dev/null
+++ b/linux/src/drivers/scsi/seagate.h
@@ -0,0 +1,139 @@
+/*
+ * seagate.h Copyright (C) 1992 Drew Eckhardt
+ * low level scsi driver header for ST01/ST02 by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+
+#ifndef _SEAGATE_H
+ #define SEAGATE_H
+/*
+ $Header
+*/
+#ifndef ASM
+int seagate_st0x_detect(Scsi_Host_Template *);
+int seagate_st0x_command(Scsi_Cmnd *);
+int seagate_st0x_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+
+int seagate_st0x_abort(Scsi_Cmnd *);
+const char *seagate_st0x_info(struct Scsi_Host *);
+int seagate_st0x_reset(Scsi_Cmnd *, unsigned int);
+int seagate_st0x_proc_info(char *,char **,off_t,int,int,int);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#include <linux/kdev_t.h>
+int seagate_st0x_biosparam(Disk *, kdev_t, int*);
+
+#define SEAGATE_ST0X { NULL, NULL, NULL, seagate_st0x_proc_info, \
+ NULL, seagate_st0x_detect, \
+ NULL, \
+ seagate_st0x_info, seagate_st0x_command, \
+ seagate_st0x_queue_command, seagate_st0x_abort, \
+ seagate_st0x_reset, NULL, seagate_st0x_biosparam, \
+ 1, 7, SG_ALL, 1, 0, 0, DISABLE_CLUSTERING}
+#endif
+
+
+/*
+ defining PARITY causes parity data to be checked
+*/
+
+#define PARITY
+
+
+/*
+ Thanks to Brian Antoine for the example code in his Messy-Loss ST-01
+ driver, and Mitsugu Suzuki for information on the ST-01
+ SCSI host.
+*/
+
+/*
+ CONTROL defines
+*/
+
+#define CMD_RST 0x01
+#define CMD_SEL 0x02
+#define CMD_BSY 0x04
+#define CMD_ATTN 0x08
+#define CMD_START_ARB 0x10
+#define CMD_EN_PARITY 0x20
+#define CMD_INTR 0x40
+#define CMD_DRVR_ENABLE 0x80
+
+/*
+ STATUS
+*/
+
+#define STAT_BSY 0x01
+#define STAT_MSG 0x02
+#define STAT_IO 0x04
+#define STAT_CD 0x08
+#define STAT_REQ 0x10
+#define STAT_SEL 0x20
+#define STAT_PARITY 0x40
+#define STAT_ARB_CMPL 0x80
+
+/*
+ REQUESTS
+*/
+
+#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG)
+#define REQ_DATAOUT 0
+#define REQ_DATAIN STAT_IO
+#define REQ_CMDOUT STAT_CD
+#define REQ_STATIN (STAT_CD | STAT_IO)
+#define REQ_MSGOUT (STAT_MSG | STAT_CD)
+#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO)
+
+extern volatile int seagate_st0x_timeout;
+
+#ifdef PARITY
+ #define BASE_CMD CMD_EN_PARITY
+#else
+ #define BASE_CMD 0
+#endif
+
+/*
+ Debugging code
+*/
+
+#define PHASE_BUS_FREE 1
+#define PHASE_ARBITRATION 2
+#define PHASE_SELECTION 4
+#define PHASE_DATAIN 8
+#define PHASE_DATAOUT 0x10
+#define PHASE_CMDOUT 0x20
+#define PHASE_MSGIN 0x40
+#define PHASE_MSGOUT 0x80
+#define PHASE_STATUSIN 0x100
+#define PHASE_ETC (PHASE_DATAIN | PHASE_DATA_OUT | PHASE_CMDOUT | PHASE_MSGIN | PHASE_MSGOUT | PHASE_STATUSIN)
+#define PRINT_COMMAND 0x200
+#define PHASE_EXIT 0x400
+#define PHASE_RESELECT 0x800
+#define DEBUG_FAST 0x1000
+#define DEBUG_SG 0x2000
+#define DEBUG_LINKED 0x4000
+#define DEBUG_BORKEN 0x8000
+
+/*
+ * Control options - these are timeouts specified in .01 seconds.
+ */
+
+/* 30, 20 work */
+#define ST0X_BUS_FREE_DELAY 25
+#define ST0X_SELECTION_DELAY 25
+
+#define eoi() __asm__("push %%eax\nmovb $0x20, %%al\noutb %%al, $0x20\npop %%eax"::)
+
+#define SEAGATE 1 /* these determine the type of the controller */
+#define FD 2
+
+#define ST0X_ID_STR "Seagate ST-01/ST-02"
+#define FD_ID_STR "TMC-8XX/TMC-950"
+
+#endif
+
diff --git a/linux/src/drivers/scsi/sr.c b/linux/src/drivers/scsi/sr.c
new file mode 100644
index 00000000..446a221b
--- /dev/null
+++ b/linux/src/drivers/scsi/sr.c
@@ -0,0 +1,1281 @@
+/*
+ * sr.c Copyright (C) 1992 David Giller
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * adapted from:
+ * sd.c Copyright (C) 1992 Drew Eckhardt
+ * Linux scsi disk driver by
+ * Drew Eckhardt <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale ericy@cais.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Modified by Eric Youngdale eric@aib.com to support loadable
+ * low-level scsi drivers.
+ *
+ * Modified by Thomas Quinot thomas@melchior.cuivre.fdn.fr to
+ * provide auto-eject.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <linux/interrupt.h>
+#include <asm/system.h>
+
+#define MAJOR_NR SCSI_CDROM_MAJOR
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sr.h"
+#include <scsi/scsi_ioctl.h> /* For the door lock/unlock commands */
+#include "constants.h"
+
+#define MAX_RETRIES 3
+#define SR_TIMEOUT (30 * HZ)
+
+static int sr_init(void);
+static void sr_finish(void);
+static int sr_attach(Scsi_Device *);
+static int sr_detect(Scsi_Device *);
+static void sr_detach(Scsi_Device *);
+
+struct Scsi_Device_Template sr_template = {NULL, "cdrom", "sr", NULL, TYPE_ROM,
+ SCSI_CDROM_MAJOR, 0, 0, 0, 1,
+ sr_detect, sr_init,
+ sr_finish, sr_attach, sr_detach};
+
+Scsi_CD * scsi_CDs = NULL;
+static int * sr_sizes;
+
+static int * sr_blocksizes;
+
+static int sr_open(struct inode *, struct file *);
+void get_sectorsize(int);
+void sr_photocd(struct inode *);
+
+extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+void requeue_sr_request (Scsi_Cmnd * SCpnt);
+static int check_cdrom_media_change(kdev_t);
+
+static void sr_release(struct inode * inode, struct file * file)
+{
+ sync_dev(inode->i_rdev);
+ if(! --scsi_CDs[MINOR(inode->i_rdev)].device->access_count)
+ {
+ sr_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ if (scsi_CDs[MINOR(inode->i_rdev)].auto_eject)
+ sr_ioctl(inode, NULL, CDROMEJECT, 0);
+ }
+ if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)
+ (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)--;
+ if(sr_template.usage_count) (*sr_template.usage_count)--;
+}
+
+static struct file_operations sr_fops =
+{
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ sr_ioctl, /* ioctl */
+ NULL, /* mmap */
+ sr_open, /* special open code */
+ sr_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ check_cdrom_media_change, /* Disk change */
+ NULL /* revalidate */
+};
+
+/*
+ * This function checks to see if the media has been changed in the
+ * CDROM drive. It is possible that we have already sensed a change,
+ * or the drive may have sensed one and not yet reported it. We must
+ * be ready for either case. This function always reports the current
+ * value of the changed bit. If flag is 0, then the changed bit is reset.
+ * This function could be done as an ioctl, but we would need to have
+ * an inode for that to work, and we do not always have one.
+ */
+
+int check_cdrom_media_change(kdev_t full_dev){
+ int retval, target;
+ struct inode inode;
+ int flag = 0;
+
+ target = MINOR(full_dev);
+
+ if (target >= sr_template.nr_dev) {
+ printk("CD-ROM request error: invalid device.\n");
+ return 0;
+ };
+
+ inode.i_rdev = full_dev; /* This is all we really need here */
+ retval = sr_ioctl(&inode, NULL, SCSI_IOCTL_TEST_UNIT_READY, 0);
+
+ if(retval){ /* Unable to test, unit probably not ready. This usually
+ * means there is no disc in the drive. Mark as changed,
+ * and we will figure it out later once the drive is
+ * available again. */
+
+ scsi_CDs[target].device->changed = 1;
+ return 1; /* This will force a flush, if called from
+ * check_disk_change */
+ };
+
+ retval = scsi_CDs[target].device->changed;
+ if(!flag) {
+ scsi_CDs[target].device->changed = 0;
+ /* If the disk changed, the capacity will now be different,
+ * so we force a re-read of this information */
+ if (retval) scsi_CDs[target].needs_sector_size = 1;
+ };
+ return retval;
+}
+
+/*
+ * rw_intr is the interrupt routine for the device driver. It will be notified on the
+ * end of a SCSI read / write, and will take on of several actions based on success or failure.
+ */
+
+static void rw_intr (Scsi_Cmnd * SCpnt)
+{
+ int result = SCpnt->result;
+ int this_count = SCpnt->this_count;
+ int good_sectors = (result == 0 ? this_count : 0);
+ int block_sectors = 0;
+
+#ifdef DEBUG
+ printk("sr.c done: %x %x\n",result, SCpnt->request.bh->b_data);
+#endif
+ /*
+ Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial success.
+ Since this is a relatively rare error condition, no care is taken to
+ avoid unnecessary additional work such as memcpy's that could be avoided.
+ */
+
+ if (driver_byte(result) != 0 && /* An error occurred */
+ SCpnt->sense_buffer[0] == 0xF0 && /* Sense data is valid */
+ (SCpnt->sense_buffer[2] == MEDIUM_ERROR ||
+ SCpnt->sense_buffer[2] == VOLUME_OVERFLOW ||
+ SCpnt->sense_buffer[2] == ILLEGAL_REQUEST))
+ {
+ long error_sector = (SCpnt->sense_buffer[3] << 24) |
+ (SCpnt->sense_buffer[4] << 16) |
+ (SCpnt->sense_buffer[5] << 8) |
+ SCpnt->sense_buffer[6];
+ int device_nr = DEVICE_NR(SCpnt->request.rq_dev);
+ if (SCpnt->request.bh != NULL)
+ block_sectors = SCpnt->request.bh->b_size >> 9;
+ if (block_sectors < 4) block_sectors = 4;
+ if (scsi_CDs[device_nr].sector_size == 2048)
+ error_sector <<= 2;
+ error_sector &= ~ (block_sectors - 1);
+ good_sectors = error_sector - SCpnt->request.sector;
+ if (good_sectors < 0 || good_sectors >= this_count)
+ good_sectors = 0;
+ /*
+ The SCSI specification allows for the value returned by READ
+ CAPACITY to be up to 75 2K sectors past the last readable
+ block. Therefore, if we hit a medium error within the last
+ 75 2K sectors, we decrease the saved size value.
+ */
+ if ((error_sector >> 1) < sr_sizes[device_nr] &&
+ scsi_CDs[device_nr].capacity - error_sector < 4*75)
+ sr_sizes[device_nr] = error_sector >> 1;
+ }
+
+ if (good_sectors > 0)
+ { /* Some sectors were read successfully. */
+ if (SCpnt->use_sg == 0) {
+ if (SCpnt->buffer != SCpnt->request.buffer)
+ {
+ int offset;
+ offset = (SCpnt->request.sector % 4) << 9;
+ memcpy((char *)SCpnt->request.buffer,
+ (char *)SCpnt->buffer + offset,
+ good_sectors << 9);
+ /* Even though we are not using scatter-gather, we look
+ * ahead and see if there is a linked request for the
+ * other half of this buffer. If there is, then satisfy
+ * it. */
+ if((offset == 0) && good_sectors == 2 &&
+ SCpnt->request.nr_sectors > good_sectors &&
+ SCpnt->request.bh &&
+ SCpnt->request.bh->b_reqnext &&
+ SCpnt->request.bh->b_reqnext->b_size == 1024) {
+ memcpy((char *)SCpnt->request.bh->b_reqnext->b_data,
+ (char *)SCpnt->buffer + 1024,
+ 1024);
+ good_sectors += 2;
+ };
+
+ scsi_free(SCpnt->buffer, 2048);
+ }
+ } else {
+ struct scatterlist * sgpnt;
+ int i;
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+ if (sgpnt[i].alt_address) {
+ if (sgpnt[i].alt_address != sgpnt[i].address) {
+ memcpy(sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);
+ };
+ scsi_free(sgpnt[i].address, sgpnt[i].length);
+ };
+ };
+ scsi_free(SCpnt->buffer, SCpnt->sglist_len); /* Free list of scatter-gather pointers */
+ if(SCpnt->request.sector % 4) good_sectors -= 2;
+ /* See if there is a padding record at the end that needs to be removed */
+ if(good_sectors > SCpnt->request.nr_sectors)
+ good_sectors -= 2;
+ };
+
+#ifdef DEBUG
+ printk("(%x %x %x) ",SCpnt->request.bh, SCpnt->request.nr_sectors,
+ good_sectors);
+#endif
+ if (SCpnt->request.nr_sectors > this_count)
+ {
+ SCpnt->request.errors = 0;
+ if (!SCpnt->request.bh)
+ panic("sr.c: linked page request (%lx %x)",
+ SCpnt->request.sector, this_count);
+ }
+
+ SCpnt = end_scsi_request(SCpnt, 1, good_sectors); /* All done */
+ if (result == 0)
+ {
+ requeue_sr_request(SCpnt);
+ return;
+ }
+ }
+
+ if (good_sectors == 0) {
+ /* We only come through here if no sectors were read successfully. */
+
+ /* Free up any indirection buffers we allocated for DMA purposes. */
+ if (SCpnt->use_sg) {
+ struct scatterlist * sgpnt;
+ int i;
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+ if (sgpnt[i].alt_address) {
+ scsi_free(sgpnt[i].address, sgpnt[i].length);
+ }
+ }
+ scsi_free(SCpnt->buffer, SCpnt->sglist_len); /* Free list of scatter-gather pointers */
+ } else {
+ if (SCpnt->buffer != SCpnt->request.buffer)
+ scsi_free(SCpnt->buffer, SCpnt->bufflen);
+ }
+
+ }
+
+ if (driver_byte(result) != 0) {
+ if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) {
+ if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
+ /* detected disc change. set a bit and quietly refuse
+ * further access. */
+
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->changed = 1;
+ SCpnt = end_scsi_request(SCpnt, 0, this_count);
+ requeue_sr_request(SCpnt);
+ return;
+ }
+ }
+
+ if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
+ printk("CD-ROM error: ");
+ print_sense("sr", SCpnt);
+ printk("command was: ");
+ print_command(SCpnt->cmnd);
+ if (scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].ten) {
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].ten = 0;
+ requeue_sr_request(SCpnt);
+ result = 0;
+ return;
+ } else {
+ SCpnt = end_scsi_request(SCpnt, 0, this_count);
+ requeue_sr_request(SCpnt); /* Do next request */
+ return;
+ }
+
+ }
+
+ if (SCpnt->sense_buffer[2] == NOT_READY) {
+ printk("CD-ROM not ready. Make sure you have a disc in the drive.\n");
+ SCpnt = end_scsi_request(SCpnt, 0, this_count);
+ requeue_sr_request(SCpnt); /* Do next request */
+ return;
+ }
+
+ if (SCpnt->sense_buffer[2] == MEDIUM_ERROR) {
+ printk("scsi%d: MEDIUM ERROR on "
+ "channel %d, id %d, lun %d, CDB: ",
+ SCpnt->host->host_no, (int) SCpnt->channel,
+ (int) SCpnt->target, (int) SCpnt->lun);
+ print_command(SCpnt->cmnd);
+ print_sense("sr", SCpnt);
+ SCpnt = end_scsi_request(SCpnt, 0, block_sectors);
+ requeue_sr_request(SCpnt);
+ return;
+ }
+
+ if (SCpnt->sense_buffer[2] == VOLUME_OVERFLOW) {
+ printk("scsi%d: VOLUME OVERFLOW on "
+ "channel %d, id %d, lun %d, CDB: ",
+ SCpnt->host->host_no, (int) SCpnt->channel,
+ (int) SCpnt->target, (int) SCpnt->lun);
+ print_command(SCpnt->cmnd);
+ print_sense("sr", SCpnt);
+ SCpnt = end_scsi_request(SCpnt, 0, block_sectors);
+ requeue_sr_request(SCpnt);
+ return;
+ }
+ }
+
+ /* We only get this far if we have an error we have not recognized */
+ if(result) {
+ printk("SCSI CD error : host %d id %d lun %d return code = %03x\n",
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->host->host_no,
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->id,
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->lun,
+ result);
+
+ if (status_byte(result) == CHECK_CONDITION)
+ print_sense("sr", SCpnt);
+
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
+ requeue_sr_request(SCpnt);
+ }
+}
+
+/*
+ * Here I tried to implement support for multisession-CD's
+ *
+ * Much of this has do be done with vendor-specific SCSI-commands, because
+ * multisession is newer than the SCSI-II standard.
+ * So I have to complete it step by step. Useful information is welcome.
+ *
+ * Actually works:
+ * - NEC: Detection and support of multisession CD's. Special handling
+ * for XA-disks is not necessary.
+ *
+ * - TOSHIBA: setting density is done here now, mounting PhotoCD's should
+ * work now without running the program "set_density"
+ * Multisession CD's are supported too.
+ *
+ * Gerd Knorr <kraxel@cs.tu-berlin.de>
+ */
+/*
+ * 19950704 operator@melchior.cuivre.fdn.fr (Thomas Quinot)
+ *
+ * - SONY: Same as Nec.
+ *
+ * - PIONEER: works with SONY code (may be others too ?)
+ */
+
+void sr_photocd(struct inode *inode)
+{
+ unsigned long sector,min,sec,frame;
+ unsigned char buf[40]; /* the buffer for the ioctl */
+ unsigned char *cmd; /* the scsi-command */
+ unsigned char *send; /* the data we send to the drive ... */
+ unsigned char *rec; /* ... and get back */
+ int rc,is_xa,no_multi;
+
+ if (scsi_CDs[MINOR(inode->i_rdev)].xa_flags & 0x02) {
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: CDROM and/or driver do not support multisession CD's");
+#endif
+ return;
+ }
+
+ if (!suser()) {
+ /* I'm not the superuser, so SCSI_IOCTL_SEND_COMMAND isn't allowed
+ * for me. That's why mpcd_sector will be initialized with zero,
+ * because I'm not able to get the right value. Necessary only if
+ * access_count is 1, else no disk change happened since the last
+ * call of this function and we can keep the old value.
+ */
+ if (1 == scsi_CDs[MINOR(inode->i_rdev)].device->access_count) {
+ scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = 0;
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags &= ~0x01;
+ }
+ return;
+ }
+
+ sector = 0;
+ is_xa = 0;
+ no_multi = 0;
+ cmd = rec = &buf[8];
+
+ switch(scsi_CDs[MINOR(inode->i_rdev)].device->manufacturer) {
+
+ case SCSI_MAN_NEC:
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: use NEC code\n");
+#endif
+ memset(buf,0,40);
+ *((unsigned int*)buf) = 0x0; /* we send nothing... */
+ *((unsigned int*)buf+1) = 0x16; /* and receive 0x16 bytes */
+ cmd[0] = 0xde;
+ cmd[1] = 0x03;
+ cmd[2] = 0xb0;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ if (rc != 0x28000002) /* drop "not ready" */
+ printk(KERN_WARNING"sr_photocd: ioctl error (NEC): 0x%x\n",rc);
+ break;
+ }
+ if (rec[14] != 0 && rec[14] != 0xb0) {
+ printk(KERN_INFO"sr_photocd: (NEC) Hmm, seems the CDROM doesn't support multisession CD's\n");
+ no_multi = 1;
+ break;
+ }
+ min = (unsigned long) rec[15]/16*10 + (unsigned long) rec[15]%16;
+ sec = (unsigned long) rec[16]/16*10 + (unsigned long) rec[16]%16;
+ frame = (unsigned long) rec[17]/16*10 + (unsigned long) rec[17]%16;
+ sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
+ is_xa = (rec[14] == 0xb0);
+#ifdef DEBUG
+ if (sector) {
+ printk(KERN_DEBUG "sr_photocd: multisession CD detected. start: %lu\n",sector);
+ }
+#endif
+ break;
+
+ case SCSI_MAN_TOSHIBA:
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: use TOSHIBA code\n");
+#endif
+
+ /* we request some disc information (is it a XA-CD ?,
+ * where starts the last session ?) */
+ memset(buf,0,40);
+ *((unsigned int*)buf) = (unsigned int) 0;
+ *((unsigned int*)buf+1) = (unsigned int) 4; /* receive 4 bytes */
+ cmd[0] = (unsigned char) 0x00c7;
+ cmd[1] = (unsigned char) 3;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ if (rc == 0x28000002) {
+ /* Got a "not ready" - error. No chance to find out if this is
+ * because there is no CD in the drive or because the drive
+ * don't knows multisession CD's. So I need to do an extra
+ * check... */
+ if (!kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_TEST_UNIT_READY, NULL)) {
+ printk(KERN_INFO "sr_photocd: (TOSHIBA) Hmm, seems the CDROM doesn't support multisession CD's\n");
+ no_multi = 1;
+ }
+ } else
+ printk(KERN_INFO"sr_photocd: ioctl error (TOSHIBA #1): 0x%x\n",rc);
+ break; /* if the first ioctl fails, we don't call the second one */
+ }
+ is_xa = (rec[0] == 0x20);
+ min = (unsigned long) rec[1]/16*10 + (unsigned long) rec[1]%16;
+ sec = (unsigned long) rec[2]/16*10 + (unsigned long) rec[2]%16;
+ frame = (unsigned long) rec[3]/16*10 + (unsigned long) rec[3]%16;
+ sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
+ if (sector) {
+ sector -= CD_BLOCK_OFFSET;
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: multisession CD detected: start: %lu\n",sector);
+#endif
+ }
+
+ /* now we do a get_density... */
+ memset(buf,0,40);
+ *((unsigned int*)buf) = (unsigned int) 0;
+ *((unsigned int*)buf+1) = (unsigned int) 12;
+ cmd[0] = (unsigned char) MODE_SENSE;
+ cmd[2] = (unsigned char) 1;
+ cmd[4] = (unsigned char) 12;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ printk(KERN_WARNING "sr_photocd: ioctl error (TOSHIBA #2): 0x%x\n",rc);
+ break;
+ }
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: get_density: 0x%x\n",rec[4]);
+#endif
+
+ /* ...and only if necessary a set_density */
+ if ((rec[4] != 0x81 && is_xa) || (rec[4] != 0 && !is_xa)) {
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: doing set_density\n");
+#endif
+ memset(buf,0,40);
+ *((unsigned int*)buf) = (unsigned int) 12; /* send 12 bytes */
+ *((unsigned int*)buf+1) = (unsigned int) 0;
+ cmd[0] = (unsigned char) MODE_SELECT;
+ cmd[1] = (unsigned char) (1 << 4);
+ cmd[4] = (unsigned char) 12;
+ send = &cmd[6]; /* this is a 6-Byte command */
+ send[ 3] = (unsigned char) 0x08; /* data for cmd */
+ /* density 0x81 for XA, 0 else */
+ send[ 4] = (is_xa) ?
+ (unsigned char) 0x81 : (unsigned char) 0;
+ send[10] = (unsigned char) 0x08;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ printk(KERN_WARNING "sr_photocd: ioctl error (TOSHIBA #3): 0x%x\n",rc);
+ }
+ /* The set_density command may have changed the
+ * sector size or capacity. */
+ scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size = 1;
+ }
+ break;
+
+ case SCSI_MAN_SONY: /* Thomas QUINOT <thomas@melchior.cuivre.fdn.fr> */
+ case SCSI_MAN_PIONEER:
+ case SCSI_MAN_UNKNOWN:
+#ifdef DEBUG
+ printk(KERN_DEBUG "sr_photocd: use SONY/PIONEER code\n");
+#endif
+ get_sectorsize(MINOR(inode->i_rdev)); /* spinup (avoid timeout) */
+ memset(buf,0,40);
+ *((unsigned int*)buf) = 0x0; /* we send nothing... */
+ *((unsigned int*)buf+1) = 0x0c; /* and receive 0x0c bytes */
+ cmd[0] = READ_TOC;
+ cmd[8] = 0x0c;
+ cmd[9] = 0x40;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+
+ if (rc != 0) {
+ if (rc != 0x28000002) /* drop "not ready" */
+ printk(KERN_WARNING "sr_photocd: ioctl error (SONY/PIONEER): 0x%x\n",rc);
+ break;
+ }
+ if ((rec[0] << 8) + rec[1] < 0x0a) {
+ printk(KERN_INFO "sr_photocd: (SONY/PIONEER) Hmm, seems the CDROM doesn't support multisession CD's\n");
+ no_multi = 1;
+ break;
+ }
+ sector = rec[11] + (rec[10] << 8) + (rec[9] << 16) + (rec[8] << 24);
+ is_xa = !!sector;
+#ifdef DEBUG
+ if (sector)
+ printk (KERN_DEBUG "sr_photocd: multisession CD detected. start: %lu\n",sector);
+#endif
+ break;
+
+ case SCSI_MAN_NEC_OLDCDR:
+ default:
+ sector = 0;
+ no_multi = 1;
+ break; }
+
+ scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = sector;
+ if (is_xa)
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags |= 0x01;
+ else
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags &= ~0x01;
+ if (no_multi)
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags |= 0x02;
+ return;
+}
+
+static int sr_open(struct inode * inode, struct file * filp)
+{
+ if(MINOR(inode->i_rdev) >= sr_template.nr_dev ||
+ !scsi_CDs[MINOR(inode->i_rdev)].device) return -ENXIO; /* No such device */
+
+ if (filp->f_mode & 2)
+ return -EROFS;
+
+ if(sr_template.usage_count) (*sr_template.usage_count)++;
+
+ sr_ioctl(inode,filp,CDROMCLOSETRAY,0);
+ check_disk_change(inode->i_rdev);
+
+ if(!scsi_CDs[MINOR(inode->i_rdev)].device->access_count++)
+ sr_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+ if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)
+ (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)++;
+
+ sr_photocd(inode);
+
+ /* If this device did not have media in the drive at boot time, then
+ * we would have been unable to get the sector size. Check to see if
+ * this is the case, and try again.
+ */
+
+ if(scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size)
+ get_sectorsize(MINOR(inode->i_rdev));
+
+ return 0;
+}
+
+
+/*
+ * do_sr_request() is the request handler function for the sr driver.
+ * Its function in life is to take block device requests, and
+ * translate them to SCSI commands.
+ */
+
+static void do_sr_request (void)
+{
+ Scsi_Cmnd * SCpnt = NULL;
+ struct request * req = NULL;
+ Scsi_Device * SDev;
+ unsigned long flags;
+ int flag = 0;
+
+ while (1==1){
+ save_flags(flags);
+ cli();
+ if (CURRENT != NULL && CURRENT->rq_status == RQ_INACTIVE) {
+ restore_flags(flags);
+ return;
+ };
+
+ INIT_SCSI_REQUEST;
+
+ SDev = scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device;
+
+ /*
+ * I am not sure where the best place to do this is. We need
+ * to hook in a place where we are likely to come if in user
+ * space.
+ */
+ if( SDev->was_reset )
+ {
+ /*
+ * We need to relock the door, but we might
+ * be in an interrupt handler. Only do this
+ * from user space, since we do not want to
+ * sleep from an interrupt.
+ */
+ if( SDev->removable && !intr_count )
+ {
+ scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
+ }
+ SDev->was_reset = 0;
+ }
+
+ if (flag++ == 0)
+ SCpnt = allocate_device(&CURRENT,
+ scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device, 0);
+ else SCpnt = NULL;
+ restore_flags(flags);
+
+ /* This is a performance enhancement. We dig down into the request list and
+ * try to find a queueable request (i.e. device not busy, and host able to
+ * accept another command. If we find one, then we queue it. This can
+ * make a big difference on systems with more than one disk drive. We want
+ * to have the interrupts off when monkeying with the request list, because
+ * otherwise the kernel might try to slip in a request in between somewhere. */
+
+ if (!SCpnt && sr_template.nr_dev > 1){
+ struct request *req1;
+ req1 = NULL;
+ save_flags(flags);
+ cli();
+ req = CURRENT;
+ while(req){
+ SCpnt = request_queueable(req,
+ scsi_CDs[DEVICE_NR(req->rq_dev)].device);
+ if(SCpnt) break;
+ req1 = req;
+ req = req->next;
+ };
+ if (SCpnt && req->rq_status == RQ_INACTIVE) {
+ if (req == CURRENT)
+ CURRENT = CURRENT->next;
+ else
+ req1->next = req->next;
+ };
+ restore_flags(flags);
+ };
+
+ if (!SCpnt)
+ return; /* Could not find anything to do */
+
+ wake_up(&wait_for_request);
+
+ /* Queue command */
+ requeue_sr_request(SCpnt);
+ }; /* While */
+}
+
+void requeue_sr_request (Scsi_Cmnd * SCpnt)
+{
+ unsigned int dev, block, realcount;
+ unsigned char cmd[10], *buffer, tries;
+ int this_count, start, end_rec;
+
+ tries = 2;
+
+ repeat:
+ if(!SCpnt || SCpnt->request.rq_status == RQ_INACTIVE) {
+ do_sr_request();
+ return;
+ }
+
+ dev = MINOR(SCpnt->request.rq_dev);
+ block = SCpnt->request.sector;
+ buffer = NULL;
+ this_count = 0;
+
+ if (dev >= sr_template.nr_dev) {
+ /* printk("CD-ROM request error: invalid device.\n"); */
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ tries = 2;
+ goto repeat;
+ }
+
+ if (!scsi_CDs[dev].use) {
+ /* printk("CD-ROM request error: device marked not in use.\n"); */
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ tries = 2;
+ goto repeat;
+ }
+
+ if (scsi_CDs[dev].device->changed) {
+ /*
+ * quietly refuse to do anything to a changed disc
+ * until the changed bit has been reset
+ */
+ /* printk("CD-ROM has been changed. Prohibiting further I/O.\n"); */
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ tries = 2;
+ goto repeat;
+ }
+
+ switch (SCpnt->request.cmd)
+ {
+ case WRITE:
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ break;
+ case READ :
+ cmd[0] = READ_6;
+ break;
+ default :
+ panic ("Unknown sr command %d\n", SCpnt->request.cmd);
+ }
+
+ cmd[1] = (SCpnt->lun << 5) & 0xe0;
+
+ /*
+ * Now do the grungy work of figuring out which sectors we need, and
+ * where in memory we are going to put them.
+ *
+ * The variables we need are:
+ *
+ * this_count= number of 512 byte sectors being read
+ * block = starting cdrom sector to read.
+ * realcount = # of cdrom sectors to read
+ *
+ * The major difference between a scsi disk and a scsi cdrom
+ * is that we will always use scatter-gather if we can, because we can
+ * work around the fact that the buffer cache has a block size of 1024,
+ * and we have 2048 byte sectors. This code should work for buffers that
+ * are any multiple of 512 bytes long.
+ */
+
+ SCpnt->use_sg = 0;
+
+ if (SCpnt->host->sg_tablesize > 0 &&
+ (!need_isa_buffer ||
+ dma_free_sectors >= 10)) {
+ struct buffer_head * bh;
+ struct scatterlist * sgpnt;
+ int count, this_count_max;
+ bh = SCpnt->request.bh;
+ this_count = 0;
+ count = 0;
+ this_count_max = (scsi_CDs[dev].ten ? 0xffff : 0xff) << 4;
+ /* Calculate how many links we can use. First see if we need
+ * a padding record at the start */
+ this_count = SCpnt->request.sector % 4;
+ if(this_count) count++;
+ while(bh && count < SCpnt->host->sg_tablesize) {
+ if ((this_count + (bh->b_size >> 9)) > this_count_max) break;
+ this_count += (bh->b_size >> 9);
+ count++;
+ bh = bh->b_reqnext;
+ };
+ /* Fix up in case of an odd record at the end */
+ end_rec = 0;
+ if(this_count % 4) {
+ if (count < SCpnt->host->sg_tablesize) {
+ count++;
+ end_rec = (4 - (this_count % 4)) << 9;
+ this_count += 4 - (this_count % 4);
+ } else {
+ count--;
+ this_count -= (this_count % 4);
+ };
+ };
+ SCpnt->use_sg = count; /* Number of chains */
+ /* scsi_malloc can only allocate in chunks of 512 bytes */
+ count = (SCpnt->use_sg * sizeof(struct scatterlist) + 511) & ~511;
+
+ SCpnt->sglist_len = count;
+ sgpnt = (struct scatterlist * ) scsi_malloc(count);
+ if (!sgpnt) {
+ printk("Warning - running *really* short on DMA buffers\n");
+ SCpnt->use_sg = 0; /* No memory left - bail out */
+ } else {
+ buffer = (unsigned char *) sgpnt;
+ count = 0;
+ bh = SCpnt->request.bh;
+ if(SCpnt->request.sector % 4) {
+ sgpnt[count].length = (SCpnt->request.sector % 4) << 9;
+ sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
+ if(!sgpnt[count].address) panic("SCSI DMA pool exhausted.");
+ sgpnt[count].alt_address = sgpnt[count].address; /* Flag to delete
+ if needed */
+ count++;
+ };
+ for(bh = SCpnt->request.bh; count < SCpnt->use_sg;
+ count++, bh = bh->b_reqnext) {
+ if (bh) { /* Need a placeholder at the end of the record? */
+ sgpnt[count].address = bh->b_data;
+ sgpnt[count].length = bh->b_size;
+ sgpnt[count].alt_address = NULL;
+ } else {
+ sgpnt[count].address = (char *) scsi_malloc(end_rec);
+ if(!sgpnt[count].address) panic("SCSI DMA pool exhausted.");
+ sgpnt[count].length = end_rec;
+ sgpnt[count].alt_address = sgpnt[count].address;
+ if (count+1 != SCpnt->use_sg) panic("Bad sr request list");
+ break;
+ };
+ if (((long) sgpnt[count].address) + sgpnt[count].length - 1 >
+ ISA_DMA_THRESHOLD && SCpnt->host->unchecked_isa_dma) {
+ sgpnt[count].alt_address = sgpnt[count].address;
+ /* We try to avoid exhausting the DMA pool, since it is easier
+ * to control usage here. In other places we might have a more
+ * pressing need, and we would be screwed if we ran out */
+ if(dma_free_sectors < (sgpnt[count].length >> 9) + 5) {
+ sgpnt[count].address = NULL;
+ } else {
+ sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
+ };
+ /* If we start running low on DMA buffers, we abort the scatter-gather
+ * operation, and free all of the memory we have allocated. We want to
+ * ensure that all scsi operations are able to do at least a non-scatter/gather
+ * operation */
+ if(sgpnt[count].address == NULL){ /* Out of dma memory */
+ printk("Warning: Running low on SCSI DMA buffers");
+ /* Try switching back to a non scatter-gather operation. */
+ while(--count >= 0){
+ if(sgpnt[count].alt_address)
+ scsi_free(sgpnt[count].address, sgpnt[count].length);
+ };
+ SCpnt->use_sg = 0;
+ scsi_free(buffer, SCpnt->sglist_len);
+ break;
+ }; /* if address == NULL */
+ }; /* if need DMA fixup */
+ }; /* for loop to fill list */
+#ifdef DEBUG
+ printk("SR: %d %d %d %d %d *** ",SCpnt->use_sg, SCpnt->request.sector,
+ this_count,
+ SCpnt->request.current_nr_sectors,
+ SCpnt->request.nr_sectors);
+ for(count=0; count<SCpnt->use_sg; count++)
+ printk("SGlist: %d %x %x %x\n", count,
+ sgpnt[count].address,
+ sgpnt[count].alt_address,
+ sgpnt[count].length);
+#endif
+ }; /* Able to allocate scatter-gather list */
+ };
+
+ if (SCpnt->use_sg == 0){
+ /* We cannot use scatter-gather. Do this the old fashion way */
+ if (!SCpnt->request.bh)
+ this_count = SCpnt->request.nr_sectors;
+ else
+ this_count = (SCpnt->request.bh->b_size >> 9);
+
+ start = block % 4;
+ if (start)
+ {
+ this_count = ((this_count > 4 - start) ?
+ (4 - start) : (this_count));
+ buffer = (unsigned char *) scsi_malloc(2048);
+ }
+ else if (this_count < 4)
+ {
+ buffer = (unsigned char *) scsi_malloc(2048);
+ }
+ else
+ {
+ this_count -= this_count % 4;
+ buffer = (unsigned char *) SCpnt->request.buffer;
+ if (((long) buffer) + (this_count << 9) > ISA_DMA_THRESHOLD &&
+ SCpnt->host->unchecked_isa_dma)
+ buffer = (unsigned char *) scsi_malloc(this_count << 9);
+ }
+ };
+
+ if (scsi_CDs[dev].sector_size == 2048)
+ block = block >> 2; /* These are the sectors that the cdrom uses */
+ else
+ block = block & 0xfffffffc;
+
+ realcount = (this_count + 3) / 4;
+
+ if (scsi_CDs[dev].sector_size == 512) realcount = realcount << 2;
+
+ if (((realcount > 0xff) || (block > 0x1fffff)) && scsi_CDs[dev].ten)
+ {
+ if (realcount > 0xffff)
+ {
+ realcount = 0xffff;
+ this_count = realcount * (scsi_CDs[dev].sector_size >> 9);
+ }
+
+ cmd[0] += READ_10 - READ_6 ;
+ cmd[2] = (unsigned char) (block >> 24) & 0xff;
+ cmd[3] = (unsigned char) (block >> 16) & 0xff;
+ cmd[4] = (unsigned char) (block >> 8) & 0xff;
+ cmd[5] = (unsigned char) block & 0xff;
+ cmd[6] = cmd[9] = 0;
+ cmd[7] = (unsigned char) (realcount >> 8) & 0xff;
+ cmd[8] = (unsigned char) realcount & 0xff;
+ }
+ else
+ {
+ if (realcount > 0xff)
+ {
+ realcount = 0xff;
+ this_count = realcount * (scsi_CDs[dev].sector_size >> 9);
+ }
+
+ cmd[1] |= (unsigned char) ((block >> 16) & 0x1f);
+ cmd[2] = (unsigned char) ((block >> 8) & 0xff);
+ cmd[3] = (unsigned char) block & 0xff;
+ cmd[4] = (unsigned char) realcount;
+ cmd[5] = 0;
+ }
+
+#ifdef DEBUG
+ {
+ int i;
+ printk("ReadCD: %d %d %d %d\n",block, realcount, buffer, this_count);
+ printk("Use sg: %d\n", SCpnt->use_sg);
+ printk("Dumping command: ");
+ for(i=0; i<12; i++) printk("%2.2x ", cmd[i]);
+ printk("\n");
+ };
+#endif
+
+ /* Some dumb host adapters can speed transfers by knowing the
+ * minimum transfersize in advance.
+ *
+ * We shouldn't disconnect in the middle of a sector, but the cdrom
+ * sector size can be larger than the size of a buffer and the
+ * transfer may be split to the size of a buffer. So it's safe to
+ * assume that we can at least transfer the minimum of the buffer
+ * size (1024) and the sector size between each connect / disconnect.
+ */
+
+ SCpnt->transfersize = (scsi_CDs[dev].sector_size > 1024) ?
+ 1024 : scsi_CDs[dev].sector_size;
+
+ SCpnt->this_count = this_count;
+ scsi_do_cmd (SCpnt, (void *) cmd, buffer,
+ realcount * scsi_CDs[dev].sector_size,
+ rw_intr, SR_TIMEOUT, MAX_RETRIES);
+}
+
+static int sr_detect(Scsi_Device * SDp){
+
+ if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 0;
+
+ printk("Detected scsi CD-ROM sr%d at scsi%d, channel %d, id %d, lun %d\n",
+ sr_template.dev_noticed++,
+ SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+
+ return 1;
+}
+
+static int sr_attach(Scsi_Device * SDp){
+ Scsi_CD * cpnt;
+ int i;
+
+ if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 1;
+
+ if (sr_template.nr_dev >= sr_template.dev_max)
+ {
+ SDp->attached--;
+ return 1;
+ }
+
+ for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++)
+ if(!cpnt->device) break;
+
+ if(i >= sr_template.dev_max) panic ("scsi_devices corrupt (sr)");
+
+ SDp->scsi_request_fn = do_sr_request;
+ scsi_CDs[i].device = SDp;
+ sr_template.nr_dev++;
+ if(sr_template.nr_dev > sr_template.dev_max)
+ panic ("scsi_devices corrupt (sr)");
+ return 0;
+}
+
+
+static void sr_init_done (Scsi_Cmnd * SCpnt)
+{
+ struct request * req;
+
+ req = &SCpnt->request;
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+}
+
+void get_sectorsize(int i){
+ unsigned char cmd[10];
+ unsigned char *buffer;
+ int the_result, retries;
+ Scsi_Cmnd * SCpnt;
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ SCpnt = allocate_device(NULL, scsi_CDs[i].device, 1);
+
+ retries = 3;
+ do {
+ cmd[0] = READ_CAPACITY;
+ cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0;
+ memset ((void *) &cmd[2], 0, 8);
+ SCpnt->request.rq_status = RQ_SCSI_BUSY; /* Mark as really busy */
+ SCpnt->cmd_len = 0;
+
+ memset(buffer, 0, 8);
+
+ /* Do the command and wait.. */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sr_init_done, SR_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ the_result = SCpnt->result;
+ retries--;
+
+ } while(the_result && retries);
+
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
+
+ wake_up(&SCpnt->device->device_wait);
+
+ if (the_result) {
+ scsi_CDs[i].capacity = 0x1fffff;
+ scsi_CDs[i].sector_size = 2048; /* A guess, just in case */
+ scsi_CDs[i].needs_sector_size = 1;
+ } else {
+ scsi_CDs[i].capacity = 1 + ((buffer[0] << 24) |
+ (buffer[1] << 16) |
+ (buffer[2] << 8) |
+ buffer[3]);
+ scsi_CDs[i].sector_size = (buffer[4] << 24) |
+ (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
+ switch (scsi_CDs[i].sector_size) {
+ /*
+ * HP 4020i CD-Recorder reports 2340 byte sectors
+ * Philips CD-Writers report 2352 byte sectors
+ *
+ * Use 2k sectors for them..
+ */
+ case 0: case 2340: case 2352:
+ scsi_CDs[i].sector_size = 2048;
+ /* fall through */
+ case 2048:
+ scsi_CDs[i].capacity *= 4;
+ /* fall through */
+ case 512:
+ break;
+ default:
+ printk ("scd%d : unsupported sector size %d.\n",
+ i, scsi_CDs[i].sector_size);
+ scsi_CDs[i].capacity = 0;
+ scsi_CDs[i].needs_sector_size = 1;
+ }
+ scsi_CDs[i].needs_sector_size = 0;
+ sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
+ };
+ scsi_free(buffer, 512);
+}
+
+static int sr_registered = 0;
+
+static int sr_init()
+{
+ int i;
+
+ if(sr_template.dev_noticed == 0) return 0;
+
+ if(!sr_registered) {
+ if (register_blkdev(MAJOR_NR,"sr",&sr_fops)) {
+ printk("Unable to get major %d for SCSI-CD\n",MAJOR_NR);
+ return 1;
+ }
+ sr_registered++;
+ }
+
+
+ if (scsi_CDs) return 0;
+ sr_template.dev_max = sr_template.dev_noticed + SR_EXTRA_DEVS;
+ scsi_CDs = (Scsi_CD *) scsi_init_malloc(sr_template.dev_max * sizeof(Scsi_CD), GFP_ATOMIC);
+ memset(scsi_CDs, 0, sr_template.dev_max * sizeof(Scsi_CD));
+
+ sr_sizes = (int *) scsi_init_malloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC);
+ memset(sr_sizes, 0, sr_template.dev_max * sizeof(int));
+
+ sr_blocksizes = (int *) scsi_init_malloc(sr_template.dev_max *
+ sizeof(int), GFP_ATOMIC);
+ for(i=0;i<sr_template.dev_max;i++) sr_blocksizes[i] = 2048;
+ blksize_size[MAJOR_NR] = sr_blocksizes;
+ return 0;
+}
+
+void sr_finish()
+{
+ int i;
+
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ blk_size[MAJOR_NR] = sr_sizes;
+
+ for (i = 0; i < sr_template.nr_dev; ++i)
+ {
+ /* If we have already seen this, then skip it. Comes up
+ * with loadable modules. */
+ if (scsi_CDs[i].capacity) continue;
+ scsi_CDs[i].capacity = 0x1fffff;
+ scsi_CDs[i].sector_size = 2048; /* A guess, just in case */
+ scsi_CDs[i].needs_sector_size = 1;
+#if 0
+ /* seems better to leave this for later */
+ get_sectorsize(i);
+ printk("Scd sectorsize = %d bytes.\n", scsi_CDs[i].sector_size);
+#endif
+ scsi_CDs[i].use = 1;
+ scsi_CDs[i].ten = 1;
+ scsi_CDs[i].remap = 1;
+ scsi_CDs[i].auto_eject = 0; /* Default is not to eject upon unmount. */
+ sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
+ }
+
+
+ /* If our host adapter is capable of scatter-gather, then we increase
+ * the read-ahead to 16 blocks (32 sectors). If not, we use
+ * a two block (4 sector) read ahead. */
+ if(scsi_CDs[0].device && scsi_CDs[0].device->host->sg_tablesize)
+ read_ahead[MAJOR_NR] = 32; /* 32 sector read-ahead. Always removable. */
+ else
+ read_ahead[MAJOR_NR] = 4; /* 4 sector read-ahead */
+
+ return;
+}
+
+static void sr_detach(Scsi_Device * SDp)
+{
+ Scsi_CD * cpnt;
+ int i;
+
+ for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++)
+ if(cpnt->device == SDp) {
+ kdev_t devi = MKDEV(MAJOR_NR, i);
+
+ /*
+ * Since the cdrom is read-only, no need to sync the device.
+ * We should be kind to our buffer cache, however.
+ */
+ invalidate_inodes(devi);
+ invalidate_buffers(devi);
+
+ /*
+ * Reset things back to a sane state so that one can re-load a new
+ * driver (perhaps the same one).
+ */
+ cpnt->device = NULL;
+ cpnt->capacity = 0;
+ SDp->attached--;
+ sr_template.nr_dev--;
+ sr_template.dev_noticed--;
+ sr_sizes[i] = 0;
+ return;
+ }
+ return;
+}
+
+
+#ifdef MODULE
+
+int init_module(void) {
+ sr_template.usage_count = &mod_use_count_;
+ return scsi_register_module(MODULE_SCSI_DEV, &sr_template);
+}
+
+void cleanup_module( void)
+{
+ scsi_unregister_module(MODULE_SCSI_DEV, &sr_template);
+ unregister_blkdev(SCSI_CDROM_MAJOR, "sr");
+ sr_registered--;
+ if(scsi_CDs != NULL) {
+ scsi_init_free((char *) scsi_CDs,
+ (sr_template.dev_noticed + SR_EXTRA_DEVS)
+ * sizeof(Scsi_CD));
+
+ scsi_init_free((char *) sr_sizes, sr_template.dev_max * sizeof(int));
+ scsi_init_free((char *) sr_blocksizes, sr_template.dev_max * sizeof(int));
+ }
+
+ blksize_size[MAJOR_NR] = NULL;
+ blk_dev[MAJOR_NR].request_fn = NULL;
+ blk_size[MAJOR_NR] = NULL;
+ read_ahead[MAJOR_NR] = 0;
+
+ sr_template.dev_max = 0;
+}
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/sr.h b/linux/src/drivers/scsi/sr.h
new file mode 100644
index 00000000..381678a6
--- /dev/null
+++ b/linux/src/drivers/scsi/sr.h
@@ -0,0 +1,40 @@
+/*
+ * sr.h by David Giller
+ * CD-ROM disk driver header file
+ *
+ * adapted from:
+ * sd.h Copyright (C) 1992 Drew Eckhardt
+ * SCSI disk driver header file by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale eric@aib.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ */
+
+#ifndef _SR_H
+#define _SR_H
+
+#include "scsi.h"
+
+typedef struct
+ {
+ unsigned capacity; /* size in blocks */
+ unsigned sector_size; /* size in bytes */
+ Scsi_Device *device;
+ unsigned long mpcd_sector; /* for reading multisession-CD's */
+ char xa_flags; /* some flags for handling XA-CD's */
+ unsigned char sector_bit_size; /* sector size = 2^sector_bit_size */
+ unsigned char sector_bit_shift; /* sectors/FS block = 2^sector_bit_shift*/
+ unsigned needs_sector_size:1; /* needs to get sector size */
+ unsigned ten:1; /* support ten byte commands */
+ unsigned remap:1; /* support remapping */
+ unsigned use:1; /* is this device still supportable */
+ unsigned auto_eject:1; /* auto-eject medium on last release. */
+ } Scsi_CD;
+
+extern Scsi_CD * scsi_CDs;
+
+#endif
diff --git a/linux/src/drivers/scsi/sr_ioctl.c b/linux/src/drivers/scsi/sr_ioctl.c
new file mode 100644
index 00000000..be62eb8c
--- /dev/null
+++ b/linux/src/drivers/scsi/sr_ioctl.c
@@ -0,0 +1,607 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <asm/segment.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sr.h"
+#include <scsi/scsi_ioctl.h>
+
+#include <linux/cdrom.h>
+
+extern void get_sectorsize(int);
+extern void sr_photocd(struct inode *);
+
+#define IOCTL_RETRIES 3
+/* The CDROM is fairly slow, so we need a little extra time */
+/* In fact, it is very slow if it has to spin up first */
+#define IOCTL_TIMEOUT 3000
+
+static void sr_ioctl_done(Scsi_Cmnd * SCpnt)
+{
+ struct request * req;
+
+ req = &SCpnt->request;
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+}
+
+/* We do our own retries because we want to know what the specific
+ error code is. Normally the UNIT_ATTENTION code will automatically
+ clear after one error */
+
+static int do_ioctl(int target, unsigned char * sr_cmd, void * buffer, unsigned buflength)
+{
+ Scsi_Cmnd * SCpnt;
+ int result;
+
+ SCpnt = allocate_device(NULL, scsi_CDs[target].device, 1);
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd(SCpnt,
+ (void *) sr_cmd, buffer, buflength, sr_ioctl_done,
+ IOCTL_TIMEOUT, IOCTL_RETRIES);
+ down(&sem);
+ }
+
+ result = SCpnt->result;
+
+ /* Minimal error checking. Ignore cases we know about, and report the rest. */
+ if(driver_byte(result) != 0)
+ switch(SCpnt->sense_buffer[2] & 0xf) {
+ case UNIT_ATTENTION:
+ scsi_CDs[target].device->changed = 1;
+ printk("Disc change detected.\n");
+ break;
+ case NOT_READY: /* This happens if there is no disc in drive */
+ printk("CDROM not ready. Make sure there is a disc in the drive.\n");
+ break;
+ case ILLEGAL_REQUEST:
+ /* CDROMCLOSETRAY should not print an error for caddy drives. */
+ if (!(sr_cmd[0] == START_STOP && sr_cmd[4] == 0x03))
+ printk("CDROM (ioctl) reports ILLEGAL REQUEST.\n");
+ break;
+ default:
+ printk("SCSI CD error: host %d id %d lun %d return code = %03x\n",
+ scsi_CDs[target].device->host->host_no,
+ scsi_CDs[target].device->id,
+ scsi_CDs[target].device->lun,
+ result);
+ printk("\tSense class %x, sense error %x, extended sense %x\n",
+ sense_class(SCpnt->sense_buffer[0]),
+ sense_error(SCpnt->sense_buffer[0]),
+ SCpnt->sense_buffer[2] & 0xf);
+
+ };
+
+ result = SCpnt->result;
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Deallocate */
+ wake_up(&SCpnt->device->device_wait);
+ /* Wake up a process waiting for device*/
+ return result;
+}
+
+int sr_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
+{
+ u_char sr_cmd[10];
+
+ kdev_t dev = inode->i_rdev;
+ int result, target, err;
+
+ target = MINOR(dev);
+
+ if (target >= sr_template.nr_dev ||
+ !scsi_CDs[target].device) return -ENXIO;
+
+ switch (cmd)
+ {
+ /* Sun-compatible */
+ case CDROMPAUSE:
+
+ sr_cmd[0] = SCMD_PAUSE_RESUME;
+ sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = 0;
+ sr_cmd[5] = sr_cmd[6] = sr_cmd[7] = 0;
+ sr_cmd[8] = 0;
+ sr_cmd[9] = 0;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+ return result;
+
+ case CDROMRESUME:
+
+ sr_cmd[0] = SCMD_PAUSE_RESUME;
+ sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = 0;
+ sr_cmd[5] = sr_cmd[6] = sr_cmd[7] = 0;
+ sr_cmd[8] = 1;
+ sr_cmd[9] = 0;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+
+ return result;
+
+ case CDROMPLAYMSF:
+ {
+ struct cdrom_msf msf;
+
+ err = verify_area (VERIFY_READ, (void *) arg, sizeof (msf));
+ if (err) return err;
+
+ memcpy_fromfs(&msf, (void *) arg, sizeof(msf));
+
+ sr_cmd[0] = SCMD_PLAYAUDIO_MSF;
+ sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+ sr_cmd[2] = 0;
+ sr_cmd[3] = msf.cdmsf_min0;
+ sr_cmd[4] = msf.cdmsf_sec0;
+ sr_cmd[5] = msf.cdmsf_frame0;
+ sr_cmd[6] = msf.cdmsf_min1;
+ sr_cmd[7] = msf.cdmsf_sec1;
+ sr_cmd[8] = msf.cdmsf_frame1;
+ sr_cmd[9] = 0;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+ return result;
+ }
+
+ case CDROMPLAYBLK:
+ {
+ struct cdrom_blk blk;
+
+ err = verify_area (VERIFY_READ, (void *) arg, sizeof (blk));
+ if (err) return err;
+
+ memcpy_fromfs(&blk, (void *) arg, sizeof(blk));
+
+ sr_cmd[0] = SCMD_PLAYAUDIO10;
+ sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+ sr_cmd[2] = blk.from >> 24;
+ sr_cmd[3] = blk.from >> 16;
+ sr_cmd[4] = blk.from >> 8;
+ sr_cmd[5] = blk.from;
+ sr_cmd[6] = 0;
+ sr_cmd[7] = blk.len >> 8;
+ sr_cmd[8] = blk.len;
+ sr_cmd[9] = 0;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+ return result;
+ }
+
+ case CDROMPLAYTRKIND:
+ {
+ struct cdrom_ti ti;
+
+ err = verify_area (VERIFY_READ, (void *) arg, sizeof (ti));
+ if (err) return err;
+
+ memcpy_fromfs(&ti, (void *) arg, sizeof(ti));
+
+ sr_cmd[0] = SCMD_PLAYAUDIO_TI;
+ sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+ sr_cmd[2] = 0;
+ sr_cmd[3] = 0;
+ sr_cmd[4] = ti.cdti_trk0;
+ sr_cmd[5] = ti.cdti_ind0;
+ sr_cmd[6] = 0;
+ sr_cmd[7] = ti.cdti_trk1;
+ sr_cmd[8] = ti.cdti_ind1;
+ sr_cmd[9] = 0;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+
+ return result;
+ }
+
+ case CDROMREADTOCHDR:
+ {
+ struct cdrom_tochdr tochdr;
+ char * buffer;
+
+ sr_cmd[0] = SCMD_READ_TOC;
+ sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5);
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
+ sr_cmd[6] = 0;
+ sr_cmd[7] = 0; /* MSB of length (12) */
+ sr_cmd[8] = 12; /* LSB of length */
+ sr_cmd[9] = 0;
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ if(!buffer) return -ENOMEM;
+
+ result = do_ioctl(target, sr_cmd, buffer, 12);
+
+ tochdr.cdth_trk0 = buffer[2];
+ tochdr.cdth_trk1 = buffer[3];
+
+ scsi_free(buffer, 512);
+
+ err = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_tochdr));
+ if (err)
+ return err;
+ memcpy_tofs ((void *) arg, &tochdr, sizeof (struct cdrom_tochdr));
+
+ return result;
+ }
+
+ case CDROMREADTOCENTRY:
+ {
+ struct cdrom_tocentry tocentry;
+ unsigned char * buffer;
+
+ err = verify_area (VERIFY_READ, (void *) arg, sizeof (struct cdrom_tocentry));
+ if (err) return err;
+
+ memcpy_fromfs (&tocentry, (void *) arg, sizeof (struct cdrom_tocentry));
+
+ sr_cmd[0] = SCMD_READ_TOC;
+ sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) |
+ (tocentry.cdte_format == CDROM_MSF ? 0x02 : 0);
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
+ sr_cmd[6] = tocentry.cdte_track;
+ sr_cmd[7] = 0; /* MSB of length (12) */
+ sr_cmd[8] = 12; /* LSB of length */
+ sr_cmd[9] = 0;
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ if(!buffer) return -ENOMEM;
+
+ result = do_ioctl (target, sr_cmd, buffer, 12);
+
+ tocentry.cdte_ctrl = buffer[5] & 0xf;
+ tocentry.cdte_adr = buffer[5] >> 4;
+ tocentry.cdte_datamode = (tocentry.cdte_ctrl & 0x04) ? 1 : 0;
+ if (tocentry.cdte_format == CDROM_MSF) {
+ tocentry.cdte_addr.msf.minute = buffer[9];
+ tocentry.cdte_addr.msf.second = buffer[10];
+ tocentry.cdte_addr.msf.frame = buffer[11];
+ }
+ else
+ tocentry.cdte_addr.lba = (((((buffer[8] << 8) + buffer[9]) << 8)
+ + buffer[10]) << 8) + buffer[11];
+
+ scsi_free(buffer, 512);
+
+ err = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_tocentry));
+ if (err)
+ return err;
+ memcpy_tofs ((void *) arg, &tocentry, sizeof (struct cdrom_tocentry));
+
+ return result;
+ }
+
+ case CDROMSTOP:
+ sr_cmd[0] = START_STOP;
+ sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 1;
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
+ sr_cmd[4] = 0;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+ return result;
+
+ case CDROMSTART:
+ sr_cmd[0] = START_STOP;
+ sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 1;
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
+ sr_cmd[4] = 1;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+ return result;
+
+ case CDROMCLOSETRAY:
+ sr_cmd[0] = START_STOP;
+ sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5);
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
+ sr_cmd[4] = 0x03;
+
+ if ((result = do_ioctl(target, sr_cmd, NULL, 255)))
+ return result;
+
+ /* Gather information about newly inserted disc */
+ check_disk_change (inode->i_rdev);
+ sr_ioctl (inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+ sr_photocd (inode);
+
+ if (scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size)
+ get_sectorsize (MINOR(inode->i_rdev));
+
+ return 0;
+
+ case CDROMEJECT:
+ /*
+ * Allow 0 for access count for auto-eject feature.
+ */
+ if (scsi_CDs[target].device -> access_count > 1)
+ return -EBUSY;
+
+ sr_ioctl (inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ sr_cmd[0] = START_STOP;
+ sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5) | 1;
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
+ sr_cmd[4] = 0x02;
+
+ if (!(result = do_ioctl(target, sr_cmd, NULL, 255)))
+ scsi_CDs[target].device -> changed = 1;
+
+ return result;
+
+ case CDROMEJECT_SW:
+ scsi_CDs[target].auto_eject = !!arg;
+ return 0;
+
+ case CDROMVOLCTRL:
+ {
+ char * buffer, * mask;
+ struct cdrom_volctrl volctrl;
+
+ err = verify_area (VERIFY_READ, (void *) arg, sizeof (struct cdrom_volctrl));
+ if (err) return err;
+
+ memcpy_fromfs (&volctrl, (void *) arg, sizeof (struct cdrom_volctrl));
+
+ /* First we get the current params so we can just twiddle the volume */
+
+ sr_cmd[0] = MODE_SENSE;
+ sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
+ sr_cmd[2] = 0xe; /* Want mode page 0xe, CDROM audio params */
+ sr_cmd[3] = 0;
+ sr_cmd[4] = 28;
+ sr_cmd[5] = 0;
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ if(!buffer) return -ENOMEM;
+
+ if ((result = do_ioctl (target, sr_cmd, buffer, 28))) {
+ printk ("Hosed while obtaining audio mode page\n");
+ scsi_free(buffer, 512);
+ return result;
+ }
+
+ sr_cmd[0] = MODE_SENSE;
+ sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
+ sr_cmd[2] = 0x4e; /* Want the mask for mode page 0xe */
+ sr_cmd[3] = 0;
+ sr_cmd[4] = 28;
+ sr_cmd[5] = 0;
+
+ mask = (unsigned char *) scsi_malloc(512);
+ if(!mask) {
+ scsi_free(buffer, 512);
+ return -ENOMEM;
+ };
+
+ if ((result = do_ioctl (target, sr_cmd, mask, 28))) {
+ printk ("Hosed while obtaining mask for audio mode page\n");
+ scsi_free(buffer, 512);
+ scsi_free(mask, 512);
+ return result;
+ }
+
+ /* Now mask and substitute our own volume and reuse the rest */
+ buffer[0] = 0; /* Clear reserved field */
+
+ buffer[21] = volctrl.channel0 & mask[21];
+ buffer[23] = volctrl.channel1 & mask[23];
+ buffer[25] = volctrl.channel2 & mask[25];
+ buffer[27] = volctrl.channel3 & mask[27];
+
+ sr_cmd[0] = MODE_SELECT;
+ sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5) | 0x10; /* Params are SCSI-2 */
+ sr_cmd[2] = sr_cmd[3] = 0;
+ sr_cmd[4] = 28;
+ sr_cmd[5] = 0;
+
+ result = do_ioctl (target, sr_cmd, buffer, 28);
+ scsi_free(buffer, 512);
+ scsi_free(mask, 512);
+ return result;
+ }
+
+ case CDROMVOLREAD:
+ {
+ char * buffer;
+ struct cdrom_volctrl volctrl;
+
+ err = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_volctrl));
+ if (err) return err;
+
+ /* Get the current params */
+
+ sr_cmd[0] = MODE_SENSE;
+ sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
+ sr_cmd[2] = 0xe; /* Want mode page 0xe, CDROM audio params */
+ sr_cmd[3] = 0;
+ sr_cmd[4] = 28;
+ sr_cmd[5] = 0;
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ if(!buffer) return -ENOMEM;
+
+ if ((result = do_ioctl (target, sr_cmd, buffer, 28))) {
+ printk ("(CDROMVOLREAD) Hosed while obtaining audio mode page\n");
+ scsi_free(buffer, 512);
+ return result;
+ }
+
+ volctrl.channel0 = buffer[21];
+ volctrl.channel1 = buffer[23];
+ volctrl.channel2 = buffer[25];
+ volctrl.channel3 = buffer[27];
+
+ memcpy_tofs ((void *) arg, &volctrl, sizeof (struct cdrom_volctrl));
+
+ scsi_free(buffer, 512);
+
+ return 0;
+ }
+
+ case CDROMSUBCHNL:
+ {
+ struct cdrom_subchnl subchnl;
+ char * buffer;
+
+ sr_cmd[0] = SCMD_READ_SUBCHANNEL;
+ sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02; /* MSF format */
+ sr_cmd[2] = 0x40; /* I do want the subchannel info */
+ sr_cmd[3] = 0x01; /* Give me current position info */
+ sr_cmd[4] = sr_cmd[5] = 0;
+ sr_cmd[6] = 0;
+ sr_cmd[7] = 0;
+ sr_cmd[8] = 16;
+ sr_cmd[9] = 0;
+
+ buffer = (unsigned char*) scsi_malloc(512);
+ if(!buffer) return -ENOMEM;
+
+ result = do_ioctl(target, sr_cmd, buffer, 16);
+
+ subchnl.cdsc_audiostatus = buffer[1];
+ subchnl.cdsc_format = CDROM_MSF;
+ subchnl.cdsc_ctrl = buffer[5] & 0xf;
+ subchnl.cdsc_trk = buffer[6];
+ subchnl.cdsc_ind = buffer[7];
+
+ subchnl.cdsc_reladdr.msf.minute = buffer[13];
+ subchnl.cdsc_reladdr.msf.second = buffer[14];
+ subchnl.cdsc_reladdr.msf.frame = buffer[15];
+ subchnl.cdsc_absaddr.msf.minute = buffer[9];
+ subchnl.cdsc_absaddr.msf.second = buffer[10];
+ subchnl.cdsc_absaddr.msf.frame = buffer[11];
+
+ scsi_free(buffer, 512);
+
+ err = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_subchnl));
+ if (err)
+ return err;
+ memcpy_tofs ((void *) arg, &subchnl, sizeof (struct cdrom_subchnl));
+ return result;
+ }
+
+ case CDROM_GET_UPC:
+ {
+ struct cdrom_mcn mcn;
+ char * buffer;
+
+ sr_cmd[0] = SCMD_READ_SUBCHANNEL;
+ sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5);
+ sr_cmd[2] = 0x40; /* I do want the subchannel info */
+ sr_cmd[3] = 0x02; /* Give me medium catalog number info */
+ sr_cmd[4] = sr_cmd[5] = 0;
+ sr_cmd[6] = 0;
+ sr_cmd[7] = 0;
+ sr_cmd[8] = 24;
+ sr_cmd[9] = 0;
+
+ buffer = (unsigned char*) scsi_malloc(512);
+ if(!buffer) return -ENOMEM;
+
+ result = do_ioctl(target, sr_cmd, buffer, 24);
+
+ memcpy (mcn.medium_catalog_number, buffer + 9, 13);
+ mcn.medium_catalog_number[13] = 0;
+
+ scsi_free(buffer, 512);
+
+ err = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_mcn));
+ if (err)
+ return err;
+ memcpy_tofs ((void *) arg, &mcn, sizeof (struct cdrom_mcn));
+ return result;
+ }
+
+ /* these are compatible with the ide-cd driver */
+ case CDROMREADRAW:
+ case CDROMREADMODE1:
+ case CDROMREADMODE2:
+ return -EINVAL;
+
+ /* block-copy from ../block/sbpcd.c with some adjustments... */
+ case CDROMMULTISESSION: /* tell start-of-last-session to user */
+ {
+ struct cdrom_multisession ms_info;
+ long lba;
+
+ err = verify_area(VERIFY_READ, (void *) arg,
+ sizeof(struct cdrom_multisession));
+ if (err) return (err);
+
+ memcpy_fromfs(&ms_info, (void *) arg, sizeof(struct cdrom_multisession));
+
+ if (ms_info.addr_format==CDROM_MSF) { /* MSF-bin requested */
+ lba = scsi_CDs[target].mpcd_sector+CD_BLOCK_OFFSET;
+ ms_info.addr.msf.minute = lba / (CD_SECS*CD_FRAMES);
+ lba %= CD_SECS*CD_FRAMES;
+ ms_info.addr.msf.second = lba / CD_FRAMES;
+ ms_info.addr.msf.frame = lba % CD_FRAMES;
+ } else if (ms_info.addr_format==CDROM_LBA) /* lba requested */
+ ms_info.addr.lba=scsi_CDs[target].mpcd_sector;
+ else return (-EINVAL);
+
+ ms_info.xa_flag=scsi_CDs[target].xa_flags & 0x01;
+
+ err=verify_area(VERIFY_WRITE,(void *) arg,
+ sizeof(struct cdrom_multisession));
+ if (err) return (err);
+
+ memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession));
+ return (0);
+ }
+
+ case BLKRAGET:
+ if (!arg)
+ return -EINVAL;
+ err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
+ if (err)
+ return err;
+ put_user(read_ahead[MAJOR(inode->i_rdev)], (int *) arg);
+ return 0;
+
+ case BLKRASET:
+ if(!suser())
+ return -EACCES;
+ if(!(inode->i_rdev))
+ return -EINVAL;
+ if(arg > 0xff)
+ return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+
+ RO_IOCTLS(dev,arg);
+
+ case CDROMRESET:
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
+ default:
+ return scsi_ioctl(scsi_CDs[target].device,cmd,(void *) arg);
+ }
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/linux/src/drivers/scsi/t128.c b/linux/src/drivers/scsi/t128.c
new file mode 100644
index 00000000..06c6d203
--- /dev/null
+++ b/linux/src/drivers/scsi/t128.c
@@ -0,0 +1,404 @@
+#define AUTOSENSE
+#define PSEUDO_DMA
+
+/*
+ * Trantor T128/T128F/T228 driver
+ * Note : architecturally, the T100 and T130 are different and won't
+ * work
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * DISTRIBUTION RELEASE 3.
+ *
+ * For more information, please consult
+ *
+ * Trantor Systems, Ltd.
+ * T128/T128F/T228 SCSI Host Adapter
+ * Hardware Specifications
+ *
+ * Trantor Systems, Ltd.
+ * 5415 Randall Place
+ * Fremont, CA 94538
+ * 1+ (415) 770-1400, FAX 1+ (415) 770-9910
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * Options :
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
+ * increase compared to polled I/O.
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
+ *
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You
+ * only really want to use this if you're having a problem with
+ * dropped characters during high speed communications, and even
+ * then, you're going to be better off twiddling with transfersize.
+ *
+ * USLEEP - enable support for devices that don't disconnect. Untested.
+ *
+ * The card is detected and initialized in one of several ways :
+ * 1. Autoprobe (default) - since the board is memory mapped,
+ * a BIOS signature is scanned for to locate the registers.
+ * An interrupt is triggered to autoprobe for the interrupt
+ * line.
+ *
+ * 2. With command line overrides - t128=address,irq may be
+ * used on the LILO command line to override the defaults.
+ *
+ * 3. With the T128_OVERRIDE compile time define. This is
+ * specified as an array of address, irq tuples. Ie, for
+ * one board at the default 0xcc000 address, IRQ5, I could say
+ * -DT128_OVERRIDE={{0xcc000, 5}}
+ *
+ * Note that if the override methods are used, place holders must
+ * be specified for other boards in the system.
+ *
+ * T128/T128F jumper/dipswitch settings (note : on my sample, the switches
+ * were epoxy'd shut, meaning I couldn't change the 0xcc000 base address) :
+ *
+ * T128 Sw7 Sw8 Sw6 = 0ws Sw5 = boot
+ * T128F Sw6 Sw7 Sw5 = 0ws Sw4 = boot Sw8 = floppy disable
+ * cc000 off off
+ * c8000 off on
+ * dc000 on off
+ * d8000 on on
+ *
+ *
+ * Interrupts
+ * There is a 12 pin jumper block, jp1, numbered as follows :
+ * T128 (JP1) T128F (J5)
+ * 2 4 6 8 10 12 11 9 7 5 3 1
+ * 1 3 5 7 9 11 12 10 8 6 4 2
+ *
+ * 3 2-4
+ * 5 1-3
+ * 7 3-5
+ * T128F only
+ * 10 8-10
+ * 12 7-9
+ * 14 10-12
+ * 15 9-11
+ */
+
+/*
+ * $Log: t128.c,v $
+ */
+
+#include <asm/system.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "t128.h"
+#define AUTOPROBE_IRQ
+#include "NCR5380.h"
+#include "constants.h"
+#include "sd.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_t128 = {
+ PROC_SCSI_T128, 4, "t128",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+
+static struct override {
+ unsigned char *address;
+ int irq;
+} overrides
+#ifdef T128_OVERRIDE
+ [] = T128_OVERRIDE;
+#else
+ [4] = {{NULL,IRQ_AUTO}, {NULL,IRQ_AUTO}, {NULL,IRQ_AUTO},
+ {NULL,IRQ_AUTO}};
+#endif
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+static struct base {
+ unsigned char *address;
+ int noauto;
+} bases[] = {{(unsigned char *) 0xcc000, 0}, {(unsigned char *) 0xc8000, 0},
+ {(unsigned char *) 0xdc000, 0}, {(unsigned char *) 0xd8000, 0}};
+
+#define NO_BASES (sizeof (bases) / sizeof (struct base))
+
+static const struct signature {
+ const char *string;
+ int offset;
+} signatures[] = {
+{"TSROM: SCSI BIOS, Version 1.12", 0x36},
+};
+
+#define NO_SIGNATURES (sizeof (signatures) / sizeof (struct signature))
+
+/*
+ * Function : t128_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ *
+ */
+
+void t128_setup(char *str, int *ints) {
+ static int commandline_current = 0;
+ int i;
+ if (ints[0] != 2)
+ printk("t128_setup : usage t128=address,irq\n");
+ else
+ if (commandline_current < NO_OVERRIDES) {
+ overrides[commandline_current].address = (unsigned char *) ints[1];
+ overrides[commandline_current].irq = ints[2];
+ for (i = 0; i < NO_BASES; ++i)
+ if (bases[i].address == (unsigned char *) ints[1]) {
+ bases[i].noauto = 1;
+ break;
+ }
+ ++commandline_current;
+ }
+}
+
+/*
+ * Function : int t128_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : detects and initializes T128,T128F, or T228 controllers
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int t128_detect(Scsi_Host_Template * tpnt) {
+ static int current_override = 0, current_base = 0;
+ struct Scsi_Host *instance;
+ unsigned char *base;
+ int sig, count;
+
+ tpnt->proc_dir = &proc_scsi_t128;
+ tpnt->proc_info = &t128_proc_info;
+
+ for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+ base = NULL;
+
+ if (overrides[current_override].address)
+ base = overrides[current_override].address;
+ else
+ for (; !base && (current_base < NO_BASES); ++current_base) {
+#if (TDEBUG & TDEBUG_INIT)
+ printk("scsi : probing address %08x\n", (unsigned int) bases[current_base].address);
+#endif
+ for (sig = 0; sig < NO_SIGNATURES; ++sig)
+ if (!bases[current_base].noauto && !memcmp
+ (bases[current_base].address + signatures[sig].offset,
+ signatures[sig].string, strlen(signatures[sig].string))) {
+ base = bases[current_base].address;
+#if (TDEBUG & TDEBUG_INIT)
+ printk("scsi-t128 : detected board.\n");
+#endif
+ break;
+ }
+ }
+
+#if defined(TDEBUG) && (TDEBUG & TDEBUG_INIT)
+ printk("scsi-t128 : base = %08x\n", (unsigned int) base);
+#endif
+
+ if (!base)
+ break;
+
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+ instance->base = base;
+
+ NCR5380_init(instance, 0);
+
+ if (overrides[current_override].irq != IRQ_AUTO)
+ instance->irq = overrides[current_override].irq;
+ else
+ instance->irq = NCR5380_probe_irq(instance, T128_IRQS);
+
+ if (instance->irq != IRQ_NONE)
+ if (request_irq(instance->irq, t128_intr, SA_INTERRUPT, "t128", NULL)) {
+ printk("scsi%d : IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = IRQ_NONE;
+ }
+
+ if (instance->irq == IRQ_NONE) {
+ printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+ printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ }
+
+#if defined(TDEBUG) && (TDEBUG & TDEBUG_INIT)
+ printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
+#endif
+
+ printk("scsi%d : at 0x%08x", instance->host_no, (int)
+ instance->base);
+ if (instance->irq == IRQ_NONE)
+ printk (" interrupts disabled");
+ else
+ printk (" irq %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ CAN_QUEUE, CMD_PER_LUN, T128_PUBLIC_RELEASE);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ ++current_override;
+ ++count;
+ }
+ return count;
+}
+
+/*
+ * Function : int t128_biosparam(Disk * disk, kdev_t dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for
+ * the specified device / size.
+ *
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ * major / minor, ip[] = {heads, sectors, cylinders}
+ *
+ * Returns : always 0 (success), initializes ip
+ *
+ */
+
+/*
+ * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+ */
+
+int t128_biosparam(Disk * disk, kdev_t dev, int * ip)
+{
+ int size = disk->capacity;
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_pread (struct Scsi_Host *instance,
+ * unsigned char *dst, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to
+ * dst
+ *
+ * Inputs : dst = destination, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+ */
+
+static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,
+ int len) {
+ register unsigned char *reg = (unsigned char *) (instance->base +
+ T_DATA_REG_OFFSET), *d = dst;
+ register int i = len;
+
+
+#if 0
+ for (; i; --i) {
+ while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY) barrier();
+#else
+ while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY) barrier();
+ for (; i; --i) {
+#endif
+ *d++ = *reg;
+ }
+
+ if (*(instance->base + T_STATUS_REG_OFFSET) & T_ST_TIM) {
+ unsigned char tmp;
+ volatile unsigned char *foo;
+ foo = instance->base + T_CONTROL_REG_OFFSET;
+ tmp = *foo;
+ *foo = tmp | T_CR_CT;
+ *foo = tmp;
+ printk("scsi%d : watchdog timer fired in NCR5380_pread()\n",
+ instance->host_no);
+ return -1;
+ } else
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_pwrite (struct Scsi_Host *instance,
+ * unsigned char *src, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from
+ * src
+ *
+ * Inputs : src = source, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+ */
+
+static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src,
+ int len) {
+ register unsigned char *reg = (unsigned char *) (instance->base +
+ T_DATA_REG_OFFSET), *s = src;
+ register int i = len;
+
+#if 0
+ for (; i; --i) {
+ while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY) barrier();
+#else
+ while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY) barrier();
+ for (; i; --i) {
+#endif
+ *reg = *s++;
+ }
+
+ if (*(instance->base + T_STATUS_REG_OFFSET) & T_ST_TIM) {
+ unsigned char tmp;
+ volatile unsigned char *foo;
+ foo = instance->base + T_CONTROL_REG_OFFSET;
+ tmp = *foo;
+ *foo = tmp | T_CR_CT;
+ *foo = tmp;
+ printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n",
+ instance->host_no);
+ return -1;
+ } else
+ return 0;
+}
+
+#include "NCR5380.c"
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = TRANTOR_T128;
+
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/t128.h b/linux/src/drivers/scsi/t128.h
new file mode 100644
index 00000000..43a89820
--- /dev/null
+++ b/linux/src/drivers/scsi/t128.h
@@ -0,0 +1,173 @@
+/*
+ * Trantor T128/T128F/T228 defines
+ * Note : architecturally, the T100 and T128 are different and won't work
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * DISTRIBUTION RELEASE 3.
+ *
+ * For more information, please consult
+ *
+ * Trantor Systems, Ltd.
+ * T128/T128F/T228 SCSI Host Adapter
+ * Hardware Specifications
+ *
+ * Trantor Systems, Ltd.
+ * 5415 Randall Place
+ * Fremont, CA 94538
+ * 1+ (415) 770-1400, FAX 1+ (415) 770-9910
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: t128.h,v $
+ */
+
+#ifndef T128_H
+#define T128_H
+
+#define T128_PUBLIC_RELEASE 3
+
+#define TDEBUG_INIT 0x1
+#define TDEBUG_TRANSFER 0x2
+
+/*
+ * The trantor boards are memory mapped. They use an NCR5380 or
+ * equivalent (my sample board had part second sourced from ZILOG).
+ * NCR's recommended "Pseudo-DMA" architecture is used, where
+ * a PAL drives the DMA signals on the 5380 allowing fast, blind
+ * transfers with proper handshaking.
+ */
+
+/*
+ * Note : a boot switch is provided for the purpose of informing the
+ * firmware to boot or not boot from attached SCSI devices. So, I imagine
+ * there are fewer people who've yanked the ROM like they do on the Seagate
+ * to make bootup faster, and I'll probably use this for autodetection.
+ */
+#define T_ROM_OFFSET 0
+
+/*
+ * Note : my sample board *WAS NOT* populated with the SRAM, so this
+ * can't be used for autodetection without a ROM present.
+ */
+#define T_RAM_OFFSET 0x1800
+
+/*
+ * All of the registers are allocated 32 bytes of address space, except
+ * for the data register (read/write to/from the 5380 in pseudo-DMA mode)
+ */
+#define T_CONTROL_REG_OFFSET 0x1c00 /* rw */
+#define T_CR_INT 0x10 /* Enable interrupts */
+#define T_CR_CT 0x02 /* Reset watchdog timer */
+
+#define T_STATUS_REG_OFFSET 0x1c20 /* ro */
+#define T_ST_BOOT 0x80 /* Boot switch */
+#define T_ST_S3 0x40 /* User settable switches, */
+#define T_ST_S2 0x20 /* read 0 when switch is on, 1 off */
+#define T_ST_S1 0x10
+#define T_ST_PS2 0x08 /* Set for Microchannel 228 */
+#define T_ST_RDY 0x04 /* 5380 DRQ */
+#define T_ST_TIM 0x02 /* indicates 40us watchdog timer fired */
+#define T_ST_ZERO 0x01 /* Always zero */
+
+#define T_5380_OFFSET 0x1d00 /* 8 registers here, see NCR5380.h */
+
+#define T_DATA_REG_OFFSET 0x1e00 /* rw 512 bytes long */
+
+#ifndef ASM
+int t128_abort(Scsi_Cmnd *);
+int t128_biosparam(Disk *, kdev_t, int*);
+int t128_detect(Scsi_Host_Template *);
+int t128_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int t128_reset(Scsi_Cmnd *, unsigned int reset_flags);
+int t128_proc_info (char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 32
+#endif
+
+/*
+ * I hadn't thought of this with the earlier drivers - but to prevent
+ * macro definition conflicts, we shouldn't define all of the internal
+ * macros when this is being used solely for the host stub.
+ */
+
+#if defined(HOSTS_C) || defined(MODULE)
+
+#define TRANTOR_T128 {NULL, NULL, NULL, NULL, \
+ "Trantor T128/T128F/T228", t128_detect, NULL, \
+ NULL, \
+ NULL, t128_queue_command, t128_abort, t128_reset, NULL, \
+ t128_biosparam, \
+ /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \
+ /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
+
+#endif
+
+#ifndef HOSTS_C
+
+#define NCR5380_implementation_fields \
+ volatile unsigned char *base
+
+#define NCR5380_local_declare() \
+ volatile unsigned char *base
+
+#define NCR5380_setup(instance) \
+ base = (volatile unsigned char *) (instance)->base
+
+#define T128_address(reg) (base + T_5380_OFFSET + ((reg) * 0x20))
+
+#if !(TDEBUG & TDEBUG_TRANSFER)
+#define NCR5380_read(reg) (*(T128_address(reg)))
+#define NCR5380_write(reg, value) (*(T128_address(reg)) = (value))
+#else
+#define NCR5380_read(reg) \
+ (((unsigned char) printk("scsi%d : read register %d at address %08x\n"\
+ , instance->hostno, (reg), T128_address(reg))), *(T128_address(reg)))
+
+#define NCR5380_write(reg, value) { \
+ printk("scsi%d : write %02x to register %d at address %08x\n", \
+ instance->hostno, (value), (reg), T128_address(reg)); \
+ *(T128_address(reg)) = (value); \
+}
+#endif
+
+#define NCR5380_intr t128_intr
+#define NCR5380_queue_command t128_queue_command
+#define NCR5380_abort t128_abort
+#define NCR5380_reset t128_reset
+#define NCR5380_proc_info t128_proc_info
+
+/* 15 14 12 10 7 5 3
+ 1101 0100 1010 1000 */
+
+#define T128_IRQS 0xc4a8
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* T128_H */
diff --git a/linux/src/drivers/scsi/tmscsim.c b/linux/src/drivers/scsi/tmscsim.c
new file mode 100644
index 00000000..be986ff0
--- /dev/null
+++ b/linux/src/drivers/scsi/tmscsim.c
@@ -0,0 +1,1930 @@
+/***********************************************************************
+ * FILE NAME : TMSCSIM.C *
+ * BY : C.L. Huang, ching@tekram.com.tw *
+ * Description: Device Driver for Tekram DC-390(T) PCI SCSI *
+ * Bus Master Host Adapter *
+ * (C)Copyright 1995-1996 Tekram Technology Co., Ltd. *
+ ***********************************************************************/
+/* Minor enhancements and bugfixes by *
+ * Kurt Garloff <K.Garloff@ping.de> *
+ ***********************************************************************/
+/* HISTORY: *
+ * *
+ * REV# DATE NAME DESCRIPTION *
+ * 1.00 04/24/96 CLH First release *
+ * 1.01 06/12/96 CLH Fixed bug of Media Change for Removable *
+ * Device, scan all LUN. Support Pre2.0.10 *
+ * 1.02 06/18/96 CLH Fixed bug of Command timeout ... *
+ * 1.03 09/25/96 KG Added tmscsim_proc_info() *
+ * 1.04 10/11/96 CLH Updating for support KV 2.0.x *
+ * 1.05 10/18/96 KG Fixed bug in DC390_abort(null ptr deref)*
+ * 1.06 10/25/96 KG Fixed module support *
+ * 1.07 11/09/96 KG Fixed tmscsim_proc_info() *
+ * 1.08 11/18/96 KG Fixed null ptr in DC390_Disconnect() *
+ * 1.09 11/30/96 KG Added register the allocated IO space *
+ * 1.10 12/05/96 CLH Modified tmscsim_proc_info(), and reset *
+ * pending interrupt in DC390_detect() *
+ * 1.11 02/05/97 KG/CLH Fixeds problem with partitions greater *
+ * than 1GB *
+ ***********************************************************************/
+
+
+#define DC390_DEBUG
+
+#define SCSI_MALLOC
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/config.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < 66354 /* 1.3.50 */
+#include "../block/blk.h"
+#else
+#include <linux/blk.h>
+#endif
+
+#include "scsi.h"
+#include "hosts.h"
+#include "tmscsim.h"
+#include "constants.h"
+#include "sd.h"
+#include <linux/stat.h>
+
+#include "dc390.h"
+
+#define PCI_DEVICE_ID_AMD53C974 PCI_DEVICE_ID_AMD_SCSI
+
+
+#ifndef VERSION_ELF_1_2_13
+struct proc_dir_entry proc_scsi_tmscsim ={
+ PROC_SCSI_DC390T, 7 ,"tmscsim",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+ };
+#endif
+
+static USHORT DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+static void DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus);
+
+static void SetXferRate( PACB pACB, PDCB pDCB );
+static void DC390_Disconnect( PACB pACB );
+static void DC390_Reselect( PACB pACB );
+static void SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void DoingSRB_Done( PACB pACB );
+static void DC390_ScsiRstDetect( PACB pACB );
+static void DC390_ResetSCSIBus( PACB pACB );
+static void RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void EnableMsgOut2( PACB pACB, PSRB pSRB );
+static void EnableMsgOut( PACB pACB, PSRB pSRB );
+static void DC390_InvalidCmd( PACB pACB );
+
+int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index );
+void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd );
+
+#ifdef MODULE
+static int DC390_release(struct Scsi_Host *host);
+static int DC390_shutdown (struct Scsi_Host *host);
+#endif
+
+
+static PSHT pSHT_start = NULL;
+static PSH pSH_start = NULL;
+static PSH pSH_current = NULL;
+static PACB pACB_start= NULL;
+static PACB pACB_current = NULL;
+static PDCB pPrevDCB = NULL;
+static USHORT adapterCnt = 0;
+static USHORT InitialTime = 0;
+static USHORT CurrSyncOffset = 0;
+static ULONG mech1addr;
+static UCHAR mech2bus, mech2Agent, mech2CfgSPenR;
+
+static PVOID DC390_phase0[]={
+ DC390_DataOut_0,
+ DC390_DataIn_0,
+ DC390_Command_0,
+ DC390_Status_0,
+ DC390_Nop_0,
+ DC390_Nop_0,
+ DC390_MsgOut_0,
+ DC390_MsgIn_0,
+ DC390_Nop_1
+ };
+
+static PVOID DC390_phase1[]={
+ DC390_DataOutPhase,
+ DC390_DataInPhase,
+ DC390_CommandPhase,
+ DC390_StatusPhase,
+ DC390_Nop_0,
+ DC390_Nop_0,
+ DC390_MsgOutPhase,
+ DC390_MsgInPhase,
+ DC390_Nop_1,
+ };
+
+UCHAR eepromBuf[MAX_ADAPTER_NUM][128];
+
+
+UCHAR clock_period1[] = {4, 5, 6, 7, 8, 10, 13, 20};
+
+UCHAR baddevname1[2][28] ={
+ "SEAGATE ST3390N 9546",
+ "HP C3323-300 4269"};
+
+#define BADDEVCNT 2
+
+
+/***********************************************************************
+ *
+ *
+ *
+ **********************************************************************/
+static void
+QLinkcmd( PSCSICMD cmd, PDCB pDCB )
+{
+ ULONG flags;
+ PSCSICMD pcmd;
+
+ save_flags(flags);
+ cli();
+
+ if( !pDCB->QIORBCnt )
+ {
+ pDCB->pQIORBhead = cmd;
+ pDCB->pQIORBtail = cmd;
+ pDCB->QIORBCnt++;
+ cmd->next = NULL;
+ }
+ else
+ {
+ pcmd = pDCB->pQIORBtail;
+ pcmd->next = cmd;
+ pDCB->pQIORBtail = cmd;
+ pDCB->QIORBCnt++;
+ cmd->next = NULL;
+ }
+
+ restore_flags(flags);
+}
+
+
+static PSCSICMD
+Getcmd( PDCB pDCB )
+{
+ ULONG flags;
+ PSCSICMD pcmd;
+
+ save_flags(flags);
+ cli();
+
+ pcmd = pDCB->pQIORBhead;
+ pDCB->pQIORBhead = pcmd->next;
+ pcmd->next = NULL;
+ pDCB->QIORBCnt--;
+
+ restore_flags(flags);
+ return( pcmd );
+}
+
+
+static PSRB
+GetSRB( PACB pACB )
+{
+ ULONG flags;
+ PSRB pSRB;
+
+ save_flags(flags);
+ cli();
+
+ pSRB = pACB->pFreeSRB;
+ if( pSRB )
+ {
+ pACB->pFreeSRB = pSRB->pNextSRB;
+ pSRB->pNextSRB = NULL;
+ }
+ restore_flags(flags);
+ return( pSRB );
+}
+
+
+static void
+RewaitSRB0( PDCB pDCB, PSRB pSRB )
+{
+ PSRB psrb1;
+ ULONG flags;
+
+ save_flags(flags);
+ cli();
+
+ if( (psrb1 = pDCB->pWaitingSRB) )
+ {
+ pSRB->pNextSRB = psrb1;
+ pDCB->pWaitingSRB = pSRB;
+ }
+ else
+ {
+ pSRB->pNextSRB = NULL;
+ pDCB->pWaitingSRB = pSRB;
+ pDCB->pWaitLast = pSRB;
+ }
+ restore_flags(flags);
+}
+
+
+static void
+RewaitSRB( PDCB pDCB, PSRB pSRB )
+{
+ PSRB psrb1;
+ ULONG flags;
+ UCHAR bval;
+
+ save_flags(flags);
+ cli();
+ pDCB->GoingSRBCnt--;
+ psrb1 = pDCB->pGoingSRB;
+ if( pSRB == psrb1 )
+ {
+ pDCB->pGoingSRB = psrb1->pNextSRB;
+ }
+ else
+ {
+ while( pSRB != psrb1->pNextSRB )
+ psrb1 = psrb1->pNextSRB;
+ psrb1->pNextSRB = pSRB->pNextSRB;
+ if( pSRB == pDCB->pGoingLast )
+ pDCB->pGoingLast = psrb1;
+ }
+ if( (psrb1 = pDCB->pWaitingSRB) )
+ {
+ pSRB->pNextSRB = psrb1;
+ pDCB->pWaitingSRB = pSRB;
+ }
+ else
+ {
+ pSRB->pNextSRB = NULL;
+ pDCB->pWaitingSRB = pSRB;
+ pDCB->pWaitLast = pSRB;
+ }
+
+ bval = pSRB->TagNumber;
+ pDCB->TagMask &= (~(1 << bval)); /* Free TAG number */
+ restore_flags(flags);
+}
+
+
+static void
+DoWaitingSRB( PACB pACB )
+{
+ ULONG flags;
+ PDCB ptr, ptr1;
+ PSRB pSRB;
+
+ save_flags(flags);
+ cli();
+
+ if( !(pACB->pActiveDCB) && !(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) )
+ {
+ ptr = pACB->pDCBRunRobin;
+ if( !ptr )
+ {
+ ptr = pACB->pLinkDCB;
+ pACB->pDCBRunRobin = ptr;
+ }
+ ptr1 = ptr;
+ for( ;ptr1; )
+ {
+ pACB->pDCBRunRobin = ptr1->pNextDCB;
+ if( !( ptr1->MaxCommand > ptr1->GoingSRBCnt ) ||
+ !( pSRB = ptr1->pWaitingSRB ) )
+ {
+ if(pACB->pDCBRunRobin == ptr)
+ break;
+ ptr1 = ptr1->pNextDCB;
+ }
+ else
+ {
+ if( !DC390_StartSCSI(pACB, ptr1, pSRB) )
+ {
+ ptr1->GoingSRBCnt++;
+ if( ptr1->pWaitLast == pSRB )
+ {
+ ptr1->pWaitingSRB = NULL;
+ ptr1->pWaitLast = NULL;
+ }
+ else
+ {
+ ptr1->pWaitingSRB = pSRB->pNextSRB;
+ }
+ pSRB->pNextSRB = NULL;
+
+ if( ptr1->pGoingSRB )
+ ptr1->pGoingLast->pNextSRB = pSRB;
+ else
+ ptr1->pGoingSRB = pSRB;
+ ptr1->pGoingLast = pSRB;
+ }
+ break;
+ }
+ }
+ }
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+SRBwaiting( PDCB pDCB, PSRB pSRB)
+{
+ if( pDCB->pWaitingSRB )
+ {
+ pDCB->pWaitLast->pNextSRB = pSRB;
+ pDCB->pWaitLast = pSRB;
+ pSRB->pNextSRB = NULL;
+ }
+ else
+ {
+ pDCB->pWaitingSRB = pSRB;
+ pDCB->pWaitLast = pSRB;
+ }
+}
+
+
+static void
+SendSRB( PSCSICMD pcmd, PACB pACB, PSRB pSRB )
+{
+ ULONG flags;
+ PDCB pDCB;
+
+ save_flags(flags);
+ cli();
+
+ pDCB = pSRB->pSRBDCB;
+ if( !(pDCB->MaxCommand > pDCB->GoingSRBCnt) || (pACB->pActiveDCB) ||
+ (pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV)) )
+ {
+ SRBwaiting(pDCB, pSRB);
+ goto SND_EXIT;
+ }
+
+ if( pDCB->pWaitingSRB )
+ {
+ SRBwaiting(pDCB, pSRB);
+/* pSRB = GetWaitingSRB(pDCB); */
+ pSRB = pDCB->pWaitingSRB;
+ pDCB->pWaitingSRB = pSRB->pNextSRB;
+ pSRB->pNextSRB = NULL;
+ }
+
+ if( !DC390_StartSCSI(pACB, pDCB, pSRB) )
+ {
+ pDCB->GoingSRBCnt++;
+ if( pDCB->pGoingSRB )
+ {
+ pDCB->pGoingLast->pNextSRB = pSRB;
+ pDCB->pGoingLast = pSRB;
+ }
+ else
+ {
+ pDCB->pGoingSRB = pSRB;
+ pDCB->pGoingLast = pSRB;
+ }
+ }
+ else
+ RewaitSRB0( pDCB, pSRB );
+
+SND_EXIT:
+ restore_flags(flags);
+ return;
+}
+
+
+/***********************************************************************
+ * Function : static int DC390_queue_command (Scsi_Cmnd *cmd,
+ * void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ * a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ ***********************************************************************/
+
+int
+DC390_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
+{
+ USHORT ioport, i;
+ Scsi_Cmnd *pcmd;
+ struct Scsi_Host *psh;
+ PACB pACB;
+ PDCB pDCB;
+ PSRB pSRB;
+ ULONG flags;
+ PUCHAR ptr,ptr1;
+
+ psh = cmd->host;
+ pACB = (PACB ) psh->hostdata;
+ ioport = pACB->IOPortBase;
+
+#ifdef DC390_DEBUG0
+/* if(pACB->scan_devices) */
+ printk("Cmd=%2x,ID=%d,LUN=%d,",cmd->cmnd[0],cmd->target,cmd->lun);
+#endif
+
+ if( (pACB->scan_devices == END_SCAN) && (cmd->cmnd[0] != INQUIRY) )
+ {
+ pACB->scan_devices = 0;
+ pPrevDCB->pNextDCB = pACB->pLinkDCB;
+ }
+ else if( (pACB->scan_devices) && (cmd->cmnd[0] == 8) )
+ {
+ pACB->scan_devices = 0;
+ pPrevDCB->pNextDCB = pACB->pLinkDCB;
+ }
+
+ if ( ( cmd->target > pACB->max_id ) || (cmd->lun > pACB->max_lun) )
+ {
+/* printk("DC390: Ignore target %d lun %d\n",
+ cmd->target, cmd->lun); */
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ return( 0 );
+ }
+
+ if( (pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
+ {
+ if( pACB->DeviceCnt < MAX_DEVICES )
+ {
+ pACB->DCBmap[cmd->target] |= (1 << cmd->lun);
+ pDCB = pACB->pDCB_free;
+#ifdef DC390_DEBUG0
+ printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target);
+#endif
+ DC390_initDCB( pACB, pDCB, cmd );
+ }
+ else /* ???? */
+ {
+/* printk("DC390: Ignore target %d lun %d\n",
+ cmd->target, cmd->lun); */
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ return(0);
+ }
+ }
+ else if( !(pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
+ {
+/* printk("DC390: Ignore target %d lun %d\n",
+ cmd->target, cmd->lun); */
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ return(0);
+ }
+ else
+ {
+ pDCB = pACB->pLinkDCB;
+ while( (pDCB->UnitSCSIID != cmd->target) ||
+ (pDCB->UnitSCSILUN != cmd->lun) )
+ {
+ pDCB = pDCB->pNextDCB;
+ }
+#ifdef DC390_DEBUG0
+ printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target);
+#endif
+ }
+
+ cmd->scsi_done = done;
+ cmd->result = 0;
+
+ save_flags(flags);
+ cli();
+
+ if( pDCB->QIORBCnt )
+ {
+ QLinkcmd( cmd, pDCB );
+ pcmd = Getcmd( pDCB );
+ }
+ else
+ pcmd = cmd;
+
+ pSRB = GetSRB( pACB );
+
+ if( !pSRB )
+ {
+ QLinkcmd( pcmd, pDCB );
+ restore_flags(flags);
+ return(0);
+ }
+
+/* BuildSRB(pSRB); */
+
+ pSRB->pSRBDCB = pDCB;
+ pSRB->pcmd = pcmd;
+ ptr = (PUCHAR) pSRB->CmdBlock;
+ ptr1 = (PUCHAR) pcmd->cmnd;
+ pSRB->ScsiCmdLen = pcmd->cmd_len;
+ for(i=0; i< pcmd->cmd_len; i++)
+ {
+ *ptr = *ptr1;
+ ptr++;
+ ptr1++;
+ }
+ if( pcmd->use_sg )
+ {
+ pSRB->SGcount = (UCHAR) pcmd->use_sg;
+ pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+ }
+ else if( pcmd->request_buffer )
+ {
+ pSRB->SGcount = 1;
+ pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+ pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+ pSRB->Segmentx.length = pcmd->request_bufflen;
+ }
+ else
+ pSRB->SGcount = 0;
+
+ pSRB->SGIndex = 0;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ pSRB->MsgCnt = 0;
+ if( pDCB->DevType != TYPE_TAPE )
+ pSRB->RetryCnt = 1;
+ else
+ pSRB->RetryCnt = 0;
+ pSRB->SRBStatus = 0;
+ pSRB->SRBFlag = 0;
+ pSRB->SRBState = 0;
+ pSRB->TotalXferredLen = 0;
+ pSRB->SGPhysAddr = 0;
+ pSRB->SGToBeXferLen = 0;
+ pSRB->ScsiPhase = 0;
+ pSRB->EndMessage = 0;
+ SendSRB( pcmd, pACB, pSRB );
+
+ restore_flags(flags);
+ return(0);
+}
+
+
+static void
+DoNextCmd( PACB pACB, PDCB pDCB )
+{
+ Scsi_Cmnd *pcmd;
+ PSRB pSRB;
+ ULONG flags;
+ PUCHAR ptr,ptr1;
+ USHORT i;
+
+
+ if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) )
+ return;
+ save_flags(flags);
+ cli();
+
+ pcmd = Getcmd( pDCB );
+ pSRB = GetSRB( pACB );
+ if( !pSRB )
+ {
+ QLinkcmd( pcmd, pDCB );
+ restore_flags(flags);
+ return;
+ }
+
+ pSRB->pSRBDCB = pDCB;
+ pSRB->pcmd = pcmd;
+ ptr = (PUCHAR) pSRB->CmdBlock;
+ ptr1 = (PUCHAR) pcmd->cmnd;
+ pSRB->ScsiCmdLen = pcmd->cmd_len;
+ for(i=0; i< pcmd->cmd_len; i++)
+ {
+ *ptr = *ptr1;
+ ptr++;
+ ptr1++;
+ }
+ if( pcmd->use_sg )
+ {
+ pSRB->SGcount = (UCHAR) pcmd->use_sg;
+ pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+ }
+ else if( pcmd->request_buffer )
+ {
+ pSRB->SGcount = 1;
+ pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+ pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+ pSRB->Segmentx.length = pcmd->request_bufflen;
+ }
+ else
+ pSRB->SGcount = 0;
+
+ pSRB->SGIndex = 0;
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ pSRB->MsgCnt = 0;
+ if( pDCB->DevType != TYPE_TAPE )
+ pSRB->RetryCnt = 1;
+ else
+ pSRB->RetryCnt = 0;
+ pSRB->SRBStatus = 0;
+ pSRB->SRBFlag = 0;
+ pSRB->SRBState = 0;
+ pSRB->TotalXferredLen = 0;
+ pSRB->SGPhysAddr = 0;
+ pSRB->SGToBeXferLen = 0;
+ pSRB->ScsiPhase = 0;
+ pSRB->EndMessage = 0;
+ SendSRB( pcmd, pACB, pSRB );
+
+ restore_flags(flags);
+ return;
+}
+
+
+/***********************************************************************
+ * Function:
+ * DC390_bios_param
+ *
+ * Description:
+ * Return the disk geometry for the given SCSI device.
+ ***********************************************************************/
+#ifdef VERSION_ELF_1_2_13
+int DC390_bios_param(Disk *disk, int devno, int geom[])
+#else
+int DC390_bios_param(Disk *disk, kdev_t devno, int geom[])
+#endif
+{
+ int heads, sectors, cylinders;
+ PACB pACB;
+
+ pACB = (PACB) disk->device->host->hostdata;
+ heads = 64;
+ sectors = 32;
+ cylinders = disk->capacity / (heads * sectors);
+
+ if ( (pACB->Gmode2 & GREATER_1G) && (cylinders > 1024) )
+ {
+ heads = 255;
+ sectors = 63;
+ cylinders = disk->capacity / (heads * sectors);
+ }
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ return (0);
+}
+
+
+/***********************************************************************
+ * Function : int DC390_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : Abort an errant SCSI command
+ *
+ * Inputs : cmd - command to abort
+ *
+ * Returns : 0 on success, -1 on failure.
+ ***********************************************************************/
+
+int
+DC390_abort (Scsi_Cmnd *cmd)
+{
+ ULONG flags;
+ PACB pACB;
+ PDCB pDCB, pdcb;
+ PSRB pSRB, psrb;
+ USHORT count, i;
+ PSCSICMD pcmd, pcmd1;
+ int status;
+
+
+#ifdef DC390_DEBUG0
+ printk("DC390 : Abort Cmd.");
+#endif
+
+ save_flags(flags);
+ cli();
+
+ pACB = (PACB) cmd->host->hostdata;
+ pDCB = pACB->pLinkDCB;
+ pdcb = pDCB;
+ while( (pDCB->UnitSCSIID != cmd->target) ||
+ (pDCB->UnitSCSILUN != cmd->lun) )
+ {
+ pDCB = pDCB->pNextDCB;
+ if( pDCB == pdcb )
+ goto NOT_RUN;
+ }
+
+ if( pDCB->QIORBCnt )
+ {
+ pcmd = pDCB->pQIORBhead;
+ if( pcmd == cmd )
+ {
+ pDCB->pQIORBhead = pcmd->next;
+ pcmd->next = NULL;
+ pDCB->QIORBCnt--;
+ status = SCSI_ABORT_SUCCESS;
+ goto ABO_X;
+ }
+ for( count = pDCB->QIORBCnt, i=0; i<count-1; i++)
+ {
+ if( pcmd->next == cmd )
+ {
+ pcmd1 = pcmd->next;
+ pcmd->next = pcmd1->next;
+ pcmd1->next = NULL;
+ pDCB->QIORBCnt--;
+ status = SCSI_ABORT_SUCCESS;
+ goto ABO_X;
+ }
+ else
+ {
+ pcmd = pcmd->next;
+ }
+ }
+ }
+
+ pSRB = pDCB->pWaitingSRB;
+ if( !pSRB )
+ goto ON_GOING;
+ if( pSRB->pcmd == cmd )
+ {
+ pDCB->pWaitingSRB = pSRB->pNextSRB;
+ goto IN_WAIT;
+ }
+ else
+ {
+ psrb = pSRB;
+ if( !(psrb->pNextSRB) )
+ goto ON_GOING;
+ while( psrb->pNextSRB->pcmd != cmd )
+ {
+ psrb = psrb->pNextSRB;
+ if( !(psrb->pNextSRB) )
+ goto ON_GOING;
+ }
+ pSRB = psrb->pNextSRB;
+ psrb->pNextSRB = pSRB->pNextSRB;
+ if( pSRB == pDCB->pWaitLast )
+ pDCB->pWaitLast = psrb; /* No check for psrb == NULL ? */
+IN_WAIT:
+ pSRB->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = pSRB;
+ cmd->next = NULL;
+ status = SCSI_ABORT_SUCCESS;
+ goto ABO_X;
+ }
+
+ON_GOING:
+ pSRB = pDCB->pGoingSRB;
+ for( count = pDCB->GoingSRBCnt, i=0; i<count; i++)
+ {
+ if( pSRB->pcmd != cmd )
+ pSRB = pSRB->pNextSRB;
+ else
+ {
+ if( (pACB->pActiveDCB == pDCB) && (pDCB->pActiveSRB == pSRB) )
+ {
+ status = SCSI_ABORT_BUSY;
+ goto ABO_X;
+ }
+ else
+ {
+ status = SCSI_ABORT_SNOOZE;
+ goto ABO_X;
+ }
+ }
+ }
+
+NOT_RUN:
+ status = SCSI_ABORT_NOT_RUNNING;
+
+ABO_X:
+ cmd->result = DID_ABORT << 16;
+ cmd->scsi_done(cmd);
+ restore_flags(flags);
+ return( status );
+}
+
+
+static void
+ResetDevParam( PACB pACB )
+{
+ PDCB pDCB, pdcb;
+
+ pDCB = pACB->pLinkDCB;
+ if( pDCB == NULL )
+ return;
+ pdcb = pDCB;
+ do
+ {
+ pDCB->SyncMode &= ~SYNC_NEGO_DONE;
+ pDCB->SyncPeriod = 0;
+ pDCB->SyncOffset = 0;
+ pDCB->CtrlR3 = FAST_CLK;
+ pDCB->CtrlR4 &= NEGATE_REQACKDATA;
+ pDCB->CtrlR4 |= EATER_25NS;
+ pDCB = pDCB->pNextDCB;
+ }
+ while( pdcb != pDCB );
+}
+
+
+static void
+RecoverSRB( PACB pACB )
+{
+ PDCB pDCB, pdcb;
+ PSRB psrb, psrb2;
+ USHORT cnt, i;
+
+ pDCB = pACB->pLinkDCB;
+ if( pDCB == NULL )
+ return;
+ pdcb = pDCB;
+ do
+ {
+ cnt = pdcb->GoingSRBCnt;
+ psrb = pdcb->pGoingSRB;
+ for (i=0; i<cnt; i++)
+ {
+ psrb2 = psrb;
+ psrb = psrb->pNextSRB;
+/* RewaitSRB( pDCB, psrb ); */
+ if( pdcb->pWaitingSRB )
+ {
+ psrb2->pNextSRB = pdcb->pWaitingSRB;
+ pdcb->pWaitingSRB = psrb2;
+ }
+ else
+ {
+ pdcb->pWaitingSRB = psrb2;
+ pdcb->pWaitLast = psrb2;
+ psrb2->pNextSRB = NULL;
+ }
+ }
+ pdcb->GoingSRBCnt = 0;
+ pdcb->pGoingSRB = NULL;
+ pdcb->TagMask = 0;
+ pdcb = pdcb->pNextDCB;
+ }
+ while( pdcb != pDCB );
+}
+
+
+/***********************************************************************
+ * Function : int DC390_reset (Scsi_Cmnd *cmd, ...)
+ *
+ * Purpose : perform a hard reset on the SCSI bus
+ *
+ * Inputs : cmd - command which caused the SCSI RESET
+ *
+ * Returns : 0 on success.
+ ***********************************************************************/
+
+#ifdef VERSION_2_0_0
+int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags)
+#else
+int DC390_reset (Scsi_Cmnd *cmd)
+#endif
+{
+ USHORT ioport;
+ unsigned long flags;
+ PACB pACB;
+ UCHAR bval;
+ USHORT i;
+
+
+#ifdef DC390_DEBUG1
+ printk("DC390: RESET,");
+#endif
+
+ pACB = (PACB ) cmd->host->hostdata;
+ ioport = pACB->IOPortBase;
+ save_flags(flags);
+ cli();
+ bval = inb(ioport+CtrlReg1);
+ bval |= DIS_INT_ON_SCSI_RST;
+ outb(bval,ioport+CtrlReg1); /* disable interrupt */
+ DC390_ResetSCSIBus( pACB );
+ for( i=0; i<500; i++ )
+ udelay(1000);
+ bval = inb(ioport+CtrlReg1);
+ bval &= ~DIS_INT_ON_SCSI_RST;
+ outb(bval,ioport+CtrlReg1); /* re-enable interrupt */
+
+ bval = DMA_IDLE_CMD;
+ outb(bval,ioport+DMA_Cmd);
+ bval = CLEAR_FIFO_CMD;
+ outb(bval,ioport+ScsiCmd);
+
+ ResetDevParam( pACB );
+ DoingSRB_Done( pACB );
+ pACB->pActiveDCB = NULL;
+
+ pACB->ACBFlag = 0;
+ DoWaitingSRB( pACB );
+
+ restore_flags(flags);
+#ifdef DC390_DEBUG1
+ printk("DC390: RESET1,");
+#endif
+ return( SCSI_RESET_SUCCESS );
+}
+
+
+#include "scsiiom.c"
+
+
+/***********************************************************************
+ * Function : static void DC390_initDCB
+ *
+ * Purpose : initialize the internal structures for a given DCB
+ *
+ * Inputs : cmd - pointer to this scsi cmd request block structure
+ *
+ ***********************************************************************/
+void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd )
+{
+ PEEprom prom;
+ UCHAR bval;
+ USHORT index;
+
+ if( pACB->DeviceCnt == 0 )
+ {
+ pACB->pLinkDCB = pDCB;
+ pACB->pDCBRunRobin = pDCB;
+ pDCB->pNextDCB = pDCB;
+ pPrevDCB = pDCB;
+ }
+ else
+ pPrevDCB->pNextDCB = pDCB;
+
+ pDCB->pDCBACB = pACB;
+ pDCB->QIORBCnt = 0;
+ pDCB->UnitSCSIID = cmd->target;
+ pDCB->UnitSCSILUN = cmd->lun;
+ pDCB->pWaitingSRB = NULL;
+ pDCB->pGoingSRB = NULL;
+ pDCB->GoingSRBCnt = 0;
+ pDCB->pActiveSRB = NULL;
+ pDCB->TagMask = 0;
+ pDCB->MaxCommand = 1;
+ pDCB->AdaptIndex = pACB->AdapterIndex;
+ index = pACB->AdapterIndex;
+ pDCB->DCBFlag = 0;
+
+ prom = (PEEprom) &eepromBuf[index][cmd->target << 2];
+ pDCB->DevMode = prom->EE_MODE1;
+ pDCB->AdpMode = eepromBuf[index][EE_MODE2];
+
+ if( pDCB->DevMode & EN_DISCONNECT_ )
+ bval = 0xC0;
+ else
+ bval = 0x80;
+ bval |= cmd->lun;
+ pDCB->IdentifyMsg = bval;
+
+ pDCB->SyncMode = 0;
+ if( pDCB->DevMode & SYNC_NEGO_ )
+ {
+ if( !(cmd->lun) || CurrSyncOffset )
+ pDCB->SyncMode = SYNC_ENABLE;
+ }
+
+ pDCB->SyncPeriod = 0;
+ pDCB->SyncOffset = 0;
+ pDCB->NegoPeriod = (clock_period1[prom->EE_SPEED] * 25) >> 2;
+
+ pDCB->CtrlR1 = pACB->AdaptSCSIID;
+ if( pDCB->DevMode & PARITY_CHK_ )
+ pDCB->CtrlR1 |= PARITY_ERR_REPO;
+
+ pDCB->CtrlR3 = FAST_CLK;
+
+ pDCB->CtrlR4 = EATER_25NS;
+ if( pDCB->AdpMode & ACTIVE_NEGATION)
+ pDCB->CtrlR4 |= NEGATE_REQACKDATA;
+}
+
+
+/***********************************************************************
+ * Function : static void DC390_initSRB
+ *
+ * Purpose : initialize the internal structures for a given SRB
+ *
+ * Inputs : psrb - pointer to this scsi request block structure
+ *
+ ***********************************************************************/
+void DC390_initSRB( PSRB psrb )
+{
+#ifndef VERSION_ELF_1_2_13
+#ifdef DC390_DEBUG0
+ printk("DC390 init: %08lx %08lx,",(ULONG)psrb,(ULONG)virt_to_bus(psrb));
+#endif
+ psrb->PhysSRB = virt_to_bus( psrb );
+#else
+ psrb->PhysSRB = (ULONG) psrb;
+#endif
+}
+
+
+void DC390_linkSRB( PACB pACB )
+{
+ USHORT count, i;
+ PSRB psrb;
+
+ count = pACB->SRBCount;
+
+ for( i=0; i< count; i++)
+ {
+ if( i != count - 1)
+ pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1];
+ else
+ pACB->SRB_array[i].pNextSRB = NULL;
+ psrb = (PSRB) &pACB->SRB_array[i];
+ DC390_initSRB( psrb );
+ }
+}
+
+
+/***********************************************************************
+ * Function : static void DC390_initACB
+ *
+ * Purpose : initialize the internal structures for a given SCSI host
+ *
+ * Inputs : psh - pointer to this host adapter's structure
+ *
+ ***********************************************************************/
+void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
+{
+ PACB pACB;
+ USHORT i;
+
+ psh->can_queue = MAX_CMD_QUEUE;
+ psh->cmd_per_lun = MAX_CMD_PER_LUN;
+ psh->this_id = (int) eepromBuf[index][EE_ADAPT_SCSI_ID];
+ psh->io_port = io_port;
+ psh->n_io_port = 0x80;
+ psh->irq = Irq;
+
+ pACB = (PACB) psh->hostdata;
+
+#ifndef VERSION_ELF_1_2_13
+ psh->max_id = 8;
+#ifdef CONFIG_SCSI_MULTI_LUN
+ if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
+ psh->max_lun = 8;
+ else
+#endif
+ psh->max_lun = 1;
+#endif
+
+ pACB->max_id = 7;
+ if( pACB->max_id == eepromBuf[index][EE_ADAPT_SCSI_ID] )
+ pACB->max_id--;
+#ifdef CONFIG_SCSI_MULTI_LUN
+ if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
+ pACB->max_lun = 7;
+ else
+#endif
+ pACB->max_lun = 0;
+
+ pACB->pScsiHost = psh;
+ pACB->IOPortBase = (USHORT) io_port;
+ pACB->pLinkDCB = NULL;
+ pACB->pDCBRunRobin = NULL;
+ pACB->pActiveDCB = NULL;
+ pACB->pFreeSRB = pACB->SRB_array;
+ pACB->SRBCount = MAX_SRB_CNT;
+ pACB->AdapterIndex = index;
+ pACB->status = 0;
+ pACB->AdaptSCSIID = eepromBuf[index][EE_ADAPT_SCSI_ID];
+ pACB->HostID_Bit = (1 << pACB->AdaptSCSIID);
+ pACB->AdaptSCSILUN = 0;
+ pACB->DeviceCnt = 0;
+ pACB->IRQLevel = Irq;
+ pACB->TagMaxNum = eepromBuf[index][EE_TAG_CMD_NUM] << 2;
+ pACB->ACBFlag = 0;
+ pACB->scan_devices = 1;
+ pACB->Gmode2 = eepromBuf[index][EE_MODE2];
+ if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
+ pACB->LUNchk = 1;
+ pACB->pDCB_free = &pACB->DCB_array[0];
+ DC390_linkSRB( pACB );
+ pACB->pTmpSRB = &pACB->TmpSRB;
+ DC390_initSRB( pACB->pTmpSRB );
+ for(i=0; i<MAX_SCSI_ID; i++)
+ pACB->DCBmap[i] = 0;
+}
+
+
+/***********************************************************************
+ * Function : static int DC390_initAdapter
+ *
+ * Purpose : initialize the SCSI chip ctrl registers
+ *
+ * Inputs : psh - pointer to this host adapter's structure
+ *
+ ***********************************************************************/
+int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
+{
+ USHORT ioport;
+ UCHAR bval;
+ PACB pACB, pacb;
+ USHORT used_irq = 0;
+
+ pacb = pACB_start;
+ if( pacb != NULL )
+ {
+ for ( ; (pacb != (PACB) -1) ; )
+ {
+ if( pacb->IRQLevel == Irq )
+ {
+ used_irq = 1;
+ break;
+ }
+ else
+ pacb = pacb->pNextACB;
+ }
+ }
+
+ if( !used_irq )
+ {
+#ifdef VERSION_ELF_1_2_13
+ if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim"))
+#else
+ if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT | SA_SHIRQ, "tmscsim", NULL))
+#endif
+ {
+ printk("DC390: register IRQ error!\n");
+ return( -1 );
+ }
+ }
+
+ request_region(io_port,psh->n_io_port,"tmscsim");
+
+ ioport = (USHORT) io_port;
+
+ pACB = (PACB) psh->hostdata;
+ bval = SEL_TIMEOUT; /* 250ms selection timeout */
+ outb(bval,ioport+Scsi_TimeOut);
+
+ bval = CLK_FREQ_40MHZ; /* Conversion factor = 0 , 40MHz clock */
+ outb(bval,ioport+Clk_Factor);
+
+ bval = NOP_CMD; /* NOP cmd - clear command register */
+ outb(bval,ioport+ScsiCmd);
+
+ bval = EN_FEATURE+EN_SCSI2_CMD; /* Enable Feature and SCSI-2 */
+ outb(bval,ioport+CtrlReg2);
+
+ bval = FAST_CLK; /* fast clock */
+ outb(bval,ioport+CtrlReg3);
+
+ bval = EATER_25NS;
+ if( eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION )
+ bval |= NEGATE_REQACKDATA;
+ outb(bval,ioport+CtrlReg4);
+
+ bval = DIS_INT_ON_SCSI_RST; /* Disable SCSI bus reset interrupt */
+ outb(bval,ioport+CtrlReg1);
+
+ return(0);
+}
+
+
+void
+DC390_EnableCfg( USHORT mechnum, UCHAR regval )
+{
+ ULONG wlval;
+
+ if(mechnum == 2)
+ {
+ outb(mech2bus, PCI_CFG2_FORWARD_REG);
+ outb(mech2CfgSPenR, PCI_CFG2_ENABLE_REG);
+ }
+ else
+ {
+ regval &= 0xFC;
+ wlval = mech1addr;
+ wlval |= (((ULONG)regval) & 0xff);
+ outl(wlval, PCI_CFG1_ADDRESS_REG);
+ }
+}
+
+
+void
+DC390_DisableCfg( USHORT mechnum )
+{
+
+ if(mechnum == 2)
+ outb(0, PCI_CFG2_ENABLE_REG);
+ else
+ outl(0, PCI_CFG1_ADDRESS_REG);
+}
+
+
+UCHAR
+DC390_inByte( USHORT mechnum, UCHAR regval )
+{
+ UCHAR bval;
+ ULONG wval;
+ ULONG flags;
+
+ save_flags(flags);
+ cli();
+ DC390_EnableCfg( mechnum, regval );
+ if(mechnum == 2)
+ {
+ wval = mech2Agent;
+ wval <<= 8;
+ wval |= ((USHORT) regval) & 0xff;
+ bval = inb(wval);
+ }
+ else
+ {
+ regval &= 3;
+ bval = inb(PCI_CFG1_DATA_REG | regval);
+ }
+ DC390_DisableCfg(mechnum);
+ restore_flags(flags);
+ return(bval);
+}
+
+
+USHORT
+DC390_inWord( USHORT mechnum, UCHAR regval )
+{
+ USHORT wval;
+ ULONG flags;
+
+ save_flags(flags);
+ cli();
+ DC390_EnableCfg(mechnum,regval);
+ if(mechnum == 2)
+ {
+ wval = mech2Agent;
+ wval <<= 8;
+ wval |= regval;
+ wval = inw(wval);
+ }
+ else
+ {
+ regval &= 3;
+ wval = inw(PCI_CFG1_DATA_REG | regval);
+ }
+ DC390_DisableCfg(mechnum);
+ restore_flags(flags);
+ return(wval);
+}
+
+
+ULONG
+DC390_inDword(USHORT mechnum, UCHAR regval )
+{
+ ULONG wlval;
+ ULONG flags;
+ USHORT wval;
+
+ save_flags(flags);
+ cli();
+ DC390_EnableCfg(mechnum,regval);
+ if(mechnum == 2)
+ {
+ wval = mech2Agent;
+ wval <<= 8;
+ wval |= regval;
+ wlval = inl(wval);
+ }
+ else
+ {
+ wlval = inl(PCI_CFG1_DATA_REG);
+ }
+ DC390_DisableCfg(mechnum);
+ restore_flags(flags);
+ return(wlval);
+}
+
+
+void
+DC390_OutB(USHORT mechnum, UCHAR regval, UCHAR bval )
+{
+
+ USHORT wval;
+ ULONG flags;
+
+ save_flags(flags);
+ cli();
+ DC390_EnableCfg(mechnum,regval);
+ if(mechnum == 2)
+ {
+ wval = mech2Agent;
+ wval <<= 8;
+ wval |= regval;
+ outb(bval, wval);
+ }
+ else
+ {
+ regval &= 3;
+ outb(bval, PCI_CFG1_DATA_REG | regval);
+ }
+ DC390_DisableCfg(mechnum);
+ restore_flags(flags);
+}
+
+
+void
+DC390_EnDisableCE( UCHAR mode, USHORT mechnum, PUCHAR regval )
+{
+
+ UCHAR bval;
+
+ bval = 0;
+ if(mode == ENABLE_CE)
+ *regval = 0xc0;
+ else
+ *regval = 0x80;
+ DC390_OutB(mechnum,*regval,bval);
+ if(mode == DISABLE_CE)
+ DC390_OutB(mechnum,*regval,bval);
+ udelay(160);
+}
+
+
+void
+DC390_EEpromOutDI( USHORT mechnum, PUCHAR regval, USHORT Carry )
+{
+ UCHAR bval;
+
+ bval = 0;
+ if(Carry)
+ {
+ bval = 0x40;
+ *regval = 0x80;
+ DC390_OutB(mechnum,*regval,bval);
+ }
+ udelay(160);
+ bval |= 0x80;
+ DC390_OutB(mechnum,*regval,bval);
+ udelay(160);
+ bval = 0;
+ DC390_OutB(mechnum,*regval,bval);
+ udelay(160);
+}
+
+
+UCHAR
+DC390_EEpromInDO( USHORT mechnum )
+{
+ UCHAR bval,regval;
+
+ regval = 0x80;
+ bval = 0x80;
+ DC390_OutB(mechnum,regval,bval);
+ udelay(160);
+ bval = 0x40;
+ DC390_OutB(mechnum,regval,bval);
+ udelay(160);
+ regval = 0x0;
+ bval = DC390_inByte(mechnum,regval);
+ if(bval == 0x22)
+ return(1);
+ else
+ return(0);
+}
+
+
+USHORT
+EEpromGetData1( USHORT mechnum )
+{
+ UCHAR i;
+ UCHAR carryFlag;
+ USHORT wval;
+
+ wval = 0;
+ for(i=0; i<16; i++)
+ {
+ wval <<= 1;
+ carryFlag = DC390_EEpromInDO(mechnum);
+ wval |= carryFlag;
+ }
+ return(wval);
+}
+
+
+void
+DC390_Prepare( USHORT mechnum, PUCHAR regval, UCHAR EEpromCmd )
+{
+ UCHAR i,j;
+ USHORT carryFlag;
+
+ carryFlag = 1;
+ j = 0x80;
+ for(i=0; i<9; i++)
+ {
+ DC390_EEpromOutDI(mechnum,regval,carryFlag);
+ carryFlag = (EEpromCmd & j) ? 1 : 0;
+ j >>= 1;
+ }
+}
+
+
+void
+DC390_ReadEEprom( USHORT mechnum, USHORT index )
+{
+ UCHAR regval,cmd;
+ PUSHORT ptr;
+ USHORT i;
+
+ ptr = (PUSHORT) &eepromBuf[index][0];
+ cmd = EEPROM_READ;
+ for(i=0; i<0x40; i++)
+ {
+ DC390_EnDisableCE(ENABLE_CE, mechnum, &regval);
+ DC390_Prepare(mechnum, &regval, cmd);
+ *ptr = EEpromGetData1(mechnum);
+ ptr++;
+ cmd++;
+ DC390_EnDisableCE(DISABLE_CE,mechnum,&regval);
+ }
+}
+
+
+USHORT
+DC390_CheckEEpromCheckSum( USHORT MechNum, USHORT index )
+{
+ USHORT wval, rc, *ptr;
+ UCHAR i;
+
+ DC390_ReadEEprom( MechNum, index );
+ wval = 0;
+ ptr = (PUSHORT) &eepromBuf[index][0];
+ for(i=0; i<128 ;i+=2, ptr++)
+ wval += *ptr;
+ if( wval == 0x1234 )
+ rc = 0;
+ else
+ rc = -1;
+ return( rc );
+}
+
+
+USHORT
+DC390_ToMech( USHORT Mechnum, USHORT BusDevFunNum )
+{
+ USHORT devnum;
+
+ devnum = BusDevFunNum;
+
+ if(Mechnum == 2)
+ {
+ if(devnum & 0x80)
+ return(-1);
+ mech2bus = (UCHAR)((devnum & 0xff00) >> 8); /* Bus num */
+ mech2Agent = ((UCHAR)(devnum & 0xff)) >> 3; /* Dev num */
+ mech2Agent |= 0xc0;
+ mech2CfgSPenR = ((UCHAR)(devnum & 0xff)) & 0x07; /* Fun num */
+ mech2CfgSPenR = (mech2CfgSPenR << 1) | 0x20;
+ }
+ else /* use mech #1 method */
+ {
+ mech1addr = 0x80000000 | ((ULONG)devnum << 8);
+ }
+ return(0);
+}
+
+/***********************************************************************
+ * Function : static int DC390_init (struct Scsi_Host *host)
+ *
+ * Purpose : initialize the internal structures for a given SCSI host
+ *
+ * Inputs : host - pointer to this host adapter's structure/
+ *
+ * Preconditions : when this function is called, the chip_type
+ * field of the pACB structure MUST have been set.
+ ***********************************************************************/
+
+static int
+DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum)
+{
+ PSH psh;
+ PACB pACB;
+
+ if( !DC390_CheckEEpromCheckSum( MechNum, index) )
+ {
+ psh = scsi_register( psht, sizeof(DC390_ACB) );
+ if( !psh )
+ return( -1 );
+ if( !pSH_start )
+ {
+ pSH_start = psh;
+ pSH_current = psh;
+ }
+ else
+ {
+ pSH_current->next = psh;
+ pSH_current = psh;
+ }
+
+#ifdef DC390_DEBUG0
+ printk("DC390: pSH = %8x,", (UINT) psh);
+ printk("DC390: Index %02i,", index);
+#endif
+
+ DC390_initACB( psh, io_port, Irq, index );
+ if( !DC390_initAdapter( psh, io_port, Irq, index ) )
+ {
+ pACB = (PACB) psh->hostdata;
+ if( !pACB_start )
+ {
+ pACB_start = pACB;
+ pACB_current = pACB;
+ pACB->pNextACB = (PACB) -1;
+ }
+ else
+ {
+ pACB_current->pNextACB = pACB;
+ pACB_current = pACB;
+ pACB->pNextACB = (PACB) -1;
+ }
+
+#ifdef DC390_DEBUG0
+ printk("DC390: pACB = %8x, pDCB_array = %8x, pSRB_array = %8x\n",
+ (UINT) pACB, (UINT) pACB->DCB_array, (UINT) pACB->SRB_array);
+ printk("DC390: ACB size= %4x, DCB size= %4x, SRB size= %4x\n",
+ sizeof(DC390_ACB), sizeof(DC390_DCB), sizeof(DC390_SRB) );
+#endif
+
+ }
+ else
+ {
+ pSH_start = NULL;
+ scsi_unregister( psh );
+ return( -1 );
+ }
+ return( 0 );
+ }
+ else
+ {
+ printk("DC390_init: EEPROM reading error!\n");
+ return( -1 );
+ }
+}
+
+
+/***********************************************************************
+ * Function : int DC390_detect(Scsi_Host_Template *psht)
+ *
+ * Purpose : detects and initializes AMD53C974 SCSI chips
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : psht - template for this SCSI adapter
+ *
+ * Returns : number of host adapters detected
+ *
+ ***********************************************************************/
+
+int
+DC390_detect(Scsi_Host_Template *psht)
+{
+#ifdef FOR_PCI_OK
+ UCHAR pci_bus, pci_device_fn;
+ int error = 0;
+ USHORT chipType = 0;
+ USHORT i;
+#endif
+
+ UCHAR irq;
+ UCHAR istatus;
+#ifndef VERSION_ELF_1_2_13
+ UINT io_port;
+#else
+ ULONG io_port;
+#endif
+ USHORT adaptCnt = 0; /* Number of boards detected */
+ USHORT pci_index = 0; /* Device index to PCI BIOS calls */
+ USHORT MechNum, BusDevFunNum;
+ ULONG wlval;
+
+#ifndef VERSION_ELF_1_2_13
+ psht->proc_dir = &proc_scsi_tmscsim;
+#endif
+
+ InitialTime = 1;
+ pSHT_start = psht;
+ pACB_start = NULL;
+
+ MechNum = 1;
+ for( ; (MechNum < 3) && (!adaptCnt); MechNum++)
+ {
+ BusDevFunNum = 0;
+ for (; adaptCnt < MAX_ADAPTER_NUM ;)
+ {
+ if( !DC390_ToMech( MechNum, BusDevFunNum) )
+ {
+ wlval = DC390_inDword( MechNum, PCI_VENDOR_ID);
+ if(wlval == ( (PCI_DEVICE_ID_AMD53C974 << 16)+
+ PCI_VENDOR_ID_AMD) )
+ {
+ io_port =DC390_inDword(MechNum,PCI_BASE_ADDRESS_0) & 0xFFFE;
+ irq = DC390_inByte( MechNum, PCI_INTERRUPT_LINE);
+#ifdef DC390_DEBUG0
+ printk("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq);
+#endif
+ if( !DC390_init(psht, io_port, irq, pci_index, MechNum) )
+ {
+ adaptCnt++;
+ pci_index++;
+ istatus = inb( (USHORT)io_port+INT_Status ); /* Reset Pending INT */
+#ifdef DC390_DEBUG0
+ printk("DC390: Mech=%2x,\n",(UCHAR) MechNum);
+#endif
+ }
+ }
+ }
+ if( BusDevFunNum != 0xfff8 )
+ BusDevFunNum += 8; /* next device # */
+ else
+ break;
+ }
+ }
+
+#ifdef FOR_PCI_OK
+ if ( pcibios_present() )
+ {
+ for (i = 0; i < MAX_ADAPTER_NUM; ++i)
+ {
+ if( !pcibios_find_device( PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD53C974,
+ pci_index, &pci_bus, &pci_device_fn) )
+ {
+ chipType = PCI_DEVICE_ID_AMD53C974;
+ pci_index++;
+ }
+
+ if( chipType )
+ {
+
+ error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &io_port);
+ error |= pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &irq);
+ if( error )
+ {
+ printk("DC390_detect: reading configuration registers error!\n");
+ InitialTime = 0;
+ return( 0 );
+ }
+
+ (USHORT) io_port = (USHORT) io_port & 0xFFFE;
+#ifdef DC390_DEBUG0
+ printk("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq);
+#endif
+ if( !DC390_init(psht, io_port, irq, i) )
+ adaptCnt++;
+ chipType = 0;
+ }
+ else
+ break;
+ }
+ }
+#endif
+
+ InitialTime = 0;
+ adapterCnt = adaptCnt;
+ return( adaptCnt );
+}
+
+
+#ifndef VERSION_ELF_1_2_13
+
+/********************************************************************
+ * Function: tmscsim_set_info()
+ *
+ * Purpose: Set adapter info (!)
+ *
+ * Not yet implemented
+ *
+ *******************************************************************/
+
+int tmscsim_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
+{
+ return(-ENOSYS); /* Currently this is a no-op */
+}
+
+/********************************************************************
+ * Function: tmscsim_proc_info(char* buffer, char **start,
+ * off_t offset, int length, int hostno, int inout)
+ *
+ * Purpose: return SCSI Adapter/Device Info
+ *
+ * Input: buffer: Pointer to a buffer where to write info
+ * start :
+ * offset:
+ * hostno: Host adapter index
+ * inout : Read (=0) or set(!=0) info
+ *
+ * Output: buffer: contains info
+ * length; length of info in buffer
+ *
+ * return value: length
+ *
+ ********************************************************************/
+
+/* KG: proc_info taken from driver aha152x.c */
+
+#undef SPRINTF
+#define SPRINTF(args...) pos += sprintf(pos, ## args)
+
+#define YESNO(YN)\
+if (YN) SPRINTF(" Yes ");\
+else SPRINTF(" No ")
+
+int tmscsim_proc_info(char *buffer, char **start,
+ off_t offset, int length, int hostno, int inout)
+{
+ int dev, spd, spd1;
+ char *pos = buffer;
+ PSH shpnt;
+ PACB acbpnt;
+ PDCB dcbpnt;
+ unsigned long flags;
+/* Scsi_Cmnd *ptr; */
+
+ acbpnt = pACB_start;
+
+ while(acbpnt != (PACB)-1)
+ {
+ shpnt = acbpnt->pScsiHost;
+ if (shpnt->host_no == hostno) break;
+ acbpnt = acbpnt->pNextACB;
+ }
+
+ if (acbpnt == (PACB)-1) return(-ESRCH);
+ if(!shpnt) return(-ESRCH);
+
+ if(inout) /* Has data been written to the file ? */
+ return(tmscsim_set_info(buffer, length, shpnt));
+
+ SPRINTF("Tekram DC390(T) PCI SCSI Host Adadpter, ");
+ SPRINTF("Driver Version 1.10, 1996/12/05\n");
+
+ save_flags(flags);
+ cli();
+
+ SPRINTF("SCSI Host Nr %i, ", shpnt->host_no);
+ SPRINTF("DC390 Adapter Nr %i\n", acbpnt->AdapterIndex);
+ SPRINTF("IOPortBase 0x%04x, ", acbpnt->IOPortBase);
+ SPRINTF("IRQLevel 0x%02x\n", acbpnt->IRQLevel);
+
+ SPRINTF("MaxID %i, MaxLUN %i, ",acbpnt->max_id, acbpnt->max_lun);
+ SPRINTF("AdapterID %i, AdapterLUN %i\n", acbpnt->AdaptSCSIID, acbpnt->AdaptSCSILUN);
+
+ SPRINTF("TagMaxNum %i, Status %i\n", acbpnt->TagMaxNum, acbpnt->status);
+
+ SPRINTF("Nr of attached devices: %i\n", acbpnt->DeviceCnt);
+
+ SPRINTF("Un ID LUN Prty Sync DsCn SndS TagQ NegoPeriod SyncSpeed SyncOffs\n");
+
+ dcbpnt = acbpnt->pLinkDCB;
+ for (dev = 0; dev < acbpnt->DeviceCnt; dev++)
+ {
+ SPRINTF("%02i %02i %02i ", dev, dcbpnt->UnitSCSIID, dcbpnt->UnitSCSILUN);
+ YESNO(dcbpnt->DevMode & PARITY_CHK_);
+ YESNO(dcbpnt->SyncMode & SYNC_NEGO_DONE);
+ YESNO(dcbpnt->DevMode & EN_DISCONNECT_);
+ YESNO(dcbpnt->DevMode & SEND_START_);
+ YESNO(dcbpnt->SyncMode & EN_TAG_QUEUING);
+ SPRINTF(" %03i ns ", (dcbpnt->NegoPeriod) << 2);
+ if (dcbpnt->SyncOffset & 0x0f)
+ {
+ spd = 1000/(dcbpnt->NegoPeriod <<2);
+ spd1 = 1000%(dcbpnt->NegoPeriod <<2);
+ spd1 = (spd1 * 10)/(dcbpnt->NegoPeriod <<2);
+ SPRINTF(" %2i.%1i M %02i\n", spd, spd1, (dcbpnt->SyncOffset & 0x0f));
+ }
+ else SPRINTF("\n");
+ /* Add more info ...*/
+ dcbpnt = dcbpnt->pNextDCB;
+ }
+
+ restore_flags(flags);
+ *start = buffer + offset;
+
+ if (pos - buffer < offset)
+ return 0;
+ else if (pos - buffer - offset < length)
+ return pos - buffer - offset;
+ else
+ return length;
+}
+#endif /* VERSION_ELF_1_2_13 */
+
+
+#ifdef MODULE
+
+/***********************************************************************
+ * Function : static int DC390_shutdown (struct Scsi_Host *host)
+ *
+ * Purpose : does a clean (we hope) shutdown of the SCSI chip.
+ * Use prior to dumping core, unloading the driver, etc.
+ *
+ * Returns : 0 on success
+ ***********************************************************************/
+static int
+DC390_shutdown (struct Scsi_Host *host)
+{
+ UCHAR bval;
+ USHORT ioport;
+ unsigned long flags;
+ PACB pACB = (PACB)(host->hostdata);
+
+ ioport = (unsigned int) pACB->IOPortBase;
+
+ save_flags (flags);
+ cli();
+
+/* pACB->soft_reset(host); */
+
+#ifdef DC390_DEBUG0
+ printk("DC390: shutdown,");
+#endif
+
+ bval = inb(ioport+CtrlReg1);
+ bval |= DIS_INT_ON_SCSI_RST;
+ outb(bval,ioport+CtrlReg1); /* disable interrupt */
+ DC390_ResetSCSIBus( pACB );
+
+ restore_flags (flags);
+ return( 0 );
+}
+
+
+int DC390_release(struct Scsi_Host *host)
+{
+ int irq_count;
+ struct Scsi_Host *tmp;
+
+ DC390_shutdown (host);
+
+ if (host->irq != IRQ_NONE)
+ {
+ for (irq_count = 0, tmp = pSH_start; tmp; tmp = tmp->next)
+ {
+ if ( tmp->irq == host->irq )
+ ++irq_count;
+ }
+ if (irq_count == 1)
+ {
+#ifdef DC390_DEBUG0
+ printk("DC390: Free IRQ %i.",host->irq);
+#endif
+#ifndef VERSION_ELF_1_2_13
+ free_irq(host->irq,NULL);
+#else
+ free_irq(host->irq);
+#endif
+ }
+ }
+
+ release_region(host->io_port,host->n_io_port);
+
+ return( 1 );
+}
+
+Scsi_Host_Template driver_template = DC390_T;
+#include "scsi_module.c"
+#endif /* def MODULE */
+
diff --git a/linux/src/drivers/scsi/tmscsim.h b/linux/src/drivers/scsi/tmscsim.h
new file mode 100644
index 00000000..361c488a
--- /dev/null
+++ b/linux/src/drivers/scsi/tmscsim.h
@@ -0,0 +1,680 @@
+/***********************************************************************
+;* File Name : TMSCSIM.H *
+;* TEKRAM DC-390(T) PCI SCSI Bus Master Host Adapter *
+;* Device Driver *
+;***********************************************************************/
+
+#ifndef TMSCSIM_H
+#define TMSCSIM_H
+
+#define IRQ_NONE 255
+
+typedef unsigned char UCHAR;
+typedef unsigned short USHORT;
+typedef unsigned long ULONG;
+typedef unsigned int UINT;
+
+typedef UCHAR *PUCHAR;
+typedef USHORT *PUSHORT;
+typedef ULONG *PULONG;
+typedef Scsi_Host_Template *PSHT;
+typedef struct Scsi_Host *PSH;
+typedef Scsi_Device *PSCSIDEV;
+typedef Scsi_Cmnd *PSCSICMD;
+typedef void *PVOID;
+typedef struct scatterlist *PSGL, SGL;
+
+
+/*;-----------------------------------------------------------------------*/
+typedef struct _SyncMsg
+{
+UCHAR ExtendMsg;
+UCHAR ExtMsgLen;
+UCHAR SyncXferReq;
+UCHAR Period;
+UCHAR ReqOffset;
+} SyncMsg;
+/*;-----------------------------------------------------------------------*/
+typedef struct _Capacity
+{
+ULONG BlockCount;
+ULONG BlockLength;
+} Capacity;
+/*;-----------------------------------------------------------------------*/
+typedef struct _SGentry
+{
+ULONG SGXferDataPtr;
+ULONG SGXferDataLen;
+} SGentry;
+
+typedef struct _SGentry1
+{
+ULONG SGXLen;
+ULONG SGXPtr;
+} SGentry1, *PSGE;
+
+
+#define MAX_ADAPTER_NUM 4
+#define MAX_DEVICES 10
+#define MAX_SG_LIST_BUF 16
+#define MAX_CMD_QUEUE 20
+#define MAX_CMD_PER_LUN 8
+#define MAX_SCSI_ID 8
+#define MAX_SRB_CNT MAX_CMD_QUEUE+4
+#define END_SCAN 2
+
+#define SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */
+
+/*
+;-----------------------------------------------------------------------
+; SCSI Request Block
+;-----------------------------------------------------------------------
+*/
+struct _SRB
+{
+UCHAR CmdBlock[12];
+
+struct _SRB *pNextSRB;
+struct _DCB *pSRBDCB;
+PSCSICMD pcmd;
+PSGL pSegmentList;
+
+ULONG PhysSRB;
+ULONG TotalXferredLen;
+ULONG SGPhysAddr; /*;a segment starting address */
+ULONG SGToBeXferLen; /*; to be xfer length */
+
+SGL Segmentx; /* make a one entry of S/G list table */
+
+PUCHAR pMsgPtr;
+USHORT SRBState;
+USHORT Revxx2; /* ??? */
+
+UCHAR MsgInBuf[6];
+UCHAR MsgOutBuf[6];
+
+UCHAR AdaptStatus;
+UCHAR TargetStatus;
+UCHAR MsgCnt;
+UCHAR EndMessage;
+UCHAR TagNumber;
+UCHAR SGcount;
+UCHAR SGIndex;
+UCHAR IORBFlag; /*;81h-Reset, 2-retry */
+
+UCHAR SRBStatus;
+UCHAR RetryCnt;
+UCHAR SRBFlag; /*; b0-AutoReqSense,b6-Read,b7-write */
+ /*; b4-settimeout,b5-Residual valid */
+UCHAR ScsiCmdLen;
+UCHAR ScsiPhase;
+UCHAR Reserved3[3]; /*;for dword alignment */
+ULONG Segment0[2];
+ULONG Segment1[2];
+};
+
+typedef struct _SRB DC390_SRB, *PSRB;
+
+/*
+;-----------------------------------------------------------------------
+; Device Control Block
+;-----------------------------------------------------------------------
+*/
+struct _DCB
+{
+struct _DCB *pNextDCB;
+struct _ACB *pDCBACB;
+
+PSCSICMD pQIORBhead;
+PSCSICMD pQIORBtail;
+PSCSICMD AboIORBhead;
+PSCSICMD AboIORBtail;
+USHORT QIORBCnt;
+USHORT AboIORBcnt;
+
+PSRB pWaitingSRB;
+PSRB pWaitLast;
+PSRB pGoingSRB;
+PSRB pGoingLast;
+PSRB pActiveSRB;
+USHORT GoingSRBCnt;
+USHORT WaitSRBCnt; /* ??? */
+
+ULONG TagMask;
+
+USHORT MaxCommand;
+USHORT AdaptIndex; /*; UnitInfo struc start */
+USHORT UnitIndex; /*; nth Unit on this card */
+UCHAR UnitSCSIID; /*; SCSI Target ID (SCSI Only) */
+UCHAR UnitSCSILUN; /*; SCSI Log. Unit (SCSI Only) */
+
+UCHAR IdentifyMsg;
+UCHAR CtrlR1;
+UCHAR CtrlR3;
+UCHAR CtrlR4;
+
+UCHAR InqDataBuf[8];
+UCHAR CapacityBuf[8];
+UCHAR DevMode;
+UCHAR AdpMode;
+UCHAR SyncMode; /*; 0:async mode */
+UCHAR NegoPeriod; /*;for nego. */
+UCHAR SyncPeriod; /*;for reg. */
+UCHAR SyncOffset; /*;for reg. and nego.(low nibble) */
+UCHAR UnitCtrlFlag;
+UCHAR DCBFlag;
+UCHAR DevType;
+UCHAR Reserved2[3]; /*;for dword alignment */
+};
+
+typedef struct _DCB DC390_DCB, *PDCB;
+/*
+;-----------------------------------------------------------------------
+; Adapter Control Block
+;-----------------------------------------------------------------------
+*/
+struct _ACB
+{
+ULONG PhysACB;
+PSH pScsiHost;
+struct _ACB *pNextACB;
+USHORT IOPortBase;
+USHORT Revxx1; /* ??? */
+
+PDCB pLinkDCB;
+PDCB pDCBRunRobin;
+PDCB pActiveDCB;
+PDCB pDCB_free;
+PSRB pFreeSRB;
+PSRB pTmpSRB;
+USHORT SRBCount;
+USHORT AdapterIndex; /*; nth Adapter this driver */
+USHORT max_id;
+USHORT max_lun;
+
+UCHAR msgin123[4];
+UCHAR status;
+UCHAR AdaptSCSIID; /*; Adapter SCSI Target ID */
+UCHAR AdaptSCSILUN; /*; Adapter SCSI LUN */
+UCHAR DeviceCnt;
+UCHAR IRQLevel;
+UCHAR TagMaxNum;
+UCHAR ACBFlag;
+UCHAR Gmode2;
+UCHAR LUNchk;
+UCHAR scan_devices;
+UCHAR HostID_Bit;
+UCHAR Reserved1[1]; /*;for dword alignment */
+UCHAR DCBmap[MAX_SCSI_ID];
+DC390_DCB DCB_array[MAX_DEVICES]; /* +74h, Len=3E8 */
+DC390_SRB SRB_array[MAX_SRB_CNT]; /* +45Ch, Len= */
+DC390_SRB TmpSRB;
+};
+
+typedef struct _ACB DC390_ACB, *PACB;
+
+/*;-----------------------------------------------------------------------*/
+
+
+#define BIT31 0x80000000
+#define BIT30 0x40000000
+#define BIT29 0x20000000
+#define BIT28 0x10000000
+#define BIT27 0x08000000
+#define BIT26 0x04000000
+#define BIT25 0x02000000
+#define BIT24 0x01000000
+#define BIT23 0x00800000
+#define BIT22 0x00400000
+#define BIT21 0x00200000
+#define BIT20 0x00100000
+#define BIT19 0x00080000
+#define BIT18 0x00040000
+#define BIT17 0x00020000
+#define BIT16 0x00010000
+#define BIT15 0x00008000
+#define BIT14 0x00004000
+#define BIT13 0x00002000
+#define BIT12 0x00001000
+#define BIT11 0x00000800
+#define BIT10 0x00000400
+#define BIT9 0x00000200
+#define BIT8 0x00000100
+#define BIT7 0x00000080
+#define BIT6 0x00000040
+#define BIT5 0x00000020
+#define BIT4 0x00000010
+#define BIT3 0x00000008
+#define BIT2 0x00000004
+#define BIT1 0x00000002
+#define BIT0 0x00000001
+
+/*;---UnitCtrlFlag */
+#define UNIT_ALLOCATED BIT0
+#define UNIT_INFO_CHANGED BIT1
+#define FORMATING_MEDIA BIT2
+#define UNIT_RETRY BIT3
+
+/*;---UnitFlags */
+#define DASD_SUPPORT BIT0
+#define SCSI_SUPPORT BIT1
+#define ASPI_SUPPORT BIT2
+
+/*;----SRBState machine definition */
+#define SRB_FREE 0
+#define SRB_WAIT BIT0
+#define SRB_READY BIT1
+#define SRB_MSGOUT BIT2 /*;arbitration+msg_out 1st byte*/
+#define SRB_MSGIN BIT3
+#define SRB_MSGIN_MULTI BIT4
+#define SRB_COMMAND BIT5
+#define SRB_START_ BIT6 /*;arbitration+msg_out+command_out*/
+#define SRB_DISCONNECT BIT7
+#define SRB_DATA_XFER BIT8
+#define SRB_XFERPAD BIT9
+#define SRB_STATUS BIT10
+#define SRB_COMPLETED BIT11
+#define SRB_ABORT_SENT BIT12
+#define DO_SYNC_NEGO BIT13
+#define SRB_UNEXPECT_RESEL BIT14
+
+/*;---ACBFlag */
+#define RESET_DEV BIT0
+#define RESET_DETECT BIT1
+#define RESET_DONE BIT2
+
+/*;---DCBFlag */
+#define ABORT_DEV_ BIT0
+
+/*;---SRBstatus */
+#define SRB_OK BIT0
+#define ABORTION BIT1
+#define OVER_RUN BIT2
+#define UNDER_RUN BIT3
+#define PARITY_ERROR BIT4
+#define SRB_ERROR BIT5
+
+/*;---SRBFlag */
+#define DATAOUT BIT7
+#define DATAIN BIT6
+#define RESIDUAL_VALID BIT5
+#define ENABLE_TIMER BIT4
+#define RESET_DEV0 BIT2
+#define ABORT_DEV BIT1
+#define AUTO_REQSENSE BIT0
+
+/*;---Adapter status */
+#define H_STATUS_GOOD 0
+#define H_SEL_TIMEOUT 0x11
+#define H_OVER_UNDER_RUN 0x12
+#define H_UNEXP_BUS_FREE 0x13
+#define H_TARGET_PHASE_F 0x14
+#define H_INVALID_CCB_OP 0x16
+#define H_LINK_CCB_BAD 0x17
+#define H_BAD_TARGET_DIR 0x18
+#define H_DUPLICATE_CCB 0x19
+#define H_BAD_CCB_OR_SG 0x1A
+#define H_ABORT 0x0FF
+
+/*; SCSI Status byte codes*/
+#define SCSI_STAT_GOOD 0x0 /*; Good status */
+#define SCSI_STAT_CHECKCOND 0x02 /*; SCSI Check Condition */
+#define SCSI_STAT_CONDMET 0x04 /*; Condition Met */
+#define SCSI_STAT_BUSY 0x08 /*; Target busy status */
+#define SCSI_STAT_INTER 0x10 /*; Intermediate status */
+#define SCSI_STAT_INTERCONDMET 0x14 /*; Intermediate condition met */
+#define SCSI_STAT_RESCONFLICT 0x18 /*; Reservation conflict */
+#define SCSI_STAT_CMDTERM 0x22 /*; Command Terminated */
+#define SCSI_STAT_QUEUEFULL 0x28 /*; Queue Full */
+
+#define SCSI_STAT_UNEXP_BUS_F 0xFD /*; Unexpect Bus Free */
+#define SCSI_STAT_BUS_RST_DETECT 0xFE /*; Scsi Bus Reset detected */
+#define SCSI_STAT_SEL_TIMEOUT 0xFF /*; Selection Time out */
+
+/*;---Sync_Mode */
+#define SYNC_DISABLE 0
+#define SYNC_ENABLE BIT0
+#define SYNC_NEGO_DONE BIT1
+#define WIDE_ENABLE BIT2
+#define WIDE_NEGO_DONE BIT3
+#define EN_TAG_QUEUING BIT4
+#define EN_ATN_STOP BIT5
+
+#define SYNC_NEGO_OFFSET 15
+
+/*;---SCSI bus phase*/
+#define SCSI_DATA_OUT 0
+#define SCSI_DATA_IN 1
+#define SCSI_COMMAND 2
+#define SCSI_STATUS_ 3
+#define SCSI_NOP0 4
+#define SCSI_NOP1 5
+#define SCSI_MSG_OUT 6
+#define SCSI_MSG_IN 7
+
+/*;----SCSI MSG BYTE*/
+#define MSG_COMPLETE 0x00
+#define MSG_EXTENDED 0x01
+#define MSG_SAVE_PTR 0x02
+#define MSG_RESTORE_PTR 0x03
+#define MSG_DISCONNECT 0x04
+#define MSG_INITIATOR_ERROR 0x05
+#define MSG_ABORT 0x06
+#define MSG_REJECT_ 0x07
+#define MSG_NOP 0x08
+#define MSG_PARITY_ERROR 0x09
+#define MSG_LINK_CMD_COMPL 0x0A
+#define MSG_LINK_CMD_COMPL_FLG 0x0B
+#define MSG_BUS_RESET 0x0C
+#define MSG_ABORT_TAG 0x0D
+#define MSG_SIMPLE_QTAG 0x20
+#define MSG_HEAD_QTAG 0x21
+#define MSG_ORDER_QTAG 0x22
+#define MSG_IDENTIFY 0x80
+#define MSG_HOST_ID 0x0C0
+
+/*;----SCSI STATUS BYTE*/
+#define STATUS_GOOD 0x00
+#define CHECK_CONDITION_ 0x02
+#define STATUS_BUSY 0x08
+#define STATUS_INTERMEDIATE 0x10
+#define RESERVE_CONFLICT 0x18
+
+/* cmd->result */
+#define STATUS_MASK_ 0xFF
+#define MSG_MASK 0xFF00
+#define RETURN_MASK 0xFF0000
+
+/*
+** Inquiry Data format
+*/
+
+typedef struct _SCSIInqData { /* INQ */
+
+ UCHAR DevType; /* Periph Qualifier & Periph Dev Type*/
+ UCHAR RMB_TypeMod; /* rem media bit & Dev Type Modifier */
+ UCHAR Vers; /* ISO, ECMA, & ANSI versions */
+ UCHAR RDF; /* AEN, TRMIOP, & response data format*/
+ UCHAR AddLen; /* length of additional data */
+ UCHAR Res1; /* reserved */
+ UCHAR Res2; /* reserved */
+ UCHAR Flags; /* RelADr,Wbus32,Wbus16,Sync,etc. */
+ UCHAR VendorID[8]; /* Vendor Identification */
+ UCHAR ProductID[16]; /* Product Identification */
+ UCHAR ProductRev[4]; /* Product Revision */
+
+
+} SCSI_INQDATA, *PSCSI_INQDATA;
+
+
+/* Inquiry byte 0 masks */
+
+
+#define SCSI_DEVTYPE 0x1F /* Peripheral Device Type */
+#define SCSI_PERIPHQUAL 0xE0 /* Peripheral Qualifier */
+
+
+/* Inquiry byte 1 mask */
+
+#define SCSI_REMOVABLE_MEDIA 0x80 /* Removable Media bit (1=removable) */
+
+
+/* Peripheral Device Type definitions */
+
+#define SCSI_DASD 0x00 /* Direct-access Device */
+#define SCSI_SEQACESS 0x01 /* Sequential-access device */
+#define SCSI_PRINTER 0x02 /* Printer device */
+#define SCSI_PROCESSOR 0x03 /* Processor device */
+#define SCSI_WRITEONCE 0x04 /* Write-once device */
+#define SCSI_CDROM 0x05 /* CD-ROM device */
+#define SCSI_SCANNER 0x06 /* Scanner device */
+#define SCSI_OPTICAL 0x07 /* Optical memory device */
+#define SCSI_MEDCHGR 0x08 /* Medium changer device */
+#define SCSI_COMM 0x09 /* Communications device */
+#define SCSI_NODEV 0x1F /* Unknown or no device type */
+
+/*
+** Inquiry flag definitions (Inq data byte 7)
+*/
+
+#define SCSI_INQ_RELADR 0x80 /* device supports relative addressing*/
+#define SCSI_INQ_WBUS32 0x40 /* device supports 32 bit data xfers */
+#define SCSI_INQ_WBUS16 0x20 /* device supports 16 bit data xfers */
+#define SCSI_INQ_SYNC 0x10 /* device supports synchronous xfer */
+#define SCSI_INQ_LINKED 0x08 /* device supports linked commands */
+#define SCSI_INQ_CMDQUEUE 0x02 /* device supports command queueing */
+#define SCSI_INQ_SFTRE 0x01 /* device supports soft resets */
+
+
+/*
+;==========================================================
+; EEPROM byte offset
+;==========================================================
+*/
+typedef struct _EEprom
+{
+UCHAR EE_MODE1;
+UCHAR EE_SPEED;
+UCHAR xx1;
+UCHAR xx2;
+} EEprom, *PEEprom;
+
+#define EE_ADAPT_SCSI_ID 64
+#define EE_MODE2 65
+#define EE_DELAY 66
+#define EE_TAG_CMD_NUM 67
+
+/*; EE_MODE1 bits definition*/
+#define PARITY_CHK_ BIT0
+#define SYNC_NEGO_ BIT1
+#define EN_DISCONNECT_ BIT2
+#define SEND_START_ BIT3
+#define TAG_QUEUING_ BIT4
+
+/*; EE_MODE2 bits definition*/
+#define MORE2_DRV BIT0
+#define GREATER_1G BIT1
+#define RST_SCSI_BUS BIT2
+#define ACTIVE_NEGATION BIT3
+#define NO_SEEK BIT4
+#define LUN_CHECK BIT5
+
+#define ENABLE_CE 1
+#define DISABLE_CE 0
+#define EEPROM_READ 0x80
+
+/*
+;==========================================================
+; AMD 53C974 Registers bit Definition
+;==========================================================
+*/
+/*
+;====================
+; SCSI Register
+;====================
+*/
+
+/*; Command Reg.(+0CH) */
+#define DMA_COMMAND BIT7
+#define NOP_CMD 0
+#define CLEAR_FIFO_CMD 1
+#define RST_DEVICE_CMD 2
+#define RST_SCSI_BUS_CMD 3
+#define INFO_XFER_CMD 0x10
+#define INITIATOR_CMD_CMPLTE 0x11
+#define MSG_ACCEPTED_CMD 0x12
+#define XFER_PAD_BYTE 0x18
+#define SET_ATN_CMD 0x1A
+#define RESET_ATN_CMD 0x1B
+#define SELECT_W_ATN 0x42
+#define SEL_W_ATN_STOP 0x43
+#define EN_SEL_RESEL 0x44
+#define SEL_W_ATN2 0x46
+#define DATA_XFER_CMD INFO_XFER_CMD
+
+
+/*; SCSI Status Reg.(+10H) */
+#define INTERRUPT BIT7
+#define ILLEGAL_OP_ERR BIT6
+#define PARITY_ERR BIT5
+#define COUNT_2_ZERO BIT4
+#define GROUP_CODE_VALID BIT3
+#define SCSI_PHASE_MASK (BIT2+BIT1+BIT0)
+
+/*; Interrupt Status Reg.(+14H) */
+#define SCSI_RESET BIT7
+#define INVALID_CMD BIT6
+#define DISCONNECTED BIT5
+#define SERVICE_REQUEST BIT4
+#define SUCCESSFUL_OP BIT3
+#define RESELECTED BIT2
+#define SEL_ATTENTION BIT1
+#define SELECTED BIT0
+
+/*; Internal State Reg.(+18H) */
+#define SYNC_OFFSET_FLAG BIT3
+#define INTRN_STATE_MASK (BIT2+BIT1+BIT0)
+
+/*; Clock Factor Reg.(+24H) */
+#define CLK_FREQ_40MHZ 0
+#define CLK_FREQ_35MHZ (BIT2+BIT1+BIT0)
+#define CLK_FREQ_30MHZ (BIT2+BIT1)
+#define CLK_FREQ_25MHZ (BIT2+BIT0)
+#define CLK_FREQ_20MHZ BIT2
+#define CLK_FREQ_15MHZ (BIT1+BIT0)
+#define CLK_FREQ_10MHZ BIT1
+
+/*; Control Reg. 1(+20H) */
+#define EXTENDED_TIMING BIT7
+#define DIS_INT_ON_SCSI_RST BIT6
+#define PARITY_ERR_REPO BIT4
+#define SCSI_ID_ON_BUS (BIT2+BIT1+BIT0)
+
+/*; Control Reg. 2(+2CH) */
+#define EN_FEATURE BIT6
+#define EN_SCSI2_CMD BIT3
+
+/*; Control Reg. 3(+30H) */
+#define ID_MSG_CHECK BIT7
+#define EN_QTAG_MSG BIT6
+#define EN_GRP2_CMD BIT5
+#define FAST_SCSI BIT4 /* ;10MB/SEC */
+#define FAST_CLK BIT3 /* ;25 - 40 MHZ */
+
+/*; Control Reg. 4(+34H) */
+#define EATER_12NS 0
+#define EATER_25NS BIT7
+#define EATER_35NS BIT6
+#define EATER_0NS (BIT7+BIT6)
+#define NEGATE_REQACKDATA BIT2
+#define NEGATE_REQACK BIT3
+/*
+;====================
+; DMA Register
+;====================
+*/
+/*; DMA Command Reg.(+40H) */
+#define READ_DIRECTION BIT7
+#define WRITE_DIRECTION 0
+#define EN_DMA_INT BIT6
+#define MAP_TO_MDL BIT5
+#define DIAGNOSTIC BIT4
+#define DMA_IDLE_CMD 0
+#define DMA_BLAST_CMD BIT0
+#define DMA_ABORT_CMD BIT1
+#define DMA_START_CMD (BIT1+BIT0)
+
+/*; DMA Status Reg.(+54H) */
+#define PCI_MS_ABORT BIT6
+#define BLAST_COMPLETE BIT5
+#define SCSI_INTERRUPT BIT4
+#define DMA_XFER_DONE BIT3
+#define DMA_XFER_ABORT BIT2
+#define DMA_XFER_ERROR BIT1
+#define POWER_DOWN BIT0
+
+/*
+; DMA SCSI Bus and Ctrl.(+70H)
+;EN_INT_ON_PCI_ABORT
+*/
+
+/*
+;==========================================================
+; SCSI Chip register address offset
+;==========================================================
+*/
+#define CtcReg_Low 0x00
+#define CtcReg_Mid 0x04
+#define ScsiFifo 0x08
+#define ScsiCmd 0x0C
+#define Scsi_Status 0x10
+#define INT_Status 0x14
+#define Sync_Period 0x18
+#define Sync_Offset 0x1C
+#define CtrlReg1 0x20
+#define Clk_Factor 0x24
+#define CtrlReg2 0x2C
+#define CtrlReg3 0x30
+#define CtrlReg4 0x34
+#define CtcReg_High 0x38
+#define DMA_Cmd 0x40
+#define DMA_XferCnt 0x44
+#define DMA_XferAddr 0x48
+#define DMA_Wk_ByteCntr 0x4C
+#define DMA_Wk_AddrCntr 0x50
+#define DMA_Status 0x54
+#define DMA_MDL_Addr 0x58
+#define DMA_Wk_MDL_Cntr 0x5C
+#define DMA_ScsiBusCtrl 0x70
+
+#define StcReg_Low CtcReg_Low
+#define StcReg_Mid CtcReg_Mid
+#define Scsi_Dest_ID Scsi_Status
+#define Scsi_TimeOut INT_Status
+#define Intern_State Sync_Period
+#define Current_Fifo Sync_Offset
+#define StcReg_High CtcReg_High
+
+#define am_target Scsi_Status
+#define am_timeout INT_Status
+#define am_seq_step Sync_Period
+#define am_fifo_count Sync_Offset
+
+
+#define DC390_read8(address) \
+ inb(DC390_ioport + (address)))
+
+#define DC390_read16(address) \
+ inw(DC390_ioport + (address)))
+
+#define DC390_read32(address) \
+ inl(DC390_ioport + (address)))
+
+#define DC390_write8(address,value) \
+ outb((value), DC390_ioport + (address)))
+
+#define DC390_write16(address,value) \
+ outw((value), DC390_ioport + (address)))
+
+#define DC390_write32(address,value) \
+ outl((value), DC390_ioport + (address)))
+
+
+/* Configuration method #1 */
+#define PCI_CFG1_ADDRESS_REG 0xcf8
+#define PCI_CFG1_DATA_REG 0xcfc
+#define PCI_CFG1_ENABLE 0x80000000
+#define PCI_CFG1_TUPPLE(bus, device, function, register) \
+ (PCI_CFG1_ENABLE | (((bus) << 16) & 0xff0000) | \
+ (((device) << 11) & 0xf800) | (((function) << 8) & 0x700)| \
+ (((register) << 2) & 0xfc))
+
+/* Configuration method #2 */
+#define PCI_CFG2_ENABLE_REG 0xcf8
+#define PCI_CFG2_FORWARD_REG 0xcfa
+#define PCI_CFG2_ENABLE 0x0f0
+#define PCI_CFG2_TUPPLE(function) \
+ (PCI_CFG2_ENABLE | (((function) << 1) & 0xe))
+
+
+#endif /* TMSCSIM_H */
diff --git a/linux/src/drivers/scsi/tmscsiw.c b/linux/src/drivers/scsi/tmscsiw.c
new file mode 100644
index 00000000..d32b9fc0
--- /dev/null
+++ b/linux/src/drivers/scsi/tmscsiw.c
@@ -0,0 +1,2096 @@
+/***********************************************************************
+ * FILE NAME : TMSCSIW.C *
+ * BY : C.L. Huang (ching@tekram.com.tw) *
+ * Description: Device Driver for Tekram DC-390W/U/F (T) PCI SCSI *
+ * Bus Master Host Adapter *
+ * (C)Copyright 1995-1996 Tekram Technology Co., Ltd. *
+ ***********************************************************************/
+/* Minor enhancements and bugfixes by *
+ * Kurt Garloff <K.Garloff@ping.de> *
+ ***********************************************************************/
+/* HISTORY: *
+ * *
+ * REV# DATE NAME DESCRIPTION *
+ * 1.00 04/03/96 CLH First release *
+ * 1.01 04/11/96 CLH Maximum support up to 4 Adapters, *
+ * support KV 1_3_85 *
+ * 1.02 04/26/96 CLH fixed bug about EEpromBuf when >1 HA *
+ * 1.03 06/12/96 CLH fixed bug of Media Change for Removable *
+ * Device, scan all LUN. Support Pre2.0.10 *
+ * 1.04 06/18/96 CLH fixed bug of Command timeout .... *
+ * 1.05 10/04/96 CLH Updating for support KV 2.0.0, 2.0.20 *
+ * 1.06 10/30/96 KG Fixed bug in DC390W_abort(), module *
+ * support, added tmscsiw_proc_info() *
+ * 1.07 11/09/96 KG Fixed bug in tmscsiw_proc_info() *
+ * 1.08 11/18/96 CLH/KG ditto, null ptr in DC390W_Disconnect() *
+ * 1.09 11/30/96 KG Fixed bug in CheckEEpromCheckSum(), *
+ * add register the allocated IO space *
+ * 1.10 12/05/96 CLH Modify in tmscsiw_proc_info() and add *
+ * in DC390W_initAdapter() for 53C875 *
+ * Rev. F with double clock. *
+ * 1.11 02/04/97 CLH Fixed bug of Formatting a partition that*
+ * across 1GB boundary, with bad sector *
+ * checking. *
+ * 1.12 02/17/97 CLH Fixed bug in CheckEEpromCheckSum() *
+ ***********************************************************************/
+
+
+#define DC390W_DEBUG
+/* #define CHK_UNDER_RUN */
+
+#define SCSI_MALLOC
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/config.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < 66354 /* 1.3.50 */
+#include "../block/blk.h"
+#else
+#include <linux/blk.h>
+#endif
+
+#include "scsi.h"
+#include "hosts.h"
+#include "tmscsiw.h"
+#include "constants.h"
+#include "sd.h"
+#include "scripts.h"
+#include <linux/stat.h>
+
+#include "dc390w.h"
+
+#ifndef VERSION_ELF_1_2_13
+struct proc_dir_entry proc_scsi_tmscsiw ={
+ PROC_SCSI_DC390WUF, 7 ,"tmscsiw",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+ };
+#endif
+
+static void DC390W_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void PrepareSG( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void DoingSRB_Done( PACB pACB );
+static void ExceptionHandler(ULONG wlval, PACB pACB, PDCB pDCB);
+static void ParityError( PACB pACB, PDCB pDCB );
+static void PhaseMismatch( PACB pACB );
+static void DC390W_ScsiRstDetect( PACB pACB );
+static void DC390W_ResetSCSIBus( PACB pACB );
+static void DC390W_ResetSCSIBus2( PACB pACB );
+static void AdjustTemp( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void SetXferRate( PACB pACB, PDCB pDCB );
+static void DataIOcommon( PACB pACB, ULONG Swlval, ULONG Cwlval );
+static void SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB );
+static void RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB );
+
+static void DC390W_CmdCompleted( PACB pACB );
+static void DC390W_Reselected( PACB pACB );
+static void DC390W_Reselected1( PACB pACB );
+static void DC390W_ReselectedT( PACB pACB );
+static void DC390W_Disconnected( PACB pACB );
+static void DC390W_MessageExtnd( PACB pACB );
+static void DC390W_Signal( PACB pACB );
+static void DC390W_UnknownMsg( PACB pACB );
+static void DC390W_MessageOut( PACB pACB );
+static void DC390W_FatalError( PACB pACB );
+static void DC390W_MessageSync( PACB pACB );
+static void DC390W_MessageWide( PACB pACB );
+static void DC390W_RestorePtr( PACB pACB );
+static void DC390W_MsgReject( PACB pACB );
+static void DC390W_Debug( PACB pACB );
+static void DC390W_download_script (struct Scsi_Host *host);
+
+int DC390W_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index);
+void DC390W_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd );
+void MyDelay( void );
+void EnDisableCE( UCHAR Flag, USHORT scsiIOPort );
+void EEpromOutDI( USHORT Carry, USHORT scsiIOPort );
+void EEpromPrepare( UCHAR EEpromCmd, USHORT scsiIOPort );
+void ReadEEprom( PUCHAR EEpromBuf, USHORT scsiIOPort );
+UCHAR EEpromInDo(USHORT scsiIOPort);
+USHORT EEpromGetData(USHORT scsiIOPort);
+USHORT CheckEEpromCheckSum( PUCHAR EEpromBuf, USHORT scsiIOPort);
+
+#ifdef MODULE
+static int DC390W_release(struct Scsi_Host *host);
+static int DC390W_shutdown (struct Scsi_Host *host);
+#endif
+
+
+static ULONG jmp_table16;
+static ULONG jmp_din16;
+static ULONG jmp_dout16;
+static PSHT pSHT_start = NULL;
+static PSH pSH_start = NULL;
+static PSH pSH_current = NULL;
+static PACB pACB_start= NULL;
+static PACB pACB_current = NULL;
+static PDCB pPrevDCB = NULL;
+static USHORT adapterCnt = 0;
+static USHORT InitialTime = 0;
+static USHORT CurrDCBscntl3 = 0;
+static UCHAR pad_buffer[128];
+
+static PVOID IntVector[]={
+ DC390W_CmdCompleted,
+ DC390W_Reselected,
+ DC390W_Reselected1,
+ DC390W_ReselectedT,
+ DC390W_Disconnected,
+ DC390W_MessageExtnd,
+ DC390W_Signal,
+ DC390W_UnknownMsg,
+ DC390W_MessageOut,
+ DC390W_FatalError,
+ DC390W_MessageSync,
+ DC390W_MessageWide,
+ DC390W_RestorePtr,
+ DC390W_MsgReject,
+ DC390W_Debug,
+ DC390W_FatalError
+ };
+
+UCHAR eepromBuf[MAX_ADAPTER_NUM][128];
+
+UCHAR clock_period[12] = {25, 31, 37, 43, 50, 62, 75, 125, 12, 15, 18, 21};
+UCHAR baddevname[2][28] ={
+ "SEAGATE ST3390N 9546",
+ "SEAGATE ST3390N ??? 0399"};
+
+#define BADDEVCNT 2
+
+/***********************************************************************
+ *
+ *
+ *
+ **********************************************************************/
+static void
+QLinkcmd( PSCSICMD cmd, PDCB pDCB )
+{
+ ULONG flags;
+ PSCSICMD pcmd;
+
+ save_flags(flags);
+ cli();
+
+ if( !pDCB->QIORBCnt )
+ {
+ pDCB->pQIORBhead = cmd;
+ pDCB->pQIORBtail = cmd;
+ pDCB->QIORBCnt++;
+ cmd->next = NULL;
+ }
+ else
+ {
+ pcmd = pDCB->pQIORBtail;
+ pcmd->next = cmd;
+ pDCB->pQIORBtail = cmd;
+ pDCB->QIORBCnt++;
+ cmd->next = NULL;
+ }
+
+ restore_flags(flags);
+}
+
+
+static PSCSICMD
+Getcmd( PDCB pDCB )
+{
+ ULONG flags;
+ PSCSICMD pcmd;
+
+ save_flags(flags);
+ cli();
+
+ pcmd = pDCB->pQIORBhead;
+ pDCB->pQIORBhead = pcmd->next;
+ pcmd->next = NULL;
+ pDCB->QIORBCnt--;
+
+ restore_flags(flags);
+ return( pcmd );
+}
+
+
+static PSRB
+GetSRB( PACB pACB )
+{
+ ULONG flags;
+ PSRB pSRB;
+
+ save_flags(flags);
+ cli();
+
+ pSRB = pACB->pFreeSRB;
+ if( pSRB )
+ {
+ pACB->pFreeSRB = pSRB->pNextSRB;
+ pSRB->pNextSRB = NULL;
+ }
+ restore_flags(flags);
+ return( pSRB );
+}
+
+
+static void
+RewaitSRB( PDCB pDCB, PSRB pSRB )
+{
+ PSRB psrb1;
+ ULONG flags;
+ UCHAR bval;
+
+ save_flags(flags);
+ cli();
+ pDCB->GoingSRBCnt--;
+ psrb1 = pDCB->pGoingSRB;
+ if( pSRB == psrb1 )
+ {
+ pDCB->pGoingSRB = psrb1->pNextSRB;
+ }
+ else
+ {
+ while( pSRB != psrb1->pNextSRB )
+ psrb1 = psrb1->pNextSRB;
+ psrb1->pNextSRB = pSRB->pNextSRB;
+ if( pSRB == pDCB->pGoingLast )
+ pDCB->pGoingLast = psrb1;
+ }
+ if( (psrb1 = pDCB->pWaitingSRB) )
+ {
+ pSRB->pNextSRB = psrb1;
+ pDCB->pWaitingSRB = pSRB;
+ }
+ else
+ {
+ pSRB->pNextSRB = NULL;
+ pDCB->pWaitingSRB = pSRB;
+ pDCB->pWaitLast = pSRB;
+ }
+
+ bval = pSRB->TagNumber;
+ pDCB->TagMask &= (~(1 << bval)); /* Free TAG number */
+ restore_flags(flags);
+}
+
+
+static void
+DoWaitingSRB( PACB pACB )
+{
+ ULONG flags;
+ PDCB ptr, ptr1;
+ PSRB pSRB;
+
+ save_flags(flags);
+ cli();
+
+ if( !(pACB->pActiveDCB) && !(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) )
+ {
+ ptr = pACB->pDCBRunRobin;
+ if( !ptr )
+ {
+ ptr = pACB->pLinkDCB;
+ pACB->pDCBRunRobin = ptr;
+ }
+ ptr1 = ptr;
+ for( ;ptr1; )
+ {
+ pACB->pDCBRunRobin = ptr1->pNextDCB;
+ if( !( ptr1->MaxCommand > ptr1->GoingSRBCnt ) ||
+ !( pSRB = ptr1->pWaitingSRB ) )
+ {
+ if(pACB->pDCBRunRobin == ptr)
+ break;
+ ptr1 = ptr1->pNextDCB;
+ }
+ else
+ {
+ DC390W_StartSCSI(pACB, ptr1, pSRB);
+ ptr1->GoingSRBCnt++;
+ if( ptr1->pWaitLast == pSRB )
+ {
+ ptr1->pWaitingSRB = NULL;
+ ptr1->pWaitLast = NULL;
+ }
+ else
+ {
+ ptr1->pWaitingSRB = pSRB->pNextSRB;
+ }
+ pSRB->pNextSRB = NULL;
+
+ if( ptr1->pGoingSRB )
+ ptr1->pGoingLast->pNextSRB = pSRB;
+ else
+ ptr1->pGoingSRB = pSRB;
+ ptr1->pGoingLast = pSRB;
+
+ break;
+ }
+ }
+ }
+ restore_flags(flags);
+ return;
+}
+
+
+static void
+SRBwaiting( PDCB pDCB, PSRB pSRB)
+{
+ if( pDCB->pWaitingSRB )
+ {
+ pDCB->pWaitLast->pNextSRB = pSRB;
+ pDCB->pWaitLast = pSRB;
+ pSRB->pNextSRB = NULL;
+ }
+ else
+ {
+ pDCB->pWaitingSRB = pSRB;
+ pDCB->pWaitLast = pSRB;
+ }
+}
+
+
+static void
+SendSRB( PSCSICMD pcmd, PACB pACB, PSRB pSRB )
+{
+ ULONG flags;
+ PDCB pDCB;
+
+ save_flags(flags);
+ cli();
+
+ pDCB = pSRB->pSRBDCB;
+ PrepareSG( pACB, pDCB, pSRB );
+ if( !(pDCB->MaxCommand > pDCB->GoingSRBCnt) || (pACB->pActiveDCB) ||
+ (pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV)) )
+ {
+ SRBwaiting(pDCB, pSRB);
+ goto SND_EXIT;
+ }
+
+ if( pDCB->pWaitingSRB )
+ {
+ SRBwaiting(pDCB, pSRB);
+/* pSRB = GetWaitingSRB(pDCB); */
+ pSRB = pDCB->pWaitingSRB;
+ pDCB->pWaitingSRB = pSRB->pNextSRB;
+ pSRB->pNextSRB = NULL;
+ }
+
+ DC390W_StartSCSI(pACB, pDCB, pSRB);
+ pDCB->GoingSRBCnt++;
+ if( pDCB->pGoingSRB )
+ {
+ pDCB->pGoingLast->pNextSRB = pSRB;
+ pDCB->pGoingLast = pSRB;
+ }
+ else
+ {
+ pDCB->pGoingSRB = pSRB;
+ pDCB->pGoingLast = pSRB;
+ }
+
+SND_EXIT:
+ restore_flags(flags);
+ return;
+}
+
+
+/***********************************************************************
+ * Function : static int DC390W_queue_command (Scsi_Cmnd *cmd,
+ * void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ * a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ ***********************************************************************/
+
+int
+DC390W_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
+{
+ USHORT ioport, i;
+ Scsi_Cmnd *pcmd;
+ struct Scsi_Host *psh;
+ PACB pACB;
+ PDCB pDCB;
+ PSRB pSRB;
+ ULONG flags;
+ PUCHAR ptr,ptr1;
+
+ psh = cmd->host;
+ pACB = (PACB ) psh->hostdata;
+ ioport = pACB->IOPortBase;
+
+#ifdef DC390W_DEBUG0
+ printk("Cmd=%x,",cmd->cmnd[0]);
+#endif
+
+ if( (pACB->scan_devices == END_SCAN) && (cmd->cmnd[0] != INQUIRY) )
+ {
+ pACB->scan_devices = 0;
+ pPrevDCB->pNextDCB = pACB->pLinkDCB;
+ }
+ else if( (pACB->scan_devices) && (cmd->cmnd[0] == 8) )
+ {
+ pACB->scan_devices = 0;
+ pPrevDCB->pNextDCB = pACB->pLinkDCB;
+ }
+
+ if ( ( cmd->target > pACB->max_id ) || (cmd->lun > pACB->max_lun) )
+ {
+/* printk("DC390W: Ignore target %d lun %d\n",
+ cmd->target, cmd->lun); */
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ return( 0 );
+ }
+
+ if( (pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
+ {
+ if( pACB->DeviceCnt < MAX_DEVICES )
+ {
+ pACB->DCBmap[cmd->target] |= (1 << cmd->lun);
+ pDCB = pACB->pDCB_free;
+#ifdef DC390W_DEBUG0
+ printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target);
+#endif
+ DC390W_initDCB( pACB, pDCB, cmd );
+ }
+ else /* ???? */
+ {
+/* printk("DC390W: Ignore target %d lun %d\n",
+ cmd->target, cmd->lun); */
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ return(0);
+ }
+ }
+ else if( !(pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) )
+ {
+/* printk("DC390W: Ignore target %d lun %d\n",
+ cmd->target, cmd->lun); */
+ cmd->result = (DID_BAD_TARGET << 16);
+ done(cmd);
+ return(0);
+ }
+ else
+ {
+ pDCB = pACB->pLinkDCB;
+ while( (pDCB->UnitSCSIID != cmd->target) ||
+ (pDCB->UnitSCSILUN != cmd->lun) )
+ {
+ pDCB = pDCB->pNextDCB;
+ }
+#ifdef DC390W_DEBUG0
+ printk("pDCB=%8x,ID=%2x,Scan=%1x", (UINT) pDCB, cmd->target,
+ pACB->scan_devices);
+#endif
+ }
+
+ cmd->scsi_done = done;
+ cmd->result = 0;
+
+ save_flags(flags);
+ cli();
+
+ if( pDCB->QIORBCnt )
+ {
+ QLinkcmd( cmd, pDCB );
+ pcmd = Getcmd( pDCB );
+ }
+ else
+ pcmd = cmd;
+
+ pSRB = GetSRB( pACB );
+
+ if( !pSRB )
+ {
+ QLinkcmd( pcmd, pDCB );
+ restore_flags(flags);
+ return(0);
+ }
+
+/* BuildSRB(pSRB); */
+
+ pSRB->pSRBDCB = pDCB;
+ pSRB->pcmd = pcmd;
+ ptr = (PUCHAR) pSRB->CmdBlock;
+ ptr1 = (PUCHAR) pcmd->cmnd;
+ (UCHAR) pSRB->__command[0] = pcmd->cmd_len;
+ for(i=0; i< pcmd->cmd_len; i++)
+ {
+ *ptr = *ptr1;
+ ptr++;
+ ptr1++;
+ }
+ if( pcmd->use_sg )
+ {
+ pSRB->SGcount = (UCHAR) pcmd->use_sg;
+ pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+ }
+ else if( pcmd->request_buffer )
+ {
+ pSRB->SGcount = 1;
+ pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+ pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+ pSRB->Segmentx.length = pcmd->request_bufflen;
+ }
+ else
+ pSRB->SGcount = 0;
+
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ pSRB->MsgCnt = 0;
+ if( pDCB->DevType != TYPE_TAPE )
+ pSRB->RetryCnt = 1;
+ else
+ pSRB->RetryCnt = 0;
+ pSRB->SRBStatus = 0;
+ pSRB->SRBFlag = 0;
+ pSRB->ScratchABuf = 0;
+ pSRB->SRBState = 0;
+ pSRB->RemainSegPtr = 0;
+ pSRB->XferredLen = 0;
+ SendSRB( pcmd, pACB, pSRB );
+
+ restore_flags(flags);
+ return(0);
+}
+
+
+static void
+DoNextCmd( PACB pACB, PDCB pDCB )
+{
+ Scsi_Cmnd *pcmd;
+ PSRB pSRB;
+ ULONG flags;
+ PUCHAR ptr,ptr1;
+ USHORT i;
+
+
+ if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) )
+ return;
+ save_flags(flags);
+ cli();
+
+ pcmd = Getcmd( pDCB );
+ pSRB = GetSRB( pACB );
+ if( !pSRB )
+ {
+ QLinkcmd( pcmd, pDCB );
+ restore_flags(flags);
+ return;
+ }
+
+ pSRB->pSRBDCB = pDCB;
+ pSRB->pcmd = pcmd;
+ ptr = (PUCHAR) pSRB->CmdBlock;
+ ptr1 = (PUCHAR) pcmd->cmnd;
+ (UCHAR) pSRB->__command[0] = pcmd->cmd_len;
+ for(i=0; i< pcmd->cmd_len; i++)
+ {
+ *ptr = *ptr1;
+ ptr++;
+ ptr1++;
+ }
+ if( pcmd->use_sg )
+ {
+ pSRB->SGcount = (UCHAR) pcmd->use_sg;
+ pSRB->pSegmentList = (PSGL) pcmd->request_buffer;
+ }
+ else if( pcmd->request_buffer )
+ {
+ pSRB->SGcount = 1;
+ pSRB->pSegmentList = (PSGL) &pSRB->Segmentx;
+ pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer;
+ pSRB->Segmentx.length = pcmd->request_bufflen;
+ }
+ else
+ pSRB->SGcount = 0;
+
+ pSRB->AdaptStatus = 0;
+ pSRB->TargetStatus = 0;
+ pSRB->MsgCnt = 0;
+ if( pDCB->DevType != TYPE_TAPE )
+ pSRB->RetryCnt = 1;
+ else
+ pSRB->RetryCnt = 0;
+ pSRB->SRBStatus = 0;
+ pSRB->SRBFlag = 0;
+ pSRB->ScratchABuf = 0;
+ pSRB->SRBState = 0;
+ SendSRB( pcmd, pACB, pSRB );
+
+ restore_flags(flags);
+ return;
+}
+
+
+/***********************************************************************
+ * Function:
+ * DC390W_bios_param
+ *
+ * Description:
+ * Return the disk geometry for the given SCSI device.
+ ***********************************************************************/
+#ifdef VERSION_ELF_1_2_13
+int DC390W_bios_param(Disk *disk, int devno, int geom[])
+#else
+int DC390W_bios_param(Disk *disk, kdev_t devno, int geom[])
+#endif
+{
+ int heads, sectors, cylinders;
+ PACB pACB;
+
+ pACB = (PACB) disk->device->host->hostdata;
+ heads = 64;
+ sectors = 32;
+ cylinders = disk->capacity / (heads * sectors);
+
+ if ( (pACB->Gmode2 & GREATER_1G) && (cylinders > 1024) )
+ {
+ heads = 255;
+ sectors = 63;
+ cylinders = disk->capacity / (heads * sectors);
+ }
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ return (0);
+}
+
+
+/***********************************************************************
+ * Function : int DC390W_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : Abort an errant SCSI command
+ *
+ * Inputs : cmd - command to abort
+ *
+ * Returns : 0 on success, -1 on failure.
+ ***********************************************************************/
+
+int
+DC390W_abort (Scsi_Cmnd *cmd)
+{
+ ULONG flags;
+ PACB pACB;
+ PDCB pDCB, pdcb;
+ PSRB pSRB, psrb;
+ USHORT count, i;
+ PSCSICMD pcmd, pcmd1;
+ int status;
+
+
+#ifdef DC390W_DEBUG0
+ printk("DC390W : Abort Cmd.");
+#endif
+
+ save_flags(flags);
+ cli();
+
+ pACB = (PACB) cmd->host->hostdata;
+ pDCB = pACB->pLinkDCB;
+ pdcb = pDCB;
+ while( (pDCB->UnitSCSIID != cmd->target) ||
+ (pDCB->UnitSCSILUN != cmd->lun) )
+ {
+ pDCB = pDCB->pNextDCB;
+ if( pDCB == pdcb )
+ goto NOT_RUN;
+ }
+
+ if( pDCB->QIORBCnt )
+ {
+ pcmd = pDCB->pQIORBhead;
+ if( pcmd == cmd )
+ {
+ pDCB->pQIORBhead = pcmd->next;
+ pcmd->next = NULL;
+ pDCB->QIORBCnt--;
+ status = SCSI_ABORT_SUCCESS;
+ goto ABO_X;
+ }
+ for( count = pDCB->QIORBCnt, i=0; i<count-1; i++)
+ {
+ if( pcmd->next == cmd )
+ {
+ pcmd1 = pcmd->next;
+ pcmd->next = pcmd1->next;
+ pcmd1->next = NULL;
+ pDCB->QIORBCnt--;
+ status = SCSI_ABORT_SUCCESS;
+ goto ABO_X;
+ }
+ else
+ {
+ pcmd = pcmd->next;
+ }
+ }
+ }
+
+ pSRB = pDCB->pWaitingSRB;
+ if( !pSRB )
+ goto ON_GOING;
+ if( pSRB->pcmd == cmd )
+ {
+ pDCB->pWaitingSRB = pSRB->pNextSRB;
+ goto IN_WAIT;
+ }
+ else
+ {
+ psrb = pSRB;
+ if( !(psrb->pNextSRB) )
+ goto ON_GOING;
+ while( psrb->pNextSRB->pcmd != cmd )
+ {
+ psrb = psrb->pNextSRB;
+ if( !(psrb->pNextSRB) )
+ goto ON_GOING;
+ }
+ pSRB = psrb->pNextSRB;
+ psrb->pNextSRB = pSRB->pNextSRB;
+ if( pSRB == pDCB->pWaitLast )
+ pDCB->pWaitLast = psrb;
+IN_WAIT:
+ pSRB->pNextSRB = pACB->pFreeSRB;
+ pACB->pFreeSRB = pSRB;
+ cmd->next = NULL;
+ status = SCSI_ABORT_SUCCESS;
+ goto ABO_X;
+ }
+
+ON_GOING:
+ pSRB = pDCB->pGoingSRB;
+ for( count = pDCB->GoingSRBCnt, i=0; i<count; i++)
+ {
+ if( pSRB->pcmd != cmd )
+ pSRB = pSRB->pNextSRB;
+ else
+ {
+ if( (pACB->pActiveDCB == pDCB) && (pDCB->pActiveSRB == pSRB) )
+ {
+ status = SCSI_ABORT_BUSY;
+ goto ABO_X;
+ }
+ else
+ {
+ status = SCSI_ABORT_SNOOZE;
+ goto ABO_X;
+ }
+ }
+ }
+
+NOT_RUN:
+ status = SCSI_ABORT_NOT_RUNNING;
+
+ABO_X:
+ cmd->result = DID_ABORT << 16;
+ cmd->scsi_done(cmd);
+ restore_flags(flags);
+ return( status );
+}
+
+
+static void
+ResetDevParam( PACB pACB )
+{
+ PDCB pDCB, pdcb;
+
+ pDCB = pACB->pLinkDCB;
+ if( pDCB == NULL )
+ return;
+ pdcb = pDCB;
+ do
+ {
+ if( pACB->AdaptType == DC390W )
+ pdcb->DCBscntl3 = SYNC_CLK_F2+ASYNC_CLK_F2;
+ else
+ pdcb->DCBscntl3 = SYNC_CLK_F4+ASYNC_CLK_F4;
+ pdcb->DCBsxfer = 0;
+ pdcb = pdcb->pNextDCB;
+ }
+ while( pdcb != pDCB );
+}
+
+
+static void
+RecoverSRB( PACB pACB )
+{
+ PDCB pDCB, pdcb;
+ PSRB psrb, psrb2;
+ USHORT cnt, i;
+
+ pDCB = pACB->pLinkDCB;
+ if( pDCB == NULL )
+ return;
+ pdcb = pDCB;
+ do
+ {
+ cnt = pdcb->GoingSRBCnt;
+ psrb = pdcb->pGoingSRB;
+ for (i=0; i<cnt; i++)
+ {
+ PrepareSG( pACB, pdcb, psrb );
+ psrb2 = psrb;
+ psrb = psrb->pNextSRB;
+/* RewaitSRB( pDCB, psrb ); */
+ if( pdcb->pWaitingSRB )
+ {
+ psrb2->pNextSRB = pdcb->pWaitingSRB;
+ pdcb->pWaitingSRB = psrb2;
+ }
+ else
+ {
+ pdcb->pWaitingSRB = psrb2;
+ pdcb->pWaitLast = psrb2;
+ psrb2->pNextSRB = NULL;
+ }
+ }
+ pdcb->GoingSRBCnt = 0;
+ pdcb->pGoingSRB = NULL;
+ pdcb->TagMask = 0;
+ pdcb = pdcb->pNextDCB;
+ }
+ while( pdcb != pDCB );
+}
+
+
+/***********************************************************************
+ * Function : int DC390W_reset (Scsi_Cmnd *cmd, ...)
+ *
+ * Purpose : perform a hard reset on the SCSI bus( and NCR chip).
+ *
+ * Inputs : cmd - command which caused the SCSI RESET
+ *
+ * Returns : 0 on success.
+ ***********************************************************************/
+
+#ifdef VERSION_2_0_0
+int DC390W_reset(Scsi_Cmnd *cmd, unsigned int resetFlags)
+#else
+int DC390W_reset (Scsi_Cmnd *cmd)
+#endif
+{
+ USHORT ioport;
+ unsigned long flags;
+ PACB pACB;
+ ULONG wlval;
+ UCHAR bval;
+ USHORT wval;
+ USHORT i;
+
+
+#ifdef DC390W_DEBUG0
+ printk("DC390W : Reset Cmd0,");
+#endif
+
+ pACB = (PACB ) cmd->host->hostdata;
+ ioport = pACB->IOPortBase;
+ save_flags(flags);
+ cli();
+ bval = inb(ioport+DCNTL);
+ bval |= IRQ_DISABLE;
+ outb(bval,ioport+DCNTL); /* disable interrupt */
+ DC390W_ResetSCSIBus( pACB );
+ for( i=0; i<500; i++ )
+ udelay(1000);
+ for(;;)
+ {
+ bval = inb(ioport+ISTAT);
+ if( bval & SCSI_INT_PENDING )
+ {
+ wval = inw( ioport+SIST0 );
+ if( wval & (SCSI_RESET+SCSI_GERROR) )
+ break;
+ }
+ if(bval & DMA_INT_PENDING)
+ {
+ bval = inb(ioport+DSTAT);
+ if(bval & ABORT_)
+ {
+ wval = inw( ioport+SIST0 );
+ break;
+ }
+ }
+ }
+ bval = inb(ioport+DCNTL);
+ bval &= ~IRQ_DISABLE;
+ outb(bval,ioport+DCNTL); /* re-enable interrupt */
+
+ ioport = pACB->IOPortBase;
+ bval = inb(ioport+STEST3);
+ bval |= CLR_SCSI_FIFO;
+ outb(bval,ioport+STEST3);
+ bval = CLR_DMA_FIFO;
+ outb(bval,ioport+CTEST3);
+ ResetDevParam( pACB );
+ DoingSRB_Done( pACB );
+ pACB->pActiveDCB = NULL;
+ wlval = pACB->jmp_reselect;
+ outl(wlval,(ioport+DSP));
+
+ pACB->ACBFlag = 0;
+ DoWaitingSRB( pACB );
+ restore_flags(flags);
+ return( SCSI_RESET_SUCCESS );
+}
+
+
+#include "scsiio.c"
+
+
+/***********************************************************************
+ * Function : static void DC390W_initDCB
+ *
+ * Purpose : initialize the internal structures for a given DCB
+ *
+ * Inputs : cmd - pointer to this scsi cmd request block structure
+ *
+ ***********************************************************************/
+void DC390W_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd )
+{
+ PEEprom prom;
+ UCHAR bval;
+ USHORT index;
+
+ if( pACB->DeviceCnt == 0 )
+ {
+ pACB->pLinkDCB = pDCB;
+ pACB->pDCBRunRobin = pDCB;
+ pDCB->pNextDCB = pDCB;
+ pPrevDCB = pDCB;
+ }
+ else
+ pPrevDCB->pNextDCB = pDCB;
+
+ pDCB->pDCBACB = pACB;
+ pDCB->QIORBCnt = 0;
+ pDCB->DCBselect = 0;
+ pDCB->DCBsxfer = 0;
+ pDCB->DCBsdid = cmd->target;
+ pDCB->UnitSCSIID = cmd->target;
+ pDCB->UnitSCSILUN = cmd->lun;
+ pDCB->pWaitingSRB = NULL;
+ pDCB->GoingSRBCnt = 0;
+ pDCB->TagMask = 0;
+
+ pDCB->MaxCommand = 1;
+ pDCB->AdaptIndex = pACB->AdapterIndex;
+ index = pACB->AdapterIndex;
+
+ prom = (PEEprom) &eepromBuf[index][cmd->target << 2];
+ pDCB->DevMode = prom->EE_MODE1;
+ pDCB->NegoPeriod = clock_period[prom->EE_SPEED];
+
+ if( pACB->AdaptType == DC390W )
+ pDCB->DCBscntl3 = SYNC_CLK_F2+ASYNC_CLK_F2;
+ else
+ pDCB->DCBscntl3 = SYNC_CLK_F4+ASYNC_CLK_F4;
+
+ if( pDCB->DevMode & PARITY_CHK_ )
+ pDCB->DCBscntl0 = EN_PARITY_CHK+SATN_IF_PARITY_ERR+FULL_ARBITRATION;
+ else
+ pDCB->DCBscntl0 = FULL_ARBITRATION;
+
+ pDCB->AdpMode = eepromBuf[index][EE_MODE2];
+
+ if( pDCB->DevMode & EN_DISCONNECT_ )
+ bval = 0xC0;
+ else
+ bval = 0x80;
+ bval |= cmd->lun;
+ pDCB->IdentifyMsg = bval;
+
+ if( pDCB->DevMode & SYNC_NEGO_ )
+ {
+ pDCB->SyncMode = SYNC_ENABLE;
+ pDCB->SyncOffset = SYNC_NEGO_OFFSET;
+ }
+
+ if( pDCB->DevMode & WIDE_NEGO_ )
+ {
+ if( cmd->lun )
+ {
+ if( !(CurrDCBscntl3 & EN_WIDE_SCSI) )
+ pDCB->DevMode &= ~WIDE_NEGO_;
+ }
+ else
+ CurrDCBscntl3 = 0;
+ }
+ pDCB->DCBFlag = 0;
+}
+
+
+/***********************************************************************
+ * Function : static void DC390W_initSRB
+ *
+ * Purpose : initialize the internal structures for a given SRB
+ *
+ * Inputs : psrb - pointer to this scsi request block structure
+ *
+ ***********************************************************************/
+void DC390W_initSRB( PSRB psrb )
+{
+#ifndef VERSION_ELF_1_2_13
+ psrb->PhysSRB = virt_to_phys( psrb );
+ psrb->__command[1] = virt_to_phys( psrb->CmdBlock );
+ psrb->__msgout0[0] = 1;
+ psrb->__msgout0[1] = virt_to_phys( psrb->MsgOutBuf );
+ psrb->SegmentPad[0] = 16;
+ psrb->SegmentPad[1] = virt_to_phys( pad_buffer );
+#else
+ psrb->PhysSRB = (ULONG) psrb;
+ psrb->__command[1] = (ULONG) psrb->CmdBlock;
+ psrb->__msgout0[0] = 1;
+ psrb->__msgout0[1] = (ULONG) psrb->MsgOutBuf;
+ psrb->SegmentPad[0] = 16;
+ psrb->SegmentPad[1] = (ULONG) pad_buffer;
+#endif
+}
+
+
+void DC390W_linkSRB( PACB pACB )
+{
+ USHORT count, i;
+ PSRB psrb;
+
+ count = pACB->SRBCount;
+
+ for( i=0; i< count; i++)
+ {
+ if( i != count - 1)
+ pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1];
+ else
+ pACB->SRB_array[i].pNextSRB = NULL;
+ psrb = (PSRB) &pACB->SRB_array[i];
+ DC390W_initSRB( psrb );
+ }
+}
+
+
+/***********************************************************************
+ * Function : static void DC390W_initACB
+ *
+ * Purpose : initialize the internal structures for a given SCSI host
+ *
+ * Inputs : psh - pointer to this host adapter's structure
+ *
+ ***********************************************************************/
+void DC390W_initACB( PSH psh, USHORT chipType, ULONG io_port, UCHAR Irq, USHORT index )
+{
+ PACB pACB;
+ USHORT i;
+ UCHAR adaptType, bval;
+
+
+ psh->can_queue = MAX_CMD_QUEUE;
+ psh->cmd_per_lun = MAX_CMD_PER_LUN;
+ psh->this_id = (int) eepromBuf[index][EE_ADAPT_SCSI_ID];
+ psh->io_port = io_port;
+ psh->n_io_port = 0x80;
+ psh->irq = Irq;
+
+ if( chipType == PCI_DEVICE_ID_NCR53C825A )
+ adaptType = DC390W;
+ else
+ {
+ outb( 2, io_port+GPREG );
+ bval = inb( io_port+GPREG );
+ if( bval & 8 )
+ adaptType = DC390U;
+ else
+ adaptType = DC390F;
+ }
+
+ pACB = (PACB) psh->hostdata;
+
+#ifndef VERSION_ELF_1_2_13
+ if( adaptType == DC390U )
+ {
+ psh->max_id = 8;
+ pACB->max_id = 7;
+ }
+ else
+ {
+ psh->max_id = 16;
+ pACB->max_id = 15;
+ }
+
+#ifdef CONFIG_SCSI_MULTI_LUN
+ if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
+ psh->max_lun = 8;
+ else
+#endif
+ psh->max_lun = 1;
+
+#else
+ pACB->max_id = 7;
+#endif
+ if( pACB->max_id == eepromBuf[index][EE_ADAPT_SCSI_ID] )
+ pACB->max_id--;
+
+#ifdef CONFIG_SCSI_MULTI_LUN
+ if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
+ pACB->max_lun = 7;
+ else
+#endif
+ pACB->max_lun = 0;
+
+ pACB->pScsiHost = psh;
+ pACB->IOPortBase = (USHORT) io_port;
+ pACB->pLinkDCB = NULL;
+ pACB->pDCBRunRobin = NULL;
+ pACB->pActiveDCB = NULL;
+ pACB->pFreeSRB = pACB->SRB_array;
+ pACB->SRBCount = MAX_SRB_CNT;
+ pACB->AdapterIndex = index;
+ pACB->status = 0;
+ pACB->AdaptSCSIID = eepromBuf[index][EE_ADAPT_SCSI_ID];
+ pACB->AdaptSCSILUN = 0;
+ pACB->DeviceCnt = 0;
+ pACB->IRQLevel = Irq;
+ pACB->AdaptType = adaptType;
+ pACB->TagMaxNum = eepromBuf[index][EE_TAG_CMD_NUM] << 2;
+ pACB->ACBFlag = 0;
+ pACB->scan_devices = 1;
+ pACB->Gmode2 = eepromBuf[index][EE_MODE2];
+ if( eepromBuf[index][EE_MODE2] & LUN_CHECK )
+ pACB->LUNchk = 1;
+ pACB->pDCB_free = &pACB->DCB_array[0];
+ DC390W_linkSRB( pACB );
+ for(i=0; i<MAX_SCSI_ID; i++)
+ pACB->DCBmap[i] = 0;
+}
+
+
+/***********************************************************************
+ * Function : static int DC390W_initAdapter
+ *
+ * Purpose : initialize the SCSI chip ctrl registers
+ *
+ * Inputs : psh - pointer to this host adapter's structure
+ *
+ ***********************************************************************/
+int DC390W_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )
+{
+ USHORT ioport, wval;
+ UCHAR bval;
+ PACB pACB, pacb;
+ USHORT used_irq = 0;
+
+ pacb = pACB_start;
+ if( pacb != NULL )
+ {
+ for ( ; (pacb != (PACB) -1) ; )
+ {
+ if( pacb->IRQLevel == Irq )
+ {
+ used_irq = 1;
+ break;
+ }
+ else
+ pacb = pacb->pNextACB;
+ }
+ }
+
+ if( !used_irq )
+ {
+#ifdef VERSION_ELF_1_2_13
+ if( request_irq(Irq, DC390W_Interrupt, SA_INTERRUPT, "tmscsiw"))
+#else
+ if( request_irq(Irq, DC390W_Interrupt, SA_INTERRUPT | SA_SHIRQ, "tmscsiw", NULL))
+#endif
+ {
+ printk("DC390W : register IRQ error!\n");
+ return( -1 );
+ }
+ }
+ request_region(io_port,psh->n_io_port,"tmscsiw");
+
+ ioport = (USHORT) io_port;
+ outb(IRQ_DISABLE, ioport+DCNTL);
+ outb(ABORT_OP, ioport+ISTAT);
+ udelay(100000);
+ outb(0, ioport+ISTAT);
+ bval = inb(ioport+DSTAT);
+ bval = inb(ioport+ISTAT);
+ wval = inw(ioport+SIST0);
+
+ pACB = (PACB) psh->hostdata;
+ bval = pACB->AdaptSCSIID;
+ bval |= ENABLE_RESEL;
+ outb(bval,ioport+SCID);
+
+ if(pACB->AdaptType == DC390W)
+ bval = SYNC_CLK_F2+ASYNC_CLK_F2;
+ else
+ { /* @1.09 */
+ bval = inb(ioport+CTEST3);
+ if( (bval & CHIP_REV_MASK) < 0x30 ) /* 53C875 Rev. F or later ? */
+ goto REVF;
+ bval = inb(ioport+STEST1);
+ if( (bval & 0x0C) == 0x0C ) /* double clock already enable ? */
+ goto REVF;
+ outb(8,ioport+STEST1); /* enable clock doubler */
+ udelay(20);
+ outb(HALT_SCSI_CLK,ioport+STEST3); /* halt clock */
+ outb(0x0C,ioport+STEST1); /* select double SCSI clock */
+ outb(0,ioport+STEST3); /* re-enable clock */
+REVF:
+ bval = SYNC_CLK_F4+ASYNC_CLK_F4;
+ }
+ outb(bval,ioport+SCNTL3);
+
+ bval = SYNC_PERIOD_F4+ASYNCHRONOUS; /* set to async */
+ outb(bval,ioport+SXFER);
+
+ bval = WRT_EN_INVALIDATE; /* Enable write and invalidate */
+ outb(bval,ioport+CTEST3);
+
+ bval = EN_DMA_FIFO_536+BURST_LEN_MSB; /* select 536 bytes DMA FIFO, burst len bit2=1 */
+ outb(bval,ioport+CTEST5);
+
+ bval = BURST_LEN8+EN_READ_LINE+EN_READ_MULTIPLE+BURST_OPCODE_FETCH+AUTO_START; /* set DMA parameter */
+ outb(bval,ioport+DMODE);
+
+ bval = EN_ABORTED+EN_SCRIPT_INT+EN_ILLEGAL_INST; /* enable DMA interrupt */
+ outb(bval,ioport+DIEN);
+
+ bval = EN_CACHE_LINE_SIZE+EN_PRE_FETCH+TOTEM_POLE_IRQ+COMPATIBLE_700;
+ outb(bval,ioport+DCNTL);
+
+ bval = EN_PHASE_MISMATCH+EN_SCSI_GERROR+EN_UNEXPECT_DISC+EN_SCSI_RESET+EN_PARITY_ERROR;
+ outb(bval,ioport+SIEN0);
+
+ bval = EN_SEL_TIMEOUT+EN_GENERAL_TIMEOUT;
+ outb(bval,ioport+SIEN1);
+
+ bval = SEL_TO_204ms; /* 250ms selection timeout */
+ outb(bval,ioport+STIME0);
+
+ wval = 1 << (eepromBuf[index][EE_ADAPT_SCSI_ID]); /* @1.11 */
+ outw(wval,ioport+RESPID0);
+
+ bval = DIS_SINGLE_INIT;
+ if( eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION )
+ bval |= ACTIVE_NEGATION_;
+ outb(bval,ioport+STEST3);
+
+ return(0);
+}
+
+
+/***********************************************************************
+ * Function : static int DC390W_init (struct Scsi_Host *host)
+ *
+ * Purpose : initialize the internal structures for a given SCSI host
+ *
+ * Inputs : host - pointer to this host adapter's structure/
+ *
+ * Preconditions : when this function is called, the chip_type
+ * field of the pACB structure MUST have been set.
+ ***********************************************************************/
+
+static int
+DC390W_init (PSHT psht, USHORT chipType, ULONG io_port, UCHAR Irq, USHORT index)
+{
+ PSH psh;
+ PACB pACB;
+
+ if( ! CheckEEpromCheckSum( &eepromBuf[index][0], (USHORT) io_port) )
+ {
+ psh = scsi_register( psht, sizeof(DC390W_ACB) );
+ if( !psh )
+ return( -1 );
+ if( !pSH_start )
+ {
+ pSH_start = psh;
+ pSH_current = psh;
+ }
+ else
+ {
+ pSH_current->next = psh;
+ pSH_current = psh;
+ }
+
+#ifdef DC390W_DEBUG0
+ printk("DC390W : pSH = %8x,", (UINT) psh);
+#endif
+
+ DC390W_initACB( psh, chipType, io_port, Irq, index );
+ if( !DC390W_initAdapter( psh, io_port, Irq, index ) )
+ {
+ pACB = (PACB) psh->hostdata;
+ if( !pACB_start )
+ {
+ pACB_start = pACB;
+ pACB_current = pACB;
+ pACB->pNextACB = (PACB) -1;
+ }
+ else
+ {
+ pACB_current->pNextACB = pACB;
+ pACB_current = pACB;
+ pACB->pNextACB = (PACB) -1;
+ }
+
+#ifdef DC390W_DEBUG0
+ printk("DC390W : pACB = %8x, pDCB_array = %8x, pSRB_array = %8x\n",
+ (UINT) pACB, (UINT) pACB->DCB_array, (UINT) pACB->SRB_array);
+ printk("DC390W : ACB size= %4x, DCB size= %4x, SRB size= %4x\n",
+ sizeof(DC390W_ACB), sizeof(DC390W_DCB), sizeof(DC390W_SRB) );
+#endif
+
+ }
+ else
+ {
+ pSH_start = NULL;
+ scsi_unregister( psh );
+ return( -1 );
+ }
+ DC390W_download_script( psh );
+ return( 0 );
+ }
+ else
+ {
+ printk("DC390W_init: EEPROM reading error!\n");
+ return( -1 );
+ }
+}
+
+
+void MyDelay( void )
+{
+ UCHAR i,j;
+
+ j = inb(0x61) & 0x10;
+
+ for(;;)
+ {
+ i = inb(0x61) & 0x10;
+ if( j ^ i)
+ break;
+ }
+}
+
+
+void EnDisableCE( UCHAR Flag, USHORT scsiIOPort )
+{
+
+ UCHAR bval;
+ USHORT port;
+
+ port = (scsiIOPort & 0xff00) + GPREG;
+ if(Flag == ENABLE_CE)
+ bval = 0x10;
+ else
+ bval = 0x00;
+ outb(bval,port);
+ udelay(8); /* Delay();*/
+}
+
+
+void EEpromOutDI( USHORT Carry, USHORT scsiIOPort )
+{
+ UCHAR bval;
+ USHORT port;
+
+ port = (scsiIOPort & 0xff00) + GPREG;
+ bval = 0x10;
+ if(Carry)
+ bval |= 0x02; /* SK=0, DI */
+ outb(bval,port);
+ udelay(8); /* Delay();*/
+ bval |= 0x04; /* SK=1, DI */
+ outb(bval,port);
+ udelay(8); /* Delay();*/
+ bval &= 0xfb; /* SK=0, DI */
+ outb(bval,port);
+ udelay(8); /* Delay();*/
+}
+
+
+void EEpromPrepare( UCHAR EEpromCmd, USHORT scsiIOPort )
+{
+ UCHAR i,j;
+ USHORT carryFlag;
+
+ carryFlag = 1;
+ j = 0x80;
+ for(i=0;i<9;i++)
+ {
+ EEpromOutDI(carryFlag,scsiIOPort);
+ carryFlag = (EEpromCmd & j) ? 1 : 0;
+ j >>= 1;
+ }
+}
+
+
+UCHAR EEpromInDo(USHORT scsiIOPort)
+{
+ UCHAR bval;
+ USHORT port;
+
+ port = (scsiIOPort & 0xff00) + GPREG;
+ bval = 0x14; /* SK=1 */
+ outb(bval,port);
+ udelay(8); /* Delay();*/
+ bval = 0x10; /* SK=0 */
+ outb(bval,port);
+ udelay(8); /* Delay();*/
+ bval = inb(port);
+ if(bval & 0x01)
+ return( 1 );
+ else
+ return( 0 );
+}
+
+
+USHORT EEpromGetData(USHORT scsiIOPort)
+{
+ UCHAR i;
+ UCHAR carryFlag;
+ USHORT wval;
+
+ wval = 0;
+ for(i=0;i<16;i++)
+ {
+ wval <<= 1;
+ carryFlag = EEpromInDo(scsiIOPort);
+ wval |= carryFlag;
+ }
+ return( wval );
+}
+
+
+void ReadEEprom( PUCHAR EEpromBuf, USHORT scsiIOPort )
+{
+ UCHAR cmd;
+
+ cmd = EEPROM_READ;
+loop_rd:
+ EnDisableCE(ENABLE_CE, scsiIOPort);
+ EEpromPrepare(cmd, scsiIOPort);
+ *((PUSHORT)EEpromBuf) = EEpromGetData(scsiIOPort);
+ EEpromBuf++;
+ EEpromBuf++;
+ cmd++;
+ EnDisableCE(DISABLE_CE, scsiIOPort);
+ if(cmd & 0x3f)
+ goto loop_rd;
+}
+
+
+USHORT CheckEEpromCheckSum( PUCHAR EEpromBuf, USHORT scsiIOPort)
+{
+ USHORT wval,port, *ptr;
+ UCHAR i,bval;
+
+ port = (scsiIOPort & 0xff00) + GPCNTL;
+ bval = 0x09; /* configure IO Pin */
+ outb(bval,port);
+ ReadEEprom(EEpromBuf,scsiIOPort); /* read eeprom data */
+ wval = 0;
+ ptr = (PUSHORT) EEpromBuf;
+ for(i=0; i<128 ;i+=2, ptr++)
+ wval += *ptr;
+ return( (wval == 0x1234) ? 0 : -1);
+}
+
+
+
+static void
+DC390W_download_script (struct Scsi_Host *host)
+{
+ ULONG wlval, wlval1, length, alignm;
+ USHORT j, k, m;
+ USHORT ioport;
+ UCHAR bval;
+ PACB pACB;
+ PSRB pSRB;
+ void *pSrc, *pSrc1;
+ ULONG *pStart;
+ ULONG Ent_reselected;
+ ULONG Ent_reselecttag;
+ ULONG Ent_select0;
+ ULONG Ent_select1;
+ ULONG Ent_check_phase;
+ ULONG Ent_status1_phase;
+ ULONG Ent_command_phase;
+ ULONG Ent_jump_table0;
+ ULONG Ent_din_phaseB;
+ ULONG Ent_dout_phaseB;
+ ULONG Ent_din_pad_0;
+ ULONG Ent_dout_pad_0;
+ ULONG Ent_jump_tablew;
+ ULONG Ent_din_pad_1;
+ ULONG Ent_dout_pad_1;
+ ULONG Ent_mout_phase;
+ ULONG Ent_status_phase;
+ ULONG Ent_min_phase;
+ ULONG Ent_jump_msgok;
+ ULONG Ent_msg__1;
+ ULONG Ent_msg___3;
+ ULONG Ent_msg___2;
+ ULONG Ent_set_atn;
+ ULONG Ent_msg__a;
+ ULONG Ent_msg__23;
+ ULONG Ent_msg__3;
+ ULONG Ent_msg__4;
+ ULONG Ent_clr_atn;
+ ULONG Ent_din_phaseW;
+ ULONG Ent_dout_phaseW;
+ ULONG Ent_din_pad_addrB;
+ ULONG Ent_dout_pad_addrB;
+ ULONG Ent_din_pad_addrW;
+ ULONG Ent_dout_pad_addrW;
+
+
+ pACB = (PACB) host->hostdata;
+ ioport = pACB->IOPortBase;
+ bval = SCRATCHAB_AS_BASE; /* set scratchB contains 4K RAM base address */
+ outb(bval,ioport+CTEST2);
+
+ wlval = inl((ioport+SCRATCHB)); /* get starting address of 4K RAM */
+/* wlval += 0x800; */ /* point to Upper 2K RAM */
+ DesPhysAddr[0] = wlval; /* destination address */
+
+#ifdef DC390W_DEBUG0
+ printk("DesAddr=%8x,",(UINT) wlval);
+#endif
+ bval = 0; /* set Scratch_A and Scratch_B to normal mode */
+ outb(bval,ioport+CTEST2);
+
+ /*-------------------------------------------------------------------
+ * patch the label in jump instruction: using offset relative
+ * to start_script
+ *------------------------------------------------------------------*/
+
+ Ent_reselected = (ULONG) reselected - (ULONG) start_script;
+ Ent_reselecttag = (ULONG) reselecttag - (ULONG) start_script;
+ Ent_select0 = (ULONG) select0 - (ULONG) start_script;
+ Ent_select1 = (ULONG) select1 - (ULONG) start_script;
+ Ent_check_phase = (ULONG) check_phase - (ULONG) start_script;
+ Ent_status1_phase = (ULONG) status1_phase - (ULONG) start_script;
+ Ent_command_phase = (ULONG) command_phase - (ULONG) start_script;
+ Ent_din_phaseB = (ULONG) din_phaseB - (ULONG) start_script;
+ Ent_dout_phaseB = (ULONG) dout_phaseB - (ULONG) start_script;
+ Ent_din_phaseW = (ULONG) din_phaseW - (ULONG) start_script;
+ Ent_dout_phaseW = (ULONG) dout_phaseW - (ULONG) start_script;
+ Ent_jump_table0 = (ULONG) jump_table0 - (ULONG) start_script;
+ Ent_din_pad_0 = (ULONG) din_pad_0 - (ULONG) start_script;
+ Ent_din_pad_addrB = (ULONG) din_pad_addrB - (ULONG) start_script;
+ Ent_dout_pad_0 = (ULONG) dout_pad_0 - (ULONG) start_script;
+ Ent_dout_pad_addrB = (ULONG) dout_pad_addrB - (ULONG) start_script;
+ Ent_jump_tablew = (ULONG) jump_tablew - (ULONG) start_script;
+ Ent_din_pad_1 = (ULONG) din_pad_1 - (ULONG) start_script;
+ Ent_din_pad_addrW = (ULONG) din_pad_addrW - (ULONG) start_script;
+ Ent_dout_pad_1 = (ULONG) dout_pad_1 - (ULONG) start_script;
+ Ent_dout_pad_addrW = (ULONG) dout_pad_addrW - (ULONG) start_script;
+ Ent_mout_phase = (ULONG) mout_phase - (ULONG) start_script;
+ Ent_status_phase = (ULONG) status_phase - (ULONG) start_script;
+ Ent_min_phase = (ULONG) min_phase - (ULONG) start_script;
+ Ent_jump_msgok = (ULONG) jump_msgok - (ULONG) start_script;
+ Ent_msg__1 = (ULONG) msg__1 - (ULONG) start_script;
+ Ent_msg___3 = (ULONG) msg___3 - (ULONG) start_script;
+ Ent_msg___2 = (ULONG) msg___2 - (ULONG) start_script;
+ Ent_set_atn = (ULONG) set_atn - (ULONG) start_script;
+ Ent_msg__a = (ULONG) msg__a - (ULONG) start_script;
+ Ent_msg__23 = (ULONG) msg__23 - (ULONG) start_script;
+ Ent_msg__3 = (ULONG) msg__3 - (ULONG) start_script;
+ Ent_msg__4 = (ULONG) msg__4 - (ULONG) start_script;
+ Ent_clr_atn = (ULONG) clr_atn - (ULONG) start_script;
+
+ jmp_select0[0] = Ent_select0 + wlval;
+ jmp_reselected[0] = Ent_reselected + wlval;
+ jmp_check_phase[0] = Ent_check_phase + wlval;
+ jmp_check_phase1[0] = Ent_check_phase + wlval;
+ jmp_check_phase2[0] = Ent_check_phase + wlval;
+ jmp_check_phase3[0] = Ent_check_phase + wlval;
+ jmp_check_phase4[0] = Ent_check_phase + wlval;
+ jmp_check_phase5[0] = Ent_check_phase + wlval;
+ jmp_check_phase6[0] = Ent_check_phase + wlval;
+ jmp_status1_phase[0] = Ent_status1_phase + wlval;
+ jmp_status1_phase1[0] = Ent_status1_phase + wlval;
+ jmp_status1_phase2[0] = Ent_status1_phase + wlval;
+ jmp_status1_phase3[0] = Ent_status1_phase + wlval;
+ jmp_command_phase[0] = Ent_command_phase + wlval;
+ for(j=0,k=1,m=0; j< (MAX_SG_LIST_BUF+1); j++)
+ {
+ jmp_dio_phaseB[k] = Ent_din_phaseB + m + wlval;
+ jmp_dio_phaseW[k] = Ent_din_phaseW + m + wlval;
+ k += 2;
+ jmp_dio_phaseB[k] = Ent_dout_phaseB + m + wlval;
+ jmp_dio_phaseW[k] = Ent_dout_phaseW + m + wlval;
+ k += 2;
+ m += 8;
+ }
+ jmp_din_pad_0[0] = Ent_din_pad_0 + wlval;
+ jmp_dout_pad_0[0] = Ent_dout_pad_0 + wlval;
+ jmp_din_pad_addrB[0] = Ent_din_pad_addrB + wlval;
+ jmp_dout_pad_addrB[0] = Ent_dout_pad_addrB + wlval;
+ jmp_din_pad_addrW[0] = Ent_din_pad_addrW + wlval;
+ jmp_dout_pad_addrW[0] = Ent_dout_pad_addrW + wlval;
+ jmp_din_pad_1[0] = Ent_din_pad_1 + wlval;
+ jmp_dout_pad_1[0] = Ent_dout_pad_1 + wlval;
+ jmp_status_phase[0] = Ent_status_phase + wlval;
+ jmp_min_phase[0] = Ent_min_phase + wlval;
+ jmp_mout_phase[0] = Ent_mout_phase + wlval;
+ jmp_jump_msgok[0] = Ent_jump_msgok + wlval;
+ jmp_msg__1[0] = Ent_msg__1 + wlval;
+ jmp_msg___3[0] = Ent_msg___3 + wlval;
+ jmp_msg___2[0] = Ent_msg___2 + wlval;
+ jmp_msg__a[0] = Ent_msg__a + wlval;
+ jmp_msg__a1[0] = Ent_msg__a + wlval;
+ jmp_msg__a2[0] = Ent_msg__a + wlval;
+ jmp_msg__23[0] = Ent_msg__23 + wlval;
+ jmp_msg__3[0] = Ent_msg__3 + wlval;
+ jmp_msg__4[0] = Ent_msg__4 + wlval;
+
+ /*--------------------------------------------------------------------
+ // patch the element in ACB struct: using Physical address
+ //-------------------------------------------------------------------*/
+
+#ifndef VERSION_ELF_1_2_13
+ wlval1 = virt_to_phys( pACB->msgin123 );
+#else
+ wlval1 = (ULONG) pACB->msgin123;
+#endif
+ ACB_msgin123_1[0] = wlval1;
+ ACB_msgin123_2[0] = wlval1;
+ ACB_msgin123_3[0] = wlval1;
+ ACB_msgin123_4[0] = wlval1;
+ ACB_msgin123_5[0] = wlval1;
+ ACB_msgin123_6[0] = wlval1;
+ ACB_msgin123_7[0] = wlval1;
+
+#ifndef VERSION_ELF_1_2_13
+ ACB_status[0] = virt_to_phys( &pACB->status );
+#else
+ ACB_status[0] = (ULONG) &pACB->status;
+#endif
+ /*--------------------------------------------------------------------
+ // patch the element in SRB struct: using offset in struct
+ //-------------------------------------------------------------------*/
+
+ pSRB = (PSRB) pACB->SRB_array;
+ select1[0] = (select1[0] & 0xffff0000) + ((ULONG) &pSRB->__select - (ULONG) &pSRB->CmdBlock);
+ SRB_msgout0[0] = (ULONG) &pSRB->__msgout0 - (ULONG) &pSRB->CmdBlock;
+ SRB_msgout01[0] = (ULONG) &pSRB->__msgout0 - (ULONG) &pSRB->CmdBlock;
+ SRB_command[0] = (ULONG) &pSRB->__command - (ULONG) &pSRB->CmdBlock;
+ SRB_SegmentPad[0] = (ULONG) &pSRB->SegmentPad - (ULONG) &pSRB->CmdBlock;
+ SRB_SegmentPad1[0] = (ULONG) &pSRB->SegmentPad - (ULONG) &pSRB->CmdBlock;
+ SRB_SegmentPad2[0] = (ULONG) &pSRB->SegmentPad - (ULONG) &pSRB->CmdBlock;
+ SRB_SegmentPad3[0] = (ULONG) &pSRB->SegmentPad - (ULONG) &pSRB->CmdBlock;
+ wlval = (ULONG) &pSRB->Segment0 - (ULONG) &pSRB->CmdBlock;
+ for(j=0,k=1; j<(MAX_SG_LIST_BUF+1); j++)
+ {
+ din_phaseB[k] = wlval;
+ dout_phaseB[k] = wlval;
+ din_phaseW[k] = wlval;
+ dout_phaseW[k] = wlval;
+ k += 2;
+ wlval += 8;
+ }
+
+
+ bval = inb(ioport+DCNTL);
+ bval |= IRQ_DISABLE;
+ outb(bval,ioport+DCNTL); /* disable interrupt */
+
+/* pSrc = scsi_init_malloc( 2048, GFP_ATOMIC); */
+ pSrc = scsi_init_malloc( 4096, GFP_ATOMIC); /* 1.11 */
+#ifdef DC390W_DEBUG0
+ printk("SrcAlloc=%8x,",(UINT) pSrc);
+#endif
+ alignm = 4 - (((ULONG) pSrc) & 3);
+ pSrc1 = (void *)(((ULONG) pSrc) + alignm);
+ length = (ULONG) end_script - (ULONG) start_script;
+ memcpy( pSrc1, (void *) start_script, length);
+ pStart = (ULONG *) ((ULONG) start_mov - (ULONG) start_script);
+ pStart =(ULONG *) (((ULONG) pStart) + ((ULONG) pSrc1));
+
+#ifdef DC390W_DEBUG0
+ printk("SrcAddr=%8x,\n",(UINT) pSrc1);
+#endif
+#ifndef VERSION_ELF_1_2_13
+ (ULONG *)pStart[1] = virt_to_phys( pSrc1 );
+#else
+ (ULONG *)pStart[1] = (ULONG) pSrc1;
+#endif
+
+/* wlval = virt_to_phys( start_script ); */ /* physical address of start_script */
+/* SrcPhysAddr[0] = wlval; */ /* sources address */
+
+/* start to download SCRIPT instruction to the RAM of NCR53c825A,875 */
+
+/* wlval = virt_to_phys( start_mov ); */
+
+#ifndef VERSION_ELF_1_2_13
+ wlval = virt_to_phys( pStart );
+#else
+ wlval = (ULONG) pStart;
+#endif
+
+ outl(wlval,ioport+DSP);
+
+ bval = inb(ioport+ISTAT);
+ while(!(bval & DMA_INT_PENDING)) /* check load start_script is finished? */
+ bval = inb(ioport+ISTAT);
+
+ bval = inb(ioport+DSTAT); /* clear interrupt */
+
+ bval = inb(ioport+DCNTL);
+ bval &= ~IRQ_DISABLE;
+ outb(bval,ioport+DCNTL); /* re-enable interrupt */
+
+ scsi_init_free((char *) pSrc, 4096);
+
+ wlval = DesPhysAddr[0]; /* starting addr of RAM */
+ wlval -= (ULONG) start_script;
+
+ pACB->jmp_reselect = wlval + (ULONG) start_script;
+ pACB->jmp_select = wlval + (ULONG) select1;
+ pACB->jmp_table8 = wlval + (ULONG) jump_table0;
+ pACB->jmp_set_atn = wlval + (ULONG) set_atn;
+ pACB->jmp_clear_ack = wlval + (ULONG) msg__a;
+ pACB->jmp_next = wlval + (ULONG) check_phase;
+ pACB->jmp_din8 = wlval + (ULONG) din_phaseB+8;
+ pACB->jmp_dout8 = wlval + (ULONG) dout_phaseB+8;
+ pACB->jmp_clear_atn = wlval + (ULONG) clr_atn;
+ pACB->jmp_reselecttag = wlval + (ULONG) reselecttag;
+
+ wlval = pACB->jmp_reselect;
+ outl(wlval,(ioport+DSP));
+ return;
+}
+
+
+/***********************************************************************
+ * Function : int DC390W_detect(Scsi_Host_Template *psht)
+ *
+ * Purpose : detects and initializes NCR53c825A,875 SCSI chips
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : psht - template for this SCSI adapter
+ *
+ * Returns : number of host adapters detected
+ *
+ ***********************************************************************/
+
+int
+DC390W_detect(Scsi_Host_Template *psht)
+{
+ UCHAR pci_bus, pci_device_fn, irq;
+#ifndef VERSION_ELF_1_2_13
+ UINT io_port, ram_base;
+#else
+ ULONG io_port, ram_base;
+#endif
+ USHORT i;
+ int error = 0;
+ USHORT adaptCnt = 0; /* Number of boards detected */
+ USHORT pci_index = 0; /* Device index to PCI BIOS calls */
+ USHORT pci_index2 = 0; /* Device index to PCI BIOS calls */
+ USHORT chipType = 0;
+
+
+#ifndef VERSION_ELF_1_2_13
+ psht->proc_dir = &proc_scsi_tmscsiw;
+#endif
+
+ InitialTime = 1;
+ pSHT_start = psht;
+ jmp_table16 = (ULONG) jump_tablew - (ULONG) jump_table0;
+ jmp_din16 = (ULONG) din_phaseW - (ULONG) din_phaseB;
+ jmp_dout16 = (ULONG) dout_phaseW - (ULONG) dout_phaseB;
+ pACB_start = NULL;
+
+ if ( pcibios_present() )
+ {
+ for (i = 0; i < MAX_ADAPTER_NUM; ++i)
+ {
+ if( !pcibios_find_device( PCI_VENDOR_ID_NCR,
+ PCI_DEVICE_ID_NCR53C825A,
+ pci_index, &pci_bus, &pci_device_fn) )
+ {
+ chipType = PCI_DEVICE_ID_NCR53C825A;
+ pci_index++;
+ }
+ else if( !pcibios_find_device( PCI_VENDOR_ID_NCR,
+ PCI_DEVICE_ID_NCR53C875,
+ pci_index2, &pci_bus, &pci_device_fn) )
+ {
+ chipType = PCI_DEVICE_ID_NCR53C875;
+ pci_index2++;
+ }
+
+ if( chipType )
+ {
+ error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &io_port);
+ error |= pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_2, &ram_base);
+ error |= pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &irq);
+ if( error )
+ {
+ printk("DC390W_detect: reading configuration registers error!\n");
+ InitialTime = 0;
+ return( 0 );
+ }
+
+ (USHORT) io_port = (USHORT) io_port & 0xFFFE;
+#ifdef DC390W_DEBUG0
+ printk("DC390W : IO_PORT=%4x,RAM_BASE=%8x,IRQ=%x,CHIPID=%x,\n",
+ (UINT) io_port, (UINT) ram_base, irq, (UCHAR)chipType);
+#endif
+
+ if( !DC390W_init(psht, chipType, io_port, irq, i) )
+ adaptCnt++;
+ chipType = 0;
+ }
+ else
+ break;
+ }
+ }
+ InitialTime = 0;
+ adapterCnt = adaptCnt;
+ return( adaptCnt );
+}
+
+#ifndef VERSION_ELF_1_2_13
+
+/********************************************************************
+ * Function: tmscsiw_set_info()
+ *
+ * Purpose: Set adapter info (!)
+ *
+ * Not yet implemented
+ *
+ *******************************************************************/
+
+int tmscsiw_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
+{
+ return(-ENOSYS); /* Currently this is a no-op */
+}
+
+/********************************************************************
+ * Function: tmscsiw_proc_info(char* buffer, char **start,
+ * off_t offset, int length, int hostno, int inout)
+ *
+ * Purpose: return SCSI Adapter/Device Info
+ *
+ * Input: buffer: Pointer to a buffer where to write info
+ * start :
+ * offset:
+ * hostno: Host adapter index
+ * inout : Read (=0) or set(!=0) info
+ *
+ * Output: buffer: contains info
+ * length; length of info in buffer
+ *
+ * return value: length
+ *
+ ********************************************************************/
+
+/* KG: proc_info taken from driver aha152x.c */
+
+#undef SPRINTF
+#define SPRINTF(args...) pos += sprintf(pos, ## args)
+
+#define YESNO(YN)\
+if (YN) SPRINTF(" Yes ");\
+else SPRINTF(" No ")
+
+int tmscsiw_proc_info(char *buffer, char **start,
+ off_t offset, int length, int hostno, int inout)
+{
+ int dev, spd, spd1;
+ char *pos = buffer;
+ PSH shpnt;
+ PACB acbpnt;
+ PDCB dcbpnt;
+ unsigned long flags;
+/* Scsi_Cmnd *ptr; */
+
+ acbpnt = pACB_start;
+
+ while(acbpnt != (PACB)-1)
+ {
+ shpnt = acbpnt->pScsiHost;
+ if (shpnt->host_no == hostno) break;
+ acbpnt = acbpnt->pNextACB;
+ }
+
+ if (acbpnt == (PACB)-1) return(-ESRCH);
+ if (!shpnt) return(-ESRCH);
+
+ if(inout) /* Has data been written to the file ? */
+ return(tmscsiw_set_info(buffer, length, shpnt));
+
+ SPRINTF("Tekram DC390W/U/F (T) PCI SCSI Host Adadpter, ");
+ SPRINTF("Driver Version 1.12, 1997/02/17\n");
+
+ save_flags(flags);
+ cli();
+
+ SPRINTF("SCSI Host Nr %i, ", hostno);
+ SPRINTF("DC390WUF Adapter Nr %i\n", acbpnt->AdapterIndex);
+ SPRINTF("IOPortBase 0x%04x, ", acbpnt -> IOPortBase);
+ SPRINTF("IRQLevel 0x%02x\n",acbpnt -> IRQLevel);
+
+ SPRINTF("Adapter Type: ");
+ switch(acbpnt->AdaptType)
+ {
+ case DC390W: SPRINTF("DC390W, Fast Wide SCSI \n"); break;
+ case DC390U: SPRINTF("DC390U, Ultra SCSI\n"); break;
+ case DC390F: SPRINTF("DC390F, Ultra Wide SCSI\n"); break;
+ default: SPRINTF("Unknown !\n");
+ }
+
+ SPRINTF("MaxID %i, MaxLUN %i, ", acbpnt->max_id, acbpnt->max_lun);
+ SPRINTF("AdapterID %i, AdapterLUN %i\n", acbpnt->AdaptSCSIID, acbpnt->AdaptSCSILUN);
+
+ SPRINTF("TagMaxNum %i, Status %i\n", acbpnt->TagMaxNum, acbpnt->status);
+
+ SPRINTF("Nr of attached devices: %i\n", acbpnt->DeviceCnt);
+
+ SPRINTF("Un ID LUN Prty Sync DsCn SndS TagQ Wide NegoPeriod SyncSpeed SyncOffs\n");
+ dcbpnt = acbpnt->pLinkDCB;
+
+ for (dev = 0; dev < acbpnt->DeviceCnt; dev++)
+ {
+ SPRINTF("%02i %02i %02i ", dev, dcbpnt->UnitSCSIID, dcbpnt->UnitSCSILUN);
+ YESNO(dcbpnt->DevMode & PARITY_CHK_);
+ YESNO(dcbpnt->DCBsxfer & OFFSET_MASK);
+ YESNO(dcbpnt->DevMode & EN_DISCONNECT_);
+ YESNO(dcbpnt->DevMode & SEND_START_);
+ YESNO(dcbpnt->MaxCommand > 1);
+ YESNO(dcbpnt->DCBscntl3 & EN_WIDE_SCSI);
+ SPRINTF(" %03i ns ", (dcbpnt->NegoPeriod) << 2);
+ if (dcbpnt->DCBsxfer & OFFSET_MASK)
+ {
+ spd = 1000/(dcbpnt->SyncPeriod <<2);
+ spd1 = 1000%(dcbpnt->SyncPeriod <<2);
+ spd1 = (spd1 * 10)/(dcbpnt->SyncPeriod <<2);
+ SPRINTF(" %2i.%1i M %02i\n", spd, spd1, dcbpnt->DCBsxfer & OFFSET_MASK);
+ }
+ else SPRINTF("\n");
+ /* Add more info ...*/
+ dcbpnt = dcbpnt->pNextDCB;
+ }
+
+ restore_flags(flags);
+ *start = buffer + offset;
+
+ if (pos - buffer < offset)
+ return 0;
+ else if (pos - buffer - offset < length)
+ return pos - buffer - offset;
+ else
+ return length;
+}
+#endif /* VERSION_ELF_1_2_13 */
+
+#ifdef MODULE
+
+/***********************************************************************
+ * Function : static int DC390W_shutdown (struct Scsi_Host *host)
+ *
+ * Purpose : does a clean (we hope) shutdown of the NCR SCSI chip.
+ * Use prior to dumping core, unloading the NCR driver, etc.
+ *
+ * Returns : 0 on success
+ ***********************************************************************/
+static int
+DC390W_shutdown (struct Scsi_Host *host)
+{
+ USHORT ioport;
+ unsigned long flags;
+ PACB pACB = (PACB) host->hostdata;
+
+ ioport = (unsigned int) pACB->IOPortBase;
+
+ save_flags (flags);
+ cli();
+
+/* pACB->soft_reset(host); */
+/*
+ * For now, we take the simplest solution : reset the SCSI bus. Eventually,
+ * - If a command is connected, kill it with an ABORT message
+ * - If commands are disconnected, connect to each target/LUN and
+ * do a ABORT, followed by a SOFT reset, followed by a hard
+ * reset.
+ */
+
+#ifdef DC390W_DEBUG0
+ printk("DC390W: shutdown,");
+#endif
+ outb(ASSERT_RST, ioport+SCNTL1);
+ udelay(25); /* Minimum amount of time to assert RST */
+ outb(0, ioport+SCNTL1);
+ restore_flags (flags);
+ return( 0 );
+}
+
+
+int DC390W_release(struct Scsi_Host *host)
+{
+ int irq_count;
+ struct Scsi_Host *tmp;
+
+ DC390W_shutdown (host);
+
+ if (host->irq != IRQ_NONE)
+ {
+ for (irq_count = 0, tmp = pSH_start; tmp; tmp = tmp->next)
+ {
+ if ( tmp->irq == host->irq )
+ ++irq_count;
+ }
+ if (irq_count == 1)
+ {
+#ifdef DC390W_DEBUG0
+ printk("DC390W: Free IRQ %i.",host->irq);
+#endif
+#ifndef VERSION_ELF_1_2_13
+ free_irq(host->irq,NULL);
+#else
+ free_irq(host->irq);
+#endif
+ }
+ }
+
+ release_region(host->io_port,host->n_io_port);
+
+ return( 1 );
+}
+
+Scsi_Host_Template driver_template = DC390WUF;
+#include "scsi_module.c"
+#endif /* def MODULE */
+
diff --git a/linux/src/drivers/scsi/tmscsiw.h b/linux/src/drivers/scsi/tmscsiw.h
new file mode 100644
index 00000000..4ebbe4aa
--- /dev/null
+++ b/linux/src/drivers/scsi/tmscsiw.h
@@ -0,0 +1,1082 @@
+/***********************************************************************
+;* File Name : TMSCSIW.H *
+;* TEKRAM DC-390 PCI Wide SCSI Bus Master Host Adapter*
+;* Device Driver *
+;***********************************************************************/
+
+#ifndef TMSCSIW_H
+#define TMSCSIW_H
+
+#define IRQ_NONE 255
+
+typedef unsigned char UCHAR;
+typedef unsigned short USHORT;
+typedef unsigned long ULONG;
+typedef unsigned int UINT;
+
+typedef UCHAR *PUCHAR;
+typedef USHORT *PUSHORT;
+typedef ULONG *PULONG;
+typedef Scsi_Host_Template *PSHT;
+typedef struct Scsi_Host *PSH;
+typedef Scsi_Device *PSCSIDEV;
+typedef Scsi_Cmnd *PSCSICMD;
+typedef void *PVOID;
+typedef struct scatterlist *PSGL, SGL;
+
+
+typedef struct _AddWkSpace
+{
+USHORT WKIOCtrlFlag; /* ;b7-Done, b6-wrtVerify, b0-in process */
+USHORT XferredBlkCnt;
+USHORT WKToXferBlkCnt;
+USHORT WKcSGListDone;
+USHORT WKMaxSGIndex;
+ULONG WKToXferLen;
+USHORT NxtSGoffset;
+} AddWkSpace;
+
+/*;-----------------------------------------------------------------------*/
+typedef struct _SyncMsg
+{
+UCHAR ExtendMsg;
+UCHAR ExtMsgLen;
+UCHAR SyncXferReq;
+UCHAR Period;
+UCHAR ReqOffset;
+} SyncMsg;
+/*;-----------------------------------------------------------------------*/
+typedef struct _Capacity
+{
+ULONG BlockCount;
+ULONG BlockLength;
+} Capacity;
+/*;-----------------------------------------------------------------------*/
+typedef struct _SGentry
+{
+ULONG SGXferDataPtr;
+ULONG SGXferDataLen;
+} SGentry;
+
+typedef struct _SGentry1
+{
+ULONG SGXLen;
+ULONG SGXPtr;
+} SGentry1, *PSGE;
+
+
+#define MAX_ADAPTER_NUM 4
+#define MAX_DEVICES 10
+#define MAX_SG_LIST_BUF 32
+#define MAX_CMD_QUEUE 20
+#define MAX_CMD_PER_LUN 8
+#define MAX_SCSI_ID 16
+#define MAX_SRB_CNT MAX_CMD_QUEUE+4
+#define END_SCAN 2
+
+/*
+;-----------------------------------------------------------------------
+; SCSI Request Block
+;-----------------------------------------------------------------------
+*/
+struct _SRB
+{
+UCHAR CmdBlock[12];
+ULONG Segment0[MAX_SG_LIST_BUF+1][2];
+ULONG SegmentPad[2];
+
+ULONG __select;
+ULONG __command[2]; /* ;len,ptr */
+ULONG __msgout0[2]; /* ;len,ptr */
+
+ULONG PhysSRB;
+ULONG ReturnAddr;
+ULONG RemainSegPtr;
+PSCSICMD pcmd;
+struct _SRB *pNextSRB;
+struct _DCB *pSRBDCB;
+PSGL pSegmentList;
+ULONG XferredLen;
+
+ULONG SGPhysAddr; /*;a segment starting address */
+ULONG XferredLen1;
+
+SGL Segmentx; /* make a one entry of S/G list table */
+
+PUCHAR pMsgPtr;
+USHORT SRBState;
+USHORT Revxx2; /* ??? */
+
+UCHAR MsgInBuf[6];
+UCHAR MsgOutBuf[6];
+UCHAR SenseDataBuf[0x12];
+UCHAR AdaptStatus;
+UCHAR TargetStatus;
+
+UCHAR MsgCnt;
+UCHAR EndMessage;
+UCHAR TagNumber;
+UCHAR InternalReq; /*; 1-ADD internal request, 0-DMD request */
+
+UCHAR SGcount;
+UCHAR SGIndex;
+UCHAR IORBFlag; /*;81h-Reset, 2-retry */
+UCHAR SRBStatus;
+
+UCHAR RetryCnt;
+UCHAR SRBFlag; /*; b0-AutoReqSense,b6-Read,b7-write */
+ /*; b4-settimeout,b5-Residual valid */
+UCHAR ScratchABuf;
+UCHAR Reserved3[1]; /*;for dword alignment */
+};
+
+typedef struct _SRB DC390W_SRB, *PSRB;
+
+/*
+;-----------------------------------------------------------------------
+; Device Control Block
+;-----------------------------------------------------------------------
+*/
+struct _DCB
+{
+UCHAR DCBselect;
+UCHAR DCBsxfer;
+UCHAR DCBsdid;
+UCHAR DCBscntl3;
+
+UCHAR DCBscntl0;
+UCHAR IdentifyMsg;
+UCHAR DevMode;
+UCHAR AdpMode;
+
+struct _DCB *pNextDCB;
+struct _ACB *pDCBACB;
+
+PSCSICMD pQIORBhead;
+PSCSICMD pQIORBtail;
+PSCSICMD AboIORBhead;
+PSCSICMD AboIORBtail;
+USHORT QIORBCnt;
+USHORT AboIORBcnt;
+
+PSRB pWaitingSRB;
+PSRB pWaitLast;
+PSRB pGoingSRB;
+PSRB pGoingLast;
+PSRB pActiveSRB;
+USHORT GoingSRBCnt;
+USHORT WaitSRBCnt; /* ??? */
+
+ULONG TagMask;
+
+USHORT MaxCommand;
+USHORT AdaptIndex; /*; UnitInfo struc start */
+USHORT UnitIndex; /*; nth Unit on this card */
+UCHAR UnitSCSIID; /*; SCSI Target ID (SCSI Only) */
+UCHAR UnitSCSILUN; /*; SCSI Log. Unit (SCSI Only) */
+
+UCHAR InqDataBuf[8];
+UCHAR CapacityBuf[8];
+UCHAR SyncMode; /*; 0:async mode */
+UCHAR NegoPeriod; /*;for nego. */
+UCHAR SyncPeriod; /*;for reg. */
+UCHAR SyncOffset; /*;for reg. and nego.(low nibble) */
+UCHAR UnitCtrlFlag;
+UCHAR DCBFlag;
+UCHAR DevType;
+UCHAR Reserved2[1]; /*;for dword alignment */
+};
+
+typedef struct _DCB DC390W_DCB, *PDCB;
+/*
+;-----------------------------------------------------------------------
+; Adapter Control Block
+;-----------------------------------------------------------------------
+*/
+struct _ACB
+{
+ULONG PhysACB;
+ULONG DevVendorID;
+PSH pScsiHost;
+struct _ACB *pNextACB;
+USHORT IOPortBase;
+USHORT Revxx1; /* ??? */
+
+PDCB pLinkDCB;
+PDCB pDCBRunRobin;
+PDCB pActiveDCB;
+PDCB pDCB_free;
+PSRB pFreeSRB;
+USHORT SRBCount;
+USHORT AdapterIndex; /*; nth Adapter this driver */
+USHORT max_id;
+USHORT max_lun;
+
+ULONG jmp_reselect;
+ULONG jmp_select;
+ULONG jmp_table8;
+ULONG jmp_set_atn;
+ULONG jmp_clear_ack;
+ULONG jmp_next;
+ULONG jmp_din8;
+ULONG jmp_dout8;
+ULONG jmp_clear_atn;
+ULONG jmp_reselecttag;
+
+UCHAR msgin123[4];
+UCHAR status;
+UCHAR AdaptSCSIID; /*; Adapter SCSI Target ID */
+UCHAR AdaptSCSILUN; /*; Adapter SCSI LUN */
+UCHAR DeviceCnt;
+UCHAR IRQLevel;
+UCHAR AdaptType; /*;1:390W, 2:390U, 3:390F */
+UCHAR TagMaxNum;
+UCHAR ACBFlag;
+UCHAR Gmode2;
+UCHAR LUNchk;
+UCHAR scan_devices;
+UCHAR Reserved1[1]; /*;for dword alignment */
+UCHAR DCBmap[MAX_SCSI_ID];
+DC390W_DCB DCB_array[MAX_DEVICES]; /* +74h, Len=3E8 */
+DC390W_SRB SRB_array[MAX_SRB_CNT]; /* +45Ch, Len= */
+};
+
+typedef struct _ACB DC390W_ACB, *PACB;
+
+/*;-----------------------------------------------------------------------*/
+
+#define PCI_DEVICE_ID_NCR53C825A 0x0003
+#define PCI_DEVICE_ID_NCR53C875 0x000f
+
+#define CHIP810_ID 0x00011000
+#define CHIP820_ID 0x00021000
+#define CHIP825_ID 0x00031000
+#define CHIP815_ID 0x00041000
+#define CHIP875_ID 0x000f1000
+
+#define DC390W 1 /*;825A - 16 BIT*/
+#define DC390U 2 /*;875 - 8 BIT*/
+#define DC390F 3 /*;875 - 16 BIT*/
+
+#define BIT31 0x80000000
+#define BIT30 0x40000000
+#define BIT29 0x20000000
+#define BIT28 0x10000000
+#define BIT27 0x08000000
+#define BIT26 0x04000000
+#define BIT25 0x02000000
+#define BIT24 0x01000000
+#define BIT23 0x00800000
+#define BIT22 0x00400000
+#define BIT21 0x00200000
+#define BIT20 0x00100000
+#define BIT19 0x00080000
+#define BIT18 0x00040000
+#define BIT17 0x00020000
+#define BIT16 0x00010000
+#define BIT15 0x00008000
+#define BIT14 0x00004000
+#define BIT13 0x00002000
+#define BIT12 0x00001000
+#define BIT11 0x00000800
+#define BIT10 0x00000400
+#define BIT9 0x00000200
+#define BIT8 0x00000100
+#define BIT7 0x00000080
+#define BIT6 0x00000040
+#define BIT5 0x00000020
+#define BIT4 0x00000010
+#define BIT3 0x00000008
+#define BIT2 0x00000004
+#define BIT1 0x00000002
+#define BIT0 0x00000001
+
+/*;---WKIOCtrlFlag */
+#define IN_PROCESSING BIT0
+#define BAD_WRITE_V_CMD BIT1
+#define VERIFY_CMD BIT2
+#define RESIDUAL_SG BIT3
+
+/*;---UnitCtrlFlag */
+#define UNIT_ALLOCATED BIT0
+#define UNIT_INFO_CHANGED BIT1
+#define FORMATING_MEDIA BIT2
+#define UNIT_RETRY BIT3
+
+/*;---UnitFlags */
+#define DASD_SUPPORT BIT0
+#define SCSI_SUPPORT BIT1
+#define ASPI_SUPPORT BIT2
+
+/*;----SRBState machine definition */
+#define SRB_FREE 0
+#define SRB_WAIT BIT0
+#define SRB_READY BIT1
+#define SRB_MSGOUT BIT2 /*;arbitration+msg_out 1st byte*/
+#define SRB_MSGIN BIT3
+#define SRB_MSGIN_MULTI BIT4
+#define SRB_COMMAND BIT5
+#define SRB_START_ BIT6 /*;arbitration+msg_out+command_out*/
+#define SRB_DISCONNECT BIT7
+#define SRB_DATA_XFER BIT8
+#define SRB_XFERPAD BIT9
+#define SRB_STATUS BIT10
+#define SRB_COMPLETED BIT11
+
+#define DO_WIDE_NEGO BIT12
+#define DO_SYNC_NEGO BIT13
+#define SRB_UNEXPECT_RESEL BIT14
+
+/*;---ACBFlag */
+#define RESET_DEV BIT0
+#define RESET_DETECT BIT1
+#define RESET_DONE BIT2
+
+/*;---DCBFlag */
+#define ABORT_DEV_ BIT0
+
+/*;---SRBstatus */
+#define SRB_OK BIT0
+#define ABORTION BIT1
+#define OVER_RUN BIT2
+#define UNDER_RUN BIT3
+#define SRB_ERROR BIT4
+
+/*;---SRBFlag */
+#define DATAOUT BIT7
+#define DATAIN BIT6
+#define RESIDUAL_VALID BIT5
+#define ENABLE_TIMER BIT4
+#define RESET_DEV0 BIT2
+#define ABORT_DEV BIT1
+#define AUTO_REQSENSE BIT0
+
+/*;---Adapter status */
+#define H_STATUS_GOOD 0
+#define H_SEL_TIMEOUT 0x11
+#define H_OVER_UNDER_RUN 0x12
+#define H_UNEXP_BUS_FREE 0x13
+#define H_TARGET_PHASE_F 0x14
+#define H_INVALID_CCB_OP 0x16
+#define H_LINK_CCB_BAD 0x17
+#define H_BAD_TARGET_DIR 0x18
+#define H_DUPLICATE_CCB 0x19
+#define H_BAD_CCB_OR_SG 0x1A
+#define H_ABORT 0x0FF
+
+/*; SCSI Status byte codes*/
+#define SCSI_STAT_GOOD 0x0 /*; Good status */
+#define SCSI_STAT_CHECKCOND 0x02 /*; SCSI Check Condition */
+#define SCSI_STAT_CONDMET 0x04 /*; Condition Met */
+#define SCSI_STAT_BUSY 0x08 /*; Target busy status */
+#define SCSI_STAT_INTER 0x10 /*; Intermediate status */
+#define SCSI_STAT_INTERCONDMET 0x14 /*; Intermediate condition met */
+#define SCSI_STAT_RESCONFLICT 0x18 /*; Reservation conflict */
+#define SCSI_STAT_CMDTERM 0x22 /*; Command Terminated */
+#define SCSI_STAT_QUEUEFULL 0x28 /*; Queue Full */
+
+#define SCSI_STAT_UNEXP_BUS_F 0xFD /*; Unexpect Bus Free */
+#define SCSI_STAT_BUS_RST_DETECT 0xFE /*; Scsi Bus Reset detected */
+#define SCSI_STAT_SEL_TIMEOUT 0xFF /*; Selection Time out */
+
+/*;---Sync_Mode */
+#define SYNC_DISABLE 0
+#define SYNC_ENABLE BIT0
+#define SYNC_NEGO_DONE BIT1
+#define WIDE_ENABLE BIT2
+#define WIDE_NEGO_DONE BIT3
+#define EN_TAG_QUEUING BIT4
+
+#define SYNC_NEGO_OFFSET 16
+
+/*;---SCSI bus phase*/
+#define SCSI_DATA_OUT 0
+#define SCSI_DATA_IN 1
+#define SCSI_COMMAND 2
+#define SCSI_STATUS_ 3
+#define SCSI_NOP0 4
+#define SCSI_NOP1 5
+#define SCSI_MSG_OUT 6
+#define SCSI_MSG_IN 7
+
+/*;----SCSI MSG BYTE*/
+#define MSG_COMPLETE 0x00
+#define MSG_EXTENDED 0x01
+#define MSG_SAVE_PTR 0x02
+#define MSG_RESTORE_PTR 0x03
+#define MSG_DISCONNECT 0x04
+#define MSG_INITIATOR_ERROR 0x05
+#define MSG_ABORT 0x06
+#define MSG_REJECT_ 0x07
+#define MSG_NOP 0x08
+#define MSG_PARITY_ERROR 0x09
+#define MSG_LINK_CMD_COMPL 0x0A
+#define MSG_LINK_CMD_COMPL_FLG 0x0B
+#define MSG_BUS_RESET 0x0C
+#define MSG_ABORT_TAG 0x0D
+#define MSG_SIMPLE_QTAG 0x20
+#define MSG_HEAD_QTAG 0x21
+#define MSG_ORDER_QTAG 0x22
+#define MSG_IDENTIFY 0x80
+#define MSG_HOST_ID 0x0C0
+
+/*;----SCSI STATUS BYTE*/
+#define STATUS_GOOD 0x00
+#define CHECK_CONDITION_ 0x02
+#define STATUS_BUSY 0x08
+#define STATUS_INTERMEDIATE 0x10
+#define RESERVE_CONFLICT 0x18
+
+/* cmd->result */
+#define STATUS_MASK_ 0xFF
+#define MSG_MASK 0xFF00
+#define RETURN_MASK 0xFF0000
+
+/*
+** Inquiry Data format
+*/
+
+typedef struct _SCSIInqData { /* INQ */
+
+ UCHAR DevType; /* Periph Qualifier & Periph Dev Type*/
+ UCHAR RMB_TypeMod; /* rem media bit & Dev Type Modifier */
+ UCHAR Vers; /* ISO, ECMA, & ANSI versions */
+ UCHAR RDF; /* AEN, TRMIOP, & response data format*/
+ UCHAR AddLen; /* length of additional data */
+ UCHAR Res1; /* reserved */
+ UCHAR Res2; /* reserved */
+ UCHAR Flags; /* RelADr,Wbus32,Wbus16,Sync,etc. */
+ UCHAR VendorID[8]; /* Vendor Identification */
+ UCHAR ProductID[16]; /* Product Identification */
+ UCHAR ProductRev[4]; /* Product Revision */
+
+
+} SCSI_INQDATA, *PSCSI_INQDATA;
+
+
+/* Inquiry byte 0 masks */
+
+
+#define SCSI_DEVTYPE 0x1F /* Peripheral Device Type */
+#define SCSI_PERIPHQUAL 0xE0 /* Peripheral Qualifier */
+
+
+/* Inquiry byte 1 mask */
+
+#define SCSI_REMOVABLE_MEDIA 0x80 /* Removable Media bit (1=removable) */
+
+
+/* Peripheral Device Type definitions */
+
+#define SCSI_DASD 0x00 /* Direct-access Device */
+#define SCSI_SEQACESS 0x01 /* Sequential-access device */
+#define SCSI_PRINTER 0x02 /* Printer device */
+#define SCSI_PROCESSOR 0x03 /* Processor device */
+#define SCSI_WRITEONCE 0x04 /* Write-once device */
+#define SCSI_CDROM 0x05 /* CD-ROM device */
+#define SCSI_SCANNER 0x06 /* Scanner device */
+#define SCSI_OPTICAL 0x07 /* Optical memory device */
+#define SCSI_MEDCHGR 0x08 /* Medium changer device */
+#define SCSI_COMM 0x09 /* Communications device */
+#define SCSI_NODEV 0x1F /* Unknown or no device type */
+
+/*
+** Inquiry flag definitions (Inq data byte 7)
+*/
+
+#define SCSI_INQ_RELADR 0x80 /* device supports relative addressing*/
+#define SCSI_INQ_WBUS32 0x40 /* device supports 32 bit data xfers */
+#define SCSI_INQ_WBUS16 0x20 /* device supports 16 bit data xfers */
+#define SCSI_INQ_SYNC 0x10 /* device supports synchronous xfer */
+#define SCSI_INQ_LINKED 0x08 /* device supports linked commands */
+#define SCSI_INQ_CMDQUEUE 0x02 /* device supports command queueing */
+#define SCSI_INQ_SFTRE 0x01 /* device supports soft resets */
+
+
+/*
+;==========================================================
+; EEPROM byte offset
+;==========================================================
+*/
+typedef struct _EEprom
+{
+UCHAR EE_MODE1;
+UCHAR EE_SPEED;
+UCHAR xx1;
+UCHAR xx2;
+} EEprom, *PEEprom;
+
+#define EE_ADAPT_SCSI_ID 64
+#define EE_MODE2 65
+#define EE_DELAY 66
+#define EE_TAG_CMD_NUM 67
+
+/*; EE_MODE1 bits definition*/
+#define PARITY_CHK_ BIT0
+#define SYNC_NEGO_ BIT1
+#define EN_DISCONNECT_ BIT2
+#define SEND_START_ BIT3
+#define TAG_QUEUING_ BIT4
+#define WIDE_NEGO_ BIT5
+
+/*; EE_MODE2 bits definition*/
+#define MORE2_DRV BIT0
+#define GREATER_1G BIT1
+#define RST_SCSI_BUS BIT2
+#define ACTIVE_NEGATION BIT3
+#define NO_SEEK BIT4
+#define LUN_CHECK BIT5
+
+#define ENABLE_CE 1
+#define DISABLE_CE 0
+#define EEPROM_READ 0x80
+
+/*
+;==========================================================
+; NCR 53C825 Registers Structure
+;==========================================================
+*/
+typedef struct _OperatingReg
+{
+UCHAR SCSICtrl0; /*; (+00)*/
+UCHAR SCSICtrl1; /*; (+01)*/
+UCHAR SCSICtrl2; /*; (+02)*/
+UCHAR SCSICtrl3; /*; (+03)*/
+UCHAR SCSIChipID; /*; (+04)*/
+UCHAR SCSIXfer; /*; (+05)*/
+UCHAR SCSIDestID; /*; (+06)*/
+UCHAR GeneralPReg; /*; (+07)*/
+UCHAR SCSI1stByteR; /*; (+08)*/
+UCHAR SCSIOPCtrlL; /*; (+09)*/
+UCHAR SCSISelID; /*; (+0A)*/
+UCHAR SCSIBusCtrlLin; /*; (+0B)*/
+UCHAR DMAStatus; /*; (+0C)*/
+UCHAR SCSIStatus0; /*; (+0D)*/
+UCHAR SCSIStatus1; /*; (+0E)*/
+UCHAR SCSIStatus2; /*; (+0F)*/
+ULONG DataStrucAddr; /*; (+10)*/
+UCHAR InterruptStatus;/*; (+14)*/
+UCHAR Reser1; /*; (+15)*/
+UCHAR Reser2; /*; (+16)*/
+UCHAR Reser3; /*; (+17)*/
+UCHAR ChipTest0; /*; (+18)*/
+UCHAR ChipTest1; /*; (+19)*/
+UCHAR ChipTest2; /*; (+1A)*/
+UCHAR ChipTest3; /*; (+1B)*/
+ULONG TempStack; /*; (+1C)*/
+UCHAR DMAFIFO; /*; (+20)*/
+UCHAR ChipTest4; /*; (+21)*/
+UCHAR ChipTest5; /*; (+22)*/
+UCHAR ChipTest6; /*; (+23)*/
+UCHAR DMAByteCnt[3]; /*; (+24)*/
+UCHAR DMACommand; /*; (+27)*/
+ULONG DMANextAddr; /*; (+28)*/
+ULONG DMAScriptsPtr; /*; (+2C)*/
+ULONG DMAScriptPtrSav;/*; (+30)*/
+ULONG ScratchA; /*; (+34)*/
+UCHAR DMAMode; /*; (+38)*/
+UCHAR DMAIntEnable; /*; (+39)*/
+UCHAR DMAWatchDog; /*; (+3A)*/
+UCHAR DMACtrl; /*; (+3B)*/
+ULONG ADDer; /*; (+3C)*/
+UCHAR SCSIIntEnable0; /*; (+40)*/
+UCHAR SCSIIntEnable1; /*; (+41)*/
+UCHAR SCSIIntStatus0; /*; (+42)*/
+UCHAR SCSIIntStatus1; /*; (+43)*/
+UCHAR SCSILongParity; /*; (+44)*/
+UCHAR WideResiData; /*; (+45)*/
+UCHAR MemAccessCtrl; /*; (+46)*/
+UCHAR GeneralPCtrl; /*; (+47)*/
+UCHAR SCSITimer0; /*; (+48)*/
+UCHAR SCSITimer1; /*; (+49)*/
+UCHAR ResponseID0; /*; (+4A)*/
+UCHAR ResponseID1; /*; (+4B)*/
+UCHAR SCSITest0; /*; (+4C)*/
+UCHAR SCSITest1; /*; (+4D)*/
+UCHAR SCSITest2; /*; (+4E)*/
+UCHAR SCSITest3; /*; (+4F)*/
+USHORT SCSIIPDataL; /*; (+50)*/
+USHORT Reser4; /*; (+52)*/
+USHORT SCSIOPDataL; /*; (+54)*/
+USHORT Reser5; /*; (+56)*/
+USHORT SCSIBusDataLin; /*; (+58)*/
+USHORT Reser6; /*; (+5A)*/
+ULONG ScratchB; /*; (+5C)*/
+ULONG ScratchC; /*; (+60)*/
+ULONG ScratchD; /*; (+64)*/
+ULONG ScratchE; /*; (+68)*/
+ULONG ScratchF; /*; (+6C)*/
+ULONG ScratchG; /*; (+70)*/
+ULONG ScratchH; /*; (+74)*/
+ULONG ScratchI; /*; (+78)*/
+ULONG ScratchJ; /*; (+7C)*/
+} OperatingReg;
+/*
+;==========================================================
+; NCR 53C825 Registers bit Definition
+;==========================================================
+*/
+/*; SCSICtrl0 (+00)*/
+#define FULL_ARBITRATION (BIT7+BIT6)
+#define START_ARBIT BIT5
+#define SEL_W_SATN BIT4
+#define EN_PARITY_CHK BIT3
+#define SATN_IF_PARITY_ERR BIT1
+#define TARGET_MODE BIT0
+
+/*; SCSICtrl1 (+01)*/
+#define ASSERT_RST BIT3
+#define ASSERT_EVEN_P BIT2
+#define START_SCSI_XFER BIT0
+
+/*; SCSICtrl2 (+02)*/
+#define DISC_UNEXPECTED BIT7
+#define CHAIN_MODE BIT6
+#define WIDE_SCSI_SEND BIT3
+#define WIDE_SCSI_RECV BIT0
+
+/*; SCSICtrl3 (+03)*/
+#define EN_FAST_20 BIT7
+#define SYNC_CLK_F4 (BIT6+BIT4)
+#define SYNC_CLK_F3 BIT6
+#define SYNC_CLK_F2 (BIT5+BIT4)
+#define SYNC_CLK_F1_5 BIT5
+#define SYNC_CLK_F1 BIT4
+#define EN_WIDE_SCSI BIT3
+#define ASYNC_CLK_F4 (BIT2+BIT0)
+#define ASYNC_CLK_F3 BIT2
+#define ASYNC_CLK_F2 (BIT1+BIT0)
+#define ASYNC_CLK_F1_5 BIT1
+#define ASYNC_CLK_F1 BIT0
+
+/*; SCSIChipID (+04)*/
+#define ENABLE_RESEL BIT6
+#define ENABLE_SEL BIT5
+#define CHIP_ID_MASK 0FH
+
+/*; SCSIXfer (+05)*/
+#define PERIOD_MASK (BIT7+BIT6+BIT5)
+#define SYNC_PERIOD_F11 (BIT7+BIT6+BIT5)
+#define SYNC_PERIOD_F10 (BIT7+BIT6)
+#define SYNC_PERIOD_F9 (BIT7+BIT5)
+#define SYNC_PERIOD_F8 BIT7
+#define SYNC_PERIOD_F7 (BIT6+BIT5)
+#define SYNC_PERIOD_F6 BIT6
+#define SYNC_PERIOD_F5 BIT5
+#define SYNC_PERIOD_F4 0
+#define OFFSET_MASK (BIT4+BIT3+BIT2+BIT1+BIT0)
+#define SYNC_OFFSET_16 BIT4
+#define SYNC_OFFSET_8 BIT3
+#define SYNC_OFFSET_7 (BIT2+BIT1+BIT0)
+#define SYNC_OFFSET_6 (BIT2+BIT1)
+#define SYNC_OFFSET_5 (BIT2+BIT0)
+#define SYNC_OFFSET_4 BIT2
+#define SYNC_OFFSET_3 (BIT1+BIT0)
+#define SYNC_OFFSET_2 BIT1
+#define SYNC_OFFSET_1 BIT0
+#define SYNC_OFFSET_0 0
+#define ASYNCHRONOUS SYNC_OFFSET_0
+/*
+; SCSIDestID (+06)
+
+; GeneralPReg (+07)
+
+; SCSI1stByteR (+08)
+*/
+/*; SCSIOPCtrlL (+09)*/
+#define ASSERT_REQ BIT7
+#define ASSERT_ACK BIT6
+#define ASSERT_BSY BIT5
+#define ASSERT_SEL BIT4
+#define ASSERT_ATN BIT3
+#define ASSERT_MSG BIT2
+#define ASSERT_C_D BIT1
+#define ASSERT_I_O BIT0
+
+/*; SCSISelID (+0A) Read*/
+#define SCSI_ID_VALID BIT7
+
+/*; SCSIBusCtrlLin (+0B) Read*/
+#define REQ_SIGNAL BIT7
+#define ACK_SIGNAL BIT6
+#define BSY_SIGNAL BIT5
+#define SEL_SIGNAL BIT4
+#define ATN_SIGNAL BIT3
+#define MSG_SIGNAL BIT2
+#define C_D_SIGNAL BIT1
+#define I_O_SIGNAL BIT0
+
+/*; DMAStatus (+0C) Read*/
+#define DMA_FIFO_EMPTY BIT7
+#define MASTER_PARITY_ERR BIT6
+#define BUS_FAULT BIT5
+#define ABORT_ BIT4
+#define SINGLE_STEP_INT BIT3
+#define SCRIPTS_INT BIT2
+#define ILLEGAL_INSTRUC BIT0
+
+/*; SCSIStatus0 (+0D) Read*/
+#define SIDL_LSB_FULL BIT7
+#define SODR_LSB_FULL BIT6
+#define SODL_LSB_FULL BIT5
+#define IN_ARBITRATION BIT4
+#define LOST_ARBITRATION BIT3
+#define WIN_ARBITRATION BIT2
+#define RST_SIGNAL BIT1
+#define PARITY_SIGNAL BIT0
+
+/*; SCSIStatus1 (+0E) Read*/
+#define SCSI_FIFO_MASK (BIT7+BIT6+BIT5+BIT4)
+
+/*; SCSIStatus2 (+0F) Read*/
+#define SIDL_MSB_FULL BIT7
+#define SODR_MSB_FULL BIT6
+#define SODL_MSB_FULL BIT5
+#define SCSI_FIFO_MSB BIT4
+
+/*; DataStrucAddr (+10)*/
+
+/*; InterruptStatus (+14)*/
+#define ABORT_OP BIT7
+#define SOFTWARE_RST BIT6
+#define SIGNAL_PROC BIT5
+#define SEMAPHORE BIT4
+#define CONNECTED BIT3
+#define INT_ON_FLY BIT2
+#define SCSI_INT_PENDING BIT1
+#define DMA_INT_PENDING BIT0
+
+/*; ChipTest0 (+18)*/
+/*; ChipTest1 (+19)*/
+#define FIFO_BYTE_EMPTY (BIT7+BIT6+BIT5+BIT4)
+#define FIFO_BYTE_FULL (BIT3+BIT2+BIT1+BIT0)
+
+/*; ChipTest2 (+1A)*/
+#define READ_DIR BIT7
+#define WRITE_DIR 0
+#define SIGNAL_PROC_ BIT6
+#define CFG_AS_IO_MAP BIT5
+#define CFG_AS_MEM_MAP BIT4
+#define SCRATCHAB_AS_BASE BIT3
+#define TRUE_EOP BIT2
+#define INTERNAL_DREQ BIT1
+#define INTERNAL_DACK BIT0
+
+/*; ChipTest3 (+1B)*/
+#define CHIP_REV_MASK (BIT7+BIT6+BIT5+BIT4)
+#define FLUSH_DMA_FIFO BIT3
+#define CLR_DMA_FIFO BIT2
+#define FETCH_PIN_MODE BIT1
+#define WRT_EN_INVALIDATE BIT0
+
+/*; TempStack (+1C)*/
+
+/*; DMAFIFO (+20)*/
+/*; 2 upper bits in ChipTest5*/
+
+/*; ChipTest4 (+21)*/
+#define BURST_DISABLE BIT7
+
+/*; ChipTest5 (+22) */
+#define EN_DMA_FIFO_536 BIT5
+#define BURST_LEN_MSB BIT2
+#define DMAFIFO_MS2B (BIT1+BIT0)
+/*
+; ChipTest6 (+23)
+; DMAByteCnt (+24)
+; DMACommand (+27)
+; DMANextAddr (+28)
+; DMAScriptsPtr (+2C)
+; DMAScriptPtrSav (+30)
+*/
+/*; ScratchA (+34)*/
+#define COMPLETED_OK_ BIT0
+#define RE_SELECTED_ BIT1
+#define OVER_RUN_ BIT2
+
+/*; DMAMode (+38)*/
+#define BURST_LEN16 (BIT7+BIT6)
+#define BURST_LEN8 BIT7
+#define BURST_LEN4 BIT6
+#define BURST_LEN2 0
+#define SRC_IO_MAP BIT5
+#define SRC_MEM_MAP 0
+#define DEST_IO_MAP BIT4
+#define DEST_MEM_MAP 0
+#define EN_READ_LINE BIT3
+#define EN_READ_MULTIPLE BIT2
+#define BURST_OPCODE_FETCH BIT1
+#define MANUAL_START BIT0
+#define AUTO_START 0
+
+/*; DMAIntEnable (+39)*/
+#define EN_MDPE BIT6
+#define EN_BUS_FAULT BIT5
+#define EN_ABORTED BIT4
+#define EN_SINGLE_STEP BIT3
+#define EN_SCRIPT_INT BIT2
+#define EN_ILLEGAL_INST BIT0
+
+
+/*; DMAWatchDog (+3A)*/
+
+/*; DMACtrl (+3B)*/
+#define EN_CACHE_LINE_SIZE BIT7
+#define PRE_FETCH_FLUSH BIT6
+#define EN_PRE_FETCH BIT5
+#define SINGLE_STEP_MIDE BIT4
+#define TOTEM_POLE_IRQ BIT3
+#define OPEN_DRAIN_IRQ 0
+#define START_DMA BIT2
+#define IRQ_DISABLE BIT1
+#define COMPATIBLE_700 BIT0
+
+/*; ADDer (+3C)*/
+
+/*; SCSIIntEnable0 (+40)*/
+#define EN_PHASE_MISMATCH BIT7
+#define EN_ARB_SEL_DONE BIT6
+#define EN_BE_SELECTED BIT5
+#define EN_BE_RESELECTED BIT4
+#define EN_SCSI_GERROR BIT3
+#define EN_UNEXPECT_DISC BIT2
+#define EN_SCSI_RESET BIT1
+#define EN_PARITY_ERROR BIT0
+
+/*; SCSIIntEnable1 (+41)*/
+#define EN_SEL_TIMEOUT BIT2
+#define EN_GENERAL_TIMEOUT BIT1
+#define EN_HANDSHAKE_TIMEOUT BIT0
+
+/*; SCSIIntStatus0 (+42)*/
+#define PHASE_MISMATCH BIT7
+#define ARB_SEL_DONE BIT6
+#define BE_SELECTED BIT5
+#define BE_RESELECTED BIT4
+#define SCSI_GERROR BIT3
+#define UNEXPECT_DISC BIT2
+#define SCSI_RESET BIT1
+#define PARITY_ERROR BIT0
+
+/*; SCSIIntStatus1 (+43)*/
+#define SEL_TIMEOUT BIT2
+#define GENERAL_TIMEOUT BIT1
+#define HANDSHAKE_TIMEOUT BIT0
+/*
+; SCSILongParity (+44)
+; WideResiData (+45)
+; MemAccessCtrl (+46)
+; GeneralPCtrl (+47)
+*/
+/*; SCSITimer0 (+48)*/
+#define HTH_TO_DISABLE 0
+#define SEL_TO_204ms (BIT3+BIT2)
+/*
+; SCSITimer1 (+49)
+; ResponseID0 (+4A)
+; ResponseID1 (+4B)
+; SCSITest0 (+4C)
+; SCSITest1 (+4D)
+; SCSITest2 (+4E)
+*/
+/*; SCSITest3 (+4F)*/
+#define ACTIVE_NEGATION_ BIT7
+#define FIFO_TEST_READ BIT6
+#define HALT_SCSI_CLK BIT5
+#define DIS_SINGLE_INIT BIT4
+#define TIMER_TEST_MODE BIT2
+#define CLR_SCSI_FIFO BIT1
+#define FIFO_TEST_WRITE BIT0
+/*
+; SCSIIPDataL (+50)
+; SCSIOPDataL (+54)
+; SCSIBusDataLin (+58)
+; ScratchB (+5C)
+; ScratchC (+60)
+; ScratchD (+64)
+; ScratchE (+68)
+; ScratchF (+6C)
+; ScratchG (+70)
+; ScratchH (+74)
+; ScratchI (+78)
+; ScratchJ (+7C)
+*/
+
+/***********************************************************************
+ * NCR53C825 I/O Address Map
+ * base address is stored at offset 0x10 in the PCI configuration space
+ *-----------------------------------------------------------------------
+ * SCSI Core Registers Offset
+ ***********************************************************************/
+#define SCNTL0 0x00 /* Scsi Control 0 R/W */
+#define SCNTL1 0x01 /* Scsi Control 1 R/W */
+#define SCNTL2 0x02 /* Scsi Control 2 R/W */
+#define SCNTL3 0x03 /* Scsi Control 3 R/W */
+#define SCID 0x04 /* Scsi Chip ID R/W */
+#define SXFER 0x05 /* Scsi Transfer R/W */
+#define SDID 0x06 /* Scsi Destination ID R/W */
+#define GPREG 0x07 /* General Purpose Bits R/W */
+#define SFBR 0x08 /* Scsi First Byte Received R/W */
+#define SOCL 0x09 /* Scsi Output Control Latch R/W */
+#define SSID 0x0A /* Scsi Selector ID R */
+#define SBCL 0x0B /* Scsi Bus Control Lines R/W */
+#define DSTAT 0x0C /* DMA Status R */
+#define SSTAT0 0x0D /* Scsi Status 0 R */
+#define SSTAT1 0x0E /* Scsi Status 1 R */
+#define SSTAT2 0x0F /* Scsi Status 2 R */
+#define DSA 0x10 /* Data Structure Address R/W */
+#define ISTAT 0x14 /* Interrupt Status R/W */
+#define RES15 0x15 /* Reserved */
+#define CTEST0 0x18 /* Reserved */
+#define CTEST1 0x19 /* Chip Test 1 R/W */
+#define CTEST2 0x1A /* Chip Test 2 R */
+#define CTEST3 0x1B /* Chip Test 3 R */
+#define TEMP 0x1C /* Temperary Stack R/W */
+#define DFIFO 0x20 /* DMA FIFO R/W */
+#define CTEST4 0x21 /* Chip Test 4 R/W */
+#define CTEST5 0x22 /* Chip Test 5 R/W */
+#define CTEST6 0x23 /* Chip Test 6 R/W */
+#define DBC 0x24 /* DMA Byte Counter R/W */
+#define DCMD 0x27 /* DMA Command R/W */
+#define DNAD 0x28 /* DMA Next Address For Data R/W */
+#define DSP 0x2C /* DMA SCIPTS Pointer R/W */
+#define DSPS 0x30 /* DMA SCIPTS Pointer Saves R/W */
+#define SCRATCHA 0x34 /* General Purpose Scratch Pad A R/W */
+#define DMODE 0x38 /* DMA Mode R/W */
+#define DIEN 0x39 /* DMA Interrupt Enable R/W */
+#define DWT 0x3A /* DMA Watchdog Timer R/W */
+#define DCNTL 0x3B /* DMA Control R/W */
+#define ADDER 0x3C /* Sum output of Internal adder R */
+#define SIEN0 0x40 /* Scsi Interrupt Enable 0 R/W */
+#define SIEN1 0x41 /* Scsi Interrupt Enable 1 R/W */
+#define SIST0 0x42 /* Scsi Interrupt Status 0 R */
+#define SIST1 0x43 /* Scsi Interrupt Status 1 R */
+#define SLPAR 0x44 /* Scsi Longitudinal Parity R/W */
+#define SWIDE 0x45 /* Reserved */
+#define MACNTL 0x46 /* Memory Access Control R/W */
+#define GPCNTL 0x47 /* General Purpose Control R/W */
+#define STIME0 0x48 /* Scsi Timer 0 R/W */
+#define STIME1 0x49 /* Scsi Timer 1 R/W */
+#define RESPID0 0x4A /* Response ID 0 R/W */
+#define RESPID1 0x4B /* Response ID 1 R/W */
+#define STEST0 0x4C /* Scsi Test 0 R */
+#define STEST1 0x4D /* Scsi Test 1 R */
+#define STEST2 0x4E /* Scsi Test 2 R/W */
+#define STEST3 0x4F /* Scsi Test 3 R/W */
+#define SIDL 0x50 /* Scsi Input Data Latch R */
+#define RES51 0x51 /* Reserved */
+#define SODL 0x54 /* Scsi Output Data Latch R/W */
+#define RES55 0x55 /* Reserved */
+#define SBDL 0x58 /* Scsi Bus Data Lines R */
+#define RES59 0x59 /* Reserved */
+#define SCRATCHB 0x5C /* General Purpose Scratch Pad B R/W */
+
+
+/*
+;==========================================================
+; structure for 53C825A register ( I/O )
+;==========================================================
+*/
+#define __scntl0 0
+#define __scntl1 1
+#define __scntl2 2
+#define __scntl3 3
+#define __scid 4
+#define __sxfer 5
+#define __sdid 6
+#define __gpreg 7
+#define __sfbr 8
+#define __socl 9
+#define __ssid 0x0A
+#define __sbcl 0x0B
+#define __dstat 0x0C
+#define __sstat0 0x0D
+#define __sstat1 0x0E
+#define __sstat2 0x0F
+#define __dsa 0x10
+#define __istat 0x14
+/*#define 0x15*/
+#define __ctest0 0x18
+#define __ctest1 0x19
+#define __ctest2 0x1A
+#define __ctest3 0x1B
+#define __temp 0x1C
+#define __dfifo 0x20
+#define __ctest4 0x21
+#define __ctest5 0x22
+#define __ctest6 0x23
+#define __dbc 0x24
+#define __dcmd 0x27
+#define __dnad 0x28
+#define __dsp 0x2C
+#define __dsps 0x30
+#define __scratcha 0x34
+#define __dmode 0x38
+#define __dien 0x39
+#define __dwt 0x3A
+#define __dcntl 0x3B
+#define __adder 0x3C
+#define __sien0 0x40
+#define __sien1 0x41
+#define __sist0 0x42
+#define __sist1 0x43
+#define __slpar 0x44
+#define __swide 0x45
+#define __mactrl 0x46
+#define __gpctrl 0x47
+#define __stime0 0x48
+#define __stime1 0x49
+#define __respid0 0x4A
+#define __respid1 0x4B
+#define __stest0 0x4C
+#define __stest1 0x4D
+#define __stest2 0x4E
+#define __stest3 0x4F
+#define __sidl 0x50 /*; For wide SCSI, this register*/
+/*#define 0x51 ; contains 2 bytes*/
+#define __sodl 0x54
+/*#define 0x55*/
+#define __sbdl 0x58
+/*#define 0x59*/
+#define __scratchb 0x5C
+/*
+;==========================================================
+; Script interrupt code(Vector)
+;==========================================================
+*/
+#define __COMPLETE 0 /*; command complete*/
+#define __RESELECTED 1 /*; reselected, without MESSAGE*/
+#define __RESELECTED1 2 /*; reselected with idenntify bytes*/
+#define __RESELECTEDT 3 /*; reselected with TAGS*/
+#define __DISCONNECTED 4 /*; disconnected*/
+#define __MSGEXTEND 5 /*; Extended msgin*/
+#define __SIGNAL 6 /*; signaled, need to clear SIG*/
+#define __MSGUNKNOWN 7 /*; Unknown message*/
+#define __MSGOUT 8 /*; Message out phase detected*/
+#define __FATALERROR 9 /*; Fatal error*/
+#define __MSGSYNC 10 /*; Sync nego input*/
+#define __MSGWIDE 11 /*; Wide nego input*/
+#define __RESTOREPTR 12 /*; restore pointer received*/
+#define __MSGREJECT 13 /*; Reject message received*/
+#define __DEBUG 14 /*; For debug*/
+
+
+#define DC390W_read8(address) \
+ inb(DC390W_ioport + (address)))
+
+#define DC390W_read16(address) \
+ inw(DC390W_ioport + (address)))
+
+#define DC390W_read32(address) \
+ inl(DC390W_ioport + (address)))
+
+#define DC390W_write8(address,value) \
+ outb((value), DC390W_ioport + (address)))
+
+#define DC390W_write16(address,value) \
+ outw((value), DC390W_ioport + (address)))
+
+#define DC390W_write32(address,value) \
+ outl((value), DC390W_ioport + (address)))
+
+#endif /* TMSCSIW_H */
diff --git a/linux/src/drivers/scsi/u14-34f.c b/linux/src/drivers/scsi/u14-34f.c
new file mode 100644
index 00000000..cad88bc2
--- /dev/null
+++ b/linux/src/drivers/scsi/u14-34f.c
@@ -0,0 +1,1995 @@
+/*
+ * u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
+ *
+ * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111
+ * Added command line option (et:[y|n]) to use the existing
+ * translation (returned by scsicam_bios_param) as disk geometry.
+ * The default is et:n, which uses the disk geometry jumpered
+ * on the board.
+ * The default value et:n is compatible with all previous revisions
+ * of this driver.
+ *
+ * 28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104
+ * Increased busy timeout from 10 msec. to 200 msec. while
+ * processing interrupts.
+ *
+ * 18 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102
+ * Improved abort handling during the eh recovery process.
+ *
+ * 13 May 1998 Rev. 4.30 for linux 2.0.33 and 2.1.101
+ * The driver is now fully SMP safe, including the
+ * abort and reset routines.
+ * Added command line options (eh:[y|n]) to choose between
+ * new_eh_code and the old scsi code.
+ * If linux version >= 2.1.101 the default is eh:y, while the eh
+ * option is ignored for previous releases and the old scsi code
+ * is used.
+ *
+ * 18 Apr 1998 Rev. 4.20 for linux 2.0.33 and 2.1.97
+ * Reworked interrupt handler.
+ *
+ * 11 Apr 1998 rev. 4.05 for linux 2.0.33 and 2.1.95
+ * Major reliability improvement: when a batch with overlapping
+ * requests is detected, requests are queued one at a time
+ * eliminating any possible board or drive reordering.
+ *
+ * 10 Apr 1998 rev. 4.04 for linux 2.0.33 and 2.1.95
+ * Improved SMP support (if linux version >= 2.1.95).
+ *
+ * 9 Apr 1998 rev. 4.03 for linux 2.0.33 and 2.1.94
+ * Performance improvement: when sequential i/o is detected,
+ * always use direct sort instead of reverse sort.
+ *
+ * 4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92
+ * io_port is now unsigned long.
+ *
+ * 17 Mar 1998 rev. 4.01 for linux 2.0.33 and 2.1.88
+ * Use new scsi error handling code (if linux version >= 2.1.88).
+ * Use new interrupt code.
+ *
+ * 12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55
+ * Use of udelay inside the wait loops to avoid timeout
+ * problems with fast cpus.
+ * Removed check about useless calls to the interrupt service
+ * routine (reported on SMP systems only).
+ * At initialization time "sorted/unsorted" is displayed instead
+ * of "linked/unlinked" to reinforce the fact that "linking" is
+ * nothing but "elevator sorting" in the actual implementation.
+ *
+ * 17 May 1997 rev. 3.10 for linux 2.0.30 and 2.1.38
+ * Use of serial_number_at_timeout in abort and reset processing.
+ * Use of the __initfunc and __initdata macro in setup code.
+ * Minor cleanups in the list_statistics code.
+ *
+ * 24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
+ * When loading as a module, parameter passing is now supported
+ * both in 2.0 and in 2.1 style.
+ * Fixed data transfer direction for some SCSI opcodes.
+ * Immediate acknowledge to request sense commands.
+ * Linked commands to each disk device are now reordered by elevator
+ * sorting. Rare cases in which reordering of write requests could
+ * cause wrong results are managed.
+ *
+ * 18 Jan 1997 rev. 2.60 for linux 2.1.21 and 2.0.28
+ * Added command line options to enable/disable linked commands
+ * (lc:[y|n]), old firmware support (of:[y|n]) and to set the max
+ * queue depth (mq:xx). Default is "u14-34f=lc:n,of:n,mq:8".
+ * Improved command linking.
+ *
+ * 8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27
+ * Added linked command support.
+ *
+ * 3 Dec 1996 rev. 2.40 for linux 2.1.14 and 2.0.27
+ * Added queue depth adjustment.
+ *
+ * 22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26
+ * The list of i/o ports to be probed can be overwritten by the
+ * "u14-34f=port0,port1,...." boot command line option.
+ * Scatter/gather lists are now allocated by a number of kmalloc
+ * calls, in order to avoid the previous size limit of 64Kb.
+ *
+ * 16 Nov 1996 rev. 2.20 for linux 2.1.10 and 2.0.25
+ * Added multichannel support.
+ *
+ * 27 Sep 1996 rev. 2.12 for linux 2.1.0
+ * Portability cleanups (virtual/bus addressing, little/big endian
+ * support).
+ *
+ * 09 Jul 1996 rev. 2.11 for linux 2.0.4
+ * "Data over/under-run" no longer implies a redo on all targets.
+ * Number of internal retries is now limited.
+ *
+ * 16 Apr 1996 rev. 2.10 for linux 1.3.90
+ * New argument "reset_flags" to the reset routine.
+ *
+ * 21 Jul 1995 rev. 2.02 for linux 1.3.11
+ * Fixed Data Transfer Direction for some SCSI commands.
+ *
+ * 13 Jun 1995 rev. 2.01 for linux 1.2.10
+ * HAVE_OLD_UX4F_FIRMWARE should be defined for U34F boards when
+ * the firmware prom is not the latest one (28008-006).
+ *
+ * 11 Mar 1995 rev. 2.00 for linux 1.2.0
+ * Fixed a bug which prevented media change detection for removable
+ * disk drives.
+ *
+ * 23 Feb 1995 rev. 1.18 for linux 1.1.94
+ * Added a check for scsi_register returning NULL.
+ *
+ * 11 Feb 1995 rev. 1.17 for linux 1.1.91
+ * U14F qualified to run with 32 sglists.
+ * Now DEBUG_RESET is disabled by default.
+ *
+ * 9 Feb 1995 rev. 1.16 for linux 1.1.90
+ * Use host->wish_block instead of host->block.
+ *
+ * 8 Feb 1995 rev. 1.15 for linux 1.1.89
+ * Cleared target_time_out counter while performing a reset.
+ *
+ * 28 Jan 1995 rev. 1.14 for linux 1.1.86
+ * Added module support.
+ * Log and do a retry when a disk drive returns a target status
+ * different from zero on a recovered error.
+ * Auto detects if U14F boards have an old firmware revision.
+ * Max number of scatter/gather lists set to 16 for all boards
+ * (most installation run fine using 33 sglists, while other
+ * has problems when using more then 16).
+ *
+ * 16 Jan 1995 rev. 1.13 for linux 1.1.81
+ * Display a message if check_region detects a port address
+ * already in use.
+ *
+ * 15 Dec 1994 rev. 1.12 for linux 1.1.74
+ * The host->block flag is set for all the detected ISA boards.
+ *
+ * 30 Nov 1994 rev. 1.11 for linux 1.1.68
+ * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only.
+ * Added optional support for using a single board at a time.
+ *
+ * 14 Nov 1994 rev. 1.10 for linux 1.1.63
+ *
+ * 28 Oct 1994 rev. 1.09 for linux 1.1.58 Final BETA release.
+ * 16 Jul 1994 rev. 1.00 for linux 1.1.29 Initial ALPHA release.
+ *
+ * This driver is a total replacement of the original UltraStor
+ * scsi driver, but it supports ONLY the 14F and 34F boards.
+ * It can be configured in the same kernel in which the original
+ * ultrastor driver is configured to allow the original U24F
+ * support.
+ *
+ * Multiple U14F and/or U34F host adapters are supported.
+ *
+ * Copyright (C) 1994-1998 Dario Ballabio (dario@milano.europe.dg.com)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that redistributions of source
+ * code retain the above copyright notice and this comment without
+ * modification.
+ *
+ * WARNING: if your 14/34F board has an old firmware revision (see below)
+ * you must change "#undef" into "#define" in the following
+ * statement.
+ */
+#undef HAVE_OLD_UX4F_FIRMWARE
+/*
+ * The UltraStor 14F, 24F, and 34F are a family of intelligent, high
+ * performance SCSI-2 host adapters.
+ * Here is the scoop on the various models:
+ *
+ * 14F - ISA first-party DMA HA with floppy support and WD1003 emulation.
+ * 24F - EISA Bus Master HA with floppy support and WD1003 emulation.
+ * 34F - VESA Local-Bus Bus Master HA (no WD1003 emulation).
+ *
+ * This code has been tested with up to two U14F boards, using both
+ * firmware 28004-005/38004-004 (BIOS rev. 2.00) and the latest firmware
+ * 28004-006/38004-005 (BIOS rev. 2.01).
+ *
+ * The latest firmware is required in order to get reliable operations when
+ * clustering is enabled. ENABLE_CLUSTERING provides a performance increase
+ * up to 50% on sequential access.
+ *
+ * Since the Scsi_Host_Template structure is shared among all 14F and 34F,
+ * the last setting of use_clustering is in effect for all of these boards.
+ *
+ * Here a sample configuration using two U14F boards:
+ *
+ U14F0: ISA 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 32, MB 16, of:n, lc:y, mq:8.
+ U14F1: ISA 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 32, MB 16, of:n, lc:y, mq:8.
+ *
+ * The boot controller must have its BIOS enabled, while other boards can
+ * have their BIOS disabled, or enabled to an higher address.
+ * Boards are named Ux4F0, Ux4F1..., according to the port address order in
+ * the io_port[] array.
+ *
+ * The following facts are based on real testing results (not on
+ * documentation) on the above U14F board.
+ *
+ * - The U14F board should be jumpered for bus on time less or equal to 7
+ * microseconds, while the default is 11 microseconds. This is order to
+ * get acceptable performance while using floppy drive and hard disk
+ * together. The jumpering for 7 microseconds is: JP13 pin 15-16,
+ * JP14 pin 7-8 and pin 9-10.
+ * The reduction has a little impact on scsi performance.
+ *
+ * - If scsi bus length exceeds 3m., the scsi bus speed needs to be reduced
+ * from 10Mhz to 5Mhz (do this by inserting a jumper on JP13 pin 7-8).
+ *
+ * - If U14F on board firmware is older than 28004-006/38004-005,
+ * the U14F board is unable to provide reliable operations if the scsi
+ * request length exceeds 16Kbyte. When this length is exceeded the
+ * behavior is:
+ * - adapter_status equal 0x96 or 0xa3 or 0x93 or 0x94;
+ * - adapter_status equal 0 and target_status equal 2 on for all targets
+ * in the next operation following the reset.
+ * This sequence takes a long time (>3 seconds), so in the meantime
+ * the SD_TIMEOUT in sd.c could expire giving rise to scsi aborts
+ * (SD_TIMEOUT has been increased from 3 to 6 seconds in 1.1.31).
+ * Because of this I had to DISABLE_CLUSTERING and to work around the
+ * bus reset in the interrupt service routine, returning DID_BUS_BUSY
+ * so that the operations are retried without complains from the scsi.c
+ * code.
+ * Any reset of the scsi bus is going to kill tape operations, since
+ * no retry is allowed for tapes. Bus resets are more likely when the
+ * scsi bus is under heavy load.
+ * Requests using scatter/gather have a maximum length of 16 x 1024 bytes
+ * when DISABLE_CLUSTERING is in effect, but unscattered requests could be
+ * larger than 16Kbyte.
+ *
+ * The new firmware has fixed all the above problems.
+ *
+ * For U34F boards the latest bios prom is 38008-002 (BIOS rev. 2.01),
+ * the latest firmware prom is 28008-006. Older firmware 28008-005 has
+ * problems when using more then 16 scatter/gather lists.
+ *
+ * The list of i/o ports to be probed can be totally replaced by the
+ * boot command line option: "u14-34f=port0,port1,port2,...", where the
+ * port0, port1... arguments are ISA/VESA addresses to be probed.
+ * For example using "u14-34f=0x230,0x340", the driver probes only the two
+ * addresses 0x230 and 0x340 in this order; "u14-34f=0" totally disables
+ * this driver.
+ *
+ * After the optional list of detection probes, other possible command line
+ * options are:
+ *
+ * eh:y use new scsi code (linux 2.2 only);
+ * eh:n use old scsi code;
+ * et:y use disk geometry returned by scsicam_bios_param;
+ * et:n use disk geometry jumpered on the board;
+ * lc:y enables linked commands;
+ * lc:n disables linked commands;
+ * of:y enables old firmware support;
+ * of:n disables old firmware support;
+ * mq:xx set the max queue depth to the value xx (2 <= xx <= 8).
+ *
+ * The default value is: "u14-34f=lc:n,of:n,mq:8,et:n".
+ * An example using the list of detection probes could be:
+ * "u14-34f=0x230,0x340,lc:y,of:n,mq:4,eh:n,et:n".
+ *
+ * When loading as a module, parameters can be specified as well.
+ * The above example would be (use 1 in place of y and 0 in place of n):
+ *
+ * modprobe u14-34f io_port=0x230,0x340 linked_comm=1 have_old_firmware=0 \
+ * max_queue_depth=4 use_new_eh_code=0 ext_tran=0
+ *
+ * ----------------------------------------------------------------------------
+ * In this implementation, linked commands are designed to work with any DISK
+ * or CD-ROM, since this linking has only the intent of clustering (time-wise)
+ * and reordering by elevator sorting commands directed to each device,
+ * without any relation with the actual SCSI protocol between the controller
+ * and the device.
+ * If Q is the queue depth reported at boot time for each device (also named
+ * cmds/lun) and Q > 2, whenever there is already an active command to the
+ * device all other commands to the same device (up to Q-1) are kept waiting
+ * in the elevator sorting queue. When the active command completes, the
+ * commands in this queue are sorted by sector address. The sort is chosen
+ * between increasing or decreasing by minimizing the seek distance between
+ * the sector of the commands just completed and the sector of the first
+ * command in the list to be sorted.
+ * Trivial math assures that the unsorted average seek distance when doing
+ * random seeks over S sectors is S/3.
+ * When (Q-1) requests are uniformly distributed over S sectors, the average
+ * distance between two adjacent requests is S/((Q-1) + 1), so the sorted
+ * average seek distance for (Q-1) random requests over S sectors is S/Q.
+ * The elevator sorting hence divides the seek distance by a factor Q/3.
+ * The above pure geometric remarks are valid in all cases and the
+ * driver effectively reduces the seek distance by the predicted factor
+ * when there are Q concurrent read i/o operations on the device, but this
+ * does not necessarily results in a noticeable performance improvement:
+ * your mileage may vary....
+ *
+ * Note: command reordering inside a batch of queued commands could cause
+ * wrong results only if there is at least one write request and the
+ * intersection (sector-wise) of all requests is not empty.
+ * When the driver detects a batch including overlapping requests
+ * (a really rare event) strict serial (pid) order is enforced.
+ * ----------------------------------------------------------------------------
+ *
+ * The boards are named Ux4F0, Ux4F1,... according to the detection order.
+ *
+ * In order to support multiple ISA boards in a reliable way,
+ * the driver sets host->wish_block = TRUE for all ISA boards.
+ */
+
+#include <linux/version.h>
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#define MAX_INT_PARAM 10
+
+#if defined(MODULE)
+#include <linux/module.h>
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,26)
+MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i");
+MODULE_PARM(linked_comm, "i");
+MODULE_PARM(have_old_firmware, "i");
+MODULE_PARM(link_statistics, "i");
+MODULE_PARM(max_queue_depth, "i");
+MODULE_PARM(use_new_eh_code, "i");
+MODULE_PARM(ext_tran, "i");
+MODULE_AUTHOR("Dario Ballabio");
+#endif
+
+#endif
+
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <linux/proc_fs.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include "u14-34f.h"
+#include <linux/stat.h>
+#include <linux/config.h>
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,36)
+#include <linux/init.h>
+#else
+#define __initfunc(A) A
+#define __initdata
+#define __init
+#endif
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
+#include <asm/spinlock.h>
+#define IRQ_FLAGS
+#define IRQ_LOCK
+#define IRQ_LOCK_SAVE
+#define IRQ_UNLOCK
+#define IRQ_UNLOCK_RESTORE
+#define SPIN_FLAGS unsigned long spin_flags;
+#define SPIN_LOCK spin_lock_irq(&io_request_lock);
+#define SPIN_LOCK_SAVE spin_lock_irqsave(&io_request_lock, spin_flags);
+#define SPIN_UNLOCK spin_unlock_irq(&io_request_lock);
+#define SPIN_UNLOCK_RESTORE \
+ spin_unlock_irqrestore(&io_request_lock, spin_flags);
+static int use_new_eh_code = TRUE;
+#else
+#define IRQ_FLAGS unsigned long irq_flags;
+#define IRQ_LOCK cli();
+#define IRQ_LOCK_SAVE do {save_flags(irq_flags); cli();} while (0);
+#define IRQ_UNLOCK sti();
+#define IRQ_UNLOCK_RESTORE do {restore_flags(irq_flags);} while (0);
+#define SPIN_FLAGS
+#define SPIN_LOCK
+#define SPIN_LOCK_SAVE
+#define SPIN_UNLOCK
+#define SPIN_UNLOCK_RESTORE
+static int use_new_eh_code = FALSE;
+#endif
+
+struct proc_dir_entry proc_scsi_u14_34f = {
+ PROC_SCSI_U14_34F, 6, "u14_34f",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* Values for the PRODUCT_ID ports for the 14/34F */
+#define PRODUCT_ID1 0x56
+#define PRODUCT_ID2 0x40 /* NOTE: Only upper nibble is used */
+
+/* Subversion values */
+#define ISA 0
+#define ESA 1
+
+#define OP_HOST_ADAPTER 0x1
+#define OP_SCSI 0x2
+#define OP_RESET 0x4
+#define DTD_SCSI 0x0
+#define DTD_IN 0x1
+#define DTD_OUT 0x2
+#define DTD_NONE 0x3
+#define HA_CMD_INQUIRY 0x1
+#define HA_CMD_SELF_DIAG 0x2
+#define HA_CMD_READ_BUFF 0x3
+#define HA_CMD_WRITE_BUFF 0x4
+
+#undef DEBUG_LINKED_COMMANDS
+#undef DEBUG_DETECT
+#undef DEBUG_INTERRUPT
+#undef DEBUG_RESET
+#undef DEBUG_GENERATE_ERRORS
+#undef DEBUG_GENERATE_ABORTS
+#undef DEBUG_GEOMETRY
+
+#define MAX_ISA 3
+#define MAX_VESA 1
+#define MAX_EISA 0
+#define MAX_PCI 0
+#define MAX_BOARDS (MAX_ISA + MAX_VESA + MAX_EISA + MAX_PCI)
+#define MAX_CHANNEL 1
+#define MAX_LUN 8
+#define MAX_TARGET 8
+#define MAX_MAILBOXES 16
+#define MAX_SGLIST 32
+#define MAX_SAFE_SGLIST 16
+#define MAX_INTERNAL_RETRIES 64
+#define MAX_CMD_PER_LUN 2
+#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
+
+#define SKIP ULONG_MAX
+#define FALSE 0
+#define TRUE 1
+#define FREE 0
+#define IN_USE 1
+#define LOCKED 2
+#define IN_RESET 3
+#define IGNORE 4
+#define READY 5
+#define ABORTING 6
+#define NO_DMA 0xff
+#define MAXLOOP 10000
+
+#define REG_LCL_MASK 0
+#define REG_LCL_INTR 1
+#define REG_SYS_MASK 2
+#define REG_SYS_INTR 3
+#define REG_PRODUCT_ID1 4
+#define REG_PRODUCT_ID2 5
+#define REG_CONFIG1 6
+#define REG_CONFIG2 7
+#define REG_OGM 8
+#define REG_ICM 12
+#define REGION_SIZE 13
+#define BSY_ASSERTED 0x01
+#define IRQ_ASSERTED 0x01
+#define CMD_RESET 0xc0
+#define CMD_OGM_INTR 0x01
+#define CMD_CLR_INTR 0x01
+#define CMD_ENA_INTR 0x81
+#define ASOK 0x00
+#define ASST 0x91
+
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+#define YESNO(a) ((a) ? 'y' : 'n')
+#define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM)
+
+#define PACKED __attribute__((packed))
+
+struct sg_list {
+ unsigned int address; /* Segment Address */
+ unsigned int num_bytes; /* Segment Length */
+ };
+
+/* MailBox SCSI Command Packet */
+struct mscp {
+ unsigned char opcode: 3; /* type of command */
+ unsigned char xdir: 2; /* data transfer direction */
+ unsigned char dcn: 1; /* disable disconnect */
+ unsigned char ca: 1; /* use cache (if available) */
+ unsigned char sg: 1; /* scatter/gather operation */
+ unsigned char target: 3; /* SCSI target id */
+ unsigned char channel: 2; /* SCSI channel number */
+ unsigned char lun: 3; /* SCSI logical unit number */
+ unsigned int data_address PACKED; /* transfer data pointer */
+ unsigned int data_len PACKED; /* length in bytes */
+ unsigned int link_address PACKED; /* for linking command chains */
+ unsigned char clink_id; /* identifies command in chain */
+ unsigned char use_sg; /* (if sg is set) 8 bytes per list */
+ unsigned char sense_len;
+ unsigned char scsi_cdbs_len; /* 6, 10, or 12 */
+ unsigned char scsi_cdbs[12]; /* SCSI commands */
+ unsigned char adapter_status; /* non-zero indicates HA error */
+ unsigned char target_status; /* non-zero indicates target error */
+ unsigned int sense_addr PACKED;
+ Scsi_Cmnd *SCpnt;
+ unsigned int index; /* cp index */
+ struct sg_list *sglist;
+ };
+
+struct hostdata {
+ struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */
+ unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
+ unsigned int last_cp_used; /* Index of last mailbox used */
+ unsigned int iocount; /* Total i/o done for this board */
+ int board_number; /* Number of this board */
+ char board_name[16]; /* Name of this board */
+ char board_id[256]; /* data from INQUIRY on this board */
+ int in_reset; /* True if board is doing a reset */
+ int target_to[MAX_TARGET][MAX_CHANNEL]; /* N. of timeout errors on target */
+ int target_redo[MAX_TARGET][MAX_CHANNEL]; /* If TRUE redo i/o on target */
+ unsigned int retries; /* Number of internal retries */
+ unsigned long last_retried_pid; /* Pid of last retried command */
+ unsigned char subversion; /* Bus type, either ISA or ESA */
+ unsigned char heads;
+ unsigned char sectors;
+
+ /* slot != 0 for the U24F, slot == 0 for both the U14F and U34F */
+ unsigned char slot;
+ };
+
+static struct Scsi_Host *sh[MAX_BOARDS + 1];
+static const char *driver_name = "Ux4F";
+static char sha[MAX_BOARDS];
+
+/* Initialize num_boards so that ihdlr can work while detect is in progress */
+static unsigned int num_boards = MAX_BOARDS;
+
+static unsigned long io_port[] __initdata = {
+
+ /* Space for MAX_INT_PARAM ports usable while loading as a module */
+ SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
+ SKIP, SKIP,
+
+ /* Possible ISA/VESA ports */
+ 0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140,
+
+ /* End of list */
+ 0x0
+ };
+
+#define HD(board) ((struct hostdata *) &sh[board]->hostdata)
+#define BN(board) (HD(board)->board_name)
+
+#define SWAP_BYTE(x) ((unsigned long)( \
+ (((unsigned long)(x) & 0x000000ffU) << 24) | \
+ (((unsigned long)(x) & 0x0000ff00U) << 8) | \
+ (((unsigned long)(x) & 0x00ff0000U) >> 8) | \
+ (((unsigned long)(x) & 0xff000000U) >> 24)))
+
+#if defined(__BIG_ENDIAN)
+#define H2DEV(x) SWAP_BYTE(x)
+#else
+#define H2DEV(x) (x)
+#endif
+
+#define DEV2H(x) H2DEV(x)
+#define V2DEV(addr) ((addr) ? H2DEV(virt_to_bus((void *)addr)) : 0)
+#define DEV2V(addr) ((addr) ? DEV2H(bus_to_virt((unsigned long)addr)) : 0)
+
+static void do_interrupt_handler(int, void *, struct pt_regs *);
+static void flush_dev(Scsi_Device *, unsigned long, unsigned int, unsigned int);
+static int do_trace = FALSE;
+static int setup_done = FALSE;
+static int link_statistics = 0;
+static int ext_tran = FALSE;
+
+#if defined(HAVE_OLD_UX4F_FIRMWARE)
+static int have_old_firmware = TRUE;
+#else
+static int have_old_firmware = FALSE;
+#endif
+
+#if defined(CONFIG_SCSI_U14_34F_LINKED_COMMANDS)
+static int linked_comm = TRUE;
+#else
+static int linked_comm = FALSE;
+#endif
+
+#if defined(CONFIG_SCSI_U14_34F_MAX_TAGS)
+static int max_queue_depth = CONFIG_SCSI_U14_34F_MAX_TAGS;
+#else
+static int max_queue_depth = MAX_CMD_PER_LUN;
+#endif
+
+static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
+ Scsi_Device *dev;
+ int j, ntag = 0, nuntag = 0, tqd, utqd;
+ IRQ_FLAGS
+
+ IRQ_LOCK_SAVE
+ j = ((struct hostdata *) host->hostdata)->board_number;
+
+ for(dev = devlist; dev; dev = dev->next) {
+
+ if (dev->host != host) continue;
+
+ if (TLDEV(dev->type) && (dev->tagged_supported || linked_comm))
+ ntag++;
+ else
+ nuntag++;
+ }
+
+ utqd = MAX_CMD_PER_LUN;
+
+ tqd = (host->can_queue - utqd * nuntag) / (ntag ? ntag : 1);
+
+ if (tqd > max_queue_depth) tqd = max_queue_depth;
+
+ if (tqd < MAX_CMD_PER_LUN) tqd = MAX_CMD_PER_LUN;
+
+ for(dev = devlist; dev; dev = dev->next) {
+ char *tag_suffix = "", *link_suffix = "";
+
+ if (dev->host != host) continue;
+
+ if (TLDEV(dev->type) && (dev->tagged_supported || linked_comm))
+ dev->queue_depth = tqd;
+ else
+ dev->queue_depth = utqd;
+
+ if (TLDEV(dev->type)) {
+ if (linked_comm && dev->queue_depth > 2)
+ link_suffix = ", sorted";
+ else
+ link_suffix = ", unsorted";
+ }
+
+ if (dev->tagged_supported && TLDEV(dev->type) && dev->tagged_queue)
+ tag_suffix = ", tagged";
+ else if (dev->tagged_supported && TLDEV(dev->type))
+ tag_suffix = ", untagged";
+
+ printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s%s.\n",
+ BN(j), host->host_no, dev->channel, dev->id, dev->lun,
+ dev->queue_depth, link_suffix, tag_suffix);
+ }
+
+ IRQ_UNLOCK_RESTORE
+ return;
+}
+
+static inline int wait_on_busy(unsigned long iobase, unsigned int loop) {
+
+ while (inb(iobase + REG_LCL_INTR) & BSY_ASSERTED) {
+ udelay(1L);
+ if (--loop == 0) return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int board_inquiry(unsigned int j) {
+ struct mscp *cpp;
+ unsigned int time, limit = 0;
+
+ cpp = &HD(j)->cp[0];
+ memset(cpp, 0, sizeof(struct mscp));
+ cpp->opcode = OP_HOST_ADAPTER;
+ cpp->xdir = DTD_IN;
+ cpp->data_address = V2DEV(HD(j)->board_id);
+ cpp->data_len = H2DEV(sizeof(HD(j)->board_id));
+ cpp->scsi_cdbs_len = 6;
+ cpp->scsi_cdbs[0] = HA_CMD_INQUIRY;
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: board_inquiry, adapter busy.\n", BN(j));
+ return TRUE;
+ }
+
+ HD(j)->cp_stat[0] = IGNORE;
+
+ /* Clear the interrupt indication */
+ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
+
+ /* Store pointer in OGM address bytes */
+ outl(V2DEV(cpp), sh[j]->io_port + REG_OGM);
+
+ /* Issue OGM interrupt */
+ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
+
+ SPIN_UNLOCK
+ IRQ_UNLOCK
+ time = jiffies;
+ while ((jiffies - time) < HZ && limit++ < 20000) udelay(100L);
+ IRQ_LOCK
+ SPIN_LOCK
+
+ if (cpp->adapter_status || HD(j)->cp_stat[0] != FREE) {
+ HD(j)->cp_stat[0] = FREE;
+ printk("%s: board_inquiry, err 0x%x.\n", BN(j), cpp->adapter_status);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+__initfunc (static inline int port_detect \
+ (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
+ unsigned char irq, dma_channel, subversion, i;
+ unsigned char in_byte;
+ char *bus_type, dma_name[16];
+
+ /* Allowed BIOS base addresses (NULL indicates reserved) */
+ void *bios_segment_table[8] = {
+ NULL,
+ (void *) 0xc4000, (void *) 0xc8000, (void *) 0xcc000, (void *) 0xd0000,
+ (void *) 0xd4000, (void *) 0xd8000, (void *) 0xdc000
+ };
+
+ /* Allowed IRQs */
+ unsigned char interrupt_table[4] = { 15, 14, 11, 10 };
+
+ /* Allowed DMA channels for ISA (0 indicates reserved) */
+ unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
+
+ /* Head/sector mappings */
+ struct {
+ unsigned char heads;
+ unsigned char sectors;
+ } mapping_table[4] = {
+ { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 }
+ };
+
+ struct config_1 {
+ unsigned char bios_segment: 3;
+ unsigned char removable_disks_as_fixed: 1;
+ unsigned char interrupt: 2;
+ unsigned char dma_channel: 2;
+ } config_1;
+
+ struct config_2 {
+ unsigned char ha_scsi_id: 3;
+ unsigned char mapping_mode: 2;
+ unsigned char bios_drive_number: 1;
+ unsigned char tfr_port: 2;
+ } config_2;
+
+ char name[16];
+
+ sprintf(name, "%s%d", driver_name, j);
+
+ if(check_region(port_base, REGION_SIZE)) {
+ printk("%s: address 0x%03lx in use, skipping probe.\n", name, port_base);
+ return FALSE;
+ }
+
+ if (inb(port_base + REG_PRODUCT_ID1) != PRODUCT_ID1) return FALSE;
+
+ in_byte = inb(port_base + REG_PRODUCT_ID2);
+
+ if ((in_byte & 0xf0) != PRODUCT_ID2) return FALSE;
+
+ *(char *)&config_1 = inb(port_base + REG_CONFIG1);
+ *(char *)&config_2 = inb(port_base + REG_CONFIG2);
+
+ irq = interrupt_table[config_1.interrupt];
+ dma_channel = dma_channel_table[config_1.dma_channel];
+ subversion = (in_byte & 0x0f);
+
+ /* Board detected, allocate its IRQ */
+ if (request_irq(irq, do_interrupt_handler,
+ SA_INTERRUPT | ((subversion == ESA) ? SA_SHIRQ : 0),
+ driver_name, (void *) &sha[j])) {
+ printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
+ return FALSE;
+ }
+
+ if (subversion == ISA && request_dma(dma_channel, driver_name)) {
+ printk("%s: unable to allocate DMA channel %u, detaching.\n",
+ name, dma_channel);
+ free_irq(irq, &sha[j]);
+ return FALSE;
+ }
+
+ if (have_old_firmware) tpnt->use_clustering = DISABLE_CLUSTERING;
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
+ tpnt->use_new_eh_code = use_new_eh_code;
+#else
+ use_new_eh_code = FALSE;
+#endif
+
+ sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
+
+ if (sh[j] == NULL) {
+ printk("%s: unable to register host, detaching.\n", name);
+
+ free_irq(irq, &sha[j]);
+
+ if (subversion == ISA) free_dma(dma_channel);
+
+ return FALSE;
+ }
+
+ sh[j]->io_port = port_base;
+ sh[j]->unique_id = port_base;
+ sh[j]->n_io_port = REGION_SIZE;
+ sh[j]->base = bios_segment_table[config_1.bios_segment];
+ sh[j]->irq = irq;
+ sh[j]->sg_tablesize = MAX_SGLIST;
+ sh[j]->this_id = config_2.ha_scsi_id;
+ sh[j]->can_queue = MAX_MAILBOXES;
+ sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
+ sh[j]->select_queue_depths = select_queue_depths;
+
+#if defined(DEBUG_DETECT)
+ {
+ unsigned char sys_mask, lcl_mask;
+
+ sys_mask = inb(sh[j]->io_port + REG_SYS_MASK);
+ lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK);
+ printk("SYS_MASK 0x%x, LCL_MASK 0x%x.\n", sys_mask, lcl_mask);
+ }
+#endif
+
+ /* Probably a bogus host scsi id, set it to the dummy value */
+ if (sh[j]->this_id == 0) sh[j]->this_id = -1;
+
+ /* If BIOS is disabled, force enable interrupts */
+ if (sh[j]->base == 0) outb(CMD_ENA_INTR, sh[j]->io_port + REG_SYS_MASK);
+
+ /* Register the I/O space that we use */
+ request_region(sh[j]->io_port, sh[j]->n_io_port, driver_name);
+
+ memset(HD(j), 0, sizeof(struct hostdata));
+ HD(j)->heads = mapping_table[config_2.mapping_mode].heads;
+ HD(j)->sectors = mapping_table[config_2.mapping_mode].sectors;
+ HD(j)->subversion = subversion;
+ HD(j)->board_number = j;
+
+ if (have_old_firmware) sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
+
+ if (HD(j)->subversion == ESA) {
+ sh[j]->unchecked_isa_dma = FALSE;
+ sh[j]->dma_channel = NO_DMA;
+ sprintf(BN(j), "U34F%d", j);
+ bus_type = "VESA";
+ }
+ else {
+ sh[j]->wish_block = TRUE;
+ sh[j]->unchecked_isa_dma = TRUE;
+ disable_dma(dma_channel);
+ clear_dma_ff(dma_channel);
+ set_dma_mode(dma_channel, DMA_MODE_CASCADE);
+ enable_dma(dma_channel);
+ sh[j]->dma_channel = dma_channel;
+ sprintf(BN(j), "U14F%d", j);
+ bus_type = "ISA";
+ }
+
+ sh[j]->max_channel = MAX_CHANNEL - 1;
+ sh[j]->max_id = MAX_TARGET;
+ sh[j]->max_lun = MAX_LUN;
+
+ if (HD(j)->subversion == ISA && !board_inquiry(j)) {
+ HD(j)->board_id[40] = 0;
+
+ if (strcmp(&HD(j)->board_id[32], "06000600")) {
+ printk("%s: %s.\n", BN(j), &HD(j)->board_id[8]);
+ printk("%s: firmware %s is outdated, FW PROM should be 28004-006.\n",
+ BN(j), &HD(j)->board_id[32]);
+ sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
+ sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
+ }
+ }
+
+ if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "BMST");
+ else sprintf(dma_name, "DMA %u", dma_channel);
+
+ for (i = 0; i < sh[j]->can_queue; i++)
+ if (! ((&HD(j)->cp[i])->sglist = kmalloc(
+ sh[j]->sg_tablesize * sizeof(struct sg_list),
+ (sh[j]->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC))) {
+ printk("%s: kmalloc SGlist failed, mbox %d, detaching.\n", BN(j), i);
+ u14_34f_release(sh[j]);
+ return FALSE;
+ }
+
+ if (max_queue_depth > MAX_TAGGED_CMD_PER_LUN)
+ max_queue_depth = MAX_TAGGED_CMD_PER_LUN;
+
+ if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
+
+ if (j == 0) {
+ printk("UltraStor 14F/34F: Copyright (C) 1994-1998 Dario Ballabio.\n");
+ printk("%s config options -> of:%c, lc:%c, mq:%d, eh:%c, et:%c.\n",
+ driver_name, YESNO(have_old_firmware), YESNO(linked_comm),
+ max_queue_depth, YESNO(use_new_eh_code), YESNO(ext_tran));
+ }
+
+ printk("%s: %s 0x%03lx, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d.\n",
+ BN(j), bus_type, (unsigned long)sh[j]->io_port, (int)sh[j]->base,
+ sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue);
+
+ if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
+ printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
+ BN(j), sh[j]->max_id, sh[j]->max_lun);
+
+ for (i = 0; i <= sh[j]->max_channel; i++)
+ printk("%s: SCSI channel %u enabled, host target ID %d.\n",
+ BN(j), i, sh[j]->this_id);
+
+ return TRUE;
+}
+
+__initfunc (void u14_34f_setup(char *str, int *ints)) {
+ int i, argc = ints[0];
+ char *cur = str, *pc;
+
+ if (argc > 0) {
+
+ if (argc > MAX_INT_PARAM) argc = MAX_INT_PARAM;
+
+ for (i = 0; i < argc; i++) io_port[i] = ints[i + 1];
+
+ io_port[i] = 0;
+ setup_done = TRUE;
+ }
+
+ while (cur && (pc = strchr(cur, ':'))) {
+ int val = 0, c = *++pc;
+
+ if (c == 'n' || c == 'N') val = FALSE;
+ else if (c == 'y' || c == 'Y') val = TRUE;
+ else val = (int) simple_strtoul(pc, NULL, 0);
+
+ if (!strncmp(cur, "lc:", 3)) linked_comm = val;
+ else if (!strncmp(cur, "of:", 3)) have_old_firmware = val;
+ else if (!strncmp(cur, "mq:", 3)) max_queue_depth = val;
+ else if (!strncmp(cur, "ls:", 3)) link_statistics = val;
+ else if (!strncmp(cur, "eh:", 3)) use_new_eh_code = val;
+ else if (!strncmp(cur, "et:", 3)) ext_tran = val;
+
+ if ((cur = strchr(cur, ','))) ++cur;
+ }
+
+ return;
+}
+
+__initfunc (int u14_34f_detect(Scsi_Host_Template *tpnt)) {
+ unsigned int j = 0, k;
+ IRQ_FLAGS
+
+ IRQ_LOCK_SAVE
+ tpnt->proc_dir = &proc_scsi_u14_34f;
+
+#if defined(MODULE)
+ /* io_port could have been modified when loading as a module */
+ if(io_port[0] != SKIP) {
+ setup_done = TRUE;
+ io_port[MAX_INT_PARAM] = 0;
+ }
+#endif
+
+ for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL;
+
+ for (k = 0; io_port[k]; k++) {
+
+ if (io_port[k] == SKIP) continue;
+
+ if (j < MAX_BOARDS && port_detect(io_port[k], j, tpnt)) j++;
+ }
+
+ num_boards = j;
+ IRQ_UNLOCK_RESTORE
+ return j;
+}
+
+static inline void build_sg_list(struct mscp *cpp, Scsi_Cmnd *SCpnt) {
+ unsigned int k, data_len = 0;
+ struct scatterlist *sgpnt;
+
+ sgpnt = (struct scatterlist *) SCpnt->request_buffer;
+
+ for (k = 0; k < SCpnt->use_sg; k++) {
+ cpp->sglist[k].address = V2DEV(sgpnt[k].address);
+ cpp->sglist[k].num_bytes = H2DEV(sgpnt[k].length);
+ data_len += sgpnt[k].length;
+ }
+
+ cpp->use_sg = SCpnt->use_sg;
+ cpp->data_address = V2DEV(cpp->sglist);
+ cpp->data_len = H2DEV(data_len);
+}
+
+static inline int do_qcomm(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
+ unsigned int i, j, k;
+ struct mscp *cpp;
+
+ static const unsigned char data_out_cmds[] = {
+ 0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e,
+ 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40,
+ 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b
+ };
+
+ static const unsigned char data_none_cmds[] = {
+ 0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e,
+ 0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47,
+ 0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5
+ };
+
+ /* j is the board number */
+ j = ((struct hostdata *) SCpnt->host->hostdata)->board_number;
+
+ if (SCpnt->host_scribble)
+ panic("%s: qcomm, pid %ld, SCpnt %p already active.\n",
+ BN(j), SCpnt->pid, SCpnt);
+
+ /* i is the mailbox number, look for the first free mailbox
+ starting from last_cp_used */
+ i = HD(j)->last_cp_used + 1;
+
+ for (k = 0; k < sh[j]->can_queue; k++, i++) {
+
+ if (i >= sh[j]->can_queue) i = 0;
+
+ if (HD(j)->cp_stat[i] == FREE) {
+ HD(j)->last_cp_used = i;
+ break;
+ }
+ }
+
+ if (k == sh[j]->can_queue) {
+ printk("%s: qcomm, no free mailbox.\n", BN(j));
+ return 1;
+ }
+
+ /* Set pointer to control packet structure */
+ cpp = &HD(j)->cp[i];
+
+ memset(cpp, 0, sizeof(struct mscp) - sizeof(struct sg_list *));
+ SCpnt->scsi_done = done;
+ cpp->index = i;
+ SCpnt->host_scribble = (unsigned char *) &cpp->index;
+
+ if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%d, pid %ld.\n",
+ BN(j), i, SCpnt->channel, SCpnt->target,
+ SCpnt->lun, SCpnt->pid);
+
+ cpp->xdir = DTD_IN;
+
+ for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
+ if (SCpnt->cmnd[0] == data_out_cmds[k]) {
+ cpp->xdir = DTD_OUT;
+ break;
+ }
+
+ if (cpp->xdir == DTD_IN)
+ for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
+ if (SCpnt->cmnd[0] == data_none_cmds[k]) {
+ cpp->xdir = DTD_NONE;
+ break;
+ }
+
+ cpp->opcode = OP_SCSI;
+ cpp->channel = SCpnt->channel;
+ cpp->target = SCpnt->target;
+ cpp->lun = SCpnt->lun;
+ cpp->SCpnt = SCpnt;
+ cpp->sense_addr = V2DEV(SCpnt->sense_buffer);
+ cpp->sense_len = sizeof SCpnt->sense_buffer;
+
+ if (SCpnt->use_sg) {
+ cpp->sg = TRUE;
+ build_sg_list(cpp, SCpnt);
+ }
+ else {
+ cpp->data_address = V2DEV(SCpnt->request_buffer);
+ cpp->data_len = H2DEV(SCpnt->request_bufflen);
+ }
+
+ cpp->scsi_cdbs_len = SCpnt->cmd_len;
+ memcpy(cpp->scsi_cdbs, SCpnt->cmnd, cpp->scsi_cdbs_len);
+
+ if (linked_comm && SCpnt->device->queue_depth > 2
+ && TLDEV(SCpnt->device->type)) {
+ HD(j)->cp_stat[i] = READY;
+ flush_dev(SCpnt->device, SCpnt->request.sector, j, FALSE);
+ return 0;
+ }
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ SCpnt->host_scribble = NULL;
+ printk("%s: qcomm, target %d.%d:%d, pid %ld, adapter busy.\n",
+ BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid);
+ return 1;
+ }
+
+ /* Store pointer in OGM address bytes */
+ outl(V2DEV(cpp), sh[j]->io_port + REG_OGM);
+
+ /* Issue OGM interrupt */
+ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
+
+ HD(j)->cp_stat[i] = IN_USE;
+ return 0;
+}
+
+int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
+ int rtn;
+ IRQ_FLAGS
+
+ IRQ_LOCK_SAVE
+ rtn = do_qcomm(SCpnt, done);
+ IRQ_UNLOCK_RESTORE
+ return rtn;
+}
+
+static inline int do_old_abort(Scsi_Cmnd *SCarg) {
+ unsigned int i, j;
+
+ j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
+
+ if (SCarg->host_scribble == NULL ||
+ (SCarg->serial_number_at_timeout &&
+ (SCarg->serial_number != SCarg->serial_number_at_timeout))) {
+ printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
+ BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ i = *(unsigned int *)SCarg->host_scribble;
+ printk("%s: abort, mbox %d, target %d.%d:%d, pid %ld.\n",
+ BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+
+ if (i >= sh[j]->can_queue)
+ panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: abort, timeout error.\n", BN(j));
+ return SCSI_ABORT_ERROR;
+ }
+
+ if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: abort, mbox %d is free.\n", BN(j), i);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_USE) {
+ printk("%s: abort, mbox %d is in use.\n", BN(j), i);
+
+ if (SCarg != HD(j)->cp[i].SCpnt)
+ panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
+ BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
+
+ if (inb(sh[j]->io_port + REG_SYS_INTR) & IRQ_ASSERTED)
+ printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i);
+
+ return SCSI_ABORT_SNOOZE;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_RESET) {
+ printk("%s: abort, mbox %d is in reset.\n", BN(j), i);
+ return SCSI_ABORT_ERROR;
+ }
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ printk("%s: abort, mbox %d is locked.\n", BN(j), i);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
+ SCarg->result = DID_ABORT << 16;
+ SCarg->host_scribble = NULL;
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
+ BN(j), i, SCarg->pid);
+ SCarg->scsi_done(SCarg);
+ return SCSI_ABORT_SUCCESS;
+ }
+
+ panic("%s: abort, mbox %d, invalid cp_stat.\n", BN(j), i);
+}
+
+int u14_34f_old_abort(Scsi_Cmnd *SCarg) {
+ int rtn;
+ IRQ_FLAGS
+
+ IRQ_LOCK_SAVE
+ rtn = do_old_abort(SCarg);
+ IRQ_UNLOCK_RESTORE
+ return rtn;
+}
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
+
+static inline int do_abort(Scsi_Cmnd *SCarg) {
+ unsigned int i, j;
+
+ j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
+
+ if (SCarg->host_scribble == NULL) {
+ printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
+ BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+ return SUCCESS;
+ }
+
+ i = *(unsigned int *)SCarg->host_scribble;
+ printk("%s: abort, mbox %d, target %d.%d:%d, pid %ld.\n",
+ BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+
+ if (i >= sh[j]->can_queue)
+ panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: abort, timeout error.\n", BN(j));
+ return FAILED;
+ }
+
+ if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: abort, mbox %d is free.\n", BN(j), i);
+ return SUCCESS;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_USE) {
+ printk("%s: abort, mbox %d is in use.\n", BN(j), i);
+
+ if (SCarg != HD(j)->cp[i].SCpnt)
+ panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
+ BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
+
+ if (inb(sh[j]->io_port + REG_SYS_INTR) & IRQ_ASSERTED)
+ printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i);
+
+ if (SCarg->eh_state == SCSI_STATE_TIMEOUT) {
+ SCarg->host_scribble = NULL;
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s, abort, mbox %d, eh_state timeout, pid %ld.\n",
+ BN(j), i, SCarg->pid);
+ return SUCCESS;
+ }
+
+ return FAILED;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_RESET) {
+ printk("%s: abort, mbox %d is in reset.\n", BN(j), i);
+ return FAILED;
+ }
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ printk("%s: abort, mbox %d is locked.\n", BN(j), i);
+ return SUCCESS;
+ }
+
+ if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
+ SCarg->result = DID_ABORT << 16;
+ SCarg->host_scribble = NULL;
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
+ BN(j), i, SCarg->pid);
+ SCarg->scsi_done(SCarg);
+ return SUCCESS;
+ }
+
+ panic("%s: abort, mbox %d, invalid cp_stat.\n", BN(j), i);
+}
+
+int u14_34f_abort(Scsi_Cmnd *SCarg) {
+
+ return do_abort(SCarg);
+}
+
+#endif /* new_eh_code */
+
+static inline int do_old_reset(Scsi_Cmnd *SCarg) {
+ unsigned int i, j, time, k, c, limit = 0;
+ int arg_done = FALSE;
+ Scsi_Cmnd *SCpnt;
+
+ j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
+ printk("%s: reset, enter, target %d.%d:%d, pid %ld.\n",
+ BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+
+ if (SCarg->host_scribble == NULL)
+ printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->pid);
+
+ if (SCarg->serial_number_at_timeout &&
+ (SCarg->serial_number != SCarg->serial_number_at_timeout)) {
+ printk("%s: reset, pid %ld, reset not running.\n", BN(j), SCarg->pid);
+ return SCSI_RESET_NOT_RUNNING;
+ }
+
+ if (HD(j)->in_reset) {
+ printk("%s: reset, exit, already in reset.\n", BN(j));
+ return SCSI_RESET_ERROR;
+ }
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: reset, exit, timeout error.\n", BN(j));
+ return SCSI_RESET_ERROR;
+ }
+
+ HD(j)->retries = 0;
+
+ for (c = 0; c <= sh[j]->max_channel; c++)
+ for (k = 0; k < sh[j]->max_id; k++) {
+ HD(j)->target_redo[k][c] = TRUE;
+ HD(j)->target_to[k][c] = 0;
+ }
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ if (HD(j)->cp_stat[i] == FREE) continue;
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
+ continue;
+ }
+
+ if (!(SCpnt = HD(j)->cp[i].SCpnt))
+ panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+ if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
+ HD(j)->cp_stat[i] = ABORTING;
+ printk("%s: reset, mbox %d aborting, pid %ld.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else {
+ HD(j)->cp_stat[i] = IN_RESET;
+ printk("%s: reset, mbox %d in reset, pid %ld.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
+
+ if (SCpnt->scsi_done == NULL)
+ panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
+
+ if (SCpnt == SCarg) arg_done = TRUE;
+ }
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: reset, cannot reset, timeout error.\n", BN(j));
+ return SCSI_RESET_ERROR;
+ }
+
+ outb(CMD_RESET, sh[j]->io_port + REG_LCL_INTR);
+ printk("%s: reset, board reset done, enabling interrupts.\n", BN(j));
+
+#if defined(DEBUG_RESET)
+ do_trace = TRUE;
+#endif
+
+ HD(j)->in_reset = TRUE;
+ SPIN_UNLOCK
+ IRQ_UNLOCK
+ time = jiffies;
+ while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L);
+ IRQ_LOCK
+ SPIN_LOCK
+ printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ if (HD(j)->cp_stat[i] == IN_RESET) {
+ SCpnt = HD(j)->cp[i].SCpnt;
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox is still waiting for its interrupt */
+ HD(j)->cp_stat[i] = LOCKED;
+
+ printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else if (HD(j)->cp_stat[i] == ABORTING) {
+ SCpnt = HD(j)->cp[i].SCpnt;
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox was never queued to the adapter */
+ HD(j)->cp_stat[i] = FREE;
+
+ printk("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else
+
+ /* Any other mailbox has already been set free by interrupt */
+ continue;
+
+ SCpnt->scsi_done(SCpnt);
+ IRQ_LOCK
+ }
+
+ HD(j)->in_reset = FALSE;
+ do_trace = FALSE;
+
+ if (arg_done) {
+ printk("%s: reset, exit, success.\n", BN(j));
+ return SCSI_RESET_SUCCESS;
+ }
+ else {
+ printk("%s: reset, exit, wakeup.\n", BN(j));
+ return SCSI_RESET_PUNT;
+ }
+}
+
+int u14_34f_old_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
+ int rtn;
+ IRQ_FLAGS
+
+ IRQ_LOCK_SAVE
+ rtn = do_old_reset(SCarg);
+ IRQ_UNLOCK_RESTORE
+ return rtn;
+}
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
+
+static inline int do_reset(Scsi_Cmnd *SCarg) {
+ unsigned int i, j, time, k, c, limit = 0;
+ int arg_done = FALSE;
+ Scsi_Cmnd *SCpnt;
+
+ j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
+ printk("%s: reset, enter, target %d.%d:%d, pid %ld.\n",
+ BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+
+ if (SCarg->host_scribble == NULL)
+ printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->pid);
+
+ if (HD(j)->in_reset) {
+ printk("%s: reset, exit, already in reset.\n", BN(j));
+ return FAILED;
+ }
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: reset, exit, timeout error.\n", BN(j));
+ return FAILED;
+ }
+
+ HD(j)->retries = 0;
+
+ for (c = 0; c <= sh[j]->max_channel; c++)
+ for (k = 0; k < sh[j]->max_id; k++) {
+ HD(j)->target_redo[k][c] = TRUE;
+ HD(j)->target_to[k][c] = 0;
+ }
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ if (HD(j)->cp_stat[i] == FREE) continue;
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
+ continue;
+ }
+
+ if (!(SCpnt = HD(j)->cp[i].SCpnt))
+ panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+ if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
+ HD(j)->cp_stat[i] = ABORTING;
+ printk("%s: reset, mbox %d aborting, pid %ld.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else {
+ HD(j)->cp_stat[i] = IN_RESET;
+ printk("%s: reset, mbox %d in reset, pid %ld.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
+
+ if (SCpnt->scsi_done == NULL)
+ panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
+
+ if (SCpnt == SCarg) arg_done = TRUE;
+ }
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: reset, cannot reset, timeout error.\n", BN(j));
+ return FAILED;
+ }
+
+ outb(CMD_RESET, sh[j]->io_port + REG_LCL_INTR);
+ printk("%s: reset, board reset done, enabling interrupts.\n", BN(j));
+
+#if defined(DEBUG_RESET)
+ do_trace = TRUE;
+#endif
+
+ HD(j)->in_reset = TRUE;
+ SPIN_UNLOCK
+ IRQ_UNLOCK
+ time = jiffies;
+ while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L);
+ IRQ_LOCK
+ SPIN_LOCK
+ printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ if (HD(j)->cp_stat[i] == IN_RESET) {
+ SCpnt = HD(j)->cp[i].SCpnt;
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox is still waiting for its interrupt */
+ HD(j)->cp_stat[i] = LOCKED;
+
+ printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else if (HD(j)->cp_stat[i] == ABORTING) {
+ SCpnt = HD(j)->cp[i].SCpnt;
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox was never queued to the adapter */
+ HD(j)->cp_stat[i] = FREE;
+
+ printk("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
+ BN(j), i, SCpnt->pid);
+ }
+
+ else
+
+ /* Any other mailbox has already been set free by interrupt */
+ continue;
+
+ SCpnt->scsi_done(SCpnt);
+ IRQ_LOCK
+ }
+
+ HD(j)->in_reset = FALSE;
+ do_trace = FALSE;
+
+ if (arg_done) printk("%s: reset, exit, pid %ld done.\n", BN(j), SCarg->pid);
+ else printk("%s: reset, exit.\n", BN(j));
+
+ return SUCCESS;
+}
+
+int u14_34f_reset(Scsi_Cmnd *SCarg) {
+
+ return do_reset(SCarg);
+}
+
+#endif /* new_eh_code */
+
+int u14_34f_biosparam(Disk *disk, kdev_t dev, int *dkinfo) {
+ unsigned int j = 0;
+ int size = disk->capacity;
+
+ dkinfo[0] = HD(j)->heads;
+ dkinfo[1] = HD(j)->sectors;
+ dkinfo[2] = size / (HD(j)->heads * HD(j)->sectors);
+
+ if (ext_tran && (scsicam_bios_param(disk, dev, dkinfo) < 0)) {
+ dkinfo[0] = 255;
+ dkinfo[1] = 63;
+ dkinfo[2] = size / (dkinfo[0] * dkinfo[1]);
+ }
+
+#if defined (DEBUG_GEOMETRY)
+ printk ("%s: biosparam, head=%d, sec=%d, cyl=%d.\n", driver_name,
+ dkinfo[0], dkinfo[1], dkinfo[2]);
+#endif
+
+ return FALSE;
+}
+
+static void sort(unsigned long sk[], unsigned int da[], unsigned int n,
+ unsigned int rev) {
+ unsigned int i, j, k, y;
+ unsigned long x;
+
+ for (i = 0; i < n - 1; i++) {
+ k = i;
+
+ for (j = k + 1; j < n; j++)
+ if (rev) {
+ if (sk[j] > sk[k]) k = j;
+ }
+ else {
+ if (sk[j] < sk[k]) k = j;
+ }
+
+ if (k != i) {
+ x = sk[k]; sk[k] = sk[i]; sk[i] = x;
+ y = da[k]; da[k] = da[i]; da[i] = y;
+ }
+ }
+
+ return;
+ }
+
+static inline int reorder(unsigned int j, unsigned long cursec,
+ unsigned int ihdlr, unsigned int il[], unsigned int n_ready) {
+ Scsi_Cmnd *SCpnt;
+ struct mscp *cpp;
+ unsigned int k, n;
+ unsigned int rev = FALSE, s = TRUE, r = TRUE;
+ unsigned int input_only = TRUE, overlap = FALSE;
+ unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
+ unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0, iseek = 0;
+ unsigned long ioseek = 0;
+
+ static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
+ static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
+ static unsigned int readysorted = 0, revcount = 0;
+ static unsigned long seeksorted = 0, seeknosort = 0;
+
+ if (link_statistics && !(++flushcount % link_statistics))
+ printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\
+ " av %ldK as %ldK.\n", flushcount, batchcount, inputcount,
+ ovlcount, readycount, readysorted, sortcount, revcount,
+ seeknosort / (readycount + 1),
+ seeksorted / (readycount + 1));
+
+ if (n_ready <= 1) return FALSE;
+
+ for (n = 0; n < n_ready; n++) {
+ k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+ if (!(cpp->xdir == DTD_IN)) input_only = FALSE;
+
+ if (SCpnt->request.sector < minsec) minsec = SCpnt->request.sector;
+ if (SCpnt->request.sector > maxsec) maxsec = SCpnt->request.sector;
+
+ sl[n] = SCpnt->request.sector;
+ ioseek += SCpnt->request.nr_sectors;
+
+ if (!n) continue;
+
+ if (sl[n] < sl[n - 1]) s = FALSE;
+ if (sl[n] > sl[n - 1]) r = FALSE;
+
+ if (link_statistics) {
+ if (sl[n] > sl[n - 1])
+ seek += sl[n] - sl[n - 1];
+ else
+ seek += sl[n - 1] - sl[n];
+ }
+
+ }
+
+ if (link_statistics) {
+ if (cursec > sl[0]) seek += cursec - sl[0]; else seek += sl[0] - cursec;
+ }
+
+ if (cursec > ((maxsec + minsec) / 2)) rev = TRUE;
+
+ if (ioseek > ((maxsec - minsec) / 2)) rev = FALSE;
+
+ if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev);
+
+ if (!input_only) for (n = 0; n < n_ready; n++) {
+ k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+ ll[n] = SCpnt->request.nr_sectors; pl[n] = SCpnt->pid;
+
+ if (!n) continue;
+
+ if ((sl[n] == sl[n - 1]) || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n]))
+ || (rev && ((sl[n] + ll[n]) > sl[n - 1]))) overlap = TRUE;
+ }
+
+ if (overlap) sort(pl, il, n_ready, FALSE);
+
+ if (link_statistics) {
+ if (cursec > sl[0]) iseek = cursec - sl[0]; else iseek = sl[0] - cursec;
+ batchcount++; readycount += n_ready, seeknosort += seek / 1024;
+ if (input_only) inputcount++;
+ if (overlap) { ovlcount++; seeksorted += iseek / 1024; }
+ else seeksorted += (iseek + maxsec - minsec) / 1024;
+ if (rev && !r) { revcount++; readysorted += n_ready; }
+ if (!rev && !s) { sortcount++; readysorted += n_ready; }
+ }
+
+#if defined(DEBUG_LINKED_COMMANDS)
+ if (link_statistics && (overlap || !(flushcount % link_statistics)))
+ for (n = 0; n < n_ready; n++) {
+ k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+ printk("%s %d.%d:%d pid %ld mb %d fc %d nr %d sec %ld ns %ld"\
+ " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
+ (ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target,
+ SCpnt->lun, SCpnt->pid, k, flushcount, n_ready,
+ SCpnt->request.sector, SCpnt->request.nr_sectors, cursec,
+ YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only),
+ YESNO(overlap), cpp->xdir);
+ }
+#endif
+ return overlap;
+}
+
+static void flush_dev(Scsi_Device *dev, unsigned long cursec, unsigned int j,
+ unsigned int ihdlr) {
+ Scsi_Cmnd *SCpnt;
+ struct mscp *cpp;
+ unsigned int k, n, n_ready = 0, il[MAX_MAILBOXES];
+
+ for (k = 0; k < sh[j]->can_queue; k++) {
+
+ if (HD(j)->cp_stat[k] != READY && HD(j)->cp_stat[k] != IN_USE) continue;
+
+ cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+ if (SCpnt->device != dev) continue;
+
+ if (HD(j)->cp_stat[k] == IN_USE) return;
+
+ il[n_ready++] = k;
+ }
+
+ if (reorder(j, cursec, ihdlr, il, n_ready)) n_ready = 1;
+
+ for (n = 0; n < n_ready; n++) {
+ k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+ if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+ printk("%s: %s, target %d.%d:%d, pid %ld, mbox %d, adapter"\
+ " busy, will abort.\n", BN(j), (ihdlr ? "ihdlr" : "qcomm"),
+ SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid, k);
+ HD(j)->cp_stat[k] = ABORTING;
+ continue;
+ }
+
+ outl(V2DEV(cpp), sh[j]->io_port + REG_OGM);
+ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
+ HD(j)->cp_stat[k] = IN_USE;
+ }
+
+}
+
+static inline void ihdlr(int irq, unsigned int j) {
+ Scsi_Cmnd *SCpnt;
+ unsigned int i, k, c, status, tstatus, reg, ret;
+ struct mscp *spp, *cpp;
+
+ if (sh[j]->irq != irq)
+ panic("%s: ihdlr, irq %d, sh[j]->irq %d.\n", BN(j), irq, sh[j]->irq);
+
+ /* Check if this board need to be serviced */
+ if (!((reg = inb(sh[j]->io_port + REG_SYS_INTR)) & IRQ_ASSERTED)) return;
+
+ HD(j)->iocount++;
+
+ if (do_trace) printk("%s: ihdlr, enter, irq %d, count %d.\n", BN(j), irq,
+ HD(j)->iocount);
+
+ /* Check if this board is still busy */
+ if (wait_on_busy(sh[j]->io_port, 20 * MAXLOOP)) {
+ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
+ printk("%s: ihdlr, busy timeout error, irq %d, reg 0x%x, count %d.\n",
+ BN(j), irq, reg, HD(j)->iocount);
+ return;
+ }
+
+ spp = (struct mscp *)DEV2V(ret = inl(sh[j]->io_port + REG_ICM));
+ cpp = spp;
+
+ /* Clear interrupt pending flag */
+ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
+
+#if defined(DEBUG_GENERATE_ABORTS)
+ if ((HD(j)->iocount > 500) && ((HD(j)->iocount % 500) < 3)) return;
+#endif
+
+ /* Find the mailbox to be serviced on this board */
+ i = cpp - HD(j)->cp;
+
+ if (cpp < HD(j)->cp || cpp >= HD(j)->cp + sh[j]->can_queue
+ || i >= sh[j]->can_queue)
+ panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n", BN(j),
+ (void *)ret, HD(j)->cp);
+
+ if (HD(j)->cp_stat[i] == IGNORE) {
+ HD(j)->cp_stat[i] = FREE;
+ return;
+ }
+ else if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: ihdlr, mbox %d unlocked, count %d.\n", BN(j), i,
+ HD(j)->iocount);
+ return;
+ }
+ else if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: ihdlr, mbox %d is free, count %d.\n", BN(j), i,
+ HD(j)->iocount);
+ return;
+ }
+ else if (HD(j)->cp_stat[i] == IN_RESET)
+ printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
+ else if (HD(j)->cp_stat[i] != IN_USE)
+ panic("%s: ihdlr, mbox %d, invalid cp_stat: %d.\n",
+ BN(j), i, HD(j)->cp_stat[i]);
+
+ HD(j)->cp_stat[i] = FREE;
+ SCpnt = cpp->SCpnt;
+
+ if (SCpnt == NULL) panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n", BN(j), i,
+ SCpnt->pid, SCpnt);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d.\n",
+ BN(j), i, SCpnt->pid, *(unsigned int *)SCpnt->host_scribble);
+
+ if (linked_comm && SCpnt->device->queue_depth > 2
+ && TLDEV(SCpnt->device->type))
+ flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
+
+ tstatus = status_byte(spp->target_status);
+
+#if defined(DEBUG_GENERATE_ERRORS)
+ if ((HD(j)->iocount > 500) && ((HD(j)->iocount % 200) < 2))
+ spp->adapter_status = 0x01;
+#endif
+
+ switch (spp->adapter_status) {
+ case ASOK: /* status OK */
+
+ /* Forces a reset if a disk drive keeps returning BUSY */
+ if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
+ status = DID_ERROR << 16;
+
+ /* If there was a bus reset, redo operation on each target */
+ else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
+ && HD(j)->target_redo[SCpnt->target][SCpnt->channel])
+ status = DID_BUS_BUSY << 16;
+
+ /* Works around a flaw in scsi.c */
+ else if (tstatus == CHECK_CONDITION
+ && SCpnt->device->type == TYPE_DISK
+ && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+ status = DID_BUS_BUSY << 16;
+
+ else
+ status = DID_OK << 16;
+
+ if (tstatus == GOOD)
+ HD(j)->target_redo[SCpnt->target][SCpnt->channel] = FALSE;
+
+ if (spp->target_status && SCpnt->device->type == TYPE_DISK)
+ printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
+ "target_status 0x%x, sense key 0x%x.\n", BN(j),
+ SCpnt->channel, SCpnt->target, SCpnt->lun,
+ SCpnt->pid, spp->target_status,
+ SCpnt->sense_buffer[2]);
+
+ HD(j)->target_to[SCpnt->target][SCpnt->channel] = 0;
+
+ if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
+
+ break;
+ case ASST: /* Selection Time Out */
+
+ if (HD(j)->target_to[SCpnt->target][SCpnt->channel] > 1)
+ status = DID_ERROR << 16;
+ else {
+ status = DID_TIME_OUT << 16;
+ HD(j)->target_to[SCpnt->target][SCpnt->channel]++;
+ }
+
+ break;
+
+ /* Perform a limited number of internal retries */
+ case 0x93: /* Unexpected bus free */
+ case 0x94: /* Target bus phase sequence failure */
+ case 0x96: /* Illegal SCSI command */
+ case 0xa3: /* SCSI bus reset error */
+
+ for (c = 0; c <= sh[j]->max_channel; c++)
+ for (k = 0; k < sh[j]->max_id; k++)
+ HD(j)->target_redo[k][c] = TRUE;
+
+
+ case 0x92: /* Data over/under-run */
+
+ if (SCpnt->device->type != TYPE_TAPE
+ && HD(j)->retries < MAX_INTERNAL_RETRIES) {
+
+#if defined(DID_SOFT_ERROR)
+ status = DID_SOFT_ERROR << 16;
+#else
+ status = DID_BUS_BUSY << 16;
+#endif
+
+ HD(j)->retries++;
+ HD(j)->last_retried_pid = SCpnt->pid;
+ }
+ else
+ status = DID_ERROR << 16;
+
+ break;
+ case 0x01: /* Invalid command */
+ case 0x02: /* Invalid parameters */
+ case 0x03: /* Invalid data list */
+ case 0x84: /* SCSI bus abort error */
+ case 0x9b: /* Auto request sense error */
+ case 0x9f: /* Unexpected command complete message error */
+ case 0xff: /* Invalid parameter in the S/G list */
+ default:
+ status = DID_ERROR << 16;
+ break;
+ }
+
+ SCpnt->result = status | spp->target_status;
+
+#if defined(DEBUG_INTERRUPT)
+ if (SCpnt->result || do_trace)
+#else
+ if ((spp->adapter_status != ASOK && HD(j)->iocount > 1000) ||
+ (spp->adapter_status != ASOK &&
+ spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
+ do_trace || msg_byte(spp->target_status))
+#endif
+ printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
+ " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
+ BN(j), i, spp->adapter_status, spp->target_status,
+ SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
+ reg, HD(j)->iocount);
+
+ /* Set the command state to inactive */
+ SCpnt->host_scribble = NULL;
+
+ SCpnt->scsi_done(SCpnt);
+
+ if (do_trace) printk("%s: ihdlr, exit, irq %d, count %d.\n", BN(j), irq,
+ HD(j)->iocount);
+
+ return;
+}
+
+static void do_interrupt_handler(int irq, void *shap, struct pt_regs *regs) {
+ unsigned int j;
+ IRQ_FLAGS
+ SPIN_FLAGS
+
+ /* Check if the interrupt must be processed by this handler */
+ if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return;
+
+ SPIN_LOCK_SAVE
+ IRQ_LOCK_SAVE
+ ihdlr(irq, j);
+ IRQ_UNLOCK_RESTORE
+ SPIN_UNLOCK_RESTORE
+}
+
+int u14_34f_release(struct Scsi_Host *shpnt) {
+ unsigned int i, j;
+ IRQ_FLAGS
+
+ IRQ_LOCK_SAVE
+
+ for (j = 0; sh[j] != NULL && sh[j] != shpnt; j++);
+
+ if (sh[j] == NULL) panic("%s: release, invalid Scsi_Host pointer.\n",
+ driver_name);
+
+ for (i = 0; i < sh[j]->can_queue; i++)
+ if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist);
+
+ free_irq(sh[j]->irq, &sha[j]);
+
+ if (sh[j]->dma_channel != NO_DMA) free_dma(sh[j]->dma_channel);
+
+ release_region(sh[j]->io_port, sh[j]->n_io_port);
+ scsi_unregister(sh[j]);
+ IRQ_UNLOCK_RESTORE
+ return FALSE;
+}
+
+#if defined(MODULE)
+Scsi_Host_Template driver_template = ULTRASTOR_14_34F;
+
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/u14-34f.h b/linux/src/drivers/scsi/u14-34f.h
new file mode 100644
index 00000000..943b8cbb
--- /dev/null
+++ b/linux/src/drivers/scsi/u14-34f.h
@@ -0,0 +1,60 @@
+/*
+ * u14-34f.h - used by the low-level driver for UltraStor 14F/34F
+ */
+#ifndef _U14_34F_H
+#define _U14_34F_H
+
+#include <scsi/scsicam.h>
+#include <linux/version.h>
+
+int u14_34f_detect(Scsi_Host_Template *);
+int u14_34f_release(struct Scsi_Host *);
+int u14_34f_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int u14_34f_abort(Scsi_Cmnd *);
+int u14_34f_old_abort(Scsi_Cmnd *);
+int u14_34f_reset(Scsi_Cmnd *);
+int u14_34f_old_reset(Scsi_Cmnd *, unsigned int);
+int u14_34f_biosparam(Disk *, kdev_t, int *);
+
+#define U14_34F_VERSION "4.33.00"
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
+
+#define ULTRASTOR_14_34F { \
+ name: "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
+ detect: u14_34f_detect, \
+ release: u14_34f_release, \
+ queuecommand: u14_34f_queuecommand, \
+ abort: u14_34f_old_abort, \
+ reset: u14_34f_old_reset, \
+ eh_abort_handler: u14_34f_abort, \
+ eh_device_reset_handler: NULL, \
+ eh_bus_reset_handler: NULL, \
+ eh_host_reset_handler: u14_34f_reset, \
+ bios_param: u14_34f_biosparam, \
+ this_id: 7, \
+ unchecked_isa_dma: 1, \
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 1 /* Enable new error code */ \
+ }
+
+#else /* Use old scsi code */
+
+#define ULTRASTOR_14_34F { \
+ name: "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
+ detect: u14_34f_detect, \
+ release: u14_34f_release, \
+ queuecommand: u14_34f_queuecommand, \
+ abort: u14_34f_old_abort, \
+ reset: u14_34f_old_reset, \
+ bios_param: u14_34f_biosparam, \
+ this_id: 7, \
+ unchecked_isa_dma: 1, \
+ use_clustering: ENABLE_CLUSTERING \
+ }
+
+#endif
+
+#endif
diff --git a/linux/src/drivers/scsi/ultrastor.c b/linux/src/drivers/scsi/ultrastor.c
new file mode 100644
index 00000000..3d9b4118
--- /dev/null
+++ b/linux/src/drivers/scsi/ultrastor.c
@@ -0,0 +1,1165 @@
+/*
+ * ultrastor.c Copyright (C) 1992 David B. Gentzel
+ * Low-level SCSI driver for UltraStor 14F, 24F, and 34F
+ * by David B. Gentzel, Whitfield Software Services, Carnegie, PA
+ * (gentzel@nova.enet.dec.com)
+ * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu)
+ * 24F and multiple command support by John F. Carr (jfc@athena.mit.edu)
+ * John's work modified by Caleb Epstein (cae@jpmorgan.com) and
+ * Eric Youngdale (ericy@cais.com).
+ * Thanks to UltraStor for providing the necessary documentation
+ */
+
+/*
+ * TODO:
+ * 1. Find out why scatter/gather is limited to 16 requests per command.
+ * This is fixed, at least on the 24F, as of version 1.12 - CAE.
+ * 2. Look at command linking (mscp.command_link and
+ * mscp.command_link_id). (Does not work with many disks,
+ * and no performance increase. ERY).
+ * 3. Allow multiple adapters.
+ */
+
+/*
+ * NOTES:
+ * The UltraStor 14F, 24F, and 34F are a family of intelligent, high
+ * performance SCSI-2 host adapters. They all support command queueing
+ * and scatter/gather I/O. Some of them can also emulate the standard
+ * WD1003 interface for use with OS's which don't support SCSI. Here
+ * is the scoop on the various models:
+ * 14F - ISA first-party DMA HA with floppy support and WD1003 emulation.
+ * 14N - ISA HA with floppy support. I think that this is a non-DMA
+ * HA. Nothing further known.
+ * 24F - EISA Bus Master HA with floppy support and WD1003 emulation.
+ * 34F - VL-Bus Bus Master HA with floppy support (no WD1003 emulation).
+ *
+ * The 14F, 24F, and 34F are supported by this driver.
+ *
+ * Places flagged with a triple question-mark are things which are either
+ * unfinished, questionable, or wrong.
+ */
+
+/* Changes from version 1.11 alpha to 1.12
+ *
+ * Increased the size of the scatter-gather list to 33 entries for
+ * the 24F adapter (it was 16). I don't have the specs for the 14F
+ * or the 34F, so they may support larger s-g lists as well.
+ *
+ * Caleb Epstein <cae@jpmorgan.com>
+ */
+
+/* Changes from version 1.9 to 1.11
+ *
+ * Patches to bring this driver up to speed with the default kernel
+ * driver which supports only the 14F and 34F adapters. This version
+ * should compile cleanly into 0.99.13, 0.99.12 and probably 0.99.11.
+ *
+ * Fixes from Eric Youngdale to fix a few possible race conditions and
+ * several problems with bit testing operations (insufficient
+ * parentheses).
+ *
+ * Removed the ultrastor_abort() and ultrastor_reset() functions
+ * (enclosed them in #if 0 / #endif). These functions, at least on
+ * the 24F, cause the SCSI bus to do odd things and generally lead to
+ * kernel panics and machine hangs. This is like the Adaptec code.
+ *
+ * Use check/snarf_region for 14f, 34f to avoid I/O space address conflicts.
+ */
+
+/* Changes from version 1.8 to version 1.9
+ *
+ * 0.99.11 patches (cae@jpmorgan.com) */
+
+/* Changes from version 1.7 to version 1.8
+ *
+ * Better error reporting.
+ */
+
+/* Changes from version 1.6 to version 1.7
+ *
+ * Removed CSIR command code.
+ *
+ * Better race condition avoidance (xchgb function added).
+ *
+ * Set ICM and OGM status to zero at probe (24F)
+ *
+ * reset sends soft reset to UltraStor adapter
+ *
+ * reset adapter if adapter interrupts with an invalid MSCP address
+ *
+ * handle aborted command interrupt (24F)
+ *
+ */
+
+/* Changes from version 1.5 to version 1.6:
+ *
+ * Read MSCP address from ICM _before_ clearing the interrupt flag.
+ * This fixes a race condition.
+ */
+
+/* Changes from version 1.4 to version 1.5:
+ *
+ * Abort now calls done when multiple commands are enabled.
+ *
+ * Clear busy when aborted command finishes, not when abort is called.
+ *
+ * More debugging messages for aborts.
+ */
+
+/* Changes from version 1.3 to version 1.4:
+ *
+ * Enable automatic request of sense data on error (requires newer version
+ * of scsi.c to be useful).
+ *
+ * Fix PORT_OVERRIDE for 14F.
+ *
+ * Fix abort and reset to work properly (config.aborted wasn't cleared
+ * after it was tested, so after a command abort no further commands would
+ * work).
+ *
+ * Boot time test to enable SCSI bus reset (defaults to not allowing reset).
+ *
+ * Fix test for OGM busy -- the busy bit is in different places on the 24F.
+ *
+ * Release ICM slot by clearing first byte on 24F.
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+
+#define ULTRASTOR_PRIVATE /* Get the private stuff from ultrastor.h */
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "ultrastor.h"
+#include "sd.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_ultrastor = {
+ PROC_SCSI_ULTRASTOR, 9, "ultrastor",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#define FALSE 0
+#define TRUE 1
+
+#ifndef ULTRASTOR_DEBUG
+#define ULTRASTOR_DEBUG (UD_ABORT|UD_CSIR|UD_RESET)
+#endif
+
+#define VERSION "1.12"
+
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+
+#define PACKED __attribute__((packed))
+#define ALIGNED(x) __attribute__((aligned(x)))
+
+
+/* The 14F uses an array of 4-byte ints for its scatter/gather list.
+ The data can be unaligned, but need not be. It's easier to give
+ the list normal alignment since it doesn't need to fit into a
+ packed structure. */
+
+typedef struct {
+ unsigned int address;
+ unsigned int num_bytes;
+} ultrastor_sg_list;
+
+
+/* MailBox SCSI Command Packet. Basic command structure for communicating
+ with controller. */
+struct mscp {
+ unsigned char opcode: 3; /* type of command */
+ unsigned char xdir: 2; /* data transfer direction */
+ unsigned char dcn: 1; /* disable disconnect */
+ unsigned char ca: 1; /* use cache (if available) */
+ unsigned char sg: 1; /* scatter/gather operation */
+ unsigned char target_id: 3; /* target SCSI id */
+ unsigned char ch_no: 2; /* SCSI channel (always 0 for 14f) */
+ unsigned char lun: 3; /* logical unit number */
+ unsigned int transfer_data PACKED; /* transfer data pointer */
+ unsigned int transfer_data_length PACKED; /* length in bytes */
+ unsigned int command_link PACKED; /* for linking command chains */
+ unsigned char scsi_command_link_id; /* identifies command in chain */
+ unsigned char number_of_sg_list; /* (if sg is set) 8 bytes per list */
+ unsigned char length_of_sense_byte;
+ unsigned char length_of_scsi_cdbs; /* 6, 10, or 12 */
+ unsigned char scsi_cdbs[12]; /* SCSI commands */
+ unsigned char adapter_status; /* non-zero indicates HA error */
+ unsigned char target_status; /* non-zero indicates target error */
+ unsigned int sense_data PACKED;
+ /* The following fields are for software only. They are included in
+ the MSCP structure because they are associated with SCSI requests. */
+ void (*done)(Scsi_Cmnd *);
+ Scsi_Cmnd *SCint;
+ ultrastor_sg_list sglist[ULTRASTOR_24F_MAX_SG]; /* use larger size for 24F */
+};
+
+
+/* Port addresses (relative to the base address) */
+#define U14F_PRODUCT_ID(port) ((port) + 0x4)
+#define CONFIG(port) ((port) + 0x6)
+
+/* Port addresses relative to the doorbell base address. */
+#define LCL_DOORBELL_MASK(port) ((port) + 0x0)
+#define LCL_DOORBELL_INTR(port) ((port) + 0x1)
+#define SYS_DOORBELL_MASK(port) ((port) + 0x2)
+#define SYS_DOORBELL_INTR(port) ((port) + 0x3)
+
+
+/* Used to store configuration info read from config i/o registers. Most of
+ this is not used yet, but might as well save it.
+
+ This structure also holds port addresses that are not at the same offset
+ on the 14F and 24F.
+
+ This structure holds all data that must be duplicated to support multiple
+ adapters. */
+
+static struct ultrastor_config
+{
+ unsigned short port_address; /* base address of card */
+ unsigned short doorbell_address; /* base address of doorbell CSRs */
+ unsigned short ogm_address; /* base address of OGM */
+ unsigned short icm_address; /* base address of ICM */
+ const void *bios_segment;
+ unsigned char interrupt: 4;
+ unsigned char dma_channel: 3;
+ unsigned char bios_drive_number: 1;
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned char ha_scsi_id: 3;
+ unsigned char subversion: 4;
+ unsigned char revision;
+ /* The slot number is used to distinguish the 24F (slot != 0) from
+ the 14F and 34F (slot == 0). */
+ unsigned char slot;
+
+#ifdef PRINT_U24F_VERSION
+ volatile int csir_done;
+#endif
+
+ /* A pool of MSCP structures for this adapter, and a bitmask of
+ busy structures. (If ULTRASTOR_14F_MAX_CMDS == 1, a 1 byte
+ busy flag is used instead.) */
+
+#if ULTRASTOR_MAX_CMDS == 1
+ unsigned char mscp_busy;
+#else
+ unsigned short mscp_free;
+#endif
+ volatile unsigned char aborted[ULTRASTOR_MAX_CMDS];
+ struct mscp mscp[ULTRASTOR_MAX_CMDS];
+} config = {0};
+
+/* Set this to 1 to reset the SCSI bus on error. */
+int ultrastor_bus_reset = 0;
+
+
+/* Allowed BIOS base addresses (NULL indicates reserved) */
+static const void *const bios_segment_table[8] = {
+ NULL, (void *)0xC4000, (void *)0xC8000, (void *)0xCC000,
+ (void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000,
+};
+
+/* Allowed IRQs for 14f */
+static const unsigned char interrupt_table_14f[4] = { 15, 14, 11, 10 };
+
+/* Allowed DMA channels for 14f (0 indicates reserved) */
+static const unsigned char dma_channel_table_14f[4] = { 5, 6, 7, 0 };
+
+/* Head/sector mappings allowed by 14f */
+static const struct {
+ unsigned char heads;
+ unsigned char sectors;
+} mapping_table[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 } };
+
+#ifndef PORT_OVERRIDE
+/* ??? A probe of address 0x310 screws up NE2000 cards */
+static const unsigned short ultrastor_ports_14f[] = {
+ 0x330, 0x340, /*0x310,*/ 0x230, 0x240, 0x210, 0x130, 0x140,
+};
+#endif
+
+static void ultrastor_interrupt(int, void *, struct pt_regs *);
+static inline void build_sg_list(struct mscp *, Scsi_Cmnd *SCpnt);
+
+
+static inline int find_and_clear_bit_16(unsigned short *field)
+{
+ int rv;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (*field == 0) panic("No free mscp");
+ asm("xorl %0,%0\n0:\tbsfw %1,%w0\n\tbtr %0,%1\n\tjnc 0b"
+ : "=&r" (rv), "=m" (*field) : "1" (*field));
+ restore_flags(flags);
+ return rv;
+}
+
+/* This has been re-implemented with the help of Richard Earnshaw,
+ <rwe@pegasus.esprit.ec.org> and works with gcc-2.5.8 and gcc-2.6.0.
+ The instability noted by jfc below appears to be a bug in
+ gcc-2.5.x when compiling w/o optimization. --Caleb
+
+ This asm is fragile: it doesn't work without the casts and it may
+ not work without optimization. Maybe I should add a swap builtin
+ to gcc. --jfc */
+static inline unsigned char xchgb(unsigned char reg,
+ volatile unsigned char *mem)
+{
+ __asm__ ("xchgb %0,%1" : "=q" (reg), "=m" (*mem) : "0" (reg));
+ return reg;
+}
+
+#if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT)
+
+static void log_ultrastor_abort(register struct ultrastor_config *config,
+ int command)
+{
+ static char fmt[80] = "abort %d (%x); MSCP free pool: %x;";
+ register int i;
+ int flags;
+ save_flags(flags);
+ cli();
+
+ for (i = 0; i < ULTRASTOR_MAX_CMDS; i++)
+ {
+ fmt[20 + i*2] = ' ';
+ if (! (config->mscp_free & (1 << i)))
+ fmt[21 + i*2] = '0' + config->mscp[i].target_id;
+ else
+ fmt[21 + i*2] = '-';
+ }
+ fmt[20 + ULTRASTOR_MAX_CMDS * 2] = '\n';
+ fmt[21 + ULTRASTOR_MAX_CMDS * 2] = 0;
+ printk(fmt, command, &config->mscp[command], config->mscp_free);
+ restore_flags(flags);
+}
+#endif
+
+static int ultrastor_14f_detect(Scsi_Host_Template * tpnt)
+{
+ size_t i;
+ unsigned char in_byte, version_byte = 0;
+ struct config_1 {
+ unsigned char bios_segment: 3;
+ unsigned char removable_disks_as_fixed: 1;
+ unsigned char interrupt: 2;
+ unsigned char dma_channel: 2;
+ } config_1;
+ struct config_2 {
+ unsigned char ha_scsi_id: 3;
+ unsigned char mapping_mode: 2;
+ unsigned char bios_drive_number: 1;
+ unsigned char tfr_port: 2;
+ } config_2;
+
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: called\n");
+#endif
+
+ /* If a 24F has already been configured, don't look for a 14F. */
+ if (config.bios_segment)
+ return FALSE;
+
+#ifdef PORT_OVERRIDE
+ if(check_region(PORT_OVERRIDE, 0xc)) {
+ printk("Ultrastor I/O space already in use\n");
+ return FALSE;
+ };
+ config.port_address = PORT_OVERRIDE;
+#else
+ for (i = 0; i < ARRAY_SIZE(ultrastor_ports_14f); i++) {
+ if(check_region(ultrastor_ports_14f[i], 0x0c)) continue;
+ config.port_address = ultrastor_ports_14f[i];
+#endif
+
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: testing port address %03X\n", config.port_address);
+#endif
+
+ in_byte = inb(U14F_PRODUCT_ID(config.port_address));
+ if (in_byte != US14F_PRODUCT_ID_0) {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+# ifdef PORT_OVERRIDE
+ printk("US14F: detect: wrong product ID 0 - %02X\n", in_byte);
+# else
+ printk("US14F: detect: no adapter at port %03X\n", config.port_address);
+# endif
+#endif
+#ifdef PORT_OVERRIDE
+ return FALSE;
+#else
+ continue;
+#endif
+ }
+ in_byte = inb(U14F_PRODUCT_ID(config.port_address) + 1);
+ /* Only upper nibble is significant for Product ID 1 */
+ if ((in_byte & 0xF0) != US14F_PRODUCT_ID_1) {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+# ifdef PORT_OVERRIDE
+ printk("US14F: detect: wrong product ID 1 - %02X\n", in_byte);
+# else
+ printk("US14F: detect: no adapter at port %03X\n", config.port_address);
+# endif
+#endif
+#ifdef PORT_OVERRIDE
+ return FALSE;
+#else
+ continue;
+#endif
+ }
+ version_byte = in_byte;
+#ifndef PORT_OVERRIDE
+ break;
+ }
+ if (i == ARRAY_SIZE(ultrastor_ports_14f)) {
+# if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: no port address found!\n");
+# endif
+ return FALSE;
+ }
+#endif
+
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: adapter found at port address %03X\n",
+ config.port_address);
+#endif
+
+ /* Set local doorbell mask to disallow bus reset unless
+ ultrastor_bus_reset is true. */
+ outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(config.port_address));
+
+ /* All above tests passed, must be the right thing. Get some useful
+ info. */
+
+ request_region(config.port_address, 0x0c,"ultrastor");
+ /* Register the I/O space that we use */
+
+ *(char *)&config_1 = inb(CONFIG(config.port_address + 0));
+ *(char *)&config_2 = inb(CONFIG(config.port_address + 1));
+ config.bios_segment = bios_segment_table[config_1.bios_segment];
+ config.doorbell_address = config.port_address;
+ config.ogm_address = config.port_address + 0x8;
+ config.icm_address = config.port_address + 0xC;
+ config.interrupt = interrupt_table_14f[config_1.interrupt];
+ config.ha_scsi_id = config_2.ha_scsi_id;
+ config.heads = mapping_table[config_2.mapping_mode].heads;
+ config.sectors = mapping_table[config_2.mapping_mode].sectors;
+ config.bios_drive_number = config_2.bios_drive_number;
+ config.subversion = (version_byte & 0x0F);
+ if (config.subversion == U34F)
+ config.dma_channel = 0;
+ else
+ config.dma_channel = dma_channel_table_14f[config_1.dma_channel];
+
+ if (!config.bios_segment) {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: not detected.\n");
+#endif
+ return FALSE;
+ }
+
+ /* Final consistency check, verify previous info. */
+ if (config.subversion != U34F)
+ if (!config.dma_channel || !(config_2.tfr_port & 0x2)) {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: consistency check failed\n");
+#endif
+ return FALSE;
+ }
+
+ /* If we were TRULY paranoid, we could issue a host adapter inquiry
+ command here and verify the data returned. But frankly, I'm
+ exhausted! */
+
+ /* Finally! Now I'm satisfied... */
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: detect succeeded\n"
+ " Port address: %03X\n"
+ " BIOS segment: %05X\n"
+ " Interrupt: %u\n"
+ " DMA channel: %u\n"
+ " H/A SCSI ID: %u\n"
+ " Subversion: %u\n",
+ config.port_address, config.bios_segment, config.interrupt,
+ config.dma_channel, config.ha_scsi_id, config.subversion);
+#endif
+ tpnt->this_id = config.ha_scsi_id;
+ tpnt->unchecked_isa_dma = (config.subversion != U34F);
+
+#if ULTRASTOR_MAX_CMDS > 1
+ config.mscp_free = ~0;
+#endif
+
+ if (request_irq(config.interrupt, ultrastor_interrupt, 0, "Ultrastor", NULL)) {
+ printk("Unable to allocate IRQ%u for UltraStor controller.\n",
+ config.interrupt);
+ return FALSE;
+ }
+ if (config.dma_channel && request_dma(config.dma_channel,"Ultrastor")) {
+ printk("Unable to allocate DMA channel %u for UltraStor controller.\n",
+ config.dma_channel);
+ free_irq(config.interrupt, NULL);
+ return FALSE;
+ }
+ tpnt->sg_tablesize = ULTRASTOR_14F_MAX_SG;
+ printk("UltraStor driver version" VERSION ". Using %d SG lists.\n",
+ ULTRASTOR_14F_MAX_SG);
+
+ return TRUE;
+}
+
+static int ultrastor_24f_detect(Scsi_Host_Template * tpnt)
+{
+ register int i;
+ struct Scsi_Host * shpnt = NULL;
+
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US24F: detect");
+#endif
+
+ /* probe each EISA slot at slot address C80 */
+ for (i = 1; i < 15; i++)
+ {
+ unsigned char config_1, config_2;
+ unsigned short addr = (i << 12) | ULTRASTOR_24F_PORT;
+
+ if (inb(addr) != US24F_PRODUCT_ID_0 &&
+ inb(addr+1) != US24F_PRODUCT_ID_1 &&
+ inb(addr+2) != US24F_PRODUCT_ID_2)
+ continue;
+
+ config.revision = inb(addr+3);
+ config.slot = i;
+ if (! (inb(addr+4) & 1))
+ {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("U24F: found disabled card in slot %u\n", i);
+#endif
+ continue;
+ }
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("U24F: found card in slot %u\n", i);
+#endif
+ config_1 = inb(addr + 5);
+ config.bios_segment = bios_segment_table[config_1 & 7];
+ switch(config_1 >> 4)
+ {
+ case 1:
+ config.interrupt = 15;
+ break;
+ case 2:
+ config.interrupt = 14;
+ break;
+ case 4:
+ config.interrupt = 11;
+ break;
+ case 8:
+ config.interrupt = 10;
+ break;
+ default:
+ printk("U24F: invalid IRQ\n");
+ return FALSE;
+ }
+ if (request_irq(config.interrupt, ultrastor_interrupt, 0, "Ultrastor", NULL))
+ {
+ printk("Unable to allocate IRQ%u for UltraStor controller.\n",
+ config.interrupt);
+ return FALSE;
+ }
+ /* BIOS addr set */
+ /* base port set */
+ config.port_address = addr;
+ config.doorbell_address = addr + 12;
+ config.ogm_address = addr + 0x17;
+ config.icm_address = addr + 0x1C;
+ config_2 = inb(addr + 7);
+ config.ha_scsi_id = config_2 & 7;
+ config.heads = mapping_table[(config_2 >> 3) & 3].heads;
+ config.sectors = mapping_table[(config_2 >> 3) & 3].sectors;
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US24F: detect: detect succeeded\n"
+ " Port address: %03X\n"
+ " BIOS segment: %05X\n"
+ " Interrupt: %u\n"
+ " H/A SCSI ID: %u\n",
+ config.port_address, config.bios_segment,
+ config.interrupt, config.ha_scsi_id);
+#endif
+ tpnt->this_id = config.ha_scsi_id;
+ tpnt->unchecked_isa_dma = 0;
+ tpnt->sg_tablesize = ULTRASTOR_24F_MAX_SG;
+
+ shpnt = scsi_register(tpnt, 0);
+ shpnt->irq = config.interrupt;
+ shpnt->dma_channel = config.dma_channel;
+ shpnt->io_port = config.port_address;
+
+#if ULTRASTOR_MAX_CMDS > 1
+ config.mscp_free = ~0;
+#endif
+ /* Mark ICM and OGM free */
+ outb(0, addr + 0x16);
+ outb(0, addr + 0x1B);
+
+ /* Set local doorbell mask to disallow bus reset unless
+ ultrastor_bus_reset is true. */
+ outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(addr+12));
+ outb(0x02, SYS_DOORBELL_MASK(addr+12));
+ printk("UltraStor driver version " VERSION ". Using %d SG lists.\n",
+ tpnt->sg_tablesize);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int ultrastor_detect(Scsi_Host_Template * tpnt)
+{
+ tpnt->proc_dir = &proc_scsi_ultrastor;
+ return ultrastor_14f_detect(tpnt) || ultrastor_24f_detect(tpnt);
+}
+
+const char *ultrastor_info(struct Scsi_Host * shpnt)
+{
+ static char buf[64];
+
+ if (config.slot)
+ sprintf(buf, "UltraStor 24F SCSI @ Slot %u IRQ%u",
+ config.slot, config.interrupt);
+ else if (config.subversion)
+ sprintf(buf, "UltraStor 34F SCSI @ Port %03X BIOS %05X IRQ%u",
+ config.port_address, (int)config.bios_segment,
+ config.interrupt);
+ else
+ sprintf(buf, "UltraStor 14F SCSI @ Port %03X BIOS %05X IRQ%u DMA%u",
+ config.port_address, (int)config.bios_segment,
+ config.interrupt, config.dma_channel);
+ return buf;
+}
+
+static inline void build_sg_list(register struct mscp *mscp, Scsi_Cmnd *SCpnt)
+{
+ struct scatterlist *sl;
+ long transfer_length = 0;
+ int i, max;
+
+ sl = (struct scatterlist *) SCpnt->request_buffer;
+ max = SCpnt->use_sg;
+ for (i = 0; i < max; i++) {
+ mscp->sglist[i].address = (unsigned int)sl[i].address;
+ mscp->sglist[i].num_bytes = sl[i].length;
+ transfer_length += sl[i].length;
+ }
+ mscp->number_of_sg_list = max;
+ mscp->transfer_data = (unsigned int)mscp->sglist;
+ /* ??? May not be necessary. Docs are unclear as to whether transfer
+ length field is ignored or whether it should be set to the total
+ number of bytes of the transfer. */
+ mscp->transfer_data_length = transfer_length;
+}
+
+int ultrastor_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ register struct mscp *my_mscp;
+#if ULTRASTOR_MAX_CMDS > 1
+ int mscp_index;
+#endif
+ unsigned int status;
+ int flags;
+
+ /* Next test is for debugging; "can't happen" */
+ if ((config.mscp_free & ((1U << ULTRASTOR_MAX_CMDS) - 1)) == 0)
+ panic("ultrastor_queuecommand: no free MSCP\n");
+ mscp_index = find_and_clear_bit_16(&config.mscp_free);
+
+ /* Has the command been aborted? */
+ if (xchgb(0xff, &config.aborted[mscp_index]) != 0)
+ {
+ status = DID_ABORT << 16;
+ goto aborted;
+ }
+
+ my_mscp = &config.mscp[mscp_index];
+
+#if 1
+ /* This way is faster. */
+ *(unsigned char *)my_mscp = OP_SCSI | (DTD_SCSI << 3);
+#else
+ my_mscp->opcode = OP_SCSI;
+ my_mscp->xdir = DTD_SCSI;
+ my_mscp->dcn = FALSE;
+#endif
+ /* Tape drives don't work properly if the cache is used. The SCSI
+ READ command for a tape doesn't have a block offset, and the adapter
+ incorrectly assumes that all reads from the tape read the same
+ blocks. Results will depend on read buffer size and other disk
+ activity.
+
+ ??? Which other device types should never use the cache? */
+ my_mscp->ca = SCpnt->device->type != TYPE_TAPE;
+ my_mscp->target_id = SCpnt->target;
+ my_mscp->ch_no = 0;
+ my_mscp->lun = SCpnt->lun;
+ if (SCpnt->use_sg) {
+ /* Set scatter/gather flag in SCSI command packet */
+ my_mscp->sg = TRUE;
+ build_sg_list(my_mscp, SCpnt);
+ } else {
+ /* Unset scatter/gather flag in SCSI command packet */
+ my_mscp->sg = FALSE;
+ my_mscp->transfer_data = (unsigned int)SCpnt->request_buffer;
+ my_mscp->transfer_data_length = SCpnt->request_bufflen;
+ }
+ my_mscp->command_link = 0; /*???*/
+ my_mscp->scsi_command_link_id = 0; /*???*/
+ my_mscp->length_of_sense_byte = sizeof SCpnt->sense_buffer;
+ my_mscp->length_of_scsi_cdbs = SCpnt->cmd_len;
+ memcpy(my_mscp->scsi_cdbs, SCpnt->cmnd, my_mscp->length_of_scsi_cdbs);
+ my_mscp->adapter_status = 0;
+ my_mscp->target_status = 0;
+ my_mscp->sense_data = (unsigned int)&SCpnt->sense_buffer;
+ my_mscp->done = done;
+ my_mscp->SCint = SCpnt;
+ SCpnt->host_scribble = (unsigned char *)my_mscp;
+
+ /* Find free OGM slot. On 24F, look for OGM status byte == 0.
+ On 14F and 34F, wait for local interrupt pending flag to clear. */
+
+ retry:
+ if (config.slot)
+ while (inb(config.ogm_address - 1) != 0 &&
+ config.aborted[mscp_index] == 0xff) barrier();
+
+ /* else??? */
+
+ while ((inb(LCL_DOORBELL_INTR(config.doorbell_address)) &
+ (config.slot ? 2 : 1))
+ && config.aborted[mscp_index] == 0xff) barrier();
+
+ /* To avoid race conditions, make the code to write to the adapter
+ atomic. This simplifies the abort code. */
+
+ save_flags(flags);
+ cli();
+
+ if (inb(LCL_DOORBELL_INTR(config.doorbell_address)) &
+ (config.slot ? 2 : 1))
+ {
+ restore_flags(flags);
+ goto retry;
+ }
+
+ status = xchgb(0, &config.aborted[mscp_index]);
+ if (status != 0xff) {
+ restore_flags(flags);
+
+#if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT)
+ printk("USx4F: queuecommand: aborted\n");
+#if ULTRASTOR_MAX_CMDS > 1
+ log_ultrastor_abort(&config, mscp_index);
+#endif
+#endif
+ status <<= 16;
+
+ aborted:
+ set_bit(mscp_index, &config.mscp_free);
+ /* If the driver queues commands, call the done proc here. Otherwise
+ return an error. */
+#if ULTRASTOR_MAX_CMDS > 1
+ SCpnt->result = status;
+ done(SCpnt);
+ return 0;
+#else
+ return status;
+#endif
+ }
+
+ /* Store pointer in OGM address bytes */
+ outl((unsigned int)my_mscp, config.ogm_address);
+
+ /* Issue OGM interrupt */
+ if (config.slot) {
+ /* Write OGM command register on 24F */
+ outb(1, config.ogm_address - 1);
+ outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address));
+ } else {
+ outb(0x1, LCL_DOORBELL_INTR(config.doorbell_address));
+ }
+
+ restore_flags(flags);
+
+#if (ULTRASTOR_DEBUG & UD_COMMAND)
+ printk("USx4F: queuecommand: returning\n");
+#endif
+
+ return 0;
+}
+
+/* This code must deal with 2 cases:
+
+ 1. The command has not been written to the OGM. In this case, set
+ the abort flag and return.
+
+ 2. The command has been written to the OGM and is stuck somewhere in
+ the adapter.
+
+ 2a. On a 24F, ask the adapter to abort the command. It will interrupt
+ when it does.
+
+ 2b. Call the command's done procedure.
+
+ */
+
+int ultrastor_abort(Scsi_Cmnd *SCpnt)
+{
+#if ULTRASTOR_DEBUG & UD_ABORT
+ char out[108];
+ unsigned char icm_status = 0, ogm_status = 0;
+ unsigned int icm_addr = 0, ogm_addr = 0;
+#endif
+ unsigned int mscp_index;
+ unsigned char old_aborted;
+ void (*done)(Scsi_Cmnd *);
+
+ if(config.slot)
+ return SCSI_ABORT_SNOOZE; /* Do not attempt an abort for the 24f */
+
+ /* Simple consistency checking */
+ if(!SCpnt->host_scribble)
+ return SCSI_ABORT_NOT_RUNNING;
+
+ mscp_index = ((struct mscp *)SCpnt->host_scribble) - config.mscp;
+ if (mscp_index >= ULTRASTOR_MAX_CMDS)
+ panic("Ux4F aborting invalid MSCP");
+
+#if ULTRASTOR_DEBUG & UD_ABORT
+ if (config.slot)
+ {
+ int port0 = (config.slot << 12) | 0xc80;
+ int i;
+ int flags;
+ save_flags(flags);
+ cli();
+ strcpy(out, "OGM %d:%x ICM %d:%x ports: ");
+ for (i = 0; i < 16; i++)
+ {
+ unsigned char p = inb(port0 + i);
+ out[28 + i * 3] = "0123456789abcdef"[p >> 4];
+ out[29 + i * 3] = "0123456789abcdef"[p & 15];
+ out[30 + i * 3] = ' ';
+ }
+ out[28 + i * 3] = '\n';
+ out[29 + i * 3] = 0;
+ ogm_status = inb(port0 + 22);
+ ogm_addr = inl(port0 + 23);
+ icm_status = inb(port0 + 27);
+ icm_addr = inl(port0 + 28);
+ restore_flags(flags);
+ }
+
+ /* First check to see if an interrupt is pending. I suspect the SiS
+ chipset loses interrupts. (I also suspect is mangles data, but
+ one bug at a time... */
+ if (config.slot ? inb(config.icm_address - 1) == 2 :
+ (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+ {
+ int flags;
+ save_flags(flags);
+ printk("Ux4F: abort while completed command pending\n");
+ restore_flags(flags);
+ cli();
+ ultrastor_interrupt(0, NULL, NULL);
+ restore_flags(flags);
+ return SCSI_ABORT_SUCCESS; /* FIXME - is this correct? -ERY */
+ }
+#endif
+
+ old_aborted = xchgb(DID_ABORT, &config.aborted[mscp_index]);
+
+ /* aborted == 0xff is the signal that queuecommand has not yet sent
+ the command. It will notice the new abort flag and fail. */
+ if (old_aborted == 0xff)
+ return SCSI_ABORT_SUCCESS;
+
+ /* On 24F, send an abort MSCP request. The adapter will interrupt
+ and the interrupt handler will call done. */
+ if (config.slot && inb(config.ogm_address - 1) == 0)
+ {
+ int flags;
+
+ save_flags(flags);
+ cli();
+ outl((int)&config.mscp[mscp_index], config.ogm_address);
+ inb(0xc80); /* delay */
+ outb(0x80, config.ogm_address - 1);
+ outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address));
+#if ULTRASTOR_DEBUG & UD_ABORT
+ log_ultrastor_abort(&config, mscp_index);
+ printk(out, ogm_status, ogm_addr, icm_status, icm_addr);
+#endif
+ restore_flags(flags);
+ return SCSI_ABORT_PENDING;
+ }
+
+#if ULTRASTOR_DEBUG & UD_ABORT
+ log_ultrastor_abort(&config, mscp_index);
+#endif
+
+ /* Can't request a graceful abort. Either this is not a 24F or
+ the OGM is busy. Don't free the command -- the adapter might
+ still be using it. Setting SCint = 0 causes the interrupt
+ handler to ignore the command. */
+
+ /* FIXME - devices that implement soft resets will still be running
+ the command after a bus reset. We would probably rather leave
+ the command in the queue. The upper level code will automatically
+ leave the command in the active state instead of requeueing it. ERY */
+
+#if ULTRASTOR_DEBUG & UD_ABORT
+ if (config.mscp[mscp_index].SCint != SCpnt)
+ printk("abort: command mismatch, %p != %p\n",
+ config.mscp[mscp_index].SCint, SCpnt);
+#endif
+ if (config.mscp[mscp_index].SCint == 0)
+ return SCSI_ABORT_NOT_RUNNING;
+
+ if (config.mscp[mscp_index].SCint != SCpnt) panic("Bad abort");
+ config.mscp[mscp_index].SCint = 0;
+ done = config.mscp[mscp_index].done;
+ config.mscp[mscp_index].done = 0;
+ SCpnt->result = DID_ABORT << 16;
+ /* I worry about reentrancy in scsi.c */
+ done(SCpnt);
+
+ /* Need to set a timeout here in case command never completes. */
+ return SCSI_ABORT_SUCCESS;
+}
+
+int ultrastor_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+{
+ int flags;
+ register int i;
+#if (ULTRASTOR_DEBUG & UD_RESET)
+ printk("US14F: reset: called\n");
+#endif
+
+ if(config.slot)
+ return SCSI_RESET_PUNT; /* Do not attempt a reset for the 24f */
+
+ save_flags(flags);
+ cli();
+
+ /* Reset the adapter and SCSI bus. The SCSI bus reset can be
+ inhibited by clearing ultrastor_bus_reset before probe. */
+ outb(0xc0, LCL_DOORBELL_INTR(config.doorbell_address));
+ if (config.slot)
+ {
+ outb(0, config.ogm_address - 1);
+ outb(0, config.icm_address - 1);
+ }
+
+#if ULTRASTOR_MAX_CMDS == 1
+ if (config.mscp_busy && config.mscp->done && config.mscp->SCint)
+ {
+ config.mscp->SCint->result = DID_RESET << 16;
+ config.mscp->done(config.mscp->SCint);
+ }
+ config.mscp->SCint = 0;
+#else
+ for (i = 0; i < ULTRASTOR_MAX_CMDS; i++)
+ {
+ if (! (config.mscp_free & (1 << i)) &&
+ config.mscp[i].done && config.mscp[i].SCint)
+ {
+ config.mscp[i].SCint->result = DID_RESET << 16;
+ config.mscp[i].done(config.mscp[i].SCint);
+ config.mscp[i].done = 0;
+ }
+ config.mscp[i].SCint = 0;
+ }
+#endif
+
+ /* FIXME - if the device implements soft resets, then the command
+ will still be running. ERY */
+
+ memset((unsigned char *)config.aborted, 0, sizeof config.aborted);
+#if ULTRASTOR_MAX_CMDS == 1
+ config.mscp_busy = 0;
+#else
+ config.mscp_free = ~0;
+#endif
+
+ restore_flags(flags);
+ return SCSI_RESET_SUCCESS;
+
+}
+
+int ultrastor_biosparam(Disk * disk, kdev_t dev, int * dkinfo)
+{
+ int size = disk->capacity;
+ unsigned int s = config.heads * config.sectors;
+
+ dkinfo[0] = config.heads;
+ dkinfo[1] = config.sectors;
+ dkinfo[2] = size / s; /* Ignore partial cylinders */
+#if 0
+ if (dkinfo[2] > 1024)
+ dkinfo[2] = 1024;
+#endif
+ return 0;
+}
+
+static void ultrastor_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned int status;
+#if ULTRASTOR_MAX_CMDS > 1
+ unsigned int mscp_index;
+#endif
+ register struct mscp *mscp;
+ void (*done)(Scsi_Cmnd *);
+ Scsi_Cmnd *SCtmp;
+
+#if ULTRASTOR_MAX_CMDS == 1
+ mscp = &config.mscp[0];
+#else
+ mscp = (struct mscp *)inl(config.icm_address);
+ mscp_index = mscp - config.mscp;
+ if (mscp_index >= ULTRASTOR_MAX_CMDS) {
+ printk("Ux4F interrupt: bad MSCP address %x\n", (unsigned int) mscp);
+ /* A command has been lost. Reset and report an error
+ for all commands. */
+ ultrastor_reset(NULL, 0);
+ return;
+ }
+#endif
+
+ /* Clean ICM slot (set ICMINT bit to 0) */
+ if (config.slot) {
+ unsigned char icm_status = inb(config.icm_address - 1);
+#if ULTRASTOR_DEBUG & (UD_INTERRUPT|UD_ERROR|UD_ABORT)
+ if (icm_status != 1 && icm_status != 2)
+ printk("US24F: ICM status %x for MSCP %d (%x)\n", icm_status,
+ mscp_index, (unsigned int) mscp);
+#endif
+ /* The manual says clear interrupt then write 0 to ICM status.
+ This seems backwards, but I'll do it anyway. --jfc */
+ outb(2, SYS_DOORBELL_INTR(config.doorbell_address));
+ outb(0, config.icm_address - 1);
+ if (icm_status == 4) {
+ printk("UltraStor abort command failed\n");
+ return;
+ }
+ if (icm_status == 3) {
+ void (*done)(Scsi_Cmnd *) = mscp->done;
+ if (done) {
+ mscp->done = 0;
+ mscp->SCint->result = DID_ABORT << 16;
+ done(mscp->SCint);
+ }
+ return;
+ }
+ } else {
+ outb(1, SYS_DOORBELL_INTR(config.doorbell_address));
+ }
+
+ SCtmp = mscp->SCint;
+ mscp->SCint = NULL;
+
+ if (SCtmp == 0)
+ {
+#if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT)
+ printk("MSCP %d (%x): no command\n", mscp_index, (unsigned int) mscp);
+#endif
+#if ULTRASTOR_MAX_CMDS == 1
+ config.mscp_busy = FALSE;
+#else
+ set_bit(mscp_index, &config.mscp_free);
+#endif
+ config.aborted[mscp_index] = 0;
+ return;
+ }
+
+ /* Save done locally and zero before calling. This is needed as
+ once we call done, we may get another command queued before this
+ interrupt service routine can return. */
+ done = mscp->done;
+ mscp->done = 0;
+
+ /* Let the higher levels know that we're done */
+ switch (mscp->adapter_status)
+ {
+ case 0:
+ status = DID_OK << 16;
+ break;
+ case 0x01: /* invalid command */
+ case 0x02: /* invalid parameters */
+ case 0x03: /* invalid data list */
+ default:
+ status = DID_ERROR << 16;
+ break;
+ case 0x84: /* SCSI bus abort */
+ status = DID_ABORT << 16;
+ break;
+ case 0x91:
+ status = DID_TIME_OUT << 16;
+ break;
+ }
+
+ SCtmp->result = status | mscp->target_status;
+
+ SCtmp->host_scribble = 0;
+
+ /* Free up mscp block for next command */
+#if ULTRASTOR_MAX_CMDS == 1
+ config.mscp_busy = FALSE;
+#else
+ set_bit(mscp_index, &config.mscp_free);
+#endif
+
+#if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT)
+ if (config.aborted[mscp_index])
+ printk("Ux4 interrupt: MSCP %d (%x) aborted = %d\n",
+ mscp_index, (unsigned int) mscp, config.aborted[mscp_index]);
+#endif
+ config.aborted[mscp_index] = 0;
+
+ if (done)
+ done(SCtmp);
+ else
+ printk("US14F: interrupt: unexpected interrupt\n");
+
+ if (config.slot ? inb(config.icm_address - 1) :
+ (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+#if (ULTRASTOR_DEBUG & UD_MULTI_CMD)
+ printk("Ux4F: multiple commands completed\n");
+#else
+ ;
+#endif
+
+#if (ULTRASTOR_DEBUG & UD_INTERRUPT)
+ printk("USx4F: interrupt: returning\n");
+#endif
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = ULTRASTOR_14F;
+
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/ultrastor.h b/linux/src/drivers/scsi/ultrastor.h
new file mode 100644
index 00000000..7a40acc5
--- /dev/null
+++ b/linux/src/drivers/scsi/ultrastor.h
@@ -0,0 +1,102 @@
+/*
+ * ultrastor.c (C) 1991 David B. Gentzel
+ * Low-level scsi driver for UltraStor 14F
+ * by David B. Gentzel, Whitfield Software Services, Carnegie, PA
+ * (gentzel@nova.enet.dec.com)
+ * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu)
+ * 24F support by John F. Carr (jfc@athena.mit.edu)
+ * John's work modified by Caleb Epstein (cae@jpmorgan.com) and
+ * Eric Youngdale (eric@tantalus.nrl.navy.mil).
+ * Thanks to UltraStor for providing the necessary documentation
+ */
+
+#ifndef _ULTRASTOR_H
+#define _ULTRASTOR_H
+#include <linux/kdev_t.h>
+
+int ultrastor_detect(Scsi_Host_Template *);
+const char *ultrastor_info(struct Scsi_Host * shpnt);
+int ultrastor_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int ultrastor_abort(Scsi_Cmnd *);
+int ultrastor_reset(Scsi_Cmnd *, unsigned int);
+int ultrastor_biosparam(Disk *, kdev_t, int *);
+
+
+#define ULTRASTOR_14F_MAX_SG 16
+#define ULTRASTOR_24F_MAX_SG 33
+
+#define ULTRASTOR_MAX_CMDS_PER_LUN 5
+#define ULTRASTOR_MAX_CMDS 16
+
+#define ULTRASTOR_24F_PORT 0xC80
+
+
+#define ULTRASTOR_14F { NULL, NULL, /* Ptr for modules*/ \
+ NULL, \
+ NULL, \
+ "UltraStor 14F/24F/34F", \
+ ultrastor_detect, \
+ NULL, /* Release */ \
+ ultrastor_info, \
+ 0, \
+ ultrastor_queuecommand, \
+ ultrastor_abort, \
+ ultrastor_reset, \
+ 0, \
+ ultrastor_biosparam, \
+ ULTRASTOR_MAX_CMDS, \
+ 0, \
+ ULTRASTOR_14F_MAX_SG, \
+ ULTRASTOR_MAX_CMDS_PER_LUN, \
+ 0, \
+ 1, \
+ ENABLE_CLUSTERING }
+
+
+#ifdef ULTRASTOR_PRIVATE
+
+#define UD_ABORT 0x0001
+#define UD_COMMAND 0x0002
+#define UD_DETECT 0x0004
+#define UD_INTERRUPT 0x0008
+#define UD_RESET 0x0010
+#define UD_MULTI_CMD 0x0020
+#define UD_CSIR 0x0040
+#define UD_ERROR 0x0080
+
+/* #define PORT_OVERRIDE 0x330 */
+
+/* Values for the PRODUCT_ID ports for the 14F */
+#define US14F_PRODUCT_ID_0 0x56
+#define US14F_PRODUCT_ID_1 0x40 /* NOTE: Only upper nibble is used */
+
+#define US24F_PRODUCT_ID_0 0x56
+#define US24F_PRODUCT_ID_1 0x63
+#define US24F_PRODUCT_ID_2 0x02
+
+/* Subversion values */
+#define U14F 0
+#define U34F 1
+
+/* MSCP field values */
+
+/* Opcode */
+#define OP_HOST_ADAPTER 0x1
+#define OP_SCSI 0x2
+#define OP_RESET 0x4
+
+/* Date Transfer Direction */
+#define DTD_SCSI 0x0
+#define DTD_IN 0x1
+#define DTD_OUT 0x2
+#define DTD_NONE 0x3
+
+/* Host Adapter command subcodes */
+#define HA_CMD_INQUIRY 0x1
+#define HA_CMD_SELF_DIAG 0x2
+#define HA_CMD_READ_BUFF 0x3
+#define HA_CMD_WRITE_BUFF 0x4
+
+#endif
+
+#endif
diff --git a/linux/src/drivers/scsi/wd7000.c b/linux/src/drivers/scsi/wd7000.c
new file mode 100644
index 00000000..d910e27b
--- /dev/null
+++ b/linux/src/drivers/scsi/wd7000.c
@@ -0,0 +1,1452 @@
+/* $Id: wd7000.c,v 1.1 1999/04/26 05:55:18 tb Exp $
+ * linux/drivers/scsi/wd7000.c
+ *
+ * Copyright (C) 1992 Thomas Wuensche
+ * closely related to the aha1542 driver from Tommy Thorn
+ * ( as close as different hardware allows on a lowlevel-driver :-) )
+ *
+ * Revised (and renamed) by John Boyd <boyd@cis.ohio-state.edu> to
+ * accommodate Eric Youngdale's modifications to scsi.c. Nov 1992.
+ *
+ * Additional changes to support scatter/gather. Dec. 1992. tw/jb
+ *
+ * No longer tries to reset SCSI bus at boot (it wasn't working anyway).
+ * Rewritten to support multiple host adapters.
+ * Miscellaneous cleanup.
+ * So far, still doesn't do reset or abort correctly, since I have no idea
+ * how to do them with this board (8^(. Jan 1994 jb
+ *
+ * This driver now supports both of the two standard configurations (per
+ * the 3.36 Owner's Manual, my latest reference) by the same method as
+ * before; namely, by looking for a BIOS signature. Thus, the location of
+ * the BIOS signature determines the board configuration. Until I have
+ * time to do something more flexible, users should stick to one of the
+ * following:
+ *
+ * Standard configuration for single-adapter systems:
+ * - BIOS at CE00h
+ * - I/O base address 350h
+ * - IRQ level 15
+ * - DMA channel 6
+ * Standard configuration for a second adapter in a system:
+ * - BIOS at C800h
+ * - I/O base address 330h
+ * - IRQ level 11
+ * - DMA channel 5
+ *
+ * Anyone who can recompile the kernel is welcome to add others as need
+ * arises, but unpredictable results may occur if there are conflicts.
+ * In any event, if there are multiple adapters in a system, they MUST
+ * use different I/O bases, IRQ levels, and DMA channels, since they will be
+ * indistinguishable (and in direct conflict) otherwise.
+ *
+ * As a point of information, the NO_OP command toggles the CMD_RDY bit
+ * of the status port, and this fact could be used as a test for the I/O
+ * base address (or more generally, board detection). There is an interrupt
+ * status port, so IRQ probing could also be done. I suppose the full
+ * DMA diagnostic could be used to detect the DMA channel being used. I
+ * haven't done any of this, though, because I think there's too much of
+ * a chance that such explorations could be destructive, if some other
+ * board's resources are used inadvertently. So, call me a wimp, but I
+ * don't want to try it. The only kind of exploration I trust is memory
+ * exploration, since it's more certain that reading memory won't be
+ * destructive.
+ *
+ * More to my liking would be a LILO boot command line specification, such
+ * as is used by the aha152x driver (and possibly others). I'll look into
+ * it, as I have time...
+ *
+ * I get mail occasionally from people who either are using or are
+ * considering using a WD7000 with Linux. There is a variety of
+ * nomenclature describing WD7000's. To the best of my knowledge, the
+ * following is a brief summary (from an old WD doc - I don't work for
+ * them or anything like that):
+ *
+ * WD7000-FASST2: This is a WD7000 board with the real-mode SST ROM BIOS
+ * installed. Last I heard, the BIOS was actually done by Columbia
+ * Data Products. The BIOS is only used by this driver (and thus
+ * by Linux) to identify the board; none of it can be executed under
+ * Linux.
+ *
+ * WD7000-ASC: This is the original adapter board, with or without BIOS.
+ * The board uses a WD33C93 or WD33C93A SBIC, which in turn is
+ * controlled by an onboard Z80 processor. The board interface
+ * visible to the host CPU is defined effectively by the Z80's
+ * firmware, and it is this firmware's revision level that is
+ * determined and reported by this driver. (The version of the
+ * on-board BIOS is of no interest whatsoever.) The host CPU has
+ * no access to the SBIC; hence the fact that it is a WD33C93 is
+ * also of no interest to this driver.
+ *
+ * WD7000-AX:
+ * WD7000-MX:
+ * WD7000-EX: These are newer versions of the WD7000-ASC. The -ASC is
+ * largely built from discrete components; these boards use more
+ * integration. The -AX is an ISA bus board (like the -ASC),
+ * the -MX is an MCA (i.e., PS/2) bus board), and the -EX is an
+ * EISA bus board.
+ *
+ * At the time of my documentation, the -?X boards were "future" products,
+ * and were not yet available. However, I vaguely recall that Thomas
+ * Wuensche had an -AX, so I believe at least it is supported by this
+ * driver. I have no personal knowledge of either -MX or -EX boards.
+ *
+ * P.S. Just recently, I've discovered (directly from WD and Future
+ * Domain) that all but the WD7000-EX have been out of production for
+ * two years now. FD has production rights to the 7000-EX, and are
+ * producing it under a new name, and with a new BIOS. If anyone has
+ * one of the FD boards, it would be nice to come up with a signature
+ * for it.
+ * J.B. Jan 1994.
+ *
+ *
+ * Revisions by Miroslav Zagorac <zaga@fly.cc.fer.hr>
+ *
+ * -- 08/24/1996. --------------------------------------------------------------
+ * Enhancement for wd7000_detect function has been made, so you don't have
+ * to enter BIOS ROM address in initialisation data (see struct Config).
+ * We cannot detect IRQ, DMA and I/O base address for now, so we have to
+ * enter them as arguments while wd_7000 is detected. If someone has IRQ,
+ * DMA or an I/O base address set to some other value, he can enter them in
+ * a configuration without any problem.
+ * Also I wrote a function wd7000_setup, so now you can enter WD-7000
+ * definition as kernel arguments, as in lilo.conf:
+ *
+ * append="wd7000=IRQ,DMA,IO"
+ *
+ * PS: If card BIOS ROM is disabled, function wd7000_detect now will recognize
+ * adapter, unlike the old one. Anyway, BIOS ROM from WD7000 adapter is
+ * useless for Linux. B^)
+ *
+ * -- 09/06/1996. --------------------------------------------------------------
+ * Auto detecting of an I/O base address from wd7000_detect function is
+ * removed, some little bugs too...
+ *
+ * Thanks to Roger Scott for driver debugging.
+ *
+ * -- 06/07/1997. --------------------------------------------------------------
+ * Added support for /proc file system (/proc/scsi/wd7000/[0...] files).
+ * Now, the driver can handle hard disks with capacity >1GB.
+ *
+ * -- 01/15/1998. --------------------------------------------------------------
+ * Added support for BUS_ON and BUS_OFF parameters in config line.
+ * Miscellaneous cleanups. Syntax of the append line is changed to:
+ *
+ * append="wd7000=IRQ,DMA,IO[,BUS_ON[,BUS_OFF]]"
+ *
+ * , where BUS_ON and BUS_OFF are time in nanoseconds.
+ *
+ * -- 03/01/1998. --------------------------------------------------------------
+ * The WD7000 driver now works on kernels' >= 2.1.x
+ *
+ * -- 06/11/1998. --------------------------------------------------------------
+ * Ugly init_scbs, alloc_scbs and free_scb functions are changed with
+ * scbs_init, scb_alloc and scb_free. Now, source code is identical on
+ * 2.0.xx and 2.1.xx kernels.
+ * WD7000 specific definitions are moved from this file to wd7000.h.
+ *
+ */
+#ifdef MODULE
+# include <linux/module.h>
+#endif
+
+#if (LINUX_VERSION_CODE >= 0x020100)
+# include <asm/spinlock.h>
+#endif
+
+#include <stdarg.h>
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/blk.h>
+#include <linux/version.h>
+#include <linux/stat.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include <scsi/scsicam.h>
+
+#undef WD7000_DEBUG /* general debug */
+#define WD7000_DEFINES /* This must be defined! */
+
+#include "wd7000.h"
+
+
+struct proc_dir_entry proc_scsi_wd7000 =
+{
+ PROC_SCSI_7000FASST,
+ 6,
+ "wd7000",
+ S_IFDIR | S_IRUGO | S_IXUGO,
+ 2
+};
+
+/*
+ * (linear) base address for ROM BIOS
+ */
+static const long wd7000_biosaddr[] = {
+ 0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000,
+ 0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000
+};
+#define NUM_ADDRS (sizeof (wd7000_biosaddr) / sizeof (long))
+
+static const ushort wd7000_iobase[] = {
+ 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338,
+ 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378,
+ 0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8,
+ 0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8
+};
+#define NUM_IOPORTS (sizeof (wd7000_iobase) / sizeof (ushort))
+
+static const short wd7000_irq[] = { 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 };
+#define NUM_IRQS (sizeof (wd7000_irq) / sizeof (short))
+
+static const short wd7000_dma[] = { 5, 6, 7 };
+#define NUM_DMAS (sizeof (wd7000_dma) / sizeof (short))
+
+/*
+ * The following is set up by wd7000_detect, and used thereafter by
+ * wd7000_intr_handle to map the irq level to the corresponding Adapter.
+ * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be
+ * changed to pick up the IRQ level correctly.
+ */
+static struct Scsi_Host *wd7000_host[IRQS];
+
+/*
+ * Add here your configuration...
+ */
+static Config configs[] =
+{
+ { 15, 6, 0x350, BUS_ON, BUS_OFF }, /* defaults for single adapter */
+ { 11, 5, 0x320, BUS_ON, BUS_OFF }, /* defaults for second adapter */
+ { 7, 6, 0x350, BUS_ON, BUS_OFF }, /* My configuration (Zaga) */
+ { -1, -1, 0x0, BUS_ON, BUS_OFF } /* Empty slot */
+};
+#define NUM_CONFIGS (sizeof(configs)/sizeof(Config))
+
+static const Signature signatures[] =
+{
+ {"SSTBIOS", 0x0000d, 7} /* "SSTBIOS" @ offset 0x0000d */
+};
+#define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
+
+/*
+ * Driver SCB structure pool.
+ *
+ * The SCBs declared here are shared by all host adapters; hence, this
+ * structure is not part of the Adapter structure.
+ */
+static Scb scbs[MAX_SCBS];
+
+
+/*
+ * END of data/declarations - code follows.
+ */
+static void setup_error (char *mesg, int *ints)
+{
+ if (ints[0] == 3)
+ printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> %s\n",
+ ints[1], ints[2], ints[3], mesg);
+ else if (ints[0] == 4)
+ printk ("wd7000_setup: \"wd7000=%d,%d,0x%x,%d\" -> %s\n",
+ ints[1], ints[2], ints[3], ints[4], mesg);
+ else
+ printk ("wd7000_setup: \"wd7000=%d,%d,0x%x,%d,%d\" -> %s\n",
+ ints[1], ints[2], ints[3], ints[4], ints[5], mesg);
+}
+
+
+/*
+ * Note: You can now set these options from the kernel's "command line".
+ * The syntax is:
+ *
+ * wd7000=<IRQ>,<DMA>,<IO>[,<BUS_ON>[,<BUS_OFF>]]
+ *
+ * , where BUS_ON and BUS_OFF are in nanoseconds. BIOS default values
+ * are 8000ns for BUS_ON and 1875ns for BUS_OFF.
+ *
+ * eg:
+ * wd7000=7,6,0x350
+ *
+ * will configure the driver for a WD-7000 controller
+ * using IRQ 15 with a DMA channel 6, at IO base address 0x350.
+ */
+void wd7000_setup (char *str, int *ints)
+{
+ static short wd7000_card_num = 0;
+ short i, j;
+
+ if (wd7000_card_num >= NUM_CONFIGS) {
+ printk ("%s: Too many \"wd7000=\" configurations in "
+ "command line!\n", __FUNCTION__);
+ return;
+ }
+
+ if ((ints[0] < 3) || (ints[0] > 5))
+ printk ("%s: Error in command line! "
+ "Usage: wd7000=<IRQ>,<DMA>,<IO>[,<BUS_ON>[,<BUS_OFF>]]\n",
+ __FUNCTION__);
+ else {
+ for (i = 0; i < NUM_IRQS; i++)
+ if (ints[1] == wd7000_irq[i])
+ break;
+
+ if (i == NUM_IRQS) {
+ setup_error ("invalid IRQ.", ints);
+ return;
+ }
+ else
+ configs[wd7000_card_num].irq = ints[1];
+
+ for (i = 0; i < NUM_DMAS; i++)
+ if (ints[2] == wd7000_dma[i])
+ break;
+
+ if (i == NUM_DMAS) {
+ setup_error ("invalid DMA channel.", ints);
+ return;
+ }
+ else
+ configs[wd7000_card_num].dma = ints[2];
+
+ for (i = 0; i < NUM_IOPORTS; i++)
+ if (ints[3] == wd7000_iobase[i])
+ break;
+
+ if (i == NUM_IOPORTS) {
+ setup_error ("invalid I/O base address.", ints);
+ return;
+ }
+ else
+ configs[wd7000_card_num].iobase = ints[3];
+
+ if (ints[0] > 3) {
+ if ((ints[4] < 500) || (ints[4] > 31875)) {
+ setup_error ("BUS_ON value is out of range (500 to 31875 nanoseconds)!",
+ ints);
+ configs[wd7000_card_num].bus_on = BUS_ON;
+ }
+ else
+ configs[wd7000_card_num].bus_on = ints[4] / 125;
+ }
+ else
+ configs[wd7000_card_num].bus_on = BUS_ON;
+
+ if (ints[0] > 4) {
+ if ((ints[5] < 500) || (ints[5] > 31875)) {
+ setup_error ("BUS_OFF value is out of range (500 to 31875 nanoseconds)!",
+ ints);
+ configs[wd7000_card_num].bus_off = BUS_OFF;
+ }
+ else
+ configs[wd7000_card_num].bus_off = ints[5] / 125;
+ }
+ else
+ configs[wd7000_card_num].bus_off = BUS_OFF;
+
+ if (wd7000_card_num) {
+ for (i = 0; i < (wd7000_card_num - 1); i++)
+ for (j = i + 1; j < wd7000_card_num; j++)
+ if (configs[i].irq == configs[j].irq) {
+ setup_error ("duplicated IRQ!", ints);
+ return;
+ }
+ else if (configs[i].dma == configs[j].dma) {
+ setup_error ("duplicated DMA channel!", ints);
+ return;
+ }
+ else if (configs[i].iobase == configs[j].iobase) {
+ setup_error ("duplicated I/O base address!", ints);
+ return;
+ }
+ }
+
+#ifdef WD7000_DEBUG
+ printk ("%s: IRQ=%d, DMA=%d, I/O=0x%x, BUS_ON=%dns, BUS_OFF=%dns\n",
+ __FUNCTION__,
+ configs[wd7000_card_num].irq,
+ configs[wd7000_card_num].dma,
+ configs[wd7000_card_num].iobase,
+ configs[wd7000_card_num].bus_on * 125,
+ configs[wd7000_card_num].bus_off * 125);
+#endif
+
+ wd7000_card_num++;
+ }
+}
+
+
+/*
+ * Since they're used a lot, I've redone the following from the macros
+ * formerly in wd7000.h, hopefully to speed them up by getting rid of
+ * all the shifting (it may not matter; GCC might have done as well anyway).
+ *
+ * xany2scsi and xscsi2int were not being used, and are no longer defined.
+ * (They were simply 4-byte versions of these routines).
+ */
+static inline void any2scsi (unchar *scsi, int any)
+{
+ *scsi++ = ((i_u) any).u[2];
+ *scsi++ = ((i_u) any).u[1];
+ *scsi = ((i_u) any).u[0];
+}
+
+
+static inline int scsi2int (unchar *scsi)
+{
+ i_u result;
+
+ result.i = 0; /* clears unused bytes */
+ result.u[2] = *scsi++;
+ result.u[1] = *scsi++;
+ result.u[0] = *scsi;
+
+ return (result.i);
+}
+
+
+static inline void wd7000_enable_intr (Adapter *host)
+{
+ host->control |= INT_EN;
+ outb (host->control, host->iobase + ASC_CONTROL);
+}
+
+
+static inline void wd7000_enable_dma (Adapter *host)
+{
+ host->control |= DMA_EN;
+ outb (host->control, host->iobase + ASC_CONTROL);
+ set_dma_mode (host->dma, DMA_MODE_CASCADE);
+ enable_dma (host->dma);
+}
+
+
+static inline short WAIT (uint port, uint mask, uint allof, uint noneof)
+{
+ register uint WAITbits;
+ register ulong WAITtimeout = jiffies + WAITnexttimeout;
+
+ while (jiffies <= WAITtimeout) {
+ WAITbits = inb (port) & mask;
+
+ if (((WAITbits & allof) == allof) && ((WAITbits & noneof) == 0))
+ return (0);
+ }
+
+ return (1);
+}
+
+
+static inline void delay (uint how_long)
+{
+ register ulong time = jiffies + how_long;
+
+ while (jiffies < time);
+}
+
+
+static inline int wd7000_command_out (Adapter *host, unchar *cmd, int len)
+{
+ if (! WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
+ for ( ; len--; cmd++)
+ do {
+ outb (*cmd, host->iobase + ASC_COMMAND);
+ WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0);
+ } while (inb (host->iobase + ASC_STAT) & CMD_REJ);
+
+ return (1);
+ }
+
+ printk ("%s: WAIT failed (%d)\n", __FUNCTION__, len + 1);
+
+ return (0);
+}
+
+
+static inline void scbs_init (void)
+{
+ short i;
+
+ for (i = 0; i < MAX_SCBS; i++)
+ memset ((void *) &(scbs[i]), 0, sizeof (Scb));
+}
+
+
+static inline Scb *scb_alloc (void)
+{
+ Scb *scb = NULL;
+ ulong flags;
+ short i;
+#ifdef WD7000_DEBUG
+ short free_scbs = 0;
+#endif
+
+ save_flags (flags);
+ cli ();
+
+ for (i = 0; i < MAX_SCBS; i++)
+ if (! scbs[i].used) {
+ scbs[i].used = 1;
+ scb = &(scbs[i]);
+
+ break;
+ }
+
+#ifdef WD7000_DEBUG
+ for (i = 0; i < MAX_SCBS; i++)
+ free_scbs += scbs[i].used ? 0 : 1;
+
+ printk ("wd7000_%s: allocating scb (0x%08x), %d scbs free\n",
+ __FUNCTION__, (int) scb, free_scbs);
+#endif
+
+ restore_flags (flags);
+
+ return (scb);
+}
+
+
+static inline void scb_free (Scb *scb)
+{
+ short i;
+ ulong flags;
+
+ save_flags (flags);
+ cli ();
+
+ for (i = 0; i < MAX_SCBS; i++)
+ if (&(scbs[i]) == scb) {
+ memset ((void *) &(scbs[i]), 0, sizeof (Scb));
+
+ break;
+ }
+
+ if (i == MAX_SCBS)
+ printk ("wd7000_%s: trying to free alien scb (0x%08x)...\n",
+ __FUNCTION__, (int) scb);
+#ifdef WD7000_DEBUG
+ else
+ printk ("wd7000_%s: freeing scb (0x%08x)\n", __FUNCTION__, (int) scb);
+#endif
+
+ restore_flags (flags);
+}
+
+
+static int mail_out (Adapter *host, Scb *scbptr)
+/*
+ * Note: this can also be used for ICBs; just cast to the parm type.
+ */
+{
+ register int i, ogmb;
+ ulong flags;
+ unchar start_ogmb;
+ Mailbox *ogmbs = host->mb.ogmb;
+ int *next_ogmb = &(host->next_ogmb);
+
+#ifdef WD7000_DEBUG
+ printk ("wd7000_%s: 0x%08x", __FUNCTION__, (int) scbptr);
+#endif
+
+ /* We first look for a free outgoing mailbox */
+ save_flags (flags);
+ cli ();
+
+ ogmb = *next_ogmb;
+ for (i = 0; i < OGMB_CNT; i++) {
+ if (ogmbs[ogmb].status == 0) {
+#ifdef WD7000_DEBUG
+ printk (" using OGMB 0x%x", ogmb);
+#endif
+ ogmbs[ogmb].status = 1;
+ any2scsi ((unchar *) ogmbs[ogmb].scbptr, (int) scbptr);
+
+ *next_ogmb = (ogmb + 1) % OGMB_CNT;
+ break;
+ }
+ else
+ ogmb = (++ogmb) % OGMB_CNT;
+ }
+
+ restore_flags (flags);
+
+#ifdef WD7000_DEBUG
+ printk (", scb is 0x%08x", (int) scbptr);
+#endif
+
+ if (i >= OGMB_CNT) {
+ /*
+ * Alternatively, we might issue the "interrupt on free OGMB",
+ * and sleep, but it must be ensured that it isn't the init
+ * task running. Instead, this version assumes that the caller
+ * will be persistent, and try again. Since it's the adapter
+ * that marks OGMB's free, waiting even with interrupts off
+ * should work, since they are freed very quickly in most cases.
+ */
+#ifdef WD7000_DEBUG
+ printk (", no free OGMBs.\n");
+#endif
+ return (0);
+ }
+
+ wd7000_enable_intr (host);
+
+ start_ogmb = START_OGMB | ogmb;
+ wd7000_command_out (host, &start_ogmb, 1);
+
+#ifdef WD7000_DEBUG
+ printk (", awaiting interrupt.\n");
+#endif
+
+ return (1);
+}
+
+
+int make_code (uint hosterr, uint scsierr)
+{
+#ifdef WD7000_DEBUG
+ int in_error = hosterr;
+#endif
+
+ switch ((hosterr >> 8) & 0xff) {
+ case 0: /* Reserved */
+ hosterr = DID_ERROR;
+ break;
+
+ case 1: /* Command Complete, no errors */
+ hosterr = DID_OK;
+ break;
+
+ case 2: /* Command complete, error logged in scb status (scsierr) */
+ hosterr = DID_OK;
+ break;
+
+ case 4: /* Command failed to complete - timeout */
+ hosterr = DID_TIME_OUT;
+ break;
+
+ case 5: /* Command terminated; Bus reset by external device */
+ hosterr = DID_RESET;
+ break;
+
+ case 6: /* Unexpected Command Received w/ host as target */
+ hosterr = DID_BAD_TARGET;
+ break;
+
+ case 80: /* Unexpected Reselection */
+ case 81: /* Unexpected Selection */
+ hosterr = DID_BAD_INTR;
+ break;
+
+ case 82: /* Abort Command Message */
+ hosterr = DID_ABORT;
+ break;
+
+ case 83: /* SCSI Bus Software Reset */
+ case 84: /* SCSI Bus Hardware Reset */
+ hosterr = DID_RESET;
+ break;
+
+ default: /* Reserved */
+ hosterr = DID_ERROR;
+ }
+
+#ifdef WD7000_DEBUG
+ if (scsierr || hosterr)
+ printk ("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n",
+ scsierr, in_error, hosterr);
+#endif
+
+ return (scsierr | (hosterr << 16));
+}
+
+
+static void wd7000_scsi_done (Scsi_Cmnd *SCpnt)
+{
+#ifdef WD7000_DEBUG
+ printk ("%s: 0x%08x\n", __FUNCTION__, (int) SCpnt);
+#endif
+
+ SCpnt->SCp.phase = 0;
+}
+
+
+static inline void wd7000_intr_ack (Adapter *host)
+{
+ outb (0, host->iobase + ASC_INTR_ACK);
+}
+
+
+void wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs)
+{
+ register int flag, icmb, errstatus, icmb_status;
+ register int host_error, scsi_error;
+ register Scb *scb; /* for SCSI commands */
+ register IcbAny *icb; /* for host commands */
+ register Scsi_Cmnd *SCpnt;
+ Adapter *host = (Adapter *) wd7000_host[irq - IRQ_MIN]->hostdata; /* This MUST be set!!! */
+ Mailbox *icmbs = host->mb.icmb;
+
+ host->int_counter++;
+
+#ifdef WD7000_DEBUG
+ printk ("%s: irq = %d, host = 0x%08x\n", __FUNCTION__, irq, (int) host);
+#endif
+
+ flag = inb (host->iobase + ASC_INTR_STAT);
+
+#ifdef WD7000_DEBUG
+ printk ("%s: intr stat = 0x%02x\n", __FUNCTION__, flag);
+#endif
+
+ if (! (inb (host->iobase + ASC_STAT) & INT_IM)) {
+ /* NB: these are _very_ possible if IRQ 15 is being used, since
+ * it's the "garbage collector" on the 2nd 8259 PIC. Specifically,
+ * any interrupt signal into the 8259 which can't be identified
+ * comes out as 7 from the 8259, which is 15 to the host. Thus, it
+ * is a good thing the WD7000 has an interrupt status port, so we
+ * can sort these out. Otherwise, electrical noise and other such
+ * problems would be indistinguishable from valid interrupts...
+ */
+#ifdef WD7000_DEBUG
+ printk ("%s: phantom interrupt...\n", __FUNCTION__);
+#endif
+ wd7000_intr_ack (host);
+ return;
+ }
+
+ if (flag & MB_INTR) {
+ /* The interrupt is for a mailbox */
+ if (! (flag & IMB_INTR)) {
+#ifdef WD7000_DEBUG
+ printk ("%s: free outgoing mailbox\n", __FUNCTION__);
+#endif
+ /*
+ * If sleep_on() and the "interrupt on free OGMB" command are
+ * used in mail_out(), wake_up() should correspondingly be called
+ * here. For now, we don't need to do anything special.
+ */
+ wd7000_intr_ack (host);
+ return;
+ }
+ else {
+ /* The interrupt is for an incoming mailbox */
+ icmb = flag & MB_MASK;
+ icmb_status = icmbs[icmb].status;
+
+ if (icmb_status & 0x80) { /* unsolicited - result in ICMB */
+#ifdef WD7000_DEBUG
+ printk ("%s: unsolicited interrupt 0x%02x\n",
+ __FUNCTION__, icmb_status);
+#endif
+ wd7000_intr_ack (host);
+ return;
+ }
+
+ /* Aaaargh! (Zaga) */
+ scb = (Scb *) bus_to_virt (scsi2int ((unchar *) icmbs[icmb].scbptr));
+
+ icmbs[icmb].status = 0;
+ if (!(scb->op & ICB_OP_MASK)) { /* an SCB is done */
+ SCpnt = scb->SCpnt;
+ if (--(SCpnt->SCp.phase) <= 0) { /* all scbs are done */
+ host_error = scb->vue | (icmb_status << 8);
+ scsi_error = scb->status;
+ errstatus = make_code (host_error, scsi_error);
+ SCpnt->result = errstatus;
+
+ scb_free (scb);
+
+ SCpnt->scsi_done (SCpnt);
+ }
+ }
+ else { /* an ICB is done */
+ icb = (IcbAny *) scb;
+ icb->status = icmb_status;
+ icb->phase = 0;
+ }
+ } /* incoming mailbox */
+ }
+
+ wd7000_intr_ack (host);
+
+#ifdef WD7000_DEBUG
+ printk ("%s: return from interrupt handler\n", __FUNCTION__);
+#endif
+}
+
+
+void do_wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs)
+{
+#if (LINUX_VERSION_CODE >= 0x020100)
+ ulong flags;
+
+ spin_lock_irqsave (&io_request_lock, flags);
+#endif
+
+ wd7000_intr_handle (irq, dev_id, regs);
+
+#if (LINUX_VERSION_CODE >= 0x020100)
+ spin_unlock_irqrestore (&io_request_lock, flags);
+#endif
+}
+
+
+int wd7000_queuecommand (Scsi_Cmnd *SCpnt, void (*done) (Scsi_Cmnd *))
+{
+ register Scb *scb;
+ register Sgb *sgb;
+ register Adapter *host = (Adapter *) SCpnt->host->hostdata;
+
+ if ((scb = scb_alloc ()) == NULL) {
+ printk ("%s: Cannot allocate SCB!\n", __FUNCTION__);
+ return (0);
+ }
+
+ SCpnt->scsi_done = done;
+ SCpnt->SCp.phase = 1;
+ SCpnt->host_scribble = (unchar *) scb;
+ scb->idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7);
+ scb->direc = 0x40; /* Disable direction check */
+ scb->SCpnt = SCpnt; /* so we can find stuff later */
+ scb->host = host;
+ memcpy (scb->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+
+ if (SCpnt->use_sg) {
+ struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
+ uint i;
+
+ if (SCpnt->host->sg_tablesize == SG_NONE)
+ panic ("%s: scatter/gather not supported.\n", __FUNCTION__);
+#ifdef WD7000_DEBUG
+ else
+ printk ("Using scatter/gather with %d elements.\n", SCpnt->use_sg);
+#endif
+
+ sgb = scb->sgb;
+ scb->op = 1;
+ any2scsi (scb->dataptr, (int) sgb);
+ any2scsi (scb->maxlen, SCpnt->use_sg * sizeof (Sgb));
+
+ for (i = 0; i < SCpnt->use_sg; i++) {
+ any2scsi (sgb[i].ptr, (int) sg[i].address);
+ any2scsi (sgb[i].len, sg[i].length);
+ }
+ }
+ else {
+ scb->op = 0;
+ any2scsi (scb->dataptr, (int) SCpnt->request_buffer);
+ any2scsi (scb->maxlen, SCpnt->request_bufflen);
+ }
+
+ while (! mail_out (host, scb)); /* keep trying */
+
+ return (1);
+}
+
+
+int wd7000_command (Scsi_Cmnd *SCpnt)
+{
+ if (! wd7000_queuecommand (SCpnt, wd7000_scsi_done))
+ return (-1);
+
+ while (SCpnt->SCp.phase > 0)
+ barrier (); /* phase counts scbs down to 0 */
+
+ return (SCpnt->result);
+}
+
+
+int wd7000_diagnostics (Adapter *host, int code)
+{
+ static IcbDiag icb = { ICB_OP_DIAGNOSTICS };
+ static unchar buf[256];
+ ulong timeout;
+
+ /*
+ * This routine is only called at init, so there should be OGMBs
+ * available. I'm assuming so here. If this is going to
+ * fail, I can just let the timeout catch the failure.
+ */
+ icb.type = code;
+ any2scsi (icb.len, sizeof (buf));
+ any2scsi (icb.ptr, (int) &buf);
+ icb.phase = 1;
+
+ mail_out (host, (Scb *) &icb);
+
+ /*
+ * Wait up to 2 seconds for completion.
+ */
+ for (timeout = jiffies + WAITnexttimeout; icb.phase && (jiffies < timeout); )
+ barrier ();
+
+ if (icb.phase) {
+ printk ("%s: timed out.\n", __FUNCTION__);
+ return (0);
+ }
+
+ if (make_code (icb.vue | (icb.status << 8), 0)) {
+ printk ("%s: failed (0x%02x,0x%02x)\n", __FUNCTION__, icb.vue, icb.status);
+ return (0);
+ }
+
+ return (1);
+}
+
+
+int wd7000_init (Adapter *host)
+{
+ InitCmd init_cmd =
+ {
+ INITIALIZATION,
+ 7,
+ host->bus_on,
+ host->bus_off,
+ 0,
+ { 0, 0, 0 },
+ OGMB_CNT,
+ ICMB_CNT
+ };
+ int diag;
+
+ /*
+ * Reset the adapter - only. The SCSI bus was initialized at power-up,
+ * and we need to do this just so we control the mailboxes, etc.
+ */
+ outb (ASC_RES, host->iobase + ASC_CONTROL);
+ delay (1); /* reset pulse: this is 10ms, only need 25us */
+ outb (0, host->iobase + ASC_CONTROL);
+ host->control = 0; /* this must always shadow ASC_CONTROL */
+
+ if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
+ printk ("%s: WAIT timed out.\n", __FUNCTION__);
+ return (0); /* 0 = not ok */
+ }
+
+ if ((diag = inb (host->iobase + ASC_INTR_STAT)) != 1) {
+ printk ("%s: ", __FUNCTION__);
+
+ switch (diag) {
+ case 2: printk ("RAM failure.\n");
+ break;
+
+ case 3: printk ("FIFO R/W failed\n");
+ break;
+
+ case 4: printk ("SBIC register R/W failed\n");
+ break;
+
+ case 5: printk ("Initialization D-FF failed.\n");
+ break;
+
+ case 6: printk ("Host IRQ D-FF failed.\n");
+ break;
+
+ case 7: printk ("ROM checksum error.\n");
+ break;
+
+ default: printk ("diagnostic code 0x%02Xh received.\n", diag);
+ }
+
+ return (0);
+ }
+
+ /* Clear mailboxes */
+ memset (&(host->mb), 0, sizeof (host->mb));
+
+ /* Execute init command */
+ any2scsi ((unchar *) &(init_cmd.mailboxes), (int) &(host->mb));
+
+ if (! wd7000_command_out (host, (unchar *) &init_cmd, sizeof (init_cmd))) {
+ printk ("%s: adapter initialization failed.\n", __FUNCTION__);
+ return (0);
+ }
+
+ if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) {
+ printk ("%s: WAIT timed out.\n", __FUNCTION__);
+ return (0);
+ }
+
+ if (request_irq (host->irq, do_wd7000_intr_handle, SA_INTERRUPT, "wd7000", NULL)) {
+ printk ("%s: can't get IRQ %d.\n", __FUNCTION__, host->irq);
+ return (0);
+ }
+
+ if (request_dma (host->dma, "wd7000")) {
+ printk ("%s: can't get DMA channel %d.\n", __FUNCTION__, host->dma);
+ free_irq (host->irq, NULL);
+ return (0);
+ }
+
+ wd7000_enable_dma (host);
+ wd7000_enable_intr (host);
+
+ if (! wd7000_diagnostics (host, ICB_DIAG_FULL)) {
+ free_dma (host->dma);
+ free_irq (host->irq, NULL);
+ return (0);
+ }
+
+ return (1);
+}
+
+
+void wd7000_revision (Adapter *host)
+{
+ static IcbRevLvl icb = { ICB_OP_GET_REVISION };
+
+ /*
+ * Like diagnostics, this is only done at init time, in fact, from
+ * wd7000_detect, so there should be OGMBs available. If it fails,
+ * the only damage will be that the revision will show up as 0.0,
+ * which in turn means that scatter/gather will be disabled.
+ */
+ icb.phase = 1;
+ mail_out (host, (Scb *) &icb);
+
+ while (icb.phase)
+ barrier (); /* wait for completion */
+
+ host->rev1 = icb.primary;
+ host->rev2 = icb.secondary;
+}
+
+
+#undef SPRINTF
+#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
+
+int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host)
+{
+ ulong flags;
+
+ save_flags (flags);
+ cli ();
+
+#ifdef WD7000_DEBUG
+ printk ("Buffer = <%.*s>, length = %d\n", length, buffer, length);
+#endif
+
+ /*
+ * Currently this is a no-op
+ */
+ printk ("Sorry, this function is currently out of order...\n");
+
+ restore_flags (flags);
+
+ return (length);
+}
+
+
+int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout)
+{
+ struct Scsi_Host *host = NULL;
+ Scsi_Device *scd;
+ Adapter *adapter;
+ ulong flags;
+ char *pos = buffer;
+ short i;
+
+#ifdef WD7000_DEBUG
+ Mailbox *ogmbs, *icmbs;
+ short count;
+#endif
+
+ /*
+ * Find the specified host board.
+ */
+ for (i = 0; i < IRQS; i++)
+ if (wd7000_host[i] && (wd7000_host[i]->host_no == hostno)) {
+ host = wd7000_host[i];
+
+ break;
+ }
+
+ /*
+ * Host not found!
+ */
+ if (! host)
+ return (-ESRCH);
+
+ /*
+ * Has data been written to the file ?
+ */
+ if (inout)
+ return (wd7000_set_info (buffer, length, host));
+
+ adapter = (Adapter *) host->hostdata;
+
+ save_flags (flags);
+ cli ();
+
+ SPRINTF ("Host scsi%d: Western Digital WD-7000 (rev %d.%d)\n", hostno, adapter->rev1, adapter->rev2);
+ SPRINTF (" IO base: 0x%x\n", adapter->iobase);
+ SPRINTF (" IRQ: %d\n", adapter->irq);
+ SPRINTF (" DMA channel: %d\n", adapter->dma);
+ SPRINTF (" Interrupts: %d\n", adapter->int_counter);
+ SPRINTF (" BUS_ON time: %d nanoseconds\n", adapter->bus_on * 125);
+ SPRINTF (" BUS_OFF time: %d nanoseconds\n", adapter->bus_off * 125);
+
+#ifdef WD7000_DEBUG
+ ogmbs = adapter->mb.ogmb;
+ icmbs = adapter->mb.icmb;
+
+ SPRINTF ("\nControl port value: 0x%x\n", adapter->control);
+ SPRINTF ("Incoming mailbox:\n");
+ SPRINTF (" size: %d\n", ICMB_CNT);
+ SPRINTF (" queued messages: ");
+
+ for (i = count = 0; i < ICMB_CNT; i++)
+ if (icmbs[i].status) {
+ count++;
+ SPRINTF ("0x%x ", i);
+ }
+
+ SPRINTF (count ? "\n" : "none\n");
+
+ SPRINTF ("Outgoing mailbox:\n");
+ SPRINTF (" size: %d\n", OGMB_CNT);
+ SPRINTF (" next message: 0x%x\n", adapter->next_ogmb);
+ SPRINTF (" queued messages: ");
+
+ for (i = count = 0; i < OGMB_CNT; i++)
+ if (ogmbs[i].status) {
+ count++;
+ SPRINTF ("0x%x ", i);
+ }
+
+ SPRINTF (count ? "\n" : "none\n");
+#endif
+
+ /*
+ * Display driver information for each device attached to the board.
+ */
+#if (LINUX_VERSION_CODE >= 0x020100)
+ scd = host->host_queue;
+#else
+ scd = scsi_devices;
+#endif
+
+ SPRINTF ("\nAttached devices: %s\n", scd ? "" : "none");
+
+ for ( ; scd; scd = scd->next)
+ if (scd->host->host_no == hostno) {
+ SPRINTF (" [Channel: %02d, Id: %02d, Lun: %02d] ",
+ scd->channel, scd->id, scd->lun);
+ SPRINTF ("%s ", (scd->type < MAX_SCSI_DEVICE_CODE) ?
+ scsi_device_types[(short) scd->type] : "Unknown device");
+
+ for (i = 0; (i < 8) && (scd->vendor[i] >= 0x20); i++)
+ SPRINTF ("%c", scd->vendor[i]);
+ SPRINTF (" ");
+
+ for (i = 0; (i < 16) && (scd->model[i] >= 0x20); i++)
+ SPRINTF ("%c", scd->model[i]);
+ SPRINTF ("\n");
+ }
+
+ SPRINTF ("\n");
+
+ restore_flags (flags);
+
+ /*
+ * Calculate start of next buffer, and return value.
+ */
+ *start = buffer + offset;
+
+ if ((pos - buffer) < offset)
+ return (0);
+ else if ((pos - buffer - offset) < length)
+ return (pos - buffer - offset);
+ else
+ return (length);
+}
+
+
+/*
+ * Returns the number of adapters this driver is supporting.
+ *
+ * The source for hosts.c says to wait to call scsi_register until 100%
+ * sure about an adapter. We need to do it a little sooner here; we
+ * need the storage set up by scsi_register before wd7000_init, and
+ * changing the location of an Adapter structure is more trouble than
+ * calling scsi_unregister.
+ *
+ */
+int wd7000_detect (Scsi_Host_Template *tpnt)
+{
+ short present = 0, biosaddr_ptr, sig_ptr, i, pass;
+ short biosptr[NUM_CONFIGS];
+ uint iobase;
+ Adapter *host = NULL;
+ struct Scsi_Host *sh;
+
+#ifdef WD7000_DEBUG
+ printk ("%s: started\n", __FUNCTION__);
+#endif
+
+ /*
+ * Set up SCB free list, which is shared by all adapters
+ */
+ scbs_init ();
+
+ for (i = 0; i < IRQS; wd7000_host[i++] = NULL);
+ for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1);
+
+ tpnt->proc_dir = &proc_scsi_wd7000;
+ tpnt->proc_info = &wd7000_proc_info;
+
+ for (pass = 0; pass < NUM_CONFIGS; pass++) {
+ short bios_match = 1;
+
+#ifdef WD7000_DEBUG
+ printk ("%s: pass %d\n", __FUNCTION__, pass + 1);
+#endif
+
+ /*
+ * First, search for BIOS SIGNATURE...
+ */
+ for (biosaddr_ptr = 0; bios_match && (biosaddr_ptr < NUM_ADDRS); biosaddr_ptr++)
+ for (sig_ptr = 0; bios_match && (sig_ptr < NUM_SIGNATURES); sig_ptr++) {
+ for (i = 0; i < pass; i++)
+ if (biosptr[i] == biosaddr_ptr)
+ break;
+
+ if (i == pass) {
+#if (LINUX_VERSION_CODE >= 0x020100)
+ char *biosaddr = (char *) ioremap (wd7000_biosaddr[biosaddr_ptr] +
+ signatures[sig_ptr].ofs,
+ signatures[sig_ptr].len);
+#else
+ char *biosaddr = (char *) (wd7000_biosaddr[biosaddr_ptr] +
+ signatures[sig_ptr].ofs);
+#endif
+ bios_match = memcmp (biosaddr, signatures[sig_ptr].sig,
+ signatures[sig_ptr].len);
+
+#if (LINUX_VERSION_CODE >= 0x020100)
+ iounmap (biosaddr);
+#else
+#endif
+ if (! bios_match) {
+ /*
+ * BIOS SIGNATURE has been found.
+ */
+ biosptr[pass] = biosaddr_ptr;
+#ifdef WD7000_DEBUG
+ printk ("WD-7000 SST BIOS detected at 0x%lx: checking...\n",
+ wd7000_biosaddr[biosaddr_ptr]);
+#endif
+ }
+ }
+ }
+
+#ifdef WD7000_DEBUG
+ if (bios_match)
+ printk ("WD-7000 SST BIOS not detected...\n");
+#endif
+
+ if (configs[pass].irq < 0)
+ continue;
+
+ iobase = configs[pass].iobase;
+
+#ifdef WD7000_DEBUG
+ printk ("%s: check IO 0x%x region...\n", __FUNCTION__, iobase);
+#endif
+
+ if (! check_region (iobase, 4)) {
+#ifdef WD7000_DEBUG
+ printk ("%s: ASC reset (IO 0x%x) ...", __FUNCTION__, iobase);
+#endif
+ /*
+ * ASC reset...
+ */
+ outb (ASC_RES, iobase + ASC_CONTROL);
+ delay (1);
+ outb (0, iobase + ASC_CONTROL);
+
+ if (WAIT (iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0))
+#ifdef WD7000_DEBUG
+ {
+ printk ("failed!\n");
+ continue;
+ }
+ else
+ printk ("ok!\n");
+#else
+ continue;
+#endif
+
+ if (inb (iobase + ASC_INTR_STAT) == 1) {
+ /*
+ * We register here, to get a pointer to the extra space,
+ * which we'll use as the Adapter structure (host) for
+ * this adapter. It is located just after the registered
+ * Scsi_Host structure (sh), and is located by the empty
+ * array hostdata.
+ */
+ sh = scsi_register (tpnt, sizeof (Adapter));
+ host = (Adapter *) sh->hostdata;
+
+#ifdef WD7000_DEBUG
+ printk ("%s: adapter allocated at 0x%x\n", __FUNCTION__, (int) host);
+#endif
+
+ memset (host, 0, sizeof (Adapter));
+
+ host->irq = configs[pass].irq;
+ host->dma = configs[pass].dma;
+ host->iobase = iobase;
+ host->int_counter = 0;
+ host->bus_on = configs[pass].bus_on;
+ host->bus_off = configs[pass].bus_off;
+ host->sh = wd7000_host[host->irq - IRQ_MIN] = sh;
+
+#ifdef WD7000_DEBUG
+ printk ("%s: Trying to init WD-7000 card at IO 0x%x, IRQ %d, DMA %d...\n",
+ __FUNCTION__, host->iobase, host->irq, host->dma);
+#endif
+
+ if (! wd7000_init (host)) { /* Initialization failed */
+ scsi_unregister (sh);
+ continue;
+ }
+
+ /*
+ * OK from here - we'll use this adapter/configuration.
+ */
+ wd7000_revision (host); /* important for scatter/gather */
+
+ /*
+ * Register our ports.
+ */
+ request_region (host->iobase, 4, "wd7000");
+
+ /*
+ * For boards before rev 6.0, scatter/gather isn't supported.
+ */
+ if (host->rev1 < 6)
+ sh->sg_tablesize = SG_NONE;
+
+ present++; /* count it */
+
+ printk ("Western Digital WD-7000 (rev %d.%d) ",
+ host->rev1, host->rev2);
+ printk ("using IO 0x%x, IRQ %d, DMA %d.\n",
+ host->iobase, host->irq, host->dma);
+ printk (" BUS_ON time: %dns, BUS_OFF time: %dns\n",
+ host->bus_on * 125, host->bus_off * 125);
+ }
+ }
+
+#ifdef WD7000_DEBUG
+ else
+ printk ("%s: IO 0x%x region is already allocated!\n", __FUNCTION__, iobase);
+#endif
+
+ }
+
+ if (! present)
+ printk ("Failed initialization of WD-7000 SCSI card!\n");
+
+ return (present);
+}
+
+
+/*
+ * I have absolutely NO idea how to do an abort with the WD7000...
+ */
+int wd7000_abort (Scsi_Cmnd *SCpnt)
+{
+ Adapter *host = (Adapter *) SCpnt->host->hostdata;
+
+ if (inb (host->iobase + ASC_STAT) & INT_IM) {
+ printk ("%s: lost interrupt\n", __FUNCTION__);
+ wd7000_intr_handle (host->irq, NULL, NULL);
+
+ return (SCSI_ABORT_SUCCESS);
+ }
+
+ return (SCSI_ABORT_SNOOZE);
+}
+
+
+/*
+ * I also have no idea how to do a reset...
+ */
+int wd7000_reset (Scsi_Cmnd *SCpnt, uint flags)
+{
+ return (SCSI_RESET_PUNT);
+}
+
+
+/*
+ * This was borrowed directly from aha1542.c. (Zaga)
+ */
+int wd7000_biosparam (Disk *disk, kdev_t dev, int *ip)
+{
+#ifdef WD7000_DEBUG
+ printk ("%s: dev=%s, size=%d, ", __FUNCTION__, kdevname (dev), disk->capacity);
+#endif
+
+ /*
+ * try default translation
+ */
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = disk->capacity / (64 * 32);
+
+ /*
+ * for disks >1GB do some guessing
+ */
+ if (ip[2] >= 1024) {
+ int info[3];
+
+ /*
+ * try to figure out the geometry from the partition table
+ */
+ if ((scsicam_bios_param (disk, dev, info) < 0) ||
+ !(((info[0] == 64) && (info[1] == 32)) ||
+ ((info[0] == 255) && (info[1] == 63)))) {
+ printk ("%s: unable to verify geometry for disk with >1GB.\n"
+ " using extended translation.\n",
+ __FUNCTION__);
+
+ ip[0] = 255;
+ ip[1] = 63;
+ ip[2] = disk->capacity / (255 * 63);
+ }
+ else {
+ ip[0] = info[0];
+ ip[1] = info[1];
+ ip[2] = info[2];
+
+ if (info[0] == 255)
+ printk ("%s: current partition table is using extended translation.\n",
+ __FUNCTION__);
+ }
+ }
+
+#ifdef WD7000_DEBUG
+ printk ("bios geometry: head=%d, sec=%d, cyl=%d\n", ip[0], ip[1], ip[2]);
+ printk ("WARNING: check, if the bios geometry is correct.\n");
+#endif
+
+ return (0);
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = WD7000;
+
+#include "scsi_module.c"
+#endif
diff --git a/linux/src/drivers/scsi/wd7000.h b/linux/src/drivers/scsi/wd7000.h
new file mode 100644
index 00000000..e17a69b1
--- /dev/null
+++ b/linux/src/drivers/scsi/wd7000.h
@@ -0,0 +1,446 @@
+/* $Id: wd7000.h,v 1.1 1999/04/26 05:55:19 tb Exp $
+ *
+ * Header file for the WD-7000 driver for Linux
+ *
+ * John Boyd <boyd@cis.ohio-state.edu> Jan 1994:
+ * This file has been reduced to only the definitions needed for the
+ * WD7000 host structure.
+ *
+ * Revision by Miroslav Zagorac <zaga@fly.cc.fer.hr> Jun 1997.
+ */
+#ifndef _WD7000_H
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+/*
+ * In this version, sg_tablesize now defaults to WD7000_SG, and will
+ * be set to SG_NONE for older boards. This is the reverse of the
+ * previous default, and was changed so that the driver-level
+ * Scsi_Host_Template would reflect the driver's support for scatter/
+ * gather.
+ *
+ * Also, it has been reported that boards at Revision 6 support scatter/
+ * gather, so the new definition of an "older" board has been changed
+ * accordingly.
+ */
+#define WD7000_Q 16
+#define WD7000_SG 16
+
+#ifdef WD7000_DEFINES
+/*
+ * Mailbox structure sizes.
+ * I prefer to keep the number of ICMBs much larger than the number of
+ * OGMBs. OGMBs are used very quickly by the driver to start one or
+ * more commands, while ICMBs are used by the host adapter per command.
+ */
+#define OGMB_CNT 16
+#define ICMB_CNT 32
+
+/*
+ * Scb's are shared by all active adapters. If you'd rather conserve
+ * memory, use a smaller number (> 0, of course) - things will should
+ * still work OK.
+ */
+#define MAX_SCBS (4 * WD7000_Q)
+
+/*
+ * WD7000-specific mailbox structure
+ */
+typedef volatile struct {
+ unchar status;
+ unchar scbptr[3]; /* SCSI-style - MSB first (big endian) */
+} Mailbox;
+
+/*
+ * This structure should contain all per-adapter global data. I.e., any
+ * new global per-adapter data should put in here.
+ */
+typedef struct {
+ struct Scsi_Host *sh; /* Pointer to Scsi_Host structure */
+ int iobase; /* This adapter's I/O base address */
+ int irq; /* This adapter's IRQ level */
+ int dma; /* This adapter's DMA channel */
+ int int_counter; /* This adapter's interrupt counter */
+ int bus_on; /* This adapter's BUS_ON time */
+ int bus_off; /* This adapter's BUS_OFF time */
+ struct { /* This adapter's mailboxes */
+ Mailbox ogmb[OGMB_CNT]; /* Outgoing mailboxes */
+ Mailbox icmb[ICMB_CNT]; /* Incoming mailboxes */
+ } mb;
+ int next_ogmb; /* to reduce contention at mailboxes */
+ unchar control; /* shadows CONTROL port value */
+ unchar rev1; /* filled in by wd7000_revision */
+ unchar rev2;
+} Adapter;
+
+
+/*
+ * possible irq range
+ */
+#define IRQ_MIN 3
+#define IRQ_MAX 15
+#define IRQS (IRQ_MAX - IRQ_MIN + 1)
+
+#define BUS_ON 64 /* x 125ns = 8000ns (BIOS default) */
+#define BUS_OFF 15 /* x 125ns = 1875ns (BIOS default) */
+
+/*
+ * Standard Adapter Configurations - used by wd7000_detect
+ */
+typedef struct {
+ short irq; /* IRQ level */
+ short dma; /* DMA channel */
+ uint iobase; /* I/O base address */
+ short bus_on; /* Time that WD7000 spends on the AT-bus when */
+ /* transferring data. BIOS default is 8000ns. */
+ short bus_off; /* Time that WD7000 spends OFF THE BUS after */
+ /* while it is transferring data. */
+ /* BIOS default is 1875ns */
+} Config;
+
+
+/*
+ * The following list defines strings to look for in the BIOS that identify
+ * it as the WD7000-FASST2 SST BIOS. I suspect that something should be
+ * added for the Future Domain version.
+ */
+typedef struct {
+ const char *sig; /* String to look for */
+ ulong ofs; /* offset from BIOS base address */
+ uint len; /* length of string */
+} Signature;
+
+/*
+ * I/O Port Offsets and Bit Definitions
+ * 4 addresses are used. Those not defined here are reserved.
+ */
+#define ASC_STAT 0 /* Status, Read */
+#define ASC_COMMAND 0 /* Command, Write */
+#define ASC_INTR_STAT 1 /* Interrupt Status, Read */
+#define ASC_INTR_ACK 1 /* Acknowledge, Write */
+#define ASC_CONTROL 2 /* Control, Write */
+
+/*
+ * ASC Status Port
+ */
+#define INT_IM 0x80 /* Interrupt Image Flag */
+#define CMD_RDY 0x40 /* Command Port Ready */
+#define CMD_REJ 0x20 /* Command Port Byte Rejected */
+#define ASC_INIT 0x10 /* ASC Initialized Flag */
+#define ASC_STATMASK 0xf0 /* The lower 4 Bytes are reserved */
+
+/*
+ * COMMAND opcodes
+ *
+ * Unfortunately, I have no idea how to properly use some of these commands,
+ * as the OEM manual does not make it clear. I have not been able to use
+ * enable/disable unsolicited interrupts or the reset commands with any
+ * discernible effect whatsoever. I think they may be related to certain
+ * ICB commands, but again, the OEM manual doesn't make that clear.
+ */
+#define NO_OP 0 /* NO-OP toggles CMD_RDY bit in ASC_STAT */
+#define INITIALIZATION 1 /* initialization (10 bytes) */
+#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */
+#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */
+#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */
+#define SOFT_RESET 5 /* SCSI bus soft reset */
+#define HARD_RESET_ACK 6 /* SCSI bus hard reset acknowledge */
+#define START_OGMB 0x80 /* start command in OGMB (n) */
+#define SCAN_OGMBS 0xc0 /* start multiple commands, signature (n) */
+ /* where (n) = lower 6 bits */
+/*
+ * For INITIALIZATION:
+ */
+typedef struct {
+ unchar op; /* command opcode (= 1) */
+ unchar ID; /* Adapter's SCSI ID */
+ unchar bus_on; /* Bus on time, x 125ns (see below) */
+ unchar bus_off; /* Bus off time, "" "" */
+ unchar rsvd; /* Reserved */
+ unchar mailboxes[3]; /* Address of Mailboxes, MSB first */
+ unchar ogmbs; /* Number of outgoing MBs, max 64, 0,1 = 1 */
+ unchar icmbs; /* Number of incoming MBs, "" "" */
+} InitCmd;
+
+/*
+ * Interrupt Status Port - also returns diagnostic codes at ASC reset
+ *
+ * if msb is zero, the lower bits are diagnostic status
+ * Diagnostics:
+ * 01 No diagnostic error occurred
+ * 02 RAM failure
+ * 03 FIFO R/W failed
+ * 04 SBIC register read/write failed
+ * 05 Initialization D-FF failed
+ * 06 Host IRQ D-FF failed
+ * 07 ROM checksum error
+ * Interrupt status (bitwise):
+ * 10NNNNNN outgoing mailbox NNNNNN is free
+ * 11NNNNNN incoming mailbox NNNNNN needs service
+ */
+#define MB_INTR 0xC0 /* Mailbox Service possible/required */
+#define IMB_INTR 0x40 /* 1 Incoming / 0 Outgoing */
+#define MB_MASK 0x3f /* mask for mailbox number */
+
+/*
+ * CONTROL port bits
+ */
+#define INT_EN 0x08 /* Interrupt Enable */
+#define DMA_EN 0x04 /* DMA Enable */
+#define SCSI_RES 0x02 /* SCSI Reset */
+#define ASC_RES 0x01 /* ASC Reset */
+
+/*
+ * Driver data structures:
+ * - mb and scbs are required for interfacing with the host adapter.
+ * An SCB has extra fields not visible to the adapter; mb's
+ * _cannot_ do this, since the adapter assumes they are contiguous in
+ * memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact
+ * to access them.
+ * - An icb is for host-only (non-SCSI) commands. ICBs are 16 bytes each;
+ * the additional bytes are used only by the driver.
+ * - For now, a pool of SCBs are kept in global storage by this driver,
+ * and are allocated and freed as needed.
+ *
+ * The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command,
+ * not when it has finished. Since the SCB must be around for completion,
+ * problems arise when SCBs correspond to OGMBs, which may be reallocated
+ * earlier (or delayed unnecessarily until a command completes).
+ * Mailboxes are used as transient data structures, simply for
+ * carrying SCB addresses to/from the 7000-FASST2.
+ *
+ * Note also since SCBs are not "permanently" associated with mailboxes,
+ * there is no need to keep a global list of Scsi_Cmnd pointers indexed
+ * by OGMB. Again, SCBs reference their Scsi_Cmnds directly, so mailbox
+ * indices need not be involved.
+ */
+
+/*
+ * WD7000-specific scatter/gather element structure
+ */
+typedef struct {
+ unchar len[3];
+ unchar ptr[3]; /* Also SCSI-style - MSB first */
+} Sgb;
+
+typedef struct { /* Command Control Block 5.4.1 */
+ unchar op; /* Command Control Block Operation Code */
+ unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */
+ /* Outbound data transfer, length is checked */
+ /* Inbound data transfer, length is checked */
+ /* Logical Unit Number */
+ unchar cdb[12]; /* SCSI Command Block */
+ volatile unchar status; /* SCSI Return Status */
+ volatile unchar vue; /* Vendor Unique Error Code */
+ unchar maxlen[3]; /* Maximum Data Transfer Length */
+ unchar dataptr[3]; /* SCSI Data Block Pointer */
+ unchar linkptr[3]; /* Next Command Link Pointer */
+ unchar direc; /* Transfer Direction */
+ unchar reserved2[6]; /* SCSI Command Descriptor Block */
+ /* end of hardware SCB */
+ Scsi_Cmnd *SCpnt; /* Scsi_Cmnd using this SCB */
+ Sgb sgb[WD7000_SG]; /* Scatter/gather list for this SCB */
+ Adapter *host; /* host adapter */
+ unchar used; /* flag */
+} Scb;
+
+/*
+ * This driver is written to allow host-only commands to be executed.
+ * These use a 16-byte block called an ICB. The format is extended by the
+ * driver to 18 bytes, to support the status returned in the ICMB and
+ * an execution phase code.
+ *
+ * There are other formats besides these; these are the ones I've tried
+ * to use. Formats for some of the defined ICB opcodes are not defined
+ * (notably, get/set unsolicited interrupt status) in my copy of the OEM
+ * manual, and others are ambiguous/hard to follow.
+ */
+#define ICB_OP_MASK 0x80 /* distinguishes scbs from icbs */
+#define ICB_OP_OPEN_RBUF 0x80 /* open receive buffer */
+#define ICB_OP_RECV_CMD 0x81 /* receive command from initiator */
+#define ICB_OP_RECV_DATA 0x82 /* receive data from initiator */
+#define ICB_OP_RECV_SDATA 0x83 /* receive data with status from init. */
+#define ICB_OP_SEND_DATA 0x84 /* send data with status to initiator */
+#define ICB_OP_SEND_STAT 0x86 /* send command status to initiator */
+ /* 0x87 is reserved */
+#define ICB_OP_READ_INIT 0x88 /* read initialization bytes */
+#define ICB_OP_READ_ID 0x89 /* read adapter's SCSI ID */
+#define ICB_OP_SET_UMASK 0x8A /* set unsolicited interrupt mask */
+#define ICB_OP_GET_UMASK 0x8B /* read unsolicited interrupt mask */
+#define ICB_OP_GET_REVISION 0x8C /* read firmware revision level */
+#define ICB_OP_DIAGNOSTICS 0x8D /* execute diagnostics */
+#define ICB_OP_SET_EPARMS 0x8E /* set execution parameters */
+#define ICB_OP_GET_EPARMS 0x8F /* read execution parameters */
+
+typedef struct {
+ unchar op;
+ unchar IDlun; /* Initiator SCSI ID/lun */
+ unchar len[3]; /* command buffer length */
+ unchar ptr[3]; /* command buffer address */
+ unchar rsvd[7]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbRecvCmd;
+
+typedef struct {
+ unchar op;
+ unchar IDlun; /* Target SCSI ID/lun */
+ unchar stat; /* (outgoing) completion status byte 1 */
+ unchar rsvd[12]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbSendStat;
+
+typedef struct {
+ unchar op;
+ volatile unchar primary; /* primary revision level (returned) */
+ volatile unchar secondary; /* secondary revision level (returned) */
+ unchar rsvd[12]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbRevLvl;
+
+typedef struct { /* I'm totally guessing here */
+ unchar op;
+ volatile unchar mask[14]; /* mask bits */
+#if 0
+ unchar rsvd[12]; /* reserved */
+#endif
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbUnsMask;
+
+typedef struct {
+ unchar op;
+ unchar type; /* diagnostics type code (0-3) */
+ unchar len[3]; /* buffer length */
+ unchar ptr[3]; /* buffer address */
+ unchar rsvd[7]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbDiag;
+
+#define ICB_DIAG_POWERUP 0 /* Power-up diags only */
+#define ICB_DIAG_WALKING 1 /* walking 1's pattern */
+#define ICB_DIAG_DMA 2 /* DMA - system memory diags */
+#define ICB_DIAG_FULL 3 /* do both 1 & 2 */
+
+typedef struct {
+ unchar op;
+ unchar rsvd1; /* reserved */
+ unchar len[3]; /* parms buffer length */
+ unchar ptr[3]; /* parms buffer address */
+ unchar idx[2]; /* index (MSB-LSB) */
+ unchar rsvd2[5]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbParms;
+
+typedef struct {
+ unchar op;
+ unchar data[14]; /* format-specific data */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbAny;
+
+typedef union {
+ unchar op; /* ICB opcode */
+ IcbRecvCmd recv_cmd; /* format for receive command */
+ IcbSendStat send_stat; /* format for send status */
+ IcbRevLvl rev_lvl; /* format for get revision level */
+ IcbDiag diag; /* format for execute diagnostics */
+ IcbParms eparms; /* format for get/set exec parms */
+ IcbAny icb; /* generic format */
+ unchar data[18];
+} Icb;
+
+#define WAITnexttimeout 200 /* 2 seconds */
+
+typedef union { /* let's cheat... */
+ int i;
+ unchar u[sizeof (int)]; /* the sizeof(int) makes it more portable */
+} i_u;
+
+#endif /* WD7000_DEFINES */
+
+
+#if (LINUX_VERSION_CODE >= 0x020100)
+
+#define WD7000 { \
+ proc_dir: &proc_scsi_wd7000, \
+ proc_info: wd7000_proc_info, \
+ name: "Western Digital WD-7000", \
+ detect: wd7000_detect, \
+ command: wd7000_command, \
+ queuecommand: wd7000_queuecommand, \
+ abort: wd7000_abort, \
+ reset: wd7000_reset, \
+ bios_param: wd7000_biosparam, \
+ can_queue: WD7000_Q, \
+ this_id: 7, \
+ sg_tablesize: WD7000_SG, \
+ cmd_per_lun: 1, \
+ unchecked_isa_dma: 1, \
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 0 \
+}
+
+#else /* Use old scsi code */
+
+#define WD7000 { \
+ proc_dir: &proc_scsi_wd7000, \
+ proc_info: wd7000_proc_info, \
+ name: "Western Digital WD-7000", \
+ detect: wd7000_detect, \
+ command: wd7000_command, \
+ queuecommand: wd7000_queuecommand, \
+ abort: wd7000_abort, \
+ reset: wd7000_reset, \
+ bios_param: wd7000_biosparam, \
+ can_queue: WD7000_Q, \
+ this_id: 7, \
+ sg_tablesize: WD7000_SG, \
+ cmd_per_lun: 1, \
+ unchecked_isa_dma: 1, \
+ use_clustering: ENABLE_CLUSTERING, \
+}
+
+#endif /* LINUX_VERSION_CODE */
+
+
+extern struct proc_dir_entry proc_scsi_wd7000;
+
+
+#ifdef WD7000_DEFINES
+int wd7000_diagnostics (Adapter *, int);
+int wd7000_init (Adapter *);
+void wd7000_revision (Adapter *);
+#endif /* WD7000_DEFINES */
+
+void wd7000_setup (char *, int *);
+int make_code (uint, uint);
+void wd7000_intr_handle (int, void *, struct pt_regs *);
+void do_wd7000_intr_handle (int, void *, struct pt_regs *);
+int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int wd7000_command (Scsi_Cmnd *);
+int wd7000_set_info (char *, int, struct Scsi_Host *);
+int wd7000_proc_info (char *, char **, off_t, int, int, int);
+int wd7000_detect (Scsi_Host_Template *);
+int wd7000_abort (Scsi_Cmnd *);
+int wd7000_reset (Scsi_Cmnd *, uint);
+int wd7000_biosparam (Disk *, kdev_t, int *);
+
+#endif /* _WD7000_H */