diff options
Diffstat (limited to 'drivers/scsi')
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(¤t->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(¤t->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 |