summaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/3w-xxxx.c41
-rw-r--r--drivers/scsi/3w-xxxx.h2
-rw-r--r--drivers/scsi/53c7,8xx.c5
-rw-r--r--drivers/scsi/53c7,8xx.h3
-rw-r--r--drivers/scsi/53c7xx.c5
-rw-r--r--drivers/scsi/53c7xx.h3
-rw-r--r--drivers/scsi/AM53C974.c7
-rw-r--r--drivers/scsi/BusLogic.c24
-rw-r--r--drivers/scsi/ChangeLog.ips17
-rw-r--r--drivers/scsi/Config.in29
-rw-r--r--drivers/scsi/Makefile117
-rw-r--r--drivers/scsi/NCR53c406a.c4
-rw-r--r--drivers/scsi/a2091.c6
-rw-r--r--drivers/scsi/a2091.h5
-rw-r--r--drivers/scsi/a3000.c6
-rw-r--r--drivers/scsi/a3000.h5
-rw-r--r--drivers/scsi/advansys.c4
-rw-r--r--drivers/scsi/aha152x.c4
-rw-r--r--drivers/scsi/aha1542.c4
-rw-r--r--drivers/scsi/aha1740.c4
-rw-r--r--drivers/scsi/aic7xxx.c4
-rw-r--r--drivers/scsi/amiga7xx.h3
-rw-r--r--drivers/scsi/atari_scsi.c5
-rw-r--r--drivers/scsi/atari_scsi.h7
-rw-r--r--drivers/scsi/atp870u.c5
-rw-r--r--drivers/scsi/blz1230.c6
-rw-r--r--drivers/scsi/blz2060.c6
-rw-r--r--drivers/scsi/bvme6000.h3
-rw-r--r--drivers/scsi/cpqfc.Readme216
-rw-r--r--drivers/scsi/cpqfcTS.h39
-rw-r--r--drivers/scsi/cpqfcTSchip.h238
-rw-r--r--drivers/scsi/cpqfcTScontrol.c2200
-rw-r--r--drivers/scsi/cpqfcTSi2c.c493
-rw-r--r--drivers/scsi/cpqfcTSinit.c1815
-rw-r--r--drivers/scsi/cpqfcTSioctl.h84
-rw-r--r--drivers/scsi/cpqfcTSstructs.h1494
-rw-r--r--drivers/scsi/cpqfcTStrigger.c30
-rw-r--r--drivers/scsi/cpqfcTSworker.c6238
-rw-r--r--drivers/scsi/cpqioctl.c76
-rw-r--r--drivers/scsi/cyberstorm.c6
-rw-r--r--drivers/scsi/cyberstormII.c5
-rw-r--r--drivers/scsi/dc390.h4
-rw-r--r--drivers/scsi/dmx3191d.c6
-rw-r--r--drivers/scsi/dmx3191d.h4
-rw-r--r--drivers/scsi/dtc.c5
-rw-r--r--drivers/scsi/dtc.h7
-rw-r--r--drivers/scsi/eata.c5
-rw-r--r--drivers/scsi/eata_dma.c4
-rw-r--r--drivers/scsi/eata_dma.h4
-rw-r--r--drivers/scsi/eata_pio.c4
-rw-r--r--drivers/scsi/esp.c10
-rw-r--r--drivers/scsi/fastlane.c7
-rw-r--r--drivers/scsi/fcal.c5
-rw-r--r--drivers/scsi/fd_mcs.c4
-rw-r--r--drivers/scsi/fdomain.c4
-rw-r--r--drivers/scsi/g_NCR5380.c5
-rw-r--r--drivers/scsi/g_NCR5380.h4
-rw-r--r--drivers/scsi/gdth.c4
-rw-r--r--drivers/scsi/gvp11.c6
-rw-r--r--drivers/scsi/hosts.c771
-rw-r--r--drivers/scsi/hosts.h6
-rw-r--r--drivers/scsi/ibmmca.c4
-rw-r--r--drivers/scsi/ide-scsi.c26
-rw-r--r--drivers/scsi/imm.c4
-rw-r--r--drivers/scsi/in2000.c7
-rw-r--r--drivers/scsi/ini9100u.c4
-rw-r--r--drivers/scsi/inia100.c4
-rw-r--r--drivers/scsi/ips.c3135
-rw-r--r--drivers/scsi/ips.h144
-rw-r--r--drivers/scsi/mac53c94.c22
-rw-r--r--drivers/scsi/mac53c94.h2
-rw-r--r--drivers/scsi/mac_scsi.c5
-rw-r--r--drivers/scsi/mca_53c9x.c4
-rw-r--r--drivers/scsi/megaraid.c4
-rw-r--r--drivers/scsi/mesh.c25
-rw-r--r--drivers/scsi/mesh.h2
-rw-r--r--drivers/scsi/mvme16x.h3
-rw-r--r--drivers/scsi/ncr53c8xx.c4
-rw-r--r--drivers/scsi/ncr53c8xx.h4
-rw-r--r--drivers/scsi/oktagon_esp.c5
-rw-r--r--drivers/scsi/pas16.c4
-rw-r--r--drivers/scsi/pas16.h3
-rw-r--r--drivers/scsi/pci2000.c4
-rw-r--r--drivers/scsi/pci2220i.c6
-rw-r--r--drivers/scsi/pcmcia/aha152x_stub.c6
-rw-r--r--drivers/scsi/pcmcia/apa1480_stub.c6
-rw-r--r--drivers/scsi/pcmcia/fdomain_stub.c6
-rw-r--r--drivers/scsi/pcmcia/qlogic_stub.c6
-rw-r--r--drivers/scsi/pluto.c5
-rw-r--r--drivers/scsi/ppa.c4
-rw-r--r--drivers/scsi/psi240i.c4
-rw-r--r--drivers/scsi/qla1280.c4
-rw-r--r--drivers/scsi/qlogicfas.c7
-rw-r--r--drivers/scsi/qlogicfc.c6
-rw-r--r--drivers/scsi/qlogicisp.c4
-rw-r--r--drivers/scsi/qlogicpti.c4
-rw-r--r--drivers/scsi/scsi.c315
-rw-r--r--drivers/scsi/scsi.h1
-rw-r--r--drivers/scsi/scsi_debug.c4
-rw-r--r--drivers/scsi/scsi_dma.c1
-rw-r--r--drivers/scsi/scsi_error.c3
-rw-r--r--drivers/scsi/scsi_lib.c45
-rw-r--r--drivers/scsi/scsi_module.c12
-rw-r--r--drivers/scsi/scsi_queue.c2
-rw-r--r--drivers/scsi/scsi_scan.c11
-rw-r--r--drivers/scsi/scsi_syms.c3
-rw-r--r--drivers/scsi/sd.c229
-rw-r--r--drivers/scsi/seagate.c132
-rw-r--r--drivers/scsi/seagate.h4
-rw-r--r--drivers/scsi/sg.c195
-rw-r--r--drivers/scsi/sgiwd93.c6
-rw-r--r--drivers/scsi/sim710.c5
-rw-r--r--drivers/scsi/sim710.h3
-rw-r--r--drivers/scsi/sr.c90
-rw-r--r--drivers/scsi/sr_ioctl.c21
-rw-r--r--drivers/scsi/st.c67
-rw-r--r--drivers/scsi/sun3_scsi.c5
-rw-r--r--drivers/scsi/sym53c416.c5
-rw-r--r--drivers/scsi/sym53c8xx.c8
-rw-r--r--drivers/scsi/sym53c8xx.h4
-rw-r--r--drivers/scsi/sym53c8xx_comm.h3
-rw-r--r--drivers/scsi/t128.c4
-rw-r--r--drivers/scsi/t128.h4
-rw-r--r--drivers/scsi/tmscsim.c5
-rw-r--r--drivers/scsi/tmscsim.h1
-rw-r--r--drivers/scsi/u14-34f.c5
-rw-r--r--drivers/scsi/ultrastor.c4
-rw-r--r--drivers/scsi/wd7000.c4
128 files changed, 16120 insertions, 2705 deletions
diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c
index ba6cc03cf..cb70168a3 100644
--- a/drivers/scsi/3w-xxxx.c
+++ b/drivers/scsi/3w-xxxx.c
@@ -3,6 +3,7 @@
Written By: Adam Radford <linux@3ware.com>
Modifications By: Joel Jacobson <linux@3ware.com>
+ Arnaldo Carvalho de Melo <acme@conectiva.com.br>
Copyright (C) 1999-2000 3ware Inc.
@@ -64,6 +65,8 @@
Bug fix so hot spare drives don't show up.
1.02.00.002 - Fix bug with tw_setfeature() call that caused oops on some
systems.
+ 08/21/00 - release previously allocated resources on failure at
+ tw_allocate_memory (acme)
*/
#include <linux/module.h>
@@ -410,35 +413,26 @@ int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
/* This function will allocate memory and check if it is 16 d-word aligned */
int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which)
{
- u32 *virt_addr;
+ u32 *virt_addr = kmalloc(size, GFP_ATOMIC);
dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n");
+ if (!virt_addr) {
+ printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n");
+ return 1;
+ }
+
+ if ((u32)virt_addr % TW_ALIGNMENT) {
+ kfree(virt_addr);
+ printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n");
+ return 1;
+ }
+
if (which == 0) {
- /* Allocate command packet memory */
- virt_addr = kmalloc(size, GFP_ATOMIC);
- if (virt_addr == NULL) {
- printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n");
- return 1;
- }
- if ((u32)virt_addr % TW_ALIGNMENT) {
- printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n");
- return 1;
- }
tw_dev->command_packet_virtual_address[request_id] = virt_addr;
tw_dev->command_packet_physical_address[request_id] =
virt_to_bus(virt_addr);
} else {
- /* Allocate generic buffer */
- virt_addr = kmalloc(size, GFP_ATOMIC);
- if (virt_addr == NULL) {
- printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n");
- return 1;
- }
- if ((u32)virt_addr % TW_ALIGNMENT) {
- printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n");
- return 1;
- }
tw_dev->alignment_virtual_address[request_id] = virt_addr;
tw_dev->alignment_physical_address[request_id] = virt_to_bus(virt_addr);
}
@@ -2390,7 +2384,6 @@ void tw_unmask_command_interrupt(TW_Device_Extension *tw_dev)
/* Now get things going */
-#ifdef MODULE
-Scsi_Host_Template driver_template = TWXXXX;
+static Scsi_Host_Template driver_template = TWXXXX;
#include "scsi_module.c"
-#endif
+
diff --git a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h
index 68d29fae7..2c781a709 100644
--- a/drivers/scsi/3w-xxxx.h
+++ b/drivers/scsi/3w-xxxx.h
@@ -338,7 +338,6 @@ int tw_state_request_finish(TW_Device_Extension *tw_dev,int request_id);
int tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id);
void tw_unmask_command_interrupt(TW_Device_Extension *tw_dev);
-#if defined(HOSTS_C) || defined(MODULE)
/* Scsi_Host_Template Initializer */
#define TWXXXX { \
next : NULL, \
@@ -371,5 +370,4 @@ void tw_unmask_command_interrupt(TW_Device_Extension *tw_dev);
use_new_eh_code : 1, \
emulated : 1 \
}
-#endif /* HOSTS_C */
#endif /* _3W_XXXX_H */
diff --git a/drivers/scsi/53c7,8xx.c b/drivers/scsi/53c7,8xx.c
index 547b04c4f..15ec102d6 100644
--- a/drivers/scsi/53c7,8xx.c
+++ b/drivers/scsi/53c7,8xx.c
@@ -6425,6 +6425,7 @@ NCR53c7x0_release(struct Scsi_Host *host) {
vfree ((void *)hostdata->events);
return 1;
}
-Scsi_Host_Template driver_template = NCR53c7xx;
-#include "scsi_module.c"
#endif /* def MODULE */
+
+static Scsi_Host_Template driver_template = NCR53c7xx;
+#include "scsi_module.c"
diff --git a/drivers/scsi/53c7,8xx.h b/drivers/scsi/53c7,8xx.h
index e7b77da7e..f84cc77f5 100644
--- a/drivers/scsi/53c7,8xx.h
+++ b/drivers/scsi/53c7,8xx.h
@@ -46,7 +46,6 @@
* array.
*/
-#if defined(HOSTS_C) || defined(MODULE)
#include <scsi/scsicam.h>
extern int NCR53c7xx_abort(Scsi_Cmnd *);
@@ -72,8 +71,6 @@ extern int NCR53c7xx_release(struct Scsi_Host *);
cmd_per_lun: 3, \
use_clustering: DISABLE_CLUSTERING}
-#endif /* defined(HOSTS_C) || defined(MODULE) */
-
#ifndef HOSTS_C
/* Register addresses, ordered numerically */
diff --git a/drivers/scsi/53c7xx.c b/drivers/scsi/53c7xx.c
index 14a354623..7caa27312 100644
--- a/drivers/scsi/53c7xx.c
+++ b/drivers/scsi/53c7xx.c
@@ -6102,6 +6102,7 @@ NCR53c7x0_release(struct Scsi_Host *host) {
free_pages ((u32)hostdata, 1);
return 1;
}
-Scsi_Host_Template driver_template = NCR53c7xx;
-#include "scsi_module.c"
#endif /* def MODULE */
+
+static Scsi_Host_Template driver_template = NCR53c7xx;
+#include "scsi_module.c"
diff --git a/drivers/scsi/53c7xx.h b/drivers/scsi/53c7xx.h
index f579d9573..f96fdcd0b 100644
--- a/drivers/scsi/53c7xx.h
+++ b/drivers/scsi/53c7xx.h
@@ -59,7 +59,6 @@
* array.
*/
-#if defined(HOSTS_C) || defined(MODULE)
#include <scsi/scsicam.h>
extern int NCR53c7xx_abort(Scsi_Cmnd *);
@@ -80,8 +79,6 @@ extern int NCR53c7xx_release(struct Scsi_Host *);
/* id */ 7, 127 /* old SG_ALL */, /* cmd per lun */ 3, \
/* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING}
-#endif /* defined(HOSTS_C) || defined(MODULE) */
-
#ifndef HOSTS_C
/* SCSI control 0 rw, default = 0xc0 */
diff --git a/drivers/scsi/AM53C974.c b/drivers/scsi/AM53C974.c
index 7abe05e6c..8d92082aa 100644
--- a/drivers/scsi/AM53C974.c
+++ b/drivers/scsi/AM53C974.c
@@ -2456,11 +2456,10 @@ int AM53C974_release(struct Scsi_Host *shp)
#ifdef MODULE
-static Scsi_Host_Template driver_template = AM53C974;
-
/* You can specify overrides=a,b,c,d in the same format at AM53C974=a,b,c,d
on boot up */
-
MODULE_PARM(overrides, "1-32i");
-#include "scsi_module.c"
#endif
+
+static Scsi_Host_Template driver_template = AM53C974;
+#include "scsi_module.c"
diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c
index 9c3429b4f..1abc1a8c7 100644
--- a/drivers/scsi/BusLogic.c
+++ b/drivers/scsi/BusLogic.c
@@ -61,7 +61,7 @@
*/
static int
- BusLogic_DriverOptionsCount = 0;
+ BusLogic_DriverOptionsCount;
/*
@@ -79,7 +79,7 @@ static BusLogic_DriverOptions_T
*/
#ifdef MODULE
-static char *BusLogic = NULL;
+static char *BusLogic;
MODULE_PARM(BusLogic, "s");
#endif
@@ -90,7 +90,7 @@ MODULE_PARM(BusLogic, "s");
*/
static BusLogic_ProbeOptions_T
- BusLogic_ProbeOptions = { 0 };
+ BusLogic_ProbeOptions;
/*
@@ -99,7 +99,7 @@ static BusLogic_ProbeOptions_T
*/
static BusLogic_GlobalOptions_T
- BusLogic_GlobalOptions = { 0 };
+ BusLogic_GlobalOptions;
/*
@@ -108,8 +108,8 @@ static BusLogic_GlobalOptions_T
*/
static BusLogic_HostAdapter_T
- *BusLogic_FirstRegisteredHostAdapter = NULL,
- *BusLogic_LastRegisteredHostAdapter = NULL;
+ *BusLogic_FirstRegisteredHostAdapter,
+ *BusLogic_LastRegisteredHostAdapter;
/*
@@ -117,7 +117,7 @@ static BusLogic_HostAdapter_T
*/
static int
- BusLogic_ProbeInfoCount = 0;
+ BusLogic_ProbeInfoCount;
/*
@@ -128,7 +128,7 @@ static int
*/
static BusLogic_ProbeInfo_T
- *BusLogic_ProbeInfoList = NULL;
+ *BusLogic_ProbeInfoList;
/*
@@ -4982,13 +4982,9 @@ BusLogic_Setup(char *str)
__setup("BusLogic=", BusLogic_Setup);
/*
- Include Module support if requested.
+ Get it all started
*/
-#ifdef MODULE
-
-SCSI_Host_Template_T driver_template = BUSLOGIC;
+static SCSI_Host_Template_T driver_template = BUSLOGIC;
#include "scsi_module.c"
-
-#endif
diff --git a/drivers/scsi/ChangeLog.ips b/drivers/scsi/ChangeLog.ips
index 52c273673..481b4d4e5 100644
--- a/drivers/scsi/ChangeLog.ips
+++ b/drivers/scsi/ChangeLog.ips
@@ -1,7 +1,22 @@
IBM ServeRAID driver Change Log
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 4.20.14 - Update patch files for kernel 2.4.0-test5
+
+ 4.20.13 - Fix some failure cases / reset code
+ - Hook into the reboot_notifier to flush the controller
+ cache
+
+ 4.20.03 - Rename version to coincide with new release schedules
+ - Performance fixes
+ - Fix truncation of /proc files with cat
+ - Merge in changes through kernel 2.4.0test1ac21
+
+ 4.10.13 - Fix for dynamic unload and proc file system
+
+ 4.10.00 - Add support for ServeRAID 4M/4L
+
4.00.06 - Fix timeout with initial FFDC command
-
+
4.00.05 - Remove wish_block from init routine
- Use linux/spinlock.h instead of asm/spinlock.h for kernels
2.3.18 and later
diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in
index a12560ec9..83f2c789f 100644
--- a/drivers/scsi/Config.in
+++ b/drivers/scsi/Config.in
@@ -31,7 +31,7 @@ mainmenu_option next_comment
comment 'SCSI low-level drivers'
if [ "$CONFIG_SGI_IP22" = "y" ]; then
- dep_tristate 'SGI WD93C93 SCSI Driver' CONFIG_SCSI_SGIWD93 $CONFIG_SCSI
+ dep_tristate 'SGI WD93C93 SCSI Driver' CONFIG_SGIWD93_SCSI $CONFIG_SCSI
fi
if [ "$CONFIG_DECSTATION" = "y" ]; then
if [ "$CONFIG_TC" = "y" ]; then
@@ -55,9 +55,6 @@ if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS
int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5
fi
-if [ "$CONFIG_X86" = "y" ]; then
- dep_tristate 'IBM ServeRAID support' CONFIG_SCSI_IPS $CONFIG_SCSI
-fi
dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI
@@ -67,6 +64,9 @@ dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI
if [ "$CONFIG_SCSI_BUSLOGIC" != "n" ]; then
bool ' Omit FlashPoint support' CONFIG_SCSI_OMIT_FLASHPOINT
fi
+if [ "$CONFIG_PCI" = "y" ]; then
+ dep_tristate 'Compaq Fibre Channel 64-bit/66Mhz HBA support' CONFIG_SCSI_CPQFCTS $CONFIG_SCSI
+fi
dep_tristate 'DMX3191D SCSI support' CONFIG_SCSI_DMX3191D $CONFIG_SCSI $CONFIG_PCI
dep_tristate 'DTC3180/3280 SCSI support' CONFIG_SCSI_DTC3280 $CONFIG_SCSI
dep_tristate 'EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support' CONFIG_SCSI_EATA $CONFIG_SCSI
@@ -89,6 +89,16 @@ if [ "$CONFIG_SCSI_GENERIC_NCR5380" != "n" ]; then
"Port CONFIG_SCSI_G_NCR5380_PORT \
Memory CONFIG_SCSI_G_NCR5380_MEM" Port
fi
+if [ "$CONFIG_MCA" = "y" ]; then
+ dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI
+ if [ "$CONFIG_SCSI_IBMMCA" != "n" ]; then
+ bool ' Standard SCSI-order' CONFIG_IBMMCA_SCSI_ORDER_STANDARD
+ bool ' Reset SCSI-devices at boottime' CONFIG_IBMMCA_SCSI_DEV_RESET
+ fi
+fi
+if [ "$CONFIG_X86" = "y" ]; then
+ dep_tristate 'IBM ServeRAID support' CONFIG_SCSI_IPS $CONFIG_SCSI
+fi
dep_tristate 'Initio 9100U(W) support' CONFIG_SCSI_INITIO $CONFIG_SCSI $CONFIG_PCI
dep_tristate 'Initio INI-A100U2W support' CONFIG_SCSI_INIA100 $CONFIG_SCSI $CONFIG_PCI
if [ "$CONFIG_PARPORT" != "n" ]; then
@@ -100,8 +110,6 @@ if [ "$CONFIG_PARPORT" != "n" ]; then
fi
fi
dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI
-dep_tristate 'symbios 53c416 SCSI support' CONFIG_SCSI_SYM53C416 $CONFIG_SCSI
-dep_tristate 'Simple 53c710 SCSI support (Compaq, NCR machines)' CONFIG_SCSI_SIM710 $CONFIG_SCSI
dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI $CONFIG_PCI
if [ "$CONFIG_SCSI_NCR53C7xx" != "n" ]; then
bool ' always negotiate synchronous transfers' CONFIG_SCSI_NCR53C7xx_sync
@@ -129,13 +137,6 @@ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then
fi
fi
if [ "$CONFIG_MCA" = "y" ]; then
- dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI
- if [ "$CONFIG_SCSI_IBMMCA" != "n" ]; then
- bool ' Standard SCSI-order' CONFIG_IBMMCA_SCSI_ORDER_STANDARD
- bool ' Reset SCSI-devices at boottime' CONFIG_IBMMCA_SCSI_DEV_RESET
- fi
-fi
-if [ "$CONFIG_MCA" = "y" ]; then
dep_tristate 'NCR MCA 53C9x SCSI support' CONFIG_SCSI_MCA_53C9X $CONFIG_SCSI
fi
dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
@@ -151,6 +152,8 @@ fi
if [ "$CONFIG_X86" = "y" ]; then
dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI
fi
+dep_tristate 'Simple 53c710 SCSI support (Compaq, NCR machines)' CONFIG_SCSI_SIM710 $CONFIG_SCSI
+dep_tristate 'Symbios 53c416 SCSI support' CONFIG_SCSI_SYM53C416 $CONFIG_SCSI
if [ "$CONFIG_PCI" = "y" ]; then
dep_tristate 'Tekram DC390(T) and Am53/79C974 SCSI support' CONFIG_SCSI_DC390T $CONFIG_SCSI
if [ "$CONFIG_SCSI_DC390T" != "n" ]; then
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index de225879f..50d26c7e4 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -4,6 +4,8 @@
# 30 May 2000, Christoph Hellwig <chhellwig@gmx.net>
# Rewritten to use lists instead of if-statements.
#
+# 20 Sep 2000, Torben Mathiasen <tmm@image.dk>
+# Changed link order to reflect new scsi initialization.
O_TARGET := scsidrv.o
@@ -22,25 +24,14 @@ else
endif
export-objs := scsi_syms.o
-list-multi := scsi_mod.o sr_mod.o initio.o a100u2w.o
+list-multi := scsi_mod.o initio.o a100u2w.o
CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF
CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM
-obj-$(CONFIG_SCSI) += scsi_mod.o
-obj-$(CONFIG_CHR_DEV_ST) += st.o
-obj-$(CONFIG_BLK_DEV_SD) += sd.o
-obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o
-obj-$(CONFIG_CHR_DEV_SG) += sg.o
+obj-$(CONFIG_SCSI) += scsi_mod.o scsi_syms.o
-obj-$(CONFIG_SCSI_ADVANSYS) += advansys.o
-obj-$(CONFIG_SCSI_PCI2000) += pci2000.o
-obj-$(CONFIG_SCSI_PCI2220I) += pci2220i.o
-obj-$(CONFIG_SCSI_PSI240I) += psi240i.o
-obj-$(CONFIG_MVME16x_SCSI) += mvme16x.o 53c7xx.o
-obj-$(CONFIG_BVME6000_SCSI) += bvme6000.o 53c7xx.o
-obj-$(CONFIG_SCSI_SIM710) += sim710.o
obj-$(CONFIG_A4000T_SCSI) += amiga7xx.o 53c7xx.o
obj-$(CONFIG_A4091_SCSI) += amiga7xx.o 53c7xx.o
obj-$(CONFIG_BLZ603EPLUS_SCSI) += amiga7xx.o 53c7xx.o
@@ -48,8 +39,7 @@ obj-$(CONFIG_WARPENGINE_SCSI) += amiga7xx.o 53c7xx.o
obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o
obj-$(CONFIG_A2091_SCSI) += a2091.o wd33c93.o
obj-$(CONFIG_GVP11_SCSI) += gvp11.o wd33c93.o
-obj-$(CONFIG_SCSI_SGIWD93) += sgiwd93.o wd33c93.o
-obj-$(CONFIG_SCSI_MCA_53C9X) += NCR53C9x.o mca_53c9x.o
+obj-$(CONFIG_SGIWD93_SCSI) += sgiwd93.o wd33c93.o
obj-$(CONFIG_CYBERSTORM_SCSI) += NCR53C9x.o cyberstorm.o
obj-$(CONFIG_CYBERSTORMII_SCSI) += NCR53C9x.o cyberstormII.o
obj-$(CONFIG_BLZ2060_SCSI) += NCR53C9x.o blz2060.o
@@ -58,69 +48,82 @@ obj-$(CONFIG_FASTLANE_SCSI) += NCR53C9x.o fastlane.o
obj-$(CONFIG_OKTAGON_SCSI) += NCR53C9x.o oktagon_esp.o oktagon_io.o
obj-$(CONFIG_ATARI_SCSI) += atari_scsi.o
obj-$(CONFIG_MAC_SCSI) += mac_scsi.o
-obj-$(CONFIG_SUN3_SCSI) += sun3_scsi.o
obj-$(CONFIG_SCSI_MAC_ESP) += mac_esp.o NCR53C9x.o
-obj-$(CONFIG_SCSI_PPA) += ppa.o
-obj-$(CONFIG_SCSI_IMM) += imm.o
-obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas.o
-obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogicisp.o
-obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o
-obj-$(CONFIG_SCSI_ACARD) += atp870u.o
-obj-$(CONFIG_SCSI_INITIO) += initio.o
-obj-$(CONFIG_SCSI_INIA100) += a100u2w.o
-obj-$(CONFIG_SCSI_QLOGIC_FC) += qlogicfc.o
+obj-$(CONFIG_SUN3_SCSI) += sun3_scsi.o
+obj-$(CONFIG_MVME16x_SCSI) += mvme16x.o 53c7xx.o
+obj-$(CONFIG_BVME6000_SCSI) += bvme6000.o 53c7xx.o
+obj-$(CONFIG_SCSI_SIM710) += sim710.o
+obj-$(CONFIG_SCSI_ADVANSYS) += advansys.o
+obj-$(CONFIG_SCSI_PCI2000) += pci2000.o
+obj-$(CONFIG_SCSI_PCI2220I) += pci2220i.o
+obj-$(CONFIG_SCSI_PSI240I) += psi240i.o
+obj-$(CONFIG_SCSI_BUSLOGIC) += BusLogic.o
+obj-$(CONFIG_SCSI_U14_34F) += u14-34f.o
+obj-$(CONFIG_SCSI_ULTRASTOR) += ultrastor.o
obj-$(CONFIG_SCSI_AHA152X) += aha152x.o
obj-$(CONFIG_SCSI_AHA1542) += aha1542.o
obj-$(CONFIG_SCSI_AHA1740) += aha1740.o
obj-$(CONFIG_SCSI_AIC7XXX) += aic7xxx.o
obj-$(CONFIG_SCSI_IPS) += ips.o
-obj-$(CONFIG_SCSI_DC390T) += tmscsim.o
-obj-$(CONFIG_SCSI_AM53C974) += AM53C974.o
-obj-$(CONFIG_SCSI_BUSLOGIC) += BusLogic.o
-obj-$(CONFIG_SCSI_EATA_DMA) += eata_dma.o
-obj-$(CONFIG_SCSI_EATA_PIO) += eata_pio.o
-obj-$(CONFIG_SCSI_U14_34F) += u14-34f.o
-obj-$(CONFIG_SCSI_SUNESP) += esp.o
-obj-$(CONFIG_SCSI_QLOGICPTI) += qlogicpti.o
-obj-$(CONFIG_SCSI_MESH) += mesh.o
-obj-$(CONFIG_SCSI_MAC53C94) += mac53c94.o
-obj-$(CONFIG_SCSI_GDTH) += gdth.o
-
-obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o
-
+obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o
obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
obj-$(CONFIG_SCSI_IN2000) += in2000.o
obj-$(CONFIG_SCSI_GENERIC_NCR5380) += g_NCR5380.o
-obj-$(CONFIG_SCSI_NCR53C7xx) += 53c7,8xx.o
-obj-$(CONFIG_SCSI_NCR53C8XX) += ncr53c8xx.o
-obj-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o
+obj-$(CONFIG_SCSI_NCR53C406A) += NCR53c406a.o
+obj-$(CONFIG_SCSI_SYM53C416) += sym53c416.o
+obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas.o
+obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogicisp.o
+obj-$(CONFIG_SCSI_QLOGIC_FC) += qlogicfc.o
+obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o
obj-$(CONFIG_SCSI_PAS16) += pas16.o
obj-$(CONFIG_SCSI_SEAGATE) += seagate.o
obj-$(CONFIG_SCSI_FD_8xx) += seagate.o
-obj-$(CONFIG_SCSI_7000FASST) += wd7000.o
-obj-$(CONFIG_SCSI_IBMMCA) += ibmmca.o
-obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o
obj-$(CONFIG_SCSI_T128) += t128.o
obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o
obj-$(CONFIG_SCSI_DTC3280) += dtc.o
-obj-$(CONFIG_SCSI_ULTRASTOR) += ultrastor.o
-obj-$(CONFIG_SCSI_PLUTO) += pluto.o
-obj-$(CONFIG_SCSI_FCAL) += fcal.o
+obj-$(CONFIG_SCSI_NCR53C7xx) += 53c7,8xx.o
+obj-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o
+obj-$(CONFIG_SCSI_NCR53C8XX) += ncr53c8xx.o
+obj-$(CONFIG_SCSI_EATA_DMA) += eata_dma.o
+obj-$(CONFIG_SCSI_EATA_PIO) += eata_pio.o
+obj-$(CONFIG_SCSI_7000FASST) += wd7000.o
+obj-$(CONFIG_SCSI_MCA_53C9X) += NCR53C9x.o mca_53c9x.o
+obj-$(CONFIG_SCSI_IBMMCA) += ibmmca.o
obj-$(CONFIG_SCSI_EATA) += eata.o
-obj-$(CONFIG_SCSI_NCR53C406A) += NCR53c406a.o
+obj-$(CONFIG_SCSI_DC390T) += tmscsim.o
+obj-$(CONFIG_SCSI_AM53C974) += AM53C974.o
obj-$(CONFIG_SCSI_MEGARAID) += megaraid.o
-obj-$(CONFIG_SCSI_SYM53C416) += sym53c416.o
+obj-$(CONFIG_SCSI_ACARD) += atp870u.o
+obj-$(CONFIG_SCSI_SUNESP) += esp.o
+obj-$(CONFIG_SCSI_GDTH) += gdth.o
+obj-$(CONFIG_SCSI_INITIO) += initio.o
+obj-$(CONFIG_SCSI_INIA100) += a100u2w.o
+obj-$(CONFIG_SCSI_QLOGICPTI) += qlogicpti.o
obj-$(CONFIG_BLK_DEV_IDESCSI) += ide-scsi.o
-obj-$(CONFIG_JAZZ_ESP) += NCR53C9x.o jazz_esp.o
+obj-$(CONFIG_SCSI_MESH) += mesh.o
+obj-$(CONFIG_SCSI_MAC53C94) += mac53c94.o
+obj-$(CONFIG_SCSI_PLUTO) += pluto.o
obj-$(CONFIG_SCSI_DECNCR) += NCR53C9x.o dec_esp.o
-obj-$(CONFIG_SUN3X_ESP) += NCR53C9x.o sun3x_esp.o
obj-$(CONFIG_BLK_DEV_3W_XXXX_RAID) += 3w-xxxx.o
+obj-$(CONFIG_SCSI_PPA) += ppa.o
+obj-$(CONFIG_SCSI_IMM) += imm.o
+obj-$(CONFIG_JAZZ_ESP) += NCR53C9x.o jazz_esp.o
+obj-$(CONFIG_SUN3X_ESP) += NCR53C9x.o sun3x_esp.o
+obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o
+obj-$(CONFIG_SCSI_FCAL) += fcal.o
+
+obj-$(CONFIG_CHR_DEV_ST) += st.o
+obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o
+obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o
+obj-$(CONFIG_CHR_DEV_SG) += sg.o
+
-scsi_mod-objs := hosts.o scsi.o scsi_ioctl.o constants.o \
+
+scsi_mod-objs := scsi.o hosts.o scsi_ioctl.o constants.o \
scsicam.o scsi_proc.o scsi_error.o \
scsi_obsolete.o scsi_queue.o scsi_lib.o \
scsi_merge.o scsi_dma.o scsi_scan.o \
- scsi_syms.o
+
sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o
initio-objs := ini9100u.o i91uscsi.o
a100u2w-objs := inia100.o i60uscsi.o
@@ -136,9 +139,6 @@ int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
obj-m := $(filter-out $(obj-y), $(obj-m))
int-m := $(filter-out $(int-y), $(int-m))
-# Take multi-part drivers out of obj-y and put components in.
-obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y)
-
O_OBJS := $(filter-out $(export-objs), $(obj-y))
OX_OBJS := $(filter $(export-objs), $(obj-y))
M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
@@ -152,6 +152,9 @@ include $(TOPDIR)/Rules.make
scsi_mod.o: $(scsi_mod-objs)
$(LD) -r -o $@ $(scsi_mod-objs)
+sd_mod.o: sd.o
+ $(LD) -r -o $@ sd.o
+
sr_mod.o: $(sr_mod-objs)
$(LD) -r -o $@ $(sr_mod-objs)
diff --git a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c
index 1ee14b6e3..5069c33cb 100644
--- a/drivers/scsi/NCR53c406a.c
+++ b/drivers/scsi/NCR53c406a.c
@@ -1059,12 +1059,10 @@ void __init calc_port_addr(void)
/* 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;
+static Scsi_Host_Template driver_template = NCR53c406a;
#include "scsi_module.c"
-#endif
/*
* Overrides for Emacs so that we get a uniform tabbing style.
diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c
index 319f961f4..b26a8677b 100644
--- a/drivers/scsi/a2091.c
+++ b/drivers/scsi/a2091.c
@@ -226,18 +226,14 @@ int __init a2091_detect(Scsi_Host_Template *tpnt)
return num_a2091;
}
-#ifdef MODULE
-
#define HOSTS_C
#include "a2091.h"
-Scsi_Host_Template driver_template = A2091_SCSI;
+static Scsi_Host_Template driver_template = A2091_SCSI;
#include "scsi_module.c"
-#endif
-
int a2091_release(struct Scsi_Host *instance)
{
#ifdef MODULE
diff --git a/drivers/scsi/a2091.h b/drivers/scsi/a2091.h
index 5a42b9300..97beba146 100644
--- a/drivers/scsi/a2091.h
+++ b/drivers/scsi/a2091.h
@@ -29,8 +29,6 @@ int wd33c93_reset(Scsi_Cmnd *, unsigned int);
#define CAN_QUEUE 16
#endif
-#ifdef HOSTS_C
-
#define A2091_SCSI { proc_name: "A2901", \
name: "Commodore A2091/A590 SCSI", \
detect: a2091_detect, \
@@ -43,7 +41,6 @@ int wd33c93_reset(Scsi_Cmnd *, unsigned int);
sg_tablesize: SG_ALL, \
cmd_per_lun: CMD_PER_LUN, \
use_clustering: DISABLE_CLUSTERING }
-#else
/*
* if the transfer address ANDed with this results in a non-zero
@@ -92,6 +89,4 @@ typedef struct {
#define ISTR_FF_FLG (1<<1)
#define ISTR_FE_FLG (1<<0)
-#endif /* else def HOSTS_C */
-
#endif /* A2091_H */
diff --git a/drivers/scsi/a3000.c b/drivers/scsi/a3000.c
index fd28a9ef2..422536d77 100644
--- a/drivers/scsi/a3000.c
+++ b/drivers/scsi/a3000.c
@@ -188,18 +188,14 @@ int __init a3000_detect(Scsi_Host_Template *tpnt)
return 1;
}
-#ifdef MODULE
-
#define HOSTS_C
#include "a3000.h"
-Scsi_Host_Template driver_template = A3000_SCSI;
+static Scsi_Host_Template driver_template = A3000_SCSI;
#include "scsi_module.c"
-#endif
-
int a3000_release(struct Scsi_Host *instance)
{
#ifdef MODULE
diff --git a/drivers/scsi/a3000.h b/drivers/scsi/a3000.h
index b6b6827d0..478c3d39a 100644
--- a/drivers/scsi/a3000.h
+++ b/drivers/scsi/a3000.h
@@ -29,8 +29,6 @@ int wd33c93_reset(Scsi_Cmnd *, unsigned int);
#define CAN_QUEUE 16
#endif
-#ifdef HOSTS_C
-
#define A3000_SCSI { proc_name: "A3000", \
proc_info: NULL, \
name: "Amiga 3000 built-in SCSI", \
@@ -44,7 +42,6 @@ int wd33c93_reset(Scsi_Cmnd *, unsigned int);
sg_tablesize: SG_ALL, \
cmd_per_lun: CMD_PER_LUN, \
use_clustering: ENABLE_CLUSTERING }
-#else
/*
* if the transfer address ANDed with this results in a non-zero
@@ -96,6 +93,4 @@ typedef struct {
#define ISTR_FF_FLG (1<<1)
#define ISTR_FE_FLG (1<<0)
-#endif /* else def HOSTS_C */
-
#endif /* A3000_H */
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
index 02315dc57..bf89438ee 100644
--- a/drivers/scsi/advansys.c
+++ b/drivers/scsi/advansys.c
@@ -6785,10 +6785,8 @@ advansys_setup(char *str, int *ints)
* --- Loadable Driver Support
*/
-#ifdef MODULE
-Scsi_Host_Template driver_template = ADVANSYS;
+static Scsi_Host_Template driver_template = ADVANSYS;
# include "scsi_module.c"
-#endif /* MODULE */
/*
diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c
index 3f17ad86f..ba54b2bf0 100644
--- a/drivers/scsi/aha152x.c
+++ b/drivers/scsi/aha152x.c
@@ -3813,9 +3813,7 @@ int aha152x_proc_info(char *buffer, char **start,
return thislength < length ? thislength : length;
}
-#if defined(MODULE)
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = AHA152X;
+static Scsi_Host_Template driver_template = AHA152X;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c
index f4834a51f..7ed3170c9 100644
--- a/drivers/scsi/aha1542.c
+++ b/drivers/scsi/aha1542.c
@@ -1764,9 +1764,7 @@ int aha1542_biosparam(Scsi_Disk * disk, kdev_t dev, int *ip)
}
-#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = AHA1542;
+static Scsi_Host_Template driver_template = AHA1542;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c
index 592301e76..c3a25c121 100644
--- a/drivers/scsi/aha1740.c
+++ b/drivers/scsi/aha1740.c
@@ -601,12 +601,10 @@ int aha1740_biosparam(Disk * disk, kdev_t dev, int* ip)
return 0;
}
-#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = AHA1740;
+static 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
diff --git a/drivers/scsi/aic7xxx.c b/drivers/scsi/aic7xxx.c
index 655107348..166ec8c59 100644
--- a/drivers/scsi/aic7xxx.c
+++ b/drivers/scsi/aic7xxx.c
@@ -12217,12 +12217,10 @@ aic7xxx_print_scratch_ram(struct aic7xxx_host *p)
#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;
+static Scsi_Host_Template driver_template = AIC7XXX;
#include "scsi_module.c"
-#endif
/*
* Overrides for Emacs so that we almost follow Linus's tabbing style.
diff --git a/drivers/scsi/amiga7xx.h b/drivers/scsi/amiga7xx.h
index 06b5ba91f..b97428f47 100644
--- a/drivers/scsi/amiga7xx.h
+++ b/drivers/scsi/amiga7xx.h
@@ -22,7 +22,6 @@ void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
#define CAN_QUEUE 24
#endif
-#if defined(HOSTS_C) || defined(MODULE)
#include <scsi/scsicam.h>
#define AMIGA7XX_SCSI {name: "Amiga NCR53c710 SCSI", \
@@ -36,5 +35,5 @@ void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
sg_tablesize: 63, \
cmd_per_lun: 3, \
use_clustering: DISABLE_CLUSTERING }
-#endif
+
#endif /* AMIGA7XX_H */
diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c
index a06cd8b7c..118f09f05 100644
--- a/drivers/scsi/atari_scsi.c
+++ b/drivers/scsi/atari_scsi.c
@@ -1126,8 +1126,5 @@ static void atari_scsi_falcon_reg_write( unsigned char reg, unsigned char value
#include "atari_NCR5380.c"
-#ifdef MODULE
-Scsi_Host_Template driver_template = ATARI_SCSI;
-
+static Scsi_Host_Template driver_template = ATARI_SCSI;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/atari_scsi.h b/drivers/scsi/atari_scsi.h
index 15280b82c..96d056994 100644
--- a/drivers/scsi/atari_scsi.h
+++ b/drivers/scsi/atari_scsi.h
@@ -51,8 +51,6 @@ int atari_scsi_release (struct Scsi_Host *);
#define DEFAULT_USE_TAGGED_QUEUING 0
-#if defined (HOSTS_C) || defined (MODULE)
-
#define ATARI_SCSI { proc_info: atari_scsi_proc_info, \
name: "Atari native SCSI", \
detect: atari_scsi_detect, \
@@ -67,10 +65,6 @@ int atari_scsi_release (struct Scsi_Host *);
cmd_per_lun: 0, /* initialized at run-time */ \
use_clustering: DISABLE_CLUSTERING }
-#endif
-
-#ifndef HOSTS_C
-
#define NCR5380_implementation_fields /* none */
#define NCR5380_read(reg) atari_scsi_reg_read( reg )
@@ -267,7 +261,6 @@ int atari_scsi_release (struct Scsi_Host *);
#define NDEBUG_ANY 0xffffffff
-#endif /* else def HOSTS_C */
#endif /* ndef ASM */
#endif /* ATARI_SCSI_H */
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c
index 0cbad04fa..b0722e20f 100644
--- a/drivers/scsi/atp870u.c
+++ b/drivers/scsi/atp870u.c
@@ -1971,8 +1971,5 @@ int atp870u_release (struct Scsi_Host *pshost)
}
-#ifdef MODULE
-Scsi_Host_Template driver_template = ATP870U;
-
+static Scsi_Host_Template driver_template = ATP870U;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/blz1230.c b/drivers/scsi/blz1230.c
index bd437e7e1..a13a0ff25 100644
--- a/drivers/scsi/blz1230.c
+++ b/drivers/scsi/blz1230.c
@@ -274,18 +274,14 @@ static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
}
}
-#ifdef MODULE
-
#define HOSTS_C
#include "blz1230.h"
-Scsi_Host_Template driver_template = SCSI_BLZ1230;
+static Scsi_Host_Template driver_template = SCSI_BLZ1230;
#include "scsi_module.c"
-#endif
-
int blz1230_esp_release(struct Scsi_Host *instance)
{
#ifdef MODULE
diff --git a/drivers/scsi/blz2060.c b/drivers/scsi/blz2060.c
index 72a67027b..3daac0f2a 100644
--- a/drivers/scsi/blz2060.c
+++ b/drivers/scsi/blz2060.c
@@ -236,18 +236,14 @@ static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
}
}
-#ifdef MODULE
-
#define HOSTS_C
#include "blz2060.h"
-Scsi_Host_Template driver_template = SCSI_BLZ2060;
+static Scsi_Host_Template driver_template = SCSI_BLZ2060;
#include "scsi_module.c"
-#endif
-
int blz2060_esp_release(struct Scsi_Host *instance)
{
#ifdef MODULE
diff --git a/drivers/scsi/bvme6000.h b/drivers/scsi/bvme6000.h
index 557368625..2f96c34a0 100644
--- a/drivers/scsi/bvme6000.h
+++ b/drivers/scsi/bvme6000.h
@@ -23,7 +23,6 @@ void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
#define CAN_QUEUE 24
#endif
-#if defined(HOSTS_C) || defined(MODULE)
#include <scsi/scsicam.h>
#define BVME6000_SCSI {name: "BVME6000 NCR53c710 SCSI", \
@@ -37,5 +36,5 @@ void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
sg_tablesize: 63, \
cmd_per_lun: 3, \
use_clustering: DISABLE_CLUSTERING }
-#endif
+
#endif /* BVME6000_SCSI_H */
diff --git a/drivers/scsi/cpqfc.Readme b/drivers/scsi/cpqfc.Readme
new file mode 100644
index 000000000..1d795a447
--- /dev/null
+++ b/drivers/scsi/cpqfc.Readme
@@ -0,0 +1,216 @@
+Notes for CPQFCTS driver for Compaq Tachyon TS
+Fibre Channel Host Bus Adapter, PCI 64-bit, 66MHz
+for Linux (RH 6.1, 6.2 kernel 2.2.12-32, 2.2.14-5)
+SMP tested
+Tested in single and dual HBA configuration, 32 and 64bit busses,
+33 and 66MHz. Only supports FC-AL.
+SEST size 512 Exchanges (simultaneous I/Os) limited by module kmalloc()
+ max of 128k bytes contiguous.
+Ver 1.3.4 Sep 7, 2000
+ Added Modinfo information
+ Fixed problem with statically linking the driver
+
+Ver 1.3.3, Aug 23, 2000
+ Fixed device/function number in ioctl
+
+Ver 1.3.2, July 27, 2000
+ Add include for Alpha compile on 2.2.14 kernel (cpq*i2c.c)
+ Change logic for different FCP-RSP sense_buffer location for HSG80 target
+ And search for Agilent Tachyon XL2 HBAs (not finished! - in test)
+
+Tested with
+(storage):
+ Compaq RA-4x000, RAID firmware ver 2.40 - 2.54
+ Seagate FC drives model ST39102FC, rev 0006
+ Hitachi DK31CJ-72FC rev J8A8
+ IBM DDYF-T18350R rev F60K
+ Compaq FC-SCSI bridge w/ DLT 35/70 Gb DLT (tape)
+(servers):
+ Compaq PL-1850R
+ Compaq PL-6500 Xeon (400MHz)
+ Compaq PL-8500 (500MHz, 66MHz, 64bit PCI)
+ Compaq Alpha DS20 (RH 6.1)
+(hubs):
+ Vixel Rapport 1000 (7-port "dumb")
+ Gadzoox Gibralter (12-port "dumb")
+ Gadzoox Capellix 2000, 3000
+(switches):
+ Brocade 2010, 2400, 2800, rev 2.0.3a (& later)
+ Gadzoox 3210 (Fabric blade beta)
+ Vixel 7100 (Fabric beta firmare - known hot plug issues)
+using "qa_test" (esp. io_test script) suite modified from Unix tests.
+
+Installation:
+copy file cpqfcTS.patch to /usr/src/linux
+patch -p1 < cpqfcTS.patch
+make menuconfig
+ (select SCSI low-level, Compaq FC HBA)
+make dep
+make modules
+make modules_install
+
+e.g. insmod -f cpqfc
+
+Due to Fabric/switch delays, driver requires 4 seconds
+to initialize. If adapters are found, there will be a entries at
+/proc/scsi/cpqfcTS/*
+
+sample contents of startup messages
+
+*************************
+ scsi_register allocating 3596 bytes for CPQFCHBA
+ ioremap'd Membase: c887e600
+ HBA Tachyon RevId 1.2
+Allocating 119808 for 576 Exchanges @ c0dc0000
+Allocating 112904 for LinkQ @ c0c20000 (576 elements)
+Allocating 110600 for TachSEST for 512 Exchanges
+ cpqfcTS: writing IMQ BASE 7C0000h PI 7C4000h
+ cpqfcTS: SEST c0e40000(virt): Wrote base E40000h @ c887e740
+cpqfcTS: New FC port 0000E8h WWN: 500507650642499D SCSI Chan/Trgt 0/0
+cpqfcTS: New FC port 0000EFh WWN: 50000E100000D5A6 SCSI Chan/Trgt 0/1
+cpqfcTS: New FC port 0000E4h WWN: 21000020370097BB SCSI Chan/Trgt 0/2
+cpqfcTS: New FC port 0000E2h WWN: 2100002037009946 SCSI Chan/Trgt 0/3
+cpqfcTS: New FC port 0000E1h WWN: 21000020370098FE SCSI Chan/Trgt 0/4
+cpqfcTS: New FC port 0000E0h WWN: 21000020370097B2 SCSI Chan/Trgt 0/5
+cpqfcTS: New FC port 0000DCh WWN: 2100002037006CC1 SCSI Chan/Trgt 0/6
+cpqfcTS: New FC port 0000DAh WWN: 21000020370059F6 SCSI Chan/Trgt 0/7
+cpqfcTS: New FC port 00000Fh WWN: 500805F1FADB0E20 SCSI Chan/Trgt 0/8
+cpqfcTS: New FC port 000008h WWN: 500805F1FADB0EBA SCSI Chan/Trgt 0/9
+cpqfcTS: New FC port 000004h WWN: 500805F1FADB1EB9 SCSI Chan/Trgt 0/10
+cpqfcTS: New FC port 000002h WWN: 500805F1FADB1ADE SCSI Chan/Trgt 0/11
+cpqfcTS: New FC port 000001h WWN: 500805F1FADBA2CA SCSI Chan/Trgt 0/12
+scsi4 : Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.2: WWN 500508B200193F50
+ on PCI bus 0 device 0xa0fc irq 5 IObaseL 0x3400, MEMBASE 0xc6ef8600
+PCI bus width 32 bits, bus speed 33 MHz
+FCP-SCSI Driver v1.3.0
+GBIC detected: Short-wave. LPSM 0h Monitor
+scsi : 5 hosts.
+ Vendor: IBM Model: DDYF-T18350R Rev: F60K
+ Type: Direct-Access ANSI SCSI revision: 03
+Detected scsi disk sdb at scsi4, channel 0, id 0, lun 0
+ Vendor: HITACHI Model: DK31CJ-72FC Rev: J8A8
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdc at scsi4, channel 0, id 1, lun 0
+ Vendor: SEAGATE Model: ST39102FC Rev: 0006
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdd at scsi4, channel 0, id 2, lun 0
+ Vendor: SEAGATE Model: ST39102FC Rev: 0006
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sde at scsi4, channel 0, id 3, lun 0
+ Vendor: SEAGATE Model: ST39102FC Rev: 0006
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdf at scsi4, channel 0, id 4, lun 0
+ Vendor: SEAGATE Model: ST39102FC Rev: 0006
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdg at scsi4, channel 0, id 5, lun 0
+ Vendor: SEAGATE Model: ST39102FC Rev: 0006
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdh at scsi4, channel 0, id 6, lun 0
+ Vendor: SEAGATE Model: ST39102FC Rev: 0006
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdi at scsi4, channel 0, id 7, lun 0
+ Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.48
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdj at scsi4, channel 0, id 8, lun 0
+ Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.48
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdk at scsi4, channel 0, id 8, lun 1
+ Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.40
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdl at scsi4, channel 0, id 9, lun 0
+ Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.40
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdm at scsi4, channel 0, id 9, lun 1
+ Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.54
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdn at scsi4, channel 0, id 10, lun 0
+ Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.54
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdo at scsi4, channel 0, id 11, lun 0
+ Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.54
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdp at scsi4, channel 0, id 11, lun 1
+ Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.54
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdq at scsi4, channel 0, id 12, lun 0
+ Vendor: COMPAQ Model: LOGICAL VOLUME Rev: 2.54
+ Type: Direct-Access ANSI SCSI revision: 02
+Detected scsi disk sdr at scsi4, channel 0, id 12, lun 1
+resize_dma_pool: unknown device type 12
+resize_dma_pool: unknown device type 12
+SCSI device sdb: hdwr sector= 512 bytes. Sectors= 35843670 [17501 MB] [17.5 GB]
+ sdb: sdb1
+SCSI device sdc: hdwr sector= 512 bytes. Sectors= 144410880 [70513 MB] [70.5 GB]
+ sdc: sdc1
+SCSI device sdd: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdd: sdd1
+SCSI device sde: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sde: sde1
+SCSI device sdf: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdf: sdf1
+SCSI device sdg: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdg: sdg1
+SCSI device sdh: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdh: sdh1
+SCSI device sdi: hdwr sector= 512 bytes. Sectors= 17783240 [8683 MB] [8.7 GB]
+ sdi: sdi1
+SCSI device sdj: hdwr sector= 512 bytes. Sectors= 2056160 [1003 MB] [1.0 GB]
+ sdj: sdj1
+SCSI device sdk: hdwr sector= 512 bytes. Sectors= 2052736 [1002 MB] [1.0 GB]
+ sdk: sdk1
+SCSI device sdl: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB]
+ sdl: sdl1
+SCSI device sdm: hdwr sector= 512 bytes. Sectors= 8380320 [4091 MB] [4.1 GB]
+ sdm: sdm1
+SCSI device sdn: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB]
+ sdn: sdn1
+SCSI device sdo: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB]
+ sdo: sdo1
+SCSI device sdp: hdwr sector= 512 bytes. Sectors= 17764320 [8673 MB] [8.7 GB]
+ sdp: sdp1
+SCSI device sdq: hdwr sector= 512 bytes. Sectors= 2056160 [1003 MB] [1.0 GB]
+ sdq: sdq1
+SCSI device sdr: hdwr sector= 512 bytes. Sectors= 2052736 [1002 MB] [1.0 GB]
+ sdr: sdr1
+
+*************************
+
+If a GBIC of type Short-wave, Long-wave, or Copper is detected, it will
+print out; otherwise, "none" is displayed. If the cabling is correct
+and a loop circuit is completed, you should see "Monitor"; otherwise,
+"LoopFail" (on open circuit) or some LPSM number/state with bit 3 set.
+
+
+ERRATA:
+1. Normally, Linux Scsi queries FC devices with INQUIRY strings. All LUNs
+found according to INQUIRY should get READ commands at sector 0 to find
+partition table, etc. Older kernels only query the first 4 devices. Some
+Linux kernels only look for one LUN per target (i.e. FC device).
+
+2. Physically removing a device, or a malfunctioning system which hides a
+device, leads to a 30-second timeout and subsequent _abort call.
+In some process contexts, this will hang the kernel (crashing the system).
+Single bit errors in frames and virtually all hot plugging events are
+gracefully handled with internal driver timer and Abort processing.
+
+3. Some SCSI drives with error conditions will not handle the 7 second timeout
+in this software driver, leading to infinite retries on timed out SCSI commands.
+The 7 secs balances the need to quickly recover from lost frames (esp. on sequence
+initiatives) and time needed by older/slower/error-state drives in responding.
+This can be easily changed in "Exchanges[].timeOut".
+
+4. Due to the nature of FC soft addressing, there is no assurance that the
+same LUNs (drives) will have the same path (e.g. /dev/sdb1) from one boot to
+next. Dynamic soft address changes (i.e. 24-bit FC port_id) are
+supported during run time (e.g. due to hot plug event) by the use of WWN to
+SCSI Nexus (channel/target/LUN) mapping.
+
+5. Compaq RA4x00 firmware version 2.54 and later supports SSP (Selective
+Storage Presentation), which maps LUNs to a WWN. If RA4x00 firmware prior
+2.54 (e.g. older controller) is used, or the FC HBA is replaced (another WWN
+is used), logical volumes on the RA4x00 will no longer be visible.
+
+
+Send questions/comments to:
+donald.zimmerman@compaq.com
+dszimmerman@yahoo.com
diff --git a/drivers/scsi/cpqfcTS.h b/drivers/scsi/cpqfcTS.h
new file mode 100644
index 000000000..966c63b8c
--- /dev/null
+++ b/drivers/scsi/cpqfcTS.h
@@ -0,0 +1,39 @@
+#ifndef CPQFCTS_H
+#define CPQFCTS_H
+#include "cpqfcTSstructs.h"
+
+// These functions are required by the Linux SCSI layers
+extern int cpqfcTS_detect(Scsi_Host_Template *);
+extern int cpqfcTS_release(struct Scsi_Host *);
+const char * cpqfcTS_info(struct Scsi_Host *);
+extern int cpqfcTS_proc_info(char *, char **, off_t, int, int, int);
+extern int cpqfcTS_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+extern int cpqfcTS_abort(Scsi_Cmnd *);
+extern int cpqfcTS_reset(Scsi_Cmnd *, unsigned int);
+extern int cpqfcTS_biosparam(Disk *, kdev_t, int[]);
+extern int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg);
+
+// note: since Tachyon TS supports an extended scatter/gather
+// linked list of infinite length (with linked Ext S/G pages,
+// limited only by available physical memory) we use SG_ALL.
+
+#define CPQFCTS { \
+ detect: cpqfcTS_detect, \
+ release: cpqfcTS_release, \
+ info: cpqfcTS_info, \
+ proc_info: cpqfcTS_proc_info, \
+ ioctl: cpqfcTS_ioctl, \
+ queuecommand: cpqfcTS_queuecommand, \
+ eh_abort_handler: cpqfcTS_abort, \
+ reset: cpqfcTS_reset, \
+ bios_param: cpqfcTS_biosparam, \
+ can_queue: CPQFCTS_REQ_QUEUE_LEN, \
+ this_id: -1, \
+ sg_tablesize: SG_ALL, \
+ cmd_per_lun: CPQFCTS_CMD_PER_LUN, \
+ present: 0, \
+ unchecked_isa_dma: 0, \
+ use_clustering: ENABLE_CLUSTERING \
+}
+
+#endif /* CPQFCTS_H */
diff --git a/drivers/scsi/cpqfcTSchip.h b/drivers/scsi/cpqfcTSchip.h
new file mode 100644
index 000000000..14b833738
--- /dev/null
+++ b/drivers/scsi/cpqfcTSchip.h
@@ -0,0 +1,238 @@
+/* Copyright(c) 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter
+ * 64-bit, 66MHz PCI
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ...
+ * SP# P225CXCBFIEL6T, Rev XC
+ * SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * 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.
+ * Written by Don Zimmerman
+*/
+#ifndef CPQFCTSCHIP_H
+#define CPQFCTSCHIP_H
+#ifndef TACHYON_CHIP_INC
+
+// FC-PH (Physical) specification levels for Login payloads
+// NOTE: These are NOT strictly complied with by any FC vendors
+
+#define FC_PH42 0x08
+#define FC_PH43 0x09
+#define FC_PH3 0x20
+
+#define TACHLITE_TS_RX_SIZE 1024 // max inbound frame size
+// "I" prefix is for Include
+
+#define IVENDID 0x00 // word
+#define IDEVID 0x02
+#define ITLCFGCMD 0x04
+#define IMEMBASE 0x18 // Tachyon
+#define ITLMEMBASE 0x1C // Tachlite
+#define IIOBASEL 0x10 // Tachyon I/O base address, lower 256 bytes
+#define IIOBASEU 0x14 // Tachyon I/O base address, upper 256 bytes
+#define ITLIOBASEL 0x14 // TachLite I/O base address, lower 256 bytes
+#define ITLIOBASEU 0x18 // TachLite I/O base address, upper 256 bytes
+#define ITLRAMBASE 0x20 // TL on-board RAM start
+#define ISROMBASE 0x24
+#define IROMBASE 0x30
+
+#define ICFGCMD 0x04 // PCI config - PCI config access (word)
+#define ICFGSTAT 0x06 // PCI status (R - word)
+#define IRCTR_WCTR 0x1F2 // ROM control / pre-fetch wait counter
+#define IPCIMCTR 0x1F3 // PCI master control register
+#define IINTPEND 0x1FD // Interrupt pending (I/O Upper - Tachyon & TL)
+#define IINTEN 0x1FE // Interrupt enable (I/O Upper - Tachyon & TL)
+#define IINTSTAT 0x1FF // Interrupt status (I/O Upper - Tachyon & TL)
+
+#define IMQ_BASE 0x80
+#define IMQ_LENGTH 0x84
+#define IMQ_CONSUMER_INDEX 0x88
+#define IMQ_PRODUCER_INDEX 0x8C // Tach copies its INDX to bits 0-7 of value
+
+/*
+// IOBASE UPPER
+#define SFSBQ_BASE 0x00 // single-frame sequences
+#define SFSBQ_LENGTH 0x04
+#define SFSBQ_PRODUCER_INDEX 0x08
+#define SFSBQ_CONSUMER_INDEX 0x0C // (R)
+#define SFS_BUFFER_LENGTH 0X10
+ // SCSI-FCP hardware assists
+#define SEST_BASE 0x40 // SSCI Exchange State Table
+#define SEST_LENGTH 0x44
+#define SCSI_BUFFER_LENGTH 0x48
+#define SEST_LINKED_LIST 0x4C
+
+#define TACHYON_My_ID 0x6C
+#define TACHYON_CONFIGURATION 0x84 // (R/W) reset val 2
+#define TACHYON_CONTROL 0x88
+#define TACHYON_STATUS 0x8C // (R)
+#define TACHYON_FLUSH_SEST 0x90 // (R/W)
+#define TACHYON_EE_CREDIT_TMR 0x94 // (R)
+#define TACHYON_BB_CREDIT_TMR 0x98 // (R)
+#define TACHYON_RCV_FRAME_ERR 0x9C // (R)
+#define FRAME_MANAGER_CONFIG 0xC0 // (R/W)
+#define FRAME_MANAGER_CONTROL 0xC4
+#define FRAME_MANAGER_STATUS 0xC8 // (R)
+#define FRAME_MANAGER_ED_TOV 0xCC
+#define FRAME_MANAGER_LINK_ERR1 0xD0 // (R)
+#define FRAME_MANAGER_LINK_ERR2 0xD4 // (R)
+#define FRAME_MANAGER_TIMEOUT2 0xD8 // (W)
+#define FRAME_MANAGER_BB_CREDIT 0xDC // (R)
+#define FRAME_MANAGER_WWN_HI 0xE0 // (R/W)
+#define FRAME_MANAGER_WWN_LO 0xE4 // (R/W)
+#define FRAME_MANAGER_RCV_AL_PA 0xE8 // (R)
+#define FRAME_MANAGER_PRIMITIVE 0xEC // {K28.5} byte1 byte2 byte3
+*/
+
+#define TL_MEM_ERQ_BASE 0x0 //ERQ Base
+#define TL_IO_ERQ_BASE 0x0 //ERQ base
+
+#define TL_MEM_ERQ_LENGTH 0x4 //ERQ Length
+#define TL_IO_ERQ_LENGTH 0x4 //ERQ Length
+
+#define TL_MEM_ERQ_PRODUCER_INDEX 0x8 //ERQ Producer Index register
+#define TL_IO_ERQ_PRODUCER_INDEX 0x8 //ERQ Producer Index register
+
+#define TL_MEM_ERQ_CONSUMER_INDEX_ADR 0xC //ERQ Consumer Index address register
+#define TL_IO_ERQ_CONSUMER_INDEX_ADR 0xC //ERQ Consumer Index address register
+
+#define TL_MEM_ERQ_CONSUMER_INDEX 0xC //ERQ Consumer Index
+#define TL_IO_ERQ_CONSUMER_INDEX 0xC //ERQ Consumer Index
+
+#define TL_MEM_SFQ_BASE 0x50 //SFQ Base
+#define TL_IO_SFQ_BASE 0x50 //SFQ base
+
+#define TL_MEM_SFQ_LENGTH 0x54 //SFQ Length
+#define TL_IO_SFQ_LENGTH 0x54 //SFQ Length
+
+#define TL_MEM_SFQ_CONSUMER_INDEX 0x58 //SFQ Consumer Index
+#define TL_IO_SFQ_CONSUMER_INDEX 0x58 //SFQ Consumer Index
+
+#define TL_MEM_IMQ_BASE 0x80 //IMQ Base
+#define TL_IO_IMQ_BASE 0x80 //IMQ base
+
+#define TL_MEM_IMQ_LENGTH 0x84 //IMQ Length
+#define TL_IO_IMQ_LENGTH 0x84 //IMQ Length
+
+#define TL_MEM_IMQ_CONSUMER_INDEX 0x88 //IMQ Consumer Index
+#define TL_IO_IMQ_CONSUMER_INDEX 0x88 //IMQ Consumer Index
+
+#define TL_MEM_IMQ_PRODUCER_INDEX_ADR 0x8C //IMQ Producer Index address register
+#define TL_IO_IMQ_PRODUCER_INDEX_ADR 0x8C //IMQ Producer Index address register
+
+#define TL_MEM_SEST_BASE 0x140 //SFQ Base
+#define TL_IO_SEST_BASE 0x40 //SFQ base
+
+#define TL_MEM_SEST_LENGTH 0x144 //SFQ Length
+#define TL_IO_SEST_LENGTH 0x44 //SFQ Length
+
+#define TL_MEM_SEST_LINKED_LIST 0x14C
+
+#define TL_MEM_SEST_SG_PAGE 0x168 // Extended Scatter/Gather page size
+
+#define TL_MEM_TACH_My_ID 0x16C
+#define TL_IO_TACH_My_ID 0x6C //My AL_PA ID
+
+#define TL_MEM_TACH_CONFIG 0x184 //Tachlite Configuration register
+#define TL_IO_CONFIG 0x84 //Tachlite Configuration register
+
+#define TL_MEM_TACH_CONTROL 0x188 //Tachlite Control register
+#define TL_IO_CTR 0x88 //Tachlite Control register
+
+#define TL_MEM_TACH_STATUS 0x18C //Tachlite Status register
+#define TL_IO_STAT 0x8C //Tachlite Status register
+
+#define TL_MEM_FM_CONFIG 0x1C0 //Frame Manager Configuration register
+#define TL_IO_FM_CONFIG 0xC0 //Frame Manager Configuration register
+
+#define TL_MEM_FM_CONTROL 0x1C4 //Frame Manager Control
+#define TL_IO_FM_CTL 0xC4 //Frame Manager Control
+
+#define TL_MEM_FM_STATUS 0x1C8 //Frame Manager Status
+#define TL_IO_FM_STAT 0xC8 //Frame Manager Status
+
+#define TL_MEM_FM_LINK_STAT1 0x1D0 //Frame Manager Link Status 1
+#define TL_IO_FM_LINK_STAT1 0xD0 //Frame Manager Link Status 1
+
+#define TL_MEM_FM_LINK_STAT2 0x1D4 //Frame Manager Link Status 2
+#define TL_IO_FM_LINK_STAT2 0xD4 //Frame Manager Link Status 2
+
+#define TL_MEM_FM_TIMEOUT2 0x1D8 // (W)
+
+#define TL_MEM_FM_BB_CREDIT0 0x1DC
+
+#define TL_MEM_FM_WWN_HI 0x1E0 //Frame Manager World Wide Name High
+#define TL_IO_FM_WWN_HI 0xE0 //Frame Manager World Wide Name High
+
+#define TL_MEM_FM_WWN_LO 0x1E4 //Frame Manager World Wide Name LOW
+#define TL_IO_FM_WWN_LO 0xE4 //Frame Manager World Wide Name Low
+
+#define TL_MEM_FM_RCV_AL_PA 0x1E8 //Frame Manager AL_PA Received register
+#define TL_IO_FM_ALPA 0xE8 //Frame Manager AL_PA Received register
+
+#define TL_MEM_FM_ED_TOV 0x1CC
+
+#define TL_IO_ROMCTR 0xFA //TL PCI ROM Control Register
+#define TL_IO_PCIMCTR 0xFB //TL PCI Master Control Register
+#define TL_IO_SOFTRST 0xFC //Tachlite Configuration register
+#define TL_MEM_SOFTRST 0x1FC //Tachlite Configuration register
+
+// completion message types (bit 8 set means Interrupt generated)
+// CM_Type
+#define OUTBOUND_COMPLETION 0
+#define ERROR_IDLE_COMPLETION 0x01
+#define OUT_HI_PRI_COMPLETION 0x01
+#define INBOUND_MFS_COMPLETION 0x02
+#define INBOUND_000_COMPLETION 0x03
+#define INBOUND_SFS_COMPLETION 0x04 // Tachyon & TachLite
+#define ERQ_FROZEN_COMPLETION 0x06 // TachLite
+#define INBOUND_C1_TIMEOUT 0x05
+#define INBOUND_BUSIED_FRAME 0x06
+#define SFS_BUF_WARN 0x07
+#define FCP_FROZEN_COMPLETION 0x07 // TachLite
+#define MFS_BUF_WARN 0x08
+#define IMQ_BUF_WARN 0x09
+#define FRAME_MGR_INTERRUPT 0x0A
+#define READ_STATUS 0x0B
+#define INBOUND_SCSI_DATA_COMPLETION 0x0C
+#define INBOUND_FCP_XCHG_COMPLETION 0x0C // TachLite
+#define INBOUND_SCSI_DATA_COMMAND 0x0D
+#define BAD_SCSI_FRAME 0x0E
+#define INB_SCSI_STATUS_COMPLETION 0x0F
+#define BUFFER_PROCESSED_COMPLETION 0x11
+
+// FC-AL (Tachyon) Loop Port State Machine defs
+// (loop "Up" states)
+#define MONITORING 0x0
+#define ARBITRATING 0x1
+#define ARBITRAT_WON 0x2
+#define OPEN 0x3
+#define OPENED 0x4
+#define XMITTD_CLOSE 0x5
+#define RCVD_CLOSE 0x6
+#define TRANSFER 0x7
+
+// (loop "Down" states)
+#define INITIALIZING 0x8
+#define O_I_INIT 0x9
+#define O_I_PROTOCOL 0xa
+#define O_I_LIP_RCVD 0xb
+#define HOST_CONTROL 0xc
+#define LOOP_FAIL 0xd
+// (no 0xe)
+#define OLD_PORT 0xf
+
+
+
+#define TACHYON_CHIP_INC
+#endif
+#endif /* CPQFCTSCHIP_H */
diff --git a/drivers/scsi/cpqfcTScontrol.c b/drivers/scsi/cpqfcTScontrol.c
new file mode 100644
index 000000000..bc90b51c3
--- /dev/null
+++ b/drivers/scsi/cpqfcTScontrol.c
@@ -0,0 +1,2200 @@
+/* Copyright 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter
+ * 64-bit, 66MHz PCI
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ...
+ * SP# P225CXCBFIEL6T, Rev XC
+ * SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * 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.
+ * Written by Don Zimmerman
+*/
+/* These functions control the host bus adapter (HBA) hardware. The main chip
+ control takes place in the interrupt handler where we process the IMQ
+ (Inbound Message Queue). The IMQ is Tachyon's way of communicating FC link
+ events and state information to the driver. The Single Frame Queue (SFQ)
+ buffers incoming FC frames for processing by the driver. References to
+ "TL/TS UG" are for:
+ "HP HPFC-5100/5166 Tachyon TL/TS ICs User Guide", August 16, 1999, 1st Ed.
+ Hewlitt Packard Manual Part Number 5968-1083E.
+*/
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h> // request_region() prototype
+#include <linux/sched.h>
+#include <linux/malloc.h> // need "kfree" for ext. S/G pages
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/unistd.h>
+#include <asm/io.h> // struct pt_regs for IRQ handler & Port I/O
+#include <asm/irq.h>
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+#include <asm/spinlock.h>
+#else
+#include <linux/spinlock.h>
+#endif
+
+#include "sd.h"
+#include "hosts.h" // Scsi_Host definition for INT handler
+#include "cpqfcTSchip.h"
+#include "cpqfcTSstructs.h"
+
+//#define IMQ_DEBUG 1
+
+static void fcParseLinkStatusCounters(TACHYON * fcChip);
+static void CpqTsGetSFQEntry(TACHYON * fcChip,
+ USHORT pi, ULONG * buffr, BOOLEAN UpdateChip);
+
+
+// Note special requirements for Q alignment! (TL/TS UG pg. 190)
+// We place critical index pointers at end of QUE elements to assist
+// in non-symbolic (i.e. memory dump) debugging
+// opcode defines placement of Queues (e.g. local/external RAM)
+
+int CpqTsCreateTachLiteQues( void* pHBA, int opcode)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+ int iStatus=0;
+ unsigned long ulAddr;
+
+
+ // NOTE! fcMemManager() will return system virtual addresses.
+ // System (kernel) virtual addresses, though non-paged, still
+ // aren't physical addresses. Convert to PHYSICAL_ADDRESS for Tachyon's
+ // DMA use.
+ ENTER("CreateTachLiteQues");
+
+
+ // Allocate primary EXCHANGES array...
+
+ printk("Allocating %u for %u Exchanges ",
+ (ULONG)sizeof(FC_EXCHANGES), TACH_MAX_XID);
+ fcChip->Exchanges = kmalloc( sizeof( FC_EXCHANGES), GFP_KERNEL );
+ printk("@ %p\n", fcChip->Exchanges);
+
+ if( fcChip->Exchanges == NULL ) // fatal error!!
+ {
+ printk("kmalloc failure on Exchanges: fatal error\n");
+ return -1;
+ }
+ // zero out the entire EXCHANGE space
+ memset( fcChip->Exchanges, 0, sizeof( FC_EXCHANGES));
+
+
+ printk("Allocating %u for LinkQ ", (ULONG)sizeof(FC_LINK_QUE));
+ cpqfcHBAdata->fcLQ = kmalloc( sizeof( FC_LINK_QUE), GFP_KERNEL );
+ printk("@ %p (%u elements)\n", cpqfcHBAdata->fcLQ, FC_LINKQ_DEPTH);
+ memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE));
+
+ if( cpqfcHBAdata->fcLQ == NULL ) // fatal error!!
+ {
+ printk("kmalloc failure on fc Link Que: fatal error\n");
+ return -1;
+ }
+ // zero out the entire EXCHANGE space
+ memset( cpqfcHBAdata->fcLQ, 0, sizeof( FC_LINK_QUE));
+
+
+
+
+ // Verify that basic Tach I/O registers are not NULL
+
+ if( !fcChip->Registers.ReMapMemBase )
+ {
+ printk("HBA base address NULL: fatal error\n");
+ return -1;
+ }
+
+
+ // Initialize the fcMemManager memory pairs (stores allocated/aligned
+ // pairs for future freeing)
+ memset( cpqfcHBAdata->dynamic_mem, 0, sizeof(cpqfcHBAdata->dynamic_mem));
+
+
+ // Allocate Tach's Exchange Request Queue (each ERQ entry 32 bytes)
+
+ fcChip->ERQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+ sizeof( TachLiteERQ ), 32*(ERQ_LEN), 0L );
+ if( !fcChip->ERQ )
+ {
+ printk("kmalloc/alignment failure on ERQ: fatal error\n");
+ return -1;
+ }
+ fcChip->ERQ->length = ERQ_LEN-1;
+ ulAddr = virt_to_bus( fcChip->ERQ);
+#if BITS_PER_LONG > 32
+ if( (ulAddr >> 32) )
+ {
+ printk(" FATAL! ERQ ptr %p exceeds Tachyon's 32-bit register size\n",
+ (void*)ulAddr);
+ return -1; // failed
+ }
+#endif
+ fcChip->ERQ->base = (ULONG)ulAddr; // copy for quick reference
+
+
+ // Allocate Tach's Inbound Message Queue (32 bytes per entry)
+
+ fcChip->IMQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+ sizeof( TachyonIMQ ), 32*(IMQ_LEN), 0L );
+ if( !fcChip->IMQ )
+ {
+ printk("kmalloc/alignment failure on IMQ: fatal error\n");
+ return -1;
+ }
+ fcChip->IMQ->length = IMQ_LEN-1;
+
+ ulAddr = virt_to_bus( fcChip->IMQ);
+#if BITS_PER_LONG > 32
+ if( (ulAddr >> 32) )
+ {
+ printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n",
+ (void*)ulAddr);
+ return -1; // failed
+ }
+#endif
+ fcChip->IMQ->base = (ULONG)ulAddr; // copy for quick reference
+
+
+ // Allocate Tach's Single Frame Queue (64 bytes per entry)
+ fcChip->SFQ = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+ sizeof( TachLiteSFQ ), 64*(SFQ_LEN),0L );
+ if( !fcChip->SFQ )
+ {
+ printk("kmalloc/alignment failure on SFQ: fatal error\n");
+ return -1;
+ }
+ fcChip->SFQ->length = SFQ_LEN-1; // i.e. Que length [# entries -
+ // min. 32; max. 4096 (0xffff)]
+
+ ulAddr = virt_to_bus( fcChip->SFQ);
+#if BITS_PER_LONG > 32
+ if( (ulAddr >> 32) )
+ {
+ printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n",
+ (void*)ulAddr);
+ return -1; // failed
+ }
+#endif
+ fcChip->SFQ->base = (ULONG)ulAddr; // copy for quick reference
+
+
+ // Allocate SCSI Exchange State Table; aligned nearest @sizeof
+ // power-of-2 boundary
+ // LIVE DANGEROUSLY! Assume the boundary for SEST mem will
+ // be on physical page (e.g. 4k) boundary.
+ printk("Allocating %u for TachSEST for %u Exchanges\n",
+ (ULONG)sizeof(TachSEST), TACH_SEST_LEN);
+ fcChip->SEST = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+ sizeof(TachSEST), 4, 0L );
+// sizeof(TachSEST), 64*TACH_SEST_LEN, 0L );
+ if( !fcChip->SEST )
+ {
+ printk("kmalloc/alignment failure on SEST: fatal error\n");
+ return -1;
+ }
+
+ fcChip->SEST->length = TACH_SEST_LEN; // e.g. DON'T subtract one
+ // (TL/TS UG, pg 153)
+
+ ulAddr = virt_to_bus( fcChip->SEST);
+#if BITS_PER_LONG > 32
+ if( (ulAddr >> 32) )
+ {
+ printk(" FATAL! SFQ ptr %p exceeds Tachyon's 32-bit register size\n",
+ (void*)ulAddr);
+ return -1; // failed
+ }
+#endif
+ fcChip->SEST->base = (ULONG)ulAddr; // copy for quick reference
+
+
+ // Now that structures are defined,
+ // fill in Tachyon chip registers...
+
+ // EEEEEEEE EXCHANGE REQUEST QUEUE
+
+ writel( fcChip->ERQ->base,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE));
+
+ writel( fcChip->ERQ->length,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_LENGTH));
+
+
+ fcChip->ERQ->producerIndex = 0L;
+ writel( fcChip->ERQ->producerIndex,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX));
+
+
+ // NOTE! write consumer index last, since the write
+ // causes Tachyon to process the other registers
+
+ ulAddr = virt_to_bus( &fcChip->ERQ->consumerIndex);
+
+ // NOTE! Tachyon DMAs to the ERQ consumer Index host
+ // address; must be correctly aligned
+ writel( (ULONG)ulAddr,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_CONSUMER_INDEX_ADR));
+
+
+
+ // IIIIIIIIIIIII INBOUND MESSAGE QUEUE
+ // Tell Tachyon where the Que starts
+
+ // set the Host's pointer for Tachyon to access
+
+ printk(" cpqfcTS: writing IMQ BASE %Xh ", fcChip->IMQ->base );
+ writel( fcChip->IMQ->base,
+ (fcChip->Registers.ReMapMemBase + IMQ_BASE));
+
+ writel( fcChip->IMQ->length,
+ (fcChip->Registers.ReMapMemBase + IMQ_LENGTH));
+
+ writel( fcChip->IMQ->consumerIndex,
+ (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX));
+
+
+ // NOTE: TachLite DMAs to the producerIndex host address
+ // must be correctly aligned with address bits 1-0 cleared
+ // Writing the BASE register clears the PI register, so write it last
+ ulAddr = virt_to_bus( &fcChip->IMQ->producerIndex);
+#if BITS_PER_LONG > 32
+ if( (ulAddr >> 32) )
+ {
+ printk(" FATAL! IMQ ptr %p exceeds Tachyon's 32-bit register size\n",
+ (void*)ulAddr);
+ return -1; // failed
+ }
+#endif
+//#if DBG
+ printk(" PI %Xh\n", (ULONG)ulAddr );
+//#endif
+ writel( (ULONG)ulAddr,
+ (fcChip->Registers.ReMapMemBase + IMQ_PRODUCER_INDEX));
+
+
+
+ // SSSSSSSSSSSSSSS SINGLE FRAME SEQUENCE
+ // Tell TachLite where the Que starts
+
+ writel( fcChip->SFQ->base,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_BASE));
+
+ writel( fcChip->SFQ->length,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_SFQ_LENGTH));
+
+
+ // tell TachLite where SEST table is & how long
+ writel( fcChip->SEST->base,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE));
+
+ printk(" cpqfcTS: SEST %p(virt): Wrote base %Xh @ %p\n",
+ fcChip->SEST, fcChip->SEST->base,
+ fcChip->Registers.ReMapMemBase + TL_MEM_SEST_BASE);
+
+ writel( fcChip->SEST->length,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_LENGTH));
+
+ writel( (TL_EXT_SG_PAGE_COUNT-1),
+ (fcChip->Registers.ReMapMemBase + TL_MEM_SEST_SG_PAGE));
+
+
+ LEAVE("CreateTachLiteQues");
+
+ return iStatus;
+}
+
+
+
+// function to return TachLite to Power On state
+// 1st - reset tachyon ('SOFT' reset)
+// others - future
+
+int CpqTsResetTachLite(void *pHBA, int type)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ ULONG ulBuff, i;
+ int ret_status=0; // def. success
+
+ ENTER("ResetTach");
+
+ switch(type)
+ {
+
+ case CLEAR_FCPORTS:
+
+ // in case he was running previously, mask Tach's interrupt
+ writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN));
+
+ // de-allocate mem for any Logged in ports
+ // (e.g., our module is unloading)
+ // search the forward linked list, de-allocating
+ // the memory we allocated when the port was initially logged in
+ {
+ PFC_LOGGEDIN_PORT pLoggedInPort = fcChip->fcPorts.pNextPort;
+ PFC_LOGGEDIN_PORT ptr;
+// printk("checking for allocated LoggedInPorts...\n");
+
+ while( pLoggedInPort )
+ {
+ ptr = pLoggedInPort;
+ pLoggedInPort = ptr->pNextPort;
+// printk("kfree(%p) on FC LoggedInPort port_id 0x%06lX\n",
+// ptr, ptr->port_id);
+ kfree( ptr );
+ }
+ }
+ // (continue resetting hardware...)
+
+ case 1: // RESTART Tachyon (power-up state)
+
+ // in case he was running previously, mask Tach's interrupt
+ writeb( 0, (fcChip->Registers.ReMapMemBase + IINTEN));
+ // turn OFF laser (NOTE: laser is turned
+ // off during reset, because GPIO4 is cleared
+ // to 0 by reset action - see TLUM, sec 7.22)
+ // However, CPQ 64-bit HBAs have a "health
+ // circuit" which keeps laser ON for a brief
+ // period after it is turned off ( < 1s)
+
+ fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 0);
+
+
+
+ // soft reset timing constraints require:
+ // 1. set RST to 1
+ // 2. read SOFTRST register
+ // (128 times per R. Callison code)
+ // 3. clear PCI ints
+ // 4. clear RST to 0
+ writel( 0xff000001L,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST));
+
+ for( i=0; i<128; i++)
+ ulBuff = readl( fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST);
+
+ // clear the soft reset
+ for( i=0; i<8; i++)
+ writel( 0, (fcChip->Registers.ReMapMemBase + TL_MEM_SOFTRST));
+
+
+
+ // clear out our copy of Tach regs,
+ // because they must be invalid now,
+ // since TachLite reset all his regs.
+ CpqTsDestroyTachLiteQues(cpqfcHBAdata,0); // remove Host-based Que structs
+ cpqfcTSClearLinkStatusCounters(fcChip); // clear our s/w accumulators
+ // lower bits give GBIC info
+ fcChip->Registers.TYstatus.value =
+ readl( fcChip->Registers.TYstatus.address );
+ break;
+
+/*
+ case 2: // freeze SCSI
+ case 3: // reset Outbound command que (ERQ)
+ case 4: // unfreeze OSM (Outbound Seq. Man.) 'er'
+ case 5: // report status
+
+ break;
+*/
+ default:
+ ret_status = -1; // invalid option passed to RESET function
+ break;
+ }
+ LEAVE("ResetTach");
+ return ret_status;
+}
+
+
+
+
+
+
+// 'addrBase' is IOBaseU for both TachLite and (older) Tachyon
+int CpqTsLaserControl( void* addrBase, int opcode )
+{
+ ULONG dwBuff;
+
+ dwBuff = readl((addrBase + TL_MEM_TACH_CONTROL) ); // read TL Control reg
+ // (change only bit 4)
+ if( opcode == 1)
+ dwBuff |= ~0xffffffefL; // set - ON
+ else
+ dwBuff &= 0xffffffefL; // clear - OFF
+ writel( dwBuff, (addrBase + TL_MEM_TACH_CONTROL)); // write TL Control reg
+ return 0;
+}
+
+
+
+
+
+// Use controller's "Options" field to determine loopback mode (if any)
+// internal loopback (silicon - no GBIC)
+// external loopback (GBIC - no FC loop)
+// no loopback: L_PORT, external cable from GBIC required
+
+int CpqTsInitializeFrameManager( void *pChip, int opcode)
+{
+ PTACHYON fcChip;
+ int iStatus;
+ ULONG wwnLo, wwnHi; // for readback verification
+
+ ENTER("InitializeFrameManager");
+ fcChip = (PTACHYON)pChip;
+ if( !fcChip->Registers.ReMapMemBase ) // undefined controller?
+ return -1;
+
+ // TL/TS UG, pg. 184
+ // 0x0065 = 100ms for RT_TOV
+ // 0x01f5 = 500ms for ED_TOV
+ // 0x07D1 = 2000ms
+ fcChip->Registers.ed_tov.value = 0x006507D1;
+ writel( fcChip->Registers.ed_tov.value,
+ (fcChip->Registers.ed_tov.address));
+
+
+ // Set LP_TOV to the FC-AL2 specified 2 secs.
+ // TL/TS UG, pg. 185
+ writel( 0x07d00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
+
+
+ // Now try to read the WWN from the adapter's NVRAM
+ iStatus = CpqTsReadWriteWWN( fcChip, 1); // '1' for READ
+
+ if( iStatus ) // NVRAM read failed?
+ {
+ printk(" WARNING! HBA NVRAM WWN read failed - make alias\n");
+ // make up a WWN. If NULL or duplicated on loop, FC loop may hang!
+
+
+ fcChip->Registers.wwn_hi = (__u32)jiffies;
+ fcChip->Registers.wwn_hi |= 0x50000000L;
+ fcChip->Registers.wwn_lo = 0x44556677L;
+ }
+
+
+ writel( fcChip->Registers.wwn_hi,
+ fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI);
+
+ writel( fcChip->Registers.wwn_lo,
+ fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO);
+
+
+ // readback for verification:
+ wwnHi = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_HI );
+
+ wwnLo = readl( fcChip->Registers.ReMapMemBase + TL_MEM_FM_WWN_LO);
+ // test for correct chip register WRITE/READ
+ DEBUG_PCI( printk(" WWN %08X%08X\n",
+ fcChip->Registers.wwn_hi, fcChip->Registers.wwn_lo ) );
+
+ if( wwnHi != fcChip->Registers.wwn_hi ||
+ wwnLo != fcChip->Registers.wwn_lo )
+ {
+ printk( "cpqfcTS: WorldWideName register load failed\n");
+ return -1; // FAILED!
+ }
+
+
+
+ // set Frame Manager Initialize command
+ fcChip->Registers.FMcontrol.value = 0x06;
+
+ // Note: for test/debug purposes, we may use "Hard" address,
+ // but we completely support "soft" addressing, including
+ // dynamically changing our address.
+ if( fcChip->Options.intLoopback == 1 ) // internal loopback
+ fcChip->Registers.FMconfig.value = 0x0f002080L;
+ else if( fcChip->Options.extLoopback == 1 ) // internal loopback
+ fcChip->Registers.FMconfig.value = 0x0f004080L;
+ else // L_Port
+ fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start)
+// fcChip->Registers.FMconfig.value = 0x01000080L; // soft address (can't pick)
+// fcChip->Registers.FMconfig.value = 0x55000100L; // hard address (55h start)
+
+ // write config to FM
+
+ if( !fcChip->Options.intLoopback && !fcChip->Options.extLoopback )
+ // (also need LASER for real LOOP)
+ fcChip->LaserControl( fcChip->Registers.ReMapMemBase, 1); // turn on LASER
+
+ writel( fcChip->Registers.FMconfig.value,
+ fcChip->Registers.FMconfig.address);
+
+
+ // issue INITIALIZE command to FM - ACTION!
+ writel( fcChip->Registers.FMcontrol.value,
+ fcChip->Registers.FMcontrol.address);
+
+ LEAVE("InitializeFrameManager");
+
+ return 0;
+}
+
+
+
+
+
+// This "look ahead" function examines the IMQ for occurence of
+// "type". Returns 1 if found, 0 if not.
+static int PeekIMQEntry( PTACHYON fcChip, ULONG type)
+{
+ ULONG CI = fcChip->IMQ->consumerIndex;
+ ULONG PI = fcChip->IMQ->producerIndex; // snapshot of IMQ indexes
+
+ while( CI != PI )
+ { // proceed with search
+ if( (++CI) >= IMQ_LEN ) CI = 0; // rollover check
+
+ switch( type )
+ {
+ case ELS_LILP_FRAME:
+ {
+ // first, we need to find an Inbound Completion message,
+ // If we find it, check the incoming frame payload (1st word)
+ // for LILP frame
+ if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x104 )
+ {
+ TachFCHDR_GCMND* fchs;
+ ULONG ulFibreFrame[2048/4]; // max DWORDS in incoming FC Frame
+ USHORT SFQpi = (USHORT)(fcChip->IMQ->QEntry[CI].word[0] & 0x0fffL);
+
+ CpqTsGetSFQEntry( fcChip,
+ SFQpi, // SFQ producer ndx
+ ulFibreFrame, // contiguous dest. buffer
+ FALSE); // DON'T update chip--this is a "lookahead"
+
+ fchs = (TachFCHDR_GCMND*)&ulFibreFrame;
+ if( fchs->pl[0] == ELS_LILP_FRAME)
+ {
+ return 1; // found the LILP frame!
+ }
+ else
+ {
+ // keep looking...
+ }
+ }
+ }
+ break;
+
+ case OUTBOUND_COMPLETION:
+ if( (fcChip->IMQ->QEntry[CI].type & 0x1FF) == 0x00 )
+ {
+
+ // any OCM errors?
+ if( fcChip->IMQ->QEntry[CI].word[2] & 0x7a000000L )
+ return 1; // found OCM error
+ }
+ break;
+
+
+
+ default:
+ break;
+ }
+ }
+ return 0; // failed to find "type"
+}
+
+
+static void SetTachTOV( CPQFCHBA* cpqfcHBAdata)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+ // TL/TS UG, pg. 184
+ // 0x0065 = 100ms for RT_TOV
+ // 0x01f5 = 500ms for ED_TOV
+ // 0x07d1 = 2000ms for ED_TOV
+
+ // SANMark Level 1 requires an "initialization backoff"
+ // (See "SANMark Test Suite Level 1":
+ // initialization_timeout.fcal.SANMark-1.fc)
+ // We have to use 2sec, 24sec, then 128sec when login/
+ // port discovery processes fail to complete.
+
+ // when port discovery completes (logins done), we set
+ // ED_TOV to 500ms -- this is the normal operational case
+ // On the first Link Down, we'll move to 2 secs (7D1 ms)
+ if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x1f5)
+ fcChip->Registers.ed_tov.value = 0x006507D1;
+
+ // If we get another LST after we moved TOV to 2 sec,
+ // increase to 24 seconds (5DC1 ms) per SANMark!
+ else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x7D1)
+ fcChip->Registers.ed_tov.value = 0x00655DC1;
+
+ // If we get still another LST, set the max TOV (Tachyon
+ // has only 16 bits for ms timer, so the max is 65.5 sec)
+ else if( (fcChip->Registers.ed_tov.value &0xFFFF) <= 0x5DC1)
+ fcChip->Registers.ed_tov.value = 0x0065FFFF;
+
+ writel( fcChip->Registers.ed_tov.value,
+ (fcChip->Registers.ed_tov.address));
+ // keep the same 2sec LP_TOV
+ writel( 0x07D00010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
+}
+
+
+// The IMQ is an array with IMQ_LEN length, each element (QEntry)
+// with eight 32-bit words. Tachyon PRODUCES a QEntry with each
+// message it wants to send to the host. The host CONSUMES IMQ entries
+
+// This function copies the current
+// (or oldest not-yet-processed) QEntry to
+// the caller, clears/ re-enables the interrupt, and updates the
+// (Host) Consumer Index.
+// Return value:
+// 0 message processed, none remain (producer and consumer
+// indexes match)
+// 1 message processed, more messages remain
+// -1 no message processed - none were available to process
+// Remarks:
+// TL/TS UG specifices that the following actions for
+// INTA_L handling:
+// 1. read PCI Interrupt Status register (0xff)
+// 2. all IMQ messages should be processed before writing the
+// IMQ consumer index.
+
+
+int CpqTsProcessIMQEntry(void *host)
+{
+ struct Scsi_Host *HostAdapter = (struct Scsi_Host *)host;
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ int iStatus;
+ USHORT i, RPCset, DPCset;
+ ULONG x_ID;
+ ULONG ulBuff, dwStatus;
+ TachFCHDR_GCMND* fchs;
+ ULONG ulFibreFrame[2048/4]; // max number of DWORDS in incoming Fibre Frame
+ UCHAR ucInboundMessageType; // Inbound CM, dword 3 "type" field
+
+ ENTER("ProcessIMQEntry");
+
+
+ // check TachLite's IMQ producer index -
+ // is a new message waiting for us?
+ // equal indexes means empty que
+
+ if( fcChip->IMQ->producerIndex != fcChip->IMQ->consumerIndex )
+ { // need to process message
+
+
+#ifdef IMQ_DEBUG
+ printk("PI %X, CI %X type: %X\n",
+ fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex,
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type);
+#endif
+ // Examine Completion Messages in IMQ
+ // what CM_Type?
+ switch( (UCHAR)(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].type
+ & 0xffL) )
+ {
+ case OUTBOUND_COMPLETION:
+
+ // Remarks:
+ // x_IDs (OX_ID, RX_ID) are partitioned by SEST entries
+ // (starting at 0), and SFS entries (starting at
+ // SEST_LEN -- outside the SEST space).
+ // Psuedo code:
+ // x_ID (OX_ID or RX_ID) from message is Trans_ID or SEST index
+ // range check - x_ID
+ // if x_ID outside 'Transactions' length, error - exit
+ // if any OCM error, copy error status to Exchange slot
+ // if FCP ASSIST transaction (x_ID within SEST),
+ // call fcComplete (to App)
+ // ...
+
+
+ ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1];
+ x_ID = ulBuff & 0x7fffL; // lower 14 bits SEST_Index/Trans_ID
+ // Range check CM OX/RX_ID value...
+ if( x_ID < TACH_MAX_XID ) // don't go beyond array space
+ {
+
+
+ if( ulBuff & 0x20000000L ) // RPC -Response Phase Complete?
+ RPCset = 1; // (SEST transactions only)
+ else
+ RPCset = 0;
+
+ if( ulBuff & 0x40000000L ) // DPC -Data Phase Complete?
+ DPCset = 1; // (SEST transactions only)
+ else
+ DPCset = 0;
+ // set the status for this Outbound transaction's ID
+ dwStatus = 0L;
+ if( ulBuff & 0x10000000L ) // SPE? (SEST Programming Error)
+ dwStatus |= SESTPROG_ERR;
+
+ ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2];
+ if( ulBuff & 0x7a000000L ) // any other errs?
+ {
+ if( ulBuff & 0x40000000L )
+ dwStatus |= INV_ENTRY;
+ if( ulBuff & 0x20000000L )
+ dwStatus |= FRAME_TO; // FTO
+ if( ulBuff & 0x10000000L )
+ dwStatus |= HOSTPROG_ERR;
+ if( ulBuff & 0x08000000L )
+ dwStatus |= LINKFAIL_TX;
+ if( ulBuff & 0x02000000L )
+ dwStatus |= ABORTSEQ_NOTIFY; // ASN
+ }
+
+
+ if( dwStatus ) // any errors?
+ {
+ // set the Outbound Completion status
+ Exchanges->fcExchange[ x_ID ].status |= dwStatus;
+
+ // if this Outbound frame was for a SEST entry, automatically
+ // reque it in the case of LINKFAIL (it will restart on PDISC)
+ if( x_ID < TACH_SEST_LEN )
+ {
+
+ printk(" #OCM error %Xh x_ID %X# ",
+ dwStatus, x_ID);
+
+ Exchanges->fcExchange[x_ID].timeOut = 30000; // seconds default
+
+
+ // We Q ABTS for each exchange.
+ // NOTE: We can get FRAME_TO on bad alpa (device gone). Since
+ // bad alpa is reported before FRAME_TO, examine the status
+ // flags to see if the device is removed. If so, DON'T
+ // post an ABTS, since it will be terminated by the bad alpa
+ // message.
+ if( dwStatus & FRAME_TO ) // check for device removed...
+ {
+ if( !(Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED) )
+ {
+ // presumes device is still there: send ABTS.
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID);
+ }
+ }
+ else // Abort all other errors
+ {
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID);
+ }
+
+ // if the HPE bit is set, we have to CLose the LOOP
+ // (see TL/TS UG, pg. 239)
+
+ if( dwStatus &= HOSTPROG_ERR )
+ // set CL bit (see TL/TS UG, pg. 172)
+ writel( 4, fcChip->Registers.FMcontrol.address);
+ }
+ }
+ // NOTE: we don't necessarily care about ALL completion messages...
+ // SCSI resp. complete OR
+ if( ((x_ID < TACH_SEST_LEN) && RPCset)||
+ (x_ID >= TACH_SEST_LEN) ) // non-SCSI command
+ {
+ // exchange done; complete to upper levels with status
+ // (if necessary) and free the exchange slot
+
+
+ if( x_ID >= TACH_SEST_LEN ) // Link Service Outbound frame?
+ // A Request or Reply has been sent
+ { // signal waiting WorkerThread
+
+ up( cpqfcHBAdata->TYOBcomplete); // frame is OUT of Tach
+
+ // WorkerThread will complete Xchng
+ }
+ else // X_ID is for FCP assist (SEST)
+ {
+ // TBD (target mode)
+// fcCompleteExchange( fcChip, x_ID); // TRE completed
+ }
+ }
+ }
+ else // ERROR CONDITION! bogus x_ID in completion message
+ {
+
+ printk(" ProcessIMQ (OBCM) x_id out of range %Xh\n", x_ID);
+
+ }
+
+
+
+ // Load the Frame Manager's error counters. We check them here
+ // because presumably the link is up and healthy enough for the
+ // counters to be meaningful (i.e., don't check them while loop
+ // is initializing).
+ fcChip->Registers.FMLinkStatus1.value = // get TL's counter
+ readl(fcChip->Registers.FMLinkStatus1.address);
+
+ fcChip->Registers.FMLinkStatus2.value = // get TL's counter
+ readl(fcChip->Registers.FMLinkStatus2.address);
+
+
+ fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators
+ break;
+
+
+
+ case ERROR_IDLE_COMPLETION: // TachLite Error Idle...
+
+ // We usually get this when the link goes down during heavy traffic.
+ // For now, presume that if SEST Exchanges are open, we will
+ // get this as our cue to INVALIDATE all SEST entries
+ // (and we OWN all the SEST entries).
+ // See TL/TS UG, pg. 53
+
+ for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+ {
+
+ // Does this VALid SEST entry need to be invalidated for Abort?
+ fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF;
+ }
+
+ CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachyon, if Link OK
+
+ break;
+
+
+ case INBOUND_SFS_COMPLETION: //0x04
+ // NOTE! we must process this SFQ message to avoid SFQ filling
+ // up and stopping TachLite. Incoming commands are placed here,
+ // as well as 'unknown' frames (e.g. LIP loop position data)
+ // write this CM's producer index to global...
+ // TL/TS UG, pg 234:
+ // Type: 0 - reserved
+ // 1 - Unassisted FCP
+ // 2 - BAD FCP
+ // 3 - Unkown Frame
+ // 4-F reserved
+
+
+ fcChip->SFQ->producerIndex = (USHORT)
+ (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] & 0x0fffL);
+
+
+ ucInboundMessageType = 0; // default to useless frame
+
+ // we can only process two Types: 1, Unassisted FCP, and 3, Unknown
+ // Also, we aren't interested in processing frame fragments
+ // so don't Que anything with 'LKF' bit set
+ if( !(fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2]
+ & 0x40000000) ) // 'LKF' link failure bit clear?
+ {
+ ucInboundMessageType = (UCHAR) // ICM DWord3, "Type"
+ (fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] & 0x0fL);
+ }
+ else
+ {
+ fcChip->fcStats.linkFailRX++;
+// printk("LKF (link failure) bit set on inbound message\n");
+ }
+
+ // clears SFQ entry from Tachyon buffer; copies to contiguous ulBuff
+ CpqTsGetSFQEntry(
+ fcChip, // i.e. this Device Object
+ (USHORT)fcChip->SFQ->producerIndex, // SFQ producer ndx
+ ulFibreFrame, TRUE); // contiguous destination buffer, update chip
+
+ // analyze the incoming frame outside the INT handler...
+ // (i.e., Worker)
+
+ if( ucInboundMessageType == 1 )
+ {
+ fchs = (TachFCHDR_GCMND*)ulFibreFrame; // cast to examine IB frame
+ // don't fill up our Q with garbage - only accept FCP-CMND
+ // or XRDY frames
+ if( (fchs->d_id & 0xFF000000) == 0x06000000 ) // CMND
+ {
+ // someone sent us a SCSI command
+
+// fcPutScsiQue( cpqfcHBAdata,
+// SFQ_UNASSISTED_FCP, ulFibreFrame);
+ }
+ else if( ((fchs->d_id & 0xFF000000) == 0x07000000) || // RSP (status)
+ (fchs->d_id & 0xFF000000) == 0x05000000 ) // XRDY
+ {
+ ULONG x_ID;
+ // Unfortunately, ABTS requires a Freeze on the chip so
+ // we can modify the shared memory SEST. When frozen,
+ // any received Exchange frames cannot be processed by
+ // Tachyon, so they will be dumped in here. It is too
+ // complex to attempt the reconstruct these frames in
+ // the correct Exchange context, so we simply seek to
+ // find status or transfer ready frames, and cause the
+ // exchange to complete with errors before the timeout
+ // expires. We use a Linux Scsi Cmnd result code that
+ // causes immediate retry.
+
+
+ // Do we have an open exchange that matches this s_id
+ // and ox_id?
+ for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+ {
+ if( (fchs->s_id & 0xFFFFFF) ==
+ (Exchanges->fcExchange[x_ID].fchs.d_id & 0xFFFFFF)
+ &&
+ (fchs->ox_rx_id & 0xFFFF0000) ==
+ (Exchanges->fcExchange[x_ID].fchs.ox_rx_id & 0xFFFF0000) )
+ {
+ // printk(" #R/X frame x_ID %08X# ", fchs->ox_rx_id );
+ // simulate the anticipated error - since the
+ // SEST was frozen, frames were lost...
+ Exchanges->fcExchange[ x_ID ].status |= SFQ_FRAME;
+
+ // presumes device is still there: send ABTS.
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID);
+ break; // done
+ }
+ }
+ }
+
+ }
+
+ else if( ucInboundMessageType == 3)
+ {
+ // FC Link Service frames (e.g. PLOGI, ACC) come in here.
+ cpqfcTSPutLinkQue( cpqfcHBAdata, SFQ_UNKNOWN, ulFibreFrame);
+
+ }
+
+ else if( ucInboundMessageType == 2 ) // "bad FCP"?
+ {
+#ifdef IMQ_DEBUG
+ printk("Bad FCP incoming frame discarded\n");
+#endif
+ }
+
+ else // don't know this type
+ {
+#ifdef IMQ_DEBUG
+ printk("Incoming frame discarded, type: %Xh\n", ucInboundMessageType);
+#endif
+ }
+
+ // Check the Frame Manager's error counters. We check them here
+ // because presumably the link is up and healthy enough for the
+ // counters to be meaningful (i.e., don't check them while loop
+ // is initializing).
+ fcChip->Registers.FMLinkStatus1.value = // get TL's counter
+ readl(fcChip->Registers.FMLinkStatus1.address);
+
+
+ fcChip->Registers.FMLinkStatus2.value = // get TL's counter
+ readl(fcChip->Registers.FMLinkStatus2.address);
+
+
+ break;
+
+
+
+
+ // We get this CM because we issued a freeze
+ // command to stop outbound frames. We issue the
+ // freeze command at Link Up time; when this message
+ // is received, the ERQ base can be switched and PDISC
+ // frames can be sent.
+
+
+ case ERQ_FROZEN_COMPLETION: // note: expect ERQ followed immediately
+ // by FCP when freezing TL
+ fcChip->Registers.TYstatus.value = // read what's frozen
+ readl(fcChip->Registers.TYstatus.address);
+ // (do nothing; wait for FCP frozen message)
+ break;
+ case FCP_FROZEN_COMPLETION:
+
+ fcChip->Registers.TYstatus.value = // read what's frozen
+ readl(fcChip->Registers.TYstatus.address);
+
+ // Signal the kernel thread to proceed with SEST modification
+ up( cpqfcHBAdata->TachFrozen);
+
+ break;
+
+
+
+ case INBOUND_C1_TIMEOUT:
+ case MFS_BUF_WARN:
+ case IMQ_BUF_WARN:
+ break;
+
+
+
+
+
+ // In older Tachyons, we 'clear' the internal 'core' interrupt state
+ // by reading the FMstatus register. In newer TachLite (Tachyon),
+ // we must WRITE the register
+ // to clear the condition (TL/TS UG, pg 179)
+ case FRAME_MGR_INTERRUPT:
+ {
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+
+ fcChip->Registers.FMstatus.value =
+ readl( fcChip->Registers.FMstatus.address );
+
+ // PROBLEM: It is possible, especially with "dumb" hubs that
+ // don't automatically LIP on by-pass of ports that are going
+ // away, for the hub by-pass process to destroy critical
+ // ordered sets of a frame. The result of this is a hung LPSM
+ // (Loop Port State Machine), which on Tachyon results in a
+ // (default 2 sec) Loop State Timeout (LST) FM message. We
+ // want to avoid this relatively huge timeout by detecting
+ // likely scenarios which will result in LST.
+ // To do this, we could examine FMstatus for Loss of Synchronization
+ // and/or Elastic Store (ES) errors. Of these, Elastic Store is better
+ // because we get this indication more quickly than the LOS.
+ // Not all ES errors are harmfull, so we don't want to LIP on every
+ // ES. Instead, on every ES, detect whether our LPSM in in one
+ // of the LST states: ARBITRATING, OPEN, OPENED, XMITTED CLOSE,
+ // or RECEIVED CLOSE. (See TL/TS UG, pg. 181)
+ // If any of these LPSM states are detected
+ // in combination with the LIP while LDn is not set,
+ // send an FM init (LIP F7,F7 for loops)!
+ // It is critical to the physical link stability NOT to reset (LIP)
+ // more than absolutely necessary; this is a basic premise of the
+ // SANMark level 1 spec.
+ {
+ ULONG Lpsm = (fcChip->Registers.FMstatus.value & 0xF0) >>4;
+
+ if( (fcChip->Registers.FMstatus.value & 0x400) // ElasticStore?
+ &&
+ !(fcChip->Registers.FMstatus.value & 0x100) // NOT LDn
+ &&
+ !(fcChip->Registers.FMstatus.value & 0x1000)) // NOT LF
+ {
+ if( (Lpsm != 0) || // not MONITORING? or
+ !(Lpsm & 0x8) )// not already offline?
+ {
+ // now check the particular LST states...
+ if( (Lpsm == ARBITRATING) || (Lpsm == OPEN) ||
+ (Lpsm == OPENED) || (Lpsm == XMITTD_CLOSE) ||
+ (Lpsm == RCVD_CLOSE) )
+ {
+ // re-init the loop before it hangs itself!
+ printk(" #req FMinit on E-S: LPSM %Xh# ",Lpsm);
+
+
+ fcChip->fcStats.FMinits++;
+ writel( 6, fcChip->Registers.FMcontrol.address); // LIP
+ }
+ }
+ }
+ else if( fcChip->Registers.FMstatus.value & 0x40000 ) // LST?
+ {
+ printk(" #req FMinit on LST, LPSM %Xh# ",Lpsm);
+
+ fcChip->fcStats.FMinits++;
+ writel( 6, fcChip->Registers.FMcontrol.address); // LIP
+ }
+ }
+
+
+ // clear only the 'interrupting' type bits for this REG read
+ writel( (fcChip->Registers.FMstatus.value & 0xff3fff00L),
+ fcChip->Registers.FMstatus.address);
+
+
+ // copy frame manager status to unused ULONG slot
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0] =
+ fcChip->Registers.FMstatus.value; // (for debugging)
+
+
+ // Load the Frame Manager's error counters. We check them here
+ // because presumably the link is up and healthy enough for the
+ // counters to be meaningful (i.e., don't check them while loop
+ // is initializing).
+ fcChip->Registers.FMLinkStatus1.value = // get TL's counter
+ readl(fcChip->Registers.FMLinkStatus1.address);
+
+ fcChip->Registers.FMLinkStatus2.value = // get TL's counter
+ readl(fcChip->Registers.FMLinkStatus2.address);
+
+ // Get FM BB_Credit Zero Reg - does not clear on READ
+ fcChip->Registers.FMBB_CreditZero.value = // get TL's counter
+ readl(fcChip->Registers.FMBB_CreditZero.address);
+
+
+
+ fcParseLinkStatusCounters( fcChip); // load into 6 s/w accumulators
+
+
+ // LINK DOWN
+
+ if( fcChip->Registers.FMstatus.value & 0x100L ) // Link DOWN bit
+ {
+
+#ifdef IMQ_DEBUG
+ printk("LinkDn\n");
+#endif
+ printk(" #LDn# ");
+
+ fcChip->fcStats.linkDown++;
+
+ SetTachTOV( cpqfcHBAdata); // must set according to SANMark
+
+ // Check the ERQ - force it to be "empty" to prevent Tach
+ // from sending out frames before we do logins.
+
+
+ if( fcChip->ERQ->producerIndex != fcChip->ERQ->consumerIndex)
+ {
+// printk("#ERQ PI != CI#");
+ CpqTsFreezeTachlite( fcChip, 1); // freeze ERQ only
+ fcChip->ERQ->producerIndex = fcChip->ERQ->consumerIndex = 0;
+ writel( fcChip->ERQ->base,
+ (fcChip->Registers.ReMapMemBase + TL_MEM_ERQ_BASE));
+ // re-writing base forces ERQ PI to equal CI
+
+ }
+
+ // link down transition occurred -- port_ids can change
+ // on next LinkUp, so we must invalidate current logins
+ // (and any I/O in progress) until PDISC or PLOGI/PRLI
+ // completes
+ {
+ pLoggedInPort = &fcChip->fcPorts;
+ while( pLoggedInPort ) // for all ports which are expecting
+ // PDISC after the next LIP, set the
+ // logoutTimer
+ {
+
+ if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec?
+ {
+ pLoggedInPort->LOGO_timer = 3; // we want 2 seconds
+ // but Timer granularity
+ // is 1 second
+ }
+ // suspend any I/O in progress until
+ // PDISC received...
+ pLoggedInPort->prli = FALSE; // block FCP-SCSI commands
+
+ pLoggedInPort = pLoggedInPort->pNextPort;
+ } // ... all Previously known ports checked
+ }
+
+ // since any hot plugging device may NOT support LILP frames
+ // (such as early Tachyon chips), clear this flag indicating
+ // we shouldn't use (our copy of) a LILP map.
+ // If we receive an LILP frame, we'll set it again.
+ fcChip->Options.LILPin = 0; // our LILPmap is invalid
+ cpqfcHBAdata->PortDiscDone = 0; // must re-validate FC ports!
+
+ // also, we want to invalidate (i.e. INITIATOR_ABORT) any
+ // open Login exchanges, in case the LinkDown happened in the
+ // middle of logins. It's possible that some ports already
+ // ACCepted login commands which we have not processed before
+ // another LinkDown occured. Any accepted Login exhanges are
+ // invalidated by LinkDown, even before they are acknowledged.
+ // It's also possible for a port to have a Queued Reply or Request
+ // for login which was interrupted by LinkDown; it may come later,
+ // but it will be unacceptable to us.
+
+ // we must scan the entire exchange space, find every Login type
+ // originated by us, and abort it. This is NOT an abort due to
+ // timeout, so we don't actually send abort to the other port -
+ // we just complete it to free up the fcExchange slot.
+
+ for( i=TACH_SEST_LEN; i< TACH_MAX_XID; i++)
+ { // looking for Extended Link Serv.Exchanges
+ if( Exchanges->fcExchange[i].type == ELS_PDISC ||
+ Exchanges->fcExchange[i].type == ELS_PLOGI ||
+ Exchanges->fcExchange[i].type == ELS_PRLI )
+ {
+ // ABORT the exchange!
+#ifdef IMQ_DEBUG
+ printk("Originator ABORT x_id %Xh, type %Xh, port_id %Xh on LDn\n",
+ i, Exchanges->fcExchange[i].type,
+ Exchanges->fcExchange[i].fchs.d_id);
+#endif
+
+ Exchanges->fcExchange[i].status |= INITIATOR_ABORT;
+ cpqfcTSCompleteExchange( fcChip, i); // abort on LDn
+ }
+ }
+
+ }
+
+ // ################ LINK UP ##################
+ if( fcChip->Registers.FMstatus.value & 0x200L ) // Link Up bit
+ { // AL_PA could have changed
+
+ // We need the following code, duplicated from LinkDn condition,
+ // because it's possible for the Tachyon to re-initialize (hard
+ // reset) without ever getting a LinkDn indication.
+ pLoggedInPort = &fcChip->fcPorts;
+ while( pLoggedInPort ) // for all ports which are expecting
+ // PDISC after the next LIP, set the
+ // logoutTimer
+ {
+ if( pLoggedInPort->pdisc) // expecting PDISC within 2 sec?
+ {
+ pLoggedInPort->LOGO_timer = 3; // we want 2 seconds
+ // but Timer granularity
+ // is 1 second
+
+ // suspend any I/O in progress until
+ // PDISC received...
+
+ }
+ pLoggedInPort = pLoggedInPort->pNextPort;
+ } // ... all Previously known ports checked
+
+ // CpqTs acquired AL_PA in register AL_PA (ACQ_ALPA)
+ fcChip->Registers.rcv_al_pa.value =
+ readl(fcChip->Registers.rcv_al_pa.address);
+
+ // Now, if our acquired address is DIFFERENT from our
+ // previous one, we are not allow to do PDISC - we
+ // must go back to PLOGI, which will terminate I/O in
+ // progress for ALL logged in FC devices...
+ // (This is highly unlikely).
+
+ if( (fcChip->Registers.my_al_pa & 0xFF) !=
+ ((fcChip->Registers.rcv_al_pa.value >> 16) &0xFF) )
+ {
+
+// printk(" #our HBA port_id changed!# "); // FC port_id changed!!
+
+ pLoggedInPort = &fcChip->fcPorts;
+ while( pLoggedInPort ) // for all ports which are expecting
+ // PDISC after the next LIP, set the
+ // logoutTimer
+ {
+ pLoggedInPort->pdisc = FALSE;
+ pLoggedInPort->prli = FALSE;
+ pLoggedInPort = pLoggedInPort->pNextPort;
+ } // ... all Previously known ports checked
+
+ // when the port_id changes, we must terminate
+ // all open exchanges.
+ cpqfcTSTerminateExchange( cpqfcHBAdata, NULL, PORTID_CHANGED);
+
+ }
+
+ // Replace the entire 24-bit port_id. We only know the
+ // lower 8 bits (alpa) from Tachyon; if a FLOGI is done,
+ // we'll get the upper 16-bits from the FLOGI ACC frame.
+ // If someone plugs into Fabric switch, we'll do FLOGI and
+ // get full 24-bit port_id; someone could then remove and
+ // hot-plug us into a dumb hub. If we send a 24-bit PLOGI
+ // to a "private" loop device, it might blow up.
+ // Consequently, we force the upper 16-bits of port_id to
+ // be re-set on every LinkUp transition
+ fcChip->Registers.my_al_pa =
+ (fcChip->Registers.rcv_al_pa.value >> 16) & 0xFF;
+
+
+ // copy frame manager status to unused ULONG slot
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] =
+ fcChip->Registers.my_al_pa; // (for debugging)
+
+ // for TachLite, we need to write the acquired al_pa
+ // back into the FMconfig register, because after
+ // first initialization, the AQ (prev. acq.) bit gets
+ // set, causing TL FM to use the AL_PA field in FMconfig.
+ // (In Tachyon, FM writes the acquired AL_PA for us.)
+ ulBuff = readl( fcChip->Registers.FMconfig.address);
+ ulBuff &= 0x00ffffffL; // mask out current al_pa
+ ulBuff |= ( fcChip->Registers.my_al_pa << 24 ); // or in acq. al_pa
+ fcChip->Registers.FMconfig.value = ulBuff; // copy it back
+ writel( fcChip->Registers.FMconfig.value, // put in TachLite
+ fcChip->Registers.FMconfig.address);
+
+
+#ifdef IMQ_DEBUG
+ printk("#LUp %Xh, FMstat 0x%08X#",
+ fcChip->Registers.my_al_pa, fcChip->Registers.FMstatus.value);
+#endif
+
+ // also set the WRITE-ONLY My_ID Register (for Fabric
+ // initialization)
+ writel( fcChip->Registers.my_al_pa,
+ fcChip->Registers.ReMapMemBase +TL_MEM_TACH_My_ID);
+
+
+ fcChip->fcStats.linkUp++;
+
+ // reset TL statistics counters
+ // (we ignore these error counters
+ // while link is down)
+ ulBuff = // just reset TL's counter
+ readl( fcChip->Registers.FMLinkStatus1.address);
+
+ ulBuff = // just reset TL's counter
+ readl( fcChip->Registers.FMLinkStatus2.address);
+
+ // for initiator, need to start verifying ports (e.g. PDISC)
+
+
+
+
+
+
+ CpqTsUnFreezeTachlite( fcChip, 2); // unfreeze Tachlite, if Link OK
+
+ // Tachyon creates an interesting problem for us on LILP frames.
+ // Instead of writing the incoming LILP frame into the SFQ before
+ // indicating LINK UP (the actual order of events), Tachyon tells
+ // us LINK UP, and later us the LILP. So we delay, then examine the
+ // IMQ for an Inbound CM (x04); if found, we can set
+ // LINKACTIVE after processing the LILP. Otherwise, just proceed.
+ // Since Tachyon imposes this time delay (and doesn't tell us
+ // what it is), we have to impose a delay before "Peeking" the IMQ
+ // for Tach hardware (DMA) delivery.
+ // Processing LILP is required by SANMark
+ udelay( 1000); // microsec delay waiting for LILP (if it comes)
+ if( PeekIMQEntry( fcChip, ELS_LILP_FRAME) )
+ { // found SFQ LILP, which will post LINKACTIVE
+// printk("skipping LINKACTIVE post\n");
+
+ }
+ else
+ cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, ulFibreFrame);
+ }
+
+
+
+ // ******* Set Fabric Login indication ********
+ if( fcChip->Registers.FMstatus.value & 0x2000 )
+ {
+ printk(" #Fabric# ");
+ fcChip->Options.fabric = 1;
+ }
+ else
+ fcChip->Options.fabric = 0;
+
+
+
+ // ******* LIP(F8,x) or BAD AL_PA? ********
+ if( fcChip->Registers.FMstatus.value & 0x30000L )
+ {
+ // copy the error AL_PAs
+ fcChip->Registers.rcv_al_pa.value =
+ readl(fcChip->Registers.rcv_al_pa.address);
+
+ // Bad AL_PA?
+ if( fcChip->Registers.FMstatus.value & 0x10000L )
+ {
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+
+ // copy "BAD" al_pa field
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1] =
+ (fcChip->Registers.rcv_al_pa.value & 0xff00L) >> 8;
+
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ NULL, // DON'T search Scsi Nexus
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1], // port id
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ if( pLoggedInPort )
+ {
+ // Just in case we got this BAD_ALPA because a device
+ // quietly disappeared (can happen on non-managed hubs such
+ // as the Vixel Rapport 1000),
+ // do an Implicit Logout. We never expect this on a Logged
+ // in port (but do expect it on port discovery).
+ // (As a reasonable alternative, this could be changed to
+ // simply start the implicit logout timer, giving the device
+ // several seconds to "come back".)
+ //
+ printk(" #BAD alpa %Xh# ",
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[1]);
+ cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort);
+ }
+ }
+ // LIP(f8,x)?
+ if( fcChip->Registers.FMstatus.value & 0x20000L )
+ {
+ // for debugging, copy al_pa field
+ fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[2] =
+ (fcChip->Registers.rcv_al_pa.value & 0xffL);
+ // get the other port's al_pa
+ // (one that sent LIP(F8,?) )
+ }
+ }
+
+ // Elastic store err
+ if( fcChip->Registers.FMstatus.value & 0x400L )
+ {
+ // don't count e-s if loop is down!
+ if( !(USHORT)(fcChip->Registers.FMstatus.value & 0x80) )
+ fcChip->fcStats.e_stores++;
+
+ }
+ }
+ break;
+
+
+ case INBOUND_FCP_XCHG_COMPLETION: // 0x0C
+
+ // Remarks:
+ // On Tachlite TL/TS, we get this message when the data phase
+ // of a SEST inbound transfer is complete. For example, if a WRITE command
+ // was received with OX_ID 0, we might respond with XFER_RDY with
+ // RX_ID 8001. This would start the SEST controlled data phases. When
+ // all data frames are received, we get this inbound completion. This means
+ // we should send a status frame to complete the status phase of the
+ // FCP-SCSI exchange, using the same OX_ID,RX_ID that we used for data
+ // frames.
+ // See Outbound CM discussion of x_IDs
+ // Psuedo Code
+ // Get SEST index (x_ID)
+ // x_ID out of range, return (err condition)
+ // set status bits from 2nd dword
+ // free transactionID & SEST entry
+ // call fcComplete with transactionID & status
+
+ ulBuff = fcChip->IMQ->QEntry[fcChip->IMQ->consumerIndex].word[0];
+ x_ID = ulBuff & 0x7fffL; // lower 14 bits SEST_Index/Trans_ID
+ // (mask out MSB "direction" bit)
+ // Range check CM OX/RX_ID value...
+ if( x_ID < TACH_SEST_LEN ) // don't go beyond SEST array space
+ {
+
+//#define FCP_COMPLETION_DBG 1
+#ifdef FCP_COMPLETION_DBG
+ printk(" FCP_CM x_ID %Xh, status %Xh, Cmnd %p\n",
+ x_ID, ulBuff, Exchanges->fcExchange[x_ID].Cmnd);
+#endif
+ if( ulBuff & 0x08000000L ) // RPC -Response Phase Complete - or -
+ // time to send response frame?
+ RPCset = 1; // (SEST transaction)
+ else
+ RPCset = 0;
+ // set the status for this Inbound SCSI transaction's ID
+ dwStatus = 0L;
+ if( ulBuff & 0x70000000L ) // any errs?
+ {
+
+ if( ulBuff & 0x40000000L )
+ dwStatus |= LINKFAIL_RX;
+
+ if( ulBuff & 0x20000000L )
+ dwStatus |= COUNT_ERROR;
+
+ if( ulBuff & 0x10000000L )
+ dwStatus |= OVERFLOW;
+ }
+
+
+ // FCP transaction done - copy status
+ Exchanges->fcExchange[ x_ID ].status = dwStatus;
+
+
+ // Did the exchange get an FCP-RSP response frame?
+ // (Note the little endian/big endian FC payload difference)
+
+ if( RPCset ) // SEST transaction Response frame rec'd
+ {
+ // complete the command in our driver...
+ cpqfcTSCompleteExchange( fcChip, x_ID);
+
+ } // end "RPCset"
+
+ else // ("target" logic)
+ {
+ // Tachlite says all data frames have been received - now it's time
+ // to analyze data transfer (successful?), then send a response
+ // frame for this exchange
+
+ ulFibreFrame[0] = x_ID; // copy for later reference
+
+ // if this was a TWE, we have to send satus response
+ if( Exchanges->fcExchange[ x_ID].type == SCSI_TWE )
+ {
+// fcPutScsiQue( cpqfcHBAdata,
+// NEED_FCP_RSP, ulFibreFrame); // (ulFibreFrame not used here)
+ }
+ }
+ }
+ else // ERROR CONDITION! bogus x_ID in completion message
+ {
+ printk("IN FCP_XCHG: bad x_ID: %Xh\n", x_ID);
+ }
+
+ break;
+
+
+
+
+ case INBOUND_SCSI_DATA_COMMAND:
+ case BAD_SCSI_FRAME:
+ case INB_SCSI_STATUS_COMPLETION:
+ case BUFFER_PROCESSED_COMPLETION:
+ break;
+ }
+
+ // Tachyon is producing;
+ // we are consuming
+ fcChip->IMQ->consumerIndex++; // increment OUR consumerIndex
+ if( fcChip->IMQ->consumerIndex >= IMQ_LEN)// check for rollover
+ fcChip->IMQ->consumerIndex = 0L; // reset it
+
+
+ if( fcChip->IMQ->producerIndex == fcChip->IMQ->consumerIndex )
+ { // all Messages are processed -
+ iStatus = 0; // no more messages to process
+
+ }
+ else
+ iStatus = 1; // more messages to process
+
+ // update TachLite's ConsumerIndex... (clears INTA_L)
+ // NOTE: according to TL/TS UG, the
+ // "host must return completion messages in sequential order".
+ // Does this mean one at a time, in the order received? We
+ // presume so.
+
+ writel( fcChip->IMQ->consumerIndex,
+ (fcChip->Registers.ReMapMemBase + IMQ_CONSUMER_INDEX));
+
+#if IMQ_DEBUG
+ printk("Process IMQ: writing consumer ndx %d\n ",
+ fcChip->IMQ->consumerIndex);
+ printk("PI %X, CI %X\n",
+ fcChip->IMQ->producerIndex,fcChip->IMQ->consumerIndex );
+#endif
+
+
+
+ }
+ else
+ {
+ // hmmm... why did we get interrupted/called with no message?
+ iStatus = -1; // nothing to process
+#if IMQ_DEBUG
+ printk("Process IMQ: no message PI %Xh CI %Xh",
+ fcChip->IMQ->producerIndex,
+ fcChip->IMQ->consumerIndex);
+#endif
+ }
+
+ LEAVE("ProcessIMQEntry");
+
+ return iStatus;
+}
+
+
+
+
+
+// This routine initializes Tachyon according to the following
+// options (opcode1):
+// 1 - RESTART Tachyon, simulate power on condition by shutting
+// down laser, resetting the hardware, de-allocating all buffers;
+// continue
+// 2 - Config Tachyon / PCI registers;
+// continue
+// 3 - Allocating memory and setting Tachyon queues (write Tachyon regs);
+// continue
+// 4 - Config frame manager registers, initialize, turn on laser
+//
+// Returns:
+// -1 on fatal error
+// 0 on success
+
+int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ ULONG ulBuff;
+ UCHAR bBuff;
+ int iStatus=-1; // assume failure
+
+ ENTER("InitializeTachLite");
+
+ // verify board's base address (sanity check)
+
+ if( !fcChip->Registers.ReMapMemBase) // NULL address for card?
+ return -1; // FATAL error!
+
+
+
+ switch( opcode1 )
+ {
+ case 1: // restore hardware to power-on (hard) restart
+
+
+ iStatus = fcChip->ResetTachyon(
+ cpqfcHBAdata, opcode2); // laser off, reset hardware
+ // de-allocate aligned buffers
+
+
+/* TBD // reset FC link Q (producer and consumer = 0)
+ fcLinkQReset(cpqfcHBAdata);
+
+*/
+
+ if( iStatus )
+ break;
+
+ case 2: // Config PCI/Tachyon registers
+ // NOTE: For Tach TL/TS, bit 31 must be set to 1. For TS chips, a read
+ // of bit 31 indicates state of M66EN signal; if 1, chip may run at
+ // 33-66MHz (see TL/TS UG, pg 159)
+
+ ulBuff = 0x80000000; // TachLite Configuration Register
+
+ writel( ulBuff, fcChip->Registers.TYconfig.address);
+// ulBuff = 0x0147L; // CpqTs PCI CFGCMD register
+// WritePCIConfiguration( fcChip->Backplane.bus,
+// fcChip->Backplane.slot, TLCFGCMD, ulBuff, 4);
+// ulBuff = 0x0L; // test!
+// ReadPCIConfiguration( fcChip->Backplane.bus,
+// fcChip->Backplane.slot, TLCFGCMD, &ulBuff, 4);
+
+ // read back for reference...
+ fcChip->Registers.TYconfig.value =
+ readl( fcChip->Registers.TYconfig.address );
+
+ // what is the PCI bus width?
+ pci_read_config_byte( cpqfcHBAdata->PciDev,
+ 0x43, // PCIMCTR offset
+ &bBuff);
+
+ fcChip->Registers.PCIMCTR = bBuff;
+
+ // set string identifying the chip on the circuit board
+
+ fcChip->Registers.TYstatus.value =
+ readl( fcChip->Registers.TYstatus.address);
+
+ {
+// Now that we are supporting multiple boards, we need to change
+// this logic to check for PCI vendor/device IDs...
+// for now, quick & dirty is simply checking Chip rev
+
+ ULONG RevId = (fcChip->Registers.TYstatus.value &0x3E0)>>5;
+ UCHAR Minor = (UCHAR)(RevId & 0x3);
+ UCHAR Major = (UCHAR)((RevId & 0x1C) >>2);
+
+ printk(" HBA Tachyon RevId %d.%d\n", Major, Minor);
+ if( (Major == 1) && (Minor == 2) )
+ {
+ sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS12);
+
+ }
+ else if( (Major == 1) && (Minor == 3) )
+ {
+ sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE66_TS13);
+ }
+ else if( (Major == 2) && (Minor == 1) )
+ {
+ sprintf( cpqfcHBAdata->fcChip.Name, SAGILENT_XL2_21);
+ }
+ else
+ sprintf( cpqfcHBAdata->fcChip.Name, STACHLITE_UNKNOWN);
+ }
+
+
+
+ case 3: // allocate mem, set Tachyon Que registers
+ iStatus = CpqTsCreateTachLiteQues( cpqfcHBAdata, opcode2);
+
+ // now that the Queues exist, Tach can DMA to them, so
+ // we can begin processing INTs
+ // INTEN register - enable INT (TachLite interrupt)
+ writeb( 0x1F, fcChip->Registers.ReMapMemBase + IINTEN);
+
+
+ if( iStatus )
+ break;
+
+
+ case 4: // Config Fame Manager, Init Loop Command, laser on
+
+ // L_PORT or loopback
+ // depending on Options
+ iStatus = CpqTsInitializeFrameManager( fcChip,0 );
+ if( iStatus )
+ {
+ // failed to initialize Frame Manager
+ break;
+ }
+
+ default:
+ break;
+ }
+ LEAVE("InitializeTachLite");
+
+ return iStatus;
+}
+
+
+
+
+// Depending on the type of platform memory allocation (e.g. dynamic),
+// it's probably best to free memory in opposite order as it was allocated.
+// Order of allocation: see other function
+
+
+int CpqTsDestroyTachLiteQues( void *pHBA, int opcode)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA*)pHBA;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ USHORT i, j, iStatus=0;
+ void* vPtr; // mem Align manager sets this to the freed address on success
+ unsigned long ulPtr; // for 64-bit pointer cast (e.g. Alpa machine)
+
+ ENTER("DestroyTachLiteQues");
+
+ if( fcChip->SEST )
+ {
+ // search out and free Pool for Extended S/G list pages
+
+ for( i=0, j=0; i < TACH_SEST_LEN; i++, j=0) // for each exchange
+ {
+ // It's possible that extended S/G pages were allocated and
+ // not cleared due to error conditions or O/S driver termination.
+ // Make sure they're all gone.
+ while( fcChip->SEST->sgPages[i].PoolPage[j] &&
+ (j < TL_MAX_SGPAGES))
+ kfree( fcChip->SEST->sgPages[i].PoolPage[j++]);
+
+ }
+ ulPtr = (unsigned long)fcChip->SEST;
+ vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+ 0,0, (ULONG)ulPtr ); // 'free' mem
+ fcChip->SEST = 0L; // null invalid ptr
+ if( !vPtr )
+ {
+ printk("SEST mem not freed\n");
+ iStatus = -1;
+ }
+ }
+
+ if( fcChip->SFQ )
+ {
+
+ ulPtr = (unsigned long)fcChip->SFQ;
+ vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+ 0,0, (ULONG)ulPtr ); // 'free' mem
+ fcChip->SFQ = 0L; // null invalid ptr
+ if( !vPtr )
+ {
+ printk("SFQ mem not freed\n");
+ iStatus = -2;
+ }
+ }
+
+
+ if( fcChip->IMQ )
+ {
+ // clear Indexes to show empty Queue
+ fcChip->IMQ->producerIndex = 0;
+ fcChip->IMQ->consumerIndex = 0;
+
+ ulPtr = (unsigned long)fcChip->IMQ;
+ vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+ 0,0, (ULONG)ulPtr ); // 'free' mem
+ fcChip->IMQ = 0L; // null invalid ptr
+ if( !vPtr )
+ {
+ printk("IMQ mem not freed\n");
+ iStatus = -3;
+ }
+ }
+
+ if( fcChip->ERQ ) // release memory blocks used by the queues
+ {
+ ulPtr = (unsigned long)fcChip->ERQ;
+ vPtr = fcMemManager( &cpqfcHBAdata->dynamic_mem[0],
+ 0,0, (ULONG)ulPtr ); // 'free' mem
+ fcChip->ERQ = 0L; // null invalid ptr
+ if( !vPtr )
+ {
+ printk("ERQ mem not freed\n");
+ iStatus = -4;
+ }
+ }
+
+ // free up the primary EXCHANGES struct
+ if( fcChip->Exchanges != NULL)
+ {
+// printk("kfree() on Exchanges @%p\n", fcChip->Exchanges);
+ kfree( fcChip->Exchanges);
+ }
+
+ // free up Link Q
+ if( cpqfcHBAdata->fcLQ != NULL )
+ {
+// printk("kfree() on LinkQ @%p\n", fcChip->fcLQ);
+ kfree( cpqfcHBAdata->fcLQ);
+ }
+
+ LEAVE("DestroyTachLiteQues");
+
+ return iStatus; // non-zero (failed) if any memory not freed
+}
+
+
+
+
+
+// The SFQ is an array with SFQ_LEN length, each element (QEntry)
+// with eight 32-bit words. TachLite places incoming FC frames (i.e.
+// a valid FC frame with our AL_PA ) in contiguous SFQ entries
+// and sends a completion message telling the host where the frame is
+// in the que.
+// This function copies the current (or oldest not-yet-processed) QEntry to
+// a caller's contiguous buffer and updates the Tachyon chip's consumer index
+//
+// NOTE:
+// An FC frame may consume one or many SFQ entries. We know the total
+// length from the completion message. The caller passes a buffer large
+// enough for the complete message (max 2k).
+
+static void CpqTsGetSFQEntry(
+ PTACHYON fcChip,
+ USHORT producerNdx,
+ ULONG *ulDestPtr, // contiguous destination buffer
+ BOOLEAN UpdateChip)
+{
+ ULONG total_bytes=0;
+ ULONG consumerIndex = fcChip->SFQ->consumerIndex;
+
+ // check passed copy of SFQ producer index -
+ // is a new message waiting for us?
+ // equal indexes means SFS is copied
+
+ while( producerNdx != consumerIndex )
+ { // need to process message
+ total_bytes += 64; // maintain count to prevent writing past buffer
+ // don't allow copies over Fibre Channel defined length!
+ if( total_bytes <= 2048 )
+ {
+ memcpy( ulDestPtr,
+ &fcChip->SFQ->QEntry[consumerIndex],
+ 64 ); // each SFQ entry is 64 bytes
+ ulDestPtr += 16; // advance pointer to next 64 byte block
+ }
+ // Tachyon is producing,
+ // and we are consuming
+
+ if( ++consumerIndex >= SFQ_LEN)// check for rollover
+ consumerIndex = 0L; // reset it
+ }
+
+ // if specified, update the Tachlite chip ConsumerIndex...
+ if( UpdateChip )
+ {
+ fcChip->SFQ->consumerIndex = consumerIndex;
+ writel( fcChip->SFQ->consumerIndex,
+ fcChip->Registers.SFQconsumerIndex.address);
+ }
+}
+
+
+
+// TachLite routinely freezes it's core ques - Outbound FIFO, Inbound FIFO,
+// and Exchange Request Queue (ERQ) on error recover -
+// (e.g. whenever a LIP occurs). Here
+// we routinely RESUME by clearing these bits, but only if the loop is up
+// to avoid ERROR IDLE messages forever.
+
+void CpqTsUnFreezeTachlite( void *pChip, int type )
+{
+ PTACHYON fcChip = (PTACHYON)pChip;
+ fcChip->Registers.TYcontrol.value =
+ readl(fcChip->Registers.TYcontrol.address);
+
+ // (bit 4 of value is GBIC LASER)
+ // if we 'unfreeze' the core machines before the loop is healthy
+ // (i.e. FLT, OS, LS failure bits set in FMstatus)
+ // we can get 'error idle' messages forever. Verify that
+ // FMstatus (Link Status) is OK before unfreezing.
+
+ if( !(fcChip->Registers.FMstatus.value & 0x07000000L) && // bits clear?
+ !(fcChip->Registers.FMstatus.value & 0x80 )) // Active LPSM?
+ {
+ fcChip->Registers.TYcontrol.value &= ~0x300L; // clear FEQ, FFA
+ if( type == 1 ) // unfreeze ERQ only
+ {
+// printk("Unfreezing ERQ\n");
+ fcChip->Registers.TYcontrol.value |= 0x10000L; // set REQ
+ }
+ else // unfreeze both ERQ and FCP-ASSIST (SEST)
+ {
+// printk("Unfreezing ERQ & FCP-ASSIST\n");
+
+ // set ROF, RIF, REQ - resume Outbound FCP, Inbnd FCP, ERQ
+ fcChip->Registers.TYcontrol.value |= 0x70000L; // set ROF, RIF, REQ
+ }
+
+ writel( fcChip->Registers.TYcontrol.value,
+ fcChip->Registers.TYcontrol.address);
+
+ }
+ // readback for verify (TachLite still frozen?)
+ fcChip->Registers.TYstatus.value =
+ readl(fcChip->Registers.TYstatus.address);
+}
+
+
+// Whenever an FC Exchange Abort is required, we must manipulate the
+// Host/Tachyon shared memory SEST table. Before doing this, we
+// must freeze Tachyon, which flushes certain buffers and ensure we
+// can manipulate the SEST without contention.
+// This freeze function will result in FCP & ERQ FROZEN completion
+// messages (per argument "type").
+
+void CpqTsFreezeTachlite( void *pChip, int type )
+{
+ PTACHYON fcChip = (PTACHYON)pChip;
+ fcChip->Registers.TYcontrol.value =
+ readl(fcChip->Registers.TYcontrol.address);
+
+ //set FFA, FEQ - freezes SCSI assist and ERQ
+ if( type == 1) // freeze ERQ only
+ fcChip->Registers.TYcontrol.value |= 0x100L; // (bit 4 is laser)
+ else // freeze both FCP assists (SEST) and ERQ
+ fcChip->Registers.TYcontrol.value |= 0x300L; // (bit 4 is laser)
+
+ writel( fcChip->Registers.TYcontrol.value,
+ fcChip->Registers.TYcontrol.address);
+
+}
+
+
+
+
+// TL has two Frame Manager Link Status Registers, with three 8-bit
+// fields each. These eight bit counters are cleared after each read,
+// so we define six 32-bit accumulators for these TL counters. This
+// function breaks out each 8-bit field and adds the value to the existing
+// sum. (s/w counters cleared independently)
+
+void fcParseLinkStatusCounters(PTACHYON fcChip)
+{
+ UCHAR bBuff;
+ ULONG ulBuff;
+
+
+// The BB0 timer usually increments when TL is initialized, resulting
+// in an initially bogus count. If our own counter is ZERO, it means we
+// are reading this thing for the first time, so we ignore the first count.
+// Also, reading the register does not clear it, so we have to keep an
+// additional static counter to detect rollover (yuk).
+
+ if( fcChip->fcStats.lastBB0timer == 0L) // TL was reset? (ignore 1st values)
+ {
+ // get TL's register counter - the "last" count
+ fcChip->fcStats.lastBB0timer =
+ fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL;
+ }
+ else // subsequent pass - check for rollover
+ {
+ // "this" count
+ ulBuff = fcChip->Registers.FMBB_CreditZero.value & 0x00ffffffL;
+ if( fcChip->fcStats.lastBB0timer > ulBuff ) // rollover happened
+ {
+ // counter advanced to max...
+ fcChip->fcStats.BB0_Timer += (0x00FFFFFFL - fcChip->fcStats.lastBB0timer);
+ fcChip->fcStats.BB0_Timer += ulBuff; // plus some more
+
+
+ }
+ else // no rollover -- more counts or no change
+ {
+ fcChip->fcStats.BB0_Timer += (ulBuff - fcChip->fcStats.lastBB0timer);
+
+ }
+
+ fcChip->fcStats.lastBB0timer = ulBuff;
+ }
+
+
+
+ bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 24);
+ fcChip->fcStats.LossofSignal += bBuff;
+
+ bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 16);
+ fcChip->fcStats.BadRXChar += bBuff;
+
+ bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus1.value >> 8);
+ fcChip->fcStats.LossofSync += bBuff;
+
+
+ bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 24);
+ fcChip->fcStats.Rx_EOFa += bBuff;
+
+ bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 16);
+ fcChip->fcStats.Dis_Frm += bBuff;
+
+ bBuff = (UCHAR)(fcChip->Registers.FMLinkStatus2.value >> 8);
+ fcChip->fcStats.Bad_CRC += bBuff;
+}
+
+
+void cpqfcTSClearLinkStatusCounters(PTACHYON fcChip)
+{
+ ENTER("ClearLinkStatusCounters");
+ memset( &fcChip->fcStats, 0, sizeof( FCSTATS));
+ LEAVE("ClearLinkStatusCounters");
+
+}
+
+
+
+
+// The following function reads the I2C hardware to get the adapter's
+// World Wide Name (WWN).
+// If the WWN is "500805f1fadb43e8" (as printed on the card), the
+// Tachyon WWN_hi (32-bit) register is 500805f1, and WWN_lo register
+// is fadb43e8.
+// In the NVRAM, the bytes appear as:
+// [2d] ..
+// [2e] ..
+// [2f] 50
+// [30] 08
+// [31] 05
+// [32] f1
+// [33] fa
+// [34] db
+// [35] 43
+// [36] e8
+//
+// In the Fibre Channel (Big Endian) format, the FC-AL LISM frame will
+// be correctly loaded by Tachyon silicon. In the login payload, bytes
+// must be correctly swapped for Big Endian format.
+
+int CpqTsReadWriteWWN( PVOID pChip, int Read)
+{
+ PTACHYON fcChip = (PTACHYON)pChip;
+#define NVRAM_SIZE 512
+ unsigned short i, count = NVRAM_SIZE;
+ UCHAR nvRam[NVRAM_SIZE], WWNbuf[8];
+ ULONG ulBuff;
+ int iStatus=-1; // assume failure
+ int WWNoffset;
+
+ ENTER("ReadWriteWWN");
+ // Now try to read the WWN from the adapter's NVRAM
+
+ if( Read ) // READing NVRAM WWN?
+ {
+ ulBuff = cpqfcTS_ReadNVRAM( fcChip->Registers.TYstatus.address,
+ fcChip->Registers.TYcontrol.address,
+ count, &nvRam[0] );
+
+ if( ulBuff ) // NVRAM read successful?
+ {
+ iStatus = 0; // success!
+
+ // for engineering/ prototype boards, the data may be
+ // invalid (GIGO, usually all "FF"); this prevents the
+ // parse routine from working correctly, which means
+ // nothing will be written to our passed buffer.
+
+ WWNoffset = cpqfcTS_GetNVRAM_data( WWNbuf, nvRam );
+
+ if( !WWNoffset ) // uninitialized NVRAM -- copy bytes directly
+ {
+ printk( "CAUTION: Copying NVRAM data on fcChip\n");
+ for( i= 0; i < 8; i++)
+ WWNbuf[i] = nvRam[i +0x2f]; // dangerous! some formats won't work
+ }
+
+ fcChip->Registers.wwn_hi = 0L;
+ fcChip->Registers.wwn_lo = 0L;
+ for( i=0; i<4; i++) // WWN bytes are big endian in NVRAM
+ {
+ ulBuff = 0L;
+ ulBuff = (ULONG)(WWNbuf[i]) << (8 * (3-i));
+ fcChip->Registers.wwn_hi |= ulBuff;
+ }
+ for( i=0; i<4; i++) // WWN bytes are big endian in NVRAM
+ {
+ ulBuff = 0L;
+ ulBuff = (ULONG)(WWNbuf[i+4]) << (8 * (3-i));
+ fcChip->Registers.wwn_lo |= ulBuff;
+ }
+ } // done reading
+ else
+ {
+
+ printk( "cpqfcTS: NVRAM read failed\n");
+
+ }
+ }
+
+ else // WRITE
+ {
+
+ // NOTE: WRITE not supported & not used in released driver.
+
+
+ printk("ReadWriteNRAM: can't write NVRAM; aborting write\n");
+ }
+
+ LEAVE("ReadWriteWWN");
+ return iStatus;
+}
+
+
+
+
+
+// The following function reads or writes the entire "NVRAM" contents of
+// the I2C hardware (i.e. the NM24C03). Note that HP's 5121A (TS 66Mhz)
+// adapter does not use the NM24C03 chip, so this function only works on
+// Compaq's adapters.
+
+int CpqTsReadWriteNVRAM( PVOID pChip, PVOID buf, int Read)
+{
+ PTACHYON fcChip = (PTACHYON)pChip;
+#define NVRAM_SIZE 512
+ ULONG ulBuff;
+ UCHAR *ucPtr = buf; // cast caller's void ptr to UCHAR array
+ int iStatus=-1; // assume failure
+
+
+ if( Read ) // READing NVRAM?
+ {
+ ulBuff = cpqfcTS_ReadNVRAM( // TRUE on success
+ fcChip->Registers.TYstatus.address,
+ fcChip->Registers.TYcontrol.address,
+ 256, // bytes to write
+ ucPtr ); // source ptr
+
+
+ if( ulBuff )
+ iStatus = 0; // success
+ else
+ {
+#ifdef DBG
+ printk( "CAUTION: NVRAM read failed\n");
+#endif
+ }
+ } // done reading
+
+ else // WRITING NVRAM
+ {
+
+ printk("cpqfcTS: WRITE of FC Controller's NVRAM disabled\n");
+ }
+
+ return iStatus;
+}
diff --git a/drivers/scsi/cpqfcTSi2c.c b/drivers/scsi/cpqfcTSi2c.c
new file mode 100644
index 000000000..b38a6a9a5
--- /dev/null
+++ b/drivers/scsi/cpqfcTSi2c.c
@@ -0,0 +1,493 @@
+/* Copyright(c) 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter
+ * 64-bit, 66MHz PCI
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ...
+ * SP# P225CXCBFIEL6T, Rev XC
+ * SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * 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.
+ * Written by Don Zimmerman
+*/
+// These functions control the NVRAM I2C hardware on
+// non-intelligent Fibre Host Adapters.
+// The primary purpose is to read the HBA's NVRAM to get adapter's
+// manufactured WWN to copy into Tachyon chip registers
+// Orignal source author unknown
+
+#include <linux/types.h>
+enum boolean { FALSE, TRUE } ;
+
+
+#ifndef UCHAR
+typedef __u8 UCHAR;
+#endif
+#ifndef BOOLEAN
+typedef __u8 BOOLEAN;
+#endif
+#ifndef USHORT
+typedef __u16 USHORT;
+#endif
+#ifndef ULONG
+typedef __u32 ULONG;
+#endif
+
+
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/io.h> // struct pt_regs for IRQ handler & Port I/O
+
+#include "cpqfcTSchip.h"
+
+static void tl_i2c_tx_byte( void* GPIOout, UCHAR data );
+/*static BOOLEAN tl_write_i2c_page_portion( void* GPIOin, void* GPIOout,
+ USHORT startOffset, // e.g. 0x2f for WWN start
+ USHORT count,
+ UCHAR *buf );
+*/
+
+//
+// Tachlite GPIO2, GPIO3 (I2C) DEFINES
+// The NVRAM chip NM24C03 defines SCL (serial clock) and SDA (serial data)
+// GPIO2 drives SDA, and GPIO3 drives SCL
+//
+// Since Tachlite inverts the state of the GPIO 0-3 outputs, SET writes 0
+// and clear writes 1. The input lines (read in TL status) is NOT inverted
+// This really helps confuse the code and debugging.
+
+#define SET_DATA_HI 0x0
+#define SET_DATA_LO 0x8
+#define SET_CLOCK_HI 0x0
+#define SET_CLOCK_LO 0x4
+
+#define SENSE_DATA_HI 0x8
+#define SENSE_DATA_LO 0x0
+#define SENSE_CLOCK_HI 0x4
+#define SENSE_CLOCK_LO 0x0
+
+#define SLAVE_READ_ADDRESS 0xA1
+#define SLAVE_WRITE_ADDRESS 0xA0
+
+
+static void i2c_delay(ULONG mstime);
+static void tl_i2c_clock_pulse( UCHAR , void* GPIOout);
+static UCHAR tl_read_i2c_data( void* );
+
+
+//-----------------------------------------------------------------------------
+//
+// Name: I2C_RX_ACK
+//
+// This routine receives an acknowledge over the I2C bus.
+//
+//-----------------------------------------------------------------------------
+static unsigned short tl_i2c_rx_ack( void* GPIOin, void* GPIOout )
+{
+ unsigned long value;
+
+ // do clock pulse, let data line float high
+ tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+
+ // slave must drive data low for acknowledge
+ value = tl_read_i2c_data( GPIOin);
+ if (value & SENSE_DATA_HI )
+ return( FALSE );
+
+ return( TRUE );
+}
+//-----------------------------------------------------------------------------
+//
+// Name: READ_I2C_REG
+//
+// This routine reads the I2C control register using the global
+// IO address stored in gpioreg.
+//
+//-----------------------------------------------------------------------------
+static UCHAR tl_read_i2c_data( void* gpioreg )
+{
+ return( (UCHAR)(readl( gpioreg ) & 0x08L) ); // GPIO3
+}
+//-----------------------------------------------------------------------------
+//
+// Name: WRITE_I2C_REG
+//
+// This routine writes the I2C control register using the global
+// IO address stored in gpioreg.
+// In Tachlite, we don't want to modify other bits in TL Control reg.
+//
+//-----------------------------------------------------------------------------
+static void tl_write_i2c_reg( void* gpioregOUT, UCHAR value )
+{
+ ULONG temp;
+
+ // First read the register and clear out the old bits
+ temp = readl( gpioregOUT ) & 0xfffffff3L;
+
+ // Now or in the new data and send it back out
+ writel( temp | value, gpioregOUT);
+}
+//-----------------------------------------------------------------------------
+//
+// Name: I2C_TX_START
+//
+// This routine transmits a start condition over the I2C bus.
+// 1. Set SCL (clock, GPIO2) HIGH, set SDA (data, GPIO3) HIGH,
+// wait 5us to stabilize.
+// 2. With SCL still HIGH, drive SDA low. The low transition marks
+// the start condition to NM24Cxx (the chip)
+// NOTE! In TL control reg., output 1 means chip sees LOW
+//
+//-----------------------------------------------------------------------------
+static unsigned short tl_i2c_tx_start( void* GPIOin, void* GPIOout )
+{
+ unsigned short i;
+ ULONG value;
+
+ if ( !(tl_read_i2c_data(GPIOin) & SENSE_DATA_HI))
+ {
+ // start with clock high, let data float high
+ tl_write_i2c_reg( GPIOout, SET_DATA_HI | SET_CLOCK_HI );
+
+ // keep sending clock pulses if slave is driving data line
+ for (i = 0; i < 10; i++)
+ {
+ tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+
+ if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI )
+ break;
+ }
+
+ // if he's still driving data low after 10 clocks, abort
+ value = tl_read_i2c_data( GPIOin ); // read status
+ if (!(value & 0x08) )
+ return( FALSE );
+ }
+
+
+ // To START, bring data low while clock high
+ tl_write_i2c_reg( GPIOout, SET_CLOCK_HI | SET_DATA_LO );
+
+ i2c_delay(0);
+
+ return( TRUE ); // TX start successful
+}
+//-----------------------------------------------------------------------------
+//
+// Name: I2C_TX_STOP
+//
+// This routine transmits a stop condition over the I2C bus.
+//
+//-----------------------------------------------------------------------------
+
+static unsigned short tl_i2c_tx_stop( void* GPIOin, void* GPIOout )
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ {
+ // Send clock pulse, drive data line low
+ tl_i2c_clock_pulse( SET_DATA_LO, GPIOout );
+
+ // To STOP, bring data high while clock high
+ tl_write_i2c_reg( GPIOout, SET_DATA_HI | SET_CLOCK_HI );
+
+ // Give the data line time to float high
+ i2c_delay(0);
+
+ // If slave is driving data line low, there's a problem; retry
+ if ( tl_read_i2c_data(GPIOin) & SENSE_DATA_HI )
+ return( TRUE ); // TX STOP successful!
+ }
+
+ return( FALSE ); // error
+}
+//-----------------------------------------------------------------------------
+//
+// Name: I2C_TX_uchar
+//
+// This routine transmits a byte across the I2C bus.
+//
+//-----------------------------------------------------------------------------
+static void tl_i2c_tx_byte( void* GPIOout, UCHAR data )
+{
+ UCHAR bit;
+
+ for (bit = 0x80; bit; bit >>= 1)
+ {
+ if( data & bit )
+ tl_i2c_clock_pulse( (UCHAR)SET_DATA_HI, GPIOout);
+ else
+ tl_i2c_clock_pulse( (UCHAR)SET_DATA_LO, GPIOout);
+ }
+}
+//-----------------------------------------------------------------------------
+//
+// Name: I2C_RX_uchar
+//
+// This routine receives a byte across the I2C bus.
+//
+//-----------------------------------------------------------------------------
+static UCHAR tl_i2c_rx_byte( void* GPIOin, void* GPIOout )
+{
+ UCHAR bit;
+ UCHAR data = 0;
+
+
+ for (bit = 0x80; bit; bit >>= 1) {
+ // do clock pulse, let data line float high
+ tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+
+ // read data line
+ if ( tl_read_i2c_data( GPIOin) & 0x08 )
+ data |= bit;
+ }
+
+ return (data);
+}
+//*****************************************************************************
+//*****************************************************************************
+// Function: read_i2c_nvram
+// Arguments: UCHAR count number of bytes to read
+// UCHAR *buf area to store the bytes read
+// Returns: 0 - failed
+// 1 - success
+//*****************************************************************************
+//*****************************************************************************
+unsigned long cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count,
+ UCHAR *buf )
+{
+ unsigned short i;
+
+ if( !( tl_i2c_tx_start(GPIOin, GPIOout) ))
+ return FALSE;
+
+ // Select the NVRAM for "dummy" write, to set the address
+ tl_i2c_tx_byte( GPIOout , SLAVE_WRITE_ADDRESS );
+ if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) )
+ return( FALSE );
+
+ // Now send the address where we want to start reading
+ tl_i2c_tx_byte( GPIOout , 0 );
+ if ( !tl_i2c_rx_ack(GPIOin, GPIOout ) )
+ return( FALSE );
+
+ // Send a repeated start condition and select the
+ // slave for reading now.
+ if( tl_i2c_tx_start(GPIOin, GPIOout) )
+ tl_i2c_tx_byte( GPIOout, SLAVE_READ_ADDRESS );
+
+ if ( !tl_i2c_rx_ack(GPIOin, GPIOout) )
+ return( FALSE );
+
+ // this loop will now read out the data and store it
+ // in the buffer pointed to by buf
+ for ( i=0; i<count; i++)
+ {
+ *buf++ = tl_i2c_rx_byte(GPIOin, GPIOout);
+
+ // Send ACK by holding data line low for 1 clock
+ if ( i < (count-1) )
+ tl_i2c_clock_pulse( 0x08, GPIOout );
+ else {
+ // Don't send ack for final byte
+ tl_i2c_clock_pulse( SET_DATA_HI, GPIOout );
+ }
+ }
+
+ tl_i2c_tx_stop(GPIOin, GPIOout);
+
+ return( TRUE );
+}
+
+//****************************************************************
+//
+//
+//
+// routines to set and clear the data and clock bits
+//
+//
+//
+//****************************************************************
+
+static void tl_set_clock(void* gpioreg)
+{
+ ULONG ret_val;
+
+ ret_val = readl( gpioreg );
+ ret_val &= 0xffffffFBL; // clear GPIO2 (SCL)
+ writel( ret_val, gpioreg);
+}
+
+static void tl_clr_clock(void* gpioreg)
+{
+ ULONG ret_val;
+
+ ret_val = readl( gpioreg );
+ ret_val |= SET_CLOCK_LO;
+ writel( ret_val, gpioreg);
+}
+
+//*****************************************************************
+//
+//
+// This routine will advance the clock by one period
+//
+//
+//*****************************************************************
+static void tl_i2c_clock_pulse( UCHAR value, void* GPIOout )
+{
+ ULONG ret_val;
+
+ // clear the clock bit
+ tl_clr_clock( GPIOout );
+
+ i2c_delay(0);
+
+
+ // read the port to preserve non-I2C bits
+ ret_val = readl( GPIOout );
+
+ // clear the data & clock bits
+ ret_val &= 0xFFFFFFf3;
+
+ // write the value passed in...
+ // data can only change while clock is LOW!
+ ret_val |= value; // the data
+ ret_val |= SET_CLOCK_LO; // the clock
+ writel( ret_val, GPIOout );
+
+ i2c_delay(0);
+
+
+ //set clock bit
+ tl_set_clock( GPIOout);
+}
+
+
+
+
+//*****************************************************************
+//
+//
+// This routine returns the 64-bit WWN
+//
+//
+//*****************************************************************
+int cpqfcTS_GetNVRAM_data( UCHAR *wwnbuf, UCHAR *buf )
+{
+ ULONG len;
+ ULONG sub_len;
+ ULONG ptr_inc;
+ ULONG i;
+ ULONG j;
+ UCHAR *data_ptr;
+ UCHAR z;
+ UCHAR name;
+ UCHAR sub_name;
+ UCHAR done;
+ int iReturn=0; // def. 0 offset is failure to find WWN field
+
+
+
+ data_ptr = (UCHAR *)buf;
+
+ done = FALSE;
+ i = 0;
+
+ while ( (i < 128) && (!done) )
+ {
+ z = data_ptr[i];\
+ if ( !(z & 0x80) )
+ {
+ len = 1 + (z & 0x07);
+
+ name = (z & 0x78) >> 3;
+ if (name == 0x0F)
+ done = TRUE;
+ }
+ else
+ {
+ name = z & 0x7F;
+ len = 3 + data_ptr[i+1] + (data_ptr[i+2] << 8);
+
+ switch (name)
+ {
+ case 0x0D:
+ //
+ j = i + 3;
+ //
+ if ( data_ptr[j] == 0x3b ) {
+ len = 6;
+ break;
+ }
+
+ while ( j<(i+len) ) {
+ sub_name = (data_ptr[j] & 0x3f);
+ sub_len = data_ptr[j+1] +
+ (data_ptr[j+2] << 8);
+ ptr_inc = sub_len + 3;
+ switch (sub_name)
+ {
+ case 0x3C:
+ memcpy( wwnbuf, &data_ptr[j+3], 8);
+ iReturn = j+3;
+ break;
+ default:
+ break;
+ }
+ j += ptr_inc;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ //
+ i += len;
+ } // end while
+ return iReturn;
+}
+
+
+
+
+
+// define a short 5 micro sec delay, and longer (ms) delay
+
+static void i2c_delay(ULONG mstime)
+{
+ ULONG i;
+
+// NOTE: we only expect to use these delays when reading
+// our adapter's NVRAM, which happens only during adapter reset.
+// Delay technique from "Linux Device Drivers", A. Rubini
+// (1st Ed.) pg 137.
+
+// printk(" delay %lx ", mstime);
+ if( mstime ) // ms delay?
+ {
+ // delay technique
+ for( i=0; i < mstime; i++)
+ udelay(1000); // 1ms per loop
+
+ }
+ else // 5 micro sec delay
+
+ udelay( 5 ); // micro secs
+
+// printk("done\n");
+}
+
+
+
diff --git a/drivers/scsi/cpqfcTSinit.c b/drivers/scsi/cpqfcTSinit.c
new file mode 100644
index 000000000..b51b515a6
--- /dev/null
+++ b/drivers/scsi/cpqfcTSinit.c
@@ -0,0 +1,1815 @@
+/* Copyright(c) 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter
+ * 64-bit, 66MHz PCI
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ...
+ * SP# P225CXCBFIEL6T, Rev XC
+ * SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * 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.
+ * Written by Don Zimmerman
+ * IOCTL and procfs added by Jouke Numan
+ * SMP testing by Chel Van Gennip
+ *
+ * portions copied from:
+ * QLogic CPQFCTS SCSI-FCP
+ * Written by Erik H. Moe, ehm@cris.com
+ * Copyright 1995, Erik H. Moe
+ * Renamed and updated to 1.3.x by Michael Griffith <grif@cs.ucr.edu>
+ * Chris Loveland <cwl@iol.unh.edu> to support the isp2100 and isp2200
+*/
+
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/ioport.h> // request_region() prototype
+#include <linux/vmalloc.h> // ioremap()
+#ifdef __alpha__
+#define __KERNEL_SYSCALLS__
+#endif
+#include <asm/unistd.h>
+#include <asm/io.h>
+#include <asm/uaccess.h> // ioctl related
+#include <asm/irq.h>
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+#include <asm/spinlock.h>
+#else
+#include <linux/spinlock.h>
+#endif
+#include "sd.h"
+#include <scsi/scsi_ioctl.h>
+#include "hosts.h"
+#include "cpqfcTSchip.h"
+#include "cpqfcTSstructs.h"
+
+#include "cpqfcTS.h"
+
+#include <linux/module.h>
+/* Embedded module documentation macros - see module.h */
+MODULE_AUTHOR("Compaq Computer Corporation");
+MODULE_DESCRIPTION("Driver for Compaq 64-bit/66Mhz PCI Fibre Channel HBA");
+
+// This struct was originally defined in
+// /usr/src/linux/include/linux/proc_fs.h
+// since it's only partially implemented, we only use first
+// few fields...
+// NOTE: proc_fs changes in 2.4 kernel
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+static struct proc_dir_entry proc_scsi_cpqfcTS =
+{
+ PROC_SCSI_CPQFCTS, // ushort low_ino (enumerated list)
+ 7, // ushort namelen
+ DEV_NAME, // const char* name
+ S_IFDIR | S_IRUGO | S_IXUGO, // mode_t mode
+ 2 // nlink_t nlink
+ // etc. ...
+};
+
+
+#endif
+
+
+
+/* local function to load our per-HBA (local) data for chip
+ registers, FC link state, all FC exchanges, etc.
+
+ We allocate space and compute address offsets for the
+ most frequently accessed addresses; others (like World Wide
+ Name) are not necessary.
+
+*/
+static void Cpqfc_initHBAdata( CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev )
+{
+
+ cpqfcHBAdata->PciDev = PciDev; // copy PCI info ptr
+
+ // since x86 port space is 64k, we only need the lower 16 bits
+ cpqfcHBAdata->fcChip.Registers.IOBaseL =
+ PciDev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
+
+ cpqfcHBAdata->fcChip.Registers.IOBaseU =
+ PciDev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK;
+
+ // 32-bit memory addresses
+ cpqfcHBAdata->fcChip.Registers.MemBase =
+ PciDev->base_address[3] & PCI_BASE_ADDRESS_MEM_MASK;
+
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase =
+ ioremap( PciDev->base_address[3] & PCI_BASE_ADDRESS_MEM_MASK,
+ 0x200);
+
+ cpqfcHBAdata->fcChip.Registers.RAMBase =
+ PciDev->base_address[4];
+
+ cpqfcHBAdata->fcChip.Registers.SROMBase = // NULL for HP TS adapter
+ PciDev->base_address[5];
+
+ // now the Tachlite chip registers
+ // the REGISTER struct holds both the physical address & last
+ // written value (some TL registers are WRITE ONLY)
+
+ cpqfcHBAdata->fcChip.Registers.SFQconsumerIndex.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_SFQ_CONSUMER_INDEX;
+
+ cpqfcHBAdata->fcChip.Registers.ERQproducerIndex.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_ERQ_PRODUCER_INDEX;
+
+ // TL Frame Manager
+ cpqfcHBAdata->fcChip.Registers.FMconfig.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_CONFIG;
+ cpqfcHBAdata->fcChip.Registers.FMcontrol.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_CONTROL;
+ cpqfcHBAdata->fcChip.Registers.FMstatus.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_STATUS;
+ cpqfcHBAdata->fcChip.Registers.FMLinkStatus1.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_LINK_STAT1;
+ cpqfcHBAdata->fcChip.Registers.FMLinkStatus2.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_LINK_STAT2;
+ cpqfcHBAdata->fcChip.Registers.FMBB_CreditZero.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_BB_CREDIT0;
+
+ // TL Control Regs
+ cpqfcHBAdata->fcChip.Registers.TYconfig.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_CONFIG;
+ cpqfcHBAdata->fcChip.Registers.TYcontrol.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_CONTROL;
+ cpqfcHBAdata->fcChip.Registers.TYstatus.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_TACH_STATUS;
+ cpqfcHBAdata->fcChip.Registers.rcv_al_pa.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_RCV_AL_PA;
+ cpqfcHBAdata->fcChip.Registers.ed_tov.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + TL_MEM_FM_ED_TOV;
+
+
+ cpqfcHBAdata->fcChip.Registers.INTEN.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTEN;
+ cpqfcHBAdata->fcChip.Registers.INTPEND.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTPEND;
+ cpqfcHBAdata->fcChip.Registers.INTSTAT.address =
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase + IINTSTAT;
+
+ DEBUG_PCI(printk(" cpqfcHBAdata->fcChip.Registers. :\n"));
+ DEBUG_PCI(printk(" IOBaseL = %x\n",
+ cpqfcHBAdata->fcChip.Registers.IOBaseL));
+ DEBUG_PCI(printk(" IOBaseU = %x\n",
+ cpqfcHBAdata->fcChip.Registers.IOBaseU));
+
+ printk(" ioremap'd Membase: %p\n", cpqfcHBAdata->fcChip.Registers.ReMapMemBase);
+
+ DEBUG_PCI(printk(" SFQconsumerIndex.address = %p\n",
+ cpqfcHBAdata->fcChip.Registers.SFQconsumerIndex.address));
+ DEBUG_PCI(printk(" ERQproducerIndex.address = %p\n",
+ cpqfcHBAdata->fcChip.Registers.ERQproducerIndex.address));
+ DEBUG_PCI(printk(" TYconfig.address = %p\n",
+ cpqfcHBAdata->fcChip.Registers.TYconfig.address));
+ DEBUG_PCI(printk(" FMconfig.address = %p\n",
+ cpqfcHBAdata->fcChip.Registers.FMconfig.address));
+ DEBUG_PCI(printk(" FMcontrol.address = %p\n",
+ cpqfcHBAdata->fcChip.Registers.FMcontrol.address));
+
+ // set default options for FC controller (chip)
+ cpqfcHBAdata->fcChip.Options.initiator = 1; // default: SCSI initiator
+ cpqfcHBAdata->fcChip.Options.target = 0; // default: SCSI target
+ cpqfcHBAdata->fcChip.Options.extLoopback = 0;// default: no loopback @GBIC
+ cpqfcHBAdata->fcChip.Options.intLoopback = 0;// default: no loopback inside chip
+
+ // set highest and lowest FC-PH version the adapter/driver supports
+ // (NOT strict compliance)
+ cpqfcHBAdata->fcChip.highest_FCPH_ver = FC_PH3;
+ cpqfcHBAdata->fcChip.lowest_FCPH_ver = FC_PH43;
+
+ // set function points for this controller / adapter
+ cpqfcHBAdata->fcChip.ResetTachyon = CpqTsResetTachLite;
+ cpqfcHBAdata->fcChip.FreezeTachyon = CpqTsFreezeTachlite;
+ cpqfcHBAdata->fcChip.UnFreezeTachyon = CpqTsUnFreezeTachlite;
+ cpqfcHBAdata->fcChip.CreateTachyonQues = CpqTsCreateTachLiteQues;
+ cpqfcHBAdata->fcChip.DestroyTachyonQues = CpqTsDestroyTachLiteQues;
+ cpqfcHBAdata->fcChip.InitializeTachyon = CpqTsInitializeTachLite;
+ cpqfcHBAdata->fcChip.LaserControl = CpqTsLaserControl;
+ cpqfcHBAdata->fcChip.ProcessIMQEntry = CpqTsProcessIMQEntry;
+ cpqfcHBAdata->fcChip.InitializeFrameManager = CpqTsInitializeFrameManager;;
+ cpqfcHBAdata->fcChip.ReadWriteWWN = CpqTsReadWriteWWN;
+ cpqfcHBAdata->fcChip.ReadWriteNVRAM = CpqTsReadWriteNVRAM;
+
+
+
+}
+
+
+/* (borrowed from linux/drivers/scsi/hosts.c) */
+static void launch_FCworker_thread(struct Scsi_Host *HostAdapter)
+{
+ DECLARE_MUTEX_LOCKED(sem);
+
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+
+ ENTER("launch_FC_worker_thread");
+
+ cpqfcHBAdata->notify_wt = &sem;
+
+ kernel_thread((int (*)(void *))cpqfcTSWorkerThread,
+ (void *) HostAdapter, 0);
+ /*
+ * Now wait for the kernel error thread to initialize itself
+
+ */
+ down (&sem);
+ cpqfcHBAdata->notify_wt = NULL;
+
+ LEAVE("launch_FC_worker_thread");
+
+}
+
+
+/* "Entry" point to discover if any supported PCI
+ bus adapter can be found
+*/
+// We're supporting:
+// Compaq 64-bit, 66MHz HBA with Tachyon TS
+// Agilent XL2
+#define HBA_TYPES 2
+
+
+int cpqfcTS_detect(Scsi_Host_Template *ScsiHostTemplate)
+{
+ int NumberOfAdapters=0; // how many of our PCI adapters are found?
+ struct pci_dev *PciDev = NULL;
+ struct Scsi_Host *HostAdapter = NULL;
+ CPQFCHBA *cpqfcHBAdata = NULL;
+ struct timer_list *cpqfcTStimer = NULL;
+ SupportedPCIcards PCIids[HBA_TYPES];
+ int i;
+
+ ENTER("cpqfcTS_detect");
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
+ ScsiHostTemplate->proc_dir = &proc_scsi_cpqfcTS;
+#else
+ ScsiHostTemplate->proc_name = "cpqfcTS";
+#endif
+
+ if( pci_present() == 0) // no PCI busses?
+ {
+ printk( " no PCI bus?@#!\n");
+ return NumberOfAdapters;
+ }
+
+ // what HBA adapters are we supporting?
+ PCIids[0].vendor_id = PCI_VENDOR_ID_COMPAQ;
+ PCIids[0].device_id = CPQ_DEVICE_ID;
+ PCIids[1].vendor_id = PCI_VENDOR_ID_HP; // i.e. 103Ch (Agilent == HP for now)
+ PCIids[1].device_id = AGILENT_XL2_ID; // i.e. 1029h
+
+ for( i=0; i < HBA_TYPES; i++)
+ {
+ // look for all HBAs of each type
+
+ while( (PciDev =
+ pci_find_device( PCIids[i].vendor_id, PCIids[i].device_id, PciDev) ))
+ {
+ // NOTE: (kernel 2.2.12-32) limits allocation to 128k bytes...
+ printk(" scsi_register allocating %d bytes for FC HBA\n",
+ (ULONG)sizeof(CPQFCHBA));
+
+ HostAdapter = scsi_register( ScsiHostTemplate, sizeof( CPQFCHBA ) );
+ DEBUG_PCI( printk(" HBA found!\n"));
+ DEBUG_PCI( printk(" HostAdapter->PciDev->irq = %u\n", PciDev->irq) );
+ DEBUG_PCI(printk(" PciDev->baseaddress[]= %lx\n", PciDev->base_address[0]));
+ DEBUG_PCI(printk(" PciDev->baseaddress[]= %lx\n", PciDev->base_address[1]));
+ DEBUG_PCI(printk(" PciDev->baseaddress[]= %lx\n", PciDev->base_address[2]));
+ DEBUG_PCI(printk(" PciDev->baseaddress[]= %lx\n", PciDev->base_address[3]));
+
+
+ HostAdapter->irq = PciDev->irq; // copy for Scsi layers
+
+ // HP Tachlite uses two (255-byte) ranges of Port I/O (lower & upper),
+ // for a total I/O port address space of 512 bytes.
+ // mask out the I/O port address (lower) & record
+ HostAdapter->io_port = (unsigned int)
+ PciDev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
+ HostAdapter->n_io_port = 0xff;
+
+ // i.e., expect 128 targets (arbitrary number), while the
+ // RA-4000 supports 32 LUNs
+ HostAdapter->max_id = 0; // incremented as devices log in
+ HostAdapter->max_lun = CPQFCTS_MAX_LUN; // LUNs per FC device
+ HostAdapter->max_channel = CPQFCTS_MAX_CHANNEL; // multiple busses?
+ HostAdapter->hostt->use_new_eh_code = 1; // new error handling
+
+ // get the pointer to our HBA specific data... (one for
+ // each HBA on the PCI bus(ses)).
+ cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+
+ // make certain our data struct is clear
+ memset( cpqfcHBAdata, 0, sizeof( CPQFCHBA ) );
+
+
+ // initialize our HBA info
+ cpqfcHBAdata->HBAnum = NumberOfAdapters;
+
+ cpqfcHBAdata->HostAdapter = HostAdapter; // back ptr
+ Cpqfc_initHBAdata( cpqfcHBAdata, PciDev ); // fill MOST fields
+
+ cpqfcHBAdata->HBAnum = NumberOfAdapters;
+
+
+ // request necessary resources and check for conflicts
+ if( request_irq( HostAdapter->irq,
+ cpqfcTS_intr_handler,
+ SA_INTERRUPT | SA_SHIRQ,
+ DEV_NAME,
+ HostAdapter) )
+ {
+ printk(" IRQ %u already used\n", HostAdapter->irq);
+ scsi_unregister( HostAdapter);
+ continue;
+ }
+
+ // Since we have two 256-byte I/O port ranges (upper
+ // and lower), check them both
+ if( check_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff) )
+ {
+ printk(" cpqfcTS address in use: %x\n",
+ cpqfcHBAdata->fcChip.Registers.IOBaseU);
+ free_irq( HostAdapter->irq, HostAdapter);
+ scsi_unregister( HostAdapter);
+ continue;
+ }
+
+ if( check_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff) )
+ {
+ printk(" cpqfcTS address in use: %x\n",
+ cpqfcHBAdata->fcChip.Registers.IOBaseL);
+ free_irq( HostAdapter->irq, HostAdapter);
+ scsi_unregister( HostAdapter);
+ continue;
+ }
+
+ // OK, we should be able to grab everything we need now.
+ request_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff, DEV_NAME);
+ request_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff, DEV_NAME);
+ DEBUG_PCI(printk(" Requesting 255 I/O addresses @ %x\n",
+ cpqfcHBAdata->fcChip.Registers.IOBaseL ));
+ DEBUG_PCI(printk(" Requesting 255 I/O addresses @ %x\n",
+ cpqfcHBAdata->fcChip.Registers.IOBaseU ));
+
+
+ // start our kernel worker thread
+
+ launch_FCworker_thread(HostAdapter);
+
+
+ // start our TimerTask...
+
+ cpqfcTStimer = &cpqfcHBAdata->cpqfcTStimer;
+
+ init_timer( cpqfcTStimer); // Linux clears next/prev values
+ cpqfcTStimer->expires = jiffies + HZ; // one second
+ cpqfcTStimer->data = (unsigned long)cpqfcHBAdata; // this adapter
+ cpqfcTStimer->function = cpqfcTSheartbeat; // handles timeouts, housekeeping
+
+ add_timer( cpqfcTStimer); // give it to Linux
+
+
+ // now initialize our hardware...
+
+ cpqfcHBAdata->fcChip.InitializeTachyon( cpqfcHBAdata, 1,1);
+
+ cpqfcHBAdata->fcStatsTime = jiffies; // (for FC Statistics delta)
+
+ // give our HBA time to initialize and login current devices...
+ {
+ // The Brocade switch (e.g. 2400, 2010, etc.) as of March 2000,
+ // has the following algorithm for FL_Port startup:
+ // Time(sec) Action
+ // 0: Device Plugin and LIP(F7,F7) transmission
+ // 1.0 LIP incoming
+ // 1.027 LISA incoming, no CLS! (link not up)
+ // 1.028 NOS incoming (switch test for N_Port)
+ // 1.577 ED_TOV expired, transmit LIPs again
+ // 3.0 LIP(F8,F7) incoming (switch passes Tach Prim.Sig)
+ // 3.028 LILP received, link up, FLOGI starts
+ // slowest(worst) case, measured on 1Gb Finisar GT analyzer
+
+ int wait_time;
+ for( wait_time = jiffies + 4*HZ; wait_time > jiffies; )
+ schedule(); // (our worker task needs to run)
+
+ }
+
+ NumberOfAdapters++;
+ } // end of while()
+ }
+
+ LEAVE("cpqfcTS_detect");
+
+ return NumberOfAdapters;
+}
+
+
+static void my_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);
+ }
+}
+
+
+
+int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg)
+{
+ int result = 0;
+ struct Scsi_Host *HostAdapter = ScsiDev->host;
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+ Scsi_Cmnd DumCmnd;
+ int i, j;
+ VENDOR_IOCTL_REQ ioc;
+ cpqfc_passthru_t *vendor_cmd;
+ Scsi_Device *SDpnt;
+ Scsi_Cmnd *ScsiPassThruCmnd;
+ unsigned long flags;
+
+ ENTER("cpqfcTS_ioctl");
+
+ // can we find an FC device mapping to this SCSI target?
+ DumCmnd.channel = ScsiDev->channel; // For searching
+ DumCmnd.target = ScsiDev->id;
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ &DumCmnd, // search Scsi Nexus
+ 0, // DON'T search linked list for FC port id
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ if( pLoggedInPort == NULL ) // not found!
+ {
+ result = -ENXIO;
+ }
+
+ else // we know what FC device to operate on...
+ {
+ switch (Cmnd)
+ {
+ // Passthrough provides a mechanism to bypass the RAID
+ // or other controller and talk directly to the devices
+ // (e.g. physical disk drive)
+ // Passthrough commands, unfortunately, tend to be vendor
+ // specific; this is tailored to COMPAQ's RAID (RA4x00)
+ case CPQFCTS_SCSI_PASSTHRU:
+ {
+ void *buf = NULL; // for kernel space buffer for user data
+
+ if( !arg)
+ return -EINVAL;
+
+ // must be super user to send stuff directly to the
+ // controller and/or physical drives...
+ if( !suser() )
+ return -EPERM;
+
+ // copy the caller's struct to our space.
+ copy_from_user_ret( &ioc, arg,
+ sizeof( VENDOR_IOCTL_REQ), -EFAULT);
+
+ vendor_cmd = ioc.argp; // i.e., CPQ specific command struct
+
+ // If necessary, grab a kernel/DMA buffer
+ if( vendor_cmd->len)
+ {
+ buf = kmalloc( vendor_cmd->len, GFP_KERNEL);
+ if( !buf)
+ return -ENOMEM;
+ }
+
+ // Now build a SCSI_CMND to pass down...
+ // This function allocates and sets Scsi_Cmnd ptrs such as
+ // ->channel, ->target, ->host
+ ScsiPassThruCmnd = scsi_allocate_device(NULL, ScsiDev, 1);
+
+ // Need data from user?
+ // make sure caller's buffer is in kernel space.
+ if( (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) &&
+ vendor_cmd->len)
+ copy_from_user_ret( buf, vendor_cmd->bufp, vendor_cmd->len, -EFAULT);
+
+ // copy the CDB (if/when MAX_COMMAND_SIZE is 16, remove copy below)
+ memcpy( &ScsiPassThruCmnd->cmnd[0],
+ &vendor_cmd->cdb[0],
+ MAX_COMMAND_SIZE);
+ // we want to copy all 16 bytes into the FCP-SCSI CDB,
+ // although the actual passthru only uses up to the
+ // first 12.
+
+ ScsiPassThruCmnd->cmd_len = 16; // sizeof FCP-SCSI CDB
+
+ // Unfortunately, the SCSI command cmnd[] field has only
+ // 12 bytes. Ideally the MAX_COMMAND_SIZE should be increased
+ // to 16 for newer Fibre Channel and SCSI-3 larger CDBs.
+ // However, to avoid a mandatory kernel rebuild, we use the SCp
+ // spare field to store the extra 4 bytes ( ugly :-(
+
+ if( MAX_COMMAND_SIZE < 16)
+ {
+ memcpy( &ScsiPassThruCmnd->SCp.buffers_residual,
+ &vendor_cmd->cdb[12], 4);
+ }
+
+
+ ScsiPassThruCmnd->SCp.sent_command = 1; // PASSTHRU!
+ // suppress LUN masking
+ // and VSA logic
+
+ // Use spare fields to copy FCP-SCSI LUN address info...
+ ScsiPassThruCmnd->SCp.phase = vendor_cmd->bus;
+ ScsiPassThruCmnd->SCp.have_data_in = vendor_cmd->pdrive;
+
+
+
+ // We copy the scheme used by scsi.c to submit commands
+ // to our own HBA. We do this in order to stall the
+ // thread calling the IOCTL until it completes, and use
+ // the same "_quecommand" function for synchronizing
+ // FC Link events with our "worker thread".
+
+ spin_lock_irqsave(&io_request_lock, flags);
+ {
+ DECLARE_MUTEX_LOCKED(sem);
+ ScsiPassThruCmnd->request.sem = &sem;
+ // eventually gets us to our own _quecommand routine
+ scsi_do_cmd( ScsiPassThruCmnd, &vendor_cmd->cdb[0],
+ buf,
+ vendor_cmd->len,
+ my_ioctl_done,
+ 10*HZ, 1);// timeout,retries
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ // Other I/Os can now resume; we wait for our ioctl
+ // command to complete
+ down(&sem);
+ spin_lock_irqsave(&io_request_lock, flags);
+ ScsiPassThruCmnd->request.sem = NULL;
+ }
+
+ result = ScsiPassThruCmnd->result;
+
+ // copy any sense data back to caller
+ if( result != 0 )
+ {
+ memcpy( vendor_cmd->sense_data, // see struct def - size=40
+ ScsiPassThruCmnd->sense_buffer,
+ sizeof(ScsiPassThruCmnd->sense_buffer));
+ }
+ SDpnt = ScsiPassThruCmnd->device;
+ scsi_release_command(ScsiPassThruCmnd); // "de-allocate"
+ ScsiPassThruCmnd = NULL;
+
+ if (!SDpnt->was_reset && SDpnt->scsi_request_fn)
+ (*SDpnt->scsi_request_fn)();
+
+ wake_up(&SDpnt->device_wait);
+ spin_unlock_irqrestore(&io_request_lock, flags);
+
+ // need to pass data back to user (space)?
+ if( (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) &&
+ vendor_cmd->len )
+ copy_to_user_ret( vendor_cmd->bufp, buf, vendor_cmd->len, -EFAULT);
+
+ if( buf)
+ kfree( buf);
+
+ return result;
+ }
+
+ case CPQFCTS_GETPCIINFO:
+ {
+ cpqfc_pci_info_struct pciinfo;
+
+ if( !arg)
+ return -EINVAL;
+
+
+
+ pciinfo.bus = cpqfcHBAdata->PciDev->bus->number;
+ pciinfo.dev_fn = cpqfcHBAdata->PciDev->devfn;
+ pciinfo.board_id = cpqfcHBAdata->PciDev->device |
+ (cpqfcHBAdata->PciDev->vendor <<16);
+
+ copy_to_user_ret( arg, &pciinfo,
+ sizeof(cpqfc_pci_info_struct), -EFAULT);
+ return 0;
+ }
+
+ case CPQFCTS_GETDRIVVER:
+ {
+ DriverVer_type DriverVer =
+ CPQFCTS_DRIVER_VER( VER_MAJOR,VER_MINOR,VER_SUBMINOR);
+
+ if( !arg)
+ return -EINVAL;
+
+ copy_to_user_ret( arg, &DriverVer,
+ sizeof(DriverVer), -EFAULT);
+ return 0;
+ }
+
+
+
+ case SCSI_IOCTL_FC_TARGET_ADDRESS:
+ result =
+ verify_area(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress));
+ if (result)
+ break;
+
+ put_user(pLoggedInPort->port_id,
+ &((Scsi_FCTargAddress *) arg)->host_port_id);
+
+ for( i=3,j=0; i>=0; i--) // copy the LOGIN port's WWN
+ put_user(pLoggedInPort->u.ucWWN[i],
+ &((Scsi_FCTargAddress *) arg)->host_wwn[j++]);
+ for( i=7; i>3; i--) // copy the LOGIN port's WWN
+ put_user(pLoggedInPort->u.ucWWN[i],
+ &((Scsi_FCTargAddress *) arg)->host_wwn[j++]);
+ break;
+ default:
+ result = -EINVAL;
+ break;
+ }
+ }
+
+ LEAVE("cpqfcTS_ioctl");
+ return result;
+}
+
+
+/* "Release" the Host Bus Adapter...
+ disable interrupts, stop the HBA, release the interrupt,
+ and free all resources */
+
+int cpqfcTS_release(struct Scsi_Host *HostAdapter)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+
+
+ ENTER("cpqfcTS_release");
+
+ DEBUG_PCI( printk(" cpqfcTS: delete timer...\n"));
+ del_timer( &cpqfcHBAdata->cpqfcTStimer);
+
+ // disable the hardware...
+ DEBUG_PCI( printk(" disable hardware, destroy queues, free mem\n"));
+ cpqfcHBAdata->fcChip.ResetTachyon( cpqfcHBAdata, CLEAR_FCPORTS);
+
+ // kill kernel thread
+ if( cpqfcHBAdata->worker_thread ) // (only if exists)
+ {
+ DECLARE_MUTEX_LOCKED(sem); // synchronize thread kill
+
+ cpqfcHBAdata->notify_wt = &sem;
+ DEBUG_PCI( printk(" killing kernel thread\n"));
+ send_sig( SIGKILL, cpqfcHBAdata->worker_thread, 1);
+ down( &sem);
+ cpqfcHBAdata->notify_wt = NULL;
+
+ }
+
+ // free Linux resources
+ DEBUG_PCI( printk(" cpqfcTS: freeing resources...\n"));
+ free_irq( HostAdapter->irq, HostAdapter);
+ scsi_unregister( HostAdapter);
+ release_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff);
+ release_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff);
+ /* we get "vfree: bad address" executing this - need to investigate...
+ if( (void*)((unsigned long)cpqfcHBAdata->fcChip.Registers.MemBase) !=
+ cpqfcHBAdata->fcChip.Registers.ReMapMemBase)
+ vfree( cpqfcHBAdata->fcChip.Registers.ReMapMemBase);
+*/
+
+ LEAVE("cpqfcTS_release");
+ return 0;
+}
+
+
+const char * cpqfcTS_info(struct Scsi_Host *HostAdapter)
+{
+ static char buf[300];
+ CPQFCHBA *cpqfcHBA;
+ int BusSpeed, BusWidth;
+
+ // get the pointer to our Scsi layer HBA buffer
+ cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata;
+
+ BusWidth = (cpqfcHBA->fcChip.Registers.PCIMCTR &0x4) > 0 ?
+ 64 : 32;
+
+ if( cpqfcHBA->fcChip.Registers.TYconfig.value & 0x80000000)
+ BusSpeed = 66;
+ else
+ BusSpeed = 33;
+
+ sprintf(buf,
+"%s: WWN %08X%08X\n on PCI bus %d device 0x%02x irq %d IObaseL 0x%x, MEMBASE 0x%x\nPCI bus width %d bits, bus speed %d MHz\nFCP-SCSI Driver v%d.%d.%d",
+ cpqfcHBA->fcChip.Name,
+ cpqfcHBA->fcChip.Registers.wwn_hi,
+ cpqfcHBA->fcChip.Registers.wwn_lo,
+ cpqfcHBA->PciDev->bus->number,
+ cpqfcHBA->PciDev->device,
+ HostAdapter->irq,
+ cpqfcHBA->fcChip.Registers.IOBaseL,
+ cpqfcHBA->fcChip.Registers.MemBase,
+ BusWidth,
+ BusSpeed,
+ VER_MAJOR, VER_MINOR, VER_SUBMINOR
+);
+
+
+ cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[ strlen(buf)]);
+ cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]);
+ return buf;
+}
+
+//
+// /proc/scsi support. The following routines allow us to do 'normal'
+// sprintf like calls to return the currently requested piece (buflenght
+// chars, starting at bufoffset) of the file. Although procfs allows for
+// a 1 Kb bytes overflow after te supplied buffer, I consider it bad
+// programming to use it to make programming a little simpler. This piece
+// of coding is borrowed from ncr53c8xx.c with some modifications
+//
+struct info_str
+{
+ char *buffer; // Pointer to output buffer
+ int buflength; // It's length
+ int bufoffset; // File offset corresponding with buf[0]
+ int buffillen; // Current filled length
+ int filpos; // Current file offset
+};
+
+static void copy_mem_info(struct info_str *info, char *data, int datalen)
+{
+
+ if (info->filpos < info->bufoffset) { // Current offset before buffer offset
+ if (info->filpos + datalen <= info->bufoffset) {
+ info->filpos += datalen; // Discard if completely before buffer
+ return;
+ } else { // Partial copy, set to begin
+ data += (info->bufoffset - info->filpos);
+ datalen -= (info->bufoffset - info->filpos);
+ info->filpos = info->bufoffset;
+ }
+ }
+
+ info->filpos += datalen; // Update current offset
+
+ if (info->buffillen == info->buflength) // Buffer full, discard
+ return;
+
+ if (info->buflength - info->buffillen < datalen) // Overflows buffer ?
+ datalen = info->buflength - info->buffillen;
+
+ memcpy(info->buffer + info->buffillen, data, datalen);
+ info->buffillen += datalen;
+}
+
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[400];
+ int len;
+
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
+
+ copy_mem_info(info, buf, len);
+ return len;
+}
+
+
+// Routine to get data for /proc RAM filesystem
+//
+int cpqfcTS_proc_info (char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+ struct Scsi_Host *host;
+ Scsi_Cmnd DumCmnd;
+ int Chan, Targ, i;
+ struct info_str info;
+ CPQFCHBA *cpqfcHBA;
+ PTACHYON fcChip;
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+ char buf[81];
+
+ // Search the Scsi host list for our controller
+ for (host=scsi_hostlist; host; host=host->next)
+ if (host->host_no == hostno)
+ break;
+
+ if (!host) return -ESRCH;
+
+ if (inout) return -EINVAL;
+
+ // get the pointer to our Scsi layer HBA buffer
+ cpqfcHBA = (CPQFCHBA *)host->hostdata;
+ fcChip = &cpqfcHBA->fcChip;
+
+ *start = buffer;
+
+ info.buffer = buffer;
+ info.buflength = length;
+ info.bufoffset = offset;
+ info.filpos = 0;
+ info.buffillen = 0;
+ copy_info(&info, "Driver version = %d.%d.%d", VER_MAJOR, VER_MINOR, VER_SUBMINOR);
+ cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[0]);
+ cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]);
+ copy_info(&info, "%s\n", buf);
+
+
+#define DISPLAY_WWN_INFO
+#ifdef DISPLAY_WWN_INFO
+ copy_info(&info, "WWN database: (\"port_id: 000000\" means disconnected)\n");
+ for ( Chan=0; Chan <= host->max_channel; Chan++) {
+ DumCmnd.channel = Chan;
+ for (Targ=0; Targ <= host->max_id; Targ++) {
+ DumCmnd.target = Targ;
+ if ((pLoggedInPort = fcFindLoggedInPort( fcChip,
+ &DumCmnd, // search Scsi Nexus
+ 0, // DON'T search list for FC port id
+ NULL, // DON'T search list for FC WWN
+ NULL))){ // DON'T care about end of list
+ copy_info(&info, "Host: scsi%d Channel: %02d TargetId: %02d -> WWN: ",
+ hostno, Chan, Targ);
+ for( i=3; i>=0; i--) // copy the LOGIN port's WWN
+ copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]);
+ for( i=7; i>3; i--) // copy the LOGIN port's WWN
+ copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]);
+ copy_info(&info, " port_id: %06X\n", pLoggedInPort->port_id);
+ }
+ }
+ }
+#endif
+
+
+// Unfortunately, the proc_info buffer isn't big enough
+// for everything we would like...
+// For FC stats, compile this and turn off WWN stuff above
+//#define DISPLAY_FC_STATS
+#ifdef DISPLAY_FC_STATS
+// get the Fibre Channel statistics
+ {
+ int DeltaSecs = (jiffies - cpqfcHBA->fcStatsTime) / HZ;
+ int days,hours,minutes,secs;
+
+ days = DeltaSecs / (3600*24); // days
+ hours = (DeltaSecs% (3600*24)) / 3600; // hours
+ minutes = (DeltaSecs%3600 /60); // minutes
+ secs = DeltaSecs%60; // secs
+copy_info( &info, "Fibre Channel Stats (time dd:hh:mm:ss %02u:%02u:%02u:%02u\n",
+ days, hours, minutes, secs);
+ }
+
+ cpqfcHBA->fcStatsTime = jiffies; // (for next delta)
+
+ copy_info( &info, " LinkUp %9u LinkDown %u\n",
+ fcChip->fcStats.linkUp, fcChip->fcStats.linkDown);
+
+ copy_info( &info, " Loss of Signal %9u Loss of Sync %u\n",
+ fcChip->fcStats.LossofSignal, fcChip->fcStats.LossofSync);
+
+ copy_info( &info, " Discarded Frames %9u Bad CRC Frame %u\n",
+ fcChip->fcStats.Dis_Frm, fcChip->fcStats.Bad_CRC);
+
+ copy_info( &info, " TACH LinkFailTX %9u TACH LinkFailRX %u\n",
+ fcChip->fcStats.linkFailTX, fcChip->fcStats.linkFailRX);
+
+ copy_info( &info, " TACH RxEOFa %9u TACH Elastic Store %u\n",
+ fcChip->fcStats.Rx_EOFa, fcChip->fcStats.e_stores);
+
+ copy_info( &info, " BufferCreditWait %9uus TACH FM Inits %u\n",
+ fcChip->fcStats.BB0_Timer*10, fcChip->fcStats.FMinits );
+
+ copy_info( &info, " FC-2 Timeouts %9u FC-2 Logouts %u\n",
+ fcChip->fcStats.timeouts, fcChip->fcStats.logouts);
+
+ copy_info( &info, " FC-2 Aborts %9u FC-4 Aborts %u\n",
+ fcChip->fcStats.FC2aborted, fcChip->fcStats.FC4aborted);
+
+ // clear the counters
+ cpqfcTSClearLinkStatusCounters( fcChip);
+#endif
+
+ return info.buffillen;
+}
+
+
+#if DEBUG_CMND
+
+UCHAR *ScsiToAscii( UCHAR ScsiCommand)
+{
+
+/*++
+
+Routine Description:
+
+ Converts a SCSI command to a text string for debugging purposes.
+
+
+Arguments:
+
+ ScsiCommand -- hex value SCSI Command
+
+
+Return Value:
+
+ An ASCII, null-terminated string if found, else returns NULL.
+
+Original code from M. McGowen, Compaq
+--*/
+
+
+ switch (ScsiCommand)
+ {
+ case 0x00:
+ return( "Test Unit Ready" );
+
+ case 0x01:
+ return( "Rezero Unit or Rewind" );
+
+ case 0x02:
+ return( "Request Block Address" );
+
+ case 0x03:
+ return( "Requese Sense" );
+
+ case 0x04:
+ return( "Format Unit" );
+
+ case 0x05:
+ return( "Read Block Limits" );
+
+ case 0x07:
+ return( "Reassign Blocks" );
+
+ case 0x08:
+ return( "Read (6)" );
+
+ case 0x0a:
+ return( "Write (6)" );
+
+ case 0x0b:
+ return( "Seek (6)" );
+
+ case 0x12:
+ return( "Inquiry" );
+
+ case 0x15:
+ return( "Mode Select (6)" );
+
+ case 0x16:
+ return( "Reserve" );
+
+ case 0x17:
+ return( "Release" );
+
+ case 0x1a:
+ return( "ModeSen(6)" );
+
+ case 0x1b:
+ return( "Start/Stop Unit" );
+
+ case 0x1c:
+ return( "Receive Diagnostic Results" );
+
+ case 0x1d:
+ return( "Send Diagnostic" );
+
+ case 0x25:
+ return( "Read Capacity" );
+
+ case 0x28:
+ return( "Read (10)" );
+
+ case 0x2a:
+ return( "Write (10)" );
+
+ case 0x2b:
+ return( "Seek (10)" );
+
+ case 0x2e:
+ return( "Write and Verify" );
+
+ case 0x2f:
+ return( "Verify" );
+
+ case 0x34:
+ return( "Pre-Fetch" );
+
+ case 0x35:
+ return( "Synchronize Cache" );
+
+ case 0x37:
+ return( "Read Defect Data (10)" );
+
+ case 0x3b:
+ return( "Write Buffer" );
+
+ case 0x3c:
+ return( "Read Buffer" );
+
+ case 0x3e:
+ return( "Read Long" );
+
+ case 0x3f:
+ return( "Write Long" );
+
+ case 0x41:
+ return( "Write Same" );
+
+ case 0x4c:
+ return( "Log Select" );
+
+ case 0x4d:
+ return( "Log Sense" );
+
+ case 0x56:
+ return( "Reserve (10)" );
+
+ case 0x57:
+ return( "Release (10)" );
+
+ case 0xa0:
+ return( "ReportLuns" );
+
+ case 0xb7:
+ return( "Read Defect Data (12)" );
+
+ case 0xca:
+ return( "Peripheral Device Addressing SCSI Passthrough" );
+
+ case 0xcb:
+ return( "Compaq Array Firmware Passthrough" );
+
+ default:
+ return( NULL );
+ }
+
+} // end ScsiToAscii()
+
+void cpqfcTS_print_scsi_cmd(Scsi_Cmnd * cmd)
+{
+
+printk("cpqfcTS: (%s) chnl 0x%02x, trgt = 0x%02x, lun = 0x%02x, cmd_len = 0x%02x\n",
+ ScsiToAscii( cmd->cmnd[0]), cmd->channel, cmd->target, cmd->lun, cmd->cmd_len);
+
+if( cmd->cmnd[0] == 0) // Test Unit Ready?
+{
+ int i;
+
+ printk("Cmnd->request_bufflen = 0x%X, ->use_sg = %d, ->bufflen = %d\n",
+ cmd->request_bufflen, cmd->use_sg, cmd->bufflen);
+ printk("Cmnd->request_buffer = %p, ->sglist_len = %d, ->buffer = %p\n",
+ cmd->request_buffer, cmd->sglist_len, cmd->buffer);
+ for (i = 0; i < cmd->cmd_len; i++)
+ printk("0x%02x ", cmd->cmnd[i]);
+ printk("\n");
+}
+
+}
+
+#endif /* DEBUG_CMND */
+
+
+
+
+static void QueCmndOnBoardLock( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd)
+{
+ int i;
+
+ for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++)
+ { // find spare slot
+ if( cpqfcHBAdata->BoardLockCmnd[i] == NULL )
+ {
+ cpqfcHBAdata->BoardLockCmnd[i] = Cmnd;
+// printk(" BoardLockCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n",
+// i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun);
+ break;
+ }
+ }
+ if( i >= CPQFCTS_REQ_QUEUE_LEN)
+ {
+ printk(" cpqfcTS WARNING: Lost Cmnd %p on BoardLock Q full!", Cmnd);
+ }
+
+}
+
+
+static void QueLinkDownCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd)
+{
+ int indx;
+
+ // Remember the command ptr so we can return; we'll complete when
+ // the device comes back, causing immediate retry
+ for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++)//, SCptr++)
+ {
+ if( cpqfcHBAdata->LinkDnCmnd[indx] == NULL ) // available?
+ {
+#ifdef DUMMYCMND_DBG
+ printk(" @add Cmnd %p to LnkDnCmnd[%d]@ ", Cmnd,indx);
+#endif
+ cpqfcHBAdata->LinkDnCmnd[indx] = Cmnd;
+ break;
+ }
+ }
+
+ if( indx >= CPQFCTS_REQ_QUEUE_LEN ) // no space for Cmnd??
+ {
+ // this will result in an _abort call later (with possible trouble)
+ printk("no buffer for LinkDnCmnd!! %p\n", Cmnd);
+ }
+}
+
+
+
+
+
+// The file "hosts.h" says not to call scsi_done from
+// inside _queuecommand, so we'll do it from the heartbeat timer
+
+static void QueBadTargetCmnd( CPQFCHBA *cpqfcHBAdata, Scsi_Cmnd *Cmnd)
+{
+ int i;
+ // printk(" can't find target %d\n", Cmnd->target);
+
+ for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++)
+ { // find spare slot
+ if( cpqfcHBAdata->BadTargetCmnd[i] == NULL )
+ {
+ cpqfcHBAdata->BadTargetCmnd[i] = Cmnd;
+// printk(" BadTargetCmnd[%d] %p Queued, chnl/target/lun %d/%d/%d\n",
+// i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun);
+ break;
+ }
+ }
+}
+
+
+// This is the "main" entry point for Linux Scsi commands --
+// it all starts here.
+
+int cpqfcTS_queuecommand(Scsi_Cmnd *Cmnd, void (* done)(Scsi_Cmnd *))
+{
+ struct Scsi_Host *HostAdapter = Cmnd->host;
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ TachFCHDR_GCMND fchs; // only use for FC destination id field
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+ ULONG ulStatus, SESTtype;
+ LONG ExchangeID;
+
+
+
+
+ ENTER("cpqfcTS_queuecommand");
+
+ PCI_TRACEO( (ULONG)Cmnd, 0x98)
+
+
+ Cmnd->scsi_done = done;
+#ifdef DEBUG_CMND
+ cpqfcTS_print_scsi_cmd( Cmnd);
+#endif
+
+ // prevent board contention with kernel thread...
+
+ if( cpqfcHBAdata->BoardLock )
+ {
+// printk(" @BrdLck Hld@ ");
+ QueCmndOnBoardLock( cpqfcHBAdata, Cmnd);
+ }
+
+ else
+ {
+
+ // in the current system (2.2.12), this routine is called
+ // after spin_lock_irqsave(), so INTs are disabled. However,
+ // we might have something pending in the LinkQ, which
+ // might cause the WorkerTask to run. In case that
+ // happens, make sure we lock it out.
+
+
+
+ PCI_TRACE( 0x98)
+ CPQ_SPINLOCK_HBA( cpqfcHBAdata)
+ PCI_TRACE( 0x98)
+
+ // can we find an FC device mapping to this SCSI target?
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ Cmnd, // search Scsi Nexus
+ 0, // DON'T search linked list for FC port id
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ if( pLoggedInPort == NULL ) // not found!
+ {
+// printk(" @Q bad targ cmnd %p@ ", Cmnd);
+ QueBadTargetCmnd( cpqfcHBAdata, Cmnd);
+ }
+
+ else // we know what FC device to send to...
+ {
+
+ // does this device support FCP target functions?
+ // (determined by PRLI field)
+
+ if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) )
+ {
+ printk(" Doesn't support TARGET functions port_id %Xh\n",
+ pLoggedInPort->port_id );
+ QueBadTargetCmnd( cpqfcHBAdata, Cmnd);
+ }
+
+ // In this case (previous login OK), the device is temporarily
+ // unavailable waiting for re-login, in which case we expect it
+ // to be back in between 25 - 500ms.
+ // If the FC port doesn't log back in within several seconds
+ // (i.e. implicit "logout"), or we get an explicit logout,
+ // we set "device_blocked" in Scsi_Device struct; in this
+ // case 30 seconds will elapse before Linux/Scsi sends another
+ // command to the device.
+ else if( pLoggedInPort->prli != TRUE )
+ {
+// printk("Device (Chnl/Target %d/%d) invalid PRLI, port_id %06lXh\n",
+// Cmnd->channel, Cmnd->target, pLoggedInPort->port_id);
+ QueLinkDownCmnd( cpqfcHBAdata, Cmnd);
+// Need to use "blocked" flag??
+// Cmnd->device->device_blocked = TRUE; // just let it timeout
+ }
+ else // device supports TARGET functions, and is logged in...
+ {
+ // (context of fchs is to "reply" to...)
+ fchs.s_id = pLoggedInPort->port_id; // destination FC address
+
+ // what is the data direction? For data TO the device,
+ // we need IWE (Intiator Write Entry). Otherwise, IRE.
+
+ if( Cmnd->cmnd[0] == WRITE_10 ||
+ Cmnd->cmnd[0] == WRITE_6 ||
+ Cmnd->cmnd[0] == WRITE_BUFFER ||
+ Cmnd->cmnd[0] == VENDOR_WRITE_OPCODE || // CPQ specific
+ Cmnd->cmnd[0] == MODE_SELECT )
+ {
+ SESTtype = SCSI_IWE; // data from HBA to Device
+ }
+ else
+ SESTtype = SCSI_IRE; // data from Device to HBA
+
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ SESTtype, // e.g. Initiator Read Entry (IRE)
+ &fchs, // we are originator; only use d_id
+ Cmnd, // Linux SCSI command (with scatter/gather list)
+ &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+ if( !ulStatus ) // Exchange setup?
+
+ {
+ if( cpqfcHBAdata->BoardLock )
+ {
+ TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+ printk(" @bl! %d, xID %Xh@ ", current->pid, ExchangeID);
+ }
+
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+ if( !ulStatus )
+ {
+ PCI_TRACEO( ExchangeID, 0xB8)
+ // submitted to Tach's Outbound Que (ERQ PI incremented)
+ // waited for completion for ELS type (Login frames issued
+ // synchronously)
+ }
+ else
+ // check reason for Exchange not being started - we might
+ // want to Queue and start later, or fail with error
+ {
+ printk("quecommand: cpqfcTSStartExchange failed: %Xh\n", ulStatus );
+ }
+ } // end good BuildExchange status
+
+ else // SEST table probably full -- why? hardware hang?
+ {
+ printk("quecommand: cpqfcTSBuildExchange faild: %Xh\n", ulStatus);
+ }
+ } // end can't do FCP-SCSI target functions
+ } // end can't find target (FC device)
+
+ CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
+ }
+
+ PCI_TRACEO( (ULONG)Cmnd, 0x9C)
+ LEAVE("cpqfcTS_queuecommand");
+ return 0;
+}
+
+
+// Entry point for upper Scsi layer intiated abort. Typically
+// this is called if the command (for hard disk) fails to complete
+// in 30 seconds. This driver intends to complete all disk commands
+// within Exchange ".timeOut" seconds (now 7) with target status, or
+// in case of ".timeOut" expiration, a DID_SOFT_ERROR which causes
+// immediate retry.
+// If any disk commands get the _abort call, except for the case that
+// the physical device was removed or unavailable due to hardware
+// errors, it should be considered a driver error and reported to
+// the author.
+
+int cpqfcTS_abort(Scsi_Cmnd *Cmnd)
+{
+ struct Scsi_Host *HostAdapter = Cmnd->host;
+ // get the pointer to our Scsi layer HBA buffer
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ int i;
+ ENTER("cpqfcTS_abort");
+
+ Cmnd->result = DID_ABORT <<16; // assume we'll find it
+
+ printk(" @Linux _abort Scsi_Cmnd %p ", Cmnd);
+ // See if we can find a Cmnd pointer that matches...
+ // The most likely case is we accepted the command
+ // from Linux Scsi (e.g. ceated a SEST entry) and it
+ // got lost somehow. If we can't find any reference
+ // to the passed pointer, we can only presume it
+ // got completed as far as our driver is concerned.
+ // If we found it, we will try to abort it through
+ // common mechanism. If FC ABTS is successful (ACC)
+ // or is rejected (RJT) by target, we will call
+ // Scsi "done" quickly. Otherwise, the ABTS will timeout
+ // and we'll call "done" later.
+
+ // Search the SEST exchanges for a matching Cmnd ptr.
+ for( i=0; i< TACH_SEST_LEN; i++)
+ {
+ if( Exchanges->fcExchange[i].Cmnd == Cmnd )
+ {
+
+ // found it!
+ printk(" x_ID %Xh, type %Xh\n", i, Exchanges->fcExchange[i].type);
+
+ Exchanges->fcExchange[i].status = INITIATOR_ABORT; // seconds default
+ Exchanges->fcExchange[i].timeOut = 10; // seconds default (changed later)
+
+ // Since we need to immediately return the aborted Cmnd to Scsi
+ // upper layers, we can't make future reference to any of it's
+ // fields (e.g the Nexus).
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i);
+
+ break;
+ }
+ }
+
+ if( i >= TACH_SEST_LEN ) // didn't find Cmnd ptr in chip's SEST?
+ {
+ // now search our non-SEST buffers (i.e. Cmnd waiting to
+ // start on the HBA or waiting to complete with error for retry).
+
+ // first check BadTargetCmnd
+ for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++)
+ {
+ if( cpqfcHBAdata->BadTargetCmnd[i] == Cmnd )
+ {
+ cpqfcHBAdata->BadTargetCmnd[i] = NULL;
+ printk("in BadTargetCmnd Q\n");
+ goto Done; // exit
+ }
+ }
+
+ // if not found above...
+
+ for( i=0; i < CPQFCTS_REQ_QUEUE_LEN; i++)
+ {
+ if( cpqfcHBAdata->LinkDnCmnd[i] == Cmnd )
+ {
+ cpqfcHBAdata->LinkDnCmnd[i] = NULL;
+ printk("in LinkDnCmnd Q\n");
+ goto Done;
+ }
+ }
+
+
+ for( i=0; i< CPQFCTS_REQ_QUEUE_LEN; i++)
+ { // find spare slot
+ if( cpqfcHBAdata->BoardLockCmnd[i] == Cmnd )
+ {
+ cpqfcHBAdata->BoardLockCmnd[i] = NULL;
+ printk("in BoardLockCmnd Q\n");
+ goto Done;
+ }
+ }
+
+ Cmnd->result = DID_ERROR <<16; // Hmmm...
+ printk("Not found! ");
+// panic("_abort");
+ }
+
+Done:
+
+// panic("_abort");
+ LEAVE("cpqfcTS_abort");
+ return 0; // (see scsi.h)
+}
+
+
+
+
+// To be done...
+int cpqfcTS_reset(Scsi_Cmnd *Cmnd, unsigned int reset_flags)
+{
+ int return_status = SUCCESS;
+
+ ENTER("cpqfcTS_reset");
+
+
+
+
+ LEAVE("cpqfcTS_reset");
+ return return_status;
+}
+
+
+
+/* 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).
+ (from hosts.h)
+*/
+
+int cpqfcTS_biosparam(Disk *disk, kdev_t n, int ip[])
+{
+ int size = disk->capacity;
+
+ ENTER("cpqfcTS_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]);
+ }
+
+ LEAVE("cpqfcTS_biosparam");
+ return 0;
+}
+
+
+
+void cpqfcTS_intr_handler( int irq,
+ void *dev_id,
+ struct pt_regs *regs)
+{
+
+ unsigned long flags, InfLoopBrk=0;
+ struct Scsi_Host *HostAdapter = dev_id;
+ CPQFCHBA *cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata;
+ int MoreMessages = 1; // assume we have something to do
+ UCHAR IntPending;
+
+ ENTER("intr_handler");
+
+ spin_lock_irqsave( &io_request_lock, flags);
+ // is this our INT?
+ IntPending = readb( cpqfcHBA->fcChip.Registers.INTPEND.address);
+
+ // broken boards can generate messages forever, so
+ // prevent the infinite loop
+#define INFINITE_IMQ_BREAK 10000
+ if( IntPending )
+ {
+
+ // mask our HBA interrupts until we handle it...
+ writeb( 0, cpqfcHBA->fcChip.Registers.INTEN.address);
+
+ if( IntPending & 0x4) // "INT" - Tach wrote to IMQ
+ {
+ while( (++InfLoopBrk < INFINITE_IMQ_BREAK) && (MoreMessages ==1) )
+ {
+ MoreMessages = CpqTsProcessIMQEntry( HostAdapter); // ret 0 when done
+ }
+ if( InfLoopBrk >= INFINITE_IMQ_BREAK )
+ {
+ printk("WARNING: Compaq FC adapter generating excessive INTs -REPLACE\n");
+ printk("or investigate alternate causes (e.g. physical FC layer)\n");
+ }
+
+ else // working normally - re-enable INTs and continue
+ writeb( 0x1F, cpqfcHBA->fcChip.Registers.INTEN.address);
+
+ } // (...ProcessIMQEntry() clears INT by writing IMQ consumer)
+ else // indications of errors or problems...
+ // these usually indicate critical system hardware problems.
+ {
+ if( IntPending & 0x10 )
+ printk(" cpqfcTS adapter external memory parity error detected\n");
+ if( IntPending & 0x8 )
+ printk(" cpqfcTS adapter PCI master address crossed 45-bit boundary\n");
+ if( IntPending & 0x2 )
+ printk(" cpqfcTS adapter DMA error detected\n");
+ if( IntPending & 0x1 )
+ printk(" cpqfcTS adapter PCI error detected\n");
+ }
+ }
+ spin_unlock_irqrestore( &io_request_lock, flags);
+ LEAVE("intr_handler");
+}
+
+
+
+
+int cpqfcTSDecodeGBICtype( PTACHYON fcChip, char cErrorString[])
+{
+ // Verify GBIC type (if any) and correct Tachyon Port State Machine
+ // (GBIC) module definition is:
+ // GPIO1, GPIO0, GPIO4 for MD2, MD1, MD0. The input states appear
+ // to be inverted -- i.e., a setting of 111 is read when there is NO
+ // GBIC present. The Module Def (MD) spec says 000 is "no GBIC"
+ // Hard code the bit states to detect Copper,
+ // Long wave (single mode), Short wave (multi-mode), and absent GBIC
+
+ ULONG ulBuff;
+
+ sprintf( cErrorString, "\nGBIC detected: ");
+
+ ulBuff = fcChip->Registers.TYstatus.value & 0x13;
+ switch( ulBuff )
+ {
+ case 0x13: // GPIO4, GPIO1, GPIO0 = 111; no GBIC!
+ sprintf( &cErrorString[ strlen( cErrorString)],
+ "NONE! ");
+ return FALSE;
+
+
+ case 0x11: // Copper GBIC detected
+ sprintf( &cErrorString[ strlen( cErrorString)],
+ "Copper. ");
+ break;
+
+ case 0x10: // Long-wave (single mode) GBIC detected
+ sprintf( &cErrorString[ strlen( cErrorString)],
+ "Long-wave. ");
+ break;
+ case 0x1: // Short-wave (multi mode) GBIC detected
+ sprintf( &cErrorString[ strlen( cErrorString)],
+ "Short-wave. ");
+ break;
+ default: // unknown GBIC - presumably it will work (?)
+ sprintf( &cErrorString[ strlen( cErrorString)],
+ "Unknown. ");
+
+ break;
+ } // end switch GBIC detection
+
+ return TRUE;
+}
+
+
+
+
+
+
+int cpqfcTSGetLPSM( PTACHYON fcChip, char cErrorString[])
+{
+ // Tachyon's Frame Manager LPSM in LinkDown state?
+ // (For non-loop port, check PSM instead.)
+ // return string with state and FALSE is Link Down
+
+ int LinkUp;
+
+ if( fcChip->Registers.FMstatus.value & 0x80 )
+ LinkUp = FALSE;
+ else
+ LinkUp = TRUE;
+
+ sprintf( &cErrorString[ strlen( cErrorString)],
+ " LPSM %Xh ",
+ (fcChip->Registers.FMstatus.value >>4) & 0xf );
+
+
+ switch( fcChip->Registers.FMstatus.value & 0xF0)
+ {
+ // bits set in LPSM
+ case 0x10:
+ sprintf( &cErrorString[ strlen( cErrorString)], "ARB");
+ break;
+ case 0x20:
+ sprintf( &cErrorString[ strlen( cErrorString)], "ARBwon");
+ break;
+ case 0x30:
+ sprintf( &cErrorString[ strlen( cErrorString)], "OPEN");
+ break;
+ case 0x40:
+ sprintf( &cErrorString[ strlen( cErrorString)], "OPENed");
+ break;
+ case 0x50:
+ sprintf( &cErrorString[ strlen( cErrorString)], "XmitCLS");
+ break;
+ case 0x60:
+ sprintf( &cErrorString[ strlen( cErrorString)], "RxCLS");
+ break;
+ case 0x70:
+ sprintf( &cErrorString[ strlen( cErrorString)], "Xfer");
+ break;
+ case 0x80:
+ sprintf( &cErrorString[ strlen( cErrorString)], "Init");
+ break;
+ case 0x90:
+ sprintf( &cErrorString[ strlen( cErrorString)], "O-IInitFin");
+ break;
+ case 0xa0:
+ sprintf( &cErrorString[ strlen( cErrorString)], "O-IProtocol");
+ break;
+ case 0xb0:
+ sprintf( &cErrorString[ strlen( cErrorString)], "O-ILipRcvd");
+ break;
+ case 0xc0:
+ sprintf( &cErrorString[ strlen( cErrorString)], "HostControl");
+ break;
+ case 0xd0:
+ sprintf( &cErrorString[ strlen( cErrorString)], "LoopFail");
+ break;
+ case 0xe0:
+ sprintf( &cErrorString[ strlen( cErrorString)], "Offline");
+ break;
+ case 0xf0:
+ sprintf( &cErrorString[ strlen( cErrorString)], "OldPort");
+ break;
+ case 0:
+ default:
+ sprintf( &cErrorString[ strlen( cErrorString)], "Monitor");
+ break;
+
+ }
+
+ return LinkUp;
+}
+
+
+
+
+#include "linux/malloc.h"
+
+// Dynamic memory allocation alignment routines
+// HP's Tachyon Fibre Channel Controller chips require
+// certain memory queues and register pointers to be aligned
+// on various boundaries, usually the size of the Queue in question.
+// Alignment might be on 2, 4, 8, ... or even 512 byte boundaries.
+// Since most O/Ss don't allow this (usually only Cache aligned -
+// 32-byte boundary), these routines provide generic alignment (after
+// O/S allocation) at any boundary, and store the original allocated
+// pointer for deletion (O/S free function). Typically, we expect
+// these functions to only be called at HBA initialization and
+// removal time (load and unload times)
+// ALGORITHM notes:
+// Memory allocation varies by compiler and platform. In the worst case,
+// we are only assured BYTE allignment, but in the best case, we can
+// request allocation on any desired boundary. Our strategy: pad the
+// allocation request size (i.e. waste memory) so that we are assured
+// of passing desired boundary near beginning of contiguous space, then
+// mask out lower address bits.
+// We define the following algorithm:
+// allocBoundary - compiler/platform specific address alignment
+// in number of bytes (default is single byte; i.e. 1)
+// n_alloc - number of bytes application wants @ aligned address
+// ab - alignment boundary, in bytes (e.g. 4, 32, ...)
+// t_alloc - total allocation needed to ensure desired boundary
+// mask - to clear least significant address bits for boundary
+// Compute:
+// t_alloc = n_alloc + (ab - allocBoundary)
+// allocate t_alloc bytes @ alloc_address
+// mask = NOT (ab - 1)
+// (e.g. if ab=32 _0001 1111 -> _1110 0000
+// aligned_address = alloc_address & mask
+// set n_alloc bytes to 0
+// return aligned_address (NULL if failed)
+//
+// If u32_AlignedAddress is non-zero, then search for BaseAddress (stored
+// from previous allocation). If found, invoke call to FREE the memory.
+// Return NULL if BaseAddress not found
+
+// we need about 8 allocations per HBA. Figuring at most 10 HBAs per server
+// size the dynamic_mem array at 80.
+
+void* fcMemManager( ALIGNED_MEM *dynamic_mem, ULONG n_alloc, ULONG ab,
+ ULONG u32_AlignedAddress)
+{
+ USHORT allocBoundary=1; // compiler specific - worst case 1
+ // best case - replace malloc() call
+ // with function that allocates exactly
+ // at desired boundary
+
+ unsigned long ulAddress;
+ ULONG t_alloc, i;
+ void *alloc_address = 0; // def. error code / address not found
+ LONG mask; // must be 32-bits wide!
+
+ ENTER("fcMemManager");
+ if( u32_AlignedAddress ) // are we freeing existing memory?
+ {
+// printk(" freeing AlignedAddress %Xh\n", u32_AlignedAddress);
+ for( i=0; i<DYNAMIC_ALLOCATIONS; i++) // look for the base address
+ {
+// printk("dynamic_mem[%u].AlignedAddress %lX\n", i, dynamic_mem[i].AlignedAddress);
+ if( dynamic_mem[i].AlignedAddress == u32_AlignedAddress )
+ {
+ alloc_address = dynamic_mem[i].BaseAllocated; // 'success' status
+ kfree( dynamic_mem[i].BaseAllocated); // return pages to kernel
+ dynamic_mem[i].BaseAllocated = 0; // clear for next use
+ dynamic_mem[i].AlignedAddress = 0;
+ break; // quit for loop; done
+ }
+ }
+ }
+ else if( n_alloc ) // want new memory?
+ {
+ t_alloc = n_alloc + (ab - allocBoundary); // pad bytes for alignment
+// printk("kmalloc() for Tach alignment: %ld bytes\n", t_alloc);
+
+ alloc_address = // total bytes (NumberOfBytes)
+ kmalloc( t_alloc, GFP_KERNEL); // allow thread block to free pages
+
+
+ // now mask off least sig. bits of address
+ if( alloc_address ) // (only if non-NULL)
+ {
+ // find place to store ptr, so we
+ // can free it later...
+ for( i=0; i<DYNAMIC_ALLOCATIONS; i++) // look for free slot
+ {
+ if( dynamic_mem[i].BaseAllocated == 0) // take 1st available
+ {
+ dynamic_mem[i].BaseAllocated = alloc_address;// address from O/S
+ break;
+ }
+ }
+ mask = (LONG)(ab - 1); // mask all low-order bits
+ mask = ~mask; // invert bits
+
+ ulAddress = (unsigned long)alloc_address;
+
+ ulAddress += (ab - allocBoundary); // add the alignment bytes-
+ // then truncate address...
+ alloc_address = (void*)(ulAddress & mask);
+
+ dynamic_mem[i].AlignedAddress =
+ (ULONG)(ulAddress & mask); // 32bit Tach address
+ memset( alloc_address, 0, n_alloc ); // clear new memory
+ }
+ else // O/S dynamic mem alloc failed!
+ alloc_address = 0; // (for debugging breakpt)
+
+ }
+
+ LEAVE("fcMemManager");
+ return alloc_address; // good (or NULL) address
+}
+
+
+
+
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = CPQFCTS;
+
+#include "scsi_module.c"
+
+
+#endif
+
+
diff --git a/drivers/scsi/cpqfcTSioctl.h b/drivers/scsi/cpqfcTSioctl.h
new file mode 100644
index 000000000..d883b4dbd
--- /dev/null
+++ b/drivers/scsi/cpqfcTSioctl.h
@@ -0,0 +1,84 @@
+// for user apps, make sure data size types are defined
+// with
+
+
+#define CCPQFCTS_IOC_MAGIC 'Z'
+
+typedef struct
+{
+ __u8 bus;
+ __u8 dev_fn;
+ __u32 board_id;
+} cpqfc_pci_info_struct;
+
+typedef __u32 DriverVer_type;
+/*
+typedef union
+{
+ struct // Peripheral Unit Device
+ {
+ __u8 Bus:6;
+ __u8 Mode:2; // b00
+ __u8 Dev;
+ } PeripDev;
+ struct // Volume Set Address
+ {
+ __u8 DevMSB:6;
+ __u8 Mode:2; // b01
+ __u8 DevLSB;
+ } LogDev;
+ struct // Logical Unit Device (SCSI-3, SCC-2 defined)
+ {
+ __u8 Targ:6;
+ __u8 Mode:2; // b10
+ __u8 Dev:5;
+ __u8 Bus:3;
+
+ } LogUnit;
+} SCSI3Addr_struct;
+
+
+typedef struct
+{
+ SCSI3Addr_struct FCP_Nexus;
+ __u8 cdb[16];
+} PassThru_Command_struct;
+*/
+
+/* this is nearly duplicated in idashare.h */
+typedef struct {
+ int lc; /* Controller number */
+ int node; /* Node (box) number */
+ int ld; /* Logical Drive on this box, if required */
+ __u32 nexus; /* SCSI Nexus */
+ void *argp; /* Argument pointer */
+} VENDOR_IOCTL_REQ;
+
+
+typedef struct {
+ char cdb[16]; /* SCSI CDB for the pass-through */
+ ushort bus; /* Target bus on the box */
+ ushort pdrive; /* Physical drive on the box */
+ int len; /* Length of the data area of the CDB */
+ int sense_len; /* Length of the sense data */
+ char sense_data[40]; /* Sense data */
+ void *bufp; /* Data area for the CDB */
+ char rw_flag; /* Read CDB or Write CDB */
+} cpqfc_passthru_t;
+
+
+
+
+/*
+** Defines for the IOCTLS.
+*/
+
+#define VENDOR_READ_OPCODE 0x26
+#define VENDOR_WRITE_OPCODE 0x27
+
+#define CPQFCTS_GETPCIINFO _IOR( CCPQFCTS_IOC_MAGIC, 1, cpqfc_pci_info_struct)
+#define CPQFCTS_GETDRIVVER _IOR( CCPQFCTS_IOC_MAGIC, 9, DriverVer_type)
+
+#define CPQFCTS_SCSI_PASSTHRU _IOWR( CCPQFCTS_IOC_MAGIC,11, VENDOR_IOCTL_REQ)
+
+
diff --git a/drivers/scsi/cpqfcTSstructs.h b/drivers/scsi/cpqfcTSstructs.h
new file mode 100644
index 000000000..2680228a4
--- /dev/null
+++ b/drivers/scsi/cpqfcTSstructs.h
@@ -0,0 +1,1494 @@
+/* Copyright(c) 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter 64-bit, 66MHz PCI
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ...
+ * SP# P225CXCBFIEL6T, Rev XC
+ * SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * 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.
+ * Written by Don Zimmerman
+*/
+#ifndef CPQFCTSSTRUCTS_H
+#define CPQFCTSSTRUCTS_H
+
+#include <linux/timer.h> // timer declaration in our host data
+#include <linux/tqueue.h> // task queue sched
+#include <asm/atomic.h>
+#include "cpqfcTSioctl.h"
+
+#define DbgDelay(secs) { int wait_time; printk( " DbgDelay %ds ", secs); \
+ for( wait_time=jiffies + (secs*HZ); \
+ wait_time > jiffies ;) ; }
+#define CPQFCTS_DRIVER_VER(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
+#define VER_MAJOR 1
+#define VER_MINOR 3
+#define VER_SUBMINOR 4
+
+// Macros for kernel (esp. SMP) tracing using a PCI analyzer
+// (e.g. x86).
+//#define PCI_KERNEL_TRACE
+#ifdef PCI_KERNEL_TRACE
+#define PCI_TRACE(x) inl( fcChip->Registers.IOBaseL +x);
+#define PCI_TRACEO(x,y) outl( x, (fcChip->Registers.IOBaseL +y));
+#else
+
+#define PCI_TRACE(x)
+#define PCI_TRACEO(x,y)
+#endif
+
+
+//#define DEBUG_CMND 1 // debug output for Linux Scsi CDBs
+//#define DUMMYCMND_DBG 1
+
+//#define DEBUG_CPQFCTS 1
+//#undef DEBUG_CPQFCTS
+#ifdef DEBUG_CPQFCTS
+#define ENTER(x) printk("cpqfcts : entering %s()\n", x);
+#define LEAVE(x) printk("cpqfcts : leaving %s()\n", x);
+#define DEBUG(x) x
+#else
+#define ENTER(x)
+#define LEAVE(x)
+#define DEBUG(x)
+#endif /* DEBUG_CPQFCTS */
+
+//#define DEBUG_CPQFCTS_PCI 1
+//#undef DEBUG_CPQFCTS_PCI
+#if DEBUG_CPQFCTS_PCI
+#define DEBUG_PCI(x) x
+#else
+#define DEBUG_PCI(x)
+#endif /* DEBUG_CPQFCTS_PCI */
+
+#define STACHLITE66_TS12 "Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.2"
+#define STACHLITE66_TS13 "Compaq FibreChannel HBA Tachyon TS HPFC-5166A/1.3"
+#define STACHLITE_UNKNOWN "Compaq FibreChannel HBA Tachyon Chip/Board Ver??"
+#define SAGILENT_XL2_21 "Agilent FC HBA, Tachyon XL2 HPFC-5200B/2.1"
+
+// PDA is Peripheral Device Address, VSA is Volume Set Addressing
+// Linux SCSI parameters
+#define CPQFCTS_MAX_TARGET_ID 64
+#define CPQFCTS_MAX_LUN 8 // The RA-4x00 supports 32 (Linux SCSI supports 8)
+#define CPQFCTS_MAX_CHANNEL 0 // One FC port on cpqfcTS HBA
+
+#define CPQFCTS_CMD_PER_LUN 15 // power of 2 -1, must be >0
+#define CPQFCTS_REQ_QUEUE_LEN (TACH_SEST_LEN/2) // must be < TACH_SEST_LEN
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#ifndef DECLARE_MUTEX_LOCKED
+#define DECLARE_MUTEX_LOCKED(sem) struct semaphore sem = MUTEX_LOCKED
+#endif
+
+#define DEV_NAME "cpqfcTS"
+
+#define CPQ_DEVICE_ID 0xA0FC
+#define AGILENT_XL2_ID 0x1029
+
+typedef struct
+{
+ __u16 vendor_id;
+ __u16 device_id;
+} SupportedPCIcards;
+
+// nn:nn denotes bit field
+ // TachyonHeader struct def.
+ // the fields shared with ODB
+ // need to have same value
+
+
+
+
+#ifndef BYTE
+//typedef UCHAR BYTE;
+typedef __u8 BYTE;
+#endif
+#ifndef UCHAR
+typedef __u8 UCHAR;
+#endif
+#ifndef LONG
+typedef __s32 LONG;
+#endif
+#ifndef ULONG
+typedef __u32 ULONG;
+#endif
+#ifndef PVOID
+typedef void * PVOID;
+#endif
+#ifndef USHORT
+typedef __u16 USHORT;
+#endif
+#ifndef BOOLEAN
+typedef __u8 BOOLEAN;
+#endif
+
+
+// macro for FC-PH reject codes
+// payload format for LS_RJT (FC payloads are big endian):
+// byte 0 1 2 3 (MSB)
+// DWORD 0 01 00 00 00
+// DWORD 1 resvd code expl. vendor
+
+#define LS_RJT_REASON( code, expl) (( code<<8) | (expl <<16))
+
+
+#define TachLiteSTATUS 0x12
+
+// Fibre Channel EXCHANGE status codes for Tachyon chips/ driver software
+// 32-bit ERROR word defines
+#define INVALID_ARGS 0x1
+#define LNKDWN_OSLS 0x2
+#define LNKDWN_LASER 0x4
+#define OUTQUE_FULL 0x8
+#define DRIVERQ_FULL 0x10
+#define SEST_FULL 0x20
+#define BAD_ALPA 0x40
+#define OVERFLOW 0x80 // inbound CM
+#define COUNT_ERROR 0x100 // inbound CM
+#define LINKFAIL_RX 0x200 // inbound CM
+#define ABORTSEQ_NOTIFY 0x400 // outbound CM
+#define LINKFAIL_TX 0x800 // outbound CM
+#define HOSTPROG_ERR 0x1000 // outbound CM
+#define FRAME_TO 0x2000 // outbound CM
+#define INV_ENTRY 0x4000 // outbound CM
+#define SESTPROG_ERR 0x8000 // outbound CM
+#define OUTBOUND_TIMEOUT 0x10000L // timeout waiting for Tachyon outbound CM
+#define INITIATOR_ABORT 0x20000L // initiator exchange timeout or O/S ABORT
+#define MEMPOOL_FAIL 0x40000L // O/S memory pool allocation failed
+#define FC2_TIMEOUT 0x80000L // driver timeout for lost frames
+#define TARGET_ABORT 0x100000L // ABTS received from FC port
+#define EXCHANGE_QUEUED 0x200000L // e.g. Link State was LDn on fcStart
+#define PORTID_CHANGED 0x400000L // fc Port address changed
+#define DEVICE_REMOVED 0x800000L // fc Port address changed
+// Several error scenarios result in SEST Exchange frames
+// unexpectedly arriving in the SFQ
+#define SFQ_FRAME 0x1000000L // SFQ frames from open Exchange
+
+// Maximum number of Host Bus Adapters (HBA) / controllers supported
+// only important for mem allocation dimensions - increase as necessary
+
+#define MAX_ADAPTERS 8
+#define MAX_RX_PAYLOAD 1024 // hardware dependent max frame payload
+// Tach header struc defines
+#define SOFi3 0x7
+#define SOFf 0x8
+#define SOFn3 0xB
+#define EOFn 0x5
+#define EOFt 0x6
+
+// FCP R_CTL defines
+#define FCP_CMND 0x6
+#define FCP_XFER_RDY 0x5
+#define FCP_RSP 0x7
+#define FCP_RESPONSE 0x777 // (arbitrary #)
+#define NEED_FCP_RSP 0x77 // (arbitrary #)
+#define FCP_DATA 0x1
+
+#define RESET_TACH 0x100 // Reset Tachyon/TachLite
+#define SCSI_IWE 0x2000 // initiator write entry (for SEST)
+#define SCSI_IRE 0x3000 // initiator read entry (for SEST)
+#define SCSI_TRE 0x400 // target read entry (for SEST)
+#define SCSI_TWE 0x500 // target write entry (for SEST)
+#define TOGGLE_LASER 0x800
+#define LIP 0x900
+#define CLEAR_FCPORTS 99 // (arbitrary #) free mem for Logged in ports
+#define FMINIT 0x707 // (arbitrary) for Frame Manager Init command
+
+// BLS == Basic Link Service
+// ELS == Extended Link Service
+#define BLS_NOP 4
+#define BLS_ABTS 0x10 // FC-PH Basic Link Service Abort Sequence
+#define BLS_ABTS_ACC 0x100 // FC-PH Basic Link Service Abort Sequence Accept
+#define BLS_ABTS_RJT 0x101 // FC-PH Basic Link Service Abort Sequence Reject
+#define ELS_PLOGI 0x03 // FC-PH Port Login (arbitrary assign)
+#define ELS_SCR 0x70 // (arb assign) State Change Registration (Fabric)
+#define FCS_NSR 0x72 // (arb assign) Name Service Request (Fabric)
+#define ELS_FLOGI 0x44 // (arb assign) Fabric Login
+#define ELS_FDISC 0x41 // (arb assign) Fabric Discovery (Login)
+#define ELS_PDISC 0x50 // FC-PH2 Port Discovery
+#define ELS_ABTX 0x06 // FC-PH Abort Exchange
+#define ELS_LOGO 0x05 // FC-PH Port Logout
+#define ELS_PRLI 0x20 // FCP-SCSI Process Login
+#define ELS_PRLO 0x21 // FCP-SCSI Process Logout
+#define ELS_LOGO_ACC 0x07 // {FC-PH} Port Logout Accept
+#define ELS_PLOGI_ACC 0x08 // {FC-PH} Port Login Accept
+#define ELS_ACC 0x18 // {FC-PH} (generic) ACCept
+#define ELS_PRLI_ACC 0x22 // {FCP-SCSI} Process Login Accept
+#define ELS_RJT 0x1000000
+#define SCSI_REPORT_LUNS 0x0A0
+#define REPORT_LUNS 0xA0 // SCSI-3 command op-code
+
+#define ELS_LILP_FRAME 0x00000711 // 1st payload word of LILP frame
+
+#define SFQ_UNASSISTED_FCP 1 // ICM, DWord3, "Type" unassisted FCP
+#define SFQ_UNKNOWN 0x31 // (arbitrary) ICM, DWord3, "Type" unknown
+
+// these "LINK" bits refer to loop or non-loop
+#define LINKACTIVE 0x2 // fcLinkQ type - LINK UP Tachyon FM 'Lup' bit set
+#define LINKDOWN 0xf2 // fcLinkQ type - LINK DOWN Tachyon FM 'Ldn' bit set
+
+//#define VOLUME_SET_ADDRESSING 1 // "channel" or "bus" 1
+
+typedef struct // 32 bytes hdr ONLY (e.g. FCP_DATA buffer for SEST)
+{
+ ULONG reserved; // dword 0 (don't use)
+ ULONG sof_eof;
+ ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID
+ ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+ ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL
+ ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+ ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID
+ ULONG ro; // dword 7 - relative offset
+} TachFCHDR;
+
+ // NOTE!! the following struct MUST be 64 bytes.
+typedef struct // 32 bytes hdr + 32 bytes payload
+{
+ ULONG reserved; // dword 0 (don't use - must clear to 0)
+ ULONG sof_eof; // dword 1 - 31:24 SOF:EOF, UAM,CLS, LCr, TFV, TimeStamp
+ ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID
+ ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+ ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL
+ ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+ ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID
+ ULONG ro; // dword 7 - relative offset
+//---------
+ __u32 pl[8]; // dwords 8-15 frame data payload
+} TachFCHDR_CMND;
+
+
+typedef struct // 32 bytes hdr + 120 bytes payload
+{
+ ULONG reserved; // dword 0 (don't use - must clear to 0)
+ ULONG sof_eof; // dword 1 - 31:24 SOF:EOF, UAM,CLS, LCr, TFV, TimeStamp
+ ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID
+ ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+ ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL
+ ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+ ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID
+ ULONG ro; // dword 7 - relative offset
+//---------
+ __u32 pl[30]; // largest necessary payload (for LOGIN cmnds)
+} TachFCHDR_GCMND;
+
+typedef struct // 32 bytes hdr + 64 bytes payload
+{
+ ULONG reserved; // dword 0 (don't use)
+ ULONG sof_eof;
+ ULONG d_id; // dword 2 - 31:24 R_CTL, 23:0 D_ID
+ ULONG s_id; // dword 3 - 31:24 CS_CTL, 23:0 S_ID
+ ULONG f_ctl; // dword 4 - 31:24 Type, 23:0 F_CTL
+ ULONG seq_cnt; // dword 5 - 31:24 SEQ_ID, 23:16 DF_CTL, 15:0 SEQ_CNT
+ ULONG ox_rx_id; // dword 6 - 31:16 OX_ID, 15:0 RX_ID
+ ULONG ro; // dword 7 - relative offset
+//---------
+ __u32 pl[18]; // payload for FCP-RSP (response buffer) RA-4x00 is 72bytes
+} TachFCHDR_RSP;
+
+
+
+
+
+
+// Inbound Message Queue structures...
+typedef struct // each entry 8 words (32 bytes)
+{
+ ULONG type; // IMQ completion message types
+ ULONG word[7]; // remainder of structure
+ // interpreted by IMQ type
+} TachyonIMQE;
+
+
+// Queues for TachLite not in original Tachyon
+// ERQ - Exchange Request Queue (for outbound commands)
+// SFQ - Single Frame Queue (for incoming frames)
+
+ // Define Tachyon Outbound Command Que
+ // (Since many Tachyon registers are Read
+ // only, maintain copies for debugging)
+ // most Tach ques need power-of-2 sizes,
+ // where registers are loaded with po2 -1
+#define TACH_SEST_LEN 512 // TachLite SEST
+
+#define ELS_EXCHANGES 64 // e.g. PLOGI, RSCN, ...
+// define the total number of outstanding (simultaneous) exchanges
+#define TACH_MAX_XID (TACH_SEST_LEN + ELS_EXCHANGES) // ELS exchanges
+
+#define ERQ_LEN 128 // power of 2, max 4096
+
+// Inbound Message Queue structures...
+#define IMQ_LEN 512 // minimum 4 entries [(power of 2) - 1]
+typedef struct // 8 words - 32 bytes
+{
+ TachyonIMQE QEntry[IMQ_LEN];
+ ULONG producerIndex; // IMQ Producer Index register
+ // @32 byte align
+ ULONG consumerIndex; // Consumer Index register (in Tachyon)
+ ULONG length; // Length register
+ ULONG base;
+} TachyonIMQ; // @ 32 * IMQ_LEN align
+
+
+
+typedef struct // inbound completion message
+{
+ ULONG Type;
+ ULONG Index;
+ ULONG TransferLength;
+} TachyonInbCM;
+
+
+
+// arbitrary numeric tags for TL structures
+#define TL_FCHS 1 // TachLite Fibre Channel Header Structure
+#define TL_IWE 2 // initiator write entry (for SEST)
+#define TL_TWE 3 // target write entry (for SEST)
+#define TL_IRE 4 // initiator read entry (for SEST)
+#define TL_TRE 5 // target read entry (for SEST)
+#define TL_IRB 6 // I/O request block
+
+ // for INCOMING frames
+#define SFQ_LEN 32 // minimum 32 entries, max 4096
+
+typedef struct // Single Frame Que
+{
+ TachFCHDR_CMND QEntry[SFQ_LEN]; // must be 64 bytes!!
+ ULONG producerIndex; // IMQ Producer Index register
+ // @32 byte align
+ ULONG consumerIndex; // Consumer Index register (in Tachyon)
+ ULONG length; // Length register
+ ULONG base;
+} TachLiteSFQ;
+
+
+typedef struct // I/O Request Block flags
+{
+ UCHAR BRD : 1;
+ UCHAR : 1; // reserved
+ UCHAR SFA : 1;
+ UCHAR DNC : 1;
+ UCHAR DIN : 1;
+ UCHAR DCM : 1;
+ UCHAR CTS : 1;
+ UCHAR SBV : 1; // IRB entry valid - IRB'B' only
+} IRBflags;
+
+typedef struct // I/O Request Block
+{ // Request 'A'
+ ULONG Req_A_SFS_Len; // total frame len (hdr + payload), min 32
+ ULONG Req_A_SFS_Addr; // 32-bit pointer to FCHS struct (to be sent)
+ ULONG Req_A_SFS_D_ID; // 24-bit FC destination (i.e. 8 bit al_pa)
+ ULONG Req_A_Trans_ID; // X_ID (OX_ID or RX_ID) and/or Index in SEST
+ // Request 'B'
+ ULONG Req_B_SFS_Len; // total frame len (hdr + payload), min 32
+ ULONG Req_B_SFS_Addr; // 32-bit pointer to FCHS struct (to be sent)
+ ULONG Req_B_SFS_D_ID; // 24-bit FC destination (i.e. 8 bit al_pa)
+ ULONG Req_B_Trans_ID; // X_ID (OX_ID or RX_ID) and/or Index in SEST
+} TachLiteIRB;
+
+
+typedef struct // TachLite placeholder for IRBs
+{ // aligned @sizeof(ERQ) for TachLite
+ // MAX commands is sum of SEST len and ERQ
+ // we know that each SEST entry requires an
+ // IRB (ERQ) entry; in addition, we provide
+ // ERQ_LEN
+ TachLiteIRB QEntry[ERQ_LEN]; // Base register; entries 32 bytes ea.
+ ULONG consumerIndex; // Consumer Index register
+ ULONG producerIndex; // ERQ Producer Index register
+ ULONG length; // Length register
+ ULONG base; // copy of base ptr for debug
+ // struct is sized for largest expected cmnd (LOGIN)
+} TachLiteERQ;
+
+
+#define TL_MAX_SGPAGES 4 // arbitrary limit to # of TL Ext. S/G pages
+ // stores array of allocated page blocks used
+ // in extended S/G lists. Affects amount of static
+ // memory consumed by driver.
+#define TL_EXT_SG_PAGE_COUNT 256 // Number of Extended Scatter/Gather a/l PAIRS
+ // Tachyon register (IOBaseU 0x68)
+ // power-of-2 value ONLY! 4 min, 256 max
+
+ // byte len is #Pairs * 2 ULONG/Pair * 4 bytes/ULONG
+#define TL_EXT_SG_PAGE_BYTELEN (TL_EXT_SG_PAGE_COUNT *2 *4)
+
+
+
+// SEST entry types: IWE, IRE, TWE, TRE
+typedef struct
+{
+ ULONG Hdr_Len;
+ ULONG Hdr_Addr;
+ ULONG RSP_Len;
+ ULONG RSP_Addr;
+ ULONG Buff_Off;
+ ULONG Link;
+ ULONG RX_ID;
+ ULONG Data_Len;
+ ULONG Exp_RO;
+ ULONG Exp_Byte_Cnt;
+ // --- extended/local Gather Len/Address pairs
+ ULONG GLen1;
+ ULONG GAddr1;
+ ULONG GLen2;
+ ULONG GAddr2;
+ ULONG GLen3;
+ ULONG GAddr3;
+} TachLiteIWE;
+
+
+typedef struct
+{
+ ULONG Seq_Accum;
+ ULONG reserved; // must clear to 0
+ ULONG RSP_Len;
+ ULONG RSP_Addr;
+ ULONG Buff_Off;
+ ULONG Buff_Index; // ULONG 5
+ ULONG Exp_RO;
+ ULONG Byte_Count;
+ ULONG reserved_; // ULONG 8
+ ULONG Exp_Byte_Cnt;
+ // --- extended/local Scatter Len/Address pairs
+ ULONG SLen1;
+ ULONG SAddr1;
+ ULONG SLen2;
+ ULONG SAddr2;
+ ULONG SLen3;
+ ULONG SAddr3;
+} TachLiteIRE;
+
+
+typedef struct // Target Write Entry
+{
+ ULONG Seq_Accum; // dword 0
+ ULONG reserved; // dword 1 must clear to 0
+ ULONG Remote_Node_ID;
+ ULONG reserved1; // dword 3 must clear to 0
+ ULONG Buff_Off;
+ ULONG Buff_Index; // ULONG 5
+ ULONG Exp_RO;
+ ULONG Byte_Count;
+ ULONG reserved_; // ULONG 8
+ ULONG Exp_Byte_Cnt;
+ // --- extended/local Scatter Len/Address pairs
+ ULONG SLen1;
+ ULONG SAddr1;
+ ULONG SLen2;
+ ULONG SAddr2;
+ ULONG SLen3;
+ ULONG SAddr3;
+} TachLiteTWE;
+
+typedef struct
+{
+ ULONG Hdr_Len;
+ ULONG Hdr_Addr;
+ ULONG RSP_Len; // DWord 2
+ ULONG RSP_Addr;
+ ULONG Buff_Off;
+ ULONG Buff_Index; // DWord 5
+ ULONG reserved;
+ ULONG Data_Len;
+ ULONG reserved_;
+ ULONG reserved__;
+ // --- extended/local Gather Len/Address pairs
+ ULONG GLen1; // DWord A
+ ULONG GAddr1;
+ ULONG GLen2;
+ ULONG GAddr2;
+ ULONG GLen3;
+ ULONG GAddr3;
+} TachLiteTRE;
+
+typedef struct
+{
+ void *PoolPage[TL_MAX_SGPAGES];
+} SGPAGES, *PSGPAGES; // linked list of S/G pairs, by Exchange
+
+
+
+typedef struct // SCSI Exchange State Table
+{
+ union // Entry can be IWE, IRE, TWE, TRE
+ { // 64 bytes per entry
+ TachLiteIWE IWE;
+ TachLiteIRE IRE;
+ TachLiteTWE TWE;
+ TachLiteTRE TRE;
+ } u[TACH_SEST_LEN];
+
+ TachFCHDR DataHDR[TACH_SEST_LEN]; // for SEST FCP_DATA frame hdr (no pl)
+ TachFCHDR_RSP RspHDR[TACH_SEST_LEN]; // space for SEST FCP_RSP frame
+ SGPAGES sgPages[TACH_SEST_LEN]; // array of Pool-allocations
+ ULONG length; // Length register
+ ULONG base; // copy of base ptr for debug
+} TachSEST;
+
+
+
+typedef struct // each register has it's own address
+ // and value (used for write-only regs)
+{
+ void* address;
+ volatile ULONG value;
+} FCREGISTER;
+
+typedef struct // Host copy - TachLite Registers
+{
+ ULONG IOBaseL, IOBaseU; // I/O port lower and upper TL register addresses
+ ULONG MemBase; // memory mapped register addresses
+ void* ReMapMemBase; // O/S VM reference for MemBase
+ ULONG wwn_hi; // WWN is set once at startup
+ ULONG wwn_lo;
+ ULONG my_al_pa; // al_pa received after LIP()
+ ULONG ROMCTR; // flags for on-board RAM/ROM
+ ULONG RAMBase; // on-board RAM (i.e. some Tachlites)
+ ULONG SROMBase; // on-board EEPROM (some Tachlites)
+ ULONG PCIMCTR; // PCI Master Control Reg (has bus width)
+
+ FCREGISTER INTEN; // copy of interrupt enable mask
+ FCREGISTER INTPEND; // interrupt pending
+ FCREGISTER INTSTAT; // interrupt status
+ FCREGISTER SFQconsumerIndex;
+ FCREGISTER ERQproducerIndex;
+ FCREGISTER TYconfig; // TachYon (chip level)
+ FCREGISTER TYcontrol;
+ FCREGISTER TYstatus;
+ FCREGISTER FMconfig; // Frame Manager (FC loop level)
+ FCREGISTER FMcontrol;
+ FCREGISTER FMstatus;
+ FCREGISTER FMLinkStatus1;
+ FCREGISTER FMLinkStatus2;
+ FCREGISTER FMBB_CreditZero;
+ FCREGISTER status;
+ FCREGISTER ed_tov; // error detect time-out value
+ FCREGISTER rcv_al_pa; // received arb. loop physical address
+ FCREGISTER primitive; // e.g. LIP(), OPN(), ...
+} TL_REGISTERS;
+
+
+
+typedef struct
+{
+ ULONG ok;
+ ULONG invalidArgs;
+ ULONG linkDown;
+ ULONG linkUp;
+ ULONG outQueFull;
+ ULONG SESTFull;
+ ULONG hpe; // host programming err (from Tach)
+ ULONG FC4aborted; // aborts from Application or upper driver layer
+ ULONG FC2aborted; // aborts from our driver's timeouts
+ ULONG timeouts; // our driver timeout (on individual exchanges)
+ ULONG logouts; // explicit - sent LOGO; implicit - device removed
+ ULONG retries;
+ ULONG linkFailTX;
+ ULONG linkFailRX;
+ ULONG CntErrors; // byte count expected != count received (typ. SEST)
+ ULONG e_stores; // elastic store errs
+ ULONG resets; // hard or soft controller resets
+ ULONG FMinits; // TACH Frame Manager Init (e.g. LIPs)
+ ULONG lnkQueFull; // too many LOGIN, loop commands
+ ULONG ScsiQueFull; // too many FCP-SCSI inbound frames
+ ULONG LossofSignal; // FM link status 1 regs
+ ULONG BadRXChar; // FM link status 1 regs
+ ULONG LossofSync; // FM link status 1 regs
+ ULONG Rx_EOFa; // FM link status 2 regs (received EOFa)
+ ULONG Dis_Frm; // FM link status 2 regs (discarded frames)
+ ULONG Bad_CRC; // FM link status 2 regs
+ ULONG BB0_Timer; // FM BB_Credit Zero Timer Reg
+ ULONG loopBreaks; // infinite loop exits
+ ULONG lastBB0timer; // static accum. buffer needed by Tachlite
+} FCSTATS;
+
+
+typedef struct // Config Options
+{ // LS Bit first
+ USHORT : 1; // bit0:
+ USHORT flogi : 1; // bit1: We sent FLOGI - wait for Fabric logins
+ USHORT fabric: 1; // bit2: Tachyon detected Fabric (FM stat LG)
+ USHORT LILPin: 1; // bit3: We can use an FC-AL LILP frame
+ USHORT target: 1; // bit4: this Port has SCSI target capability
+ USHORT initiator: 1; // bit5: this Port has SCSI initiator capability
+ USHORT extLoopback: 1; // bit6: loopback at GBIC
+ USHORT intLoopback: 1; // bit7: loopback in HP silicon
+ USHORT : 1; // bit8:
+ USHORT : 1; // bit9:
+ USHORT : 1; // bit10:
+ USHORT : 1; // bit11:
+ USHORT : 1; // bit12:
+ USHORT : 1; // bit13:
+ USHORT : 1; // bit14:
+ USHORT : 1; // bit15:
+} FC_OPTIONS;
+
+
+
+typedef struct dyn_mem_pair
+{
+ void *BaseAllocated; // address as allocated from O/S;
+ unsigned long AlignedAddress; // aligned address (used by Tachyon DMA)
+} ALIGNED_MEM;
+
+
+
+
+// these structs contain only CRUCIAL (stuff we actually use) parameters
+// from FC-PH(n) logins. (Don't save entire LOGIN payload to save mem.)
+
+// Implicit logout happens when the loop goes down - we require PDISC
+// to restore. Explicit logout is when WE decide never to talk to someone,
+// or when a target refuses to talk to us, i.e. sends us a LOGO frame or
+// LS_RJT reject in response to our PLOGI request.
+
+#define IMPLICIT_LOGOUT 1
+#define EXPLICIT_LOGOUT 2
+
+typedef struct
+{
+ UCHAR channel; // SCSI "bus"
+ UCHAR target;
+ UCHAR InqDeviceType; // byte 0 from SCSI Inquiry response
+ UCHAR VolumeSetAddressing; // FCP-SCSI LUN coding (40h for VSA)
+ UCHAR LunMasking; // True if selective presentation supported
+ UCHAR lun[CPQFCTS_MAX_LUN];
+} SCSI_NEXUS;
+
+
+typedef struct
+{
+ union
+ {
+ UCHAR ucWWN[8]; // a FC 64-bit World Wide Name/ PortID of target
+ // addressing of single target on single loop...
+ u64 liWWN;
+ } u;
+
+ ULONG port_id; // a FC 24-bit address of port (lower 8 bits = al_pa)
+
+ Scsi_Cmnd ScsiCmnd; // command buffer for Report Luns
+#define REPORT_LUNS_PL 256
+ UCHAR ReportLunsPayload[REPORT_LUNS_PL];
+
+ SCSI_NEXUS ScsiNexus; // LUNs per FC device
+
+ ULONG LOGO_counter; // might try several times before logging out for good
+ ULONG LOGO_timer; // after LIP, ports expecting PDISC must time-out and
+ // LOGOut if successful PDISC not completed in 2 secs
+
+ ULONG concurrent_seq; // must be 1 or greater
+ ULONG rx_data_size; // e.g. 128, 256, 1024, 2048 per FC-PH spec
+ ULONG BB_credit;
+ ULONG EE_credit;
+
+ ULONG fcp_info; // from PRLI (i.e. INITIATOR/ TARGET flags)
+ // flags for login process
+ BOOLEAN Originator; // Login sequence Originated (if false, we
+ // responded to another port's login sequence)
+ BOOLEAN plogi; // PLOGI frame ACCepted (originated or responded)
+ BOOLEAN pdisc; // PDISC frame was ORIGINATED (self-login logic)
+ BOOLEAN prli; // PRLI frame ACCepted (originated or responded)
+ BOOLEAN flogi; // FLOGI frame ACCepted (originated or responded)
+ BOOLEAN logo; // port permanently logged out (invalid login param)
+ BOOLEAN flogiReq; // Fabric login required (set in LIP process)
+ UCHAR highest_ver;
+ UCHAR lowest_ver;
+
+
+ // when the "target" (actually FC Port) is waiting for login
+ // (e.g. after Link reset), set the device_blocked bit;
+ // after Port completes login, un-block target.
+ UCHAR device_blocked; // see Scsi_Device struct
+
+ // define singly-linked list of logged-in ports
+ // once a port_id is identified, it is remembered,
+ // even if the port is removed indefinitely
+ PVOID pNextPort; // actually, type PFC_LOGGEDIN_PORT; void for Compiler
+
+} FC_LOGGEDIN_PORT, *PFC_LOGGEDIN_PORT;
+
+
+
+// This serves as the ESB (Exchange Status Block),
+// and has timeout counter; used for ABORTs
+typedef struct
+{ // FC-1 X_IDs
+ ULONG type; // ELS_PLOGI, SCSI_IWE, ... (0 if free)
+ PFC_LOGGEDIN_PORT pLoggedInPort; // FC device on other end of Exchange
+ Scsi_Cmnd *Cmnd; // Linux SCSI command packet includes S/G list
+ ULONG timeOut; // units of ??, DEC by driver, Abort when 0
+ ULONG reTries; // need one or more retries?
+ ULONG status; // flags indicating errors (0 if none)
+ TachLiteIRB IRB; // I/O Request Block, gets copied to ERQ
+ TachFCHDR_GCMND fchs; // location of IRB's Req_A_SFS_Addr
+} FC_EXCHANGE, *PFC_EXCHANGE;
+
+// Unfortunately, Linux limits our kmalloc() allocations to 128k.
+// Because of this and the fact that our ScsiRegister allocation
+// is also constrained, we move this large structure out for
+// allocation after Scsi Register.
+// (In other words, this cumbersome indirection is necessary
+// because of kernel memory allocation constraints!)
+
+typedef struct // we will allocate this dynamically
+{
+ FC_EXCHANGE fcExchange[ TACH_MAX_XID ];
+} FC_EXCHANGES;
+
+
+
+
+
+
+
+
+
+
+
+typedef struct
+{
+ char Name[64]; // name of controller ("HP Tachlite TL Rev2.0, 33MHz, 64bit bus")
+ //PVOID pAdapterDevExt; // back pointer to device object/extension
+ ULONG ChipType; // local numeric key for Tachyon Type / Rev.
+ ULONG status; // our Driver - logical status
+
+ TL_REGISTERS Registers; // reg addresses & host memory copies
+ // FC-4 mapping of 'transaction' to X_IDs
+ UCHAR LILPmap[32*4]; // Loop Position Map of ALPAs (late FC-AL only)
+ FC_OPTIONS Options; // e.g. Target, Initiator, loopback...
+ UCHAR highest_FCPH_ver; // FC-PH version limits
+ UCHAR lowest_FCPH_ver; // FC-PH version limits
+
+ FC_EXCHANGES *Exchanges;
+ ULONG fcLsExchangeLRU; // Least Recently Used counter (Link Service)
+ ULONG fcSestExchangeLRU; // Least Recently Used counter (FCP-SCSI)
+ FC_LOGGEDIN_PORT fcPorts; // linked list of every FC port ever seen
+ FCSTATS fcStats; // FC comm err counters
+
+ // Host memory QUEUE pointers
+ TachLiteERQ *ERQ; // Exchange Request Que
+ TachyonIMQ *IMQ; // Inbound Message Que
+ TachLiteSFQ *SFQ; // Single Frame Queue
+ TachSEST *SEST; // SCSI Exchange State Table
+
+ // these function pointers are for "generic" functions, which are
+ // replaced with Host Bus Adapter types at
+ // runtime.
+ int (*CreateTachyonQues)( void* , int);
+ int (*DestroyTachyonQues)( void* , int);
+ int (*LaserControl)(void*, int ); // e.g. On/Off
+ int (*ResetTachyon)(void*, int );
+ void (*FreezeTachyon)(void*, int );
+ void (*UnFreezeTachyon)(void*, int );
+ int (*InitializeTachyon)(void*, int, int );
+ int (*InitializeFrameManager)(void*, int );
+ int (*ProcessIMQEntry)(void*);
+ int (*ReadWriteWWN)(void*, int ReadWrite);
+ int (*ReadWriteNVRAM)(void*, void*, int ReadWrite);
+
+} TACHYON, *PTACHYON;
+
+
+void cpqfcTSClearLinkStatusCounters(TACHYON * fcChip);
+
+int CpqTsCreateTachLiteQues( void* pHBA, int opcode);
+int CpqTsDestroyTachLiteQues( void* , int);
+int CpqTsInitializeTachLite( void *pHBA, int opcode1, int opcode2);
+
+int CpqTsProcessIMQEntry(void* pHBA);
+int CpqTsResetTachLite(void *pHBA, int type);
+void CpqTsFreezeTachlite(void *pHBA, int type);
+void CpqTsUnFreezeTachlite(void *pHBA, int type);
+int CpqTsInitializeFrameManager(void *pHBA, int);
+int CpqTsLaserControl( void* addrBase, int opcode );
+int CpqTsReadWriteWWN(void*, int ReadWrite);
+int CpqTsReadWriteNVRAM(void*, void* data, int ReadWrite);
+
+void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter);
+void cpqfcTSWorkerThread( void *host);
+
+int cpqfcTS_GetNVRAM_data( UCHAR *wwnbuf, UCHAR *buf );
+ULONG cpqfcTS_ReadNVRAM( void* GPIOin, void* GPIOout , USHORT count,
+ UCHAR *buf );
+
+BOOLEAN tl_write_i2c_nvram( void* GPIOin, void* GPIOout,
+ USHORT startOffset, // e.g. 0x2f for WWN start
+ USHORT count,
+ UCHAR *buf );
+
+
+// define misc functions
+int cpqfcTSGetLPSM( PTACHYON fcChip, char cErrorString[]);
+int cpqfcTSDecodeGBICtype( PTACHYON fcChip, char cErrorString[]);
+void* fcMemManager( ALIGNED_MEM *dyn_mem_pair, ULONG n_alloc, ULONG ab,
+ ULONG ulAlignedAddress);
+
+void BigEndianSwap( UCHAR *source, UCHAR *dest, USHORT cnt);
+
+//ULONG virt_to_phys( PVOID virtaddr );
+
+
+// Linux interrupt handler
+void cpqfcTS_intr_handler( int irq,void *dev_id,struct pt_regs *regs);
+void cpqfcTSheartbeat( unsigned long ptr );
+
+
+
+// The biggest Q element we deal with is Aborts - we
+// need 4 bytes for x_ID, and a Scsi_Cmnd (~284 bytes)
+//#define LINKQ_ITEM_SIZE ((4+sizeof(Scsi_Cmnd)+3)/4)
+#define LINKQ_ITEM_SIZE (3*16)
+typedef struct
+{
+ ULONG Type; // e.g. LINKUP, SFQENTRY, PDISC, BLS_ABTS, ...
+ ULONG ulBuff[ LINKQ_ITEM_SIZE ];
+} LINKQ_ITEM;
+
+#define FC_LINKQ_DEPTH TACH_MAX_XID
+typedef struct
+{
+ ULONG producer;
+ ULONG consumer; // when producer equals consumer, Q empty
+
+ LINKQ_ITEM Qitem[ FC_LINKQ_DEPTH ];
+
+} FC_LINK_QUE, *PFC_LINK_QUE;
+
+
+ // DPC routines post to here on Inbound SCSI frames
+ // User thread processes
+#define FC_SCSIQ_DEPTH 32
+
+typedef struct
+{
+ int Type; // e.g. SCSI
+ ULONG ulBuff[ 3*16 ];
+} SCSIQ_ITEM;
+
+typedef struct
+{
+ ULONG producer;
+ ULONG consumer; // when producer equals consumer, Q empty
+
+ SCSIQ_ITEM Qitem[ FC_SCSIQ_DEPTH ];
+
+} FC_SCSI_QUE, *PFC_SCSI_QUE;
+
+
+
+
+
+#define DYNAMIC_ALLOCATIONS 4 // Tachyon aligned allocations: ERQ,IMQ,SFQ,SEST
+
+// Linux space allocated per HBA (chip state, etc.)
+typedef struct
+{
+ struct Scsi_Host *HostAdapter; // back pointer to Linux Scsi struct
+
+ TACHYON fcChip; // All Tachyon registers, Queues, functions
+ ALIGNED_MEM dynamic_mem[DYNAMIC_ALLOCATIONS];
+
+ struct pci_dev *PciDev;
+
+ Scsi_Cmnd *LinkDnCmnd[CPQFCTS_REQ_QUEUE_LEN]; // collects Cmnds during LDn
+ // (for Acceptable targets)
+ Scsi_Cmnd *BoardLockCmnd[CPQFCTS_REQ_QUEUE_LEN]; // SEST was full
+
+ Scsi_Cmnd *BadTargetCmnd[CPQFCTS_MAX_TARGET_ID]; // missing targets
+
+ u_char HBAnum; // 0-based host number
+
+
+ struct timer_list cpqfcTStimer; // FC utility timer for implicit
+ // logouts, FC protocol timeouts, etc.
+ int fcStatsTime; // Statistics delta reporting time
+
+ struct task_struct *worker_thread; // our kernel thread
+ int PortDiscDone; // set by SendLogins(), cleared by LDn
+
+ struct semaphore *TachFrozen;
+ struct semaphore *TYOBcomplete; // handshake for Tach outbound frames
+ struct semaphore *fcQueReady; // FibreChannel work for our kernel thread
+ struct semaphore *notify_wt; // synchronizes kernel thread kill
+ struct semaphore *BoardLock;
+
+ PFC_LINK_QUE fcLQ; // the WorkerThread operates on this
+
+ spinlock_t hba_spinlock; // held/released by WorkerThread
+
+} CPQFCHBA;
+
+#define CPQ_SPINLOCK_HBA( x ) spin_lock(&x->hba_spinlock);
+#define CPQ_SPINUNLOCK_HBA(x) spin_unlock(&x->hba_spinlock);
+
+
+
+void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata,
+ PFC_LOGGEDIN_PORT pFcPort);
+
+
+void cpqfcTSTerminateExchange( CPQFCHBA*, SCSI_NEXUS *target, int );
+
+PFC_LOGGEDIN_PORT fcPortLoggedIn(
+ CPQFCHBA *cpqfcHBAdata,
+ TachFCHDR_GCMND* fchs,
+ BOOLEAN,
+ BOOLEAN);
+void fcProcessLoggedIn(
+ CPQFCHBA *cpqfcHBAdata, TachFCHDR_GCMND* fchs);
+
+
+ULONG cpqfcTSBuildExchange(
+ CPQFCHBA *cpqfcHBAdata,
+ ULONG type, // e.g. PLOGI
+ TachFCHDR_GCMND* InFCHS, // incoming FCHS
+ void *Data, // the CDB, scatter/gather, etc.
+ LONG *ExchangeID ); // allocated exchange ID
+
+ULONG cpqfcTSStartExchange(
+ CPQFCHBA *cpqfcHBAdata,
+ LONG ExchangeID );
+
+void cpqfcTSCompleteExchange(
+ PTACHYON fcChip,
+ ULONG exchange_ID);
+
+
+PFC_LOGGEDIN_PORT fcFindLoggedInPort(
+ PTACHYON fcChip,
+ Scsi_Cmnd *Cmnd, // (We want the channel/target/lun Nexus from Cmnd)
+ ULONG port_id, // search linked list for al_pa, or
+ UCHAR wwn[8], // search linked list for WWN, or...
+ PFC_LOGGEDIN_PORT *pLastLoggedInPort
+);
+
+// don't do this unless you have the right hardware!
+#define TRIGGERABLE_HBA 1
+#ifdef TRIGGERABLE_HBA
+void TriggerHBA( void*, int);
+#endif
+
+void cpqfcTSPutLinkQue(
+ CPQFCHBA *cpqfcHBAdata,
+ int Type,
+ void *QueContent);
+
+void fcPutScsiQue(
+ CPQFCHBA *cpqfcHBAdata,
+ int Type,
+ void *QueContent);
+
+void fcLinkQReset(
+ CPQFCHBA *);
+void fcScsiQReset(
+ CPQFCHBA *);
+void fcSestReset(
+ CPQFCHBA *);
+
+
+
+
+
+extern const UCHAR valid_al_pa[];
+extern const int number_of_al_pa;
+
+#define FCP_RESID_UNDER 0x80000
+#define FCP_RESID_OVER 0x40000
+#define FCP_SNS_LEN_VALID 0x20000
+#define FCP_RSP_LEN_VALID 0x10000
+
+// RSP_CODE definitions (dpANS Fibre Channel Protocol for SCSI, pg 34)
+#define FCP_DATA_LEN_NOT_BURST_LEN 0x1000000
+#define FCP_CMND_FIELD_INVALID 0x2000000
+#define FCP_DATA_RO_NOT_XRDY_RO 0x3000000
+#define FCP_TASKFUNCTION_NS 0x4000000
+#define FCP_TASKFUNCTION_FAIL 0x5000000
+
+// FCP-SCSI response status struct
+typedef struct // see "TachFCHDR_RSP" definition - 64 bytes
+{
+ __u32 reserved;
+ __u32 reserved1;
+ __u32 fcp_status; // field validity and SCSI status
+ __u32 fcp_resid;
+ __u32 fcp_sns_len; // length of FCP_SNS_INFO field
+ __u32 fcp_rsp_len; // length of FCP_RSP_INFO field (expect 8)
+ __u32 fcp_rsp_info; // 4 bytes of FCP protocol response information
+ __u32 fcp_rsp_info2; // (4 more bytes, since most implementations use 8)
+ __u8 fcp_sns_info[36]; // bytes for SCSI sense (ASC, ASCQ)
+
+} FCP_STATUS_RESPONSE, *PFCP_STATUS_RESPONSE;
+
+
+// Fabric State Change Registration
+typedef struct scrpl
+{
+ __u32 command;
+ __u32 function;
+} SCR_PL;
+
+// Fabric Name Service Request
+typedef struct nsrpl
+{
+ __u32 CT_Rev; // (& IN_ID) WORD 0
+ __u32 FCS_Type; // WORD 1
+ __u32 Command_code; // WORD 2
+ __u32 reason_code; // WORD 3
+ __u32 FCP; // WORD 4 (lower byte)
+
+} NSR_PL;
+
+
+
+// "FC.H"
+#define MAX_RX_SIZE 0x800 // Max Receive Buffer Size is 2048
+#define MIN_RX_SIZE 0x100 // Min Size is 256, per FC-PLDA Spec
+#define MAX_TARGET_RXIDS SEST_DEPTH
+#define TARGET_RX_SIZE SEST_BUFFER_LENGTH
+
+#define CLASS_1 0x01
+#define CLASS_2 0x02
+#define CLASS_3 0x03
+
+#define FC_PH42 0x08
+#define FC_PH43 0x09
+#define FC_PH3 0x20
+
+#define RR_TOV 2 // Minimum Time for target to wait for
+ // PDISC after a LIP.
+#define E_D_TOV 2 // Minimum Time to wait for Sequence
+ // Completion.
+#define R_A_TOV 0 // Minimum Time for Target to wait
+ // before reclaiming resources.
+//
+// R_CTL Field
+//
+// Routing Bits (31-28)
+//
+#define FC4_DEVICE_DATA 0x00000000
+#define EXT_LINK_DATA 0x20000000
+#define FC4_LINK_DATA 0x30000000
+#define VIDEO_DATA 0x40000000
+#define BASIC_LINK_DATA 0x80000000
+#define LINK_CONTROL 0xC0000000
+#define ROUTING_MASK 0xF0000000
+
+//
+// Information Bits (27-24)
+//
+#define UNCAT_INFORMATION 0x00000000
+#define SOLICITED_DATA 0x01000000
+#define UNSOLICITED_CONTROL 0x02000000
+#define SOLICITED_CONTROL 0x03000000
+#define UNSOLICITED_DATA 0x04000000
+#define DATA_DESCRIPTOR 0x05000000
+#define UNSOLICITED_COMMAND 0x06000000
+#define COMMAND_STATUS 0x07000000
+#define INFO_MASK 0x0F000000
+//
+// (Link Control Codes)
+//
+#define ACK_1 0x00000000
+#define ACK_0_OR_N 0x01000000
+#define P_RJT 0x02000000
+#define F_RJT 0x03000000
+#define P_BSY 0x04000000
+#define FABRIC_BUSY_TO_DF 0x05000000 // Fabric Busy to Data Frame
+#define FABRIC_BUSY_TO_LC 0x06000000 // Fabric Busy to Link Ctl Frame
+#define LINK_CREDIT_RESET 0x07000000
+//
+// (Link Service Command Codes)
+//
+//#define LS_RJT 0x01000000 // LS Reject
+
+#define LS_ACC 0x02000000 // LS Accept
+#define LS_PLOGI 0x03000000 // N_PORT Login
+#define LS_FLOGI 0x04000000 // F_PORT Login
+#define LS_LOGO 0x05000000 // Logout
+#define LS_ABTX 0x06000000 // Abort Exchange
+#define LS_RCS 0x07000000 // Read Connection Status
+#define LS_RES 0x08000000 // Read Exchange Status
+#define LS_RSS 0x09000000 // Read Sequence Status
+#define LS_RSI 0x0A000000 // Request Seq Initiative
+#define LS_ESTS 0x0B000000 // Establish Steaming
+#define LS_ESTC 0x0C000000 // Estimate Credit
+#define LS_ADVC 0x0D000000 // Advice Credit
+#define LS_RTV 0x0E000000 // Read Timeout Value
+#define LS_RLS 0x0F000000 // Read Link Status
+#define LS_ECHO 0x10000000 // Echo
+#define LS_TEST 0x11000000 // Test
+#define LS_RRQ 0x12000000 // Reinstate Rec. Qual.
+#define LS_PRLI 0x20000000 // Process Login
+#define LS_PRLO 0x21000000 // Process Logout
+#define LS_TPRLO 0x24000000 // 3rd Party Process Logout
+#define LS_PDISC 0x50000000 // Process Discovery
+#define LS_FDISC 0x51000000 // Fabric Discovery
+#define LS_ADISC 0x52000000 // Discover Address
+#define LS_RNC 0x53000000 // Report Node Capability
+#define LS_SCR 0x62000000 // State Change Registration
+#define LS_MASK 0xFF000000
+
+//
+// TYPE Bit Masks
+//
+#define BASIC_LINK_SERVICE 0x00000000
+#define EXT_LINK_SERVICE 0x01000000
+
+#define LLC 0x04000000
+#define LLC_SNAP 0x05000000
+#define SCSI_FCP 0x08000000
+#define SCSI_GPP 0x09000000
+#define IPI3_MASTER 0x11000000
+#define IPI3_SLAVE 0x12000000
+#define IPI3_PEER 0x13000000
+#define CP_IPI3_MASTER 0x15000000
+#define CP_IPI3_SLAVE 0x16000000
+#define CP_IPI3_PEER 0x17000000
+#define SBCCS_CHANNEL 0x19000000
+#define SBCCS_CONTROL 0x1A000000
+#define FIBRE_SERVICES 0x20000000
+#define FC_FG 0x21000000
+#define FC_XS 0x22000000
+#define FC_AL 0x23000000
+#define SNMP 0x24000000
+#define HIPPI_FP 0x40000000
+#define TYPE_MASK 0xFF000000
+
+typedef struct {
+ UCHAR seq_id_valid;
+ UCHAR seq_id;
+ USHORT reserved; // 2 bytes reserved
+ ULONG ox_rx_id;
+ USHORT low_seq_cnt;
+ USHORT high_seq_cnt;
+} BA_ACC_PAYLOAD;
+
+typedef struct {
+ UCHAR reserved;
+ UCHAR reason_code;
+ UCHAR reason_explain;
+ UCHAR vendor_unique;
+} BA_RJT_PAYLOAD;
+
+
+typedef struct {
+ ULONG command_code;
+ ULONG sid;
+ USHORT ox_id;
+ USHORT rx_id;
+} RRQ_MESSAGE;
+
+typedef struct {
+ ULONG command_code;
+ UCHAR vendor;
+ UCHAR explain;
+ UCHAR reason;
+ UCHAR reserved;
+} REJECT_MESSAGE;
+
+
+#define N_OR_F_PORT 0x1000
+#define RANDOM_RELATIVE_OFFSET 0x4000
+#define CONTINUOSLY_INCREASING 0x8000
+
+#define CLASS_VALID 0x8000
+#define INTERMIX_MODE 0x4000
+#define TRANSPARENT_STACKED 0x2000
+#define LOCKDOWN_STACKED 0x1000
+#define SEQ_DELIVERY 0x800
+
+#define XID_NOT_SUPPORTED 0x00
+#define XID_SUPPORTED 0x4000
+#define XID_REQUIRED 0xC000
+
+#define ASSOCIATOR_NOT_SUPPORTED 0x00
+#define ASSOCIATOR_SUPPORTED 0x1000
+#define ASSOCIATOR_REQUIRED 0x3000
+
+#define INIT_ACK0_SUPPORT 0x800
+#define INIT_ACKN_SUPPORT 0x400
+
+#define RECIP_ACK0_SUPPORT 0x8000
+#define RECIP_ACKN_SUPPORT 0x4000
+
+#define X_ID_INTERLOCK 0x2000
+
+#define ERROR_POLICY 0x1800 // Error Policy Supported
+#define ERROR_DISCARD 0x00 // Only Discard Supported
+#define ERROR_DISC_PROCESS 0x02 // Discard and process supported
+
+#define NODE_ID 0x01
+#define IEEE_EXT 0x20
+
+//
+// Categories Supported Per Sequence
+//
+#define CATEGORIES_PER_SEQUENCE 0x300
+#define ONE_CATEGORY_SEQUENCE 0x00 // 1 Category per Sequence
+#define TWO_CATEGORY_SEQUENCE 0x01 // 2 Categories per Sequence
+#define MANY_CATEGORY_SEQUENCE 0x03 // > 2 Categories/Sequence
+
+typedef struct {
+
+ USHORT initiator_control;
+ USHORT service_options;
+
+ USHORT rx_data_size;
+ USHORT recipient_control;
+
+ USHORT ee_credit;
+ USHORT concurrent_sequences;
+
+ USHORT reserved;
+ USHORT open_sequences;
+
+} CLASS_PARAMETERS;
+
+typedef struct {
+ ULONG login_cmd;
+ //
+ // Common Service Parameters
+ //
+ struct {
+
+ USHORT bb_credit;
+ UCHAR lowest_ver;
+ UCHAR highest_ver;
+
+ USHORT bb_rx_size;
+ USHORT common_features;
+
+ USHORT rel_offset;
+ USHORT concurrent_seq;
+
+
+ ULONG e_d_tov;
+ } cmn_services;
+
+ //
+ // Port Name
+ //
+ UCHAR port_name[8];
+
+ //
+ // Node/Fabric Name
+ //
+ UCHAR node_name[8];
+
+ //
+ // Class 1, 2 and 3 Service Parameters
+ //
+ CLASS_PARAMETERS class1;
+ CLASS_PARAMETERS class2;
+ CLASS_PARAMETERS class3;
+
+ ULONG reserved[4];
+
+ //
+ // Vendor Version Level
+ //
+ UCHAR vendor_id[2];
+ UCHAR vendor_version[6];
+ ULONG buffer_size;
+ USHORT rxid_start;
+ USHORT total_rxids;
+} LOGIN_PAYLOAD;
+
+
+typedef struct
+{
+ ULONG cmd; // 4 bytes
+ UCHAR n_port_identifier[3];
+ UCHAR reserved;
+ UCHAR port_name[8];
+} LOGOUT_PAYLOAD;
+
+
+//
+// PRLI Request Service Parameter Defines
+//
+#define PRLI_ACC 0x01
+#define PRLI_REQ 0x02
+#define ORIG_PROCESS_ASSOC_VALID 0x8000
+#define RESP_PROCESS_ASSOC_VALID 0x4000
+#define ESTABLISH_PAIR 0x2000
+#define DATA_OVERLAY_ALLOWED 0x40
+#define INITIATOR_FUNCTION 0x20
+#define TARGET_FUNCTION 0x10
+#define CMD_DATA_MIXED 0x08
+#define DATA_RESP_MIXED 0x04
+#define READ_XFER_RDY 0x02
+#define WRITE_XFER_RDY 0x01
+
+#define RESPONSE_CODE_MASK 0xF00
+#define REQUEST_EXECUTED 0x100
+#define NO_RESOURCES 0x200
+#define INIT_NOT_COMPLETE 0x300
+#define IMAGE_DOES_NOT_EXIST 0x400
+#define BAD_PREDEFINED_COND 0x500
+#define REQ_EXEC_COND 0x600
+#define NO_MULTI_PAGE 0x700
+
+typedef struct {
+ USHORT payload_length;
+ UCHAR page_length;
+ UCHAR cmd;
+
+
+ ULONG valid;
+
+ ULONG orig_process_associator;
+
+ ULONG resp_process_associator;
+
+ ULONG fcp_info;
+} PRLI_REQUEST;
+
+typedef struct {
+
+ USHORT payload_length;
+ UCHAR page_length;
+ UCHAR cmd;
+
+ ULONG valid;
+ ULONG orig_process_associator;
+
+ ULONG resp_process_associator;
+ ULONG reserved;
+} PRLO_REQUEST;
+
+typedef struct {
+ ULONG cmd;
+
+ ULONG hard_address;
+
+ UCHAR port_name[8];
+
+ UCHAR node_name[8];
+
+ ULONG s_id;
+} ADISC_PAYLOAD;
+
+// J. McCarty's LINK.H
+//
+// LS_RJT Reason Codes
+//
+
+#define INVALID_COMMAND_CODE 0x01
+#define LOGICAL_ERROR 0x03
+#define LOGICAL_BUSY 0x05
+#define PROTOCOL_ERROR 0x07
+#define UNABLE_TO_PERFORM 0x09
+#define COMMAND_NOT_SUPPORTED 0x0B
+#define LS_VENDOR_UNIQUE 0xFF
+
+//
+// LS_RJT Reason Codes Explanations
+//
+#define NO_REASON 0x00
+#define OPTIONS_ERROR 0x01
+#define INITIATOR_CTL_ERROR 0x03
+#define RECIPIENT_CTL_ERROR 0x05
+#define DATA_FIELD_SIZE_ERROR 0x07
+#define CONCURRENT_SEQ_ERROR 0x09
+#define CREDIT_ERROR 0x0B
+#define INVALID_PORT_NAME 0x0D
+#define INVALID_NODE_NAME 0x0E
+#define INVALID_CSP 0x0F // Invalid Service Parameters
+#define INVALID_ASSOC_HDR 0x11 // Invalid Association Header
+#define ASSOC_HDR_REQUIRED 0x13 // Association Header Required
+#define LS_INVALID_S_ID 0x15
+#define INVALID_OX_RX_ID 0x17 // Invalid OX_ID RX_ID Combination
+#define CMD_IN_PROCESS 0x19
+#define INVALID_IDENTIFIER 0x1F // Invalid N_PORT Identifier
+#define INVALID_SEQ_ID 0x21
+#define ABT_INVALID_XCHNG 0x23 // Attempt to Abort an invalid Exchange
+#define ABT_INACTIVE_XCHNG 0x25 // Attempt to Abort an inactive Exchange
+#define NEED_REC_QUAL 0x27 // Recovery Qualifier required
+#define NO_LOGIN_RESOURCES 0x29 // No resources to support login
+#define NO_DATA 0x2A // Unable to supply requested data
+#define REQUEST_NOT_SUPPORTED 0x2C // Request Not Supported
+
+//
+// Link Control Codes
+//
+
+//
+// P_BSY Action Codes
+//
+#define SEQUENCE_TERMINATED 0x01000000
+#define SEQUENCE_ACTIVE 0x02000000
+
+//
+// P_BSY Reason Codes
+//
+#define PHYS_NPORT_BUSY 0x010000
+#define NPORT_RESOURCE_BUSY 0x020000
+
+//
+// P_RJT, F_RJT Action Codes
+//
+
+#define RETRYABLE_ERROR 0x01000000
+#define NON_RETRYABLE_ERROR 0x02000000
+
+//
+// P_RJT, F_RJT Reason Codes
+//
+#define INVALID_D_ID 0x010000
+#define INVALID_S_ID 0x020000
+#define NPORT_NOT_AVAIL_TMP 0x030000
+#define NPORT_NOT_AVAIL_PERM 0x040000
+#define CLASS_NOT_SUPPORTED 0x050000
+#define USAGE_ERROR 0x060000
+#define TYPE_NOT_SUPPORTED 0x070000
+#define INVAL_LINK_CONTROL 0x080000
+#define INVAL_R_CTL 0x090000
+#define INVAL_F_CTL 0x0A0000
+#define INVAL_OX_ID 0x0B0000
+#define INVAL_RX_ID 0x0C0000
+#define INVAL_SEQ_ID 0x0D0000
+#define INVAL_DF_CTL 0x0E0000
+#define INVAL_SEQ_CNT 0x0F0000
+#define INVAL_PARAMS 0x100000
+#define EXCHANGE_ERROR 0x110000
+#define LS_PROTOCOL_ERROR 0x120000
+#define INCORRECT_LENGTH 0x130000
+#define UNEXPECTED_ACK 0x140000
+#define LOGIN_REQ 0x160000
+#define EXCESSIVE_SEQ 0x170000
+#define NO_EXCHANGE 0x180000
+#define SEC_HDR_NOT_SUPPORTED 0x190000
+#define NO_FABRIC 0x1A0000
+#define P_VENDOR_UNIQUE 0xFF0000
+
+//
+// BA_RJT Reason Codes
+//
+#define BA_INVALID_COMMAND 0x00010000
+#define BA_LOGICAL_ERROR 0x00030000
+#define BA_LOGICAL_BUSY 0x00050000
+#define BA_PROTOCOL_ERROR 0x00070000
+#define BA_UNABLE_TO_PERFORM 0x00090000
+
+//
+// BA_RJT Reason Explanation Codes
+//
+#define BA_NO_REASON 0x00000000
+#define BA_INVALID_OX_RX 0x00000300
+#define BA_SEQUENCE_ABORTED 0x00000500
+
+
+
+#endif /* CPQFCTSSTRUCTS_H */
+
diff --git a/drivers/scsi/cpqfcTStrigger.c b/drivers/scsi/cpqfcTStrigger.c
new file mode 100644
index 000000000..4220d9d92
--- /dev/null
+++ b/drivers/scsi/cpqfcTStrigger.c
@@ -0,0 +1,30 @@
+// Routine to trigger Finisar GTA analyzer. Runs of GPIO2
+// NOTE: DEBUG ONLY! Could interfere with FCMNGR/Miniport operation
+// since it writes directly to the Tachyon board. This function
+// developed for Compaq HBA Tachyon TS v1.2 (Rev X5 PCB)
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+
+void TriggerHBA( void* IOBaseUpper, int Print)
+{
+ __u32 long value;
+
+ // get initial value in hopes of not modifying any other GPIO line
+ IOBaseUpper += 0x188; // TachTL/TS Control reg
+
+ value = readl( IOBaseUpper);
+ // set HIGH to trigger external analyzer (tested on Dolche Finisar 1Gb GTA)
+ // The Finisar anaylzer triggers on low-to-high TTL transition
+ value |= 0x01; // set bit 0
+
+ writel( value, IOBaseUpper);
+
+ if( Print)
+ printk( " -GPIO0 set- ");
+}
+
diff --git a/drivers/scsi/cpqfcTSworker.c b/drivers/scsi/cpqfcTSworker.c
new file mode 100644
index 000000000..d3416ab6d
--- /dev/null
+++ b/drivers/scsi/cpqfcTSworker.c
@@ -0,0 +1,6238 @@
+/* Copyright(c) 2000, Compaq Computer Corporation
+ * Fibre Channel Host Bus Adapter
+ * 64-bit, 66MHz PCI
+ * Originally developed and tested on:
+ * (front): [chip] Tachyon TS HPFC-5166A/1.2 L2C1090 ...
+ * SP# P225CXCBFIEL6T, Rev XC
+ * SP# 161290-001, Rev XD
+ * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
+ *
+ * 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.
+ * Written by Don Zimmerman
+*/
+
+#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 <linux/smp_lock.h>
+
+#define __KERNEL_SYSCALLS__
+
+#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
+
+#include <linux/unistd.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+
+
+#include "sd.h"
+#include "hosts.h" // struct Scsi_Host definition for T handler
+#include "cpqfcTSchip.h"
+#include "cpqfcTSstructs.h"
+
+//#define LOGIN_DBG 1
+
+// REMARKS:
+// Since Tachyon chips may be permitted to wait from 500ms up to 2 sec
+// to empty an outgoing frame from its FIFO to the Fibre Channel stream,
+// we cannot do everything we need to in the interrupt handler. Specifically,
+// every time a link re-init (e.g. LIP) takes place, all SCSI I/O has to be
+// suspended until the login sequences have been completed. Login commands
+// are frames just like SCSI commands are frames; they are subject to the same
+// timeout issues and delays. Also, various specs provide up to 2 seconds for
+// devices to log back in (i.e. respond with ACC to a login frame), so I/O to
+// that device has to be suspended.
+// A serious problem here occurs on highly loaded FC-AL systems. If our FC port
+// has a low priority (e.g. high arbitrated loop physical address, alpa), and
+// some other device is hogging bandwidth (permissible under FC-AL), we might
+// time out thinking the link is hung, when it's simply busy. Many such
+// considerations complicate the design. Although Tachyon assumes control
+// (in silicon) for many link-specific issues, the Linux driver is left with the
+// rest, which turns out to be a difficult, time critical chore.
+
+// These "worker" functions will handle things like FC Logins; all
+// processes with I/O to our device must wait for the Login to complete
+// and (if successful) I/O to resume. In the event of a malfunctioning or
+// very busy loop, it may take hundreds of millisecs or even seconds to complete
+// a frame send. We don't want to hang up the entire server (and all
+// processes which don't depend on Fibre) during this wait.
+
+// The Tachyon chip can have around 30,000 I/O operations ("exchanges")
+// open at one time. However, each exchange must be initiated
+// synchronously (i.e. each of the 30k I/O had to be started one at a
+// time by sending a starting frame via Tachyon's outbound que).
+
+// To accomodate kernel "module" build, this driver limits the exchanges
+// to 256, because of the contiguous physical memory limitation of 128M.
+
+// Typical FC Exchanges are opened presuming the FC frames start without errors,
+// while Exchange completion is handled in the interrupt handler. This
+// optimizes performance for the "everything's working" case.
+// However, when we have FC related errors or hot plugging of FC ports, we pause
+// I/O and handle FC-specific tasks in the worker thread. These FC-specific
+// functions will handle things like FC Logins and Aborts. As the Login sequence
+// completes to each and every target, I/O can resume to that target.
+
+// Our kernel "worker thread" must share the HBA with threads calling
+// "queuecommand". We define a "BoardLock" semaphore which indicates
+// to "queuecommand" that the HBA is unavailable, and Cmnds are added to a
+// board lock Q. When the worker thread finishes with the board, the board
+// lock Q commands are completed with status causing immediate retry.
+// Typically, the board is locked while Logins are in progress after an
+// FC Link Down condition. When Cmnds are re-queued after board lock, the
+// particular Scsi channel/target may or may not have logged back in. When
+// the device is waiting for login, the "prli" flag is clear, in which case
+// commands are passed to a Link Down Q. Whenever the login finally completes,
+// the LinkDown Q is completed, again with status causing immediate retry.
+// When FC devices are logged in, we build and start FC commands to the
+// devices.
+
+// NOTE!! As of May 2000, kernel 2.2.14, the error recovery logic for devices
+// that never log back in (e.g. physically removed) is NOT completely
+// understood. I've still seen instances of system hangs on failed Write
+// commands (possibly from the ext2 layer?) on device removal. Such special
+// cases need to be evaluated from a system/application view - e.g., how
+// exactly does the system want me to complete commands when the device is
+// physically removed??
+
+// local functions
+
+static void SetLoginFields(
+ PFC_LOGGEDIN_PORT pLoggedInPort,
+ TachFCHDR_GCMND* fchs,
+ BOOLEAN PDisc,
+ BOOLEAN Originator);
+
+static void AnalyzeIncomingFrame(
+ CPQFCHBA *cpqfcHBAdata,
+ ULONG QNdx );
+
+static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds );
+
+static int verify_PLOGI( PTACHYON fcChip,
+ TachFCHDR_GCMND* fchs, ULONG* reject_explain);
+static int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain);
+
+static void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type);
+static void BuildLinkServicePayload(
+ PTACHYON fcChip, ULONG type, void* payload);
+
+static void UnblockScsiDevice( struct Scsi_Host *HostAdapter,
+ PFC_LOGGEDIN_PORT pLoggedInPort);
+
+static void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID);
+
+static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata);
+
+static void RevalidateSEST( struct Scsi_Host *HostAdapter,
+ PFC_LOGGEDIN_PORT pLoggedInPort);
+
+static void IssueReportLunsCommand(
+ CPQFCHBA* cpqfcHBAdata,
+ TachFCHDR_GCMND* fchs);
+
+
+// (see scsi_error.c comments on kernel task creation)
+
+void cpqfcTSWorkerThread( void *host)
+{
+ struct Scsi_Host *HostAdapter = (struct Scsi_Host*)host;
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+#ifdef PCI_KERNEL_TRACE
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+#endif
+ struct fs_struct *fs;
+ DECLARE_MUTEX_LOCKED(fcQueReady);
+ DECLARE_MUTEX_LOCKED(fcTYOBcomplete);
+ DECLARE_MUTEX_LOCKED(TachFrozen);
+ DECLARE_MUTEX_LOCKED(BoardLock);
+
+ ENTER("WorkerThread");
+
+ lock_kernel();
+ /*
+ * If we were started as result of loading a module, close all of the
+ * user space pages. We don't need them, and if we didn't close them
+ * they would be locked into memory.
+ */
+ exit_mm(current);
+
+ current->session = 1;
+ current->pgrp = 1;
+
+ /* Become as one with the init task */
+
+ exit_fs(current); /* current->fs->count--; */
+ fs = init_task.fs;
+ // Some kernels compiled for SMP, while actually running
+ // on a uniproc machine, will return NULL for this call
+ if( !fs)
+ {
+ printk(" cpqfcTS FATAL: fs is NULL! Is this an SMP kernel on uniproc machine?\n ");
+ }
+
+ else
+ {
+ current->fs = fs;
+ atomic_inc(&fs->count);
+ }
+
+ siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
+
+
+ /*
+ * Set the name of this process.
+ */
+ sprintf(current->comm, "cpqfcTS_wt_%d", HostAdapter->host_no);
+
+ cpqfcHBAdata->fcQueReady = &fcQueReady; // primary wait point
+ cpqfcHBAdata->TYOBcomplete = &fcTYOBcomplete;
+ cpqfcHBAdata->TachFrozen = &TachFrozen;
+
+
+ cpqfcHBAdata->worker_thread = current;
+
+ unlock_kernel();
+
+ if( cpqfcHBAdata->notify_wt != NULL )
+ up( cpqfcHBAdata->notify_wt); // OK to continue
+
+ while(1)
+ {
+ unsigned long flags;
+
+ down_interruptible( &fcQueReady); // wait for something to do
+
+ if (signal_pending(current) )
+ break;
+
+ PCI_TRACE( 0x90)
+ // first, take the IO lock so the SCSI upper layers can't call
+ // into our _quecommand function (this also disables INTs)
+ spin_lock_irqsave( &io_request_lock, flags); // STOP _que function
+ PCI_TRACE( 0x90)
+
+ CPQ_SPINLOCK_HBA( cpqfcHBAdata)
+ // next, set this pointer to indicate to the _quecommand function
+ // that the board is in use, so it should que the command and
+ // immediately return (we don't actually require the semaphore function
+ // in this driver rev)
+
+ cpqfcHBAdata->BoardLock = &BoardLock;
+
+ PCI_TRACE( 0x90)
+
+ // release the IO lock (and re-enable interrupts)
+ spin_unlock_irqrestore( &io_request_lock, flags);
+
+ // disable OUR HBA interrupt (keep them off as much as possible
+ // during error recovery)
+ disable_irq( cpqfcHBAdata->HostAdapter->irq);
+
+ // OK, let's process the Fibre Channel Link Q and do the work
+ cpqfcTS_WorkTask( HostAdapter);
+
+ // hopefully, no more "work" to do;
+ // re-enable our INTs for "normal" completion processing
+ enable_irq( cpqfcHBAdata->HostAdapter->irq);
+
+
+ cpqfcHBAdata->BoardLock = NULL; // allow commands to be queued
+ CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
+
+
+ // Now, complete any Cmnd we Q'd up while BoardLock was held
+
+ CompleteBoardLockCmnd( cpqfcHBAdata);
+
+
+ }
+ // hopefully, the signal was for our module exit...
+ if( cpqfcHBAdata->notify_wt != NULL )
+ up( cpqfcHBAdata->notify_wt); // yep, we're outta here
+}
+
+
+// Freeze Tachyon routine.
+// If Tachyon is already frozen, return FALSE
+// If Tachyon is not frozen, call freeze function, return TRUE
+//
+static BOOLEAN FreezeTach( CPQFCHBA *cpqfcHBAdata)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ BOOLEAN FrozeTach = FALSE;
+ // It's possible that the chip is already frozen; if so,
+ // "Freezing" again will NOT! generate another Freeze
+ // Completion Message.
+
+ if( (fcChip->Registers.TYstatus.value & 0x70000) != 0x70000)
+ { // (need to freeze...)
+ fcChip->FreezeTachyon( fcChip, 2); // both ERQ and FCP assists
+
+ // 2. Get Tach freeze confirmation
+ // (synchronize SEST manipulation with Freeze Completion Message)
+ // we need INTs on so semaphore can be set.
+ enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Semaphore
+ down_interruptible( cpqfcHBAdata->TachFrozen); // wait for INT handler sem.
+ // can we TIMEOUT semaphore wait?? TBD
+ disable_irq( cpqfcHBAdata->HostAdapter->irq);
+
+ FrozeTach = TRUE;
+ } // (else, already frozen)
+
+ return FrozeTach;
+}
+
+
+
+
+// This is the kernel worker thread task, which processes FC
+// tasks which were queued by the Interrupt handler or by
+// other WorkTask functions.
+
+#define DBG 1
+//#undef DBG
+void cpqfcTS_WorkTask( struct Scsi_Host *HostAdapter)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG QconsumerNdx;
+ LONG ExchangeID;
+ ULONG ulStatus=0;
+ TachFCHDR_GCMND fchs;
+ PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+
+ ENTER("WorkTask");
+
+ // copy current index to work on
+ QconsumerNdx = fcLQ->consumer;
+
+ PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x90)
+
+
+ // NOTE: when this switch completes, we will "consume" the Que item
+// printk("Que type %Xh\n", fcLQ->Qitem[QconsumerNdx].Type);
+ switch( fcLQ->Qitem[QconsumerNdx].Type )
+ {
+ // incoming frame - link service (ACC, UNSOL REQ, etc.)
+ // or FCP-SCSI command
+ case SFQ_UNKNOWN:
+ AnalyzeIncomingFrame( cpqfcHBAdata, QconsumerNdx );
+
+ break;
+
+
+
+ case EXCHANGE_QUEUED: // an Exchange (i.e. FCP-SCSI) was previously
+ // Queued because the link was down. The
+ // heartbeat timer detected it and Queued it here.
+ // We attempt to start it again, and if
+ // successful we clear the EXCHANGE_Q flag.
+ // If the link doesn't come up, the Exchange
+ // will eventually time-out.
+
+ ExchangeID = (LONG) // x_ID copied from DPC timeout function
+ fcLQ->Qitem[QconsumerNdx].ulBuff[0];
+
+ // It's possible that a Q'd exchange could have already
+ // been started by other logic (e.g. ABTS process)
+ // Don't start if already started (Q'd flag clear)
+
+ if( Exchanges->fcExchange[ExchangeID].status & EXCHANGE_QUEUED )
+ {
+// printk(" *Start Q'd x_ID %Xh: type %Xh ",
+// ExchangeID, Exchanges->fcExchange[ExchangeID].type);
+
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID);
+ if( !ulStatus )
+ {
+// printk("success* ");
+ }
+ else
+ {
+#ifdef DBG
+
+ if( ulStatus == EXCHANGE_QUEUED)
+ printk("Queued* ");
+ else
+ printk("failed* ");
+
+#endif
+ }
+ }
+ break;
+
+
+ case LINKDOWN:
+ // (lots of things already done in INT handler) future here?
+ break;
+
+
+ case LINKACTIVE: // Tachyon set the Lup bit in FM status
+ // NOTE: some misbehaving FC ports (like Tach2.1)
+ // can re-LIP immediately after a LIP completes.
+
+ // if "initiator", need to verify LOGs with ports
+// printk("\n*LNKUP* ");
+
+ if( fcChip->Options.initiator )
+ SendLogins( cpqfcHBAdata, NULL ); // PLOGI or PDISC, based on fcPort data
+ // if SendLogins successfully completes, PortDiscDone
+ // will be set.
+
+
+ // If SendLogins was successful, then we expect to get incoming
+ // ACCepts or REJECTs, which are handled below.
+
+ break;
+
+ // LinkService and Fabric request/reply processing
+ case ELS_FDISC: // need to send Fabric Discovery (Login)
+ case ELS_FLOGI: // need to send Fabric Login
+ case ELS_SCR: // need to send State Change Registration
+ case FCS_NSR: // need to send Name Service Request
+ case ELS_PLOGI: // need to send PLOGI
+ case ELS_ACC: // send generic ACCept
+ case ELS_PLOGI_ACC: // need to send ELS ACCept frame to recv'd PLOGI
+ case ELS_PRLI_ACC: // need to send ELS ACCept frame to recv'd PRLI
+ case ELS_LOGO: // need to send ELS LOGO (logout)
+ case ELS_LOGO_ACC: // need to send ELS ACCept frame to recv'd PLOGI
+ case ELS_RJT: // ReJecT reply
+ case ELS_PRLI: // need to send ELS PRLI
+
+
+// printk(" *ELS %Xh* ", fcLQ->Qitem[QconsumerNdx].Type);
+ // if PortDiscDone is not set, it means the SendLogins routine
+ // failed to complete -- assume that LDn occured, so login frames
+ // are invalid
+ if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn
+ {
+ printk("Discard Q'd ELS login frame\n");
+ break;
+ }
+
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ fcLQ->Qitem[QconsumerNdx].Type, // e.g. PLOGI
+ (TachFCHDR_GCMND*)
+ fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
+ NULL, // no data (no scatter/gather list)
+ &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+ if( !ulStatus ) // Exchange setup?
+ {
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+ if( !ulStatus )
+ {
+ // submitted to Tach's Outbound Que (ERQ PI incremented)
+ // waited for completion for ELS type (Login frames issued
+ // synchronously)
+ }
+ else
+ // check reason for Exchange not being started - we might
+ // want to Queue and start later, or fail with error
+ {
+
+ }
+ }
+
+ else // Xchange setup failed...
+ printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
+
+ break;
+
+ case SCSI_REPORT_LUNS:
+ // pass the incoming frame (actually, it's a PRLI frame)
+ // so we can send REPORT_LUNS, in order to determine VSA/PDU
+ // FCP-SCSI Lun address mode
+ IssueReportLunsCommand( cpqfcHBAdata, (TachFCHDR_GCMND*)
+ fcLQ->Qitem[QconsumerNdx].ulBuff);
+
+ break;
+
+
+
+
+ case BLS_ABTS: // need to ABORT one or more exchanges
+ {
+ LONG x_ID = fcLQ->Qitem[QconsumerNdx].ulBuff[0];
+ BOOLEAN FrozeTach = FALSE;
+
+ if( x_ID > TACH_SEST_LEN ) // (in)sanity check
+ {
+// printk( " cpqfcTS ERROR! BOGUS x_ID %Xh", x_ID);
+ break;
+ }
+
+
+ if( Exchanges->fcExchange[ x_ID].Cmnd == NULL ) // should be RARE
+ {
+// printk(" ABTS %Xh Scsi Cmnd null! ", x_ID);
+
+ break; // nothing to abort!
+ }
+
+//#define ABTS_DBG
+#ifdef ABTS_DBG
+ printk("INV SEST[%X] ", x_ID);
+ if( Exchanges->fcExchange[x_ID].status & FC2_TIMEOUT)
+ {
+ printk("FC2TO");
+ }
+ if( Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT)
+ {
+ printk("IA");
+ }
+ if( Exchanges->fcExchange[x_ID].status & PORTID_CHANGED)
+ {
+ printk("PORTID");
+ }
+ if( Exchanges->fcExchange[x_ID].status & DEVICE_REMOVED)
+ {
+ printk("DEVRM");
+ }
+ if( Exchanges->fcExchange[x_ID].status & LINKFAIL_TX)
+ {
+ printk("LKF");
+ }
+ if( Exchanges->fcExchange[x_ID].status & FRAME_TO)
+ {
+ printk("FRMTO");
+ }
+ if( Exchanges->fcExchange[x_ID].status & ABORTSEQ_NOTIFY)
+ {
+ printk("ABSQ");
+ }
+ if( Exchanges->fcExchange[x_ID].status & SFQ_FRAME)
+ {
+ printk("SFQFR");
+ }
+
+ if( Exchanges->fcExchange[ x_ID].type == 0x2000)
+ printk(" WR");
+ else if( Exchanges->fcExchange[ x_ID].type == 0x3000)
+ printk(" RD");
+ else if( Exchanges->fcExchange[ x_ID].type == 0x10)
+ printk(" ABTS");
+ else
+ printk(" %Xh", Exchanges->fcExchange[ x_ID].type);
+
+ if( !(Exchanges->fcExchange[x_ID].status & INITIATOR_ABORT))
+ {
+ printk(" Cmd %p, ",
+ Exchanges->fcExchange[ x_ID].Cmnd);
+
+ printk(" brd/chn/trg/lun %d/%d/%d/%d port_id %06X\n",
+ cpqfcHBAdata->HBAnum,
+ Exchanges->fcExchange[ x_ID].Cmnd->channel,
+ Exchanges->fcExchange[ x_ID].Cmnd->target,
+ Exchanges->fcExchange[ x_ID].Cmnd->lun,
+ Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF);
+ }
+ else // assume that Cmnd ptr is invalid on _abort()
+ {
+ printk(" Cmd ptr invalid\n");
+ }
+
+#endif
+
+
+ // Steps to ABORT a SEST exchange:
+ // 1. Freeze TL SCSI assists & ERQ (everything)
+ // 2. Receive FROZEN inbound CM (must succeed!)
+ // 3. Invalidate x_ID SEST entry
+ // 4. Resume TL SCSI assists & ERQ (everything)
+ // 5. Build/start on exchange - change "type" to BLS_ABTS,
+ // timeout to X sec (RA_TOV from PLDA is actually 0)
+ // 6. Set Exchange Q'd status if ABTS cannot be started,
+ // or simply complete Exchange in "Terminate" condition
+
+ PCI_TRACEO( x_ID, 0xB4)
+
+ // 1 & 2 . Freeze Tach & get confirmation of freeze
+ FrozeTach = FreezeTach( cpqfcHBAdata);
+
+ // 3. OK, Tachyon is frozen, so we can invalidate SEST exchange.
+ // FC2_TIMEOUT means we are originating the abort, while
+ // TARGET_ABORT means we are ACCepting an abort.
+ // LINKFAIL_TX, ABORTSEQ_NOFITY, INV_ENTRY or FRAME_TO are
+ // all from Tachyon:
+ // Exchange was corrupted by LDn or other FC physical failure
+ // INITIATOR_ABORT means the upper layer driver/application
+ // requested the abort.
+
+
+
+ // clear bit 31 (VALid), to invalidate & take control from TL
+ fcChip->SEST->u[ x_ID].IWE.Hdr_Len &= 0x7FFFFFFF;
+
+
+ // examine and Tach's "Linked List" for IWEs that
+ // received (nearly) simultaneous transfer ready (XRDY)
+ // repair linked list if necessary (TBD!)
+ // (If we ignore the "Linked List", we will time out
+ // WRITE commands where we received the FCP-SCSI XFRDY
+ // frame (because Tachyon didn't processes it). Linked List
+ // management should be done as an optimization.
+
+// readl( fcChip->Registers.ReMapMemBase+TL_MEM_SEST_LINKED_LIST ));
+
+
+
+
+ // 4. Resume all Tachlite functions (for other open Exchanges)
+ // as quickly as possible to allow other exchanges to other ports
+ // to resume. Freezing Tachyon may cause cascading errors, because
+ // any received SEST frame cannot be processed by the SEST.
+ // Don't "unfreeze" unless Link is operational
+ if( FrozeTach ) // did we just freeze it (above)?
+ fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists
+
+
+ PCI_TRACEO( x_ID, 0xB4)
+
+ // Note there is no confirmation that the chip is "unfrozen". Also,
+ // if the Link is down when unfreeze is called, it has no effect.
+ // Chip will unfreeze when the Link is back up.
+
+ // 5. Now send out Abort commands if possible
+ // Some Aborts can't be "sent" (Port_id changed or gone);
+ // if the device is gone, there is no port_id to send the ABTS to.
+
+ if( !(Exchanges->fcExchange[ x_ID].status & PORTID_CHANGED)
+ &&
+ !(Exchanges->fcExchange[ x_ID].status & DEVICE_REMOVED) )
+ {
+ Exchanges->fcExchange[ x_ID].type = BLS_ABTS;
+ fchs.s_id = Exchanges->fcExchange[ x_ID].fchs.d_id;
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ BLS_ABTS,
+ &fchs, // (uses only s_id)
+ NULL, // (no scatter/gather list for ABTS)
+ &x_ID );// ABTS on this Exchange ID
+
+ if( !ulStatus ) // Exchange setup build OK?
+ {
+
+ // ABTS may be needed because an Exchange was corrupted
+ // by a Link disruption. If the Link is UP, we can
+ // presume that this ABTS can start immediately; otherwise,
+ // set Que'd status so the Login functions
+ // can restart it when the FC physical Link is restored
+ if( ((fcChip->Registers.FMstatus.value &0xF0) &0x80)) // loop init?
+ {
+// printk(" *set Q status x_ID %Xh on LDn* ", x_ID);
+ Exchanges->fcExchange[ x_ID].status |= EXCHANGE_QUEUED;
+ }
+
+ else // what FC device (port_id) does the Cmd belong to?
+ {
+ PFC_LOGGEDIN_PORT pLoggedInPort =
+ Exchanges->fcExchange[ x_ID].pLoggedInPort;
+
+ // if Port is logged in, we might start the abort.
+
+ if( (pLoggedInPort != NULL)
+ &&
+ (pLoggedInPort->prli == TRUE) )
+ {
+ // it's possible that an Exchange has already been Queued
+ // to start after Login completes. Check and don't
+ // start it (again) here if Q'd status set
+// printk(" ABTS xchg %Xh ", x_ID);
+ if( Exchanges->fcExchange[x_ID].status & EXCHANGE_QUEUED)
+ {
+// printk("already Q'd ");
+ }
+ else
+ {
+// printk("starting ");
+
+ fcChip->fcStats.FC2aborted++;
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID );
+ if( !ulStatus )
+ {
+ // OK
+ // submitted to Tach's Outbound Que (ERQ PI incremented)
+ }
+ else
+ {
+/* printk("ABTS exchange start failed -status %Xh, x_ID %Xh ",
+ ulStatus, x_ID);
+*/
+ }
+ }
+ }
+ else
+ {
+/* printk(" ABTS NOT starting xchg %Xh, %p ",
+ x_ID, pLoggedInPort);
+ if( pLoggedInPort )
+ printk("prli %d ", pLoggedInPort->prli);
+*/
+ }
+ }
+ }
+ else // what the #@!
+ { // how do we fail to build an Exchange for ABTS??
+ printk("ABTS exchange build failed -status %Xh, x_ID %Xh\n",
+ ulStatus, x_ID);
+ }
+ }
+ else // abort without ABTS -- just complete exchange/Cmnd to Linux
+ {
+// printk(" *Terminating x_ID %Xh on %Xh* ",
+// x_ID, Exchanges->fcExchange[x_ID].status);
+ cpqfcTSCompleteExchange( fcChip, x_ID);
+
+
+ }
+ } // end of ABTS case
+ break;
+
+
+
+ case BLS_ABTS_ACC: // need to ACCept one ABTS
+ // (NOTE! this code not updated for Linux yet..)
+
+
+ printk(" *ABTS_ACC* ");
+ // 1. Freeze TL
+
+ fcChip->FreezeTachyon( fcChip, 2); // both ERQ and FCP assists
+
+ memcpy( // copy the incoming ABTS frame
+ &fchs,
+ fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
+ sizeof( fchs));
+
+ // 3. OK, Tachyon is frozen so we can invalidate SEST entry
+ // (if necessary)
+ // Status FC2_TIMEOUT means we are originating the abort, while
+ // TARGET_ABORT means we are ACCepting an abort
+
+ ExchangeID = fchs.ox_rx_id & 0x7FFF; // RX_ID for exchange
+// printk("ABTS ACC for Target ExchangeID %Xh\n", ExchangeID);
+
+
+ // sanity check on received ExchangeID
+ if( Exchanges->fcExchange[ ExchangeID].status == TARGET_ABORT )
+ {
+ // clear bit 31 (VALid), to invalidate & take control from TL
+// printk("Invalidating SEST exchange %Xh\n", ExchangeID);
+ fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len &= 0x7FFFFFFF;
+ }
+
+
+ // 4. Resume all Tachlite functions (for other open Exchanges)
+ // as quickly as possible to allow other exchanges to other ports
+ // to resume. Freezing Tachyon for too long may royally screw
+ // up everything!
+ fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists
+
+ // Note there is no confirmation that the chip is "unfrozen". Also,
+ // if the Link is down when unfreeze is called, it has no effect.
+ // Chip will unfreeze when the Link is back up.
+
+ // 5. Now send out Abort ACC reply for this exchange
+ Exchanges->fcExchange[ ExchangeID].type = BLS_ABTS_ACC;
+
+ fchs.s_id = Exchanges->fcExchange[ ExchangeID].fchs.d_id;
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ BLS_ABTS_ACC,
+ &fchs,
+ NULL, // no data (no scatter/gather list)
+ &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+ if( !ulStatus ) // Exchange setup?
+ {
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+ if( !ulStatus )
+ {
+ // submitted to Tach's Outbound Que (ERQ PI incremented)
+ // waited for completion for ELS type (Login frames issued
+ // synchronously)
+ }
+ else
+ // check reason for Exchange not being started - we might
+ // want to Queue and start later, or fail with error
+ {
+
+ }
+ }
+ break;
+
+
+ case BLS_ABTS_RJT: // need to ReJecT one ABTS; reject implies the
+ // exchange doesn't exist in the TARGET context.
+ // ExchangeID has to come from LinkService space.
+
+ printk(" *ABTS_RJT* ");
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ BLS_ABTS_RJT,
+ (TachFCHDR_GCMND*)
+ fcLQ->Qitem[QconsumerNdx].ulBuff, // incoming fchs
+ NULL, // no data (no scatter/gather list)
+ &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+ if( !ulStatus ) // Exchange setup OK?
+ {
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+ // If it fails, we aren't required to retry.
+ }
+ if( ulStatus )
+ {
+ printk("Failed to send BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID);
+ }
+ else
+ {
+ printk("Sent BLS_RJT for ABTS, X_ID %Xh\n", ExchangeID);
+
+ }
+
+ break;
+
+
+
+ default:
+ break;
+ } // end switch
+//doNothing:
+ // done with this item - now set the NEXT index
+
+ if( QconsumerNdx+1 >= FC_LINKQ_DEPTH ) // rollover test
+ {
+ fcLQ->consumer = 0;
+ }
+ else
+ {
+ fcLQ->consumer++;
+ }
+
+ PCI_TRACEO( fcLQ->Qitem[QconsumerNdx].Type, 0x94)
+
+ LEAVE("WorkTask");
+ return;
+}
+
+
+
+
+// When Tachyon reports link down, bad al_pa, or Link Service (e.g. Login)
+// commands come in, post to the LinkQ so that action can be taken outside the
+// interrupt handler.
+// This circular Q works like Tachyon's que - the producer points to the next
+// (unused) entry. Called by Interrupt handler, WorkerThread, Timer
+// sputlinkq
+void cpqfcTSPutLinkQue( CPQFCHBA *cpqfcHBAdata,
+ int Type,
+ void *QueContent)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+// FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+ ULONG ndx;
+
+ ENTER("cpqfcTSPutLinkQ");
+
+ ndx = fcLQ->producer;
+
+ ndx += 1; // test for Que full
+
+
+
+ if( ndx >= FC_LINKQ_DEPTH ) // rollover test
+ ndx = 0;
+
+ if( ndx == fcLQ->consumer ) // QUE full test
+ {
+ // QUE was full! lost LK command (fatal to logic)
+ fcChip->fcStats.lnkQueFull++;
+
+ printk("*LinkQ Full!*");
+ TriggerHBA( fcChip->Registers.ReMapMemBase, 1);
+/*
+ {
+ int i;
+ printk("LinkQ PI %d, CI %d\n", fcLQ->producer,
+ fcLQ->consumer);
+
+ for( i=0; i< FC_LINKQ_DEPTH; )
+ {
+ printk(" [%d]%Xh ", i, fcLQ->Qitem[i].Type);
+ if( (++i %8) == 0) printk("\n");
+ }
+
+ }
+*/
+ printk( "cpqfcTS: WARNING!! PutLinkQue - FULL!\n"); // we're hung
+ }
+ else // QUE next element
+ {
+ // Prevent certain multiple (back-to-back) requests.
+ // This is important in that we don't want to issue multiple
+ // ABTS for the same Exchange, or do multiple FM inits, etc.
+ // We can never be sure of the timing of events reported to
+ // us by Tach's IMQ, which can depend on system/bus speeds,
+ // FC physical link circumstances, etc.
+
+ if( (fcLQ->producer != fcLQ->consumer)
+ &&
+ (Type == FMINIT) )
+ {
+ LONG lastNdx; // compute previous producer index
+ if( fcLQ->producer)
+ lastNdx = fcLQ->producer- 1;
+ else
+ lastNdx = FC_LINKQ_DEPTH-1;
+
+
+ if( fcLQ->Qitem[lastNdx].Type == FMINIT)
+ {
+// printk(" *skip FMINIT Q post* ");
+// goto DoneWithPutQ;
+ }
+
+ }
+
+ // OK, add the Q'd item...
+
+ fcLQ->Qitem[fcLQ->producer].Type = Type;
+
+ memcpy(
+ fcLQ->Qitem[fcLQ->producer].ulBuff,
+ QueContent,
+ sizeof(fcLQ->Qitem[fcLQ->producer].ulBuff));
+
+ fcLQ->producer = ndx; // increment Que producer
+
+ // set semaphore to wake up Kernel (worker) thread
+ //
+ up( cpqfcHBAdata->fcQueReady );
+ }
+
+//DoneWithPutQ:
+
+ LEAVE("cpqfcTSPutLinkQ");
+}
+
+
+
+
+// reset device ext FC link Q
+void cpqfcTSLinkQReset( CPQFCHBA *cpqfcHBAdata)
+
+{
+ PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+ fcLQ->producer = 0;
+ fcLQ->consumer = 0;
+
+}
+
+
+
+
+
+// When Tachyon gets an unassisted FCP-SCSI frame, post here so
+// an arbitrary context thread (e.g. IOCTL loopback test function)
+// can process it.
+
+// (NOTE: Not revised for Linux)
+// This Q works like Tachyon's que - the producer points to the next
+// (unused) entry.
+void cpqfcTSPutScsiQue( CPQFCHBA *cpqfcHBAdata,
+ int Type,
+ void *QueContent)
+{
+// CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+// PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+// ULONG ndx;
+
+// ULONG *pExchangeID;
+// LONG ExchangeID;
+
+/*
+ KeAcquireSpinLockAtDpcLevel( &pDevExt->fcScsiQueLock);
+ ndx = pDevExt->fcScsiQue.producer + 1; // test for Que full
+
+ if( ndx >= FC_SCSIQ_DEPTH ) // rollover test
+ ndx = 0;
+
+ if( ndx == pDevExt->fcScsiQue.consumer ) // QUE full test
+ {
+ // QUE was full! lost LK command (fatal to logic)
+ fcChip->fcStats.ScsiQueFull++;
+#ifdef DBG
+ printk( "fcPutScsiQue - FULL!\n");
+#endif
+
+ }
+ else // QUE next element
+ {
+ pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].Type = Type;
+
+ if( Type == FCP_RSP )
+ {
+ // this TL inbound message type means that a TL SEST exchange has
+ // copied an FCP response frame into a buffer pointed to by the SEST
+ // entry. That buffer is allocated in the SEST structure at ->RspHDR.
+ // Copy the RspHDR for use by the Que handler.
+ pExchangeID = (ULONG *)QueContent;
+
+ memcpy(
+ pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff,
+ &fcChip->SEST->RspHDR[ *pExchangeID ],
+ sizeof(pDevExt->fcScsiQue.Qitem[0].ulBuff)); // (any element for size)
+
+ }
+ else
+ {
+ memcpy(
+ pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff,
+ QueContent,
+ sizeof(pDevExt->fcScsiQue.Qitem[pDevExt->fcScsiQue.producer].ulBuff));
+ }
+
+ pDevExt->fcScsiQue.producer = ndx; // increment Que
+
+
+ KeSetEvent( &pDevExt->TYIBscsi, // signal any waiting thread
+ 0, // no priority boost
+ FALSE ); // no waiting later for this event
+ }
+ KeReleaseSpinLockFromDpcLevel( &pDevExt->fcScsiQueLock);
+*/
+}
+
+
+
+
+
+
+
+static void ProcessELS_Request( CPQFCHBA*,TachFCHDR_GCMND*);
+
+static void ProcessELS_Reply( CPQFCHBA*,TachFCHDR_GCMND*);
+
+static void ProcessFCS_Reply( CPQFCHBA*,TachFCHDR_GCMND*);
+
+void cpqfcTSImplicitLogout( CPQFCHBA* cpqfcHBAdata,
+ PFC_LOGGEDIN_PORT pFcPort)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+
+ if( pFcPort->port_id != 0xFFFC01 ) // don't care about Fabric
+ {
+ fcChip->fcStats.logouts++;
+ printk("cpqfcTS: Implicit logout of WWN %08X%08X, port_id %06X\n",
+ (ULONG)pFcPort->u.liWWN,
+ (ULONG)(pFcPort->u.liWWN >>32),
+ pFcPort->port_id);
+
+ // Terminate I/O with this (Linux) Scsi target
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pFcPort->ScsiNexus,
+ DEVICE_REMOVED);
+ }
+
+ // Do an "implicit logout" - we can't really Logout the device
+ // (i.e. with LOGOut Request) because of port_id confusion
+ // (i.e. the Other port has no port_id).
+ // A new login for that WWN will have to re-write port_id (0 invalid)
+ pFcPort->port_id = 0; // invalid!
+ pFcPort->pdisc = FALSE;
+ pFcPort->prli = FALSE;
+ pFcPort->plogi = FALSE;
+ pFcPort->flogi = FALSE;
+ pFcPort->LOGO_timer = 0;
+ pFcPort->device_blocked = TRUE; // block Scsi Requests
+}
+
+
+// On FC-AL, there is a chance that a previously known device can
+// be quietly removed (e.g. with non-managed hub),
+// while a NEW device (with different WWN) took the same alpa or
+// even 24-bit port_id. This chance is unlikely but we must always
+// check for it.
+static void TestDuplicatePortId( CPQFCHBA* cpqfcHBAdata,
+ PFC_LOGGEDIN_PORT pLoggedInPort)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ // set "other port" at beginning of fcPorts list
+ PFC_LOGGEDIN_PORT pOtherPortWithPortId = fcChip->fcPorts.pNextPort;
+ while( pOtherPortWithPortId )
+ {
+ if( (pOtherPortWithPortId->port_id ==
+ pLoggedInPort->port_id)
+ &&
+ (pOtherPortWithPortId != pLoggedInPort) )
+ {
+ // trouble! (Implicitly) Log the other guy out
+ printk(" *port_id %Xh is duplicated!* ",
+ pOtherPortWithPortId->port_id);
+ cpqfcTSImplicitLogout( cpqfcHBAdata, pOtherPortWithPortId);
+ }
+ pOtherPortWithPortId = pOtherPortWithPortId->pNextPort;
+ }
+}
+
+
+
+
+
+
+// Dynamic Memory Allocation for newly discovered FC Ports.
+// For simplicity, maintain fcPorts structs for ALL
+// for discovered devices, including those we never do I/O with
+// (e.g. Fabric addresses)
+
+static PFC_LOGGEDIN_PORT CreateFcPort(
+ CPQFCHBA* cpqfcHBAdata,
+ PFC_LOGGEDIN_PORT pLastLoggedInPort,
+ TachFCHDR_GCMND* fchs,
+ LOGIN_PAYLOAD* plogi)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ PFC_LOGGEDIN_PORT pNextLoggedInPort = NULL;
+ int i;
+
+
+ printk("cpqfcTS: New FC port %06Xh WWN: ", fchs->s_id);
+ for( i=3; i>=0; i--) // copy the LOGIN port's WWN
+ printk("%02X", plogi->port_name[i]);
+ for( i=7; i>3; i--) // copy the LOGIN port's WWN
+ printk("%02X", plogi->port_name[i]);
+
+
+ // allocate mem for new port
+ // (these are small and rare allocations...)
+ pNextLoggedInPort = kmalloc( sizeof( FC_LOGGEDIN_PORT), GFP_ATOMIC );
+
+
+ // allocation succeeded? Fill out NEW PORT
+ if( pNextLoggedInPort )
+ {
+ // clear out any garbage (sometimes exists)
+ memset( pNextLoggedInPort, 0, sizeof( FC_LOGGEDIN_PORT));
+
+
+ // If we login to a Fabric, we don't want to treat it
+ // as a SCSI device...
+ if( (fchs->s_id & 0xFFF000) != 0xFFF000)
+ {
+ int i;
+
+ // create a unique "virtual" SCSI Nexus (for now, just a
+ // new target ID) -- we will update channel/target on REPORT_LUNS
+ // special case for very first SCSI target...
+ if( cpqfcHBAdata->HostAdapter->max_id == 0)
+ {
+ pNextLoggedInPort->ScsiNexus.target = 0;
+ fcChip->fcPorts.ScsiNexus.target = -1; // don't use "stub"
+ }
+ else
+ {
+ pNextLoggedInPort->ScsiNexus.target =
+ cpqfcHBAdata->HostAdapter->max_id;
+ }
+
+ // initialize the lun[] Nexus struct for lun masking
+ for( i=0; i< CPQFCTS_MAX_LUN; i++)
+ pNextLoggedInPort->ScsiNexus.lun[i] = 0xFF; // init to NOT USED
+
+ pNextLoggedInPort->ScsiNexus.channel = 0; // cpqfcTS has 1 FC port
+
+ printk(" SCSI Chan/Trgt %d/%d",
+ pNextLoggedInPort->ScsiNexus.channel,
+ pNextLoggedInPort->ScsiNexus.target);
+
+ // tell Scsi layers about the new target...
+ cpqfcHBAdata->HostAdapter->max_id++;
+// printk("HostAdapter->max_id = %d\n",
+// cpqfcHBAdata->HostAdapter->max_id);
+ }
+ else
+ {
+ // device is NOT SCSI (in case of Fabric)
+ pNextLoggedInPort->ScsiNexus.target = -1; // invalid
+ }
+
+ // create forward link to new port
+ pLastLoggedInPort->pNextPort = pNextLoggedInPort;
+ printk("\n");
+
+ }
+ return pNextLoggedInPort; // NULL on allocation failure
+} // end NEW PORT (WWN) logic
+
+
+
+// For certain cases, we want to terminate exchanges without
+// sending ABTS to the device. Examples include when an FC
+// device changed it's port_id after Loop re-init, or when
+// the device sent us a logout. In the case of changed port_id,
+// we want to complete the command and return SOFT_ERROR to
+// force a re-try. In the case of LOGOut, we might return
+// BAD_TARGET if the device is really gone.
+// Since we must ensure that Tachyon is not operating on the
+// exchange, we have to freeze the chip
+// sterminateex
+void cpqfcTSTerminateExchange(
+ CPQFCHBA* cpqfcHBAdata, SCSI_NEXUS *ScsiNexus, int TerminateStatus)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG x_ID;
+
+ if( ScsiNexus )
+ {
+// printk("TerminateExchange: ScsiNexus chan/target %d/%d\n",
+// ScsiNexus->channel, ScsiNexus->target);
+
+ }
+
+ for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+ {
+ if( Exchanges->fcExchange[x_ID].type ) // in use?
+ {
+ if( ScsiNexus == NULL ) // our HBA changed - term. all
+ {
+ Exchanges->fcExchange[x_ID].status = TerminateStatus;
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID );
+ }
+ else
+ {
+ // If a device, according to WWN, has been removed, it's
+ // port_id may be used by another working device, so we
+ // have to terminate by SCSI target, NOT port_id.
+ if( Exchanges->fcExchange[x_ID].Cmnd) // Cmnd in progress?
+ {
+ if( (Exchanges->fcExchange[x_ID].Cmnd->target == ScsiNexus->target)
+ &&
+ (Exchanges->fcExchange[x_ID].Cmnd->channel == ScsiNexus->channel))
+ {
+ Exchanges->fcExchange[x_ID].status = TerminateStatus;
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &x_ID ); // timed-out
+ }
+ }
+
+ // (in case we ever need it...)
+ // all SEST structures have a remote node ID at SEST DWORD 2
+ // if( (fcChip->SEST->u[ x_ID ].TWE.Remote_Node_ID >> 8)
+ // == port_id)
+ }
+ }
+ }
+}
+
+
+static void ProcessELS_Request(
+ CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+// FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+// ULONG ox_id = (fchs->ox_rx_id >>16);
+ PFC_LOGGEDIN_PORT pLoggedInPort=NULL, pLastLoggedInPort;
+ BOOLEAN NeedReject = FALSE;
+ ULONG ls_reject_code = 0; // default don'n know??
+
+
+ // Check the incoming frame for a supported ELS type
+ switch( fchs->pl[0] & 0xFFFF)
+ {
+ case 0x0050: // PDISC?
+
+ // Payload for PLOGI and PDISC is identical (request & reply)
+ if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload?
+ {
+ LOGIN_PAYLOAD logi; // FC-PH Port Login
+
+ // PDISC payload OK. If critical login fields
+ // (e.g. WWN) matches last login for this port_id,
+ // we may resume any prior exchanges
+ // with the other port
+
+
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ 0, // don't search linked list for port_id
+ &logi.port_name[0], // search linked list for WWN
+ &pLastLoggedInPort); // must return non-NULL; when a port_id
+ // is not found, this pointer marks the
+ // end of the singly linked list
+
+ if( pLoggedInPort != NULL) // WWN found (prior login OK)
+ {
+
+ if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id)
+ {
+ // Yes. We were expecting PDISC?
+ if( pLoggedInPort->pdisc )
+ {
+ // Yes; set fields accordingly. (PDISC, not Originator)
+ SetLoginFields( pLoggedInPort, fchs, TRUE, FALSE);
+
+ // send 'ACC' reply
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC)
+ fchs );
+
+ // OK to resume I/O...
+ }
+ else
+ {
+ printk("Not expecting PDISC (pdisc=FALSE)\n");
+ NeedReject = TRUE;
+ // set reject reason code
+ ls_reject_code =
+ LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+ }
+ }
+ else
+ {
+ if( pLoggedInPort->port_id != 0)
+ {
+ printk("PDISC PortID change: old %Xh, new %Xh\n",
+ pLoggedInPort->port_id, fchs->s_id &0xFFFFFF);
+ }
+ NeedReject = TRUE;
+ // set reject reason code
+ ls_reject_code =
+ LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+
+ }
+ }
+ else
+ {
+ printk("PDISC Request from unknown WWN\n");
+ NeedReject = TRUE;
+
+ // set reject reason code
+ ls_reject_code =
+ LS_RJT_REASON( LOGICAL_ERROR, INVALID_PORT_NAME);
+ }
+
+ }
+ else // Payload unacceptable
+ {
+ printk("payload unacceptable\n");
+ NeedReject = TRUE; // reject code already set
+
+ }
+
+ if( NeedReject)
+ {
+ ULONG port_id;
+ // The PDISC failed. Set login struct flags accordingly,
+ // terminate any I/O to this port, and Q a PLOGI
+ if( pLoggedInPort )
+ {
+ pLoggedInPort->pdisc = FALSE;
+ pLoggedInPort->prli = FALSE;
+ pLoggedInPort->plogi = FALSE;
+
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+ port_id = pLoggedInPort->port_id;
+ }
+ else
+ {
+ port_id = fchs->s_id &0xFFFFFF;
+ }
+ fchs->reserved = ls_reject_code; // borrow this (unused) field
+ cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs );
+ }
+
+ break;
+
+
+
+ case 0x0003: // PLOGI?
+
+ // Payload for PLOGI and PDISC is identical (request & reply)
+ if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) ) // valid payload?
+ {
+ LOGIN_PAYLOAD logi; // FC-PH Port Login
+ BOOLEAN NeedReject = FALSE;
+
+ // PDISC payload OK. If critical login fields
+ // (e.g. WWN) matches last login for this port_id,
+ // we may resume any prior exchanges
+ // with the other port
+
+
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ 0, // don't search linked list for port_id
+ &logi.port_name[0], // search linked list for WWN
+ &pLastLoggedInPort); // must return non-NULL; when a port_id
+ // is not found, this pointer marks the
+ // end of the singly linked list
+
+ if( pLoggedInPort == NULL) // WWN not found -New Port
+ {
+ pLoggedInPort = CreateFcPort(
+ cpqfcHBAdata,
+ pLastLoggedInPort,
+ fchs,
+ &logi);
+ if( pLoggedInPort == NULL )
+ {
+ printk(" cpqfcTS: New port allocation failed - lost FC device!\n");
+ // Now Q a LOGOut Request, since we won't be talking to that device
+
+ NeedReject = TRUE;
+
+ // set reject reason code
+ ls_reject_code =
+ LS_RJT_REASON( LOGICAL_ERROR, NO_LOGIN_RESOURCES);
+
+ }
+ }
+ if( !NeedReject )
+ {
+
+ // OK - we have valid fcPort ptr; set fields accordingly.
+ // (not PDISC, not Originator)
+ SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE);
+
+ // send 'ACC' reply
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_PLOGI_ACC, // (PDISC same as PLOGI ACC)
+ fchs );
+ }
+ }
+ else // Payload unacceptable
+ {
+ printk("payload unacceptable\n");
+ NeedReject = TRUE; // reject code already set
+ }
+
+ if( NeedReject)
+ {
+ // The PDISC failed. Set login struct flags accordingly,
+ // terminate any I/O to this port, and Q a PLOGI
+ pLoggedInPort->pdisc = FALSE;
+ pLoggedInPort->prli = FALSE;
+ pLoggedInPort->plogi = FALSE;
+
+ fchs->reserved = ls_reject_code; // borrow this (unused) field
+
+ // send 'RJT' reply
+ cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_RJT, fchs );
+ }
+
+ // terminate any exchanges with this device...
+ if( pLoggedInPort )
+ {
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+ }
+ break;
+
+
+
+ case 0x1020: // PRLI?
+ {
+ BOOLEAN NeedReject = TRUE;
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ (fchs->s_id & 0xFFFFFF), // search linked list for port_id
+ NULL, // DON'T search linked list for WWN
+ NULL); // don't care
+
+ if( pLoggedInPort == NULL )
+ {
+ // huh?
+ printk(" Unexpected PRLI Request -not logged in!\n");
+
+ // set reject reason code
+ ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+
+ // Q a LOGOut here?
+ }
+ else
+ {
+ // verify the PRLI ACC payload
+ if( !verify_PRLI( fchs, &ls_reject_code) )
+ {
+ // PRLI Reply is acceptable; were we expecting it?
+ if( pLoggedInPort->plogi )
+ {
+ // yes, we expected the PRLI ACC (not PDISC; not Originator)
+ SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE);
+
+ // Q an ACCept Reply
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_PRLI_ACC,
+ fchs );
+
+ NeedReject = FALSE;
+ }
+ else
+ {
+ // huh?
+ printk(" (unexpected) PRLI REQEST with plogi FALSE\n");
+
+ // set reject reason code
+ ls_reject_code = LS_RJT_REASON( PROTOCOL_ERROR, INITIATOR_CTL_ERROR);
+
+ // Q a LOGOut here?
+
+ }
+ }
+ else
+ {
+ printk(" PRLI REQUEST payload failed verify\n");
+ // (reject code set by "verify")
+
+ // Q a LOGOut here?
+ }
+ }
+
+ if( NeedReject )
+ {
+ // Q a ReJecT Reply with reason code
+ fchs->reserved = ls_reject_code;
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_RJT, // Q Type
+ fchs );
+ }
+ }
+ break;
+
+
+
+
+ case 0x0005: // LOGOut?
+ {
+ // was this LOGOUT because we sent a ELS_PDISC to an FC device
+ // with changed (or new) port_id, or does the port refuse
+ // to communicate to us?
+ // We maintain a logout counter - if we get 3 consecutive LOGOuts,
+ // give up!
+ LOGOUT_PAYLOAD logo;
+ BOOLEAN GiveUpOnDevice = FALSE;
+ ULONG ls_reject_code = 0;
+
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logo, sizeof(logo));
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ 0, // don't search linked list for port_id
+ &logo.port_name[0], // search linked list for WWN
+ NULL); // don't care about end of list
+
+ if( pLoggedInPort ) // found the device?
+ {
+ // Q an ACC reply
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_LOGO_ACC, // Q Type
+ fchs ); // device to respond to
+
+ // set login struct fields (LOGO_counter increment)
+ SetLoginFields( pLoggedInPort, fchs, FALSE, FALSE);
+
+ // are we an Initiator?
+ if( fcChip->Options.initiator)
+ {
+ // we're an Initiator, so check if we should
+ // try (another?) login
+
+ // Fabrics routinely log out from us after
+ // getting device info - don't try to log them
+ // back in.
+ if( (fchs->s_id & 0xFFF000) == 0xFFF000 )
+ {
+ ; // do nothing
+ }
+ else if( pLoggedInPort->LOGO_counter <= 3)
+ {
+ // try (another) login (PLOGI request)
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_PLOGI, // Q Type
+ fchs );
+
+ // Terminate I/O with "retry" potential
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pLoggedInPort->ScsiNexus,
+ PORTID_CHANGED);
+ }
+ else
+ {
+ printk(" Got 3 LOGOuts - terminating comm. with port_id %Xh\n",
+ fchs->s_id &&0xFFFFFF);
+ GiveUpOnDevice = TRUE;
+ }
+ }
+ else
+ {
+ GiveUpOnDevice = TRUE;
+ }
+
+
+ if( GiveUpOnDevice == TRUE )
+ {
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pLoggedInPort->ScsiNexus,
+ DEVICE_REMOVED);
+ }
+ }
+ else // we don't know this WWN!
+ {
+ // Q a ReJecT Reply with reason code
+ fchs->reserved = ls_reject_code;
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_RJT, // Q Type
+ fchs );
+ }
+ }
+ break;
+
+
+
+
+ // FABRIC only case
+ case 0x0461: // ELS RSCN (Registered State Change Notification)?
+ {
+ int Ports;
+ int i;
+ __u32 Buff;
+ // Typically, one or more devices have been added to or dropped
+ // from the Fabric.
+ // The format of this frame is defined in FC-FLA (Rev 2.7, Aug 1997)
+ // The first 32-bit word has a 2-byte Payload Length, which
+ // includes the 4 bytes of the first word. Consequently,
+ // this PL len must never be less than 4, must be a multiple of 4,
+ // and has a specified max value 256.
+ // (Endianess!)
+ Ports = ((fchs->pl[0] >>24) - 4) / 4;
+ Ports = Ports > 63 ? 63 : Ports;
+
+ printk(" RSCN ports: %d\n", Ports);
+ if( Ports <= 0 ) // huh?
+ {
+ // ReJecT the command
+ fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM, 0);
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_RJT, // Q Type
+ fchs );
+
+ break;
+ }
+ else // Accept the command
+ {
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_ACC, // Q Type
+ fchs );
+ }
+
+ // Check the "address format" to determine action.
+ // We have 3 cases:
+ // 0 = Port Address; 24-bit address of affected device
+ // 1 = Area Address; MS 16 bits valid
+ // 2 = Domain Address; MS 8 bits valid
+ for( i=0; i<Ports; i++)
+ {
+ BigEndianSwap( (UCHAR*)&fchs->pl[i+1],(UCHAR*)&Buff, 4);
+ switch( Buff & 0xFF000000)
+ {
+
+ case 0: // Port Address?
+
+ case 0x01000000: // Area Domain?
+ case 0x02000000: // Domain Address
+ // For example, "port_id" 0x201300
+ // OK, let's try a Name Service Request (Query)
+ fchs->s_id = 0xFFFFFC; // Name Server Address
+ cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs);
+
+ break;
+
+
+ default: // huh? new value on version change?
+ break;
+ }
+ }
+ }
+ break;
+
+
+
+
+ default: // don't support this request (yet)
+ // set reject reason code
+ fchs->reserved = LS_RJT_REASON( UNABLE_TO_PERFORM,
+ REQUEST_NOT_SUPPORTED);
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_RJT, // Q Type
+ fchs );
+ break;
+ }
+}
+
+
+static void ProcessELS_Reply(
+ CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG ox_id = (fchs->ox_rx_id >>16);
+ ULONG ls_reject_code;
+ PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort;
+
+ // If this is a valid reply, then we MUST have sent a request.
+ // Verify that we can find a valid request OX_ID corresponding to
+ // this reply
+
+
+ if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0)
+ {
+ printk(" *Discarding ACC/RJT frame, xID %04X/%04X* ",
+ ox_id, fchs->ox_rx_id & 0xffff);
+ goto Quit; // exit this routine
+ }
+
+
+ // Is the reply a RJT (reject)?
+ if( (fchs->pl[0] & 0xFFFFL) == 0x01) // Reject reply?
+ {
+// ****** REJECT REPLY ********
+ switch( Exchanges->fcExchange[ox_id].type )
+ {
+
+ case ELS_FDISC: // we sent out Fabric Discovery
+ case ELS_FLOGI: // we sent out FLOGI
+
+ printk("RJT received on Fabric Login from %Xh, reason %Xh\n",
+ fchs->s_id, fchs->pl[1]);
+
+ break;
+
+ default:
+ break;
+ }
+
+ goto Done;
+ }
+
+ // OK, we have an ACCept...
+ // What's the ACC type? (according to what we sent)
+ switch( Exchanges->fcExchange[ox_id].type )
+ {
+
+ case ELS_PLOGI: // we sent out PLOGI
+ if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) )
+ {
+ LOGIN_PAYLOAD logi; // FC-PH Port Login
+
+ // login ACC payload acceptable; search for WWN in our list
+ // of fcPorts
+
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ 0, // don't search linked list for port_id
+ &logi.port_name[0], // search linked list for WWN
+ &pLastLoggedInPort); // must return non-NULL; when a port_id
+ // is not found, this pointer marks the
+ // end of the singly linked list
+
+ if( pLoggedInPort == NULL) // WWN not found - new port
+ {
+
+ pLoggedInPort = CreateFcPort(
+ cpqfcHBAdata,
+ pLastLoggedInPort,
+ fchs,
+ &logi);
+
+ if( pLoggedInPort == NULL )
+ {
+ printk(" cpqfcTS: New port allocation failed - lost FC device!\n");
+ // Now Q a LOGOut Request, since we won't be talking to that device
+
+ goto Done; // exit with error! dropped login frame
+ }
+ }
+ else // WWN was already known. Ensure that any open
+ // exchanges for this WWN are terminated.
+ // NOTE: It's possible that a device can change its
+ // 24-bit port_id after a Link init or Fabric change
+ // (e.g. LIP or Fabric RSCN). In that case, the old
+ // 24-bit port_id may be duplicated, or no longer exist.
+ {
+
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+ }
+
+ // We have an fcPort struct - set fields accordingly
+ // not PDISC, originator
+ SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE);
+
+ // We just set a "port_id"; is it duplicated?
+ TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort);
+
+ // For Fabric operation, we issued PLOGI to 0xFFFFFC
+ // so we can send SCR (State Change Registration)
+ // Check for this special case...
+ if( fchs->s_id == 0xFFFFFC )
+ {
+ // PLOGI ACC was a Fabric response... issue SCR
+ fchs->s_id = 0xFFFFFD; // address for SCR
+ cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_SCR, fchs);
+ }
+
+ else
+ {
+ // Now we need a PRLI to enable FCP-SCSI operation
+ // set flags and Q up a ELS_PRLI
+ cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PRLI, fchs);
+ }
+ }
+ else
+ {
+ // login payload unacceptable - reason in ls_reject_code
+ // Q up a Logout Request
+ printk("Login Payload unacceptable\n");
+
+ }
+ break;
+
+
+ // PDISC logic very similar to PLOGI, except we never want
+ // to allocate mem for "new" port, and we set flags differently
+ // (might combine later with PLOGI logic for efficiency)
+ case ELS_PDISC: // we sent out PDISC
+ if( !verify_PLOGI( fcChip, fchs, &ls_reject_code) )
+ {
+ LOGIN_PAYLOAD logi; // FC-PH Port Login
+ BOOLEAN NeedLogin = FALSE;
+
+ // login payload acceptable; search for WWN in our list
+ // of (previously seen) fcPorts
+
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ 0, // don't search linked list for port_id
+ &logi.port_name[0], // search linked list for WWN
+ &pLastLoggedInPort); // must return non-NULL; when a port_id
+ // is not found, this pointer marks the
+ // end of the singly linked list
+
+ if( pLoggedInPort != NULL) // WWN found?
+ {
+ // WWN has same port_id as last login? (Of course, a properly
+ // working FC device should NEVER ACCept a PDISC if it's
+ // port_id changed, but check just in case...)
+ if( (fchs->s_id & 0xFFFFFF) == pLoggedInPort->port_id)
+ {
+ // Yes. We were expecting PDISC?
+ if( pLoggedInPort->pdisc )
+ {
+ int i;
+
+
+ // PDISC expected -- set fields. (PDISC, Originator)
+ SetLoginFields( pLoggedInPort, fchs, TRUE, TRUE);
+
+ // We are ready to resume FCP-SCSI to this device...
+ // Do we need to start anything that was Queued?
+
+ for( i=0; i< TACH_SEST_LEN; i++)
+ {
+ // see if any exchange for this PDISC'd port was queued
+ if( ((fchs->s_id &0xFFFFFF) ==
+ (Exchanges->fcExchange[i].fchs.d_id & 0xFFFFFF))
+ &&
+ (Exchanges->fcExchange[i].status & EXCHANGE_QUEUED))
+ {
+ fchs->reserved = i; // copy ExchangeID
+// printk(" *Q x_ID %Xh after PDISC* ",i);
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata, EXCHANGE_QUEUED, fchs );
+ }
+ }
+
+ // Complete commands Q'd while we were waiting for Login
+
+ UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort);
+ }
+ else
+ {
+ printk("Not expecting PDISC (pdisc=FALSE)\n");
+ NeedLogin = TRUE;
+ }
+ }
+ else
+ {
+ printk("PDISC PortID change: old %Xh, new %Xh\n",
+ pLoggedInPort->port_id, fchs->s_id &0xFFFFFF);
+ NeedLogin = TRUE;
+
+ }
+ }
+ else
+ {
+ printk("PDISC ACC from unknown WWN\n");
+ NeedLogin = TRUE;
+ }
+
+ if( NeedLogin)
+ {
+
+ // The PDISC failed. Set login struct flags accordingly,
+ // terminate any I/O to this port, and Q a PLOGI
+ if( pLoggedInPort ) // FC device previously known?
+ {
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ ELS_LOGO, // Q Type
+ fchs ); // has port_id to send to
+
+ // There are a variety of error scenarios which can result
+ // in PDISC failure, so as a catchall, add the check for
+ // duplicate port_id.
+ TestDuplicatePortId( cpqfcHBAdata, pLoggedInPort);
+
+// TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+ pLoggedInPort->pdisc = FALSE;
+ pLoggedInPort->prli = FALSE;
+ pLoggedInPort->plogi = FALSE;
+
+ cpqfcTSTerminateExchange( cpqfcHBAdata,
+ &pLoggedInPort->ScsiNexus, PORTID_CHANGED);
+ }
+ cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs );
+ }
+ }
+ else
+ {
+ // login payload unacceptable - reason in ls_reject_code
+ // Q up a Logout Request
+ printk("ERROR: Login Payload unacceptable!\n");
+
+ }
+
+ break;
+
+
+
+ case ELS_PRLI: // we sent out PRLI
+
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search Scsi Nexus
+ (fchs->s_id & 0xFFFFFF), // search linked list for port_id
+ NULL, // DON'T search linked list for WWN
+ NULL); // don't care
+
+ if( pLoggedInPort == NULL )
+ {
+ // huh?
+ printk(" Unexpected PRLI ACCept frame!\n");
+
+ // Q a LOGOut here?
+
+ goto Done;
+ }
+
+ // verify the PRLI ACC payload
+ if( !verify_PRLI( fchs, &ls_reject_code) )
+ {
+ // PRLI Reply is acceptable; were we expecting it?
+ if( pLoggedInPort->plogi )
+ {
+ // yes, we expected the PRLI ACC (not PDISC; Originator)
+ SetLoginFields( pLoggedInPort, fchs, FALSE, TRUE);
+
+ // OK, let's send a REPORT_LUNS command to determine
+ // whether VSA or PDA FCP-LUN addressing is used.
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs );
+
+ // It's possible that a device we were talking to changed
+ // port_id, and has logged back in. This function ensures
+ // that I/O will resume.
+ UnblockScsiDevice( cpqfcHBAdata->HostAdapter, pLoggedInPort);
+
+ }
+ else
+ {
+ // huh?
+ printk(" (unexpected) PRLI ACCept with plogi FALSE\n");
+
+ // Q a LOGOut here?
+ goto Done;
+ }
+ }
+ else
+ {
+ printk(" PRLI ACCept payload failed verify\n");
+
+ // Q a LOGOut here?
+ }
+
+ break;
+
+ case ELS_FLOGI: // we sent out FLOGI (Fabric Login)
+
+ // update the upper 16 bits of our port_id in Tachyon
+ // the switch adds those upper 16 bits when responding
+ // to us (i.e. we are the destination_id)
+ fcChip->Registers.my_al_pa = (fchs->d_id & 0xFFFFFF);
+ writel( fcChip->Registers.my_al_pa,
+ fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID);
+
+ // now send out a PLOGI to the well known port_id 0xFFFFFC
+ fchs->s_id = 0xFFFFFC;
+ cpqfcTSPutLinkQue( cpqfcHBAdata, ELS_PLOGI, fchs);
+
+ break;
+
+
+ case ELS_FDISC: // we sent out FDISC (Fabric Discovery (Login))
+
+ printk( " ELS_FDISC success ");
+ break;
+
+
+ case ELS_SCR: // we sent out State Change Registration
+ // now we can issue Name Service Request to find any
+ // Fabric-connected devices we might want to login to.
+
+
+ fchs->s_id = 0xFFFFFC; // Name Server Address
+ cpqfcTSPutLinkQue( cpqfcHBAdata, FCS_NSR, fchs);
+
+
+ break;
+
+
+ default:
+ printk(" *Discarding unknown ACC frame, xID %04X/%04X* ",
+ ox_id, fchs->ox_rx_id & 0xffff);
+ break;
+ }
+
+
+Done:
+ // Regardless of whether the Reply is valid or not, the
+ // the exchange is done - complete
+ cpqfcTSCompleteExchange( fcChip, (fchs->ox_rx_id >>16)); // complete
+
+Quit:
+ return;
+}
+
+
+
+
+
+
+// **************** Fibre Channel Services **************
+// This is where we process the Directory (Name) Service Reply
+// to know which devices are on the Fabric
+
+static void ProcessFCS_Reply(
+ CPQFCHBA* cpqfcHBAdata, TachFCHDR_GCMND* fchs)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG ox_id = (fchs->ox_rx_id >>16);
+// ULONG ls_reject_code;
+// PFC_LOGGEDIN_PORT pLoggedInPort, pLastLoggedInPort;
+
+ // If this is a valid reply, then we MUST have sent a request.
+ // Verify that we can find a valid request OX_ID corresponding to
+ // this reply
+
+ if( Exchanges->fcExchange[(fchs->ox_rx_id >>16)].type == 0)
+ {
+ printk(" *Discarding Reply frame, xID %04X/%04X* ",
+ ox_id, fchs->ox_rx_id & 0xffff);
+ goto Quit; // exit this routine
+ }
+
+
+ // OK, we were expecting it. Now check to see if it's a
+ // "Name Service" Reply, and if so force a re-validation of
+ // Fabric device logins (i.e. Start the login timeout and
+ // send PDISC or PLOGI)
+ // (Endianess Byte Swap?)
+ if( fchs->pl[1] == 0x02FC ) // Name Service
+ {
+ // got a new (or NULL) list of Fabric attach devices...
+ // Invalidate current logins
+
+ PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts;
+ while( pLoggedInPort ) // for all ports which are expecting
+ // PDISC after the next LIP, set the
+ // logoutTimer
+ {
+
+ if( (pLoggedInPort->port_id & 0xFFFF00) // Fabric device?
+ &&
+ (pLoggedInPort->port_id != 0xFFFFFC) ) // NOT the F_Port
+ {
+ pLoggedInPort->LOGO_timer = 6; // what's the Fabric timeout??
+ // suspend any I/O in progress until
+ // PDISC received...
+ pLoggedInPort->prli = FALSE; // block FCP-SCSI commands
+ }
+
+ pLoggedInPort = pLoggedInPort->pNextPort;
+ }
+
+ if( fchs->pl[2] == 0x0280) // ACCept?
+ {
+ // Send PLOGI or PDISC to these Fabric devices
+ SendLogins( cpqfcHBAdata, &fchs->pl[4] );
+ }
+
+
+ // As of this writing, the only reason to reject is because NO
+ // devices are left on the Fabric. We already started
+ // "logged out" timers; if the device(s) don't come
+ // back, we'll do the implicit logout in the heart beat
+ // timer routine
+ else // ReJecT
+ {
+ // this just means no Fabric device is visible at this instant
+ }
+ }
+
+ // Regardless of whether the Reply is valid or not, the
+ // the exchange is done - complete
+ cpqfcTSCompleteExchange( fcChip, (fchs->ox_rx_id >>16)); // complete
+
+Quit:
+ return;
+}
+
+
+
+
+
+
+
+static void AnalyzeIncomingFrame(
+ CPQFCHBA *cpqfcHBAdata,
+ ULONG QNdx )
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ PFC_LINK_QUE fcLQ = cpqfcHBAdata->fcLQ;
+ TachFCHDR_GCMND* fchs =
+ (TachFCHDR_GCMND*)fcLQ->Qitem[QNdx].ulBuff;
+// ULONG ls_reject_code; // reason for rejecting login
+ LONG ExchangeID;
+// FC_LOGGEDIN_PORT *pLoggedInPort;
+ BOOLEAN AbortAccept;
+
+ ENTER("AnalyzeIncomingFrame");
+
+
+
+ switch( fcLQ->Qitem[QNdx].Type) // FCP or Unknown
+ {
+
+ case SFQ_UNKNOWN: // unknown frame (e.g. LIP position frame, NOP, etc.)
+
+
+ // ********* FC-4 Device Data/ Fibre Channel Service *************
+ if( ((fchs->d_id &0xF0000000) == 0) // R_CTL (upper nibble) 0x0?
+ &&
+ (fchs->f_ctl & 0x20000000) ) // TYPE 20h is Fibre Channel Service
+ {
+
+ // ************** FCS Reply **********************
+
+ if( (fchs->d_id & 0xff000000L) == 0x03000000L) // (31:23 R_CTL)
+ {
+ ProcessFCS_Reply( cpqfcHBAdata, fchs );
+
+ } // end of FCS logic
+
+ }
+
+
+ // *********** Extended Link Service **************
+
+ else if( fchs->d_id & 0x20000000 // R_CTL 0x2?
+ &&
+ (fchs->f_ctl & 0x01000000) ) // TYPE = 1
+ {
+
+ // these frames are either a response to
+ // something we sent (0x23) or "unsolicited"
+ // frames (0x22).
+
+
+ // **************Extended Link REPLY **********************
+ // R_CTL Solicited Control Reply
+
+ if( (fchs->d_id & 0xff000000L) == 0x23000000L) // (31:23 R_CTL)
+ {
+
+ ProcessELS_Reply( cpqfcHBAdata, fchs );
+
+ } // end of "R_CTL Solicited Control Reply"
+
+
+
+
+ // **************Extended Link REQUEST **********************
+ // (unsolicited commands from another port or task...)
+
+ // R_CTL Ext Link REQUEST
+ else if( (fchs->d_id & 0xff000000L) == 0x22000000L &&
+ (fchs->ox_rx_id != 0xFFFFFFFFL) ) // (ignore LIP frame)
+ {
+
+
+
+ ProcessELS_Request( cpqfcHBAdata, fchs );
+
+ }
+
+
+
+ // ************** LILP **********************
+ else if( (fchs->d_id & 0xff000000L) == 0x22000000L &&
+ (fchs->ox_rx_id == 0xFFFFFFFFL)) // (e.g., LIP frames)
+
+ {
+ // SANMark specifies that when available, we must use
+ // the LILP frame to determine which ALPAs to send Port Discovery
+ // to...
+
+ if( fchs->pl[0] == 0x0711L) // ELS_PLOGI?
+ {
+// UCHAR *ptr = (UCHAR*)&fchs->pl[1];
+// printk(" %d ALPAs found\n", *ptr);
+ memcpy( fcChip->LILPmap, &fchs->pl[1], 32*4); // 32 DWORDs
+ fcChip->Options.LILPin = 1; // our LILPmap is valid!
+ // now post to make Port Discovery happen...
+ cpqfcTSPutLinkQue( cpqfcHBAdata, LINKACTIVE, fchs);
+ }
+ }
+ }
+
+
+ // ***************** BASIC LINK SERVICE *****************
+
+ else if( fchs->d_id & 0x80000000 // R_CTL:
+ && // Basic Link Service Request
+ !(fchs->f_ctl & 0xFF000000) ) // type=0 for BLS
+ {
+
+ // Check for ABTS (Abort Sequence)
+ if( (fchs->d_id & 0x8F000000) == 0x81000000)
+ {
+ // look for OX_ID, S_ID pair that matches in our
+ // fcExchanges table; if found, reply with ACCept and complete
+ // the exchange
+
+ // Per PLDA, an ABTS is sent by an initiator; therefore
+ // assume that if we have an exhange open to the port who
+ // sent ABTS, it will be the d_id of what we sent.
+ for( ExchangeID = 0, AbortAccept=FALSE;
+ ExchangeID < TACH_SEST_LEN; ExchangeID++)
+ {
+ // Valid "target" exchange 24-bit port_id matches?
+ // NOTE: For the case of handling Intiator AND Target
+ // functions on the same chip, we can have TWO Exchanges
+ // with the same OX_ID -- OX_ID/FFFF for the CMND, and
+ // OX_ID/RX_ID for the XRDY or DATA frame(s). Ideally,
+ // we would like to support ABTS from Initiators or Targets,
+ // but it's not clear that can be supported on Tachyon for
+ // all cases (requires more investigation).
+
+ if( (Exchanges->fcExchange[ ExchangeID].type == SCSI_TWE ||
+ Exchanges->fcExchange[ ExchangeID].type == SCSI_TRE)
+ &&
+ ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
+ (fchs->s_id & 0xFFFFFF)) )
+ {
+
+ // target xchnge port_id matches -- how about OX_ID?
+ if( (Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id &0xFFFF0000)
+ == (fchs->ox_rx_id & 0xFFFF0000) )
+ // yes! post ACCept response; will be completed by fcStart
+ {
+ Exchanges->fcExchange[ ExchangeID].status = TARGET_ABORT;
+
+ // copy (add) rx_id field for simplified ACCept reply
+ fchs->ox_rx_id =
+ Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id;
+
+ cpqfcTSPutLinkQue( cpqfcHBAdata,
+ BLS_ABTS_ACC, // Q Type
+ fchs ); // void QueContent
+ AbortAccept = TRUE;
+ printk("ACCepting ABTS for x_ID %8.8Xh, SEST pair %8.8Xh\n",
+ fchs->ox_rx_id, Exchanges->fcExchange[ ExchangeID].fchs.ox_rx_id);
+ break; // ABTS can affect only ONE exchange -exit loop
+ }
+ }
+ } // end of FOR loop
+ if( !AbortAccept ) // can't ACCept ABTS - send Reject
+ {
+ printk("ReJecTing: can't find ExchangeID %8.8Xh for ABTS command\n",
+ fchs->ox_rx_id);
+ if( Exchanges->fcExchange[ ExchangeID].type
+ &&
+ !(fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len
+ & 0x80000000))
+ {
+ cpqfcTSCompleteExchange( fcChip, ExchangeID);
+ }
+ else
+ {
+ printk("Unexpected ABTS ReJecT! SEST[%X] Dword 0: %Xh\n",
+ ExchangeID, fcChip->SEST->u[ ExchangeID].IWE.Hdr_Len);
+ }
+ }
+ }
+
+ // Check for BLS {ABTS? (Abort Sequence)} ACCept
+ else if( (fchs->d_id & 0x8F000000) == 0x84000000)
+ {
+ // target has responded with ACC for our ABTS;
+ // complete the indicated exchange with ABORTED status
+ // Make no checks for correct RX_ID, since
+ // all we need to conform ABTS ACC is the OX_ID.
+ // Verify that the d_id matches!
+
+ ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC
+// printk("ABTS ACC x_ID 0x%04X 0x%04X, status %Xh\n",
+// fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff,
+// Exchanges->fcExchange[ExchangeID].status);
+
+
+
+ if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense
+ {
+ // Does "target" exchange 24-bit port_id match?
+ // (See "NOTE" above for handling Intiator AND Target in
+ // the same device driver)
+ // First, if this is a target response, then we originated
+ // (initiated) it with BLS_ABTS:
+
+ if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS)
+
+ &&
+ // Second, does the source of this ACC match the destination
+ // of who we originally sent it to?
+ ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
+ (fchs->s_id & 0xFFFFFF)) )
+ {
+ cpqfcTSCompleteExchange( fcChip, ExchangeID );
+ }
+ }
+ }
+ // Check for BLS {ABTS? (Abort Sequence)} ReJecT
+ else if( (fchs->d_id & 0x8F000000) == 0x85000000)
+ {
+ // target has responded with RJT for our ABTS;
+ // complete the indicated exchange with ABORTED status
+ // Make no checks for correct RX_ID, since
+ // all we need to conform ABTS ACC is the OX_ID.
+ // Verify that the d_id matches!
+
+ ExchangeID = (fchs->ox_rx_id >> 16) & 0x7FFF; // x_id from ACC
+// printk("BLS_ABTS RJT on Exchange 0x%04X 0x%04X\n",
+// fchs->ox_rx_id >> 16, fchs->ox_rx_id & 0xffff);
+
+ if( ExchangeID < TACH_SEST_LEN ) // x_ID makes sense
+ {
+ // Does "target" exchange 24-bit port_id match?
+ // (See "NOTE" above for handling Intiator AND Target in
+ // the same device driver)
+ // First, if this is a target response, then we originated
+ // (initiated) it with BLS_ABTS:
+
+ if( (Exchanges->fcExchange[ ExchangeID].type == BLS_ABTS)
+
+ &&
+ // Second, does the source of this ACC match the destination
+ // of who we originally sent it to?
+ ((Exchanges->fcExchange[ ExchangeID].fchs.d_id & 0xFFFFFF) ==
+ (fchs->s_id & 0xFFFFFF)) )
+ {
+ // YES! NOTE: There is a bug in CPQ's RA-4000 box
+ // where the "reason code" isn't returned in the payload
+ // For now, simply presume the reject is because the target
+ // already completed the exchange...
+
+// printk("complete x_ID %Xh on ABTS RJT\n", ExchangeID);
+ cpqfcTSCompleteExchange( fcChip, ExchangeID );
+ }
+ }
+ } // end of ABTS check
+ } // end of Basic Link Service Request
+ break;
+
+ default:
+ printk("AnalyzeIncomingFrame: unknown type: %Xh(%d)\n",
+ fcLQ->Qitem[QNdx].Type,
+ fcLQ->Qitem[QNdx].Type);
+ break;
+ }
+}
+
+
+// Function for Port Discovery necessary after every FC
+// initialization (e.g. LIP).
+// Also may be called if from Fabric Name Service logic.
+
+static void SendLogins( CPQFCHBA *cpqfcHBAdata, __u32 *FabricPortIds )
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG ulStatus=0;
+ TachFCHDR_GCMND fchs; // copy fields for transmission
+ int i;
+ ULONG loginType;
+ LONG ExchangeID;
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+ __u32 PortIds[ number_of_al_pa];
+ int NumberOfPorts=0;
+
+ // We're going to presume (for now) that our limit of Fabric devices
+ // is the same as the number of alpa on a private loop (126 devices).
+ // (Of course this could be changed to support however many we have
+ // memory for).
+ memset( &PortIds[0], 0, sizeof(PortIds));
+
+ // First, check if this login is for our own Link Initialization
+ // (e.g. LIP on FC-AL), or if we have knowledge of Fabric devices
+ // from a switch. If we are logging into Fabric devices, we'll
+ // have a non-NULL FabricPortId pointer
+
+ if( FabricPortIds != NULL) // may need logins
+ {
+ int LastPort=FALSE;
+ i = 0;
+ while( !LastPort)
+ {
+ // port IDs From NSR payload; byte swap needed?
+ BigEndianSwap( (UCHAR*)FabricPortIds, (UCHAR*)&PortIds[i], 4);
+
+// printk("FPortId[%d] %Xh ", i, PortIds[i]);
+ if( PortIds[i] & 0x80000000)
+ LastPort = TRUE;
+
+ PortIds[i] &= 0xFFFFFF; // get 24-bit port_id
+ // some non-Fabric devices (like the Crossroads Fibre/Scsi bridge)
+ // erroneously use ALPA 0.
+ if( PortIds[i] ) // need non-zero port_id...
+ i++;
+
+ if( i >= number_of_al_pa ) // (in)sanity check
+ break;
+ FabricPortIds++; // next...
+ }
+
+ NumberOfPorts = i;
+// printk("NumberOf Fabric ports %d", NumberOfPorts);
+ }
+
+ else // need to send logins on our "local" link
+ {
+
+ // are we a loop port? If so, check for reception of LILP frame,
+ // and if received use it (SANMark requirement)
+ if( fcChip->Options.LILPin )
+ {
+ int j=0;
+ // sanity check on number of ALPAs from LILP frame...
+ // For format of LILP frame, see FC-AL specs or
+ // "Fibre Channel Bench Reference", J. Stai, 1995 (ISBN 1-879936-17-8)
+ // First byte is number of ALPAs
+ i = fcChip->LILPmap[0] >= (32*4) ? 32*4 : fcChip->LILPmap[0];
+ NumberOfPorts = i;
+// printk(" LILP alpa count %d ", i);
+ while( i > 0)
+ {
+ PortIds[j] = fcChip->LILPmap[1+ j];
+ j++; i--;
+ }
+ }
+ else // have to send login to everybody
+ {
+ int j=0;
+ i = number_of_al_pa;
+ NumberOfPorts = i;
+ while( i > 0)
+ {
+ PortIds[j] = valid_al_pa[j]; // all legal ALPAs
+ j++; i--;
+ }
+ }
+ }
+
+
+ // Now we have a copy of the port_ids (and how many)...
+ for( i = 0; i < NumberOfPorts; i++)
+ {
+ // 24-bit FC Port ID
+ fchs.s_id = PortIds[i]; // note: only 8-bits used for ALPA
+
+
+ // don't log into ourselves (Linux Scsi disk scan will stop on
+ // no TARGET support error on us, and quit trying for rest of devices)
+ if( (fchs.s_id & 0xFF ) == (fcChip->Registers.my_al_pa & 0xFF) )
+ continue;
+
+ // fabric login needed?
+ if( (fchs.s_id == 0) ||
+ (fcChip->Options.fabric == 1) )
+ {
+ fcChip->Options.flogi = 1; // fabric needs longer for login
+ // Do we need FLOGI or FDISC?
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search SCSI Nexus
+ 0xFFFFFC, // search linked list for Fabric port_id
+ NULL, // don't search WWN
+ NULL); // (don't care about end of list)
+
+ if( pLoggedInPort ) // If found, we have prior experience with
+ // this port -- check whether PDISC is needed
+ {
+ if( pLoggedInPort->flogi )
+ {
+ // does the switch support FDISC?? (FLOGI for now...)
+ loginType = ELS_FLOGI; // prior FLOGI still valid
+ }
+ else
+ loginType = ELS_FLOGI; // expired FLOGI
+ }
+ else // first FLOGI?
+ loginType = ELS_FLOGI;
+
+
+ fchs.s_id = 0xFFFFFE; // well known F_Port address
+
+ // Fabrics are not required to support FDISC, and
+ // it's not clear if that helps us anyway, since
+ // we'll want a Name Service Request to re-verify
+ // visible devices...
+ // Consequently, we always want our upper 16 bit
+ // port_id to be zero (we'll be rejected if we
+ // use our prior port_id if we've been plugged into
+ // a different switch port).
+ // Trick Tachyon to send to ALPA 0 (see TL/TS UG, pg 87)
+ // If our ALPA is 55h for instance, we want the FC frame
+ // s_id to be 0x000055, while Tach's my_al_pa register
+ // must be 0x000155, to force an OPN at ALPA 0
+ // (the Fabric port)
+ fcChip->Registers.my_al_pa &= 0xFF; // only use ALPA for FLOGI
+ writel( fcChip->Registers.my_al_pa | 0x0100,
+ fcChip->Registers.ReMapMemBase + TL_MEM_TACH_My_ID);
+ }
+
+ else // not FLOGI...
+ {
+ // should we send PLOGI or PDISC? Check if any prior port_id
+ // (e.g. alpa) completed a PLOGI/PRLI exchange by checking
+ // the pdisc flag.
+
+ pLoggedInPort = fcFindLoggedInPort(
+ fcChip,
+ NULL, // don't search SCSI Nexus
+ fchs.s_id, // search linked list for al_pa
+ NULL, // don't search WWN
+ NULL); // (don't care about end of list)
+
+
+
+ if( pLoggedInPort ) // If found, we have prior experience with
+ // this port -- check whether PDISC is needed
+ {
+ if( pLoggedInPort->pdisc )
+ {
+ loginType = ELS_PDISC; // prior PLOGI and PRLI maybe still valid
+
+ }
+ else
+ loginType = ELS_PLOGI; // prior knowledge, but can't use PDISC
+ }
+ else // never talked to this port_id before
+ loginType = ELS_PLOGI; // prior knowledge, but can't use PDISC
+ }
+
+
+
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ loginType, // e.g. PLOGI
+ &fchs, // no incoming frame (we are originator)
+ NULL, // no data (no scatter/gather list)
+ &ExchangeID );// fcController->fcExchanges index, -1 if failed
+
+ if( !ulStatus ) // Exchange setup OK?
+ {
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, ExchangeID );
+ if( !ulStatus )
+ {
+ // submitted to Tach's Outbound Que (ERQ PI incremented)
+ // waited for completion for ELS type (Login frames issued
+ // synchronously)
+
+ if( loginType == ELS_PDISC )
+ {
+ // now, we really shouldn't Revalidate SEST exchanges until
+ // we get an ACC reply from our target and verify that
+ // the target address/WWN is unchanged. However, when a fast
+ // target gets the PDISC, they can send SEST Exchange data
+ // before we even get around to processing the PDISC ACC.
+ // Consequently, we lose the I/O.
+ // To avoid this, go ahead and Revalidate when the PDISC goes
+ // out, anticipating that the ACC will be truly acceptable
+ // (this happens 99.9999....% of the time).
+ // If we revalidate a SEST write, and write data goes to a
+ // target that is NOT the one we originated the WRITE to,
+ // that target is required (FCP-SCSI specs, etc) to discard
+ // our WRITE data.
+
+ // Re-validate SEST entries (Tachyon hardware assists)
+ RevalidateSEST( cpqfcHBAdata->HostAdapter, pLoggedInPort);
+ //TriggerHBA( fcChip->Registers.ReMapMemBase, 1);
+ }
+ }
+ else // give up immediately on error
+ {
+#ifdef LOGIN_DBG
+ printk("SendLogins: fcStartExchange failed: %Xh\n", ulStatus );
+#endif
+ break;
+ }
+
+
+ if( fcChip->Registers.FMstatus.value & 0x080 ) // LDn during Port Disc.
+ {
+ ulStatus = LNKDWN_OSLS;
+#ifdef LOGIN_DBG
+ printk("SendLogins: PortDisc aborted (LDn) @alpa %Xh\n", fchs.s_id);
+#endif
+ break;
+ }
+ // Check the exchange for bad status (i.e. FrameTimeOut),
+ // and complete on bad status (most likely due to BAD_ALPA)
+ // on LDn, DPC function may already complete (ABORT) a started
+ // exchange, so check type first (type = 0 on complete).
+ if( Exchanges->fcExchange[ExchangeID].status )
+ {
+#ifdef LOGIN_DBG
+ printk("completing x_ID %X on status %Xh\n",
+ ExchangeID, Exchanges->fcExchange[ExchangeID].status);
+#endif
+ cpqfcTSCompleteExchange( fcChip, ExchangeID);
+ }
+ }
+ else // Xchange setup failed...
+ {
+#ifdef LOGIN_DBG
+ printk("FC: cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
+#endif
+ break;
+ }
+ }
+ if( !ulStatus )
+ {
+ // set the event signifying that all ALPAs were sent out.
+#ifdef LOGIN_DBG
+ printk("SendLogins: PortDiscDone\n");
+#endif
+ cpqfcHBAdata->PortDiscDone = 1;
+
+
+ // TL/TS UG, pg. 184
+ // 0x0065 = 100ms for RT_TOV
+ // 0x01f5 = 500ms for ED_TOV
+ fcChip->Registers.ed_tov.value = 0x006501f5L;
+ writel( fcChip->Registers.ed_tov.value,
+ (fcChip->Registers.ed_tov.address));
+
+ // set the LP_TOV back to ED_TOV (i.e. 500 ms)
+ writel( 0x00000010, fcChip->Registers.ReMapMemBase +TL_MEM_FM_TIMEOUT2);
+ }
+ else
+ {
+ printk("SendLogins: failed at xchng %Xh, alpa %Xh, status %Xh\n",
+ ExchangeID, fchs.s_id, ulStatus);
+ }
+ LEAVE("SendLogins");
+
+}
+
+
+// for REPORT_LUNS documentation, see "In-Depth Exploration of Scsi",
+// D. Deming, 1994, pg 7-19 (ISBN 1-879936-08-9)
+static void ScsiReportLunsDone(Scsi_Cmnd *Cmnd)
+{
+ struct Scsi_Host *HostAdapter = Cmnd->host;
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+ int LunListLen=0;
+ int i;
+ ULONG x_ID = 0xFFFFFFFF;
+ UCHAR *ucBuff = Cmnd->request_buffer;
+
+// printk("cpqfcTS: ReportLunsDone \n");
+ // first, we need to find the Exchange for this command,
+ // so we can find the fcPort struct to make the indicated
+ // changes.
+ for( i=0; i< TACH_SEST_LEN; i++)
+ {
+ if( Exchanges->fcExchange[i].type // exchange defined?
+ &&
+ (Exchanges->fcExchange[i].Cmnd == Cmnd) ) // matches?
+
+ {
+ x_ID = i; // found exchange!
+ break;
+ }
+ }
+ if( x_ID == 0xFFFFFFFF)
+ {
+// printk("cpqfcTS: ReportLuns failed - no FC Exchange\n");
+ goto Done; // Report Luns FC Exchange gone;
+ // exchange probably Terminated by Implicit logout
+ }
+
+
+ // search linked list for the port_id we sent INQUIRY to
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ NULL, // DON'T search Scsi Nexus (we will set it)
+ Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF,
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ if( !pLoggedInPort )
+ {
+// printk("cpqfcTS: ReportLuns failed - device gone\n");
+ goto Done; // error! can't find logged in Port
+ }
+ LunListLen = ucBuff[3];
+ LunListLen += ucBuff[2]>>8;
+
+ if( !LunListLen ) // failed
+ {
+ // generically speaking, a soft error means we should retry...
+ if( (Cmnd->result >> 16) == DID_SOFT_ERROR )
+ {
+ if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) &&
+ (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset"
+ {
+ TachFCHDR_GCMND *fchs = &Exchanges->fcExchange[ x_ID].fchs;
+ // did we fail because of "check condition, device reset?"
+ // e.g. the device was reset (i.e., at every power up)
+ // retry the Report Luns
+
+ // who are we sending it to?
+ // we know this because we have a copy of the command
+ // frame from the original Report Lun command -
+ // switch the d_id/s_id fields, because the Exchange Build
+ // context is "reply to source".
+
+ fchs->s_id = fchs->d_id; // (temporarily re-use the struct)
+ cpqfcTSPutLinkQue( cpqfcHBAdata, SCSI_REPORT_LUNS, fchs );
+ }
+ }
+ else // probably, the device doesn't support Report Luns
+ pLoggedInPort->ScsiNexus.VolumeSetAddressing = 0;
+ }
+ else // we have LUN info - check VSA mode
+ {
+ // for now, assume all LUNs will have same addr mode
+ // for VSA, payload byte 8 will be 0x40; otherwise, 0
+ pLoggedInPort->ScsiNexus.VolumeSetAddressing = ucBuff[8];
+
+ // Since we got a Report Luns answer, set lun masking flag
+ pLoggedInPort->ScsiNexus.LunMasking = 1;
+
+ if( LunListLen > 8*CPQFCTS_MAX_LUN) // We expect CPQFCTS_MAX_LUN max
+ LunListLen = 8*CPQFCTS_MAX_LUN;
+
+/*
+ printk("Device WWN %08X%08X Reports Luns @: ",
+ (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF),
+ (ULONG)(pLoggedInPort->u.liWWN>>32));
+
+ for( i=8; i<LunListLen+8; i+=8)
+ {
+ printk("%02X%02X ", ucBuff[i], ucBuff[i+1] );
+ }
+ printk("\n");
+*/
+
+ // Since the device was kind enough to tell us where the
+ // LUNs are, lets ensure they are contiguous for Linux's
+ // SCSI driver scan, which expects them to start at 0.
+ // Since Linux only supports 8 LUNs, only copy the first
+ // eight from the report luns command
+
+ // e.g., the Compaq RA4x00 f/w Rev 2.54 and above may report
+ // LUNs 4001, 4004, etc., because other LUNs are masked from
+ // this HBA (owned by someone else). We'll make those appear as
+ // LUN 0, 1... to Linux
+ {
+ int j;
+ int AppendLunList = 0;
+ // Walk through the LUN list. The 'j' array number is
+ // Linux's lun #, while the value of .lun[j] is the target's
+ // lun #.
+ // Once we build a LUN list, it's possible for a known device
+ // to go offline while volumes (LUNs) are added. Later,
+ // the device will do another PLOGI ... Report Luns command,
+ // and we must not alter the existing Linux Lun map.
+ // (This will be very rare).
+ for( j=0; j < CPQFCTS_MAX_LUN; j++)
+ {
+ if( pLoggedInPort->ScsiNexus.lun[j] != 0xFF )
+ {
+ AppendLunList = 1;
+ break;
+ }
+ }
+ if( AppendLunList )
+ {
+ int k;
+ int FreeLunIndex;
+// printk("cpqfcTS: AppendLunList\n");
+
+ // If we get a new Report Luns, we cannot change
+ // any existing LUN mapping! (Only additive entry)
+ // For all LUNs in ReportLun list
+ // if RL lun != ScsiNexus lun
+ // if RL lun present in ScsiNexus lun[], continue
+ // else find ScsiNexus lun[]==FF and add, continue
+
+ for( i=8, j=0; i<LunListLen+8 && j< CPQFCTS_MAX_LUN; i+=8, j++)
+ {
+ if( pLoggedInPort->ScsiNexus.lun[j] != ucBuff[i+1] )
+ {
+ // something changed from the last Report Luns
+ printk(" cpqfcTS: Report Lun change!\n");
+ for( k=0, FreeLunIndex=CPQFCTS_MAX_LUN;
+ k < CPQFCTS_MAX_LUN; k++)
+ {
+ if( pLoggedInPort->ScsiNexus.lun[k] == 0xFF)
+ {
+ FreeLunIndex = k;
+ break;
+ }
+ if( pLoggedInPort->ScsiNexus.lun[k] == ucBuff[i+1] )
+ break; // we already masked this lun
+ }
+ if( k >= CPQFCTS_MAX_LUN )
+ {
+ printk(" no room for new LUN %d\n", ucBuff[i+1]);
+ }
+ else if( k == FreeLunIndex ) // need to add LUN
+ {
+ pLoggedInPort->ScsiNexus.lun[k] = ucBuff[i+1];
+// printk("add [%d]->%02d\n", k, pLoggedInPort->ScsiNexus.lun[k]);
+
+ }
+ else
+ {
+ // lun already known
+ }
+ break;
+ }
+ }
+ // print out the new list...
+ for( j=0; j< CPQFCTS_MAX_LUN; j++)
+ {
+ if( pLoggedInPort->ScsiNexus.lun[j] == 0xFF)
+ break; // done
+// printk("[%d]->%02d ", j, pLoggedInPort->ScsiNexus.lun[j]);
+ }
+ }
+ else
+ {
+// printk("Linux SCSI LUNs[] -> Device LUNs: ");
+ // first time - this is easy
+ for( i=8, j=0; i<LunListLen+8 && j< CPQFCTS_MAX_LUN; i+=8, j++)
+ {
+ pLoggedInPort->ScsiNexus.lun[j] = ucBuff[i+1];
+// printk("[%d]->%02d ", j, pLoggedInPort->ScsiNexus.lun[j]);
+ }
+// printk("\n");
+ }
+ }
+ }
+
+Done:
+}
+
+// After successfully getting a "Process Login" (PRLI) from an
+// FC port, we want to Discover the LUNs so that we know the
+// addressing type (e.g., FCP-SCSI Volume Set Address, Peripheral
+// Unit Device), and whether SSP (Selective Storage Presentation or
+// Lun Masking) has made the LUN numbers non-zero based or
+// non-contiguous. To remain backward compatible with the SCSI-2
+// driver model, which expects a contiguous LUNs starting at 0,
+// will use the ReportLuns info to map from "device" to "Linux"
+// LUNs.
+static void IssueReportLunsCommand(
+ CPQFCHBA* cpqfcHBAdata,
+ TachFCHDR_GCMND* fchs)
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+ Scsi_Cmnd *Cmnd;
+ LONG x_ID;
+ ULONG ulStatus;
+ UCHAR *ucBuff;
+
+
+ if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn
+ {
+ printk("Discard Q'd ReportLun command\n");
+ goto Done;
+ }
+
+ // find the device (from port_id) we're talking to
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ NULL, // DON'T search Scsi Nexus
+ fchs->s_id & 0xFFFFFF,
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+ if( pLoggedInPort ) // we'd BETTER find it!
+ {
+
+
+ if( !(pLoggedInPort->fcp_info & TARGET_FUNCTION) )
+ goto Done; // forget it - FC device not a "target"
+
+ // now use the port's Scsi Command buffer for the
+ // Report Luns Command
+
+ Cmnd = &pLoggedInPort->ScsiCmnd;
+ ucBuff = pLoggedInPort->ReportLunsPayload;
+
+ memset( Cmnd, 0, sizeof(Scsi_Cmnd));
+ memset( ucBuff, 0, REPORT_LUNS_PL);
+
+ Cmnd->scsi_done = ScsiReportLunsDone;
+ Cmnd->host = cpqfcHBAdata->HostAdapter;
+
+ Cmnd->request_buffer = pLoggedInPort->ReportLunsPayload;
+ Cmnd->request_bufflen = REPORT_LUNS_PL;
+
+ Cmnd->cmnd[0] = 0xA0;
+ Cmnd->cmnd[8] = REPORT_LUNS_PL >> 8;
+ Cmnd->cmnd[9] = (UCHAR)REPORT_LUNS_PL;
+ Cmnd->cmd_len = 12;
+
+ Cmnd->channel = pLoggedInPort->ScsiNexus.channel;
+ Cmnd->target = pLoggedInPort->ScsiNexus.target;
+
+
+ ulStatus = cpqfcTSBuildExchange(
+ cpqfcHBAdata,
+ SCSI_IRE,
+ fchs,
+ Cmnd, // buffer for Report Lun data
+ &x_ID );// fcController->fcExchanges index, -1 if failed
+
+ if( !ulStatus ) // Exchange setup?
+ {
+ ulStatus = cpqfcTSStartExchange( cpqfcHBAdata, x_ID );
+ if( !ulStatus )
+ {
+ // submitted to Tach's Outbound Que (ERQ PI incremented)
+ // waited for completion for ELS type (Login frames issued
+ // synchronously)
+ }
+ else
+ // check reason for Exchange not being started - we might
+ // want to Queue and start later, or fail with error
+ {
+
+ }
+ }
+
+ else // Xchange setup failed...
+ printk(" cpqfcTSBuildExchange failed: %Xh\n", ulStatus );
+ }
+ else // like, we just got a PRLI ACC, and now the port is gone?
+ {
+ printk(" can't send ReportLuns - no login for port_id %Xh\n",
+ fchs->s_id & 0xFFFFFF);
+ }
+
+
+
+Done:
+
+}
+
+
+
+
+
+
+
+static void CompleteBoardLockCmnd( CPQFCHBA *cpqfcHBAdata)
+{
+ int i;
+ for( i = CPQFCTS_REQ_QUEUE_LEN-1; i>= 0; i--)
+ {
+ if( cpqfcHBAdata->BoardLockCmnd[i] != NULL )
+ {
+ Scsi_Cmnd *Cmnd = cpqfcHBAdata->BoardLockCmnd[i];
+ cpqfcHBAdata->BoardLockCmnd[i] = NULL;
+ Cmnd->result = (DID_SOFT_ERROR << 16); // ask for retry
+// printk(" BoardLockCmnd[%d] %p Complete, chnl/target/lun %d/%d/%d\n",
+// i,Cmnd, Cmnd->channel, Cmnd->target, Cmnd->lun);
+ if( Cmnd->scsi_done != NULL)
+ (*Cmnd->scsi_done)(Cmnd);
+ }
+ }
+}
+
+
+
+
+
+
+// runs every 1 second for FC exchange timeouts and implicit FC device logouts
+
+void cpqfcTSheartbeat( unsigned long ptr )
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)ptr;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts;
+ ULONG i;
+ unsigned long flags;
+ DECLARE_MUTEX_LOCKED(BoardLock);
+
+ PCI_TRACE( 0xA8)
+
+ if( cpqfcHBAdata->BoardLock) // Worker Task Running?
+ goto Skip;
+
+ spin_lock_irqsave( &io_request_lock, flags); // STOP _que function
+
+ PCI_TRACE( 0xA8)
+
+
+ cpqfcHBAdata->BoardLock = &BoardLock; // stop Linux SCSI command queuing
+
+ // release the IO lock (and re-enable interrupts)
+ spin_unlock_irqrestore( &io_request_lock, flags);
+
+ // Ensure no contention from _quecommand or Worker process
+ CPQ_SPINLOCK_HBA( cpqfcHBAdata)
+
+ PCI_TRACE( 0xA8)
+
+
+ disable_irq( cpqfcHBAdata->HostAdapter->irq); // our IRQ
+
+ // Complete the "bad target" commands (normally only used during
+ // initialization, since we aren't supposed to call "scsi_done"
+ // inside the queuecommand() function).
+
+ for( i=0; i< CPQFCTS_MAX_TARGET_ID; i++)
+ {
+ if( cpqfcHBAdata->BadTargetCmnd[i] )
+ {
+ Scsi_Cmnd *Cmnd = cpqfcHBAdata->BadTargetCmnd[i];
+ cpqfcHBAdata->BadTargetCmnd[i] = NULL;
+ Cmnd->result = (DID_BAD_TARGET << 16);
+ if( Cmnd->scsi_done != NULL)
+ (*Cmnd->scsi_done)(Cmnd);
+ }
+ else
+ break;
+ }
+
+
+ // logged in ports -- re-login check (ports required to verify login with
+ // PDISC after LIP within 2 secs)
+
+ // prevent contention
+ while( pLoggedInPort ) // for all ports which are expecting
+ // PDISC after the next LIP, check to see if
+ // time is up!
+ {
+ // Important: we only detect "timeout" condition on TRANSITION
+ // from non-zero to zero
+ if( pLoggedInPort->LOGO_timer ) // time-out "armed"?
+ {
+ if( !(--pLoggedInPort->LOGO_timer) ) // DEC from 1 to 0?
+ {
+ // LOGOUT time! Per PLDA, PDISC hasn't complete in 2 secs, so
+ // issue LOGO request and destroy all I/O with other FC port(s).
+
+/*
+ printk(" ~cpqfcTS heartbeat: LOGOut!~ ");
+ printk("Linux SCSI Chanl/Target %d/%d (port_id %06Xh) WWN %08X%08X\n",
+ pLoggedInPort->ScsiNexus.channel,
+ pLoggedInPort->ScsiNexus.target,
+ pLoggedInPort->port_id,
+ (ULONG)(pLoggedInPort->u.liWWN &0xFFFFFFFF),
+ (ULONG)(pLoggedInPort->u.liWWN>>32));
+
+*/
+ cpqfcTSImplicitLogout( cpqfcHBAdata, pLoggedInPort);
+
+ }
+ // else simply decremented - maybe next time...
+ }
+ pLoggedInPort = pLoggedInPort->pNextPort;
+ }
+
+
+
+
+
+ // ************ FC EXCHANGE TIMEOUT CHECK **************
+
+ for( i=0; i< TACH_MAX_XID; i++)
+ {
+ if( Exchanges->fcExchange[i].type ) // exchange defined?
+ {
+
+ if( !Exchanges->fcExchange[i].timeOut ) // time expired
+ {
+ // Set Exchange timeout status
+ Exchanges->fcExchange[i].status |= FC2_TIMEOUT;
+
+ if( i >= TACH_SEST_LEN ) // Link Service Exchange
+ {
+ cpqfcTSCompleteExchange( fcChip, i); // Don't "abort" LinkService
+ }
+
+ else // SEST Exchange TO -- may post ABTS to Worker Thread Que
+ {
+ // (Make sure we don't keep timing it out; let other functions
+ // complete it or set the timeOut as needed)
+ Exchanges->fcExchange[i].timeOut = 30000; // seconds default
+
+ if( Exchanges->fcExchange[i].type
+ &
+ (BLS_ABTS | BLS_ABTS_ACC ) )
+ {
+ // For BLS_ABTS*, an upper level might still have
+ // an outstanding command waiting for low-level completion.
+ // Also, in the case of a WRITE, we MUST get confirmation
+ // of either ABTS ACC or RJT before re-using the Exchange.
+ // It's possible that the RAID cache algorithm can hang
+ // if we fail to complete a WRITE to a LBA, when a READ
+ // comes later to that same LBA. Therefore, we must
+ // ensure that the target verifies receipt of ABTS for
+ // the exchange
+
+ printk("~TO Q'd ABTS (x_ID %Xh)~ ", i);
+// TriggerHBA( fcChip->Registers.ReMapMemBase);
+
+ // On timeout of a ABTS exchange, check to
+ // see if the FC device has a current valid login.
+ // If so, restart it.
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ Exchanges->fcExchange[i].Cmnd, // find Scsi Nexus
+ 0, // DON'T search linked list for FC port id
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ // device exists?
+ if( pLoggedInPort ) // device exists?
+ {
+ if( pLoggedInPort->prli ) // logged in for FCP-SCSI?
+ {
+ // attempt to restart the ABTS
+ printk(" ~restarting ABTS~ ");
+ cpqfcTSStartExchange( cpqfcHBAdata, i );
+
+ }
+ }
+ }
+ else // not an ABTS
+ {
+
+ // We expect the WorkerThread to change the xchng type to
+ // abort and set appropriate timeout.
+ cpqfcTSPutLinkQue( cpqfcHBAdata, BLS_ABTS, &i ); // timed-out
+ }
+ }
+ }
+ else // time not expired...
+ {
+ // decrement timeout: 1 or more seconds left
+ --Exchanges->fcExchange[i].timeOut;
+ }
+ }
+ }
+
+
+ enable_irq( cpqfcHBAdata->HostAdapter->irq);
+
+
+ CPQ_SPINUNLOCK_HBA( cpqfcHBAdata)
+
+ cpqfcHBAdata->BoardLock = NULL; // Linux SCSI commands may be queued
+
+ // Now, complete any Cmnd we Q'd up while BoardLock was held
+
+ CompleteBoardLockCmnd( cpqfcHBAdata);
+
+
+ // restart the timer to run again (1 sec later)
+Skip:
+ mod_timer( &cpqfcHBAdata->cpqfcTStimer, jiffies + HZ);
+
+ PCI_TRACEO( i, 0xA8)
+ return;
+}
+
+
+// put valid FC-AL physical address in spec order
+static const UCHAR valid_al_pa[]={
+ 0xef, 0xe8, 0xe4, 0xe2,
+ 0xe1, 0xE0, 0xDC, 0xDA,
+ 0xD9, 0xD6, 0xD5, 0xD4,
+ 0xD3, 0xD2, 0xD1, 0xCe,
+ 0xCd, 0xCc, 0xCb, 0xCa,
+ 0xC9, 0xC7, 0xC6, 0xC5,
+ 0xC3, 0xBc, 0xBa, 0xB9,
+ 0xB6, 0xB5, 0xB4, 0xB3,
+ 0xB2, 0xB1, 0xae, 0xad,
+ 0xAc, 0xAb, 0xAa, 0xA9,
+
+ 0xA7, 0xA6, 0xA5, 0xA3,
+ 0x9f, 0x9e, 0x9d, 0x9b,
+ 0x98, 0x97, 0x90, 0x8f,
+ 0x88, 0x84, 0x82, 0x81,
+ 0x80, 0x7c, 0x7a, 0x79,
+ 0x76, 0x75, 0x74, 0x73,
+ 0x72, 0x71, 0x6e, 0x6d,
+ 0x6c, 0x6b, 0x6a, 0x69,
+ 0x67, 0x66, 0x65, 0x63,
+ 0x5c, 0x5a, 0x59, 0x56,
+
+ 0x55, 0x54, 0x53, 0x52,
+ 0x51, 0x4e, 0x4d, 0x4c,
+ 0x4b, 0x4a, 0x49, 0x47,
+ 0x46, 0x45, 0x43, 0x3c,
+ 0x3a, 0x39, 0x36, 0x35,
+ 0x34, 0x33, 0x32, 0x31,
+ 0x2e, 0x2d, 0x2c, 0x2b,
+ 0x2a, 0x29, 0x27, 0x26,
+ 0x25, 0x23, 0x1f, 0x1E,
+ 0x1d, 0x1b, 0x18, 0x17,
+
+ 0x10, 0x0f, 8, 4, 2, 1 }; // ALPA 0 (Fabric) is special case
+
+const int number_of_al_pa = (sizeof(valid_al_pa) );
+
+
+
+// this function looks up an al_pa from the table of valid al_pa's
+// we decrement from the last decimal loop ID, because soft al_pa
+// (our typical case) are assigned with highest priority (and high al_pa)
+// first. See "In-Depth FC-AL", R. Kembel pg. 38
+// INPUTS:
+// al_pa - 24 bit port identifier (8 bit al_pa on private loop)
+// RETURN:
+// Loop ID - serves are index to array of logged in ports
+// -1 - invalid al_pa (not all 8 bit values are legal)
+
+#if (0)
+static int GetLoopID( ULONG al_pa )
+{
+ int i;
+
+ for( i = number_of_al_pa -1; i >= 0; i--) // dec.
+ {
+ if( valid_al_pa[i] == (UCHAR)al_pa ) // take lowest 8 bits
+ return i; // success - found valid al_pa; return decimal LoopID
+ }
+ return -1; // failed - not found
+}
+#endif
+
+
+// Search the singly (forward) linked list "fcPorts" looking for
+// either the SCSI target (if != -1), port_id (if not NULL),
+// or WWN (if not null), in that specific order.
+// If we find a SCSI nexus (from Cmnd arg), set the SCp.phase
+// field according to VSA or PDU
+// RETURNS:
+// Ptr to logged in port struct if found
+// (NULL if not found)
+// pLastLoggedInPort - ptr to last struct (for adding new ones)
+//
+PFC_LOGGEDIN_PORT fcFindLoggedInPort(
+ PTACHYON fcChip,
+ Scsi_Cmnd *Cmnd, // search linked list for Scsi Nexus (channel/target/lun)
+ ULONG port_id, // search linked list for al_pa, or
+ UCHAR wwn[8], // search linked list for WWN, or...
+ PFC_LOGGEDIN_PORT *pLastLoggedInPort )
+
+{
+ PFC_LOGGEDIN_PORT pLoggedInPort = &fcChip->fcPorts;
+ BOOLEAN target_id_valid=FALSE;
+ BOOLEAN port_id_valid=FALSE;
+ BOOLEAN wwn_valid=FALSE;
+ int i;
+
+
+ if( Cmnd != NULL )
+ target_id_valid = TRUE;
+
+ else if( port_id ) // note! 24-bit NULL address is illegal
+ port_id_valid = TRUE;
+
+ else
+ {
+ for( i=0; i<8; i++) // valid WWN passed? NULL WWN invalid
+ {
+ if( wwn ) // non-null arg? (OK to pass NULL when not searching WWN)
+ {
+ if( wwn[i] != 0 )
+ wwn_valid = TRUE; // any non-zero byte makes (presumably) valid
+ }
+ }
+ }
+ // check other options ...
+
+
+ // In case multiple search options are given, we use a priority
+ // scheme:
+ // While valid pLoggedIn Ptr
+ // If port_id is valid
+ // if port_id matches, return Ptr
+ // If wwn is valid
+ // if wwn matches, return Ptr
+ // Next Ptr in list
+ //
+ // Return NULL (not found)
+
+
+ while( pLoggedInPort ) // NULL marks end of list (1st ptr always valid)
+ {
+ if( pLastLoggedInPort ) // caller's pointer valid?
+ *pLastLoggedInPort = pLoggedInPort; // end of linked list
+
+ if( target_id_valid )
+ {
+ // check Linux Scsi Cmnd for channel/target Nexus match
+ // (all luns are accessed through matching "pLoggedInPort")
+ if( (pLoggedInPort->ScsiNexus.target == Cmnd->target)
+ &&
+ (pLoggedInPort->ScsiNexus.channel == Cmnd->channel))
+ {
+ // For "passthru" modes, the IOCTL caller is responsible
+ // for setting the FCP-LUN addressing
+ if( !Cmnd->SCp.sent_command ) // NOT passthru?
+ {
+
+ // set the FCP-LUN addressing type
+ Cmnd->SCp.phase = pLoggedInPort->ScsiNexus.VolumeSetAddressing;
+
+ // set the Device Type we got from the snooped INQUIRY string
+ Cmnd->SCp.Message = pLoggedInPort->ScsiNexus.InqDeviceType;
+
+ // handle LUN masking; if not "default" (illegal) lun value,
+ // the use it. These lun values are set by a successful
+ // Report Luns command
+ if( pLoggedInPort->ScsiNexus.LunMasking == 1)
+ {
+ // we KNOW all the valid LUNs... 0xFF is invalid!
+ Cmnd->SCp.have_data_in = pLoggedInPort->ScsiNexus.lun[Cmnd->lun];
+ }
+ else
+ Cmnd->SCp.have_data_in = Cmnd->lun; // Linux & target luns match
+ }
+ break; // found it!
+ }
+ }
+
+ if( port_id_valid ) // look for alpa first
+ {
+ if( pLoggedInPort->port_id == port_id )
+ break; // found it!
+ }
+ if( wwn_valid ) // look for wwn second
+ {
+
+ if( !memcmp( &pLoggedInPort->u.ucWWN[0], &wwn[0], 8))
+ {
+ // all 8 bytes of WWN match
+ break; // found it!
+ }
+ }
+
+ pLoggedInPort = pLoggedInPort->pNextPort; // try next port
+ }
+
+ return pLoggedInPort;
+}
+
+
+
+
+//
+// We need to examine the SEST table and re-validate
+// any open Exchanges for this LoggedInPort
+// To make Tachyon pay attention, Freeze FCP assists,
+// set VAL bits, Unfreeze FCP assists
+static void RevalidateSEST( struct Scsi_Host *HostAdapter,
+ PFC_LOGGEDIN_PORT pLoggedInPort)
+{
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG x_ID;
+ BOOLEAN TachFroze = FALSE;
+
+
+ // re-validate any SEST exchanges that are permitted
+ // to survive the link down (e.g., good PDISC performed)
+ for( x_ID = 0; x_ID < TACH_SEST_LEN; x_ID++)
+ {
+
+ // If the SEST entry port_id matches the pLoggedInPort,
+ // we need to re-validate
+ if( (Exchanges->fcExchange[ x_ID].type == SCSI_IRE)
+ ||
+ (Exchanges->fcExchange[ x_ID].type == SCSI_IWE))
+ {
+
+ if( (Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF) // (24-bit port ID)
+ == pLoggedInPort->port_id)
+ {
+// printk(" re-val xID %Xh ", x_ID);
+ if( !TachFroze ) // freeze if not already frozen
+ TachFroze |= FreezeTach( cpqfcHBAdata);
+ fcChip->SEST->u[ x_ID].IWE.Hdr_Len |= 0x80000000; // set VAL bit
+ }
+ }
+ }
+
+ if( TachFroze)
+ {
+ fcChip->UnFreezeTachyon( fcChip, 2); // both ERQ and FCP assists
+ }
+}
+
+
+// Complete an Linux Cmnds that we Queued because
+// our FC link was down (cause immediate retry)
+
+static void UnblockScsiDevice( struct Scsi_Host *HostAdapter,
+ PFC_LOGGEDIN_PORT pLoggedInPort)
+{
+// Scsi_Device *sdev = HostAdapter->host_queue;
+ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;
+ Scsi_Cmnd* *SCptr = &cpqfcHBAdata->LinkDnCmnd[0];
+ Scsi_Cmnd *Cmnd;
+ int indx;
+
+
+
+ // if the device was previously "blocked", make sure
+ // we unblock it so Linux SCSI will resume
+
+ pLoggedInPort->device_blocked = FALSE; // clear our flag
+
+ // check the Link Down command ptr buffer;
+ // we can complete now causing immediate retry
+ for( indx=0; indx < CPQFCTS_REQ_QUEUE_LEN; indx++, SCptr++)
+ {
+ if( *SCptr != NULL ) // scsi command to complete?
+ {
+#ifdef DUMMYCMND_DBG
+ printk("complete Cmnd %p in LinkDnCmnd[%d]\n", *SCptr,indx);
+#endif
+ Cmnd = *SCptr;
+
+
+ // Are there any Q'd commands for this target?
+ if( (Cmnd->target == pLoggedInPort->ScsiNexus.target)
+ &&
+ (Cmnd->channel == pLoggedInPort->ScsiNexus.channel) )
+ {
+ Cmnd->result = (DID_SOFT_ERROR <<16); // force retry
+ if( Cmnd->scsi_done != NULL)
+ (*Cmnd->scsi_done)(Cmnd);
+ else
+ printk("LinkDnCmnd scsi_done ptr null, port_id %Xh\n",
+ pLoggedInPort->port_id);
+ *SCptr = NULL; // free this slot for next use
+ }
+ }
+ }
+}
+
+
+//#define WWN_DBG 1
+
+static void SetLoginFields(
+ PFC_LOGGEDIN_PORT pLoggedInPort,
+ TachFCHDR_GCMND* fchs,
+ BOOLEAN PDisc,
+ BOOLEAN Originator)
+{
+ LOGIN_PAYLOAD logi; // FC-PH Port Login
+ PRLI_REQUEST prli; // copy for BIG ENDIAN switch
+ int i;
+#ifdef WWN_DBG
+ ULONG ulBuff;
+#endif
+
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&logi, sizeof(logi));
+
+ pLoggedInPort->Originator = Originator;
+ pLoggedInPort->port_id = fchs->s_id & 0xFFFFFF;
+
+ switch( fchs->pl[0] & 0xffff )
+ {
+ case 0x00000002: // PLOGI or PDISC ACCept?
+ if( PDisc ) // PDISC accept
+ goto PDISC_case;
+
+ case 0x00000003: // ELS_PLOGI or ELS_PLOGI_ACC
+
+ // Login BB_credit typically 0 for Tachyons
+ pLoggedInPort->BB_credit = logi.cmn_services.bb_credit;
+
+ // e.g. 128, 256, 1024, 2048 per FC-PH spec
+ // We have to use this when setting up SEST Writes,
+ // since that determines frame size we send.
+ pLoggedInPort->rx_data_size = logi.class3.rx_data_size;
+ pLoggedInPort->plogi = TRUE;
+ pLoggedInPort->pdisc = FALSE;
+ pLoggedInPort->prli = FALSE; // ELS_PLOGI resets
+ pLoggedInPort->flogi = FALSE; // ELS_PLOGI resets
+ pLoggedInPort->logo = FALSE; // ELS_PLOGI resets
+ pLoggedInPort->LOGO_counter = 0;// ELS_PLOGI resets
+ pLoggedInPort->LOGO_timer = 0;// ELS_PLOGI resets
+
+ // was this PLOGI to a Fabric?
+ if( pLoggedInPort->port_id == 0xFFFFFC ) // well know address
+ pLoggedInPort->flogi = TRUE;
+
+
+ for( i=0; i<8; i++) // copy the LOGIN port's WWN
+ pLoggedInPort->u.ucWWN[i] = logi.port_name[i];
+
+#ifdef WWN_DBG
+ ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+ if( pLoggedInPort->Originator)
+ printk("o");
+ else
+ printk("r");
+ printk("PLOGI port_id %Xh, WWN %08X",
+ pLoggedInPort->port_id, ulBuff);
+
+ ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+ printk("%08Xh fcPort %p\n", ulBuff, pLoggedInPort);
+#endif
+ break;
+
+
+
+
+ case 0x00000005: // ELS_LOGO (logout)
+
+
+ pLoggedInPort->plogi = FALSE;
+ pLoggedInPort->pdisc = FALSE;
+ pLoggedInPort->prli = FALSE; // ELS_PLOGI resets
+ pLoggedInPort->flogi = FALSE; // ELS_PLOGI resets
+ pLoggedInPort->logo = TRUE; // ELS_PLOGI resets
+ pLoggedInPort->LOGO_counter++; // ELS_PLOGI resets
+ pLoggedInPort->LOGO_timer = 0;
+#ifdef WWN_DBG
+ ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+ if( pLoggedInPort->Originator)
+ printk("o");
+ else
+ printk("r");
+ printk("LOGO port_id %Xh, WWN %08X",
+ pLoggedInPort->port_id, ulBuff);
+
+ ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+ printk("%08Xh\n", ulBuff);
+#endif
+ break;
+
+
+
+PDISC_case:
+ case 0x00000050: // ELS_PDISC or ELS_PDISC_ACC
+ pLoggedInPort->LOGO_timer = 0; // stop the time-out
+
+ pLoggedInPort->prli = TRUE; // ready to accept FCP-SCSI I/O
+
+
+
+#ifdef WWN_DBG
+ ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+ if( pLoggedInPort->Originator)
+ printk("o");
+ else
+ printk("r");
+ printk("PDISC port_id %Xh, WWN %08X",
+ pLoggedInPort->port_id, ulBuff);
+
+ ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+ printk("%08Xh\n", ulBuff);
+#endif
+
+
+
+ break;
+
+
+
+ case 0x1020L: // PRLI?
+ case 0x1002L: // PRLI ACCept?
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&prli, sizeof(prli));
+
+ pLoggedInPort->fcp_info = prli.fcp_info; // target/initiator flags
+ pLoggedInPort->prli = TRUE; // PLOGI resets, PDISC doesn't
+
+ pLoggedInPort->pdisc = TRUE; // expect to send (or receive) PDISC
+ // next time
+ pLoggedInPort->LOGO_timer = 0; // will be set next LinkDown
+#ifdef WWN_DBG
+ ulBuff = (ULONG)pLoggedInPort->u.liWWN;
+ if( pLoggedInPort->Originator)
+ printk("o");
+ else
+ printk("r");
+ printk("PRLI port_id %Xh, WWN %08X",
+ pLoggedInPort->port_id, ulBuff);
+
+ ulBuff = (ULONG)(pLoggedInPort->u.liWWN >> 32);
+ printk("%08Xh\n", ulBuff);
+#endif
+
+ break;
+
+ }
+
+ return;
+}
+
+
+
+
+
+
+static void BuildLinkServicePayload( PTACHYON fcChip, ULONG type, void* payload)
+{
+ LOGIN_PAYLOAD *plogi; // FC-PH Port Login
+ LOGIN_PAYLOAD PlogiPayload; // copy for BIG ENDIAN switch
+ PRLI_REQUEST *prli; // FCP-SCSI Process Login
+ PRLI_REQUEST PrliPayload; // copy for BIG ENDIAN switch
+ LOGOUT_PAYLOAD *logo;
+ LOGOUT_PAYLOAD LogoutPayload;
+// PRLO_REQUEST *prlo;
+// PRLO_REQUEST PrloPayload;
+ REJECT_MESSAGE rjt, *prjt;
+
+ memset( &PlogiPayload, 0, sizeof( PlogiPayload));
+ plogi = &PlogiPayload; // load into stack buffer,
+ // then BIG-ENDIAN switch a copy to caller
+
+
+ switch( type ) // payload type can be ELS_PLOGI, ELS_PRLI, ADISC, ...
+ {
+ case ELS_FDISC:
+ case ELS_FLOGI:
+ case ELS_PLOGI_ACC: // FC-PH PORT Login Accept
+ case ELS_PLOGI: // FC-PH PORT Login
+ case ELS_PDISC: // FC-PH2 Port Discovery - same payload as ELS_PLOGI
+ plogi->login_cmd = LS_PLOGI;
+ if( type == ELS_PDISC)
+ plogi->login_cmd = LS_PDISC;
+ else if( type == ELS_PLOGI_ACC )
+ plogi->login_cmd = LS_ACC;
+
+ plogi->cmn_services.bb_credit = 0x00;
+ plogi->cmn_services.lowest_ver = fcChip->lowest_FCPH_ver;
+ plogi->cmn_services.highest_ver = fcChip->highest_FCPH_ver;
+ plogi->cmn_services.bb_rx_size = TACHLITE_TS_RX_SIZE;
+ plogi->cmn_services.common_features = CONTINUOSLY_INCREASING |
+ RANDOM_RELATIVE_OFFSET;
+
+ // fill in with World Wide Name based Port Name - 8 UCHARs
+ // get from Tach registers WWN hi & lo
+ LoadWWN( fcChip, plogi->port_name, 0);
+ // fill in with World Wide Name based Node/Fabric Name - 8 UCHARs
+ // get from Tach registers WWN hi & lo
+ LoadWWN( fcChip, plogi->node_name, 1);
+
+ // For Seagate Drives.
+ //
+ plogi->cmn_services.common_features |= 0x800;
+ plogi->cmn_services.rel_offset = 0xFE;
+ plogi->cmn_services.concurrent_seq = 1;
+ plogi->class1.service_options = 0x00;
+ plogi->class2.service_options = 0x00;
+ plogi->class3.service_options = CLASS_VALID;
+ plogi->class3.initiator_control = 0x00;
+ plogi->class3.rx_data_size = MAX_RX_PAYLOAD;
+ plogi->class3.recipient_control =
+ ERROR_DISCARD | ONE_CATEGORY_SEQUENCE;
+ plogi->class3.concurrent_sequences = 1;
+ plogi->class3.open_sequences = 1;
+ plogi->vendor_id[0] = 'C'; plogi->vendor_id[1] = 'Q';
+ plogi->vendor_version[0] = 'C'; plogi->vendor_version[1] = 'Q';
+ plogi->vendor_version[2] = ' '; plogi->vendor_version[3] = '0';
+ plogi->vendor_version[4] = '0'; plogi->vendor_version[5] = '0';
+
+
+ // FLOGI specific fields... (see FC-FLA, Rev 2.7, Aug 1999, sec 5.1)
+ if( (type == ELS_FLOGI) || (type == ELS_FDISC) )
+ {
+ if( type == ELS_FLOGI )
+ plogi->login_cmd = LS_FLOGI;
+ else
+ plogi->login_cmd = LS_FDISC;
+
+ plogi->cmn_services.lowest_ver = 0x20;
+ plogi->cmn_services.common_features = 0x0800;
+ plogi->cmn_services.rel_offset = 0;
+ plogi->cmn_services.concurrent_seq = 0;
+
+ plogi->class3.service_options = 0x8800;
+ plogi->class3.rx_data_size = 0;
+ plogi->class3.recipient_control = 0;
+ plogi->class3.concurrent_sequences = 0;
+ plogi->class3.open_sequences = 0;
+ }
+
+ // copy back to caller's buff, w/ BIG ENDIAN swap
+ BigEndianSwap( (UCHAR*)&PlogiPayload, payload, sizeof(PlogiPayload));
+ break;
+
+
+ case ELS_ACC: // generic Extended Link Service ACCept
+ plogi->login_cmd = LS_ACC;
+ // copy back to caller's buff, w/ BIG ENDIAN swap
+ BigEndianSwap( (UCHAR*)&PlogiPayload, payload, 4);
+ break;
+
+
+
+ case ELS_SCR: // Fabric State Change Registration
+ {
+ SCR_PL scr; // state change registration
+
+ memset( &scr, 0, sizeof(scr));
+
+ scr.command = LS_SCR; // 0x62000000
+ // see FC-FLA, Rev 2.7, Table A.22 (pg 82)
+ scr.function = 3; // 1 = Events detected by Fabric
+ // 2 = N_Port detected registration
+ // 3 = Full registration
+
+ // copy back to caller's buff, w/ BIG ENDIAN swap
+ BigEndianSwap( (UCHAR*)&scr, payload, sizeof(SCR_PL));
+ }
+
+ break;
+
+
+ case FCS_NSR: // Fabric Name Service Request
+ {
+ NSR_PL nsr; // Name Server Req. payload
+
+ memset( &nsr, 0, sizeof(NSR_PL));
+
+ // see Brocade Fabric Programming Guide,
+ // Rev 1.3, pg 4-44
+ nsr.CT_Rev = 0x01000000;
+ nsr.FCS_Type = 0xFC020000;
+ nsr.Command_code = 0x01710000;
+ nsr.FCP = 8;
+
+ // copy back to caller's buff, w/ BIG ENDIAN swap
+ BigEndianSwap( (UCHAR*)&nsr, payload, sizeof(NSR_PL));
+ }
+
+ break;
+
+
+
+
+ case ELS_LOGO: // FC-PH PORT LogOut
+ logo = &LogoutPayload; // load into stack buffer,
+ // then BIG-ENDIAN switch a copy to caller
+ logo->cmd = LS_LOGO;
+ // load the 3 UCHARs of the node name
+ // (if private loop, upper two UCHARs 0)
+ logo->reserved = 0;
+
+ logo->n_port_identifier[0] = (UCHAR)(fcChip->Registers.my_al_pa);
+ logo->n_port_identifier[1] =
+ (UCHAR)(fcChip->Registers.my_al_pa>>8);
+ logo->n_port_identifier[2] =
+ (UCHAR)(fcChip->Registers.my_al_pa>>16);
+ // fill in with World Wide Name based Port Name - 8 UCHARs
+ // get from Tach registers WWN hi & lo
+ LoadWWN( fcChip, logo->port_name, 0);
+
+ BigEndianSwap( (UCHAR*)&LogoutPayload,
+ payload, sizeof(LogoutPayload) ); // 16 UCHAR struct
+ break;
+
+
+ case ELS_LOGO_ACC: // Logout Accept (FH-PH pg 149, table 74)
+ logo = &LogoutPayload; // load into stack buffer,
+ // then BIG-ENDIAN switch a copy to caller
+ logo->cmd = LS_ACC;
+ BigEndianSwap( (UCHAR*)&LogoutPayload, payload, 4 ); // 4 UCHAR cmnd
+ break;
+
+
+ case ELS_RJT: // ELS_RJT link service reject (FH-PH pg 155)
+
+ prjt = (REJECT_MESSAGE*)payload; // pick up passed data
+ rjt.command_code = ELS_RJT;
+ // reverse fields, because of Swap that follows...
+ rjt.vendor = prjt->reserved; // vendor specific
+ rjt.explain = prjt->reason; //
+ rjt.reason = prjt->explain; //
+ rjt.reserved = prjt->vendor; //
+ // BIG-ENDIAN switch a copy to caller
+ BigEndianSwap( (UCHAR*)&rjt, payload, 8 ); // 8 UCHAR cmnd
+ break;
+
+
+
+
+
+ case ELS_PRLI_ACC: // Process Login ACCept
+ case ELS_PRLI: // Process Login
+ case ELS_PRLO: // Process Logout
+ memset( &PrliPayload, 0, sizeof( PrliPayload));
+ prli = &PrliPayload; // load into stack buffer,
+
+ if( type == ELS_PRLI )
+ prli->cmd = 0x20; // Login
+ else if( type == ELS_PRLO )
+ prli->cmd = 0x21; // Logout
+ else if( type == ELS_PRLI_ACC )
+ {
+ prli->cmd = 0x02; // Login ACCept
+ prli->valid = REQUEST_EXECUTED;
+ }
+
+
+ prli->valid |= SCSI_FCP | ESTABLISH_PAIR;
+ prli->fcp_info = READ_XFER_RDY;
+ prli->page_length = 0x10;
+ prli->payload_length = 20;
+ // Can be initiator AND target
+
+ if( fcChip->Options.initiator )
+ prli->fcp_info |= INITIATOR_FUNCTION;
+ if( fcChip->Options.target )
+ prli->fcp_info |= TARGET_FUNCTION;
+
+ BigEndianSwap( (UCHAR*)&PrliPayload, payload, prli->payload_length);
+ break;
+
+
+
+ default: // no can do - programming error
+ printk(" BuildLinkServicePayload unknown!\n");
+ break;
+ }
+}
+
+// loads 8 UCHARs for PORT name or NODE name base on
+// controller's WWN.
+void LoadWWN( PTACHYON fcChip, UCHAR* dest, UCHAR type)
+{
+ UCHAR* bPtr, i;
+
+ switch( type )
+ {
+ case 0: // Port_Name
+ bPtr = (UCHAR*)&fcChip->Registers.wwn_hi;
+ for( i =0; i<4; i++)
+ dest[i] = *bPtr++;
+ bPtr = (UCHAR*)&fcChip->Registers.wwn_lo;
+ for( i =4; i<8; i++)
+ dest[i] = *bPtr++;
+ break;
+ case 1: // Node/Fabric _Name
+ bPtr = (UCHAR*)&fcChip->Registers.wwn_hi;
+ for( i =0; i<4; i++)
+ dest[i] = *bPtr++;
+ bPtr = (UCHAR*)&fcChip->Registers.wwn_lo;
+ for( i =4; i<8; i++)
+ dest[i] = *bPtr++;
+ break;
+ }
+
+}
+
+
+
+// We check the Port Login payload for required values. Note that
+// ELS_PLOGI and ELS_PDISC (Port DISCover) use the same payload.
+
+
+int verify_PLOGI( PTACHYON fcChip,
+ TachFCHDR_GCMND* fchs,
+ ULONG* reject_explain)
+{
+ LOGIN_PAYLOAD login;
+
+ // source, dest, len (should be mult. of 4)
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&login, sizeof(login));
+
+ // check FC version
+ // if other port's highest supported version
+ // is less than our lowest, and
+ // if other port's lowest
+ if( login.cmn_services.highest_ver < fcChip->lowest_FCPH_ver ||
+ login.cmn_services.lowest_ver > fcChip->highest_FCPH_ver )
+ {
+ *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, OPTIONS_ERROR);
+ return LOGICAL_ERROR;
+ }
+
+ // Receive Data Field Size must be >=128
+ // per FC-PH
+ if (login.cmn_services.bb_rx_size < 128)
+ {
+ *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, DATA_FIELD_SIZE_ERROR);
+ return LOGICAL_ERROR;
+ }
+
+ // Only check Class 3 params
+ if( login.class3.service_options & CLASS_VALID)
+ {
+ if (login.class3.rx_data_size < 128)
+ {
+ *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, INVALID_CSP);
+ return LOGICAL_ERROR;
+ }
+ if( login.class3.initiator_control & XID_REQUIRED)
+ {
+ *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, INITIATOR_CTL_ERROR);
+ return LOGICAL_ERROR;
+ }
+ }
+ return 0; // success
+}
+
+
+
+
+int verify_PRLI( TachFCHDR_GCMND* fchs, ULONG* reject_explain)
+{
+ PRLI_REQUEST prli; // buffer for BIG ENDIAN
+
+ // source, dest, len (should be mult. of 4)
+ BigEndianSwap( (UCHAR*)&fchs->pl[0], (UCHAR*)&prli, sizeof(prli));
+
+ if( prli.fcp_info == 0 ) // i.e., not target or initiator?
+ {
+ *reject_explain = LS_RJT_REASON( LOGICAL_ERROR, OPTIONS_ERROR);
+ return LOGICAL_ERROR;
+ }
+
+ return 0; // success
+}
+
+
+// SWAP UCHARs as required by Fibre Channel (i.e. BIG ENDIAN)
+// INPUTS:
+// source - ptr to LITTLE ENDIAN ULONGS
+// cnt - number of UCHARs to switch (should be mult. of ULONG)
+// OUTPUTS:
+// dest - ptr to BIG ENDIAN copy
+// RETURN:
+// none
+//
+void BigEndianSwap( UCHAR *source, UCHAR *dest, USHORT cnt)
+{
+ int i,j;
+
+ source+=3; // start at MSB of 1st ULONG
+ for( j=0; j < cnt; j+=4, source+=4, dest+=4) // every ULONG
+ {
+ for( i=0; i<4; i++) // every UCHAR in ULONG
+ *(dest+i) = *(source-i);
+ }
+}
+
+
+
+
+// Build FC Exchanges............
+
+static void buildFCPstatus(
+ PTACHYON fcChip,
+ ULONG ExchangeID);
+
+static LONG FindFreeExchange( PTACHYON fcChip, ULONG type );
+
+static ULONG build_SEST_sgList(
+ ULONG *SESTalPairStart,
+ Scsi_Cmnd *Cmnd,
+ ULONG *sgPairs,
+ PSGPAGES sgPages // link list of TL Ext. S/G pages from O/S Pool
+);
+
+static int build_FCP_payload( Scsi_Cmnd *Cmnd,
+ UCHAR* payload, ULONG type, ULONG fcp_dl );
+
+
+/*
+ IRB
+ ERQ __________________
+ | | / | Req_A_SFS_Len | ____________________
+ |----------| / | Req_A_SFS_Addr |------->| Reserved |
+ | IRB | / | Req_A_D_ID | | SOF EOF TimeStamp |
+ |-----------/ | Req_A_SEST_Index |-+ | R_CTL | D_ID |
+ | IRB | | Req_B... | | | CS_CTL| S_ID |
+ |-----------\ | | | | TYPE | F_CTL |
+ | IRB | \ | | | | SEQ_ID | SEQ_CNT |
+ |----------- \ | | +-->+--| OX_ID | RX_ID |
+ | | \ |__________________| | | RO |
+ | | pl (payload/cmnd) |
+ | | ..... |
+ | |___________________|
+ |
+ |
++-------------------------------------------+
+|
+|
+| e.g. IWE
+| SEST __________________ for FCP_DATA
+| | | / | | Hdr_Len | ____________________
+| |----------| / | Hdr_Addr_Addr |------->| Reserved |
+| | [0] | / |Remote_ID| RSP_Len| | SOF EOF TimeStamp |
+| |-----------/ | RSP_Addr |---+ | R_CTL | D_ID |
++-> [1] | | | Buff_Off | | | CS_CTL| S_ID |
+ |-----------\ |BuffIndex| Link | | | TYPE | F_CTL |
+ | [2] | \ | Rsvd | RX_ID | | | SEQ_ID | SEQ_CNT |
+ |----------- \ | Data_Len | | | OX_ID | RX_ID |
+ | ... | \ | Exp_RO | | | RO |
+ |----------| | Exp_Byte_Cnt | | |___________________|
+ | SEST_LEN | +--| Len | |
+ |__________| | | Address | |
+ | | ... | | for FCP_RSP
+ | |__________________| | ____________________
+ | +----| Reserved |
+ | | SOF EOF TimeStamp |
+ | | R_CTL | D_ID |
+ | | CS_CTL| S_ID |
+ +--- local or extended | .... |
+ scatter/gather lists
+ defining upper-layer
+ data (e.g. from user's App)
+
+
+*/
+// All TachLite commands must start with a SFS (Single Frame Sequence)
+// command. In the simplest case (a NOP Basic Link command),
+// only one frame header and ERQ entry is required. The most complex
+// case is the SCSI assisted command, which requires an ERQ entry,
+// SEST entry, and several frame headers and data buffers all
+// logically linked together.
+// Inputs:
+// cpqfcHBAdata - controller struct
+// type - PLOGI, SCSI_IWE, etc.
+// InFCHS - Incoming Tachlite FCHS which prompted this exchange
+// (only s_id set if we are originating)
+// Data - PVOID to data struct consistent with "type"
+// fcExchangeIndex - pointer to OX/RD ID value of built exchange
+// Return:
+// fcExchangeIndex - OX/RD ID value if successful
+// 0 - success
+// INVALID_ARGS - NULL/ invalid passed args
+// BAD_ALPA - Bad source al_pa address
+// LNKDWN_OSLS - Link Down (according to this controller)
+// OUTQUE_FULL - Outbound Que full
+// DRIVERQ_FULL - controller's Exchange array full
+// SEST_FULL - SEST table full
+//
+// Remarks:
+// Psuedo code:
+// Check for NULL pointers / bad args
+// Build outgoing FCHS - the header/payload struct
+// Build IRB (for ERQ entry)
+// if SCSI command, build SEST entry (e.g. IWE, TRE,...)
+// return success
+
+//sbuildex
+ULONG cpqfcTSBuildExchange(
+ CPQFCHBA *cpqfcHBAdata,
+ ULONG type, // e.g. PLOGI
+ TachFCHDR_GCMND* InFCHS, // incoming FCHS
+ void *Data, // the CDB, scatter/gather, etc.
+ LONG *fcExchangeIndex ) // points to allocated exchange,
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG ulStatus = 0; // assume OK
+ USHORT ox_ID, rx_ID=0xFFFF;
+ ULONG SfsLen=0L;
+ TachLiteIRB* pIRB;
+ IRBflags IRB_flags;
+ UCHAR *pIRB_flags = (UCHAR*)&IRB_flags;
+ TachFCHDR_GCMND* CMDfchs;
+ TachFCHDR* dataHDR; // 32 byte HEADER ONLY FCP-DATA buffer
+ TachFCHDR_RSP* rspHDR; // 32 byte header + RSP payload
+ Scsi_Cmnd *Cmnd = (Scsi_Cmnd*)Data; // Linux Scsi CDB, S/G, ...
+ TachLiteIWE* pIWE;
+ TachLiteIRE* pIRE;
+ TachLiteTWE* pTWE;
+ TachLiteTRE* pTRE;
+ ULONG fcp_dl; // total byte length of DATA transfered
+ ULONG fl; // frame length (FC frame size, 128, 256, 512, 1024)
+ ULONG sgPairs; // number of valid scatter/gather pairs
+ int FCP_SCSI_command;
+ BA_ACC_PAYLOAD *ba_acc;
+ BA_RJT_PAYLOAD *ba_rjt;
+
+ // check passed ARGS
+ if( !fcChip->ERQ ) // NULL ptr means uninitialized Tachlite chip
+ return INVALID_ARGS;
+
+
+ if( type == SCSI_IRE ||
+ type == SCSI_TRE ||
+ type == SCSI_IWE ||
+ type == SCSI_TWE)
+ FCP_SCSI_command = 1;
+
+ else
+ FCP_SCSI_command = 0;
+
+
+ // for commands that pass payload data (e.g. SCSI write)
+ // examine command struct - verify that the
+ // length of s/g buffers is adequate for total payload
+ // length (end of list is NULL address)
+
+ if( FCP_SCSI_command )
+ {
+ if( Data ) // must have data descriptor (S/G list -- at least
+ // one address with at least 1 byte of data)
+ {
+ // something to do (later)?
+ }
+
+ else
+ return INVALID_ARGS; // invalid DATA ptr
+ }
+
+
+
+ // we can build an Exchange for later Queuing (on the TL chip)
+ // if an empty slot is available in the DevExt for this controller
+ // look for available Exchange slot...
+
+ if( type != FCP_RESPONSE &&
+ type != BLS_ABTS &&
+ type != BLS_ABTS_ACC ) // already have Exchange slot!
+ *fcExchangeIndex = FindFreeExchange( fcChip, type );
+
+ if( *fcExchangeIndex != -1 ) // Exchange is available?
+ {
+ // assign tmp ptr (shorthand)
+ CMDfchs = &Exchanges->fcExchange[ *fcExchangeIndex].fchs;
+
+
+ if( Cmnd != NULL ) // (necessary for ABTS cases)
+ {
+ Exchanges->fcExchange[ *fcExchangeIndex].Cmnd = Cmnd; // Linux Scsi
+ Exchanges->fcExchange[ *fcExchangeIndex].pLoggedInPort =
+ fcFindLoggedInPort( fcChip,
+ Exchanges->fcExchange[ *fcExchangeIndex].Cmnd, // find Scsi Nexus
+ 0, // DON'T search linked list for FC port id
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ }
+
+
+ // Build the command frame header (& data) according
+ // to command type
+
+ // fields common for all SFS frame types
+ CMDfchs->reserved = 0L; // must clear
+ CMDfchs->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; LCr=0, no TS
+
+ // get the destination port_id from incoming FCHS
+ // (initialized before calling if we're Originator)
+ // Frame goes to port it was from - the source_id
+
+ CMDfchs->d_id = InFCHS->s_id &0xFFFFFF; // destination (add R_CTL later)
+ CMDfchs->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+
+
+ // now enter command-specific fields
+ switch( type )
+ {
+
+ case BLS_NOP: // FC defined basic link service command NO-OP
+ // ensure unique X_IDs! (use tracking function)
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += 32L; // add len to LSB (header only - no payload)
+
+ // TYPE[31-24] 00 Basic Link Service
+ // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+ CMDfchs->d_id |= 0x80000000L; // R_CTL = 80 for NOP (Basic Link Ser.)
+ CMDfchs->f_ctl = 0x00310000L; // xchng originator, 1st seq,....
+ CMDfchs->seq_cnt = 0x0L;
+ CMDfchs->ox_rx_id = 0xFFFF; // RX_ID for now; OX_ID on start
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+ CMDfchs->pl[0] = 0xaabbccddL; // words 8-15 frame data payload (n/a)
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 1; // seconds
+ // (NOP should complete ~instantly)
+ break;
+
+
+
+
+ case BLS_ABTS_ACC: // Abort Sequence ACCept
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += 32 + 12; // add len to LSB (header + 3 DWORD payload)
+
+ CMDfchs->d_id |= 0x84000000L; // R_CTL = 84 for BASIC ACCept
+ // TYPE[31-24] 00 Basic Link Service
+ // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I.
+ CMDfchs->f_ctl = 0x00910000L; // xchnge responder, last seq, xfer SI
+ // CMDfchs->seq_id & count might be set from DataHdr?
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 5; // seconds
+ // (Timeout in case of weird error)
+
+ // now set the ACCept payload...
+ ba_acc = (BA_ACC_PAYLOAD*)&CMDfchs->pl[0];
+ memset( ba_acc, 0, sizeof( BA_ACC_PAYLOAD));
+ // Since PLDA requires (only) entire Exchange aborts, we don't need
+ // to worry about what the last sequence was.
+
+ // We expect that a "target" task is accepting the abort, so we
+ // can use the OX/RX ID pair
+ ba_acc->ox_rx_id = CMDfchs->ox_rx_id;
+
+ // source, dest, #bytes
+ BigEndianSwap((UCHAR *)&CMDfchs->ox_rx_id, (UCHAR *)&ba_acc->ox_rx_id, 4);
+
+ ba_acc->low_seq_cnt = 0;
+ ba_acc->high_seq_cnt = 0xFFFF;
+
+
+ break;
+
+
+ case BLS_ABTS_RJT: // Abort Sequence ACCept
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += 32 + 12; // add len to LSB (header + 3 DWORD payload)
+
+ CMDfchs->d_id |= 0x85000000L; // R_CTL = 85 for BASIC ReJecT
+ // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I.
+ // TYPE[31-24] 00 Basic Link Service
+ CMDfchs->f_ctl = 0x00910000L; // xchnge responder, last seq, xfer SI
+ // CMDfchs->seq_id & count might be set from DataHdr?
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 5; // seconds
+ // (Timeout in case of weird error)
+
+ CMDfchs->ox_rx_id = InFCHS->ox_rx_id; // copy from sender!
+
+ // now set the ReJecT payload...
+ ba_rjt = (BA_RJT_PAYLOAD*)&CMDfchs->pl[0];
+ memset( ba_rjt, 0, sizeof( BA_RJT_PAYLOAD));
+
+ // We expect that a "target" task couldn't find the Exhange in the
+ // array of active exchanges, so we use a new LinkService X_ID.
+ // See Reject payload description in FC-PH (Rev 4.3), pg. 140
+ ba_rjt->reason_code = 0x09; // "unable to perform command request"
+ ba_rjt->reason_explain = 0x03; // invalid OX/RX ID pair
+
+
+ break;
+
+
+
+ case BLS_ABTS: // FC defined basic link service command ABTS
+ // Abort Sequence
+
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += 32L; // add len to LSB (header only - no payload)
+
+ // TYPE[31-24] 00 Basic Link Service
+ // f_ctl[23:0] exchg originator, not 1st seq, xfer S.I.
+ CMDfchs->d_id |= 0x81000000L; // R_CTL = 81 for ABTS
+ CMDfchs->f_ctl = 0x00110000L; // xchnge originator, last seq, xfer SI
+ // CMDfchs->seq_id & count might be set from DataHdr?
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds
+ // (ABTS must timeout when responder is gone)
+ break;
+
+
+
+ case FCS_NSR: // Fabric Name Service Request
+ Exchanges->fcExchange[ *fcExchangeIndex].reTries = 2;
+
+
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds
+ // OX_ID, linked to Driver Transaction ID
+ // (fix-up at Queing time)
+ CMDfchs->ox_rx_id = 0xFFFF; // RX_ID - Responder (target) to modify
+ // OX_ID set at ERQueing time
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += (32L + sizeof(NSR_PL)); // add len (header & NSR payload)
+
+ CMDfchs->d_id |= 0x02000000L; // R_CTL = 02 for -
+ // Name Service Request: Unsolicited
+ // TYPE[31-24] 01 Extended Link Service
+ // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+ CMDfchs->f_ctl = 0x20210000L;
+ // OX_ID will be fixed-up at Tachyon enqueing time
+ CMDfchs->seq_cnt = 0; // seq ID, DF_ctl, seq cnt
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+
+ BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]);
+
+
+
+
+
+
+ break;
+
+
+
+
+ case ELS_PLOGI: // FC-PH extended link service command Port Login
+ // (May, 2000)
+ // NOTE! This special case facilitates SANMark testing. The SANMark
+ // test script for initialization-timeout.fcal.SANMark-1.fc
+ // "eats" the OPN() primitive without issuing an R_RDY, causing
+ // Tachyon to report LST (loop state timeout), which causes a
+ // LIP. To avoid this, simply send out the frame (i.e. assuming a
+ // buffer credit of 1) without waiting for R_RDY. Many FC devices
+ // (other than Tachyon) have been doing this for years. We don't
+ // ever want to do this for non-Link Service frames unless the
+ // other device really did report non-zero login BB credit (i.e.
+ // in the PLOGI ACCept frame).
+// CMDfchs->sof_eof |= 0x00000400L; // LCr=1
+
+ case ELS_FDISC: // Fabric Discovery (Login)
+ case ELS_FLOGI: // Fabric Login
+ case ELS_SCR: // Fabric State Change Registration
+ case ELS_LOGO: // FC-PH extended link service command Port Logout
+ case ELS_PDISC: // FC-PH extended link service cmnd Port Discovery
+ case ELS_PRLI: // FC-PH extended link service cmnd Process Login
+
+ Exchanges->fcExchange[ *fcExchangeIndex].reTries = 2;
+
+
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 2; // seconds
+ // OX_ID, linked to Driver Transaction ID
+ // (fix-up at Queing time)
+ CMDfchs->ox_rx_id = 0xFFFF; // RX_ID - Responder (target) to modify
+ // OX_ID set at ERQueing time
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ if( type == ELS_LOGO )
+ SfsLen += (32L + 16L); // add len (header & PLOGI payload)
+ else if( type == ELS_PRLI )
+ SfsLen += (32L + 20L); // add len (header & PRLI payload)
+ else if( type == ELS_SCR )
+ SfsLen += (32L + sizeof(SCR_PL)); // add len (header & SCR payload)
+ else
+ SfsLen += (32L + 116L); // add len (header & PLOGI payload)
+
+ CMDfchs->d_id |= 0x22000000L; // R_CTL = 22 for -
+ // Extended Link_Data: Unsolicited Control
+ // TYPE[31-24] 01 Extended Link Service
+ // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+ CMDfchs->f_ctl = 0x01210000L;
+ // OX_ID will be fixed-up at Tachyon enqueing time
+ CMDfchs->seq_cnt = 0; // seq ID, DF_ctl, seq cnt
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+
+ BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]);
+
+ break;
+
+
+
+ case ELS_LOGO_ACC: // FC-PH extended link service logout accept
+ case ELS_RJT: // extended link service reject (add reason)
+ case ELS_ACC: // ext. link service generic accept
+ case ELS_PLOGI_ACC:// ext. link service login accept (PLOGI or PDISC)
+ case ELS_PRLI_ACC: // ext. link service process login accept
+
+
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 1; // assume done
+ // ensure unique X_IDs! (use tracking function)
+ // OX_ID from initiator cmd
+ ox_ID = (USHORT)(InFCHS->ox_rx_id >> 16);
+ rx_ID = 0xFFFF; // RX_ID, linked to Driver Exchange ID
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (not SEST index)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ if( type == ELS_RJT )
+ {
+ SfsLen += (32L + 8L); // add len (header + payload)
+
+ // ELS_RJT reason codes (utilize unused "reserved" field)
+ CMDfchs->pl[0] = 1;
+ CMDfchs->pl[1] = InFCHS->reserved;
+
+ }
+ else if( (type == ELS_LOGO_ACC) || (type == ELS_ACC) )
+ SfsLen += (32L + 4L); // add len (header + payload)
+ else if( type == ELS_PLOGI_ACC )
+ SfsLen += (32L + 116L); // add len (header + payload)
+ else if( type == ELS_PRLI_ACC )
+ SfsLen += (32L + 20L); // add len (header + payload)
+
+ CMDfchs->d_id |= 0x23000000L; // R_CTL = 23 for -
+ // Extended Link_Data: Control Reply
+ // TYPE[31-24] 01 Extended Link Service
+ // f_ctl[23:0] exchg responder, last seq, e_s, tsi
+ CMDfchs->f_ctl = 0x01990000L;
+ CMDfchs->seq_cnt = 0x0L;
+ CMDfchs->ox_rx_id = 0L; // clear
+ CMDfchs->ox_rx_id = ox_ID; // load upper 16 bits
+ CMDfchs->ox_rx_id <<= 16; // shift them
+
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+
+ BuildLinkServicePayload( fcChip, type, &CMDfchs->pl[0]);
+
+ break;
+
+
+ // Fibre Channel SCSI 'originator' sequences...
+ // (originator means 'initiator' in FCP-SCSI)
+ case SCSI_IWE: // TachLite Initiator Write Entry
+ {
+ PFC_LOGGEDIN_PORT pLoggedInPort =
+ Exchanges->fcExchange[ *fcExchangeIndex].pLoggedInPort;
+
+ Exchanges->fcExchange[ *fcExchangeIndex].reTries = 1;
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 7; // FC2 timeout
+
+ // first, build FCP_CMND
+ // unique X_ID fix-ups in StartExchange
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS FCP-CMND (not SEST index)
+
+ // NOTE: unlike FC LinkService login frames, normal
+ // SCSI commands are sent without outgoing verification
+ IRB_flags.DCM = 1; // Disable completion message for Cmnd frame
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += 64L; // add len to LSB (header & CMND payload)
+
+ CMDfchs->d_id |= (0x06000000L); // R_CTL = 6 for command
+
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+ // valid RO
+ CMDfchs->f_ctl = 0x08210008L;
+ CMDfchs->seq_cnt = 0x0L;
+ CMDfchs->ox_rx_id = 0L; // clear for now (-or- in later)
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+
+ // now, fill out FCP-DATA header
+ // (use buffer inside SEST object)
+ dataHDR = &fcChip->SEST->DataHDR[ *fcExchangeIndex ];
+ dataHDR->reserved = 0L; // must clear
+ dataHDR->sof_eof = 0x75002000L; // SOFi3:EOFn no UAM; no CLS, noLCr, no TS
+ dataHDR->d_id = (InFCHS->s_id | 0x01000000L); // R_CTL= FCP_DATA
+ dataHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] xfer S.I.| valid RO
+ dataHDR->f_ctl = 0x08010008L;
+ dataHDR->seq_cnt = 0x02000000L; // sequence ID: df_ctl : seqence count
+ dataHDR->ox_rx_id = 0L; // clear; fix-up dataHDR fields later
+ dataHDR->ro = 0x0L; // relative offset (n/a)
+
+ // Now setup the SEST entry
+ pIWE = &fcChip->SEST->u[ *fcExchangeIndex ].IWE;
+
+ // fill out the IWE:
+
+ // VALid entry:Dir outbound:DCM:enable CM:enal INT: FC frame len
+ pIWE->Hdr_Len = 0x8e000020L; // data frame Len always 32 bytes
+
+
+ // from login parameters with other port, what's the largest frame
+ // we can send?
+ if( pLoggedInPort == NULL)
+ {
+ ulStatus = INVALID_ARGS; // failed! give up
+ break;
+ }
+ if( pLoggedInPort->rx_data_size >= 2048)
+ fl = 0x00020000; // 2048 code (only support 1024!)
+ else if( pLoggedInPort->rx_data_size >= 1024)
+ fl = 0x00020000; // 1024 code
+ else if( pLoggedInPort->rx_data_size >= 512)
+ fl = 0x00010000; // 512 code
+ else
+ fl = 0; // 128 bytes -- should never happen
+
+
+ pIWE->Hdr_Len |= fl; // add xmit FC frame len for data phase
+ pIWE->Hdr_Addr = virt_to_bus( dataHDR );
+ pIWE->RSP_Len = sizeof(TachFCHDR_RSP) ; // hdr+data (recv'd RSP frame)
+ pIWE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+
+ memset( &fcChip->SEST->RspHDR[ *fcExchangeIndex].pl, 0,
+ sizeof( FCP_STATUS_RESPONSE) ); // clear out previous status
+
+ pIWE->RSP_Addr = virt_to_bus(
+ &fcChip->SEST->RspHDR[ *fcExchangeIndex ]);
+
+ // Do we need local or extended gather list?
+ // depends on size - we can handle 3 len/addr pairs
+ // locally.
+
+ fcp_dl = build_SEST_sgList(
+ &pIWE->GLen1,
+ Cmnd, // S/G list
+ &sgPairs, // return # of pairs in S/G list (from "Data" descriptor)
+ &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+
+ if( !fcp_dl ) // error building S/G list?
+ {
+ ulStatus = MEMPOOL_FAIL;
+ break; // give up
+ }
+
+ // Now that we know total data length in
+ // the passed S/G buffer, set FCP CMND frame
+ build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl );
+
+
+
+ if( sgPairs > 3 ) // need extended s/g list
+ pIWE->Buff_Off = 0x78000000L; // extended data | (no offset)
+ else // local data pointers (in SEST)
+ pIWE->Buff_Off = 0xf8000000L; // local data | (no offset)
+
+ // ULONG 5
+ pIWE->Link = 0x0000ffffL; // Buff_Index | Link
+
+ pIWE->RX_ID = 0x0L; // DWord 6: RX_ID set by target XFER_RDY
+
+ // DWord 7
+ pIWE->Data_Len = 0L; // TL enters rcv'd XFER_RDY BURST_LEN
+ pIWE->Exp_RO = 0L; // DWord 8
+ // DWord 9
+ pIWE->Exp_Byte_Cnt = fcp_dl; // sum of gather buffers
+ }
+ break;
+
+
+
+
+
+ case SCSI_IRE: // TachLite Initiator Read Entry
+
+ if( Cmnd->timeout != 0)
+ {
+// printk("Cmnd->timeout %d\n", Cmnd->timeout);
+ // per Linux Scsi
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = Cmnd->timeout;
+ }
+ else // use our best guess, based on FC & device
+ {
+
+ if( Cmnd->SCp.Message == 1 ) // Tape device? (from INQUIRY)
+ {
+ // turn off our timeouts (for now...)
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 0xFFFFFFFF;
+ }
+ else
+ {
+ Exchanges->fcExchange[ *fcExchangeIndex].reTries = 1;
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 7; // per SCSI req.
+ }
+ }
+
+
+ // first, build FCP_CMND
+
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS FCP-CMND (not SEST index)
+ // NOTE: unlike FC LinkService login frames,
+ // normal SCSI commands are sent "open loop"
+ IRB_flags.DCM = 1; // Disable completion message for Cmnd frame
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += 64L; // add len to LSB (header & CMND payload)
+
+ CMDfchs->d_id |= (0x06000000L); // R_CTL = 6 for command
+
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] exchg originator, 1st seq, xfer S.I.
+ // valid RO
+ CMDfchs->f_ctl = 0x08210008L;
+ CMDfchs->seq_cnt = 0x0L;
+ // x_ID & data direction bit set later
+ CMDfchs->ox_rx_id = 0xFFFF; // clear
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+
+
+
+ // Now setup the SEST entry
+ pIRE = &fcChip->SEST->u[ *fcExchangeIndex ].IRE;
+
+ // fill out the IRE:
+ // VALid entry:Dir outbound:enable CM:enal INT:
+ pIRE->Seq_Accum = 0xCE000000L; // VAL,DIR inbound,DCM| INI,DAT,RSP
+
+ pIRE->reserved = 0L;
+ pIRE->RSP_Len = sizeof(TachFCHDR_RSP) ; // hdr+data (recv'd RSP frame)
+ pIRE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+
+
+ pIRE->RSP_Addr = virt_to_bus(
+ &fcChip->SEST->RspHDR[ *fcExchangeIndex ]);
+
+
+ // Do we need local or extended gather list?
+ // depends on size - we can handle 3 len/addr pairs
+ // locally.
+
+ fcp_dl = build_SEST_sgList(
+ &pIRE->SLen1,
+ Cmnd, // SCSI command Data desc. with S/G list
+ &sgPairs, // return # of pairs in S/G list (from "Data" descriptor)
+ &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+
+
+ if( !fcp_dl ) // error building S/G list?
+ {
+ // It is permissible to have a ZERO LENGTH Read command.
+ // If there is the case, simply set fcp_dl (and Exp_Byte_Cnt)
+ // to 0 and continue.
+ if( Cmnd->request_bufflen == 0 )
+ {
+ fcp_dl = 0; // no FC DATA frames expected
+
+ }
+ else
+ {
+ ulStatus = MEMPOOL_FAIL;
+ break; // give up
+ }
+ }
+
+ // now that we know the S/G length, build CMND payload
+ build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl );
+
+
+ if( sgPairs > 3 ) // need extended s/g list
+ pIRE->Buff_Off = 0x00000000; // DWord 4: extended s/g list, no offset
+ else
+ pIRE->Buff_Off = 0x80000000; // local data, no offset
+
+ pIRE->Buff_Index = 0x0L; // DWord 5: Buff_Index | Reserved
+
+ pIRE->Exp_RO = 0x0L; // DWord 6: Expected Rel. Offset
+
+ pIRE->Byte_Count = 0; // DWord 7: filled in by TL on err
+ pIRE->reserved_ = 0; // DWord 8: reserved
+ // NOTE: 0 length READ is OK.
+ pIRE->Exp_Byte_Cnt = fcp_dl;// DWord 9: sum of scatter buffers
+
+ break;
+
+
+
+
+ // Fibre Channel SCSI 'responder' sequences...
+ // (originator means 'target' in FCP-SCSI)
+ case SCSI_TWE: // TachLite Target Write Entry
+
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 10; // per SCSI req.
+
+ // first, build FCP_CMND
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (XFER_RDY)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += (32L + 12L);// add SFS len (header & XFER_RDY payload)
+
+ CMDfchs->d_id |= (0x05000000L); // R_CTL = 5 for XFER_RDY
+
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] exchg responder, 1st seq, xfer S.I.
+ // valid RO
+ CMDfchs->f_ctl = 0x08810008L;
+ CMDfchs->seq_cnt = 0x01000000; // sequence ID: df_ctl: sequence count
+ // use originator (other port's) OX_ID
+ CMDfchs->ox_rx_id = InFCHS->ox_rx_id; // we want upper 16 bits
+ CMDfchs->ro = 0x0L; // relative offset (n/a)
+
+ // now, fill out FCP-RSP header
+ // (use buffer inside SEST object)
+
+ rspHDR = &fcChip->SEST->RspHDR[ *fcExchangeIndex ];
+ rspHDR->reserved = 0L; // must clear
+ rspHDR->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; no CLS, noLCr, no TS
+ rspHDR->d_id = (InFCHS->s_id | 0x07000000L); // R_CTL= FCP_RSP
+ rspHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] responder|last seq| xfer S.I.
+ rspHDR->f_ctl = 0x08910000L;
+ rspHDR->seq_cnt = 0x03000000; // sequence ID
+ rspHDR->ox_rx_id = InFCHS->ox_rx_id; // gives us OX_ID
+ rspHDR->ro = 0x0L; // relative offset (n/a)
+
+
+ // Now setup the SEST entry
+
+ pTWE = &fcChip->SEST->u[ *fcExchangeIndex ].TWE;
+
+ // fill out the TWE:
+
+ // VALid entry:Dir outbound:enable CM:enal INT:
+ pTWE->Seq_Accum = 0xC4000000L; // upper word flags
+ pTWE->reserved = 0L;
+ pTWE->Remote_Node_ID = 0L; // no more auto RSP frame! (TL/TS change)
+ pTWE->Remote_Node_ID |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+
+
+ // Do we need local or extended gather list?
+ // depends on size - we can handle 3 len/addr pairs
+ // locally.
+
+ fcp_dl = build_SEST_sgList(
+ &pTWE->SLen1,
+ Cmnd, // S/G list
+ &sgPairs, // return # of pairs in S/G list (from "Data" descriptor)
+ &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+
+
+ if( !fcp_dl ) // error building S/G list?
+ {
+ ulStatus = MEMPOOL_FAIL;
+ break; // give up
+ }
+
+ // now that we know the S/G length, build CMND payload
+ build_FCP_payload( Cmnd, (UCHAR*)&CMDfchs->pl[0], type, fcp_dl );
+
+
+ if( sgPairs > 3 ) // need extended s/g list
+ pTWE->Buff_Off = 0x00000000; // extended s/g list, no offset
+ else
+ pTWE->Buff_Off = 0x80000000; // local data, no offset
+
+ pTWE->Buff_Index = 0; // Buff_Index | Link
+ pTWE->Exp_RO = 0;
+ pTWE->Byte_Count = 0; // filled in by TL on err
+ pTWE->reserved_ = 0;
+ pTWE->Exp_Byte_Cnt = fcp_dl;// sum of scatter buffers
+
+ break;
+
+
+
+
+
+
+ case SCSI_TRE: // TachLite Target Read Entry
+
+ // It doesn't make much sense for us to "time-out" a READ,
+ // but we'll use it for design consistency and internal error recovery.
+ Exchanges->fcExchange[ *fcExchangeIndex].timeOut = 10; // per SCSI req.
+
+ // I/O request block settings...
+ *pIRB_flags = 0; // clear IRB flags
+ // check PRLI (process login) info
+ // to see if Initiator Requires XFER_RDY
+ // if not, don't send one!
+ // { PRLI check...}
+ IRB_flags.SFA = 0; // don't send XFER_RDY - start data
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += (32L + 12L);// add SFS len (header & XFER_RDY payload)
+
+
+
+ // now, fill out FCP-DATA header
+ // (use buffer inside SEST object)
+ dataHDR = &fcChip->SEST->DataHDR[ *fcExchangeIndex ];
+
+ dataHDR->reserved = 0L; // must clear
+ dataHDR->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; no CLS,noLCr,no TS
+ dataHDR->d_id = (InFCHS->s_id | 0x01000000L); // R_CTL= FCP_DATA
+ dataHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+
+
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] exchg responder, not 1st seq, xfer S.I.
+ // valid RO
+ dataHDR->f_ctl = 0x08810008L;
+ dataHDR->seq_cnt = 0x01000000; // sequence ID (no XRDY)
+ dataHDR->ox_rx_id = InFCHS->ox_rx_id & 0xFFFF0000; // we want upper 16 bits
+ dataHDR->ro = 0x0L; // relative offset (n/a)
+
+ // now, fill out FCP-RSP header
+ // (use buffer inside SEST object)
+ rspHDR = &fcChip->SEST->RspHDR[ *fcExchangeIndex ];
+
+ rspHDR->reserved = 0L; // must clear
+ rspHDR->sof_eof = 0x75000000L; // SOFi3:EOFn no UAM; no CLS, noLCr, no TS
+ rspHDR->d_id = (InFCHS->s_id | 0x07000000L); // R_CTL= FCP_RSP
+ rspHDR->s_id = fcChip->Registers.my_al_pa; // CS_CTL = 0
+ // TYPE[31-24] 8 for FCP SCSI
+ // f_ctl[23:0] responder|last seq| xfer S.I.
+ rspHDR->f_ctl = 0x08910000L;
+ rspHDR->seq_cnt = 0x02000000; // sequence ID: df_ctl: sequence count
+
+ rspHDR->ro = 0x0L; // relative offset (n/a)
+
+
+ // Now setup the SEST entry
+ pTRE = &fcChip->SEST->u[ *fcExchangeIndex ].TRE;
+
+
+ // VALid entry:Dir outbound:enable CM:enal INT:
+ pTRE->Hdr_Len = 0x86010020L; // data frame Len always 32 bytes
+ pTRE->Hdr_Addr = virt_to_bus( dataHDR );
+ pTRE->RSP_Len = 64L; // hdr+data (TL assisted RSP frame)
+ pTRE->RSP_Len |= (InFCHS->s_id << 8); // MS 24 bits Remote_ID
+ pTRE->RSP_Addr = virt_to_bus( rspHDR );
+
+
+ // Do we need local or extended gather list?
+ // depends on size - we can handle 3 len/addr pairs
+ // locally.
+
+ fcp_dl = build_SEST_sgList(
+ &pTRE->GLen1,
+ Cmnd, // S/G list
+ &sgPairs, // return # of pairs in S/G list (from "Data" descriptor)
+ &fcChip->SEST->sgPages[ *fcExchangeIndex ]);// (for Freeing later)
+
+
+ if( !fcp_dl ) // error building S/G list?
+ {
+ ulStatus = MEMPOOL_FAIL;
+ break; // give up
+ }
+
+ // no payload or command to build -- READ doesn't need XRDY
+
+
+ if( sgPairs > 3 ) // need extended s/g list
+ pTRE->Buff_Off = 0x78000000L; // extended data | (no offset)
+ else // local data pointers (in SEST)
+ pTRE->Buff_Off = 0xf8000000L; // local data | (no offset)
+
+ // ULONG 5
+ pTRE->Buff_Index = 0L; // Buff_Index | reserved
+ pTRE->reserved = 0x0L; // DWord 6
+
+ // DWord 7: NOTE: zero length will
+ // hang TachLite!
+ pTRE->Data_Len = fcp_dl; // e.g. sum of scatter buffers
+
+ pTRE->reserved_ = 0L; // DWord 8
+ pTRE->reserved__ = 0L; // DWord 9
+
+ break;
+
+
+
+
+
+
+
+ case FCP_RESPONSE:
+ // Target response frame: this sequence uses an OX/RX ID
+ // pair from a completed SEST exchange. We built most
+ // of the response frame when we created the TWE/TRE.
+
+ *pIRB_flags = 0; // clear IRB flags
+ IRB_flags.SFA = 1; // send SFS (RSP)
+ SfsLen = *pIRB_flags;
+
+ SfsLen <<= 24; // shift flags to MSB
+ SfsLen += sizeof(TachFCHDR_RSP);// add SFS len (header & RSP payload)
+
+
+ Exchanges->fcExchange[ *fcExchangeIndex].type =
+ FCP_RESPONSE; // change Exchange type to "response" phase
+
+ // take advantage of prior knowledge of OX/RX_ID pair from
+ // previous XFER outbound frame (still in fchs of exchange)
+ fcChip->SEST->RspHDR[ *fcExchangeIndex ].ox_rx_id =
+ CMDfchs->ox_rx_id;
+
+ // Check the status of the DATA phase of the exchange so we can report
+ // status to the initiator
+ buildFCPstatus( fcChip, *fcExchangeIndex); // set RSP payload fields
+
+ memcpy(
+ CMDfchs, // re-use same XFER fchs for Response frame
+ &fcChip->SEST->RspHDR[ *fcExchangeIndex ],
+ sizeof( TachFCHDR_RSP ));
+
+
+ break;
+
+ default:
+ printk("cpqfcTS: don't know how to build FC type: %Xh(%d)\n", type,type);
+ break;
+
+ }
+
+
+
+ if( !ulStatus) // no errors above?
+ {
+ // FCHS is built; now build IRB
+
+ // link the just built FCHS (the "command") to the IRB entry
+ // for this Exchange.
+ pIRB = &Exchanges->fcExchange[ *fcExchangeIndex].IRB;
+
+ // len & flags according to command type above
+ pIRB->Req_A_SFS_Len = SfsLen; // includes IRB flags & len
+ pIRB->Req_A_SFS_Addr = virt_to_bus(CMDfchs); // TL needs physical addr
+ // of frame to send
+ pIRB->Req_A_SFS_D_ID = CMDfchs->d_id << 8; // Dest_ID must be consistent!
+
+ // Exchange is complete except for "fix-up" fields to be set
+ // at Tachyon Queuing time:
+ // IRB->Req_A_Trans_ID (OX_ID/ RX_ID):
+ // for SEST entry, lower bits correspond to actual FC Exchange ID
+ // fchs->OX_ID or RX_ID
+ }
+ else
+ {
+#ifdef DBG
+ printk( "FC Error: SEST build Pool Allocation failed\n");
+#endif
+ // return resources...
+ cpqfcTSCompleteExchange( fcChip, *fcExchangeIndex); // SEST build failed
+ }
+ }
+ else // no Exchanges available
+ {
+ ulStatus = SEST_FULL;
+ printk( "FC Error: no fcExchanges available\n");
+ }
+ return ulStatus;
+}
+
+
+
+
+
+
+// set RSP payload fields
+static void buildFCPstatus( PTACHYON fcChip, ULONG ExchangeID)
+{
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ FC_EXCHANGE *pExchange = &Exchanges->fcExchange[ExchangeID]; // shorthand
+ PFCP_STATUS_RESPONSE pFcpStatus;
+
+ memset( &fcChip->SEST->RspHDR[ ExchangeID ].pl, 0,
+ sizeof( FCP_STATUS_RESPONSE) );
+ if( pExchange->status ) // something wrong?
+ {
+ pFcpStatus = (PFCP_STATUS_RESPONSE) // cast RSP buffer for this xchng
+ &fcChip->SEST->RspHDR[ ExchangeID ].pl;
+ if( pExchange->status & COUNT_ERROR )
+ {
+
+ // set FCP response len valid (so we can report count error)
+ pFcpStatus->fcp_status |= FCP_RSP_LEN_VALID;
+ pFcpStatus->fcp_rsp_len = 0x04000000; // 4 byte len (BIG Endian)
+
+ pFcpStatus->fcp_rsp_info = FCP_DATA_LEN_NOT_BURST_LEN; // RSP_CODE
+ }
+ }
+}
+
+
+
+
+// This routine builds scatter/gather lists into SEST entries
+// INPUTS:
+// SESTalPair - SEST address @DWordA "Local Buffer Length"
+// sgList - Scatter/Gather linked list of Len/Address data buffers
+// OUTPUT:
+// sgPairs - number of valid address/length pairs
+// Remarks:
+// The SEST data buffer pointers only depend on number of
+// length/ address pairs, NOT on the type (IWE, TRE,...)
+// Up to 3 pairs can be referenced in the SEST - more than 3
+// require this Extended S/G list page. The page holds 4, 8, 16...
+// len/addr pairs, per Scatter/Gather List Page Length Reg.
+// TachLite allows pages to be linked to any depth.
+
+//#define DBG_SEST_SGLIST 1 // for printing out S/G pairs with Ext. pages
+
+static ULONG build_SEST_sgList(
+ ULONG *SESTalPairStart, // the 3 len/address buffers in SEST
+ Scsi_Cmnd *Cmnd,
+ ULONG *sgPairs,
+ PSGPAGES sgPages) // link list of TL Ext. S/G pages from O/S Pool
+
+{
+ ULONG i, AllocatedPages=0; // Tach Ext. S/G page allocations
+ ULONG* alPair = SESTalPairStart;
+ ULONG alignedPageAddress; // TL hardware alignment requirement
+ int PairCount;
+ unsigned long ulBuff;
+ ULONG total_data_len=0; // (in bytes)
+ ULONG bytes_to_go = Cmnd->request_bufflen; // total xfer (S/G sum)
+ ULONG thisMappingLen;
+ struct scatterlist *sgl; // S/G list (Linux format)
+
+
+
+ if( !Cmnd->use_sg ) // no S/G list?
+ {
+ *sgPairs = 1; // use "local" S/G pair in SEST entry
+ // (for now, ignore address bits above #31)
+ *alPair++ = bytes_to_go & 0x7ffff; // bits 18-0, length
+ ulBuff = virt_to_bus( Cmnd->request_buffer);
+#if BITS_PER_LONG > 32
+ if( ulBuff >>32 )
+ {
+ printk("FATAL! Tachyon DMA address %p exceeds 32 bits\n", (void*)ulBuff );
+ return 0;
+ }
+#endif
+ *alPair = (ULONG)ulBuff;
+ return bytes_to_go;
+ }
+
+
+ // [TBD - update for Linux to support > 32 bits addressing]
+ // since the format for local & extended S/G lists is different,
+ // check if S/G pairs exceeds 3.
+ *sgPairs = Cmnd->use_sg;
+ sgl = (struct scatterlist*)Cmnd->request_buffer;
+
+ if( *sgPairs <= 3 ) // need "local" SEST list
+ {
+ while( bytes_to_go)
+ {
+ thisMappingLen = sgl->length; // we want them ALL on every pass
+ bytes_to_go = bytes_to_go - thisMappingLen;
+
+ // we have L/A pair; L = thisMappingLen, A = physicalAddress
+ // load into SEST...
+ total_data_len += thisMappingLen & 0x7ffff; // mask in valid bits
+ // per SEST format
+ *alPair = thisMappingLen & 0x7ffff; // bits 18-0, length
+// physicalAddress.HighPart <= 19; // shift to bit 19
+
+ // pick up bits 44-32 of upper 64-bit address
+ // and load into 31-19 LBAU (upper addr) of SEST entry
+// *alPair++ |=(ULONG)((physicalAddress.HighPart & 0xFFF8));
+ // on Tachlite TS's local S/G, we can handle 13 extra address bits
+ // i.e., bits 31-19 are actually bits 44-32 of physicalAddress
+
+ alPair++;
+
+ ulBuff = virt_to_bus( sgl->address);
+#if BITS_PER_LONG > 32
+ if( ulBuff >>32 )
+ {
+ printk("cqpfcTS: Tach DMA address %p > 32 bits\n", (void*)ulBuff );
+ return 0;
+ }
+#endif
+ *alPair++ = (ULONG)ulBuff; // lower 32 bits (31-0)
+
+ ++sgl; // next S/G pair
+#ifdef DBG_SEST_SGLIST
+ printk(" thisLen %d ", thisMappingLen);
+ printk(" remain %d\n", bytes_to_go);
+#endif
+
+ }
+ }
+
+
+
+
+ else // more than 3 pairs requires Extended S/G page (Pool Allocation)
+ {
+ // clear out SEST DWORDs (local S/G addr) C-F (A-B set in following logic)
+
+
+
+ for( i=2; i<6; i++)
+ alPair[i] = 0;
+
+ PairCount = TL_EXT_SG_PAGE_COUNT; // forces initial page allocation
+
+ while( bytes_to_go )
+ {
+
+
+ // Per SEST format, we can support 524287 byte lenghts per
+ // S/G pair. Typical user buffers are 4k, and very rarely
+ // exceed 12k due to fragmentation of physical memory pages.
+ // However, on certain O/S system (not "user") buffers (on platforms
+ // with huge memories like 256Meg), it's possible to exceed this
+ // length in a single S/G address/len mapping.
+ //
+ // Check for Tachyon length boundary
+ //
+ if( sgl->length > 0x7ffff )
+ {
+ // never ask for more than we can handle
+ thisMappingLen = sgl->length & 0x7ffff;
+ }
+ else
+ thisMappingLen = sgl->length;
+
+
+
+ // should we load into "this" extended S/G page, or allocate
+ // new page?
+
+ if( PairCount >= TL_EXT_SG_PAGE_COUNT )
+ {
+ // have we exceeded the max possible extended pages?
+ if( AllocatedPages >= TL_MAX_SGPAGES)
+ {
+ printk("Error: aborted loop on %d Ext. S/G page allocations\n",
+ AllocatedPages);
+
+ total_data_len = 0; // failure!! Ext. S/G is All-or-none affair
+ break; // failed
+ }
+
+ // Allocate the TL Extended S/G list page from O/S pool. We have
+ // to allocated twice what we want to ensure required TL alignment
+ // (Tachlite TL/TS User Man. Rev 6.0, p 168)
+ // We store the original allocated PVOID so we can free later
+
+ sgPages->PoolPage[ AllocatedPages] =
+ kmalloc( TL_EXT_SG_PAGE_BYTELEN*2,GFP_ATOMIC); // double for alignment
+
+
+ if( !sgPages->PoolPage[ AllocatedPages] ) // Allocation failed?
+ {
+
+ printk("Error: Allocation failed @ %d S/G page allocations\n",
+ AllocatedPages);
+
+ total_data_len = 0; // failure!! Ext. S/G is All-or-none affair
+ break; // give up
+ }
+ // clear out memory we just allocated
+ memset( sgPages->PoolPage[AllocatedPages], 0,
+ TL_EXT_SG_PAGE_BYTELEN*2);
+
+
+ // align the memory - TL requires sizeof() Ext. S/G page alignment.
+ // We doubled the actual required size so we could mask off LSBs
+ // to get desired offset
+
+ ulBuff = virt_to_bus( sgPages->PoolPage[AllocatedPages]);
+
+#if BITS_PER_LONG > 32
+ if( ulBuff >>32 )
+ {
+ printk("cqpfcTS: Tach ext. S/G DMA address %p > 32 bits\n",
+ (void*)ulBuff );
+ return 0;
+ }
+#endif
+
+ ulBuff += TL_EXT_SG_PAGE_BYTELEN; // ensures we pass align. boundary
+ ulBuff &= (0xFFFFFFFF - (TL_EXT_SG_PAGE_BYTELEN -1) );// mask off LSBs
+
+ alignedPageAddress = (ULONG)ulBuff;
+#ifdef DBG_SEST_SGLIST
+ printk("new PoolPage: %p, alignedPageAddress %lXh\n",
+ sgPages->PoolPage[AllocatedPages], ulBuff);
+#endif
+
+
+ // set pointer, in SEST if first Ext. S/G page, or in last pair
+ // of linked Ext. S/G pages...
+ // (Only 32-bit PVOIDs, so just load lower 32 bits)
+ // NOTE: the Len field must be '0' if this is the first Ext. S/G
+ // pointer in SEST, and not 0 otherwise.
+ if( alPair == SESTalPairStart) // initial Ext. S/G list?
+ *alPair = 0;
+ else // not the SEST entry... Len must be non-0, so
+ // arbitrarily set it to number bytes remaining
+ *alPair = ( bytes_to_go & 0x7ffff);
+
+#ifdef DBG_SEST_SGLIST
+ printk("PairCount %d @%p even %Xh, ",
+ PairCount, alPair, *alPair);
+#endif
+ alPair++; // next DWORD
+
+ *alPair = alignedPageAddress; // TL needs 32-bit physical
+#ifdef DBG_SEST_SGLIST
+ printk("odd %Xh\n", *alPair);
+#endif
+
+ // now reset the pointer to the ACTUAL (Extended) S/G page
+ // which will accept the Len/ PhysicalAddress pairs
+ alPair = bus_to_virt(alignedPageAddress);
+
+ AllocatedPages++;
+ PairCount = 1; // starting new Ext. S/G page
+ } // end of new TL Ext. S/G page allocation
+
+
+ *alPair = thisMappingLen; // bits 18-0, length (range check above)
+
+
+// physicalAddress.HighPart <= 19; // shift to bit 19
+
+ // pick up bits 44-32 of upper 64-bit address
+ // and load into 31-19 LBAU (upper addr) of SEST entry
+// *alPair |=(ULONG)((physicalAddress.HighPart & 0xFFF8));
+
+
+#ifdef DBG_SEST_SGLIST
+ printk("PairCount %d @%p, even %Xh, ",
+ PairCount, alPair, *alPair);
+#endif
+
+ alPair++; // next DWORD
+ // on Tachlite TS's local S/G, we can handle 13 extra address bits
+ // i.e., bits 31-19 are actually bits 44-32 of physicalAddress
+
+
+ ulBuff = virt_to_bus( sgl->address);
+#if BITS_PER_LONG > 32
+ if( ulBuff >>32 )
+ {
+ printk("cqpfcTS: Tach DMA address %p > 32 bits\n", (void*)ulBuff );
+ return 0;
+ }
+#endif
+ *alPair = (ULONG)ulBuff; // lower 32 bits (31-0)
+
+
+#ifdef DBG_SEST_SGLIST
+ printk("odd %Xh\n", *alPair);
+#endif
+ alPair++; // next DWORD
+
+
+ PairCount++; // next Length/Address pair
+ bytes_to_go -= thisMappingLen;
+ total_data_len += thisMappingLen;
+ sgl++; // next S/G pair
+ }
+ }
+ return total_data_len;
+}
+
+
+
+// The Tachlite SEST table is referenced to OX_ID (or RX_ID). To optimize
+// performance and debuggability, we index the Exchange structure to FC X_ID
+// This enables us to build exchanges for later en-queing to Tachyon,
+// provided we have an open X_ID slot. At Tachyon queing time, we only
+// need an ERQ slot; then "fix-up" references in the
+// IRB, FCHS, etc. as needed.
+// RETURNS:
+// 0 if successful
+// non-zero on error
+//sstartex
+ULONG cpqfcTSStartExchange(
+ CPQFCHBA *cpqfcHBAdata,
+ LONG ExchangeID )
+{
+ PTACHYON fcChip = &cpqfcHBAdata->fcChip;
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ FC_EXCHANGE *pExchange = &Exchanges->fcExchange[ ExchangeID ]; // shorthand
+ USHORT producer, consumer;
+ ULONG ulStatus=0;
+ short int ErqIndex;
+ BOOLEAN CompleteExchange = FALSE; // e.g. ACC replies are complete
+ BOOLEAN SestType=FALSE;
+ ULONG InboundData=0;
+
+ // We will manipulate Tachlite chip registers here to successfully
+ // start exchanges.
+
+ // Check that link is not down -- we can't start an exchange on a
+ // down link!
+
+ if( fcChip->Registers.FMstatus.value & 0x80) // LPSM offline?
+ {
+printk("fcStartExchange: PSM offline (%Xh), x_ID %Xh, type %Xh, port_id %Xh\n",
+ fcChip->Registers.FMstatus.value & 0xFF,
+ ExchangeID,
+ pExchange->type,
+ pExchange->fchs.d_id);
+
+ if( ExchangeID >= TACH_SEST_LEN ) // Link Service Outbound frame?
+ {
+ // Our most popular LinkService commands are port discovery types
+ // (PLOGI/ PDISC...), which are implicitly nullified by Link Down
+ // events, so it makes no sense to Que them. However, ABTS should
+ // be queued, since exchange sequences are likely destroyed by
+ // Link Down events, and we want to notify other ports of broken
+ // sequences by aborting the corresponding exchanges.
+ if( pExchange->type != BLS_ABTS )
+ {
+ ulStatus = LNKDWN_OSLS;
+ goto Done;
+ // don't Que most LinkServ exchanges on LINK DOWN
+ }
+ }
+
+ printk("fcStartExchange: Que x_ID %Xh, type %Xh\n",
+ ExchangeID, pExchange->type);
+ pExchange->status |= EXCHANGE_QUEUED;
+ ulStatus = EXCHANGE_QUEUED;
+ goto Done;
+ }
+
+ // Make sure ERQ has available space.
+
+ producer = (USHORT)fcChip->ERQ->producerIndex; // copies for logical arith.
+ consumer = (USHORT)fcChip->ERQ->consumerIndex;
+ producer++; // We are testing for full que by incrementing
+
+ if( producer >= ERQ_LEN ) // rollover condition?
+ producer = 0;
+ if( consumer != producer ) // ERQ not full?
+ {
+ // ****************** Need Atomic access to chip registers!!********
+
+ // remember ERQ PI for copying IRB
+ ErqIndex = (USHORT)fcChip->ERQ->producerIndex;
+ fcChip->ERQ->producerIndex = producer; // this is written to Tachyon
+ // we have an ERQ slot! If SCSI command, need SEST slot
+ // otherwise we are done.
+
+ // Note that Tachyon requires that bit 15 of the OX_ID or RX_ID be
+ // set according to direction of data to/from Tachyon for SEST assists.
+ // For consistency, enforce this rule for Link Service (non-SEST)
+ // exchanges as well.
+
+ // fix-up the X_ID field in IRB
+ pExchange->IRB.Req_A_Trans_ID = ExchangeID & 0x7FFF; // 15-bit field
+
+ // fix-up the X_ID field in fchs -- depends on Originator or Responder,
+ // outgoing or incoming data?
+ switch( pExchange->type )
+ {
+ // ORIGINATOR types... we're setting our OX_ID and
+ // defaulting the responder's RX_ID to 0xFFFF
+
+ case SCSI_IRE:
+ // Requirement: set MSB of x_ID for Incoming TL data
+ // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50)
+ InboundData = 0x8000;
+
+ case SCSI_IWE:
+ SestType = TRUE;
+ pExchange->fchs.ox_rx_id = (ExchangeID | InboundData);
+ pExchange->fchs.ox_rx_id <<= 16; // MSW shift
+ pExchange->fchs.ox_rx_id |= 0xffff; // add default RX_ID
+
+ // now fix-up the Data HDR OX_ID (TL automatically does rx_id)
+ // (not necessary for IRE -- data buffer unused)
+ if( pExchange->type == SCSI_IWE)
+ {
+ fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id =
+ pExchange->fchs.ox_rx_id;
+
+ }
+
+ break;
+
+
+ case FCS_NSR: // ext. link service Name Service Request
+ case ELS_SCR: // ext. link service State Change Registration
+ case ELS_FDISC:// ext. link service login
+ case ELS_FLOGI:// ext. link service login
+ case ELS_LOGO: // FC-PH extended link service logout
+ case BLS_NOP: // Basic link service No OPeration
+ case ELS_PLOGI:// ext. link service login (PLOGI)
+ case ELS_PDISC:// ext. link service login (PDISC)
+ case ELS_PRLI: // ext. link service process login
+
+ pExchange->fchs.ox_rx_id = ExchangeID;
+ pExchange->fchs.ox_rx_id <<= 16; // MSW shift
+ pExchange->fchs.ox_rx_id |= 0xffff; // and RX_ID
+
+ break;
+
+
+
+
+ // RESPONDER types... we must set our RX_ID while preserving
+ // sender's OX_ID
+ // outgoing (or no) data
+ case ELS_RJT: // extended link service reject
+ case ELS_LOGO_ACC: // FC-PH extended link service logout accept
+ case ELS_ACC: // ext. generic link service accept
+ case ELS_PLOGI_ACC:// ext. link service login accept (PLOGI or PDISC)
+ case ELS_PRLI_ACC: // ext. link service process login accept
+
+ CompleteExchange = TRUE; // Reply (ACC or RJT) is end of exchange
+ pExchange->fchs.ox_rx_id |= (ExchangeID & 0xFFFF);
+
+ break;
+
+
+ // since we are a Responder, OX_ID should already be set by
+ // cpqfcTSBuildExchange(). We need to -OR- in RX_ID
+ case SCSI_TWE:
+ SestType = TRUE;
+ // Requirement: set MSB of x_ID for Incoming TL data
+ // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50)
+
+ pExchange->fchs.ox_rx_id &= 0xFFFF0000; // clear RX_ID
+ // Requirement: set MSB of RX_ID for Incoming TL data
+ // (see "Tachyon TL/TS User's Manual", Rev 6.0, Sept.'98, pg. 50)
+ pExchange->fchs.ox_rx_id |= (ExchangeID | 0x8000);
+ break;
+
+
+ case SCSI_TRE:
+ SestType = TRUE;
+
+ // there is no XRDY for SEST target read; the data
+ // header needs to be updated. Also update the RSP
+ // exchange IDs for the status frame, in case it is sent automatically
+ fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id |= ExchangeID;
+ fcChip->SEST->RspHDR[ ExchangeID ].ox_rx_id =
+ fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id;
+
+ // for easier FCP response logic (works for TWE and TRE),
+ // copy exchange IDs. (Not needed if TRE 'RSP' bit set)
+ pExchange->fchs.ox_rx_id =
+ fcChip->SEST->DataHDR[ ExchangeID ].ox_rx_id;
+
+ break;
+
+
+ case FCP_RESPONSE: // using existing OX_ID/ RX_ID pair,
+ // start SFS FCP-RESPONSE frame
+ // OX/RX_ID should already be set! (See "fcBuild" above)
+ CompleteExchange = TRUE; // RSP is end of FCP-SCSI exchange
+
+
+ break;
+
+
+ case BLS_ABTS_RJT: // uses new RX_ID, since SEST x_ID non-existent
+ case BLS_ABTS_ACC: // using existing OX_ID/ RX_ID pair from SEST entry
+ CompleteExchange = TRUE; // ACC or RJT marks end of FCP-SCSI exchange
+ case BLS_ABTS: // using existing OX_ID/ RX_ID pair from SEST entry
+
+
+ break;
+
+
+ default:
+ printk("Error on fcStartExchange: undefined type %Xh(%d)\n",
+ pExchange->type, pExchange->type);
+ return INVALID_ARGS;
+ }
+
+
+ // X_ID fields are entered -- copy IRB to Tachyon's ERQ
+
+
+ memcpy(
+ &fcChip->ERQ->QEntry[ ErqIndex ], // dest.
+ &pExchange->IRB,
+ 32); // fixed (hardware) length!
+
+ PCI_TRACEO( ExchangeID, 0xA0)
+
+ // ACTION! May generate INT and IMQ entry
+ writel( fcChip->ERQ->producerIndex,
+ fcChip->Registers.ERQproducerIndex.address);
+
+
+ if( ExchangeID >= TACH_SEST_LEN ) // Link Service Outbound frame?
+ {
+
+ // wait for completion! (TDB -- timeout and chip reset)
+
+
+ PCI_TRACEO( ExchangeID, 0xA4)
+
+ enable_irq( cpqfcHBAdata->HostAdapter->irq); // only way to get Sem.
+
+ down_interruptible( cpqfcHBAdata->TYOBcomplete);
+
+ disable_irq( cpqfcHBAdata->HostAdapter->irq);
+ PCI_TRACE( 0xA4)
+
+ // On login exchanges, BAD_ALPA (non-existent port_id) results in
+ // FTO (Frame Time Out) on the Outbound Completion message.
+ // If we got an FTO status, complete the exchange (free up slot)
+ if( CompleteExchange || // flag from Reply frames
+ pExchange->status ) // typically, can get FRAME_TO
+ {
+ cpqfcTSCompleteExchange( fcChip, ExchangeID);
+ }
+ }
+
+ else // SEST Exchange
+ {
+ ulStatus = 0; // ship & pray success (e.g. FCP-SCSI)
+
+ if( CompleteExchange ) // by Type of exchange (e.g. end-of-xchng)
+ {
+ cpqfcTSCompleteExchange( fcChip, ExchangeID);
+ }
+
+ else
+ pExchange->status &= ~EXCHANGE_QUEUED; // clear ExchangeQueued flag
+
+ }
+ }
+
+
+ else // ERQ 'producer' = 'consumer' and QUE is full
+ {
+ ulStatus = OUTQUE_FULL; // Outbound (ERQ) Que full
+ }
+
+Done:
+ PCI_TRACE( 0xA0)
+ return ulStatus;
+}
+
+
+
+
+
+// Scan fcController->fcExchanges array for a usuable index (a "free"
+// exchange).
+// Inputs:
+// fcChip - pointer to TachLite chip structure
+// Return:
+// index - exchange array element where exchange can be built
+// -1 - exchange array is full
+// REMARKS:
+// Although this is a (yuk!) linear search, we presume
+// that the system will complete exchanges about as quickly as
+// they are submitted. A full Exchange array (and hence, max linear
+// search time for free exchange slot) almost guarantees a Fibre problem
+// of some sort.
+// In the interest of making exchanges easier to debug, we want a LRU
+// (Least Recently Used) scheme.
+
+
+static LONG FindFreeExchange( PTACHYON fcChip, ULONG type )
+{
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ ULONG i;
+ ULONG ulStatus=-1; // assume failure
+
+
+ if( type == SCSI_IRE ||
+ type == SCSI_TRE ||
+ type == SCSI_IWE ||
+ type == SCSI_TWE)
+ {
+ // SCSI type - X_IDs should be from 0 to TACH_SEST_LEN-1
+ if( fcChip->fcSestExchangeLRU >= TACH_SEST_LEN) // rollover?
+ fcChip->fcSestExchangeLRU = 0;
+ i = fcChip->fcSestExchangeLRU; // typically it's already free!
+
+ if( Exchanges->fcExchange[i].type == 0 ) // check for "free" element
+ {
+ ulStatus = 0; // success!
+ }
+
+ else
+ { // YUK! we need to do a linear search for free element.
+ // Fragmentation of the fcExchange array is due to excessively
+ // long completions or timeouts.
+
+ while( TRUE )
+ {
+ if( ++i >= TACH_SEST_LEN ) // rollover check
+ i = 0; // beginning of SEST X_IDs
+
+// printk( "looping for SCSI xchng ID: i=%d, type=%Xh\n",
+// i, Exchanges->fcExchange[i].type);
+
+ if( Exchanges->fcExchange[i].type == 0 ) // "free"?
+ {
+ ulStatus = 0; // success!
+ break;
+ }
+ if( i == fcChip->fcSestExchangeLRU ) // wrapped-around array?
+ {
+ printk( "SEST X_ID space full\n");
+ break; // failed - prevent inf. loop
+ }
+ }
+ }
+ fcChip->fcSestExchangeLRU = i + 1; // next! (rollover check next pass)
+ }
+
+
+
+ else // Link Service type - X_IDs should be from TACH_SEST_LEN
+ // to TACH_MAX_XID
+ {
+ if( fcChip->fcLsExchangeLRU >= TACH_MAX_XID || // range check
+ fcChip->fcLsExchangeLRU < TACH_SEST_LEN ) // (e.g. startup)
+ fcChip->fcLsExchangeLRU = TACH_SEST_LEN;
+
+ i = fcChip->fcLsExchangeLRU; // typically it's already free!
+ if( Exchanges->fcExchange[i].type == 0 ) // check for "free" element
+ {
+ ulStatus = 0; // success!
+ }
+
+ else
+ { // YUK! we need to do a linear search for free element
+ // Fragmentation of the fcExchange array is due to excessively
+ // long completions or timeouts.
+
+ while( TRUE )
+ {
+ if( ++i >= TACH_MAX_XID ) // rollover check
+ i = TACH_SEST_LEN;// beginning of Link Service X_IDs
+
+// printk( "looping for xchng ID: i=%d, type=%Xh\n",
+// i, Exchanges->fcExchange[i].type);
+
+ if( Exchanges->fcExchange[i].type == 0 ) // "free"?
+ {
+ ulStatus = 0; // success!
+ break;
+ }
+ if( i == fcChip->fcLsExchangeLRU ) // wrapped-around array?
+ {
+ printk( "LinkService X_ID space full\n");
+ break; // failed - prevent inf. loop
+ }
+ }
+ }
+ fcChip->fcLsExchangeLRU = i + 1; // next! (rollover check next pass)
+
+ }
+
+ if( !ulStatus ) // success?
+ Exchanges->fcExchange[i].type = type; // allocate it.
+
+ else
+ i = -1; // error - all exchanges "open"
+
+ return i;
+}
+
+
+
+
+
+// We call this routine to free an Exchange for any reason:
+// completed successfully, completed with error, aborted, etc.
+
+// returns FALSE if Exchange failed and "retry" is acceptable
+// returns TRUE if Exchange was successful, or retry is impossible
+// (e.g. port/device gone).
+//scompleteexchange
+
+void cpqfcTSCompleteExchange(
+ PTACHYON fcChip,
+ ULONG x_ID)
+{
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+
+ if( x_ID < TACH_SEST_LEN ) // SEST-based (or LinkServ for FCP exchange)
+ {
+ if( Exchanges->fcExchange[ x_ID ].Cmnd == NULL ) // what#@!
+ {
+// TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+ printk(" x_ID %Xh, type %Xh, NULL ptr!\n", x_ID,
+ Exchanges->fcExchange[ x_ID ].type);
+
+ goto CleanUpSestResources; // this path should be very rare.
+ }
+
+ // we have Linux Scsi Cmnd ptr..., now check our Exchange status
+ // to decide how to complete this SEST FCP exchange
+
+ if( Exchanges->fcExchange[ x_ID ].status ) // perhaps a Tach indicated problem,
+ // or abnormal exchange completion
+ {
+ // set FCP Link statistics
+
+ if( Exchanges->fcExchange[ x_ID ].status & FC2_TIMEOUT)
+ fcChip->fcStats.timeouts++;
+ if( Exchanges->fcExchange[ x_ID ].status & INITIATOR_ABORT)
+ fcChip->fcStats.FC4aborted++;
+ if( Exchanges->fcExchange[ x_ID ].status & COUNT_ERROR)
+ fcChip->fcStats.CntErrors++;
+ if( Exchanges->fcExchange[ x_ID ].status & LINKFAIL_TX)
+ fcChip->fcStats.linkFailTX++;
+ if( Exchanges->fcExchange[ x_ID ].status & LINKFAIL_RX)
+ fcChip->fcStats.linkFailRX++;
+ if( Exchanges->fcExchange[ x_ID ].status & OVERFLOW)
+ fcChip->fcStats.CntErrors++;
+
+ // First, see if the Scsi upper level initiated an ABORT on this
+ // exchange...
+ if( Exchanges->fcExchange[ x_ID ].status == INITIATOR_ABORT )
+ {
+ printk(" DID_ABORT, x_ID %Xh, Cmnd %p ",
+ x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+ goto CleanUpSestResources; // (we don't expect Linux _aborts)
+ }
+
+ // Did our driver timeout the Exchange, or did Tachyon indicate
+ // a failure during transmission? Ask for retry with "SOFT_ERROR"
+ else if( Exchanges->fcExchange[ x_ID ].status & FC2_TIMEOUT)
+ {
+// printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n",
+// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+ Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+ }
+
+ // Did frame(s) for an open exchange arrive in the SFQ,
+ // meaning the SEST was unable to process them?
+ else if( Exchanges->fcExchange[ x_ID ].status & SFQ_FRAME)
+ {
+// printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n",
+// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+ Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+ }
+
+ // Did our driver timeout the Exchange, or did Tachyon indicate
+ // a failure during transmission? Ask for retry with "SOFT_ERROR"
+ else if(
+ (Exchanges->fcExchange[ x_ID ].status & LINKFAIL_TX) ||
+ (Exchanges->fcExchange[ x_ID ].status & PORTID_CHANGED) ||
+ (Exchanges->fcExchange[ x_ID ].status & FRAME_TO) ||
+ (Exchanges->fcExchange[ x_ID ].status & INV_ENTRY) ||
+ (Exchanges->fcExchange[ x_ID ].status & ABORTSEQ_NOTIFY) )
+
+
+ {
+// printk("result DID_SOFT_ERROR, x_ID %Xh, Cmnd %p\n",
+// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+ Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+
+
+ }
+
+ // e.g., a LOGOut happened, or device never logged back in.
+ else if( Exchanges->fcExchange[ x_ID ].status & DEVICE_REMOVED)
+ {
+// printk(" *LOGOut or timeout on login!* ");
+ // trigger?
+// TriggerHBA( fcChip->Registers.ReMapMemBase, 0);
+
+ Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_BAD_TARGET <<16);
+ }
+
+
+ // Did Tachyon indicate a CNT error? We need further analysis
+ // to determine if the exchange is acceptable
+ else if( Exchanges->fcExchange[ x_ID ].status == COUNT_ERROR)
+ {
+ UCHAR ScsiStatus;
+ FCP_STATUS_RESPONSE *pFcpStatus =
+ (PFCP_STATUS_RESPONSE)&fcChip->SEST->RspHDR[ x_ID ].pl;
+
+ ScsiStatus = pFcpStatus->fcp_status >>24;
+
+ // If the command is a SCSI Read/Write type, we don't tolerate
+ // count errors of any kind; assume the count error is due to
+ // a dropped frame and ask for retry...
+
+ if(( (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x8) ||
+ (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x28) ||
+ (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0xA) ||
+ (Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0] == 0x2A) )
+ &&
+ ScsiStatus == 0 )
+ {
+ // ask for retry
+/* printk("COUNT_ERROR retry, x_ID %Xh, status %Xh, Cmnd %p\n",
+ x_ID, Exchanges->fcExchange[ x_ID ].status,
+ Exchanges->fcExchange[ x_ID ].Cmnd);*/
+ Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_SOFT_ERROR <<16);
+ }
+
+ else // need more analysis
+ {
+ cpqfcTSCheckandSnoopFCP(fcChip, x_ID); // (will set ->result)
+ }
+ }
+
+ // default: NOTE! We don't ever want to get here. Getting here
+ // implies something new is happening that we've never had a test
+ // case for. Need code maintenance! Return "ERROR"
+ else
+ {
+ printk("DEFAULT result %Xh, x_ID %Xh, Cmnd %p\n",
+ Exchanges->fcExchange[ x_ID ].status, x_ID,
+ Exchanges->fcExchange[ x_ID ].Cmnd);
+ Exchanges->fcExchange[ x_ID ].Cmnd->result = (DID_ERROR <<16);
+ }
+ }
+ else // definitely no Tach problem, but perhaps an FCP problem
+ {
+ // set FCP Link statistic
+ fcChip->fcStats.ok++;
+ cpqfcTSCheckandSnoopFCP( fcChip, x_ID); // (will set ->result)
+ }
+
+ // OK, we've set the Scsi "->result" field, so proceed with calling
+ // Linux Scsi "done" (if not NULL), and free any kernel memory we
+ // may have allocated for the exchange.
+
+ PCI_TRACEO( (ULONG)Exchanges->fcExchange[x_ID].Cmnd, 0xAC);
+ // complete the command back to upper Scsi drivers
+ if( Exchanges->fcExchange[ x_ID ].Cmnd->scsi_done != NULL)
+ {
+ // Calling "done" on an Linux _abort() aborted
+ // Cmnd causes a kernel panic trying to re-free mem.
+ // Actually, we shouldn't do anything with an _abort CMND
+ if( Exchanges->fcExchange[ x_ID ].Cmnd->result != (DID_ABORT<<16) )
+ {
+ PCI_TRACE(0xAC)
+ (*Exchanges->fcExchange[ x_ID ].Cmnd->scsi_done)
+ (Exchanges->fcExchange[ x_ID ].Cmnd);
+ }
+ else
+ {
+
+// printk(" not calling scsi_done on x_ID %Xh, Cmnd %p\n",
+// x_ID, Exchanges->fcExchange[ x_ID ].Cmnd);
+ }
+ }
+ else{
+ printk(" x_ID %Xh, type %Xh, Cdb0 %Xh\n", x_ID,
+ Exchanges->fcExchange[ x_ID ].type,
+ Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0]);
+ printk(" cpqfcTS: Null scsi_done function pointer!\n");
+ }
+
+
+ // Now, clean up non-Scsi_Cmnd items...
+CleanUpSestResources:
+
+ // Was an Extended Scatter/Gather page allocated? We know
+ // this by checking DWORD 4, bit 31 ("LOC") of SEST entry
+ if( !(fcChip->SEST->u[ x_ID ].IWE.Buff_Off & 0x80000000))
+ {
+ int i = 0;
+
+ // extended S/G list was used -- Free the allocated ext. S/G pages
+
+ while( fcChip->SEST->sgPages[x_ID].PoolPage[i] &&
+ (i < TL_MAX_SGPAGES) )
+ {
+ kfree( fcChip->SEST->sgPages[x_ID].PoolPage[i]);
+ fcChip->SEST->sgPages[x_ID].PoolPage[i] = NULL;
+ i++;
+ }
+ }
+
+ Exchanges->fcExchange[ x_ID ].Cmnd = NULL;
+ } // Done with FCP (SEST) exchanges
+
+
+ // the remaining logic is common to ALL Exchanges:
+ // FCP(SEST) and LinkServ.
+
+ Exchanges->fcExchange[ x_ID ].type = 0; // there -- FREE!
+ Exchanges->fcExchange[ x_ID ].status = 0;
+
+ PCI_TRACEO( x_ID, 0xAC)
+
+
+ return;
+} // (END of CompleteExchange function)
+
+
+
+
+// Unfortunately, we must snoop all command completions in
+// order to manipulate certain return fields, and take note of
+// device types, etc., to facilitate the Fibre-Channel to SCSI
+// "mapping".
+// (Watch for BIG Endian confusion on some payload fields)
+void cpqfcTSCheckandSnoopFCP( PTACHYON fcChip, ULONG x_ID)
+{
+ FC_EXCHANGES *Exchanges = fcChip->Exchanges;
+ Scsi_Cmnd *Cmnd = Exchanges->fcExchange[ x_ID].Cmnd;
+ FCP_STATUS_RESPONSE *pFcpStatus =
+ (PFCP_STATUS_RESPONSE)&fcChip->SEST->RspHDR[ x_ID ].pl;
+ UCHAR ScsiStatus;
+
+ ScsiStatus = pFcpStatus->fcp_status >>24;
+
+#ifdef FCP_COMPLETION_DBG
+ printk("ScsiStatus = 0x%X\n", ScsiStatus);
+#endif
+
+ // First, check FCP status
+ if( pFcpStatus->fcp_status & FCP_RSP_LEN_VALID )
+ {
+ // check response code (RSP_CODE) -- most popular is bad len
+ // 1st 4 bytes of rsp info -- only byte 3 interesting
+ if( pFcpStatus->fcp_rsp_info & FCP_DATA_LEN_NOT_BURST_LEN )
+ {
+
+ // do we EVER get here?
+ printk("cpqfcTS: FCP data len not burst len, x_ID %Xh\n", x_ID);
+ }
+ }
+
+ // for now, go by the ScsiStatus, and manipulate certain
+ // commands when necessary...
+ if( ScsiStatus == 0) // SCSI status byte "good"?
+ {
+ Cmnd->result = 0; // everything's OK
+
+ if( (Cmnd->cmnd[0] == INQUIRY))
+ {
+ UCHAR *InquiryData = Cmnd->request_buffer;
+ PFC_LOGGEDIN_PORT pLoggedInPort;
+
+ // We need to manipulate INQUIRY
+ // strings for COMPAQ RAID controllers to force
+ // Linux to scan additional LUNs. Namely, set
+ // the Inquiry string byte 2 (ANSI-approved version)
+ // to 2.
+
+ if( !memcmp( &InquiryData[8], "COMPAQ", 6 ))
+ {
+ InquiryData[2] = 0x2; // claim SCSI-2 compliance,
+ // so multiple LUNs may be scanned.
+ // (no SCSI-2 problems known in CPQ)
+ }
+
+ // snoop the Inquiry to detect Disk, Tape, etc. type
+ // (search linked list for the port_id we sent INQUIRY to)
+ pLoggedInPort = fcFindLoggedInPort( fcChip,
+ NULL, // DON'T search Scsi Nexus (we will set it)
+ Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF,
+ NULL, // DON'T search linked list for FC WWN
+ NULL); // DON'T care about end of list
+
+ if( pLoggedInPort )
+ {
+ pLoggedInPort->ScsiNexus.InqDeviceType = InquiryData[0];
+ }
+ else
+ {
+ printk("cpqfcTS: can't find LoggedIn FC port %06X for INQUIRY\n",
+ Exchanges->fcExchange[ x_ID].fchs.d_id & 0xFFFFFF);
+ }
+ }
+ }
+
+
+ // Scsi Status not good -- pass it back to caller
+
+ else
+ {
+ Cmnd->result = ScsiStatus; // SCSI status byte is 1st
+
+ // check for valid "sense" data
+
+ if( pFcpStatus->fcp_status & FCP_SNS_LEN_VALID )
+ { // limit Scsi Sense field length!
+ int SenseLen = pFcpStatus->fcp_sns_len >>24; // (BigEndian) lower byte
+
+ SenseLen = SenseLen > sizeof( Cmnd->sense_buffer) ?
+ sizeof( Cmnd->sense_buffer) : SenseLen;
+
+
+#ifdef FCP_COMPLETION_DBG
+ printk("copy sense_buffer %p, len %d, result %Xh\n",
+ Cmnd->sense_buffer, SenseLen, Cmnd->result);
+#endif
+
+ // NOTE: There is some dispute over the FCP response
+ // format. Most FC devices assume that FCP_RSP_INFO
+ // is 8 bytes long, in spite of the fact that FCP_RSP_LEN
+ // is (virtually) always 0 and the field is "invalid".
+ // Some other devices assume that
+ // the FCP_SNS_INFO begins after FCP_RSP_LEN bytes (i.e. 0)
+ // when the FCP_RSP is invalid (this almost appears to be
+ // one of those "religious" issues).
+ // Consequently, we test the usual position of FCP_SNS_INFO
+ // for 7Xh, since the SCSI sense format says the first
+ // byte ("error code") should be 0x70 or 0x71. In practice,
+ // we find that every device does in fact have 0x70 or 0x71
+ // in the first byte position, so this test works for all
+ // FC devices.
+ // (This logic is especially effective for the CPQ/DEC HSG80
+ // & HSG60 controllers).
+
+ if( (pFcpStatus->fcp_sns_info[0] & 0x70) == 0x70 )
+ memcpy( Cmnd->sense_buffer,
+ &pFcpStatus->fcp_sns_info[0], SenseLen);
+ else
+ {
+ unsigned char *sbPtr =
+ (unsigned char *)&pFcpStatus->fcp_sns_info[0];
+ sbPtr -= 8; // back up 8 bytes hoping to find the
+ // start of the sense buffer
+ memcpy( Cmnd->sense_buffer, sbPtr, SenseLen);
+ }
+
+ // in the special case of Device Reset, tell upper layer
+ // to immediately retry (with SOFT_ERROR status)
+ // look for Sense Key Unit Attention (0x6) with ASC Device
+ // Reset (0x29)
+ // printk("SenseLen %d, Key = 0x%X, ASC = 0x%X\n",
+ // SenseLen, Cmnd->sense_buffer[2],
+ // Cmnd->sense_buffer[12]);
+ if( ((Cmnd->sense_buffer[2] & 0xF) == 0x6) &&
+ (Cmnd->sense_buffer[12] == 0x29) ) // Sense Code "reset"
+ {
+ Cmnd->result |= (DID_SOFT_ERROR << 16); // "Host" status byte 3rd
+ }
+
+ // check for SenseKey "HARDWARE ERROR", ASC InternalTargetFailure
+ else if( ((Cmnd->sense_buffer[2] & 0xF) == 0x4) && // "hardware error"
+ (Cmnd->sense_buffer[12] == 0x44) ) // Addtl. Sense Code
+ {
+// printk("HARDWARE_ERROR, Channel/Target/Lun %d/%d/%d\n",
+// Cmnd->channel, Cmnd->target, Cmnd->lun);
+ Cmnd->result |= (DID_ERROR << 16); // "Host" status byte 3rd
+ }
+
+ } // (end of sense len valid)
+
+ // there is no sense data to help out Linux's Scsi layers...
+ // We'll just return the Scsi status and hope he will "do the
+ // right thing"
+ else
+ {
+ // as far as we know, the Scsi status is sufficient
+ Cmnd->result |= (DID_OK << 16); // "Host" status byte 3rd
+ }
+ }
+}
+
+
+
+//PPPPPPPPPPPPPPPPPPPPPPPPP PAYLOAD PPPPPPPPP
+// build data PAYLOAD; SCSI FCP_CMND I.U.
+// remember BIG ENDIAN payload - DWord values must be byte-reversed
+// (hence the affinity for byte pointer building).
+
+static int build_FCP_payload( Scsi_Cmnd *Cmnd,
+ UCHAR* payload, ULONG type, ULONG fcp_dl )
+{
+ int i;
+
+
+ switch( type)
+ {
+
+ case SCSI_IWE:
+ case SCSI_IRE:
+ // 8 bytes FCP_LUN
+ // Peripheral Device or Volume Set addressing, and LUN mapping
+ // When the FC port was looked up, we copied address mode
+ // and any LUN mask to the scratch pad SCp.phase & .mode
+
+ *payload++ = (UCHAR)Cmnd->SCp.phase;
+
+ // Now, because of "lun masking"
+ // (aka selective storage presentation),
+ // the contiguous Linux Scsi lun number may not match the
+ // device's lun number, so we may have to "map".
+
+ *payload++ = (UCHAR)Cmnd->SCp.have_data_in;
+
+ // We don't know of anyone in the FC business using these
+ // extra "levels" of addressing. In fact, confusion still exists
+ // just using the FIRST level... ;-)
+
+ *payload++ = 0; // 2nd level addressing
+ *payload++ = 0;
+ *payload++ = 0; // 3rd level addressing
+ *payload++ = 0;
+ *payload++ = 0; // 4th level addressing
+ *payload++ = 0;
+
+ // 4 bytes Control Field FCP_CNTL
+ *payload++ = 0; // byte 0: (MSB) reserved
+ *payload++ = 0; // byte 1: task codes
+ *payload++ = 0; // byte 2: task management flags
+ // byte 3: (LSB) execution management codes
+ // bit 0 write, bit 1 read (don't set together)
+
+ if( fcp_dl != 0 )
+ {
+ if( type == SCSI_IWE ) // WRITE
+ *payload++ = 1;
+ else // READ
+ *payload++ = 2;
+ }
+ else
+ {
+ // On some devices, if RD or WR bits are set,
+ // and fcp_dl is 0, they will generate an error on the command.
+ // (i.e., if direction is specified, they insist on a length).
+ *payload++ = 0; // no data (necessary for CPQ)
+ }
+
+
+ // NOTE: clean this up if/when MAX_COMMAND_SIZE is increased to 16
+ // FCP_CDB allows 16 byte SCSI command descriptor blk;
+ // Linux SCSI CDB array is MAX_COMMAND_SIZE (12 at this time...)
+ for( i=0; (i < Cmnd->cmd_len) && i < MAX_COMMAND_SIZE; i++)
+ *payload++ = Cmnd->cmnd[i];
+
+ if( Cmnd->cmd_len == 16 )
+ {
+ memcpy( payload, &Cmnd->SCp.buffers_residual, 4);
+ }
+ payload+= (16 - i);
+
+ // FCP_DL is largest number of expected data bytes
+ // per CDB (i.e. read/write command)
+ *payload++ = (UCHAR)(fcp_dl >>24); // (MSB) 8 bytes data len FCP_DL
+ *payload++ = (UCHAR)(fcp_dl >>16);
+ *payload++ = (UCHAR)(fcp_dl >>8);
+ *payload++ = (UCHAR)fcp_dl; // (LSB)
+ break;
+
+ case SCSI_TWE: // need FCP_XFER_RDY
+ *payload++ = 0; // (4 bytes) DATA_RO (MSB byte 0)
+ *payload++ = 0;
+ *payload++ = 0;
+ *payload++ = 0; // LSB (byte 3)
+ // (4 bytes) BURST_LEN
+ // size of following FCP_DATA payload
+ *payload++ = (UCHAR)(fcp_dl >>24); // (MSB) 8 bytes data len FCP_DL
+ *payload++ = (UCHAR)(fcp_dl >>16);
+ *payload++ = (UCHAR)(fcp_dl >>8);
+ *payload++ = (UCHAR)fcp_dl; // (LSB)
+ // 4 bytes RESERVED
+ *payload++ = 0;
+ *payload++ = 0;
+ *payload++ = 0;
+ *payload++ = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
diff --git a/drivers/scsi/cpqioctl.c b/drivers/scsi/cpqioctl.c
new file mode 100644
index 000000000..7f09a4341
--- /dev/null
+++ b/drivers/scsi/cpqioctl.c
@@ -0,0 +1,76 @@
+// Test program for CPQFCTS ioctl calls
+// build with:
+// gcc -o cpqioctl cpqioctl.c
+// ld -o cpqioctl /lib/crt0.o cpqioctl.o -lc
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/types.h>
+#include "../../include/scsi/scsi.h"
+#include "cpqfcTSioctl.h"
+
+typedef struct scsi_fctargaddress {
+ unsigned long host_port_id;
+ unsigned char host_wwn[8];
+} Scsi_FCTargAddress;
+
+int main(int argc, char **argv) {
+
+ int fd, i;
+ Scsi_FCTargAddress targ;
+ int uselect=0;
+
+
+
+ if ( argc < 2 ) {
+ printf("usage: cpqioctl <Devfile>\n");
+ exit(1);
+ }
+
+ if ( (fd = open(argv[1], O_RDONLY)) == -1) {
+ perror("open");
+ exit(1);
+ }
+
+ if ( ioctl(fd, SCSI_IOCTL_FC_TARGET_ADDRESS, &targ) ) {
+ perror("ioctl");
+ exit(1);
+ }
+
+
+ printf("portid: %08x. wwn: ", targ.host_port_id);
+
+ for (i=0;i<8;i++) printf(" %02x", targ.host_wwn[i]);
+ printf("\n");
+
+ while( uselect != 27 ) // not ESC key
+ {
+ printf("\n IOCTL \n");
+ printf( "1. Get PCI info\n");
+ printf( "2. Send Passthru\n");
+ printf( " ==> ");
+ scanf("%c", &uselect);
+
+ switch( uselect )
+ {
+ case '1':
+ {
+ cciss_pci_info_struct pciinfo;
+
+ if( ioctl( fd, CCPQFCTS_GETPCIINFO ,&pciinfo ))
+ perror("ioctl");
+ else
+ printf( "\nPCI bus %d, dev_fn %d, board_id %Xh\n",
+ pciinfo.bus, pciinfo.dev_fn, pciinfo.board_id);
+ }
+
+ }
+ }
+
+
+ close(fd);
+ return 0;
+}
diff --git a/drivers/scsi/cyberstorm.c b/drivers/scsi/cyberstorm.c
index 4d720b99a..87a3e842d 100644
--- a/drivers/scsi/cyberstorm.c
+++ b/drivers/scsi/cyberstorm.c
@@ -302,18 +302,14 @@ static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
}
}
-#ifdef MODULE
-
#define HOSTS_C
#include "cyberstorm.h"
-Scsi_Host_Template driver_template = SCSI_CYBERSTORM;
+static Scsi_Host_Template driver_template = SCSI_CYBERSTORM;
#include "scsi_module.c"
-#endif
-
int cyber_esp_release(struct Scsi_Host *instance)
{
#ifdef MODULE
diff --git a/drivers/scsi/cyberstormII.c b/drivers/scsi/cyberstormII.c
index 9ff180665..cfd5ff6cb 100644
--- a/drivers/scsi/cyberstormII.c
+++ b/drivers/scsi/cyberstormII.c
@@ -251,17 +251,14 @@ static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
}
}
-#ifdef MODULE
-
#define HOSTS_C
#include "cyberstormII.h"
-Scsi_Host_Template driver_template = SCSI_CYBERSTORMII;
+static Scsi_Host_Template driver_template = SCSI_CYBERSTORMII;
#include "scsi_module.c"
-#endif
int cyberII_esp_release(struct Scsi_Host *instance)
{
diff --git a/drivers/scsi/dc390.h b/drivers/scsi/dc390.h
index 727fa4c23..96b4b929d 100644
--- a/drivers/scsi/dc390.h
+++ b/drivers/scsi/dc390.h
@@ -18,8 +18,6 @@
#define DC390_BANNER "Tekram DC390/AM53C974"
#define DC390_VERSION "2.0d 1998/12/25"
-#if defined(HOSTS_C) || defined(MODULE)
-
#include <scsi/scsicam.h>
extern int DC390_detect(Scsi_Host_Template *psht);
@@ -53,6 +51,4 @@ extern int DC390_proc_info(char *buffer, char **start, off_t offset, int length,
use_clustering: DISABLE_CLUSTERING \
}
-#endif /* defined(HOSTS_C) || defined(MODULE) */
-
#endif /* DC390_H */
diff --git a/drivers/scsi/dmx3191d.c b/drivers/scsi/dmx3191d.c
index 4d1eecef4..327743bf8 100644
--- a/drivers/scsi/dmx3191d.c
+++ b/drivers/scsi/dmx3191d.c
@@ -115,10 +115,6 @@ int dmx3191d_release_resources(struct Scsi_Host *instance)
}
-#ifdef MODULE
-Scsi_Host_Template driver_template = DMX3191D;
-
+static Scsi_Host_Template driver_template = DMX3191D;
#include "scsi_module.c"
-#endif /* MODULE */
-
diff --git a/drivers/scsi/dmx3191d.h b/drivers/scsi/dmx3191d.h
index c037d3bf0..7a5ad1b69 100644
--- a/drivers/scsi/dmx3191d.h
+++ b/drivers/scsi/dmx3191d.h
@@ -32,7 +32,6 @@ int dmx3191d_release_resources(struct Scsi_Host *);
int dmx3191d_reset(Scsi_Cmnd *, unsigned int);
-#if defined(HOSTS_C) || defined(MODULE)
#define DMX3191D { \
proc_info: dmx3191d_proc_info, \
name: "Domex DMX3191D", \
@@ -49,10 +48,8 @@ int dmx3191d_reset(Scsi_Cmnd *, unsigned int);
cmd_per_lun: 2, \
use_clustering: DISABLE_CLUSTERING \
}
-#endif /* HOSTS_C || MODULE */
-#ifndef HOSTS_C
#define NCR5380_read(reg) inb(port + reg)
#define NCR5380_write(reg, value) outb(value, port + reg)
@@ -67,7 +64,6 @@ int dmx3191d_reset(Scsi_Cmnd *, unsigned int);
#define NCR5380_queue_command dmx3191d_queue_command
#define NCR5380_reset dmx3191d_reset
-#endif /* HOSTS_C */
#endif /* ASM */
#endif /* __DMX3191D_H */
diff --git a/drivers/scsi/dtc.c b/drivers/scsi/dtc.c
index 8917f32f8..5e2635cba 100644
--- a/drivers/scsi/dtc.c
+++ b/drivers/scsi/dtc.c
@@ -432,9 +432,6 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance,
#include "NCR5380.c"
-#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = DTC3x80;
-
+static Scsi_Host_Template driver_template = DTC3x80;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/dtc.h b/drivers/scsi/dtc.h
index 9acdf4a04..7076cbbe0 100644
--- a/drivers/scsi/dtc.h
+++ b/drivers/scsi/dtc.h
@@ -55,8 +55,6 @@ int dtc_proc_info (char *buffer, char **start, off_t offset,
* macros when this is being used solely for the host stub.
*/
-#if defined(HOSTS_C) || defined(MODULE)
-
#define DTC3x80 { \
name: "DTC 3180/3280 ", \
detect: dtc_detect, \
@@ -70,10 +68,6 @@ int dtc_proc_info (char *buffer, char **start, off_t offset,
cmd_per_lun: CMD_PER_LUN , \
use_clustering: DISABLE_CLUSTERING}
-#endif
-
-#ifndef HOSTS_C
-
#define NCR5380_implementation_fields \
volatile unsigned int base
@@ -124,6 +118,5 @@ int dtc_proc_info (char *buffer, char **start, off_t offset,
#define DTC_IRQS 0x9c00
-#endif /* else def HOSTS_C */
#endif /* ndef ASM */
#endif /* DTC3280_H */
diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c
index 4f3c09d6c..0d0711878 100644
--- a/drivers/scsi/eata.c
+++ b/drivers/scsi/eata.c
@@ -2292,12 +2292,11 @@ int eata2x_release(struct Scsi_Host *shpnt) {
return FALSE;
}
-#if defined(MODULE)
-Scsi_Host_Template driver_template = EATA;
+static Scsi_Host_Template driver_template = EATA;
#include "scsi_module.c"
-#else
+#ifndef MODULE
#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
void eata2x_setup(char *str, int *ints) {
diff --git a/drivers/scsi/eata_dma.c b/drivers/scsi/eata_dma.c
index ca3b48110..09b6929b2 100644
--- a/drivers/scsi/eata_dma.c
+++ b/drivers/scsi/eata_dma.c
@@ -1520,11 +1520,9 @@ int eata_detect(Scsi_Host_Template * tpnt)
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;
+static Scsi_Host_Template driver_template = EATA_DMA;
#include "scsi_module.c"
-#endif
/*
* Overrides for Emacs so that we almost follow Linus's tabbing style.
diff --git a/drivers/scsi/eata_dma.h b/drivers/scsi/eata_dma.h
index 616744efe..9d4447a89 100644
--- a/drivers/scsi/eata_dma.h
+++ b/drivers/scsi/eata_dma.h
@@ -10,8 +10,6 @@
#ifndef _EATA_DMA_H
#define _EATA_DMA_H
-#ifndef HOSTS_C
-
#include "eata_generic.h"
@@ -67,8 +65,6 @@
#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 *);
diff --git a/drivers/scsi/eata_pio.c b/drivers/scsi/eata_pio.c
index b276f8629..ef4561fdc 100644
--- a/drivers/scsi/eata_pio.c
+++ b/drivers/scsi/eata_pio.c
@@ -985,12 +985,10 @@ int eata_pio_detect(Scsi_Host_Template * tpnt)
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;
+static Scsi_Host_Template driver_template = EATA_PIO;
#include "scsi_module.c"
-#endif
/*
* Overrides for Emacs so that we almost follow Linus's tabbing style.
diff --git a/drivers/scsi/esp.c b/drivers/scsi/esp.c
index 6339d1e90..f928bb3c6 100644
--- a/drivers/scsi/esp.c
+++ b/drivers/scsi/esp.c
@@ -1,4 +1,4 @@
-/* $Id: esp.c,v 1.95 2000/08/23 22:32:37 davem Exp $
+/* $Id: esp.c,v 1.96 2000/08/24 03:51:26 davem Exp $
* esp.c: EnhancedScsiProcessor Sun SCSI driver code.
*
* Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.edu)
@@ -1599,7 +1599,9 @@ static void esp_exec_cmd(struct esp *esp)
if (SDptr->sync) {
/* this targets sync is known */
+#ifndef __sparc_v9__
do_sync_known:
+#endif
if (SDptr->disconnect)
*cmdp++ = IDENTIFY(1, lun);
else
@@ -2583,6 +2585,8 @@ static int esp_do_data(struct esp *esp)
esp_advance_phase(SCptr, thisphase);
ESPDATA(("newphase<%s> ", (thisphase == in_datain) ? "DATAIN" : "DATAOUT"));
hmuch = dma_can_transfer(esp, SCptr);
+ if (hmuch > (64 * 1024) && (esp->erev != fashme))
+ hmuch = (64 * 1024);
ESPDATA(("hmuch<%d> ", hmuch));
esp->current_transfer_size = hmuch;
@@ -4360,10 +4364,8 @@ int esp_revoke(Scsi_Device* SDptr)
return 0;
}
-#ifdef MODULE
-Scsi_Host_Template driver_template = SCSI_SPARC_ESP;
+static Scsi_Host_Template driver_template = SCSI_SPARC_ESP;
#include "scsi_module.c"
EXPORT_NO_SYMBOLS;
-#endif /* MODULE */
diff --git a/drivers/scsi/fastlane.c b/drivers/scsi/fastlane.c
index 1c23fd858..0126ac431 100644
--- a/drivers/scsi/fastlane.c
+++ b/drivers/scsi/fastlane.c
@@ -349,18 +349,13 @@ static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write)
}
}
-#ifdef MODULE
-
#define HOSTS_C
#include "fastlane.h"
-Scsi_Host_Template driver_template = SCSI_FASTLANE;
-
+static Scsi_Host_Template driver_template = SCSI_FASTLANE;
#include "scsi_module.c"
-#endif
-
int fastlane_esp_release(struct Scsi_Host *instance)
{
#ifdef MODULE
diff --git a/drivers/scsi/fcal.c b/drivers/scsi/fcal.c
index dd3231f74..05ff2f7d9 100644
--- a/drivers/scsi/fcal.c
+++ b/drivers/scsi/fcal.c
@@ -292,11 +292,8 @@ static int fcal_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cmn
return 0;
}
-#ifdef MODULE
-
-Scsi_Host_Template driver_template = FCAL;
+static Scsi_Host_Template driver_template = FCAL;
#include "scsi_module.c"
EXPORT_NO_SYMBOLS;
-#endif /* MODULE */
diff --git a/drivers/scsi/fd_mcs.c b/drivers/scsi/fd_mcs.c
index b9aac12da..097893204 100644
--- a/drivers/scsi/fd_mcs.c
+++ b/drivers/scsi/fd_mcs.c
@@ -1465,9 +1465,7 @@ int fd_mcs_biosparam( Scsi_Disk *disk, kdev_t dev, int *info_array )
return 0;
}
-#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = FD_MCS;
+static Scsi_Host_Template driver_template = FD_MCS;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c
index 9694cf5e7..d6ac51931 100644
--- a/drivers/scsi/fdomain.c
+++ b/drivers/scsi/fdomain.c
@@ -2028,9 +2028,7 @@ int fdomain_16x0_biosparam( Scsi_Disk *disk, kdev_t dev, int *info_array )
return 0;
}
-#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = FDOMAIN_16X0;
+static Scsi_Host_Template driver_template = FDOMAIN_16X0;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c
index 594116c1f..c582c8e72 100644
--- a/drivers/scsi/g_NCR5380.c
+++ b/drivers/scsi/g_NCR5380.c
@@ -885,13 +885,14 @@ int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int leng
#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;
+static Scsi_Host_Template driver_template = GENERIC_NCR5380;
#include <linux/module.h>
#include "scsi_module.c"
+#ifdef MODULE
+
MODULE_PARM(ncr_irq, "i");
MODULE_PARM(ncr_dma, "i");
MODULE_PARM(ncr_addr, "i");
diff --git a/drivers/scsi/g_NCR5380.h b/drivers/scsi/g_NCR5380.h
index 6897c91c1..3ab55b6f7 100644
--- a/drivers/scsi/g_NCR5380.h
+++ b/drivers/scsi/g_NCR5380.h
@@ -70,8 +70,6 @@ int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int leng
#define CAN_QUEUE 16
#endif
-#if defined(HOSTS_C) || defined(MODULE)
-
#define GENERIC_NCR5380 { \
proc_info: generic_NCR5380_proc_info, \
name: "Generic NCR5380/NCR53C400 Scsi Driver", \
@@ -88,8 +86,6 @@ int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int leng
cmd_per_lun: CMD_PER_LUN , \
use_clustering: DISABLE_CLUSTERING}
-#endif
-
#ifndef HOSTS_C
#define __STRVAL(x) #x
diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
index 8f9d9216d..dd26cda1e 100644
--- a/drivers/scsi/gdth.c
+++ b/drivers/scsi/gdth.c
@@ -3704,7 +3704,5 @@ void __init gdth_setup(char *str,int *ints)
}
-#ifdef MODULE
-Scsi_Host_Template driver_template = GDTH;
+static Scsi_Host_Template driver_template = GDTH;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c
index 0c6150964..7eb632ba0 100644
--- a/drivers/scsi/gvp11.c
+++ b/drivers/scsi/gvp11.c
@@ -352,18 +352,14 @@ release:
}
-#ifdef MODULE
-
#define HOSTS_C
#include "gvp11.h"
-Scsi_Host_Template driver_template = GVP11_SCSI;
+static Scsi_Host_Template driver_template = GVP11_SCSI;
#include "scsi_module.c"
-#endif
-
int gvp11_release(struct Scsi_Host *instance)
{
#ifdef MODULE
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index ab934fb2d..b04de5a8a 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -11,6 +11,10 @@
* Jiffies wrap fixes (host->resetting), 3 Dec 1998 Andrea Arcangeli
* Added QLOGIC QLA1280 SCSI controller kernel host support.
* August 4, 1999 Fred Lewis, Intel DuPont
+ *
+ * Updated to reflect the new initialization scheme for the higher
+ * level of scsi drivers (sd/sr/st)
+ * September 17, 2000 Torben Mathiasen <tmm@image.dk>
*/
@@ -22,8 +26,6 @@
#define __NO_VERSION__
#include <linux/module.h>
-
-#include <linux/config.h>
#include <linux/blk.h>
#include <linux/kernel.h>
#include <linux/string.h>
@@ -36,361 +38,8 @@
#include <linux/unistd.h>
#include "scsi.h"
-
-#ifndef NULL
-#define NULL 0L
-#endif
-
-#define HOSTS_C
-
#include "hosts.h"
-#if defined(CONFIG_A4000T_SCSI) || \
- defined(CONFIG_WARPENGINE_SCSI) || \
- defined(CONFIG_A4091_SCSI) || \
- defined (CONFIG_GVP_TURBO_SCSI) || \
- defined (CONFIG_BLZ603EPLUS_SCSI)
-#define AMIGA7XXCONFIG
-#endif
-
-#ifdef AMIGA7XXCONFIG
-#include "amiga7xx.h"
-#endif
-
-#ifdef CONFIG_MVME16x_SCSI
-#include "mvme16x.h"
-#endif
-
-#ifdef CONFIG_BVME6000_SCSI
-#include "bvme6000.h"
-#endif
-
-#ifdef CONFIG_SCSI_SIM710
-#include "sim710.h"
-#endif
-
-#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_CYBERSTORM_SCSI
-#include "cyberstorm.h"
-#endif
-
-#ifdef CONFIG_CYBERSTORMII_SCSI
-#include "cyberstormII.h"
-#endif
-
-#ifdef CONFIG_BLZ2060_SCSI
-#include "blz2060.h"
-#endif
-
-#ifdef CONFIG_BLZ1230_SCSI
-#include "blz1230.h"
-#endif
-
-#ifdef CONFIG_FASTLANE_SCSI
-#include "fastlane.h"
-#endif
-
-#ifdef CONFIG_OKTAGON_SCSI
-#include "oktagon_esp.h"
-#endif
-
-#ifdef CONFIG_ATARI_SCSI
-#include "atari_scsi.h"
-#endif
-
-#if defined(CONFIG_MAC_SCSI) || defined(CONFIG_MAC_SCSI_OLD)
-#include "mac_scsi.h"
-#endif
-
-#ifdef CONFIG_SUN3_SCSI
-#include "sun3_scsi.h"
-#endif
-
-#ifdef CONFIG_SCSI_MAC_ESP
-#include "mac_esp.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_IPS
-#include "ips.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_FD_MCS
-#include "fd_mcs.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_QLOGIC_FC
-#include "qlogicfc.h"
-#endif
-
-#ifdef CONFIG_SCSI_QLOGIC_1280
-#include "qla1280.h"
-#endif
-
-#ifdef CONFIG_SCSI_SEAGATE
-#include "seagate.h"
-#endif
-
-#ifdef CONFIG_SCSI_T128
-#include "t128.h"
-#endif
-
-#ifdef CONFIG_SCSI_DMX3191D
-#include "dmx3191d.h"
-#endif
-
-#ifdef CONFIG_SCSI_DTC3280
-#include "dtc.h"
-#endif
-
-#ifdef CONFIG_SCSI_NCR53C7xx
-#include "53c7,8xx.h"
-#endif
-
-#ifdef CONFIG_SCSI_SYM53C8XX
-#include "sym53c8xx.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_MCA_53C9X
-#include "mca_53c9x.h"
-#endif
-
-#ifdef CONFIG_SCSI_IBMMCA
-#include "ibmmca.h"
-#endif
-
-#ifdef CONFIG_SCSI_EATA
-#include "eata.h"
-#endif
-
-#ifdef CONFIG_SCSI_NCR53C406A
-#include "NCR53c406a.h"
-#endif
-
-#ifdef CONFIG_SCSI_SYM53C416
-#include "sym53c416.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_ACARD
-#include "atp870u.h"
-#endif
-
-#ifdef CONFIG_SCSI_SUNESP
-#include "esp.h"
-#endif
-
-#ifdef CONFIG_SCSI_SGIWD93
-#include "sgiwd93.h"
-#endif
-
-#ifdef CONFIG_SCSI_QLOGICPTI
-#include "qlogicpti.h"
-#endif
-
-#ifdef CONFIG_BLK_DEV_IDESCSI
-#include "ide-scsi.h"
-#endif
-
-#ifdef CONFIG_SCSI_MESH
-#include "mesh.h"
-#endif
-
-#ifdef CONFIG_SCSI_MAC53C94
-#include "mac53c94.h"
-#endif
-
-#ifdef CONFIG_SCSI_GDTH
-#include "gdth.h"
-#endif
-
-#ifdef CONFIG_SCSI_PCI2000
-#include "pci2000.h"
-#endif
-
-#ifdef CONFIG_SCSI_PCI2220I
-#include "pci2220i.h"
-#endif
-
-#ifdef CONFIG_SCSI_PSI240I
-#include "psi240i.h"
-#endif
-
-#ifdef CONFIG_SCSI_PLUTO
-#include "pluto.h"
-#endif
-
-#ifdef CONFIG_SCSI_INITIO
-#include "ini9100u.h"
-#endif
-
-#ifdef CONFIG_SCSI_INIA100
-#include "inia100.h"
-#endif
-
-#ifdef CONFIG_SCSI_DEBUG
-#include "scsi_debug.h"
-#endif
-
-#ifdef CONFIG_SCSI_ACORNSCSI_3
-#include "../acorn/scsi/acornscsi.h"
-#endif
-
-#ifdef CONFIG_SCSI_CUMANA_1
-#include "../acorn/scsi/cumana_1.h"
-#endif
-
-#ifdef CONFIG_SCSI_CUMANA_2
-#include "../acorn/scsi/cumana_2.h"
-#endif
-
-#ifdef CONFIG_SCSI_ECOSCSI
-#include "../acorn/scsi/ecoscsi.h"
-#endif
-
-#ifdef CONFIG_SCSI_OAK1
-#include "../acorn/scsi/oak.h"
-#endif
-
-#ifdef CONFIG_SCSI_POWERTECSCSI
-#include "../acorn/scsi/powertec.h"
-#endif
-
-#ifdef CONFIG_SCSI_ARXESCSI
-#include "../acorn/scsi/arxescsi.h"
-#endif
-
-#ifdef CONFIG_I2O_SCSI
-#include "../i2o/i2o_scsi.h"
-#endif
-
-#ifdef CONFIG_JAZZ_ESP
-#include "jazz_esp.h"
-#endif
-
-#ifdef CONFIG_SCSI_DECNCR
-#include "dec_esp.h"
-#endif
-
-#ifdef CONFIG_SUN3X_ESP
-#include "sun3x_esp.h"
-#endif
-
-#ifdef CONFIG_IPHASE5526
-#include "../net/fc/iph5526_scsi.h"
-#endif
-
-#ifdef CONFIG_BLK_DEV_3W_XXXX_RAID
-#include "3w-xxxx.h"
-#endif
-
-/*
- * Moved ppa driver to the end of the probe list
- * since it is a removable host adapter.
- * This means the parallel ZIP drive will not bump
- * the order of the /dev/sd devices - campbell@torque.net
- */
-#ifdef CONFIG_SCSI_PPA
-#include "ppa.h"
-#endif
-
-#ifdef CONFIG_SCSI_IMM
-#include "imm.h"
-#endif
-
/*
static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/hosts.c,v 1.20 1996/12/12 19:18:32 davem Exp $";
*/
@@ -422,281 +71,6 @@ static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/hosts.c,v
Scsi_Host_Template * scsi_hosts = NULL;
-static Scsi_Host_Template builtin_scsi_hosts[] =
-{
-#ifdef CONFIG_AMIGA
-#ifdef AMIGA7XXCONFIG
- AMIGA7XX_SCSI,
-#endif
-#ifdef CONFIG_A3000_SCSI
- A3000_SCSI,
-#endif
-#ifdef CONFIG_A2091_SCSI
- A2091_SCSI,
-#endif
-#ifdef CONFIG_GVP11_SCSI
- GVP11_SCSI,
-#endif
-#ifdef CONFIG_CYBERSTORM_SCSI
- SCSI_CYBERSTORM,
-#endif
-#ifdef CONFIG_CYBERSTORMII_SCSI
- SCSI_CYBERSTORMII,
-#endif
-#ifdef CONFIG_BLZ2060_SCSI
- SCSI_BLZ2060,
-#endif
-#ifdef CONFIG_BLZ1230_SCSI
- SCSI_BLZ1230,
-#endif
-#ifdef CONFIG_FASTLANE_SCSI
- SCSI_FASTLANE,
-#endif
-#ifdef CONFIG_OKTAGON_SCSI
- SCSI_OKTAGON_ESP,
-#endif
-#endif
-
-#ifdef CONFIG_ATARI
-#ifdef CONFIG_ATARI_SCSI
- ATARI_SCSI,
-#endif
-#endif
-
-#ifdef CONFIG_MAC
-#ifdef CONFIG_MAC_SCSI_OLD
- MAC_SCSI,
-#endif
-#ifdef CONFIG_SCSI_MAC_ESP
- SCSI_MAC_ESP,
-#endif
-#ifdef CONFIG_MAC_SCSI
- MAC_NCR5380,
-#endif
-#endif
-
-#ifdef CONFIG_SUN3_SCSI
- SUN3_NCR5380,
-#endif
-
-#ifdef CONFIG_MVME16x_SCSI
- MVME16x_SCSI,
-#endif
-#ifdef CONFIG_BVME6000_SCSI
- BVME6000_SCSI,
-#endif
-#ifdef CONFIG_SCSI_SIM710
- SIM710_SCSI,
-#endif
-#ifdef CONFIG_SCSI_ADVANSYS
- ADVANSYS,
-#endif
-
-#ifdef CONFIG_SCSI_PCI2000
- PCI2000,
-#endif
-#ifdef CONFIG_SCSI_PCI2220I
- PCI2220I,
-#endif
-#ifdef CONFIG_SCSI_PSI240I
- PSI240I,
-#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_IPS
- IPS,
-#endif
-#ifdef CONFIG_SCSI_FD_MCS
- FD_MCS,
-#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_SYM53C416
- SYM53C416,
-#endif
-#ifdef CONFIG_SCSI_QLOGIC_FAS
- QLOGICFAS,
-#endif
-#ifdef CONFIG_SCSI_QLOGIC_ISP
- QLOGICISP,
-#endif
-#ifdef CONFIG_SCSI_QLOGIC_FC
- QLOGICFC,
-#endif
-#ifdef CONFIG_SCSI_QLOGIC_1280
- QLA1280_LINUX_TEMPLATE,
-#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_DMX3191D
- DMX3191D,
-#endif
-#ifdef CONFIG_SCSI_DTC3280
- DTC3x80,
-#endif
-#ifdef CONFIG_SCSI_NCR53C7xx
- NCR53c7xx,
-#endif
-#ifdef CONFIG_SCSI_SYM53C8XX
- SYM53C8XX,
-#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_MCA_53C9X
- MCA_53C9X,
-#endif
-#ifdef CONFIG_SCSI_IBMMCA
- IBMMCA,
-#endif
-#ifdef CONFIG_SCSI_EATA
- EATA,
-#endif
-#ifdef CONFIG_SCSI_DC390T
- DC390_T,
-#endif
-#ifdef CONFIG_SCSI_AM53C974
- AM53C974,
-#endif
-#ifdef CONFIG_SCSI_MEGARAID
- MEGARAID,
-#endif
-#ifdef CONFIG_SCSI_ACARD
- ATP870U,
-#endif
-#ifdef CONFIG_SCSI_SUNESP
- SCSI_SPARC_ESP,
-#endif
-#ifdef CONFIG_SCSI_GDTH
- GDTH,
-#endif
-#ifdef CONFIG_SCSI_INITIO
- INI9100U,
-#endif
-#ifdef CONFIG_SCSI_INIA100
- INIA100,
-#endif
-#ifdef CONFIG_SCSI_QLOGICPTI
- QLOGICPTI,
-#endif
-#ifdef CONFIG_BLK_DEV_IDESCSI
- IDESCSI,
-#endif
-#ifdef CONFIG_SCSI_MESH
- SCSI_MESH,
-#endif
-#ifdef CONFIG_SCSI_MAC53C94
- SCSI_MAC53C94,
-#endif
-#ifdef CONFIG_SCSI_PLUTO
- PLUTO,
-#endif
-#ifdef CONFIG_ARCH_ACORN
-#ifdef CONFIG_SCSI_ACORNSCSI_3
- ACORNSCSI_3,
-#endif
-#ifdef CONFIG_SCSI_CUMANA_1
- CUMANA_NCR5380,
-#endif
-#ifdef CONFIG_SCSI_CUMANA_2
- CUMANA_FAS216,
-#endif
-#ifdef CONFIG_SCSI_ARXESCSI
- ARXEScsi,
-#endif
-#ifdef CONFIG_SCSI_ECOSCSI
- ECOSCSI_NCR5380,
-#endif
-#ifdef CONFIG_SCSI_OAK1
- OAK_NCR5380,
-#endif
-#ifdef CONFIG_SCSI_POWERTECSCSI
- POWERTECSCSI,
-#endif
-#endif
-#ifdef CONFIG_IPHASE5526
- IPH5526_SCSI_FC,
-#endif
-#ifdef CONFIG_SCSI_DECNCR
- SCSI_DEC_ESP,
-#endif
-#ifdef CONFIG_BLK_DEV_3W_XXXX_RAID
- TWXXXX,
-#endif
-/* Put I2O last so that host specific controllers always win */
-#ifdef CONFIG_I2O_SCSI
- I2OSCSI,
-#endif
-/* "Removable host adapters" below this line (Parallel Port/USB/other) */
-#ifdef CONFIG_SCSI_PPA
- PPA,
-#endif
-#ifdef CONFIG_SCSI_IMM
- IMM,
-#endif
-#ifdef CONFIG_SCSI_SGIWD93
- SGIWD93_SCSI,
-#endif
-#ifdef CONFIG_JAZZ_ESP
- SCSI_JAZZ_ESP,
-#endif
-#ifdef CONFIG_SUN3X_ESP
- SCSI_SUN3X_ESP,
-#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
@@ -771,12 +145,12 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
hname = (tpnt->proc_name) ? tpnt->proc_name : "";
hname_len = strlen(hname);
for (shn = scsi_host_no_list;shn;shn = shn->next) {
- if (!(shn->host_registered) && shn->loaded_as_module &&
+ if (!(shn->host_registered) &&
(hname_len > 0) && (0 == strncmp(hname, shn->name, hname_len))) {
flag_new = 0;
retval->host_no = shn->host_no;
shn->host_registered = 1;
- shn->loaded_as_module = scsi_loadable_module_flag;
+ shn->loaded_as_module = 1;
break;
}
}
@@ -785,7 +159,7 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
retval->host_failed = 0;
if(j > 0xffff) panic("Too many extra bytes requested\n");
retval->extra_bytes = j;
- retval->loaded_as_module = scsi_loadable_module_flag;
+ retval->loaded_as_module = 1;
if (flag_new) {
shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC);
shn->name = kmalloc(hname_len + 1, GFP_ATOMIC);
@@ -794,7 +168,7 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
shn->name[hname_len] = 0;
shn->host_no = max_scsi_hosts++;
shn->host_registered = 1;
- shn->loaded_as_module = scsi_loadable_module_flag;
+ shn->loaded_as_module = 1;
shn->next = NULL;
if (scsi_host_no_list) {
for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next)
@@ -890,135 +264,6 @@ scsi_register_device(struct Scsi_Device_Template * sdpnt)
}
/*
- * Why is this a separate function? Because the kernel_thread code
- * effectively does a fork, and there is a builtin exit() call when
- * the child returns. The difficulty is that scsi_init() is
- * marked __init, which means the memory is unmapped after bootup
- * is complete, which means that the thread's exit() call gets wiped.
- *
- * The lesson is to *NEVER*, *NEVER* call kernel_thread() from an
- * __init function, if that function could ever return.
- */
-static void launch_error_handler_thread(struct Scsi_Host * shpnt)
-{
- DECLARE_MUTEX_LOCKED(sem);
-
- shpnt->eh_notify = &sem;
-
- kernel_thread((int (*)(void *))scsi_error_handler,
- (void *) shpnt, 0);
-
- /*
- * Now wait for the kernel error thread to initialize itself
- * as it might be needed when we scan the bus.
- */
- down (&sem);
- shpnt->eh_notify = NULL;
-}
-
-unsigned int __init scsi_init(void)
-{
- static int called = 0;
- int i, pcount;
- unsigned long flags;
- 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) {
-
- /* The detect routine must carefully spinunlock/spinlock if
- it enables interrupts, since all interrupt handlers do
- spinlock as well.
- All lame drivers are going to fail due to the following
- spinlock. For the time beeing let's use it only for drivers
- using the new scsi code. NOTE: the detect routine could
- redefine the value tpnt->use_new_eh_code. (DB, 13 May 1998) */
-
- if (tpnt->use_new_eh_code) {
- spin_lock_irqsave(&io_request_lock, flags);
- tpnt->present = tpnt->detect(tpnt);
- spin_unlock_irqrestore(&io_request_lock, flags);
- }
- else
- tpnt->present = tpnt->detect(tpnt);
-
- }
-
- if (tpnt->detect && tpnt->present)
- {
- /* 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);
-
- /*
- * Now start the error recovery thread for the host.
- */
- if( shpnt->hostt->use_new_eh_code )
- {
- launch_error_handler_thread(shpnt);
- }
- }
-
- printk ("scsi : %d host%s.\n", next_scsi_host,
- (next_scsi_host == 1) ? "" : "s");
-
- /* 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
diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
index f7e546fa6..7d107541e 100644
--- a/drivers/scsi/hosts.h
+++ b/drivers/scsi/hosts.h
@@ -459,7 +459,6 @@ extern void build_proc_dir_entries(Scsi_Host_Template *);
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);
@@ -506,11 +505,6 @@ struct Scsi_Device_Template
void scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt);
-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 */
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
index aa4cca390..3e425043a 100644
--- a/drivers/scsi/ibmmca.c
+++ b/drivers/scsi/ibmmca.c
@@ -3294,12 +3294,10 @@ static int option_setup(char *str)
__setup("ibmmcascsi=", option_setup);
#endif
-#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = IBMMCA;
+static Scsi_Host_Template driver_template = IBMMCA;
#include "scsi_module.c"
-#endif
/*--------------------------------------------------------------------*/
diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
index 832a2d9f4..a6f4f74ca 100644
--- a/drivers/scsi/ide-scsi.c
+++ b/drivers/scsi/ide-scsi.c
@@ -31,7 +31,6 @@
#define IDESCSI_VERSION "0.9"
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
@@ -582,16 +581,6 @@ int idescsi_init (void)
failed = 0;
while ((drive = ide_scan_devices (media[i], idescsi_driver.name, NULL, failed++)) != NULL) {
-#ifndef CONFIG_BLK_DEV_IDETAPE
- /*
- * The Onstream DI-30 does not handle clean emulation, yet.
- */
- if (strstr(drive->id->model, "OnStream DI-30")) {
- printk("ide-tape: ide-scsi emulation is not supported for %s.\n", drive->id->model);
- continue;
- }
-#endif /* CONFIG_BLK_DEV_IDETAPE */
-
if ((scsi = (idescsi_scsi_t *) kmalloc (sizeof (idescsi_scsi_t), GFP_KERNEL)) == NULL) {
printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name);
continue;
@@ -827,18 +816,17 @@ int idescsi_bios (Disk *disk, kdev_t dev, int *parm)
return 0;
}
-#ifdef MODULE
-Scsi_Host_Template idescsi_template = IDESCSI;
+static Scsi_Host_Template idescsi_template = IDESCSI;
-int init_module (void)
+static int __init init_idescsi_module(void)
{
- idescsi_init ();
- idescsi_template.module = &__this_module;
+ idescsi_init();
+ idescsi_template.module = THIS_MODULE;
scsi_register_module (MODULE_SCSI_HA, &idescsi_template);
return 0;
}
-void cleanup_module (void)
+static void __exit exit_idescsi_module(void)
{
ide_drive_t *drive;
byte media[] = {TYPE_DISK, TYPE_TAPE, TYPE_PROCESSOR, TYPE_WORM, TYPE_ROM, TYPE_SCANNER, TYPE_MOD, 255};
@@ -855,4 +843,6 @@ void cleanup_module (void)
}
ide_unregister_module(&idescsi_module);
}
-#endif /* MODULE */
+
+module_init(init_idescsi_module);
+module_exit(exit_idescsi_module);
diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c
index effaf3ec1..2f0c1a185 100644
--- a/drivers/scsi/imm.c
+++ b/drivers/scsi/imm.c
@@ -114,10 +114,8 @@ static int imm_pb_claim(int host_no)
* Parallel port probing routines *
***************************************************************************/
-#ifdef MODULE
-Scsi_Host_Template driver_template = IMM;
+static Scsi_Host_Template driver_template = IMM;
#include "scsi_module.c"
-#endif
int imm_detect(Scsi_Host_Template * host)
{
diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c
index 1b1ff4371..4453fdab7 100644
--- a/drivers/scsi/in2000.c
+++ b/drivers/scsi/in2000.c
@@ -2359,11 +2359,6 @@ static int stop = 0;
}
-#ifdef MODULE
-
-Scsi_Host_Template driver_template = IN2000;
-
+static Scsi_Host_Template driver_template = IN2000;
#include "scsi_module.c"
-#endif
-
diff --git a/drivers/scsi/ini9100u.c b/drivers/scsi/ini9100u.c
index 4b903ea98..fa03acae8 100644
--- a/drivers/scsi/ini9100u.c
+++ b/drivers/scsi/ini9100u.c
@@ -143,10 +143,8 @@
unsigned int i91u_debug = DEBUG_DEFAULT;
#endif
-#ifdef MODULE
-Scsi_Host_Template driver_template = INI9100U;
+static Scsi_Host_Template driver_template = INI9100U;
#include "scsi_module.c"
-#endif
char *i91uCopyright = "Copyright (C) 1996-98";
char *i91uInitioName = "by Initio Corporation";
diff --git a/drivers/scsi/inia100.c b/drivers/scsi/inia100.c
index 984d9b6db..5d0efa845 100644
--- a/drivers/scsi/inia100.c
+++ b/drivers/scsi/inia100.c
@@ -92,10 +92,8 @@
#include <linux/malloc.h>
#include "inia100.h"
-#ifdef MODULE
-Scsi_Host_Template driver_template = INIA100;
+static Scsi_Host_Template driver_template = INIA100;
#include "scsi_module.c"
-#endif
#define ORC_RDWORD(x,y) (short)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) ))
diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c
index 9ebe9e927..8630e8ddf 100644
--- a/drivers/scsi/ips.c
+++ b/drivers/scsi/ips.c
@@ -78,17 +78,40 @@
/* - Sync with other changes from the 2.3 kernels */
/* 4.00.06 - Fix timeout with initial FFDC command */
/* 4.00.06a - Port to 2.4 (trivial) -- Christoph Hellwig <hch@caldera.de> */
+/* 4.10.00 - Add support for ServeRAID 4M/4L */
+/* 4.10.13 - Fix for dynamic unload and proc file system */
+/* 4.20.03 - Rename version to coincide with new release schedules */
+/* Performance fixes */
+/* Fix truncation of /proc files with cat */
+/* Merge in changes through kernel 2.4.0test1ac21 */
+/* 4.20.13 - Fix some failure cases / reset code */
+/* - Hook into the reboot_notifier to flush the controller cache */
/* */
/*****************************************************************************/
/*
* Conditional Compilation directives for this driver:
*
- * NO_IPS_RESET - Don't reset the controller (no matter what)
- * IPS_DEBUG - More verbose error messages
- * IPS_PCI_PROBE_DEBUG - Print out more detail on the PCI probe
+ * IPS_DEBUG - Turn on debugging info
*
+ *
+ * Parameters:
+ *
+ * debug:<number> - Set debug level to <number>
+ * NOTE: only works when IPS_DEBUG compile directive
+ * is used.
+ *
+ * 1 - Normal debug messages
+ * 2 - Verbose debug messages
+ * 11 - Method trace (non interrupt)
+ * 12 - Method trace (includes interrupt)
+ *
+ * noreset - Don't reset the controller
+ * nocmdline - Turn off passthru support
+ * noi2o - Don't use I2O Queues (ServeRAID 4 only)
+ * nommap - Don't use memory mapped I/O
*/
+
#include <linux/module.h>
@@ -101,10 +124,12 @@
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/malloc.h>
+#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
+#include <linux/reboot.h>
#include <linux/blk.h>
#include <linux/types.h>
@@ -127,25 +152,29 @@
#include <asm/spinlock.h>
#endif
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+#include <linux/init.h>
+#endif
+
#include <linux/smp.h>
+#ifdef MODULE
+ static char *ips = NULL;
+ MODULE_PARM(ips, "s");
+#endif
+
/*
* DRIVER_VER
*/
-#define IPS_VERSION_HIGH "4.00" /* MUST be 4 chars */
-#define IPS_VERSION_LOW ".06 " /* MUST be 4 chars */
+#define IPS_VERSION_HIGH "4.20"
+#define IPS_VERSION_LOW ".20 "
#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
struct proc_dir_entry proc_scsi_ips = {
-#if !defined(PROC_SCSI_IPS)
- 0, /* Use dynamic inode allocation */
-#else
- PROC_SCSI_IPS,
-#endif
+ 0,
3, "ips",
S_IFDIR | S_IRUGO | S_IXUGO, 2
-}
-;
+};
#endif
#if !defined(__i386__)
@@ -160,12 +189,14 @@ struct proc_dir_entry proc_scsi_ips = {
#error "To use the command-line interface you need to define SG_BIG_BUFF"
#endif
-#if IPS_DEBUG >= 12
- #define DBG(s) printk(KERN_NOTICE s "\n"); MDELAY(2*IPS_ONE_SEC)
-#elif IPS_DEBUG >= 11
- #define DBG(s) printk(KERN_NOTICE s "\n")
+#ifdef IPS_DEBUG
+ #define METHOD_TRACE(s, i) if (ips_debug >= (i+10)) printk(KERN_NOTICE s "\n");
+ #define DEBUG(i, s) if (ips_debug >= i) printk(KERN_NOTICE s "\n");
+ #define DEBUG_VAR(i, s, v...) if (ips_debug >= i) printk(KERN_NOTICE s "\n", v);
#else
- #define DBG(s)
+ #define METHOD_TRACE(s, i)
+ #define DEBUG(i, s)
+ #define DEBUG_VAR(i, s, v...)
#endif
/*
@@ -174,11 +205,26 @@ struct proc_dir_entry proc_scsi_ips = {
static const char * ips_name = "ips";
static struct Scsi_Host * ips_sh[IPS_MAX_ADAPTERS]; /* Array of host controller structures */
static ips_ha_t * ips_ha[IPS_MAX_ADAPTERS]; /* Array of HA structures */
+static unsigned int ips_next_controller = 0;
static unsigned int ips_num_controllers = 0;
+static unsigned int ips_released_controllers = 0;
static int ips_cmd_timeout = 60;
static int ips_reset_timeout = 60 * 5;
+static int ips_force_memio = 1; /* Always use Memory Mapped I/O */
+static int ips_force_i2o = 1; /* Always use I2O command delivery */
+static int ips_resetcontroller = 1; /* Reset the controller */
+static int ips_cmdline = 1; /* Support for passthru */
-#define MAX_ADAPTER_NAME 7
+#ifdef IPS_DEBUG
+static int ips_debug = 0; /* Debug mode */
+#endif
+
+/*
+ * Necessary forward function protoypes
+ */
+static int ips_halt(struct notifier_block *nb, ulong event, void *buf);
+
+#define MAX_ADAPTER_NAME 9
static char ips_adapter_name[][30] = {
"ServeRAID",
@@ -187,7 +233,13 @@ static char ips_adapter_name[][30] = {
"ServeRAID on motherboard",
"ServeRAID 3H",
"ServeRAID 3L",
- "ServeRAID 4H"
+ "ServeRAID 4H",
+ "ServeRAID 4M",
+ "ServeRAID 4L"
+};
+
+static struct notifier_block ips_notifier = {
+ ips_halt, NULL, 0
};
/*
@@ -252,8 +304,6 @@ IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK, IPS_DATA_UNK
*/
int ips_detect(Scsi_Host_Template *);
int ips_release(struct Scsi_Host *);
-int ips_abort(Scsi_Cmnd *);
-int ips_reset(Scsi_Cmnd *, unsigned int);
int ips_eh_abort(Scsi_Cmnd *);
int ips_eh_reset(Scsi_Cmnd *);
int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *));
@@ -261,21 +311,26 @@ int ips_biosparam(Disk *, kdev_t, int *);
const char * ips_info(struct Scsi_Host *);
void do_ipsintr(int, void *, struct pt_regs *);
static int ips_hainit(ips_ha_t *);
-static int ips_map_status(ips_scb_t *, ips_stat_t *);
+static int ips_map_status(ips_ha_t *, ips_scb_t *, ips_stat_t *);
static int ips_send(ips_ha_t *, ips_scb_t *, ips_scb_callback);
static int ips_send_wait(ips_ha_t *, ips_scb_t *, int, int);
static int ips_send_cmd(ips_ha_t *, ips_scb_t *);
-static int ips_chkstatus(ips_ha_t *);
static int ips_online(ips_ha_t *, ips_scb_t *);
static int ips_inquiry(ips_ha_t *, ips_scb_t *);
static int ips_rdcap(ips_ha_t *, ips_scb_t *);
static int ips_msense(ips_ha_t *, ips_scb_t *);
static int ips_reqsen(ips_ha_t *, ips_scb_t *);
static int ips_allocatescbs(ips_ha_t *);
-static int ips_reset_adapter(ips_ha_t *);
-static int ips_statupd(ips_ha_t *);
-static int ips_issue(ips_ha_t *, ips_scb_t *);
-static int ips_isintr(ips_ha_t *);
+static int ips_reset_copperhead(ips_ha_t *);
+static int ips_reset_copperhead_memio(ips_ha_t *);
+static int ips_reset_morpheus(ips_ha_t *);
+static int ips_issue_copperhead(ips_ha_t *, ips_scb_t *);
+static int ips_issue_copperhead_memio(ips_ha_t *, ips_scb_t *);
+static int ips_issue_i2o(ips_ha_t *, ips_scb_t *);
+static int ips_issue_i2o_memio(ips_ha_t *, ips_scb_t *);
+static int ips_isintr_copperhead(ips_ha_t *);
+static int ips_isintr_copperhead_memio(ips_ha_t *);
+static int ips_isintr_morpheus(ips_ha_t *);
static int ips_wait(ips_ha_t *, int, int);
static int ips_write_driver_status(ips_ha_t *, int);
static int ips_read_adapter_status(ips_ha_t *, int);
@@ -283,7 +338,22 @@ static int ips_read_subsystem_parameters(ips_ha_t *, int);
static int ips_read_config(ips_ha_t *, int);
static int ips_clear_adapter(ips_ha_t *, int);
static int ips_readwrite_page5(ips_ha_t *, int, int);
-static void ips_intr(ips_ha_t *);
+static int ips_init_copperhead(ips_ha_t *);
+static int ips_init_copperhead_memio(ips_ha_t *);
+static int ips_init_morpheus(ips_ha_t *);
+static int ips_isinit_copperhead(ips_ha_t *);
+static int ips_isinit_copperhead_memio(ips_ha_t *);
+static int ips_isinit_morpheus(ips_ha_t *);
+static u32 ips_statupd_copperhead(ips_ha_t *);
+static u32 ips_statupd_copperhead_memio(ips_ha_t *);
+static u32 ips_statupd_morpheus(ips_ha_t *);
+static void ips_select_queue_depth(struct Scsi_Host *, Scsi_Device *);
+static void ips_chkstatus(ips_ha_t *, IPS_STATUS *);
+static void ips_enable_int_copperhead(ips_ha_t *);
+static void ips_enable_int_copperhead_memio(ips_ha_t *);
+static void ips_enable_int_morpheus(ips_ha_t *);
+static void ips_intr_copperhead(ips_ha_t *);
+static void ips_intr_morpheus(ips_ha_t *);
static void ips_next(ips_ha_t *, int);
static void ipsintr_blocking(ips_ha_t *, struct ips_scb *);
static void ipsintr_done(ips_ha_t *, struct ips_scb *);
@@ -292,6 +362,7 @@ static void ips_free(ips_ha_t *);
static void ips_init_scb(ips_ha_t *, ips_scb_t *);
static void ips_freescb(ips_ha_t *, ips_scb_t *);
static void ips_statinit(ips_ha_t *);
+static void ips_statinit_memio(ips_ha_t *);
static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time_t);
static void ips_ffdc_reset(ips_ha_t *, int);
static void ips_ffdc_time(ips_ha_t *, int);
@@ -311,6 +382,9 @@ static inline ips_copp_wait_item_t * ips_removeq_copp_head(ips_copp_queue_t *);
static int ips_erase_bios(ips_ha_t *);
static int ips_program_bios(ips_ha_t *, char *, int);
static int ips_verify_bios(ips_ha_t *, char *, int);
+static int ips_erase_bios_memio(ips_ha_t *);
+static int ips_program_bios_memio(ips_ha_t *, char *, int);
+static int ips_verify_bios_memio(ips_ha_t *, char *, int);
#ifndef NO_IPS_CMDLINE
static int ips_is_passthru(Scsi_Cmnd *);
@@ -331,6 +405,76 @@ static int copy_info(IPS_INFOSTR *, char *, ...);
/****************************************************************************/
/* */
+/* Routine Name: ips_setup */
+/* */
+/* Routine Description: */
+/* */
+/* setup parameters to the driver */
+/* */
+/****************************************************************************/
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+static int
+ips_setup(char *ips_str) {
+#else
+void
+ips_setup(char *ips_str, int *dummy) {
+#endif
+ int i;
+ char *p;
+ char *key;
+ char *value;
+ char tokens[3] = {',', '.', 0};
+ IPS_OPTION options[] = {
+ {"noreset", &ips_resetcontroller, 0},
+#ifdef IPS_DEBUG
+ {"debug", &ips_debug, 1},
+#endif
+ {"noi2o", &ips_force_i2o, 0},
+ {"nommap", &ips_force_memio, 0},
+ {"nocmdline", &ips_cmdline, 0},
+ };
+
+ METHOD_TRACE("ips_setup", 1);
+
+ for (key = strtok(ips_str, tokens); key; key = strtok(NULL, tokens)) {
+ p = key;
+
+ /* Search for value */
+ while ((p) && (*p != ':'))
+ p++;
+
+ if (p) {
+ *p = '\0';
+ value = p+1;
+ } else
+ value = NULL;
+
+ /*
+ * We now have key/value pairs.
+ * Update the variables
+ */
+ for (i = 0; i < (sizeof(options) / sizeof(options[0])); i++) {
+ if (strnicmp(key, options[i].option_name, strlen(ips_str)) == 0) {
+ if (value)
+ *options[i].option_flag = simple_strtoul(value, NULL, 0);
+ else
+ *options[i].option_flag = options[i].option_value;
+
+ break;
+ }
+ }
+ }
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+ return (1);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+__setup("ips=", ips_setup);
+#endif
+
+/****************************************************************************/
+/* */
/* Routine Name: ips_detect */
/* */
/* Routine Description: */
@@ -345,15 +489,38 @@ ips_detect(Scsi_Host_Template *SHT) {
struct Scsi_Host *sh;
ips_ha_t *ha;
u32 io_addr;
+ u32 mem_addr;
+ u32 io_len;
+ u32 mem_len;
u16 planer;
u8 revision_id;
u8 bus;
u8 func;
u8 irq;
- int index;
- struct pci_dev *dev = NULL;
+ u16 deviceID[2];
+ int i;
+ int j;
+ char *ioremap_ptr;
+ char *mem_ptr;
+ struct pci_dev *dev[2];
+ struct pci_dev *morpheus = NULL;
+ struct pci_dev *trombone = NULL;
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,14)
+ u32 currbar;
+ u32 maskbar;
+ u8 barnum;
+#endif
- DBG("ips_detect");
+ METHOD_TRACE("ips_detect", 1);
+
+#ifdef MODULE
+ if (ips)
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,13)
+ ips_setup(ips);
+#else
+ ips_setup(ips, NULL);
+#endif
+#endif
SHT->proc_info = ips_proc_info;
#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
@@ -363,226 +530,524 @@ ips_detect(Scsi_Host_Template *SHT) {
#endif
#if defined(CONFIG_PCI)
-
+
/* initalize number of controllers */
ips_num_controllers = 0;
-
+ ips_next_controller = 0;
+ ips_released_controllers = 0;
+
if (!pci_present())
return (0);
- for (index = 0; index < IPS_MAX_ADAPTERS; index++) {
+ morpheus = pci_find_device(IPS_VENDORID, IPS_MORPHEUS_DEVICEID, morpheus);
+ trombone = pci_find_device(IPS_VENDORID, IPS_COPPERHEAD_DEVICEID, trombone);
+
+ /* determine which controller to probe first */
+ if (!morpheus) {
+ /* we only have trombone */
+ dev[0] = trombone;
+ dev[1] = NULL;
+ deviceID[0] = IPS_COPPERHEAD_DEVICEID;
+ } else if (!trombone) {
+ /* we only have morpheus */
+ dev[0] = morpheus;
+ dev[1] = NULL;
+ deviceID[0] = IPS_MORPHEUS_DEVICEID;
+ } else {
+ /* we have both in the system */
+ if (trombone->bus < morpheus->bus) {
+ dev[0] = trombone;
+ dev[1] = morpheus;
+ deviceID[0] = IPS_COPPERHEAD_DEVICEID;
+ deviceID[1] = IPS_MORPHEUS_DEVICEID;
+ } else if (trombone->bus > morpheus->bus) {
+ dev[0] = morpheus;
+ dev[1] = trombone;
+ deviceID[0] = IPS_MORPHEUS_DEVICEID;
+ deviceID[1] = IPS_COPPERHEAD_DEVICEID;
+ } else {
+ /* further detection required */
+ if (trombone->devfn < morpheus->devfn) {
+ dev[0] = trombone;
+ dev[1] = morpheus;
+ deviceID[0] = IPS_COPPERHEAD_DEVICEID;
+ deviceID[1] = IPS_MORPHEUS_DEVICEID;
+ } else {
+ dev[0] = morpheus;
+ dev[1] = trombone;
+ deviceID[0] = IPS_MORPHEUS_DEVICEID;
+ deviceID[1] = IPS_COPPERHEAD_DEVICEID;
+ }
+ }
+ }
- if (!(dev = pci_find_device(IPS_VENDORID, IPS_DEVICEID, dev)))
+ /* Now scan the controllers */
+ for (i = 0; i < 2; i++) {
+ if (!dev[i])
break;
- if (pci_enable_device(dev))
- break;
-
- /* stuff that we get in dev */
- irq = dev->irq;
- bus = dev->bus->number;
- func = dev->devfn;
- io_addr = pci_resource_start(dev, 0);
-
- /* check I/O address */
- if (pci_resource_flags(dev, 0) & IORESOURCE_MEM)
- continue;
+ do {
+ if (ips_next_controller >= IPS_MAX_ADAPTERS)
+ break;
- /* get planer status */
- if (pci_read_config_word(dev, 0x04, &planer)) {
- printk(KERN_WARNING "(%s%d) can't get planer status.\n",
- ips_name, index);
- continue;
- }
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
+ if (pci_enable_device(dev[i]))
+ break;
+#endif
- /* check to see if an onboard planer controller is disabled */
- if (!(planer & 0x000C)) {
+ /* stuff that we get in dev */
+ irq = dev[i]->irq;
+ bus = dev[i]->bus->number;
+ func = dev[i]->devfn;
- #ifdef IPS_PCI_PROBE_DEBUG
- printk(KERN_NOTICE "(%s%d) detect, Onboard ServeRAID disabled by BIOS\n",
- ips_name, index);
- #endif
+ /* Init MEM/IO addresses to 0 */
+ mem_addr = 0;
+ io_addr = 0;
+ mem_len = 0;
+ io_len = 0;
- continue;
- }
+ for (j = 0; j < 2; j++) {
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
+ if (!pci_resource_start(dev[i], j))
+ break;
- #ifdef IPS_PCI_PROBE_DEBUG
- printk(KERN_NOTICE "(%s%d) detect bus %d, func %x, irq %d, io %x\n",
- ips_name, index, bus, func, irq, io_addr);
- #endif
+ if (pci_resource_flags(dev[i], j) & IORESOURCE_IO) {
+ io_addr = pci_resource_start(dev[i], j);
+ io_len = pci_resource_len(dev[i], j);
+ } else {
+ mem_addr = pci_resource_start(dev[i], j);
+ mem_len = pci_resource_len(dev[i], j);
+ }
+#elif LINUX_VERSION_CODE >= LinuxVersionCode(2,3,14)
+ if (!dev[i]->resource[j].start)
+ break;
- /* get the revision ID */
- if (pci_read_config_byte(dev, 0x08, &revision_id)) {
- printk(KERN_WARNING "(%s%d) can't get revision id.\n",
- ips_name, index);
+ if ((dev[i]->resource[j].start & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+ io_addr = dev[i]->resource[j].start;
+ io_len = dev[i]->resource[j].end - dev[i]->resource[j].start + 1;
+ } else {
+ mem_addr = dev[i]->resource[j].start;
+ mem_len = dev[i]->resource[j].end - dev[i]->resource[j].start + 1;
+ }
+#else
+ if (!dev[i]->base_address[j])
+ break;
- continue;
- }
+ if ((dev[i]->base_address[j] & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+ barnum = PCI_BASE_ADDRESS_0 + (j * 4);
+ io_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_IO_MASK;
- /* found a controller */
- sh = scsi_register(SHT, sizeof(ips_ha_t));
+ /* Get Size */
+ pci_read_config_dword(dev[i], barnum, &currbar);
+ pci_write_config_dword(dev[i], barnum, ~0);
+ pci_read_config_dword(dev[i], barnum, &maskbar);
+ pci_write_config_dword(dev[i], barnum, currbar);
- if (sh == NULL) {
- printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n",
- ips_name, index);
+ io_len = ~(maskbar & PCI_BASE_ADDRESS_IO_MASK) + 1;
+ } else {
+ barnum = PCI_BASE_ADDRESS_0 + (j * 4);
+ mem_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_MEM_MASK;
- continue;
- }
+ /* Get Size */
+ pci_read_config_dword(dev[i], barnum, &currbar);
+ pci_write_config_dword(dev[i], barnum, ~0);
+ pci_read_config_dword(dev[i], barnum, &maskbar);
+ pci_write_config_dword(dev[i], barnum, currbar);
- ha = IPS_HA(sh);
- memset(ha, 0, sizeof(ips_ha_t));
+ mem_len = ~(maskbar & PCI_BASE_ADDRESS_MEM_MASK) + 1;
+ }
+#endif
+ }
- /* Initialize spin lock */
- spin_lock_init(&ha->scb_lock);
- spin_lock_init(&ha->copp_lock);
- spin_lock_init(&ha->ips_lock);
- spin_lock_init(&ha->copp_waitlist.lock);
- spin_lock_init(&ha->scb_waitlist.lock);
- spin_lock_init(&ha->scb_activelist.lock);
+ /* setup memory mapped area (if applicable) */
+ if (mem_addr) {
+ u32 base;
+ u32 offs;
- ips_sh[ips_num_controllers] = sh;
- ips_ha[ips_num_controllers] = ha;
- ips_num_controllers++;
- ha->active = 1;
+ DEBUG_VAR(1, "(%s%d) detect, Memory region %x, size: %d",
+ ips_name, ips_next_controller, mem_addr, mem_len);
- ha->enq = kmalloc(sizeof(IPS_ENQ), GFP_KERNEL|GFP_DMA);
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17)
+ if (check_mem_region(mem_addr, mem_len)) {
+ /* Couldn't allocate io space */
+ printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n",
+ ips_name, ips_next_controller, io_addr, io_len);
- if (!ha->enq) {
- printk(KERN_WARNING "(%s%d) Unable to allocate host inquiry structure - skipping contoller\n",
- ips_name, index);
+ ips_next_controller++;
- ha->active = 0;
+ continue;
+ }
- continue;
- }
+ request_mem_region(mem_addr, mem_len, "ips");
+#endif
- ha->adapt = kmalloc(sizeof(IPS_ADAPTER), GFP_KERNEL|GFP_DMA);
+ base = mem_addr & PAGE_MASK;
+ offs = mem_addr - base;
- if (!ha->adapt) {
- printk(KERN_WARNING "(%s%d) Unable to allocate host adapt structure - skipping controller\n",
- ips_name, index);
+ ioremap_ptr = ioremap(base, PAGE_SIZE);
+ mem_ptr = ioremap_ptr + offs;
+ } else {
+ ioremap_ptr = NULL;
+ mem_ptr = NULL;
+ }
- ha->active = 0;
+ /* setup I/O mapped area (if applicable) */
+ if (io_addr) {
+ DEBUG_VAR(1, "(%s%d) detect, IO region %x, size: %d",
+ ips_name, ips_next_controller, io_addr, io_len);
- continue;
- }
+ if (check_region(io_addr, io_len)) {
+ /* Couldn't allocate io space */
+ printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n",
+ ips_name, ips_next_controller, io_addr, io_len);
- ha->conf = kmalloc(sizeof(IPS_CONF), GFP_KERNEL|GFP_DMA);
+ ips_next_controller++;
- if (!ha->conf) {
- printk(KERN_WARNING "(%s%d) Unable to allocate host conf structure - skipping controller\n",
- ips_name, index);
+ continue;
+ }
- ha->active = 0;
+ request_region(io_addr, io_len, "ips");
+ }
- continue;
- }
+ /* get planer status */
+ if (pci_read_config_word(dev[i], 0x04, &planer)) {
+ printk(KERN_WARNING "(%s%d) can't get planer status.\n",
+ ips_name, ips_next_controller);
- ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_KERNEL|GFP_DMA);
+ ips_next_controller++;
- if (!ha->nvram) {
- printk(KERN_WARNING "(%s%d) Unable to allocate host nvram structure - skipping controller\n",
- ips_name, index);
+ continue;
+ }
+
+ /* check to see if an onboard planer controller is disabled */
+ if (!(planer & 0x000C)) {
- ha->active = 0;
+ DEBUG_VAR(1, "(%s%d) detect, Onboard ServeRAID disabled by BIOS",
+ ips_name, ips_next_controller);
- continue;
- }
+ ips_next_controller++;
- ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_KERNEL|GFP_DMA);
+ continue;
+ }
- if (!ha->subsys) {
- printk(KERN_WARNING "(%s%d) Unable to allocate host subsystem structure - skipping controller\n",
- ips_name, index);
+ DEBUG_VAR(1, "(%s%d) detect bus %d, func %x, irq %d, io %x, mem: %x, ptr: %x",
+ ips_name, ips_next_controller, bus, func, irq, io_addr, mem_addr, (u32) mem_ptr);
- ha->active = 0;
+ /* get the revision ID */
+ if (pci_read_config_byte(dev[i], 0x08, &revision_id)) {
+ printk(KERN_WARNING "(%s%d) can't get revision id.\n",
+ ips_name, ips_next_controller);
- continue;
- }
+ ips_next_controller++;
- ha->dummy = kmalloc(sizeof(IPS_IO_CMD), GFP_KERNEL|GFP_DMA);
+ continue;
+ }
- if (!ha->dummy) {
- printk(KERN_WARNING "(%s%d) Unable to allocate host dummy structure - skipping controller\n",
- ips_name, index);
+ /* found a controller */
+ sh = scsi_register(SHT, sizeof(ips_ha_t));
- ha->active = 0;
+ if (sh == NULL) {
+ printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n",
+ ips_name, ips_next_controller);
- continue;
- }
+ ips_next_controller++;
- ha->ioctl_data = kmalloc(IPS_IOCTL_SIZE, GFP_KERNEL|GFP_DMA);
- ha->ioctl_datasize = IPS_IOCTL_SIZE;
- if (!ha->ioctl_data) {
- printk(KERN_WARNING "(%s%d) Unable to allocate ioctl data - skipping controller\n",
- ips_name, index);
+ continue;
+ }
- ha->active = 0;
+ ha = IPS_HA(sh);
+ memset(ha, 0, sizeof(ips_ha_t));
- continue;
- }
+ /* Initialize spin lock */
+ spin_lock_init(&ha->scb_lock);
+ spin_lock_init(&ha->copp_lock);
+ spin_lock_init(&ha->ips_lock);
+ spin_lock_init(&ha->copp_waitlist.lock);
+ spin_lock_init(&ha->scb_waitlist.lock);
+ spin_lock_init(&ha->scb_activelist.lock);
- /* Store away needed values for later use */
- sh->io_port = io_addr;
- sh->n_io_port = 255;
- sh->unique_id = io_addr;
- sh->irq = irq;
- sh->select_queue_depths = NULL;
- sh->sg_tablesize = sh->hostt->sg_tablesize;
- sh->can_queue = sh->hostt->can_queue;
- sh->cmd_per_lun = sh->hostt->cmd_per_lun;
- sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma;
- sh->use_clustering = sh->hostt->use_clustering;
-
- /* Store info in HA structure */
- ha->io_addr = io_addr;
- ha->irq = irq;
- ha->host_num = index;
- ha->revision_id = revision_id;
-
- /* install the interrupt handler */
- if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) {
- printk(KERN_WARNING "(%s%d) unable to install interrupt handler - skipping controller\n",
- ips_name, index);
+ ips_sh[ips_next_controller] = sh;
+ ips_ha[ips_next_controller] = ha;
+ ips_num_controllers++;
+ ha->active = 1;
- ha->active = 0;
+ ha->enq = kmalloc(sizeof(IPS_ENQ), GFP_ATOMIC|GFP_DMA);
- continue;
- }
+ if (!ha->enq) {
+ printk(KERN_WARNING "(%s%d) Unable to allocate host inquiry structure - skipping contoller\n",
+ ips_name, ips_next_controller);
- /*
- * Allocate a temporary SCB for initialization
- */
- ha->scbs = (ips_scb_t *) kmalloc(sizeof(ips_scb_t), GFP_KERNEL|GFP_DMA);
- if (!ha->scbs) {
- /* couldn't allocate a temp SCB */
- printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
- ips_name, index);
+ ha->active = 0;
+ ips_free(ha);
+ ips_next_controller++;
+ ips_num_controllers--;
- ha->active = 0;
+ continue;
+ }
- continue;
- }
+ ha->adapt = kmalloc(sizeof(IPS_ADAPTER), GFP_ATOMIC|GFP_DMA);
- memset(ha->scbs, 0, sizeof(ips_scb_t));
- ha->scbs->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_KERNEL|GFP_DMA);
- if (!ha->scbs->sg_list) {
- /* couldn't allocate a temp SCB S/G list */
- printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
- ips_name, index);
+ if (!ha->adapt) {
+ printk(KERN_WARNING "(%s%d) Unable to allocate host adapt structure - skipping controller\n",
+ ips_name, ips_next_controller);
+ ha->active = 0;
+ ips_free(ha);
+ ips_next_controller++;
+ ips_num_controllers--;
- ha->active = 0;
+ continue;
+ }
+
+ ha->conf = kmalloc(sizeof(IPS_CONF), GFP_ATOMIC|GFP_DMA);
+
+ if (!ha->conf) {
+ printk(KERN_WARNING "(%s%d) Unable to allocate host conf structure - skipping controller\n",
+ ips_name, ips_next_controller);
+
+ ha->active = 0;
+ ips_free(ha);
+ ips_next_controller++;
+ ips_num_controllers--;
+
+ continue;
+ }
+
+ ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_ATOMIC|GFP_DMA);
+
+ if (!ha->nvram) {
+ printk(KERN_WARNING "(%s%d) Unable to allocate host nvram structure - skipping controller\n",
+ ips_name, ips_next_controller);
+
+ ha->active = 0;
+ ips_free(ha);
+ ips_next_controller++;
+ ips_num_controllers--;
+
+ continue;
+ }
+
+ ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_ATOMIC|GFP_DMA);
+
+ if (!ha->subsys) {
+ printk(KERN_WARNING "(%s%d) Unable to allocate host subsystem structure - skipping controller\n",
+ ips_name, ips_next_controller);
+
+ ha->active = 0;
+ ips_free(ha);
+ ips_next_controller++;
+ ips_num_controllers--;
+
+ continue;
+ }
+
+ ha->dummy = kmalloc(sizeof(IPS_IO_CMD), GFP_ATOMIC|GFP_DMA);
+
+ if (!ha->dummy) {
+ printk(KERN_WARNING "(%s%d) Unable to allocate host dummy structure - skipping controller\n",
+ ips_name, ips_next_controller);
+
+ ha->active = 0;
+ ips_free(ha);
+ ips_next_controller++;
+ ips_num_controllers--;
+
+ continue;
+ }
+
+ ha->ioctl_data = kmalloc(IPS_IOCTL_SIZE, GFP_ATOMIC|GFP_DMA);
+ ha->ioctl_datasize = IPS_IOCTL_SIZE;
+ if (!ha->ioctl_data) {
+ printk(KERN_WARNING "(%s%d) Unable to allocate ioctl data - skipping controller\n",
+ ips_name, ips_next_controller);
+
+ ha->active = 0;
+ ips_free(ha);
+ ips_next_controller++;
+ ips_num_controllers--;
+
+ continue;
+ }
+
+ /* Store away needed values for later use */
+ sh->io_port = io_addr;
+ sh->n_io_port = io_addr ? 255 : 0;
+ sh->unique_id = (io_addr) ? io_addr : mem_addr;
+ sh->irq = irq;
+ sh->select_queue_depths = ips_select_queue_depth;
+ sh->sg_tablesize = sh->hostt->sg_tablesize;
+ sh->can_queue = sh->hostt->can_queue;
+ sh->cmd_per_lun = sh->hostt->cmd_per_lun;
+ sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma;
+ sh->use_clustering = sh->hostt->use_clustering;
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,32)
+ sh->wish_block = FALSE;
+#endif
+
+ /* Store info in HA structure */
+ ha->irq = irq;
+ ha->io_addr = io_addr;
+ ha->io_len = io_len;
+ ha->mem_addr = mem_addr;
+ ha->mem_len = mem_len;
+ ha->mem_ptr = mem_ptr;
+ ha->ioremap_ptr = ioremap_ptr;
+ ha->host_num = ips_next_controller;
+ ha->revision_id = revision_id;
+ ha->device_id = deviceID[i];
+ ha->pcidev = dev[i];
+
+ /*
+ * Setup Functions
+ */
+ if (IPS_IS_MORPHEUS(ha)) {
+ /* morpheus */
+ ha->func.isintr = ips_isintr_morpheus;
+ ha->func.isinit = ips_isinit_morpheus;
+ ha->func.issue = ips_issue_i2o_memio;
+ ha->func.init = ips_init_morpheus;
+ ha->func.statupd = ips_statupd_morpheus;
+ ha->func.reset = ips_reset_morpheus;
+ ha->func.intr = ips_intr_morpheus;
+ ha->func.enableint = ips_enable_int_morpheus;
+ } else if (IPS_USE_MEMIO(ha)) {
+ /* copperhead w/MEMIO */
+ ha->func.isintr = ips_isintr_copperhead_memio;
+ ha->func.isinit = ips_isinit_copperhead_memio;
+ ha->func.init = ips_init_copperhead_memio;
+ ha->func.statupd = ips_statupd_copperhead_memio;
+ ha->func.statinit = ips_statinit_memio;
+ ha->func.reset = ips_reset_copperhead_memio;
+ ha->func.intr = ips_intr_copperhead;
+ ha->func.erasebios = ips_erase_bios_memio;
+ ha->func.programbios = ips_program_bios_memio;
+ ha->func.verifybios = ips_verify_bios_memio;
+ ha->func.enableint = ips_enable_int_copperhead_memio;
+
+ if (IPS_USE_I2O_DELIVER(ha))
+ ha->func.issue = ips_issue_i2o_memio;
+ else
+ ha->func.issue = ips_issue_copperhead_memio;
+ } else {
+ /* copperhead */
+ ha->func.isintr = ips_isintr_copperhead;
+ ha->func.isinit = ips_isinit_copperhead;
+ ha->func.init = ips_init_copperhead;
+ ha->func.statupd = ips_statupd_copperhead;
+ ha->func.statinit = ips_statinit;
+ ha->func.reset = ips_reset_copperhead;
+ ha->func.intr = ips_intr_copperhead;
+ ha->func.erasebios = ips_erase_bios;
+ ha->func.programbios = ips_program_bios;
+ ha->func.verifybios = ips_verify_bios;
+ ha->func.enableint = ips_enable_int_copperhead;
+
+ if (IPS_USE_I2O_DELIVER(ha))
+ ha->func.issue = ips_issue_i2o;
+ else
+ ha->func.issue = ips_issue_copperhead;
+ }
+
+ /*
+ * Initialize the card if it isn't already
+ */
+ if (!(*ha->func.isinit)(ha)) {
+ if (!(*ha->func.init)(ha)) {
+ /*
+ * Initialization failed
+ */
+ printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping controller\n",
+ ips_name, ips_next_controller);
+
+ ha->active = 0;
+ ips_free(ha);
+ ips_next_controller++;
+ ips_num_controllers--;
+
+ continue;
+ }
+ }
+
+ /* install the interrupt handler */
+ if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) {
+ printk(KERN_WARNING "(%s%d) unable to install interrupt handler - skipping controller\n",
+ ips_name, ips_next_controller);
+
+ ha->active = 0;
+ ips_free(ha);
+ ips_next_controller++;
+ ips_num_controllers--;
+
+ continue;
+ }
+
+ /*
+ * Allocate a temporary SCB for initialization
+ */
+ ha->scbs = (ips_scb_t *) kmalloc(sizeof(ips_scb_t), GFP_ATOMIC|GFP_DMA);
+ if (!ha->scbs) {
+ /* couldn't allocate a temp SCB */
+ printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
+ ips_name, ips_next_controller);
+
+ ha->active = 0;
+ ips_free(ha);
+ free_irq(ha->irq, ha);
+ ips_next_controller++;
+ ips_num_controllers--;
+
+ continue;
+ }
+
+ memset(ha->scbs, 0, sizeof(ips_scb_t));
+ ha->scbs->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_ATOMIC|GFP_DMA);
+ if (!ha->scbs->sg_list) {
+ /* couldn't allocate a temp SCB S/G list */
+ printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
+ ips_name, ips_next_controller);
+
+ ha->active = 0;
+ ips_free(ha);
+ free_irq(ha->irq, ha);
+ ips_next_controller++;
+ ips_num_controllers--;
+
+ continue;
+ }
+
+ ha->max_cmds = 1;
+
+ ips_next_controller++;
+ } while ((dev[i] = pci_find_device(IPS_VENDORID, deviceID[i], dev[i])));
+ }
+
+ /*
+ * Do Phase 2 Initialization
+ * Controller init
+ */
+ for (i = 0; i < ips_next_controller; i++) {
+ ha = ips_ha[i];
+ sh = ips_sh[i];
+
+ if (!ha->active) {
+ scsi_unregister(sh);
+ ips_ha[i] = NULL;
+ ips_sh[i] = NULL;
continue;
}
- ha->max_cmds = 1;
-
if (!ips_hainit(ha)) {
printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping\n",
- ips_name, index);
+ ips_name, i);
ha->active = 0;
+ ips_free(ha);
+ free_irq(ha->irq, ha);
+ scsi_unregister(sh);
+ ips_ha[i] = NULL;
+ ips_sh[i] = NULL;
+ ips_num_controllers--;
continue;
}
@@ -597,9 +1062,15 @@ ips_detect(Scsi_Host_Template *SHT) {
/* allocate CCBs */
if (!ips_allocatescbs(ha)) {
printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
- ips_name, index);
+ ips_name, i);
ha->active = 0;
+ ips_free(ha);
+ free_irq(ha->irq, ha);
+ scsi_unregister(sh);
+ ips_ha[i] = NULL;
+ ips_sh[i] = NULL;
+ ips_num_controllers--;
continue;
}
@@ -607,9 +1078,12 @@ ips_detect(Scsi_Host_Template *SHT) {
/* finish setting values */
sh->max_id = ha->ntargets;
sh->max_lun = ha->nlun;
- sh->max_channel = ha->nbus;
+ sh->max_channel = ha->nbus - 1;
sh->can_queue = ha->max_cmds-1;
- } /* end for */
+ }
+
+ if (ips_num_controllers > 0)
+ register_reboot_notifier(&ips_notifier);
return (ips_num_controllers);
@@ -635,7 +1109,7 @@ ips_release(struct Scsi_Host *sh) {
ips_ha_t *ha;
int i;
- DBG("ips_release");
+ METHOD_TRACE("ips_release", 1);
for (i = 0; i < IPS_MAX_ADAPTERS && ips_sh[i] != sh; i++);
@@ -678,121 +1152,119 @@ ips_release(struct Scsi_Host *sh) {
/* free extra memory */
ips_free(ha);
+ /* Free I/O Region */
+ if (ha->io_addr)
+ release_region(ha->io_addr, ha->io_len);
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,3,17)
+ if (ha->mem_addr)
+ release_mem_region(ha->mem_addr, ha->mem_len);
+#endif
+
/* free IRQ */
free_irq(ha->irq, ha);
- /* unregister with SCSI sub system */
- scsi_unregister(sh);
+ ips_released_controllers++;
+
+ if (ips_num_controllers == ips_released_controllers)
+ unregister_reboot_notifier(&ips_notifier);
return (FALSE);
}
/****************************************************************************/
/* */
-/* Routine Name: ips_eh_abort */
+/* Routine Name: ips_halt */
/* */
/* Routine Description: */
/* */
-/* Abort a command (using the new error code stuff) */
+/* Perform cleanup when the system reboots */
/* */
/****************************************************************************/
-int
-ips_eh_abort(Scsi_Cmnd *SC) {
- ips_ha_t *ha;
- ips_copp_wait_item_t *item;
+static int
+ips_halt(struct notifier_block *nb, ulong event, void *buf) {
+ ips_scb_t *scb;
+ ips_ha_t *ha;
+ int i;
- DBG("ips_eh_abort");
+ if ((event != SYS_RESTART) && (event != SYS_HALT) &&
+ (event != SYS_POWER_OFF))
+ return (NOTIFY_DONE);
- if (!SC)
- return (FAILED);
+ for (i = 0; i < ips_next_controller; i++) {
+ ha = (ips_ha_t *) ips_ha[i];
- ha = (ips_ha_t *) SC->host->hostdata;
+ if (!ha)
+ continue;
- if (!ha)
- return (FAILED);
+ if (!ha->active)
+ continue;
- if (!ha->active)
- return (FAILED);
+ /* flush the cache on the controller */
+ scb = &ha->scbs[ha->max_cmds-1];
- if (SC->serial_number != SC->serial_number_at_timeout) {
- /* HMM, looks like a bogus command */
-#if IPS_DEBUG >= 1
- printk(KERN_NOTICE "Abort called with bogus scsi command\n");
-#endif
+ ips_init_scb(ha, scb);
- return (FAILED);
- }
+ scb->timeout = ips_cmd_timeout;
+ scb->cdb[0] = IPS_CMD_FLUSH;
- if (test_and_set_bit(IPS_IN_ABORT, &ha->flags))
- return (FAILED);
+ scb->cmd.flush_cache.op_code = IPS_CMD_FLUSH;
+ scb->cmd.flush_cache.command_id = IPS_COMMAND_ID(ha, scb);
+ scb->cmd.flush_cache.state = IPS_NORM_STATE;
+ scb->cmd.flush_cache.reserved = 0;
+ scb->cmd.flush_cache.reserved2 = 0;
+ scb->cmd.flush_cache.reserved3 = 0;
+ scb->cmd.flush_cache.reserved4 = 0;
- /* See if the command is on the copp queue */
- IPS_QUEUE_LOCK(&ha->copp_waitlist);
- item = ha->copp_waitlist.head;
- while ((item) && (item->scsi_cmd != SC))
- item = item->next;
- IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
+ printk("(%s%d) Flushing Cache.\n", ips_name, ha->host_num);
- if (item) {
- /* Found it */
- ips_removeq_copp(&ha->copp_waitlist, item);
- clear_bit(IPS_IN_ABORT, &ha->flags);
-
- return (SUCCESS);
+ /* send command */
+ if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE)
+ printk("(%s%d) Incomplete Flush.\n", ips_name, ha->host_num);
+ else
+ printk("(%s%d) Flushing Complete.\n", ips_name, ha->host_num);
}
- /* See if the command is on the wait queue */
- if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
- /* command not sent yet */
- clear_bit(IPS_IN_ABORT, &ha->flags);
-
- return (SUCCESS);
- } else {
- /* command must have already been sent */
- clear_bit(IPS_IN_ABORT, &ha->flags);
-
- return (FAILED);
- }
+ unregister_reboot_notifier(&ips_notifier);
+ return (NOTIFY_OK);
}
/****************************************************************************/
/* */
-/* Routine Name: ips_abort */
+/* Routine Name: ips_eh_abort */
/* */
/* Routine Description: */
/* */
-/* Abort a command */
+/* Abort a command (using the new error code stuff) */
/* */
/****************************************************************************/
int
-ips_abort(Scsi_Cmnd *SC) {
+ips_eh_abort(Scsi_Cmnd *SC) {
ips_ha_t *ha;
ips_copp_wait_item_t *item;
- DBG("ips_abort");
+ METHOD_TRACE("ips_eh_abort", 1);
if (!SC)
- return (SCSI_ABORT_SNOOZE);
+ return (FAILED);
ha = (ips_ha_t *) SC->host->hostdata;
if (!ha)
- return (SCSI_ABORT_SNOOZE);
+ return (FAILED);
if (!ha->active)
- return (SCSI_ABORT_SNOOZE);
+ return (FAILED);
if (SC->serial_number != SC->serial_number_at_timeout) {
/* HMM, looks like a bogus command */
-#if IPS_DEBUG >= 1
- printk(KERN_NOTICE "Abort called with bogus scsi command\n");
-#endif
+ DEBUG(1, "Abort called with bogus scsi command");
- return (SCSI_ABORT_NOT_RUNNING);
+ return (FAILED);
}
if (test_and_set_bit(IPS_IN_ABORT, &ha->flags))
- return (SCSI_ABORT_SNOOZE);
+ return (FAILED);
/* See if the command is on the copp queue */
IPS_QUEUE_LOCK(&ha->copp_waitlist);
@@ -806,7 +1278,7 @@ ips_abort(Scsi_Cmnd *SC) {
ips_removeq_copp(&ha->copp_waitlist, item);
clear_bit(IPS_IN_ABORT, &ha->flags);
- return (SCSI_ABORT_PENDING);
+ return (SUCCESS);
}
/* See if the command is on the wait queue */
@@ -814,12 +1286,12 @@ ips_abort(Scsi_Cmnd *SC) {
/* command not sent yet */
clear_bit(IPS_IN_ABORT, &ha->flags);
- return (SCSI_ABORT_PENDING);
+ return (SUCCESS);
} else {
/* command must have already been sent */
clear_bit(IPS_IN_ABORT, &ha->flags);
- return (SCSI_ABORT_SNOOZE);
+ return (FAILED);
}
}
@@ -836,22 +1308,21 @@ ips_abort(Scsi_Cmnd *SC) {
/****************************************************************************/
int
ips_eh_reset(Scsi_Cmnd *SC) {
+ int ret;
+ int i;
u32 cpu_flags;
ips_ha_t *ha;
ips_scb_t *scb;
ips_copp_wait_item_t *item;
- DBG("ips_eh_reset");
+ METHOD_TRACE("ips_eh_reset", 1);
#ifdef NO_IPS_RESET
return (FAILED);
#else
if (!SC) {
-
-#if IPS_DEBUG >= 1
- printk(KERN_NOTICE "Reset called with NULL scsi command\n");
-#endif
+ DEBUG(1, "Reset called with NULL scsi command");
return (FAILED);
}
@@ -859,10 +1330,7 @@ ips_eh_reset(Scsi_Cmnd *SC) {
ha = (ips_ha_t *) SC->host->hostdata;
if (!ha) {
-
-#if IPS_DEBUG >= 1
- printk(KERN_NOTICE "Reset called with NULL ha struct\n");
-#endif
+ DEBUG(1, "Reset called with NULL ha struct");
return (FAILED);
}
@@ -900,149 +1368,72 @@ ips_eh_reset(Scsi_Cmnd *SC) {
* command must have already been sent
* reset the controller
*/
- if (!ips_reset_adapter(ha)) {
- clear_bit(IPS_IN_RESET, &ha->flags);
-
- return (FAILED);
- }
-
- if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
- clear_bit(IPS_IN_RESET, &ha->flags);
-
- return (FAILED);
- }
-
- /* FFDC */
- if (ha->subsys->param[3] & 0x300000) {
- struct timeval tv;
-
- do_gettimeofday(&tv);
- IPS_HA_LOCK(cpu_flags);
- ha->last_ffdc = tv.tv_sec;
- ha->reset_count++;
- IPS_HA_UNLOCK(cpu_flags);
- ips_ffdc_reset(ha, IPS_INTR_IORL);
- }
-
- /* Now fail all of the active commands */
-#if IPS_DEBUG >= 1
- printk(KERN_WARNING "(%s%d) Failing active commands\n",
+ printk(KERN_NOTICE "(%s%d) Resetting controller.\n",
ips_name, ha->host_num);
-#endif
- while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
- scb->scsi_cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24);
- scb->scsi_cmd->scsi_done(scb->scsi_cmd);
- ips_freescb(ha, scb);
- }
-
- /* Reset the number of active IOCTLs */
- IPS_HA_LOCK(cpu_flags);
- ha->num_ioctl = 0;
- IPS_HA_UNLOCK(cpu_flags);
+ ret = (*ha->func.reset)(ha);
- clear_bit(IPS_IN_RESET, &ha->flags);
+ if (!ret) {
+ Scsi_Cmnd *scsi_cmd;
- if (!test_bit(IPS_IN_INTR, &ha->flags)) {
- /*
- * Only execute the next command when
- * we are not being called from the
- * interrupt handler. The interrupt
- * handler wants to do this and since
- * interrupts are turned off here....
- */
- ips_next(ha, IPS_INTR_IORL);
- }
-
- return (SUCCESS);
-
-#endif /* NO_IPS_RESET */
-
-}
-
-/****************************************************************************/
-/* */
-/* Routine Name: ips_reset */
-/* */
-/* Routine Description: */
-/* */
-/* Reset the controller */
-/* */
-/* NOTE: this routine is called under the io_request_lock spinlock */
-/* */
-/****************************************************************************/
-int
-ips_reset(Scsi_Cmnd *SC, unsigned int flags) {
- u32 cpu_flags;
- ips_ha_t *ha;
- ips_scb_t *scb;
- ips_copp_wait_item_t *item;
-
- DBG("ips_reset");
-
-#ifdef NO_IPS_RESET
- return (SCSI_RESET_SNOOZE);
-#else
-
- if (!SC) {
+ printk(KERN_NOTICE
+ "(%s%d) Controller reset failed - controller now offline.\n",
+ ips_name, ha->host_num);
-#if IPS_DEBUG >= 1
- printk(KERN_NOTICE "Reset called with NULL scsi command\n");
-#endif
+ /* Now fail all of the active commands */
+ DEBUG_VAR(1, "(%s%d) Failing active commands",
+ ips_name, ha->host_num);
- return (SCSI_RESET_SNOOZE);
- }
+ while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
+ scb->scsi_cmd->result = DID_ERROR << 16;
+ scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ ips_freescb(ha, scb);
+ }
- ha = (ips_ha_t *) SC->host->hostdata;
+ /* Now fail all of the pending commands */
+ DEBUG_VAR(1, "(%s%d) Failing pending commands",
+ ips_name, ha->host_num);
- if (!ha) {
+ while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
+ scsi_cmd->result = DID_ERROR;
+ scsi_cmd->scsi_done(scsi_cmd);
+ }
-#if IPS_DEBUG >= 1
- printk(KERN_NOTICE "Reset called with NULL ha struct\n");
-#endif
+ ha->active = FALSE;
+ clear_bit(IPS_IN_RESET, &ha->flags);
- return (SCSI_RESET_SNOOZE);
+ return (FAILED);
}
- if (!ha->active)
- return (SCSI_RESET_SNOOZE);
-
- if (test_and_set_bit(IPS_IN_RESET, &ha->flags))
- return (SCSI_RESET_SNOOZE);
-
- /* See if the command is on the copp queue */
- IPS_QUEUE_LOCK(&ha->copp_waitlist);
- item = ha->copp_waitlist.head;
- while ((item) && (item->scsi_cmd != SC))
- item = item->next;
- IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
-
- if (item) {
- /* Found it */
- ips_removeq_copp(&ha->copp_waitlist, item);
- clear_bit(IPS_IN_RESET, &ha->flags);
+ if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
+ Scsi_Cmnd *scsi_cmd;
- return (SCSI_RESET_SNOOZE);
- }
+ printk(KERN_NOTICE
+ "(%s%d) Controller reset failed - controller now offline.\n",
+ ips_name, ha->host_num);
- /* See if the command is on the wait queue */
- if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
- /* command not sent yet */
- clear_bit(IPS_IN_RESET, &ha->flags);
+ /* Now fail all of the active commands */
+ DEBUG_VAR(1, "(%s%d) Failing active commands",
+ ips_name, ha->host_num);
- return (SCSI_RESET_SNOOZE);
- }
+ while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
+ scb->scsi_cmd->result = DID_ERROR << 16;
+ scb->scsi_cmd->scsi_done(scb->scsi_cmd);
+ ips_freescb(ha, scb);
+ }
- /* reset the controller */
- if (!ips_reset_adapter(ha)) {
- clear_bit(IPS_IN_RESET, &ha->flags);
+ /* Now fail all of the pending commands */
+ DEBUG_VAR(1, "(%s%d) Failing pending commands",
+ ips_name, ha->host_num);
- return (SCSI_RESET_ERROR);
- }
+ while ((scsi_cmd = ips_removeq_wait_head(&ha->scb_waitlist))) {
+ scsi_cmd->result = DID_ERROR << 16;
+ scsi_cmd->scsi_done(scsi_cmd);
+ }
- if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
+ ha->active = FALSE;
clear_bit(IPS_IN_RESET, &ha->flags);
- return (SCSI_RESET_ERROR);
+ return (FAILED);
}
/* FFDC */
@@ -1058,16 +1449,19 @@ ips_reset(Scsi_Cmnd *SC, unsigned int flags) {
}
/* Now fail all of the active commands */
-#if IPS_DEBUG >= 1
- printk(KERN_WARNING "(%s%d) Failing active commands\n",
- ips_name, ha->host_num);
-#endif
+ DEBUG_VAR(1, "(%s%d) Failing active commands",
+ ips_name, ha->host_num);
+
while ((scb = ips_removeq_scb_head(&ha->scb_activelist))) {
scb->scsi_cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24);
scb->scsi_cmd->scsi_done(scb->scsi_cmd);
ips_freescb(ha, scb);
}
+ /* Reset DCDB active command bits */
+ for (i = 1; i < ha->nbus; i++)
+ ha->dcdb_active[i-1] = 0;
+
/* Reset the number of active IOCTLs */
IPS_HA_LOCK(cpu_flags);
ha->num_ioctl = 0;
@@ -1086,7 +1480,7 @@ ips_reset(Scsi_Cmnd *SC, unsigned int flags) {
ips_next(ha, IPS_INTR_IORL);
}
- return (SCSI_RESET_SUCCESS);
+ return (SUCCESS);
#endif /* NO_IPS_RESET */
@@ -1110,7 +1504,7 @@ ips_queue(Scsi_Cmnd *SC, void (*done) (Scsi_Cmnd *)) {
u32 cpu_flags;
DECLARE_MUTEX_LOCKED(sem);
- DBG("ips_queue");
+ METHOD_TRACE("ips_queue", 1);
ha = (ips_ha_t *) SC->host->hostdata;
@@ -1118,7 +1512,7 @@ ips_queue(Scsi_Cmnd *SC, void (*done) (Scsi_Cmnd *)) {
return (1);
if (!ha->active)
- return (1);
+ return (DID_ERROR);
#ifndef NO_IPS_CMDLINE
if (ips_is_passthru(SC)) {
@@ -1151,24 +1545,28 @@ ips_queue(Scsi_Cmnd *SC, void (*done) (Scsi_Cmnd *)) {
SC->scsi_done = done;
-#if IPS_DEBUG >= 10
- printk(KERN_NOTICE "%s: ips_queue: cmd 0x%X (%d %d %d)\n",
- ips_name,
- SC->cmnd[0],
- SC->channel,
- SC->target,
- SC->lun);
-#if IPS_DEBUG >= 11
- MDELAY(2*IPS_ONE_SEC);
-#endif
-#endif
+ DEBUG_VAR(2, "(%s%d): ips_queue: cmd 0x%X (%d %d %d)",
+ ips_name,
+ ha->host_num,
+ SC->cmnd[0],
+ SC->channel,
+ SC->target,
+ SC->lun);
+
+ /* Check for command to initiator IDs */
+ if ((SC->channel > 0) && (SC->target == ha->ha_id[SC->channel])) {
+ SC->result = DID_NO_CONNECT << 16;
+ done(SC);
+
+ return (0);
+ }
#ifndef NO_IPS_CMDLINE
if (ips_is_passthru(SC)) {
ips_copp_wait_item_t *scratch;
/* allocate space for the scribble */
- scratch = kmalloc(sizeof(ips_copp_wait_item_t), GFP_KERNEL);
+ scratch = kmalloc(sizeof(ips_copp_wait_item_t), GFP_ATOMIC);
if (!scratch) {
SC->result = DID_ERROR << 16;
@@ -1224,10 +1622,8 @@ ips_queue(Scsi_Cmnd *SC, void (*done) (Scsi_Cmnd *)) {
datasize = *((u32 *) &SC->cmnd[8]);
if (copy_to_user(user_area, kern_area, datasize) > 0) {
-#if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "(%s%d) passthru failed - unable to copy out user data\n",
- ips_name, ha->host_num);
-#endif
+ DEBUG_VAR(1, "(%s%d) passthru failed - unable to copy out user data",
+ ips_name, ha->host_num);
SC->result = DID_ERROR << 16;
SC->scsi_done(SC);
@@ -1255,7 +1651,7 @@ ips_biosparam(Disk *disk, kdev_t dev, int geom[]) {
int sectors;
int cylinders;
- DBG("ips_biosparam");
+ METHOD_TRACE("ips_biosparam", 1);
ha = (ips_ha_t *) disk->device->host->hostdata;
@@ -1281,10 +1677,8 @@ ips_biosparam(Disk *disk, kdev_t dev, int geom[]) {
cylinders = disk->capacity / (heads * sectors);
-#if IPS_DEBUG >= 2
- printk(KERN_NOTICE "Geometry: heads: %d, sectors: %d, cylinders: %d\n",
- heads, sectors, cylinders);
-#endif
+ DEBUG_VAR(2, "Geometry: heads: %d, sectors: %d, cylinders: %d",
+ heads, sectors, cylinders);
geom[0] = heads;
geom[1] = sectors;
@@ -1295,6 +1689,40 @@ ips_biosparam(Disk *disk, kdev_t dev, int geom[]) {
/****************************************************************************/
/* */
+/* Routine Name: ips_select_queue_depth */
+/* */
+/* Routine Description: */
+/* */
+/* Select queue depths for the devices on the contoller */
+/* */
+/****************************************************************************/
+static void
+ips_select_queue_depth(struct Scsi_Host *host, Scsi_Device *scsi_devs) {
+ Scsi_Device *device;
+ ips_ha_t *ha;
+ int count = 0;
+
+ ha = IPS_HA(host);
+
+ for (device = scsi_devs; device; device = device->next) {
+ if (device->host == host) {
+ if ((device->channel == 0) && (device->type == 0))
+ count++;
+ }
+ }
+
+ for (device = scsi_devs; device; device = device->next) {
+ if (device->host == host) {
+ if ((device->channel == 0) && (device->type == 0))
+ device->queue_depth = ha->max_cmds / count - 1;
+ else
+ device->queue_depth = 2;
+ }
+ }
+}
+
+/****************************************************************************/
+/* */
/* Routine Name: do_ipsintr */
/* */
/* Routine Description: */
@@ -1307,7 +1735,7 @@ do_ipsintr(int irq, void *dev_id, struct pt_regs *regs) {
ips_ha_t *ha;
u32 cpu_flags;
- DBG("do_ipsintr");
+ METHOD_TRACE("do_ipsintr", 2);
ha = (ips_ha_t *) dev_id;
@@ -1333,7 +1761,7 @@ do_ipsintr(int irq, void *dev_id, struct pt_regs *regs) {
return;
}
- ips_intr(ha);
+ (*ha->func.intr)(ha);
clear_bit(IPS_IN_INTR, &ha->flags);
@@ -1345,7 +1773,7 @@ do_ipsintr(int irq, void *dev_id, struct pt_regs *regs) {
/****************************************************************************/
/* */
-/* Routine Name: ips_intr */
+/* Routine Name: ips_intr_copperhead */
/* */
/* Routine Description: */
/* */
@@ -1355,13 +1783,14 @@ do_ipsintr(int irq, void *dev_id, struct pt_regs *regs) {
/* */
/****************************************************************************/
void
-ips_intr(ips_ha_t *ha) {
+ips_intr_copperhead(ips_ha_t *ha) {
ips_stat_t *sp;
ips_scb_t *scb;
- int status;
+ IPS_STATUS cstatus;
+ int intrstatus;
u32 cpu_flags;
- DBG("ips_intr");
+ METHOD_TRACE("ips_intr", 2);
if (!ha)
return;
@@ -1370,16 +1799,36 @@ ips_intr(ips_ha_t *ha) {
return;
IPS_HA_LOCK(cpu_flags);
- while (ips_isintr(ha)) {
+
+ intrstatus = (*ha->func.isintr)(ha);
+
+ if (!intrstatus) {
+ /*
+ * Unexpected/Shared interrupt
+ */
+ IPS_HA_UNLOCK(cpu_flags);
+
+ return;
+ }
+
+ while (TRUE) {
sp = &ha->sp;
- if ((status = ips_chkstatus(ha)) < 0) {
- /* unexpected interrupt - no ccb */
+ intrstatus = (*ha->func.isintr)(ha);
+
+ if (!intrstatus)
+ break;
+ else
+ cstatus.value = (*ha->func.statupd)(ha);
+
+ if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
printk(KERN_WARNING "(%s%d) Spurious interrupt; no ccb.\n",
ips_name, ha->host_num);
- continue ;
+
+ continue;
}
+ ips_chkstatus(ha, &cstatus);
scb = (ips_scb_t *) sp->scb_addr;
/*
@@ -1389,8 +1838,84 @@ ips_intr(ips_ha_t *ha) {
IPS_HA_UNLOCK(cpu_flags);
(*scb->callback) (ha, scb);
IPS_HA_LOCK(cpu_flags);
+ } /* end while */
+
+ IPS_HA_UNLOCK(cpu_flags);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_intr_morpheus */
+/* */
+/* Routine Description: */
+/* */
+/* Polling interrupt handler */
+/* */
+/* ASSUMES interrupts are disabled */
+/* */
+/****************************************************************************/
+void
+ips_intr_morpheus(ips_ha_t *ha) {
+ ips_stat_t *sp;
+ ips_scb_t *scb;
+ IPS_STATUS cstatus;
+ int intrstatus;
+ u32 cpu_flags;
+
+ METHOD_TRACE("ips_intr_morpheus", 2);
+
+ if (!ha)
+ return;
+
+ if (!ha->active)
+ return;
+
+ IPS_HA_LOCK(cpu_flags);
+
+ intrstatus = (*ha->func.isintr)(ha);
+
+ if (!intrstatus) {
+ /*
+ * Unexpected/Shared interrupt
+ */
+ IPS_HA_UNLOCK(cpu_flags);
+
+ return;
}
+ while (TRUE) {
+ sp = &ha->sp;
+
+ intrstatus = (*ha->func.isintr)(ha);
+
+ if (!intrstatus)
+ break;
+ else
+ cstatus.value = (*ha->func.statupd)(ha);
+
+ if (cstatus.value == 0xffffffff)
+ /* No more to process */
+ break;
+
+ if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) {
+ printk(KERN_WARNING "(%s%d) Spurious interrupt; no ccb.\n",
+ ips_name, ha->host_num);
+
+ continue;
+ }
+
+ ips_chkstatus(ha, &cstatus);
+ scb = (ips_scb_t *) sp->scb_addr;
+
+ /*
+ * use the callback function to finish things up
+ * NOTE: interrupts are OFF for this
+ */
+ IPS_HA_UNLOCK(cpu_flags);
+ (*scb->callback) (ha, scb);
+ IPS_HA_LOCK(cpu_flags);
+ } /* end while */
+
IPS_HA_UNLOCK(cpu_flags);
}
@@ -1409,7 +1934,7 @@ ips_info(struct Scsi_Host *SH) {
char *bp;
ips_ha_t *ha;
- DBG("ips_info");
+ METHOD_TRACE("ips_info", 1);
ha = IPS_HA(SH);
@@ -1449,10 +1974,10 @@ ips_proc_info(char *buffer, char **start, off_t offset,
int ret;
ips_ha_t *ha = NULL;
- DBG("ips_proc_info");
+ METHOD_TRACE("ips_proc_info", 1);
/* Find our host structure */
- for (i = 0; i < ips_num_controllers; i++) {
+ for (i = 0; i < ips_next_controller; i++) {
if (ips_sh[i] && ips_sh[i]->host_no == hostno) {
ha = (ips_ha_t *) ips_sh[i]->hostdata;
@@ -1494,7 +2019,7 @@ ips_proc_info(char *buffer, char **start, off_t offset,
/****************************************************************************/
static int
ips_is_passthru(Scsi_Cmnd *SC) {
- DBG("ips_is_passthru");
+ METHOD_TRACE("ips_is_passthru", 1);
if (!SC)
return (0);
@@ -1528,24 +2053,20 @@ static int
ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) {
ips_passthru_t *pt;
- DBG("ips_make_passthru");
+ METHOD_TRACE("ips_make_passthru", 1);
if (!SC->request_bufflen || !SC->request_buffer) {
/* no data */
-#if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "(%s%d) No passthru structure\n",
- ips_name, ha->host_num);
-#endif
+ DEBUG_VAR(1, "(%s%d) No passthru structure",
+ ips_name, ha->host_num);
return (IPS_FAILURE);
}
if (SC->request_bufflen < sizeof(ips_passthru_t)) {
/* wrong size */
-#if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "(%s%d) Passthru structure wrong size\n",
+ DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
ips_name, ha->host_num);
-#endif
return (IPS_FAILURE);
}
@@ -1555,10 +2076,8 @@ ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) {
(((char *) SC->request_buffer)[2] != 'P') ||
(((char *) SC->request_buffer)[3] != 'P')) {
/* signature doesn't match */
-#if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "(%s%d) Wrong signature on passthru structure.\n",
- ips_name, ha->host_num);
-#endif
+ DEBUG_VAR(1, "(%s%d) Wrong signature on passthru structure.",
+ ips_name, ha->host_num);
return (IPS_FAILURE);
}
@@ -1602,10 +2121,8 @@ ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) {
if (SC->cmnd[0] == IPS_IOCTL_COMMAND) {
if (SC->request_bufflen < (sizeof(ips_passthru_t) + pt->CmdBSize)) {
/* wrong size */
- #if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "(%s%d) Passthru structure wrong size\n",
- ips_name, ha->host_num);
- #endif
+ DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
+ ips_name, ha->host_num);
return (IPS_FAILURE);
}
@@ -1617,10 +2134,8 @@ ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) {
} else if (SC->cmnd[0] == IPS_IOCTL_NEW_COMMAND) {
if (SC->request_bufflen < (sizeof(ips_passthru_t))) {
/* wrong size */
- #if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "(%s%d) Passthru structure wrong size\n",
- ips_name, ha->host_num);
- #endif
+ DEBUG_VAR(1, "(%s%d) Passthru structure wrong size",
+ ips_name, ha->host_num);
return (IPS_FAILURE);
}
@@ -1639,14 +2154,23 @@ ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) {
return (IPS_FAILURE);
/* don't flash the BIOS on future cards */
- if (ha->revision_id > IPS_REVID_TROMBONE64) {
-#if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "(%s%d) flash bios failed - unsupported controller\n",
- ips_name, ha->host_num);
-#endif
+ if ((ha->device_id != IPS_COPPERHEAD_DEVICEID) ||
+ (ha->revision_id > IPS_REVID_TROMBONE64)) {
+ DEBUG_VAR(1, "(%s%d) flash bios failed - unsupported controller",
+ ips_name, ha->host_num);
+
return (IPS_FAILURE);
}
+ /*
+ * Check to make sure we have functions
+ * to handle the request
+ */
+ if ((!ha->func.programbios) ||
+ (!ha->func.erasebios) ||
+ (!ha->func.verifybios))
+ return (IPS_FAILURE);
+
/* copy in the size/buffer ptr from the scsi command */
memcpy(&pt->CmdBuffer, &SC->cmnd[4], 4);
memcpy(&pt->CmdBSize, &SC->cmnd[8], 4);
@@ -1660,7 +2184,7 @@ ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) {
void *bigger_struct;
/* try to allocate a bigger struct */
- bigger_struct = kmalloc(pt->CmdBSize, GFP_KERNEL|GFP_DMA);
+ bigger_struct = kmalloc(pt->CmdBSize, GFP_ATOMIC|GFP_DMA);
if (bigger_struct) {
/* free the old memory */
kfree(ha->ioctl_data);
@@ -1674,37 +2198,29 @@ ips_make_passthru(ips_ha_t *ha, Scsi_Cmnd *SC, ips_scb_t *scb) {
/* copy in the buffer */
if (copy_from_user(ha->ioctl_data, pt->CmdBuffer, pt->CmdBSize) > 0) {
-#if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "(%s%d) flash bios failed - unable to copy user buffer\n",
- ips_name, ha->host_num);
-#endif
+ DEBUG_VAR(1, "(%s%d) flash bios failed - unable to copy user buffer",
+ ips_name, ha->host_num);
return (IPS_FAILURE);
}
- if (ips_erase_bios(ha)) {
-#if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "(%s%d) flash bios failed - unable to erase flash\n",
- ips_name, ha->host_num);
-#endif
+ if ((*ha->func.erasebios)(ha)) {
+ DEBUG_VAR(1, "(%s%d) flash bios failed - unable to erase flash",
+ ips_name, ha->host_num);
return (IPS_FAILURE);
}
- if (ips_program_bios(ha, ha->ioctl_data, pt->CmdBSize)) {
-#if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "(%s%d) flash bios failed - unable to program flash\n",
- ips_name, ha->host_num);
-#endif
+ if ((*ha->func.programbios)(ha, ha->ioctl_data, pt->CmdBSize)) {
+ DEBUG_VAR(1, "(%s%d) flash bios failed - unable to program flash",
+ ips_name, ha->host_num);
return (IPS_FAILURE);
}
- if (ips_verify_bios(ha, ha->ioctl_data, pt->CmdBSize)) {
-#if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "(%s%d) flash bios failed - unable to verify flash\n",
- ips_name, ha->host_num);
-#endif
+ if ((*ha->func.verifybios)(ha, ha->ioctl_data, pt->CmdBSize)) {
+ DEBUG_VAR(1, "(%s%d) flash bios failed - unable to verify flash",
+ ips_name, ha->host_num);
return (IPS_FAILURE);
}
@@ -1728,7 +2244,7 @@ static int
ips_usrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) {
IPS_SG_LIST *sg_list;
- DBG("ips_usrcmd");
+ METHOD_TRACE("ips_usrcmd", 1);
if ((!scb) || (!pt) || (!ha))
return (0);
@@ -1811,7 +2327,7 @@ ips_newusrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) {
char *kern_area;
u32 datasize;
- DBG("ips_usrcmd");
+ METHOD_TRACE("ips_usrcmd", 1);
if ((!scb) || (!pt) || (!ha))
return (0);
@@ -1848,7 +2364,7 @@ ips_newusrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) {
void *bigger_struct;
/* try to allocate a bigger struct */
- bigger_struct = kmalloc(pt->CmdBSize, GFP_KERNEL|GFP_DMA);
+ bigger_struct = kmalloc(pt->CmdBSize, GFP_ATOMIC|GFP_DMA);
if (bigger_struct) {
/* free the old memory */
kfree(ha->ioctl_data);
@@ -1869,10 +2385,8 @@ ips_newusrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) {
datasize = *((u32 *) &scb->scsi_cmd->cmnd[8]);
if (copy_from_user(kern_area, user_area, datasize) > 0) {
-#if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "(%s%d) passthru failed - unable to copy in user data\n",
- ips_name, ha->host_num);
-#endif
+ DEBUG_VAR(1, "(%s%d) passthru failed - unable to copy in user data",
+ ips_name, ha->host_num);
return (0);
}
@@ -1923,12 +2437,11 @@ static void
ips_cleanup_passthru(ips_ha_t *ha, ips_scb_t *scb) {
ips_passthru_t *pt;
- DBG("ips_cleanup_passthru");
+ METHOD_TRACE("ips_cleanup_passthru", 1);
if ((!scb) || (!scb->scsi_cmd) || (!scb->scsi_cmd->request_buffer)) {
-#if IPS_DEBUG_PT >= 1
- printk(KERN_NOTICE "IPS couldn't cleanup\n");
-#endif
+ DEBUG_VAR(1, "(%s%d) couldn't cleanup after passthru",
+ ips_name, ha->host_num);
return ;
}
@@ -1962,12 +2475,13 @@ static int
ips_host_info(ips_ha_t *ha, char *ptr, off_t offset, int len) {
IPS_INFOSTR info;
- DBG("ips_host_info");
+ METHOD_TRACE("ips_host_info", 1);
info.buffer = ptr;
info.length = len;
info.offset = offset;
info.pos = 0;
+ info.localpos = 0;
copy_info(&info, "\nIBM ServeRAID General Information:\n\n");
@@ -1977,7 +2491,16 @@ ips_host_info(ips_ha_t *ha, char *ptr, off_t offset, int len) {
else
copy_info(&info, "\tController Type : Unknown\n");
- copy_info(&info, "\tIO port address : 0x%lx\n", ha->io_addr);
+ if (ha->io_addr)
+ copy_info(&info, "\tIO region : 0x%lx (%d bytes)\n",
+ ha->io_addr, ha->io_len);
+
+ if (ha->mem_addr) {
+ copy_info(&info, "\tMemory region : 0x%lx (%d bytes)\n",
+ ha->mem_addr, ha->mem_len);
+ copy_info(&info, "\tShared memory address : 0x%lx\n", ha->mem_ptr);
+ }
+
copy_info(&info, "\tIRQ number : %d\n", ha->irq);
if (ha->nvram->signature == IPS_NVRAM_P5_SIG)
@@ -2017,7 +2540,7 @@ ips_host_info(ips_ha_t *ha, char *ptr, off_t offset, int len) {
copy_info(&info, "\n");
- return (info.pos > info.offset ? info.pos - info.offset : 0);
+ return (info.localpos);
}
/****************************************************************************/
@@ -2031,10 +2554,7 @@ ips_host_info(ips_ha_t *ha, char *ptr, off_t offset, int len) {
/****************************************************************************/
static void
copy_mem_info(IPS_INFOSTR *info, char *data, int len) {
- DBG("copy_mem_info");
-
- if (info->pos + len > info->length)
- len = info->length - info->pos;
+ METHOD_TRACE("copy_mem_info", 1);
if (info->pos + len < info->offset) {
info->pos += len;
@@ -2043,12 +2563,17 @@ copy_mem_info(IPS_INFOSTR *info, char *data, int len) {
if (info->pos < info->offset) {
data += (info->offset - info->pos);
- len -= (info->offset - info->pos);
+ len -= (info->offset - info->pos);
+ info->pos += (info->offset - info->pos);
}
+ if (info->localpos + len > info->length)
+ len = info->length - info->localpos;
+
if (len > 0) {
- memcpy(info->buffer + info->pos, data, len);
+ memcpy(info->buffer + info->localpos, data, len);
info->pos += len;
+ info->localpos += len;
}
}
@@ -2064,10 +2589,10 @@ copy_mem_info(IPS_INFOSTR *info, char *data, int len) {
static int
copy_info(IPS_INFOSTR *info, char *fmt, ...) {
va_list args;
- char buf[81];
+ char buf[128];
int len;
- DBG("copy_info");
+ METHOD_TRACE("copy_info", 1);
va_start(args, fmt);
len = vsprintf(buf, fmt, args);
@@ -2091,57 +2616,32 @@ copy_info(IPS_INFOSTR *info, char *fmt, ...) {
/****************************************************************************/
static int
ips_hainit(ips_ha_t *ha) {
- int i;
+ int i;
+ struct timeval tv;
- DBG("ips_hainit");
+ METHOD_TRACE("ips_hainit", 1);
if (!ha)
return (0);
- /* initialize status queue */
- ips_statinit(ha);
+ if (ha->func.statinit)
+ (*ha->func.statinit)(ha);
+
+ if (ha->func.enableint)
+ (*ha->func.enableint)(ha);
+ /* Send FFDC */
ha->reset_count = 1;
+ do_gettimeofday(&tv);
+ ha->last_ffdc = tv.tv_sec;
+ ips_ffdc_reset(ha, IPS_INTR_IORL);
- /* Setup HBA ID's */
if (!ips_read_config(ha, IPS_INTR_IORL)) {
-
-#ifndef NO_IPS_RESET
-
- ha->reset_count++;
-
- /* Try to reset the controller and try again */
- if (!ips_reset_adapter(ha)) {
- printk(KERN_WARNING "(%s%d) unable to reset controller.\n",
- ips_name, ha->host_num);
-
- return (0);
- }
-
- if (!ips_clear_adapter(ha, IPS_INTR_IORL)) {
- printk(KERN_WARNING "(%s%d) unable to initialize controller.\n",
- ips_name, ha->host_num);
-
- return (0);
- }
-
-#endif
-
- if (!ips_read_config(ha, IPS_INTR_IORL)) {
- printk(KERN_WARNING "(%s%d) unable to read config from controller.\n",
- ips_name, ha->host_num);
-
- return (0);
- }
- } /* end if */
-
- /* write driver version */
- if (!ips_write_driver_status(ha, IPS_INTR_IORL)) {
- printk(KERN_WARNING "(%s%d) unable to write driver info to controller.\n",
+ printk(KERN_WARNING "(%s%d) unable to read config from controller.\n",
ips_name, ha->host_num);
return (0);
- }
+ } /* end if */
if (!ips_read_adapter_status(ha, IPS_INTR_IORL)) {
printk(KERN_WARNING "(%s%d) unable to read controller status.\n",
@@ -2157,19 +2657,18 @@ ips_hainit(ips_ha_t *ha) {
return (0);
}
- /* FFDC */
- if (ha->subsys->param[3] & 0x300000) {
- struct timeval tv;
+ /* write nvram user page 5 */
+ if (!ips_write_driver_status(ha, IPS_INTR_IORL)) {
+ printk(KERN_WARNING "(%s%d) unable to write driver info to controller.\n",
+ ips_name, ha->host_num);
- do_gettimeofday(&tv);
- ha->last_ffdc = tv.tv_sec;
- ips_ffdc_reset(ha, IPS_INTR_IORL);
+ return (0);
}
/* set limits on SID, LUN, BUS */
ha->ntargets = IPS_MAX_TARGETS + 1;
ha->nlun = 1;
- ha->nbus = (ha->enq->ucMaxPhysicalDevices / IPS_MAX_TARGETS);
+ ha->nbus = (ha->enq->ucMaxPhysicalDevices / IPS_MAX_TARGETS) + 1;
switch (ha->conf->logical_drive[0].ucStripeSize) {
case 4:
@@ -2240,13 +2739,14 @@ ips_next(ips_ha_t *ha, int intr) {
ips_scb_t *scb;
Scsi_Cmnd *SC;
Scsi_Cmnd *p;
+ Scsi_Cmnd *q;
ips_copp_wait_item_t *item;
int ret;
int intr_status;
u32 cpu_flags;
u32 cpu_flags2;
- DBG("ips_next");
+ METHOD_TRACE("ips_next", 1);
if (!ha)
return ;
@@ -2392,7 +2892,8 @@ ips_next(ips_ha_t *ha, int intr) {
IPS_HA_UNLOCK(cpu_flags);
- SC = ips_removeq_wait(&ha->scb_waitlist, p);
+ q = p;
+ SC = ips_removeq_wait(&ha->scb_waitlist, q);
SC->result = DID_OK;
SC->host_scribble = NULL;
@@ -2419,28 +2920,41 @@ ips_next(ips_ha_t *ha, int intr) {
sg = SC->request_buffer;
- for (i = 0; i < SC->use_sg; i++) {
- scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
- scb->sg_list[i].length = sg[i].length;
+ if (SC->use_sg == 1) {
+ if (sg[0].length > ha->max_xfer) {
+ scb->breakup = 1;
+ scb->data_len = ha->max_xfer;
+ } else
+ scb->data_len = sg[0].length;
- if (scb->data_len + sg[i].length > ha->max_xfer) {
- /*
- * Data Breakup required
- */
- scb->breakup = i;
- break;
- }
+ scb->dcdb.transfer_length = scb->data_len;
+ scb->data_busaddr = VIRT_TO_BUS(sg[0].address);
+ scb->sg_len = 0;
+ } else {
- scb->data_len += sg[i].length;
- }
+ for (i = 0; i < SC->use_sg; i++) {
+ scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
+ scb->sg_list[i].length = sg[i].length;
- if (!scb->breakup)
- scb->sg_len = SC->use_sg;
- else
- scb->sg_len = scb->breakup;
+ if (scb->data_len + sg[i].length > ha->max_xfer) {
+ /*
+ * Data Breakup required
+ */
+ scb->breakup = i;
+ break;
+ }
+
+ scb->data_len += sg[i].length;
+ }
+
+ if (!scb->breakup)
+ scb->sg_len = SC->use_sg;
+ else
+ scb->sg_len = scb->breakup;
- scb->dcdb.transfer_length = scb->data_len;
- scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
+ scb->dcdb.transfer_length = scb->data_len;
+ scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
+ }
} else {
if (SC->request_bufflen) {
if (SC->request_bufflen > ha->max_xfer) {
@@ -2527,7 +3041,7 @@ ips_next(ips_ha_t *ha, int intr) {
/****************************************************************************/
static inline void
ips_putq_scb_head(ips_scb_queue_t *queue, ips_scb_t *item) {
- DBG("ips_putq_scb_head");
+ METHOD_TRACE("ips_putq_scb_head", 1);
if (!item)
return ;
@@ -2558,7 +3072,7 @@ ips_putq_scb_head(ips_scb_queue_t *queue, ips_scb_t *item) {
/****************************************************************************/
static inline void
ips_putq_scb_tail(ips_scb_queue_t *queue, ips_scb_t *item) {
- DBG("ips_putq_scb_tail");
+ METHOD_TRACE("ips_putq_scb_tail", 1);
if (!item)
return ;
@@ -2595,7 +3109,7 @@ static inline ips_scb_t *
ips_removeq_scb_head(ips_scb_queue_t *queue) {
ips_scb_t *item;
- DBG("ips_removeq_scb_head");
+ METHOD_TRACE("ips_removeq_scb_head", 1);
IPS_QUEUE_LOCK(queue);
@@ -2635,7 +3149,7 @@ static inline ips_scb_t *
ips_removeq_scb(ips_scb_queue_t *queue, ips_scb_t *item) {
ips_scb_t *p;
- DBG("ips_removeq_scb");
+ METHOD_TRACE("ips_removeq_scb", 1);
if (!item)
return (NULL);
@@ -2686,7 +3200,7 @@ ips_removeq_scb(ips_scb_queue_t *queue, ips_scb_t *item) {
/****************************************************************************/
static inline void
ips_putq_wait_head(ips_wait_queue_t *queue, Scsi_Cmnd *item) {
- DBG("ips_putq_wait_head");
+ METHOD_TRACE("ips_putq_wait_head", 1);
if (!item)
return ;
@@ -2717,7 +3231,7 @@ ips_putq_wait_head(ips_wait_queue_t *queue, Scsi_Cmnd *item) {
/****************************************************************************/
static inline void
ips_putq_wait_tail(ips_wait_queue_t *queue, Scsi_Cmnd *item) {
- DBG("ips_putq_wait_tail");
+ METHOD_TRACE("ips_putq_wait_tail", 1);
if (!item)
return ;
@@ -2754,7 +3268,7 @@ static inline Scsi_Cmnd *
ips_removeq_wait_head(ips_wait_queue_t *queue) {
Scsi_Cmnd *item;
- DBG("ips_removeq_wait_head");
+ METHOD_TRACE("ips_removeq_wait_head", 1);
IPS_QUEUE_LOCK(queue);
@@ -2794,7 +3308,7 @@ static inline Scsi_Cmnd *
ips_removeq_wait(ips_wait_queue_t *queue, Scsi_Cmnd *item) {
Scsi_Cmnd *p;
- DBG("ips_removeq_wait");
+ METHOD_TRACE("ips_removeq_wait", 1);
if (!item)
return (NULL);
@@ -2845,7 +3359,7 @@ ips_removeq_wait(ips_wait_queue_t *queue, Scsi_Cmnd *item) {
/****************************************************************************/
static inline void
ips_putq_copp_head(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) {
- DBG("ips_putq_copp_head");
+ METHOD_TRACE("ips_putq_copp_head", 1);
if (!item)
return ;
@@ -2876,7 +3390,7 @@ ips_putq_copp_head(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) {
/****************************************************************************/
static inline void
ips_putq_copp_tail(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) {
- DBG("ips_putq_copp_tail");
+ METHOD_TRACE("ips_putq_copp_tail", 1);
if (!item)
return ;
@@ -2913,7 +3427,7 @@ static inline ips_copp_wait_item_t *
ips_removeq_copp_head(ips_copp_queue_t *queue) {
ips_copp_wait_item_t *item;
- DBG("ips_removeq_copp_head");
+ METHOD_TRACE("ips_removeq_copp_head", 1);
IPS_QUEUE_LOCK(queue);
@@ -2953,7 +3467,7 @@ static inline ips_copp_wait_item_t *
ips_removeq_copp(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) {
ips_copp_wait_item_t *p;
- DBG("ips_removeq_copp");
+ METHOD_TRACE("ips_removeq_copp", 1);
if (!item)
return (NULL);
@@ -3002,7 +3516,7 @@ ips_removeq_copp(ips_copp_queue_t *queue, ips_copp_wait_item_t *item) {
/****************************************************************************/
static void
ipsintr_blocking(ips_ha_t *ha, ips_scb_t *scb) {
- DBG("ipsintr_blocking");
+ METHOD_TRACE("ipsintr_blocking", 2);
if ((ha->waitflag == TRUE) &&
(ha->cmd_in_progress == scb->cdb[0])) {
@@ -3023,7 +3537,14 @@ ipsintr_blocking(ips_ha_t *ha, ips_scb_t *scb) {
/****************************************************************************/
static void
ipsintr_done(ips_ha_t *ha, ips_scb_t *scb) {
- DBG("ipsintr_done");
+ METHOD_TRACE("ipsintr_done", 2);
+
+ if (!scb) {
+ printk(KERN_WARNING "(%s%d) Spurious interrupt; scb NULL.\n",
+ ips_name, ha->host_num);
+
+ return ;
+ }
if (scb->scsi_cmd == NULL) {
/* unexpected interrupt */
@@ -3050,7 +3571,7 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) {
int ret;
u32 cpu_flags;
- DBG("ips_done");
+ METHOD_TRACE("ips_done", 1);
if (!scb)
return ;
@@ -3082,31 +3603,46 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) {
sg = scb->scsi_cmd->request_buffer;
- scb->data_len = 0;
+ if (scb->scsi_cmd->use_sg == 1) {
+ if (sg[0].length - (bk_save * ha->max_xfer)) {
+ /* Further breakup required */
+ scb->data_len = ha->max_xfer;
+ scb->data_busaddr = VIRT_TO_BUS(sg[0].address + (bk_save * ha->max_xfer));
+ scb->breakup = bk_save + 1;
+ } else {
+ scb->data_len = sg[0].length - (bk_save * ha->max_xfer);
+ scb->data_busaddr = VIRT_TO_BUS(sg[0].address + (bk_save * ha->max_xfer));
+ }
- for (i = bk_save; i < scb->scsi_cmd->use_sg; i++) {
- scb->sg_list[i - bk_save].address = VIRT_TO_BUS(sg[i].address);
- scb->sg_list[i - bk_save].length = sg[i].length;
+ scb->dcdb.transfer_length = scb->data_len;
+ scb->sg_len = 0;
+ } else {
+ scb->data_len = 0;
- if (scb->data_len + sg[i].length > ha->max_xfer) {
- /*
- * Data Breakup required
- */
- scb->breakup = i;
- break;
- }
+ for (i = bk_save; i < scb->scsi_cmd->use_sg; i++) {
+ scb->sg_list[i - bk_save].address = VIRT_TO_BUS(sg[i].address);
+ scb->sg_list[i - bk_save].length = sg[i].length;
- scb->data_len += sg[i].length;
- }
+ if (scb->data_len + sg[i].length > ha->max_xfer) {
+ /*
+ * Data Breakup required
+ */
+ scb->breakup = i;
+ break;
+ }
- if (!scb->breakup)
- scb->sg_len = scb->scsi_cmd->use_sg - bk_save;
- else
- scb->sg_len = scb->breakup - bk_save;
+ scb->data_len += sg[i].length;
+ }
- scb->dcdb.transfer_length = scb->data_len;
- scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
- } else {
+ if (!scb->breakup)
+ scb->sg_len = scb->scsi_cmd->use_sg - bk_save;
+ else
+ scb->sg_len = scb->breakup - bk_save;
+
+ scb->dcdb.transfer_length = scb->data_len;
+ scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
+ }
+ } else {
/* Non S/G Request */
if (scb->scsi_cmd->request_bufflen - (bk_save * ha->max_xfer)) {
/* Further breakup required */
@@ -3185,31 +3721,29 @@ ips_done(ips_ha_t *ha, ips_scb_t *scb) {
/* */
/****************************************************************************/
static int
-ips_map_status(ips_scb_t *scb, ips_stat_t *sp) {
+ips_map_status(ips_ha_t *ha, ips_scb_t *scb, ips_stat_t *sp) {
int errcode;
+ int device_error;
- DBG("ips_map_status");
+ METHOD_TRACE("ips_map_status", 1);
if (scb->bus) {
-#if IPS_DEBUG >= 10
- printk(KERN_NOTICE "(%s) Physical device error: %x %x, Sense Key: %x, ASC: %x, ASCQ: %x\n",
- ips_name,
- scb->basic_status,
- scb->extended_status,
- scb->dcdb.sense_info[2] & 0xf,
- scb->dcdb.sense_info[12],
- scb->dcdb.sense_info[13]);
-#endif
-
- /* copy SCSI status and sense data for DCDB commands */
- memcpy(scb->scsi_cmd->sense_buffer, scb->dcdb.sense_info,
- sizeof(scb->scsi_cmd->sense_buffer));
- scb->scsi_cmd->result = scb->dcdb.scsi_status;
- } else
- scb->scsi_cmd->result = 0;
+ DEBUG_VAR(2, "(%s%d) Physical device error (%d %d %d): %x %x, Sense Key: %x, ASC: %x, ASCQ: %x",
+ ips_name,
+ ha->host_num,
+ scb->scsi_cmd->channel,
+ scb->scsi_cmd->target,
+ scb->scsi_cmd->lun,
+ scb->basic_status,
+ scb->extended_status,
+ scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[2] & 0xf : 0,
+ scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[12] : 0,
+ scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[13] : 0);
+ }
/* default driver error */
errcode = DID_ERROR;
+ device_error = 0;
switch (scb->basic_status & IPS_GSC_STATUS_MASK) {
case IPS_CMD_TIMEOUT:
@@ -3224,25 +3758,19 @@ ips_map_status(ips_scb_t *scb, ips_stat_t *sp) {
break;
case IPS_PHYS_DRV_ERROR:
- /*
- * For physical drive errors that
- * are not on a logical drive should
- * be DID_OK. The SCSI errcode will
- * show what the real error is.
- */
- if (scb->bus)
- errcode = DID_OK;
-
switch (scb->extended_status) {
case IPS_ERR_SEL_TO:
- if (scb->bus) {
- scb->scsi_cmd->result |= DID_TIME_OUT << 16;
+ if (scb->bus)
+ errcode = DID_NO_CONNECT;
- return (0);
- }
break;
+
case IPS_ERR_OU_RUN:
if ((scb->bus) && (scb->dcdb.transfer_length < scb->data_len)) {
+ /* Underrun - set default to no error */
+ errcode = DID_OK;
+
+ /* Restrict access to physical DASD */
if ((scb->scsi_cmd->cmnd[0] == INQUIRY) &&
((((char *) scb->scsi_cmd->buffer)[0] & 0x1f) == TYPE_DISK)) {
/* underflow -- no error */
@@ -3250,23 +3778,16 @@ ips_map_status(ips_scb_t *scb, ips_stat_t *sp) {
errcode = DID_TIME_OUT;
break;
}
-
- /* normal underflow Occured */
- if (scb->dcdb.transfer_length >= scb->scsi_cmd->underflow) {
- scb->scsi_cmd->result |= DID_OK << 16;
-
- return (0);
- }
- }
+ } else
+ errcode = DID_ERROR;
break;
+
case IPS_ERR_RECOVERY:
/* don't fail recovered errors */
- if (scb->bus) {
- scb->scsi_cmd->result |= DID_OK << 16;
+ if (scb->bus)
+ errcode = DID_OK;
- return (0);
- }
break;
case IPS_ERR_HOST_RESET:
@@ -3275,11 +3796,25 @@ ips_map_status(ips_scb_t *scb, ips_stat_t *sp) {
break;
case IPS_ERR_CKCOND:
+ if (scb->bus) {
+ memcpy(scb->scsi_cmd->sense_buffer, scb->dcdb.sense_info,
+ sizeof(scb->scsi_cmd->sense_buffer));
+
+ device_error = 2; /* check condition */
+ }
+
+ errcode = DID_OK;
+
+ break;
+
+ default:
+ errcode = DID_ERROR;
break;
+
} /* end switch */
} /* end switch */
- scb->scsi_cmd->result |= (errcode << 16);
+ scb->scsi_cmd->result = device_error | (errcode << 16);
return (1);
}
@@ -3297,7 +3832,7 @@ static int
ips_send(ips_ha_t *ha, ips_scb_t *scb, ips_scb_callback callback) {
int ret;
- DBG("ips_send");
+ METHOD_TRACE("ips_send", 1);
scb->callback = callback;
@@ -3319,7 +3854,7 @@ static int
ips_send_wait(ips_ha_t *ha, ips_scb_t *scb, int timeout, int intr) {
int ret;
- DBG("ips_send_wait");
+ METHOD_TRACE("ips_send_wait", 1);
ha->waitflag = TRUE;
ha->cmd_in_progress = scb->cdb[0];
@@ -3347,7 +3882,7 @@ static int
ips_send_cmd(ips_ha_t *ha, ips_scb_t *scb) {
int ret;
- DBG("ips_send_cmd");
+ METHOD_TRACE("ips_send_cmd", 1);
ret = IPS_SUCCESS;
@@ -3576,7 +4111,7 @@ ips_send_cmd(ips_ha_t *ha, ips_scb_t *scb) {
memcpy(scb->dcdb.scsi_cdb, scb->scsi_cmd->cmnd, scb->scsi_cmd->cmd_len);
}
- return (ips_issue(ha, scb));
+ return ((*ha->func.issue)(ha, scb));
}
/****************************************************************************/
@@ -3588,60 +4123,57 @@ ips_send_cmd(ips_ha_t *ha, ips_scb_t *scb) {
/* Check the status of commands to logical drives */
/* */
/****************************************************************************/
-static int
-ips_chkstatus(ips_ha_t *ha) {
+static void
+ips_chkstatus(ips_ha_t *ha, IPS_STATUS *pstatus) {
ips_scb_t *scb;
- ips_stat_t *sp = &ha->sp;
+ ips_stat_t *sp;
u8 basic_status;
u8 ext_status;
- int command_id;
int errcode;
- int ret;
- DBG("ips_chkstatus");
+ METHOD_TRACE("ips_chkstatus", 1);
- command_id = ips_statupd(ha);
-
- if (command_id > (IPS_MAX_CMDS-1)) {
- printk(KERN_NOTICE "(%s%d) invalid command id received: %d\n",
- ips_name, ha->host_num, command_id);
-
- return (-1);
- }
+ scb = &ha->scbs[pstatus->fields.command_id];
+ scb->basic_status = basic_status = pstatus->fields.basic_status & IPS_BASIC_STATUS_MASK;
+ scb->extended_status = ext_status = pstatus->fields.extended_status;
- scb = &ha->scbs[command_id];
- sp->scb_addr = (u32) scb;
+ sp = &ha->sp;
sp->residue_len = 0;
- scb->basic_status = basic_status = ha->adapt->p_status_tail->basic_status & IPS_BASIC_STATUS_MASK;
- scb->extended_status = ext_status = ha->adapt->p_status_tail->extended_status;
+ sp->scb_addr = (u32) scb;
/* Remove the item from the active queue */
ips_removeq_scb(&ha->scb_activelist, scb);
if (!scb->scsi_cmd)
/* internal commands are handled in do_ipsintr */
- return (0);
+ return ;
+
+ DEBUG_VAR(2, "(%s%d) ips_chkstatus: cmd 0x%X id %d (%d %d %d)",
+ ips_name,
+ ha->host_num,
+ scb->cdb[0],
+ scb->cmd.basic_io.command_id,
+ scb->bus,
+ scb->target_id,
+ scb->lun);
#ifndef NO_IPS_CMDLINE
if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd)))
/* passthru - just returns the raw result */
- return (0);
+ return ;
#endif
errcode = DID_OK;
- ret = 0;
if (((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_SUCCESS) ||
((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_RECOVERED_ERROR)) {
if (scb->bus == 0) {
-#if IPS_DEBUG >= 1
if ((basic_status & IPS_GSC_STATUS_MASK) == IPS_CMD_RECOVERED_ERROR) {
- printk(KERN_NOTICE "(%s%d) Recovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x\n",
- ips_name, ha->host_num,
- scb->cmd.basic_io.op_code, basic_status, ext_status);
+ DEBUG_VAR(1, "(%s%d) Recovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x",
+ ips_name, ha->host_num,
+ scb->cmd.basic_io.op_code, basic_status, ext_status);
}
-#endif
switch (scb->scsi_cmd->cmnd[0]) {
case ALLOW_MEDIUM_REMOVAL:
@@ -3650,7 +4182,6 @@ ips_chkstatus(ips_ha_t *ha) {
case WRITE_FILEMARKS:
case SPACE:
errcode = DID_ERROR;
- ret = 1;
break;
case START_STOP:
@@ -3659,7 +4190,6 @@ ips_chkstatus(ips_ha_t *ha) {
case TEST_UNIT_READY:
if (!ips_online(ha, scb)) {
errcode = DID_TIME_OUT;
- ret = 1;
}
break;
@@ -3668,7 +4198,6 @@ ips_chkstatus(ips_ha_t *ha) {
ips_inquiry(ha, scb);
} else {
errcode = DID_TIME_OUT;
- ret = 1;
}
break;
@@ -3687,7 +4216,6 @@ ips_chkstatus(ips_ha_t *ha) {
case MODE_SENSE:
if (!ips_online(ha, scb) || !ips_msense(ha, scb)) {
errcode = DID_ERROR;
- ret = 1;
}
break;
@@ -3696,7 +4224,6 @@ ips_chkstatus(ips_ha_t *ha) {
ips_rdcap(ha, scb);
else {
errcode = DID_TIME_OUT;
- ret = 1;
}
break;
@@ -3706,7 +4233,6 @@ ips_chkstatus(ips_ha_t *ha) {
case FORMAT_UNIT:
errcode = DID_ERROR;
- ret = 1;
break;
case SEEK_10:
@@ -3718,7 +4244,6 @@ ips_chkstatus(ips_ha_t *ha) {
default:
errcode = DID_ERROR;
- ret = 1;
} /* end switch */
scb->scsi_cmd->result = errcode << 16;
@@ -3728,23 +4253,17 @@ ips_chkstatus(ips_ha_t *ha) {
((((char *) scb->scsi_cmd->buffer)[0] & 0x1f) == TYPE_DISK)) {
scb->scsi_cmd->result = DID_TIME_OUT << 16;
-
- ret = 1;
}
} /* else */
} else { /* recovered error / success */
-#if IPS_DEBUG >= 1
if (scb->bus == 0) {
- printk(KERN_NOTICE "(%s%d) Unrecovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x\n",
- ips_name, ha->host_num,
- scb->cmd.basic_io.op_code, basic_status, ext_status);
+ DEBUG_VAR(1, "(%s%d) Unrecovered Logical Drive Error OpCode: %x, BSB: %x, ESB: %x",
+ ips_name, ha->host_num,
+ scb->cmd.basic_io.op_code, basic_status, ext_status);
}
-#endif
- ret = ips_map_status(scb, sp);
+ ips_map_status(ha, scb, sp);
} /* else */
-
- return (ret);
}
/****************************************************************************/
@@ -3758,7 +4277,7 @@ ips_chkstatus(ips_ha_t *ha) {
/****************************************************************************/
static int
ips_online(ips_ha_t *ha, ips_scb_t *scb) {
- DBG("ips_online");
+ METHOD_TRACE("ips_online", 1);
if (scb->target_id >= IPS_MAX_LD)
return (0);
@@ -3792,7 +4311,7 @@ static int
ips_inquiry(ips_ha_t *ha, ips_scb_t *scb) {
IPS_INQ_DATA inq;
- DBG("ips_inquiry");
+ METHOD_TRACE("ips_inquiry", 1);
memset(&inq, 0, sizeof(IPS_INQ_DATA));
@@ -3823,7 +4342,7 @@ static int
ips_rdcap(ips_ha_t *ha, ips_scb_t *scb) {
IPS_CAPACITY *cap;
- DBG("ips_rdcap");
+ METHOD_TRACE("ips_rdcap", 1);
if (scb->scsi_cmd->bufflen < 8)
return (0);
@@ -3852,7 +4371,7 @@ ips_msense(ips_ha_t *ha, ips_scb_t *scb) {
u32 cylinders;
ips_mdata_t mdata;
- DBG("ips_msense");
+ METHOD_TRACE("ips_msense", 1);
if (ha->enq->ulDriveSize[scb->target_id] > 0x400000 &&
(ha->enq->ucMiscFlag & 0x8) == 0) {
@@ -3930,7 +4449,7 @@ static int
ips_reqsen(ips_ha_t *ha, ips_scb_t *scb) {
char *sp;
- DBG("ips_reqsen");
+ METHOD_TRACE("ips_reqsen", 1);
sp = (char *) scb->scsi_cmd->sense_buffer;
memset(sp, 0, sizeof(scb->scsi_cmd->sense_buffer));
@@ -3956,7 +4475,7 @@ static void
ips_free(ips_ha_t *ha) {
int i;
- DBG("ips_free");
+ METHOD_TRACE("ips_free", 1);
if (ha) {
if (ha->enq) {
@@ -4004,6 +4523,14 @@ ips_free(ips_ha_t *ha) {
kfree(ha->scbs);
ha->scbs = NULL;
} /* end if */
+
+ /* free memory mapped (if applicable) */
+ if (ha->mem_ptr) {
+ iounmap(ha->ioremap_ptr);
+ ha->ioremap_ptr = NULL;
+ ha->mem_ptr = NULL;
+ ha->mem_addr = 0;
+ }
}
}
@@ -4021,10 +4548,10 @@ ips_allocatescbs(ips_ha_t *ha) {
ips_scb_t *scb_p;
int i;
- DBG("ips_allocatescbs");
+ METHOD_TRACE("ips_allocatescbs", 1);
/* Allocate memory for the CCBs */
- ha->scbs = (ips_scb_t *) kmalloc(ha->max_cmds * sizeof(ips_scb_t), GFP_KERNEL|GFP_DMA);
+ ha->scbs = (ips_scb_t *) kmalloc(ha->max_cmds * sizeof(ips_scb_t), GFP_ATOMIC|GFP_DMA);
memset(ha->scbs, 0, ha->max_cmds * sizeof(ips_scb_t));
@@ -4032,7 +4559,7 @@ ips_allocatescbs(ips_ha_t *ha) {
scb_p = &ha->scbs[i];
/* allocate S/G list */
- scb_p->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_KERNEL|GFP_DMA);
+ scb_p->sg_list = (IPS_SG_LIST *) kmalloc(sizeof(IPS_SG_LIST) * IPS_MAX_SG, GFP_ATOMIC|GFP_DMA);
if (! scb_p->sg_list)
return (0);
@@ -4061,7 +4588,7 @@ static void
ips_init_scb(ips_ha_t *ha, ips_scb_t *scb) {
IPS_SG_LIST *sg_list;
- DBG("ips_init_scb");
+ METHOD_TRACE("ips_init_scb", 1);
if (scb == NULL)
return ;
@@ -4102,7 +4629,7 @@ ips_getscb(ips_ha_t *ha) {
ips_scb_t *scb;
u32 cpu_flags;
- DBG("ips_getscb");
+ METHOD_TRACE("ips_getscb", 1);
IPS_SCB_LOCK(cpu_flags);
if ((scb = ha->scb_freelist) == NULL) {
@@ -4136,7 +4663,7 @@ static void
ips_freescb(ips_ha_t *ha, ips_scb_t *scb) {
u32 cpu_flags;
- DBG("ips_freescb");
+ METHOD_TRACE("ips_freescb", 1);
/* check to make sure this is not our "special" scb */
if (IPS_COMMAND_ID(ha, scb) < (ha->max_cmds - 1)) {
@@ -4149,7 +4676,32 @@ ips_freescb(ips_ha_t *ha, ips_scb_t *scb) {
/****************************************************************************/
/* */
-/* Routine Name: ips_reset_adapter */
+/* Routine Name: ips_isinit_copperhead */
+/* */
+/* Routine Description: */
+/* */
+/* Reset the controller */
+/* */
+/****************************************************************************/
+static int
+ips_isinit_copperhead(ips_ha_t *ha) {
+ u8 scpr;
+ u8 isr;
+
+ METHOD_TRACE("ips_isinit_copperhead", 1);
+
+ isr = inb(ha->io_addr + IPS_REG_HISR);
+ scpr = inb(ha->io_addr + IPS_REG_SCPR);
+
+ if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0))
+ return (0);
+ else
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_isinit_copperhead_memio */
/* */
/* Routine Description: */
/* */
@@ -4157,21 +4709,377 @@ ips_freescb(ips_ha_t *ha, ips_scb_t *scb) {
/* */
/****************************************************************************/
static int
-ips_reset_adapter(ips_ha_t *ha) {
+ips_isinit_copperhead_memio(ips_ha_t *ha) {
+ u8 isr;
+ u8 scpr;
+
+ METHOD_TRACE("ips_is_init_copperhead_memio", 1);
+
+ isr = readb(ha->mem_ptr + IPS_REG_HISR);
+ scpr = readb(ha->mem_ptr + IPS_REG_SCPR);
+
+ if (((isr & IPS_BIT_EI) == 0) && ((scpr & IPS_BIT_EBM) == 0))
+ return (0);
+ else
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_isinit_morpheus */
+/* */
+/* Routine Description: */
+/* */
+/* Reset the controller */
+/* */
+/****************************************************************************/
+static int
+ips_isinit_morpheus(ips_ha_t *ha) {
+ u32 post;
+ u32 bits;
+
+ METHOD_TRACE("ips_is_init_morpheus", 1);
+
+ post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
+ bits = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ if (post == 0)
+ return (0);
+ else if (bits & 0x3)
+ return (0);
+ else
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_enable_int_copperhead */
+/* */
+/* Routine Description: */
+/* Turn on interrupts */
+/* */
+/****************************************************************************/
+static void
+ips_enable_int_copperhead(ips_ha_t *ha) {
+ METHOD_TRACE("ips_enable_int_copperhead", 1);
+
+ outb(ha->io_addr + IPS_REG_HISR, IPS_BIT_EI);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_enable_int_copperhead_memio */
+/* */
+/* Routine Description: */
+/* Turn on interrupts */
+/* */
+/****************************************************************************/
+static void
+ips_enable_int_copperhead_memio(ips_ha_t *ha) {
+ METHOD_TRACE("ips_enable_int_copperhead_memio", 1);
+
+ writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_enable_int_morpheus */
+/* */
+/* Routine Description: */
+/* Turn on interrupts */
+/* */
+/****************************************************************************/
+static void
+ips_enable_int_morpheus(ips_ha_t *ha) {
+ u32 Oimr;
+
+ METHOD_TRACE("ips_enable_int_morpheus", 1);
+
+ Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR);
+ Oimr &= ~0x08;
+ writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_init_copperhead */
+/* */
+/* Routine Description: */
+/* */
+/* Initialize a copperhead controller */
+/* */
+/****************************************************************************/
+static int
+ips_init_copperhead(ips_ha_t *ha) {
u8 Isr;
u8 Cbsp;
u8 PostByte[IPS_MAX_POST_BYTES];
u8 ConfigByte[IPS_MAX_CONFIG_BYTES];
int i, j;
+
+ METHOD_TRACE("ips_init_copperhead", 1);
+
+ for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
+ for (j = 0; j < 45; j++) {
+ Isr = inb(ha->io_addr + IPS_REG_HISR);
+ if (Isr & IPS_BIT_GHI)
+ break;
+
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (j >= 45)
+ /* error occured */
+ return (0);
+
+ PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
+ outb(Isr, ha->io_addr + IPS_REG_HISR);
+ }
+
+ if (PostByte[0] < IPS_GOOD_POST_STATUS) {
+ printk(KERN_WARNING "(%s%d) reset controller fails (post status %x %x).\n",
+ ips_name, ha->host_num, PostByte[0], PostByte[1]);
+
+ return (0);
+ }
+
+ for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
+ for (j = 0; j < 240; j++) {
+ Isr = inb(ha->io_addr + IPS_REG_HISR);
+ if (Isr & IPS_BIT_GHI)
+ break;
+
+ MDELAY(IPS_ONE_SEC); /* 100 msec */
+ }
+
+ if (j >= 240)
+ /* error occured */
+ return (0);
+
+ ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
+ outb(Isr, ha->io_addr + IPS_REG_HISR);
+ }
+
+ for (i = 0; i < 240; i++) {
+ Cbsp = inb(ha->io_addr + IPS_REG_CBSP);
+
+ if ((Cbsp & IPS_BIT_OP) == 0)
+ break;
+
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (i >= 240)
+ /* reset failed */
+ return (0);
+
+ /* setup CCCR */
+ outw(0x1010, ha->io_addr + IPS_REG_CCCR);
+
+ /* Enable busmastering */
+ outb(IPS_BIT_EBM, ha->io_addr + IPS_REG_SCPR);
+
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ /* fix for anaconda64 */
+ outl(0, ha->io_addr + IPS_REG_NDAE);
+
+ /* Enable interrupts */
+ outb(IPS_BIT_EI, ha->io_addr + IPS_REG_HISR);
+
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_init_copperhead_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Initialize a copperhead controller with memory mapped I/O */
+/* */
+/****************************************************************************/
+static int
+ips_init_copperhead_memio(ips_ha_t *ha) {
+ u8 Isr;
+ u8 Cbsp;
+ u8 PostByte[IPS_MAX_POST_BYTES];
+ u8 ConfigByte[IPS_MAX_CONFIG_BYTES];
+ int i, j;
+
+ METHOD_TRACE("ips_init_copperhead_memio", 1);
+
+ for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
+ for (j = 0; j < 45; j++) {
+ Isr = readb(ha->mem_ptr + IPS_REG_HISR);
+ if (Isr & IPS_BIT_GHI)
+ break;
+
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (j >= 45)
+ /* error occured */
+ return (0);
+
+ PostByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
+ writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
+ }
+
+ if (PostByte[0] < IPS_GOOD_POST_STATUS) {
+ printk(KERN_WARNING "(%s%d) reset controller fails (post status %x %x).\n",
+ ips_name, ha->host_num, PostByte[0], PostByte[1]);
+
+ return (0);
+ }
+
+ for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
+ for (j = 0; j < 240; j++) {
+ Isr = readb(ha->mem_ptr + IPS_REG_HISR);
+ if (Isr & IPS_BIT_GHI)
+ break;
+
+ MDELAY(IPS_ONE_SEC); /* 100 msec */
+ }
+
+ if (j >= 240)
+ /* error occured */
+ return (0);
+
+ ConfigByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
+ writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
+ }
+
+ for (i = 0; i < 240; i++) {
+ Cbsp = readb(ha->mem_ptr + IPS_REG_CBSP);
+
+ if ((Cbsp & IPS_BIT_OP) == 0)
+ break;
+
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (i >= 240)
+ /* error occured */
+ return (0);
+
+ /* setup CCCR */
+ writel(0x1010, ha->mem_ptr + IPS_REG_CCCR);
+
+ /* Enable busmastering */
+ writeb(IPS_BIT_EBM, ha->mem_ptr + IPS_REG_SCPR);
+
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ /* fix for anaconda64 */
+ writel(0, ha->mem_ptr + IPS_REG_NDAE);
+
+ /* Enable interrupts */
+ writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR);
+
+ /* if we get here then everything went OK */
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_init_morpheus */
+/* */
+/* Routine Description: */
+/* */
+/* Initialize a morpheus controller */
+/* */
+/****************************************************************************/
+static int
+ips_init_morpheus(ips_ha_t *ha) {
+ u32 Post;
+ u32 Config;
+ u32 Isr;
+ u32 Oimr;
+ int i;
+
+ METHOD_TRACE("ips_init_morpheus", 1);
+
+ /* Wait up to 45 secs for Post */
+ for (i = 0; i < 45; i++) {
+ Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ if (Isr & IPS_BIT_I960_MSG0I)
+ break;
+
+ MDELAY(IPS_ONE_SEC);
+ }
+
+ if (i >= 45) {
+ /* error occured */
+ printk(KERN_WARNING "(%s%d) timeout waiting for post.\n",
+ ips_name, ha->host_num);
+
+ return (0);
+ }
+
+ Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
+
+ /* Clear the interrupt bit */
+ Isr = (u32) IPS_BIT_I960_MSG0I;
+ writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ if (Post < (IPS_GOOD_POST_STATUS << 8)) {
+ printk(KERN_WARNING "(%s%d) reset controller fails (post status %x).\n",
+ ips_name, ha->host_num, Post);
+
+ return (0);
+ }
+
+ /* Wait up to 240 secs for config bytes */
+ for (i = 0; i < 240; i++) {
+ Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ if (Isr & IPS_BIT_I960_MSG1I)
+ break;
+
+ MDELAY(IPS_ONE_SEC); /* 100 msec */
+ }
+
+ if (i >= 240) {
+ /* error occured */
+ printk(KERN_WARNING "(%s%d) timeout waiting for config.\n",
+ ips_name, ha->host_num);
+
+ return (0);
+ }
+
+ Config = readl(ha->mem_ptr + IPS_REG_I960_MSG1);
+
+ /* Clear interrupt bit */
+ Isr = (u32) IPS_BIT_I960_MSG1I;
+ writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ /* Turn on the interrupts */
+ Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR);
+ Oimr &= ~0x8;
+ writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
+
+ /* if we get here then everything went OK */
+ return (1);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_reset_copperhead */
+/* */
+/* Routine Description: */
+/* */
+/* Reset the controller */
+/* */
+/****************************************************************************/
+static int
+ips_reset_copperhead(ips_ha_t *ha) {
int reset_counter;
u32 cpu_flags;
- DBG("ips_reset_adapter");
+ METHOD_TRACE("ips_reset_copperhead", 1);
-#if IPS_DEBUG >= 1
- printk(KERN_WARNING "ips_reset_adapter: io addr: %x, irq: %d\n",
- ha->io_addr, ha->irq);
-#endif
+ DEBUG_VAR(1, "(%s%d) ips_reset_copperhead: io addr: %x, irq: %d",
+ ips_name, ha->host_num, ha->io_addr, ha->irq);
IPS_HA_LOCK(cpu_flags);
@@ -4185,109 +5093,107 @@ ips_reset_adapter(ips_ha_t *ha) {
outb(0, ha->io_addr + IPS_REG_SCPR);
MDELAY(IPS_ONE_SEC);
- for (i = 0; i < IPS_MAX_POST_BYTES; i++) {
- for (j = 0; j < 45; j++) {
- Isr = inb(ha->io_addr + IPS_REG_HISR);
- if (Isr & IPS_BIT_GHI)
- break;
-
- MDELAY(IPS_ONE_SEC);
- }
-
- if (j >= 45) {
- /* error occured */
- if (reset_counter < 2)
- continue;
- else {
- /* reset failed */
- IPS_HA_UNLOCK(cpu_flags);
-
- return (0);
- }
- }
+ if ((*ha->func.init)(ha))
+ break;
+ else if (reset_counter >= 2) {
+ IPS_HA_UNLOCK(cpu_flags);
- PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
- outb(Isr, ha->io_addr + IPS_REG_HISR);
+ return (0);
}
+ }
- if (PostByte[0] < IPS_GOOD_POST_STATUS) {
- printk("(%s%d) reset controller fails (post status %x %x).\n",
- ips_name, ha->host_num, PostByte[0], PostByte[1]);
+ IPS_HA_UNLOCK(cpu_flags);
- IPS_HA_UNLOCK(cpu_flags);
+ return (1);
+}
- return (0);
- }
+/****************************************************************************/
+/* */
+/* Routine Name: ips_reset_copperhead_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Reset the controller */
+/* */
+/****************************************************************************/
+static int
+ips_reset_copperhead_memio(ips_ha_t *ha) {
+ int reset_counter;
+ u32 cpu_flags;
- for (i = 0; i < IPS_MAX_CONFIG_BYTES; i++) {
- for (j = 0; j < 240; j++) {
- Isr = inb(ha->io_addr + IPS_REG_HISR);
- if (Isr & IPS_BIT_GHI)
- break;
+ METHOD_TRACE("ips_reset_copperhead_memio", 1);
- MDELAY(IPS_ONE_SEC); /* 100 msec */
- }
+ DEBUG_VAR(1, "(%s%d) ips_reset_copperhead_memio: mem addr: %x, irq: %d",
+ ips_name, ha->host_num, ha->mem_addr, ha->irq);
- if (j >= 240) {
- /* error occured */
- if (reset_counter < 2)
- continue;
- else {
- /* reset failed */
- IPS_HA_UNLOCK(cpu_flags);
+ IPS_HA_LOCK(cpu_flags);
- return (0);
- }
- }
+ reset_counter = 0;
- ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
- outb(Isr, ha->io_addr + IPS_REG_HISR);
- }
+ while (reset_counter < 2) {
+ reset_counter++;
- if (ConfigByte[0] == 0 && ConfigByte[1] == 2) {
- printk("(%s%d) reset controller fails (status %x %x).\n",
- ips_name, ha->host_num, ConfigByte[0], ConfigByte[1]);
+ writeb(IPS_BIT_RST, ha->mem_ptr + IPS_REG_SCPR);
+ MDELAY(IPS_ONE_SEC);
+ writeb(0, ha->mem_ptr + IPS_REG_SCPR);
+ MDELAY(IPS_ONE_SEC);
+ if ((*ha->func.init)(ha))
+ break;
+ else if (reset_counter >= 2) {
IPS_HA_UNLOCK(cpu_flags);
return (0);
}
+ }
- for (i = 0; i < 240; i++) {
- Cbsp = inb(ha->io_addr + IPS_REG_CBSP);
+ IPS_HA_UNLOCK(cpu_flags);
- if ((Cbsp & IPS_BIT_OP) == 0)
- break;
+ return (1);
+}
- MDELAY(IPS_ONE_SEC);
- }
+/****************************************************************************/
+/* */
+/* Routine Name: ips_reset_morpheus */
+/* */
+/* Routine Description: */
+/* */
+/* Reset the controller */
+/* */
+/****************************************************************************/
+static int
+ips_reset_morpheus(ips_ha_t *ha) {
+ int reset_counter;
+ u8 junk;
+ u32 cpu_flags;
- if (i >= 240) {
- /* error occured */
- if (reset_counter < 2)
- continue;
- else {
- /* reset failed */
- IPS_HA_UNLOCK(cpu_flags);
+ METHOD_TRACE("ips_reset_morpheus", 1);
- return (0);
- }
- }
+ DEBUG_VAR(1, "(%s%d) ips_reset_morpheus: mem addr: %x, irq: %d",
+ ips_name, ha->host_num, ha->mem_addr, ha->irq);
- /* setup CCCR */
- outw(0x1010, ha->io_addr + IPS_REG_CCCR);
+ IPS_HA_LOCK(cpu_flags);
+
+ reset_counter = 0;
- /* Enable busmastering */
- outb(IPS_BIT_EBM, ha->io_addr + IPS_REG_SCPR);
+ while (reset_counter < 2) {
+ reset_counter++;
- /* setup status queues */
- ips_statinit(ha);
+ writel(0x80000000, ha->mem_ptr + IPS_REG_I960_IDR);
- /* Enable interrupts */
- outb(IPS_BIT_EI, ha->io_addr + IPS_REG_HISR);
+ /* Delay for 300 msec */
+ MDELAY(300 * IPS_ONE_MSEC);
- /* if we get here then everything went OK */
- break;
+ /* Do a PCI config read to wait for adapter */
+ pci_read_config_byte(ha->pcidev, 4, &junk);
+
+ if ((*ha->func.init)(ha))
+ break;
+ else if (reset_counter >= 2) {
+ IPS_HA_UNLOCK(cpu_flags);
+
+ return (0);
+ }
}
IPS_HA_UNLOCK(cpu_flags);
@@ -4308,7 +5214,7 @@ static void
ips_statinit(ips_ha_t *ha) {
u32 phys_status_start;
- DBG("ips_statinit");
+ METHOD_TRACE("ips_statinit", 1);
ha->adapt->p_status_start = ha->adapt->status;
ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
@@ -4326,18 +5232,45 @@ ips_statinit(ips_ha_t *ha) {
/****************************************************************************/
/* */
-/* Routine Name: ips_statupd */
+/* Routine Name: ips_statinit_memio */
/* */
/* Routine Description: */
/* */
-/* Remove an element from the status queue */
+/* Initialize the status queues on the controller */
/* */
/****************************************************************************/
-static int
-ips_statupd(ips_ha_t *ha) {
- int command_id;
+static void
+ips_statinit_memio(ips_ha_t *ha) {
+ u32 phys_status_start;
+
+ METHOD_TRACE("ips_statinit_memio", 1);
+
+ ha->adapt->p_status_start = ha->adapt->status;
+ ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
+ ha->adapt->p_status_tail = ha->adapt->status;
- DBG("ips_statupd");
+ phys_status_start = VIRT_TO_BUS(ha->adapt->status);
+ writel(phys_status_start, ha->mem_ptr + IPS_REG_SQSR);
+ writel(phys_status_start + IPS_STATUS_Q_SIZE, ha->mem_ptr + IPS_REG_SQER);
+ writel(phys_status_start + IPS_STATUS_SIZE, ha->mem_ptr + IPS_REG_SQHR);
+ writel(phys_status_start, ha->mem_ptr + IPS_REG_SQTR);
+
+ ha->adapt->hw_status_start = phys_status_start;
+ ha->adapt->hw_status_tail = phys_status_start;
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_statupd_copperhead */
+/* */
+/* Routine Description: */
+/* */
+/* Remove an element from the status queue */
+/* */
+/****************************************************************************/
+static u32
+ips_statupd_copperhead(ips_ha_t *ha) {
+ METHOD_TRACE("ips_statupd_copperhead", 1);
if (ha->adapt->p_status_tail != ha->adapt->p_status_end) {
ha->adapt->p_status_tail++;
@@ -4349,47 +5282,87 @@ ips_statupd(ips_ha_t *ha) {
outl(ha->adapt->hw_status_tail, ha->io_addr + IPS_REG_SQTR);
- command_id = ha->adapt->p_status_tail->command_id;
+ return (ha->adapt->p_status_tail->value);
+}
- return (command_id);
+/****************************************************************************/
+/* */
+/* Routine Name: ips_statupd_copperhead_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Remove an element from the status queue */
+/* */
+/****************************************************************************/
+static u32
+ips_statupd_copperhead_memio(ips_ha_t *ha) {
+ METHOD_TRACE("ips_statupd_copperhead_memio", 1);
+
+ if (ha->adapt->p_status_tail != ha->adapt->p_status_end) {
+ ha->adapt->p_status_tail++;
+ ha->adapt->hw_status_tail += sizeof(IPS_STATUS);
+ } else {
+ ha->adapt->p_status_tail = ha->adapt->p_status_start;
+ ha->adapt->hw_status_tail = ha->adapt->hw_status_start;
+ }
+
+ writel(ha->adapt->hw_status_tail, ha->mem_ptr + IPS_REG_SQTR);
+
+ return (ha->adapt->p_status_tail->value);
}
/****************************************************************************/
/* */
-/* Routine Name: ips_issue */
+/* Routine Name: ips_statupd_morpheus */
/* */
/* Routine Description: */
/* */
-/* Send a command down to the controller */
+/* Remove an element from the status queue */
/* */
-/* ASSUMED to be called from within a lock */
+/****************************************************************************/
+static u32
+ips_statupd_morpheus(ips_ha_t *ha) {
+ u32 val;
+
+ METHOD_TRACE("ips_statupd_morpheus", 1);
+
+ val = readl(ha->mem_ptr + IPS_REG_I2O_OUTMSGQ);
+
+ return (val);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_issue_copperhead */
+/* */
+/* Routine Description: */
+/* */
+/* Send a command down to the controller */
/* */
/****************************************************************************/
static int
-ips_issue(ips_ha_t *ha, ips_scb_t *scb) {
+ips_issue_copperhead(ips_ha_t *ha, ips_scb_t *scb) {
u32 TimeOut;
u16 val;
u32 cpu_flags;
- DBG("ips_issue");
-
-#if IPS_DEBUG >= 10
- if (scb->scsi_cmd)
- printk(KERN_NOTICE "%s: ips_issue: cmd 0x%X id %d (%d %d %d)\n",
- ips_name,
- scb->cdb[0],
- scb->cmd.basic_io.command_id,
- scb->bus,
- scb->target_id,
- scb->lun);
- else
- printk(KERN_NOTICE "%s: ips_issue: logical cmd id %d\n",
- ips_name,
- scb->cmd.basic_io.command_id);
-#if IPS_DEBUG >= 11
- MDELAY(IPS_ONE_SEC);
-#endif
-#endif
+ METHOD_TRACE("ips_issue_copperhead", 1);
+
+ if (scb->scsi_cmd) {
+ DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+ ips_name,
+ ha->host_num,
+ scb->cdb[0],
+ scb->cmd.basic_io.command_id,
+ scb->bus,
+ scb->target_id,
+ scb->lun);
+ } else {
+ DEBUG_VAR(2, KERN_NOTICE "(%s%d) ips_issue: logical cmd id %d",
+ ips_name,
+ ha->host_num,
+ scb->cmd.basic_io.command_id);
+ }
IPS_HA_LOCK(cpu_flags);
@@ -4423,7 +5396,150 @@ ips_issue(ips_ha_t *ha, ips_scb_t *scb) {
/****************************************************************************/
/* */
-/* Routine Name: ips_isintr */
+/* Routine Name: ips_issue_copperhead_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Send a command down to the controller */
+/* */
+/****************************************************************************/
+static int
+ips_issue_copperhead_memio(ips_ha_t *ha, ips_scb_t *scb) {
+ u32 TimeOut;
+ u32 val;
+ u32 cpu_flags;
+
+ METHOD_TRACE("ips_issue_copperhead_memio", 1);
+
+ if (scb->scsi_cmd) {
+ DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+ ips_name,
+ ha->host_num,
+ scb->cdb[0],
+ scb->cmd.basic_io.command_id,
+ scb->bus,
+ scb->target_id,
+ scb->lun);
+ } else {
+ DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
+ ips_name,
+ ha->host_num,
+ scb->cmd.basic_io.command_id);
+ }
+
+ IPS_HA_LOCK(cpu_flags);
+
+ TimeOut = 0;
+
+ while ((val = readl(ha->mem_ptr + IPS_REG_CCCR)) & IPS_BIT_SEM) {
+ UDELAY(1000);
+
+ if (++TimeOut >= IPS_SEM_TIMEOUT) {
+ if (!(val & IPS_BIT_START_STOP))
+ break;
+
+ printk(KERN_WARNING "(%s%d) ips_issue val [0x%x].\n",
+ ips_name, ha->host_num, val);
+ printk(KERN_WARNING "(%s%d) ips_issue semaphore chk timeout.\n",
+ ips_name, ha->host_num);
+
+ IPS_HA_UNLOCK(cpu_flags);
+
+ return (IPS_FAILURE);
+ } /* end if */
+ } /* end while */
+
+ writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_CCSAR);
+ writel(IPS_BIT_START_CMD, ha->mem_ptr + IPS_REG_CCCR);
+
+ IPS_HA_UNLOCK(cpu_flags);
+
+ return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_issue_i2o */
+/* */
+/* Routine Description: */
+/* */
+/* Send a command down to the controller */
+/* */
+/****************************************************************************/
+static int
+ips_issue_i2o(ips_ha_t *ha, ips_scb_t *scb) {
+ u32 cpu_flags;
+
+ METHOD_TRACE("ips_issue_i2o", 1);
+
+ if (scb->scsi_cmd) {
+ DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+ ips_name,
+ ha->host_num,
+ scb->cdb[0],
+ scb->cmd.basic_io.command_id,
+ scb->bus,
+ scb->target_id,
+ scb->lun);
+ } else {
+ DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
+ ips_name,
+ ha->host_num,
+ scb->cmd.basic_io.command_id);
+ }
+
+ IPS_HA_LOCK(cpu_flags);
+
+ outl(scb->scb_busaddr, ha->io_addr + IPS_REG_I2O_INMSGQ);
+
+ IPS_HA_UNLOCK(cpu_flags);
+
+ return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_issue_i2o_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Send a command down to the controller */
+/* */
+/****************************************************************************/
+static int
+ips_issue_i2o_memio(ips_ha_t *ha, ips_scb_t *scb) {
+ u32 cpu_flags;
+
+ METHOD_TRACE("ips_issue_i2o_memio", 1);
+
+ if (scb->scsi_cmd) {
+ DEBUG_VAR(2, "(%s%d) ips_issue: cmd 0x%X id %d (%d %d %d)",
+ ips_name,
+ ha->host_num,
+ scb->cdb[0],
+ scb->cmd.basic_io.command_id,
+ scb->bus,
+ scb->target_id,
+ scb->lun);
+ } else {
+ DEBUG_VAR(2, "(%s%d) ips_issue: logical cmd id %d",
+ ips_name,
+ ha->host_num,
+ scb->cmd.basic_io.command_id);
+ }
+
+ IPS_HA_LOCK(cpu_flags);
+
+ writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_I2O_INMSGQ);
+
+ IPS_HA_UNLOCK(cpu_flags);
+
+ return (IPS_SUCCESS);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_isintr_copperhead */
/* */
/* Routine Description: */
/* */
@@ -4431,10 +5547,10 @@ ips_issue(ips_ha_t *ha, ips_scb_t *scb) {
/* */
/****************************************************************************/
static int
-ips_isintr(ips_ha_t *ha) {
+ips_isintr_copperhead(ips_ha_t *ha) {
u8 Isr;
- DBG("ips_isintr");
+ METHOD_TRACE("ips_isintr_copperhead", 2);
Isr = inb(ha->io_addr + IPS_REG_HISR);
@@ -4455,6 +5571,61 @@ ips_isintr(ips_ha_t *ha) {
/****************************************************************************/
/* */
+/* Routine Name: ips_isintr_copperhead_memio */
+/* */
+/* Routine Description: */
+/* */
+/* Test to see if an interrupt is for us */
+/* */
+/****************************************************************************/
+static int
+ips_isintr_copperhead_memio(ips_ha_t *ha) {
+ u8 Isr;
+
+ METHOD_TRACE("ips_isintr_memio", 2);
+
+ Isr = readb(ha->mem_ptr + IPS_REG_HISR);
+
+ if (Isr == 0xFF)
+ /* ?!?! Nothing really there */
+ return (0);
+
+ if (Isr & IPS_BIT_SCE)
+ return (1);
+ else if (Isr & (IPS_BIT_SQO | IPS_BIT_GHI)) {
+ /* status queue overflow or GHI */
+ /* just clear the interrupt */
+ writeb(Isr, ha->mem_ptr + IPS_REG_HISR);
+ }
+
+ return (0);
+}
+
+/****************************************************************************/
+/* */
+/* Routine Name: ips_isintr_morpheus */
+/* */
+/* Routine Description: */
+/* */
+/* Test to see if an interrupt is for us */
+/* */
+/****************************************************************************/
+static int
+ips_isintr_morpheus(ips_ha_t *ha) {
+ u32 Isr;
+
+ METHOD_TRACE("ips_isintr_morpheus", 2);
+
+ Isr = readl(ha->mem_ptr + IPS_REG_I2O_HIR);
+
+ if (Isr & IPS_BIT_I2O_OPQI)
+ return (1);
+ else
+ return (0);
+}
+
+/****************************************************************************/
+/* */
/* Routine Name: ips_wait */
/* */
/* Routine Description: */
@@ -4467,7 +5638,7 @@ ips_wait(ips_ha_t *ha, int time, int intr) {
int ret;
u8 done;
- DBG("ips_wait");
+ METHOD_TRACE("ips_wait", 1);
ret = IPS_FAILURE;
done = FALSE;
@@ -4502,7 +5673,7 @@ ips_wait(ips_ha_t *ha, int time, int intr) {
while (test_and_set_bit(IPS_IN_INTR, &ha->flags))
UDELAY(1000);
- ips_intr(ha);
+ (*ha->func.intr)(ha);
clear_bit(IPS_IN_INTR, &ha->flags);
} else if (intr == IPS_INTR_HAL) {
@@ -4528,7 +5699,7 @@ ips_wait(ips_ha_t *ha, int time, int intr) {
while (test_and_set_bit(IPS_IN_INTR, &ha->flags))
UDELAY(1000);
- ips_intr(ha);
+ (*ha->func.intr)(ha);
clear_bit(IPS_IN_INTR, &ha->flags);
@@ -4553,7 +5724,7 @@ ips_wait(ips_ha_t *ha, int time, int intr) {
/****************************************************************************/
static int
ips_write_driver_status(ips_ha_t *ha, int intr) {
- DBG("ips_write_driver_status");
+ METHOD_TRACE("ips_write_driver_status", 1);
if (!ips_readwrite_page5(ha, FALSE, intr)) {
printk(KERN_WARNING "(%s%d) unable to read NVRAM page 5.\n",
@@ -4565,21 +5736,18 @@ ips_write_driver_status(ips_ha_t *ha, int intr) {
/* check to make sure the page has a valid */
/* signature */
if (ha->nvram->signature != IPS_NVRAM_P5_SIG) {
-#if IPS_DEBUG >= 1
- printk("(%s%d) NVRAM page 5 has an invalid signature: %X.\n",
- ips_name, ha->host_num, ha->nvram->signature);
-#endif
+ DEBUG_VAR(1, "(%s%d) NVRAM page 5 has an invalid signature: %X.",
+ ips_name, ha->host_num, ha->nvram->signature);
+
return (1);
}
-#if IPS_DEBUG >= 2
- printk("(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.\n",
- ips_name, ha->host_num, ha->nvram->adapter_type, ha->nvram->adapter_slot,
- ha->nvram->bios_high[0], ha->nvram->bios_high[1],
- ha->nvram->bios_high[2], ha->nvram->bios_high[3],
- ha->nvram->bios_low[0], ha->nvram->bios_low[1],
- ha->nvram->bios_low[2], ha->nvram->bios_low[3]);
-#endif
+ DEBUG_VAR(2, "(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.",
+ ips_name, ha->host_num, ha->nvram->adapter_type, ha->nvram->adapter_slot,
+ ha->nvram->bios_high[0], ha->nvram->bios_high[1],
+ ha->nvram->bios_high[2], ha->nvram->bios_high[3],
+ ha->nvram->bios_low[0], ha->nvram->bios_low[1],
+ ha->nvram->bios_low[2], ha->nvram->bios_low[3]);
/* save controller type */
ha->ad_type = ha->nvram->adapter_type;
@@ -4614,7 +5782,7 @@ ips_read_adapter_status(ips_ha_t *ha, int intr) {
ips_scb_t *scb;
int ret;
- DBG("ips_read_adapter_status");
+ METHOD_TRACE("ips_read_adapter_status", 1);
scb = &ha->scbs[ha->max_cmds-1];
@@ -4633,8 +5801,9 @@ ips_read_adapter_status(ips_ha_t *ha, int intr) {
scb->cmd.basic_io.reserved = 0;
/* send command */
- ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr);
- if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+ if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
+ (ret == IPS_SUCCESS_IMM) ||
+ ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
return (0);
return (1);
@@ -4654,7 +5823,7 @@ ips_read_subsystem_parameters(ips_ha_t *ha, int intr) {
ips_scb_t *scb;
int ret;
- DBG("ips_read_subsystem_parameters");
+ METHOD_TRACE("ips_read_subsystem_parameters", 1);
scb = &ha->scbs[ha->max_cmds-1];
@@ -4673,8 +5842,9 @@ ips_read_subsystem_parameters(ips_ha_t *ha, int intr) {
scb->cmd.basic_io.reserved = 0;
/* send command */
- ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr);
- if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+ if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
+ (ret == IPS_SUCCESS_IMM) ||
+ ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
return (0);
return (1);
@@ -4695,7 +5865,7 @@ ips_read_config(ips_ha_t *ha, int intr) {
int i;
int ret;
- DBG("ips_read_config");
+ METHOD_TRACE("ips_read_config", 1);
/* set defaults for initiator IDs */
ha->conf->init_id[0] = IPS_ADAPTER_ID;
@@ -4745,7 +5915,7 @@ ips_readwrite_page5(ips_ha_t *ha, int write, int intr) {
ips_scb_t *scb;
int ret;
- DBG("ips_readwrite_page5");
+ METHOD_TRACE("ips_readwrite_page5", 1);
scb = &ha->scbs[ha->max_cmds-1];
@@ -4789,7 +5959,7 @@ ips_clear_adapter(ips_ha_t *ha, int intr) {
ips_scb_t *scb;
int ret;
- DBG("ips_clear_adapter");
+ METHOD_TRACE("ips_clear_adapter", 1);
scb = &ha->scbs[ha->max_cmds-1];
@@ -4807,8 +5977,9 @@ ips_clear_adapter(ips_ha_t *ha, int intr) {
scb->cmd.config_sync.reserved3 = 0;
/* issue command */
- ret = ips_send_wait(ha, scb, ips_reset_timeout, intr);
- if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+ if (((ret = ips_send_wait(ha, scb, ips_reset_timeout, intr)) == IPS_FAILURE) ||
+ (ret == IPS_SUCCESS_IMM) ||
+ ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
return (0);
/* send unlock stripe command */
@@ -4826,8 +5997,9 @@ ips_clear_adapter(ips_ha_t *ha, int intr) {
scb->cmd.unlock_stripe.reserved3 = 0;
/* issue command */
- ret = ips_send_wait(ha, scb, ips_reset_timeout, intr);
- if ((ret == IPS_FAILURE) || (ret == IPS_SUCCESS_IMM))
+ if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
+ (ret == IPS_SUCCESS_IMM) ||
+ ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1))
return (0);
return (1);
@@ -4846,7 +6018,7 @@ static void
ips_ffdc_reset(ips_ha_t *ha, int intr) {
ips_scb_t *scb;
- DBG("ips_ffdc_reset");
+ METHOD_TRACE("ips_ffdc_reset", 1);
scb = &ha->scbs[ha->max_cmds-1];
@@ -4879,12 +6051,10 @@ static void
ips_ffdc_time(ips_ha_t *ha, int intr) {
ips_scb_t *scb;
- DBG("ips_ffdc_time");
+ METHOD_TRACE("ips_ffdc_time", 1);
-#if IPS_DEBUG >= 1
- printk(KERN_NOTICE "(%s%d) Sending time update.\n",
- ips_name, ha->host_num);
-#endif
+ DEBUG_VAR(1, "(%s%d) Sending time update.",
+ ips_name, ha->host_num);
scb = &ha->scbs[ha->max_cmds-1];
@@ -4933,6 +6103,8 @@ ips_fix_ffdc_time(ips_ha_t *ha, ips_scb_t *scb, time_t current_time) {
{30, 30},
{31, 31} };
+ METHOD_TRACE("ips_fix_ffdc_time", 1);
+
days = current_time / IPS_SECS_DAY;
rem = current_time % IPS_SECS_DAY;
@@ -4981,6 +6153,8 @@ ips_erase_bios(ips_ha_t *ha) {
int timeout;
u8 status;
+ METHOD_TRACE("ips_erase_bios", 1);
+
/* Clear the status register */
outl(0, ha->io_addr + IPS_REG_FLAP);
if (ha->revision_id == IPS_REVID_TROMBONE64)
@@ -5077,6 +6251,115 @@ ips_erase_bios(ips_ha_t *ha) {
/****************************************************************************/
/* */
+/* Routine Name: ips_erase_bios_memio */
+/* */
+/* Routine Description: */
+/* Erase the BIOS on the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_erase_bios_memio(ips_ha_t *ha) {
+ int timeout;
+ u8 status;
+
+ METHOD_TRACE("ips_erase_bios_memio", 1);
+
+ /* Clear the status register */
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ writeb(0x50, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ /* Erase Setup */
+ writeb(0x20, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ /* Erase Confirm */
+ writeb(0xD0, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ /* Erase Status */
+ writeb(0x70, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ timeout = 80000; /* 80 seconds */
+
+ while (timeout > 0) {
+ if (ha->revision_id == IPS_REVID_TROMBONE64) {
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ UDELAY(5); /* 5 us */
+ }
+
+ status = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+ if (status & 0x80)
+ break;
+
+ MDELAY(1);
+ timeout--;
+ }
+
+ /* check for timeout */
+ if (timeout <= 0) {
+ /* timeout */
+
+ /* try to suspend the erase */
+ writeb(0xB0, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ /* wait for 10 seconds */
+ timeout = 10000;
+ while (timeout > 0) {
+ if (ha->revision_id == IPS_REVID_TROMBONE64) {
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ UDELAY(5); /* 5 us */
+ }
+
+ status = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+ if (status & 0xC0)
+ break;
+
+ MDELAY(1);
+ timeout--;
+ }
+
+ return (1);
+ }
+
+ /* check for valid VPP */
+ if (status & 0x08)
+ /* VPP failure */
+ return (1);
+
+ /* check for succesful flash */
+ if (status & 0x30)
+ /* sequence error */
+ return (1);
+
+ /* Otherwise, we were successful */
+ /* clear status */
+ writeb(0x50, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ /* enable reads */
+ writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ return (0);
+}
+
+/****************************************************************************/
+/* */
/* Routine Name: ips_program_bios */
/* */
/* Routine Description: */
@@ -5089,6 +6372,8 @@ ips_program_bios(ips_ha_t *ha, char *buffer, int buffersize) {
int timeout;
u8 status;
+ METHOD_TRACE("ips_program_bios", 1);
+
for (i = 0; i < buffersize; i++) {
/* write a byte */
outl(i, ha->io_addr + IPS_REG_FLAP);
@@ -5162,6 +6447,93 @@ ips_program_bios(ips_ha_t *ha, char *buffer, int buffersize) {
/****************************************************************************/
/* */
+/* Routine Name: ips_program_bios_memio */
+/* */
+/* Routine Description: */
+/* Program the BIOS on the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_program_bios_memio(ips_ha_t *ha, char *buffer, int buffersize) {
+ int i;
+ int timeout;
+ u8 status;
+
+ METHOD_TRACE("ips_program_bios_memio", 1);
+
+ for (i = 0; i < buffersize; i++) {
+ /* write a byte */
+ writel(i, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ writeb(0x40, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ writeb(buffer[i], ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ /* wait up to one second */
+ timeout = 1000;
+ while (timeout > 0) {
+ if (ha->revision_id == IPS_REVID_TROMBONE64) {
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ UDELAY(5); /* 5 us */
+ }
+
+ status = readb(ha->mem_ptr + IPS_REG_FLDP);
+
+ if (status & 0x80)
+ break;
+
+ MDELAY(1);
+ timeout--;
+ }
+
+ if (timeout == 0) {
+ /* timeout error */
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ return (1);
+ }
+
+ /* check the status */
+ if (status & 0x18) {
+ /* programming error */
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ return (1);
+ }
+ } /* end for */
+
+ /* Enable reading */
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ writeb(0xFF, ha->mem_ptr + IPS_REG_FLDP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ return (0);
+}
+
+/****************************************************************************/
+/* */
/* Routine Name: ips_verify_bios */
/* */
/* Routine Description: */
@@ -5173,6 +6545,8 @@ ips_verify_bios(ips_ha_t *ha, char *buffer, int buffersize) {
u8 checksum;
int i;
+ METHOD_TRACE("ips_verify_bios", 1);
+
/* test 1st byte */
outl(0, ha->io_addr + IPS_REG_FLAP);
if (ha->revision_id == IPS_REVID_TROMBONE64)
@@ -5205,13 +6579,56 @@ ips_verify_bios(ips_ha_t *ha, char *buffer, int buffersize) {
return (0);
}
-#if defined (MODULE)
+/****************************************************************************/
+/* */
+/* Routine Name: ips_verify_bios_memio */
+/* */
+/* Routine Description: */
+/* Verify the BIOS on the adapter */
+/* */
+/****************************************************************************/
+static int
+ips_verify_bios_memio(ips_ha_t *ha, char *buffer, int buffersize) {
+ u8 checksum;
+ int i;
-Scsi_Host_Template driver_template = IPS;
+ METHOD_TRACE("ips_verify_bios_memio", 1);
- #include "scsi_module.c"
+ /* test 1st byte */
+ writel(0, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0x55)
+ return (1);
+
+ writel(1, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+ if (readb(ha->mem_ptr + IPS_REG_FLDP) != 0xAA)
+ return (1);
+
+ checksum = 0xff;
+ for (i = 2; i < buffersize; i++) {
+
+ writel(i, ha->mem_ptr + IPS_REG_FLAP);
+ if (ha->revision_id == IPS_REVID_TROMBONE64)
+ UDELAY(5); /* 5 us */
+
+ checksum = (u8) checksum + readb(ha->mem_ptr + IPS_REG_FLDP);
+ }
+
+ if (checksum != 0)
+ /* failure */
+ return (1);
+ else
+ /* success */
+ return (0);
+}
+
+static Scsi_Host_Template driver_template = IPS;
+#include "scsi_module.c"
-#endif
/*
diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h
index e93fe8153..e55b26a0e 100644
--- a/drivers/scsi/ips.h
+++ b/drivers/scsi/ips.h
@@ -1,5 +1,5 @@
/*****************************************************************************/
-/* ips.h -- driver for the IBM ServeRAID adapter */
+/* ips.h -- driver for the IBM ServeRAID controller */
/* */
/* Written By: Keith Mitchell, IBM Corporation */
/* */
@@ -53,8 +53,6 @@
/* Prototypes */
extern int ips_detect(Scsi_Host_Template *);
extern int ips_release(struct Scsi_Host *);
- extern int ips_abort(Scsi_Cmnd *);
- extern int ips_reset(Scsi_Cmnd *, unsigned int);
extern int ips_eh_abort(Scsi_Cmnd *);
extern int ips_eh_reset(Scsi_Cmnd *);
extern int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *));
@@ -71,7 +69,21 @@
#define IPS_HA(x) ((ips_ha_t *) x->hostdata)
#define IPS_COMMAND_ID(ha, scb) (int) (scb - ha->scbs)
-
+ #define IPS_IS_TROMBONE(ha) (((ha->device_id == IPS_COPPERHEAD_DEVICEID) && \
+ (ha->revision_id >= IPS_REVID_TROMBONE32) && \
+ (ha->revision_id <= IPS_REVID_TROMBONE64)) ? 1 : 0)
+ #define IPS_IS_CLARINET(ha) (((ha->device_id == IPS_COPPERHEAD_DEVICEID) && \
+ (ha->revision_id >= IPS_REVID_CLARINETP1) && \
+ (ha->revision_id <= IPS_REVID_CLARINETP3)) ? 1 : 0)
+ #define IPS_IS_MORPHEUS(ha) (ha->device_id == IPS_MORPHEUS_DEVICEID)
+ #define IPS_USE_I2O_DELIVER(ha) ((IPS_IS_MORPHEUS(ha) || \
+ (IPS_IS_TROMBONE(ha) && \
+ (ips_force_i2o))) ? 1 : 0)
+ #define IPS_USE_I2O_STATUS(ha) (IPS_IS_MORPHEUS(ha))
+ #define IPS_USE_MEMIO(ha) ((IPS_IS_MORPHEUS(ha) || \
+ ((IPS_IS_TROMBONE(ha) || IPS_IS_CLARINET(ha)) && \
+ (ips_force_memio))) ? 1 : 0)
+
#ifndef VIRT_TO_BUS
#define VIRT_TO_BUS(x) (unsigned int)virt_to_bus((void *) x)
#endif
@@ -79,7 +91,7 @@
#ifndef UDELAY
#define UDELAY udelay
#endif
-
+
#ifndef MDELAY
#define MDELAY mdelay
#endif
@@ -87,19 +99,15 @@
#ifndef verify_area_20
#define verify_area_20(t,a,sz) (0) /* success */
#endif
-
+
#ifndef PUT_USER
#define PUT_USER put_user
#endif
-
+
#ifndef __PUT_USER
#define __PUT_USER __put_user
#endif
-
- #ifndef PUT_USER_RET
- #define PUT_USER_RET put_user_ret
- #endif
-
+
#ifndef GET_USER
#define GET_USER get_user
#endif
@@ -108,10 +116,6 @@
#define __GET_USER __get_user
#endif
- #ifndef GET_USER_RET
- #define GET_USER_RET get_user_ret
- #endif
-
/*
* Lock macros
*/
@@ -137,6 +141,14 @@
#define IPS_REG_CBSP 0x07 /* CBSP register */
#define IPS_REG_FLAP 0x18 /* Flash address port */
#define IPS_REG_FLDP 0x1C /* Flash data port */
+ #define IPS_REG_NDAE 0x38 /* Anaconda 64 NDAE Register */
+ #define IPS_REG_I2O_INMSGQ 0x40 /* I2O Inbound Message Queue */
+ #define IPS_REG_I2O_OUTMSGQ 0x44 /* I2O Outbound Message Queue */
+ #define IPS_REG_I2O_HIR 0x30 /* I2O Interrupt Status */
+ #define IPS_REG_I960_IDR 0x20 /* i960 Inbound Doorbell */
+ #define IPS_REG_I960_MSG0 0x18 /* i960 Outbound Reg 0 */
+ #define IPS_REG_I960_MSG1 0x1C /* i960 Outbound Reg 1 */
+ #define IPS_REG_I960_OIMR 0x34 /* i960 Oubound Int Mask Reg */
/*
* Adapter register bit equates
@@ -152,6 +164,9 @@
#define IPS_BIT_EBM 0x02 /* SCPR Enable Bus Master */
#define IPS_BIT_EI 0x80 /* HISR Enable Interrupts */
#define IPS_BIT_OP 0x01 /* OP bit in CBSP */
+ #define IPS_BIT_I2O_OPQI 0x08 /* General Host Interrupt */
+ #define IPS_BIT_I960_MSG0I 0x01 /* Message Register 0 Interrupt*/
+ #define IPS_BIT_I960_MSG1I 0x02 /* Message Register 1 Interrupt*/
/*
* Adapter Command ID Equates
@@ -202,13 +217,15 @@
#define IPS_INTR_HAL 2
#define IPS_ADAPTER_ID 0xF
#define IPS_VENDORID 0x1014
- #define IPS_DEVICEID 0x002E
+ #define IPS_COPPERHEAD_DEVICEID 0x002E
+ #define IPS_MORPHEUS_DEVICEID 0x01BD
#define IPS_IOCTL_SIZE 8192
#define IPS_STATUS_SIZE 4
#define IPS_STATUS_Q_SIZE (IPS_MAX_CMDS+1) * IPS_STATUS_SIZE
+ #define IPS_MEMMAP_SIZE 128
#define IPS_ONE_MSEC 1
#define IPS_ONE_SEC 1000
-
+
/*
* Geometry Settings
*/
@@ -336,10 +353,12 @@
/*
* Scsi_Host Template
*/
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,27)
#define IPS { \
next : NULL, \
module : NULL, \
proc_info : NULL, \
+ proc_dir : NULL, \
name : NULL, \
detect : ips_detect, \
release : ips_release, \
@@ -351,8 +370,8 @@
eh_device_reset_handler : NULL, \
eh_bus_reset_handler : NULL, \
eh_host_reset_handler : ips_eh_reset, \
- abort : ips_abort, \
- reset : ips_reset, \
+ abort : NULL, \
+ reset : NULL, \
slave_attach : NULL, \
bios_param : ips_biosparam, \
can_queue : 0, \
@@ -363,7 +382,37 @@
unchecked_isa_dma : 0, \
use_clustering : ENABLE_CLUSTERING, \
use_new_eh_code : 1 \
- }
+}
+#else
+ #define IPS { \
+ next : NULL, \
+ module : NULL, \
+ proc_info : NULL, \
+ name : NULL, \
+ detect : ips_detect, \
+ release : ips_release, \
+ info : ips_info, \
+ command : NULL, \
+ queuecommand : ips_queue, \
+ eh_strategy_handler : NULL, \
+ eh_abort_handler : ips_eh_abort, \
+ eh_device_reset_handler : NULL, \
+ eh_bus_reset_handler : NULL, \
+ eh_host_reset_handler : ips_eh_reset, \
+ abort : NULL, \
+ reset : NULL, \
+ slave_attach : NULL, \
+ bios_param : ips_biosparam, \
+ can_queue : 0, \
+ this_id: -1, \
+ sg_tablesize : IPS_MAX_SG, \
+ cmd_per_lun: 16, \
+ present : 0, \
+ unchecked_isa_dma : 0, \
+ use_clustering : ENABLE_CLUSTERING, \
+ use_new_eh_code : 1 \
+}
+#endif
/*
* IBM PCI Raid Command Formats
@@ -531,11 +580,15 @@ typedef struct {
u8 reserved2[3];
} IPS_DCDB_TABLE, *PIPS_DCDB_TABLE;
-typedef struct {
- volatile u8 reserved;
- volatile u8 command_id;
- volatile u8 basic_status;
- volatile u8 extended_status;
+typedef union {
+ struct {
+ volatile u8 reserved;
+ volatile u8 command_id;
+ volatile u8 basic_status;
+ volatile u8 extended_status;
+ } fields;
+
+ volatile u32 value;
} IPS_STATUS, *PIPS_STATUS;
typedef struct {
@@ -610,7 +663,7 @@ typedef struct {
u8 ucCompression;
u8 ucNvramType;
u32 ulNvramSize;
-} IPS_HARDWARE, *PIPS_HARDWARE;
+} IPS_HARDWARE, *PIPS_HARDWARE;
typedef struct {
u8 ucLogDriveCount;
@@ -777,8 +830,15 @@ typedef struct _IPS_INFOSTR {
int length;
int offset;
int pos;
+ int localpos;
} IPS_INFOSTR;
+typedef struct {
+ char *option_name;
+ int *option_flag;
+ int option_value;
+} IPS_OPTION;
+
/*
* Status Info
*/
@@ -823,6 +883,24 @@ typedef struct ips_copp_queue {
spinlock_t lock;
} ips_copp_queue_t;
+/* forward decl for host structure */
+struct ips_ha;
+
+typedef struct {
+ int (*reset)(struct ips_ha *);
+ int (*issue)(struct ips_ha *, struct ips_scb *);
+ int (*isinit)(struct ips_ha *);
+ int (*isintr)(struct ips_ha *);
+ int (*init)(struct ips_ha *);
+ int (*erasebios)(struct ips_ha *);
+ int (*programbios)(struct ips_ha *, char *, int);
+ int (*verifybios)(struct ips_ha *, char *, int);
+ u32 (*statupd)(struct ips_ha *);
+ void (*statinit)(struct ips_ha *);
+ void (*intr)(struct ips_ha *);
+ void (*enableint)(struct ips_ha *);
+} ips_hw_func_t;
+
typedef struct ips_ha {
u8 ha_id[IPS_MAX_CHANNELS+1];
u32 dcdb_active[IPS_MAX_CHANNELS];
@@ -857,12 +935,18 @@ typedef struct ips_ha {
u16 reset_count; /* number of resets */
u32 last_ffdc; /* last time we sent ffdc info*/
u8 revision_id; /* Revision level */
-
- #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+ u16 device_id; /* PCI device ID */
+ u8 reserved;
+ u32 mem_addr; /* Memory mapped address */
+ u32 io_len; /* Size of IO Address */
+ u32 mem_len; /* Size of memory address */
+ char *mem_ptr; /* Memory mapped Ptr */
+ char *ioremap_ptr; /* ioremapped memory pointer */
+ ips_hw_func_t func; /* hw function pointers */
+ struct pci_dev *pcidev; /* PCI device handle */
spinlock_t scb_lock;
spinlock_t copp_lock;
spinlock_t ips_lock;
- #endif
} ips_ha_t;
typedef void (*ips_scb_callback) (ips_ha_t *, struct ips_scb *);
diff --git a/drivers/scsi/mac53c94.c b/drivers/scsi/mac53c94.c
index f407740b9..87b46476b 100644
--- a/drivers/scsi/mac53c94.c
+++ b/drivers/scsi/mac53c94.c
@@ -47,6 +47,7 @@ struct fsc_state {
Scsi_Cmnd *current_req; /* req we're currently working on */
enum fsc_phase phase; /* what we're currently trying to do */
struct dbdma_cmd *dma_cmds; /* space for dbdma commands, aligned */
+ void *dma_cmd_space;
};
static struct fsc_state *all_53c94s;
@@ -113,6 +114,7 @@ mac53c94_detect(Scsi_Host_Template *tp)
DBDMA_ALIGN(dma_cmd_space);
memset(state->dma_cmds, 0, (host->sg_tablesize + 1)
* sizeof(struct dbdma_cmd));
+ state->dma_cmd_space = dma_cmd_space;
*prev_statep = state;
prev_statep = &state->next;
@@ -130,6 +132,22 @@ mac53c94_detect(Scsi_Host_Template *tp)
}
int
+mac53c94_release(struct Scsi_Host *host)
+{
+ struct fsc_state *fp = (struct fsc_state *) host->hostdata;
+
+ if (fp == 0)
+ return 0;
+ if (fp->regs)
+ iounmap((void *) fp->regs);
+ if (fp->dma)
+ iounmap((void *) fp->dma);
+ kfree(fp->dma_cmd_space);
+ free_irq(fp->intr, fp);
+ return 0;
+}
+
+int
mac53c94_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
{
unsigned long flags;
@@ -537,3 +555,7 @@ data_goes_out(Scsi_Cmnd *cmd)
return 0;
}
}
+
+static Scsi_Host_Template driver_template = SCSI_MAC53C94;
+
+#include "scsi_module.c"
diff --git a/drivers/scsi/mac53c94.h b/drivers/scsi/mac53c94.h
index 320e7f0b3..171eeb148 100644
--- a/drivers/scsi/mac53c94.h
+++ b/drivers/scsi/mac53c94.h
@@ -8,6 +8,7 @@
#define _MAC53C94_H
int mac53c94_detect(Scsi_Host_Template *);
+int mac53c94_release(struct Scsi_Host *);
int mac53c94_command(Scsi_Cmnd *);
int mac53c94_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
int mac53c94_abort(Scsi_Cmnd *);
@@ -17,6 +18,7 @@ int mac53c94_reset(Scsi_Cmnd *, unsigned int);
proc_name: "53c94", \
name: "53C94", \
detect: mac53c94_detect, \
+ release: mac53c94_release, \
command: mac53c94_command, \
queuecommand: mac53c94_queue, \
abort: mac53c94_abort, \
diff --git a/drivers/scsi/mac_scsi.c b/drivers/scsi/mac_scsi.c
index 62f7bd3c1..54f3f7c39 100644
--- a/drivers/scsi/mac_scsi.c
+++ b/drivers/scsi/mac_scsi.c
@@ -662,9 +662,6 @@ void scsi_mac_polled (void)
-#ifdef MODULE
-
-Scsi_Host_Template driver_template = MAC_NCR5380;
+static Scsi_Host_Template driver_template = MAC_NCR5380;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/mca_53c9x.c b/drivers/scsi/mca_53c9x.c
index 6639a3a0a..6267a6189 100644
--- a/drivers/scsi/mca_53c9x.c
+++ b/drivers/scsi/mca_53c9x.c
@@ -419,10 +419,8 @@ static void dma_led_off(struct NCR_ESP *esp)
outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR);
}
-#ifdef MODULE
-Scsi_Host_Template driver_template = MCA_53C9X;
+static Scsi_Host_Template driver_template = MCA_53C9X;
#include "scsi_module.c"
-#endif
/*
* OK, here's the goods I promised. The NCR 86C01 is an MCA interface chip
diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c
index 03e6d62ab..fea2a9b4f 100644
--- a/drivers/scsi/megaraid.c
+++ b/drivers/scsi/megaraid.c
@@ -2032,8 +2032,6 @@ static int __init megaraid_setup(char *str)
__setup("megaraid=", megaraid_setup);
-#ifdef MODULE
-Scsi_Host_Template driver_template = MEGARAID;
+static Scsi_Host_Template driver_template = MEGARAID;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
index adafbe43e..f80a7c497 100644
--- a/drivers/scsi/mesh.c
+++ b/drivers/scsi/mesh.c
@@ -149,6 +149,8 @@ struct mesh_state {
struct dbdma_cmd *dma_cmds; /* space for dbdma commands, aligned */
int clk_freq;
struct mesh_target tgts[8];
+ void *dma_cmd_space;
+ struct device_node *ofnode;
#ifndef MESH_NEW_STYLE_EH
Scsi_Cmnd *completed_q;
Scsi_Cmnd *completed_qtail;
@@ -262,6 +264,7 @@ mesh_detect(Scsi_Host_Template *tp)
panic("no mesh state");
memset(ms, 0, sizeof(*ms));
ms->host = mesh_host;
+ ms->ofnode = mesh;
ms->mesh = (volatile struct mesh_regs *)
ioremap(mesh->addrs[0].address, 0x1000);
ms->dma = (volatile struct dbdma_regs *)
@@ -278,6 +281,7 @@ mesh_detect(Scsi_Host_Template *tp)
ms->dma_cmds = (struct dbdma_cmd *) DBDMA_ALIGN(dma_cmd_space);
memset(ms->dma_cmds, 0, (mesh_host->sg_tablesize + 1)
* sizeof(struct dbdma_cmd));
+ ms->dma_cmd_space = dma_cmd_space;
ms->current_req = 0;
for (tgt = 0; tgt < 8; ++tgt) {
@@ -324,6 +328,23 @@ mesh_detect(Scsi_Host_Template *tp)
}
int
+mesh_release(struct Scsi_Host *host)
+{
+ struct mesh_state *ms = (struct mesh_state *) host->hostdata;
+
+ if (ms == 0)
+ return 0;
+ if (ms->mesh)
+ iounmap((void *) ms->mesh);
+ if (ms->dma)
+ iounmap((void *) ms->dma);
+ kfree(ms->dma_cmd_space);
+ free_irq(ms->meshintr, ms);
+ feature_clear(ms->ofnode, FEATURE_MESH_enable);
+ return 0;
+}
+
+int
mesh_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
{
unsigned long flags;
@@ -1910,3 +1931,7 @@ static void dumpslog(struct mesh_state *ms)
} while (i != ms->log_ix);
}
#endif /* MESH_DBG */
+
+static Scsi_Host_Template driver_template = SCSI_MESH;
+
+#include "scsi_module.c"
diff --git a/drivers/scsi/mesh.h b/drivers/scsi/mesh.h
index 7ceeaf1a4..19b2c9fc9 100644
--- a/drivers/scsi/mesh.h
+++ b/drivers/scsi/mesh.h
@@ -8,6 +8,7 @@
#define _MESH_H
int mesh_detect(Scsi_Host_Template *);
+int mesh_release(struct Scsi_Host *);
int mesh_command(Scsi_Cmnd *);
int mesh_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
int mesh_abort(Scsi_Cmnd *);
@@ -17,6 +18,7 @@ int mesh_reset(Scsi_Cmnd *, unsigned int);
proc_name: "mesh", \
name: "MESH", \
detect: mesh_detect, \
+ release: mesh_release, \
command: mesh_command, \
queuecommand: mesh_queue, \
abort: mesh_abort, \
diff --git a/drivers/scsi/mvme16x.h b/drivers/scsi/mvme16x.h
index 172b82a74..7f59d7e9a 100644
--- a/drivers/scsi/mvme16x.h
+++ b/drivers/scsi/mvme16x.h
@@ -23,7 +23,6 @@ void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
#define CAN_QUEUE 24
#endif
-#if defined(HOSTS_C) || defined(MODULE)
#include <scsi/scsicam.h>
#define MVME16x_SCSI {name: "MVME16x NCR53c710 SCSI", \
@@ -37,5 +36,5 @@ void NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
sg_tablesize: 63, \
cmd_per_lun: 3, \
use_clustering: DISABLE_CLUSTERING }
-#endif
+
#endif /* MVME16x_SCSI_H */
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index 72f4ce11d..3f8854aaf 100644
--- a/drivers/scsi/ncr53c8xx.c
+++ b/drivers/scsi/ncr53c8xx.c
@@ -9513,7 +9513,5 @@ const char *ncr53c8xx_info (struct Scsi_Host *host)
** Module stuff
*/
-#ifdef MODULE
-Scsi_Host_Template driver_template = NCR53C8XX;
+static Scsi_Host_Template driver_template = NCR53C8XX;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/ncr53c8xx.h b/drivers/scsi/ncr53c8xx.h
index 40d3533c2..1746ca509 100644
--- a/drivers/scsi/ncr53c8xx.h
+++ b/drivers/scsi/ncr53c8xx.h
@@ -50,8 +50,6 @@
** Used by hosts.c and ncr53c8xx.c with module configuration.
*/
-#if defined(HOSTS_C) || defined(MODULE)
-
#include <scsi/scsicam.h>
int ncr53c8xx_abort(Scsi_Cmnd *);
@@ -96,6 +94,4 @@ int ncr53c8xx_release(struct Scsi_Host *);
#endif /* LINUX_VERSION_CODE */
-#endif /* defined(HOSTS_C) || defined(MODULE) */
-
#endif /* NCR53C8XX_H */
diff --git a/drivers/scsi/oktagon_esp.c b/drivers/scsi/oktagon_esp.c
index 61a8128c2..2e22157f0 100644
--- a/drivers/scsi/oktagon_esp.c
+++ b/drivers/scsi/oktagon_esp.c
@@ -570,18 +570,15 @@ void dma_advance_sg(Scsi_Cmnd *sp)
sp->SCp.ptr = sp->SCp.buffer->address;
}
-#ifdef MODULE
#define HOSTS_C
#include "oktagon_esp.h"
-Scsi_Host_Template driver_template = SCSI_OKTAGON_ESP;
+static Scsi_Host_Template driver_template = SCSI_OKTAGON_ESP;
#include "scsi_module.c"
-#endif
-
int oktagon_esp_release(struct Scsi_Host *instance)
{
#ifdef MODULE
diff --git a/drivers/scsi/pas16.c b/drivers/scsi/pas16.c
index 3fedd3b4e..d44ce6a18 100644
--- a/drivers/scsi/pas16.c
+++ b/drivers/scsi/pas16.c
@@ -597,12 +597,12 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src
#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;
+static Scsi_Host_Template driver_template = MV_PAS16;
#include "scsi_module.c"
+#ifdef MODULE
MODULE_PARM(pas16_addr, "h");
MODULE_PARM(pas16_irq, "i");
#endif
diff --git a/drivers/scsi/pas16.h b/drivers/scsi/pas16.h
index fd8b83df6..543a4cfc5 100644
--- a/drivers/scsi/pas16.h
+++ b/drivers/scsi/pas16.h
@@ -140,8 +140,6 @@ int pas16_proc_info (char *buffer ,char **start, off_t offset,
* macros when this is being used solely for the host stub.
*/
-#if defined(HOSTS_C) || defined(MODULE)
-
#define MV_PAS16 { \
name: "Pro Audio Spectrum-16 SCSI", \
detect: pas16_detect, \
@@ -155,7 +153,6 @@ int pas16_proc_info (char *buffer ,char **start, off_t offset,
cmd_per_lun: CMD_PER_LUN , \
use_clustering: DISABLE_CLUSTERING}
-#endif
#ifndef HOSTS_C
#define NCR5380_implementation_fields \
diff --git a/drivers/scsi/pci2000.c b/drivers/scsi/pci2000.c
index 2681c520d..425932efc 100644
--- a/drivers/scsi/pci2000.c
+++ b/drivers/scsi/pci2000.c
@@ -855,9 +855,7 @@ int Pci2000_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
}
-#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = PCI2000;
+static Scsi_Host_Template driver_template = PCI2000;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/pci2220i.c b/drivers/scsi/pci2220i.c
index 8a59351aa..1869be7c0 100644
--- a/drivers/scsi/pci2220i.c
+++ b/drivers/scsi/pci2220i.c
@@ -2509,7 +2509,7 @@ VOID SetupFinish (PADAPTER2220I padapter, char *str, int irq)
init_timer (&padapter->reconTimer);
padapter->reconTimer.function = ReconTimerExpiry;
padapter->reconTimer.data = (unsigned long)padapter;
- printk("\nPCI-%sI EIDE CONTROLLER: at I/O = %lX/%lX IRQ = %ld\n", str, padapter->basePort, padapter->regBase, irq);
+ printk("\nPCI-%sI EIDE CONTROLLER: at I/O = %lX/%lX IRQ = %d\n", str, padapter->basePort, padapter->regBase, irq);
printk("Version %s, Compiled %s %s\n\n", PCI2220I_VERSION, __DATE__, __TIME__);
}
/****************************************************************
@@ -2920,9 +2920,7 @@ int Pci2220i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
}
-#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = PCI2220I;
+static Scsi_Host_Template driver_template = PCI2220I;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c
index 195e131d0..75914a1a2 100644
--- a/drivers/scsi/pcmcia/aha152x_stub.c
+++ b/drivers/scsi/pcmcia/aha152x_stub.c
@@ -5,7 +5,7 @@
This driver supports the Adaptec AHA-1460, the New Media Bus
Toaster, and the New Media Toast & Jam.
- aha152x_cs.c 1.53 2000/05/04 01:30:00
+ aha152x_cs.c 1.54 2000/06/12 21:27:25
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
@@ -18,7 +18,7 @@
rights and limitations under the License.
The initial developer of the original code is David A. Hinds
- <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
Alternatively, the contents of this file may be used under the
@@ -62,7 +62,7 @@ static int pc_debug = PCMCIA_DEBUG;
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version =
-"aha152x_cs.c 1.53 2000/05/04 01:30:00 (David Hinds)";
+"aha152x_cs.c 1.54 2000/06/12 21:27:25 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
diff --git a/drivers/scsi/pcmcia/apa1480_stub.c b/drivers/scsi/pcmcia/apa1480_stub.c
index a6419f194..24ee469cd 100644
--- a/drivers/scsi/pcmcia/apa1480_stub.c
+++ b/drivers/scsi/pcmcia/apa1480_stub.c
@@ -2,7 +2,7 @@
A driver for the Adaptec APA1480 CardBus SCSI Host Adapter
- apa1480_cb.c 1.19 2000/02/14 22:39:25
+ apa1480_cb.c 1.22 2000/06/12 21:27:25
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
@@ -15,7 +15,7 @@
rights and limitations under the License.
The initial developer of the original code is David A. Hinds
- <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
Alternatively, the contents of this file may be used under the
@@ -55,7 +55,7 @@ static int pc_debug = PCMCIA_DEBUG;
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version =
-"apa1480_cb.c 1.19 2000/02/14 22:39:25 (David Hinds)";
+"apa1480_cb.c 1.22 2000/06/12 21:27:25 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c
index 533bf5a50..3c480a3ca 100644
--- a/drivers/scsi/pcmcia/fdomain_stub.c
+++ b/drivers/scsi/pcmcia/fdomain_stub.c
@@ -2,7 +2,7 @@
A driver for Future Domain-compatible PCMCIA SCSI cards
- fdomain_cs.c 1.42 2000/05/04 01:30:00
+ fdomain_cs.c 1.43 2000/06/12 21:27:25
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
@@ -15,7 +15,7 @@
rights and limitations under the License.
The initial developer of the original code is David A. Hinds
- <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
Alternatively, the contents of this file may be used under the
@@ -59,7 +59,7 @@ static int pc_debug = PCMCIA_DEBUG;
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version =
-"fdomain_cs.c 1.42 2000/05/04 01:30:00 (David Hinds)";
+"fdomain_cs.c 1.43 2000/06/12 21:27:25 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c
index bcbbecfb8..1cfee657f 100644
--- a/drivers/scsi/pcmcia/qlogic_stub.c
+++ b/drivers/scsi/pcmcia/qlogic_stub.c
@@ -2,7 +2,7 @@
A driver for the Qlogic SCSI card
- qlogic_cs.c 1.78 2000/05/04 01:30:00
+ qlogic_cs.c 1.79 2000/06/12 21:27:26
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
@@ -15,7 +15,7 @@
rights and limitations under the License.
The initial developer of the original code is David A. Hinds
- <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
Alternatively, the contents of this file may be used under the
@@ -66,7 +66,7 @@ static int pc_debug = PCMCIA_DEBUG;
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version =
-"qlogic_cs.c 1.78 2000/05/04 01:30:00 (David Hinds)";
+"qlogic_cs.c 1.79 2000/06/12 21:27:26 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
diff --git a/drivers/scsi/pluto.c b/drivers/scsi/pluto.c
index b4ebd88f3..73dfaf299 100644
--- a/drivers/scsi/pluto.c
+++ b/drivers/scsi/pluto.c
@@ -331,11 +331,8 @@ static int pluto_encode_addr(Scsi_Cmnd *SCpnt, u16 *addr, fc_channel *fc, fcp_cm
return 0;
}
-#ifdef MODULE
-
-Scsi_Host_Template driver_template = PLUTO;
+static Scsi_Host_Template driver_template = PLUTO;
#include "scsi_module.c"
EXPORT_NO_SYMBOLS;
-#endif /* MODULE */
diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c
index 54f7da01f..96b3c8400 100644
--- a/drivers/scsi/ppa.c
+++ b/drivers/scsi/ppa.c
@@ -99,10 +99,8 @@ static int ppa_pb_claim(int host_no)
* Parallel port probing routines *
***************************************************************************/
-#ifdef MODULE
-Scsi_Host_Template driver_template = PPA;
+static Scsi_Host_Template driver_template = PPA;
#include "scsi_module.c"
-#endif
/*
* Start of Chipset kludges
diff --git a/drivers/scsi/psi240i.c b/drivers/scsi/psi240i.c
index 61cabd7c4..ccd233120 100644
--- a/drivers/scsi/psi240i.c
+++ b/drivers/scsi/psi240i.c
@@ -713,10 +713,8 @@ int Psi240i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
}
-#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = PSI240I;
+static Scsi_Host_Template driver_template = PSI240I;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
index f3d059df8..12c0c9256 100644
--- a/drivers/scsi/qla1280.c
+++ b/drivers/scsi/qla1280.c
@@ -6163,11 +6163,9 @@ qla1280_debounce_register(volatile uint16_t *addr)
/*
* Declarations for load module
*/
-#ifdef MODULE
-Scsi_Host_Template driver_template = QLA1280_LINUX_TEMPLATE;
+static Scsi_Host_Template driver_template = QLA1280_LINUX_TEMPLATE;
#include "scsi_module.c"
-#endif
/************************************************************************
* qla1280_check_for_dead_scsi_bus *
diff --git a/drivers/scsi/qlogicfas.c b/drivers/scsi/qlogicfas.c
index 9bc66953d..365086080 100644
--- a/drivers/scsi/qlogicfas.c
+++ b/drivers/scsi/qlogicfas.c
@@ -643,8 +643,10 @@ int qlogicfas_biosparam(Disk * disk, kdev_t dev, int ip[])
ip[0] = 0xff;
ip[1] = 0x3f;
ip[2] = disk->capacity / (ip[0] * ip[1]);
+#if 0
if (ip[2] > 1023)
ip[2] = 1023;
+#endif
}
return 0;
}
@@ -674,10 +676,7 @@ 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;
-
+static Scsi_Host_Template driver_template = QLOGICFAS;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/qlogicfc.c b/drivers/scsi/qlogicfc.c
index 540fdb012..63692ca0b 100644
--- a/drivers/scsi/qlogicfc.c
+++ b/drivers/scsi/qlogicfc.c
@@ -2226,10 +2226,6 @@ void isp2x00_print_scsi_cmd(Scsi_Cmnd * cmd)
#endif /* DEBUG_ISP2x00 */
-#ifdef MODULE
-
-Scsi_Host_Template driver_template = QLOGICFC;
+static Scsi_Host_Template driver_template = QLOGICFC;
#include "scsi_module.c"
-
-#endif
diff --git a/drivers/scsi/qlogicisp.c b/drivers/scsi/qlogicisp.c
index b48a87326..4f3350e99 100644
--- a/drivers/scsi/qlogicisp.c
+++ b/drivers/scsi/qlogicisp.c
@@ -1989,8 +1989,6 @@ void isp1020_print_scsi_cmd(Scsi_Cmnd *cmd)
#endif /* DEBUG_ISP1020 */
-#ifdef MODULE
-Scsi_Host_Template driver_template = QLOGICISP;
+static Scsi_Host_Template driver_template = QLOGICISP;
#include "scsi_module.c"
-#endif /* MODULE */
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index b510231be..d471dddd9 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -1526,10 +1526,8 @@ int qlogicpti_reset(Scsi_Cmnd *Cmnd, unsigned int reset_flags)
return return_status;
}
-#ifdef MODULE
-Scsi_Host_Template driver_template = QLOGICPTI;
+static Scsi_Host_Template driver_template = QLOGICPTI;
#include "scsi_module.c"
EXPORT_NO_SYMBOLS;
-#endif /* MODULE */
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 10b65fa2d..542876daf 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -36,6 +36,9 @@
* out_of_space hacks, D. Gilbert (dpg) 990608
*/
+#define REVISION "Revision: 1.00"
+#define VERSION "Id: scsi.c 1.00 2000/09/26"
+
#include <linux/config.h>
#include <linux/module.h>
@@ -384,7 +387,7 @@ Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait,
* return NULL.
*/
SCpnt = NULL;
- break;
+ goto busy;
}
}
/*
@@ -402,6 +405,7 @@ Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait,
if (SCpnt) {
break;
}
+ busy:
/*
* If we have been asked to wait for a free block, then
* wait here.
@@ -495,30 +499,7 @@ Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait,
return SCpnt;
}
-/*
- * Function: scsi_release_command
- *
- * Purpose: Release a command block.
- *
- * Arguments: SCpnt - command block we are releasing.
- *
- * Notes: The command block can no longer be used by the caller once
- * this funciton is called. This is in effect the inverse
- * of scsi_allocate_device. Note that we also must perform
- * a couple of additional tasks. We must first wake up any
- * processes that might have blocked waiting for a command
- * block, and secondly we must hit the queue handler function
- * to make sure that the device is busy.
- *
- * The idea is that a lot of the mid-level internals gunk
- * gets hidden in this function. Upper level drivers don't
- * have any chickens to wave in the air to get things to
- * work reliably.
- *
- * This function is deprecated, and drivers should be
- * rewritten to use Scsi_Request instead of Scsi_Cmnd.
- */
-void scsi_release_command(Scsi_Cmnd * SCpnt)
+inline void __scsi_release_command(Scsi_Cmnd * SCpnt)
{
unsigned long flags;
Scsi_Device * SDpnt;
@@ -562,6 +543,43 @@ void scsi_release_command(Scsi_Cmnd * SCpnt)
* they wake up.
*/
wake_up(&SDpnt->scpnt_wait);
+}
+
+/*
+ * Function: scsi_release_command
+ *
+ * Purpose: Release a command block.
+ *
+ * Arguments: SCpnt - command block we are releasing.
+ *
+ * Notes: The command block can no longer be used by the caller once
+ * this funciton is called. This is in effect the inverse
+ * of scsi_allocate_device. Note that we also must perform
+ * a couple of additional tasks. We must first wake up any
+ * processes that might have blocked waiting for a command
+ * block, and secondly we must hit the queue handler function
+ * to make sure that the device is busy. Note - there is an
+ * option to not do this - there were instances where we could
+ * recurse too deeply and blow the stack if this happened
+ * when we were indirectly called from the request function
+ * itself.
+ *
+ * The idea is that a lot of the mid-level internals gunk
+ * gets hidden in this function. Upper level drivers don't
+ * have any chickens to wave in the air to get things to
+ * work reliably.
+ *
+ * This function is deprecated, and drivers should be
+ * rewritten to use Scsi_Request instead of Scsi_Cmnd.
+ */
+void scsi_release_command(Scsi_Cmnd * SCpnt)
+{
+ request_queue_t *q;
+ Scsi_Device * SDpnt;
+
+ SDpnt = SCpnt->device;
+
+ __scsi_release_command(SCpnt);
/*
* Finally, hit the queue request function to make sure that
@@ -569,12 +587,8 @@ void scsi_release_command(Scsi_Cmnd * SCpnt)
* This won't block - if the device cannot take any more, life
* will go on.
*/
- {
- request_queue_t *q;
-
- q = &SDpnt->request_queue;
- scsi_queue_next_request(q, NULL);
- }
+ q = &SDpnt->request_queue;
+ scsi_queue_next_request(q, NULL);
}
/*
@@ -1361,13 +1375,8 @@ void scsi_finish_command(Scsi_Cmnd * SCpnt)
SCpnt->done(SCpnt);
}
-#if defined(CONFIG_MODULES) || defined(CONFIG_BLK_DEV_IDESCSI) || defined(CONFIG_USB_STORAGE)
static int scsi_register_host(Scsi_Host_Template *);
static void scsi_unregister_host(Scsi_Host_Template *);
-#endif
-
-
-int scsi_loadable_module_flag; /* Set after we scan builtin drivers */
/*
* Function: scsi_release_commandblocks()
@@ -1432,10 +1441,9 @@ void scsi_build_commandblocks(Scsi_Device * SDpnt)
kmalloc(sizeof(Scsi_Cmnd),
GFP_ATOMIC |
(host->unchecked_isa_dma ? GFP_DMA : 0));
- memset(SCpnt, 0, sizeof(Scsi_Cmnd));
if (NULL == SCpnt)
break; /* If not, the next line will oops ... */
- memset(&SCpnt->eh_timeout, 0, sizeof(SCpnt->eh_timeout));
+ memset(SCpnt, 0, sizeof(Scsi_Cmnd));
SCpnt->host = host;
SCpnt->device = SDpnt;
SCpnt->target = SDpnt->id;
@@ -1499,120 +1507,6 @@ void __init scsi_host_no_insert(char *str, int n)
}
}
-#ifndef MODULE /* { */
-
-char scsi_host_no_table[20][10] __initdata = {};
-int scsi_host_no_set __initdata = 0;
-
-/*
- * scsi_dev_init() is our initialization routine, which in turn calls host
- * initialization, bus scanning, and sd/st initialization routines.
- * This is only used at boot time.
- */
-int __init scsi_dev_init(void)
-{
- Scsi_Device *SDpnt;
- struct Scsi_Host *shpnt;
- struct Scsi_Device_Template *sdtpnt;
- struct proc_dir_entry *generic;
-#ifdef FOO_ON_YOU
- return;
-#endif
-
- /* Initialize list of host_no if kernel parameter set */
- if (scsi_host_no_set) {
- int i;
- for (i = 0;i < sizeof(scsi_host_no_table)/sizeof(scsi_host_no_table[0]);i++)
- scsi_host_no_insert(scsi_host_no_table[i], i);
- }
-
- /* Yes we're here... */
-
- scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", NULL);
- /*
- * This makes /proc/scsi and /proc/scsi/scsi visible.
- */
-#ifdef CONFIG_PROC_FS
- proc_scsi = proc_mkdir("scsi", 0);
- if (!proc_scsi) {
- printk (KERN_ERR "cannot init /proc/scsi\n");
- return -ENOMEM;
- }
-
- generic = create_proc_info_entry ("scsi/scsi", 0, 0, scsi_proc_info);
- if (!generic) {
- printk (KERN_ERR "cannot init /proc/scsi/scsi\n");
- remove_proc_entry("scsi", 0);
- return -ENOMEM;
- }
- generic->write_proc = proc_scsi_gen_write;
-#endif
-
- /* Init a few things so we can "malloc" memory. */
- scsi_loadable_module_flag = 0;
-
- /* initialize all hosts */
- scsi_init();
-
- /*
- * This is where the processing takes place for most everything
- * when commands are completed. Until we do this, we will not be able
- * to queue any commands.
- */
- init_bh(SCSI_BH, scsi_bottom_half_handler);
-
- 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, shpnt->host_queue);
- }
-
- 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 (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
- for (SDpnt = shpnt->host_queue; 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);
- if (0 == SDpnt->has_cmdblocks) {
- printk("scsi_dev_init: DANGER, no command blocks\n");
- /* What to do now ?? */
- }
- }
- }
- }
-
- /*
- * This should build the DMA pool.
- */
- scsi_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;
-}
-#endif /* MODULE */ /* } */
-
#ifdef CONFIG_PROC_FS
static int scsi_proc_info(char *buffer, char **start, off_t offset, int length)
{
@@ -1908,15 +1802,8 @@ out:
#endif
/*
- * Some host adapters that are plugging into other subsystems register
- * their hosts through the modules entrypoints, and don't use the big
- * list in hosts.c.
- */
-#if defined(CONFIG_MODULES) || defined(CONFIG_BLK_DEV_IDESCSI) || defined(CONFIG_USB_STORAGE) || defined(CONFIG_USB_MICROTEK) /* 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.
+ * This entry point should be called by a driver if it is trying
+ * to add a low level scsi driver to the system.
*/
static int scsi_register_host(Scsi_Host_Template * tpnt)
{
@@ -1957,8 +1844,8 @@ static int scsi_register_host(Scsi_Host_Template * tpnt)
return 1;
}
/*
- * The low-level driver failed to register a driver. We
- * can do this now.
+ * The low-level driver failed to register a driver.
+ * We can do this now.
*/
scsi_register(tpnt, 0);
}
@@ -2004,9 +1891,6 @@ static int scsi_register_host(Scsi_Host_Template * tpnt)
}
}
- printk("scsi : %d host%s.\n", next_scsi_host,
- (next_scsi_host == 1) ? "" : "s");
-
/* The next step is to call scan_scsis here. This generates the
* Scsi_Devices entries
*/
@@ -2084,15 +1968,13 @@ static int scsi_register_host(Scsi_Host_Template * tpnt)
static void scsi_unregister_host(Scsi_Host_Template * tpnt)
{
int online_status;
- int pcount;
+ int pcount0, pcount;
Scsi_Cmnd *SCpnt;
Scsi_Device *SDpnt;
Scsi_Device *SDpnt1;
struct Scsi_Device_Template *sdtpnt;
struct Scsi_Host *sh1;
struct Scsi_Host *shpnt;
- Scsi_Host_Template *SHT;
- Scsi_Host_Template *SHTp;
char name[10]; /* host_no>=10^9? I don't think so. */
/*
@@ -2226,9 +2108,10 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt)
/* Next we go through and remove the instances of the individual hosts
* that were detected */
+ pcount0 = next_scsi_host;
for (shpnt = scsi_hostlist; shpnt; shpnt = sh1) {
sh1 = shpnt->next;
- if (shpnt->hostt != tpnt || !shpnt->loaded_as_module)
+ if (shpnt->hostt != tpnt)
continue;
pcount = next_scsi_host;
/* Remove the /proc/scsi directory entry */
@@ -2242,7 +2125,7 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt)
* written host adapters.
*/
if (shpnt->irq)
- free_irq(shpnt->irq, NULL);
+ free_irq(shpnt->irq, NULL);
if (shpnt->dma_channel != 0xff)
free_dma(shpnt->dma_channel);
if (shpnt->io_port && shpnt->n_io_port)
@@ -2261,8 +2144,9 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt)
if (!scsi_hosts)
scsi_resize_dma_pool();
- printk("scsi : %d host%s.\n", next_scsi_host,
- (next_scsi_host == 1) ? "" : "s");
+ if (pcount0 != next_scsi_host)
+ printk("scsi : %d host%s left.\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",
@@ -2271,24 +2155,21 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt)
(scsi_memory_upper_value - scsi_init_memory_start) / 1024);
#endif
- /* There were some hosts that were loaded at boot time, so we cannot
- do any more than this */
- if (tpnt->present)
- return;
+ /* Remove it from the linked list and /proc */
+ if (tpnt->present) {
+ Scsi_Host_Template **SHTp = &scsi_hosts;
+ Scsi_Host_Template *SHT;
- /* 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;
+ while ((SHT = *SHTp) != NULL) {
+ if (SHT == tpnt) {
+ *SHTp = SHT->next;
+ break;
+ }
+ SHTp = &SHT->next;
}
- /* Rebuild the /proc/scsi directory entries */
- remove_proc_entry(tpnt->proc_name, proc_scsi);
+ /* Rebuild the /proc/scsi directory entries */
+ remove_proc_entry(tpnt->proc_name, proc_scsi);
+ }
MOD_DEC_USE_COUNT;
}
@@ -2421,6 +2302,10 @@ static int scsi_unregister_device(struct Scsi_Device_Template *tpnt)
}
+/* This function should be called by drivers which needs to register
+ * with the midlevel scsi system. As of 2.4.0-test9pre3 this is our
+ * main device/hosts register function /mathiasen
+ */
int scsi_register_module(int module_type, void *ptr)
{
switch (module_type) {
@@ -2448,6 +2333,8 @@ int scsi_register_module(int module_type, void *ptr)
}
}
+/* Reverse the actions taken above
+ */
void scsi_unregister_module(int module_type, void *ptr)
{
switch (module_type) {
@@ -2466,8 +2353,6 @@ void scsi_unregister_module(int module_type, void *ptr)
return;
}
-#endif /* CONFIG_MODULES */
-
#ifdef CONFIG_PROC_FS
/*
* Function: scsi_dump_status
@@ -2574,17 +2459,11 @@ static void scsi_dump_status(int level)
}
#endif /* CONFIG_PROC_FS */
-static int scsi_host_no_init (char *str)
+static int __init scsi_host_no_init (char *str)
{
static int next_no = 0;
char *temp;
-#ifndef MODULE
- int len;
- scsi_host_no_set = 1;
- memset(scsi_host_no_table, 0, sizeof(scsi_host_no_table));
-#endif /* MODULE */
-
while (str) {
temp = str;
while (*temp && (*temp != ':') && (*temp != ','))
@@ -2593,35 +2472,34 @@ static int scsi_host_no_init (char *str)
temp = NULL;
else
*temp++ = 0;
-#ifdef MODULE
scsi_host_no_insert(str, next_no);
-#else
- if (next_no < sizeof(scsi_host_no_table)/sizeof(scsi_host_no_table[0])) {
- if ((len = strlen(str)) >= sizeof(scsi_host_no_table[0]))
- len = sizeof(scsi_host_no_table[0])-1;
- strncpy(scsi_host_no_table[next_no], str, len);
- scsi_host_no_table[next_no][len] = 0;
- }
-#endif /* MODULE */
str = temp;
next_no++;
}
return 1;
}
-#ifndef MODULE
-__setup("scsihosts=", scsi_host_no_init);
-#endif
-
-#ifdef MODULE
static char *scsihosts;
MODULE_PARM(scsihosts, "s");
+MODULE_DESCRIPTION("SCSI core");
+
+#ifndef MODULE
+int __init scsi_setup(char *str)
+{
+ scsihosts = str;
+ return 1;
+}
+
+__setup("scsihosts=", scsi_setup);
+#endif
-int init_module(void)
+static int __init init_scsi(void)
{
struct proc_dir_entry *generic;
+ printk(KERN_INFO "SCSI subsystem driver " REVISION "\n");
+
if( scsi_init_minimal_dma_pool() != 0 )
{
return 1;
@@ -2645,10 +2523,10 @@ int init_module(void)
generic->write_proc = proc_scsi_gen_write;
#endif
- scsi_loadable_module_flag = 1;
-
scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", NULL);
- scsi_host_no_init (scsihosts);
+ if (scsihosts)
+ printk("scsi: host order: %s\n", scsihosts);
+ scsi_host_no_init (scsihosts);
/*
* This is where the processing takes place for most everything
* when commands are completed.
@@ -2658,7 +2536,7 @@ int init_module(void)
return 0;
}
-void cleanup_module(void)
+static void __exit exit_scsi(void)
{
Scsi_Host_Name *shn, *shn2 = NULL;
@@ -2688,7 +2566,8 @@ void cleanup_module(void)
}
-#endif /* MODULE */
+module_init(init_scsi);
+module_exit(exit_scsi);
/*
* Function: scsi_get_host_dev()
diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
index e40d64ccb..8dafee2bd 100644
--- a/drivers/scsi/scsi.h
+++ b/drivers/scsi/scsi.h
@@ -492,6 +492,7 @@ extern void scsi_done(Scsi_Cmnd * SCpnt);
extern void scsi_finish_command(Scsi_Cmnd *);
extern int scsi_retry_command(Scsi_Cmnd *);
extern Scsi_Cmnd *scsi_allocate_device(Scsi_Device *, int, int);
+extern void __scsi_release_command(Scsi_Cmnd *);
extern void scsi_release_command(Scsi_Cmnd *);
extern void scsi_do_cmd(Scsi_Cmnd *, const void *cmnd,
void *buffer, unsigned bufflen,
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 4bed377ed..c52465a8c 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -767,12 +767,10 @@ void *scsi_debug_get_handle(void)
}
#endif
-#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = SCSI_DEBUG;
+static Scsi_Host_Template driver_template = SCSI_DEBUG;
#include "scsi_module.c"
-#endif
/*
* Overrides for Emacs so that we almost follow Linus's tabbing style.
diff --git a/drivers/scsi/scsi_dma.c b/drivers/scsi/scsi_dma.c
index ccb366593..3de835130 100644
--- a/drivers/scsi/scsi_dma.c
+++ b/drivers/scsi/scsi_dma.c
@@ -260,6 +260,7 @@ void scsi_resize_dma_pool(void)
new_dma_sectors += (2048 >> 9) * SDpnt->queue_depth;
} else if (SDpnt->type == TYPE_SCANNER ||
SDpnt->type == TYPE_PROCESSOR ||
+ SDpnt->type == TYPE_COMM ||
SDpnt->type == TYPE_MEDIUM_CHANGER ||
SDpnt->type == TYPE_ENCLOSURE) {
new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 3d6e45fcd..e7b8bbe19 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -1861,6 +1861,9 @@ void scsi_error_handler(void *data)
* Flush resources
*/
+ exit_files(current);
+ current->files = init_task.files;
+ atomic_inc(&current->files->count);
daemonize();
/*
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 362173b56..ace7c7e2c 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -381,6 +381,8 @@ void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt)
* uptodate - 1 if I/O indicates success, 0 for I/O error.
* sectors - number of sectors we want to mark.
* requeue - indicates whether we should requeue leftovers.
+ * frequeue - indicates that if we release the command block
+ * that the queue request function should be called.
*
* Lock status: Assumed that lock is not held upon entry.
*
@@ -395,10 +397,12 @@ void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt)
static Scsi_Cmnd *__scsi_end_request(Scsi_Cmnd * SCpnt,
int uptodate,
int sectors,
- int requeue)
+ int requeue,
+ int frequeue)
{
struct request *req;
struct buffer_head *bh;
+ Scsi_Device * SDpnt;
ASSERT_LOCK(&io_request_lock, 0);
@@ -458,11 +462,20 @@ static Scsi_Cmnd *__scsi_end_request(Scsi_Cmnd * SCpnt,
}
add_blkdev_randomness(MAJOR(req->rq_dev));
+ SDpnt = SCpnt->device;
+
/*
* This will goose the queue request function at the end, so we don't
* need to worry about launching another command.
*/
- scsi_release_command(SCpnt);
+ __scsi_release_command(SCpnt);
+
+ if( frequeue ) {
+ request_queue_t *q;
+
+ q = &SDpnt->request_queue;
+ scsi_queue_next_request(q, NULL);
+ }
return NULL;
}
@@ -488,7 +501,7 @@ static Scsi_Cmnd *__scsi_end_request(Scsi_Cmnd * SCpnt,
*/
Scsi_Cmnd *scsi_end_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors)
{
- return __scsi_end_request(SCpnt, uptodate, sectors, 1);
+ return __scsi_end_request(SCpnt, uptodate, sectors, 1, 1);
}
/*
@@ -648,7 +661,8 @@ void scsi_io_completion(Scsi_Cmnd * SCpnt, int good_sectors,
SCpnt = __scsi_end_request(SCpnt,
1,
good_sectors,
- result == 0);
+ result == 0,
+ 1);
/*
* If the command completed without error, then either finish off the
@@ -718,8 +732,8 @@ void scsi_io_completion(Scsi_Cmnd * SCpnt, int good_sectors,
}
break;
case NOT_READY:
- printk(KERN_INFO "Device %x not ready.\n",
- SCpnt->request.rq_dev);
+ printk(KERN_INFO "Device %s not ready.\n",
+ kdevname(SCpnt->request.rq_dev));
SCpnt = scsi_end_request(SCpnt, 0, this_count);
return;
break;
@@ -962,6 +976,7 @@ void scsi_request_fn(request_queue_t * q)
}
} else {
+ SRpnt = NULL;
STpnt = scsi_get_request_dev(req);
if (!STpnt) {
panic("Unable to find device associated with request");
@@ -1010,7 +1025,7 @@ void scsi_request_fn(request_queue_t * q)
*/
blkdev_dequeue_request(req);
- if (req != &SCpnt->request) {
+ if (req != &SCpnt->request && req != &SRpnt->sr_request ) {
memcpy(&SCpnt->request, req, sizeof(struct request));
/*
@@ -1048,8 +1063,12 @@ void scsi_request_fn(request_queue_t * q)
* get those allocated here.
*/
if (!SDpnt->scsi_init_io_fn(SCpnt)) {
- scsi_end_request(SCpnt, 0,
- SCpnt->request.nr_sectors);
+ SCpnt = __scsi_end_request(SCpnt, 0,
+ SCpnt->request.nr_sectors, 0, 0);
+ if( SCpnt != NULL )
+ {
+ panic("Should not have leftover blocks\n");
+ }
spin_lock_irq(&io_request_lock);
SHpnt->host_busy--;
SDpnt->device_busy--;
@@ -1060,8 +1079,12 @@ void scsi_request_fn(request_queue_t * q)
*/
if (!STpnt->init_command(SCpnt)) {
scsi_release_buffers(SCpnt);
- scsi_end_request(SCpnt, 0,
- SCpnt->request.nr_sectors);
+ SCpnt = __scsi_end_request(SCpnt, 0,
+ SCpnt->request.nr_sectors, 0, 0);
+ if( SCpnt != NULL )
+ {
+ panic("Should not have leftover blocks\n");
+ }
spin_lock_irq(&io_request_lock);
SHpnt->host_busy--;
SDpnt->device_busy--;
diff --git a/drivers/scsi/scsi_module.c b/drivers/scsi/scsi_module.c
index b7900f70d..c14a8eef7 100644
--- a/drivers/scsi/scsi_module.c
+++ b/drivers/scsi/scsi_module.c
@@ -30,23 +30,27 @@
*/
#include <linux/module.h>
+#include <linux/init.h>
-int init_module(void)
+static int __init init_this_scsi_driver(void)
{
- driver_template.module = &__this_module;
+ driver_template.module = THIS_MODULE;
scsi_register_module(MODULE_SCSI_HA, &driver_template);
if (driver_template.present)
return 0;
scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
- return -1;
+ return -ENODEV;
}
-void cleanup_module(void)
+static void __exit exit_this_scsi_driver(void)
{
scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
}
+module_init(init_this_scsi_driver);
+module_exit(exit_this_scsi_driver);
+
/*
* 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
diff --git a/drivers/scsi/scsi_queue.c b/drivers/scsi/scsi_queue.c
index 162088204..4793f1e8b 100644
--- a/drivers/scsi/scsi_queue.c
+++ b/drivers/scsi/scsi_queue.c
@@ -118,7 +118,7 @@ int scsi_mlqueue_insert(Scsi_Cmnd * cmd, int reason)
* If a host is inactive and cannot queue any commands, I don't see
* how things could possibly work anyways.
*/
- if (cmd->device->device_blocked == 0) {
+ if (cmd->device->device_busy == 0) {
if (scsi_retry_command(cmd) == 0) {
return 0;
}
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 4bbfd56bf..04ea9fbf1 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -127,12 +127,14 @@ static struct dev_info device_list[] =
{"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},
+ {"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},
{"nCipher", "Fastness Crypto", "*", BLIST_FORCELUN},
+ {"DEC","HSG80","*", BLIST_FORCELUN},
+ {"COMPAQ","LOGICAL VOLUME","*", BLIST_FORCELUN},
{"NEC", "PD-1 ODX654P", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
{"MATSHITA", "PD-1", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
{"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN},
@@ -142,6 +144,8 @@ static struct dev_info device_list[] =
{"DGC", "DISK", "*", BLIST_SPARSELUN}, // Dell PV 650F (no tgt @ LUN 0)
{"DELL", "PV530F", "*", BLIST_SPARSELUN}, // Dell PV 530F
{"SONY", "TSL", "*", BLIST_FORCELUN}, // DDS3 & DDS4 autoloaders
+ {"DELL", "PERCRAID", "*", BLIST_FORCELUN},
+ {"HP", "NetRAID-4M", "*", BLIST_FORCELUN},
/*
* Must be at end of list...
@@ -578,6 +582,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
case TYPE_SCANNER:
case TYPE_MEDIUM_CHANGER:
case TYPE_ENCLOSURE:
+ case TYPE_COMM:
SDpnt->writeable = 1;
break;
case TYPE_WORM:
diff --git a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c
index 94820f5db..f0e85c70e 100644
--- a/drivers/scsi/scsi_syms.c
+++ b/drivers/scsi/scsi_syms.c
@@ -6,8 +6,6 @@
#include <linux/config.h>
#include <linux/module.h>
-#ifdef CONFIG_MODULES
-
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
@@ -93,4 +91,3 @@ EXPORT_SYMBOL(scsi_hosts);
EXPORT_SYMBOL(scsi_devicelist);
EXPORT_SYMBOL(scsi_device_types);
-#endif /* CONFIG_MODULES */
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 263fcd09e..3441ebed2 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -19,6 +19,9 @@
* scsi disks using eight major numbers.
*
* Modified by Richard Gooch rgooch@atnf.csiro.au to support devfs.
+ *
+ * Modified by Torben Mathiasen tmm@image.dk
+ * Resource allocation fixes in sd_init and cleanups.
*/
#include <linux/config.h>
@@ -77,7 +80,7 @@
struct hd_struct *sd;
-static Scsi_Disk *rscsi_disks = NULL;
+static Scsi_Disk *rscsi_disks;
static int *sd_sizes;
static int *sd_blocksizes;
static int *sd_hardsizes; /* Hardware sector size */
@@ -93,10 +96,30 @@ static void sd_finish(void);
static int sd_attach(Scsi_Device *);
static int sd_detect(Scsi_Device *);
static void sd_detach(Scsi_Device *);
-static void rw_intr(Scsi_Cmnd * SCpnt);
-
static int sd_init_command(Scsi_Cmnd *);
+static struct Scsi_Device_Template sd_template = {
+ name:"disk",
+ tag:"sd",
+ scsi_type:TYPE_DISK,
+ major:SCSI_DISK0_MAJOR,
+ /*
+ * Secondary range of majors that this driver handles.
+ */
+ min_major:SCSI_DISK1_MAJOR,
+ max_major:SCSI_DISK7_MAJOR,
+ blk:1,
+ detect:sd_detect,
+ init:sd_init,
+ finish:sd_finish,
+ attach:sd_attach,
+ detach:sd_detach,
+ init_command:sd_init_command,
+};
+
+
+static void rw_intr(Scsi_Cmnd * SCpnt);
+
#if defined(CONFIG_PPC)
/*
* Moved from arch/ppc/pmac_setup.c. This is where it really belongs.
@@ -240,25 +263,6 @@ static void sd_devname(unsigned int disknum, char *buffer)
}
}
-struct Scsi_Device_Template sd_template = {
- name:"disk",
- tag:"sd",
- scsi_type:TYPE_DISK,
- major:SCSI_DISK0_MAJOR,
- /*
- * Secondary range of majors that this driver handles.
- */
- min_major:SCSI_DISK1_MAJOR,
- max_major:SCSI_DISK7_MAJOR,
- blk:1,
- detect:sd_detect,
- init:sd_init,
- finish:sd_finish,
- attach:sd_attach,
- detach:sd_detach,
- init_command:sd_init_command,
-};
-
static request_queue_t *sd_find_queue(kdev_t dev)
{
Scsi_Disk *dpnt;
@@ -717,13 +721,14 @@ static int sd_init_onedisk(int i)
sd_devname(i, nbuff);
/*
- * If the device is offline, don't try and read capacity or any of the other
- * nicities.
+ * If the device is offline, don't try and read capacity or any
+ * of the other niceties.
*/
- if (rscsi_disks[i].device->online == FALSE) {
+ if (rscsi_disks[i].device->online == FALSE)
return i;
- }
- /* We need to retry the READ_CAPACITY because a UNIT_ATTENTION is
+
+ /*
+ * 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.
*/
@@ -804,7 +809,8 @@ static int sd_init_onedisk(int i)
} while(time1);
printk(".");
}
- } while (the_result && spintime && time_after(spintime_value + 100 * HZ, jiffies));
+ } while (the_result && spintime &&
+ time_after(spintime_value + 100 * HZ, jiffies));
if (spintime) {
if (the_result)
printk("not responding...\n");
@@ -833,15 +839,16 @@ static int sd_init_onedisk(int i)
/*
* The SCSI standard says:
* "READ CAPACITY is necessary for self configuring software"
- * While not mandatory, support of READ CAPACITY is strongly encouraged.
+ * 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.
+ * 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.
+ * 2. Recovery from when someone attempts to read past the
+ * end of the raw device will be slower.
*/
if (the_result) {
@@ -864,15 +871,15 @@ static int sd_init_onedisk(int i)
rscsi_disks[i].capacity = 0x1fffff;
sector_size = 512;
- /* Set dirty bit for removable devices if not ready - sometimes drives
- * will not report this properly. */
+ /* Set dirty bit for removable devices if not ready -
+ * sometimes drives will not report this properly. */
if (rscsi_disks[i].device->removable &&
SRpnt->sr_sense_buffer[2] == NOT_READY)
rscsi_disks[i].device->changed = 1;
} else {
/*
- * FLOPTICAL , if read_capa is ok , drive is assumed to be ready
+ * FLOPTICAL, if read_capa is ok, drive is assumed to be ready
*/
rscsi_disks[i].ready = 1;
@@ -886,7 +893,8 @@ static int sd_init_onedisk(int i)
if (sector_size == 0) {
sector_size = 512;
- printk("%s : sector size 0 reported, assuming 512.\n", nbuff);
+ printk("%s : sector size 0 reported, assuming 512.\n",
+ nbuff);
}
if (sector_size != 512 &&
sector_size != 1024 &&
@@ -921,31 +929,30 @@ static int sd_init_onedisk(int i)
* 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 m;
int hard_sector = sector_size;
+ int sz = rscsi_disks[i].capacity * (hard_sector/256);
+
/* 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 %s: hdwr sector= %d bytes."
- " Sectors= %d [%d MB] [%d.%1d GB]\n",
- nbuff, hard_sector, rscsi_disks[i].capacity,
- mb, sz_quot, sz_rem);
+
+ printk("SCSI device %s: "
+ "%d %d-byte hdwr sectors (%d MB)\n",
+ nbuff, rscsi_disks[i].capacity,
+ hard_sector, (sz/2 - sz/1250 + 974)/1950);
}
+
+ /* Rescale capacity to 512-byte units */
if (sector_size == 4096)
rscsi_disks[i].capacity <<= 3;
if (sector_size == 2048)
- rscsi_disks[i].capacity <<= 2; /* Change into 512 byte sectors */
+ rscsi_disks[i].capacity <<= 2;
if (sector_size == 1024)
- rscsi_disks[i].capacity <<= 1; /* Change into 512 byte sectors */
+ rscsi_disks[i].capacity <<= 1;
if (sector_size == 256)
- rscsi_disks[i].capacity >>= 1; /* Change into 512 byte sectors */
+ rscsi_disks[i].capacity >>= 1;
}
@@ -957,17 +964,23 @@ static int sd_init_onedisk(int i)
/* 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 !
+ * 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 )
+ *
+ * Changed to get all pages (0x3f) rather than page 1 to
+ * get around devices which do not have a page 1. Since
+ * we're only interested in the header anyway, this should
+ * be fine.
+ * -- Matthew Dharm (mdharm-scsi@one-eyed-alien.net)
*/
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;
+ cmd[2] = 0x3f; /* Get all pages */
+ cmd[4] = 8; /* But we only want the 8 byte header */
SRpnt->sr_cmd_len = 0;
SRpnt->sr_sense_buffer[0] = 0;
SRpnt->sr_sense_buffer[2] = 0;
@@ -1005,7 +1018,7 @@ static int sd_init_onedisk(int i)
* their size, and reads partition table entries for them.
*/
-static int sd_registered = 0;
+static int sd_registered;
static int sd_init()
{
@@ -1021,7 +1034,7 @@ static int sd_init()
sd_template.dev_max = N_SD_MAJORS * SCSI_DISKS_PER_MAJOR;
if (!sd_registered) {
- for (i = 0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++) {
+ for (i = 0; i < N_USED_SD_MAJORS; i++) {
if (devfs_register_blkdev(SD_MAJOR(i), "sd", &sd_fops)) {
printk("Unable to get major %d for SCSI disk\n", SD_MAJOR(i));
return 1;
@@ -1033,16 +1046,24 @@ static int sd_init()
if (rscsi_disks)
return 0;
- rscsi_disks = (Scsi_Disk *)
- kmalloc(sd_template.dev_max * sizeof(Scsi_Disk), GFP_ATOMIC);
+ rscsi_disks = kmalloc(sd_template.dev_max * sizeof(Scsi_Disk), GFP_ATOMIC);
+ if (!rscsi_disks)
+ goto cleanup_devfs;
memset(rscsi_disks, 0, sd_template.dev_max * sizeof(Scsi_Disk));
/* for every (necessary) major: */
- sd_sizes = (int *) kmalloc((sd_template.dev_max << 4) * sizeof(int), GFP_ATOMIC);
+ sd_sizes = kmalloc((sd_template.dev_max << 4) * sizeof(int), GFP_ATOMIC);
+ if (!sd_sizes)
+ goto cleanup_disks;
memset(sd_sizes, 0, (sd_template.dev_max << 4) * sizeof(int));
- sd_blocksizes = (int *) kmalloc((sd_template.dev_max << 4) * sizeof(int), GFP_ATOMIC);
- sd_hardsizes = (int *) kmalloc((sd_template.dev_max << 4) * sizeof(int), GFP_ATOMIC);
+ sd_blocksizes = kmalloc((sd_template.dev_max << 4) * sizeof(int), GFP_ATOMIC);
+ if (!sd_blocksizes)
+ goto cleanup_sizes;
+
+ sd_hardsizes = kmalloc((sd_template.dev_max << 4) * sizeof(int), GFP_ATOMIC);
+ if (!sd_hardsizes)
+ goto cleanup_blocksizes;
for (i = 0; i < sd_template.dev_max << 4; i++) {
sd_blocksizes[i] = 1024;
@@ -1053,22 +1074,29 @@ static int sd_init()
blksize_size[SD_MAJOR(i)] = sd_blocksizes + i * (SCSI_DISKS_PER_MAJOR << 4);
hardsect_size[SD_MAJOR(i)] = sd_hardsizes + i * (SCSI_DISKS_PER_MAJOR << 4);
}
- sd = (struct hd_struct *) kmalloc((sd_template.dev_max << 4) *
+ sd = kmalloc((sd_template.dev_max << 4) *
sizeof(struct hd_struct),
GFP_ATOMIC);
+ if (!sd)
+ goto cleanup_sd;
memset(sd, 0, (sd_template.dev_max << 4) * sizeof(struct hd_struct));
if (N_USED_SD_MAJORS > 1)
- sd_gendisks = (struct gendisk *)
- kmalloc(N_USED_SD_MAJORS * sizeof(struct gendisk), GFP_ATOMIC);
+ sd_gendisks = kmalloc(N_USED_SD_MAJORS * sizeof(struct gendisk), GFP_ATOMIC);
+ if (!sd_gendisks)
+ goto cleanup_sd_gendisks;
for (i = 0; i < N_USED_SD_MAJORS; i++) {
sd_gendisks[i] = sd_gendisk;
sd_gendisks[i].de_arr = kmalloc (SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].de_arr,
GFP_ATOMIC);
+ if (!sd_gendisks[i].de_arr)
+ goto cleanup_gendisks_de_arr;
memset (sd_gendisks[i].de_arr, 0,
SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].de_arr);
sd_gendisks[i].flags = kmalloc (SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].flags,
GFP_ATOMIC);
+ if (!sd_gendisks[i].flags)
+ goto cleanup_gendisks_flags;
memset (sd_gendisks[i].flags, 0,
SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].flags);
sd_gendisks[i].major = SD_MAJOR(i);
@@ -1085,6 +1113,31 @@ static int sd_init()
LAST_SD_GENDISK.next = NULL;
return 0;
+
+cleanup_gendisks_flags:
+ kfree(sd_gendisks[i].de_arr);
+cleanup_gendisks_de_arr:
+ while (--i >= 0 ) {
+ kfree(sd_gendisks[i].de_arr);
+ kfree(sd_gendisks[i].flags);
+ }
+ kfree(sd_gendisks);
+cleanup_sd_gendisks:
+ kfree(sd);
+cleanup_sd:
+ kfree(sd_hardsizes);
+cleanup_blocksizes:
+ kfree(sd_blocksizes);
+cleanup_sizes:
+ kfree(sd_sizes);
+cleanup_disks:
+ kfree(rscsi_disks);
+cleanup_devfs:
+ for (i = 0; i < N_USED_SD_MAJORS; i++) {
+ devfs_unregister_blkdev(SD_MAJOR(i), "sd");
+ }
+ sd_registered--;
+ return 1;
}
@@ -1093,7 +1146,7 @@ static void sd_finish()
struct gendisk *gendisk;
int i;
- for (i = 0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++) {
+ for (i = 0; i < N_USED_SD_MAJORS; i++) {
blk_dev[SD_MAJOR(i)].queue = sd_find_queue;
}
for (gendisk = gendisk_head; gendisk != NULL; gendisk = gendisk->next)
@@ -1286,14 +1339,13 @@ static void sd_detach(Scsi_Device * SDp)
return;
}
-#ifdef MODULE
-
-int init_module(void)
+static int __init init_sd(void)
{
- sd_template.module = &__this_module;
+ sd_template.module = THIS_MODULE;
return scsi_register_module(MODULE_SCSI_DEV, &sd_template);
}
-void cleanup_module(void)
+
+static void __exit exit_sd(void)
{
struct gendisk **prev_sdgd_link;
struct gendisk *sdgd;
@@ -1302,15 +1354,15 @@ void cleanup_module(void)
scsi_unregister_module(MODULE_SCSI_DEV, &sd_template);
- for (i = 0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++)
+ for (i = 0; i < N_USED_SD_MAJORS; i++)
devfs_unregister_blkdev(SD_MAJOR(i), "sd");
sd_registered--;
if (rscsi_disks != NULL) {
- kfree((char *) rscsi_disks);
- kfree((char *) sd_sizes);
- kfree((char *) sd_blocksizes);
- kfree((char *) sd_hardsizes);
+ kfree(rscsi_disks);
+ kfree(sd_sizes);
+ kfree(sd_blocksizes);
+ kfree(sd_hardsizes);
kfree((char *) sd);
/*
@@ -1331,7 +1383,7 @@ void cleanup_module(void)
removed > N_USED_SD_MAJORS ? "total" : "just", removed);
}
- for (i = 0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++) {
+ for (i = 0; i < N_USED_SD_MAJORS; i++) {
blk_size[SD_MAJOR(i)] = NULL;
hardsect_size[SD_MAJOR(i)] = NULL;
read_ahead[SD_MAJOR(i)] = 0;
@@ -1340,23 +1392,6 @@ void cleanup_module(void)
if (sd_gendisks != &sd_gendisk)
kfree(sd_gendisks);
}
-#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:
- */
+module_init(init_sd);
+module_exit(exit_sd);
diff --git a/drivers/scsi/seagate.c b/drivers/scsi/seagate.c
index 53f194e43..1ee407d14 100644
--- a/drivers/scsi/seagate.c
+++ b/drivers/scsi/seagate.c
@@ -5,7 +5,8 @@
*
* Note : TMC-880 boards don't work because they have two bits in
* the status register flipped, I'll fix this "RSN"
- * [why do I have strong feeling that above message is from 1993? :-) pavel@ucw.cz]
+ * [why do I have strong feeling that above message is from 1993? :-)
+ * pavel@ucw.cz]
*
* 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.
@@ -18,6 +19,13 @@
*
* 1998-jul-29 - created DPRINTK macros and made it work under
* linux 2.1.112, simplified some #defines etc. <pavel@ucw.cz>
+ *
+ * Aug 2000 - aeb - deleted seagate_st0x_biosparam(). It would try to
+ * read the physical disk geometry, a bad mistake. Of course it doesnt
+ * matter much what geometry one invents, but on large disks it
+ * returned 256 (or more) heads, causing all kind of failures.
+ * Of course this means that people might see a different geometry now,
+ * so boot parameters may be necessary in some cases.
*/
/*
@@ -1702,127 +1710,7 @@ int seagate_st0x_reset (Scsi_Cmnd * SCpnt, unsigned int reset_flags)
}
-int seagate_st0x_biosparam (Disk * disk, kdev_t dev, int *ip)
-{
- unsigned char buf[256 + sizeof (Scsi_Ioctl_Command)],
- cmd[6], *data, *page;
- Scsi_Ioctl_Command *sic = (Scsi_Ioctl_Command *) buf;
- int 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;
-
- data = sic->data;
-
- 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.
- */
- sic->inlen = 0;
- sic->outlen = 256;
-
- memcpy (data, cmd, 6);
-
- if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND,
- sic)))
- {
-/*
- * 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,
- sic)))
- {
- 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. Multiply
- heads*sectors*cyl to get capacity. Then start rounding down.
- */
- capacity = heads * sectors * cylinders;
-
- /* Old MFM Drives use this, so does the Seagate */
- sectors = 17;
- 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;
+static Scsi_Host_Template driver_template = SEAGATE_ST0X;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/seagate.h b/drivers/scsi/seagate.h
index c8ead00c4..3aedafd57 100644
--- a/drivers/scsi/seagate.h
+++ b/drivers/scsi/seagate.h
@@ -20,16 +20,12 @@ int seagate_st0x_abort(Scsi_Cmnd *);
const char *seagate_st0x_info(struct Scsi_Host *);
int seagate_st0x_reset(Scsi_Cmnd *, unsigned int);
-#include <linux/kdev_t.h>
-int seagate_st0x_biosparam(Disk *, kdev_t, int*);
-
#define SEAGATE_ST0X { detect: seagate_st0x_detect, \
info: seagate_st0x_info, \
command: seagate_st0x_command, \
queuecommand: seagate_st0x_queue_command, \
abort: seagate_st0x_abort, \
reset: seagate_st0x_reset, \
- bios_param: seagate_st0x_biosparam, \
can_queue: 1, \
this_id: 7, \
sg_tablesize: SG_ALL, \
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 75493008e..6d5b92f1f 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -17,8 +17,11 @@
* any later version.
*
*/
- static char * sg_version_str = "Version: 3.1.16 (20000716)";
- static int sg_version_num = 30116; /* 2 digits for each component */
+#include <linux/config.h>
+#ifdef CONFIG_PROC_FS
+ static char * sg_version_str = "Version: 3.1.17 (20001002)";
+#endif
+ static int sg_version_num = 30117; /* 2 digits for each component */
/*
* D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes:
* - scsi logging is available via SCSI_LOG_TIMEOUT macros. First
@@ -38,7 +41,6 @@
* # cat /proc/scsi/sg/debug
*
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
@@ -67,10 +69,8 @@
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
static int sg_proc_init(void);
-#ifdef MODULE
static void sg_proc_cleanup(void);
#endif
-#endif
#ifndef LINUX_VERSION_CODE
#include <linux/version.h>
@@ -112,12 +112,12 @@ static void sg_finish(void);
static int sg_detect(Scsi_Device *);
static void sg_detach(Scsi_Device *);
-static Scsi_Cmnd * dummy_cmdp = 0; /* only used for sizeof */
+static Scsi_Request * dummy_cmdp = 0; /* only used for sizeof */
static rwlock_t sg_dev_arr_lock = RW_LOCK_UNLOCKED; /* Also used to lock
file descriptor list for device */
-struct Scsi_Device_Template sg_template =
+static struct Scsi_Device_Template sg_template =
{
tag:"sg",
scsi_type:0xff,
@@ -148,12 +148,12 @@ struct sg_fd;
typedef struct sg_request /* SG_MAX_QUEUE requests outstanding per file */
{
- Scsi_Cmnd * my_cmdp; /* != 0 when request with lower levels */
+ Scsi_Request * my_cmdp; /* != 0 when request with lower levels */
struct sg_request * nextrp; /* NULL -> tail request (slist) */
struct sg_fd * parentfp; /* NULL -> not in use */
Sg_scatter_hold data; /* hold buffer, perhaps scatter list */
sg_io_hdr_t header; /* scsi command+info, see <scsi/sg.h> */
- unsigned char sense_b[sizeof(dummy_cmdp->sense_buffer)];
+ unsigned char sense_b[sizeof(dummy_cmdp->sr_sense_buffer)];
char res_used; /* 1 -> using reserve buffer, 0 -> not ... */
char orphan; /* 1 -> drop on sight, 0 -> normal */
char sg_io_owned; /* 1 -> packet belongs to SG_IO */
@@ -230,15 +230,17 @@ static Sg_request * sg_add_request(Sg_fd * sfp);
static int sg_remove_request(Sg_fd * sfp, Sg_request * srp);
static int sg_res_in_use(Sg_fd * sfp);
static int sg_dio_in_use(Sg_fd * sfp);
-static void sg_clr_scpnt(Scsi_Cmnd * SCpnt);
-static void sg_shorten_timeout(Scsi_Cmnd * scpnt);
+static void sg_clr_srpnt(Scsi_Request * SRpnt);
+static void sg_shorten_timeout(Scsi_Request * srpnt);
static int sg_ms_to_jif(unsigned int msecs);
static unsigned sg_jif_to_ms(int jifs);
static int sg_allow_access(unsigned char opcode, char dev_type);
-static int sg_last_dev(void);
static int sg_build_dir(Sg_request * srp, Sg_fd * sfp, int dxfer_len);
static void sg_unmap_and(Sg_scatter_hold * schp, int free_also);
static Sg_device * sg_get_dev(int dev);
+#ifdef CONFIG_PROC_FS
+static int sg_last_dev(void);
+#endif
static Sg_device ** sg_dev_arr = NULL;
@@ -267,7 +269,8 @@ static int sg_open(struct inode * inode, struct file * filp)
* else try and use this device. Also, if error recovery fails, it
* may try and take the device offline, in which case all further
* access to the device is prohibited. */
- if(! scsi_block_when_processing_errors(sdp->device))
+ if (! ((flags & O_NONBLOCK) ||
+ scsi_block_when_processing_errors(sdp->device)))
return -ENXIO;
SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags));
@@ -275,7 +278,7 @@ static int sg_open(struct inode * inode, struct file * filp)
if (flags & O_EXCL) {
if (O_RDONLY == (flags & O_ACCMODE))
return -EACCES; /* Can't lock it with read only access */
- if (sdp->headfp && (filp->f_flags & O_NONBLOCK))
+ if (sdp->headfp && (flags & O_NONBLOCK))
return -EBUSY;
res = 0; /* following is a macro that beats race condition */
__wait_event_interruptible(sdp->o_excl_wait,
@@ -285,7 +288,7 @@ static int sg_open(struct inode * inode, struct file * filp)
return res; /* -ERESTARTSYS because signal hit process */
}
else if (sdp->exclude) { /* some other fd has an exclusive lock on dev */
- if (filp->f_flags & O_NONBLOCK)
+ if (flags & O_NONBLOCK)
return -EBUSY;
res = 0; /* following is a macro that beats race condition */
__wait_event_interruptible(sdp->o_excl_wait, (! sdp->exclude), res);
@@ -349,9 +352,6 @@ static ssize_t sg_read(struct file * filp, char * buf,
return -ENXIO;
SCSI_LOG_TIMEOUT(3, printk("sg_read: dev=%d, count=%d\n",
MINOR(sdp->i_rdev), (int)count));
-
- if(! scsi_block_when_processing_errors(sdp->device))
- return -ENXIO;
if (ppos != &filp->f_pos)
; /* FIXME: Hmm. Seek to the right place, or fail? */
if ((k = verify_area(VERIFY_WRITE, buf, count)))
@@ -447,20 +447,16 @@ static ssize_t sg_read(struct file * filp, char * buf,
static ssize_t sg_new_read(Sg_fd * sfp, char * buf, size_t count,
Sg_request * srp)
{
- Sg_device * sdp = sfp->parentdp;
sg_io_hdr_t * hp = &srp->header;
int k, len;
- if(! scsi_block_when_processing_errors(sdp->device) )
- return -ENXIO;
if (count < size_sg_io_hdr)
return -EINVAL;
-
hp->sb_len_wr = 0;
if ((hp->mx_sb_len > 0) && hp->sbp) {
if ((CHECK_CONDITION & hp->masked_status) ||
(DRIVER_SENSE & hp->driver_status)) {
- int sb_len = sizeof(dummy_cmdp->sense_buffer);
+ int sb_len = sizeof(dummy_cmdp->sr_sense_buffer);
sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len;
len = 8 + (int)srp->sense_b[7]; /* Additional sense length field */
len = (len > sb_len) ? sb_len : len;
@@ -492,14 +488,15 @@ static ssize_t sg_write(struct file * filp, const char * buf,
Sg_request * srp;
struct sg_header old_hdr;
sg_io_hdr_t * hp;
- unsigned char cmnd[sizeof(dummy_cmdp->cmnd)];
+ unsigned char cmnd[sizeof(dummy_cmdp->sr_cmnd)];
if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp)))
return -ENXIO;
SCSI_LOG_TIMEOUT(3, printk("sg_write: dev=%d, count=%d\n",
MINOR(sdp->i_rdev), (int)count));
- if(! scsi_block_when_processing_errors(sdp->device) )
+ if (! ((filp->f_flags & O_NONBLOCK) ||
+ scsi_block_when_processing_errors(sdp->device)))
return -ENXIO;
if (ppos != &filp->f_pos)
; /* FIXME: Hmm. Seek to the right place, or fail? */
@@ -581,7 +578,7 @@ static ssize_t sg_new_write(Sg_fd * sfp, const char * buf, size_t count,
int k;
Sg_request * srp;
sg_io_hdr_t * hp;
- unsigned char cmnd[sizeof(dummy_cmdp->cmnd)];
+ unsigned char cmnd[sizeof(dummy_cmdp->sr_cmnd)];
int timeout;
if (count < size_sg_io_hdr)
@@ -625,7 +622,7 @@ static int sg_common_write(Sg_fd * sfp, Sg_request * srp,
unsigned char * cmnd, int timeout, int blocking)
{
int k;
- Scsi_Cmnd * SCpnt;
+ Scsi_Request * SRpnt;
Sg_device * sdp = sfp->parentdp;
sg_io_hdr_t * hp = &srp->header;
@@ -652,38 +649,34 @@ static int sg_common_write(Sg_fd * sfp, Sg_request * srp,
return k;
}
/* SCSI_LOG_TIMEOUT(7, printk("sg_write: allocating device\n")); */
- SCpnt = scsi_allocate_device(sdp->device, blocking, TRUE);
- if (! SCpnt) {
- sg_finish_rem_req(srp);
- return (signal_pending(current)) ? -EINTR : -EAGAIN;
- /* No available command blocks, or, interrupted while waiting */
- }
+ SRpnt = scsi_allocate_request(sdp->device);
+
/* SCSI_LOG_TIMEOUT(7, printk("sg_write: device allocated\n")); */
- srp->my_cmdp = SCpnt;
- SCpnt->request.rq_dev = sdp->i_rdev;
- SCpnt->request.rq_status = RQ_ACTIVE;
- SCpnt->sense_buffer[0] = 0;
- SCpnt->cmd_len = hp->cmd_len;
+ srp->my_cmdp = SRpnt;
+ SRpnt->sr_request.rq_dev = sdp->i_rdev;
+ SRpnt->sr_request.rq_status = RQ_ACTIVE;
+ SRpnt->sr_sense_buffer[0] = 0;
+ SRpnt->sr_cmd_len = hp->cmd_len;
/* Set the LUN field in the command structure, overriding user input */
if (! (hp->flags & SG_FLAG_LUN_INHIBIT))
cmnd[1] = (cmnd[1] & 0x1f) | (sdp->device->lun << 5);
/* SCSI_LOG_TIMEOUT(7, printk("sg_write: do cmd\n")); */
- SCpnt->use_sg = srp->data.k_use_sg;
- SCpnt->sglist_len = srp->data.sglist_len;
- SCpnt->bufflen = srp->data.bufflen;
- SCpnt->underflow = 0;
- SCpnt->buffer = srp->data.buffer;
+ SRpnt->sr_use_sg = srp->data.k_use_sg;
+ SRpnt->sr_sglist_len = srp->data.sglist_len;
+ SRpnt->sr_bufflen = srp->data.bufflen;
+ SRpnt->sr_underflow = 0;
+ SRpnt->sr_buffer = srp->data.buffer;
switch (hp->dxfer_direction) {
case SG_DXFER_TO_FROM_DEV:
case SG_DXFER_FROM_DEV:
- SCpnt->sc_data_direction = SCSI_DATA_READ; break;
+ SRpnt->sr_data_direction = SCSI_DATA_READ; break;
case SG_DXFER_TO_DEV:
- SCpnt->sc_data_direction = SCSI_DATA_WRITE; break;
+ SRpnt->sr_data_direction = SCSI_DATA_WRITE; break;
case SG_DXFER_UNKNOWN:
- SCpnt->sc_data_direction = SCSI_DATA_UNKNOWN; break;
+ SRpnt->sr_data_direction = SCSI_DATA_UNKNOWN; break;
default:
- SCpnt->sc_data_direction = SCSI_DATA_NONE; break;
+ SRpnt->sr_data_direction = SCSI_DATA_NONE; break;
}
srp->data.k_use_sg = 0;
srp->data.sglist_len = 0;
@@ -692,10 +685,10 @@ static int sg_common_write(Sg_fd * sfp, Sg_request * srp,
hp->duration = jiffies; /* unit jiffies now, millisecs after done */
/* Now send everything of to mid-level. The next time we hear about this
packet is when sg_cmd_done_bh() is called (i.e. a callback). */
- scsi_do_cmd(SCpnt, (void *)cmnd,
- (void *)SCpnt->buffer, hp->dxfer_len,
+ scsi_do_req(SRpnt, (void *)cmnd,
+ (void *)SRpnt->sr_buffer, hp->dxfer_len,
sg_cmd_done_bh, timeout, SG_DEFAULT_RETRIES);
- /* dxfer_len overwrites SCpnt->bufflen, hence need for b_malloc_len */
+ /* dxfer_len overwrites SRpnt->sr_bufflen, hence need for b_malloc_len */
return 0;
}
@@ -712,8 +705,6 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
return -ENXIO;
SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: dev=%d, cmd=0x%x\n",
MINOR(sdp->i_rdev), (int)cmd_in));
- if(! scsi_block_when_processing_errors(sdp->device) )
- return -ENXIO;
read_only = (O_RDWR != (filp->f_flags & O_ACCMODE));
switch(cmd_in)
@@ -885,7 +876,11 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
case SG_EMULATED_HOST:
return put_user(sdp->device->host->hostt->emulated, (int *)arg);
case SG_SCSI_RESET:
- if (! scsi_block_when_processing_errors(sdp->device))
+ if (filp->f_flags & O_NONBLOCK) {
+ if (sdp->device->host->in_recovery)
+ return -EBUSY;
+ }
+ else if (! scsi_block_when_processing_errors(sdp->device))
return -EBUSY;
result = get_user(val, (int *)arg);
if (result) return result;
@@ -989,7 +984,8 @@ static int sg_fasync(int fd, struct file * filp, int mode)
* mid level when a command is completed (or has failed). */
static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt)
{
- int dev = MINOR(SCpnt->request.rq_dev);
+ Scsi_Request * SRpnt = SCpnt->sc_request;
+ int dev = MINOR(SRpnt->sr_request.rq_dev);
Sg_device * sdp = NULL;
Sg_fd * sfp;
Sg_request * srp = NULL;
@@ -1002,15 +998,15 @@ static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt)
if (NULL == sdp) {
read_unlock(&sg_dev_arr_lock);
SCSI_LOG_TIMEOUT(1, printk("sg...bh: bad args dev=%d\n", dev));
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
return;
}
sfp = sdp->headfp;
while (sfp) {
read_lock(&sfp->rq_list_lock);
for (srp = sfp->headrp; srp; srp = srp->nextrp) {
- if (SCpnt == srp->my_cmdp)
+ if (SRpnt == srp->my_cmdp)
break;
}
read_unlock(&sfp->rq_list_lock);
@@ -1021,41 +1017,41 @@ static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt)
read_unlock(&sg_dev_arr_lock);
if (! srp) {
SCSI_LOG_TIMEOUT(1, printk("sg...bh: req missing, dev=%d\n", dev));
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
return;
}
/* First transfer ownership of data buffers to sg_device object. */
- srp->data.k_use_sg = SCpnt->use_sg;
- srp->data.sglist_len = SCpnt->sglist_len;
- srp->data.bufflen = SCpnt->bufflen;
- srp->data.buffer = SCpnt->buffer;
- sg_clr_scpnt(SCpnt);
+ srp->data.k_use_sg = SRpnt->sr_use_sg;
+ srp->data.sglist_len = SRpnt->sr_sglist_len;
+ srp->data.bufflen = SRpnt->sr_bufflen;
+ srp->data.buffer = SRpnt->sr_buffer;
+ sg_clr_srpnt(SRpnt);
srp->my_cmdp = NULL;
srp->done = 1;
SCSI_LOG_TIMEOUT(4, printk("sg...bh: dev=%d, pack_id=%d, res=0x%x\n",
- dev, srp->header.pack_id, (int)SCpnt->result));
+ dev, srp->header.pack_id, (int)SRpnt->sr_result));
srp->header.resid = SCpnt->resid;
/* sg_unmap_and(&srp->data, 0); */ /* unmap locked pages a.s.a.p. */
/* N.B. unit of duration changes here from jiffies to millisecs */
srp->header.duration = sg_jif_to_ms(jiffies - (int)srp->header.duration);
- if (0 != SCpnt->result) {
- memcpy(srp->sense_b, SCpnt->sense_buffer, sizeof(srp->sense_b));
- srp->header.status = 0xff & SCpnt->result;
- srp->header.masked_status = status_byte(SCpnt->result);
- srp->header.msg_status = msg_byte(SCpnt->result);
- srp->header.host_status = host_byte(SCpnt->result);
- srp->header.driver_status = driver_byte(SCpnt->result);
+ if (0 != SRpnt->sr_result) {
+ memcpy(srp->sense_b, SRpnt->sr_sense_buffer, sizeof(srp->sense_b));
+ srp->header.status = 0xff & SRpnt->sr_result;
+ srp->header.masked_status = status_byte(SRpnt->sr_result);
+ srp->header.msg_status = msg_byte(SRpnt->sr_result);
+ srp->header.host_status = host_byte(SRpnt->sr_result);
+ srp->header.driver_status = driver_byte(SRpnt->sr_result);
if ((sdp->sgdebug > 0) &&
((CHECK_CONDITION == srp->header.masked_status) ||
(COMMAND_TERMINATED == srp->header.masked_status)))
- print_sense("sg_cmd_done_bh", SCpnt);
+ print_req_sense("sg_cmd_done_bh", SRpnt);
/* Following if statement is a patch supplied by Eric Youngdale */
- if (driver_byte(SCpnt->result) != 0
- && (SCpnt->sense_buffer[0] & 0x7f) == 0x70
- && (SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION
+ if (driver_byte(SRpnt->sr_result) != 0
+ && (SRpnt->sr_sense_buffer[0] & 0x7f) == 0x70
+ && (SRpnt->sr_sense_buffer[2] & 0xf) == UNIT_ATTENTION
&& sdp->device->removable) {
/* Detected disc change. Set the bit - this may be used if */
/* there are filesystems using this device. */
@@ -1064,8 +1060,8 @@ static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt)
}
/* Rely on write phase to clean out srp status values, so no "else" */
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
if (sfp->closed) { /* whoops this fd already released, cleanup */
SCSI_LOG_TIMEOUT(1,
printk("sg...bh: already closed, freeing ...\n"));
@@ -1300,19 +1296,19 @@ static void sg_detach(Scsi_Device * scsidp)
return;
}
-#ifdef MODULE
-
+MODULE_AUTHOR("Douglas Gilbert");
+MODULE_DESCRIPTION("SCSI generic (sg) driver");
MODULE_PARM(def_reserved_size, "i");
MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd");
-int init_module(void) {
+static int __init init_sg(void) {
if (def_reserved_size >= 0)
sg_big_buff = def_reserved_size;
- sg_template.module = &__this_module;
+ sg_template.module = THIS_MODULE;
return scsi_register_module(MODULE_SCSI_DEV, &sg_template);
}
-void cleanup_module( void)
+static void __exit exit_sg( void)
{
#ifdef CONFIG_PROC_FS
sg_proc_cleanup();
@@ -1327,7 +1323,6 @@ void cleanup_module( void)
}
sg_template.dev_max = 0;
}
-#endif /* MODULE */
#if 0
@@ -1336,7 +1331,7 @@ extern void scsi_old_times_out (Scsi_Cmnd * SCpnt);
#endif
/* Can't see clean way to abort a command so shorten timeout to 1 jiffy */
-static void sg_shorten_timeout(Scsi_Cmnd * scpnt)
+static void sg_shorten_timeout(Scsi_Request * srpnt)
{
#if 0 /* scsi_syms.c is very miserly about exported functions */
scsi_delete_timer(scpnt);
@@ -1975,6 +1970,7 @@ static Sg_request * sg_get_rq_mark(Sg_fd * sfp, int pack_id)
return resp;
}
+#ifdef CONFIG_PROC_FS
static Sg_request * sg_get_nth_request(Sg_fd * sfp, int nth)
{
Sg_request * resp;
@@ -1988,6 +1984,7 @@ static Sg_request * sg_get_nth_request(Sg_fd * sfp, int nth)
read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
return resp;
}
+#endif
/* always adds to end of list */
static Sg_request * sg_add_request(Sg_fd * sfp)
@@ -2067,6 +2064,7 @@ static int sg_remove_request(Sg_fd * sfp, Sg_request * srp)
return res;
}
+#ifdef CONFIG_PROC_FS
static Sg_fd * sg_get_nth_sfp(Sg_device * sdp, int nth)
{
Sg_fd * resp;
@@ -2080,6 +2078,7 @@ static Sg_fd * sg_get_nth_sfp(Sg_device * sdp, int nth)
read_unlock_irqrestore(&sg_dev_arr_lock, iflags);
return resp;
}
+#endif
static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev)
{
@@ -2366,14 +2365,14 @@ static void sg_free(char * buff, int size, int mem_src)
sg_low_free(buff, size, mem_src);
}
-static void sg_clr_scpnt(Scsi_Cmnd * SCpnt)
+static void sg_clr_srpnt(Scsi_Request * SRpnt)
{
- SCpnt->use_sg = 0;
- SCpnt->sglist_len = 0;
- SCpnt->bufflen = 0;
- SCpnt->buffer = NULL;
- SCpnt->underflow = 0;
- SCpnt->request.rq_dev = MKDEV(0, 0); /* "sg" _disowns_ command blk */
+ SRpnt->sr_use_sg = 0;
+ SRpnt->sr_sglist_len = 0;
+ SRpnt->sr_bufflen = 0;
+ SRpnt->sr_buffer = NULL;
+ SRpnt->sr_underflow = 0;
+ SRpnt->sr_request.rq_dev = MKDEV(0, 0); /* "sg" _disowns_ command blk */
}
static int sg_ms_to_jif(unsigned int msecs)
@@ -2413,6 +2412,7 @@ static int sg_allow_access(unsigned char opcode, char dev_type)
}
+#ifdef CONFIG_PROC_FS
static int sg_last_dev()
{
int k;
@@ -2424,6 +2424,7 @@ static int sg_last_dev()
read_unlock_irqrestore(&sg_dev_arr_lock, iflags);
return k + 1; /* origin 1 */
}
+#endif
static Sg_device * sg_get_dev(int dev)
{
@@ -2543,7 +2544,6 @@ static int sg_proc_init()
return 0;
}
-#ifdef MODULE
static void sg_proc_cleanup()
{
int k;
@@ -2555,7 +2555,6 @@ static void sg_proc_cleanup()
remove_proc_entry(sg_proc_leaf_names[k], sg_proc_sgp);
remove_proc_entry(sg_proc_sg_dirname, proc_scsi);
}
-#endif
static int sg_proc_dressz_read(char * buffer, char ** start, off_t offset,
int size, int * eof, void * data)
@@ -2642,8 +2641,8 @@ static int sg_proc_debug_info(char * buffer, int * len, off_t * begin,
/* stop indenting so far ... */
PRINT_PROC(srp->res_used ? " rb>> " :
((SG_INFO_DIRECT_IO_MASK & hp->info) ? " dio>> " : " "));
- blen = srp->my_cmdp ? srp->my_cmdp->bufflen : srp->data.bufflen;
- usg = srp->my_cmdp ? srp->my_cmdp->use_sg : srp->data.k_use_sg;
+ blen = srp->my_cmdp ? srp->my_cmdp->sr_bufflen : srp->data.bufflen;
+ usg = srp->my_cmdp ? srp->my_cmdp->sr_use_sg : srp->data.k_use_sg;
PRINT_PROC(srp->done ? ((1 == srp->done) ? "rcv:" : "fin:")
: (srp->my_cmdp ? "act:" : "prior:"));
PRINT_PROC(" id=%d blen=%d", srp->header.pack_id, blen);
@@ -2785,3 +2784,7 @@ static int sg_proc_version_info(char * buffer, int * len, off_t * begin,
return 1;
}
#endif /* CONFIG_PROC_FS */
+
+
+module_init(init_sg);
+module_exit(exit_sg);
diff --git a/drivers/scsi/sgiwd93.c b/drivers/scsi/sgiwd93.c
index 5a475aa9a..cd92b715e 100644
--- a/drivers/scsi/sgiwd93.c
+++ b/drivers/scsi/sgiwd93.c
@@ -317,18 +317,14 @@ int __init sgiwd93_detect(Scsi_Host_Template *SGIblows)
return 1; /* Found one. */
}
-#ifdef MODULE
-
#define HOSTS_C
#include "sgiwd93.h"
-Scsi_Host_Template driver_template = SGIWD93_SCSI;
+static Scsi_Host_Template driver_template = SGIWD93_SCSI;
#include "scsi_module.c"
-#endif
-
int sgiwd93_release(struct Scsi_Host *instance)
{
#ifdef MODULE
diff --git a/drivers/scsi/sim710.c b/drivers/scsi/sim710.c
index e7489348d..8ce5a87ab 100644
--- a/drivers/scsi/sim710.c
+++ b/drivers/scsi/sim710.c
@@ -1603,7 +1603,8 @@ sim710_release(struct Scsi_Host *host)
return 1;
}
-Scsi_Host_Template driver_template = SIM710_SCSI;
+#endif
+
+static Scsi_Host_Template driver_template = SIM710_SCSI;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/sim710.h b/drivers/scsi/sim710.h
index 9d86cea47..5fed80efb 100644
--- a/drivers/scsi/sim710.h
+++ b/drivers/scsi/sim710.h
@@ -21,7 +21,6 @@ int sim710_release(struct Scsi_Host *);
#define sim710_release NULL
#endif
-#if defined(HOSTS_C) || defined(MODULE)
#include <scsi/scsicam.h>
#define SIM710_SCSI { proc_name: "sim710", \
@@ -41,8 +40,6 @@ int sim710_release(struct Scsi_Host *);
use_clustering: DISABLE_CLUSTERING, \
use_new_eh_code: 1}
-#endif
-
#ifndef HOSTS_C
#ifdef __BIG_ENDIAN
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index c5680bca3..6357f150a 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -28,6 +28,9 @@
* Modified by Jens Axboe <axboe@suse.de> - support DVD-RAM
* transparently and loose the GHOST hack
*
+ * Modified by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * check resource allocation in sr_init and some cleanups
+ *
*/
#include <linux/module.h>
@@ -40,6 +43,7 @@
#include <linux/errno.h>
#include <linux/cdrom.h>
#include <linux/interrupt.h>
+#include <linux/init.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -67,7 +71,7 @@ static void sr_detach(Scsi_Device *);
static int sr_init_command(Scsi_Cmnd *);
-struct Scsi_Device_Template sr_template =
+static struct Scsi_Device_Template sr_template =
{
name:"cdrom",
tag:"sr",
@@ -82,11 +86,11 @@ struct Scsi_Device_Template sr_template =
init_command:sr_init_command
};
-Scsi_CD *scsi_CDs = NULL;
-static int *sr_sizes = NULL;
+Scsi_CD *scsi_CDs;
+static int *sr_sizes;
-static int *sr_blocksizes = NULL;
-static int *sr_hardsizes = NULL;
+static int *sr_blocksizes;
+static int *sr_hardsizes;
static int sr_open(struct cdrom_device_info *, int);
void get_sectorsize(int);
@@ -692,7 +696,7 @@ static int sr_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command
return cgc->stat;
}
-static int sr_registered = 0;
+static int sr_registered;
static int sr_init()
{
@@ -710,30 +714,45 @@ static int sr_init()
}
if (scsi_CDs)
return 0;
- sr_template.dev_max =
- sr_template.dev_noticed + SR_EXTRA_DEVS;
- scsi_CDs = (Scsi_CD *) kmalloc(sr_template.dev_max * sizeof(Scsi_CD), GFP_ATOMIC);
+
+ sr_template.dev_max = sr_template.dev_noticed + SR_EXTRA_DEVS;
+ scsi_CDs = kmalloc(sr_template.dev_max * sizeof(Scsi_CD), GFP_ATOMIC);
+ if (!scsi_CDs)
+ goto cleanup_devfs;
memset(scsi_CDs, 0, sr_template.dev_max * sizeof(Scsi_CD));
- sr_sizes = (int *) kmalloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC);
+ sr_sizes = kmalloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC);
+ if (!sr_sizes)
+ goto cleanup_cds;
memset(sr_sizes, 0, sr_template.dev_max * sizeof(int));
- sr_blocksizes = (int *) kmalloc(sr_template.dev_max *
- sizeof(int), GFP_ATOMIC);
+ sr_blocksizes = kmalloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC);
+ if (!sr_blocksizes)
+ goto cleanup_sizes;
- sr_hardsizes = (int *) kmalloc(sr_template.dev_max *
- sizeof(int), GFP_ATOMIC);
+ sr_hardsizes = kmalloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC);
+ if (!sr_hardsizes)
+ goto cleanup_blocksizes;
/*
* These are good guesses for the time being.
*/
- for (i = 0; i < sr_template.dev_max; i++)
- {
+ for (i = 0; i < sr_template.dev_max; i++) {
sr_blocksizes[i] = 2048;
sr_hardsizes[i] = 2048;
}
blksize_size[MAJOR_NR] = sr_blocksizes;
hardsect_size[MAJOR_NR] = sr_hardsizes;
return 0;
+cleanup_blocksizes:
+ kfree(sr_blocksizes);
+cleanup_sizes:
+ kfree(sr_sizes);
+cleanup_cds:
+ kfree(scsi_CDs);
+cleanup_devfs:
+ devfs_unregister_blkdev(MAJOR_NR, "sr");
+ sr_registered--;
+ return 1;
}
void sr_finish()
@@ -830,29 +849,26 @@ static void sr_detach(Scsi_Device * SDp)
return;
}
-
-#ifdef MODULE
-
-int init_module(void)
+static int __init init_sr(void)
{
- sr_template.module = &__this_module;
+ sr_template.module = THIS_MODULE;
return scsi_register_module(MODULE_SCSI_DEV, &sr_template);
}
-void cleanup_module(void)
+static void __exit exit_sr(void)
{
scsi_unregister_module(MODULE_SCSI_DEV, &sr_template);
devfs_unregister_blkdev(MAJOR_NR, "sr");
sr_registered--;
if (scsi_CDs != NULL) {
- kfree((char *) scsi_CDs);
+ kfree(scsi_CDs);
- kfree((char *) sr_sizes);
+ kfree(sr_sizes);
sr_sizes = NULL;
- kfree((char *) sr_blocksizes);
+ kfree(sr_blocksizes);
sr_blocksizes = NULL;
- kfree((char *) sr_hardsizes);
+ kfree(sr_hardsizes);
sr_hardsizes = NULL;
}
blksize_size[MAJOR_NR] = NULL;
@@ -863,23 +879,5 @@ void cleanup_module(void)
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:
- */
+module_init(init_sr);
+module_exit(exit_sr);
diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c
index bbb24f4eb..82869fb81 100644
--- a/drivers/scsi/sr_ioctl.c
+++ b/drivers/scsi/sr_ioctl.c
@@ -284,6 +284,8 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg)
int result, target = MINOR(cdi->dev);
unsigned char buffer[32];
+ memset(sr_cmd, 0, sizeof(sr_cmd));
+
switch (cmd) {
case CDROMREADTOCHDR:
{
@@ -292,10 +294,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg)
sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
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;
result = sr_do_ioctl(target, sr_cmd, buffer, 12, 1, SCSI_DATA_READ);
@@ -314,9 +313,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg)
(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;
result = sr_do_ioctl(target, sr_cmd, buffer, 12, 0, SCSI_DATA_READ);
@@ -334,6 +331,20 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg)
break;
}
+ case CDROMPLAYTRKIND: {
+ struct cdrom_ti* ti = (struct cdrom_ti*)arg;
+
+ sr_cmd[0] = GPCMD_PLAYAUDIO_TI;
+ sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+ sr_cmd[4] = ti->cdti_trk0;
+ sr_cmd[5] = ti->cdti_ind0;
+ sr_cmd[7] = ti->cdti_trk1;
+ sr_cmd[8] = ti->cdti_ind1;
+
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0, SCSI_DATA_NONE);
+ break;
+ }
+
default:
return -EINVAL;
}
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index dd5d17866..b0e5026fc 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -155,7 +155,7 @@ static int st_attach(Scsi_Device *);
static int st_detect(Scsi_Device *);
static void st_detach(Scsi_Device *);
-struct Scsi_Device_Template st_template =
+static struct Scsi_Device_Template st_template =
{
name:"tape",
tag:"st",
@@ -173,6 +173,47 @@ static int find_partition(Scsi_Tape *);
static int update_partition(Scsi_Tape *);
static int st_int_ioctl(Scsi_Tape *, unsigned int, unsigned long);
+
+
+/* #include "osst_detect.h" */
+#ifndef SIGS_FROM_OSST
+#define SIGS_FROM_OSST \
+ {"OnStream", "SC-", "", "osst"}, \
+ {"OnStream", "DI-", "", "osst"}, \
+ {"OnStream", "DP-", "", "osst"}, \
+ {"OnStream", "USB", "", "osst"}, \
+ {"OnStream", "FW-", "", "osst"}
+#endif
+
+struct st_reject_data {
+ char *vendor;
+ char *model;
+ char *rev;
+ char *driver_hint; /* Name of the correct driver, NULL if unknown */
+};
+
+static struct st_reject_data reject_list[] = {
+ /* {"XXX", "Yy-", "", NULL}, example */
+ SIGS_FROM_OSST,
+ {NULL, }};
+
+/* If the device signature is on the list of incompatible drives, the
+ function returns a pointer to the name of the correct driver (if known) */
+static char * st_incompatible(Scsi_Device* SDp)
+{
+ struct st_reject_data *rp;
+
+ for (rp=&(reject_list[0]); rp->vendor != NULL; rp++)
+ if (!strncmp(rp->vendor, SDp->vendor, strlen(rp->vendor)) &&
+ !strncmp(rp->model, SDp->model, strlen(rp->model)) &&
+ !strncmp(rp->rev, SDp->rev, strlen(rp->rev))) {
+ if (rp->driver_hint)
+ return rp->driver_hint;
+ else
+ return "unknown";
+ }
+ return NULL;
+}
/* Convert the result to success code */
@@ -3460,9 +3501,17 @@ static int st_attach(Scsi_Device * SDp)
ST_partstat *STps;
int i, mode, target_nbr;
unsigned long flags = 0;
+ char *stp;
if (SDp->type != TYPE_TAPE)
return 1;
+ if ((stp = st_incompatible(SDp))) {
+ printk(KERN_INFO
+ "st: Found incompatible tape at scsi%d, channel %d, id %d, lun %d\n",
+ SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+ printk(KERN_INFO "st: The suggested driver is %s.\n", stp);
+ return 1;
+ }
write_lock_irqsave(&st_dev_arr_lock, flags);
if (st_template.nr_dev >= st_template.dev_max) {
@@ -3486,6 +3535,8 @@ static int st_attach(Scsi_Device * SDp)
if (tmp_da == NULL || tmp_ba == NULL) {
if (tmp_da != NULL)
kfree(tmp_da);
+ if (tmp_ba != NULL)
+ kfree(tmp_ba);
SDp->attached--;
write_unlock_irqrestore(&st_dev_arr_lock, flags);
printk(KERN_ERR "st: Can't extend device array.\n");
@@ -3624,7 +3675,7 @@ static int st_attach(Scsi_Device * SDp)
static int st_detect(Scsi_Device * SDp)
{
- if (SDp->type != TYPE_TAPE)
+ if (SDp->type != TYPE_TAPE || st_incompatible(SDp))
return 0;
printk(KERN_WARNING
@@ -3698,17 +3749,15 @@ static void st_detach(Scsi_Device * SDp)
}
-#ifdef MODULE
-
-int __init init_module(void)
+static int __init init_st(void)
{
validate_options();
- st_template.module = &__this_module;
+ st_template.module = THIS_MODULE;
return scsi_register_module(MODULE_SCSI_DEV, &st_template);
}
-void cleanup_module(void)
+static void __exit exit_st(void)
{
int i;
@@ -3734,4 +3783,6 @@ void cleanup_module(void)
st_template.dev_max = 0;
printk(KERN_INFO "st: Unloaded.\n");
}
-#endif /* MODULE */
+
+module_init(init_st);
+module_exit(exit_st);
diff --git a/drivers/scsi/sun3_scsi.c b/drivers/scsi/sun3_scsi.c
index 741c86d52..29084c39d 100644
--- a/drivers/scsi/sun3_scsi.c
+++ b/drivers/scsi/sun3_scsi.c
@@ -529,10 +529,7 @@ static int sun3scsi_dma_finish(void)
#include "sun3_NCR5380.c"
-#ifdef MODULE
-
-Scsi_Host_Template driver_template = SUN3_NCR5380;
+static Scsi_Host_Template driver_template = SUN3_NCR5380;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/sym53c416.c b/drivers/scsi/sym53c416.c
index e79f3381c..3ea1adac1 100644
--- a/drivers/scsi/sym53c416.c
+++ b/drivers/scsi/sym53c416.c
@@ -813,7 +813,8 @@ MODULE_PARM(sym53c416_1, "1-2i");
MODULE_PARM(sym53c416_2, "1-2i");
MODULE_PARM(sym53c416_3, "1-2i");
-Scsi_Host_Template driver_template = SYM53C416;
+#endif
+
+static Scsi_Host_Template driver_template = SYM53C416;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/sym53c8xx.c b/drivers/scsi/sym53c8xx.c
index bec37e4a7..62af8f2f1 100644
--- a/drivers/scsi/sym53c8xx.c
+++ b/drivers/scsi/sym53c8xx.c
@@ -107,6 +107,7 @@
#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#include <linux/config.h>
#ifdef MODULE
#include <linux/module.h>
#endif
@@ -698,6 +699,9 @@ spinlock_t sym53c8xx_lock = SPIN_LOCK_UNLOCKED;
#elif defined(__alpha__)
# define pcivtobus(p) ((p) & 0xfffffffful)
# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))
+#elif defined(CONFIG_PPC)
+# define pcivtobus(p) phys_to_bus(p)
+# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))
#else /* others */
# define pcivtobus(p) (p)
# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))
@@ -15059,7 +15063,5 @@ sym_read_Tekram_nvram (ncr_slot *np, u_short device_id, Tekram_nvram *nvram)
** Module stuff
*/
-#ifdef MODULE
-Scsi_Host_Template driver_template = SYM53C8XX;
+static Scsi_Host_Template driver_template = SYM53C8XX;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/sym53c8xx.h b/drivers/scsi/sym53c8xx.h
index 128fe165d..d78453e1b 100644
--- a/drivers/scsi/sym53c8xx.h
+++ b/drivers/scsi/sym53c8xx.h
@@ -65,8 +65,6 @@
** Used by hosts.c and sym53c8xx.c with module configuration.
*/
-#if defined(HOSTS_C) || defined(MODULE)
-
#include <scsi/scsicam.h>
int sym53c8xx_abort(Scsi_Cmnd *);
@@ -111,6 +109,4 @@ int sym53c8xx_release(struct Scsi_Host *);
#endif /* LINUX_VERSION_CODE */
-#endif /* defined(HOSTS_C) || defined(MODULE) */
-
#endif /* SYM53C8XX_H */
diff --git a/drivers/scsi/sym53c8xx_comm.h b/drivers/scsi/sym53c8xx_comm.h
index 703b36c31..78f23f6d3 100644
--- a/drivers/scsi/sym53c8xx_comm.h
+++ b/drivers/scsi/sym53c8xx_comm.h
@@ -498,7 +498,8 @@ spinlock_t DRIVER_SMP_LOCK = SPIN_LOCK_UNLOCKED;
# define memcpy_to_pci(a, b, c) memcpy_toio((a), (b), (c))
#endif
-#ifndef SCSI_NCR_PCI_MEM_NOT_SUPPORTED
+#if (defined(SCSI_NCR_NVRAM_SUPPORT) && !defined(NCR_IOMAPPED)) || \
+ (defined(__i386__) && !defined(SCSI_NCR_PCI_MEM_NOT_SUPPORTED))
static u_long __init remap_pci_mem(u_long base, u_long size)
{
u_long page_base = ((u_long) base) & PAGE_MASK;
diff --git a/drivers/scsi/t128.c b/drivers/scsi/t128.c
index 0420c4dc1..f152035b9 100644
--- a/drivers/scsi/t128.c
+++ b/drivers/scsi/t128.c
@@ -393,9 +393,7 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src
#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;
+static Scsi_Host_Template driver_template = TRANTOR_T128;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/t128.h b/drivers/scsi/t128.h
index 9e4e98d04..f31516077 100644
--- a/drivers/scsi/t128.h
+++ b/drivers/scsi/t128.h
@@ -117,8 +117,6 @@ int t128_proc_info (char *buffer, char **start, off_t offset,
* macros when this is being used solely for the host stub.
*/
-#if defined(HOSTS_C) || defined(MODULE)
-
#define TRANTOR_T128 { \
name: "Trantor T128/T128F/T228", \
detect: t128_detect, \
@@ -132,8 +130,6 @@ int t128_proc_info (char *buffer, char **start, off_t offset,
cmd_per_lun: CMD_PER_LUN, \
use_clustering: DISABLE_CLUSTERING}
-#endif
-
#ifndef HOSTS_C
#define NCR5380_implementation_fields \
diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c
index 47d1d0c2d..56a2cacfc 100644
--- a/drivers/scsi/tmscsim.c
+++ b/drivers/scsi/tmscsim.c
@@ -2588,6 +2588,7 @@ int DC390_release(struct Scsi_Host *host)
return( 1 );
}
-Scsi_Host_Template driver_template = DC390_T;
+#endif
+
+static Scsi_Host_Template driver_template = DC390_T;
#include "scsi_module.c"
-#endif /* def MODULE */
diff --git a/drivers/scsi/tmscsim.h b/drivers/scsi/tmscsim.h
index 1b6e2ff08..c824bf0d0 100644
--- a/drivers/scsi/tmscsim.h
+++ b/drivers/scsi/tmscsim.h
@@ -442,7 +442,6 @@ typedef struct _SCSIInqData { /* INQUIRY */
/* see include/scsi/scsi.h for the rest */
#define TYPE_PRINTER 0x02 /* Printer device */
-#define TYPE_COMM 0x09 /* Communications device */
/*
** Inquiry flag definitions (Inq data byte 7)
diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c
index aa6706e8c..c99853935 100644
--- a/drivers/scsi/u14-34f.c
+++ b/drivers/scsi/u14-34f.c
@@ -1964,12 +1964,11 @@ int u14_34f_release(struct Scsi_Host *shpnt) {
return FALSE;
}
-#if defined(MODULE)
-Scsi_Host_Template driver_template = ULTRASTOR_14_34F;
+static Scsi_Host_Template driver_template = ULTRASTOR_14_34F;
#include "scsi_module.c"
-#else
+#ifndef MODULE
#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
void u14_34f_setup(char *str, int *ints) {
diff --git a/drivers/scsi/ultrastor.c b/drivers/scsi/ultrastor.c
index 620b24748..377cdf139 100644
--- a/drivers/scsi/ultrastor.c
+++ b/drivers/scsi/ultrastor.c
@@ -1161,9 +1161,7 @@ static void do_ultrastor_interrupt(int irq, void *dev_id, struct pt_regs *regs)
spin_unlock_irqrestore(&io_request_lock, flags);
}
-#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = ULTRASTOR_14F;
+static Scsi_Host_Template driver_template = ULTRASTOR_14F;
#include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c
index 6f2681117..b1399d36d 100644
--- a/drivers/scsi/wd7000.c
+++ b/drivers/scsi/wd7000.c
@@ -1778,9 +1778,7 @@ int wd7000_biosparam (Disk *disk, kdev_t dev, int *ip)
return (0);
}
-#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
-Scsi_Host_Template driver_template = WD7000;
+static Scsi_Host_Template driver_template = WD7000;
#include "scsi_module.c"
-#endif