## Automatically generated incremental diff ## From: linux-2.0.37-pre8 ## To: linux-2.0.37-pre9 ## Robot: $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $ diff -urN linux-2.0.37-pre8/arch/alpha/Makefile linux-2.0.37-pre9/arch/alpha/Makefile --- linux-2.0.37-pre8/arch/alpha/Makefile 1998-06-03 15:17:46.000000000 -0700 +++ linux-2.0.37-pre9/arch/alpha/Makefile 2003-08-15 15:04:42.000000000 -0700 @@ -27,9 +27,9 @@ CFLAGS := $(CFLAGS) -mno-fp-regs # determine if we can use the BWX instructions with GAS -$(shell rm -f /tmp/GAS_VER) +dummy=$(shell rm -f /tmp/GAS_VER) #$(shell $(AS) --version >& /tmp/GAS_VER) -$(shell $(AS) --version > /tmp/GAS_VER 2>&1) +dummy=$(shell $(AS) --version > /tmp/GAS_VER 2>&1) OLD_GAS := $(shell if cat /tmp/GAS_VER | grep 'version 2.7' > /dev/null; then echo yes; else echo no; fi) ifneq ($(OLD_GAS),yes) diff -urN linux-2.0.37-pre8/arch/i386/kernel/bios32.c linux-2.0.37-pre9/arch/i386/kernel/bios32.c --- linux-2.0.37-pre8/arch/i386/kernel/bios32.c 1997-08-04 08:38:56.000000000 -0700 +++ linux-2.0.37-pre9/arch/i386/kernel/bios32.c 2003-08-15 15:04:42.000000000 -0700 @@ -730,7 +730,7 @@ outb (0x00, 0xCFB); outb (0x00, 0xCF8); outb (0x00, 0xCFA); - if (inb (0xCF8) == 0x00 && inb (0xCFB) == 0x00) { + if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) { restore_flags(flags); printk("pcibios_init: Using configuration type 2\n"); return &pci_direct_conf2; diff -urN linux-2.0.37-pre8/arch/i386/mm/init.c linux-2.0.37-pre9/arch/i386/mm/init.c --- linux-2.0.37-pre8/arch/i386/mm/init.c 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/arch/i386/mm/init.c 2003-08-15 15:04:42.000000000 -0700 @@ -140,7 +140,7 @@ */ if (!smp_scan_config(639*0x400,0x400)) /* Scan the top 1K of base RAM */ { - if(!(smp_scan_config(0xF0000,0x10000)) /* Scan the 64K of bios */ + if(!smp_scan_config(0xF0000,0x10000)) /* Scan the 64K of bios */ { /* * If it is an SMP machine we should know now, unless the diff -urN linux-2.0.37-pre8/CREDITS linux-2.0.37-pre9/CREDITS --- linux-2.0.37-pre8/CREDITS 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/CREDITS 2003-08-15 15:04:42.000000000 -0700 @@ -1646,6 +1646,7 @@ E: lnz@dandelion.com W: http://www.dandelion.com/Linux/ D: BusLogic SCSI driver +D: Mylex DAC960 PCI RAID driver D: Miscellaneous kernel fixes S: 3078 Sulphur Spring Court S: San Jose, California 95148 diff -urN linux-2.0.37-pre8/Documentation/Configure.help linux-2.0.37-pre9/Documentation/Configure.help --- linux-2.0.37-pre8/Documentation/Configure.help 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/Documentation/Configure.help 2003-08-15 15:04:42.000000000 -0700 @@ -337,6 +337,12 @@ Documentation/modules.txt. It's pretty unlikely that you have one of these: say N. +Mylex DAC960/DAC1100 PCI RAID Controller support +CONFIG_BLK_DEV_DAC960 + This driver adds support for the Mylex DAC960, AcceleRAID, and + eXtremeRAID PCI RAID controllers. See README.DAC960 for further + information about this driver. + Parallel port IDE device support CONFIG_PARIDE There are many external CD-ROM and disk devices that connect through @@ -1726,66 +1732,102 @@ Adaptec AIC7xxx chipset SCSI controller support CONFIG_SCSI_AIC7XXX This is support for the various aic7xxx based Adaptec SCSI - controllers. These include the 274x EISA cards, 284x VLB cards, - 294x PCI cards, 394x PCI cards, 3985 PCI card, and several versions - of the Adaptec built-in SCSI controllers on various PC motherboards. + controllers. These include the 274x EISA cards; 284x VLB cards; 2902, + 2910, 293x, 294x, 394x, 3985 and several other PCI and motherboard based + SCSI controllers from Adaptec. It does not support the AAA-13x RAID + controllers from Adaptec, nor will it likely ever support them. It + does not support the 2920 cards from Adaptec that use the Future Domain + SCSI controller chip. For those cards, you need the "Future Domain + 16xx SCSI support" driver. + + In general, if the controller is based on an Adaptec SCSI controller + chip from the aic777x series or the aic78xx series, it should work. The + only exception is the 7810 which is specifically not supported (that's the + RAID controller chip on the AAA-13x cards). + Information on the configuration options for this controller can be - found by checking the README.aic7xxx file, usually in - /usr/src/linux/drivers/scsi. + found by checking the help file for each of the available + configuration options. You should read drivers/scsi/README.aic7xxx + at a minimum before contacting the maintainer with any questions. + The SCSI-HOWTO, available via FTP (user: anonymous) at + ftp://metalab.unc.edu/pub/Linux/docs/HOWTO can also be of great help. + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. The module will be + called aic7xxx.o. -Override driver defaults for commands per LUN -CONFIG_OVERRIDE_CMDS - Use this option to allow you to override the default maximum number - of commands that a single device on the aic7xxx controller is - allowed to have active at one time. This option only effects tagged - queueing capable devices. The driver uses a "failsafe" value of 8 - by default. This is much lower than many devices can handle, but - left in place for safety sake. - NOTE: This does not actually enabled tagged queueing on any - particular device. The driver has changed in this respect. Please - see the file README.aic7xxx in /usr/src/linux/drivers/scsi for more - information on how to get particular devices to use tagged command - queueing. - Default: N - -Maximum number of commands per LUN -CONFIG_AIC7XXX_CMDS_PER_LUN - Specify the maximum number of commands per lun you would like to - allocate per device. Reasonable figures are in the range of 14 to - 32 commands per device, but depending on hardware could be increased - or decreased from that figure. If the number is too high for any - particular device, the driver will automatically compensate usually - after only 10 minutes of uptime and will issue a message to alert - you to the fact that the number of commands for that device has been - reduced. It will not hinder performance if a portion of your - devices eventually have their commands per lun reduced, but is a - waste of memory if all of your devices end up reducing this number - down to a more reasonable figure. Default: 24 +Enable or Disable Tagged Command Queueing by default +CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT + This option causes the aic7xxx driver to attempt to use tagged command + queueing on any devices that claim to support it. If this is set to yes, + you can still turn off TCQ on troublesome devices with the use of the + tag_info boot parameter. See /usr/src/linux/drivers/scsi/README.aic7xxx + for more information on that and other aic7xxx setup commands. If this + option is turned off, you may still enable TCQ on known good devices by + use of the tag_info boot parameter. + + If you are unsure about your devices then it is safest to say N here. + + However, TCQ can increase performance on some hard drives by as much + as 50% or more, so I would recommend that if you say N here, that you + at least read the README.aic7xxx file so you will know how to enable + this option manually should your drives prove to be safe in regards + to TCQ. + + Conversely, certain drives are known to lock up or cause bus resets when + TCQ is enabled on them. If you have a Western Digital Enterprise SCSI + drive for instance, then don't even bother to enable TCQ on it as the + drive will become unreliable, and it will actually reduce performance. + +Default number of TCQ commands per device +CONFIG_AIC7XXX_CMDS_PER_DEVICE + Specify the number of commands you would like to allocate per SCSI + device when Tagged Command Queueing (TCQ) is enabled on that device. + + Reasonable figures are in the range of 8 to 24 commands per device, + but depending on hardware could be increased or decreased from that + figure. If the number is too high for any particular device, the + driver will automatically compensate usually after only 10 minutes + of uptime. It will not hinder performance if some of your devices + eventually have their command depth reduced, but is a waste of memory + if all of your devices end up reducing this number down to a more + reasonable figure. + + NOTE: Certain very broken drives are known to lock up when given more + commands than they like to deal with. Quantum Fireball drives are the + most common in this category. For the Quantum Fireball drives I would + suggest no more than 8 commands per device. + + Default: 8 Collect statistics to report in /proc CONFIG_AIC7XXX_PROC_STATS This option tells the driver to keep track of how many commands have been sent to each particular device and report that information to - the user via the /proc/scsi/aic7xxx/x file, where x is the number - of the aic7xxx controller you want the information on. This adds - a small amount of overhead to each and every SCSI command the - aic7xxx driver handles, so if you aren't really interested in this - information, it is best to leave it disabled. Default: N + the user via the /proc/scsi/aic7xxx/n file, where n is the number of + the aic7xxx controller you want the information on. This adds a + small amount of overhead to each and every SCSI command the aic7xxx + driver handles, so if you aren't really interested in this + information, it is best to leave it disabled. This will only work if + you also say Y to "/proc filesystem support", below. + + If unsure, say N. Delay in seconds after SCSI bus reset CONFIG_AIC7XXX_RESET_DELAY This sets how long the driver will wait after resetting the SCSI bus before attempting to communicate with the devices on the SCSI bus - again. This delay will be used during the reset phase at bootup - time as well as after any reset that might occur during normal - operation. Reasonable numbers range anywhere from 5 to 15 seconds - depending on your devices. DAT tape drives are notorious for needing - more time after a bus reset to be ready for the next command, but - most hard drives and CD-ROM devices are ready in only a few seconds. - This option has a maximum upper limit of 20 seconds to avoid bad - interactions between the aic7xxx driver and the rest of the Linux - kernel. The default value has been reduced. If this doesn't work - with your hardware, try increasing this value. Default: 5 + again. This delay will be used during the reset phase at bootup time + as well as after any reset that might occur during normal operation. + Reasonable numbers range anywhere from 5 to 15 seconds depending on + your devices. DAT tape drives are notorious for needing more time + after a bus reset to be ready for the next command, but most hard + drives and CD-ROM devices are ready in only a few seconds. This + option has a maximum upper limit of 20 seconds to avoid bad + interactions between the aic7xxx driver and the rest of the linux + kernel. The default value has been reduced to 5 seconds. If this + doesn't work with your hardware, try increasing this value. BusLogic SCSI support CONFIG_SCSI_BUSLOGIC diff -urN linux-2.0.37-pre8/drivers/block/genhd.c linux-2.0.37-pre9/drivers/block/genhd.c --- linux-2.0.37-pre8/drivers/block/genhd.c 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/drivers/block/genhd.c 2003-08-15 15:04:42.000000000 -0700 @@ -86,7 +86,6 @@ maj = "hd"; } #endif -#ifdef CONFIG_BLK_DEV_DAC960 if (hd->major >= DAC960_MAJOR+0 && hd->major <= DAC960_MAJOR+7) { int controller = hd->major - DAC960_MAJOR; @@ -98,7 +97,6 @@ maj, controller, minor >> hd->minor_shift, partition); return buf; } -#endif #if defined(CONFIG_BLK_CPQ_DA) || defined(CONFIG_BLK_CPQ_DA_MODULE) if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) { int ctlr = hd->major - COMPAQ_SMART2_MAJOR; @@ -124,11 +122,9 @@ char buf[40]; hd->part[minor].start_sect = start; hd->part[minor].nr_sects = size; -#ifdef CONFIG_BLK_DEV_DAC960 if (hd->major >= DAC960_MAJOR+0 && hd->major <= DAC960_MAJOR+7) printk(" p%d", (minor & ((1 << hd->minor_shift) - 1))); else -#endif #if defined(CONFIG_BLK_CPQ_DA) || defined(CONFIG_BLK_CPQ_DA_MODULE) if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) printk(" p%d", (minor & ((1 << hd->minor_shift) - 1))); diff -urN linux-2.0.37-pre8/drivers/block/ll_rw_blk.c linux-2.0.37-pre9/drivers/block/ll_rw_blk.c --- linux-2.0.37-pre8/drivers/block/ll_rw_blk.c 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/drivers/block/ll_rw_blk.c 2003-08-15 15:04:42.000000000 -0700 @@ -263,6 +263,11 @@ short disk_index; switch (major) { + case DAC960_MAJOR+0: + disk_index = (minor & 0x00f8) >> 3; + if (disk_index < 4) + drive_stat_acct(req->cmd, req->nr_sectors, disk_index); + break; case SCSI_DISK_MAJOR: disk_index = (minor & 0x0070) >> 4; if (disk_index < 4) @@ -359,7 +364,7 @@ lock_buffer(bh); if (blk_size[major]) - if (((blk_size[major][MINOR(bh->b_rdev)]<<1)+count) < sector) { + if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) { bh->b_state &= (1 << BH_Lock) | (1 << BH_FreeOnIO); /* This may well happen - the kernel calls bread() without checking the size of the device, e.g., diff -urN linux-2.0.37-pre8/drivers/pci/pci.c linux-2.0.37-pre9/drivers/pci/pci.c --- linux-2.0.37-pre8/drivers/pci/pci.c 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/drivers/pci/pci.c 2003-08-15 15:04:42.000000000 -0700 @@ -527,8 +527,10 @@ DEVICE( INTEL, INTEL_82450GX, "82450GX Orion P6"), DEVICE( KTI, KTI_ET32P2, "ET32P2"), DEVICE( ADAPTEC, ADAPTEC_7810, "AIC-7810 RAID"), + DEVICE( ADAPTEC, ADAPTEC_7821, "AIC-7860"), DEVICE( ADAPTEC, ADAPTEC_7850, "AIC-7850"), DEVICE( ADAPTEC, ADAPTEC_7855, "AIC-7855"), + DEVICE( ADAPTEC, ADAPTEC_3860, "AIC-7860"), DEVICE( ADAPTEC, ADAPTEC_5800, "AIC-5800"), DEVICE( ADAPTEC, ADAPTEC_1480A, "AIC-1480A"), DEVICE( ADAPTEC, ADAPTEC_7860, "AIC-7860"), @@ -544,12 +546,26 @@ DEVICE( ADAPTEC, ADAPTEC_7882, "AIC-7882U"), DEVICE( ADAPTEC, ADAPTEC_7883, "AIC-7883U"), DEVICE( ADAPTEC, ADAPTEC_7884, "AIC-7884U"), + DEVICE( ADAPTEC, ADAPTEC_7885, "AIC-7885U"), + DEVICE( ADAPTEC, ADAPTEC_7886, "AIC-7886U"), + DEVICE( ADAPTEC, ADAPTEC_7887, "AIC-7887U"), + DEVICE( ADAPTEC, ADAPTEC_7888, "AIC-7888U"), DEVICE( ADAPTEC, ADAPTEC_1030, "ABA-1030 DVB receiver"), - DEVICE( ADAPTEC2, ADAPTEC2_2940U2, "AHA-2940U2"), - DEVICE( ADAPTEC2, ADAPTEC2_7890, "AIC-7890/1"), - DEVICE( ADAPTEC2, ADAPTEC2_3940U2, "AHA-3940U2"), - DEVICE( ADAPTEC2, ADAPTEC2_3950U2D, "AHA-3950U2D"), - DEVICE( ADAPTEC2, ADAPTEC2_7896, "AIC-7896/7"), + DEVICE( ADAPTEC2, ADAPTEC2_2940U2,"AHA-2940U2"), + DEVICE( ADAPTEC2, ADAPTEC2_2930U2,"AHA-2930U2"), + DEVICE( ADAPTEC2, ADAPTEC2_7890B, "AIC-7890/1"), + DEVICE( ADAPTEC2, ADAPTEC2_7890, "AIC-7890/1"), + DEVICE( ADAPTEC2, ADAPTEC2_3940U2,"AHA-3940U2"), + DEVICE( ADAPTEC2, ADAPTEC2_3950U2D, "AHA-3950U2D"), + DEVICE( ADAPTEC2, ADAPTEC2_7896, "AIC-7896/7"), + DEVICE( ADAPTEC2, ADAPTEC2_7892A, "AIC-7892"), + DEVICE( ADAPTEC2, ADAPTEC2_7892B, "AIC-7892"), + DEVICE( ADAPTEC2, ADAPTEC2_7892D, "AIC-7892"), + DEVICE( ADAPTEC2, ADAPTEC2_7892P, "AIC-7892"), + DEVICE( ADAPTEC2, ADAPTEC2_7899A, "AIC-7899"), + DEVICE( ADAPTEC2, ADAPTEC2_7899B, "AIC-7899"), + DEVICE( ADAPTEC2, ADAPTEC2_7899D, "AIC-7899"), + DEVICE( ADAPTEC2, ADAPTEC2_7899P, "AIC-7899"), DEVICE( ATRONICS, ATRONICS_2015, "IDE-2015PL"), DEVICE( TIGERJET, TIGERJET_300, "Tiger300 ISDN"), DEVICE( ARK, ARK_STING, "Stingray"), diff -urN linux-2.0.37-pre8/drivers/scsi/aic7xxx.c linux-2.0.37-pre9/drivers/scsi/aic7xxx.c --- linux-2.0.37-pre8/drivers/scsi/aic7xxx.c 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/drivers/scsi/aic7xxx.c 2003-08-15 15:04:42.000000000 -0700 @@ -100,7 +100,7 @@ * * Further driver modifications made by Doug Ledford * - * Copyright (c) 1997-1998 Doug Ledford + * Copyright (c) 1997-1999 Doug Ledford * * These changes are released under the same licensing terms as the FreeBSD * driver written by Justin Gibbs. Please see his Copyright notice above @@ -336,6 +336,7 @@ #include "aic7xxx/sequencer.h" #include "aic7xxx/scsi_message.h" #include "aic7xxx_reg.h" +#include #include #include /* for kmalloc() */ @@ -354,7 +355,7 @@ 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -#define AIC7XXX_C_VERSION "5.1.7" +#define AIC7XXX_C_VERSION "5.1.13" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -447,8 +448,10 @@ * You can try raising me if tagged queueing is enabled, or lowering * me if you only have 4 SCBs. */ -#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN -#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN +#ifdef CONFIG_AIC7XXX_CMDS_PER_DEVICE +#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_CMDS_PER_DEVICE +#else +#define AIC7XXX_CMDS_PER_DEVICE 8 #endif /* Set this to the delay in seconds after SCSI bus reset. */ @@ -493,7 +496,7 @@ * * *** Determining commands per LUN *** * - * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its + * When AIC7XXX_CMDS_PER_DEVICE is not defined, the driver will use its * own algorithm to determine the commands/LUN. If SCB paging is * enabled, which is always now, the default is 8 commands per lun * that indicates it supports tagged queueing. All non-tagged devices @@ -511,8 +514,13 @@ * Make a define that will tell the driver not to use tagged queueing * by default. */ +#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT +#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\ + 0, 0, 0, 0, 0, 0, 0, 0} +#else #define DEFAULT_TAG_COMMANDS {255, 255, 255, 255, 255, 255, 255, 255,\ 255, 255, 255, 255, 255, 255, 255, 255} +#endif /* * Modify this as you see fit for your system. By setting tag_commands @@ -551,6 +559,27 @@ }; */ +static adapter_tag_info_t aic7xxx_tag_info[] = +{ + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS}, + {DEFAULT_TAG_COMMANDS} +}; + + /* * Define an array of board names that can be indexed by aha_type. * Don't forget to change this when changing the types! @@ -577,11 +606,14 @@ "Adaptec AHA-2944 Ultra SCSI host adapter", /* AIC_7884 */ "Adaptec AIC-7895 Ultra SCSI host adapter", /* AIC_7895 */ "Adaptec AIC-7890/1 Ultra2 SCSI host adapter", /* AIC_7890 */ + "Adaptec AHA-293X Ultra2 SCSI host adapter", /* AIC_7890 */ "Adaptec AHA-294X Ultra2 SCSI host adapter", /* AIC_7890 */ "Adaptec AIC-7896/7 Ultra2 SCSI host adapter", /* AIC_7896 */ "Adaptec AHA-394X Ultra2 SCSI host adapter", /* AIC_7897 */ "Adaptec AHA-395X Ultra2 SCSI host adapter", /* AIC_7897 */ "Adaptec PCMCIA SCSI controller", /* card bus stuff */ + "Adaptec AIC-7892 Ultra 160/m SCSI host adapter", /* AIC_7892 */ + "Adaptec AIC-7899 Ultra 160/m SCSI host adapter", /* AIC_7899 */ }; /* @@ -884,6 +916,7 @@ * and what flags weren't. This way, I could clean up the flag usage on * a use by use basis. Doug Ledford */ + AHC_RESET_DELAY = 0x00080000, AHC_A_SCANNED = 0x00100000, AHC_B_SCANNED = 0x00200000, AHC_MULTI_CHANNEL = 0x00400000, @@ -910,6 +943,8 @@ AHC_AIC7890 = 0x0006, AHC_AIC7895 = 0x0007, AHC_AIC7896 = 0x0008, + AHC_AIC7892 = 0x0009, + AHC_AIC7899 = 0x000a, AHC_VL = 0x0100, AHC_EISA = 0x0200, AHC_PCI = 0x0400, @@ -926,6 +961,7 @@ AHC_QUEUE_REGS = 0x0040, AHC_SG_PRELOAD = 0x0080, AHC_SPIOCAP = 0x0100, + AHC_ULTRA160 = 0x0200, AHC_AIC7770_FE = AHC_FENONE, AHC_AIC7850_FE = AHC_SPIOCAP, AHC_AIC7860_FE = AHC_ULTRA|AHC_SPIOCAP, @@ -935,6 +971,8 @@ AHC_QUEUE_REGS|AHC_SG_PRELOAD, AHC_AIC7895_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA, AHC_AIC7896_FE = AHC_AIC7890_FE, + AHC_AIC7892_FE = AHC_AIC7890_FE|AHC_ULTRA160, + AHC_AIC7899_FE = AHC_AIC7890_FE|AHC_ULTRA160, } ahc_feature; struct aic7xxx_scb { @@ -1042,6 +1080,10 @@ unsigned long isr_count; /* Interrupt count */ unsigned long spurious_int; scb_data_type *scb_data; + volatile unsigned short needsdtr; + volatile unsigned short sdtr_pending; + volatile unsigned short needwdtr; + volatile unsigned short wdtr_pending; struct aic7xxx_cmd_queue { Scsi_Cmnd *head; Scsi_Cmnd *tail; @@ -1073,12 +1115,15 @@ volatile unsigned char dev_temp_queue_depth[MAX_TARGETS]; unsigned char dev_commands_sent[MAX_TARGETS]; + unsigned int dev_timer_active; /* Which devs have a timer set */ + struct timer_list dev_timer; + unsigned long dev_expires[MAX_TARGETS]; + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) spinlock_t spin_lock; volatile unsigned char cpu_lock_count[NR_CPUS]; #endif - unsigned short dev_timer_active; /* Which devs have a timer set */ #ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS Scsi_Cmnd *dev_wdtr_cmnd[MAX_TARGETS]; @@ -1091,12 +1136,6 @@ volatile scb_queue_type delayed_scbs[MAX_TARGETS]; - unsigned long dev_expires[MAX_TARGETS]; - struct timer_list dev_timer; - - /* - * The next 64.... - */ unsigned char msg_buf[9]; /* The message for the target */ unsigned char msg_type; @@ -1123,16 +1162,11 @@ volatile unsigned char qoutfifo[256]; volatile unsigned char qinfifo[256]; unsigned int irq; /* IRQ for this adapter */ - volatile unsigned short needsdtr; - volatile unsigned short sdtr_pending; - volatile unsigned short needwdtr; - volatile unsigned short wdtr_pending; int instance; /* aic7xxx instance number */ int scsi_id; /* host adapter SCSI ID */ int scsi_id_b; /* channel B for twin adapters */ unsigned int bios_address; int board_name_index; - unsigned long reset_start; unsigned short needsdtr_copy; /* default config */ unsigned short needwdtr_copy; /* default config */ unsigned short ultraenb; /* Ultra mode target list */ @@ -1150,7 +1184,6 @@ struct Scsi_Host *host; /* pointer to scsi host */ int host_no; /* SCSI host number */ unsigned long mbase; /* I/O memory address */ - unsigned long last_reset; ahc_chip chip; /* chip type */ /* @@ -1166,21 +1199,21 @@ * * NOTE: Enabling this feature is likely to cause a noticeable performance * decrease as the accesses into the stats structures blows apart multiple - * cache lines and is CPU time consuming. We keep the xfer count always - * for use by the aic7xxx_proc.c code, but only do the bins if the - * proc stats code is enabled. + * cache lines and is CPU time consuming. + * + * NOTE: Since it doesn't really buy us much, but consumes *tons* of RAM + * and blows apart all sorts of cache lines, I modified this so that we + * no longer look at the LUN. All LUNs now go into the same bin on each + * device for stats purposes. */ struct aic7xxx_xferstats { - long w_total; /* total writes */ - long r_total; /* total reads */ + long w_total; /* total writes */ + long r_total; /* total reads */ #ifdef AIC7XXX_PROC_STATS - long xfers; /* total xfer count */ - long w_total512; /* 512 byte blocks written */ - long r_total512; /* 512 byte blocks read */ - long w_bins[10]; /* binned write */ - long r_bins[10]; /* binned reads */ + long w_bins[8]; /* binned write */ + long r_bins[8]; /* binned reads */ #endif /* AIC7XXX_PROC_STATS */ - } stats[MAX_TARGETS][MAX_LUNS]; /* [(channel << 3)|target][lun] */ + } stats[MAX_TARGETS]; /* [(channel << 3)|target] */ #if 0 struct target_cmd *targetcmds; @@ -1412,35 +1445,6 @@ #endif -/* - * See the comments earlier in the file for what this item is all about - * If you have more than 4 controllers, you will need to increase the - * the number of items in the array below. Additionally, if you don't - * want to have lilo pass a humongous config line to the aic7xxx driver, - * then you can get in and manually adjust these instead of leaving them - * at the default. Pay attention to the comments earlier in this file - * concerning this array if you are going to hand modify these values. - */ -static adapter_tag_info_t aic7xxx_tag_info[] = -{ - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS}, - {DEFAULT_TAG_COMMANDS} -}; - #define VERBOSE_NORMAL 0x0000 #define VERBOSE_NEGOTIATION 0x0001 #define VERBOSE_SEQINT 0x0002 @@ -1510,6 +1514,8 @@ return( timer->prev != NULL ); } +#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075 + #endif static inline unsigned char @@ -2796,6 +2802,7 @@ Scsi_Cmnd *cmd; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) unsigned int cpu_flags = 0; +#endif DRIVER_LOCK while (p->completeq.head != NULL) @@ -2803,20 +2810,9 @@ cmd = p->completeq.head; p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble; cmd->host_scribble = NULL; - sti(); cmd->scsi_done(cmd); - cli(); } DRIVER_UNLOCK -#else - while (p->completeq.head != NULL) - { - cmd = p->completeq.head; - p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble; - cmd->host_scribble = NULL; - cmd->scsi_done(cmd); - } -#endif } /*+F************************************************************************* @@ -3090,7 +3086,7 @@ int x; #endif /* AIC7XXX_PROC_STATS */ - sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7]; + sp = &p->stats[TARGET_INDEX(cmd)]; /* * For block devices, cmd->request.cmd is always == either READ or @@ -3107,8 +3103,6 @@ aic7xxx_verbose &= 0xffff; #endif #ifdef AIC7XXX_PROC_STATS - sp->xfers++; - sp->w_total512 += (actual >> 9); ptr = sp->w_bins; #endif /* AIC7XXX_PROC_STATS */ } @@ -3120,23 +3114,27 @@ aic7xxx_verbose &= 0xffff; #endif #ifdef AIC7XXX_PROC_STATS - sp->xfers++; - sp->r_total512 += (actual >> 9); ptr = sp->r_bins; #endif /* AIC7XXX_PROC_STATS */ } #ifdef AIC7XXX_PROC_STATS - for (x = 9; x <= 17; x++) + x = -10; + while(actual) { - if (actual < (1 << x)) - { - ptr[x - 9]++; - break; - } + actual >>= 1; + x++; + } + if (x < 0) + { + ptr[0]++; } - if (x > 17) + else if (x > 7) { - ptr[x - 9]++; + ptr[7]++; + } + else + { + ptr[x]++; } #endif /* AIC7XXX_PROC_STATS */ } @@ -3484,13 +3482,13 @@ "delayed_scbs queue!\n", p->host_no, channel, i, lun); scbq_init(&p->delayed_scbs[i]); } - if ( !(p->dev_timer_active & (0x01 << p->scsi_id)) || + if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) || time_after_eq(p->dev_timer.expires, p->dev_expires[i]) ) { del_timer(&p->dev_timer); p->dev_timer.expires = p->dev_expires[i]; add_timer(&p->dev_timer); - p->dev_timer_active |= (0x01 << p->scsi_id); + p->dev_timer_active |= (0x01 << MAX_TARGETS); } } } @@ -3623,7 +3621,7 @@ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS)) printk(INFO_LEAD "Cleaning disconnected scbs " "list.\n", p->host_no, channel, target, lun); - if (p->features & AHC_PAGESCBS) + if (p->flags & AHC_PAGESCBS) { unsigned char next, prev, scb_index; @@ -3675,7 +3673,7 @@ * Walk the free list making sure no entries on the free list have * a valid SCB_TAG value or SCB_CONTROL byte. */ - if (p->features & AHC_PAGESCBS) + if (p->flags & AHC_PAGESCBS) { unsigned char next; @@ -3956,12 +3954,6 @@ */ aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL); - /* - * Convince Mid Level SCSI code to leave us be for a little bit... - */ - p->last_reset = jiffies; - p->host->last_reset = (jiffies + (HZ * AIC7XXX_RESET_DELAY)); - if ( !(p->features & AHC_TWIN) ) { restart_sequencer(p); @@ -4007,7 +3999,8 @@ } if ( (p->dev_active_cmds[tindex] >= p->dev_temp_queue_depth[tindex]) || - (p->dev_flags[tindex] & (DEVICE_RESET_DELAY|DEVICE_WAS_BUSY)) ) + (p->dev_flags[tindex] & (DEVICE_RESET_DELAY|DEVICE_WAS_BUSY)) || + (p->flags & AHC_RESET_DELAY) ) { scbq_insert_tail(&p->delayed_scbs[tindex], scb); } @@ -4126,14 +4119,17 @@ #else spin_lock_irqsave(&io_request_lock, cpu_flags); #endif - p->dev_timer_active &= ~(0x01 << p->scsi_id); + p->dev_timer_active &= ~(0x01 << MAX_TARGETS); + if ( (p->dev_timer_active & (0x01 << p->scsi_id)) && + time_after_eq(jiffies, p->dev_expires[p->scsi_id]) ) + { + p->flags &= ~AHC_RESET_DELAY; + p->dev_timer_active &= ~(0x01 << p->scsi_id); + } for(i=0; iscsi_id ) - { - continue; - } - if ( (p->dev_timer_active & (0x01 << i)) && + if ( (i != p->scsi_id) && + (p->dev_timer_active & (0x01 << i)) && time_after_eq(jiffies, p->dev_expires[i]) ) { p->dev_timer_active &= ~(0x01 << i); @@ -4159,7 +4155,7 @@ } else if ( p->dev_timer_active & (0x01 << i) ) { - if ( p->dev_timer_active & (0x01 << p->scsi_id) ) + if ( p->dev_timer_active & (0x01 << MAX_TARGETS) ) { if ( time_after_eq(p->dev_timer.expires, p->dev_expires[i]) ) { @@ -4169,11 +4165,11 @@ else { p->dev_timer.expires = p->dev_expires[i]; - p->dev_timer_active |= (0x01 << p->scsi_id); + p->dev_timer_active |= (0x01 << MAX_TARGETS); } } } - if ( p->dev_timer_active & (0x01 << p->scsi_id) ) + if ( p->dev_timer_active & (0x01 << MAX_TARGETS) ) { add_timer(&p->dev_timer); } @@ -4899,10 +4895,10 @@ { p->dev_expires[tindex] = jiffies + (HZ / 10); } - if ( !(p->dev_timer_active & (0x01 << p->scsi_id)) ) + if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) ) { p->dev_timer.expires = p->dev_expires[tindex]; - p->dev_timer_active |= (0x01 << p->scsi_id); + p->dev_timer_active |= (0x01 << MAX_TARGETS); add_timer(&p->dev_timer); } else if ( time_after_eq(p->dev_timer.expires, @@ -6368,7 +6364,7 @@ * Determines the queue depth for a given device. There are two ways * a queue depth can be obtained for a tagged queueing device. One * way is the default queue depth which is determined by whether - * AIC7XXX_CMDS_PER_LUN is defined. If it is defined, then it is used + * AIC7XXX_CMDS_PER_DEVICE is defined. If it is defined, then it is used * as the default queue depth. Otherwise, we use either 4 or 8 as the * default queue depth (dependent on the number of hardware SCBs). * The other way we determine queue depth is through the use of the @@ -6396,11 +6392,7 @@ { int tag_enabled = TRUE; -#ifdef AIC7XXX_CMDS_PER_LUN - default_depth = AIC7XXX_CMDS_PER_LUN; -#else - default_depth = 8; /* Not many SCBs to work with. */ -#endif + default_depth = AIC7XXX_CMDS_PER_DEVICE; if (!(p->discenable & target_mask)) { @@ -6962,7 +6954,7 @@ } printk("\n"); #endif - if (checksum != scarray[len - 1]) + if ( (checksum != scarray[len - 1]) || (checksum == 0) ) { return (0); } @@ -7446,7 +7438,6 @@ } p->host = host; - p->last_reset = jiffies; p->host_no = host->host_no; host->unique_id = p->instance; p->isr_count = 0; @@ -7532,10 +7523,9 @@ } aic_outb(p, 0, SEQ_FLAGS); - /* - * Detect SCB parameters and initialize the SCB array. - */ detect_maxscb(p); + + printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs); if (aic7xxx_verbose & VERBOSE_PROBE2) { @@ -7846,6 +7836,11 @@ */ aic7xxx_loadseq(p); + /* + * Make sure the AUTOFLUSHDIS bit is *not* set in the SBLKCTL register + */ + aic_outb(p, aic_inb(p, SBLKCTL) & ~AUTOFLUSHDIS, SBLKCTL); + if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 ) { aic_outb(p, ENABLE, BCTL); /* Enable the boards BUS drivers. */ @@ -8132,6 +8127,8 @@ p->flags |= AHC_TERM_ENB_A; if ( (p->features & AHC_TWIN) && (aic_inb(p, SCSICONF + 1) & TERM_ENB) ) p->flags |= AHC_TERM_ENB_B; + aic_outb(p, 0, DISC_DSB); + aic_outb(p, 0, DISC_DSB + 1); break; case (AHC_AIC7770|AHC_VL): @@ -8190,14 +8187,16 @@ { printk("aic7xxx: Using leftover BIOS values.\n"); } - if ( *sxfrctl1 & STPWEN ) + if ( ((p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) && (*sxfrctl1 & STPWEN) ) { p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH; sc->adapter_control &= ~CFAUTOTERM; sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM; } if (aic7xxx_extended) - p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B; + p->flags |= (AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B); + else + p->flags &= ~(AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B); } else { @@ -8285,7 +8284,7 @@ mask = (0x01 << i); if (!have_seeprom) { - if(aic_inb(p, SCSISEQ) != 0) + if (aic_inb(p, SCSISEQ) != 0) { /* * OK...the BIOS set things up and left behind the settings we need. @@ -8345,20 +8344,30 @@ } if (p->flags & AHC_NEWEEPROM_FMT) { - if (sc->device_flags[i] & CFSYNCHISULTRA) + if ( (sc->device_flags[i] & CFNEWULTRAFORMAT) && + !(p->features & AHC_ULTRA2) ) { - p->ultraenb |= mask; - } - else if (sc->device_flags[i] & CFNEWULTRAFORMAT) - { - if ( ((sc->device_flags[i] & (CFSYNCHISULTRA | CFXFER)) == 0x03) && - !(p->features & AHC_ULTRA2) ) + /* + * I know of two different Ultra BIOSes that do this differently. + * One on the Gigabyte 6BXU mb that wants flags[i] & CFXFER to + * be == to 0x03 and SYNCISULTRA to be true to mean 40MByte/s + * while on the IBM Netfinity 5000 they want the same thing + * to be something else, while flags[i] & CFXFER == 0x03 and + * SYNCISULTRA false should be 40MByte/s. So, we set both to + * 40MByte/s and the lower speeds be damned. People will have + * to select around the conversely mapped lower speeds in order + * to select lower speeds on these boards. + */ + if ((sc->device_flags[i] & (CFXFER)) == 0x03) { sc->device_flags[i] &= ~CFXFER; sc->device_flags[i] |= CFSYNCHISULTRA; - p->ultraenb |= mask; } } + if (sc->device_flags[i] & CFSYNCHISULTRA) + { + p->ultraenb |= mask; + } } else if (sc->adapter_control & CFULTRAEN) { @@ -8789,6 +8798,14 @@ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850, AHC_PAGESCBS, AHC_AIC7850_FE, 6, 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7821, AHC_AIC7860, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7860_FE, 7, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_3860, AHC_AIC7860, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7860_FE, 7, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, AHC_AIC7860_FE, 7, @@ -8831,6 +8848,18 @@ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880, AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7885, AHC_AIC7880, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7886, AHC_AIC7880, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, AHC_AIC7895_FE, 19, @@ -8839,33 +8868,70 @@ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, AHC_AIC7890_FE, 20, 32, C46 }, - {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890B, AHC_AIC7890, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7890_FE, 20, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2930U2, AHC_AIC7890, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, AHC_AIC7890_FE, 21, 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7890_FE, 22, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7896_FE, 22, + AHC_AIC7896_FE, 23, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7896_FE, 23, + AHC_AIC7896_FE, 24, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3950U2D, AHC_AIC7896, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7896_FE, 24, + AHC_AIC7896_FE, 25, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7860_FE, 25, + AHC_AIC7860_FE, 26, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892A, AHC_AIC7892, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7892_FE, 27, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892B, AHC_AIC7892, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7892_FE, 27, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892D, AHC_AIC7892, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7892_FE, 27, 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892P, AHC_AIC7892, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7892_FE, 27, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899A, AHC_AIC7899, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7899_FE, 28, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899B, AHC_AIC7899, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7899_FE, 28, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899D, AHC_AIC7899, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7899_FE, 28, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899P, AHC_AIC7899, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7899_FE, 28, + 32, C56_66 }, }; unsigned short command; unsigned int devconfig, i, oldverbose; -#ifdef MMAPIO - unsigned long page_offset, base; -#endif #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) struct pci_dev *pdev = NULL; #else @@ -9016,21 +9082,68 @@ temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK; temp_p->unpause = INTEN; temp_p->pause = temp_p->unpause | PAUSE; + if ( ((temp_p->base == 0) && + (temp_p->mbase == 0)) || + (temp_p->irq == 0) ) + { + printk("aic7xxx: <%s> at PCI %d/%d\n", + board_names[aic_pdevs[i].board_name_index], + PCI_SLOT(temp_p->pci_device_fn), + PCI_FUNC(temp_p->pci_device_fn)); + printk("aic7xxx: Controller disabled by BIOS, ignoring.\n"); + kfree(temp_p); + temp_p = NULL; + continue; + } #ifdef MMAPIO - base = temp_p->mbase & PAGE_MASK; - page_offset = temp_p->mbase - base; + { + unsigned long page_offset, base; + + base = temp_p->mbase & PAGE_MASK; + page_offset = temp_p->mbase - base; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - temp_p->maddr = ioremap_nocache(base, page_offset + 256); + temp_p->maddr = ioremap_nocache(base, page_offset + 256); #else - temp_p->maddr = vremap(base, page_offset + 256); + temp_p->maddr = vremap(base, page_offset + 256); #endif - if(temp_p->maddr) - { - temp_p->maddr += page_offset; + if(temp_p->maddr) + { + temp_p->maddr += page_offset; + /* + * We need to check the I/O with the MMAPed address. Some machines + * simply fail to work with MMAPed I/O and certain controllers. + */ + if(aic_inb(temp_p, HCNTRL) == 0xff) + { + /* + * OK.....we failed our test....go back to programmed I/O + */ + printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d\n", + board_names[aic_pdevs[i].board_name_index], + PCI_SLOT(temp_p->pci_device_fn), + PCI_FUNC(temp_p->pci_device_fn)); + printk(KERN_INFO "aic7xxx: MMAPed I/O failed, reverting to " + "Programmed I/O.\n"); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) + iounmap((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK)); +#else + vfree((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK)); +#endif + temp_p->maddr = 0; + } + } } #endif + /* + * We HAVE to make sure the first pause_sequencer() and all other + * subsequent I/O that isn't PCI config space I/O takes place + * after the MMAPed I/O region is configured and tested. The + * problem is the PowerPC architecture that doesn't support + * programmed I/O at all, so we have to have the MMAP I/O set up + * for this pause to even work on those machines. + */ pause_sequencer(temp_p); /* @@ -9065,45 +9178,36 @@ } /* - * Doing a switch based upon i is really gross, but since Justin - * changed around the chip ID stuff, we can't use that any more. - * Since we don't scan the devices the same way as FreeBSD, we end - * up doing this gross hack in order to avoid totally splitting - * away from Justin's init code in ahc_pci.c + * We need to set the CHNL? assignments before loading the SEEPROM + * The 3940 and 3985 cards (original stuff, not any of the later + * stuff) are 7870 and 7880 class chips. The Ultra2 stuff falls + * under 7896 and 7897. The 7895 is in a class by itself :) */ - switch (i) + switch (temp_p->chip & AHC_CHIPID_MASK) { - case 7: /* 3940 */ - case 12: /* 3940-Ultra */ - switch(PCI_SLOT(temp_p->pci_device_fn)) + case AHC_AIC7870: /* 3840 / 3985 */ + case AHC_AIC7880: /* 3840 UW / 3985 UW */ + if(temp_p->flags & AHC_MULTI_CHANNEL) { - case 5: - temp_p->flags |= AHC_CHNLB; - break; - default: - break; - } - break; - - case 8: /* 3985 */ - case 13: /* 3985-Ultra */ - switch(PCI_SLOT(temp_p->pci_device_fn)) - { - case 8: - temp_p->flags |= AHC_CHNLB; - break; - case 12: - temp_p->flags |= AHC_CHNLC; - break; - default: - break; + switch(PCI_SLOT(temp_p->pci_device_fn)) + { + case 5: + temp_p->flags |= AHC_CHNLB; + break; + case 8: + temp_p->flags |= AHC_CHNLB; + break; + case 12: + temp_p->flags |= AHC_CHNLC; + break; + default: + break; + } } break; - case 15: - case 18: - case 19: - case 20: + case AHC_AIC7895: /* 7895 */ + case AHC_AIC7896: /* 7896/7 */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) if (PCI_FUNC(temp_p->pdev->devfn) != 0) { @@ -9179,16 +9283,6 @@ aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) | CACHETHEN | MPARCKEN | USCBSIZE32 | CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0); - /* FALLTHROUGH */ - default: - /* - * We attempt to read a SEEPROM on *everything*. If we fail, - * then we fail, but this covers things like 2910c cards that - * now have SEEPROMs with their 7856 chipset that we would - * otherwise ignore. They still don't have a BIOS, but they - * have a SEEPROM that the SCSISelect utility on the Adaptec - * diskettes can configure. - */ aic7xxx_load_seeprom(temp_p, &sxfrctl1); break; case AHC_AIC7850: @@ -9200,14 +9294,13 @@ aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) | CACHETHEN | MPARCKEN) & ~DPARCKEN, DSCOMMAND0); + /* FALLTHROUGH */ + default: aic7xxx_load_seeprom(temp_p, &sxfrctl1); break; case AHC_AIC7880: /* - * Only set the DSCOMMAND0 register if this is a Rev B. - * chipset. For those, we also enable Ultra mode by - * force due to brain-damage on the part of some BIOSes - * We overload the devconfig variable here since we can. + * Check the rev of the chipset before we change DSCOMMAND0 */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_read_config_dword(pdev, DEVCONFIG, &devconfig); @@ -10892,23 +10985,14 @@ } action = BUS_RESET; } - if ( ((jiffies - p->last_reset) < (HZ * 3)) && - (action & (HOST_RESET | BUS_RESET)) ) + if ( (p->flags & AHC_RESET_DELAY) && + (action & (HOST_RESET | BUS_RESET)) ) { if (aic7xxx_verbose & VERBOSE_RESET_PROCESS) printk(INFO_LEAD "Reset called too soon after " "last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd)); action = RESET_DELAY; } - if ( (action & (BUS_RESET | HOST_RESET)) && (p->flags & AHC_IN_RESET) - && ((jiffies - p->reset_start) > (2 * HZ * 3)) ) - { - printk(KERN_ERR "(scsi%d:%d:%d:%d) Yikes!! Card must have left to go " - "back to Adaptec!!\n", p->host_no, CTL_OF_CMD(cmd)); - unpause_sequencer(p, FALSE); - DRIVER_UNLOCK - return(SCSI_RESET_SNOOZE); - } /* * By this point, we want to already know what we are going to do and * only have the following code implement our course of action. @@ -10940,15 +11024,23 @@ case BUS_RESET: case HOST_RESET: default: - p->reset_start = jiffies; - p->flags |= AHC_IN_RESET; + p->flags |= AHC_IN_RESET | AHC_RESET_DELAY; + p->dev_expires[p->scsi_id] = jiffies + (3 * HZ); + p->dev_timer_active |= (0x01 << p->scsi_id); + if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) || + time_after_eq(p->dev_timer.expires, p->dev_expires[p->scsi_id]) ) + { + del_timer(&p->dev_timer); + p->dev_timer.expires = p->dev_expires[p->scsi_id]; + add_timer(&p->dev_timer); + p->dev_timer_active |= (0x01 << MAX_TARGETS); + } aic7xxx_reset_channel(p, cmd->channel, TRUE); if ( (p->features & AHC_TWIN) && (action & HOST_RESET) ) { aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE); restart_sequencer(p); } - p->last_reset = jiffies; if (action != HOST_RESET) result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET; else @@ -10970,11 +11062,13 @@ * entirely and avoids getting a bogus dead command back through the * mid-level code due to too many retries. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,132) if ( flags & SCSI_RESET_SYNCHRONOUS ) { - cmd->result = DID_RESET << 16; + cmd->result = DID_BUS_BUSY << 16; cmd->done(cmd); } +#endif p->flags &= ~AHC_IN_RESET; /* * We can't rely on run_waiting_queues to unpause the sequencer for @@ -11000,16 +11094,21 @@ int aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[]) { - int heads, sectors, cylinders; + int heads, sectors, cylinders, ret; struct aic7xxx_host *p; + struct buffer_head *bh; p = (struct aic7xxx_host *) disk->device->host->hostdata; + bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024); - /* - * XXX - if I could portably find the card's configuration - * information, then this could be autodetected instead - * of left to a boot-time switch. - */ + if ( bh ) + { + ret = scsi_partsize(bh, disk->capacity, &geom[2], &geom[0], &geom[1]); + brelse(bh); + if ( ret != -1 ) + return(ret); + } + heads = 64; sectors = 32; cylinders = disk->capacity / (heads * sectors); diff -urN linux-2.0.37-pre8/drivers/scsi/aic7xxx_proc.c linux-2.0.37-pre9/drivers/scsi/aic7xxx_proc.c --- linux-2.0.37-pre8/drivers/scsi/aic7xxx_proc.c 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/drivers/scsi/aic7xxx_proc.c 2003-08-15 15:04:42.000000000 -0700 @@ -31,7 +31,7 @@ #define BLS (&aic7xxx_buffer[size]) #define HDRB \ -" < 512 512-1K 1-2K 2-4K 4-8K 8-16K 16-32K 32-64K 64-128K >128K" +" < 2K 2K+ 4K+ 8K+ 16K+ 32K+ 64K+ 128K+" #ifdef PROC_DEBUG extern int vsprintf(char *, const char *, va_list); @@ -86,7 +86,7 @@ int size = 0; unsigned char i; struct aic7xxx_xferstats *sp; - unsigned char target, lun; + unsigned char target; HBAptr = NULL; @@ -130,16 +130,12 @@ size = 4096; for (target = 0; target < MAX_TARGETS; target++) { - for (lun = 0; lun < MAX_LUNS; lun++) - { - if ( ((lun == 0) && (p->dev_flags[target] & DEVICE_PRESENT)) || - (p->stats[target][lun].r_total != 0) ) + if (p->dev_flags[target] & DEVICE_PRESENT) #ifdef AIC7XXX_PROC_STATS - size += 512; + size += 512; #else - size += 256; + size += 256; #endif - } } if (aic7xxx_buffer_size != size) { @@ -164,21 +160,17 @@ size += sprintf(BLS, "%s", AIC7XXX_H_VERSION); size += sprintf(BLS, "\n"); size += sprintf(BLS, "Compile Options:\n"); -#ifdef AIC7XXX_RESET_DELAY - size += sprintf(BLS, " AIC7XXX_RESET_DELAY : %d\n", AIC7XXX_RESET_DELAY); +#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT + size += sprintf(BLS, " TCQ Enabled By Default : Enabled\n"); +#else + size += sprintf(BLS, " TCQ Enabled By Default : Disabled\n"); #endif - size += sprintf(BLS, " AIC7XXX_TAGGED_QUEUEING: Adapter Support Enabled\n"); - size += sprintf(BLS, " Check below to see " - "which\n" - " devices use tagged " - "queueing\n"); - size += sprintf(BLS, " AIC7XXX_PAGE_ENABLE : Enabled (This is no longer " - "an option)\n"); #ifdef AIC7XXX_PROC_STATS size += sprintf(BLS, " AIC7XXX_PROC_STATS : Enabled\n"); #else size += sprintf(BLS, " AIC7XXX_PROC_STATS : Disabled\n"); #endif + size += sprintf(BLS, " AIC7XXX_RESET_DELAY : %d\n", AIC7XXX_RESET_DELAY); size += sprintf(BLS, "\n"); size += sprintf(BLS, "Adapter Configuration:\n"); size += sprintf(BLS, " SCSI Adapter: %s\n", @@ -254,11 +246,7 @@ } size += sprintf(BLS, " Tag Queue Enable Flags: 0x%04x\n", p->tagenable); size += sprintf(BLS, "Ordered Queue Tag Flags: 0x%04x\n", p->orderedtag); -#ifdef AIC7XXX_CMDS_PER_LUN - size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_LUN); -#else - size += sprintf(BLS, "Default Tag Queue Depth: %d\n", 8); -#endif + size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_DEVICE); size += sprintf(BLS, " Tagged Queue By Device array for aic7xxx host " "instance %d:\n", p->instance); size += sprintf(BLS, " {"); @@ -273,89 +261,83 @@ size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]); size += sprintf(BLS, "\n"); - size += sprintf(BLS, "Statistics:\n"); + size += sprintf(BLS, "Statistics:\n\n"); for (target = 0; target < MAX_TARGETS; target++) { - for (lun = 0; lun < MAX_LUNS; lun++) + sp = &p->stats[target]; + if ((p->dev_flags[target] & DEVICE_PRESENT) == 0) { - sp = &p->stats[target][lun]; - if ( (!(p->dev_flags[target] & DEVICE_PRESENT)) || - ((p->stats[target][lun].r_total == 0) && (lun != 0)) ) - { - continue; - } - if (p->features & AHC_TWIN) + continue; + } + if (p->features & AHC_TWIN) + { + size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n", + p->host_no, (target >> 3), (target & 0x7), 0); + } + else + { + size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n", + p->host_no, 0, target, 0); + } + size += sprintf(BLS, " Device using %s/%s", + (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ? + "Wide" : "Narrow", + (p->transinfo[target].cur_offset != 0) ? + "Sync transfers at " : "Async transfers.\n" ); + if (p->transinfo[target].cur_offset != 0) + { + struct aic7xxx_syncrate *sync_rate; + int period = p->transinfo[target].cur_period; + int rate = (p->transinfo[target].cur_width == + MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0; + + sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2); + if (sync_rate != NULL) { - size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n", - p->host_no, (target >> 3), (target & 0x7), lun); + size += sprintf(BLS, "%s MByte/sec, offset %d\n", + sync_rate->rate[rate], + p->transinfo[target].cur_offset ); } else { - size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n", - p->host_no, 0, target, lun); - } - size += sprintf(BLS, " Device using %s/%s\n", - (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ? - "Wide" : "Narrow", - (p->transinfo[target].cur_offset != 0) ? - "Sync transfers at" : "Async transfers." ); - if (p->transinfo[target].cur_offset != 0) - { - struct aic7xxx_syncrate *sync_rate; - int period = p->transinfo[target].cur_period; - int rate = (p->transinfo[target].cur_width == - MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0; - - sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2); - if (sync_rate != NULL) - { - size += sprintf(BLS, " %s MByte/sec, offset %d\n", - sync_rate->rate[rate], - p->transinfo[target].cur_offset ); - } - else - { - size += sprintf(BLS, " 3.3 MByte/sec, offset %d\n", - p->transinfo[target].cur_offset ); - } + size += sprintf(BLS, "3.3 MByte/sec, offset %d\n", + p->transinfo[target].cur_offset ); } - size += sprintf(BLS, " Device Negotiation Settings\n"); - size += sprintf(BLS, " Period Offset Bus Width\n"); - size += sprintf(BLS, "User %03d %03d %d\n", - p->transinfo[target].user_period, - p->transinfo[target].user_offset, - p->transinfo[target].user_width); - size += sprintf(BLS, "Goal %03d %03d %d\n", - p->transinfo[target].goal_period, - p->transinfo[target].goal_offset, - p->transinfo[target].goal_width); - size += sprintf(BLS, "Current %03d %03d %d\n", - p->transinfo[target].cur_period, - p->transinfo[target].cur_offset, - p->transinfo[target].cur_width); + } + size += sprintf(BLS, " Transinfo settings: "); + size += sprintf(BLS, "current(%d/%d/%d), ", + p->transinfo[target].cur_period, + p->transinfo[target].cur_offset, + p->transinfo[target].cur_width); + size += sprintf(BLS, "goal(%d/%d/%d), ", + p->transinfo[target].goal_period, + p->transinfo[target].goal_offset, + p->transinfo[target].goal_width); + size += sprintf(BLS, "user(%d/%d/%d)\n", + p->transinfo[target].user_period, + p->transinfo[target].user_offset, + p->transinfo[target].user_width); #ifdef AIC7XXX_PROC_STATS - size += sprintf(BLS, " Total transfers %ld (%ld read;%ld written)\n", - sp->xfers, sp->r_total, sp->w_total); - size += sprintf(BLS, " blks(512) rd=%ld; blks(512) wr=%ld\n", - sp->r_total512, sp->w_total512); - size += sprintf(BLS, "%s\n", HDRB); - size += sprintf(BLS, " Reads:"); - for (i = 0; i < NUMBER(sp->r_bins); i++) - { - size += sprintf(BLS, "%6ld ", sp->r_bins[i]); - } - size += sprintf(BLS, "\n"); - size += sprintf(BLS, "Writes:"); - for (i = 0; i < NUMBER(sp->w_bins); i++) - { - size += sprintf(BLS, "%6ld ", sp->w_bins[i]); - } + size += sprintf(BLS, " Total transfers %ld (%ld reads and %ld writes)\n", + sp->r_total + sp->w_total, sp->r_total, sp->w_total); + size += sprintf(BLS, "%s\n", HDRB); + size += sprintf(BLS, " Reads:"); + for (i = 0; i < NUMBER(sp->r_bins); i++) + { + size += sprintf(BLS, " %7ld", sp->r_bins[i]); + } + size += sprintf(BLS, "\n"); + size += sprintf(BLS, " Writes:"); + for (i = 0; i < NUMBER(sp->w_bins); i++) + { + size += sprintf(BLS, " %7ld", sp->w_bins[i]); + } + size += sprintf(BLS, "\n"); #else - size += sprintf(BLS, " Total transfers: %ld/%ld read/written)\n", - sp->r_total, sp->w_total); + size += sprintf(BLS, " Total transfers %ld (%ld reads and %ld writes)\n", + sp->r_total + sp->w_total, sp->r_total, sp->w_total); #endif /* AIC7XXX_PROC_STATS */ - size += sprintf(BLS, "\n\n"); - } + size += sprintf(BLS, "\n\n"); } if (size >= aic7xxx_buffer_size) diff -urN linux-2.0.37-pre8/drivers/scsi/Config.in linux-2.0.37-pre9/drivers/scsi/Config.in --- linux-2.0.37-pre8/drivers/scsi/Config.in 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/drivers/scsi/Config.in 2003-08-15 15:04:42.000000000 -0700 @@ -21,10 +21,8 @@ dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then - bool ' Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N - if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then - int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 24 - fi + bool ' Enable Tagged Command Queueing (TCQ) by default' CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT + int ' Maximum number of TCQ commands per device' CONFIG_AIC7XXX_CMDS_PER_DEVICE 8 bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5 fi @@ -86,6 +84,9 @@ bool ' Buggy EPP chipset support' CONFIG_SCSI_PPA_HAVE_PEDANTIC fi dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI +dep_tristate 'PCI-2000 support' CONFIG_SCSI_PCI2000 $CONFIG_SCSI +dep_tristate 'PCI2220I support' CONFIG_SCSI_PCI2220I $CONFIG_SCSI +dep_tristate 'PSI240I support' CONFIG_SCSI_PSI240I $CONFIG_SCSI dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI if [ "$CONFIG_PCI" = "y" ]; then dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI diff -urN linux-2.0.37-pre8/drivers/scsi/hosts.c linux-2.0.37-pre9/drivers/scsi/hosts.c --- linux-2.0.37-pre8/drivers/scsi/hosts.c 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/drivers/scsi/hosts.c 2003-08-15 15:04:42.000000000 -0700 @@ -201,6 +201,18 @@ #include "inia100.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_DEBUG #include "scsi_debug.h" #endif @@ -360,6 +372,15 @@ #ifdef CONFIG_SCSI_INITIO INI9100U, #endif +#ifdef CONFIG_SCSI_PCI2000 + PCI2000, +#endif +#ifdef CONFIG_SCSI_PCI2220I + PCI2220I, +#endif +#ifdef CONFIG_SCSI_PSI240I + PSI240I, +#endif #ifdef CONFIG_BLK_DEV_IDESCSI IDESCSI, #endif diff -urN linux-2.0.37-pre8/drivers/scsi/Makefile linux-2.0.37-pre9/drivers/scsi/Makefile --- linux-2.0.37-pre8/drivers/scsi/Makefile 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/drivers/scsi/Makefile 2003-08-15 15:04:42.000000000 -0700 @@ -412,6 +412,30 @@ endif endif +ifeq ($(CONFIG_SCSI_PCI2000),y) +L_OBJS += pci2000.o +else + ifeq ($(CONFIG_SCSI_PCI2000),m) + M_OBJS += pci2000.o + endif +endif + +ifeq ($(CONFIG_SCSI_PCI2220I),y) +L_OBJS += pci2220i.o +else + ifeq ($(CONFIG_SCSI_PCI2220I),m) + M_OBJS += pci2220i.o + endif +endif + +ifeq ($(CONFIG_SCSI_PSI240I),y) +L_OBJS += psi240i.o +else + ifeq ($(CONFIG_SCSI_PSI240I),m) + M_OBJS += psi240i.o + endif +endif + ifeq ($(CONFIG_BLK_DEV_IDESCSI),y) L_OBJS += ide-scsi.o endif diff -urN linux-2.0.37-pre8/drivers/scsi/pci2000.c linux-2.0.37-pre9/drivers/scsi/pci2000.c --- linux-2.0.37-pre8/drivers/scsi/pci2000.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.37-pre9/drivers/scsi/pci2000.c 2003-08-15 15:04:42.000000000 -0700 @@ -0,0 +1,716 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: pci2000i.c + * + * Revisions 1.10 Jan-21-1999 + * + * - Fixed sign on message to reflect proper controller name. + * - Added support for RAID status monitoring and control. + * + *-M*************************************************************************/ +#define PCI2000_VERSION "1.10" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include "hosts.h" + +#include "pci2000.h" +#include "psi_roy.h" + +#include + +struct proc_dir_entry Proc_Scsi_Pci2000 = + { PROC_SCSI_PCI2000, 7, "pci2000", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; + +//#define DEBUG 1 + +#ifdef DEBUG +#define DEB(x) x +#define STOP_HERE {int st;for(st=0;st<100;st++){st=1;}} +#else +#define DEB(x) +#define STOP_HERE +#endif + +typedef struct + { + ULONG address; + ULONG length; + } SCATGATH, *PSCATGATH; + +typedef struct + { + Scsi_Cmnd *SCpnt; + SCATGATH scatGath[16]; + UCHAR tag; + } DEV2000, *PDEV2000; + +typedef struct + { + USHORT basePort; + USHORT mb0; + USHORT mb1; + USHORT mb2; + USHORT mb3; + USHORT mb4; + USHORT cmd; + USHORT tag; + DEV2000 dev[MAX_BUS][MAX_UNITS]; + } ADAPTER2000, *PADAPTER2000; + +#define HOSTDATA(host) ((PADAPTER2000)&host->hostdata) + + +static struct Scsi_Host *PsiHost[MAXADAPTER] = {NULL,}; // One for each adapter +static int NumAdapters = 0; +/**************************************************************** + * Name: WaitReady :LOCAL + * + * Description: Wait for controller ready. + * + * Parameters: padapter - Pointer adapter data structure. + * + * Returns: TRUE on not ready. + * + ****************************************************************/ +static int WaitReady (PADAPTER2000 padapter) + { + ULONG timer; + + timer = jiffies + TIMEOUT_COMMAND; // calculate the timeout value + do { + if ( !inb_p (padapter->cmd) ) + return FALSE; + } while ( timer > jiffies ); // test for timeout + return TRUE; + } +/**************************************************************** + * Name: OpDone :LOCAL + * + * Description: Clean up operation and issue done to caller. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * status - Caller status. + * + * Returns: Nothing. + * + ****************************************************************/ +static void OpDone (Scsi_Cmnd *SCpnt, ULONG status) + { + SCpnt->result = status; + SCpnt->scsi_done (SCpnt); + } +/**************************************************************** + * Name: Command :LOCAL + * + * Description: Issue queued command to the PCI-2000. + * + * Parameters: padapter - Pointer to adapter information structure. + * cmd - PCI-2000 command byte. + * + * Returns: Non-zero command tag if operation is accepted. + * + ****************************************************************/ +static UCHAR Command (PADAPTER2000 padapter, UCHAR cmd) + { + outb_p (cmd, padapter->cmd); + if ( WaitReady (padapter) ) + return 0; + + if ( inw_p (padapter->mb0) ) + return 0; + + return inb_p (padapter->mb1); + } +/**************************************************************** + * Name: BuildSgList :LOCAL + * + * Description: Build the scatter gather list for controller. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * padapter - Pointer to adapter information structure. + * pdev - Pointer to adapter device structure. + * + * Returns: Non-zero in not scatter gather. + * + ****************************************************************/ +static int BuildSgList (Scsi_Cmnd *SCpnt, PADAPTER2000 padapter, PDEV2000 pdev) + { + int z; + + if ( SCpnt->use_sg ) + { + for ( z = 0; z < SCpnt->use_sg; z++ ) + { + pdev->scatGath[z].address = virt_to_bus (((struct scatterlist *)SCpnt->request_buffer)[z].address); + pdev->scatGath[z].length = ((struct scatterlist *)SCpnt->request_buffer)[z].length; + } + outl (virt_to_bus (pdev->scatGath), padapter->mb2); + outl ((SCpnt->use_sg << 24) | SCpnt->request_bufflen, padapter->mb3); + return FALSE; + } + outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2); + outl (SCpnt->request_bufflen, padapter->mb3); + return TRUE; + } +/********************************************************************* + * Name: PsiRaidCmd + * + * Description: Execute a simple command. + * + * Parameters: padapter - Pointer to adapter control structure. + * cmd - Roy command byte. + * + * Returns: Return error status. + * + ********************************************************************/ +static int PsiRaidCmd (PADAPTER2000 padapter, char cmd) + { + if ( WaitReady (padapter) ) // test for command register ready + return DID_TIME_OUT; + outb_p (cmd, padapter->cmd); // issue command + if ( WaitReady (padapter) ) // wait for adapter ready + return DID_TIME_OUT; + return DID_OK; + } +/**************************************************************** + * Name: Irq_Handler :LOCAL + * + * Description: Interrupt handler. + * + * Parameters: irq - Hardware IRQ number. + * dev_id - + * regs - + * + * Returns: TRUE if drive is not ready in time. + * + ****************************************************************/ +static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) + { + struct Scsi_Host *shost = NULL; // Pointer to host data block + PADAPTER2000 padapter; // Pointer to adapter control structure + PDEV2000 pdev; + Scsi_Cmnd *SCpnt; + UCHAR tag = 0; + UCHAR tag0; + ULONG error; + int pun; + int bus; + int z; + + DEB(printk ("\npci2000 recieved interrupt ")); + for ( z = 0; z < NumAdapters; z++ ) // scan for interrupt to process + { + if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) ) + { + tag = inb_p (HOSTDATA(PsiHost[z])->tag); + if ( tag ) + { + shost = PsiHost[z]; + break; + } + } + } + + if ( !shost ) + { + DEB (printk ("\npci2000: not my interrupt")); + return; + } + + padapter = HOSTDATA(shost); + + tag0 = tag & 0x7F; // mask off the error bit + for ( bus = 0; bus < MAX_BUS; bus++ ) // scan the busses + { + for ( pun = 0; pun < MAX_UNITS; pun++ ) // scan the targets + { + pdev = &padapter->dev[bus][pun]; + if ( !pdev->tag ) + continue; + if ( pdev->tag == tag0 ) // is this it? + { + pdev->tag = 0; + SCpnt = pdev->SCpnt; + goto irqProceed; + } + } + } + + outb_p (0xFF, padapter->tag); // clear the op interrupt + outb_p (CMD_DONE, padapter->cmd); // complete the op + return; // done, but, with what? + +irqProceed:; + if ( tag & ERR08_TAGGED ) // is there an error here? + { + if ( WaitReady (padapter) ) + { + OpDone (SCpnt, DID_TIME_OUT << 16); + return; + } + + outb_p (tag0, padapter->mb0); // get real error code + outb_p (CMD_ERROR, padapter->cmd); + if ( WaitReady (padapter) ) // wait for controller to suck up the op + { + OpDone (SCpnt, DID_TIME_OUT << 16); + return; + } + + error = inl (padapter->mb0); // get error data + outb_p (0xFF, padapter->tag); // clear the op interrupt + outb_p (CMD_DONE, padapter->cmd); // complete the op + + DEB (printk ("status: %lX ", error)); + if ( error == 0x00020002 ) // is this error a check condition? + { + if ( bus ) // are we doint SCSI commands? + { + OpDone (SCpnt, (DID_OK << 16) | 2); + return; + } + if ( *SCpnt->cmnd == SCSIOP_TEST_UNIT_READY ) + OpDone (SCpnt, (DRIVER_SENSE << 24) | (DID_OK << 16) | 2); // test caller we have sense data too + else + OpDone (SCpnt, DID_ERROR << 16); + return; + } + OpDone (SCpnt, DID_ERROR << 16); + return; + } + + outb_p (0xFF, padapter->tag); // clear the op interrupt + outb_p (CMD_DONE, padapter->cmd); // complete the op + OpDone (SCpnt, DID_OK << 16); + } +/**************************************************************** + * Name: Pci2220i_QueueCommand + * + * Description: Process a queued command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * done - Pointer to done function to call. + * + * Returns: Status code. + * + ****************************************************************/ +int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) + { + UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB + PADAPTER2000 padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure + int rc = -1; // command return code + UCHAR bus = SCpnt->channel; + UCHAR pun = SCpnt->target; + UCHAR lun = SCpnt->lun; + UCHAR cmd; + PDEV2000 pdev = &padapter->dev[bus][pun]; + + if ( !done ) + { + printk("pci2000_queuecommand: %02X: done can't be NULL\n", *cdb); + return 0; + } + + SCpnt->scsi_done = done; + pdev->SCpnt = SCpnt; // Save this command data + + if ( WaitReady (padapter) ) + { + rc = DID_ERROR; + goto finished; + } + + outw_p (pun | (lun << 8), padapter->mb0); + + if ( bus ) + { + DEB (if(*cdb) printk ("\nCDB: %X- %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9])); + DEB (if(*cdb) printk ("\ntimeout_per_command: %d, timeout_total: %d, timeout: %d, internal_timout: %d", SCpnt->timeout_per_command, + SCpnt->timeout_total, SCpnt->timeout, SCpnt->internal_timeout)); + outl (SCpnt->timeout_per_command, padapter->mb1); + outb_p (CMD_SCSI_TIMEOUT, padapter->cmd); + if ( WaitReady (padapter) ) + { + rc = DID_ERROR; + goto finished; + } + + outw_p (pun | (lun << 8), padapter->mb0); + outw_p (SCpnt->cmd_len << 8, padapter->mb0 + 2); + outl (virt_to_bus (cdb), padapter->mb1); + if ( BuildSgList (SCpnt, padapter, pdev) ) + cmd = CMD_SCSI_THRU; + else + cmd = CMD_SCSI_THRU_SG; + if ( (pdev->tag = Command (padapter, cmd)) == 0 ) + rc = DID_TIME_OUT; + goto finished; + } + else + { + if ( lun ) + { + rc = DID_BAD_TARGET; + goto finished; + } + } + + switch ( *cdb ) + { + case SCSIOP_INQUIRY: // inquiry CDB + if ( cdb[2] == SC_MY_RAID ) + { + switch ( cdb[3] ) + { + case MY_SCSI_REBUILD: + OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_REBUILD) << 16); + return 0; + case MY_SCSI_ALARMMUTE: + OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_MUTE) << 16); + return 0; + case MY_SCSI_DEMOFAIL: + OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_FAIL) << 16); + return 0; + default: + if ( SCpnt->use_sg ) + { + rc = DID_ERROR; + goto finished; + } + else + outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2); + outl (cdb[5], padapter->mb0); + outl (cdb[3], padapter->mb3); + cmd = CMD_DASD_RAID_RQ; + break; + } + break; + } + + if ( SCpnt->use_sg ) + { + outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2); + } + else + { + outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2); + } + outl (SCpnt->request_bufflen, padapter->mb3); + cmd = CMD_DASD_SCSI_INQ; + break; + + case SCSIOP_TEST_UNIT_READY: // test unit ready CDB + outl (virt_to_bus (SCpnt->sense_buffer), padapter->mb2); + outl (sizeof (SCpnt->sense_buffer), padapter->mb3); + cmd = CMD_TEST_READY; + break; + + case SCSIOP_READ_CAPACITY: // read capctiy CDB + if ( SCpnt->use_sg ) + { + outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2); + } + else + { + outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2); + } + outl (8, padapter->mb3); + cmd = CMD_DASD_CAP; + break; + case SCSIOP_VERIFY: // verify CDB + outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2); + outl (XSCSI2LONG (&cdb[2]), padapter->mb1); + cmd = CMD_READ_SG; + break; + case SCSIOP_READ: // read10 CDB + outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2); + outl (XSCSI2LONG (&cdb[2]), padapter->mb1); + if ( BuildSgList (SCpnt, padapter, pdev) ) + cmd = CMD_READ; + else + cmd = CMD_READ_SG; + break; + case SCSIOP_READ6: // read6 CDB + outw_p (cdb[4], padapter->mb0 + 2); + outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1); + if ( BuildSgList (SCpnt, padapter, pdev) ) + cmd = CMD_READ; + else + cmd = CMD_READ_SG; + break; + case SCSIOP_WRITE: // write10 CDB + outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2); + outl (XSCSI2LONG (&cdb[2]), padapter->mb1); + if ( BuildSgList (SCpnt, padapter, pdev) ) + cmd = CMD_WRITE; + else + cmd = CMD_WRITE_SG; + break; + case SCSIOP_WRITE6: // write6 CDB + outw_p (cdb[4], padapter->mb0 + 2); + outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1); + if ( BuildSgList (SCpnt, padapter, pdev) ) + cmd = CMD_WRITE; + else + cmd = CMD_WRITE_SG; + break; + case SCSIOP_START_STOP_UNIT: + cmd = CMD_EJECT_MEDIA; + break; + case SCSIOP_MEDIUM_REMOVAL: + switch ( cdb[4] ) + { + case 0: + cmd = CMD_UNLOCK_DOOR; + break; + case 1: + cmd = CMD_LOCK_DOOR; + break; + default: + cmd = 0; + break; + } + if ( cmd ) + break; + default: + DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb)); + OpDone (SCpnt, DID_ERROR << 16); + return 0; + } + + if ( (pdev->tag = Command (padapter, cmd)) == 0 ) + rc = DID_TIME_OUT; +finished:; + if ( rc != -1 ) + OpDone (SCpnt, rc << 16); + return 0; + } +/**************************************************************** + * Name: internal_done :LOCAL + * + * Description: Done handler for non-queued commands + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Nothing. + * + ****************************************************************/ +static void internal_done (Scsi_Cmnd * SCpnt) + { + SCpnt->SCp.Status++; + } +/**************************************************************** + * Name: Pci2220i_Command + * + * Description: Process a command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Status code. + * + ****************************************************************/ +int Pci2000_Command (Scsi_Cmnd *SCpnt) + { + DEB(printk("pci2000_command: ..calling pci2000_queuecommand\n")); + + Pci2000_QueueCommand (SCpnt, internal_done); + + SCpnt->SCp.Status = 0; + while (!SCpnt->SCp.Status) + barrier (); + return SCpnt->result; + } +/**************************************************************** + * Name: Pci2220i_Detect + * + * Description: Detect and initialize our boards. + * + * Parameters: tpnt - Pointer to SCSI host template structure. + * + * Returns: Number of adapters found. + * + ****************************************************************/ +int Pci2000_Detect (Scsi_Host_Template *tpnt) + { + int pci_index = 0; + struct Scsi_Host *pshost; + PADAPTER2000 padapter; + int z, zz; + int setirq; + + if ( pcibios_present () ) + { + for ( pci_index = 0; pci_index <= MAXADAPTER; ++pci_index ) + { + UCHAR pci_bus, pci_device_fn; + + if ( pcibios_find_device (VENDOR_PSI, DEVICE_ROY_1, pci_index, &pci_bus, &pci_device_fn) != 0 ) + break; + + pshost = scsi_register (tpnt, sizeof(ADAPTER2000)); + padapter = HOSTDATA(pshost); + + pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &padapter->basePort); + padapter->basePort &= 0xFFFE; + DEB (printk ("\nBase Regs = %#04X", padapter->basePort)); // get the base I/O port address + padapter->mb0 = padapter->basePort + RTR_MAILBOX; // get the 32 bit mail boxes + padapter->mb1 = padapter->basePort + RTR_MAILBOX + 4; + padapter->mb2 = padapter->basePort + RTR_MAILBOX + 8; + padapter->mb3 = padapter->basePort + RTR_MAILBOX + 12; + padapter->mb4 = padapter->basePort + RTR_MAILBOX + 16; + padapter->cmd = padapter->basePort + RTR_LOCAL_DOORBELL; // command register + padapter->tag = padapter->basePort + RTR_PCI_DOORBELL; // tag/response register + + if ( WaitReady (padapter) ) + goto unregister; + outb_p (0x84, padapter->mb0); + outb_p (CMD_SPECIFY, padapter->cmd); + if ( WaitReady (padapter) ) + goto unregister; + + pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq); + setirq = 1; + for ( z = 0; z < pci_index; z++ ) // scan for shared interrupts + { + if ( PsiHost[z]->irq == pshost->irq ) // if shared then, don't posses + setirq = 0; + } + if ( setirq ) // if not shared, posses + { + if ( request_irq (pshost->irq, Irq_Handler, 0, "pci2000", NULL) ) + { + printk ("Unable to allocate IRQ for PSI-2000 controller.\n"); + goto unregister; + } + } + PsiHost[pci_index] = pshost; // save SCSI_HOST pointer + + pshost->unique_id = padapter->basePort; + pshost->max_id = 16; + pshost->max_channel = 1; + + for ( zz = 0; zz < MAX_BUS; zz++ ) + for ( z = 0; z < MAX_UNITS; z++ ) + padapter->dev[zz][z].tag = 0; + + printk("\nPSI-2000 Intelligent Storage SCSI CONTROLLER: at I/O = %X IRQ = %d\n", padapter->basePort, pshost->irq); + printk("Version %s, Compiled %s %s\n\n", PCI2000_VERSION, __DATE__, __TIME__); + continue; +unregister:; + scsi_unregister (pshost); + } + } + NumAdapters = pci_index; + return pci_index; + } +/**************************************************************** + * Name: Pci2220i_Abort + * + * Description: Process the Abort command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Allways snooze. + * + ****************************************************************/ +int Pci2000_Abort (Scsi_Cmnd *SCpnt) + { + DEB (printk ("pci2000_abort\n")); + return SCSI_ABORT_SNOOZE; + } +/**************************************************************** + * Name: Pci2220i_Reset + * + * Description: Process the Reset command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * flags - Flags about the reset command + * + * Returns: No active command at this time, so this means + * that each time we got some kind of response the + * last time through. Tell the mid-level code to + * request sense information in order to decide what + * to do next. + * + ****************************************************************/ +int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) + { + return SCSI_RESET_PUNT; + } + +#include "sd.h" + +/**************************************************************** + * Name: Pci2220i_BiosParam + * + * Description: Process the biosparam request from the SCSI manager to + * return C/H/S data. + * + * Parameters: disk - Pointer to SCSI disk structure. + * dev - Major/minor number from kernel. + * geom - Pointer to integer array to place geometry data. + * + * Returns: zero. + * + ****************************************************************/ +int Pci2000_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[]) + { + PADAPTER2000 padapter; + + padapter = HOSTDATA(disk->device->host); + + if ( WaitReady (padapter) ) + return 0; + outb_p (disk->device->id, padapter->mb0); + outb_p (CMD_GET_PARMS, padapter->cmd); + if ( WaitReady (padapter) ) + return 0; + + geom[0] = inb_p (padapter->mb2 + 3); + geom[1] = inb_p (padapter->mb2 + 2); + geom[2] = inw_p (padapter->mb2); + return 0; + } + + +#ifdef MODULE +/* Eventually this will go into an include file, but this will be later */ +Scsi_Host_Template driver_template = PCI2000; + +#include "scsi_module.c" +#endif + diff -urN linux-2.0.37-pre8/drivers/scsi/pci2000.h linux-2.0.37-pre9/drivers/scsi/pci2000.h --- linux-2.0.37-pre8/drivers/scsi/pci2000.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.37-pre9/drivers/scsi/pci2000.h 2003-08-15 15:04:42.000000000 -0700 @@ -0,0 +1,226 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: pci2000.h + * + * Description: Header file for the SCSI driver for the PCI-2000 + * interface card. + * + *-M*************************************************************************/ +#ifndef _PCI2000_H +#define _PCI2000_H + +#include +#include + +#ifndef PSI_EIDE_SCSIOP +#define PSI_EIDE_SCSIOP 1 + +/************************************************/ +/* definition of standard data types */ +/************************************************/ +#define CHAR char +#define UCHAR unsigned char +#define SHORT short +#define USHORT unsigned short +#define BOOL long +#define LONG long +#define ULONG unsigned long +#define VOID void + +typedef CHAR *PCHAR; +typedef UCHAR *PUCHAR; +typedef SHORT *PSHORT; +typedef USHORT *PUSHORT; +typedef BOOL *PBOOL; +typedef LONG *PLONG; +typedef ULONG *PULONG; +typedef VOID *PVOID; + + +/************************************************/ +/* Misc. macros */ +/************************************************/ +#define ANY2SCSI(up, p) \ +((UCHAR *)up)[0] = (((ULONG)(p)) >> 8); \ +((UCHAR *)up)[1] = ((ULONG)(p)); + +#define SCSI2LONG(up) \ +( (((long)*(((UCHAR *)up))) << 16) \ ++ (((long)(((UCHAR *)up)[1])) << 8) \ ++ ((long)(((UCHAR *)up)[2])) ) + +#define XANY2SCSI(up, p) \ +((UCHAR *)up)[0] = ((long)(p)) >> 24; \ +((UCHAR *)up)[1] = ((long)(p)) >> 16; \ +((UCHAR *)up)[2] = ((long)(p)) >> 8; \ +((UCHAR *)up)[3] = ((long)(p)); + +#define XSCSI2LONG(up) \ +( (((long)(((UCHAR *)up)[0])) << 24) \ ++ (((long)(((UCHAR *)up)[1])) << 16) \ ++ (((long)(((UCHAR *)up)[2])) << 8) \ ++ ((long)(((UCHAR *)up)[3])) ) + +/************************************************/ +/* SCSI CDB operation codes */ +/************************************************/ +#define SCSIOP_TEST_UNIT_READY 0x00 +#define SCSIOP_REZERO_UNIT 0x01 +#define SCSIOP_REWIND 0x01 +#define SCSIOP_REQUEST_BLOCK_ADDR 0x02 +#define SCSIOP_REQUEST_SENSE 0x03 +#define SCSIOP_FORMAT_UNIT 0x04 +#define SCSIOP_READ_BLOCK_LIMITS 0x05 +#define SCSIOP_REASSIGN_BLOCKS 0x07 +#define SCSIOP_READ6 0x08 +#define SCSIOP_RECEIVE 0x08 +#define SCSIOP_WRITE6 0x0A +#define SCSIOP_PRINT 0x0A +#define SCSIOP_SEND 0x0A +#define SCSIOP_SEEK6 0x0B +#define SCSIOP_TRACK_SELECT 0x0B +#define SCSIOP_SLEW_PRINT 0x0B +#define SCSIOP_SEEK_BLOCK 0x0C +#define SCSIOP_PARTITION 0x0D +#define SCSIOP_READ_REVERSE 0x0F +#define SCSIOP_WRITE_FILEMARKS 0x10 +#define SCSIOP_FLUSH_BUFFER 0x10 +#define SCSIOP_SPACE 0x11 +#define SCSIOP_INQUIRY 0x12 +#define SCSIOP_VERIFY6 0x13 +#define SCSIOP_RECOVER_BUF_DATA 0x14 +#define SCSIOP_MODE_SELECT 0x15 +#define SCSIOP_RESERVE_UNIT 0x16 +#define SCSIOP_RELEASE_UNIT 0x17 +#define SCSIOP_COPY 0x18 +#define SCSIOP_ERASE 0x19 +#define SCSIOP_MODE_SENSE 0x1A +#define SCSIOP_START_STOP_UNIT 0x1B +#define SCSIOP_STOP_PRINT 0x1B +#define SCSIOP_LOAD_UNLOAD 0x1B +#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C +#define SCSIOP_SEND_DIAGNOSTIC 0x1D +#define SCSIOP_MEDIUM_REMOVAL 0x1E +#define SCSIOP_READ_CAPACITY 0x25 +#define SCSIOP_READ 0x28 +#define SCSIOP_WRITE 0x2A +#define SCSIOP_SEEK 0x2B +#define SCSIOP_LOCATE 0x2B +#define SCSIOP_WRITE_VERIFY 0x2E +#define SCSIOP_VERIFY 0x2F +#define SCSIOP_SEARCH_DATA_HIGH 0x30 +#define SCSIOP_SEARCH_DATA_EQUAL 0x31 +#define SCSIOP_SEARCH_DATA_LOW 0x32 +#define SCSIOP_SET_LIMITS 0x33 +#define SCSIOP_READ_POSITION 0x34 +#define SCSIOP_SYNCHRONIZE_CACHE 0x35 +#define SCSIOP_COMPARE 0x39 +#define SCSIOP_COPY_COMPARE 0x3A +#define SCSIOP_WRITE_DATA_BUFF 0x3B +#define SCSIOP_READ_DATA_BUFF 0x3C +#define SCSIOP_CHANGE_DEFINITION 0x40 +#define SCSIOP_READ_SUB_CHANNEL 0x42 +#define SCSIOP_READ_TOC 0x43 +#define SCSIOP_READ_HEADER 0x44 +#define SCSIOP_PLAY_AUDIO 0x45 +#define SCSIOP_PLAY_AUDIO_MSF 0x47 +#define SCSIOP_PLAY_TRACK_INDEX 0x48 +#define SCSIOP_PLAY_TRACK_RELATIVE 0x49 +#define SCSIOP_PAUSE_RESUME 0x4B +#define SCSIOP_LOG_SELECT 0x4C +#define SCSIOP_LOG_SENSE 0x4D +#define SCSIOP_MODE_SELECT10 0x55 +#define SCSIOP_MODE_SENSE10 0x5A +#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6 +#define SCSIOP_MECHANISM_STATUS 0xBD +#define SCSIOP_READ_CD 0xBE + +// SCSI read capacity structure +typedef struct _READ_CAPACITY_DATA + { + ULONG blks; /* total blocks (converted to little endian) */ + ULONG blksiz; /* size of each (converted to little endian) */ + } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA; + +// SCSI inquiry data +typedef struct _INQUIRYDATA + { + UCHAR DeviceType :5; + UCHAR DeviceTypeQualifier :3; + UCHAR DeviceTypeModifier :7; + UCHAR RemovableMedia :1; + UCHAR Versions; + UCHAR ResponseDataFormat; + UCHAR AdditionalLength; + UCHAR Reserved[2]; + UCHAR SoftReset :1; + UCHAR CommandQueue :1; + UCHAR Reserved2 :1; + UCHAR LinkedCommands :1; + UCHAR Synchronous :1; + UCHAR Wide16Bit :1; + UCHAR Wide32Bit :1; + UCHAR RelativeAddressing :1; + UCHAR VendorId[8]; + UCHAR ProductId[16]; + UCHAR ProductRevisionLevel[4]; + UCHAR VendorSpecific[20]; + UCHAR Reserved3[40]; + } INQUIRYDATA, *PINQUIRYDATA; + +#endif + +// function prototypes +int Pci2000_Detect (Scsi_Host_Template *tpnt); +int Pci2000_Command (Scsi_Cmnd *SCpnt); +int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); +int Pci2000_Abort (Scsi_Cmnd *SCpnt); +int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int flags); +int Pci2000_BiosParam (Disk *disk, kdev_t dev, int geom[]); + +#ifndef NULL + #define NULL 0 +#endif + +extern struct proc_dir_entry Proc_Scsi_Pci2000; + +#define PCI2000 { NULL, NULL, \ + &Proc_Scsi_Pci2000,/* proc_dir_entry */ \ + NULL, \ + "PCI-2000 SCSI Intelligent Disk Controller",\ + Pci2000_Detect, \ + NULL, \ + NULL, \ + Pci2000_Command, \ + Pci2000_QueueCommand, \ + Pci2000_Abort, \ + Pci2000_Reset, \ + NULL, \ + Pci2000_BiosParam, \ + 16, \ + -1, \ + 16, \ + 1, \ + 0, \ + 0, \ + DISABLE_CLUSTERING } + +#endif diff -urN linux-2.0.37-pre8/drivers/scsi/pci2220i.c linux-2.0.37-pre9/drivers/scsi/pci2220i.c --- linux-2.0.37-pre8/drivers/scsi/pci2220i.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.37-pre9/drivers/scsi/pci2220i.c 2003-08-15 15:04:42.000000000 -0700 @@ -0,0 +1,819 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: pci2220i.c + * + * Description: SCSI driver for the PCI2220I EIDE interface card. + * + *-M*************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include "hosts.h" + +#include "pci2220i.h" +#include "psi_dale.h" + +#include + +struct proc_dir_entry Proc_Scsi_Pci2220i = + { PROC_SCSI_PCI2220I, 7, "pci2220i", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; + +//#define DEBUG 1 + +#ifdef DEBUG +#define DEB(x) x +#define STOP_HERE {int st;for(st=0;st<100;st++){st=1;}} +#else +#define DEB(x) +#define STOP_HERE +#endif + +#define MAXADAPTER 4 /* Increase this and the sizes of the arrays below, if you need more. */ + +#define MAX_BUS_MASTER_BLOCKS 1 // This is the maximum we can bus master for (1024 bytes) + +#define PORT_DATA 0 +#define PORT_ERROR 1 +#define PORT_SECTOR_COUNT 2 +#define PORT_LBA_0 3 +#define PORT_LBA_8 4 +#define PORT_LBA_16 5 +#define PORT_LBA_24 6 +#define PORT_STAT_CMD 7 +#define PORT_STAT_SEL 8 +#define PORT_FAIL 9 +#define PORT_ALT_STAT 10 + +typedef struct + { + UCHAR device; // device code + UCHAR byte6; // device select register image + UCHAR spigot; // spigot number + UCHAR sparebyte; // placeholder + USHORT sectors; // number of sectors per track + USHORT heads; // number of heads + USHORT cylinders; // number of cylinders for this device + USHORT spareword; // placeholder + ULONG blocks; // number of blocks on device + } OUR_DEVICE, *POUR_DEVICE; + +typedef struct + { + USHORT ports[12]; + USHORT regDmaDesc; // address of the DMA discriptor register for direction of transfer + USHORT regDmaCmdStat; // Byte #1 of DMA command status register + USHORT regDmaAddrPci; // 32 bit register for PCI address of DMA + USHORT regDmaAddrLoc; // 32 bit register for local bus address of DMA + USHORT regDmaCount; // 32 bit register for DMA transfer count + USHORT regDmaMode; // 32 bit register for DMA mode control + USHORT regRemap; // 32 bit local space remap + USHORT regDesc; // 32 bit local region descriptor + USHORT regRange; // 32 bit local range + USHORT regIrqControl; // 16 bit Interrupt enable/disable and status + USHORT regScratchPad; // scratch pad I/O base address + USHORT regBase; // Base I/O register for data space + USHORT basePort; // PLX base I/O port + USHORT timingMode; // timing mode currently set for adapter + ULONG timingAddress; // address to use on adapter for current timing mode + OUR_DEVICE device[4]; + IDE_STRUCT ide; + ULONG startSector; + USHORT sectorCount; + Scsi_Cmnd *SCpnt; + VOID *buffer; + USHORT expectingIRQ; + USHORT readPhase; + } ADAPTER2220I, *PADAPTER2220I; + +#define HOSTDATA(host) ((PADAPTER2220I)&host->hostdata) + + +static struct Scsi_Host *PsiHost[MAXADAPTER] = {NULL,}; // One for each adapter +static int NumAdapters = 0; +static IDENTIFY_DATA identifyData; +static SETUP DaleSetup; + +/**************************************************************** + * Name: WriteData :LOCAL + * + * Description: Write data to device. + * + * Parameters: padapter - Pointer adapter data structure. + * + * Returns: TRUE if drive does not assert DRQ in time. + * + ****************************************************************/ +static int WriteData (PADAPTER2220I padapter) + { + ULONG timer; + USHORT *pports = padapter->ports; + + timer = jiffies + TIMEOUT_DRQ; // calculate the timeout value + do { + if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ ) + { + outb_p (0, padapter->regDmaDesc); // write operation + outl (padapter->timingAddress, padapter->regDmaAddrLoc); + outl (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci); + outl ((ULONG)padapter->ide.ide.ide[2] * (ULONG)512, padapter->regDmaCount); + outb_p (1, padapter->regDmaMode); // interrupts off + outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear + return 0; + } + } while ( timer > jiffies ); // test for timeout + + padapter->ide.ide.ides.cmd = 0; // null out the command byte + return 1; + } +/**************************************************************** + * Name: IdeCmd :LOCAL + * + * Description: Process a queued command from the SCSI manager. + * + * Parameters: padapter - Pointer adapter data structure. + * + * Returns: Zero if no error or status register contents on error. + * + ****************************************************************/ +static UCHAR IdeCmd (PADAPTER2220I padapter) + { + ULONG timer; + USHORT *pports = padapter->ports; + UCHAR status; + + outb_p (padapter->ide.ide.ides.spigot, pports[PORT_STAT_SEL]); // select the spigot + outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]); // select the drive + timer = jiffies + TIMEOUT_READY; // calculate the timeout value + DEB(printk ("\npci2220i Issueing new command: 0x%X",padapter->ide.ide.ides.cmd)); + do { + status = inb_p (padapter->ports[PORT_STAT_CMD]); + if ( status & IDE_STATUS_DRDY ) + { + outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]); + outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]); + outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]); + outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]); + padapter->expectingIRQ = 1; + outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]); + + if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE ) + return (WriteData (padapter)); + return 0; + } + } while ( timer > jiffies ); // test for timeout + + padapter->ide.ide.ides.cmd = 0; // null out the command byte + return status; + } +/**************************************************************** + * Name: SetupTransfer :LOCAL + * + * Description: Setup a data transfer command. + * + * Parameters: padapter - Pointer adapter data structure. + * drive - Drive/head register upper nibble only. + * + * Returns: TRUE if no data to transfer. + * + ****************************************************************/ +static int SetupTransfer (PADAPTER2220I padapter, UCHAR drive) + { + if ( padapter->sectorCount ) + { + *(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector; + padapter->ide.ide.ide[6] |= drive; +// padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount; + padapter->ide.ide.ides.sectors = ( padapter->sectorCount > MAX_BUS_MASTER_BLOCKS ) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount; + padapter->sectorCount -= padapter->ide.ide.ides.sectors; // bump the start and count for next xfer + padapter->startSector += padapter->ide.ide.ides.sectors; + return 0; + } + else + { + padapter->ide.ide.ides.cmd = 0; // null out the command byte + padapter->SCpnt = NULL; + return 1; + } + } +/**************************************************************** + * Name: DecodeError :LOCAL + * + * Description: Decode and process device errors. + * + * Parameters: pshost - Pointer to host data block. + * status - Status register code. + * + * Returns: The driver status code. + * + ****************************************************************/ +static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status) + { + PADAPTER2220I padapter = HOSTDATA(pshost); + UCHAR error; + + padapter->expectingIRQ = 0; + padapter->SCpnt = NULL; + if ( status & IDE_STATUS_WRITE_FAULT ) + { + return DID_PARITY << 16; + } + if ( status & IDE_STATUS_BUSY ) + return DID_BUS_BUSY << 16; + + error = inb_p (padapter->ports[PORT_ERROR]); + DEB(printk ("\npci2220i error register: %x", error)); + switch ( error ) + { + case IDE_ERROR_AMNF: + case IDE_ERROR_TKONF: + case IDE_ERROR_ABRT: + case IDE_ERROR_IDFN: + case IDE_ERROR_UNC: + case IDE_ERROR_BBK: + default: + return DID_ERROR << 16; + } + return DID_ERROR << 16; + } +/**************************************************************** + * Name: Irq_Handler :LOCAL + * + * Description: Interrupt handler. + * + * Parameters: irq - Hardware IRQ number. + * dev_id - + * regs - + * + * Returns: TRUE if drive is not ready in time. + * + ****************************************************************/ +static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) + { + struct Scsi_Host *shost = NULL; // Pointer to host data block + PADAPTER2220I padapter; // Pointer to adapter control structure + USHORT *pports; // I/O port array + Scsi_Cmnd *SCpnt; + UCHAR status; + int z; + +// DEB(printk ("\npci2220i recieved interrupt\n")); + + for ( z = 0; z < NumAdapters; z++ ) // scan for interrupt to process + { + if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) ) + { + if ( inw_p (HOSTDATA(PsiHost[z])->regIrqControl) & 0x8000 ) + { + shost = PsiHost[z]; + break; + } + } + } + + if ( !shost ) + { + DEB (printk ("\npci2220i: not my interrupt")); + return; + } + + padapter = HOSTDATA(shost); + pports = padapter->ports; + SCpnt = padapter->SCpnt; + + if ( !padapter->expectingIRQ ) + { + DEB(printk ("\npci2220i Unsolicited interrupt\n")); + return; + } + padapter->expectingIRQ = 0; + + status = inb_p (padapter->ports[PORT_STAT_CMD]); // read the device status + if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) ) + goto irqerror; + + switch ( padapter->ide.ide.ides.cmd ) // decide how to handle the interrupt + { + case IDE_CMD_READ_MULTIPLE: + if ( padapter->readPhase == 1 ) // is this a bus master channel complete? + { + DEB(printk ("\npci2220i processing read interrupt cleanup")); + outb_p (0x08, padapter->regDmaCmdStat); // cancel interrupt from DMA engine + padapter->buffer += padapter->ide.ide.ides.sectors * 512; + if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) ) + { + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + padapter->readPhase = 0; + if ( !(status = IdeCmd (padapter)) ) + { + DEB (printk ("\npci2220i interrupt complete, waiting for another")); + return; + } + } + if ( status & IDE_STATUS_DRQ ) + { + DEB(printk ("\npci2220i processing read interrupt start bus master cycle")); + outb_p (8, padapter->regDmaDesc); // read operation + padapter->readPhase = 1; + padapter->expectingIRQ = 1; + outl (padapter->timingAddress, padapter->regDmaAddrLoc); + outl (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci); + outl ((ULONG)padapter->ide.ide.ides.sectors * (ULONG)512, padapter->regDmaCount); + outb_p (5, padapter->regDmaMode); // interrupt enable/disable + outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear + return; + } + break; + + case IDE_CMD_WRITE_MULTIPLE: + DEB(printk ("\npci2220i processing write interrupt cleanup")); + padapter->buffer += padapter->ide.ide.ides.sectors * 512; + if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) ) + { + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + if ( !(status = IdeCmd (padapter)) ) + { + DEB (printk ("\npci2220i interrupt complete, waiting for another")); + return; + } + break; + + case IDE_COMMAND_IDENTIFY: + { + PINQUIRYDATA pinquiryData = SCpnt->request_buffer; + + DEB(printk ("\npci2220i processing verify interrupt cleanup")); + if ( status & IDE_STATUS_DRQ ) + { + insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1); + + memset (pinquiryData, 0, SCpnt->request_bufflen); // Zero INQUIRY data structure. + pinquiryData->DeviceType = 0; + pinquiryData->Versions = 2; + pinquiryData->AdditionalLength = 35 - 4; + + // Fill in vendor identification fields. + for ( z = 0; z < 20; z += 2 ) + { + pinquiryData->VendorId[z] = ((UCHAR *)identifyData.ModelNumber)[z + 1]; + pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z]; + } + + // Initialize unused portion of product id. + for ( z = 0; z < 4; z++ ) + pinquiryData->ProductId[12 + z] = ' '; + + // Move firmware revision from IDENTIFY data to + // product revision in INQUIRY data. + for ( z = 0; z < 4; z += 2 ) + { + pinquiryData->ProductRevisionLevel[z] = ((UCHAR *)identifyData.FirmwareRevision)[z + 1]; + pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z]; + } + + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + break; + } + + default: + DEB(printk ("\npci2220i no real process here!")); + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + +irqerror:; + DEB(printk ("\npci2220i error Device Status: %X\n", status)); + SCpnt->result = DecodeError (shost, status); + SCpnt->scsi_done (SCpnt); + } +/**************************************************************** + * Name: Pci2220i_QueueCommand + * + * Description: Process a queued command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * done - Pointer to done function to call. + * + * Returns: Status code. + * + ****************************************************************/ +int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) + { + UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB + PADAPTER2220I padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure + POUR_DEVICE pdev = &padapter->device[SCpnt->target];// Pointer to device information + UCHAR rc; // command return code + + SCpnt->scsi_done = done; + padapter->ide.ide.ides.spigot = pdev->spigot; + padapter->buffer = SCpnt->request_buffer; + if (done) + { + if ( !pdev->device || SCpnt->lun ) + { + SCpnt->result = DID_BAD_TARGET << 16; + done (SCpnt); + return 0; + } + } + else + { + printk("pci2220i_queuecommand: %02X: done can't be NULL\n", *cdb); + return 0; + } + + DEB (if(*cdb) printk ("\nCDB: %X- %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9])); + switch ( *cdb ) + { + case SCSIOP_INQUIRY: // inquiry CDB + { + padapter->ide.ide.ide[6] = pdev->byte6; + padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY; + break; + } + + case SCSIOP_TEST_UNIT_READY: // test unit ready CDB + SCpnt->result = DID_OK << 16; + done (SCpnt); + return 0; + + case SCSIOP_READ_CAPACITY: // read capctiy CDB + { + PREAD_CAPACITY_DATA pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer; + + pdata->blksiz = 0x20000; + XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks); + SCpnt->result = DID_OK << 16; + done (SCpnt); + return 0; + } + + case SCSIOP_VERIFY: // verify CDB + *(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]); + padapter->ide.ide.ide[6] |= pdev->byte6; + padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8)); + padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY; + break; + + case SCSIOP_READ: // read10 CDB + padapter->startSector = XSCSI2LONG (&cdb[2]); + padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8); + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE; + padapter->readPhase = 0; + break; + + case SCSIOP_READ6: // read6 CDB + padapter->startSector = SCSI2LONG (&cdb[1]); + padapter->sectorCount = cdb[4]; + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE; + padapter->readPhase = 0; + break; + + case SCSIOP_WRITE: // write10 CDB + padapter->startSector = XSCSI2LONG (&cdb[2]); + padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8); + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE; + break; + case SCSIOP_WRITE6: // write6 CDB + padapter->startSector = SCSI2LONG (&cdb[1]); + padapter->sectorCount = cdb[4]; + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE; + break; + + default: + DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb)); + SCpnt->result = DID_ERROR << 16; + done (SCpnt); + return 0; + } + + padapter->SCpnt = SCpnt; // Save this command data + + rc = IdeCmd (padapter); + if ( rc ) + { + padapter->expectingIRQ = 0; + DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd)); + SCpnt->result = DID_ERROR << 16; + done (SCpnt); + return 0; + } + if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE ) + { + if ( WriteData (padapter) ) + { + padapter->expectingIRQ = 0; + DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to accept data\n", *cdb, padapter->ide.ide.ides.cmd)); + SCpnt->result = DID_ERROR << 16; + done (SCpnt); + return 0; + } + } + DEB (printk(" now waiting for initial interrupt ")); + return 0; + } + +static void internal_done(Scsi_Cmnd * SCpnt) + { + SCpnt->SCp.Status++; + } +/**************************************************************** + * Name: Pci2220i_Command + * + * Description: Process a command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Status code. + * + ****************************************************************/ +int Pci2220i_Command (Scsi_Cmnd *SCpnt) + { + DEB(printk("pci2220i_command: ..calling pci2220i_queuecommand\n")); + + Pci2220i_QueueCommand (SCpnt, internal_done); + + SCpnt->SCp.Status = 0; + while (!SCpnt->SCp.Status) + barrier (); + return SCpnt->result; + } +/**************************************************************** + * Name: ReadFlash + * + * Description: Read information from controller Flash memory. + * + * Parameters: hostdata - Pointer to host interface data structure. + * pdata - Pointer to data structures. + * base - base address in Flash. + * length - lenght of data space in bytes. + * + * Returns: Nothing. + * + ****************************************************************/ +VOID ReadFlash (PADAPTER2220I hostdata, VOID *pdata, ULONG base, ULONG length) + { + ULONG oldremap; + UCHAR olddesc; + ULONG z; + UCHAR *pd = (UCHAR *)pdata; + + oldremap = inl (hostdata->regRemap); // save values to restore later + olddesc = inb_p (hostdata->regDesc); + + outl (base | 1, hostdata->regRemap); // remap to Flash space as specified + outb_p (0x40, hostdata->regDesc); // describe remap region as 8 bit + for ( z = 0; z < length; z++) // get "length" data count + *pd++ = inb_p (hostdata->regBase + z); // read in the data + + outl (oldremap, hostdata->regRemap); // restore remap register values + outb_p (olddesc, hostdata->regDesc); + } + +/**************************************************************** + * Name: Pci2220i_Detect + * + * Description: Detect and initialize our boards. + * + * Parameters: tpnt - Pointer to SCSI host template structure. + * + * Returns: Number of adapters found. + * + ****************************************************************/ +int Pci2220i_Detect (Scsi_Host_Template *tpnt) + { + int pci_index = 0; + struct Scsi_Host *pshost; + PADAPTER2220I hostdata; + ULONG modearray[] = {DALE_DATA_MODE2, DALE_DATA_MODE3, DALE_DATA_MODE4, DALE_DATA_MODE4P}; + int unit; + int z; + int setirq; + + if ( pcibios_present () ) + { + for ( pci_index = 0; pci_index <= MAXADAPTER; ++pci_index ) + { + UCHAR pci_bus, pci_device_fn; + + if ( pcibios_find_device (VENDOR_PSI, DEVICE_DALE_1, pci_index, &pci_bus, &pci_device_fn) != 0 ) + break; + + pshost = scsi_register (tpnt, sizeof(ADAPTER2220I)); + hostdata = HOSTDATA(pshost); + + pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &hostdata->basePort); + hostdata->basePort &= 0xFFFE; + DEB (printk ("\nBase Regs = %#04X", hostdata->basePort)); + hostdata->regRemap = hostdata->basePort + RTR_LOCAL_REMAP; // 32 bit local space remap + DEB (printk (" %#04X", hostdata->regRemap)); + hostdata->regDesc = hostdata->basePort + RTR_REGIONS; // 32 bit local region descriptor + DEB (printk (" %#04X", hostdata->regDesc)); + hostdata->regRange = hostdata->basePort + RTR_LOCAL_RANGE; // 32 bit local range + DEB (printk (" %#04X", hostdata->regRange)); + hostdata->regIrqControl = hostdata->basePort + RTR_INT_CONTROL_STATUS; // 16 bit interupt control and status + DEB (printk (" %#04X", hostdata->regIrqControl)); + hostdata->regScratchPad = hostdata->basePort + RTR_MAILBOX; // 16 byte scratchpad I/O base address + DEB (printk (" %#04X", hostdata->regScratchPad)); + + pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &hostdata->regBase); + hostdata->regBase &= 0xFFFE; + for ( z = 0; z < 9; z++ ) // build regester address array + hostdata->ports[z] = hostdata->regBase + 0x80 + (z * 4); + hostdata->ports[PORT_FAIL] = hostdata->regBase + REG_FAIL; + hostdata->ports[PORT_ALT_STAT] = hostdata->regBase + REG_ALT_STAT; + DEB (printk ("\nPorts =")); + DEB (for (z=0;z<11;z++) printk(" %#04X", hostdata->ports[z]);); + + hostdata->regDmaDesc = hostdata->regBase + RTL_DMA1_DESC_PTR; // address of the DMA discriptor register for direction of transfer + DEB (printk ("\nDMA Regs = %#04X", hostdata->regDmaDesc)); + hostdata->regDmaCmdStat = hostdata->regBase + RTL_DMA_COMMAND_STATUS + 1; // Byte #1 of DMA command status register + DEB (printk (" %#04X", hostdata->regDmaCmdStat)); + hostdata->regDmaAddrPci = hostdata->regBase + RTL_DMA1_PCI_ADDR; // 32 bit register for PCI address of DMA + DEB (printk (" %#04X", hostdata->regDmaAddrPci)); + hostdata->regDmaAddrLoc = hostdata->regBase + RTL_DMA1_LOCAL_ADDR; // 32 bit register for local bus address of DMA + DEB (printk (" %#04X", hostdata->regDmaAddrLoc)); + hostdata->regDmaCount = hostdata->regBase + RTL_DMA1_COUNT; // 32 bit register for DMA transfer count + DEB (printk (" %#04X", hostdata->regDmaCount)); + hostdata->regDmaMode = hostdata->regBase + RTL_DMA1_MODE + 1; // 32 bit register for DMA mode control + DEB (printk (" %#04X", hostdata->regDmaMode)); + + if ( !inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES) ) // if no devices on this board + goto unregister; + + pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq); + setirq = 1; + for ( z = 0; z < pci_index; z++ ) // scan for shared interrupts + { + if ( PsiHost[z]->irq == pshost->irq ) // if shared then, don't posses + setirq = 0; + } + if ( setirq ) // if not shared, posses + { + if ( request_irq (pshost->irq, Irq_Handler, 0, "pci2220i", NULL) ) + { + printk ("Unable to allocate IRQ for PSI-2220I controller.\n"); + goto unregister; + } + } + PsiHost[pci_index] = pshost; // save SCSI_HOST pointer + + pshost->unique_id = hostdata->regBase; + pshost->max_id = 4; + + outb_p (0x01, hostdata->regRange); // fix our range register because other drivers want to tromp on it + + hostdata->timingMode = inb_p (hostdata->regScratchPad + DALE_TIMING_MODE); + hostdata->timingAddress = modearray[hostdata->timingMode - 2]; + ReadFlash (hostdata, &DaleSetup, DALE_FLASH_SETUP, sizeof (SETUP)); + + for ( z = 0; z < inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES); ++z ) + { + unit = inb_p (hostdata->regScratchPad + DALE_CHANNEL_DEVICE_0 + z) & 0x0F; + hostdata->device[unit].device = inb_p (hostdata->regScratchPad + DALE_SCRATH_DEVICE_0 + unit); + hostdata->device[unit].byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0); + hostdata->device[unit].spigot = (UCHAR)(1 << (unit >> 1)); + hostdata->device[unit].sectors = DaleSetup.setupDevice[unit].sectors; + hostdata->device[unit].heads = DaleSetup.setupDevice[unit].heads; + hostdata->device[unit].cylinders = DaleSetup.setupDevice[unit].cylinders; + hostdata->device[unit].blocks = DaleSetup.setupDevice[unit].blocks; + DEB (printk ("\nHOSTDATA->device = %X", hostdata->device[unit].device)); + DEB (printk ("\n byte6 = %X", hostdata->device[unit].byte6)); + DEB (printk ("\n spigot = %X", hostdata->device[unit].spigot)); + DEB (printk ("\n sectors = %X", hostdata->device[unit].sectors)); + DEB (printk ("\n heads = %X", hostdata->device[unit].heads)); + DEB (printk ("\n cylinders = %X", hostdata->device[unit].cylinders)); + DEB (printk ("\n blocks = %lX", hostdata->device[unit].blocks)); + } + + printk("\nPSI-2220I EIDE CONTROLLER: at I/O = %X/%X IRQ = %d\n", hostdata->basePort, hostdata->regBase, pshost->irq); + printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n"); + + NumAdapters++; + continue; +unregister:; + scsi_unregister (pshost); + } + } + return NumAdapters; + } +/**************************************************************** + * Name: Pci2220i_Abort + * + * Description: Process the Abort command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Allways snooze. + * + ****************************************************************/ +int Pci2220i_Abort (Scsi_Cmnd *SCpnt) + { + DEB (printk ("pci2220i_abort\n")); + return SCSI_ABORT_SNOOZE; + } +/**************************************************************** + * Name: Pci2220i_Reset + * + * Description: Process the Reset command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * flags - Flags about the reset command + * + * Returns: No active command at this time, so this means + * that each time we got some kind of response the + * last time through. Tell the mid-level code to + * request sense information in order to decide what + * to do next. + * + ****************************************************************/ +int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) + { + return SCSI_RESET_PUNT; + } + +#include "sd.h" + +/**************************************************************** + * Name: Pci2220i_BiosParam + * + * Description: Process the biosparam request from the SCSI manager to + * return C/H/S data. + * + * Parameters: disk - Pointer to SCSI disk structure. + * dev - Major/minor number from kernel. + * geom - Pointer to integer array to place geometry data. + * + * Returns: zero. + * + ****************************************************************/ +int Pci2220i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[]) + { + POUR_DEVICE pdev; + + pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]); + + geom[0] = pdev->heads; + geom[1] = pdev->sectors; + geom[2] = pdev->cylinders; + return 0; + } + + +#ifdef MODULE +/* Eventually this will go into an include file, but this will be later */ +Scsi_Host_Template driver_template = PCI2220I; + +#include "scsi_module.c" +#endif + diff -urN linux-2.0.37-pre8/drivers/scsi/pci2220i.h linux-2.0.37-pre9/drivers/scsi/pci2220i.h --- linux-2.0.37-pre8/drivers/scsi/pci2220i.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.37-pre9/drivers/scsi/pci2220i.h 2003-08-15 15:04:42.000000000 -0700 @@ -0,0 +1,345 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: pci2220i.h + * + * Description: Header file for the SCSI driver for the PCI2220I + * EIDE interface card. + * + *-M*************************************************************************/ + +#ifndef _PCI2220I_H +#define _PCI2220I_H + +#include +#include + +#ifndef PSI_EIDE_SCSIOP +#define PSI_EIDE_SCSIOP 1 + +/************************************************/ +/* Some defines that we like */ +/************************************************/ +#define CHAR char +#define UCHAR unsigned char +#define SHORT short +#define USHORT unsigned short +#define BOOL unsigned short +#define LONG long +#define ULONG unsigned long +#define VOID void + +/************************************************/ +/* Timeout konstants */ +/************************************************/ +#define TIMEOUT_READY 10 // 100 mSec +#define TIMEOUT_DRQ 40 // 400 mSec + +/************************************************/ +/* Misc. macros */ +/************************************************/ +#define ANY2SCSI(up, p) \ +((UCHAR *)up)[0] = (((ULONG)(p)) >> 8); \ +((UCHAR *)up)[1] = ((ULONG)(p)); + +#define SCSI2LONG(up) \ +( (((long)*(((UCHAR *)up))) << 16) \ ++ (((long)(((UCHAR *)up)[1])) << 8) \ ++ ((long)(((UCHAR *)up)[2])) ) + +#define XANY2SCSI(up, p) \ +((UCHAR *)up)[0] = ((long)(p)) >> 24; \ +((UCHAR *)up)[1] = ((long)(p)) >> 16; \ +((UCHAR *)up)[2] = ((long)(p)) >> 8; \ +((UCHAR *)up)[3] = ((long)(p)); + +#define XSCSI2LONG(up) \ +( (((long)(((UCHAR *)up)[0])) << 24) \ ++ (((long)(((UCHAR *)up)[1])) << 16) \ ++ (((long)(((UCHAR *)up)[2])) << 8) \ ++ ((long)(((UCHAR *)up)[3])) ) + +/************************************************/ +/* SCSI CDB operation codes */ +/************************************************/ +#define SCSIOP_TEST_UNIT_READY 0x00 +#define SCSIOP_REZERO_UNIT 0x01 +#define SCSIOP_REWIND 0x01 +#define SCSIOP_REQUEST_BLOCK_ADDR 0x02 +#define SCSIOP_REQUEST_SENSE 0x03 +#define SCSIOP_FORMAT_UNIT 0x04 +#define SCSIOP_READ_BLOCK_LIMITS 0x05 +#define SCSIOP_REASSIGN_BLOCKS 0x07 +#define SCSIOP_READ6 0x08 +#define SCSIOP_RECEIVE 0x08 +#define SCSIOP_WRITE6 0x0A +#define SCSIOP_PRINT 0x0A +#define SCSIOP_SEND 0x0A +#define SCSIOP_SEEK6 0x0B +#define SCSIOP_TRACK_SELECT 0x0B +#define SCSIOP_SLEW_PRINT 0x0B +#define SCSIOP_SEEK_BLOCK 0x0C +#define SCSIOP_PARTITION 0x0D +#define SCSIOP_READ_REVERSE 0x0F +#define SCSIOP_WRITE_FILEMARKS 0x10 +#define SCSIOP_FLUSH_BUFFER 0x10 +#define SCSIOP_SPACE 0x11 +#define SCSIOP_INQUIRY 0x12 +#define SCSIOP_VERIFY6 0x13 +#define SCSIOP_RECOVER_BUF_DATA 0x14 +#define SCSIOP_MODE_SELECT 0x15 +#define SCSIOP_RESERVE_UNIT 0x16 +#define SCSIOP_RELEASE_UNIT 0x17 +#define SCSIOP_COPY 0x18 +#define SCSIOP_ERASE 0x19 +#define SCSIOP_MODE_SENSE 0x1A +#define SCSIOP_START_STOP_UNIT 0x1B +#define SCSIOP_STOP_PRINT 0x1B +#define SCSIOP_LOAD_UNLOAD 0x1B +#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C +#define SCSIOP_SEND_DIAGNOSTIC 0x1D +#define SCSIOP_MEDIUM_REMOVAL 0x1E +#define SCSIOP_READ_CAPACITY 0x25 +#define SCSIOP_READ 0x28 +#define SCSIOP_WRITE 0x2A +#define SCSIOP_SEEK 0x2B +#define SCSIOP_LOCATE 0x2B +#define SCSIOP_WRITE_VERIFY 0x2E +#define SCSIOP_VERIFY 0x2F +#define SCSIOP_SEARCH_DATA_HIGH 0x30 +#define SCSIOP_SEARCH_DATA_EQUAL 0x31 +#define SCSIOP_SEARCH_DATA_LOW 0x32 +#define SCSIOP_SET_LIMITS 0x33 +#define SCSIOP_READ_POSITION 0x34 +#define SCSIOP_SYNCHRONIZE_CACHE 0x35 +#define SCSIOP_COMPARE 0x39 +#define SCSIOP_COPY_COMPARE 0x3A +#define SCSIOP_WRITE_DATA_BUFF 0x3B +#define SCSIOP_READ_DATA_BUFF 0x3C +#define SCSIOP_CHANGE_DEFINITION 0x40 +#define SCSIOP_READ_SUB_CHANNEL 0x42 +#define SCSIOP_READ_TOC 0x43 +#define SCSIOP_READ_HEADER 0x44 +#define SCSIOP_PLAY_AUDIO 0x45 +#define SCSIOP_PLAY_AUDIO_MSF 0x47 +#define SCSIOP_PLAY_TRACK_INDEX 0x48 +#define SCSIOP_PLAY_TRACK_RELATIVE 0x49 +#define SCSIOP_PAUSE_RESUME 0x4B +#define SCSIOP_LOG_SELECT 0x4C +#define SCSIOP_LOG_SENSE 0x4D +#define SCSIOP_MODE_SELECT10 0x55 +#define SCSIOP_MODE_SENSE10 0x5A +#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6 +#define SCSIOP_MECHANISM_STATUS 0xBD +#define SCSIOP_READ_CD 0xBE + +// IDE command definitions +#define IDE_COMMAND_ATAPI_RESET 0x08 +#define IDE_COMMAND_READ 0x20 +#define IDE_COMMAND_WRITE 0x30 +#define IDE_COMMAND_RECALIBRATE 0x10 +#define IDE_COMMAND_SEEK 0x70 +#define IDE_COMMAND_SET_PARAMETERS 0x91 +#define IDE_COMMAND_VERIFY 0x40 +#define IDE_COMMAND_ATAPI_PACKET 0xA0 +#define IDE_COMMAND_ATAPI_IDENTIFY 0xA1 +#define IDE_CMD_READ_MULTIPLE 0xC4 +#define IDE_CMD_WRITE_MULTIPLE 0xC5 +#define IDE_CMD_SET_MULTIPLE 0xC6 +#define IDE_COMMAND_WRITE_DMA 0xCA +#define IDE_COMMAND_READ_DMA 0xC8 +#define IDE_COMMAND_IDENTIFY 0xEC + +// IDE status definitions +#define IDE_STATUS_ERROR 0x01 +#define IDE_STATUS_INDEX 0x02 +#define IDE_STATUS_CORRECTED_ERROR 0x04 +#define IDE_STATUS_DRQ 0x08 +#define IDE_STATUS_DSC 0x10 +#define IDE_STATUS_WRITE_FAULT 0x20 +#define IDE_STATUS_DRDY 0x40 +#define IDE_STATUS_BUSY 0x80 + +// IDE error definitions +#define IDE_ERROR_AMNF 0x01 +#define IDE_ERROR_TKONF 0x02 +#define IDE_ERROR_ABRT 0x04 +#define IDE_ERROR_MCR 0x08 +#define IDE_ERROR_IDFN 0x10 +#define IDE_ERROR_MC 0x20 +#define IDE_ERROR_UNC 0x40 +#define IDE_ERROR_BBK 0x80 + +// IDE interface structure +typedef struct _IDE_STRUCT + { + union + { + UCHAR ide[9]; + struct + { + USHORT data; + UCHAR sectors; + UCHAR lba[4]; + UCHAR cmd; + UCHAR spigot; + } ides; + } ide; + } IDE_STRUCT; + +// SCSI read capacity structure +typedef struct _READ_CAPACITY_DATA + { + ULONG blks; /* total blocks (converted to little endian) */ + ULONG blksiz; /* size of each (converted to little endian) */ + } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA; + +// SCSI inquiry data +typedef struct _INQUIRYDATA + { + UCHAR DeviceType :5; + UCHAR DeviceTypeQualifier :3; + UCHAR DeviceTypeModifier :7; + UCHAR RemovableMedia :1; + UCHAR Versions; + UCHAR ResponseDataFormat; + UCHAR AdditionalLength; + UCHAR Reserved[2]; + UCHAR SoftReset :1; + UCHAR CommandQueue :1; + UCHAR Reserved2 :1; + UCHAR LinkedCommands :1; + UCHAR Synchronous :1; + UCHAR Wide16Bit :1; + UCHAR Wide32Bit :1; + UCHAR RelativeAddressing :1; + UCHAR VendorId[8]; + UCHAR ProductId[16]; + UCHAR ProductRevisionLevel[4]; + UCHAR VendorSpecific[20]; + UCHAR Reserved3[40]; + } INQUIRYDATA, *PINQUIRYDATA; + +// IDE IDENTIFY data +typedef struct _IDENTIFY_DATA + { + USHORT GeneralConfiguration; // 00 + USHORT NumberOfCylinders; // 02 + USHORT Reserved1; // 04 + USHORT NumberOfHeads; // 06 + USHORT UnformattedBytesPerTrack; // 08 + USHORT UnformattedBytesPerSector; // 0A + USHORT SectorsPerTrack; // 0C + USHORT VendorUnique1[3]; // 0E + USHORT SerialNumber[10]; // 14 + USHORT BufferType; // 28 + USHORT BufferSectorSize; // 2A + USHORT NumberOfEccBytes; // 2C + USHORT FirmwareRevision[4]; // 2E + USHORT ModelNumber[20]; // 36 + UCHAR MaximumBlockTransfer; // 5E + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 + USHORT Capabilities; // 62 + USHORT Reserved2; // 64 + UCHAR VendorUnique3; // 66 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:1; // 6A + USHORT Reserved3:15; + USHORT NumberOfCurrentCylinders; // 6C + USHORT NumberOfCurrentHeads; // 6E + USHORT CurrentSectorsPerTrack; // 70 + ULONG CurrentSectorCapacity; // 72 + USHORT Reserved4[197]; // 76 + } IDENTIFY_DATA, *PIDENTIFY_DATA; + +// Identify data without the Reserved4. +typedef struct _IDENTIFY_DATA2 { + USHORT GeneralConfiguration; // 00 + USHORT NumberOfCylinders; // 02 + USHORT Reserved1; // 04 + USHORT NumberOfHeads; // 06 + USHORT UnformattedBytesPerTrack; // 08 + USHORT UnformattedBytesPerSector; // 0A + USHORT SectorsPerTrack; // 0C + USHORT VendorUnique1[3]; // 0E + USHORT SerialNumber[10]; // 14 + USHORT BufferType; // 28 + USHORT BufferSectorSize; // 2A + USHORT NumberOfEccBytes; // 2C + USHORT FirmwareRevision[4]; // 2E + USHORT ModelNumber[20]; // 36 + UCHAR MaximumBlockTransfer; // 5E + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 + USHORT Capabilities; // 62 + USHORT Reserved2; // 64 + UCHAR VendorUnique3; // 66 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:1; // 6A + USHORT Reserved3:15; + USHORT NumberOfCurrentCylinders; // 6C + USHORT NumberOfCurrentHeads; // 6E + USHORT CurrentSectorsPerTrack; // 70 + ULONG CurrentSectorCapacity; // 72 + } IDENTIFY_DATA2, *PIDENTIFY_DATA2; + +#endif // PSI_EIDE_SCSIOP + +// function prototypes +int Pci2220i_Detect (Scsi_Host_Template *tpnt); +int Pci2220i_Command (Scsi_Cmnd *SCpnt); +int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); +int Pci2220i_Abort (Scsi_Cmnd *SCpnt); +int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int flags); +int Pci2220i_BiosParam (Disk *disk, kdev_t dev, int geom[]); + +#ifndef NULL + #define NULL 0 +#endif + +extern struct proc_dir_entry Proc_Scsi_Pci2220i; + +#define PCI2220I { NULL, NULL, \ + &Proc_Scsi_Pci2220i,/* proc_dir_entry */ \ + NULL, \ + "PCI-2220I EIDE Disk Controller", \ + Pci2220i_Detect, \ + NULL, \ + NULL, \ + Pci2220i_Command, \ + Pci2220i_QueueCommand, \ + Pci2220i_Abort, \ + Pci2220i_Reset, \ + NULL, \ + Pci2220i_BiosParam, \ + 1, \ + -1, \ + SG_NONE, \ + 1, \ + 0, \ + 0, \ + DISABLE_CLUSTERING } + +#endif diff -urN linux-2.0.37-pre8/drivers/scsi/psi240i.c linux-2.0.37-pre9/drivers/scsi/psi240i.c --- linux-2.0.37-pre8/drivers/scsi/psi240i.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.37-pre9/drivers/scsi/psi240i.c 2003-08-15 15:04:42.000000000 -0700 @@ -0,0 +1,717 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: psi240i.c + * + * Description: SCSI driver for the PSI240I EIDE interface card. + * + *-M*************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include "hosts.h" + +#include "psi240i.h" +#include "psi_chip.h" + +#include + +struct proc_dir_entry Proc_Scsi_Psi240i = + { PROC_SCSI_PSI240I, 7, "psi240i", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; + +//#define DEBUG 1 + +#ifdef DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + +#define MAXBOARDS 2 /* Increase this and the sizes of the arrays below, if you need more. */ + +#define PORT_DATA 0 +#define PORT_ERROR 1 +#define PORT_SECTOR_COUNT 2 +#define PORT_LBA_0 3 +#define PORT_LBA_8 4 +#define PORT_LBA_16 5 +#define PORT_LBA_24 6 +#define PORT_STAT_CMD 7 +#define PORT_SEL_FAIL 8 +#define PORT_IRQ_STATUS 9 +#define PORT_ADDRESS 10 +#define PORT_FAIL 11 +#define PORT_ALT_STAT 12 + +typedef struct + { + UCHAR device; // device code + UCHAR byte6; // device select register image + UCHAR spigot; // spigot number + UCHAR expectingIRQ; // flag for expecting and interrupt + USHORT sectors; // number of sectors per track + USHORT heads; // number of heads + USHORT cylinders; // number of cylinders for this device + USHORT spareword; // placeholder + ULONG blocks; // number of blocks on device + } OUR_DEVICE, *POUR_DEVICE; + +typedef struct + { + USHORT ports[13]; + OUR_DEVICE device[8]; + Scsi_Cmnd *pSCmnd; + IDE_STRUCT ide; + ULONG startSector; + USHORT sectorCount; + Scsi_Cmnd *SCpnt; + VOID *buffer; + USHORT expectingIRQ; + } ADAPTER240I, *PADAPTER240I; + +#define HOSTDATA(host) ((PADAPTER240I)&host->hostdata) + +static struct Scsi_Host *PsiHost[6] = {NULL,}; /* One for each IRQ level (10-15) */ +static IDENTIFY_DATA identifyData; +static SETUP ChipSetup; + +static USHORT portAddr[6] = {CHIP_ADRS_0, CHIP_ADRS_1, CHIP_ADRS_2, CHIP_ADRS_3, CHIP_ADRS_4, CHIP_ADRS_5}; + +/**************************************************************** + * Name: WriteData :LOCAL + * + * Description: Write data to device. + * + * Parameters: padapter - Pointer adapter data structure. + * + * Returns: TRUE if drive does not assert DRQ in time. + * + ****************************************************************/ +static int WriteData (PADAPTER240I padapter) + { + ULONG timer; + USHORT *pports = padapter->ports; + + timer = jiffies + TIMEOUT_DRQ; // calculate the timeout value + do { + if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ ) + { + outsw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ide[2] * 256); + return 0; + } + } while ( timer > jiffies ); // test for timeout + + padapter->ide.ide.ides.cmd = 0; // null out the command byte + return 1; + } +/**************************************************************** + * Name: IdeCmd :LOCAL + * + * Description: Process a queued command from the SCSI manager. + * + * Parameters: padapter - Pointer adapter data structure. + * + * Returns: Zero if no error or status register contents on error. + * + ****************************************************************/ +static UCHAR IdeCmd (PADAPTER240I padapter) + { + ULONG timer; + USHORT *pports = padapter->ports; + UCHAR status; + + outb_p (padapter->ide.ide.ides.spigot, pports[PORT_SEL_FAIL]); // select the spigot + outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]); // select the drive + timer = jiffies + TIMEOUT_READY; // calculate the timeout value + do { + status = inb_p (padapter->ports[PORT_STAT_CMD]); + if ( status & IDE_STATUS_DRDY ) + { + outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]); + outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]); + outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]); + outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]); + padapter->expectingIRQ = 1; + outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]); + + if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE ) + return (WriteData (padapter)); + + return 0; + } + } while ( timer > jiffies ); // test for timeout + + padapter->ide.ide.ides.cmd = 0; // null out the command byte + return status; + } +/**************************************************************** + * Name: SetupTransfer :LOCAL + * + * Description: Setup a data transfer command. + * + * Parameters: padapter - Pointer adapter data structure. + * drive - Drive/head register upper nibble only. + * + * Returns: TRUE if no data to transfer. + * + ****************************************************************/ +static int SetupTransfer (PADAPTER240I padapter, UCHAR drive) + { + if ( padapter->sectorCount ) + { + *(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector; + padapter->ide.ide.ide[6] |= drive; + padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount; + padapter->sectorCount -= padapter->ide.ide.ides.sectors; // bump the start and count for next xfer + padapter->startSector += padapter->ide.ide.ides.sectors; + return 0; + } + else + { + padapter->ide.ide.ides.cmd = 0; // null out the command byte + padapter->SCpnt = NULL; + return 1; + } + } +/**************************************************************** + * Name: DecodeError :LOCAL + * + * Description: Decode and process device errors. + * + * Parameters: pshost - Pointer to host data block. + * status - Status register code. + * + * Returns: The driver status code. + * + ****************************************************************/ +static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status) + { + PADAPTER240I padapter = HOSTDATA(pshost); + UCHAR error; + + padapter->expectingIRQ = 0; + padapter->SCpnt = NULL; + if ( status & IDE_STATUS_WRITE_FAULT ) + { + return DID_PARITY << 16; + } + if ( status & IDE_STATUS_BUSY ) + return DID_BUS_BUSY << 16; + + error = inb_p (padapter->ports[PORT_ERROR]); + DEB(printk ("\npsi240i error register: %x", error)); + switch ( error ) + { + case IDE_ERROR_AMNF: + case IDE_ERROR_TKONF: + case IDE_ERROR_ABRT: + case IDE_ERROR_IDFN: + case IDE_ERROR_UNC: + case IDE_ERROR_BBK: + default: + return DID_ERROR << 16; + } + return DID_ERROR << 16; + } +/**************************************************************** + * Name: Irq_Handler :LOCAL + * + * Description: Interrupt handler. + * + * Parameters: irq - Hardware IRQ number. + * dev_id - + * regs - + * + * Returns: TRUE if drive is not ready in time. + * + ****************************************************************/ +static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) + { + struct Scsi_Host *shost; // Pointer to host data block + PADAPTER240I padapter; // Pointer to adapter control structure + USHORT *pports; // I/O port array + Scsi_Cmnd *SCpnt; + UCHAR status; + int z; + + DEB(printk ("\npsi240i recieved interrupt\n")); + + shost = PsiHost[irq - 10]; + if ( !shost ) + panic ("Splunge!"); + + padapter = HOSTDATA(shost); + pports = padapter->ports; + SCpnt = padapter->SCpnt; + + if ( !padapter->expectingIRQ ) + { + DEB(printk ("\npsi240i Unsolicited interrupt\n")); + return; + } + padapter->expectingIRQ = 0; + + status = inb_p (padapter->ports[PORT_STAT_CMD]); // read the device status + if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) ) + goto irqerror; + + DEB(printk ("\npsi240i processing interrupt")); + switch ( padapter->ide.ide.ides.cmd ) // decide how to handle the interrupt + { + case IDE_CMD_READ_MULTIPLE: + if ( status & IDE_STATUS_DRQ ) + { + insw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ides.sectors * 256); + padapter->buffer += padapter->ide.ide.ides.sectors * 512; + if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) ) + { + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + if ( !(status = IdeCmd (padapter)) ) + return; + } + break; + + case IDE_CMD_WRITE_MULTIPLE: + padapter->buffer += padapter->ide.ide.ides.sectors * 512; + if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) ) + { + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + if ( !(status = IdeCmd (padapter)) ) + return; + break; + + case IDE_COMMAND_IDENTIFY: + { + PINQUIRYDATA pinquiryData = SCpnt->request_buffer; + + if ( status & IDE_STATUS_DRQ ) + { + insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1); + + memset (pinquiryData, 0, SCpnt->request_bufflen); // Zero INQUIRY data structure. + pinquiryData->DeviceType = 0; + pinquiryData->Versions = 2; + pinquiryData->AdditionalLength = 35 - 4; + + // Fill in vendor identification fields. + for ( z = 0; z < 20; z += 2 ) + { + pinquiryData->VendorId[z] = ((UCHAR *)identifyData.ModelNumber)[z + 1]; + pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z]; + } + + // Initialize unused portion of product id. + for ( z = 0; z < 4; z++ ) + pinquiryData->ProductId[12 + z] = ' '; + + // Move firmware revision from IDENTIFY data to + // product revision in INQUIRY data. + for ( z = 0; z < 4; z += 2 ) + { + pinquiryData->ProductRevisionLevel[z] = ((UCHAR *)identifyData.FirmwareRevision)[z + 1]; + pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z]; + } + + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + break; + } + + default: + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + +irqerror:; + DEB(printk ("\npsi240i error Device Status: %X\n", status)); + SCpnt->result = DecodeError (shost, status); + SCpnt->scsi_done (SCpnt); + } +/**************************************************************** + * Name: Psi240i_QueueCommand + * + * Description: Process a queued command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * done - Pointer to done function to call. + * + * Returns: Status code. + * + ****************************************************************/ +int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) + { + UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB + PADAPTER240I padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure + POUR_DEVICE pdev = &padapter->device[SCpnt->target];// Pointer to device information + UCHAR rc; // command return code + + SCpnt->scsi_done = done; + padapter->ide.ide.ides.spigot = pdev->spigot; + padapter->buffer = SCpnt->request_buffer; + if (done) + { + if ( !pdev->device || SCpnt->lun ) + { + SCpnt->result = DID_BAD_TARGET << 16; + done (SCpnt); + return 0; + } + } + else + { + printk("psi240i_queuecommand: %02X: done can't be NULL\n", *cdb); + return 0; + } + + switch ( *cdb ) + { + case SCSIOP_INQUIRY: // inquiry CDB + { + padapter->ide.ide.ide[6] = pdev->byte6; + padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY; + break; + } + + case SCSIOP_TEST_UNIT_READY: // test unit ready CDB + SCpnt->result = DID_OK << 16; + done (SCpnt); + return 0; + + case SCSIOP_READ_CAPACITY: // read capctiy CDB + { + PREAD_CAPACITY_DATA pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer; + + pdata->blksiz = 0x20000; + XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks); + SCpnt->result = DID_OK << 16; + done (SCpnt); + return 0; + } + + case SCSIOP_VERIFY: // verify CDB + *(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]); + padapter->ide.ide.ide[6] |= pdev->byte6; + padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8)); + padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY; + break; + + case SCSIOP_READ: // read10 CDB + padapter->startSector = XSCSI2LONG (&cdb[2]); + padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8); + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE; + break; + + case SCSIOP_READ6: // read6 CDB + padapter->startSector = SCSI2LONG (&cdb[1]); + padapter->sectorCount = cdb[4]; + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE; + break; + + case SCSIOP_WRITE: // write10 CDB + padapter->startSector = XSCSI2LONG (&cdb[2]); + padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8); + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE; + break; + case SCSIOP_WRITE6: // write6 CDB + padapter->startSector = SCSI2LONG (&cdb[1]); + padapter->sectorCount = cdb[4]; + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE; + break; + + default: + DEB (printk ("psi240i_queuecommand: Unsupported command %02X\n", *cdb)); + SCpnt->result = DID_ERROR << 16; + done (SCpnt); + return 0; + } + + padapter->SCpnt = SCpnt; // Save this command data + + rc = IdeCmd (padapter); + if ( rc ) + { + padapter->expectingIRQ = 0; + DEB (printk ("psi240i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd)); + SCpnt->result = DID_ERROR << 16; + done (SCpnt); + return 0; + } + DEB (printk("psi240i_queuecommand: %02X, %02X now waiting for interrupt ", *cdb, padapter->ide.ide.ides.cmd)); + return 0; + } + +static void internal_done(Scsi_Cmnd * SCpnt) + { + SCpnt->SCp.Status++; + } +/**************************************************************** + * Name: Psi240i_Command + * + * Description: Process a command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Status code. + * + ****************************************************************/ +int Psi240i_Command (Scsi_Cmnd *SCpnt) + { + DEB(printk("psi240i_command: ..calling psi240i_queuecommand\n")); + + Psi240i_QueueCommand (SCpnt, internal_done); + + SCpnt->SCp.Status = 0; + while (!SCpnt->SCp.Status) + barrier (); + return SCpnt->result; + } +/*************************************************************************** + * Name: ReadChipMemory + * + * Description: Read information from controller memory. + * + * Parameters: psetup - Pointer to memory image of setup information. + * base - base address of memory. + * length - lenght of data space in bytes. + * port - I/O address of data port. + * + * Returns: Nothing. + * + **************************************************************************/ +void ReadChipMemory (void *pdata, USHORT base, USHORT length, USHORT port) + { + USHORT z, zz; + UCHAR *pd = (UCHAR *)pdata; + outb_p (SEL_NONE, port + REG_SEL_FAIL); // setup data port + zz = 0; + while ( zz < length ) + { + outw_p (base, port + REG_ADDRESS); // setup address + + for ( z = 0; z < 8; z++ ) + { + if ( (zz + z) < length ) + *pd++ = inb_p (port + z); // read data byte + } + zz += 8; + base += 8; + } + } +/**************************************************************** + * Name: Psi240i_Detect + * + * Description: Detect and initialize our boards. + * + * Parameters: tpnt - Pointer to SCSI host template structure. + * + * Returns: Number of adapters found. + * + ****************************************************************/ +int Psi240i_Detect (Scsi_Host_Template *tpnt) + { + int board; + int count = 0; + int unit; + int z; + USHORT port; + CHIP_CONFIG_N chipConfig; + CHIP_DEVICE_N chipDevice[8]; + struct Scsi_Host *pshost; + ULONG flags; + + for ( board = 0; board < 6; board++ ) // scan for I/O ports + { + port = portAddr[board]; // get base address to test + if ( check_region (port, 16) ) // test for I/O addresses available + continue; // nope + if ( inb_p (port + REG_FAIL) != CHIP_ID ) // do the first test for likley hood that it is us + continue; + outb_p (SEL_NONE, port + REG_SEL_FAIL); // setup EEPROM/RAM access + outw (0, port + REG_ADDRESS); // setup EEPROM address zero + if ( inb_p (port) != 0x55 ) // test 1st byte + continue; // nope + if ( inb_p (port + 1) != 0xAA ) // test 2nd byte + continue; // nope + + // at this point our board is found and can be accessed. Now we need to initialize + // our informatation and register with the kernel. + + + ReadChipMemory (&chipConfig, CHIP_CONFIG, sizeof (chipConfig), port); + ReadChipMemory (&chipDevice, CHIP_DEVICE, sizeof (chipDevice), port); + ReadChipMemory (&ChipSetup, CHIP_EEPROM_DATA, sizeof (ChipSetup), port); + + if ( !chipConfig.numDrives ) // if no devices on this board + continue; + + pshost = scsi_register (tpnt, sizeof(ADAPTER240I)); + + save_flags (flags); + cli (); + if ( request_irq (chipConfig.irq, Irq_Handler, 0, "psi240i", NULL) ) + { + printk ("Unable to allocate IRQ for PSI-240I controller.\n"); + restore_flags (flags); + goto unregister; + } + + PsiHost[chipConfig.irq - 10] = pshost; + pshost->unique_id = port; + pshost->io_port = port; + pshost->n_io_port = 16; /* Number of bytes of I/O space used */ + pshost->irq = chipConfig.irq; + + for ( z = 0; z < 11; z++ ) // build regester address array + HOSTDATA(pshost)->ports[z] = port + z; + HOSTDATA(pshost)->ports[11] = port + REG_FAIL; + HOSTDATA(pshost)->ports[12] = port + REG_ALT_STAT; + DEB (printk ("\nPorts =")); + DEB (for (z=0;z<13;z++) printk(" %#04X",HOSTDATA(pshost)->ports[z]);); + + for ( z = 0; z < chipConfig.numDrives; ++z ) + { + unit = chipDevice[z].channel & 0x0F; + HOSTDATA(pshost)->device[unit].device = ChipSetup.setupDevice[unit].device; + HOSTDATA(pshost)->device[unit].byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0); + HOSTDATA(pshost)->device[unit].spigot = (UCHAR)(1 << (unit >> 1)); + HOSTDATA(pshost)->device[unit].sectors = ChipSetup.setupDevice[unit].sectors; + HOSTDATA(pshost)->device[unit].heads = ChipSetup.setupDevice[unit].heads; + HOSTDATA(pshost)->device[unit].cylinders = ChipSetup.setupDevice[unit].cylinders; + HOSTDATA(pshost)->device[unit].blocks = ChipSetup.setupDevice[unit].blocks; + DEB (printk ("\nHOSTDATA->device = %X", HOSTDATA(pshost)->device[unit].device)); + DEB (printk ("\n byte6 = %X", HOSTDATA(pshost)->device[unit].byte6)); + DEB (printk ("\n spigot = %X", HOSTDATA(pshost)->device[unit].spigot)); + DEB (printk ("\n sectors = %X", HOSTDATA(pshost)->device[unit].sectors)); + DEB (printk ("\n heads = %X", HOSTDATA(pshost)->device[unit].heads)); + DEB (printk ("\n cylinders = %X", HOSTDATA(pshost)->device[unit].cylinders)); + DEB (printk ("\n blocks = %lX", HOSTDATA(pshost)->device[unit].blocks)); + } + + restore_flags (flags); + printk("\nPSI-240I EIDE CONTROLLER: at I/O = %x IRQ = %d\n", port, chipConfig.irq); + printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n"); + count++; + continue; + +unregister:; + scsi_unregister (pshost); + } + return count; + } +/**************************************************************** + * Name: Psi240i_Abort + * + * Description: Process the Abort command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Allways snooze. + * + ****************************************************************/ +int Psi240i_Abort (Scsi_Cmnd *SCpnt) + { + DEB (printk ("psi240i_abort\n")); + return SCSI_ABORT_SNOOZE; + } +/**************************************************************** + * Name: Psi240i_Reset + * + * Description: Process the Reset command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * flags - Flags about the reset command + * + * Returns: No active command at this time, so this means + * that each time we got some kind of response the + * last time through. Tell the mid-level code to + * request sense information in order to decide what + * to do next. + * + ****************************************************************/ +int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) + { + return SCSI_RESET_PUNT; + } + +#include "sd.h" + +/**************************************************************** + * Name: Psi240i_BiosParam + * + * Description: Process the biosparam request from the SCSI manager to + * return C/H/S data. + * + * Parameters: disk - Pointer to SCSI disk structure. + * dev - Major/minor number from kernel. + * geom - Pointer to integer array to place geometry data. + * + * Returns: zero. + * + ****************************************************************/ +int Psi240i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[]) + { + POUR_DEVICE pdev; + + pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]); + + geom[0] = pdev->heads; + geom[1] = pdev->sectors; + geom[2] = pdev->cylinders; + return 0; + } + + +#ifdef MODULE +/* Eventually this will go into an include file, but this will be later */ +Scsi_Host_Template driver_template = PSI240I; + +#include "scsi_module.c" +#endif + diff -urN linux-2.0.37-pre8/drivers/scsi/psi240i.h linux-2.0.37-pre9/drivers/scsi/psi240i.h --- linux-2.0.37-pre8/drivers/scsi/psi240i.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.37-pre9/drivers/scsi/psi240i.h 2003-08-15 15:04:42.000000000 -0700 @@ -0,0 +1,344 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: psi240i.h + * + * Description: Header file for the SCSI driver for the PSI240I + * EIDE interface card. + * + *-M*************************************************************************/ +#ifndef _PSI240I_H +#define _PSI240I_H + +#include +#include + +#ifndef PSI_EIDE_SCSIOP +#define PSI_EIDE_SCSIOP 1 + +/************************************************/ +/* Some defines that we like */ +/************************************************/ +#define CHAR char +#define UCHAR unsigned char +#define SHORT short +#define USHORT unsigned short +#define BOOL unsigned short +#define LONG long +#define ULONG unsigned long +#define VOID void + +/************************************************/ +/* Timeout konstants */ +/************************************************/ +#define TIMEOUT_READY 10 // 100 mSec +#define TIMEOUT_DRQ 40 // 400 mSec + +/************************************************/ +/* Misc. macros */ +/************************************************/ +#define ANY2SCSI(up, p) \ +((UCHAR *)up)[0] = (((ULONG)(p)) >> 8); \ +((UCHAR *)up)[1] = ((ULONG)(p)); + +#define SCSI2LONG(up) \ +( (((long)*(((UCHAR *)up))) << 16) \ ++ (((long)(((UCHAR *)up)[1])) << 8) \ ++ ((long)(((UCHAR *)up)[2])) ) + +#define XANY2SCSI(up, p) \ +((UCHAR *)up)[0] = ((long)(p)) >> 24; \ +((UCHAR *)up)[1] = ((long)(p)) >> 16; \ +((UCHAR *)up)[2] = ((long)(p)) >> 8; \ +((UCHAR *)up)[3] = ((long)(p)); + +#define XSCSI2LONG(up) \ +( (((long)(((UCHAR *)up)[0])) << 24) \ ++ (((long)(((UCHAR *)up)[1])) << 16) \ ++ (((long)(((UCHAR *)up)[2])) << 8) \ ++ ((long)(((UCHAR *)up)[3])) ) + +/************************************************/ +/* SCSI CDB operation codes */ +/************************************************/ +#define SCSIOP_TEST_UNIT_READY 0x00 +#define SCSIOP_REZERO_UNIT 0x01 +#define SCSIOP_REWIND 0x01 +#define SCSIOP_REQUEST_BLOCK_ADDR 0x02 +#define SCSIOP_REQUEST_SENSE 0x03 +#define SCSIOP_FORMAT_UNIT 0x04 +#define SCSIOP_READ_BLOCK_LIMITS 0x05 +#define SCSIOP_REASSIGN_BLOCKS 0x07 +#define SCSIOP_READ6 0x08 +#define SCSIOP_RECEIVE 0x08 +#define SCSIOP_WRITE6 0x0A +#define SCSIOP_PRINT 0x0A +#define SCSIOP_SEND 0x0A +#define SCSIOP_SEEK6 0x0B +#define SCSIOP_TRACK_SELECT 0x0B +#define SCSIOP_SLEW_PRINT 0x0B +#define SCSIOP_SEEK_BLOCK 0x0C +#define SCSIOP_PARTITION 0x0D +#define SCSIOP_READ_REVERSE 0x0F +#define SCSIOP_WRITE_FILEMARKS 0x10 +#define SCSIOP_FLUSH_BUFFER 0x10 +#define SCSIOP_SPACE 0x11 +#define SCSIOP_INQUIRY 0x12 +#define SCSIOP_VERIFY6 0x13 +#define SCSIOP_RECOVER_BUF_DATA 0x14 +#define SCSIOP_MODE_SELECT 0x15 +#define SCSIOP_RESERVE_UNIT 0x16 +#define SCSIOP_RELEASE_UNIT 0x17 +#define SCSIOP_COPY 0x18 +#define SCSIOP_ERASE 0x19 +#define SCSIOP_MODE_SENSE 0x1A +#define SCSIOP_START_STOP_UNIT 0x1B +#define SCSIOP_STOP_PRINT 0x1B +#define SCSIOP_LOAD_UNLOAD 0x1B +#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C +#define SCSIOP_SEND_DIAGNOSTIC 0x1D +#define SCSIOP_MEDIUM_REMOVAL 0x1E +#define SCSIOP_READ_CAPACITY 0x25 +#define SCSIOP_READ 0x28 +#define SCSIOP_WRITE 0x2A +#define SCSIOP_SEEK 0x2B +#define SCSIOP_LOCATE 0x2B +#define SCSIOP_WRITE_VERIFY 0x2E +#define SCSIOP_VERIFY 0x2F +#define SCSIOP_SEARCH_DATA_HIGH 0x30 +#define SCSIOP_SEARCH_DATA_EQUAL 0x31 +#define SCSIOP_SEARCH_DATA_LOW 0x32 +#define SCSIOP_SET_LIMITS 0x33 +#define SCSIOP_READ_POSITION 0x34 +#define SCSIOP_SYNCHRONIZE_CACHE 0x35 +#define SCSIOP_COMPARE 0x39 +#define SCSIOP_COPY_COMPARE 0x3A +#define SCSIOP_WRITE_DATA_BUFF 0x3B +#define SCSIOP_READ_DATA_BUFF 0x3C +#define SCSIOP_CHANGE_DEFINITION 0x40 +#define SCSIOP_READ_SUB_CHANNEL 0x42 +#define SCSIOP_READ_TOC 0x43 +#define SCSIOP_READ_HEADER 0x44 +#define SCSIOP_PLAY_AUDIO 0x45 +#define SCSIOP_PLAY_AUDIO_MSF 0x47 +#define SCSIOP_PLAY_TRACK_INDEX 0x48 +#define SCSIOP_PLAY_TRACK_RELATIVE 0x49 +#define SCSIOP_PAUSE_RESUME 0x4B +#define SCSIOP_LOG_SELECT 0x4C +#define SCSIOP_LOG_SENSE 0x4D +#define SCSIOP_MODE_SELECT10 0x55 +#define SCSIOP_MODE_SENSE10 0x5A +#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6 +#define SCSIOP_MECHANISM_STATUS 0xBD +#define SCSIOP_READ_CD 0xBE + +// IDE command definitions +#define IDE_COMMAND_ATAPI_RESET 0x08 +#define IDE_COMMAND_READ 0x20 +#define IDE_COMMAND_WRITE 0x30 +#define IDE_COMMAND_RECALIBRATE 0x10 +#define IDE_COMMAND_SEEK 0x70 +#define IDE_COMMAND_SET_PARAMETERS 0x91 +#define IDE_COMMAND_VERIFY 0x40 +#define IDE_COMMAND_ATAPI_PACKET 0xA0 +#define IDE_COMMAND_ATAPI_IDENTIFY 0xA1 +#define IDE_CMD_READ_MULTIPLE 0xC4 +#define IDE_CMD_WRITE_MULTIPLE 0xC5 +#define IDE_CMD_SET_MULTIPLE 0xC6 +#define IDE_COMMAND_WRITE_DMA 0xCA +#define IDE_COMMAND_READ_DMA 0xC8 +#define IDE_COMMAND_IDENTIFY 0xEC + +// IDE status definitions +#define IDE_STATUS_ERROR 0x01 +#define IDE_STATUS_INDEX 0x02 +#define IDE_STATUS_CORRECTED_ERROR 0x04 +#define IDE_STATUS_DRQ 0x08 +#define IDE_STATUS_DSC 0x10 +#define IDE_STATUS_WRITE_FAULT 0x20 +#define IDE_STATUS_DRDY 0x40 +#define IDE_STATUS_BUSY 0x80 + +// IDE error definitions +#define IDE_ERROR_AMNF 0x01 +#define IDE_ERROR_TKONF 0x02 +#define IDE_ERROR_ABRT 0x04 +#define IDE_ERROR_MCR 0x08 +#define IDE_ERROR_IDFN 0x10 +#define IDE_ERROR_MC 0x20 +#define IDE_ERROR_UNC 0x40 +#define IDE_ERROR_BBK 0x80 + +// IDE interface structure +typedef struct _IDE_STRUCT + { + union + { + UCHAR ide[9]; + struct + { + USHORT data; + UCHAR sectors; + UCHAR lba[4]; + UCHAR cmd; + UCHAR spigot; + } ides; + } ide; + } IDE_STRUCT; + +// SCSI read capacity structure +typedef struct _READ_CAPACITY_DATA + { + ULONG blks; /* total blocks (converted to little endian) */ + ULONG blksiz; /* size of each (converted to little endian) */ + } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA; + +// SCSI inquiry data +typedef struct _INQUIRYDATA + { + UCHAR DeviceType :5; + UCHAR DeviceTypeQualifier :3; + UCHAR DeviceTypeModifier :7; + UCHAR RemovableMedia :1; + UCHAR Versions; + UCHAR ResponseDataFormat; + UCHAR AdditionalLength; + UCHAR Reserved[2]; + UCHAR SoftReset :1; + UCHAR CommandQueue :1; + UCHAR Reserved2 :1; + UCHAR LinkedCommands :1; + UCHAR Synchronous :1; + UCHAR Wide16Bit :1; + UCHAR Wide32Bit :1; + UCHAR RelativeAddressing :1; + UCHAR VendorId[8]; + UCHAR ProductId[16]; + UCHAR ProductRevisionLevel[4]; + UCHAR VendorSpecific[20]; + UCHAR Reserved3[40]; + } INQUIRYDATA, *PINQUIRYDATA; + +// IDE IDENTIFY data +typedef struct _IDENTIFY_DATA + { + USHORT GeneralConfiguration; // 00 + USHORT NumberOfCylinders; // 02 + USHORT Reserved1; // 04 + USHORT NumberOfHeads; // 06 + USHORT UnformattedBytesPerTrack; // 08 + USHORT UnformattedBytesPerSector; // 0A + USHORT SectorsPerTrack; // 0C + USHORT VendorUnique1[3]; // 0E + USHORT SerialNumber[10]; // 14 + USHORT BufferType; // 28 + USHORT BufferSectorSize; // 2A + USHORT NumberOfEccBytes; // 2C + USHORT FirmwareRevision[4]; // 2E + USHORT ModelNumber[20]; // 36 + UCHAR MaximumBlockTransfer; // 5E + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 + USHORT Capabilities; // 62 + USHORT Reserved2; // 64 + UCHAR VendorUnique3; // 66 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:1; // 6A + USHORT Reserved3:15; + USHORT NumberOfCurrentCylinders; // 6C + USHORT NumberOfCurrentHeads; // 6E + USHORT CurrentSectorsPerTrack; // 70 + ULONG CurrentSectorCapacity; // 72 + USHORT Reserved4[197]; // 76 + } IDENTIFY_DATA, *PIDENTIFY_DATA; + +// Identify data without the Reserved4. +typedef struct _IDENTIFY_DATA2 { + USHORT GeneralConfiguration; // 00 + USHORT NumberOfCylinders; // 02 + USHORT Reserved1; // 04 + USHORT NumberOfHeads; // 06 + USHORT UnformattedBytesPerTrack; // 08 + USHORT UnformattedBytesPerSector; // 0A + USHORT SectorsPerTrack; // 0C + USHORT VendorUnique1[3]; // 0E + USHORT SerialNumber[10]; // 14 + USHORT BufferType; // 28 + USHORT BufferSectorSize; // 2A + USHORT NumberOfEccBytes; // 2C + USHORT FirmwareRevision[4]; // 2E + USHORT ModelNumber[20]; // 36 + UCHAR MaximumBlockTransfer; // 5E + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 + USHORT Capabilities; // 62 + USHORT Reserved2; // 64 + UCHAR VendorUnique3; // 66 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:1; // 6A + USHORT Reserved3:15; + USHORT NumberOfCurrentCylinders; // 6C + USHORT NumberOfCurrentHeads; // 6E + USHORT CurrentSectorsPerTrack; // 70 + ULONG CurrentSectorCapacity; // 72 + } IDENTIFY_DATA2, *PIDENTIFY_DATA2; + +#endif // PSI_EIDE_SCSIOP + +// function prototypes +int Psi240i_Detect (Scsi_Host_Template *tpnt); +int Psi240i_Command (Scsi_Cmnd *SCpnt); +int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); +int Psi240i_Abort (Scsi_Cmnd *SCpnt); +int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int flags); +int Psi240i_BiosParam (Disk *disk, kdev_t dev, int geom[]); + +#ifndef NULL + #define NULL 0 +#endif + +extern struct proc_dir_entry Proc_Scsi_Psi240i; + +#define PSI240I { NULL, NULL, \ + &Proc_Scsi_Psi240i,/* proc_dir_entry */ \ + NULL, \ + "PSI-240I EIDE Disk Controller", \ + Psi240i_Detect, \ + NULL, \ + NULL, \ + Psi240i_Command, \ + Psi240i_QueueCommand, \ + Psi240i_Abort, \ + Psi240i_Reset, \ + NULL, \ + Psi240i_BiosParam, \ + 1, \ + -1, \ + SG_NONE, \ + 1, \ + 0, \ + 0, \ + DISABLE_CLUSTERING } + +#endif diff -urN linux-2.0.37-pre8/drivers/scsi/psi_chip.h linux-2.0.37-pre9/drivers/scsi/psi_chip.h --- linux-2.0.37-pre8/drivers/scsi/psi_chip.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.37-pre9/drivers/scsi/psi_chip.h 2003-08-15 15:04:42.000000000 -0700 @@ -0,0 +1,194 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: psi_chip.h + * + * Description: This file contains the interface defines and + * error codes. + * + *-M*************************************************************************/ +#ifndef PSI_CHIP +#define PSI_CHIP + +/************************************************/ +/* Misc konstants */ +/************************************************/ +#define CHIP_MAXDRIVES 8 + +/************************************************/ +/* Chip I/O addresses */ +/************************************************/ +#define CHIP_ADRS_0 0x0130 +#define CHIP_ADRS_1 0x0150 +#define CHIP_ADRS_2 0x0190 +#define CHIP_ADRS_3 0x0210 +#define CHIP_ADRS_4 0x0230 +#define CHIP_ADRS_5 0x0250 + +/************************************************/ +/* EEPROM locations */ +/************************************************/ +#define CHIP_EEPROM_BIOS 0x0000 // BIOS base address +#define CHIP_EEPROM_DATA 0x2000 // SETUP data base address +#define CHIP_EEPROM_FACTORY 0x2400 // FACTORY data base address +#define CHIP_EEPROM_SETUP 0x3000 // SETUP PROGRAM base address + +#define CHIP_EEPROM_SIZE 32768U // size of the entire EEPROM +#define CHIP_EEPROM_BIOS_SIZE 8192 // size of the BIOS in bytes +#define CHIP_EEPROM_DATA_SIZE 4096 // size of factory, setup, log data block in bytes +#define CHIP_EEPROM_SETUP_SIZE 20480U // size of the setup program in bytes + +/************************************************/ +/* Chip Interrupts */ +/************************************************/ +#define CHIP_IRQ_10 0x72 +#define CHIP_IRQ_11 0x73 +#define CHIP_IRQ_12 0x74 + +/************************************************/ +/* Chip Setup addresses */ +/************************************************/ +#define CHIP_SETUP_BASE 0x0000C000L + +/************************************************/ +/* Chip Register address offsets */ +/************************************************/ +#define REG_DATA 0x00 +#define REG_ERROR 0x01 +#define REG_SECTOR_COUNT 0x02 +#define REG_LBA_0 0x03 +#define REG_LBA_8 0x04 +#define REG_LBA_16 0x05 +#define REG_LBA_24 0x06 +#define REG_STAT_CMD 0x07 +#define REG_SEL_FAIL 0x08 +#define REG_IRQ_STATUS 0x09 +#define REG_ADDRESS 0x0A +#define REG_FAIL 0x0C +#define REG_ALT_STAT 0x0E +#define REG_DRIVE_ADRS 0x0F + +/************************************************/ +/* Chip RAM locations */ +/************************************************/ +#define CHIP_DEVICE 0x8000 +#define CHIP_DEVICE_0 0x8000 +#define CHIP_DEVICE_1 0x8008 +#define CHIP_DEVICE_2 0x8010 +#define CHIP_DEVICE_3 0x8018 +#define CHIP_DEVICE_4 0x8020 +#define CHIP_DEVICE_5 0x8028 +#define CHIP_DEVICE_6 0x8030 +#define CHIP_DEVICE_7 0x8038 +typedef struct + { + UCHAR channel; // channel of this device (0-8). + UCHAR spt; // Sectors Per Track. + ULONG spc; // Sectors Per Cylinder. + } CHIP_DEVICE_N; + +#define CHIP_CONFIG 0x8100 // address of boards configuration. +typedef struct + { + UCHAR irq; // interrupt request channel number + UCHAR numDrives; // Number of accessable drives + UCHAR fastFormat; // Boolean for fast format enable + } CHIP_CONFIG_N; + +#define CHIP_MAP 0x8108 // eight byte device type map. + + +#define CHIP_RAID 0x8120 // array of RAID signature structures and LBA +#define CHIP_RAID_1 0x8120 +#define CHIP_RAID_2 0x8130 +#define CHIP_RAID_3 0x8140 +#define CHIP_RAID_4 0x8150 + +/************************************************/ +/* Chip Register Masks */ +/************************************************/ +#define CHIP_ID 0x7B +#define SEL_RAM 0x8000 +#define MASK_FAIL 0x80 + +/************************************************/ +/* Chip cable select bits */ +/************************************************/ +#define SECTORSXFER 8 + +/************************************************/ +/* Chip cable select bits */ +/************************************************/ +#define SEL_NONE 0x00 +#define SEL_1 0x01 +#define SEL_2 0x02 +#define SEL_3 0x04 +#define SEL_4 0x08 + +/************************************************/ +/* Programmable Interrupt Controller*/ +/************************************************/ +#define PIC1 0x20 // first 8259 base port address +#define PIC2 0xA0 // second 8259 base port address +#define INT_OCW1 1 // Operation Control Word 1: IRQ mask +#define EOI 0x20 // non-specific end-of-interrupt + +/************************************************/ +/* Device/Geometry controls */ +/************************************************/ +#define GEOMETRY_NONE 0x0 // No device +#define GEOMETRY_AUTO 0x1 // Geometry set automatically +#define GEOMETRY_USER 0x2 // User supplied geometry + +#define DEVICE_NONE 0x0 // No device present +#define DEVICE_INACTIVE 0x1 // device present but not registered active +#define DEVICE_ATAPI 0x2 // ATAPI device (CD_ROM, Tape, Etc...) +#define DEVICE_DASD_NONLBA 0x3 // Non LBA incompatible device +#define DEVICE_DASD_LBA 0x4 // LBA compatible device + +/************************************************/ +/* Setup Structure Definitions */ +/************************************************/ +typedef struct // device setup parameters + { + UCHAR geometryControl; // geometry control flags + UCHAR device; // device code + USHORT sectors; // number of sectors per track + USHORT heads; // number of heads + USHORT cylinders; // number of cylinders for this device + ULONG blocks; // number of blocks on device + USHORT spare1; + USHORT spare2; + } SETUP_DEVICE, *PSETUP_DEVICE; + +typedef struct // master setup structure + { + USHORT startupDelay; + USHORT promptBIOS; + USHORT fastFormat; + USHORT spare2; + USHORT spare3; + USHORT spare4; + USHORT spare5; + USHORT spare6; + SETUP_DEVICE setupDevice[8]; + } SETUP, *PSETUP; + +#endif \ No newline at end of file diff -urN linux-2.0.37-pre8/drivers/scsi/psi_dale.h linux-2.0.37-pre9/drivers/scsi/psi_dale.h --- linux-2.0.37-pre8/drivers/scsi/psi_dale.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.37-pre9/drivers/scsi/psi_dale.h 2003-08-15 15:04:42.000000000 -0700 @@ -0,0 +1,187 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: psi_dale.h + * + * Description: This file contains the interface defines and + * error codes. + * + *-M*************************************************************************/ + +#ifndef PSI_DALE +#define PSI_DALE + +/************************************************/ +/* Dale PCI setup */ +/************************************************/ +#define VENDOR_PSI 0x1256 +#define DEVICE_DALE_1 0x4401 /* 'D1' */ + +/************************************************/ +/* Misc konstants */ +/************************************************/ +#define DALE_MAXDRIVES 4 +#define SECTORSXFER 8 +#define BYTES_PER_SECTOR 512 +#define DEFAULT_TIMING_MODE 5 + +/************************************************/ +/* EEPROM locations */ +/************************************************/ +#define DALE_FLASH_PAGE_SIZE 128 // number of bytes per page +#define DALE_FLASH_SIZE 65536L + +#define DALE_FLASH_BIOS 0x00080000L // BIOS base address +#define DALE_FLASH_SETUP 0x00088000L // SETUP PROGRAM base address offset from BIOS +#define DALE_FLASH_RAID 0x00088400L // RAID signature storage +#define DALE_FLASH_FACTORY 0x00089000L // FACTORY data base address offset from BIOS + +#define DALE_FLASH_BIOS_SIZE 32768U // size of FLASH BIOS REGION + +/************************************************/ +/* DALE Register address offsets */ +/************************************************/ +#define REG_DATA 0x80 +#define REG_ERROR 0x84 +#define REG_SECTOR_COUNT 0x88 +#define REG_LBA_0 0x8C +#define REG_LBA_8 0x90 +#define REG_LBA_16 0x94 +#define REG_LBA_24 0x98 +#define REG_STAT_CMD 0x9C +#define REG_STAT_SEL 0xA0 +#define REG_FAIL 0xB0 +#define REG_ALT_STAT 0xB8 +#define REG_DRIVE_ADRS 0xBC + +#define DALE_DATA_SLOW 0x00040000L +#define DALE_DATA_MODE2 0x00040000L +#define DALE_DATA_MODE3 0x00050000L +#define DALE_DATA_MODE4 0x00060000L +#define DALE_DATA_MODE4P 0x00070000L + +#define RTR_LOCAL_RANGE 0x000 +#define RTR_LOCAL_REMAP 0x004 +#define RTR_EXP_RANGE 0x010 +#define RTR_EXP_REMAP 0x014 +#define RTR_REGIONS 0x018 +#define RTR_DM_MASK 0x01C +#define RTR_DM_LOCAL_BASE 0x020 +#define RTR_DM_IO_BASE 0x024 +#define RTR_DM_PCI_REMAP 0x028 +#define RTR_DM_IO_CONFIG 0x02C +#define RTR_MAILBOX 0x040 +#define RTR_LOCAL_DOORBELL 0x060 +#define RTR_PCI_DOORBELL 0x064 +#define RTR_INT_CONTROL_STATUS 0x068 +#define RTR_EEPROM_CONTROL_STATUS 0x06C + +#define RTL_DMA0_MODE 0x00 +#define RTL_DMA0_PCI_ADDR 0x04 +#define RTL_DMA0_LOCAL_ADDR 0x08 +#define RTL_DMA0_COUNT 0x0C +#define RTL_DMA0_DESC_PTR 0x10 +#define RTL_DMA1_MODE 0x14 +#define RTL_DMA1_PCI_ADDR 0x18 +#define RTL_DMA1_LOCAL_ADDR 0x1C +#define RTL_DMA1_COUNT 0x20 +#define RTL_DMA1_DESC_PTR 0x24 +#define RTL_DMA_COMMAND_STATUS 0x28 +#define RTL_DMA_ARB0 0x2C +#define RTL_DMA_ARB1 0x30 + +/************************************************/ +/* Dale Scratchpad locations */ +/************************************************/ +#define DALE_CHANNEL_DEVICE_0 0 // device channel locations +#define DALE_CHANNEL_DEVICE_1 1 +#define DALE_CHANNEL_DEVICE_2 2 +#define DALE_CHANNEL_DEVICE_3 3 + +#define DALE_SCRATH_DEVICE_0 4 // device type codes +#define DALE_SCRATH_DEVICE_1 5 +#define DALE_SCRATH_DEVICE_2 6 +#define DALE_SCRATH_DEVICE_3 7 + +#define DALE_RAID_0_STATUS 8 +#define DALE_RAID_1_STATUS 9 + +#define DALE_TIMING_MODE 12 // bus master timing mode (2, 3, 4, 5) +#define DALE_NUM_DRIVES 13 // number of addressable drives on this board +#define DALE_RAID_ON 14 // RAID status On +#define DALE_LAST_ERROR 15 // Last error code from BIOS + +/************************************************/ +/* Dale cable select bits */ +/************************************************/ +#define SEL_NONE 0x00 +#define SEL_1 0x01 +#define SEL_2 0x02 + +/************************************************/ +/* Programmable Interrupt Controller */ +/************************************************/ +#define PIC1 0x20 // first 8259 base port address +#define PIC2 0xA0 // second 8259 base port address +#define INT_OCW1 1 // Operation Control Word 1: IRQ mask +#define EOI 0x20 // non-specific end-of-interrupt + +/************************************************/ +/* Device/Geometry controls */ +/************************************************/ +#define GEOMETRY_NONE 0x0 // No device +#define GEOMETRY_SET 0x1 // Geometry set +#define GEOMETRY_LBA 0x2 // Geometry set in default LBA mode +#define GEOMETRY_PHOENIX 0x3 // Geometry set in Pheonix BIOS compatibility mode + +#define DEVICE_NONE 0x0 // No device present +#define DEVICE_INACTIVE 0x1 // device present but not registered active +#define DEVICE_ATAPI 0x2 // ATAPI device (CD_ROM, Tape, Etc...) +#define DEVICE_DASD_NONLBA 0x3 // Non LBA incompatible device +#define DEVICE_DASD_LBA 0x4 // LBA compatible device + +/************************************************/ +/* Setup Structure Definitions */ +/************************************************/ +typedef struct // device setup parameters + { + UCHAR geometryControl; // geometry control flags + UCHAR device; // device code + USHORT sectors; // number of sectors per track + USHORT heads; // number of heads + USHORT cylinders; // number of cylinders for this device + ULONG blocks; // number of blocks on device + ULONG realCapacity; // number of real blocks on this device for drive changed testing + } SETUP_DEVICE, *PSETUP_DEVICE; + +typedef struct // master setup structure + { + USHORT startupDelay; + BOOL promptBIOS; + BOOL fastFormat; + BOOL shareInterrupt; + BOOL rebootRebuil; + USHORT timingMode; + USHORT spare5; + USHORT spare6; + SETUP_DEVICE setupDevice[4]; + } SETUP, *PSETUP; + +#endif diff -urN linux-2.0.37-pre8/drivers/scsi/psi_roy.h linux-2.0.37-pre9/drivers/scsi/psi_roy.h --- linux-2.0.37-pre8/drivers/scsi/psi_roy.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.37-pre9/drivers/scsi/psi_roy.h 2003-08-15 15:04:42.000000000 -0700 @@ -0,0 +1,337 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: psi_roy.h + * + * Description: This file contains the host interface command and + * error codes. + * + *-M*************************************************************************/ + +#ifndef ROY_HOST +#define ROY_HOST + +/************************************************/ +/* PCI setup */ +/************************************************/ +#define VENDOR_PSI 0x1256 +#define DEVICE_ROY_1 0x5201 /* 'R1' */ + +/************************************************/ +/* controller constants */ +/************************************************/ +#define MAXADAPTER 4 // Increase this and the sizes of the arrays below, if you need more. +#define MAX_BUS 2 +#define MAX_UNITS 16 +#define TIMEOUT_COMMAND 30 // number of jiffies for command busy timeout + +/************************************************/ +/* I/O address offsets */ +/************************************************/ +#define RTR_MAILBOX 0x040 +#define RTR_LOCAL_DOORBELL 0x060 +#define RTR_PCI_DOORBELL 0x064 + +/************************************************/ +/* */ +/* Host command codes */ +/* */ +/************************************************/ +#define CMD_READ_CHS 0x01 /* read sectors as specified (CHS mode) */ +#define CMD_READ 0x02 /* read sectors as specified (RBA mode) */ +#define CMD_READ_SG 0x03 /* read sectors using scatter/gather list */ +#define CMD_WRITE_CHS 0x04 /* write sectors as specified (CHS mode) */ +#define CMD_WRITE 0x05 /* write sectors as specified (RBA mode) */ +#define CMD_WRITE_SG 0x06 /* write sectors using scatter/gather list (LBA mode) */ +#define CMD_READ_CHS_SG 0x07 /* read sectors using scatter/gather list (CHS mode) */ +#define CMD_WRITE_CHS_SG 0x08 /* write sectors using scatter/gather list (CHS mode) */ +#define CMD_VERIFY_CHS 0x09 /* verify data on sectors as specified (CHS mode) */ +#define CMD_VERIFY 0x0A /* verify data on sectors as specified (RBA mode) */ +#define CMD_DASD_CDB 0x0B /* process CDB for a DASD device */ +#define CMD_DASD_CDB_SG 0x0C /* process CDB for a DASD device with scatter/gather */ + +#define CMD_READ_ABS 0x10 /* read absolute disk */ +#define CMD_WRITE_ABS 0x11 /* write absolute disk */ +#define CMD_VERIFY_ABS 0x12 /* verify absolute disk */ +#define CMD_TEST_READY 0x13 /* test unit ready and return status code */ +#define CMD_LOCK_DOOR 0x14 /* lock device door */ +#define CMD_UNLOCK_DOOR 0x15 /* unlock device door */ +#define CMD_EJECT_MEDIA 0x16 /* eject the media */ +#define CMD_UPDATE_CAP 0x17 /* update capacity information */ +#define CMD_TEST_PRIV 0x18 /* test and setup private format media */ + + +#define CMD_SCSI_THRU 0x30 /* SCSI pass through CDB */ +#define CMD_SCSI_THRU_SG 0x31 /* SCSI pass through CDB with scatter/gather */ +#define CMD_SCSI_REQ_SENSE 0x32 /* SCSI pass through request sense after check condition */ + +#define CMD_DASD_RAID_RQ 0x35 /* request DASD RAID drive data */ +#define CMD_DASD_RAID_RQ0 0x31 /* byte 1 subcommand to query for RAID 0 informatation */ +#define CMD_DASD_RAID_RQ1 0x32 /* byte 1 subcommand to query for RAID 1 informatation */ +#define CMD_DASD_RAID_RQ5 0x33 /* byte 1 subcommand to query for RAID 5 informatation */ + +#define CMD_DASD_SCSI_INQ 0x36 /* do DASD inquire and return in SCSI format */ +#define CMD_DASD_CAP 0x37 /* read DASD capacity */ +#define CMD_DASD_INQ 0x38 /* do DASD inquire for type data and return SCSI/EIDE inquiry */ +#define CMD_SCSI_INQ 0x39 /* do SCSI inquire */ +#define CMD_READ_SETUP 0x3A /* Get setup structures from controller */ +#define CMD_WRITE_SETUP 0x3B /* Put setup structures in controller and burn in flash */ +#define CMD_READ_CONFIG 0x3C /* Get the entire configuration and setup structures */ +#define CMD_WRITE_CONFIG 0x3D /* Put the entire configuration and setup structures in flash */ + +#define CMD_TEXT_DEVICE 0x3E /* obtain device text */ +#define CMD_TEXT_SIGNON 0x3F /* get sign on banner */ + +#define CMD_QUEUE 0x40 /* any command below this generates a queue tag interrupt to host*/ + +#define CMD_PREFETCH 0x40 /* prefetch sectors as specified */ +#define CMD_TEST_WRITE 0x41 /* Test a device for write protect */ +#define CMD_LAST_STATUS 0x42 /* get last command status and error data*/ +#define CMD_ABORT 0x43 /* abort command as specified */ +#define CMD_ERROR 0x44 /* fetch error code from a tagged op */ +#define CMD_DONE 0x45 /* done with operation */ +#define CMD_DIAGNOSTICS 0x46 /* execute controller diagnostics and wait for results */ +#define CMD_FEATURE_MODE 0x47 /* feature mode control word */ +#define CMD_DASD_INQUIRE 0x48 /* inquire as to DASD SCSI device (32 possible) */ +#define CMD_FEATURE_QUERY 0x49 /* query the feature control word */ +#define CMD_DASD_EJECT 0x4A /* Eject removable media for DASD type */ +#define CMD_DASD_LOCK 0x4B /* Lock removable media for DASD type */ +#define CMD_DASD_TYPE 0x4C /* obtain DASD device type */ +#define CMD_NUM_DEV 0x4D /* obtain the number of devices connected to the controller */ +#define CMD_GET_PARMS 0x4E /* obtain device parameters */ +#define CMD_SPECIFY 0x4F /* specify operating system for scatter/gather operations */ + +#define CMD_RAID_GET_DEV 0x50 /* read RAID device geometry */ +#define CMD_RAID_READ 0x51 /* read RAID 1 parameter block */ +#define CMD_RAID_WRITE 0x52 /* write RAID 1 parameter block */ +#define CMD_RAID_LITEUP 0x53 /* Light up the drive light for identification */ +#define CMD_RAID_REBUILD 0x54 /* issue a RAID 1 pair rebuild */ +#define CMD_RAID_MUTE 0x55 /* mute RAID failure alarm */ +#define CMD_RAID_FAIL 0x56 /* induce a RAID failure */ +#define CMD_RAID_STATUS 0x57 /* get status of RAID pair */ +#define CMD_RAID_STOP 0x58 /* stop any reconstruct in progress */ +#define CMD_RAID_START 0x59 /* start reconstruct */ +#define CMD_RAID0_READ 0x5A /* read RAID 0 parameter block */ +#define CMD_RAID0_WRITE 0x5B /* write RAID 0 parameter block */ +#define CMD_RAID5_READ 0x5C /* read RAID 5 parameter block */ +#define CMD_RAID5_WRITE 0x5D /* write RAID 5 parameter block */ + +#define CMD_ERASE_TABLES 0x5F /* erase partition table and RAID signatutures */ + +#define CMD_SCSI_GET 0x60 /* get SCSI pass through devices */ +#define CMD_SCSI_TIMEOUT 0x61 /* set SCSI pass through timeout */ +#define CMD_SCSI_ERROR 0x62 /* get SCSI pass through request sense length and residual data count */ +#define CMD_GET_SPARMS 0x63 /* get SCSI bus and user parms */ +#define CMD_SCSI_ABORT 0x64 /* abort by setting time-out to zero */ + +#define CMD_CHIRP_CHIRP 0x77 /* make a chirp chirp sound */ +#define CMD_GET_LAST_DONE 0x78 /* get tag of last done in progress */ +#define CMD_GET_FEATURES 0x79 /* get feature code and ESN */ +#define CMD_CLEAR_CACHE 0x7A /* Clear cache on specified device */ +#define CMD_BIOS_TEST 0x7B /* Test whether or not to load BIOS */ +#define CMD_WAIT_FLUSH 0x7C /* wait for cache flushed and invalidate read cache */ +#define CMD_RESET_BUS 0x7D /* reset the SCSI bus */ +#define CMD_STARTUP_QRY 0x7E /* startup in progress query */ +#define CMD_RESET 0x7F /* reset the controller */ + +#define CMD_RESTART_RESET 0x80 /* reload and restart the controller at any reset issued */ +#define CMD_SOFT_RESET 0x81 /* do a soft reset NOW! */ + +/************************************************/ +/* */ +/* Host return errors */ +/* */ +/************************************************/ +#define ERR08_TAGGED 0x80 /* doorbell error ored with tag */ + +#define ERR16_NONE 0x0000 /* no errors */ +#define ERR16_SC_COND_MET 0x0004 /* SCSI status - Condition Met */ +#define ERR16_CMD 0x0101 /* command error */ +#define ERR16_SC_CHECK_COND 0x0002 /* SCSI status - Check Condition */ +#define ERR16_CMD_NOT 0x0201 /* command not supported */ +#define ERR16_NO_DEVICE 0x0301 /* invalid device selection */ +#define ERR16_SECTOR 0x0202 /* bad sector */ +#define ERR16_PROTECT 0x0303 /* write protected */ +#define ERR16_NOSECTOR 0x0404 /* sector not found */ +#define ERR16_MEDIA 0x0C0C /* invalid media */ +#define ERR16_CONTROL 0x2020 /* controller error */ +#define ERR16_CONTROL_DMA 0x2120 /* controller DMA engine error */ +#define ERR16_NO_ALARM 0x2220 /* alarm is not active */ +#define ERR16_OP_BUSY 0x2320 /* operation busy */ +#define ERR16_SEEK 0x4040 /* seek failure */ +#define ERR16_DEVICE_FAIL 0x4140 /* device has failed */ +#define ERR16_TIMEOUT 0x8080 /* timeout error */ +#define ERR16_DEV_NOT_READY 0xAAAA /* drive not ready */ +#define ERR16_UNDEFINED 0xBBBB /* undefined error */ +#define ERR16_WRITE_FAULT 0xCCCC /* write fault */ +#define ERR16_INVALID_DEV 0x4001 /* invalid device access */ +#define ERR16_DEVICE_BUSY 0x4002 /* device is busy */ +#define ERR16_MEMORY 0x4003 /* device pass thru requires too much memory */ +#define ERR16_NO_FEATURE 0x40FA /* feature no implemented */ +#define ERR16_NOTAG 0x40FD /* no tag space available */ +#define ERR16_NOT_READY 0x40FE /* controller not ready error */ +#define ERR16_SETUP_FLASH 0x5050 /* error when writing setup to flash memory */ +#define ERR16_SETUP_SIZE 0x5051 /* setup block size error */ +#define ERR16_SENSE 0xFFFF /* sense opereration failed */ +#define ERR16_SC_BUSY 0x0008 /* SCSI status - Busy */ +#define ERR16_SC_RES_CONFL 0x0018 /* SCSI status - Reservation Conflict */ +#define ERR16_SC_CMD_TERM 0x0022 /* SCSI status - Command Terminated */ +#define ERR16_SC_OTHER 0x00FF /* SCSI status - not recognized (any value masked) */ +#define ERR16_MEDIA_CHANGED 0x8001 /* devices media has been changed */ + +#define ERR32_NONE 0x00000000 /* no errors */ +#define ERR32_SC_COND_MET 0x00000004 /* SCSI status - Condition Met */ +#define ERR32_CMD 0x00010101 /* command error */ +#define ERR32_SC_CHECK_COND 0x00020002 /* SCSI status - Check Condition */ +#define ERR32_CMD_NOT 0x00030201 /* command not supported */ +#define ERR32_NO_DEVICE 0x00040301 /* invalid device selection */ +#define ERR32_SECTOR 0x00050202 /* bad sector */ +#define ERR32_PROTECT 0x00060303 /* write protected */ +#define ERR32_NOSECTOR 0x00070404 /* sector not found */ +#define ERR32_MEDIA 0x00080C0C /* invalid media */ +#define ERR32_CONTROL 0x00092020 /* controller error */ +#define ERR32_CONTROL_DMA 0x000A2120 /* Controller DMA error */ +#define ERR32_NO_ALARM 0x000B2220 /* alarm is not active */ +#define ERR32_OP_BUSY 0x000C2320 /* operation busy */ +#define ERR32_SEEK 0x000D4040 /* seek failure */ +#define ERR32_DEVICE_FAIL 0x000E4140 /* device has failed */ +#define ERR32_TIMEOUT 0x000F8080 /* timeout error */ +#define ERR32_DEV_NOT_READY 0x0010AAAA /* drive not ready */ +#define ERR32_UNDEFINED 0x0011BBBB /* undefined error */ +#define ERR32_WRITE_FAULT 0x0012CCCC /* write fault */ +#define ERR32_INVALID_DEV 0x00134001 /* invalid device access */ +#define ERR32_DEVICE_BUSY 0x00144002 /* device is busy */ +#define ERR32_MEMORY 0x00154003 /* device pass thru requires too much memory */ +#define ERR32_NO_FEATURE 0x001640FA /* feature no implemented */ +#define ERR32_NOTAG 0x001740FD /* no tag space available */ +#define ERR32_NOT_READY 0x001840FE /* controller not ready error */ +#define ERR32_SETUP_FLASH 0x00195050 /* error when writing setup to flash memory */ +#define ERR32_SETUP_SIZE 0x001A5051 /* setup block size error */ +#define ERR32_SENSE 0x001BFFFF /* sense opereration failed */ +#define ERR32_SC_BUSY 0x001C0008 /* SCSI status - Busy */ +#define ERR32_SC_RES_CONFL 0x001D0018 /* SCSI status - Reservation Conflict */ +#define ERR32_SC_CMD_TERM 0x001E0022 /* SCSI status - Command Terminated */ +#define ERR32_SC_OTHER 0x001F00FF /* SCSI status - not recognized (any value masked) */ +#define ERR32_MEDIA_CHANGED 0x00208001 /* devices media has been changed */ + +/************************************************/ +/* */ +/* Host Operating System specification codes */ +/* */ +/************************************************/ +#define SPEC_INTERRUPT 0x80 /* specification requires host interrupt */ +#define SPEC_BACKWARD_SG 0x40 /* specification requires scatter/gather items reversed */ +#define SPEC_DOS_BLOCK 0x01 /* DOS DASD blocking on pass through */ +#define SPEC_OS2_V3 0x02 /* OS/2 Warp */ +#define SPCE_SCO_3242 0x04 /* SCO 3.4.2.2 */ +#define SPEC_QNX_4X 0x05 /* QNX 4.XX */ +#define SPEC_NOVELL_NWPA 0x08 /* Novell NWPA scatter/gather support */ + +/************************************************/ +/* */ +/* Inquire structures */ +/* */ +/************************************************/ +typedef struct _CNT_SCSI_INQ + { + UCHAR devt; /* 00: device type */ + UCHAR devtm; /* 01: device type modifier */ + UCHAR svers; /* 02: SCSI version */ + UCHAR rfmt; /* 03: response data format */ + UCHAR adlen; /* 04: additional length of data */ + UCHAR res1; /* 05: */ + UCHAR res2; /* 06: */ + UCHAR fncs; /* 07: functional capabilities */ + UCHAR vid[8]; /* 08: vendor ID */ + UCHAR pid[16]; /* 10: product ID */ + UCHAR rev[4]; /* 20: product revision */ + } CNT_SCSI_INQ; + +typedef struct _CNT_IDE_INQ + { + USHORT GeneralConfiguration; /* 00 */ + USHORT NumberOfCylinders; /* 02 */ + USHORT Reserved1; /* 04 */ + USHORT NumberOfHeads; /* 06 */ + USHORT UnformattedBytesPerTrack; /* 08 */ + USHORT UnformattedBytesPerSector; /* 0A */ + USHORT SectorsPerTrack; /* 0C */ + USHORT VendorUnique1[3]; /* 0E */ + USHORT SerialNumber[10]; /* 14 */ + USHORT BufferType; /* 28 */ + USHORT BufferSectorSize; /* 2A */ + USHORT NumberOfEccBytes; /* 2C */ + USHORT FirmwareRevision[4]; /* 2E */ + USHORT ModelNumber[20]; /* 36 */ + UCHAR MaximumBlockTransfer; /* 5E */ + UCHAR VendorUnique2; /* 5F */ + USHORT DoubleWordIo; /* 60 */ + USHORT Capabilities; /* 62 */ + USHORT Reserved2; /* 64 */ + UCHAR VendorUnique3; /* 66 */ + UCHAR PioCycleTimingMode; /* 67 */ + UCHAR VendorUnique4; /* 68 */ + UCHAR DmaCycleTimingMode; /* 69 */ + USHORT TranslationFieldsValid; /* 6A */ + USHORT NumberOfCurrentCylinders; /* 6C */ + USHORT NumberOfCurrentHeads; /* 6E */ + USHORT CurrentSectorsPerTrack; /* 70 */ + ULONG CurrentSectorCapacity; /* 72 */ + } CNT_IDE_INQ; + +typedef struct _DASD_INQUIRE + { + ULONG type; /* 0 = SCSI, 1 = IDE */ + union + { + CNT_SCSI_INQ scsi; /* SCSI inquire data */ + CNT_IDE_INQ ide; /* IDE inquire data */ + } inq; + } DASD_INQUIRE; + +/************************************************/ +/* */ +/* Device Codes */ +/* */ +/************************************************/ +#define DEVC_DASD 0x00 /* Direct-access Storage Device */ +#define DEVC_SEQACESS 0x01 /* Sequential-access device */ +#define DEVC_PRINTER 0x02 /* Printer device */ +#define DEVC_PROCESSOR 0x03 /* Processor device */ +#define DEVC_WRITEONCE 0x04 /* Write-once device */ +#define DEVC_CDROM 0x05 /* CD-ROM device */ +#define DEVC_SCANNER 0x06 /* Scanner device */ +#define DEVC_OPTICAL 0x07 /* Optical memory device */ +#define DEVC_MEDCHGR 0x08 /* Medium changer device */ +#define DEVC_DASD_REMOVABLE 0x80 /* Direct-access storage device, Removable */ +#define DEVC_NONE 0xFF /* no device */ + +// SCSI controls for RAID +#define SC_MY_RAID 0xBF // our special CDB command byte for Win95... interface +#define MY_SCSI_QUERY0 0x31 // byte 1 subcommand to query driver for RAID 0 informatation +#define MY_SCSI_QUERY1 0x32 // byte 1 subcommand to query driver for RAID 1 informatation +#define MY_SCSI_QUERY5 0x33 // byte 1 subcommand to query driver for RAID 5 informatation +#define MY_SCSI_REBUILD 0x40 // byte 1 subcommand to reconstruct a mirrored pair +#define MY_SCSI_DEMOFAIL 0x54 // byte 1 subcommand for RAID failure demonstration +#define MY_SCSI_ALARMMUTE 0x60 // byte 1 subcommand to mute any alarm currently on + + +#endif + diff -urN linux-2.0.37-pre8/drivers/scsi/README.aic7xxx linux-2.0.37-pre9/drivers/scsi/README.aic7xxx --- linux-2.0.37-pre8/drivers/scsi/README.aic7xxx 1998-11-15 10:33:06.000000000 -0800 +++ linux-2.0.37-pre9/drivers/scsi/README.aic7xxx 2003-08-15 15:04:42.000000000 -0700 @@ -17,6 +17,10 @@ AHA-274xT AHA-2842 AHA-2910B + AHA-2920C + AHA-2930 + AHA-2930U + AHA-2930U2 AHA-2940 AHA-2940W AHA-2940U @@ -37,8 +41,10 @@ AHA-3940U AHA-3940W AHA-3940UW + AHA-3940AUW AHA-3940U2W AHA-3950U2B + AHA-3950U2D AHA-3985 AHA-3985U AHA-3985W @@ -52,6 +58,7 @@ AIC-787x AIC-788x AIC-789x + AIC-3860 Bus Types ---------------------------- @@ -69,8 +76,30 @@ AHA-398x - PCI RAID controllers with three separate SCSI controllers on-board. - NOTE: The AHA-2920 is NOT an AIC-7xxx based controller, and is not - handled by this driver. + Not Supported Devices + ------------------------------ + Adaptec Cards + ---------------------------- + AHA-2920 (Only the cards that use the Future Domain chipset are not + supported, any 2920 cards based on Adaptec AIC chipsets, + such as the 2920C, are supported) + AAA-13x Raid Adapters + AAA-113x Raid Port Card + + Motherboard Chipsets + ---------------------------- + AIC-7810 + + Bus Types + ---------------------------- + R - Raid Port busses are not supported. + + The hardware RAID devices sold by Adaptec are *NOT* supported by this + driver (and will people please stop emailing me about them, they are + a totally separate beast from the bare SCSI controllers and this driver + can not be retrofitted in any sane manner to support the hardware RAID + features on those cards - Doug Ledford). + People ------------------------------ @@ -83,7 +112,7 @@ Jess Johnson jester@frenzy.com (AIC7xxx FAQ author) Doug Ledford dledford@redhat.com - (Current Linux aic7xxx-5.x.x Driver/Patch/FTP/FAQ maintainer) + (Current Linux aic7xxx-5.x.x Driver/Patch/FTP maintainer) Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original author of the driver. John has since retired from the project. Thanks @@ -299,13 +328,13 @@ binary->hex conversion then send an email to the aic7xxx mailing list and someone can help you out. - "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to enable - tagged queueing on specific devices. As of driver version 5.0.6, we - now globally enable tagged queueing by default, but we also disable - tagged queueing on all individual devices by default. In order to - enable tagged queueing for certian devices at boot time, a user may - use this boot param. The driver will then parse this message out - and enable the specific device entries that are present based upon + "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to disable + or enable Tagged Command Queueing (TCQ) on specific devices. As of + driver version 5.1.11, TCQ is now either on or off by default + according to the setting you choose during the make config process. + In order to en/disable TCQ for certian devices at boot time, a user + may use this boot param. The driver will then parse this message out + and en/disable the specific device entries that are present based upon the value given. The param line is parsed in the following manner: { - first instance indicates the start of this parameter values @@ -337,18 +366,13 @@ commas with no value specified will simply increment to the next id without changing anything for the missing values. - tag_info:{{8,8},,{8,8}} - First adapter, scsi id 0 to 8, id 1 to 8, remainder stay at their - default. Second adapter stays entirely at default. Third - adapter, id 0 to 8, id 1 to 8, remainder at default (identical to - first adapter). - - tag_info:{,,,{,,,64}} + tag_info:{,,,{,,,255}} First, second, and third adapters at default values. Fourth - adapter, id 3 to 64. Notice that leading commas simply increment - what the first number effects, and there are no need for trailing - commas. When you close out an adapter, or the entire entry, - anything not explicitly set stays at the default value. + adapter, id 3 is disabled. Notice that leading commas simply + increment what the first number effects, and there are no need + for trailing commas. When you close out an adapter, or the + entire entry, anything not explicitly set stays at the default + value. A final note on this option. The scanner I used for this isn't perfect or highly robust. If you mess the line up, the worst that @@ -400,10 +424,10 @@ see this documentation, you need to use one of the advanced configuration programs (menuconfig and xconfig). If you are using the "make menuconfig" method of configuring your kernel, then you would simply highlight the - option in question and hit the F1 key. If you are using the "make xconfig" - method of configuring your kernel, then simply click on the help button next - to the option you have questions about. The help information from the - Configure.help file will then get automatically displayed. + option in question and hit the ? key. If you are using the "make xconfig" + method of configuring your kernel, then simply click on the help button + next to the option you have questions about. The help information from + the Configure.help file will then get automatically displayed. /proc support ------------------------------ @@ -449,10 +473,16 @@ ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/ - European Linux mirror of Teleport site + Web sites + ------------------------------ + http://developer.redhat.com/aic7xxx/ + - Primary web site maintained by Doug Ledford. I haven't actually + put anything up yet....but I'm planning on it. This information + is put here as an add for the vapor page :) Dean W. Gehnert deang@teleport.com $Revision: 3.0 $ -Modified by Doug Ledford 1998 +Modified by Doug Ledford 1998-9 diff -urN linux-2.0.37-pre8/drivers/scsi/scsi.c linux-2.0.37-pre9/drivers/scsi/scsi.c --- linux-2.0.37-pre8/drivers/scsi/scsi.c 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/drivers/scsi/scsi.c 2003-08-15 15:04:42.000000000 -0700 @@ -295,6 +295,7 @@ {"YAMAHA","CDR100","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"YAMAHA","CDR102","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"nCipher","Fastness Crypto","*", BLIST_FORCELUN}, +{"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN}, /* * Must be at end of list... */ diff -urN linux-2.0.37-pre8/drivers/scsi/scsicam.c linux-2.0.37-pre9/drivers/scsi/scsicam.c --- linux-2.0.37-pre8/drivers/scsi/scsicam.c 1998-11-15 10:33:10.000000000 -0800 +++ linux-2.0.37-pre9/drivers/scsi/scsicam.c 2003-08-15 15:04:42.000000000 -0700 @@ -26,9 +26,8 @@ #include "scsi.h" #include "hosts.h" #include "sd.h" +#include -static int partsize(struct buffer_head *bh, unsigned long capacity, - unsigned int *cyls, unsigned int *hds, unsigned int *secs); static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds, unsigned int *secs); @@ -56,7 +55,7 @@ return -1; /* try to infer mapping from partition table */ - ret_code = partsize (bh, (unsigned long) size, (unsigned int *) ip + 2, + ret_code = scsi_partsize (bh, (unsigned long) size, (unsigned int *) ip + 2, (unsigned int *) ip + 0, (unsigned int *) ip + 1); brelse (bh); @@ -85,7 +84,7 @@ } /* - * Function : static int partsize(struct buffer_head *bh, unsigned long + * Function : static int scsi_partsize(struct buffer_head *bh, unsigned long * capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs); * * Purpose : to determine the BIOS mapping used to create the partition @@ -95,7 +94,7 @@ * */ -static int partsize(struct buffer_head *bh, unsigned long capacity, +int scsi_partsize(struct buffer_head *bh, unsigned long capacity, unsigned int *cyls, unsigned int *hds, unsigned int *secs) { struct partition *p, *largest = NULL; int i, largest_cyl; diff -urN linux-2.0.37-pre8/drivers/scsi/scsi_syms.c linux-2.0.37-pre9/drivers/scsi/scsi_syms.c --- linux-2.0.37-pre8/drivers/scsi/scsi_syms.c 1998-06-03 15:17:49.000000000 -0700 +++ linux-2.0.37-pre9/drivers/scsi/scsi_syms.c 2003-08-15 15:04:42.000000000 -0700 @@ -26,13 +26,11 @@ #include "constants.h" #include "sd.h" +#include /* * This source file contains the symbol table used by scsi loadable * modules. */ -extern int scsicam_bios_param (Disk * disk, - int dev, int *ip ); - extern void print_command (unsigned char *command); extern void print_sense(const char * devclass, Scsi_Cmnd * SCpnt); @@ -47,6 +45,7 @@ X(scsi_register), X(scsi_unregister), X(scsicam_bios_param), + X(scsi_partsize), X(allocate_device), X(scsi_do_cmd), X(scsi_command_size), diff -urN linux-2.0.37-pre8/drivers/scsi/seagate.c linux-2.0.37-pre9/drivers/scsi/seagate.c --- linux-2.0.37-pre8/drivers/scsi/seagate.c 1998-11-15 10:33:10.000000000 -0800 +++ linux-2.0.37-pre9/drivers/scsi/seagate.c 2003-08-15 15:04:42.000000000 -0700 @@ -164,7 +164,7 @@ } Signature; static const Signature signatures[] = { -#ifdef CONFIG_SCSI_SEAGATE +#if defined(CONFIG_SCSI_SEAGATE) || defined(CONFIG_SCSI_SEAGATE_MODULE) {"ST01 v1.7 (C) Copyright 1987 Seagate", 15, 37, SEAGATE}, {"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40, SEAGATE}, diff -urN linux-2.0.37-pre8/drivers/scsi/tripace.c linux-2.0.37-pre9/drivers/scsi/tripace.c --- linux-2.0.37-pre8/drivers/scsi/tripace.c 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/drivers/scsi/tripace.c 2003-08-15 15:04:42.000000000 -0700 @@ -620,7 +620,7 @@ u32 insert_bit(u32, short int); static void Get_Base(Adapter *); -void tc2550_intr(int, struct pt_regs *); +void tc2550_intr(int, void *, struct pt_regs *); static void internal_done(Scsi_Cmnd *); @@ -1314,7 +1314,7 @@ return 0; } -void tc2550_intr(int irq, struct pt_regs *regs) +void tc2550_intr(int irq, void *dev_id, struct pt_regs *regs) { void (*my_done) (Scsi_Cmnd *) = NULL; diff -urN linux-2.0.37-pre8/include/linux/pci.h linux-2.0.37-pre9/include/linux/pci.h --- linux-2.0.37-pre8/include/linux/pci.h 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/include/linux/pci.h 2003-08-15 15:04:42.000000000 -0700 @@ -962,10 +962,12 @@ #define PCI_VENDOR_ID_ADAPTEC 0x9004 #define PCI_DEVICE_ID_ADAPTEC_7810 0x1078 +#define PCI_DEVICE_ID_ADAPTEC_7821 0x2178 #define PCI_DEVICE_ID_ADAPTEC_7850 0x5078 #define PCI_DEVICE_ID_ADAPTEC_7855 0x5578 #define PCI_DEVICE_ID_ADAPTEC_5800 0x5800 -#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075 +#define PCI_DEVICE_ID_ADAPTEC_3860 0x6038 +#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075 #define PCI_DEVICE_ID_ADAPTEC_7860 0x6078 #define PCI_DEVICE_ID_ADAPTEC_7861 0x6178 #define PCI_DEVICE_ID_ADAPTEC_7870 0x7078 @@ -979,14 +981,28 @@ #define PCI_DEVICE_ID_ADAPTEC_7882 0x8278 #define PCI_DEVICE_ID_ADAPTEC_7883 0x8378 #define PCI_DEVICE_ID_ADAPTEC_7884 0x8478 +#define PCI_DEVICE_ID_ADAPTEC_7885 0x8578 +#define PCI_DEVICE_ID_ADAPTEC_7886 0x8678 +#define PCI_DEVICE_ID_ADAPTEC_7887 0x8778 +#define PCI_DEVICE_ID_ADAPTEC_7888 0x8878 #define PCI_DEVICE_ID_ADAPTEC_1030 0x8b78 #define PCI_VENDOR_ID_ADAPTEC2 0x9005 #define PCI_DEVICE_ID_ADAPTEC2_2940U2 0x0010 +#define PCI_DEVICE_ID_ADAPTEC2_2930U2 0x0011 +#define PCI_DEVICE_ID_ADAPTEC2_7890B 0x0013 #define PCI_DEVICE_ID_ADAPTEC2_7890 0x001f #define PCI_DEVICE_ID_ADAPTEC2_3940U2 0x0050 #define PCI_DEVICE_ID_ADAPTEC2_3950U2D 0x0051 #define PCI_DEVICE_ID_ADAPTEC2_7896 0x005f +#define PCI_DEVICE_ID_ADAPTEC2_7892A 0x0080 +#define PCI_DEVICE_ID_ADAPTEC2_7892B 0x0081 +#define PCI_DEVICE_ID_ADAPTEC2_7892D 0x0083 +#define PCI_DEVICE_ID_ADAPTEC2_7892P 0x008f +#define PCI_DEVICE_ID_ADAPTEC2_7899A 0x00c0 +#define PCI_DEVICE_ID_ADAPTEC2_7899B 0x00c1 +#define PCI_DEVICE_ID_ADAPTEC2_7899D 0x00c3 +#define PCI_DEVICE_ID_ADAPTEC2_7899P 0x00cf #define PCI_VENDOR_ID_ATRONICS 0x907f #define PCI_DEVICE_ID_ATRONICS_2015 0x2015 diff -urN linux-2.0.37-pre8/include/linux/proc_fs.h linux-2.0.37-pre9/include/linux/proc_fs.h --- linux-2.0.37-pre8/include/linux/proc_fs.h 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/include/linux/proc_fs.h 2003-08-15 15:04:42.000000000 -0700 @@ -153,6 +153,9 @@ PROC_SCSI_ATARI, PROC_SCSI_GDTH, PROC_SCSI_INI9100U, + PROC_SCSI_PCI2000, + PROC_SCSI_PCI2220I, + PROC_SCSI_PSI240I, PROC_SCSI_IDESCSI, PROC_SCSI_SCSI_DEBUG, PROC_SCSI_NOT_PRESENT, diff -urN linux-2.0.37-pre8/include/scsi/scsicam.h linux-2.0.37-pre9/include/scsi/scsicam.h --- linux-2.0.37-pre8/include/scsi/scsicam.h 1996-05-01 21:48:55.000000000 -0700 +++ linux-2.0.37-pre9/include/scsi/scsicam.h 2003-08-15 15:04:42.000000000 -0700 @@ -14,4 +14,6 @@ #define SCSICAM_H #include extern int scsicam_bios_param (Disk *disk, kdev_t dev, int *ip); +extern int scsi_partsize(struct buffer_head *bh, unsigned long capacity, + unsigned int *cyls, unsigned int *hds, unsigned int *secs); #endif /* def SCSICAM_H */ diff -urN linux-2.0.37-pre8/kernel/sys.c linux-2.0.37-pre9/kernel/sys.c --- linux-2.0.37-pre8/kernel/sys.c 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/kernel/sys.c 2003-08-15 15:04:42.000000000 -0700 @@ -226,6 +226,9 @@ void ctrl_alt_del(void) { if (C_A_D) { +#ifdef CONFIG_BLK_DEV_DAC960 + DAC960_Finalize(); +#endif #ifdef CONFIG_SCSI_GDTH gdth_halt(); #endif diff -urN linux-2.0.37-pre8/linux/Documentation/README.DAC960 linux-2.0.37-pre9/linux/Documentation/README.DAC960 --- linux-2.0.37-pre8/linux/Documentation/README.DAC960 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.37-pre9/linux/Documentation/README.DAC960 2003-08-15 15:04:42.000000000 -0700 @@ -0,0 +1,670 @@ + Mylex DAC960/DAC1100 PCI RAID Controller Driver for Linux + + Version 2.0.0 for Linux 2.0.36 + Version 2.2.0 for Linux 2.2.3 + + PRODUCTION RELEASE + + 23 March 1999 + + Leonard N. Zubkoff + Dandelion Digital + lnz@dandelion.com + + Copyright 1998-1999 by Leonard N. Zubkoff + + + INTRODUCTION + +Mylex, Inc. designs and manufactures a variety of high performance PCI RAID +controllers. Mylex Corporation is located at 34551 Ardenwood Blvd., Fremont, +California 94555, USA and can be reached at 510/796-6100 or on the World Wide +Web at http://www.mylex.com. Mylex RAID Technical Support can be reached by +electronic mail at support@mylex.com (for eXtremeRAID 1100 and older DAC960 +models) or techsup@mylex.com (for AcceleRAID models), by voice at 510/608-2400, +or by FAX at 510/745-7715. Contact information for offices in Europe and Japan +is available on the Web site. + +The latest information on Linux support for DAC960 PCI RAID Controllers, as +well as the most recent release of this driver, will always be available from +my Linux Home Page at URL "http://www.dandelion.com/Linux/". The Linux DAC960 +driver supports all current DAC960 PCI family controllers including the +AcceleRAID models, as well as the eXtremeRAID 1100; see below for a complete +list. For simplicity, in most places this documentation refers to DAC960 +generically rather than explicitly listing all the models. + +Bug reports should be sent via electronic mail to "lnz@dandelion.com". Please +include with the bug report the complete configuration messages reported by the +driver at startup, along with any subsequent system messages relevant to the +controller's operation, and a detailed description of your system's hardware +configuration. + +Please consult the DAC960 RAID controller documentation for detailed +information regarding installation and configuration of the controllers. This +document primarily provides information specific to the Linux DAC960 support. + + + DRIVER FEATURES + +The DAC960 RAID controllers are supported solely as high performance RAID +controllers, not as interfaces to arbitrary SCSI devices. The Linux DAC960 +driver operates at the block device level, the same level as the SCSI and IDE +drivers. Unlike other RAID controllers currently supported on Linux, the +DAC960 driver is not dependent on the SCSI subsystem, and hence avoids all the +complexity and unnecessary code that would be associated with an implementation +as a SCSI driver. The DAC960 driver is designed for as high a performance as +possible with no compromises or extra code for compatibility with lower +performance devices. The DAC960 driver includes extensive error logging and +online configuration management capabilities. Except for initial configuration +of the controller and adding new disk drives, most everything can be handled +from Linux while the system is operational. + +The DAC960 driver is architected to support up to 8 controllers per system. +Each DAC960 controller can support up to 15 disk drives per channel, for a +maximum of 45 drives on a three channel controller. The drives installed on a +controller are divided into one or more "Drive Groups", and then each Drive +Group is subdivided further into 1 to 32 "Logical Drives". Each Logical Drive +has a specific RAID Level and caching policy associated with it, and it appears +to Linux as a single block device. Logical Drives are further subdivided into +up to 7 partitions through the normal Linux and PC disk partitioning schemes. +Logical Drives are also known as "System Drives", and Drive Groups are also +called "Packs". Both terms are in use in the Mylex documentation; I have +chosen to standardize on the more generic "Logical Drive" and "Drive Group". + +DAC960 RAID disk devices are named in the style of the Device File System +(DEVFS). The device corresponding to Logical Drive D on Controller C is +referred to as /dev/rd/cCdD, and the partitions are called /dev/rd/cCdDp1 +through /dev/rd/cCdDp7. For example, partition 3 of Logical Drive 5 on +Controller 2 is referred to as /dev/rd/c2d5p3. Note that unlike with SCSI +disks the device names will not change in the event of a disk drive failure. +The DAC960 driver is assigned major numbers 48 - 55 with one major number per +controller. The 8 bits of minor number are divided into 5 bits for the Logical +Drive and 3 bits for the partition. + + + SUPPORTED DAC960/DAC1100 PCI RAID CONTROLLERS + +The following list comprises the supported DAC960 and DAC1100 PCI RAID +Controllers as of the date of this document. It is recommended that anyone +purchasing a Mylex PCI RAID Controller not in the following table contact the +author beforehand to verify that it is or will be supported. + +eXtremeRAID 1100 (DAC1164P) + 3 Wide Ultra-2/LVD SCSI channels + 233MHz StrongARM SA 110 Processor + 64 Bit PCI (backward compatible with 32 Bit PCI slots) + 16MB/32MB/64MB Parity SDRAM Memory with Battery Backup + +AcceleRAID 250 (DAC960PTL1) + Uses onboard Symbios SCSI chips on certain motherboards + Also includes one onboard Wide Ultra-2/LVD SCSI Channel + 66MHz Intel i960RD RISC Processor + 4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory + +AcceleRAID 200 (DAC960PTL0) + Uses onboard Symbios SCSI chips on certain motherboards + Includes no onboard SCSI Channels + 66MHz Intel i960RD RISC Processor + 4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory + +AcceleRAID 150 (DAC960PRL) + Uses onboard Symbios SCSI chips on certain motherboards + Also includes one onboard Wide Ultra-2/LVD SCSI Channel + 33MHz Intel i960RP RISC Processor + 4MB Parity EDO Memory + +DAC960PJ 1/2/3 Wide Ultra SCSI-3 Channels + 66MHz Intel i960RD RISC Processor + 4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory + +DAC960PG 1/2/3 Wide Ultra SCSI-3 Channels + 33MHz Intel i960RP RISC Processor + 4MB/8MB ECC EDO Memory + +DAC960PU 1/2/3 Wide Ultra SCSI-3 Channels + Intel i960CF RISC Processor + 4MB/8MB EDRAM or 2MB/4MB/8MB/16MB/32MB DRAM Memory + +DAC960PD 1/2/3 Wide Fast SCSI-2 Channels + Intel i960CF RISC Processor + 4MB/8MB EDRAM or 2MB/4MB/8MB/16MB/32MB DRAM Memory + +DAC960PL 1/2/3 Wide Fast SCSI-2 Channels + Intel i960 RISC Processor + 2MB/4MB/8MB/16MB/32MB DRAM Memory + +For the eXtremeRAID 1100, firmware version 5.06-0-52 or above is required. + +For the AcceleRAID 250, 200, and 150, firmware version 4.06-0-57 or above is +required. + +For the DAC960PJ and DAC960PG, firmware version 4.06-0-00 or above is required. + +For the DAC960PU, DAC960PD, and DAC960PL, firmware version 3.51-0-04 or above +is required. + +Note that earlier revisions of the DAC960PU, DAC960PD, and DAC960PL controllers +were delivered with version 2.xx firmware. Version 2.xx firmware is not +supported by this driver and no support is envisioned. Contact Mylex RAID +Technical Support to inquire about upgrading controllers with version 2.xx +firmware to version 3.51-0-04. Upgrading to version 3.xx firmware requires +installation of higher capacity Flash ROM chips, and not all DAC960PD and +DAC960PL controllers can be upgraded. + +Please note that not all SCSI disk drives are suitable for use with DAC960 +controllers, and only particular firmware versions of any given model may +actually function correctly. Similarly, not all motherboards have a BIOS that +properly initializes the AcceleRAID 250, AcceleRAID 200, AcceleRAID 150, +DAC960PJ, and DAC960PG because the Intel i960RD/RP is a multi-function device. +If in doubt, contact Mylex RAID Technical Support (support@mylex.com) to verify +compatibility. Mylex makes available a hard disk compatibility list by FTP at +ftp://ftp.mylex.com/pub/dac960/diskcomp.html. + + + DRIVER INSTALLATION + +This distribution was prepared for Linux kernel version 2.0.36 or 2.2.3. + +To install the DAC960 RAID driver, you may use the following commands, +replacing "/usr/src" with wherever you keep your Linux kernel source tree: + + cd /usr/src + tar -xvzf DAC960-2.0.0.tar.gz (or DAC960-2.2.0.tar.gz) + mv README.DAC960 linux/Documentation + mv DAC960.[ch] linux/drivers/block + patch -p0 < DAC960.patch + cd linux + make config + make depend + make bzImage (or zImage) + +Then install "arch/i386/boot/bzImage" or "arch/i386/boot/zImage" as your +standard kernel, run lilo if appropriate, and reboot. + +To create the necessary devices in /dev, the "make_rd" script included in +"DAC960-Utilities.tar.gz" from http://www.dandelion.com/Linux/ may be used. +LILO 21 and FDISK v2.9 include DAC960 support; also included in this archive +are patches to LILO 20 and FDISK v2.8 that add DAC960 support, along with +statically linked executables of LILO and FDISK. This modified version of LILO +will allow booting from a DAC960 controller and/or mounting the root file +system from a DAC960. Unfortunately, installing directly onto a DAC960 will be +problematic until the various Linux distribution vendors update their +installation utilities. VA Research Linux Systems (http://www.varesearch.com) +has created an installation CD capable of installing its modified Red Hat 5.2 +environment directly onto DAC960 controllers, and copies of this CD are being +made available to owners of supported Mylex RAID products by VA Research in +cooperation with Mylex. Please see http://www.mylex.com/support/prsfrm.html to +request an installation CD. + + + INSTALLATION NOTES + +Before installing Linux or adding DAC960 logical drives to an existing Linux +system, the controller must first be configured to provide one or more logical +drives using the BIOS Configuration Utility or DACCF. Please note that since +there are only at most 6 usable partitions on each logical drive, systems +requiring more partitions should subdivide a drive group into multiple logical +drives, each of which can have up to 6 partitions. Also, note that with large +disk arrays it is advisable to enable the 8GB BIOS Geometry (255/63) rather +than accepting the default 2GB BIOS Geometry (128/32); failing to so do will +cause the logical drive geometry to have more than 65535 cylinders which will +make it impossible for FDISK to be used properly. The 8GB BIOS Geometry can be +enabled by configuring the DAC960 BIOS, which is accessible via Alt-M during +the BIOS initialization sequence. + +For maximum performance and the most efficient E2FSCK performance, it is +recommended that EXT2 file systems be built with a 4KB block size and 16 block +stride to match the DAC960 controller's 64KB default stripe size. The command +"mke2fs -b 4096 -R stride=16 " is appropriate. + + + DAC960 ANNOUNCEMENTS MAILING LIST + +The DAC960 Announcements Mailing List provides a forum for informing Linux +users of new driver releases and other announcements regarding Linux support +for DAC960 PCI RAID Controllers. To join the mailing list, send a message to +"dac960-announce-request@dandelion.com" with the line "subscribe" in the +message body. + + + CONTROLLER CONFIGURATION AND STATUS MONITORING + +The DAC960 RAID controllers running firmware 4.06 or above include a Background +Initialization facility so that system downtime is minimized both for initial +installation and subsequent configuration of additional storage. The BIOS +Configuration Utility (accessible via Alt-R during the BIOS initialization +sequence) is used to quickly configure the controller, and then the logical +drives that have been created are available for immediate use even while they +are still being initialized by the controller. The primary need for online +configuration and status monitoring is then to avoid system downtime when disk +drives fail and must be replaced. Note that with a SAF-TE (SCSI Accessed +Fault-Tolerant Enclosure) enclosure, the controller is able to rebuild failed +drives automatically as soon as a drive replacement is made available. + +The primary interfaces for controller configuration and status monitoring are +special files created in the /proc/rd/... hierarchy along with the normal +system console logging mechanism. Whenever the system is operating, the DAC960 +driver queries each controller for status information every 10 seconds, and +checks for additional conditions every 60 seconds. The initial status of each +controller is always available for controller N in /proc/rd/cN/initial_status, +and the current status as of the last status monitoring query is available in +/proc/rd/cN/current_status. In addition, status changes are also logged by the +driver to the system console and will appear in the log files maintained by +syslog. The progress of asynchronous rebuild or consistency check operations +is also available in /proc/rd/cN/current_status, and progress messages are +logged to the system console at most every 60 seconds. + +To simplify the monitoring process for custom software, the special file +/proc/rd/status returns "OK" when all DAC960 controllers in the system are +operating normally and no failures have occurred, or "ALERT" if any logical +drives are offline or critical or any non-standby physical drives are dead. + +Configuration commands for controller N are available via the special file +/proc/rd/cN/user_command. A human readable command can be written to this +special file to initiate a configuration operation, and the results of the +operation can then be read back from the special file in addition to being +logged to the system console. The shell command sequence + + echo "" > /proc/rd/c0/user_command + cat /proc/rd/c0/user_command + +is typically used to execute configuration commands. The configuration +commands are: + + flush-cache + + The "flush-cache" command flushes the controller's cache. The system + automatically flushes the cache at shutdown or if the driver module is + unloaded, so this command is only needed to be certain a write back cache + is flushed to disk before the system is powered off by a command to a UPS. + Note that the flush-cache command also stops an asynchronous rebuild or + consistency check, so it should not be used except when the system is being + halted. + + kill : + + The "kill" command marks the physical drive : as DEAD. + This command is provided primarily for testing, and should not be used + during normal system operation. + + make-online : + + The "make-online" command changes the physical drive : + from status DEAD to status ONLINE. In cases where multiple physical drives + have been killed simultaneously, this command may be used to bring them + back online, after which a consistency check is advisable. + + Warning: make-online should only be used on a dead physical drive that is + an active part of a drive group, never on a standby drive. + + make-standby : + + The "make-standby" command changes physical drive : + from status DEAD to status STANDBY. It should only be used in cases where + a dead drive was replaced after an automatic rebuild was performed onto a + standby drive. It cannot be used to add a standby drive to the controller + configuration if one was not created initially; the BIOS Configuration + Utility must be used for that currently. + + rebuild : + + The "rebuild" command initiates an asynchronous rebuild onto physical drive + :. It should only be used when a dead drive has been + replaced. + + check-consistency + + The "check-consistency" command initiates an asynchronous consistency check + of with automatic restoration. It can be used + whenever it is desired to verify the consistency of the redundancy + information. + + cancel-rebuild + cancel-consistency-check + + The "cancel-rebuild" and "cancel-consistency-check" commands cancel any + rebuild or consistency check operations previously initiated. + + + EXAMPLE I - DRIVE FAILURE WITHOUT A STANDBY DRIVE + +The following annotated logs demonstrate the controller configuration and and +online status monitoring capabilities of the Linux DAC960 Driver. The test +configuration comprises 6 1GB Quantum Atlas I disk drives on two channels of a +DAC960PJ controller. The physical drives are configured into a single drive +group without a standby drive, and the drive group has been configured into two +logical drives, one RAID-5 and one RAID-6. First, here is the current status +of the RAID configuration: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status +***** DAC960 RAID Driver Version 2.0.0 of 23 March 1999 ***** +Copyright 1998-1999 by Leonard N. Zubkoff +Configuring Mylex DAC960PJ PCI RAID Controller + Firmware Version: 4.06-0-08, Channels: 3, Memory Size: 8MB + PCI Bus: 0, Device: 19, Function: 1, I/O Address: Unassigned + PCI Address: 0xFD4FC000 mapped at 0x8807000, IRQ Channel: 9 + Controller Queue Depth: 128, Maximum Blocks per Command: 128 + Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33 + Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63 + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Online, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Online, 5498880 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Online, 3305472 blocks, Write Thru + No Rebuild or Consistency Check in Progress + +gwynedd:/u/lnz# cat /proc/rd/status +OK + +The above messages indicate that everything is healthy, and /proc/rd/status +returns "OK" indicating that there are no problems with any DAC960 controller +in the system. For demonstration purposes, while I/O is active Physical Drive +1:1 is now disconnected, simulating a drive failure. The failure is noted by +the driver within 10 seconds of the controller's having detected it, and the +driver logs the following console status messages indicating that Logical +Drives 0 and 1 are now CRITICAL as a result of Physical Drive 1:1 being DEAD: + +DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now CRITICAL +DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now CRITICAL +DAC960#0: Physical Drive 1:2 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02 +DAC960#0: Physical Drive 1:3 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02 +DAC960#0: Physical Drive 1:1 killed because of timeout on SCSI command +DAC960#0: Physical Drive 1:1 is now DEAD + +The Sense Keys logged here are just Check Condition / Unit Attention conditions +arising from a SCSI bus reset that is forced by the controller during its error +recovery procedures. Concurrently with the above, the driver status available +from /proc/rd also reflects the drive failure. The status message in +/proc/rd/status has changed from "OK" to "ALERT": + +gwynedd:/u/lnz# cat /proc/rd/status +ALERT + +and /proc/rd/c0/current_status has been updated: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Dead, 2201600 blocks + 1:2 - Disk: Online, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Critical, 5498880 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Critical, 3305472 blocks, Write Thru + No Rebuild or Consistency Check in Progress + +Since there are no standby drives configured, the system can continue to access +the logical drives in a performance degraded mode until the failed drive is +replaced and a rebuild operation completed to restore the redundancy of the +logical drives. Once Physical Drive 1:1 is replaced with a properly +functioning drive, or if the physical drive was killed without having failed +(e.g., due to electrical problems on the SCSI bus), the user can instruct the +controller to initiate a rebuild operation onto the newly replaced drive: + +gwynedd:/u/lnz# echo "rebuild 1:1" > /proc/rd/c0/user_command +gwynedd:/u/lnz# cat /proc/rd/c0/user_command +Rebuild of Physical Drive 1:1 Initiated + +The echo command instructs the controller to initiate an asynchronous rebuild +operation onto Physical Drive 1:1, and the status message that results from the +operation is then available for reading from /proc/rd/c0/user_command, as well +as being logged to the console by the driver. + +Within 10 seconds of this command the driver logs the initiation of the +asynchronous rebuild operation: + +DAC960#0: Rebuild of Physical Drive 1:1 Initiated +DAC960#0: Physical Drive 1:1 Error Log: Sense Key = 6, ASC = 29, ASCQ = 01 +DAC960#0: Physical Drive 1:1 is now WRITE-ONLY +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 1% completed + +and /proc/rd/c0/current_status is updated: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Write-Only, 2201600 blocks + 1:2 - Disk: Online, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Critical, 5498880 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Critical, 3305472 blocks, Write Thru + Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 6% completed + +As the rebuild progresses, the current status in /proc/rd/c0/current_status is +updated every 10 seconds: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Write-Only, 2201600 blocks + 1:2 - Disk: Online, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Critical, 5498880 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Critical, 3305472 blocks, Write Thru + Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 15% completed + +and every minute a progress message is logged to the console by the driver: + +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 32% completed +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 63% completed +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 94% completed +DAC960#0: Rebuild in Progress: Logical Drive 1 (/dev/rd/c0d1) 94% completed + +Finally, the rebuild completes successfully. The driver logs the status of the +logical and physical drives and the rebuild completion: + +DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now ONLINE +DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now ONLINE +DAC960#0: Physical Drive 1:1 is now ONLINE +DAC960#0: Rebuild Completed Successfully + +/proc/rd/c0/current_status is updated: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Online, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Online, 5498880 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Online, 3305472 blocks, Write Thru + Rebuild Completed Successfully + +and /proc/rd/status indicates that everything is healthy once again: + +gwynedd:/u/lnz# cat /proc/rd/status +OK + + + EXAMPLE II - DRIVE FAILURE WITH A STANDBY DRIVE + +The following annotated logs demonstrate the controller configuration and and +online status monitoring capabilities of the Linux DAC960 Driver. The test +configuration comprises 6 1GB Quantum Atlas I disk drives on two channels of a +DAC960PJ controller. The physical drives are configured into a single drive +group with a standby drive, and the drive group has been configured into two +logical drives, one RAID-5 and one RAID-6. First, here is the current status +of the RAID configuration: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status +***** DAC960 RAID Driver Version 2.0.0 of 23 March 1999 ***** +Copyright 1998-1999 by Leonard N. Zubkoff +Configuring Mylex DAC960PJ PCI RAID Controller + Firmware Version: 4.06-0-08, Channels: 3, Memory Size: 8MB + PCI Bus: 0, Device: 19, Function: 1, I/O Address: Unassigned + PCI Address: 0xFD4FC000 mapped at 0x8807000, IRQ Channel: 9 + Controller Queue Depth: 128, Maximum Blocks per Command: 128 + Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33 + Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63 + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Online, 2201600 blocks + 1:3 - Disk: Standby, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Online, 4399104 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Online, 2754560 blocks, Write Thru + No Rebuild or Consistency Check in Progress + +gwynedd:/u/lnz# cat /proc/rd/status +OK + +The above messages indicate that everything is healthy, and /proc/rd/status +returns "OK" indicating that there are no problems with any DAC960 controller +in the system. For demonstration purposes, while I/O is active Physical Drive +1:2 is now disconnected, simulating a drive failure. The failure is noted by +the driver within 10 seconds of the controller's having detected it, and the +driver logs the following console status messages: + +DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now CRITICAL +DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now CRITICAL +DAC960#0: Physical Drive 1:1 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02 +DAC960#0: Physical Drive 1:3 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02 +DAC960#0: Physical Drive 1:2 killed because of timeout on SCSI command +DAC960#0: Physical Drive 1:2 is now DEAD +DAC960#0: Physical Drive 1:2 killed because it was removed + +Since a standby drive is configured, the controller automatically begins +rebuilding onto the standby drive: + +DAC960#0: Physical Drive 1:3 is now WRITE-ONLY +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 4% completed + +Concurrently with the above, the driver status available from /proc/rd also +reflects the drive failure and automatic rebuild. The status message in +/proc/rd/status has changed from "OK" to "ALERT": + +gwynedd:/u/lnz# cat /proc/rd/status +ALERT + +and /proc/rd/c0/current_status has been updated: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Dead, 2201600 blocks + 1:3 - Disk: Write-Only, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Critical, 4399104 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Critical, 2754560 blocks, Write Thru + Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 4% completed + +As the rebuild progresses, the current status in /proc/rd/c0/current_status is +updated every 10 seconds: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Dead, 2201600 blocks + 1:3 - Disk: Write-Only, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Critical, 4399104 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Critical, 2754560 blocks, Write Thru + Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 40% completed + +and every minute a progress message is logged on the console by the driver: + +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 40% completed +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 76% completed +DAC960#0: Rebuild in Progress: Logical Drive 1 (/dev/rd/c0d1) 66% completed +DAC960#0: Rebuild in Progress: Logical Drive 1 (/dev/rd/c0d1) 84% completed + +Finally, the rebuild completes successfully. The driver logs the status of the +logical and physical drives and the rebuild completion: + +DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now ONLINE +DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now ONLINE +DAC960#0: Physical Drive 1:3 is now ONLINE +DAC960#0: Rebuild Completed Successfully + +/proc/rd/c0/current_status is updated: + +***** DAC960 RAID Driver Version 2.0.0 of 23 March 1999 ***** +Copyright 1998-1999 by Leonard N. Zubkoff +Configuring Mylex DAC960PJ PCI RAID Controller + Firmware Version: 4.06-0-08, Channels: 3, Memory Size: 8MB + PCI Bus: 0, Device: 19, Function: 1, I/O Address: Unassigned + PCI Address: 0xFD4FC000 mapped at 0x8807000, IRQ Channel: 9 + Controller Queue Depth: 128, Maximum Blocks per Command: 128 + Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33 + Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63 + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Dead, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Online, 4399104 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Online, 2754560 blocks, Write Thru + Rebuild Completed Successfully + +and /proc/rd/status indicates that everything is healthy once again: + +gwynedd:/u/lnz# cat /proc/rd/status +OK + +Note that the absence of a viable standby drive does not create an "ALERT" +status. Once dead Physical Drive 1:2 has been replaced, the controller must be +told that this has occurred and that the newly replaced drive should become the +new standby drive: + +gwynedd:/u/lnz# echo "make-standby 1:2" > /proc/rd/c0/user_command +gwynedd:/u/lnz# cat /proc/rd/c0/user_command +Make Standby of Physical Drive 1:2 Succeeded + +The echo command instructs the controller to make Physical Drive 1:2 into a +standby drive, and the status message that results from the operation is then +available for reading from /proc/rd/c0/user_command, as well as being logged to +the console by the driver. Within 60 seconds of this command the driver logs: + +DAC960#0: Physical Drive 1:2 Error Log: Sense Key = 6, ASC = 29, ASCQ = 01 +DAC960#0: Physical Drive 1:2 is now STANDBY +DAC960#0: Make Standby of Physical Drive 1:2 Succeeded + +and /proc/rd/c0/current_status is updated: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Standby, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Online, 4399104 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Online, 2754560 blocks, Write Thru + Rebuild Completed Successfully diff -urN linux-2.0.37-pre8/linux/drivers/block/DAC960.c linux-2.0.37-pre9/linux/drivers/block/DAC960.c --- linux-2.0.37-pre8/linux/drivers/block/DAC960.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.37-pre9/linux/drivers/block/DAC960.c 2003-08-15 15:04:42.000000000 -0700 @@ -0,0 +1,3013 @@ +/* + + Linux Driver for Mylex DAC960 and DAC1100 PCI RAID Controllers + + Copyright 1998-1999 by Leonard N. Zubkoff + + This program is free software; you may redistribute and/or modify it under + the terms of the GNU General Public License Version 2 as published by the + Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for complete details. + + The author respectfully requests that any modifications to this software be + sent directly to him for evaluation and testing. + +*/ + + +#define DAC960_DriverVersion "2.0.0" +#define DAC960_DriverDate "23 March 1999" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "DAC960.h" + + +/* + DAC960_ControllerCount is the number of DAC960 Controllers detected. +*/ + +static int + DAC960_ControllerCount = 0; + + +/* + DAC960_ActiveControllerCount is the number of Active DAC960 Controllers + detected. +*/ + +static int + DAC960_ActiveControllerCount = 0; + + +/* + DAC960_Controllers is an array of pointers to the DAC960 Controller + structures. +*/ + +static DAC960_Controller_T + *DAC960_Controllers[DAC960_MaxControllers] = { NULL }; + + +/* + DAC960_FileOperations is the File Operations structure for DAC960 Logical + Disk Devices. +*/ + +static FileOperations_T + DAC960_FileOperations = + { lseek: NULL, + read: block_read, + write: block_write, + readdir: NULL, + select: NULL, + ioctl: DAC960_Ioctl, + mmap: NULL, + open: DAC960_Open, + release: DAC960_Release, + fsync: block_fsync, + fasync: NULL, + check_media_change: NULL, + revalidate: NULL }; + + +/* + DAC960_ProcDirectoryEntry is the DAC960 /proc/rd directory entry. +*/ + +static PROC_DirectoryEntry_T + DAC960_ProcDirectoryEntry; + + +/* + DAC960_AnnounceDriver announces the Driver Version and Date, Author's Name, + Copyright Notice, and Electronic Mail Address. +*/ + +static void DAC960_AnnounceDriver(DAC960_Controller_T *Controller) +{ + DAC960_Announce("***** DAC960 RAID Driver Version " + DAC960_DriverVersion " of " + DAC960_DriverDate " *****\n", Controller); + DAC960_Announce("Copyright 1998-1999 by Leonard N. Zubkoff " + "\n", Controller); +} + + +/* + DAC960_Failure prints a standardized error message, and then returns false. +*/ + +static boolean DAC960_Failure(DAC960_Controller_T *Controller, + char *ErrorMessage) +{ + DAC960_Error("While configuring DAC960 PCI RAID Controller at\n", + Controller); + if (Controller->IO_Address == 0) + DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A " + "PCI Address 0x%X\n", Controller, + Controller->Bus, Controller->Device, + Controller->Function, Controller->PCI_Address); + else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address " + "0x%X PCI Address 0x%X\n", Controller, + Controller->Bus, Controller->Device, + Controller->Function, Controller->IO_Address, + Controller->PCI_Address); + DAC960_Error("%s FAILED - DETACHING\n", Controller, ErrorMessage); + return false; +} + + +/* + DAC960_ClearCommand clears critical fields of Command. +*/ + +static inline void DAC960_ClearCommand(DAC960_Command_T *Command) +{ + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + CommandMailbox->Words[0] = 0; + CommandMailbox->Words[1] = 0; + CommandMailbox->Words[2] = 0; + CommandMailbox->Words[3] = 0; + Command->CommandStatus = 0; +} + + +/* + DAC960_AllocateCommand allocates a Command structure from Controller's + free list. +*/ + +static inline DAC960_Command_T *DAC960_AllocateCommand(DAC960_Controller_T + *Controller) +{ + DAC960_Command_T *Command = Controller->FreeCommands; + if (Command == NULL) return NULL; + Controller->FreeCommands = Command->Next; + Command->Next = NULL; + return Command; +} + + +/* + DAC960_DeallocateCommand deallocates Command, returning it to Controller's + free list. +*/ + +static inline void DAC960_DeallocateCommand(DAC960_Command_T *Command) +{ + DAC960_Controller_T *Controller = Command->Controller; + Command->Next = Controller->FreeCommands; + Controller->FreeCommands = Command; +} + + +/* + DAC960_QueueCommand queues Command. +*/ + +static void DAC960_QueueCommand(DAC960_Command_T *Command) +{ + DAC960_Controller_T *Controller = Command->Controller; + void *ControllerBaseAddress = Controller->BaseAddress; + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + DAC960_CommandMailbox_T *NextCommandMailbox; + CommandMailbox->Common.CommandIdentifier = Command - Controller->Commands; + switch (Controller->ControllerType) + { + case DAC960_V5_Controller: + NextCommandMailbox = Controller->NextCommandMailbox; + DAC960_V5_WriteCommandMailbox(NextCommandMailbox, CommandMailbox); + if (Controller->PreviousCommandMailbox1->Words[0] == 0 || + Controller->PreviousCommandMailbox2->Words[0] == 0) + if (Controller->DualModeMemoryMailboxInterface) + DAC960_V5_MemoryMailboxNewCommand(ControllerBaseAddress); + else DAC960_V5_HardwareMailboxNewCommand(ControllerBaseAddress); + Controller->PreviousCommandMailbox2 = Controller->PreviousCommandMailbox1; + Controller->PreviousCommandMailbox1 = NextCommandMailbox; + if (++NextCommandMailbox > Controller->LastCommandMailbox) + NextCommandMailbox = Controller->FirstCommandMailbox; + Controller->NextCommandMailbox = NextCommandMailbox; + break; + case DAC960_V4_Controller: + NextCommandMailbox = Controller->NextCommandMailbox; + DAC960_V4_WriteCommandMailbox(NextCommandMailbox, CommandMailbox); + if (Controller->PreviousCommandMailbox1->Words[0] == 0 || + Controller->PreviousCommandMailbox2->Words[0] == 0) + if (Controller->DualModeMemoryMailboxInterface) + DAC960_V4_MemoryMailboxNewCommand(ControllerBaseAddress); + else DAC960_V4_HardwareMailboxNewCommand(ControllerBaseAddress); + Controller->PreviousCommandMailbox2 = Controller->PreviousCommandMailbox1; + Controller->PreviousCommandMailbox1 = NextCommandMailbox; + if (++NextCommandMailbox > Controller->LastCommandMailbox) + NextCommandMailbox = Controller->FirstCommandMailbox; + Controller->NextCommandMailbox = NextCommandMailbox; + break; + case DAC960_V3_Controller: + while (DAC960_V3_MailboxFullP(ControllerBaseAddress)) + udelay(1); + DAC960_V3_WriteCommandMailbox(ControllerBaseAddress, CommandMailbox); + DAC960_V3_NewCommand(ControllerBaseAddress); + break; + } +} + + +/* + DAC960_ExecuteCommand executes Command and waits for completion. It + returns true on success and false on failure. +*/ + +static boolean DAC960_ExecuteCommand(DAC960_Command_T *Command) +{ + DAC960_Controller_T *Controller = Command->Controller; + Semaphore_T Semaphore = MUTEX_LOCKED; + unsigned long ProcessorFlags; + Command->Semaphore = &Semaphore; + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + DAC960_QueueCommand(Command); + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); + down(&Semaphore); + return Command->CommandStatus == DAC960_NormalCompletion; +} + + +/* + DAC960_ExecuteType3 executes a DAC960 Type 3 Command and waits for + completion. It returns true on success and false on failure. +*/ + +static boolean DAC960_ExecuteType3(DAC960_Controller_T *Controller, + DAC960_CommandOpcode_T CommandOpcode, + void *DataPointer) +{ + DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + boolean Result; + DAC960_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->Type3.CommandOpcode = CommandOpcode; + CommandMailbox->Type3.BusAddress = Virtual_to_Bus(DataPointer); + Result = DAC960_ExecuteCommand(Command); + DAC960_DeallocateCommand(Command); + return Result; +} + + +/* + DAC960_ExecuteType3D executes a DAC960 Type 3D Command and waits for + completion. It returns true on success and false on failure. +*/ + +static boolean DAC960_ExecuteType3D(DAC960_Controller_T *Controller, + DAC960_CommandOpcode_T CommandOpcode, + unsigned char Channel, + unsigned char TargetID, + void *DataPointer) +{ + DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + boolean Result; + DAC960_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->Type3D.CommandOpcode = CommandOpcode; + CommandMailbox->Type3D.Channel = Channel; + CommandMailbox->Type3D.TargetID = TargetID; + CommandMailbox->Type3D.BusAddress = Virtual_to_Bus(DataPointer); + Result = DAC960_ExecuteCommand(Command); + DAC960_DeallocateCommand(Command); + return Result; +} + + +/* + DAC960_EnableMemoryMailboxInterface enables the Memory Mailbox Interface. +*/ + +static boolean DAC960_EnableMemoryMailboxInterface(DAC960_Controller_T + *Controller) +{ + void *ControllerBaseAddress = Controller->BaseAddress; + DAC960_CommandMailbox_T *CommandMailboxesMemory; + DAC960_StatusMailbox_T *StatusMailboxesMemory; + DAC960_CommandMailbox_T CommandMailbox; + DAC960_CommandStatus_T CommandStatus; + void *SavedMemoryMailboxesAddress = NULL; + short NextCommandMailboxIndex = 0; + short NextStatusMailboxIndex = 0; + long TimeoutCounter = 1000000; + int i; + if (Controller->ControllerType == DAC960_V5_Controller) + DAC960_V5_RestoreMemoryMailboxInfo(Controller, + &SavedMemoryMailboxesAddress, + &NextCommandMailboxIndex, + &NextStatusMailboxIndex); + else DAC960_V4_RestoreMemoryMailboxInfo(Controller, + &SavedMemoryMailboxesAddress, + &NextCommandMailboxIndex, + &NextStatusMailboxIndex); + if (SavedMemoryMailboxesAddress == NULL) + CommandMailboxesMemory = + (DAC960_CommandMailbox_T *) __get_free_pages(GFP_KERNEL, 1, 0); + else CommandMailboxesMemory = SavedMemoryMailboxesAddress; + memset(CommandMailboxesMemory, 0, PAGE_SIZE << 1); + Controller->FirstCommandMailbox = CommandMailboxesMemory; + CommandMailboxesMemory += DAC960_CommandMailboxCount - 1; + Controller->LastCommandMailbox = CommandMailboxesMemory; + Controller->NextCommandMailbox = + &Controller->FirstCommandMailbox[NextCommandMailboxIndex]; + if (--NextCommandMailboxIndex < 0) + NextCommandMailboxIndex = DAC960_CommandMailboxCount - 1; + Controller->PreviousCommandMailbox1 = + &Controller->FirstCommandMailbox[NextCommandMailboxIndex]; + if (--NextCommandMailboxIndex < 0) + NextCommandMailboxIndex = DAC960_CommandMailboxCount - 1; + Controller->PreviousCommandMailbox2 = + &Controller->FirstCommandMailbox[NextCommandMailboxIndex]; + StatusMailboxesMemory = + (DAC960_StatusMailbox_T *) (CommandMailboxesMemory + 1); + Controller->FirstStatusMailbox = StatusMailboxesMemory; + StatusMailboxesMemory += DAC960_StatusMailboxCount - 1; + Controller->LastStatusMailbox = StatusMailboxesMemory; + Controller->NextStatusMailbox = + &Controller->FirstStatusMailbox[NextStatusMailboxIndex]; + if (SavedMemoryMailboxesAddress != NULL) return true; + /* Enable the Memory Mailbox Interface. */ + CommandMailbox.TypeX.CommandOpcode = 0x2B; + CommandMailbox.TypeX.CommandIdentifier = 0; + CommandMailbox.TypeX.CommandOpcode2 = 0x10; + CommandMailbox.TypeX.CommandMailboxesBusAddress = + Virtual_to_Bus(Controller->FirstCommandMailbox); + CommandMailbox.TypeX.StatusMailboxesBusAddress = + Virtual_to_Bus(Controller->FirstStatusMailbox); + for (i = 0; i < 2; i++) + switch (Controller->ControllerType) + { + case DAC960_V5_Controller: + while (--TimeoutCounter >= 0) + { + if (DAC960_V5_HardwareMailboxEmptyP(ControllerBaseAddress)) + break; + udelay(1); + } + if (TimeoutCounter < 0) return false; + DAC960_V5_WriteHardwareMailbox(ControllerBaseAddress, &CommandMailbox); + DAC960_V5_HardwareMailboxNewCommand(ControllerBaseAddress); + while (--TimeoutCounter >= 0) + { + if (DAC960_V5_HardwareMailboxStatusAvailableP( + ControllerBaseAddress)) + break; + udelay(1); + } + if (TimeoutCounter < 0) return false; + CommandStatus = DAC960_V5_ReadStatusRegister(ControllerBaseAddress); + DAC960_V5_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress); + DAC960_V5_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress); + if (CommandStatus == DAC960_NormalCompletion) return true; + Controller->DualModeMemoryMailboxInterface = true; + CommandMailbox.TypeX.CommandOpcode2 = 0x14; + break; + case DAC960_V4_Controller: + while (--TimeoutCounter >= 0) + { + if (!DAC960_V4_HardwareMailboxFullP(ControllerBaseAddress)) + break; + udelay(1); + } + if (TimeoutCounter < 0) return false; + DAC960_V4_WriteHardwareMailbox(ControllerBaseAddress, &CommandMailbox); + DAC960_V4_HardwareMailboxNewCommand(ControllerBaseAddress); + while (--TimeoutCounter >= 0) + { + if (DAC960_V4_HardwareMailboxStatusAvailableP( + ControllerBaseAddress)) + break; + udelay(1); + } + if (TimeoutCounter < 0) return false; + CommandStatus = DAC960_V4_ReadStatusRegister(ControllerBaseAddress); + DAC960_V4_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress); + DAC960_V4_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress); + if (CommandStatus == DAC960_NormalCompletion) return true; + Controller->DualModeMemoryMailboxInterface = true; + CommandMailbox.TypeX.CommandOpcode2 = 0x14; + break; + default: + break; + } + return false; +} + + +/* + DAC960_DetectControllers detects DAC960 PCI RAID Controllers by interrogating + the PCI Configuration Space for Controller Type. +*/ + +static void DAC960_DetectControllers(DAC960_ControllerType_T ControllerType) +{ + unsigned short VendorID = 0, DeviceID = 0, Index = 0; + unsigned char Bus, DeviceFunction, IRQ_Channel; + unsigned int BaseAddress0, BaseAddress1; + unsigned int MemoryWindowSize = 0; + switch (ControllerType) + { + case DAC960_V5_Controller: + VendorID = PCI_VENDOR_ID_DEC; + DeviceID = PCI_DEVICE_ID_DEC_21285; + MemoryWindowSize = DAC960_V5_RegisterWindowSize; + break; + case DAC960_V4_Controller: + VendorID = PCI_VENDOR_ID_MYLEX; + DeviceID = PCI_DEVICE_ID_MYLEX_DAC960P_V4; + MemoryWindowSize = DAC960_V4_RegisterWindowSize; + break; + case DAC960_V3_Controller: + VendorID = PCI_VENDOR_ID_MYLEX; + DeviceID = PCI_DEVICE_ID_MYLEX_DAC960P_V3; + MemoryWindowSize = DAC960_V3_RegisterWindowSize; + break; + } + while (pcibios_find_device(VendorID, DeviceID, + Index++, &Bus, &DeviceFunction) == 0) + { + DAC960_Controller_T *Controller = (DAC960_Controller_T *) + kmalloc(sizeof(DAC960_Controller_T), GFP_ATOMIC); + DAC960_IO_Address_T IO_Address = 0; + DAC960_PCI_Address_T PCI_Address = 0; + unsigned char Device = DeviceFunction >> 3; + unsigned char Function = DeviceFunction & 0x7; + unsigned short SubsystemVendorID, SubsystemDeviceID; + pcibios_read_config_dword(Bus, DeviceFunction, + PCI_BASE_ADDRESS_0, &BaseAddress0); + pcibios_read_config_dword(Bus, DeviceFunction, + PCI_BASE_ADDRESS_1, &BaseAddress1); + pcibios_read_config_word(Bus, DeviceFunction, + PCI_SUBSYSTEM_VENDOR_ID, &SubsystemVendorID); + pcibios_read_config_word(Bus, DeviceFunction, + PCI_SUBSYSTEM_ID, &SubsystemDeviceID); + pcibios_read_config_byte(Bus, DeviceFunction, + PCI_INTERRUPT_LINE, &IRQ_Channel); + switch (ControllerType) + { + case DAC960_V5_Controller: + if (!(SubsystemVendorID == PCI_VENDOR_ID_MYLEX && + SubsystemDeviceID == PCI_DEVICE_ID_MYLEX_DAC960P_V5)) + goto Ignore; + PCI_Address = BaseAddress0 & PCI_BASE_ADDRESS_MEM_MASK; + break; + case DAC960_V4_Controller: + PCI_Address = BaseAddress0 & PCI_BASE_ADDRESS_MEM_MASK; + break; + case DAC960_V3_Controller: + IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK; + PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK; + break; + } + if (DAC960_ControllerCount == DAC960_MaxControllers) + { + DAC960_Error("More than %d DAC960 Controllers detected - " + "ignoring from Controller at\n", + NULL, DAC960_MaxControllers); + goto Ignore; + } + if (Controller == NULL) + { + DAC960_Error("Unable to allocate Controller structure for " + "Controller at\n", NULL); + goto Ignore; + } + memset(Controller, 0, sizeof(DAC960_Controller_T)); + Controller->ControllerNumber = DAC960_ControllerCount; + DAC960_Controllers[DAC960_ControllerCount++] = Controller; + DAC960_AnnounceDriver(Controller); + Controller->ControllerType = ControllerType; + Controller->IO_Address = IO_Address; + Controller->PCI_Address = PCI_Address; + Controller->Bus = Bus; + Controller->Device = Device; + Controller->Function = Function; + sprintf(Controller->ControllerName, "c%d", Controller->ControllerNumber); + /* + Acquire shared access to the IRQ Channel. + */ + if (IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS) + { + DAC960_Error("IRQ Channel %d illegal for Controller at\n", + Controller, IRQ_Channel); + goto Failure; + } + strcpy(Controller->FullModelName, "DAC960"); + if (request_irq(IRQ_Channel, DAC960_InterruptHandler, + SA_INTERRUPT | SA_SHIRQ, Controller->FullModelName, + Controller) < 0) + { + DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n", + Controller, IRQ_Channel); + goto Failure; + } + Controller->IRQ_Channel = IRQ_Channel; + /* + Map the Controller Register Window. + */ + if (MemoryWindowSize < PAGE_SIZE) + MemoryWindowSize = PAGE_SIZE; + Controller->MemoryMappedAddress = + ioremap_nocache(PCI_Address & PAGE_MASK, MemoryWindowSize); + Controller->BaseAddress = + Controller->MemoryMappedAddress + (PCI_Address & ~PAGE_MASK); + if (Controller->MemoryMappedAddress == NULL) + { + DAC960_Error("Unable to map Controller Register Window for " + "Controller at\n", Controller); + goto Failure; + } + switch (ControllerType) + { + case DAC960_V5_Controller: + DAC960_V5_DisableInterrupts(Controller->BaseAddress); + if (!DAC960_EnableMemoryMailboxInterface(Controller)) + { + DAC960_Error("Unable to Enable Memory Mailbox Interface " + "for Controller at\n", Controller); + goto Failure; + } + DAC960_V5_EnableInterrupts(Controller->BaseAddress); + break; + case DAC960_V4_Controller: + DAC960_V4_DisableInterrupts(Controller->BaseAddress); + if (!DAC960_EnableMemoryMailboxInterface(Controller)) + { + DAC960_Error("Unable to Enable Memory Mailbox Interface " + "for Controller at\n", Controller); + goto Failure; + } + DAC960_V4_EnableInterrupts(Controller->BaseAddress); + break; + case DAC960_V3_Controller: + request_region(Controller->IO_Address, 0x80, + Controller->FullModelName); + DAC960_V3_EnableInterrupts(Controller->BaseAddress); + break; + } + DAC960_ActiveControllerCount++; + Controller->Commands[0].Controller = Controller; + Controller->Commands[0].Next = NULL; + Controller->FreeCommands = &Controller->Commands[0]; + continue; + Failure: + if (IO_Address == 0) + DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A " + "PCI Address 0x%X\n", Controller, + Bus, Device, Function, PCI_Address); + else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address " + "0x%X PCI Address 0x%X\n", Controller, + Bus, Device, Function, IO_Address, PCI_Address); + if (Controller == NULL) break; + if (Controller->IRQ_Channel > 0) + free_irq(IRQ_Channel, Controller); + if (Controller->MemoryMappedAddress != NULL) + iounmap(Controller->MemoryMappedAddress); + DAC960_Controllers[Controller->ControllerNumber] = NULL; + Ignore: + kfree(Controller); + } +} + + +/* + DAC960_ReadControllerConfiguration reads the Configuration Information + from Controller and initializes the Controller structure. +*/ + +static boolean DAC960_ReadControllerConfiguration(DAC960_Controller_T + *Controller) +{ + DAC960_Enquiry2_T Enquiry2; + DAC960_Config2_T Config2; + int LogicalDriveNumber, Channel, TargetID; + if (!DAC960_ExecuteType3(Controller, DAC960_Enquiry, + &Controller->Enquiry[0])) + return DAC960_Failure(Controller, "ENQUIRY"); + if (!DAC960_ExecuteType3(Controller, DAC960_Enquiry2, &Enquiry2)) + return DAC960_Failure(Controller, "ENQUIRY2"); + if (!DAC960_ExecuteType3(Controller, DAC960_ReadConfig2, &Config2)) + return DAC960_Failure(Controller, "READ CONFIG2"); + if (!DAC960_ExecuteType3(Controller, DAC960_GetLogicalDriveInformation, + &Controller->LogicalDriveInformation[0])) + return DAC960_Failure(Controller, "GET LOGICAL DRIVE INFORMATION"); + for (Channel = 0; Channel < Enquiry2.ActualChannels; Channel++) + for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++) + if (!DAC960_ExecuteType3D(Controller, DAC960_GetDeviceState, + Channel, TargetID, + &Controller->DeviceState[0][Channel][TargetID])) + return DAC960_Failure(Controller, "GET DEVICE STATE"); + /* + Initialize the Controller Model Name and Full Model Name fields. + */ + switch (Enquiry2.HardwareID.SubModel) + { + case DAC960_P_PD_PU: + if (Enquiry2.SCSICapability.BusSpeed == DAC960_Ultra) + strcpy(Controller->ModelName, "DAC960PU"); + else strcpy(Controller->ModelName, "DAC960PD"); + break; + case DAC960_PL: + strcpy(Controller->ModelName, "DAC960PL"); + break; + case DAC960_PG: + strcpy(Controller->ModelName, "DAC960PG"); + break; + case DAC960_PJ: + strcpy(Controller->ModelName, "DAC960PJ"); + break; + case DAC960_PR: + strcpy(Controller->ModelName, "DAC960PR"); + break; + case DAC960_PT: + strcpy(Controller->ModelName, "DAC960PT"); + break; + case DAC960_PTL0: + strcpy(Controller->ModelName, "DAC960PTL0"); + break; + case DAC960_PRL: + strcpy(Controller->ModelName, "DAC960PRL"); + break; + case DAC960_PTL1: + strcpy(Controller->ModelName, "DAC960PTL1"); + break; + case DAC1164_P: + strcpy(Controller->ModelName, "DAC1164P"); + break; + default: + return DAC960_Failure(Controller, "MODEL VERIFICATION"); + } + strcpy(Controller->FullModelName, "Mylex "); + strcat(Controller->FullModelName, Controller->ModelName); + /* + Initialize the Controller Firmware Version field and verify that it + is a supported firmware version. The supported firmware versions are: + + DAC1164P 5.06 and above + DAC960PTL/PJ/PG 4.06 and above + DAC960PU/PD/PL 3.51 and above + */ + sprintf(Controller->FirmwareVersion, "%d.%02d-%c-%02d", + Enquiry2.FirmwareID.MajorVersion, Enquiry2.FirmwareID.MinorVersion, + Enquiry2.FirmwareID.FirmwareType, Enquiry2.FirmwareID.TurnID); + if (!((Controller->FirmwareVersion[0] == '5' && + strcmp(Controller->FirmwareVersion, "5.06") >= 0) || + (Controller->FirmwareVersion[0] == '4' && + strcmp(Controller->FirmwareVersion, "4.06") >= 0) || + (Controller->FirmwareVersion[0] == '3' && + strcmp(Controller->FirmwareVersion, "3.51") >= 0))) + { + DAC960_Failure(Controller, "FIRMWARE VERSION VERIFICATION"); + DAC960_Error("Firmware Version = '%s'\n", Controller, + Controller->FirmwareVersion); + return false; + } + /* + Initialize the Controller Channels, Memory Size, and SAF-TE Fault + Management Enabled fields. + */ + Controller->Channels = Enquiry2.ActualChannels; + Controller->MemorySize = Enquiry2.MemorySize >> 20; + Controller->SAFTE_FaultManagementEnabled = + Enquiry2.FaultManagementType == DAC960_SAFTE; + /* + Initialize the Controller Queue Depth, Driver Queue Depth, Logical Drive + Count, Maximum Blocks per Command, and Maximum Scatter/Gather Segments. + The Driver Queue Depth must be at most one less than the Controller Queue + Depth to allow for an automatic drive rebuild operation. + */ + Controller->ControllerQueueDepth = Controller->Enquiry[0].MaxCommands; + Controller->DriverQueueDepth = Controller->ControllerQueueDepth - 1; + Controller->LogicalDriveCount = Controller->Enquiry[0].NumberOfLogicalDrives; + Controller->MaxBlocksPerCommand = Enquiry2.MaxBlocksPerCommand; + Controller->MaxScatterGatherSegments = Enquiry2.MaxScatterGatherEntries; + /* + Initialize the Stripe Size, Segment Size, and Geometry Translation. + */ + Controller->StripeSize = Config2.BlocksPerStripe * Config2.BlockFactor + >> (10 - DAC960_BlockSizeBits); + Controller->SegmentSize = Config2.BlocksPerCacheLine * Config2.BlockFactor + >> (10 - DAC960_BlockSizeBits); + switch (Config2.DriveGeometry) + { + case DAC960_Geometry_128_32: + Controller->GeometryTranslationHeads = 128; + Controller->GeometryTranslationSectors = 32; + break; + case DAC960_Geometry_255_63: + Controller->GeometryTranslationHeads = 255; + Controller->GeometryTranslationSectors = 63; + break; + default: + return DAC960_Failure(Controller, "CONFIG2 DRIVE GEOMETRY"); + } + /* + Initialize the Logical Drive Initial State. + */ + for (LogicalDriveNumber = 0; + LogicalDriveNumber < Controller->LogicalDriveCount; + LogicalDriveNumber++) + Controller->LogicalDriveInitialState[LogicalDriveNumber] = + Controller->LogicalDriveInformation[0] + [LogicalDriveNumber].LogicalDriveState; + return true; +} + + +/* + DAC960_ReportControllerConfiguration reports the configuration of + Controller. +*/ + +static boolean DAC960_ReportControllerConfiguration(DAC960_Controller_T + *Controller) +{ + int LogicalDriveNumber, Channel, TargetID; + DAC960_Info("Configuring Mylex %s PCI RAID Controller\n", + Controller, Controller->ModelName); + DAC960_Info(" Firmware Version: %s, Channels: %d, Memory Size: %dMB\n", + Controller, Controller->FirmwareVersion, + Controller->Channels, Controller->MemorySize); + DAC960_Info(" PCI Bus: %d, Device: %d, Function: %d, I/O Address: ", + Controller, Controller->Bus, + Controller->Device, Controller->Function); + if (Controller->IO_Address == 0) + DAC960_Info("Unassigned\n", Controller); + else DAC960_Info("0x%X\n", Controller, Controller->IO_Address); + DAC960_Info(" PCI Address: 0x%X mapped at 0x%lX, IRQ Channel: %d\n", + Controller, Controller->PCI_Address, + (unsigned long) Controller->BaseAddress, + Controller->IRQ_Channel); + DAC960_Info(" Controller Queue Depth: %d, " + "Maximum Blocks per Command: %d\n", + Controller, Controller->ControllerQueueDepth, + Controller->MaxBlocksPerCommand); + DAC960_Info(" Driver Queue Depth: %d, " + "Maximum Scatter/Gather Segments: %d\n", + Controller, Controller->DriverQueueDepth, + Controller->MaxScatterGatherSegments); + DAC960_Info(" Stripe Size: %dKB, Segment Size: %dKB, " + "BIOS Geometry: %d/%d\n", Controller, + Controller->StripeSize, + Controller->SegmentSize, + Controller->GeometryTranslationHeads, + Controller->GeometryTranslationSectors); + if (Controller->SAFTE_FaultManagementEnabled) + DAC960_Info(" SAF-TE Fault Management Enabled\n", Controller); + DAC960_Info(" Physical Devices:\n", Controller); + for (Channel = 0; Channel < Controller->Channels; Channel++) + for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++) + { + DAC960_DeviceState_T *DeviceState = + &Controller->DeviceState[Controller->DeviceStateIndex] + [Channel][TargetID]; + if (!DeviceState->Present) continue; + switch (DeviceState->DeviceType) + { + case DAC960_OtherType: + DAC960_Info(" %d:%d - Other\n", Controller, Channel, TargetID); + break; + case DAC960_DiskType: + DAC960_Info(" %d:%d - Disk: %s, %d blocks\n", Controller, + Channel, TargetID, + (DeviceState->DeviceState == DAC960_Device_Dead + ? "Dead" + : DeviceState->DeviceState == DAC960_Device_WriteOnly + ? "Write-Only" + : DeviceState->DeviceState == DAC960_Device_Online + ? "Online" : "Standby"), + DeviceState->DiskSize); + break; + case DAC960_SequentialType: + DAC960_Info(" %d:%d - Sequential\n", Controller, + Channel, TargetID); + break; + case DAC960_CDROM_or_WORM_Type: + DAC960_Info(" %d:%d - CD-ROM or WORM\n", Controller, + Channel, TargetID); + break; + } + + } + DAC960_Info(" Logical Drives:\n", Controller); + for (LogicalDriveNumber = 0; + LogicalDriveNumber < Controller->LogicalDriveCount; + LogicalDriveNumber++) + { + DAC960_LogicalDriveInformation_T *LogicalDriveInformation = + &Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex][LogicalDriveNumber]; + DAC960_Info(" /dev/rd/c%dd%d: RAID-%d, %s, %d blocks, %s\n", + Controller, Controller->ControllerNumber, LogicalDriveNumber, + LogicalDriveInformation->RAIDLevel, + (LogicalDriveInformation->LogicalDriveState == + DAC960_LogicalDrive_Online + ? "Online" + : LogicalDriveInformation->LogicalDriveState == + DAC960_LogicalDrive_Critical + ? "Critical" : "Offline"), + LogicalDriveInformation->LogicalDriveSize, + (LogicalDriveInformation->WriteBack + ? "Write Back" : "Write Thru")); + } + return true; +} + + +/* + DAC960_RegisterBlockDevice registers the Block Device structures + associated with Controller. +*/ + +static boolean DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller) +{ + static void (*RequestFunctions[DAC960_MaxControllers])(void) = + { DAC960_RequestFunction0, DAC960_RequestFunction1, + DAC960_RequestFunction2, DAC960_RequestFunction3, + DAC960_RequestFunction4, DAC960_RequestFunction5, + DAC960_RequestFunction6, DAC960_RequestFunction7 }; + int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber; + GenericDiskInfo_T *GenericDiskInfo; + int MinorNumber; + /* + Register the Block Device Major Number for this DAC960 Controller. + */ + if (register_blkdev(MajorNumber, "rd", &DAC960_FileOperations) < 0) + { + DAC960_Error("UNABLE TO ACQUIRE MAJOR NUMBER %d - DETACHING\n", + Controller, MajorNumber); + return false; + } + /* + Initialize the I/O Request Function. + */ + blk_dev[MajorNumber].request_fn = + RequestFunctions[Controller->ControllerNumber]; + /* + Initialize the Disk Partitions array, Partition Sizes array, Block Sizes + array, Max Sectors per Request array, and Max Segments per Request array. + */ + for (MinorNumber = 0; MinorNumber < DAC960_MinorCount; MinorNumber++) + { + Controller->BlockSizes[MinorNumber] = BLOCK_SIZE; + Controller->MaxSectorsPerRequest[MinorNumber] = + Controller->MaxBlocksPerCommand; + Controller->MaxSegmentsPerRequest[MinorNumber] = + Controller->MaxScatterGatherSegments; + } + Controller->GenericDiskInfo.part = Controller->DiskPartitions; + Controller->GenericDiskInfo.sizes = Controller->PartitionSizes; + blksize_size[MajorNumber] = Controller->BlockSizes; + max_sectors[MajorNumber] = Controller->MaxSectorsPerRequest; + max_segments[MajorNumber] = Controller->MaxSegmentsPerRequest; + /* + Initialize Read Ahead to 128 sectors. + */ + read_ahead[MajorNumber] = 128; + /* + Complete initialization of the Generic Disk Information structure. + */ + Controller->GenericDiskInfo.major = MajorNumber; + Controller->GenericDiskInfo.major_name = "rd"; + Controller->GenericDiskInfo.minor_shift = DAC960_MaxPartitionsBits; + Controller->GenericDiskInfo.max_p = DAC960_MaxPartitions; + Controller->GenericDiskInfo.max_nr = DAC960_MaxLogicalDrives; + Controller->GenericDiskInfo.init = DAC960_InitializeGenericDiskInfo; + Controller->GenericDiskInfo.nr_real = Controller->LogicalDriveCount; + Controller->GenericDiskInfo.real_devices = Controller; + Controller->GenericDiskInfo.next = NULL; + /* + Install the Generic Disk Information structure at the end of the list. + */ + if ((GenericDiskInfo = gendisk_head) != NULL) + { + while (GenericDiskInfo->next != NULL) + GenericDiskInfo = GenericDiskInfo->next; + GenericDiskInfo->next = &Controller->GenericDiskInfo; + } + else gendisk_head = &Controller->GenericDiskInfo; + /* + Indicate the Block Device Registration completed successfully, + */ + return true; +} + + +/* + DAC960_UnregisterBlockDevice unregisters the Block Device structures + associated with Controller. +*/ + +static void DAC960_UnregisterBlockDevice(DAC960_Controller_T *Controller) +{ + int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber; + /* + Unregister the Block Device Major Number for this DAC960 Controller. + */ + unregister_blkdev(MajorNumber, "rd"); + /* + Remove the I/O Request Function. + */ + blk_dev[MajorNumber].request_fn = NULL; + /* + Remove the Disk Partitions array, Partition Sizes array, Block Sizes + array, Max Sectors per Request array, and Max Segments per Request array. + */ + Controller->GenericDiskInfo.part = NULL; + Controller->GenericDiskInfo.sizes = NULL; + blk_size[MajorNumber] = NULL; + blksize_size[MajorNumber] = NULL; + max_sectors[MajorNumber] = NULL; + max_segments[MajorNumber] = NULL; + /* + Remove the Generic Disk Information structure from the list. + */ + if (gendisk_head != &Controller->GenericDiskInfo) + { + GenericDiskInfo_T *GenericDiskInfo = gendisk_head; + while (GenericDiskInfo != NULL && + GenericDiskInfo->next != &Controller->GenericDiskInfo) + GenericDiskInfo = GenericDiskInfo->next; + if (GenericDiskInfo != NULL) + GenericDiskInfo->next = GenericDiskInfo->next->next; + } + else gendisk_head = Controller->GenericDiskInfo.next; +} + + +/* + DAC960_InitializeController initializes Controller. +*/ + +static void DAC960_InitializeController(DAC960_Controller_T *Controller) +{ + if (DAC960_ReadControllerConfiguration(Controller) && + DAC960_ReportControllerConfiguration(Controller) && + DAC960_RegisterBlockDevice(Controller)) + { + /* + Initialize the Command structures. + */ + DAC960_Command_T *Commands = Controller->Commands; + int CommandIdentifier; + Controller->FreeCommands = NULL; + for (CommandIdentifier = 0; + CommandIdentifier < Controller->DriverQueueDepth; + CommandIdentifier++) + { + Commands[CommandIdentifier].Controller = Controller; + Commands[CommandIdentifier].Next = Controller->FreeCommands; + Controller->FreeCommands = &Commands[CommandIdentifier]; + } + /* + Initialize the Monitoring Timer. + */ + init_timer(&Controller->MonitoringTimer); + Controller->MonitoringTimer.expires = + jiffies + DAC960_MonitoringTimerInterval; + Controller->MonitoringTimer.data = (unsigned long) Controller; + Controller->MonitoringTimer.function = DAC960_MonitoringTimerFunction; + add_timer(&Controller->MonitoringTimer); + Controller->ControllerInitialized = true; + } + else DAC960_FinalizeController(Controller); +} + + +/* + DAC960_FinalizeController finalizes Controller. +*/ + +static void DAC960_FinalizeController(DAC960_Controller_T *Controller) +{ + if (Controller->ControllerInitialized) + { + del_timer(&Controller->MonitoringTimer); + DAC960_Notice("Flushing Cache...", Controller); + DAC960_ExecuteType3(Controller, DAC960_Flush, NULL); + DAC960_Notice("done\n", Controller); + switch (Controller->ControllerType) + { + case DAC960_V5_Controller: + if (!Controller->DualModeMemoryMailboxInterface) + DAC960_V5_SaveMemoryMailboxInfo(Controller); + break; + case DAC960_V4_Controller: + if (!Controller->DualModeMemoryMailboxInterface) + DAC960_V4_SaveMemoryMailboxInfo(Controller); + break; + case DAC960_V3_Controller: + break; + } + } + free_irq(Controller->IRQ_Channel, Controller); + iounmap(Controller->MemoryMappedAddress); + if (Controller->IO_Address > 0) + release_region(Controller->IO_Address, 0x80); + DAC960_UnregisterBlockDevice(Controller); + DAC960_Controllers[Controller->ControllerNumber] = NULL; + kfree(Controller); +} + + +/* + DAC960_Initialize initializes the DAC960 Driver. +*/ + +void DAC960_Initialize(void) +{ + int ControllerNumber; + DAC960_DetectControllers(DAC960_V5_Controller); + DAC960_DetectControllers(DAC960_V4_Controller); + DAC960_DetectControllers(DAC960_V3_Controller); + if (DAC960_ActiveControllerCount == 0) return; + for (ControllerNumber = 0; + ControllerNumber < DAC960_ControllerCount; + ControllerNumber++) + if (DAC960_Controllers[ControllerNumber] != NULL) + DAC960_InitializeController(DAC960_Controllers[ControllerNumber]); + DAC960_CreateProcEntries(); +} + + +/* + DAC960_Finalize finalizes the DAC960 Driver. +*/ + +void DAC960_Finalize(void) +{ + int ControllerNumber; + if (DAC960_ActiveControllerCount == 0) return; + for (ControllerNumber = 0; + ControllerNumber < DAC960_ControllerCount; + ControllerNumber++) + if (DAC960_Controllers[ControllerNumber] != NULL) + DAC960_FinalizeController(DAC960_Controllers[ControllerNumber]); + DAC960_DestroyProcEntries(); +} + + +/* + DAC960_ProcessRequest attempts to remove one I/O Request from Controller's + I/O Request Queue and queues it to the Controller. WaitForCommand is true if + this function should wait for a Command to become available if necessary. + This function returns true if an I/O Request was queued and false otherwise. +*/ + +static boolean DAC960_ProcessRequest(DAC960_Controller_T *Controller, + boolean WaitForCommand) +{ + IO_Request_T **RequestQueuePointer = + &blk_dev[DAC960_MAJOR + Controller->ControllerNumber].current_request; + IO_Request_T *Request; + DAC960_Command_T *Command; + char *RequestBuffer; + while (true) + { + Request = *RequestQueuePointer; + if (Request == NULL || Request->rq_status == RQ_INACTIVE) return false; + Command = DAC960_AllocateCommand(Controller); + if (Command != NULL) break; + if (!WaitForCommand) return false; + sleep_on(&Controller->CommandWaitQueue); + } + DAC960_ClearCommand(Command); + if (Request->cmd == READ) + Command->CommandType = DAC960_ReadCommand; + else Command->CommandType = DAC960_WriteCommand; + Command->Semaphore = Request->sem; + Command->LogicalDriveNumber = DAC960_LogicalDriveNumber(Request->rq_dev); + Command->BlockNumber = + Request->sector + + Controller->GenericDiskInfo.part[MINOR(Request->rq_dev)].start_sect; + Command->BlockCount = Request->nr_sectors; + Command->SegmentCount = Request->nr_segments; + Command->BufferHeader = Request->bh; + RequestBuffer = Request->buffer; + Request->rq_status = RQ_INACTIVE; + *RequestQueuePointer = Request->next; + wake_up(&wait_for_request); + if (Command->SegmentCount == 1) + { + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + if (Command->CommandType == DAC960_ReadCommand) + CommandMailbox->Type5.CommandOpcode = DAC960_Read; + else CommandMailbox->Type5.CommandOpcode = DAC960_Write; + CommandMailbox->Type5.LD.TransferLength = Command->BlockCount; + CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber; + CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber; + CommandMailbox->Type5.BusAddress = Virtual_to_Bus(RequestBuffer); + } + else + { + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + DAC960_ScatterGatherSegment_T + *ScatterGatherList = Command->ScatterGatherList; + BufferHeader_T *BufferHeader = Command->BufferHeader; + char *LastDataEndPointer = NULL; + int SegmentNumber = 0; + if (Command->CommandType == DAC960_ReadCommand) + CommandMailbox->Type5.CommandOpcode = DAC960_ReadWithOldScatterGather; + else + CommandMailbox->Type5.CommandOpcode = DAC960_WriteWithOldScatterGather; + CommandMailbox->Type5.LD.TransferLength = Command->BlockCount; + CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber; + CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber; + CommandMailbox->Type5.BusAddress = Virtual_to_Bus(ScatterGatherList); + CommandMailbox->Type5.ScatterGatherCount = Command->SegmentCount; + while (BufferHeader != NULL) + { + if (BufferHeader->b_data == LastDataEndPointer) + { + ScatterGatherList[SegmentNumber-1].SegmentByteCount += + BufferHeader->b_size; + LastDataEndPointer += BufferHeader->b_size; + } + else + { + ScatterGatherList[SegmentNumber].SegmentDataPointer = + Virtual_to_Bus(BufferHeader->b_data); + ScatterGatherList[SegmentNumber].SegmentByteCount = + BufferHeader->b_size; + LastDataEndPointer = BufferHeader->b_data + BufferHeader->b_size; + if (SegmentNumber++ > Controller->MaxScatterGatherSegments) + panic("DAC960: Scatter/Gather Segment Overflow\n"); + } + BufferHeader = BufferHeader->b_reqnext; + } + if (SegmentNumber != Command->SegmentCount) + panic("DAC960: SegmentNumber != SegmentCount\n"); + } + DAC960_QueueCommand(Command); + return true; +} + + +/* + DAC960_ProcessRequests attempts to remove as many I/O Requests as possible + from Controller's I/O Request Queue and queue them to the Controller. +*/ + +static inline void DAC960_ProcessRequests(DAC960_Controller_T *Controller) +{ + int Counter = 0; + while (DAC960_ProcessRequest(Controller, Counter++ == 0)) ; +} + + +/* + DAC960_RequestFunction0 is the I/O Request Function for DAC960 Controller 0. +*/ + +static void DAC960_RequestFunction0(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[0]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction1 is the I/O Request Function for DAC960 Controller 1. +*/ + +static void DAC960_RequestFunction1(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[1]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction2 is the I/O Request Function for DAC960 Controller 2. +*/ + +static void DAC960_RequestFunction2(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[2]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction3 is the I/O Request Function for DAC960 Controller 3. +*/ + +static void DAC960_RequestFunction3(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[3]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction4 is the I/O Request Function for DAC960 Controller 4. +*/ + +static void DAC960_RequestFunction4(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[4]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction5 is the I/O Request Function for DAC960 Controller 5. +*/ + +static void DAC960_RequestFunction5(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[5]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction6 is the I/O Request Function for DAC960 Controller 6. +*/ + +static void DAC960_RequestFunction6(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[6]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction7 is the I/O Request Function for DAC960 Controller 7. +*/ + +static void DAC960_RequestFunction7(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[7]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_ReadWriteError prints an appropriate error message for Command when + an error occurs on a Read or Write operation. +*/ + +static void DAC960_ReadWriteError(DAC960_Command_T *Command) +{ + DAC960_Controller_T *Controller = Command->Controller; + char *CommandName = "UNKNOWN"; + switch (Command->CommandType) + { + case DAC960_ReadCommand: + case DAC960_ReadRetryCommand: + CommandName = "READ"; + break; + case DAC960_WriteCommand: + case DAC960_WriteRetryCommand: + CommandName = "WRITE"; + break; + case DAC960_MonitoringCommand: + case DAC960_ImmediateCommand: + break; + } + switch (Command->CommandStatus) + { + case DAC960_IrrecoverableDataError: + DAC960_Error("Irrecoverable Data Error on %s:\n", + Controller, CommandName); + break; + case DAC960_LogicalDriveNonexistentOrOffline: + DAC960_Error("Logical Drive Nonexistent or Offline on %s:\n", + Controller, CommandName); + break; + case DAC960_AccessBeyondEndOfLogicalDrive: + DAC960_Error("Attempt to Access Beyond End of Logical Drive " + "on %s:\n", Controller, CommandName); + break; + case DAC960_BadDataEncountered: + DAC960_Error("Bad Data Encountered on %s:\n", Controller, CommandName); + break; + default: + DAC960_Error("Unexpected Error Status %04X on %s:\n", + Controller, Command->CommandStatus, CommandName); + break; + } + DAC960_Error(" /dev/rd/c%dd%d: absolute blocks %d..%d\n", + Controller, Controller->ControllerNumber, + Command->LogicalDriveNumber, Command->BlockNumber, + Command->BlockNumber + Command->BlockCount - 1); + if (DAC960_PartitionNumber(Command->BufferHeader->b_rdev) > 0) + DAC960_Error(" /dev/rd/c%dd%dp%d: relative blocks %d..%d\n", + Controller, Controller->ControllerNumber, + Command->LogicalDriveNumber, + DAC960_PartitionNumber(Command->BufferHeader->b_rdev), + Command->BufferHeader->b_rsector, + Command->BufferHeader->b_rsector + Command->BlockCount - 1); +} + + +/* + DAC960_ProcessCompletedBuffer performs completion processing for an + individual Buffer. +*/ + +static inline void DAC960_ProcessCompletedBuffer(BufferHeader_T *BufferHeader, + boolean SuccessfulIO) +{ + mark_buffer_uptodate(BufferHeader, SuccessfulIO); + unlock_buffer(BufferHeader); +} + + +/* + DAC960_ProcessCompletedCommand performs completion processing for Command. +*/ + +static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command) +{ + DAC960_Controller_T *Controller = Command->Controller; + DAC960_CommandType_T CommandType = Command->CommandType; + DAC960_CommandStatus_T CommandStatus = Command->CommandStatus; + BufferHeader_T *BufferHeader = Command->BufferHeader; + if (CommandType == DAC960_ReadCommand || + CommandType == DAC960_WriteCommand) + { + if (CommandStatus == DAC960_NormalCompletion) + { + /* + Perform completion processing for all buffers in this I/O Request. + */ + while (BufferHeader != NULL) + { + BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext; + BufferHeader->b_reqnext = NULL; + DAC960_ProcessCompletedBuffer(BufferHeader, true); + BufferHeader = NextBufferHeader; + } + /* + Wake up requestor for swap file paging requests. + */ + if (Command->Semaphore != NULL) + { + up(Command->Semaphore); + Command->Semaphore = NULL; + } + add_blkdev_randomness(DAC960_MAJOR + Controller->ControllerNumber); + } + else if ((CommandStatus == DAC960_IrrecoverableDataError || + CommandStatus == DAC960_BadDataEncountered) && + BufferHeader != NULL && + BufferHeader->b_reqnext != NULL) + { + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + if (CommandType == DAC960_ReadCommand) + { + Command->CommandType = DAC960_ReadRetryCommand; + CommandMailbox->Type5.CommandOpcode = DAC960_Read; + } + else + { + Command->CommandType = DAC960_WriteRetryCommand; + CommandMailbox->Type5.CommandOpcode = DAC960_Write; + } + Command->BlockCount = BufferHeader->b_size >> DAC960_BlockSizeBits; + CommandMailbox->Type5.LD.TransferLength = Command->BlockCount; + CommandMailbox->Type5.BusAddress = + Virtual_to_Bus(BufferHeader->b_data); + DAC960_QueueCommand(Command); + return; + } + else + { + if (CommandStatus != DAC960_LogicalDriveNonexistentOrOffline) + DAC960_ReadWriteError(Command); + /* + Perform completion processing for all buffers in this I/O Request. + */ + while (BufferHeader != NULL) + { + BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext; + BufferHeader->b_reqnext = NULL; + DAC960_ProcessCompletedBuffer(BufferHeader, false); + BufferHeader = NextBufferHeader; + } + /* + Wake up requestor for swap file paging requests. + */ + if (Command->Semaphore != NULL) + { + up(Command->Semaphore); + Command->Semaphore = NULL; + } + } + } + else if (CommandType == DAC960_ReadRetryCommand || + CommandType == DAC960_WriteRetryCommand) + { + BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext; + BufferHeader->b_reqnext = NULL; + /* + Perform completion processing for this single buffer. + */ + if (CommandStatus == DAC960_NormalCompletion) + DAC960_ProcessCompletedBuffer(BufferHeader, true); + else + { + if (CommandStatus != DAC960_LogicalDriveNonexistentOrOffline) + DAC960_ReadWriteError(Command); + DAC960_ProcessCompletedBuffer(BufferHeader, false); + } + if (NextBufferHeader != NULL) + { + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + Command->BlockNumber += + BufferHeader->b_size >> DAC960_BlockSizeBits; + Command->BlockCount = + NextBufferHeader->b_size >> DAC960_BlockSizeBits; + Command->BufferHeader = NextBufferHeader; + CommandMailbox->Type5.LD.TransferLength = Command->BlockCount; + CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber; + CommandMailbox->Type5.BusAddress = + Virtual_to_Bus(NextBufferHeader->b_data); + DAC960_QueueCommand(Command); + return; + } + } + else if (CommandType == DAC960_MonitoringCommand) + { + DAC960_CommandOpcode_T CommandOpcode = + Command->CommandMailbox.Common.CommandOpcode; + unsigned int OldCriticalLogicalDriveCount = 0; + unsigned int NewCriticalLogicalDriveCount = 0; + if (CommandOpcode == DAC960_Enquiry) + { + DAC960_Enquiry_T *OldEnquiry = + &Controller->Enquiry[Controller->EnquiryIndex]; + DAC960_Enquiry_T *NewEnquiry = + &Controller->Enquiry[Controller->EnquiryIndex ^= 1]; + OldCriticalLogicalDriveCount = OldEnquiry->CriticalLogicalDriveCount; + NewCriticalLogicalDriveCount = NewEnquiry->CriticalLogicalDriveCount; + if (NewEnquiry->StatusFlags.DeferredWriteError != + OldEnquiry->StatusFlags.DeferredWriteError) + DAC960_Critical("Deferred Write Error Flag is now %s\n", Controller, + (NewEnquiry->StatusFlags.DeferredWriteError + ? "TRUE" : "FALSE")); + if ((NewCriticalLogicalDriveCount > 0 || + NewCriticalLogicalDriveCount != OldCriticalLogicalDriveCount) || + (NewEnquiry->OfflineLogicalDriveCount > 0 || + NewEnquiry->OfflineLogicalDriveCount != + OldEnquiry->OfflineLogicalDriveCount) || + (NewEnquiry->DeadDriveCount > 0 || + NewEnquiry->DeadDriveCount != + OldEnquiry->DeadDriveCount) || + (NewEnquiry->EventLogSequenceNumber != + OldEnquiry->EventLogSequenceNumber) || + Controller->MonitoringTimerCount == 0 || + (jiffies - Controller->SecondaryMonitoringTime + >= DAC960_SecondaryMonitoringInterval)) + { + Controller->NeedLogicalDriveInformation = true; + Controller->NewEventLogSequenceNumber = + NewEnquiry->EventLogSequenceNumber; + Controller->NeedErrorTableInformation = true; + Controller->NeedDeviceStateInformation = true; + Controller->DeviceStateChannel = 0; + Controller->DeviceStateTargetID = 0; + Controller->SecondaryMonitoringTime = jiffies; + } + if (NewEnquiry->RebuildFlag == DAC960_StandbyRebuildInProgress || + NewEnquiry->RebuildFlag == DAC960_BackgroundRebuildInProgress || + OldEnquiry->RebuildFlag == DAC960_StandbyRebuildInProgress || + OldEnquiry->RebuildFlag == DAC960_BackgroundRebuildInProgress) + Controller->NeedRebuildProgress = true; + if (OldEnquiry->RebuildFlag == DAC960_BackgroundCheckInProgress) + switch (NewEnquiry->RebuildFlag) + { + case DAC960_NoStandbyRebuildOrCheckInProgress: + DAC960_Progress("Consistency Check Completed Successfully\n", + Controller); + break; + case DAC960_StandbyRebuildInProgress: + case DAC960_BackgroundRebuildInProgress: + break; + case DAC960_BackgroundCheckInProgress: + Controller->NeedConsistencyCheckProgress = true; + break; + case DAC960_StandbyRebuildCompletedWithError: + DAC960_Progress("Consistency Check Completed with Error\n", + Controller); + break; + case DAC960_BackgroundRebuildOrCheckFailed_DriveFailed: + DAC960_Progress("Consistency Check Failed - " + "Physical Drive Failed\n", Controller); + break; + case DAC960_BackgroundRebuildOrCheckFailed_LogicalDriveFailed: + DAC960_Progress("Consistency Check Failed - " + "Logical Drive Failed\n", Controller); + break; + case DAC960_BackgroundRebuildOrCheckFailed_OtherCauses: + DAC960_Progress("Consistency Check Failed - Other Causes\n", + Controller); + break; + case DAC960_BackgroundRebuildOrCheckSuccessfullyTerminated: + DAC960_Progress("Consistency Check Successfully Terminated\n", + Controller); + break; + } + else if (NewEnquiry->RebuildFlag == DAC960_BackgroundCheckInProgress) + Controller->NeedConsistencyCheckProgress = true; + } + else if (CommandOpcode == DAC960_GetLogicalDriveInformation) + { + int LogicalDriveNumber; + for (LogicalDriveNumber = 0; + LogicalDriveNumber < Controller->LogicalDriveCount; + LogicalDriveNumber++) + { + DAC960_LogicalDriveInformation_T *OldLogicalDriveInformation = + &Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex] + [LogicalDriveNumber]; + DAC960_LogicalDriveInformation_T *NewLogicalDriveInformation = + &Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex ^ 1] + [LogicalDriveNumber]; + if (NewLogicalDriveInformation->LogicalDriveState != + OldLogicalDriveInformation->LogicalDriveState) + DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) " + "is now %s\n", Controller, + LogicalDriveNumber, + Controller->ControllerNumber, + LogicalDriveNumber, + (NewLogicalDriveInformation->LogicalDriveState + == DAC960_LogicalDrive_Online + ? "ONLINE" + : NewLogicalDriveInformation->LogicalDriveState + == DAC960_LogicalDrive_Critical + ? "CRITICAL" : "OFFLINE")); + if (NewLogicalDriveInformation->WriteBack != + OldLogicalDriveInformation->WriteBack) + DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) " + "is now %s\n", Controller, + LogicalDriveNumber, + Controller->ControllerNumber, + LogicalDriveNumber, + (NewLogicalDriveInformation->WriteBack + ? "WRITE BACK" : "WRITE THRU")); + } + Controller->LogicalDriveInformationIndex ^= 1; + } + else if (CommandOpcode == DAC960_PerformEventLogOperation) + { + DAC960_EventLogEntry_T *EventLogEntry = &Controller->EventLogEntry; + if (EventLogEntry->SequenceNumber == + Controller->OldEventLogSequenceNumber) + { + unsigned char SenseKey = EventLogEntry->SenseKey; + unsigned char AdditionalSenseCode = + EventLogEntry->AdditionalSenseCode; + unsigned char AdditionalSenseCodeQualifier = + EventLogEntry->AdditionalSenseCodeQualifier; + if (SenseKey == 9 && + AdditionalSenseCode == 0x80 && + AdditionalSenseCodeQualifier < DAC960_EventMessagesCount) + DAC960_Critical("Physical Drive %d:%d %s\n", Controller, + EventLogEntry->Channel, + EventLogEntry->TargetID, + DAC960_EventMessages[ + AdditionalSenseCodeQualifier]); + else if (!((SenseKey == 2 && + AdditionalSenseCode == 0x04 && + (AdditionalSenseCodeQualifier == 0x01 || + AdditionalSenseCodeQualifier == 0x02)) || + (SenseKey == 6 && AdditionalSenseCode == 0x29 && + Controller->MonitoringTimerCount == 0))) + DAC960_Critical("Physical Drive %d:%d Error Log: " + "Sense Key = %d, ASC = %02X, ASCQ = %02X\n", + Controller, + EventLogEntry->Channel, + EventLogEntry->TargetID, + SenseKey, + AdditionalSenseCode, + AdditionalSenseCodeQualifier); + } + Controller->OldEventLogSequenceNumber++; + } + else if (CommandOpcode == DAC960_GetErrorTable) + { + DAC960_ErrorTable_T *OldErrorTable = + &Controller->ErrorTable[Controller->ErrorTableIndex]; + DAC960_ErrorTable_T *NewErrorTable = + &Controller->ErrorTable[Controller->ErrorTableIndex ^= 1]; + int Channel, TargetID; + for (Channel = 0; Channel < Controller->Channels; Channel++) + for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++) + { + DAC960_ErrorTableEntry_T *NewErrorEntry = + &NewErrorTable->ErrorTableEntries[Channel][TargetID]; + DAC960_ErrorTableEntry_T *OldErrorEntry = + &OldErrorTable->ErrorTableEntries[Channel][TargetID]; + if ((NewErrorEntry->ParityErrorCount != + OldErrorEntry->ParityErrorCount) || + (NewErrorEntry->SoftErrorCount != + OldErrorEntry->SoftErrorCount) || + (NewErrorEntry->HardErrorCount != + OldErrorEntry->HardErrorCount) || + (NewErrorEntry->MiscErrorCount != + OldErrorEntry->MiscErrorCount)) + DAC960_Critical("Physical Drive %d:%d Errors: " + "Parity = %d, Soft = %d, " + "Hard = %d, Misc = %d\n", + Controller, Channel, TargetID, + NewErrorEntry->ParityErrorCount, + NewErrorEntry->SoftErrorCount, + NewErrorEntry->HardErrorCount, + NewErrorEntry->MiscErrorCount); + } + } + else if (CommandOpcode == DAC960_GetDeviceState) + { + DAC960_DeviceState_T *OldDeviceState = + &Controller->DeviceState[Controller->DeviceStateIndex] + [Controller->DeviceStateChannel] + [Controller->DeviceStateTargetID]; + DAC960_DeviceState_T *NewDeviceState = + &Controller->DeviceState[Controller->DeviceStateIndex ^ 1] + [Controller->DeviceStateChannel] + [Controller->DeviceStateTargetID]; + if (NewDeviceState->DeviceState != OldDeviceState->DeviceState) + DAC960_Critical("Physical Drive %d:%d is now %s\n", Controller, + Controller->DeviceStateChannel, + Controller->DeviceStateTargetID, + (NewDeviceState->DeviceState == DAC960_Device_Dead + ? "DEAD" + : NewDeviceState->DeviceState + == DAC960_Device_WriteOnly + ? "WRITE-ONLY" + : NewDeviceState->DeviceState + == DAC960_Device_Online + ? "ONLINE" : "STANDBY")); + if (++Controller->DeviceStateTargetID == DAC960_MaxTargets) + { + Controller->DeviceStateChannel++; + Controller->DeviceStateTargetID = 0; + } + } + else if (CommandOpcode == DAC960_GetRebuildProgress) + { + unsigned int LogicalDriveNumber = + Controller->RebuildProgress.LogicalDriveNumber; + unsigned int LogicalDriveSize = + Controller->RebuildProgress.LogicalDriveSize; + unsigned int BlocksCompleted = + LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks; + switch (CommandStatus) + { + case DAC960_NormalCompletion: + Controller->EphemeralProgressMessage = true; + DAC960_Progress("Rebuild in Progress: " + "Logical Drive %d (/dev/rd/c%dd%d) " + "%d%% completed\n", + Controller, LogicalDriveNumber, + Controller->ControllerNumber, + LogicalDriveNumber, + (100 * (BlocksCompleted >> 7)) + / (LogicalDriveSize >> 7)); + Controller->EphemeralProgressMessage = false; + break; + case DAC960_RebuildFailed_LogicalDriveFailure: + DAC960_Progress("Rebuild Failed due to " + "Logical Drive Failure\n", Controller); + break; + case DAC960_RebuildFailed_BadBlocksOnOther: + DAC960_Progress("Rebuild Failed due to " + "Bad Blocks on Other Drives\n", Controller); + break; + case DAC960_RebuildFailed_NewDriveFailed: + DAC960_Progress("Rebuild Failed due to " + "Failure of Drive Being Rebuilt\n", Controller); + break; + case DAC960_RebuildSuccessful: + case DAC960_NoRebuildOrCheckInProgress: + DAC960_Progress("Rebuild Completed Successfully\n", Controller); + break; + } + } + else if (CommandOpcode == DAC960_RebuildStat) + { + unsigned int LogicalDriveNumber = + Controller->RebuildProgress.LogicalDriveNumber; + unsigned int LogicalDriveSize = + Controller->RebuildProgress.LogicalDriveSize; + unsigned int BlocksCompleted = + LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks; + if (CommandStatus == DAC960_NormalCompletion) + { + Controller->EphemeralProgressMessage = true; + DAC960_Progress("Consistency Check in Progress: " + "Logical Drive %d (/dev/rd/c%dd%d) " + "%d%% completed\n", + Controller, LogicalDriveNumber, + Controller->ControllerNumber, + LogicalDriveNumber, + (100 * (BlocksCompleted >> 7)) + / (LogicalDriveSize >> 7)); + Controller->EphemeralProgressMessage = false; + } + } + if (Controller->NeedLogicalDriveInformation && + NewCriticalLogicalDriveCount >= OldCriticalLogicalDriveCount) + { + Controller->NeedLogicalDriveInformation = false; + Command->CommandMailbox.Type3.CommandOpcode = + DAC960_GetLogicalDriveInformation; + Command->CommandMailbox.Type3.BusAddress = + Virtual_to_Bus( + &Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex ^ 1]); + DAC960_QueueCommand(Command); + return; + } + if (Controller->NewEventLogSequenceNumber + - Controller->OldEventLogSequenceNumber > 0) + { + Command->CommandMailbox.Type3E.CommandOpcode = + DAC960_PerformEventLogOperation; + Command->CommandMailbox.Type3E.OperationType = + DAC960_GetEventLogEntry; + Command->CommandMailbox.Type3E.OperationQualifier = 1; + Command->CommandMailbox.Type3E.SequenceNumber = + Controller->OldEventLogSequenceNumber; + Command->CommandMailbox.Type3E.BusAddress = + Virtual_to_Bus(&Controller->EventLogEntry); + DAC960_QueueCommand(Command); + return; + } + if (Controller->NeedErrorTableInformation) + { + Controller->NeedErrorTableInformation = false; + Command->CommandMailbox.Type3.CommandOpcode = DAC960_GetErrorTable; + Command->CommandMailbox.Type3.BusAddress = + Virtual_to_Bus( + &Controller->ErrorTable[Controller->ErrorTableIndex ^ 1]); + DAC960_QueueCommand(Command); + return; + } + if (Controller->NeedDeviceStateInformation) + { + while (Controller->DeviceStateChannel < Controller->Channels) + { + DAC960_DeviceState_T *OldDeviceState = + &Controller->DeviceState[Controller->DeviceStateIndex] + [Controller->DeviceStateChannel] + [Controller->DeviceStateTargetID]; + if (OldDeviceState->Present && + OldDeviceState->DeviceType == DAC960_DiskType) + { + Command->CommandMailbox.Type3D.CommandOpcode = + DAC960_GetDeviceState; + Command->CommandMailbox.Type3D.Channel = + Controller->DeviceStateChannel; + Command->CommandMailbox.Type3D.TargetID = + Controller->DeviceStateTargetID; + Command->CommandMailbox.Type3D.BusAddress = + Virtual_to_Bus(&Controller->DeviceState + [Controller->DeviceStateIndex ^ 1] + [Controller->DeviceStateChannel] + [Controller->DeviceStateTargetID]); + DAC960_QueueCommand(Command); + return; + } + if (++Controller->DeviceStateTargetID == DAC960_MaxTargets) + { + Controller->DeviceStateChannel++; + Controller->DeviceStateTargetID = 0; + } + } + Controller->NeedDeviceStateInformation = false; + Controller->DeviceStateIndex ^= 1; + } + if (Controller->NeedRebuildProgress) + { + Controller->NeedRebuildProgress = false; + Command->CommandMailbox.Type3.CommandOpcode = + DAC960_GetRebuildProgress; + Command->CommandMailbox.Type3.BusAddress = + Virtual_to_Bus(&Controller->RebuildProgress); + DAC960_QueueCommand(Command); + return; + } + if (Controller->NeedConsistencyCheckProgress) + { + Controller->NeedConsistencyCheckProgress = false; + Command->CommandMailbox.Type3.CommandOpcode = DAC960_RebuildStat; + Command->CommandMailbox.Type3.BusAddress = + Virtual_to_Bus(&Controller->RebuildProgress); + DAC960_QueueCommand(Command); + return; + } + if (Controller->NeedLogicalDriveInformation && + NewCriticalLogicalDriveCount < OldCriticalLogicalDriveCount) + { + Controller->NeedLogicalDriveInformation = false; + Command->CommandMailbox.Type3.CommandOpcode = + DAC960_GetLogicalDriveInformation; + Command->CommandMailbox.Type3.BusAddress = + Virtual_to_Bus( + &Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex ^ 1]); + DAC960_QueueCommand(Command); + return; + } + Controller->MonitoringTimerCount++; + Controller->MonitoringTimer.expires = + jiffies + DAC960_MonitoringTimerInterval; + add_timer(&Controller->MonitoringTimer); + } + else if (CommandType == DAC960_ImmediateCommand) + { + up(Command->Semaphore); + Command->Semaphore = NULL; + return; + } + else panic("DAC960: Unknown Command Type %d\n", CommandType); + /* + Queue a Status Monitoring Command to the Controller using the just + completed Command if one was deferred previously due to lack of a + free Command when the Monitoring Timer Function was called. + */ + if (Controller->MonitoringCommandDeferred) + { + Controller->MonitoringCommandDeferred = false; + DAC960_QueueMonitoringCommand(Command); + return; + } + /* + Execute a User Command using the just completed Command if one was + deferred previously due to lack of a free Command. + */ + if (Controller->UserCommandDeferred) + { + Controller->UserCommandDeferred = false; + Controller->UserCommand = Command; + up(Controller->UserCommandSemaphore); + return; + } + /* + Deallocate the Command, and wake up any processes waiting on a free Command. + */ + DAC960_DeallocateCommand(Command); + wake_up(&Controller->CommandWaitQueue); +} + + +/* + DAC960_InterruptHandler handles hardware interrupts from DAC960 Controllers. +*/ + +static void DAC960_InterruptHandler(int IRQ_Channel, + void *DeviceIdentifier, + Registers_T *InterruptRegisters) +{ + DAC960_Controller_T *Controller = (DAC960_Controller_T *) DeviceIdentifier; + void *ControllerBaseAddress = Controller->BaseAddress; + DAC960_StatusMailbox_T *NextStatusMailbox; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockIH(Controller, &ProcessorFlags); + /* + Process Hardware Interrupts for Controller. + */ + switch (Controller->ControllerType) + { + case DAC960_V5_Controller: + DAC960_V5_AcknowledgeInterrupt(ControllerBaseAddress); + NextStatusMailbox = Controller->NextStatusMailbox; + while (NextStatusMailbox->Fields.Valid) + { + DAC960_CommandIdentifier_T CommandIdentifier = + NextStatusMailbox->Fields.CommandIdentifier; + DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier]; + Command->CommandStatus = NextStatusMailbox->Fields.CommandStatus; + NextStatusMailbox->Word = 0; + if (++NextStatusMailbox > Controller->LastStatusMailbox) + NextStatusMailbox = Controller->FirstStatusMailbox; + DAC960_ProcessCompletedCommand(Command); + } + Controller->NextStatusMailbox = NextStatusMailbox; + break; + case DAC960_V4_Controller: + DAC960_V4_AcknowledgeInterrupt(ControllerBaseAddress); + NextStatusMailbox = Controller->NextStatusMailbox; + while (NextStatusMailbox->Fields.Valid) + { + DAC960_CommandIdentifier_T CommandIdentifier = + NextStatusMailbox->Fields.CommandIdentifier; + DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier]; + Command->CommandStatus = NextStatusMailbox->Fields.CommandStatus; + NextStatusMailbox->Word = 0; + if (++NextStatusMailbox > Controller->LastStatusMailbox) + NextStatusMailbox = Controller->FirstStatusMailbox; + DAC960_ProcessCompletedCommand(Command); + } + Controller->NextStatusMailbox = NextStatusMailbox; + break; + case DAC960_V3_Controller: + while (DAC960_V3_StatusAvailableP(ControllerBaseAddress)) + { + DAC960_CommandIdentifier_T CommandIdentifier = + DAC960_V3_ReadStatusCommandIdentifier(ControllerBaseAddress); + DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier]; + Command->CommandStatus = + DAC960_V3_ReadStatusRegister(ControllerBaseAddress); + DAC960_V3_AcknowledgeInterrupt(ControllerBaseAddress); + DAC960_V3_AcknowledgeStatus(ControllerBaseAddress); + DAC960_ProcessCompletedCommand(Command); + } + break; + } + /* + Attempt to remove additional I/O Requests from the Controller's + I/O Request Queue and queue them to the Controller. + */ + while (DAC960_ProcessRequest(Controller, false)) ; + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockIH(Controller, &ProcessorFlags); +} + + +/* + DAC960_QueueMonitoringCommand queues a Monitoring Command to Controller. +*/ + +static void DAC960_QueueMonitoringCommand(DAC960_Command_T *Command) +{ + DAC960_Controller_T *Controller = Command->Controller; + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + DAC960_ClearCommand(Command); + Command->CommandType = DAC960_MonitoringCommand; + CommandMailbox->Type3.CommandOpcode = DAC960_Enquiry; + CommandMailbox->Type3.BusAddress = + Virtual_to_Bus(&Controller->Enquiry[Controller->EnquiryIndex ^ 1]); + DAC960_QueueCommand(Command); +} + + +/* + DAC960_MonitoringTimerFunction is the timer function for monitoring + the status of DAC960 Controllers. +*/ + +static void DAC960_MonitoringTimerFunction(unsigned long TimerData) +{ + DAC960_Controller_T *Controller = (DAC960_Controller_T *) TimerData; + DAC960_Command_T *Command; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + /* + Queue a Status Monitoring Command to Controller. + */ + Command = DAC960_AllocateCommand(Controller); + if (Command != NULL) + DAC960_QueueMonitoringCommand(Command); + else Controller->MonitoringCommandDeferred = true; + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); +} + + +/* + DAC960_Open is the Device Open Function for the DAC960 Driver. +*/ + +static int DAC960_Open(Inode_T *Inode, File_T *File) +{ + int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev); + int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev); + DAC960_Controller_T *Controller; + if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1) + return -ENXIO; + Controller = DAC960_Controllers[ControllerNumber]; + if (Controller == NULL || + LogicalDriveNumber > Controller->LogicalDriveCount - 1) + return -ENXIO; + if (Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex] + [LogicalDriveNumber].LogicalDriveState + == DAC960_LogicalDrive_Offline) + return -ENXIO; + if (Controller->LogicalDriveInitialState[LogicalDriveNumber] + == DAC960_LogicalDrive_Offline) + { + Controller->LogicalDriveInitialState[LogicalDriveNumber] = + DAC960_LogicalDrive_Online; + DAC960_InitializeGenericDiskInfo(&Controller->GenericDiskInfo); + resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber); + } + if (Controller->GenericDiskInfo.sizes[MINOR(Inode->i_rdev)] == 0) + return -ENXIO; + /* + Increment Controller and Logical Drive Usage Counts. + */ + Controller->ControllerUsageCount++; + Controller->LogicalDriveUsageCount[LogicalDriveNumber]++; + return 0; +} + + +/* + DAC960_Release is the Device Release Function for the DAC960 Driver. +*/ + +static void DAC960_Release(Inode_T *Inode, File_T *File) +{ + int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev); + int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev); + DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber]; + /* + Force any buffered data to be written. + */ + fsync_dev(Inode->i_rdev); + /* + Decrement the Logical Drive and Controller Usage Counts. + */ + Controller->LogicalDriveUsageCount[LogicalDriveNumber]--; + Controller->ControllerUsageCount--; +} + + +/* + DAC960_Ioctl is the Device Ioctl Function for the DAC960 Driver. +*/ + +static int DAC960_Ioctl(Inode_T *Inode, File_T *File, + unsigned int Request, unsigned long Argument) +{ + int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev); + int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev); + int PartitionNumber, ErrorCode; + unsigned short Cylinders; + DiskGeometry_T *Geometry; + DAC960_Controller_T *Controller; + if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1) + return -ENXIO; + Controller = DAC960_Controllers[ControllerNumber]; + if (Controller == NULL || + LogicalDriveNumber > Controller->LogicalDriveCount - 1) + return -ENXIO; + switch (Request) + { + case HDIO_GETGEO: + /* Get BIOS Disk Geometry. */ + Geometry = (DiskGeometry_T *) Argument; + if (Geometry == NULL) return -EINVAL; + ErrorCode = verify_area(VERIFY_WRITE, Geometry, sizeof(DiskGeometry_T)); + if (ErrorCode != 0) return ErrorCode; + Cylinders = + Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex] + [LogicalDriveNumber].LogicalDriveSize + / (Controller->GeometryTranslationHeads * + Controller->GeometryTranslationSectors); + put_user(Controller->GeometryTranslationHeads, &Geometry->heads); + put_user(Controller->GeometryTranslationSectors, &Geometry->sectors); + put_user(Cylinders, &Geometry->cylinders); + put_user(Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)] + .start_sect, &Geometry->start); + return 0; + case BLKGETSIZE: + /* Get Device Size. */ + if ((long *) Argument == NULL) return -EINVAL; + ErrorCode = verify_area(VERIFY_WRITE, (long *) Argument, sizeof(long)); + if (ErrorCode != 0) return ErrorCode; + put_user(Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)].nr_sects, + (long *) Argument); + return 0; + case BLKRAGET: + /* Get Read-Ahead. */ + if ((int *) Argument == NULL) return -EINVAL; + ErrorCode = verify_area(VERIFY_WRITE, (int *) Argument, sizeof(int)); + if (ErrorCode != 0) return ErrorCode; + put_user(read_ahead[MAJOR(Inode->i_rdev)], (int *) Argument); + return 0; + case BLKRASET: + /* Set Read-Ahead. */ + if (!suser()) return -EACCES; + if (Argument > 256) return -EINVAL; + read_ahead[MAJOR(Inode->i_rdev)] = Argument; + return 0; + case BLKFLSBUF: + /* Flush Buffers. */ + if (!suser()) return -EACCES; + fsync_dev(Inode->i_rdev); + invalidate_buffers(Inode->i_rdev); + return 0; + case BLKRRPART: + /* Re-Read Partition Table. */ + if (!suser()) return -EACCES; + if (Controller->LogicalDriveUsageCount[LogicalDriveNumber] > 1) + return -EBUSY; + for (PartitionNumber = 0; + PartitionNumber < DAC960_MaxPartitions; + PartitionNumber++) + { + KernelDevice_T Device = DAC960_KernelDevice(ControllerNumber, + LogicalDriveNumber, + PartitionNumber); + int MinorNumber = DAC960_MinorNumber(LogicalDriveNumber, + PartitionNumber); + if (Controller->GenericDiskInfo.part[MinorNumber].nr_sects == 0) + continue; + /* + Flush all changes and invalidate buffered state. + */ + sync_dev(Device); + invalidate_inodes(Device); + invalidate_buffers(Device); + /* + Clear existing partition sizes. + */ + if (PartitionNumber > 0) + { + Controller->GenericDiskInfo.part[MinorNumber].start_sect = 0; + Controller->GenericDiskInfo.part[MinorNumber].nr_sects = 0; + } + /* + Reset the Block Size so that the partition table can be read. + */ + set_blocksize(Device, BLOCK_SIZE); + } + resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber); + return 0; + } + return -EINVAL; +} + + +/* + DAC960_GenericDiskInit is the Generic Disk Information Initialization + Function for the DAC960 Driver. +*/ + +static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *GenericDiskInfo) +{ + DAC960_Controller_T *Controller = + (DAC960_Controller_T *) GenericDiskInfo->real_devices; + DAC960_LogicalDriveInformation_T *LogicalDriveInformation = + Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex]; + int LogicalDriveNumber; + for (LogicalDriveNumber = 0; + LogicalDriveNumber < Controller->LogicalDriveCount; + LogicalDriveNumber++) + GenericDiskInfo->part[DAC960_MinorNumber(LogicalDriveNumber, 0)].nr_sects = + LogicalDriveInformation[LogicalDriveNumber].LogicalDriveSize; +} + + +/* + DAC960_Message prints Driver Messages. +*/ + +static void DAC960_Message(DAC960_MessageLevel_T MessageLevel, + char *Format, + DAC960_Controller_T *Controller, + ...) +{ + static char Buffer[DAC960_LineBufferSize]; + static boolean BeginningOfLine = true; + va_list Arguments; + int Length = 0; + va_start(Arguments, Controller); + Length = vsprintf(Buffer, Format, Arguments); + va_end(Arguments); + if (Controller == NULL) + printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel], + DAC960_ControllerCount, Buffer); + else if (MessageLevel == DAC960_AnnounceLevel || + MessageLevel == DAC960_InfoLevel) + { + if (!Controller->ControllerInitialized) + { + strcpy(&Controller->InitialStatusBuffer[ + Controller->InitialStatusLength], Buffer); + Controller->InitialStatusLength += Length; + if (MessageLevel == DAC960_AnnounceLevel) + { + static int AnnouncementLines = 0; + if (++AnnouncementLines <= 2) + printk("%sDAC960: %s", DAC960_MessageLevelMap[MessageLevel], + Buffer); + } + else + { + if (BeginningOfLine) + { + if (Buffer[0] != '\n' || Length > 1) + printk("%sDAC960#%d: %s", + DAC960_MessageLevelMap[MessageLevel], + Controller->ControllerNumber, Buffer); + } + else printk("%s", Buffer); + } + } + else + { + strcpy(&Controller->CurrentStatusBuffer[ + Controller->CurrentStatusLength], Buffer); + Controller->CurrentStatusLength += Length; + } + } + else if (MessageLevel == DAC960_ProgressLevel) + { + strcpy(Controller->RebuildProgressBuffer, Buffer); + Controller->RebuildProgressLength = Length; + if (Controller->EphemeralProgressMessage) + { + if (jiffies - Controller->LastProgressReportTime + >= DAC960_ProgressReportingInterval) + { + printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel], + Controller->ControllerNumber, Buffer); + Controller->LastProgressReportTime = jiffies; + } + } + else printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel], + Controller->ControllerNumber, Buffer); + } + else if (MessageLevel == DAC960_UserCriticalLevel) + { + strcpy(&Controller->UserStatusBuffer[Controller->UserStatusLength], + Buffer); + Controller->UserStatusLength += Length; + if (Buffer[0] != '\n' || Length > 1) + printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel], + Controller->ControllerNumber, Buffer); + } + else + { + if (BeginningOfLine) + printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel], + Controller->ControllerNumber, Buffer); + else printk("%s", Buffer); + } + BeginningOfLine = (Buffer[Length-1] == '\n'); +} + + +/* + DAC960_AllocateUserCommand allocates a Command structure for a User Command. + If a User Command is already active, NULL is returned. +*/ + +static DAC960_Command_T *DAC960_AllocateUserCommand(DAC960_Controller_T + *Controller) +{ + Semaphore_T Semaphore = MUTEX_LOCKED; + DAC960_Command_T *Command = NULL; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + /* + Acquire exclusive access to the User Command facility. + */ + if (Controller->UserCommandActive) goto Done; + Controller->UserCommandActive = true; + /* + Allocate a Command. If none is available, set the User Command Deferred + flag to tell the Interrupt Handler to save the next Command completed. + */ + Command = DAC960_AllocateCommand(Controller); + if (Command == NULL) + { + Controller->UserCommandDeferred = true; + Controller->UserCommandSemaphore = &Semaphore; + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); + down(&Semaphore); + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + Command = Controller->UserCommand; + Controller->UserCommand = NULL; + } + /* + Initialize the Command. + */ + DAC960_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + /* + Release exclusive access to Controller. + */ + Done: + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); + return Command; +} + + +/* + DAC960_DeallocateUserCommand deallocates a User Command. +*/ + +static void DAC960_DeallocateUserCommand(DAC960_Command_T *Command) +{ + DAC960_Controller_T *Controller = Command->Controller; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + /* + Deallocate the Command. + */ + DAC960_DeallocateCommand(Command); + /* + Release exclusive access to the User Command facility. + */ + Controller->UserCommandActive = false; + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); +} + + +/* + DAC960_ParsePhysicalDrive parses spaces followed by a Physical Drive + Channel:TargetID specification from a User Command string. It updates + Channel and TargetID and returns true on success and returns false otherwise. +*/ + +static boolean DAC960_ParsePhysicalDrive(DAC960_Controller_T *Controller, + char *UserCommandString, + unsigned char *Channel, + unsigned char *TargetID) +{ + char *NewUserCommandString = UserCommandString; + unsigned long XChannel, XTargetID; + while (*UserCommandString == ' ') UserCommandString++; + if (UserCommandString == NewUserCommandString) + return false; + XChannel = simple_strtoul(UserCommandString, &NewUserCommandString, 10); + if (NewUserCommandString == UserCommandString || + *NewUserCommandString != ':' || + XChannel >= Controller->Channels) + return false; + UserCommandString = ++NewUserCommandString; + XTargetID = simple_strtoul(UserCommandString, &NewUserCommandString, 10); + if (NewUserCommandString == UserCommandString || + *NewUserCommandString != '\0' || + XTargetID >= DAC960_MaxTargets) + return false; + *Channel = XChannel; + *TargetID = XTargetID; + return true; +} + + +/* + DAC960_ParseLogicalDrive parses spaces followed by a Logical Drive Number + specification from a User Command string. It updates LogicalDriveNumber and + returns true on success and returns false otherwise. +*/ + +static boolean DAC960_ParseLogicalDrive(DAC960_Controller_T *Controller, + char *UserCommandString, + unsigned char *LogicalDriveNumber) +{ + char *NewUserCommandString = UserCommandString; + unsigned long XLogicalDriveNumber; + while (*UserCommandString == ' ') UserCommandString++; + if (UserCommandString == NewUserCommandString) + return false; + XLogicalDriveNumber = + simple_strtoul(UserCommandString, &NewUserCommandString, 10); + if (NewUserCommandString == UserCommandString || + *NewUserCommandString != '\0' || + XLogicalDriveNumber >= Controller->LogicalDriveCount) + return false; + *LogicalDriveNumber = XLogicalDriveNumber; + return true; +} + + +/* + DAC960_SetDeviceState sets the Device State for a Physical Drive. +*/ + +static void DAC960_SetDeviceState(DAC960_Controller_T *Controller, + DAC960_Command_T *Command, + unsigned char Channel, + unsigned char TargetID, + DAC960_PhysicalDeviceState_T DeviceState, + const char *DeviceStateString) +{ + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + CommandMailbox->Type3D.CommandOpcode = DAC960_StartDevice; + CommandMailbox->Type3D.Channel = Channel; + CommandMailbox->Type3D.TargetID = TargetID; + CommandMailbox->Type3D.DeviceState = DeviceState; + CommandMailbox->Type3D.Modifier = 0; + DAC960_ExecuteCommand(Command); + switch (Command->CommandStatus) + { + case DAC960_NormalCompletion: + DAC960_UserCritical("%s of Physical Drive %d:%d Succeeded\n", Controller, + DeviceStateString, Channel, TargetID); + break; + case DAC960_UnableToStartDevice: + DAC960_UserCritical("%s of Physical Drive %d:%d Failed - " + "Unable to Start Device\n", Controller, + DeviceStateString, Channel, TargetID); + break; + case DAC960_NoDeviceAtAddress: + DAC960_UserCritical("%s of Physical Drive %d:%d Failed - " + "No Device at Address\n", Controller, + DeviceStateString, Channel, TargetID); + break; + case DAC960_InvalidChannelOrTargetOrModifier: + DAC960_UserCritical("%s of Physical Drive %d:%d Failed - " + "Invalid Channel or Target or Modifier\n", + Controller, DeviceStateString, Channel, TargetID); + break; + case DAC960_ChannelBusy: + DAC960_UserCritical("%s of Physical Drive %d:%d Failed - " + "Channel Busy\n", Controller, + DeviceStateString, Channel, TargetID); + break; + default: + DAC960_UserCritical("%s of Physical Drive %d:%d Failed - " + "Unexpected Status %04X\n", Controller, + DeviceStateString, Channel, TargetID, + Command->CommandStatus); + break; + } +} + + +/* + DAC960_ExecuteUserCommand executes a User Command. +*/ + +static boolean DAC960_ExecuteUserCommand(DAC960_Controller_T *Controller, + char *UserCommand) +{ + DAC960_Command_T *Command; + DAC960_CommandMailbox_T *CommandMailbox; + unsigned char Channel, TargetID, LogicalDriveNumber; + Command = DAC960_AllocateUserCommand(Controller); + if (Command == NULL) return false; + CommandMailbox = &Command->CommandMailbox; + Controller->UserStatusLength = 0; + if (strcmp(UserCommand, "flush-cache") == 0) + { + CommandMailbox->Type3.CommandOpcode = DAC960_Flush; + DAC960_ExecuteCommand(Command); + DAC960_UserCritical("Cache Flush Completed\n", Controller); + } + else if (strncmp(UserCommand, "kill", 4) == 0 && + DAC960_ParsePhysicalDrive(Controller, &UserCommand[4], + &Channel, &TargetID)) + { + DAC960_DeviceState_T *DeviceState = + &Controller->DeviceState[Controller->DeviceStateIndex] + [Channel][TargetID]; + if (DeviceState->Present && + DeviceState->DeviceType == DAC960_DiskType && + DeviceState->DeviceState != DAC960_Device_Dead) + DAC960_SetDeviceState(Controller, Command, Channel, TargetID, + DAC960_Device_Dead, "Kill"); + else DAC960_UserCritical("Kill of Physical Drive %d:%d Illegal\n", + Controller, Channel, TargetID); + } + else if (strncmp(UserCommand, "make-online", 11) == 0 && + DAC960_ParsePhysicalDrive(Controller, &UserCommand[11], + &Channel, &TargetID)) + { + DAC960_DeviceState_T *DeviceState = + &Controller->DeviceState[Controller->DeviceStateIndex] + [Channel][TargetID]; + if (DeviceState->Present && + DeviceState->DeviceType == DAC960_DiskType && + DeviceState->DeviceState == DAC960_Device_Dead) + DAC960_SetDeviceState(Controller, Command, Channel, TargetID, + DAC960_Device_Online, "Make Online"); + else DAC960_UserCritical("Make Online of Physical Drive %d:%d Illegal\n", + Controller, Channel, TargetID); + + } + else if (strncmp(UserCommand, "make-standby", 12) == 0 && + DAC960_ParsePhysicalDrive(Controller, &UserCommand[12], + &Channel, &TargetID)) + { + DAC960_DeviceState_T *DeviceState = + &Controller->DeviceState[Controller->DeviceStateIndex] + [Channel][TargetID]; + if (DeviceState->Present && + DeviceState->DeviceType == DAC960_DiskType && + DeviceState->DeviceState == DAC960_Device_Dead) + DAC960_SetDeviceState(Controller, Command, Channel, TargetID, + DAC960_Device_Standby, "Make Standby"); + else DAC960_UserCritical("Make Standby of Physical Drive %d:%d Illegal\n", + Controller, Channel, TargetID); + } + else if (strncmp(UserCommand, "rebuild", 7) == 0 && + DAC960_ParsePhysicalDrive(Controller, &UserCommand[7], + &Channel, &TargetID)) + { + CommandMailbox->Type3D.CommandOpcode = DAC960_RebuildAsync; + CommandMailbox->Type3D.Channel = Channel; + CommandMailbox->Type3D.TargetID = TargetID; + DAC960_ExecuteCommand(Command); + switch (Command->CommandStatus) + { + case DAC960_NormalCompletion: + DAC960_UserCritical("Rebuild of Physical Drive %d:%d Initiated\n", + Controller, Channel, TargetID); + break; + case DAC960_AttemptToRebuildOnlineDrive: + DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - " + "Attempt to Rebuild Online or " + "Unresponsive Drive\n", + Controller, Channel, TargetID); + break; + case DAC960_NewDiskFailedDuringRebuild: + DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - " + "New Disk Failed During Rebuild\n", + Controller, Channel, TargetID); + break; + case DAC960_InvalidDeviceAddress: + DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - " + "Invalid Device Address\n", + Controller, Channel, TargetID); + break; + case DAC960_RebuildOrCheckAlreadyInProgress: + DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - " + "Rebuild or Consistency Check Already " + "in Progress\n", Controller, Channel, TargetID); + break; + default: + DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - " + "Unexpected Status %04X\n", Controller, + Channel, TargetID, Command->CommandStatus); + break; + } + } + else if (strncmp(UserCommand, "check-consistency", 17) == 0 && + DAC960_ParseLogicalDrive(Controller, &UserCommand[17], + &LogicalDriveNumber)) + { + CommandMailbox->Type3C.CommandOpcode = DAC960_CheckConsistencyAsync; + CommandMailbox->Type3C.LogicalDriveNumber = LogicalDriveNumber; + CommandMailbox->Type3C.AutoRestore = true; + DAC960_ExecuteCommand(Command); + switch (Command->CommandStatus) + { + case DAC960_NormalCompletion: + DAC960_UserCritical("Consistency Check of Logical Drive %d " + "(/dev/rd/c%dd%d) Initiated\n", + Controller, LogicalDriveNumber, + Controller->ControllerNumber, + LogicalDriveNumber); + break; + case DAC960_DependentDiskIsDead: + DAC960_UserCritical("Consistency Check of Logical Drive %d " + "(/dev/rd/c%dd%d) Failed - " + "Dependent Physical Drive is DEAD\n", + Controller, LogicalDriveNumber, + Controller->ControllerNumber, + LogicalDriveNumber); + break; + case DAC960_InvalidOrNonredundantLogicalDrive: + DAC960_UserCritical("Consistency Check of Logical Drive %d " + "(/dev/rd/c%dd%d) Failed - " + "Invalid or Nonredundant Logical Drive\n", + Controller, LogicalDriveNumber, + Controller->ControllerNumber, + LogicalDriveNumber); + break; + case DAC960_RebuildOrCheckAlreadyInProgress: + DAC960_UserCritical("Consistency Check of Logical Drive %d " + "(/dev/rd/c%dd%d) Failed - Rebuild or " + "Consistency Check Already in Progress\n", + Controller, LogicalDriveNumber, + Controller->ControllerNumber, + LogicalDriveNumber); + break; + default: + DAC960_UserCritical("Consistency Check of Logical Drive %d " + "(/dev/rd/c%dd%d) Failed - " + "Unexpected Status %04X\n", + Controller, LogicalDriveNumber, + Controller->ControllerNumber, + LogicalDriveNumber, Command->CommandStatus); + break; + } + } + else if (strcmp(UserCommand, "cancel-rebuild") == 0 || + strcmp(UserCommand, "cancel-consistency-check") == 0) + { + unsigned char OldRebuildRateConstant; + CommandMailbox->Type3R.CommandOpcode = DAC960_RebuildControl; + CommandMailbox->Type3R.RebuildRateConstant = 0xFF; + CommandMailbox->Type3R.BusAddress = + Virtual_to_Bus(&OldRebuildRateConstant); + DAC960_ExecuteCommand(Command); + switch (Command->CommandStatus) + { + case DAC960_NormalCompletion: + DAC960_UserCritical("Rebuild or Consistency Check Cancelled\n", + Controller); + break; + default: + DAC960_UserCritical("Cancellation of Rebuild or " + "Consistency Check Failed - " + "Unexpected Status %04X\n", + Controller, Command->CommandStatus); + break; + } + } + else DAC960_UserCritical("Illegal User Command: '%s'\n", + Controller, UserCommand); + DAC960_DeallocateUserCommand(Command); + return true; +} + + +/* + DAC960_ProcReadStatus implements reading /proc/rd/status. +*/ + +static int DAC960_ProcReadStatus(Inode_T *Inode, File_T *File, + char *Buffer, int Count) +{ + char *StatusMessage = "OK\n"; + int StatusMessageOffset, BytesAvailable; + int ControllerNumber; + for (ControllerNumber = 0; + ControllerNumber < DAC960_ControllerCount; + ControllerNumber++) + { + DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber]; + DAC960_Enquiry_T *Enquiry; + if (Controller == NULL) continue; + Enquiry = &Controller->Enquiry[Controller->EnquiryIndex]; + if (Enquiry->CriticalLogicalDriveCount > 0 || + Enquiry->OfflineLogicalDriveCount > 0 || + Enquiry->DeadDriveCount > 0) + { + StatusMessage = "ALERT\n"; + break; + } + } + StatusMessageOffset = File->f_pos; + BytesAvailable = strlen(StatusMessage) - StatusMessageOffset; + if (Count > BytesAvailable) Count = BytesAvailable; + if (Count <= 0) return 0; + memcpy_tofs(Buffer, &StatusMessage[StatusMessageOffset], Count); + File->f_pos += Count; + return Count; +} + + +/* + DAC960_ProcReadInitialStatus implements reading /proc/rd/cN/initial_status. +*/ + +static int DAC960_ProcReadInitialStatus(Inode_T *Inode, File_T *File, + char *Buffer, int Count) +{ + DAC960_Controller_T *Controller = NULL; + int InitialStatusOffset, BytesAvailable; + int ControllerNumber; + for (ControllerNumber = 0; + ControllerNumber < DAC960_ControllerCount; + ControllerNumber++) + { + Controller = DAC960_Controllers[ControllerNumber]; + if (Controller == NULL) continue; + if (Controller->InitialStatusProcEntry.low_ino == Inode->i_ino) break; + } + InitialStatusOffset = File->f_pos; + BytesAvailable = Controller->InitialStatusLength - InitialStatusOffset; + if (Count > BytesAvailable) Count = BytesAvailable; + if (Count <= 0) return 0; + memcpy_tofs(Buffer, + &Controller->InitialStatusBuffer[InitialStatusOffset], Count); + File->f_pos += Count; + return Count; +} + + +/* + DAC960_ProcReadCurrentStatus implements reading /proc/rd/cN/current_status. +*/ + +static int DAC960_ProcReadCurrentStatus(Inode_T *Inode, File_T *File, + char *Buffer, int Count) +{ + DAC960_Controller_T *Controller = NULL; + int CurrentStatusOffset, BytesAvailable; + int ControllerNumber; + for (ControllerNumber = 0; + ControllerNumber < DAC960_ControllerCount; + ControllerNumber++) + { + Controller = DAC960_Controllers[ControllerNumber]; + if (Controller == NULL) continue; + if (Controller->CurrentStatusProcEntry.low_ino == Inode->i_ino) break; + } + Controller->CurrentStatusLength = 0; + DAC960_AnnounceDriver(Controller); + DAC960_ReportControllerConfiguration(Controller); + Controller->CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' '; + Controller->CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' '; + if (Controller->RebuildProgressLength > 0) + { + strcpy(&Controller->CurrentStatusBuffer[Controller->CurrentStatusLength], + Controller->RebuildProgressBuffer); + Controller->CurrentStatusLength += Controller->RebuildProgressLength; + } + else + { + char *StatusMessage = "No Rebuild or Consistency Check in Progress\n"; + strcpy(&Controller->CurrentStatusBuffer[Controller->CurrentStatusLength], + StatusMessage); + Controller->CurrentStatusLength += strlen(StatusMessage); + } + CurrentStatusOffset = File->f_pos; + BytesAvailable = Controller->CurrentStatusLength - CurrentStatusOffset; + if (Count > BytesAvailable) Count = BytesAvailable; + if (Count <= 0) return 0; + memcpy_tofs(Buffer, + &Controller->CurrentStatusBuffer[CurrentStatusOffset], Count); + File->f_pos += Count; + return Count; +} + + +/* + DAC960_ProcReadUserCommand implements reading /proc/rd/cN/user_command. +*/ + +static int DAC960_ProcReadUserCommand(Inode_T *Inode, File_T *File, + char *Buffer, int Count) +{ + DAC960_Controller_T *Controller = NULL; + int UserStatusOffset, BytesAvailable; + int ControllerNumber; + for (ControllerNumber = 0; + ControllerNumber < DAC960_ControllerCount; + ControllerNumber++) + { + Controller = DAC960_Controllers[ControllerNumber]; + if (Controller == NULL) continue; + if (Controller->UserCommandProcEntry.low_ino == Inode->i_ino) break; + } + UserStatusOffset = File->f_pos; + BytesAvailable = Controller->UserStatusLength - UserStatusOffset; + if (Count > BytesAvailable) Count = BytesAvailable; + if (Count <= 0) return 0; + memcpy_tofs(Buffer, &Controller->UserStatusBuffer[UserStatusOffset], Count); + File->f_pos += Count; + return Count; +} + + +/* + DAC960_ProcWriteUserCommand implements writing /proc/rd/cN/user_command. +*/ + +static int DAC960_ProcWriteUserCommand(Inode_T *Inode, File_T *File, + const char *Buffer, int Count) +{ + DAC960_Controller_T *Controller = NULL; + char CommandBuffer[80]; + int ControllerNumber, Length; + if (Count > sizeof(CommandBuffer)-1) return -EINVAL; + memcpy_fromfs(CommandBuffer, Buffer, Count); + CommandBuffer[Count] = '\0'; + Length = strlen(CommandBuffer); + if (CommandBuffer[Length-1] == '\n') + CommandBuffer[--Length] = '\0'; + for (ControllerNumber = 0; + ControllerNumber < DAC960_ControllerCount; + ControllerNumber++) + { + Controller = DAC960_Controllers[ControllerNumber]; + if (Controller == NULL) continue; + if (Controller->UserCommandProcEntry.low_ino == Inode->i_ino) break; + } + return (DAC960_ExecuteUserCommand(Controller, CommandBuffer) + ? Count : -EBUSY); +} + + +/* + DAC960_CreateProcEntries creates the /proc/rd/... entries for the DAC960 + Driver. +*/ + +static void DAC960_CreateProcEntries(void) +{ + static PROC_DirectoryEntry_T StatusProcEntry; + static FileOperations_T + StatusFileOperations = { read: DAC960_ProcReadStatus }; + static InodeOperations_T + StatusInodeOperations = { &StatusFileOperations }; + static FileOperations_T + InitialStatusFileOperations = { read: DAC960_ProcReadInitialStatus }; + static InodeOperations_T + InitialStatusInodeOperations = { &InitialStatusFileOperations }; + static FileOperations_T + CurrentStatusFileOperations = { read: DAC960_ProcReadCurrentStatus }; + static InodeOperations_T + CurrentStatusInodeOperations = { &CurrentStatusFileOperations }; + static FileOperations_T + UserCommandFileOperations = { read: DAC960_ProcReadUserCommand, + write: DAC960_ProcWriteUserCommand }; + static InodeOperations_T + UserCommandInodeOperations = { &UserCommandFileOperations }; + int ControllerNumber; + DAC960_ProcDirectoryEntry.name = "rd"; + DAC960_ProcDirectoryEntry.namelen = strlen(DAC960_ProcDirectoryEntry.name); + DAC960_ProcDirectoryEntry.mode = S_IFDIR | S_IRUGO | S_IXUGO; + DAC960_ProcDirectoryEntry.ops = &proc_dir_inode_operations; + proc_register_dynamic(&proc_root, &DAC960_ProcDirectoryEntry); + StatusProcEntry.name = "status"; + StatusProcEntry.namelen = strlen(StatusProcEntry.name); + StatusProcEntry.mode = S_IFREG | S_IRUGO; + StatusProcEntry.ops = &StatusInodeOperations; + proc_register_dynamic(&DAC960_ProcDirectoryEntry, &StatusProcEntry); + for (ControllerNumber = 0; + ControllerNumber < DAC960_ControllerCount; + ControllerNumber++) + { + DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber]; + PROC_DirectoryEntry_T *ControllerProcEntry, *InitialStatusProcEntry; + PROC_DirectoryEntry_T *CurrentStatusProcEntry, *UserCommandProcEntry; + if (Controller == NULL) continue; + ControllerProcEntry = &Controller->ControllerProcEntry; + ControllerProcEntry->name = Controller->ControllerName; + ControllerProcEntry->namelen = strlen(ControllerProcEntry->name); + ControllerProcEntry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + ControllerProcEntry->ops = &proc_dir_inode_operations; + proc_register_dynamic(&DAC960_ProcDirectoryEntry, ControllerProcEntry); + InitialStatusProcEntry = &Controller->InitialStatusProcEntry; + InitialStatusProcEntry->name = "initial_status"; + InitialStatusProcEntry->namelen = strlen(InitialStatusProcEntry->name); + InitialStatusProcEntry->mode = S_IFREG | S_IRUGO; + InitialStatusProcEntry->ops = &InitialStatusInodeOperations; + proc_register_dynamic(ControllerProcEntry, InitialStatusProcEntry); + CurrentStatusProcEntry = &Controller->CurrentStatusProcEntry; + CurrentStatusProcEntry->name = "current_status"; + CurrentStatusProcEntry->namelen = strlen(CurrentStatusProcEntry->name); + CurrentStatusProcEntry->mode = S_IFREG | S_IRUGO; + CurrentStatusProcEntry->ops = &CurrentStatusInodeOperations; + proc_register_dynamic(ControllerProcEntry, CurrentStatusProcEntry); + UserCommandProcEntry = &Controller->UserCommandProcEntry; + UserCommandProcEntry->name = "user_command"; + UserCommandProcEntry->namelen = strlen(UserCommandProcEntry->name); + UserCommandProcEntry->mode = S_IFREG | S_IWUSR | S_IRUSR; + UserCommandProcEntry->ops = &UserCommandInodeOperations; + proc_register_dynamic(ControllerProcEntry, UserCommandProcEntry); + } +} + + +/* + DAC960_DestroyProcEntries destroys the /proc/rd/... entries for the DAC960 + Driver. +*/ + +static void DAC960_DestroyProcEntries(void) +{ + proc_unregister(&proc_root, DAC960_ProcDirectoryEntry.low_ino); +} diff -urN linux-2.0.37-pre8/linux/drivers/block/DAC960.h linux-2.0.37-pre9/linux/drivers/block/DAC960.h --- linux-2.0.37-pre8/linux/drivers/block/DAC960.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.0.37-pre9/linux/drivers/block/DAC960.h 2003-08-15 15:04:42.000000000 -0700 @@ -0,0 +1,2071 @@ +/* + + Linux Driver for Mylex DAC960 and DAC1100 PCI RAID Controllers + + Copyright 1998-1999 by Leonard N. Zubkoff + + This program is free software; you may redistribute and/or modify it under + the terms of the GNU General Public License Version 2 as published by the + Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for complete details. + + The author respectfully requests that any modifications to this software be + sent directly to him for evaluation and testing. + +*/ + + +/* + DAC960_DriverVersion protects the private portion of this file. +*/ + +#ifdef DAC960_DriverVersion + + +/* + Define the maximum number of DAC960 Controllers supported by this driver. +*/ + +#define DAC960_MaxControllers 8 + + +/* + Define the maximum number of Controller Channels supported by this driver. +*/ + +#define DAC960_MaxChannels 3 + + +/* + Define the maximum number of Targets per Channel supported by this driver. +*/ + +#define DAC960_MaxTargets 16 + + +/* + Define the maximum number of Logical Drives supported by any DAC960 model. +*/ + +#define DAC960_MaxLogicalDrives 32 + + +/* + Define the maximum number of Partitions allowed for each Logical Drive. +*/ + +#define DAC960_MaxPartitions 8 +#define DAC960_MaxPartitionsBits 3 + + +/* + Define the maximum Driver Queue Depth and Controller Queue Depth supported + by any DAC960 model. +*/ + +#define DAC960_MaxDriverQueueDepth 127 +#define DAC960_MaxControllerQueueDepth 128 + + +/* + Define the maximum number of Scatter/Gather Segments supported by any + DAC960 model. +*/ + +#define DAC960_MaxScatterGatherSegments 33 + + +/* + Define the DAC960 Controller Monitoring Timer Interval. +*/ + +#define DAC960_MonitoringTimerInterval (10 * HZ) + + +/* + Define the DAC960 Controller Secondary Monitoring Interval. +*/ + +#define DAC960_SecondaryMonitoringInterval (60 * HZ) + + +/* + Define the DAC960 Controller Progress Reporting Interval. +*/ + +#define DAC960_ProgressReportingInterval (60 * HZ) + + +/* + Define the number of Command Mailboxes and Status Mailboxes used by the + Memory Mailbox Interface. +*/ + +#define DAC960_CommandMailboxCount 256 +#define DAC960_StatusMailboxCount 1024 + + +/* + Define macros to extract the Controller Number, Logical Drive Number, and + Partition Number from a Kernel Device, and to construct a Major Number, Minor + Number, and Kernel Device from the Controller Number, Logical Drive Number, + and Partition Number. There is one Major Number assigned to each Controller. + The associated Minor Number is divided into the Logical Drive Number and + Partition Number. +*/ + +#define DAC960_ControllerNumber(Device) \ + (MAJOR(Device) - DAC960_MAJOR) + +#define DAC960_LogicalDriveNumber(Device) \ + (MINOR(Device) >> DAC960_MaxPartitionsBits) + +#define DAC960_PartitionNumber(Device) \ + (MINOR(Device) & (DAC960_MaxPartitions - 1)) + +#define DAC960_MajorNumber(ControllerNumber) \ + (DAC960_MAJOR + (ControllerNumber)) + +#define DAC960_MinorNumber(LogicalDriveNumber, PartitionNumber) \ + (((LogicalDriveNumber) << DAC960_MaxPartitionsBits) | (PartitionNumber)) + +#define DAC960_MinorCount (DAC960_MaxLogicalDrives \ + * DAC960_MaxPartitions) + +#define DAC960_KernelDevice(ControllerNumber, \ + LogicalDriveNumber, \ + PartitionNumber) \ + MKDEV(DAC960_MajorNumber(ControllerNumber), \ + DAC960_MinorNumber(LogicalDriveNumber, PartitionNumber)) + + +/* + Define the DAC960 Controller fixed Block Size and Block Size Bits. +*/ + +#define DAC960_BlockSize 512 +#define DAC960_BlockSizeBits 9 + + +/* + Define the Controller Line, Status Buffer, Rebuild Progress, and + User Message Sizes. +*/ + +#define DAC960_LineBufferSize 100 +#define DAC960_StatusBufferSize 5000 +#define DAC960_RebuildProgressSize 200 +#define DAC960_UserMessageSize 200 + + +/* + Define the Driver Message Levels. +*/ + +typedef enum DAC960_MessageLevel +{ + DAC960_AnnounceLevel = 0, + DAC960_InfoLevel = 1, + DAC960_NoticeLevel = 2, + DAC960_WarningLevel = 3, + DAC960_ErrorLevel = 4, + DAC960_ProgressLevel = 5, + DAC960_CriticalLevel = 6, + DAC960_UserCriticalLevel = 7 +} +DAC960_MessageLevel_T; + +static char + *DAC960_MessageLevelMap[] = + { KERN_NOTICE, KERN_NOTICE, KERN_NOTICE, KERN_WARNING, + KERN_ERR, KERN_CRIT, KERN_CRIT, KERN_CRIT }; + + +/* + Define Driver Message macros. +*/ + +#define DAC960_Announce(Format, Arguments...) \ + DAC960_Message(DAC960_AnnounceLevel, Format, ##Arguments) + +#define DAC960_Info(Format, Arguments...) \ + DAC960_Message(DAC960_InfoLevel, Format, ##Arguments) + +#define DAC960_Notice(Format, Arguments...) \ + DAC960_Message(DAC960_NoticeLevel, Format, ##Arguments) + +#define DAC960_Warning(Format, Arguments...) \ + DAC960_Message(DAC960_WarningLevel, Format, ##Arguments) + +#define DAC960_Error(Format, Arguments...) \ + DAC960_Message(DAC960_ErrorLevel, Format, ##Arguments) + +#define DAC960_Progress(Format, Arguments...) \ + DAC960_Message(DAC960_ProgressLevel, Format, ##Arguments) + +#define DAC960_Critical(Format, Arguments...) \ + DAC960_Message(DAC960_CriticalLevel, Format, ##Arguments) + +#define DAC960_UserCritical(Format, Arguments...) \ + DAC960_Message(DAC960_UserCriticalLevel, Format, ##Arguments) + + +/* + Define the types of DAC960 Controllers that are supported. +*/ + +typedef enum +{ + DAC960_V5_Controller = 1, /* DAC1164P */ + DAC960_V4_Controller = 2, /* DAC960PTL/PJ/PG */ + DAC960_V3_Controller = 3 /* DAC960PU/PD/PL */ +} +DAC960_ControllerType_T; + + +/* + Define a Boolean data type. +*/ + +typedef enum { false, true } __attribute__ ((packed)) boolean; + + +/* + Define a 32 bit I/O Address data type. +*/ + +typedef unsigned int DAC960_IO_Address_T; + + +/* + Define a 32 bit PCI Bus Address data type. +*/ + +typedef unsigned int DAC960_PCI_Address_T; + + +/* + Define a 32 bit Bus Address data type. +*/ + +typedef unsigned int DAC960_BusAddress_T; + + +/* + Define a 32 bit Byte Count data type. +*/ + +typedef unsigned int DAC960_ByteCount_T; + + +/* + Define types for some of the structures that interface with the rest + of the Linux Kernel and I/O Subsystem. +*/ + +typedef struct buffer_head BufferHeader_T; +typedef struct file File_T; +typedef struct file_operations FileOperations_T; +typedef struct gendisk GenericDiskInfo_T; +typedef struct hd_geometry DiskGeometry_T; +typedef struct hd_struct DiskPartition_T; +typedef struct inode Inode_T; +typedef struct inode_operations InodeOperations_T; +typedef kdev_t KernelDevice_T; +typedef struct proc_dir_entry PROC_DirectoryEntry_T; +typedef unsigned long ProcessorFlags_T; +typedef struct pt_regs Registers_T; +typedef struct request IO_Request_T; +typedef struct semaphore Semaphore_T; +typedef struct timer_list Timer_T; +typedef struct wait_queue WaitQueue_T; + + +/* + Define the DAC960 V5 Controller Interface Register Offsets. +*/ + +#define DAC960_V5_RegisterWindowSize 0x80 + +typedef enum +{ + DAC960_V5_InboundDoorBellRegisterOffset = 0x60, + DAC960_V5_OutboundDoorBellRegisterOffset = 0x61, + DAC960_V5_InterruptMaskRegisterOffset = 0x34, + DAC960_V5_CommandOpcodeRegisterOffset = 0x50, + DAC960_V5_CommandIdentifierRegisterOffset = 0x51, + DAC960_V5_MailboxRegister2Offset = 0x52, + DAC960_V5_MailboxRegister3Offset = 0x53, + DAC960_V5_MailboxRegister4Offset = 0x54, + DAC960_V5_MailboxRegister5Offset = 0x55, + DAC960_V5_MailboxRegister6Offset = 0x56, + DAC960_V5_MailboxRegister7Offset = 0x57, + DAC960_V5_MailboxRegister8Offset = 0x58, + DAC960_V5_MailboxRegister9Offset = 0x59, + DAC960_V5_MailboxRegister10Offset = 0x5A, + DAC960_V5_MailboxRegister11Offset = 0x5B, + DAC960_V5_MailboxRegister12Offset = 0x5C, + DAC960_V5_StatusCommandIdentifierRegOffset = 0x5D, + DAC960_V5_StatusRegisterOffset = 0x5E +} +DAC960_V5_RegisterOffsets_T; + + +/* + Define the structure of the DAC960 V5 Inbound Door Bell Register. +*/ + +typedef union DAC960_V5_InboundDoorBellRegister +{ + unsigned char All; + struct { + boolean HardwareMailboxNewCommand:1; /* Bit 0 */ + boolean AcknowledgeHardwareMailboxStatus:1; /* Bit 1 */ + boolean GenerateInterrupt:1; /* Bit 2 */ + boolean ControllerReset:1; /* Bit 3 */ + boolean MemoryMailboxNewCommand:1; /* Bit 4 */ + unsigned char :3; /* Bits 5-7 */ + } Write; + struct { + boolean HardwareMailboxEmpty:1; /* Bit 0 */ + unsigned char :7; /* Bits 1-7 */ + } Read; +} +DAC960_V5_InboundDoorBellRegister_T; + + +/* + Define the structure of the DAC960 V5 Outbound Door Bell Register. +*/ + +typedef union DAC960_V5_OutboundDoorBellRegister +{ + unsigned char All; + struct { + boolean AcknowledgeHardwareMailboxInterrupt:1; /* Bit 0 */ + boolean AcknowledgeMemoryMailboxInterrupt:1; /* Bit 1 */ + unsigned char :6; /* Bits 2-7 */ + } Write; + struct { + boolean HardwareMailboxStatusAvailable:1; /* Bit 0 */ + boolean MemoryMailboxStatusAvailable:1; /* Bit 1 */ + unsigned char :6; /* Bits 2-7 */ + } Read; +} +DAC960_V5_OutboundDoorBellRegister_T; + + +/* + Define the structure of the DAC960 V5 Interrupt Mask Register. +*/ + +typedef union DAC960_V5_InterruptMaskRegister +{ + unsigned char All; + struct { + unsigned char :2; /* Bits 0-1 */ + boolean DisableInterrupts:1; /* Bit 2 */ + unsigned char :5; /* Bits 3-7 */ + } Bits; +} +DAC960_V5_InterruptMaskRegister_T; + + +/* + Define the DAC960 V4 Controller Interface Register Offsets. +*/ + +#define DAC960_V4_RegisterWindowSize 0x2000 + +typedef enum +{ + DAC960_V4_InboundDoorBellRegisterOffset = 0x0020, + DAC960_V4_OutboundDoorBellRegisterOffset = 0x002C, + DAC960_V4_InterruptMaskRegisterOffset = 0x0034, + DAC960_V4_CommandOpcodeRegisterOffset = 0x1000, + DAC960_V4_CommandIdentifierRegisterOffset = 0x1001, + DAC960_V4_MailboxRegister2Offset = 0x1002, + DAC960_V4_MailboxRegister3Offset = 0x1003, + DAC960_V4_MailboxRegister4Offset = 0x1004, + DAC960_V4_MailboxRegister5Offset = 0x1005, + DAC960_V4_MailboxRegister6Offset = 0x1006, + DAC960_V4_MailboxRegister7Offset = 0x1007, + DAC960_V4_MailboxRegister8Offset = 0x1008, + DAC960_V4_MailboxRegister9Offset = 0x1009, + DAC960_V4_MailboxRegister10Offset = 0x100A, + DAC960_V4_MailboxRegister11Offset = 0x100B, + DAC960_V4_MailboxRegister12Offset = 0x100C, + DAC960_V4_StatusCommandIdentifierRegOffset = 0x1018, + DAC960_V4_StatusRegisterOffset = 0x101A +} +DAC960_V4_RegisterOffsets_T; + + +/* + Define the structure of the DAC960 V4 Inbound Door Bell Register. +*/ + +typedef union DAC960_V4_InboundDoorBellRegister +{ + unsigned int All; + struct { + boolean HardwareMailboxNewCommand:1; /* Bit 0 */ + boolean AcknowledgeHardwareMailboxStatus:1; /* Bit 1 */ + boolean GenerateInterrupt:1; /* Bit 2 */ + boolean ControllerReset:1; /* Bit 3 */ + boolean MemoryMailboxNewCommand:1; /* Bit 4 */ + unsigned int :27; /* Bits 5-31 */ + } Write; + struct { + boolean HardwareMailboxFull:1; /* Bit 0 */ + unsigned int :31; /* Bits 1-31 */ + } Read; +} +DAC960_V4_InboundDoorBellRegister_T; + + +/* + Define the structure of the DAC960 V4 Outbound Door Bell Register. +*/ + +typedef union DAC960_V4_OutboundDoorBellRegister +{ + unsigned int All; + struct { + boolean AcknowledgeHardwareMailboxInterrupt:1; /* Bit 0 */ + boolean AcknowledgeMemoryMailboxInterrupt:1; /* Bit 1 */ + unsigned int :30; /* Bits 2-31 */ + } Write; + struct { + boolean HardwareMailboxStatusAvailable:1; /* Bit 0 */ + boolean MemoryMailboxStatusAvailable:1; /* Bit 1 */ + unsigned int :30; /* Bits 2-31 */ + } Read; +} +DAC960_V4_OutboundDoorBellRegister_T; + + +/* + Define the structure of the DAC960 V4 Interrupt Mask Register. +*/ + +typedef union DAC960_V4_InterruptMaskRegister +{ + unsigned int All; + struct { + unsigned int MessageUnitInterruptMask1:2; /* Bits 0-1 */ + boolean DisableInterrupts:1; /* Bit 2 */ + unsigned int MessageUnitInterruptMask2:5; /* Bits 3-7 */ + unsigned int Reserved0:24; /* Bits 8-31 */ + } Bits; +} +DAC960_V4_InterruptMaskRegister_T; + + +/* + Define the DAC960 V3 Controller Interface Register Offsets. +*/ + +#define DAC960_V3_RegisterWindowSize 0x80 + +typedef enum +{ + DAC960_V3_CommandOpcodeRegisterOffset = 0x00, + DAC960_V3_CommandIdentifierRegisterOffset = 0x01, + DAC960_V3_MailboxRegister2Offset = 0x02, + DAC960_V3_MailboxRegister3Offset = 0x03, + DAC960_V3_MailboxRegister4Offset = 0x04, + DAC960_V3_MailboxRegister5Offset = 0x05, + DAC960_V3_MailboxRegister6Offset = 0x06, + DAC960_V3_MailboxRegister7Offset = 0x07, + DAC960_V3_MailboxRegister8Offset = 0x08, + DAC960_V3_MailboxRegister9Offset = 0x09, + DAC960_V3_MailboxRegister10Offset = 0x0A, + DAC960_V3_MailboxRegister11Offset = 0x0B, + DAC960_V3_MailboxRegister12Offset = 0x0C, + DAC960_V3_StatusCommandIdentifierRegOffset = 0x0D, + DAC960_V3_StatusRegisterOffset = 0x0E, + DAC960_V3_InboundDoorBellRegisterOffset = 0x40, + DAC960_V3_OutboundDoorBellRegisterOffset = 0x41, + DAC960_V3_InterruptEnableRegisterOffset = 0x43 +} +DAC960_V3_RegisterOffsets_T; + + +/* + Define the structure of the DAC960 V3 Inbound Door Bell Register. +*/ + +typedef union DAC960_V3_InboundDoorBellRegister +{ + unsigned char All; + struct { + boolean NewCommand:1; /* Bit 0 */ + boolean AcknowledgeStatus:1; /* Bit 1 */ + boolean GenerateInterrupt:1; /* Bit 2 */ + boolean ControllerReset:1; /* Bit 3 */ + unsigned char :4; /* Bits 4-7 */ + } Write; + struct { + boolean MailboxFull:1; /* Bit 0 */ + unsigned char :7; /* Bits 1-7 */ + } Read; +} +DAC960_V3_InboundDoorBellRegister_T; + + +/* + Define the structure of the DAC960 V3 Outbound Door Bell Register. +*/ + +typedef union DAC960_V3_OutboundDoorBellRegister +{ + unsigned char All; + struct { + boolean AcknowledgeInterrupt:1; /* Bit 0 */ + unsigned char :7; /* Bits 1-7 */ + } Write; + struct { + boolean StatusAvailable:1; /* Bit 0 */ + unsigned char :7; /* Bits 1-7 */ + } Read; +} +DAC960_V3_OutboundDoorBellRegister_T; + + +/* + Define the structure of the DAC960 V3 Interrupt Enable Register. +*/ + +typedef union DAC960_V3_InterruptEnableRegister +{ + unsigned char All; + struct { + boolean EnableInterrupts:1; /* Bit 0 */ + unsigned char :7; /* Bits 1-7 */ + } Bits; +} +DAC960_V3_InterruptEnableRegister_T; + + +/* + Define the DAC960 Command Identifier type. +*/ + +typedef unsigned char DAC960_CommandIdentifier_T; + + +/* + Define the DAC960 Command Opcodes. +*/ + +typedef enum +{ + /* I/O Commands */ + DAC960_ReadExtended = 0x33, + DAC960_WriteExtended = 0x34, + DAC960_ReadAheadExtended = 0x35, + DAC960_ReadExtendedWithScatterGather = 0xB3, + DAC960_WriteExtendedWithScatterGather = 0xB4, + DAC960_Read = 0x36, + DAC960_ReadWithOldScatterGather = 0xB6, + DAC960_Write = 0x37, + DAC960_WriteWithOldScatterGather = 0xB7, + DAC960_DCDB = 0x04, + DAC960_DCDBWithScatterGather = 0x84, + DAC960_Flush = 0x0A, + /* Controller Status Related Commands */ + DAC960_Enquiry = 0x53, + DAC960_Enquiry2 = 0x1C, + DAC960_GetLogicalDriveElement = 0x55, + DAC960_GetLogicalDriveInformation = 0x19, + DAC960_IOPortRead = 0x39, + DAC960_IOPortWrite = 0x3A, + DAC960_GetSDStats = 0x3E, + DAC960_GetPDStats = 0x3F, + DAC960_PerformEventLogOperation = 0x72, + /* Device Related Commands */ + DAC960_StartDevice = 0x10, + DAC960_GetDeviceState = 0x50, + DAC960_StopChannel = 0x13, + DAC960_StartChannel = 0x12, + DAC960_ResetChannel = 0x1A, + /* Commands Associated with Data Consistency and Errors */ + DAC960_Rebuild = 0x09, + DAC960_RebuildAsync = 0x16, + DAC960_CheckConsistency = 0x0F, + DAC960_CheckConsistencyAsync = 0x1E, + DAC960_RebuildStat = 0x0C, + DAC960_GetRebuildProgress = 0x27, + DAC960_RebuildControl = 0x1F, + DAC960_ReadBadBlockTable = 0x0B, + DAC960_ReadBadDataTable = 0x25, + DAC960_ClearBadDataTable = 0x26, + DAC960_GetErrorTable = 0x17, + DAC960_AddCapacityAsync = 0x2A, + /* Configuration Related Commands */ + DAC960_ReadConfig2 = 0x3D, + DAC960_WriteConfig2 = 0x3C, + DAC960_ReadConfigurationOnDisk = 0x4A, + DAC960_WriteConfigurationOnDisk = 0x4B, + DAC960_ReadConfiguration = 0x4E, + DAC960_ReadBackupConfiguration = 0x4D, + DAC960_WriteConfiguration = 0x4F, + DAC960_AddConfiguration = 0x4C, + DAC960_ReadConfigurationLabel = 0x48, + DAC960_WriteConfigurationLabel = 0x49, + /* Firmware Upgrade Related Commands */ + DAC960_LoadImage = 0x20, + DAC960_StoreImage = 0x21, + DAC960_ProgramImage = 0x22, + /* Diagnostic Commands */ + DAC960_SetDiagnosticMode = 0x31, + DAC960_RunDiagnostic = 0x32, + /* Subsystem Service Commands */ + DAC960_GetSubsystemData = 0x70, + DAC960_SetSubsystemParameters = 0x71 +} +__attribute__ ((packed)) +DAC960_CommandOpcode_T; + + +/* + Define the DAC960 Command Status Codes. +*/ + +#define DAC960_NormalCompletion 0x0000 /* Common */ +#define DAC960_CheckConditionReceived 0x0002 /* Common */ +#define DAC960_NoDeviceAtAddress 0x0102 /* Common */ +#define DAC960_InvalidDeviceAddress 0x0105 /* Common */ +#define DAC960_InvalidParameter 0x0105 /* Common */ +#define DAC960_IrrecoverableDataError 0x0001 /* I/O */ +#define DAC960_LogicalDriveNonexistentOrOffline 0x0002 /* I/O */ +#define DAC960_AccessBeyondEndOfLogicalDrive 0x0105 /* I/O */ +#define DAC960_BadDataEncountered 0x010C /* I/O */ +#define DAC960_DeviceBusy 0x0008 /* DCDB */ +#define DAC960_DeviceNonresponsive 0x000E /* DCDB */ +#define DAC960_CommandTerminatedAbnormally 0x000F /* DCDB */ +#define DAC960_UnableToStartDevice 0x0002 /* Device */ +#define DAC960_InvalidChannelOrTargetOrModifier 0x0105 /* Device */ +#define DAC960_ChannelBusy 0x0106 /* Device */ +#define DAC960_ChannelNotStopped 0x0002 /* Device */ +#define DAC960_AttemptToRebuildOnlineDrive 0x0002 /* Consistency */ +#define DAC960_RebuildBadBlocksEncountered 0x0003 /* Consistency */ +#define DAC960_NewDiskFailedDuringRebuild 0x0004 /* Consistency */ +#define DAC960_RebuildOrCheckAlreadyInProgress 0x0106 /* Consistency */ +#define DAC960_DependentDiskIsDead 0x0002 /* Consistency */ +#define DAC960_InconsistentBlocksFound 0x0003 /* Consistency */ +#define DAC960_InvalidOrNonredundantLogicalDrive 0x0105 /* Consistency */ +#define DAC960_NoRebuildOrCheckInProgress 0x0105 /* Consistency */ +#define DAC960_RebuildInProgress_DataValid 0x0000 /* Consistency */ +#define DAC960_RebuildFailed_LogicalDriveFailure 0x0002 /* Consistency */ +#define DAC960_RebuildFailed_BadBlocksOnOther 0x0003 /* Consistency */ +#define DAC960_RebuildFailed_NewDriveFailed 0x0004 /* Consistency */ +#define DAC960_RebuildSuccessful 0x0100 /* Consistency */ +#define DAC960_AddCapacityInProgress 0x0004 /* Consistency */ +#define DAC960_AddCapacityFailedOrSuspended 0x00F4 /* Consistency */ +#define DAC960_Config2ChecksumError 0x0002 /* Configuration */ +#define DAC960_ConfigurationSuspended 0x0106 /* Configuration */ +#define DAC960_FailedToConfigureNVRAM 0x0105 /* Configuration */ +#define DAC960_ConfigurationNotSavedStateChange 0x0106 /* Configuration */ +#define DAC960_SubsystemNotInstalled 0x0001 /* Subsystem */ +#define DAC960_SubsystemFailed 0x0002 /* Subsystem */ +#define DAC960_SubsystemBusy 0x0106 /* Subsystem */ + +typedef unsigned short DAC960_CommandStatus_T; + + +/* + Define the Enquiry reply structure. +*/ + +typedef struct DAC960_Enquiry +{ + unsigned char NumberOfLogicalDrives; /* Byte 0 */ + unsigned int :24; /* Bytes 1-3 */ + unsigned int LogicalDriveSizes[32]; /* Bytes 4-131 */ + unsigned short FlashAge; /* Bytes 132-133 */ + struct { + boolean DeferredWriteError:1; /* Byte 134 Bit 0 */ + boolean BatteryLow:1; /* Byte 134 Bit 1 */ + unsigned char :6; /* Byte 134 Bits 2-7 */ + } StatusFlags; + unsigned char :8; /* Byte 135 */ + unsigned char MinorFirmwareVersion; /* Byte 136 */ + unsigned char MajorFirmwareVersion; /* Byte 137 */ + enum { + DAC960_NoStandbyRebuildOrCheckInProgress = 0x00, + DAC960_StandbyRebuildInProgress = 0x01, + DAC960_BackgroundRebuildInProgress = 0x02, + DAC960_BackgroundCheckInProgress = 0x03, + DAC960_StandbyRebuildCompletedWithError = 0xFF, + DAC960_BackgroundRebuildOrCheckFailed_DriveFailed = 0xF0, + DAC960_BackgroundRebuildOrCheckFailed_LogicalDriveFailed = 0xF1, + DAC960_BackgroundRebuildOrCheckFailed_OtherCauses = 0xF2, + DAC960_BackgroundRebuildOrCheckSuccessfullyTerminated = 0xF3 + } __attribute__ ((packed)) RebuildFlag; /* Byte 138 */ + unsigned char MaxCommands; /* Byte 139 */ + unsigned char OfflineLogicalDriveCount; /* Byte 140 */ + unsigned char :8; /* Byte 141 */ + unsigned short EventLogSequenceNumber; /* Bytes 142-143 */ + unsigned char CriticalLogicalDriveCount; /* Byte 144 */ + unsigned int :24; /* Bytes 145-147 */ + unsigned char DeadDriveCount; /* Byte 148 */ + unsigned char :8; /* Byte 149 */ + unsigned char RebuildCount; /* Byte 150 */ + struct { + unsigned char :3; /* Byte 151 Bits 0-2 */ + boolean BatteryBackupUnitPresent:1; /* Byte 151 Bit 3 */ + unsigned char :3; /* Byte 151 Bits 4-6 */ + unsigned char :1; /* Byte 151 Bit 7 */ + } MiscFlags; + struct { + unsigned char TargetID; + unsigned char Channel; + } DeadDrives[21]; /* Bytes 152-194 */ + unsigned char Reserved[62]; /* Bytes 195-255 */ +} +__attribute__ ((packed)) +DAC960_Enquiry_T; + + +/* + Define the Enquiry2 reply structure. +*/ + +typedef struct DAC960_Enquiry2 +{ + struct { + enum { + DAC960_P_PD_PU = 0x01, + DAC960_PL = 0x02, + DAC960_PG = 0x10, + DAC960_PJ = 0x11, + DAC960_PR = 0x12, + DAC960_PT = 0x13, + DAC960_PTL0 = 0x14, + DAC960_PRL = 0x15, + DAC960_PTL1 = 0x16, + DAC1164_P = 0x20 + } __attribute__ ((packed)) SubModel; /* Byte 0 */ + unsigned char ActualChannels; /* Byte 1 */ + enum { + DAC960_FiveChannelBoard = 0x01, + DAC960_ThreeChannelBoard = 0x02, + DAC960_TwoChannelBoard = 0x03, + DAC960_ThreeChannelASIC_DAC = 0x04 + } __attribute__ ((packed)) Model; /* Byte 2 */ + enum { + DAC960_EISA_Controller = 0x01, + DAC960_MicroChannel_Controller = 0x02, + DAC960_PCI_Controller = 0x03, + DAC960_SCSItoSCSI_Controller = 0x08 + } __attribute__ ((packed)) ProductFamily; /* Byte 3 */ + } HardwareID; /* Bytes 0-3 */ + /* MajorVersion.MinorVersion-FirmwareType-TurnID */ + struct { + unsigned char MajorVersion; /* Byte 4 */ + unsigned char MinorVersion; /* Byte 5 */ + unsigned char TurnID; /* Byte 6 */ + char FirmwareType; /* Byte 7 */ + } FirmwareID; /* Bytes 4-7 */ + unsigned char :8; /* Byte 8 */ + unsigned int :24; /* Bytes 9-11 */ + unsigned char ConfiguredChannels; /* Byte 12 */ + unsigned char ActualChannels; /* Byte 13 */ + unsigned char MaxTargets; /* Byte 14 */ + unsigned char MaxTags; /* Byte 15 */ + unsigned char MaxLogicalDrives; /* Byte 16 */ + unsigned char MaxArms; /* Byte 17 */ + unsigned char MaxSpans; /* Byte 18 */ + unsigned char :8; /* Byte 19 */ + unsigned int :32; /* Bytes 20-23 */ + unsigned int MemorySize; /* Bytes 24-27 */ + unsigned int CacheSize; /* Bytes 28-31 */ + unsigned int FlashMemorySize; /* Bytes 32-35 */ + unsigned int NonVolatileMemorySize; /* Bytes 36-39 */ + struct { + enum { + DAC960_DRAM = 0x00, + DAC960_EDO = 0x01, + DAC960_SDRAM = 0x02 + } __attribute__ ((packed)) RamType:3; /* Byte 40 Bits 0-2 */ + enum { + DAC960_None = 0x00, + DAC960_Parity = 0x01, + DAC960_ECC = 0x02 + } __attribute__ ((packed)) ErrorCorrection:3; /* Byte 40 Bits 3-5 */ + boolean FastPageMode:1; /* Byte 40 Bit 6 */ + boolean LowPowerMemory:1; /* Byte 40 Bit 7 */ + unsigned char :8; /* Bytes 41 */ + } MemoryType; + unsigned short ClockSpeed; /* Bytes 42-43 */ + unsigned short MemorySpeed; /* Bytes 44-45 */ + unsigned short HardwareSpeed; /* Bytes 46-47 */ + unsigned int :32; /* Bytes 48-51 */ + unsigned int :32; /* Bytes 52-55 */ + unsigned char :8; /* Byte 56 */ + unsigned char :8; /* Byte 57 */ + unsigned short :16; /* Bytes 58-59 */ + unsigned short MaxCommands; /* Bytes 60-61 */ + unsigned short MaxScatterGatherEntries; /* Bytes 62-63 */ + unsigned short MaxDriveCommands; /* Bytes 64-65 */ + unsigned short MaxIODescriptors; /* Bytes 66-67 */ + unsigned short MaxCombinedSectors; /* Bytes 68-69 */ + unsigned char Latency; /* Byte 70 */ + unsigned char :8; /* Byte 71 */ + unsigned char SCSITimeout; /* Byte 72 */ + unsigned char :8; /* Byte 73 */ + unsigned short MinFreeLines; /* Bytes 74-75 */ + unsigned int :32; /* Bytes 76-79 */ + unsigned int :32; /* Bytes 80-83 */ + unsigned char RebuildRateConstant; /* Byte 84 */ + unsigned char :8; /* Byte 85 */ + unsigned char :8; /* Byte 86 */ + unsigned char :8; /* Byte 87 */ + unsigned int :32; /* Bytes 88-91 */ + unsigned int :32; /* Bytes 92-95 */ + unsigned short PhysicalDriveBlockSize; /* Bytes 96-97 */ + unsigned short LogicalDriveBlockSize; /* Bytes 98-99 */ + unsigned short MaxBlocksPerCommand; /* Bytes 100-101 */ + unsigned short BlockFactor; /* Bytes 102-103 */ + unsigned short CacheLineSize; /* Bytes 104-105 */ + struct { + enum { + DAC960_Narrow_8bit = 0x00, + DAC960_Wide_16bit = 0x01, + DAC960_Wide_32bit = 0x02 + } __attribute__ ((packed)) BusWidth:2; /* Byte 106 Bits 0-1 */ + enum { + DAC960_Fast = 0x00, + DAC960_Ultra = 0x01, + DAC960_Ultra2 = 0x02 + } __attribute__ ((packed)) BusSpeed:2; /* Byte 106 Bits 2-3 */ + boolean Differential:1; /* Byte 106 Bit 4 */ + unsigned char :3; /* Byte 106 Bits 5-7 */ + } SCSICapability; + unsigned char :8; /* Byte 107 */ + unsigned int :32; /* Bytes 108-111 */ + unsigned short FirmwareBuildNumber; /* Bytes 112-113 */ + enum { + DAC960_AEMI = 0x01, + DAC960_OEM1 = 0x02, + DAC960_OEM2 = 0x04, + DAC960_OEM3 = 0x08, + DAC960_Conner = 0x10, + DAC960_SAFTE = 0x20 + } __attribute__ ((packed)) FaultManagementType; /* Byte 114 */ + unsigned char :8; /* Byte 115 */ + struct { + boolean Clustering:1; /* Byte 116 Bit 0 */ + boolean MylexOnlineRAIDExpansion:1; /* Byte 116 Bit 1 */ + unsigned int :30; /* Bytes 116-119 */ + } FirmwareFeatures; + unsigned int :32; /* Bytes 120-123 */ + unsigned int :32; /* Bytes 124-127 */ +} +DAC960_Enquiry2_T; + + +/* + Define the Logical Drive State type. +*/ + +typedef enum +{ + DAC960_LogicalDrive_Online = 0x03, + DAC960_LogicalDrive_Critical = 0x04, + DAC960_LogicalDrive_Offline = 0xFF +} +__attribute__ ((packed)) +DAC960_LogicalDriveState_T; + + +/* + Define the Get Logical Drive Information reply structure. +*/ + +typedef struct DAC960_LogicalDriveInformation +{ + unsigned int LogicalDriveSize; /* Bytes 0-3 */ + DAC960_LogicalDriveState_T LogicalDriveState; /* Byte 4 */ + unsigned char RAIDLevel:7; /* Byte 5 Bits 0-6 */ + boolean WriteBack:1; /* Byte 5 Bit 7 */ + unsigned int :16; /* Bytes 6-7 */ +} +DAC960_LogicalDriveInformation_T; + + +/* + Define the Perform Event Log Operation Types. +*/ + +typedef enum +{ + DAC960_GetEventLogEntry = 0x00 +} +__attribute__ ((packed)) +DAC960_PerformEventLogOpType_T; + + +/* + Define the Get Event Log Entry reply structure. +*/ + +typedef struct DAC960_EventLogEntry +{ + unsigned char MessageType; /* Byte 0 */ + unsigned char MessageLength; /* Byte 1 */ + unsigned char TargetID:5; /* Byte 2 Bits 0-4 */ + unsigned char Channel:3; /* Byte 2 Bits 5-7 */ + unsigned char LogicalUnit:6; /* Byte 3 Bits 0-5 */ + unsigned char :2; /* Byte 3 Bits 6-7 */ + unsigned short SequenceNumber; /* Bytes 4-5 */ + unsigned char ErrorCode:7; /* Byte 6 Bits 0-6 */ + boolean Valid:1; /* Byte 6 Bit 7 */ + unsigned char SegmentNumber; /* Byte 7 */ + unsigned char SenseKey:4; /* Byte 8 Bits 0-3 */ + unsigned char :1; /* Byte 8 Bit 4 */ + boolean ILI:1; /* Byte 8 Bit 5 */ + boolean EOM:1; /* Byte 8 Bit 6 */ + boolean Filemark:1; /* Byte 8 Bit 7 */ + unsigned char Information[4]; /* Bytes 9-12 */ + unsigned char AdditionalSenseLength; /* Byte 13 */ + unsigned char CommandSpecificInformation[4]; /* Bytes 14-17 */ + unsigned char AdditionalSenseCode; /* Byte 18 */ + unsigned char AdditionalSenseCodeQualifier; /* Byte 19 */ + unsigned char Dummy[12]; /* Bytes 20-31 */ +} +DAC960_EventLogEntry_T; + +#define DAC960_EventMessagesCount 13 + +static char + *DAC960_EventMessages[DAC960_EventMessagesCount] = + { "killed because write recovery failed", + "killed because of SCSI bus reset failure", + "killed because of double check condition", + "killed because it was removed", + "killed because of gross error on SCSI chip", + "killed because of bad tag returned from drive", + "killed because of timeout on SCSI command", + "killed because of reset SCSI command issued from system", + "killed because busy or parity error count exceeded limit", + "killed because of 'kill drive' command from system", + "killed because of selection timeout", + "killed due to SCSI phase sequence error", + "killed due to unknown status" }; + + +/* + Define the Physical Device State type. +*/ + +typedef enum +{ + DAC960_Device_Dead = 0x00, + DAC960_Device_WriteOnly = 0x02, + DAC960_Device_Online = 0x03, + DAC960_Device_Standby = 0x10 +} +__attribute__ ((packed)) +DAC960_PhysicalDeviceState_T; + + +/* + Define the Get Device State reply structure. +*/ + +typedef struct DAC960_DeviceState +{ + boolean Present:1; /* Byte 0 Bit 0 */ + unsigned char :7; /* Byte 0 Bits 1-7 */ + enum { + DAC960_OtherType = 0x00, + DAC960_DiskType = 0x01, + DAC960_SequentialType = 0x02, + DAC960_CDROM_or_WORM_Type = 0x03 + } __attribute__ ((packed)) DeviceType:2; /* Byte 1 Bits 0-1 */ + boolean :1; /* Byte 1 Bit 2 */ + boolean Fast20:1; /* Byte 1 Bit 3 */ + boolean Sync:1; /* Byte 1 Bit 4 */ + boolean Fast:1; /* Byte 1 Bit 5 */ + boolean Wide:1; /* Byte 1 Bit 6 */ + boolean TaggedQueuingSupported:1; /* Byte 1 Bit 7 */ + DAC960_PhysicalDeviceState_T DeviceState; /* Byte 2 */ + unsigned char :8; /* Byte 3 */ + unsigned char SynchronousMultiplier; /* Byte 4 */ + unsigned char SynchronousOffset:5; /* Byte 5 Bits 0-4 */ + unsigned char :3; /* Byte 5 Bits 5-7 */ + unsigned long DiskSize __attribute__ ((packed)); /* Bytes 6-9 */ +} +DAC960_DeviceState_T; + + +/* + Define the Get Rebuild Progress reply structure. +*/ + +typedef struct DAC960_RebuildProgress +{ + unsigned int LogicalDriveNumber; /* Bytes 0-3 */ + unsigned int LogicalDriveSize; /* Bytes 4-7 */ + unsigned int RemainingBlocks; /* Bytes 8-11 */ +} +DAC960_RebuildProgress_T; + + +/* + Define the Error Table Entry and Get Error Table reply structure. +*/ + +typedef struct DAC960_ErrorTableEntry +{ + unsigned char ParityErrorCount; /* Byte 0 */ + unsigned char SoftErrorCount; /* Byte 1 */ + unsigned char HardErrorCount; /* Byte 2 */ + unsigned char MiscErrorCount; /* Byte 3 */ +} +DAC960_ErrorTableEntry_T; + + +/* + Define the Get Error Table reply structure. +*/ + +typedef struct DAC960_ErrorTable +{ + DAC960_ErrorTableEntry_T + ErrorTableEntries[DAC960_MaxChannels][DAC960_MaxTargets]; +} +DAC960_ErrorTable_T; + + +/* + Define the Config2 reply structure. +*/ + +typedef struct DAC960_Config2 +{ + unsigned char :1; /* Byte 0 Bit 0 */ + boolean ActiveNegationEnabled:1; /* Byte 0 Bit 1 */ + unsigned char :5; /* Byte 0 Bits 2-6 */ + boolean NoRescanIfResetReceivedDuringScan:1; /* Byte 0 Bit 7 */ + boolean StorageWorksSupportEnabled:1; /* Byte 1 Bit 0 */ + boolean HewlettPackardSupportEnabled:1; /* Byte 1 Bit 1 */ + boolean NoDisconnectOnFirstCommand:1; /* Byte 1 Bit 2 */ + unsigned char :2; /* Byte 1 Bits 3-4 */ + boolean AEMI_ARM:1; /* Byte 1 Bit 5 */ + boolean AEMI_OFM:1; /* Byte 1 Bit 6 */ + unsigned char :1; /* Byte 1 Bit 7 */ + enum { + DAC960_OEMID_Mylex = 0x00, + DAC960_OEMID_IBM = 0x08, + DAC960_OEMID_HP = 0x0A, + DAC960_OEMID_DEC = 0x0C, + DAC960_OEMID_Siemens = 0x10, + DAC960_OEMID_Intel = 0x12 + } __attribute__ ((packed)) OEMID; /* Byte 2 */ + unsigned char OEMModelNumber; /* Byte 3 */ + unsigned char PhysicalSector; /* Byte 4 */ + unsigned char LogicalSector; /* Byte 5 */ + unsigned char BlockFactor; /* Byte 6 */ + boolean ReadAheadEnabled:1; /* Byte 7 Bit 0 */ + boolean LowBIOSDelay:1; /* Byte 7 Bit 1 */ + unsigned char :2; /* Byte 7 Bits 2-3 */ + boolean ReassignRestrictedToOneSector:1; /* Byte 7 Bit 4 */ + unsigned char :1; /* Byte 7 Bit 5 */ + boolean ForceUnitAccessDuringWriteRecovery:1; /* Byte 7 Bit 6 */ + boolean EnableLeftSymmetricRAID5Algorithm:1; /* Byte 7 Bit 7 */ + unsigned char DefaultRebuildRate; /* Byte 8 */ + unsigned char :8; /* Byte 9 */ + unsigned char BlocksPerCacheLine; /* Byte 10 */ + unsigned char BlocksPerStripe; /* Byte 11 */ + struct { + enum { + DAC960_Async = 0x00, + DAC960_Sync_8MHz = 0x01, + DAC960_Sync_5MHz = 0x02, + DAC960_Sync_10or20MHz = 0x03 /* Bits 0-1 */ + } __attribute__ ((packed)) Speed:2; + boolean Force8Bit:1; /* Bit 2 */ + boolean DisableFast20:1; /* Bit 3 */ + unsigned char :3; /* Bits 4-6 */ + boolean EnableTaggedQueuing:1; /* Bit 7 */ + } __attribute__ ((packed)) ChannelParameters[6]; /* Bytes 12-17 */ + unsigned char SCSIInitiatorID; /* Byte 18 */ + unsigned char :8; /* Byte 19 */ + enum { + DAC960_StartupMode_ControllerSpinUp = 0x00, + DAC960_StartupMode_PowerOnSpinUp = 0x01 + } __attribute__ ((packed)) StartupMode; /* Byte 20 */ + unsigned char SimultaneousDeviceSpinUpCount; /* Byte 21 */ + unsigned char SecondsDelayBetweenSpinUps; /* Byte 22 */ + unsigned char Reserved1[29]; /* Bytes 23-51 */ + boolean BIOSDisabled:1; /* Byte 52 Bit 0 */ + boolean CDROMBootEnabled:1; /* Byte 52 Bit 1 */ + unsigned char :3; /* Byte 52 Bits 2-4 */ + enum { + DAC960_Geometry_128_32 = 0x00, + DAC960_Geometry_255_63 = 0x01, + DAC960_Geometry_Reserved1 = 0x02, + DAC960_Geometry_Reserved2 = 0x03 + } __attribute__ ((packed)) DriveGeometry:2; /* Byte 52 Bits 5-6 */ + unsigned char :1; /* Byte 52 Bit 7 */ + unsigned char Reserved2[9]; /* Bytes 53-61 */ + unsigned short Checksum; /* Bytes 62-63 */ +} +DAC960_Config2_T; + + +/* + Define the Scatter/Gather List Type 1 32 Bit Address 32 Bit Byte Count + structure. +*/ + +typedef struct DAC960_ScatterGatherSegment +{ + DAC960_BusAddress_T SegmentDataPointer; /* Bytes 0-3 */ + DAC960_ByteCount_T SegmentByteCount; /* Bytes 4-7 */ +} +DAC960_ScatterGatherSegment_T; + + +/* + Define the 13 Byte DAC960 Command Mailbox structure. Bytes 13-15 are + not used. The Command Mailbox structure is padded to 16 bytes for + efficient access. +*/ + +typedef union DAC960_CommandMailbox +{ + unsigned int Words[4]; /* Words 0-3 */ + unsigned char Bytes[16]; /* Bytes 0-15 */ + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + unsigned char Dummy[14]; /* Bytes 2-15 */ + } __attribute__ ((packed)) Common; + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + unsigned char Dummy1[6]; /* Bytes 2-7 */ + DAC960_BusAddress_T BusAddress; /* Bytes 8-11 */ + unsigned char Dummy2[4]; /* Bytes 12-15 */ + } __attribute__ ((packed)) Type3; + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + unsigned char Dummy1[5]; /* Bytes 2-6 */ + unsigned char LogicalDriveNumber:6; /* Byte 7 Bits 0-6 */ + boolean AutoRestore:1; /* Byte 7 Bit 7 */ + unsigned char Dummy2[8]; /* Bytes 8-15 */ + } __attribute__ ((packed)) Type3C; + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + unsigned char Channel; /* Byte 2 */ + unsigned char TargetID; /* Byte 3 */ + DAC960_PhysicalDeviceState_T DeviceState:5; /* Byte 4 Bits 0-4 */ + unsigned char Modifier:3; /* Byte 4 Bits 5-7 */ + unsigned char Dummy1[3]; /* Bytes 5-7 */ + DAC960_BusAddress_T BusAddress; /* Bytes 8-11 */ + unsigned char Dummy2[4]; /* Bytes 12-15 */ + } __attribute__ ((packed)) Type3D; + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + DAC960_PerformEventLogOpType_T OperationType; /* Byte 2 */ + unsigned char OperationQualifier; /* Byte 3 */ + unsigned short SequenceNumber; /* Bytes 4-5 */ + unsigned char Dummy1[2]; /* Bytes 6-7 */ + DAC960_BusAddress_T BusAddress; /* Bytes 8-11 */ + unsigned char Dummy2[4]; /* Bytes 12-15 */ + } __attribute__ ((packed)) Type3E; + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + unsigned char Dummy1[2]; /* Bytes 2-3 */ + unsigned char RebuildRateConstant; /* Byte 4 */ + unsigned char Dummy2[3]; /* Bytes 5-7 */ + DAC960_BusAddress_T BusAddress; /* Bytes 8-11 */ + unsigned char Dummy3[4]; /* Bytes 12-15 */ + } __attribute__ ((packed)) Type3R; + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + struct { + unsigned short TransferLength:11; /* Bytes 2-3 */ + unsigned char LogicalDriveNumber:5; /* Byte 3 Bits 3-7 */ + } __attribute__ ((packed)) LD; + unsigned int LogicalBlockAddress; /* Bytes 4-7 */ + DAC960_BusAddress_T BusAddress; /* Bytes 8-11 */ + unsigned char ScatterGatherCount:6; /* Byte 12 Bits 0-5 */ + enum { + DAC960_ScatterGather_32BitAddress_32BitByteCount = 0x0, + DAC960_ScatterGather_32BitAddress_16BitByteCount = 0x1, + DAC960_ScatterGather_32BitByteCount_32BitAddress = 0x2, + DAC960_ScatterGather_16BitByteCount_32BitAddress = 0x3 + } __attribute__ ((packed)) ScatterGatherType:2; /* Byte 12 Bits 6-7 */ + unsigned char Dummy[3]; /* Bytes 13-15 */ + } __attribute__ ((packed)) Type5; + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + unsigned char CommandOpcode2; /* Byte 2 */ + unsigned char :8; /* Byte 3 */ + DAC960_BusAddress_T CommandMailboxesBusAddress; /* Bytes 4-7 */ + DAC960_BusAddress_T StatusMailboxesBusAddress; /* Bytes 8-11 */ + unsigned char Dummy[4]; /* Bytes 12-15 */ + } __attribute__ ((packed)) TypeX; +} +DAC960_CommandMailbox_T; + + +/* + Define the DAC960 Controller Status Mailbox structure. +*/ + +typedef union DAC960_StatusMailbox +{ + unsigned int Word; /* Bytes 0-3 */ + struct { + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 0 */ + unsigned char :7; /* Byte 1 Bits 0-6 */ + boolean Valid:1; /* Byte 1 Bit 7 */ + DAC960_CommandStatus_T CommandStatus; /* Bytes 2-3 */ + } Fields; +} +DAC960_StatusMailbox_T; + + +/* + Define the DAC960 Driver Command Types. +*/ + +typedef enum +{ + DAC960_ReadCommand = 1, + DAC960_WriteCommand = 2, + DAC960_ReadRetryCommand = 3, + DAC960_WriteRetryCommand = 4, + DAC960_MonitoringCommand = 5, + DAC960_ImmediateCommand = 6 +} +DAC960_CommandType_T; + + +/* + Define the DAC960 Driver Command structure. +*/ + +typedef struct DAC960_Command +{ + DAC960_CommandType_T CommandType; + DAC960_CommandMailbox_T CommandMailbox; + DAC960_CommandStatus_T CommandStatus; + struct DAC960_Controller *Controller; + struct DAC960_Command *Next; + Semaphore_T *Semaphore; + unsigned int LogicalDriveNumber; + unsigned int BlockNumber; + unsigned int BlockCount; + unsigned int SegmentCount; + BufferHeader_T *BufferHeader; + DAC960_ScatterGatherSegment_T + ScatterGatherList[DAC960_MaxScatterGatherSegments]; +} +DAC960_Command_T; + + +/* + Define the DAC960 Driver Controller structure. +*/ + +typedef struct DAC960_Controller +{ + void *BaseAddress; + void *MemoryMappedAddress; + DAC960_ControllerType_T ControllerType; + DAC960_IO_Address_T IO_Address; + DAC960_PCI_Address_T PCI_Address; + unsigned char ControllerNumber; + unsigned char ControllerName[4]; + unsigned char ModelName[12]; + unsigned char FullModelName[18]; + unsigned char FirmwareVersion[14]; + unsigned char Bus; + unsigned char Device; + unsigned char Function; + unsigned char IRQ_Channel; + unsigned char Channels; + unsigned char MemorySize; + unsigned char LogicalDriveCount; + unsigned char GeometryTranslationHeads; + unsigned char GeometryTranslationSectors; + unsigned short ControllerQueueDepth; + unsigned short DriverQueueDepth; + unsigned short MaxBlocksPerCommand; + unsigned short MaxScatterGatherSegments; + unsigned short StripeSize; + unsigned short SegmentSize; + unsigned short NewEventLogSequenceNumber; + unsigned short OldEventLogSequenceNumber; + unsigned short InitialStatusLength; + unsigned short CurrentStatusLength; + unsigned short UserStatusLength; + unsigned short RebuildProgressLength; + unsigned int ControllerUsageCount; + unsigned int EnquiryIndex; + unsigned int LogicalDriveInformationIndex; + unsigned int ErrorTableIndex; + unsigned int DeviceStateIndex; + unsigned int DeviceStateChannel; + unsigned int DeviceStateTargetID; + unsigned long MonitoringTimerCount; + unsigned long SecondaryMonitoringTime; + unsigned long LastProgressReportTime; + boolean DualModeMemoryMailboxInterface; + boolean SAFTE_FaultManagementEnabled; + boolean ControllerInitialized; + boolean UserCommandActive; + boolean UserCommandDeferred; + boolean MonitoringCommandDeferred; + boolean NeedLogicalDriveInformation; + boolean NeedErrorTableInformation; + boolean NeedDeviceStateInformation; + boolean NeedRebuildProgress; + boolean NeedConsistencyCheckProgress; + boolean EphemeralProgressMessage; + Timer_T MonitoringTimer; + Semaphore_T *UserCommandSemaphore; + GenericDiskInfo_T GenericDiskInfo; + DAC960_Command_T *FreeCommands; + DAC960_Command_T *UserCommand; + DAC960_CommandMailbox_T *FirstCommandMailbox; + DAC960_CommandMailbox_T *LastCommandMailbox; + DAC960_CommandMailbox_T *NextCommandMailbox; + DAC960_CommandMailbox_T *PreviousCommandMailbox1; + DAC960_CommandMailbox_T *PreviousCommandMailbox2; + DAC960_StatusMailbox_T *FirstStatusMailbox; + DAC960_StatusMailbox_T *LastStatusMailbox; + DAC960_StatusMailbox_T *NextStatusMailbox; + PROC_DirectoryEntry_T ControllerProcEntry; + PROC_DirectoryEntry_T InitialStatusProcEntry; + PROC_DirectoryEntry_T CurrentStatusProcEntry; + PROC_DirectoryEntry_T UserCommandProcEntry; + WaitQueue_T *CommandWaitQueue; + DAC960_Enquiry_T Enquiry[2]; + DAC960_ErrorTable_T ErrorTable[2]; + DAC960_EventLogEntry_T EventLogEntry; + DAC960_RebuildProgress_T RebuildProgress; + DAC960_LogicalDriveInformation_T + LogicalDriveInformation[2][DAC960_MaxLogicalDrives]; + DAC960_LogicalDriveState_T LogicalDriveInitialState[DAC960_MaxLogicalDrives]; + DAC960_DeviceState_T DeviceState[2][DAC960_MaxChannels][DAC960_MaxTargets]; + DAC960_Command_T Commands[DAC960_MaxDriverQueueDepth]; + DiskPartition_T DiskPartitions[DAC960_MinorCount]; + int LogicalDriveUsageCount[DAC960_MaxLogicalDrives]; + int PartitionSizes[DAC960_MinorCount]; + int BlockSizes[DAC960_MinorCount]; + int MaxSectorsPerRequest[DAC960_MinorCount]; + int MaxSegmentsPerRequest[DAC960_MinorCount]; + char InitialStatusBuffer[DAC960_StatusBufferSize]; + char CurrentStatusBuffer[DAC960_StatusBufferSize]; + char UserStatusBuffer[DAC960_UserMessageSize]; + char RebuildProgressBuffer[DAC960_RebuildProgressSize]; +} +DAC960_Controller_T; + + +/* + DAC960_AcquireControllerLock acquires exclusive access to Controller. +*/ + +static inline +void DAC960_AcquireControllerLock(DAC960_Controller_T *Controller, + ProcessorFlags_T *ProcessorFlags) +{ + save_flags(*ProcessorFlags); + cli(); +} + + +/* + DAC960_ReleaseControllerLock releases exclusive access to Controller. +*/ + +static inline +void DAC960_ReleaseControllerLock(DAC960_Controller_T *Controller, + ProcessorFlags_T *ProcessorFlags) +{ + restore_flags(*ProcessorFlags); +} + + +/* + DAC960_AcquireControllerLockRF acquires exclusive access to Controller, + but is only called from the request function when interrupts are disabled. +*/ + +static inline +void DAC960_AcquireControllerLockRF(DAC960_Controller_T *Controller, + ProcessorFlags_T *ProcessorFlags) +{ +} + + +/* + DAC960_ReleaseControllerLockRF releases exclusive access to Controller, + but is only called from the request function when interrupts are disabled. +*/ + +static inline +void DAC960_ReleaseControllerLockRF(DAC960_Controller_T *Controller, + ProcessorFlags_T *ProcessorFlags) +{ +} + + +/* + DAC960_AcquireControllerLockIH acquires exclusive access to Controller, + but is only called from the interrupt handler when interrupts are disabled. +*/ + +static inline +void DAC960_AcquireControllerLockIH(DAC960_Controller_T *Controller, + ProcessorFlags_T *ProcessorFlags) +{ +} + + +/* + DAC960_ReleaseControllerLockIH releases exclusive access to Controller, + but is only called from the interrupt handler when interrupts are disabled. +*/ + +static inline +void DAC960_ReleaseControllerLockIH(DAC960_Controller_T *Controller, + ProcessorFlags_T *ProcessorFlags) +{ +} + + +/* + Define inline functions to provide an abstraction for reading and writing the + DAC960 V5 Controller Interface Registers. +*/ + +static inline +void DAC960_V5_HardwareMailboxNewCommand(void *ControllerBaseAddress) +{ + DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.HardwareMailboxNewCommand = true; + writeb(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V5_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress) +{ + DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.AcknowledgeHardwareMailboxStatus = true; + writeb(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V5_GenerateInterrupt(void *ControllerBaseAddress) +{ + DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.GenerateInterrupt = true; + writeb(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V5_ControllerReset(void *ControllerBaseAddress) +{ + DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.ControllerReset = true; + writeb(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V5_MemoryMailboxNewCommand(void *ControllerBaseAddress) +{ + DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.MemoryMailboxNewCommand = true; + writeb(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset); +} + +static inline +boolean DAC960_V5_HardwareMailboxEmptyP(void *ControllerBaseAddress) +{ + DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = + readb(ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset); + return InboundDoorBellRegister.Read.HardwareMailboxEmpty; +} + +static inline +void DAC960_V5_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress) +{ + DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = 0; + OutboundDoorBellRegister.Write.AcknowledgeHardwareMailboxInterrupt = true; + writeb(OutboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V5_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress) +{ + DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = 0; + OutboundDoorBellRegister.Write.AcknowledgeMemoryMailboxInterrupt = true; + writeb(OutboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V5_AcknowledgeInterrupt(void *ControllerBaseAddress) +{ + DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = 0; + OutboundDoorBellRegister.Write.AcknowledgeHardwareMailboxInterrupt = true; + OutboundDoorBellRegister.Write.AcknowledgeMemoryMailboxInterrupt = true; + writeb(OutboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset); +} + +static inline +boolean DAC960_V5_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress) +{ + DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = + readb(ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset); + return OutboundDoorBellRegister.Read.HardwareMailboxStatusAvailable; +} + +static inline +boolean DAC960_V5_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress) +{ + DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = + readb(ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset); + return OutboundDoorBellRegister.Read.MemoryMailboxStatusAvailable; +} + +static inline +void DAC960_V5_EnableInterrupts(void *ControllerBaseAddress) +{ + DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister; + InterruptMaskRegister.All = 0; + InterruptMaskRegister.Bits.DisableInterrupts = false; + writeb(InterruptMaskRegister.All, + ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset); +} + +static inline +void DAC960_V5_DisableInterrupts(void *ControllerBaseAddress) +{ + DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister; + InterruptMaskRegister.All = 0; + InterruptMaskRegister.Bits.DisableInterrupts = true; + writeb(InterruptMaskRegister.All, + ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset); +} + +static inline +boolean DAC960_V5_InterruptsEnabledP(void *ControllerBaseAddress) +{ + DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister; + InterruptMaskRegister.All = + readb(ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset); + return !InterruptMaskRegister.Bits.DisableInterrupts; +} + +static inline +void DAC960_V5_WriteCommandMailbox(DAC960_CommandMailbox_T *NextCommandMailbox, + DAC960_CommandMailbox_T *CommandMailbox) +{ + NextCommandMailbox->Words[1] = CommandMailbox->Words[1]; + NextCommandMailbox->Words[2] = CommandMailbox->Words[2]; + NextCommandMailbox->Words[3] = CommandMailbox->Words[3]; + NextCommandMailbox->Words[0] = CommandMailbox->Words[0]; +} + +static inline +void DAC960_V5_WriteHardwareMailbox(void *ControllerBaseAddress, + DAC960_CommandMailbox_T *CommandMailbox) +{ + writel(CommandMailbox->Words[0], + ControllerBaseAddress + DAC960_V5_CommandOpcodeRegisterOffset); + writel(CommandMailbox->Words[1], + ControllerBaseAddress + DAC960_V5_MailboxRegister4Offset); + writel(CommandMailbox->Words[2], + ControllerBaseAddress + DAC960_V5_MailboxRegister8Offset); + writeb(CommandMailbox->Bytes[12], + ControllerBaseAddress + DAC960_V5_MailboxRegister12Offset); +} + +static inline DAC960_CommandIdentifier_T +DAC960_V5_ReadStatusCommandIdentifier(void *ControllerBaseAddress) +{ + return readb(ControllerBaseAddress + + DAC960_V5_StatusCommandIdentifierRegOffset); +} + +static inline DAC960_CommandStatus_T +DAC960_V5_ReadStatusRegister(void *ControllerBaseAddress) +{ + return readw(ControllerBaseAddress + DAC960_V5_StatusRegisterOffset); +} + +static inline +void DAC960_V5_SaveMemoryMailboxInfo(DAC960_Controller_T *Controller) +{ + void *ControllerBaseAddress = Controller->BaseAddress; + writel(0x743C485E, + ControllerBaseAddress + DAC960_V5_CommandOpcodeRegisterOffset); + writel((unsigned long) Controller->FirstCommandMailbox, + ControllerBaseAddress + DAC960_V5_MailboxRegister4Offset); + writew(Controller->NextCommandMailbox - Controller->FirstCommandMailbox, + ControllerBaseAddress + DAC960_V5_MailboxRegister8Offset); + writew(Controller->NextStatusMailbox - Controller->FirstStatusMailbox, + ControllerBaseAddress + DAC960_V5_MailboxRegister10Offset); +} + +static inline +void DAC960_V5_RestoreMemoryMailboxInfo(DAC960_Controller_T *Controller, + void **MemoryMailboxAddress, + short *NextCommandMailboxIndex, + short *NextStatusMailboxIndex) +{ + void *ControllerBaseAddress = Controller->BaseAddress; + if (readl(ControllerBaseAddress + + DAC960_V5_CommandOpcodeRegisterOffset) != 0x743C485E) + return; + *MemoryMailboxAddress = + (void *) readl(ControllerBaseAddress + DAC960_V5_MailboxRegister4Offset); + *NextCommandMailboxIndex = + readw(ControllerBaseAddress + DAC960_V5_MailboxRegister8Offset); + *NextStatusMailboxIndex = + readw(ControllerBaseAddress + DAC960_V5_MailboxRegister10Offset); +} + + +/* + Define inline functions to provide an abstraction for reading and writing the + DAC960 V4 Controller Interface Registers. +*/ + +static inline +void DAC960_V4_HardwareMailboxNewCommand(void *ControllerBaseAddress) +{ + DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.HardwareMailboxNewCommand = true; + writel(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V4_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress) +{ + DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.AcknowledgeHardwareMailboxStatus = true; + writel(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V4_GenerateInterrupt(void *ControllerBaseAddress) +{ + DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.GenerateInterrupt = true; + writel(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V4_ControllerReset(void *ControllerBaseAddress) +{ + DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.ControllerReset = true; + writel(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V4_MemoryMailboxNewCommand(void *ControllerBaseAddress) +{ + DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.MemoryMailboxNewCommand = true; + writel(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset); +} + +static inline +boolean DAC960_V4_HardwareMailboxFullP(void *ControllerBaseAddress) +{ + DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = + readl(ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset); + return InboundDoorBellRegister.Read.HardwareMailboxFull; +} + +static inline +void DAC960_V4_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress) +{ + DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = 0; + OutboundDoorBellRegister.Write.AcknowledgeHardwareMailboxInterrupt = true; + writel(OutboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V4_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress) +{ + DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = 0; + OutboundDoorBellRegister.Write.AcknowledgeMemoryMailboxInterrupt = true; + writel(OutboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V4_AcknowledgeInterrupt(void *ControllerBaseAddress) +{ + DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = 0; + OutboundDoorBellRegister.Write.AcknowledgeHardwareMailboxInterrupt = true; + OutboundDoorBellRegister.Write.AcknowledgeMemoryMailboxInterrupt = true; + writel(OutboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset); +} + +static inline +boolean DAC960_V4_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress) +{ + DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = + readl(ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset); + return OutboundDoorBellRegister.Read.HardwareMailboxStatusAvailable; +} + +static inline +boolean DAC960_V4_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress) +{ + DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = + readl(ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset); + return OutboundDoorBellRegister.Read.MemoryMailboxStatusAvailable; +} + +static inline +void DAC960_V4_EnableInterrupts(void *ControllerBaseAddress) +{ + DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister; + InterruptMaskRegister.All = 0; + InterruptMaskRegister.Bits.MessageUnitInterruptMask1 = 0x3; + InterruptMaskRegister.Bits.DisableInterrupts = false; + InterruptMaskRegister.Bits.MessageUnitInterruptMask2 = 0x1F; + writel(InterruptMaskRegister.All, + ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset); +} + +static inline +void DAC960_V4_DisableInterrupts(void *ControllerBaseAddress) +{ + DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister; + InterruptMaskRegister.All = 0; + InterruptMaskRegister.Bits.MessageUnitInterruptMask1 = 0x3; + InterruptMaskRegister.Bits.DisableInterrupts = true; + InterruptMaskRegister.Bits.MessageUnitInterruptMask2 = 0x1F; + writel(InterruptMaskRegister.All, + ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset); +} + +static inline +boolean DAC960_V4_InterruptsEnabledP(void *ControllerBaseAddress) +{ + DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister; + InterruptMaskRegister.All = + readl(ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset); + return !InterruptMaskRegister.Bits.DisableInterrupts; +} + +static inline +void DAC960_V4_WriteCommandMailbox(DAC960_CommandMailbox_T *NextCommandMailbox, + DAC960_CommandMailbox_T *CommandMailbox) +{ + NextCommandMailbox->Words[1] = CommandMailbox->Words[1]; + NextCommandMailbox->Words[2] = CommandMailbox->Words[2]; + NextCommandMailbox->Words[3] = CommandMailbox->Words[3]; + NextCommandMailbox->Words[0] = CommandMailbox->Words[0]; +} + +static inline +void DAC960_V4_WriteHardwareMailbox(void *ControllerBaseAddress, + DAC960_CommandMailbox_T *CommandMailbox) +{ + writel(CommandMailbox->Words[0], + ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset); + writel(CommandMailbox->Words[1], + ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset); + writel(CommandMailbox->Words[2], + ControllerBaseAddress + DAC960_V4_MailboxRegister8Offset); + writeb(CommandMailbox->Bytes[12], + ControllerBaseAddress + DAC960_V4_MailboxRegister12Offset); +} + +static inline DAC960_CommandIdentifier_T +DAC960_V4_ReadStatusCommandIdentifier(void *ControllerBaseAddress) +{ + return readb(ControllerBaseAddress + + DAC960_V4_StatusCommandIdentifierRegOffset); +} + +static inline DAC960_CommandStatus_T +DAC960_V4_ReadStatusRegister(void *ControllerBaseAddress) +{ + return readw(ControllerBaseAddress + DAC960_V4_StatusRegisterOffset); +} + +static inline +void DAC960_V4_SaveMemoryMailboxInfo(DAC960_Controller_T *Controller) +{ + void *ControllerBaseAddress = Controller->BaseAddress; + writel(0xAABBFFFF, + ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset); + writel((unsigned long) Controller->FirstCommandMailbox, + ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset); + writew(Controller->NextCommandMailbox - Controller->FirstCommandMailbox, + ControllerBaseAddress + DAC960_V4_MailboxRegister8Offset); + writew(Controller->NextStatusMailbox - Controller->FirstStatusMailbox, + ControllerBaseAddress + DAC960_V4_MailboxRegister10Offset); +} + +static inline +void DAC960_V4_RestoreMemoryMailboxInfo(DAC960_Controller_T *Controller, + void **MemoryMailboxAddress, + short *NextCommandMailboxIndex, + short *NextStatusMailboxIndex) +{ + void *ControllerBaseAddress = Controller->BaseAddress; + if (readl(ControllerBaseAddress + + DAC960_V4_CommandOpcodeRegisterOffset) != 0xAABBFFFF) + return; + *MemoryMailboxAddress = + (void *) readl(ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset); + *NextCommandMailboxIndex = + readw(ControllerBaseAddress + DAC960_V4_MailboxRegister8Offset); + *NextStatusMailboxIndex = + readw(ControllerBaseAddress + DAC960_V4_MailboxRegister10Offset); +} + + +/* + Define inline functions to provide an abstraction for reading and writing the + DAC960 V3 Controller Interface Registers. +*/ + +static inline +void DAC960_V3_NewCommand(void *ControllerBaseAddress) +{ + DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.NewCommand = true; + writeb(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V3_AcknowledgeStatus(void *ControllerBaseAddress) +{ + DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.AcknowledgeStatus = true; + writeb(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V3_GenerateInterrupt(void *ControllerBaseAddress) +{ + DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.GenerateInterrupt = true; + writeb(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V3_ControllerReset(void *ControllerBaseAddress) +{ + DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.ControllerReset = true; + writeb(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset); +} + +static inline +boolean DAC960_V3_MailboxFullP(void *ControllerBaseAddress) +{ + DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = + readb(ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset); + return InboundDoorBellRegister.Read.MailboxFull; +} + +static inline +void DAC960_V3_AcknowledgeInterrupt(void *ControllerBaseAddress) +{ + DAC960_V3_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = 0; + OutboundDoorBellRegister.Write.AcknowledgeInterrupt = true; + writeb(OutboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V3_OutboundDoorBellRegisterOffset); +} + +static inline +boolean DAC960_V3_StatusAvailableP(void *ControllerBaseAddress) +{ + DAC960_V3_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = + readb(ControllerBaseAddress + DAC960_V3_OutboundDoorBellRegisterOffset); + return OutboundDoorBellRegister.Read.StatusAvailable; +} + +static inline +void DAC960_V3_EnableInterrupts(void *ControllerBaseAddress) +{ + DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister; + InterruptEnableRegister.All = 0; + InterruptEnableRegister.Bits.EnableInterrupts = true; + writeb(InterruptEnableRegister.All, + ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset); +} + +static inline +void DAC960_V3_DisableInterrupts(void *ControllerBaseAddress) +{ + DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister; + InterruptEnableRegister.All = 0; + InterruptEnableRegister.Bits.EnableInterrupts = false; + writeb(InterruptEnableRegister.All, + ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset); +} + +static inline +boolean DAC960_V3_InterruptsEnabledP(void *ControllerBaseAddress) +{ + DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister; + InterruptEnableRegister.All = + readb(ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset); + return InterruptEnableRegister.Bits.EnableInterrupts; +} + +static inline +void DAC960_V3_WriteCommandMailbox(void *ControllerBaseAddress, + DAC960_CommandMailbox_T *CommandMailbox) +{ + writel(CommandMailbox->Words[0], + ControllerBaseAddress + DAC960_V3_CommandOpcodeRegisterOffset); + writel(CommandMailbox->Words[1], + ControllerBaseAddress + DAC960_V3_MailboxRegister4Offset); + writel(CommandMailbox->Words[2], + ControllerBaseAddress + DAC960_V3_MailboxRegister8Offset); + writeb(CommandMailbox->Bytes[12], + ControllerBaseAddress + DAC960_V3_MailboxRegister12Offset); +} + +static inline DAC960_CommandIdentifier_T +DAC960_V3_ReadStatusCommandIdentifier(void *ControllerBaseAddress) +{ + return readb(ControllerBaseAddress + + DAC960_V3_StatusCommandIdentifierRegOffset); +} + +static inline DAC960_CommandStatus_T +DAC960_V3_ReadStatusRegister(void *ControllerBaseAddress) +{ + return readw(ControllerBaseAddress + DAC960_V3_StatusRegisterOffset); +} + + +/* + Virtual_to_Bus and Bus_to_Virtual map between Kernel Virtual Addresses + and PCI Bus Addresses. +*/ + +static inline DAC960_BusAddress_T Virtual_to_Bus(void *VirtualAddress) +{ + return (DAC960_BusAddress_T) virt_to_bus(VirtualAddress); +} + +static inline void *Bus_to_Virtual(DAC960_BusAddress_T BusAddress) +{ + return (void *) bus_to_virt(BusAddress); +} + + +/* + Define compatibility macros between Linux 2.0 and Linux 2.1. +*/ + +#if LINUX_VERSION_CODE < 0x20100 + +#define MODULE_PARM(Variable, Type) +#define ioremap_nocache(Offset, Size) vremap(Offset, Size) +#define iounmap(Address) vfree(Address) + +#endif + + +/* + Define prototypes for the forward referenced DAC960 Driver Internal Functions. +*/ + +static void DAC960_FinalizeController(DAC960_Controller_T *); +static void DAC960_RequestFunction0(void); +static void DAC960_RequestFunction1(void); +static void DAC960_RequestFunction2(void); +static void DAC960_RequestFunction3(void); +static void DAC960_RequestFunction4(void); +static void DAC960_RequestFunction5(void); +static void DAC960_RequestFunction6(void); +static void DAC960_RequestFunction7(void); +static void DAC960_InterruptHandler(int, void *, Registers_T *); +static void DAC960_QueueMonitoringCommand(DAC960_Command_T *); +static void DAC960_MonitoringTimerFunction(unsigned long); +static int DAC960_Open(Inode_T *, File_T *); +static void DAC960_Release(Inode_T *, File_T *); +static int DAC960_Ioctl(Inode_T *, File_T *, unsigned int, unsigned long); +static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *); +static void DAC960_Message(DAC960_MessageLevel_T, char *, + DAC960_Controller_T *, ...); +static void DAC960_CreateProcEntries(void); +static void DAC960_DestroyProcEntries(void); + + +#endif /* DAC960_DriverVersion */ diff -urN linux-2.0.37-pre8/Makefile linux-2.0.37-pre9/Makefile --- linux-2.0.37-pre8/Makefile 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/Makefile 2003-08-15 15:04:42.000000000 -0700 @@ -2,7 +2,7 @@ PATCHLEVEL = 0 SUBLEVEL = 37 -ARCH = i386 +ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) # # For SMP kernels, set this. We don't want to have this in the config file diff -urN linux-2.0.37-pre8/net/appletalk/ddp.c linux-2.0.37-pre9/net/appletalk/ddp.c --- linux-2.0.37-pre8/net/appletalk/ddp.c 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/net/appletalk/ddp.c 2003-08-15 15:04:42.000000000 -0700 @@ -408,6 +408,7 @@ * Scan the networks. */ + atif->status |= ATIF_PROBE; for(netct=0;netct<=netrange;netct++) { /* @@ -435,8 +436,10 @@ if(atif->status&ATIF_PROBE_FAIL) break; } - if(!(atif->status&ATIF_PROBE_FAIL)) + if(!(atif->status&ATIF_PROBE_FAIL)) { + atif->status &= ~ATIF_PROBE; return 0; + } } atif->status&=~ATIF_PROBE_FAIL; } @@ -444,6 +447,7 @@ if(probe_net>ntohs(atif->nets.nr_lastnet)) probe_net=ntohs(atif->nets.nr_firstnet); } + atif->status &= ~ATIF_PROBE; return -EADDRINUSE; /* Network is full... */ } diff -urN linux-2.0.37-pre8/net/ipv4/ip_sockglue.c linux-2.0.37-pre9/net/ipv4/ip_sockglue.c --- linux-2.0.37-pre8/net/ipv4/ip_sockglue.c 2003-08-15 15:04:41.000000000 -0700 +++ linux-2.0.37-pre9/net/ipv4/ip_sockglue.c 2003-08-15 15:04:42.000000000 -0700 @@ -549,12 +549,18 @@ if(err) return err; - dev=dev_get(sk->ip_mc_name); - /* Someone ran off with the interface, its probably - been downed. */ - if(dev==NULL) - return -ENODEV; - ia.s_addr = dev->pa_addr; + if(*sk->ip_mc_name) + { + dev=dev_get(sk->ip_mc_name); + /* Someone ran off with the interface, its probably + been downed. */ + if(dev==NULL) + return -ENODEV; + ia.s_addr = dev->pa_addr; + } + else + ia.s_addr = 0L; + put_user(sizeof(ia),(int *) optlen); memcpy_tofs((void *)optval, &ia, sizeof(ia)); return 0; diff -urN linux-2.0.37-pre8/net/netrom/af_netrom.c linux-2.0.37-pre9/net/netrom/af_netrom.c --- linux-2.0.37-pre8/net/netrom/af_netrom.c 1998-11-15 10:33:22.000000000 -0800 +++ linux-2.0.37-pre9/net/netrom/af_netrom.c 2003-08-15 15:04:42.000000000 -0700 @@ -960,10 +960,18 @@ */ if (frametype != NR_CONNREQ) { /* + * Here it would be nice to be able to send a reset but + * NET/ROM doesn't have one. The following hack would + * have been a way to extend the protocol but apparently + * it kills BPQ boxes... :-( + */ +#if 0 + /* * Never reply to a CONNACK/CHOKE. */ if (frametype != NR_CONNACK || flags != NR_CHOKE_FLAG) nr_transmit_dm(skb, 1); +#endif return 0; } diff -urN linux-2.0.37-pre8/net/netsyms.c linux-2.0.37-pre9/net/netsyms.c --- linux-2.0.37-pre8/net/netsyms.c 1998-07-13 13:47:41.000000000 -0700 +++ linux-2.0.37-pre9/net/netsyms.c 2003-08-15 15:04:42.000000000 -0700 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -131,6 +132,11 @@ X(tr_type_trans), #endif +#ifdef CONFIG_FDDI + X(fddi_setup), + X(fddi_type_trans), +#endif + #ifdef CONFIG_NET_ALIAS #include #endif diff -urN linux-2.0.37-pre8/README linux-2.0.37-pre9/README --- linux-2.0.37-pre8/README 1996-06-26 01:05:26.000000000 -0700 +++ linux-2.0.37-pre9/README 2003-08-15 15:04:42.000000000 -0700 @@ -76,15 +76,6 @@ the current directory, but an alternative directory can be specified as the second argument. - - make sure your /usr/include/asm, /usr/include/linux, and /usr/include/scsi - directories are just symlinks to the kernel sources: - - cd /usr/include - rm -rf asm linux scsi - ln -s /usr/src/linux/include/asm-i386 asm - ln -s /usr/src/linux/include/linux linux - ln -s /usr/src/linux/include/scsi scsi - - make sure you have no stale .o files and dependencies lying around: cd /usr/src/linux