diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-24 00:12:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-24 00:12:35 +0000 |
commit | 482368b1a8e45430672c58c9a42e7d2004367126 (patch) | |
tree | ce2a1a567d4d62dee7c2e71a46a99cf72cf1d606 /drivers/scsi | |
parent | e4d0251c6f56ab2e191afb70f80f382793e23f74 (diff) |
Merge with 2.3.47. Guys, this is buggy as shit. You've been warned.
Diffstat (limited to 'drivers/scsi')
57 files changed, 9722 insertions, 2242 deletions
diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in index e4af6a65e..9faa91e1b 100644 --- a/drivers/scsi/Config.in +++ b/drivers/scsi/Config.in @@ -8,7 +8,7 @@ fi dep_tristate ' SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI -if [ "$CONFIG_BLK_DEV_ST" != "n" ]; then +if [ "$CONFIG_CHR_DEV_ST" != "n" ]; then int 'Maximum number of SCSI tapes that can be loaded as modules' CONFIG_ST_EXTRA_DEVS 2 fi @@ -193,3 +193,8 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then fi endmenu + +if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then + source drivers/scsi/pcmcia/Config.in +fi + diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index af4a242ff..16f26fbcd 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -13,6 +13,16 @@ MIX_OBJS := MOD_LIST_NAME := SCSI_MODULES SCSI_SRCS = $(wildcard $(L_OBJS:%.o=%.c)) +ALL_SUB_DIRS := pcmcia +ifeq ($(CONFIG_PCMCIA),y) + SUB_DIRS += pcmcia + MOD_IN_SUB_DIRS += pcmcia +else + ifeq ($(CONFIG_PCMCIA),m) + MOD_IN_SUB_DIRS += pcmcia + endif +endif + CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM diff --git a/drivers/scsi/README.aic7xxx b/drivers/scsi/README.aic7xxx index 100f9ff95..70ef93d92 100644 --- a/drivers/scsi/README.aic7xxx +++ b/drivers/scsi/README.aic7xxx @@ -208,6 +208,19 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD will often result in the machine crashing or spontaneously rebooting during startup. Examples of machines that need this are the Dell PowerEdge 6300 machines. + + "aic7xxx=seltime:2" - This option controls how long the card waits + during a device selection sequence for the device to respond. + The original SCSI spec says that this "should be" 256ms. This + is generally not required with modern devices. However, some + very old SCSI I devices need the full 256ms. Most modern devices + can run fine with only 64ms. The default for this option is + 64ms. If you need to change this option, then use the following + table to set the proper value in the example above: + 0 - 256ms + 1 - 128ms + 2 - 64ms + 3 - 32ms "aic7xxx=panic_on_abort" - This option is for debugging and will cause the driver to panic the linux kernel and freeze the system the first @@ -485,12 +498,14 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD Web sites ------------------------------ - http://people.redhat.com/dledford/aic7xxx.html - - Primary web site maintained by Doug Ledford. + http://people.redhat.com/dledford/ + - My web site, also the primary aic7xxx site with several related + pages. Dean W. Gehnert deang@teleport.com $Revision: 3.0 $ -Modified by Doug Ledford 1998-9 +Modified by Doug Ledford 1998-2000 + diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c index 38550e1d3..30faaa9ad 100644 --- a/drivers/scsi/aha152x.c +++ b/drivers/scsi/aha152x.c @@ -200,13 +200,9 @@ **************************************************************************/ -#if defined(PCMCIA) -#define MODULE -#endif - #include <linux/module.h> -#if defined(PCMCIA) +#ifdef PCMCIA #undef MODULE #endif @@ -877,9 +873,9 @@ static int tc1550_porttest(int io_port) static int checksetup(struct aha152x_setup *setup) { - int i; #if !defined(PCMCIA) + int i; for (i = 0; i < PORT_COUNT && (setup->io_port != ports[i]); i++) ; diff --git a/drivers/scsi/aic7xxx.c b/drivers/scsi/aic7xxx.c index 77f52f337..6ca7147d5 100644 --- a/drivers/scsi/aic7xxx.c +++ b/drivers/scsi/aic7xxx.c @@ -264,7 +264,7 @@ */ #define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a)) -#define AIC7XXX_C_VERSION "5.1.21" +#define AIC7XXX_C_VERSION "5.2.0" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -719,6 +719,11 @@ struct seeprom_config { #define aic7xxx_position(cmd) ((cmd)->SCp.have_data_in) /* + * The stored DMA mapping for single-buffer data transfers. + */ +#define aic7xxx_mapping(cmd) ((cmd)->SCp.phase) + +/* * So we can keep track of our host structs */ static struct aic7xxx_host *first_aic7xxx = NULL; @@ -823,6 +828,7 @@ typedef enum { * and what flags weren't. This way, I could clean up the flag usage on * a use by use basis. Doug Ledford */ + AHC_NO_STPWR = 0x00040000, AHC_RESET_DELAY = 0x00080000, AHC_A_SCANNED = 0x00100000, AHC_B_SCANNED = 0x00200000, @@ -883,6 +889,17 @@ typedef enum { AHC_AIC7899_FE = AHC_AIC7890_FE|AHC_ULTRA3, } ahc_feature; +#define SCB_DMA_ADDR(scb, addr) ((unsigned long)(addr) + (scb)->scb_dma->dma_offset) + +struct aic7xxx_scb_dma { + unsigned long dma_offset; /* Correction you have to add + * to virtual address to get + * dma handle in this region */ + dma_addr_t dma_address; /* DMA handle of the start, + * for unmap */ + unsigned int dma_len; /* DMA length */ +}; + struct aic7xxx_scb { struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */ Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */ @@ -891,15 +908,17 @@ struct aic7xxx_scb { struct hw_scatterlist *sg_list; /* SG list in adapter format */ unsigned char tag_action; unsigned char sg_count; - unsigned char sense_cmd[6]; /* + unsigned char *sense_cmd; /* * Allocate 6 characters for * sense command. */ + unsigned char *cmnd; unsigned int sg_length; /* We init this during buildscb so we * don't have to calculate anything * during underflow/overflow/stat code */ void *kmalloc_ptr; + struct aic7xxx_scb_dma *scb_dma; }; /* @@ -937,6 +956,8 @@ typedef struct { unsigned char numscbs; /* current number of scbs */ unsigned char maxhscbs; /* hardware scbs */ unsigned char maxscbs; /* max scbs including pageable scbs */ + unsigned int hscbs_dma; /* DMA handle to hscbs */ + unsigned int hscbs_dma_len; /* length of the above DMA area */ void *hscb_kmalloc_ptr; } scb_data_type; @@ -1014,6 +1035,9 @@ struct aic7xxx_host { volatile unsigned char activescbs; /* active scbs */ volatile unsigned char max_activescbs; volatile unsigned char qinfifonext; + volatile unsigned char *untagged_scbs; + volatile unsigned char *qoutfifo; + volatile unsigned char *qinfifo; #define DEVICE_PRESENT 0x01 #define BUS_DEVICE_RESET_PENDING 0x02 @@ -1063,16 +1087,9 @@ struct aic7xxx_host { * We put the less frequently used host structure items after the more * frequently used items to try and ease the burden on the cache subsystem. * These entries are not *commonly* accessed, whereas the preceding entries - * are accessed very often. The only exceptions are the qinfifo, qoutfifo, - * and untagged_scbs array. But, they are often accessed only once and each - * access into these arrays is likely to blow a cache line, so they are put - * down here so we can minimize the number of cache lines required to hold - * the preceeding entries. + * are accessed very often. */ - volatile unsigned char untagged_scbs[256]; - volatile unsigned char qoutfifo[256]; - volatile unsigned char qinfifo[256]; unsigned int irq; /* IRQ for this adapter */ int instance; /* aic7xxx instance number */ int scsi_id; /* host adapter SCSI ID */ @@ -1085,9 +1102,7 @@ struct aic7xxx_host { unsigned short ultraenb; /* Ultra mode target list */ unsigned short bios_control; /* bios control - SEEPROM */ unsigned short adapter_control; /* adapter control - SEEPROM */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) - struct pci_dev *pdev; -#endif + struct pci_dev *pdev; unsigned char pci_bus; unsigned char pci_device_fn; struct seeprom_config sc; @@ -1098,6 +1113,7 @@ struct aic7xxx_host { int host_no; /* SCSI host number */ unsigned long mbase; /* I/O memory address */ ahc_chip chip; /* chip type */ + dma_addr_t fifo_dma; /* DMA handle for fifo arrays */ /* * Statistics Kept: @@ -1354,10 +1370,10 @@ static int aic7xxx_scbram = 0; /* * So that we can set how long each device is given as a selection timeout. * The table of values goes like this: - * 0 - 256ms - * 1 - 128ms - * 2 - 64ms - * 3 - 32ms + * 0 - 256ms + * 1 - 128ms + * 2 - 64ms + * 3 - 32ms * We default to 64ms because it's fast. Some old SCSI-I devices need a * longer time. The final value has to be left shifted by 3, hence 0x10 * is the final value. @@ -1467,6 +1483,7 @@ aic_inb(struct aic7xxx_host *p, long port) { x = inb(p->base + port); } + mb(); return(x); #else return(inb(p->base + port)); @@ -1770,6 +1787,7 @@ aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr, struct ins_format1 *fmt1_ins; struct ins_format3 *fmt3_ins; unsigned char opcode; + volatile unsigned char hcntrl; instr = *(union ins_formats*) &seqprog[instrptr * 4]; @@ -1871,12 +1889,13 @@ aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr, } } aic_outb(p, (instr.integer & 0xff), SEQRAM); - udelay(50); + hcntrl = aic_inb(p, HCNTRL); aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM); - udelay(50); + hcntrl = aic_inb(p, HCNTRL); aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM); - udelay(50); + hcntrl = aic_inb(p, HCNTRL); aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM); + hcntrl = aic_inb(p, HCNTRL); udelay(50); break; @@ -1991,21 +2010,6 @@ aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded) /*+F************************************************************************* * Function: - * aic7xxx_delay - * - * Description: - * Delay for specified amount of time. We use mdelay because the timer - * interrupt is not guaranteed to be enabled. This will cause an - * infinite loop since jiffies (clock ticks) is not updated. - *-F*************************************************************************/ -static void -aic7xxx_delay(int seconds) -{ - mdelay(seconds * 1000); -} - -/*+F************************************************************************* - * Function: * aic7xxx_info * * Description: @@ -2728,15 +2732,14 @@ static int aic7xxx_allocate_scb(struct aic7xxx_host *p) { struct aic7xxx_scb *scbp = NULL; - int scb_size = sizeof(struct aic7xxx_scb) + - sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG; + int scb_size = (sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG) + 12 + 6; int i; int step = PAGE_SIZE / 1024; unsigned long scb_count = 0; struct hw_scatterlist *hsgp; struct aic7xxx_scb *scb_ap; - unsigned long temp; - + struct aic7xxx_scb_dma *scb_dma; + unsigned char *bufs; if (p->scb_data->numscbs < p->scb_data->maxscbs) { @@ -2750,6 +2753,11 @@ aic7xxx_allocate_scb(struct aic7xxx_host *p) * efficiency since scb_size * (i -1) is growing slightly faster * than the right hand side. If the number of SG array elements * is changed, this function may not be near so efficient any more. + * + * Since the DMA'able buffers are now allocated in a seperate + * chunk this algorithm has been modified to match. The '12' + * and '6' factors in scb_size are for the DMA'able command byte + * and sensebuffers respectively. -DaveM */ for ( i=step;; i *= 2 ) { @@ -2760,44 +2768,53 @@ aic7xxx_allocate_scb(struct aic7xxx_host *p) } } scb_count = MIN( (i-1), p->scb_data->maxscbs - p->scb_data->numscbs); - scb_ap = (struct aic7xxx_scb *)kmalloc(scb_size * scb_count, GFP_ATOMIC); - if (scb_ap != NULL) + scb_ap = (struct aic7xxx_scb *)kmalloc(sizeof (struct aic7xxx_scb) * scb_count + + sizeof(struct aic7xxx_scb_dma), GFP_ATOMIC); + if (scb_ap == NULL) + return(0); + scb_dma = (struct aic7xxx_scb_dma *)&scb_ap[scb_count]; + hsgp = (struct hw_scatterlist *) + pci_alloc_consistent(p->pdev, scb_size * scb_count, + &scb_dma->dma_address); + if (hsgp == NULL) { -#ifdef AIC7XXX_VERBOSE_DEBUGGING - if (aic7xxx_verbose > 0xffff) - { - if (p->scb_data->numscbs == 0) - printk(INFO_LEAD "Allocating initial %ld SCB structures.\n", - p->host_no, -1, -1, -1, scb_count); - else - printk(INFO_LEAD "Allocating %ld additional SCB structures.\n", - p->host_no, -1, -1, -1, scb_count); - } -#endif - memset(scb_ap, 0, scb_count * scb_size); - temp = (unsigned long) &scb_ap[scb_count]; - temp += 1023; - temp &= ~1023; - hsgp = (struct hw_scatterlist *)temp; - for (i=0; i < scb_count; i++) - { - scbp = &scb_ap[i]; - scbp->hscb = &p->scb_data->hscbs[p->scb_data->numscbs]; - scbp->sg_list = &hsgp[i * AIC7XXX_MAX_SG]; - memset(scbp->hscb, 0, sizeof(struct aic7xxx_hwscb)); - scbp->hscb->tag = p->scb_data->numscbs; - /* - * Place in the scb array; never is removed - */ - p->scb_data->scb_array[p->scb_data->numscbs++] = scbp; - scbq_insert_tail(&p->scb_data->free_scbs, scbp); - } - scbp->kmalloc_ptr = scb_ap; + kfree(scb_ap); + return(0); } - else + bufs = (unsigned char *)&hsgp[scb_count * AIC7XXX_MAX_SG]; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) { - return(0); + if (p->scb_data->numscbs == 0) + printk(INFO_LEAD "Allocating initial %ld SCB structures.\n", + p->host_no, -1, -1, -1, scb_count); + else + printk(INFO_LEAD "Allocating %ld additional SCB structures.\n", + p->host_no, -1, -1, -1, scb_count); + } +#endif + memset(scb_ap, 0, sizeof (struct aic7xxx_scb) * scb_count); + scb_dma->dma_offset = (unsigned long)scb_dma->dma_address + - (unsigned long)hsgp; + scb_dma->dma_len = scb_size * scb_count; + for (i=0; i < scb_count; i++) + { + scbp = &scb_ap[i]; + scbp->hscb = &p->scb_data->hscbs[p->scb_data->numscbs]; + scbp->sg_list = &hsgp[i * AIC7XXX_MAX_SG]; + scbp->sense_cmd = bufs; + scbp->cmnd = bufs + 6; + bufs += 12 + 6; + scbp->scb_dma = scb_dma; + memset(scbp->hscb, 0, sizeof(struct aic7xxx_hwscb)); + scbp->hscb->tag = p->scb_data->numscbs; + /* + * Place in the scb array; never is removed + */ + p->scb_data->scb_array[p->scb_data->numscbs++] = scbp; + scbq_insert_tail(&p->scb_data->free_scbs, scbp); } + scbp->kmalloc_ptr = scb_ap; } return(scb_count); } @@ -2882,6 +2899,24 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb) struct aic7xxx_scb *scbp; unsigned char queue_depth; + if (cmd->use_sg > 1) + { + struct scatterlist *sg; + + sg = (struct scatterlist *)cmd->request_buffer; + pci_unmap_sg(p->pdev, sg, cmd->use_sg, scsi_to_pci_dma_dir(cmd->sc_data_direction)); + } + else if (cmd->request_bufflen) + pci_unmap_single(p->pdev, aic7xxx_mapping(cmd), + cmd->request_bufflen, + scsi_to_pci_dma_dir(cmd->sc_data_direction)); + if (scb->flags & SCB_SENSE) + { + pci_unmap_single(p->pdev, + le32_to_cpu(scb->sg_list[0].address), + sizeof(cmd->sense_buffer), + PCI_DMA_FROMDEVICE); + } if (scb->flags & SCB_RECOVERY_SCB) { p->flags &= ~AHC_ABORT_PENDING; @@ -3889,11 +3924,19 @@ aic7xxx_reset_current_bus(struct aic7xxx_host *p) while ( (aic_inb(p, SCSISEQ) & SCSIRSTO) == 0) mdelay(5); - mdelay(10); + /* + * Some of the new Ultra2 chipsets need a longer delay after a chip + * reset than just the init setup creates, so we have to delay here + * before we go into a reset in order to make the chips happy. + */ + if (p->features & AHC_ULTRA2) + mdelay(250); + else + mdelay(50); /* Turn off the bus reset. */ aic_outb(p, 0, SCSISEQ); - mdelay(5); + mdelay(10); aic7xxx_clear_intstat(p); /* Re-enable reset interrupts. */ @@ -4772,6 +4815,7 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) case CHECK_CONDITION: if ( !(scb->flags & SCB_SENSE) ) { + unsigned char *sense_buffer; /* * XXX - How do we save the residual (if there is one). */ @@ -4782,14 +4826,13 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) * Send a sense command to the requesting target. * XXX - revisit this and get rid of the memcopys. */ - memcpy(&scb->sense_cmd[0], &generic_sense[0], + memcpy(scb->sense_cmd, &generic_sense[0], sizeof(generic_sense)); scb->sense_cmd[1] = (cmd->lun << 5); scb->sense_cmd[4] = sizeof(cmd->sense_buffer); - scb->sg_list[0].address = - cpu_to_le32(VIRT_TO_BUS(&cmd->sense_buffer[0])); + sense_buffer = cmd->sense_buffer; scb->sg_list[0].length = cpu_to_le32(sizeof(cmd->sense_buffer)); @@ -4801,11 +4844,10 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) hscb->control = 0; hscb->target_status = 0; hscb->SG_list_pointer = - cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[0])); - hscb->data_pointer = scb->sg_list[0].address; + cpu_to_le32(SCB_DMA_ADDR(scb, scb->sg_list)); hscb->data_count = scb->sg_list[0].length; hscb->SCSI_cmd_pointer = - cpu_to_le32(VIRT_TO_BUS(&scb->sense_cmd[0])); + cpu_to_le32(SCB_DMA_ADDR(scb, scb->sense_cmd)); hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]); hscb->residual_SG_segment_count = 0; hscb->residual_data_count[0] = 0; @@ -4854,12 +4896,14 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) * SENSE information anyway. */ if (cmd->next->cmnd[0] != TEST_UNIT_READY) - { - scb->sg_list[0].address = - cpu_to_le32(VIRT_TO_BUS(&cmd->next->sense_buffer[0])); - hscb->data_pointer = scb->sg_list[0].address; - } + sense_buffer = cmd->next->sense_buffer; } + scb->sg_list[0].address = + cpu_to_le32(pci_map_single(p->pdev, sense_buffer, + sizeof(cmd->sense_buffer), + PCI_DMA_FROMDEVICE)); + hscb->data_pointer = scb->sg_list[0].address; + scb->flags |= SCB_SENSE; /* * Ensure the target is busy since this will be an @@ -5134,7 +5178,7 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) else if (scb->flags & SCB_MSGOUT_PPR) { unsigned int max_sync, period; - unsigned char options = p->transinfo[tindex].goal_options; + unsigned char options = 0; if (p->features & AHC_ULTRA2) { @@ -5145,9 +5189,10 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) (p->dev_flags[tindex] & DEVICE_SCSI_3) && (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT) && - (options != 0) ) + (p->transinfo[tindex].goal_options != 0) ) { max_sync = AHC_SYNCRATE_ULTRA3; + options = p->transinfo[tindex].goal_options; } else { @@ -5296,6 +5341,188 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) } break; + case WIDE_RESIDUE: + { + unsigned char resid_sgcnt, index; + unsigned char scb_index = aic_inb(p, SCB_TAG); + unsigned int cur_addr, resid_dcnt; + unsigned int native_addr, native_length; + int i; + + if(scb_index > p->scb_data->numscbs) + { + printk(WARN_LEAD "invalid scb_index during WIDE_RESIDUE.\n", + p->host_no, -1, -1, -1); + /* + * XXX: Add error handling here + */ + break; + } + scb = p->scb_data->scb_array[scb_index]; + if(!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) + { + printk(WARN_LEAD "invalid scb during WIDE_RESIDUE flags:0x%x " + "scb->cmd:0x%lx\n", p->host_no, CTL_OF_SCB(scb), + scb->flags, (unsigned long)scb->cmd); + break; + } + + /* + * We have a valid scb to use on this WIDE_RESIDUE message, so + * we need to walk the sg list looking for this particular sg + * segment, then see if we happen to be at the very beginning of + * the segment. If we are, then we have to back things up to + * the previous segment. If not, then we simply need to remove + * one byte from this segments address and add one to the byte + * count. + */ + cur_addr = aic_inb(p, SHADDR) | (aic_inb(p, SHADDR + 1) << 8) | + (aic_inb(p, SHADDR + 2) << 16) | (aic_inb(p, SHADDR + 3) << 24); + resid_sgcnt = aic_inb(p, SCB_RESID_SGCNT); + resid_dcnt = aic_inb(p, SCB_RESID_DCNT) | + (aic_inb(p, SCB_RESID_DCNT + 1) << 8) | + (aic_inb(p, SCB_RESID_DCNT + 2) << 24); + index = scb->sg_count - resid_sgcnt; + native_addr = le32_to_cpu(scb->sg_list[index].address); + native_length = le32_to_cpu(scb->sg_list[index].length); + /* + * Make sure this is a valid sg_seg for the given pointer + */ + if(cur_addr < native_addr || + cur_addr > (native_addr + native_length)) + { + printk(WARN_LEAD "invalid cur_addr:0x%x during WIDE_RESIDUE\n", + p->host_no, CTL_OF_SCB(scb), cur_addr); + if(index > 0) + printk(WARN_LEAD " sg_address[-1]:0x%x sg_length[-1]:%d\n", + p->host_no, CTL_OF_SCB(scb), + le32_to_cpu(scb->sg_list[index - 1].address), + le32_to_cpu(scb->sg_list[index - 1].length)); + printk(WARN_LEAD " sg_address:0x%x sg_length:%d\n", + p->host_no, CTL_OF_SCB(scb), + native_addr, native_length); + if(resid_sgcnt > 1) + printk(WARN_LEAD " sg_address[1]:0x%x sg_length[1]:%d\n", + p->host_no, CTL_OF_SCB(scb), + le32_to_cpu(scb->sg_list[index + 1].address), + le32_to_cpu(scb->sg_list[index + 1].length)); + break; + } + + /* + * If our current address matches the sg_seg->address then we + * have to back up the sg array to the previous segment and set + * it up to have only one byte of transfer left to go. + */ + if(cur_addr == native_addr) + { + if(index == 0) + { + printk(WARN_LEAD "bogus WIDE_RESIDUE message, no data has been " + "transferred.\n", p->host_no, CTL_OF_SCB(scb)); + break; + } + resid_sgcnt++; + index--; + cur_addr = le32_to_cpu(scb->sg_list[index].address) + + le32_to_cpu(scb->sg_list[index].length) - 1; + native_addr = aic_inb(p, SG_NEXT) | (aic_inb(p, SG_NEXT + 1) << 8) + | (aic_inb(p, SG_NEXT + 2) << 16) | (aic_inb(p, SG_NEXT + 3) << 24); + native_addr -= SG_SIZEOF; + aic_outb(p, resid_sgcnt, SG_COUNT); + aic_outb(p, resid_sgcnt, SCB_RESID_SGCNT); + aic_outb(p, native_addr & 0xff, SG_NEXT); + aic_outb(p, (native_addr >> 8) & 0xff, SG_NEXT + 1); + aic_outb(p, (native_addr >> 16) & 0xff, SG_NEXT + 2); + aic_outb(p, (native_addr >> 24) & 0xff, SG_NEXT + 3); + aic_outb(p, 1, SCB_RESID_DCNT); + aic_outb(p, 0, SCB_RESID_DCNT + 1); + aic_outb(p, 0, SCB_RESID_DCNT + 2); + aic_outb(p, 1, HCNT); + aic_outb(p, 0, HCNT + 1); + aic_outb(p, 0, HCNT + 2); + aic_outb(p, cur_addr & 0xff, HADDR); + aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1); + aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2); + aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3); + /* + * The sequencer actually wants to find the new address and byte + * count in the SHCNT and SHADDR register sets. These registers + * are a shadow of the regular HCNT and HADDR registers. On the + * Ultra2 controllers, these registers are read only and the way + * we have to set their values is to put the values we want into + * the HCNT and HADDR registers and then output PRELOADEN into + * the DFCNTRL register which causes the card to latch the current + * values in the HADDR and HCNT registers and drop it through to + * the shadow registers. On older cards we copy them directly + * across by hand. + */ + if(p->features & AHC_ULTRA2) + { + aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); + i=0; + udelay(1); + while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000)) + { + aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); + udelay(1); + } + } + else + { + aic_outb(p, 1, STCNT); + aic_outb(p, 0, STCNT + 1); + aic_outb(p, 0, STCNT + 2); + aic_outb(p, cur_addr & 0xff, SHADDR); + aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1); + aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2); + aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3); + } + } + else + { + /* + * Back the data pointer up by one and add one to the remaining + * byte count. Then store that in the HCNT and HADDR registers. + */ + cur_addr--; + resid_dcnt++; + aic_outb(p, resid_dcnt & 0xff, SCB_RESID_DCNT); + aic_outb(p, (resid_dcnt >> 8) & 0xff, SCB_RESID_DCNT + 1); + aic_outb(p, (resid_dcnt >> 16) & 0xff, SCB_RESID_DCNT + 2); + aic_outb(p, resid_dcnt & 0xff, HCNT); + aic_outb(p, (resid_dcnt >> 8) & 0xff, HCNT + 1); + aic_outb(p, (resid_dcnt >> 16) & 0xff, HCNT + 2); + aic_outb(p, cur_addr & 0xff, HADDR); + aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1); + aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2); + aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3); + if(p->features & AHC_ULTRA2) + { + aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); + i=0; + udelay(1); + while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000)) + { + aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); + udelay(1); + } + } + else + { + aic_outb(p, resid_dcnt & 0xff, STCNT); + aic_outb(p, (resid_dcnt >> 8) & 0xff, STCNT + 1); + aic_outb(p, (resid_dcnt >> 16) & 0xff, STCNT + 2); + aic_outb(p, cur_addr & 0xff, SHADDR); + aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1); + aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2); + aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3); + } + } + } + break; + + #if AIC7XXX_NOT_YET case TRACEPOINT: { @@ -5655,16 +5882,6 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) break; } - /* - * If we aren't on one of the new Ultra3 cards, then reject any PPR - * message since we can't support any option field other than 0 - */ - if( !(p->features & AHC_ULTRA3) ) - { - reject = TRUE; - break; - } - if (p->msg_len < (MSG_EXT_PPR_LEN + 2)) { break; @@ -6810,7 +7027,7 @@ do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) return; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,95) spin_lock_irqsave(&io_request_lock, cpu_flags); - if(test_and_set_bit(AHC_IN_ISR_BIT, &p->flags)) + if(test_and_set_bit(AHC_IN_ISR_BIT, (void *)&p->flags)) { return; } @@ -6820,7 +7037,7 @@ do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) } while ( (aic_inb(p, INTSTAT) & INT_PEND) ); aic7xxx_done_cmds_complete(p); aic7xxx_run_waiting_queues(p); - clear_bit(AHC_IN_ISR_BIT, &p->flags); + clear_bit(AHC_IN_ISR_BIT, (void *)&p->flags); spin_unlock_irqrestore(&io_request_lock, cpu_flags); #else if(set_bit(AHC_IN_ISR_BIT, (int *)&p->flags)) @@ -7212,6 +7429,21 @@ read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc) #undef CLOCK_PULSE } +#define CLOCK_PULSE(p) \ + do { \ + int limit = 0; \ + do { \ + mb(); \ + pause_sequencer(p); /* This is just to generate some PCI */ \ + /* traffic so the PCI read is flushed */ \ + /* it shouldn't be needed, but some */ \ + /* chipsets do indeed appear to need */ \ + /* something to force PCI reads to get */ \ + /* flushed */ \ + udelay(1); /* Do nothing */ \ + } while (((aic_inb(p, SEECTL) & SEERDY) == 0) && (++limit < 1000)); \ + } while(0) + /*+F************************************************************************* * Function: * acquire_seeprom @@ -7222,7 +7454,6 @@ read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc) static int acquire_seeprom(struct aic7xxx_host *p) { - int wait; /* * Request access of the memory port. When access is @@ -7232,12 +7463,7 @@ acquire_seeprom(struct aic7xxx_host *p) * should be no contention. */ aic_outb(p, SEEMS, SEECTL); - wait = 1000; /* 1000 msec = 1 second */ - while ((wait > 0) && ((aic_inb(p, SEECTL) & SEERDY) == 0)) - { - wait--; - mdelay(1); /* 1 msec */ - } + CLOCK_PULSE(p); if ((aic_inb(p, SEECTL) & SEERDY) == 0) { aic_outb(p, 0, SEECTL); @@ -7256,7 +7482,12 @@ acquire_seeprom(struct aic7xxx_host *p) static void release_seeprom(struct aic7xxx_host *p) { + /* + * Make sure the SEEPROM is ready before we release it. + */ + CLOCK_PULSE(p); aic_outb(p, 0, SEECTL); + CLOCK_PULSE(p); } /*+F************************************************************************* @@ -7322,12 +7553,6 @@ read_seeprom(struct aic7xxx_host *p, int offset, }; struct seeprom_cmd seeprom_read = {3, {1, 1, 0}}; -#define CLOCK_PULSE(p) \ - while ((aic_inb(p, SEECTL) & SEERDY) == 0) \ - { \ - ; /* Do nothing */ \ - } - /* * Request access of the memory port. */ @@ -7443,7 +7668,6 @@ read_seeprom(struct aic7xxx_host *p, int offset, } return (1); -#undef CLOCK_PULSE } /*+F************************************************************************* @@ -7458,12 +7682,18 @@ read_brdctl(struct aic7xxx_host *p) { unsigned char brdctl, value; + /* + * Make sure the SEEPROM is ready before we access it + */ + CLOCK_PULSE(p); if (p->features & AHC_ULTRA2) { brdctl = BRDRW_ULTRA2; aic_outb(p, brdctl, BRDCTL); - udelay(4); - return(aic_inb(p, BRDCTL)); + CLOCK_PULSE(p); + value = aic_inb(p, BRDCTL); + CLOCK_PULSE(p); + return(value); } brdctl = BRDRW; if ( !((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) || @@ -7472,10 +7702,11 @@ read_brdctl(struct aic7xxx_host *p) brdctl |= BRDCS; } aic_outb(p, brdctl, BRDCTL); - udelay(1); + CLOCK_PULSE(p); value = aic_inb(p, BRDCTL); + CLOCK_PULSE(p); aic_outb(p, 0, BRDCTL); - udelay(1); + CLOCK_PULSE(p); return (value); } @@ -7491,18 +7722,23 @@ write_brdctl(struct aic7xxx_host *p, unsigned char value) { unsigned char brdctl; + /* + * Make sure the SEEPROM is ready before we access it + */ + CLOCK_PULSE(p); if (p->features & AHC_ULTRA2) { brdctl = value; aic_outb(p, brdctl, BRDCTL); - udelay(4); + CLOCK_PULSE(p); brdctl |= BRDSTB_ULTRA2; aic_outb(p, brdctl, BRDCTL); - udelay(4); + CLOCK_PULSE(p); brdctl &= ~BRDSTB_ULTRA2; aic_outb(p, brdctl, BRDCTL); - udelay(4); + CLOCK_PULSE(p); read_brdctl(p); + CLOCK_PULSE(p); } else { @@ -7514,19 +7750,21 @@ write_brdctl(struct aic7xxx_host *p, unsigned char value) } brdctl = BRDSTB | BRDCS; aic_outb(p, brdctl, BRDCTL); - udelay(1); + CLOCK_PULSE(p); brdctl |= value; aic_outb(p, brdctl, BRDCTL); - udelay(1); + CLOCK_PULSE(p); brdctl &= ~BRDSTB; aic_outb(p, brdctl, BRDCTL); - udelay(1); + CLOCK_PULSE(p); brdctl &= ~BRDCS; aic_outb(p, brdctl, BRDCTL); - udelay(1); + CLOCK_PULSE(p); } } +#undef CLOCK_PULSE + /*+F************************************************************************* * Function: * aic785x_cable_detect @@ -7903,12 +8141,6 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, unsigned char term, scsi_conf; struct Scsi_Host *host; - /* - * Lock out other contenders for our i/o space. - */ - request_region(p->base, MAXREG - MINREG, "aic7xxx"); - - host = p->host; p->scb_data->maxscbs = AIC7XXX_MAXSCB; @@ -7944,12 +8176,6 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, p->dev_timer.function = (void *)aic7xxx_timer; p->dev_timer_active = 0; - for (i = 0; i < NUMBER(p->untagged_scbs); i++) - { - p->untagged_scbs[i] = SCB_LIST_NULL; - p->qinfifo[i] = SCB_LIST_NULL; - p->qoutfifo[i] = SCB_LIST_NULL; - } /* * We currently have no commands of any type */ @@ -7980,7 +8206,7 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, printk("VLB slot %d\n", p->pci_device_fn); break; default: - printk("PCI %d/%d\n", PCI_SLOT(p->pci_device_fn), + printk("PCI %d/%d/%d\n", p->pci_bus, PCI_SLOT(p->pci_device_fn), PCI_FUNC(p->pci_device_fn)); break; } @@ -8016,6 +8242,20 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, } aic_outb(p, 0, SEQ_FLAGS); + /* + * We are starting to do real work on the card....it's possible we could + * generate some spurious interrupts at this point, especially in the + * event of a PCI error or some such. If there are other devices already + * registered on the same interrupt as us, this could cause the machine + * to lock up. So, we disable the interrupt this card is on until we + * finish our card setup. We only need to do this for modules, if we are + * compiled into the kernel then interrupts are already off during this + * part of the code. + */ +#ifdef MODULE + disable_irq(p->irq); +#endif + detect_maxscb(p); @@ -8051,15 +8291,15 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, #endif if ( (aic7xxx_stpwlev >> p->instance) & 0x01 ) { - devconfig |= 0x02; + devconfig |= STPWLEVEL; if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("(scsi%d) Force setting STPWLEV bit\n", p->host_no); + printk("(scsi%d) Force setting STPWLEVEL bit\n", p->host_no); } else { - devconfig &= ~0x02; + devconfig &= ~STPWLEVEL; if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("(scsi%d) Force clearing STPWLEV bit\n", p->host_no); + printk("(scsi%d) Force clearing STPWLEVEL bit\n", p->host_no); } #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_write_config_byte(p->pdev, DEVCONFIG, devconfig); @@ -8121,11 +8361,6 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, } /* - * Clear out any possible pending interrupts. - */ - aic7xxx_clear_intstat(p); - - /* * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels */ if (p->features & AHC_TWIN) @@ -8168,7 +8403,17 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, aic_outb(p, (scsi_conf & ENSPCHK) | aic7xxx_seltime | term | ENSTIMER | ACTNEGEN, SXFRCTL1); aic_outb(p, 0, SIMODE0); - aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1); + /* + * If we are a cardbus adapter then don't enable SCSI reset detection. + * We shouldn't likely be sharing SCSI busses with someone else, and + * if we don't have a cable currently plugged into the controller then + * we won't have a power source for the SCSI termination, which means + * we'll see infinite incoming bus resets. + */ + if(p->flags & AHC_NO_STPWR) + aic_outb(p, ENSELTIMO | ENSCSIPERR, SIMODE1); + else + aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1); aic_outb(p, 0, SCSIRATE); if ( p->features & AHC_ULTRA2) aic_outb(p, 0, SCSIOFFSET); @@ -8227,44 +8472,57 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, { size_t array_size; unsigned int hscb_physaddr; - unsigned long temp; array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb); if (p->scb_data->hscbs == NULL) { - /* - * A little padding so we can align thing the way we want + /* pci_alloc_consistent enforces the alignment already and + * clears the area as well. */ - p->scb_data->hscbs = kmalloc(array_size + 0x1f, GFP_ATOMIC); + p->scb_data->hscbs = pci_alloc_consistent(p->pdev, array_size, + &p->scb_data->hscbs_dma); + /* We have to use pci_free_consistent, not kfree */ + p->scb_data->hscb_kmalloc_ptr = NULL; + p->scb_data->hscbs_dma_len = array_size; } if (p->scb_data->hscbs == NULL) { printk("(scsi%d) Unable to allocate hardware SCB array; " "failing detection.\n", p->host_no); + aic_outb(p, 0, SIMODE1); +#ifdef MODULE + enable_irq(p->irq); +#endif p->irq = 0; return(0); } - /* - * Save the actual kmalloc buffer pointer off, then align our - * buffer to a 32 byte boundary - */ - p->scb_data->hscb_kmalloc_ptr = p->scb_data->hscbs; - temp = (unsigned long)p->scb_data->hscbs; - temp += 0x1f; - temp &= ~0x1f; - p->scb_data->hscbs = (struct aic7xxx_hwscb *)temp; - /* At least the control byte of each SCB needs to be 0. */ - memset(p->scb_data->hscbs, 0, array_size); - - /* Tell the sequencer where it can find the hardware SCB array. */ - hscb_physaddr = VIRT_TO_BUS(p->scb_data->hscbs); + + hscb_physaddr = p->scb_data->hscbs_dma; aic_outb(p, hscb_physaddr & 0xFF, HSCB_ADDR); aic_outb(p, (hscb_physaddr >> 8) & 0xFF, HSCB_ADDR + 1); aic_outb(p, (hscb_physaddr >> 16) & 0xFF, HSCB_ADDR + 2); aic_outb(p, (hscb_physaddr >> 24) & 0xFF, HSCB_ADDR + 3); /* Set up the fifo areas at the same time */ - hscb_physaddr = VIRT_TO_BUS(&p->untagged_scbs[0]); + p->untagged_scbs = pci_alloc_consistent(p->pdev, 3*256, &p->fifo_dma); + if (p->untagged_scbs == NULL) + { + printk("(scsi%d) Unable to allocate hardware FIFO arrays; " + "failing detection.\n", p->host_no); + p->irq = 0; + return(0); + } + + p->qoutfifo = p->untagged_scbs + 256; + p->qinfifo = p->qoutfifo + 256; + for (i = 0; i < 256; i++) + { + p->untagged_scbs[i] = SCB_LIST_NULL; + p->qinfifo[i] = SCB_LIST_NULL; + p->qoutfifo[i] = SCB_LIST_NULL; + } + + hscb_physaddr = p->fifo_dma; aic_outb(p, hscb_physaddr & 0xFF, SCBID_ADDR); aic_outb(p, (hscb_physaddr >> 8) & 0xFF, SCBID_ADDR + 1); aic_outb(p, (hscb_physaddr >> 16) & 0xFF, SCBID_ADDR + 2); @@ -8315,11 +8573,6 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, first_aic7xxx = p; /* - * Clear out any possible pending interrupts, again. - */ - aic7xxx_clear_intstat(p); - - /* * Allocate the first set of scbs for this controller. This is to stream- * line code elsewhere in the driver. If we have to check for the existence * of scbs in certain code sections, it slows things down. However, as @@ -8373,20 +8626,21 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel); } - /* - * Some of the new Ultra2 chipsets need a longer delay after a chip - * reset than just the init setup creates, so we have to delay here - * before we go into a reset in order to make the chips happy. - */ - if (p->features & AHC_ULTRA2) - mdelay(250); aic7xxx_reset_current_bus(p); /* - * Delay for the reset delay. + * Delay for the reset delay by setting the timer, this will delay + * future commands sent to any devices. */ - if (!reset_delay) - aic7xxx_delay(AIC7XXX_RESET_DELAY); + p->flags |= AHC_RESET_DELAY; + for(i=0; i<MAX_TARGETS; i++) + { + p->dev_expires[i] = jiffies + (4 * HZ); + p->dev_timer_active |= (0x01 << i); + } + p->dev_timer.expires = p->dev_expires[p->scsi_id]; + add_timer(&p->dev_timer); + p->dev_timer_active |= (0x01 << MAX_TARGETS); } else { @@ -8396,11 +8650,6 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, "the no_reset\n", p->host_no); printk(KERN_INFO "(scsi%d) option unless you have a verifiable need " "for it.\n", p->host_no); - printk(KERN_INFO "(scsi%d) The no_reset option is known to break some " - "systems,\n", p->host_no); - printk(KERN_INFO "(scsi%d) and is not supported by the driver author\n", - p->host_no); - aic7xxx_delay(AIC7XXX_RESET_DELAY); } } @@ -8426,10 +8675,23 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, { printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring " "controller.\n", p->host_no, p->irq); + aic_outb(p, 0, SIMODE1); +#ifdef MODULE + enable_irq(p->irq); +#endif p->irq = 0; return (0); } + if(aic_inb(p, INTSTAT) & INT_PEND) + printk(INFO_LEAD "spurious interrupt during configuration, cleared.\n", + p->host_no, -1, -1 , -1); + aic7xxx_clear_intstat(p); + +#ifdef MODULE + enable_irq(p->irq); +#endif + unpause_sequencer(p, /* unpause_always */ TRUE); return (found); @@ -8462,7 +8724,7 @@ aic7xxx_chip_reset(struct aic7xxx_host *p) wait = 1000; /* 1 second (1000 * 1 msec) */ while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK)) { - mdelay(1); /* 1 msec */ + udelay(1); /* 1 msec */ } pause_sequencer(p); @@ -8566,9 +8828,11 @@ aic7xxx_free(struct aic7xxx_host *p) */ if (p->scb_data != NULL) { + struct aic7xxx_scb_dma *scb_dma = NULL; if (p->scb_data->hscbs != NULL) { - kfree(p->scb_data->hscb_kmalloc_ptr); + pci_free_consistent(p->pdev, p->scb_data->hscbs_dma_len, + p->scb_data->hscbs, p->scb_data->hscbs_dma); p->scb_data->hscbs = p->scb_data->hscb_kmalloc_ptr = NULL; } /* @@ -8579,6 +8843,14 @@ aic7xxx_free(struct aic7xxx_host *p) */ for (i = 0; i < p->scb_data->numscbs; i++) { + if (p->scb_data->scb_array[i]->scb_dma != scb_dma) + { + scb_dma = p->scb_data->scb_array[i]->scb_dma; + pci_free_consistent(p->pdev, scb_dma->dma_len, + (void *)((unsigned long)scb_dma->dma_address + - scb_dma->dma_offset), + scb_dma->dma_address); + } if (p->scb_data->scb_array[i]->kmalloc_ptr != NULL) kfree(p->scb_data->scb_array[i]->kmalloc_ptr); p->scb_data->scb_array[i] = NULL; @@ -8606,6 +8878,7 @@ aic7xxx_free(struct aic7xxx_host *p) } } + pci_free_consistent(p->pdev, 3*256, (void *)p->untagged_scbs, p->fifo_dma); } /*+F************************************************************************* @@ -8657,15 +8930,15 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1) if (!have_seeprom) { p->sc_size = 128; - have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), + have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)), scarray, p->sc_size, p->sc_type); if (!have_seeprom) { if(p->sc_type == C46) - have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), + have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)), scarray, p->sc_size, C56_66); else - have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), + have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)), scarray, p->sc_size, C46); } } @@ -9098,260 +9371,6 @@ aic7xxx_detect(Scsi_Host_Template *template) template->sg_tablesize = AIC7XXX_MAX_SG; -#if defined(__i386__) || defined(__alpha__) -#ifdef CONFIG_PCI - /* - * PCI-bus chipset probe. - */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) - if (pci_present()) - { - if (pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82450GX, - NULL)) - aic7xxx_no_probe = 1; - if (pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82451NX, - NULL)) - aic7xxx_no_probe = 1; - } -#else -#define PCI_DEVICE_ID_INTEL_82451NX 0x84ca - if (pcibios_present()) - { - unsigned char pci_bus, pci_devfn; - if (!(pcibios_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82450GX, - 0, &pci_bus, &pci_devfn)) ) - aic7xxx_no_probe = 1; - if (!(pcibios_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82451NX, - 0, &pci_bus, &pci_devfn)) ) - aic7xxx_no_probe = 1; - } -#endif /* LINUX_VERSION_CODE */ -#endif /* CONFIG_PCI */ - /* - * EISA/VL-bus card signature probe. - */ - slot = MINSLOT; - while ( (slot <= MAXSLOT) && - !(aic7xxx_no_probe) ) - { - base = SLOTBASE(slot) + MINREG; - - if (check_region(base, MAXREG - MINREG)) - { - /* - * Some other driver has staked a - * claim to this i/o region already. - */ - slot++; - continue; /* back to the beginning of the for loop */ - } - flags = 0; - type = aic7xxx_probe(slot, base + AHC_HID0, &flags); - if (type == -1) - { - slot++; - continue; - } - temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC); - if (temp_p == NULL) - { - printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n"); - slot++; - continue; /* back to the beginning of the while loop */ - } - /* - * Pause the card preserving the IRQ type. Allow the operator - * to override the IRQ trigger. - */ - if (aic7xxx_irq_trigger == 1) - hcntrl = IRQMS; /* Level */ - else if (aic7xxx_irq_trigger == 0) - hcntrl = 0; /* Edge */ - else - hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */ - memset(temp_p, 0, sizeof(struct aic7xxx_host)); - temp_p->unpause = hcntrl | INTEN; - temp_p->pause = hcntrl | PAUSE | INTEN; - temp_p->base = base; - temp_p->mbase = 0; - temp_p->maddr = 0; - temp_p->pci_bus = 0; - temp_p->pci_device_fn = slot; - aic_outb(temp_p, hcntrl | PAUSE, HCNTRL); - while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ; - if (aic7xxx_chip_reset(temp_p) == -1) - temp_p->irq = 0; - else - temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F; - temp_p->flags |= AHC_PAGESCBS; - - switch (temp_p->irq) - { - case 9: - case 10: - case 11: - case 12: - case 14: - case 15: - break; - - default: - printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ " - "level %d, ignoring.\n", temp_p->irq); - kfree(temp_p); - slot++; - continue; /* back to the beginning of the while loop */ - } - - /* - * We are commited now, everything has been checked and this card - * has been found, now we just set it up - */ - - /* - * Insert our new struct into the list at the end - */ - if (list_p == NULL) - { - list_p = current_p = temp_p; - } - else - { - current_p = list_p; - while (current_p->next != NULL) - current_p = current_p->next; - current_p->next = temp_p; - } - - switch (type) - { - case 0: - temp_p->board_name_index = 2; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at EISA %d\n", - board_names[2], slot); - /* FALLTHROUGH */ - case 1: - { - temp_p->chip = AHC_AIC7770 | AHC_EISA; - temp_p->features |= AHC_AIC7770_FE; - temp_p->bios_control = aic_inb(temp_p, HA_274_BIOSCTRL); - - /* - * Get the primary channel information. Right now we don't - * do anything with this, but someday we will be able to inform - * the mid-level SCSI code which channel is primary. - */ - if (temp_p->board_name_index == 0) - { - temp_p->board_name_index = 3; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at EISA %d\n", - board_names[3], slot); - } - if (temp_p->bios_control & CHANNEL_B_PRIMARY) - { - temp_p->flags |= AHC_CHANNEL_B_PRIMARY; - } - - if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED) - { - temp_p->flags &= ~AHC_BIOS_ENABLED; - } - else - { - temp_p->flags &= ~AHC_USEDEFAULTS; - temp_p->flags |= AHC_BIOS_ENABLED; - if ( (temp_p->bios_control & 0x20) == 0 ) - { - temp_p->bios_address = 0xcc000; - temp_p->bios_address += (0x4000 * (temp_p->bios_control & 0x07)); - } - else - { - temp_p->bios_address = 0xd0000; - temp_p->bios_address += (0x8000 * (temp_p->bios_control & 0x06)); - } - } - temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8; - temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1); - if (temp_p->features & AHC_WIDE) - { - temp_p->scsi_id = temp_p->adapter_control & HWSCSIID; - temp_p->scsi_id_b = temp_p->scsi_id; - } - else - { - temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID; - temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID; - } - aic7xxx_load_seeprom(temp_p, &sxfrctl1); - break; - } - - case 2: - case 3: - temp_p->chip = AHC_AIC7770 | AHC_VL; - temp_p->features |= AHC_AIC7770_FE; - if (type == 2) - temp_p->flags |= AHC_BIOS_ENABLED; - else - temp_p->flags &= ~AHC_BIOS_ENABLED; - if (aic_inb(temp_p, SCSICONF) & TERM_ENB) - sxfrctl1 = STPWEN; - aic7xxx_load_seeprom(temp_p, &sxfrctl1); - temp_p->board_name_index = 4; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at VLB %d\n", - board_names[2], slot); - switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL ) - { - case 0x00: - temp_p->bios_address = 0xe0000; - break; - case 0x20: - temp_p->bios_address = 0xc8000; - break; - case 0x40: - temp_p->bios_address = 0xd0000; - break; - case 0x60: - temp_p->bios_address = 0xd8000; - break; - default: - break; /* can't get here */ - } - break; - - default: /* Won't get here. */ - break; - } - if (aic7xxx_verbose & VERBOSE_PROBE2) - { - printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%lx, IRQ %d (%s)\n", - (temp_p->flags & AHC_USEDEFAULTS) ? "dis" : "en", temp_p->base, - temp_p->irq, - (temp_p->pause & IRQMS) ? "level sensitive" : "edge triggered"); - printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", - (temp_p->flags & AHC_EXTEND_TRANS_A) ? "en" : "dis"); - } - - /* - * Set the FIFO threshold and the bus off time. - */ - hostconf = aic_inb(temp_p, HOSTCONF); - aic_outb(temp_p, hostconf & DFTHRSH, BUSSPD); - aic_outb(temp_p, (hostconf << 2) & BOFF, BUSTIME); - slot++; - found++; - } - -#endif /* defined(__i386__) || defined(__alpha__) */ - #ifdef CONFIG_PCI /* * PCI-bus probe. @@ -9485,7 +9504,7 @@ aic7xxx_detect(Scsi_Host_Template *template) 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_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_NO_STPWR, AHC_AIC7860_FE, 26, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892A, AHC_AIC7892, @@ -9594,8 +9613,8 @@ aic7xxx_detect(Scsi_Host_Template *template) if (aic7xxx_verbose & VERBOSE_PROBE2) printk("aic7xxx: <%s> at PCI %d/%d\n", board_names[aic_pdevs[i].board_name_index], - PCI_SLOT(temp_p->pdev->devfn), - PCI_FUNC(temp_p->pdev->devfn)); + PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn)); pci_read_config_word(pdev, PCI_COMMAND, &command); if (aic7xxx_verbose & VERBOSE_PROBE2) { @@ -9651,8 +9670,9 @@ aic7xxx_detect(Scsi_Host_Template *template) if ( temp_p == NULL ) continue; if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at PCI %d/%d\n", + printk("aic7xxx: <%s> at PCI %d/%d/%d\n", board_names[aic_pdevs[i].board_name_index], + temp_p->pci_bus, PCI_SLOT(temp_p->pci_device_fn), PCI_FUNC(temp_p->pci_device_fn)); pcibios_read_config_word(pci_bus, pci_devfn, PCI_COMMAND, &command); @@ -9682,14 +9702,28 @@ aic7xxx_detect(Scsi_Host_Template *template) #endif /* AIC7XXX_STRICT_PCI_SETUP */ #endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) */ + if(check_region(temp_p->base, MAXREG - MINREG)) + { + printk("aic7xxx: <%s> at PCI %d/%d/%d\n", + board_names[aic_pdevs[i].board_name_index], + temp_p->pci_bus, + PCI_SLOT(temp_p->pci_device_fn), + PCI_FUNC(temp_p->pci_device_fn)); + printk("aic7xxx: I/O ports already in use, ignoring.\n"); + kfree(temp_p); + temp_p = NULL; + continue; + } + 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", + printk("aic7xxx: <%s> at PCI %d/%d/%d\n", board_names[aic_pdevs[i].board_name_index], + temp_p->pci_bus, PCI_SLOT(temp_p->pci_device_fn), PCI_FUNC(temp_p->pci_device_fn)); printk("aic7xxx: Controller disabled by BIOS, ignoring.\n"); @@ -9724,8 +9758,9 @@ aic7xxx_detect(Scsi_Host_Template *template) /* * OK.....we failed our test....go back to programmed I/O */ - printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d\n", + printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n", board_names[aic_pdevs[i].board_name_index], + temp_p->pci_bus, PCI_SLOT(temp_p->pci_device_fn), PCI_FUNC(temp_p->pci_device_fn)); printk(KERN_INFO "aic7xxx: MMAPed I/O failed, reverting to " @@ -9742,6 +9777,11 @@ aic7xxx_detect(Scsi_Host_Template *template) #endif /* + * Lock out other contenders for our i/o space. + */ + request_region(temp_p->base, MAXREG - MINREG, "aic7xxx"); + + /* * 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 @@ -9773,14 +9813,31 @@ aic7xxx_detect(Scsi_Host_Template *template) /* * Get current termination setting */ - sxfrctl1 = aic_inb(temp_p, SXFRCTL1) & STPWEN; + sxfrctl1 = aic_inb(temp_p, SXFRCTL1); if (aic7xxx_chip_reset(temp_p) == -1) { + release_region(temp_p->base, MAXREG - MINREG); kfree(temp_p); temp_p = NULL; continue; } + /* + * Very quickly put the term setting back into the register since + * the chip reset may cause odd things to happen. This is to keep + * LVD busses with lots of drives from draining the power out of + * the diffsense line before we get around to running the + * configure_termination() function. Also restore the STPWLEVEL + * bit of DEVCONFIG + */ + aic_outb(temp_p, sxfrctl1, SXFRCTL1); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) + pcibios_write_config_dword(temp_p->pci_bus, temp_p->pci_device_fn, + DEVCONFIG, devconfig); +#else + pci_write_config_dword(temp_p->pdev, DEVCONFIG, devconfig); +#endif + sxfrctl1 &= STPWEN; /* * We need to set the CHNL? assignments before loading the SEEPROM @@ -9815,7 +9872,7 @@ aic7xxx_detect(Scsi_Host_Template *template) case AHC_AIC7896: /* 7896/7 */ case AHC_AIC7899: /* 7899 */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) - if (PCI_FUNC(temp_p->pdev->devfn) != 0) + if (PCI_FUNC(pdev->devfn) != 0) { temp_p->flags |= AHC_CHNLB; } @@ -10007,8 +10064,9 @@ aic7xxx_detect(Scsi_Host_Template *template) } else if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) { - printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d\n", + printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n", board_names[aic_pdevs[i].board_name_index], + temp_p->pci_bus, PCI_SLOT(temp_p->pci_device_fn), PCI_FUNC(temp_p->pci_device_fn)); printk("aic7xxx: external SCB RAM detected, " @@ -10025,8 +10083,9 @@ aic7xxx_detect(Scsi_Host_Template *template) } else if (devconfig & RAMPSM) { - printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d\n", + printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n", board_names[aic_pdevs[i].board_name_index], + temp_p->pci_bus, PCI_SLOT(temp_p->pci_device_fn), PCI_FUNC(temp_p->pci_device_fn)); printk("aic7xxx: external SCB RAM detected, " @@ -10091,6 +10150,235 @@ aic7xxx_detect(Scsi_Host_Template *template) } /* for PCI_DEVICES */ } /* PCI BIOS present */ #endif CONFIG_PCI + +#if defined(__i386__) || defined(__alpha__) + /* + * EISA/VL-bus card signature probe. + */ + slot = MINSLOT; + while ( (slot <= MAXSLOT) && + !(aic7xxx_no_probe) ) + { + base = SLOTBASE(slot) + MINREG; + + if (check_region(base, MAXREG - MINREG)) + { + /* + * Some other driver has staked a + * claim to this i/o region already. + */ + slot++; + continue; /* back to the beginning of the for loop */ + } + flags = 0; + type = aic7xxx_probe(slot, base + AHC_HID0, &flags); + if (type == -1) + { + slot++; + continue; + } + temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC); + if (temp_p == NULL) + { + printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n"); + slot++; + continue; /* back to the beginning of the while loop */ + } + /* + * Lock out other contenders for our i/o space. + */ + request_region(base, MAXREG - MINREG, "aic7xxx"); + + /* + * Pause the card preserving the IRQ type. Allow the operator + * to override the IRQ trigger. + */ + if (aic7xxx_irq_trigger == 1) + hcntrl = IRQMS; /* Level */ + else if (aic7xxx_irq_trigger == 0) + hcntrl = 0; /* Edge */ + else + hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */ + memset(temp_p, 0, sizeof(struct aic7xxx_host)); + temp_p->unpause = hcntrl | INTEN; + temp_p->pause = hcntrl | PAUSE | INTEN; + temp_p->base = base; + temp_p->mbase = 0; + temp_p->maddr = 0; + temp_p->pci_bus = 0; + temp_p->pci_device_fn = slot; + aic_outb(temp_p, hcntrl | PAUSE, HCNTRL); + while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ; + if (aic7xxx_chip_reset(temp_p) == -1) + temp_p->irq = 0; + else + temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F; + temp_p->flags |= AHC_PAGESCBS; + + switch (temp_p->irq) + { + case 9: + case 10: + case 11: + case 12: + case 14: + case 15: + break; + + default: + printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ " + "level %d, ignoring.\n", temp_p->irq); + kfree(temp_p); + release_region(base, MAXREG - MINREG); + slot++; + continue; /* back to the beginning of the while loop */ + } + + /* + * We are commited now, everything has been checked and this card + * has been found, now we just set it up + */ + + /* + * Insert our new struct into the list at the end + */ + if (list_p == NULL) + { + list_p = current_p = temp_p; + } + else + { + current_p = list_p; + while (current_p->next != NULL) + current_p = current_p->next; + current_p->next = temp_p; + } + + switch (type) + { + case 0: + temp_p->board_name_index = 2; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at EISA %d\n", + board_names[2], slot); + /* FALLTHROUGH */ + case 1: + { + temp_p->chip = AHC_AIC7770 | AHC_EISA; + temp_p->features |= AHC_AIC7770_FE; + temp_p->bios_control = aic_inb(temp_p, HA_274_BIOSCTRL); + + /* + * Get the primary channel information. Right now we don't + * do anything with this, but someday we will be able to inform + * the mid-level SCSI code which channel is primary. + */ + if (temp_p->board_name_index == 0) + { + temp_p->board_name_index = 3; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at EISA %d\n", + board_names[3], slot); + } + if (temp_p->bios_control & CHANNEL_B_PRIMARY) + { + temp_p->flags |= AHC_CHANNEL_B_PRIMARY; + } + + if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED) + { + temp_p->flags &= ~AHC_BIOS_ENABLED; + } + else + { + temp_p->flags &= ~AHC_USEDEFAULTS; + temp_p->flags |= AHC_BIOS_ENABLED; + if ( (temp_p->bios_control & 0x20) == 0 ) + { + temp_p->bios_address = 0xcc000; + temp_p->bios_address += (0x4000 * (temp_p->bios_control & 0x07)); + } + else + { + temp_p->bios_address = 0xd0000; + temp_p->bios_address += (0x8000 * (temp_p->bios_control & 0x06)); + } + } + temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8; + temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1); + if (temp_p->features & AHC_WIDE) + { + temp_p->scsi_id = temp_p->adapter_control & HWSCSIID; + temp_p->scsi_id_b = temp_p->scsi_id; + } + else + { + temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID; + temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID; + } + aic7xxx_load_seeprom(temp_p, &sxfrctl1); + break; + } + + case 2: + case 3: + temp_p->chip = AHC_AIC7770 | AHC_VL; + temp_p->features |= AHC_AIC7770_FE; + if (type == 2) + temp_p->flags |= AHC_BIOS_ENABLED; + else + temp_p->flags &= ~AHC_BIOS_ENABLED; + if (aic_inb(temp_p, SCSICONF) & TERM_ENB) + sxfrctl1 = STPWEN; + aic7xxx_load_seeprom(temp_p, &sxfrctl1); + temp_p->board_name_index = 4; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at VLB %d\n", + board_names[2], slot); + switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL ) + { + case 0x00: + temp_p->bios_address = 0xe0000; + break; + case 0x20: + temp_p->bios_address = 0xc8000; + break; + case 0x40: + temp_p->bios_address = 0xd0000; + break; + case 0x60: + temp_p->bios_address = 0xd8000; + break; + default: + break; /* can't get here */ + } + break; + + default: /* Won't get here. */ + break; + } + if (aic7xxx_verbose & VERBOSE_PROBE2) + { + printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%lx, IRQ %d (%s)\n", + (temp_p->flags & AHC_USEDEFAULTS) ? "dis" : "en", temp_p->base, + temp_p->irq, + (temp_p->pause & IRQMS) ? "level sensitive" : "edge triggered"); + printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n", + (temp_p->flags & AHC_EXTEND_TRANS_A) ? "en" : "dis"); + } + + /* + * Set the FIFO threshold and the bus off time. + */ + hostconf = aic_inb(temp_p, HOSTCONF); + aic_outb(temp_p, hostconf & DFTHRSH, BUSSPD); + aic_outb(temp_p, (hostconf << 2) & BOFF, BUSTIME); + slot++; + found++; + } + +#endif /* defined(__i386__) || defined(__alpha__) */ + /* * Now, we re-order the probed devices by BIOS address and BUS class. * In general, we follow this algorithm to make the adapters show up @@ -10333,6 +10621,7 @@ aic7xxx_allocate_negotiation_command(struct aic7xxx_host *p, cmd->lun = 0; cmd->request_bufflen = 255; cmd->request_buffer = buffer; + cmd->sc_data_direction = SCSI_DATA_READ; cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0; cmd->bufflen = 0; cmd->buffer = NULL; @@ -10391,7 +10680,7 @@ aic7xxx_negotiation_complete(Scsi_Cmnd *cmd) * instead of slowing down if those exist. That's hard to do with simple * checksums though. */ - if(aic7xxx_verbose & VERBOSE_NEGOTIATION) + if(aic7xxx_verbose & VERBOSE_NEGOTIATION2) { printk(INFO_LEAD "reducing SCSI transfer speed due to Domain " "validation failure.\n", p->host_no, CTL_OF_CMD(cmd)); @@ -10422,7 +10711,7 @@ aic7xxx_negotiation_complete(Scsi_Cmnd *cmd) } else { - if(aic7xxx_verbose & VERBOSE_NEGOTIATION) + if(aic7xxx_verbose & VERBOSE_NEGOTIATION2) { printk(INFO_LEAD "Performing Domain validation.\n", p->host_no, CTL_OF_CMD(cmd)); @@ -10447,7 +10736,7 @@ aic7xxx_negotiation_complete(Scsi_Cmnd *cmd) } else { - if( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && + if( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && (p->needdv & (1<<tindex)) ) { printk(INFO_LEAD "Successfully completed Domain validation.\n", @@ -10663,7 +10952,8 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, * little-endian format. */ hscb->SCSI_cmd_length = cmd->cmd_len; - hscb->SCSI_cmd_pointer = cpu_to_le32(VIRT_TO_BUS(cmd->cmnd)); + memcpy(scb->cmnd, cmd->cmnd, cmd->cmd_len); + hscb->SCSI_cmd_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, scb->cmnd)); if (cmd->use_sg) { @@ -10675,10 +10965,11 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, * differences and the kernel SG list uses virtual addresses where * we need physical addresses. */ - int i; + int i, use_sg; sg = (struct scatterlist *)cmd->request_buffer; scb->sg_length = 0; + use_sg = pci_map_sg(p->pdev, sg, cmd->use_sg, scsi_to_pci_dma_dir(cmd->sc_data_direction)); /* * Copy the segments into the SG array. NOTE!!! - We used to * have the first entry both in the data_pointer area and the first @@ -10686,29 +10977,34 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, * entry in both places, but now we download the address of * scb->sg_list[1] instead of 0 to the sg pointer in the hscb. */ - for (i = 0; i < cmd->use_sg; i++) + for (i = 0; i < use_sg; i++) { - scb->sg_list[i].address = cpu_to_le32(VIRT_TO_BUS(sg[i].address)); - scb->sg_list[i].length = cpu_to_le32(sg[i].length); - scb->sg_length += sg[i].length; + unsigned int len = sg_dma_len(sg+i); + scb->sg_list[i].address = cpu_to_le32(sg_dma_address(sg+i)); + scb->sg_list[i].length = cpu_to_le32(len); + scb->sg_length += len; } /* Copy the first SG into the data pointer area. */ hscb->data_pointer = scb->sg_list[0].address; hscb->data_count = scb->sg_list[0].length; - scb->sg_count = cmd->use_sg; - hscb->SG_segment_count = cmd->use_sg; - hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[1])); + scb->sg_count = i; + hscb->SG_segment_count = i; + hscb->SG_list_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, &scb->sg_list[1])); } else { if (cmd->request_bufflen) { - scb->sg_count = 1; - scb->sg_list[0].address = cpu_to_le32(VIRT_TO_BUS(cmd->request_buffer)); + unsigned int address = pci_map_single(p->pdev, cmd->request_buffer, + cmd->request_bufflen, + scsi_to_pci_dma_dir(cmd->sc_data_direction)); + aic7xxx_mapping(cmd) = address; + scb->sg_list[0].address = cpu_to_le32(address); scb->sg_list[0].length = cpu_to_le32(cmd->request_bufflen); + scb->sg_count = 1; scb->sg_length = cmd->request_bufflen; hscb->SG_segment_count = 1; - hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[0])); + hscb->SG_list_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, &scb->sg_list[0])); hscb->data_count = scb->sg_list[0].length; hscb->data_pointer = scb->sg_list[0].address; } @@ -11991,7 +12287,7 @@ aic7xxx_print_card(struct aic7xxx_host *p) break; case AHC_PCI: default: - printk("PCI %d/%d.\n", PCI_SLOT(p->pci_device_fn), + printk("PCI %d/%d/%d.\n", p->pci_bus, PCI_SLOT(p->pci_device_fn), PCI_FUNC(p->pci_device_fn)); break; } diff --git a/drivers/scsi/aic7xxx/aic7xxx.reg b/drivers/scsi/aic7xxx/aic7xxx.reg index 6cc347d5b..fb554727b 100644 --- a/drivers/scsi/aic7xxx/aic7xxx.reg +++ b/drivers/scsi/aic7xxx/aic7xxx.reg @@ -691,7 +691,8 @@ register INTSTAT { mask NO_IDENT 0x20|SEQINT /* no IDENTIFY after reconnect*/ mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */ mask EXTENDED_MSG 0x40|SEQINT /* Extended message received */ - mask ABORT_REQUESTED 0x50|SEQINT /* Reconect of aborted SCB */ + mask WIDE_RESIDUE 0x50|SEQINT /* need kernel to back up */ + /* the SG array for us */ mask REJECT_MSG 0x60|SEQINT /* Reject message received */ mask BAD_STATUS 0x70|SEQINT /* Bad status from target */ mask RESIDUAL 0x80|SEQINT /* Residual byte count != 0 */ diff --git a/drivers/scsi/aic7xxx/aic7xxx.seq b/drivers/scsi/aic7xxx/aic7xxx.seq index 8e8dc98ee..de3afbf92 100644 --- a/drivers/scsi/aic7xxx/aic7xxx.seq +++ b/drivers/scsi/aic7xxx/aic7xxx.seq @@ -517,10 +517,12 @@ data_phase_loop: } data_phase_inbounds: /* If we are the last SG block, tell the hardware. */ -if ((p->features & AHC_ULTRA2) == 0) { cmp SG_COUNT,0x01 jne data_phase_wideodd; - and DMAPARAMS, ~WIDEODD; -} + if ((p->features & AHC_ULTRA2) == 0) { + and DMAPARAMS, ~WIDEODD; + } else { + mvi SG_CACHEPTR, LAST_SEG; + } data_phase_wideodd: if ((p->features & AHC_ULTRA2) != 0) { mov SINDEX, ALLONES; @@ -530,7 +532,7 @@ data_phase_dma_loop: test SSTAT0, SDONE jnz data_phase_dma_done; test SSTAT1,PHASEMIS jz data_phase_dma_loop; /* ie. underrun */ data_phase_dma_phasemis: - test SSTAT0,SDONE jnz . + 2; + test SSTAT0,SDONE jnz data_phase_dma_done; clr SINDEX; /* Remember the phasemiss */ } else { mov DMAPARAMS call dma; @@ -615,9 +617,10 @@ prefetched_segs_avail: test SSTAT1, REQINIT jz .; test SSTAT1,PHASEMIS jz data_phase_loop; +/* This drops the last SG segment down to the shadow layer for us */ if ((p->features & AHC_ULTRA2) != 0) { mov DFCNTRL, DMAPARAMS; - test SSTAT0, SDONE jnz .; + test SSTAT0, SDONE jnz .; } data_phase_finish: @@ -713,7 +716,7 @@ p_command: mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION); test SSTAT0, SDONE jnz .; p_command_dma_loop: - test SSTAT0, DMADONE jnz p_command_ultra2_dma_done; + test SSTAT0, SDONE jnz p_command_ultra2_dma_done; test SSTAT1,PHASEMIS jz p_command_dma_loop; /* ie. underrun */ p_command_ultra2_dma_done: and DFCNTRL, ~HDMAEN; @@ -830,6 +833,7 @@ p_mesgin: cmp A,MSG_EXTENDED je mesgin_extended; cmp A,MSG_MESSAGE_REJECT je mesgin_reject; cmp A,MSG_NOOP je mesgin_done; + cmp A,MSG_IGN_WIDE_RESIDUE je mesgin_wide_residue; rej_mesgin: /* @@ -1057,6 +1061,36 @@ mesgin_reject: jmp mesgin_done; /* + * Wide Residue. We handle the simple cases, but pass of the one hard case + * to the kernel (when the residue byte happened to cause us to advance our + * sg element array, so we know have to back that advance out). + */ +mesgin_wide_residue: + mvi ARG_1 call inb_next; /* ACK the wide_residue and get */ + /* the size byte */ +/* + * See if we'll ignore this wide residue (because it's an overrun byte) + */ + if ((p->features & AHC_ULTRA2) != 0) { + test SSTAT2, WIDE_RES jnz mesgin_done; + } else { + test SCB_RESID_SGCNT,0xff jnz wide_residue_int; + test SCB_RESID_DCNT[0],0xff jnz wide_residue_int; + test SCB_RESID_DCNT[1],0xff jnz wide_residue_int; + test SCB_RESID_DCNT[2],0xff jnz wide_residue_int; + jmp mesgin_done; + } +wide_residue_int: +/* + * In order for this to be reliable, we have to do all sorts of horrible + * magic in terms of resetting the datafifo and reloading the shadow layer + * with the correct new values (so that a subsequent save data pointers + * message will do the right thing). We let the kernel do that work. + */ + mvi INTSTAT,WIDE_RESIDUE; + jmp mesgin_done; + +/* * [ ADD MORE MESSAGE HANDLING HERE ] */ @@ -1093,7 +1127,7 @@ inb_next_wait: * before continuing. */ test SSTAT1, REQINIT jz inb_next_wait; - test SSTAT1, SCSIPERR jnz inb_next_wait; + test SSTAT1, SCSIPERR jnz .; and LASTPHASE, PHASE_MASK, SCSISIGI; cmp LASTPHASE, P_MESGIN jne mesgin_phasemis; inb_first: diff --git a/drivers/scsi/aic7xxx_proc.c b/drivers/scsi/aic7xxx_proc.c index 1fb00893e..ec80af90d 100644 --- a/drivers/scsi/aic7xxx_proc.c +++ b/drivers/scsi/aic7xxx_proc.c @@ -178,7 +178,7 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length, size += sprintf(BLS, " SCSI Adapter: %s\n", board_names[p->board_name_index]); if (p->flags & AHC_TWIN) - size += sprintf(BLS, " Twin Channel\n"); + size += sprintf(BLS, " Twin Channel Controller "); else { char *channel = ""; @@ -209,9 +209,22 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length, ultra = "Ultra-2 LVD/SE "; else if (p->features & AHC_ULTRA) ultra = "Ultra "; - size += sprintf(BLS, " %s%sController%s\n", + size += sprintf(BLS, " %s%sController%s ", ultra, wide, channel); } + switch(p->chip & ~AHC_CHIPID_MASK) + { + case AHC_VL: + size += sprintf(BLS, "at VLB slot %d\n", p->pci_device_fn); + break; + case AHC_EISA: + size += sprintf(BLS, "at EISA slot %d\n", p->pci_device_fn); + break; + default: + size += sprintf(BLS, "at PCI %d/%d/%d\n", p->pci_bus, + PCI_SLOT(p->pci_device_fn), PCI_FUNC(p->pci_device_fn)); + break; + } if( !(p->maddr) ) { size += sprintf(BLS, " Programmed I/O Base: %lx\n", p->base); @@ -224,11 +237,6 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length, { size += sprintf(BLS, " BIOS Memory Address: 0x%08x\n", p->bios_address); } - if( p->chip & AHC_PCI ) - { - size += sprintf(BLS, " PCI Bus 0x%02x Device 0x%02x\n", p->pci_bus, - p->pci_device_fn); - } size += sprintf(BLS, " Adapter SEEPROM Config: %s\n", (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." : ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." : diff --git a/drivers/scsi/aic7xxx_reg.h b/drivers/scsi/aic7xxx_reg.h index f86e1bec8..4baa00911 100644 --- a/drivers/scsi/aic7xxx_reg.h +++ b/drivers/scsi/aic7xxx_reg.h @@ -188,8 +188,8 @@ #define BRDRW 0x04 #define BRDRW_ULTRA2 0x02 #define BRDCTL1 0x02 -#define BRDSTB_ULTRA2 0x01 #define BRDCTL0 0x01 +#define BRDSTB_ULTRA2 0x01 #define SEECTL 0x1e #define EXTARBACK 0x80 @@ -402,7 +402,7 @@ #define RESIDUAL 0x81 #define BAD_STATUS 0x71 #define REJECT_MSG 0x61 -#define ABORT_REQUESTED 0x51 +#define WIDE_RESIDUE 0x51 #define EXTENDED_MSG 0x41 #define NO_MATCH 0x31 #define NO_IDENT 0x21 @@ -465,6 +465,8 @@ #define TARGCRCENDEN 0x08 #define TARGCRCCNTEN 0x04 +#define QOUTCNT 0x9e + #define SCSIPHASE 0x9e #define SP_STATUS 0x20 #define SP_COMMAND 0x10 @@ -473,8 +475,6 @@ #define SP_DATA_IN 0x02 #define SP_DATA_OUT 0x01 -#define QOUTCNT 0x9e - #define SFUNCT 0x9f #define ALT_MODE 0x80 @@ -595,8 +595,8 @@ #define RD_DFTHRSH_63 0x03 #define RD_DFTHRSH_50 0x02 #define RD_DFTHRSH_25 0x01 -#define WR_DFTHRSH_MIN 0x00 #define RD_DFTHRSH_MIN 0x00 +#define WR_DFTHRSH_MIN 0x00 #define SG_CACHEPTR 0xfc #define SG_USER_DATA 0xfc @@ -604,18 +604,18 @@ #define LAST_SEG_DONE 0x01 +#define CMD_GROUP_CODE_SHIFT 0x05 +#define BUS_8_BIT 0x00 +#define QOUTFIFO_OFFSET 0x01 +#define CCSGRAM_MAXSEGS 0x10 #define CMD_GROUP2_BYTE_DELTA 0xfa #define MAX_OFFSET_8BIT 0x0f #define BUS_16_BIT 0x01 #define QINFIFO_OFFSET 0x02 #define CMD_GROUP5_BYTE_DELTA 0x0b -#define CMD_GROUP_CODE_SHIFT 0x05 #define MAX_OFFSET_ULTRA2 0x7f #define MAX_OFFSET_16BIT 0x08 -#define BUS_8_BIT 0x00 -#define QOUTFIFO_OFFSET 0x01 #define UNTAGGEDSCB_OFFSET 0x00 -#define CCSGRAM_MAXSEGS 0x10 #define SCB_LIST_NULL 0xff #define SG_SIZEOF 0x08 #define CMD_GROUP4_BYTE_DELTA 0x04 diff --git a/drivers/scsi/aic7xxx_seq.c b/drivers/scsi/aic7xxx_seq.c index 49502b6cd..511bfcb73 100644 --- a/drivers/scsi/aic7xxx_seq.c +++ b/drivers/scsi/aic7xxx_seq.c @@ -26,12 +26,12 @@ static unsigned char seqprog[] = { 0x00, 0x4d, 0x12, 0x70, 0x01, 0x4e, 0x9c, 0x18, 0xbf, 0x60, 0xc0, 0x08, - 0x00, 0x6a, 0xa8, 0x5c, + 0x00, 0x6a, 0xbe, 0x5c, 0xff, 0x4e, 0xc8, 0x18, - 0x02, 0x6a, 0xbe, 0x5b, + 0x02, 0x6a, 0xd4, 0x5b, 0xff, 0x52, 0x20, 0x09, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0x52, 0x34, 0x5c, + 0x00, 0x52, 0x4a, 0x5c, 0x03, 0xb0, 0x52, 0x31, 0xff, 0xb0, 0x52, 0x09, 0xff, 0xb1, 0x54, 0x09, @@ -76,7 +76,7 @@ static unsigned char seqprog[] = { 0x10, 0x03, 0xfc, 0x78, 0xff, 0x50, 0xc8, 0x08, 0x88, 0x6a, 0xcc, 0x00, - 0x49, 0x6a, 0x24, 0x5c, + 0x49, 0x6a, 0x3a, 0x5c, 0x01, 0x6a, 0x26, 0x01, 0xff, 0x6a, 0xca, 0x08, 0x08, 0x01, 0x02, 0x00, @@ -117,11 +117,11 @@ static unsigned char seqprog[] = { 0xff, 0x65, 0xca, 0x18, 0xff, 0x65, 0xd8, 0x68, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0x9a, 0x5c, + 0x00, 0x65, 0xb0, 0x5c, 0x40, 0x51, 0xf0, 0x78, 0xe4, 0x6a, 0x06, 0x00, 0x08, 0x01, 0x02, 0x00, - 0x04, 0x6a, 0x56, 0x5b, + 0x04, 0x6a, 0x6c, 0x5b, 0x01, 0x50, 0xa0, 0x18, 0x00, 0x50, 0xf6, 0xe0, 0xff, 0x6a, 0xa0, 0x08, @@ -147,13 +147,13 @@ static unsigned char seqprog[] = { 0x08, 0x6a, 0x66, 0x58, 0x80, 0x6a, 0x68, 0x00, 0x80, 0x36, 0x6c, 0x00, - 0x00, 0x65, 0x08, 0x5c, + 0x00, 0x65, 0x1e, 0x5c, 0xff, 0x3d, 0xc8, 0x08, 0xbf, 0x64, 0x5a, 0x79, - 0x80, 0x64, 0x20, 0x72, - 0xa0, 0x64, 0x50, 0x72, - 0xc0, 0x64, 0x48, 0x72, - 0xe0, 0x64, 0x90, 0x72, + 0x80, 0x64, 0x22, 0x72, + 0xa0, 0x64, 0x52, 0x72, + 0xc0, 0x64, 0x4a, 0x72, + 0xe0, 0x64, 0x92, 0x72, 0x01, 0x6a, 0x22, 0x01, 0x00, 0x65, 0x22, 0x41, 0xf7, 0x11, 0x22, 0x08, @@ -173,13 +173,13 @@ static unsigned char seqprog[] = { 0x03, 0xa9, 0x18, 0x31, 0x03, 0xa9, 0x10, 0x30, 0x08, 0x6a, 0xcc, 0x00, - 0xa9, 0x6a, 0x1e, 0x5c, + 0xa9, 0x6a, 0x34, 0x5c, 0x00, 0x65, 0x7a, 0x41, 0xa8, 0x6a, 0x6a, 0x00, 0x79, 0x6a, 0x6a, 0x00, 0x40, 0x3d, 0x62, 0x69, 0x04, 0x35, 0x6a, 0x00, - 0x00, 0x65, 0x78, 0x5b, + 0x00, 0x65, 0x8e, 0x5b, 0x80, 0x6a, 0xd4, 0x01, 0x10, 0x36, 0x4e, 0x69, 0x10, 0x36, 0x6c, 0x00, @@ -187,10 +187,10 @@ static unsigned char seqprog[] = { 0x03, 0x8c, 0x10, 0x30, 0x05, 0xa3, 0x70, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0xac, 0x6a, 0x16, 0x5c, - 0x00, 0x65, 0x10, 0x5c, + 0xac, 0x6a, 0x2c, 0x5c, + 0x00, 0x65, 0x26, 0x5c, 0x38, 0x6a, 0xcc, 0x00, - 0xa3, 0x6a, 0x1a, 0x5c, + 0xa3, 0x6a, 0x30, 0x5c, 0xff, 0x38, 0x8a, 0x69, 0x80, 0x02, 0x04, 0x00, 0xe7, 0x35, 0x6a, 0x08, @@ -199,51 +199,52 @@ static unsigned char seqprog[] = { 0xff, 0x6a, 0x10, 0x00, 0xff, 0x6a, 0x12, 0x00, 0xff, 0x6a, 0x14, 0x00, - 0x01, 0x38, 0x8e, 0x61, + 0x01, 0x38, 0x90, 0x61, 0xbf, 0x35, 0x6a, 0x08, + 0x02, 0x6a, 0xf8, 0x01, 0xff, 0x69, 0xca, 0x08, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x0b, 0x92, 0x69, - 0x04, 0x0b, 0x9e, 0x69, - 0x10, 0x0c, 0x94, 0x79, - 0x04, 0x0b, 0x9c, 0x69, + 0x04, 0x0b, 0x94, 0x69, + 0x04, 0x0b, 0xa0, 0x69, + 0x10, 0x0c, 0x96, 0x79, + 0x04, 0x0b, 0xa0, 0x69, 0xff, 0x6a, 0xca, 0x08, - 0x00, 0x35, 0x60, 0x5b, - 0x80, 0x02, 0xf2, 0x69, - 0xff, 0x65, 0xe2, 0x79, + 0x00, 0x35, 0x76, 0x5b, + 0x80, 0x02, 0xf4, 0x69, + 0xff, 0x65, 0xe4, 0x79, 0xff, 0x38, 0x70, 0x18, - 0xff, 0x38, 0xe2, 0x79, - 0x80, 0xea, 0xbe, 0x61, + 0xff, 0x38, 0xe4, 0x79, + 0x80, 0xea, 0xc0, 0x61, 0xef, 0x38, 0xc8, 0x18, 0x80, 0x6a, 0xc8, 0x00, - 0x00, 0x65, 0xb0, 0x49, + 0x00, 0x65, 0xb2, 0x49, 0x33, 0x38, 0xc8, 0x28, 0xff, 0x64, 0xd0, 0x09, 0x04, 0x39, 0xc0, 0x31, 0x09, 0x6a, 0xd6, 0x01, - 0x80, 0xeb, 0xb6, 0x79, + 0x80, 0xeb, 0xb8, 0x79, 0xf7, 0xeb, 0xd6, 0x09, - 0x08, 0xeb, 0xba, 0x69, + 0x08, 0xeb, 0xbc, 0x69, 0x01, 0x6a, 0xd6, 0x01, 0x08, 0xe9, 0x10, 0x31, 0x03, 0x8c, 0x10, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0x39, 0x6a, 0x1c, 0x5c, + 0x39, 0x6a, 0x32, 0x5c, 0x08, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x0d, 0x93, 0x26, 0x01, - 0x00, 0x65, 0x9a, 0x5c, - 0x88, 0x6a, 0x8a, 0x5c, - 0x00, 0x65, 0x10, 0x5c, + 0x00, 0x65, 0xb0, 0x5c, + 0x88, 0x6a, 0xa0, 0x5c, + 0x00, 0x65, 0x26, 0x5c, 0xff, 0x6a, 0xc8, 0x08, 0x08, 0x39, 0x72, 0x18, 0x00, 0x3a, 0x74, 0x20, - 0x01, 0x0c, 0xda, 0x79, + 0x01, 0x0c, 0xdc, 0x79, 0x10, 0x0c, 0x7a, 0x79, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x0b, 0xe0, 0x69, - 0x00, 0x65, 0xfa, 0x59, + 0x04, 0x0b, 0xe2, 0x69, + 0x00, 0x65, 0xfc, 0x59, 0x03, 0x08, 0x52, 0x31, 0xff, 0x38, 0x50, 0x09, 0xff, 0x08, 0x52, 0x09, @@ -251,275 +252,285 @@ static unsigned char seqprog[] = { 0xff, 0x0a, 0x56, 0x09, 0xff, 0x38, 0x50, 0x09, 0x00, 0x65, 0x22, 0x41, - 0x00, 0x65, 0xfa, 0x59, + 0x00, 0x65, 0xfc, 0x59, 0x7f, 0x02, 0x04, 0x08, 0xe1, 0x6a, 0x22, 0x01, 0x00, 0x65, 0x22, 0x41, - 0x04, 0x93, 0x10, 0x6a, + 0x04, 0x93, 0x12, 0x6a, 0xdf, 0x93, 0x26, 0x09, - 0x20, 0x93, 0xfe, 0x69, + 0x20, 0x93, 0x00, 0x6a, 0x02, 0x93, 0x26, 0x01, - 0x01, 0x94, 0x00, 0x7a, - 0x01, 0x94, 0x00, 0x7a, - 0x01, 0x94, 0x00, 0x7a, - 0x01, 0x94, 0x00, 0x7a, - 0x01, 0x94, 0x00, 0x7a, - 0x01, 0x94, 0x00, 0x7a, - 0x10, 0x94, 0x0e, 0x6a, + 0x01, 0x94, 0x02, 0x7a, + 0x01, 0x94, 0x02, 0x7a, + 0x01, 0x94, 0x02, 0x7a, + 0x01, 0x94, 0x02, 0x7a, + 0x01, 0x94, 0x02, 0x7a, + 0x01, 0x94, 0x02, 0x7a, + 0x10, 0x94, 0x10, 0x6a, 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0x12, 0x6a, + 0x08, 0x93, 0x14, 0x6a, 0xdf, 0x93, 0x26, 0x09, - 0x20, 0x93, 0x16, 0x6a, + 0x20, 0x93, 0x18, 0x6a, 0x03, 0x08, 0x52, 0x31, 0xff, 0x38, 0x50, 0x09, 0x12, 0x01, 0x02, 0x00, 0xff, 0x6a, 0xd4, 0x0c, - 0x00, 0x65, 0x78, 0x5b, + 0x00, 0x65, 0x8e, 0x5b, 0x05, 0xb4, 0x10, 0x31, 0x02, 0x6a, 0x1a, 0x31, 0x03, 0x8c, 0x10, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0xb4, 0x6a, 0x1a, 0x5c, + 0xb4, 0x6a, 0x30, 0x5c, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, - 0x00, 0x65, 0x10, 0x5c, - 0x3d, 0x6a, 0x60, 0x5b, + 0x00, 0x65, 0x26, 0x5c, + 0x3d, 0x6a, 0x76, 0x5b, 0xac, 0x6a, 0x26, 0x01, - 0x04, 0x0b, 0x36, 0x6a, - 0x01, 0x0b, 0x3c, 0x6a, - 0x10, 0x0c, 0x38, 0x7a, + 0x04, 0x0b, 0x38, 0x6a, + 0x04, 0x0b, 0x3e, 0x6a, + 0x10, 0x0c, 0x3a, 0x7a, 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0x3e, 0x6a, + 0x08, 0x93, 0x40, 0x6a, 0xdf, 0x93, 0x26, 0x09, - 0x20, 0x93, 0x42, 0x6a, + 0x20, 0x93, 0x44, 0x6a, 0x12, 0x01, 0x02, 0x00, 0x00, 0x65, 0x22, 0x41, - 0x00, 0x65, 0x78, 0x5b, + 0x00, 0x65, 0x8e, 0x5b, 0xff, 0x06, 0x44, 0x09, 0x00, 0x65, 0x22, 0x41, 0x10, 0x3d, 0x06, 0x00, 0xff, 0x34, 0xca, 0x08, - 0x80, 0x65, 0x74, 0x62, + 0x80, 0x65, 0x76, 0x62, 0x0f, 0xa1, 0xca, 0x08, 0x07, 0xa1, 0xca, 0x08, 0x40, 0xa0, 0xc8, 0x08, 0x00, 0x65, 0xca, 0x00, 0x80, 0x65, 0xca, 0x00, - 0x80, 0xa0, 0x64, 0x7a, + 0x80, 0xa0, 0x66, 0x7a, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x76, 0x42, - 0x20, 0xa0, 0x7c, 0x7a, + 0x00, 0x65, 0x78, 0x42, + 0x20, 0xa0, 0x7e, 0x7a, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x08, 0x5c, - 0xa0, 0x3d, 0x84, 0x62, + 0x00, 0x65, 0x1e, 0x5c, + 0xa0, 0x3d, 0x86, 0x62, 0x23, 0xa0, 0x0c, 0x08, - 0x00, 0x65, 0x08, 0x5c, - 0xa0, 0x3d, 0x84, 0x62, - 0x00, 0xb9, 0x7c, 0x42, - 0xff, 0x65, 0x7c, 0x62, + 0x00, 0x65, 0x1e, 0x5c, + 0xa0, 0x3d, 0x86, 0x62, + 0x00, 0xb9, 0x7e, 0x42, + 0xff, 0x65, 0x7e, 0x62, 0xa1, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, - 0x10, 0x51, 0x84, 0x72, + 0x10, 0x51, 0x86, 0x72, 0x40, 0x6a, 0x18, 0x00, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x08, 0x5c, - 0xa0, 0x3d, 0x4e, 0x72, + 0x00, 0x65, 0x1e, 0x5c, + 0xa0, 0x3d, 0x50, 0x72, 0x40, 0x6a, 0x18, 0x00, 0xff, 0x34, 0xa6, 0x08, - 0x80, 0x34, 0x8c, 0x62, + 0x80, 0x34, 0x8e, 0x62, 0x7f, 0xa0, 0x40, 0x09, 0x08, 0x6a, 0x68, 0x00, 0x00, 0x65, 0x22, 0x41, - 0x64, 0x6a, 0x50, 0x5b, - 0x80, 0x64, 0x00, 0x6b, - 0x04, 0x64, 0xe2, 0x72, - 0x02, 0x64, 0xe8, 0x72, - 0x00, 0x6a, 0xaa, 0x72, - 0x03, 0x64, 0xfc, 0x72, - 0x01, 0x64, 0xde, 0x72, - 0x07, 0x64, 0x3e, 0x73, - 0x08, 0x64, 0xa6, 0x72, + 0x64, 0x6a, 0x66, 0x5b, + 0x80, 0x64, 0x04, 0x6b, + 0x04, 0x64, 0xe6, 0x72, + 0x02, 0x64, 0xec, 0x72, + 0x00, 0x6a, 0xae, 0x72, + 0x03, 0x64, 0x00, 0x73, + 0x01, 0x64, 0xe2, 0x72, + 0x07, 0x64, 0x42, 0x73, + 0x08, 0x64, 0xaa, 0x72, + 0x23, 0x64, 0x46, 0x73, 0x11, 0x6a, 0x22, 0x01, - 0x07, 0x6a, 0x42, 0x5b, + 0x07, 0x6a, 0x58, 0x5b, 0xff, 0x06, 0xd4, 0x08, 0x00, 0x65, 0x22, 0x41, - 0xff, 0xa8, 0xae, 0x6a, - 0xff, 0xa2, 0xc6, 0x7a, + 0xff, 0xa8, 0xb2, 0x6a, + 0xff, 0xa2, 0xca, 0x7a, 0x01, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0x34, 0x5c, - 0xff, 0xa2, 0xc6, 0x7a, + 0x00, 0xb9, 0x4a, 0x5c, + 0xff, 0xa2, 0xca, 0x7a, 0x71, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, - 0x40, 0x51, 0xc6, 0x62, + 0x40, 0x51, 0xca, 0x62, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0x34, 0x5c, + 0x00, 0xb9, 0x4a, 0x5c, 0xff, 0x3e, 0x74, 0x09, 0xff, 0x90, 0x7c, 0x08, 0x00, 0x65, 0x50, 0x58, 0x00, 0x65, 0x34, 0x41, - 0x20, 0xa0, 0xce, 0x6a, + 0x20, 0xa0, 0xd2, 0x6a, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0x6a, 0xde, 0x5b, - 0xff, 0x6a, 0xf4, 0x5b, + 0x00, 0x6a, 0xf4, 0x5b, + 0xff, 0x6a, 0x0a, 0x5c, 0xff, 0xf8, 0xc8, 0x08, 0xff, 0x4f, 0xc8, 0x08, - 0x01, 0x6a, 0xde, 0x5b, - 0x00, 0xb9, 0xf4, 0x5b, + 0x01, 0x6a, 0xf4, 0x5b, + 0x00, 0xb9, 0x0a, 0x5c, 0x01, 0x4f, 0x9e, 0x18, 0x02, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0xa2, 0x5c, + 0x00, 0x65, 0xb8, 0x5c, 0x00, 0x65, 0x34, 0x41, 0x41, 0x6a, 0x22, 0x01, 0x00, 0x65, 0x22, 0x41, 0x04, 0xa0, 0x40, 0x01, - 0x00, 0x65, 0xba, 0x5c, + 0x00, 0x65, 0xd0, 0x5c, 0x00, 0x65, 0x34, 0x41, - 0x10, 0x36, 0xa6, 0x7a, + 0x10, 0x36, 0xaa, 0x7a, 0x05, 0x38, 0x46, 0x31, 0x04, 0x14, 0x58, 0x31, 0x03, 0xa9, 0x60, 0x31, 0xa3, 0x6a, 0xcc, 0x00, - 0x38, 0x6a, 0x1a, 0x5c, + 0x38, 0x6a, 0x30, 0x5c, 0xac, 0x6a, 0xcc, 0x00, - 0x14, 0x6a, 0x1c, 0x5c, - 0xa9, 0x6a, 0x1e, 0x5c, - 0x00, 0x65, 0xa6, 0x42, + 0x14, 0x6a, 0x32, 0x5c, + 0xa9, 0x6a, 0x34, 0x5c, + 0x00, 0x65, 0xaa, 0x42, 0xef, 0x36, 0x6c, 0x08, - 0x00, 0x65, 0xa6, 0x42, + 0x00, 0x65, 0xaa, 0x42, 0x0f, 0x64, 0xc8, 0x08, 0x07, 0x64, 0xc8, 0x08, 0x00, 0x37, 0x6e, 0x00, 0xff, 0x6a, 0xa4, 0x00, - 0x00, 0x65, 0xae, 0x5b, - 0xff, 0x51, 0x12, 0x73, - 0x20, 0x36, 0x1c, 0x7b, - 0x00, 0x90, 0x9c, 0x5b, - 0x00, 0x65, 0x1e, 0x43, + 0x00, 0x65, 0xc4, 0x5b, + 0xff, 0x51, 0x16, 0x73, + 0x20, 0x36, 0x20, 0x7b, + 0x00, 0x90, 0xb2, 0x5b, + 0x00, 0x65, 0x22, 0x43, 0xff, 0x06, 0xd4, 0x08, - 0x00, 0x65, 0x08, 0x5c, - 0xe0, 0x3d, 0x38, 0x63, - 0x20, 0x12, 0x38, 0x63, - 0x51, 0x6a, 0x46, 0x5b, - 0x00, 0x65, 0x96, 0x5b, + 0x00, 0x65, 0x1e, 0x5c, + 0xe0, 0x3d, 0x3c, 0x63, + 0x20, 0x12, 0x3c, 0x63, + 0x51, 0x6a, 0x5c, 0x5b, + 0x00, 0x65, 0xac, 0x5b, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0xa1, 0x30, 0x63, - 0x04, 0xa0, 0x30, 0x7b, + 0x00, 0xa1, 0x34, 0x63, + 0x04, 0xa0, 0x34, 0x7b, 0xfb, 0xa0, 0x40, 0x09, 0x80, 0x36, 0x6c, 0x00, - 0x80, 0xa0, 0xa6, 0x7a, + 0x80, 0xa0, 0xaa, 0x7a, 0x7f, 0xa0, 0x40, 0x09, - 0xff, 0x6a, 0x42, 0x5b, - 0x00, 0x65, 0xa6, 0x42, - 0x04, 0xa0, 0x36, 0x7b, - 0x00, 0x65, 0xba, 0x5c, - 0x00, 0x65, 0x38, 0x43, - 0x00, 0x65, 0xa2, 0x5c, + 0xff, 0x6a, 0x58, 0x5b, + 0x00, 0x65, 0xaa, 0x42, + 0x04, 0xa0, 0x3a, 0x7b, + 0x00, 0x65, 0xd0, 0x5c, + 0x00, 0x65, 0x3c, 0x43, + 0x00, 0x65, 0xb8, 0x5c, 0x31, 0x6a, 0x22, 0x01, - 0x0c, 0x6a, 0x42, 0x5b, - 0x00, 0x65, 0xa6, 0x42, + 0x0c, 0x6a, 0x58, 0x5b, + 0x00, 0x65, 0xaa, 0x42, 0x61, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0xa6, 0x42, + 0x00, 0x65, 0xaa, 0x42, + 0x51, 0x6a, 0x5c, 0x5b, + 0x20, 0x0d, 0xaa, 0x6a, + 0xff, 0xa8, 0x54, 0x6b, + 0xff, 0xa9, 0x54, 0x6b, + 0xff, 0xaa, 0x54, 0x6b, + 0xff, 0xab, 0x54, 0x6b, + 0x00, 0x65, 0xaa, 0x42, + 0x51, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0xaa, 0x42, 0x10, 0x3d, 0x06, 0x00, 0xff, 0x65, 0x68, 0x0c, 0xff, 0x06, 0xd4, 0x08, - 0x01, 0x0c, 0x48, 0x7b, - 0x04, 0x0c, 0x48, 0x6b, + 0x01, 0x0c, 0x5e, 0x7b, + 0x04, 0x0c, 0x60, 0x6b, 0xe0, 0x03, 0x7a, 0x08, - 0xe0, 0x3d, 0x5c, 0x63, + 0xe0, 0x3d, 0x72, 0x63, 0xff, 0x65, 0xcc, 0x08, 0xff, 0x12, 0xda, 0x0c, 0xff, 0x06, 0xd4, 0x0c, 0xff, 0x65, 0x0c, 0x08, - 0x02, 0x0b, 0x58, 0x7b, + 0x02, 0x0b, 0x6e, 0x7b, 0xff, 0x6a, 0xd4, 0x0c, 0xd1, 0x6a, 0x22, 0x01, 0x00, 0x65, 0x22, 0x41, 0xff, 0x65, 0x26, 0x09, - 0x01, 0x0b, 0x70, 0x6b, - 0x10, 0x0c, 0x62, 0x7b, - 0x04, 0x0b, 0x6a, 0x6b, + 0x01, 0x0b, 0x86, 0x6b, + 0x10, 0x0c, 0x78, 0x7b, + 0x04, 0x0b, 0x80, 0x6b, 0xff, 0x6a, 0xca, 0x08, - 0x04, 0x93, 0x6e, 0x6b, - 0x01, 0x94, 0x6c, 0x7b, - 0x10, 0x94, 0x6e, 0x6b, + 0x04, 0x93, 0x84, 0x6b, + 0x01, 0x94, 0x82, 0x7b, + 0x10, 0x94, 0x84, 0x6b, 0xc7, 0x93, 0x26, 0x09, 0xff, 0x99, 0xd4, 0x08, - 0x38, 0x93, 0x72, 0x6b, + 0x38, 0x93, 0x88, 0x6b, 0xff, 0x6a, 0xd4, 0x0c, - 0x80, 0x36, 0x76, 0x6b, + 0x80, 0x36, 0x8c, 0x6b, 0x21, 0x6a, 0x22, 0x05, 0xff, 0x65, 0x20, 0x09, - 0xff, 0x51, 0x84, 0x63, + 0xff, 0x51, 0x9a, 0x63, 0xff, 0x37, 0xc8, 0x08, - 0xa1, 0x6a, 0x90, 0x43, + 0xa1, 0x6a, 0xa6, 0x43, 0xff, 0x51, 0xc8, 0x08, - 0xb9, 0x6a, 0x90, 0x43, + 0xb9, 0x6a, 0xa6, 0x43, 0xff, 0x90, 0xa4, 0x08, - 0xff, 0xba, 0x94, 0x73, + 0xff, 0xba, 0xaa, 0x73, 0xff, 0xba, 0x20, 0x09, 0xff, 0x65, 0xca, 0x18, - 0x00, 0x6c, 0x88, 0x63, + 0x00, 0x6c, 0x9e, 0x63, 0xff, 0x90, 0xca, 0x0c, 0xff, 0x6a, 0xca, 0x04, - 0x20, 0x36, 0xa8, 0x7b, - 0x00, 0x90, 0x7c, 0x5b, - 0xff, 0x65, 0xa8, 0x73, - 0xff, 0x52, 0xa6, 0x73, + 0x20, 0x36, 0xbe, 0x7b, + 0x00, 0x90, 0x92, 0x5b, + 0xff, 0x65, 0xbe, 0x73, + 0xff, 0x52, 0xbc, 0x73, 0xff, 0xba, 0xcc, 0x08, 0xff, 0x52, 0x20, 0x09, 0xff, 0x66, 0x74, 0x09, 0xff, 0x65, 0x20, 0x0d, 0xff, 0xba, 0x7e, 0x0c, - 0x00, 0x6a, 0xa8, 0x5c, + 0x00, 0x6a, 0xbe, 0x5c, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0x51, 0x34, 0x44, - 0xff, 0x3f, 0x02, 0x74, + 0x00, 0x51, 0x4a, 0x44, + 0xff, 0x3f, 0x18, 0x74, 0xff, 0x6a, 0xa2, 0x00, - 0x00, 0x3f, 0x7c, 0x5b, - 0xff, 0x65, 0x02, 0x74, + 0x00, 0x3f, 0x92, 0x5b, + 0xff, 0x65, 0x18, 0x74, 0x20, 0x36, 0x6c, 0x00, - 0x20, 0xa0, 0xbc, 0x6b, + 0x20, 0xa0, 0xd2, 0x6b, 0xff, 0xb9, 0xa2, 0x0c, 0xff, 0x6a, 0xa2, 0x04, 0xff, 0x65, 0xa4, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x28, 0x5c, + 0x45, 0x6a, 0x3e, 0x5c, 0x01, 0x6a, 0xd0, 0x01, 0x09, 0x6a, 0xd6, 0x01, - 0x80, 0xeb, 0xc8, 0x7b, + 0x80, 0xeb, 0xde, 0x7b, 0x01, 0x6a, 0xd6, 0x01, 0x01, 0xe9, 0xa4, 0x34, 0x88, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x28, 0x5c, + 0x45, 0x6a, 0x3e, 0x5c, 0x01, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x0d, 0x6a, 0x26, 0x01, - 0x00, 0x65, 0x9a, 0x5c, + 0x00, 0x65, 0xb0, 0x5c, 0xff, 0x99, 0xa4, 0x0c, 0xff, 0x65, 0xa4, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x28, 0x5c, + 0x45, 0x6a, 0x3e, 0x5c, 0x01, 0x6a, 0xd0, 0x01, 0x01, 0x6a, 0xdc, 0x05, 0x88, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x28, 0x5c, + 0x45, 0x6a, 0x3e, 0x5c, 0x01, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x01, 0x6a, 0x26, 0x05, 0x01, 0x65, 0xd8, 0x31, 0x09, 0xee, 0xdc, 0x01, - 0x80, 0xee, 0xf8, 0x7b, + 0x80, 0xee, 0x0e, 0x7c, 0xff, 0x6a, 0xdc, 0x0d, 0xff, 0x65, 0x32, 0x09, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0x9a, 0x44, + 0x00, 0x65, 0xb0, 0x44, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0x6a, 0xbe, 0x5b, + 0x00, 0x6a, 0xd4, 0x5b, 0xff, 0x52, 0xa2, 0x0c, - 0x01, 0x0c, 0x08, 0x7c, - 0x04, 0x0c, 0x08, 0x6c, + 0x01, 0x0c, 0x1e, 0x7c, + 0x04, 0x0c, 0x1e, 0x6c, 0xe0, 0x03, 0x06, 0x08, 0xe0, 0x03, 0x7a, 0x0c, 0xff, 0x8c, 0x10, 0x08, @@ -542,29 +553,29 @@ static unsigned char seqprog[] = { 0x00, 0x6c, 0xda, 0x24, 0xff, 0x65, 0xc8, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x41, 0x6a, 0x24, 0x5c, + 0x41, 0x6a, 0x3a, 0x5c, 0xff, 0x90, 0xe2, 0x09, 0x20, 0x6a, 0xd0, 0x01, - 0x04, 0x35, 0x46, 0x7c, + 0x04, 0x35, 0x5c, 0x7c, 0x1d, 0x6a, 0xdc, 0x01, - 0xdc, 0xee, 0x42, 0x64, - 0x00, 0x65, 0x52, 0x44, + 0xdc, 0xee, 0x58, 0x64, + 0x00, 0x65, 0x68, 0x44, 0x01, 0x6a, 0xdc, 0x01, 0x20, 0xa0, 0xd8, 0x31, 0x09, 0xee, 0xdc, 0x01, - 0x80, 0xee, 0x4c, 0x7c, + 0x80, 0xee, 0x62, 0x7c, 0x19, 0x6a, 0xdc, 0x01, - 0xd8, 0xee, 0x50, 0x64, + 0xd8, 0xee, 0x66, 0x64, 0xff, 0x6a, 0xdc, 0x09, - 0x18, 0xee, 0x54, 0x6c, + 0x18, 0xee, 0x6a, 0x6c, 0xff, 0x6a, 0xd4, 0x0c, 0x88, 0x6a, 0xcc, 0x00, - 0x41, 0x6a, 0x24, 0x5c, + 0x41, 0x6a, 0x3a, 0x5c, 0x20, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x35, 0x7e, 0x6c, + 0x04, 0x35, 0x94, 0x6c, 0xa0, 0x6a, 0xca, 0x00, 0x20, 0x65, 0xc8, 0x18, 0xff, 0x6c, 0x32, 0x09, @@ -575,14 +586,14 @@ static unsigned char seqprog[] = { 0xff, 0x6c, 0x32, 0x09, 0xff, 0x6c, 0x32, 0x09, 0xff, 0x6c, 0x32, 0x09, - 0x00, 0x65, 0x6a, 0x64, + 0x00, 0x65, 0x80, 0x64, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0x9a, 0x5c, - 0x04, 0x35, 0x76, 0x7b, - 0xa0, 0x6a, 0x8a, 0x5c, - 0x00, 0x65, 0x8c, 0x5c, - 0x00, 0x65, 0x8c, 0x5c, - 0x00, 0x65, 0x8c, 0x44, + 0x00, 0x65, 0xb0, 0x5c, + 0x04, 0x35, 0x8c, 0x7b, + 0xa0, 0x6a, 0xa0, 0x5c, + 0x00, 0x65, 0xa2, 0x5c, + 0x00, 0x65, 0xa2, 0x5c, + 0x00, 0x65, 0xa2, 0x44, 0xff, 0x65, 0xcc, 0x08, 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x08, @@ -591,19 +602,19 @@ static unsigned char seqprog[] = { 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x0c, - 0x08, 0x94, 0x9a, 0x7c, + 0x08, 0x94, 0xb0, 0x7c, 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0x9e, 0x6c, + 0x08, 0x93, 0xb4, 0x6c, 0xff, 0x6a, 0xd4, 0x0c, 0xff, 0x40, 0x74, 0x09, 0xff, 0x90, 0x80, 0x08, 0xff, 0x6a, 0x72, 0x05, - 0xff, 0x40, 0xb6, 0x64, - 0xff, 0x3f, 0xae, 0x64, + 0xff, 0x40, 0xcc, 0x64, + 0xff, 0x3f, 0xc4, 0x64, 0xff, 0x6a, 0xca, 0x04, 0xff, 0x3f, 0x20, 0x09, 0x01, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0x34, 0x5c, + 0x00, 0xb9, 0x4a, 0x5c, 0xff, 0xba, 0x7e, 0x0c, 0xff, 0x40, 0x20, 0x09, 0xff, 0xba, 0x80, 0x0c, @@ -761,49 +772,52 @@ struct sequencer_patch { { aic7xxx_patch2_func, 192, 2, 3 }, { aic7xxx_patch8_func, 192, 1, 1 }, { aic7xxx_patch0_func, 194, 3, 1 }, - { aic7xxx_patch10_func, 197, 2, 1 }, - { aic7xxx_patch8_func, 199, 7, 2 }, - { aic7xxx_patch0_func, 206, 1, 1 }, - { aic7xxx_patch2_func, 211, 14, 3 }, - { aic7xxx_patch10_func, 224, 1, 1 }, - { aic7xxx_patch0_func, 225, 9, 1 }, - { aic7xxx_patch8_func, 239, 2, 1 }, - { aic7xxx_patch8_func, 241, 1, 1 }, - { aic7xxx_patch10_func, 242, 6, 3 }, - { aic7xxx_patch2_func, 242, 2, 2 }, - { aic7xxx_patch0_func, 244, 4, 1 }, - { aic7xxx_patch8_func, 249, 1, 1 }, - { aic7xxx_patch8_func, 253, 19, 1 }, - { aic7xxx_patch2_func, 273, 3, 3 }, - { aic7xxx_patch10_func, 275, 1, 1 }, - { aic7xxx_patch0_func, 276, 5, 1 }, - { aic7xxx_patch10_func, 281, 1, 2 }, - { aic7xxx_patch0_func, 282, 9, 1 }, - { aic7xxx_patch11_func, 298, 1, 2 }, - { aic7xxx_patch0_func, 299, 1, 1 }, - { aic7xxx_patch5_func, 359, 1, 2 }, - { aic7xxx_patch0_func, 360, 1, 1 }, - { aic7xxx_patch3_func, 363, 1, 1 }, - { aic7xxx_patch2_func, 373, 3, 2 }, - { aic7xxx_patch0_func, 376, 5, 1 }, - { aic7xxx_patch11_func, 384, 1, 2 }, - { aic7xxx_patch0_func, 385, 1, 1 }, - { aic7xxx_patch6_func, 390, 1, 1 }, - { aic7xxx_patch1_func, 427, 3, 1 }, - { aic7xxx_patch10_func, 432, 11, 1 }, - { aic7xxx_patch2_func, 480, 7, 2 }, - { aic7xxx_patch0_func, 487, 8, 1 }, - { aic7xxx_patch2_func, 496, 4, 2 }, - { aic7xxx_patch0_func, 500, 6, 1 }, - { aic7xxx_patch2_func, 506, 4, 2 }, - { aic7xxx_patch0_func, 510, 3, 1 }, - { aic7xxx_patch12_func, 520, 10, 1 }, - { aic7xxx_patch2_func, 539, 17, 4 }, - { aic7xxx_patch13_func, 547, 4, 2 }, - { aic7xxx_patch0_func, 551, 2, 1 }, - { aic7xxx_patch0_func, 556, 33, 1 }, - { aic7xxx_patch12_func, 589, 4, 1 }, - { aic7xxx_patch6_func, 593, 2, 1 }, - { aic7xxx_patch6_func, 596, 9, 1 }, + { aic7xxx_patch10_func, 198, 1, 2 }, + { aic7xxx_patch0_func, 199, 1, 1 }, + { aic7xxx_patch8_func, 200, 7, 2 }, + { aic7xxx_patch0_func, 207, 1, 1 }, + { aic7xxx_patch2_func, 212, 14, 3 }, + { aic7xxx_patch10_func, 225, 1, 1 }, + { aic7xxx_patch0_func, 226, 9, 1 }, + { aic7xxx_patch8_func, 240, 2, 1 }, + { aic7xxx_patch8_func, 242, 1, 1 }, + { aic7xxx_patch10_func, 243, 6, 3 }, + { aic7xxx_patch2_func, 243, 2, 2 }, + { aic7xxx_patch0_func, 245, 4, 1 }, + { aic7xxx_patch8_func, 250, 1, 1 }, + { aic7xxx_patch8_func, 254, 19, 1 }, + { aic7xxx_patch2_func, 274, 3, 3 }, + { aic7xxx_patch10_func, 276, 1, 1 }, + { aic7xxx_patch0_func, 277, 5, 1 }, + { aic7xxx_patch10_func, 282, 1, 2 }, + { aic7xxx_patch0_func, 283, 9, 1 }, + { aic7xxx_patch11_func, 299, 1, 2 }, + { aic7xxx_patch0_func, 300, 1, 1 }, + { aic7xxx_patch5_func, 361, 1, 2 }, + { aic7xxx_patch0_func, 362, 1, 1 }, + { aic7xxx_patch3_func, 365, 1, 1 }, + { aic7xxx_patch2_func, 375, 3, 2 }, + { aic7xxx_patch0_func, 378, 5, 1 }, + { aic7xxx_patch11_func, 386, 1, 2 }, + { aic7xxx_patch0_func, 387, 1, 1 }, + { aic7xxx_patch6_func, 392, 1, 1 }, + { aic7xxx_patch8_func, 420, 1, 2 }, + { aic7xxx_patch0_func, 421, 5, 1 }, + { aic7xxx_patch1_func, 438, 3, 1 }, + { aic7xxx_patch10_func, 443, 11, 1 }, + { aic7xxx_patch2_func, 491, 7, 2 }, + { aic7xxx_patch0_func, 498, 8, 1 }, + { aic7xxx_patch2_func, 507, 4, 2 }, + { aic7xxx_patch0_func, 511, 6, 1 }, + { aic7xxx_patch2_func, 517, 4, 2 }, + { aic7xxx_patch0_func, 521, 3, 1 }, + { aic7xxx_patch12_func, 531, 10, 1 }, + { aic7xxx_patch2_func, 550, 17, 4 }, + { aic7xxx_patch13_func, 558, 4, 2 }, + { aic7xxx_patch0_func, 562, 2, 1 }, + { aic7xxx_patch0_func, 567, 33, 1 }, + { aic7xxx_patch12_func, 600, 4, 1 }, + { aic7xxx_patch6_func, 604, 2, 1 }, + { aic7xxx_patch6_func, 607, 9, 1 }, }; diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c index 7e9fb2ab3..849ce76c9 100644 --- a/drivers/scsi/constants.c +++ b/drivers/scsi/constants.c @@ -410,11 +410,13 @@ static const char *snstext[] = { #endif /* Print sense information */ -void print_sense(const char * devclass, Scsi_Cmnd * SCpnt) +static +void print_sense_internal(const char * devclass, + const unsigned char * sense_buffer, + kdev_t dev) { int i, s; int sense_class, valid, code; - unsigned char * sense_buffer = SCpnt->sense_buffer; const char * error = NULL; sense_class = (sense_buffer[0] >> 4) & 0x07; @@ -423,8 +425,8 @@ void print_sense(const char * devclass, Scsi_Cmnd * SCpnt) if (sense_class == 7) { /* extended sense data */ s = sense_buffer[7] + 8; - if(s > sizeof(SCpnt->sense_buffer)) - s = sizeof(SCpnt->sense_buffer); + if(s > SCSI_SENSE_BUFFERSIZE) + s = SCSI_SENSE_BUFFERSIZE; if (!valid) printk("[valid=0] "); @@ -455,10 +457,10 @@ void print_sense(const char * devclass, Scsi_Cmnd * SCpnt) #if (CONSTANTS & CONST_SENSE) printk( "%s%s: sense key %s\n", devclass, - kdevname(SCpnt->request.rq_dev), snstext[sense_buffer[2] & 0x0f]); + kdevname(dev), snstext[sense_buffer[2] & 0x0f]); #else printk("%s%s: sns = %2x %2x\n", devclass, - kdevname(SCpnt->request.rq_dev), sense_buffer[0], sense_buffer[2]); + kdevname(dev), sense_buffer[0], sense_buffer[2]); #endif /* Check to see if additional sense information is available */ @@ -495,11 +497,11 @@ void print_sense(const char * devclass, Scsi_Cmnd * SCpnt) #if (CONSTANTS & CONST_SENSE) if (sense_buffer[0] < 15) printk("%s%s: old sense key %s\n", devclass, - kdevname(SCpnt->request.rq_dev), snstext[sense_buffer[0] & 0x0f]); + kdevname(dev), snstext[sense_buffer[0] & 0x0f]); else #endif printk("%s%s: sns = %2x %2x\n", devclass, - kdevname(SCpnt->request.rq_dev), sense_buffer[0], sense_buffer[2]); + kdevname(dev), sense_buffer[0], sense_buffer[2]); printk("Non-extended sense class %d code 0x%0x ", sense_class, code); s = 4; @@ -515,6 +517,18 @@ void print_sense(const char * devclass, Scsi_Cmnd * SCpnt) return; } +void print_sense(const char * devclass, Scsi_Cmnd * SCpnt) +{ + print_sense_internal(devclass, SCpnt->sense_buffer, + SCpnt->request.rq_dev); +} + +void print_req_sense(const char * devclass, Scsi_Request * SRpnt) +{ + print_sense_internal(devclass, SRpnt->sr_sense_buffer, + SRpnt->sr_request.rq_dev); +} + #if (CONSTANTS & CONST_MSG) static const char *one_byte_msgs[] = { /* 0x00 */ "Command Complete", NULL, "Save Pointers", diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c index b6a058c03..d87bf6d70 100644 --- a/drivers/scsi/eata.c +++ b/drivers/scsi/eata.c @@ -910,7 +910,7 @@ static inline int port_detect \ if (info.sign != EATA_SIGNATURE) return FALSE; if (DEV2H(info.data_len) < EATA_2_0A_SIZE) { - printk("%s: config structure size (%ld bytes) too short, detaching.\n", + printk("%s: config structure size (%d bytes) too short, detaching.\n", name, DEV2H(info.data_len)); return FALSE; } diff --git a/drivers/scsi/eata_dma_proc.c b/drivers/scsi/eata_dma_proc.c index dd6034561..725a70a4a 100644 --- a/drivers/scsi/eata_dma_proc.c +++ b/drivers/scsi/eata_dma_proc.c @@ -167,6 +167,7 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length, cmnd[9] = 0; scmd->cmd_len = 10; + scmd->sc_data_direction = DATA_READ; /* * Do the command and wait for it to finish. @@ -291,6 +292,7 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length, cmnd[9] = 0; scmd->cmd_len = 10; + scmd->sc_data_direction = SCSI_DATA_READ; /* * Do the command and wait for it to finish. diff --git a/drivers/scsi/esp.c b/drivers/scsi/esp.c index 61ce25c36..fccfb59c8 100644 --- a/drivers/scsi/esp.c +++ b/drivers/scsi/esp.c @@ -1,4 +1,4 @@ -/* $Id: esp.c,v 1.90 2000/01/28 13:42:56 jj Exp $ +/* $Id: esp.c,v 1.92 2000/02/18 13:49:58 davem Exp $ * esp.c: EnhancedScsiProcessor Sun SCSI driver code. * * Copyright (C) 1995, 1998 David S. Miller (davem@caip.rutgers.edu) @@ -1412,14 +1412,20 @@ static void esp_get_dmabufs(struct esp *esp, Scsi_Cmnd *sp) sp->SCp.this_residual = sp->request_bufflen; sp->SCp.buffer = (struct scatterlist *) sp->request_buffer; sp->SCp.buffers_residual = 0; - sp->SCp.have_data_in = sbus_map_single(esp->sdev, sp->SCp.buffer, - sp->SCp.this_residual); - sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.have_data_in); + if (sp->request_bufflen) { + sp->SCp.have_data_in = sbus_map_single(esp->sdev, sp->SCp.buffer, + sp->SCp.this_residual, + scsi_to_sbus_dma_dir(sp->sc_data_direction)); + sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.have_data_in); + } else { + sp->SCp.ptr = NULL; + } } else { sp->SCp.buffer = (struct scatterlist *) sp->buffer; sp->SCp.buffers_residual = sbus_map_sg(esp->sdev, sp->SCp.buffer, - sp->use_sg); + sp->use_sg, + scsi_to_sbus_dma_dir(sp->sc_data_direction)); sp->SCp.this_residual = sg_dma_len(sp->SCp.buffer); sp->SCp.ptr = (char *) ((unsigned long)sg_dma_address(sp->SCp.buffer)); } @@ -1427,12 +1433,14 @@ static void esp_get_dmabufs(struct esp *esp, Scsi_Cmnd *sp) static void esp_release_dmabufs(struct esp *esp, Scsi_Cmnd *sp) { - if (sp->use_sg == 0) { + if (sp->use_sg) { + sbus_unmap_sg(esp->sdev, sp->buffer, sp->use_sg, + scsi_to_sbus_dma_dir(sp->sc_data_direction)); + } else if (sp->request_bufflen) { sbus_unmap_single(esp->sdev, sp->SCp.have_data_in, - sp->request_bufflen); - } else { - sbus_unmap_sg(esp->sdev, sp->buffer, sp->use_sg); + sp->request_bufflen, + scsi_to_sbus_dma_dir(sp->sc_data_direction)); } } diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c index 979f1295c..59d9f2415 100644 --- a/drivers/scsi/fdomain.c +++ b/drivers/scsi/fdomain.c @@ -270,10 +270,6 @@ **************************************************************************/ -#ifdef PCMCIA -#define MODULE -#endif - #include <linux/module.h> #ifdef PCMCIA diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 500c19b09..321b783a5 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -681,6 +681,7 @@ static Scsi_Host_Template builtin_scsi_hosts[] = * MAX_SCSI_HOSTS here. */ +Scsi_Host_Name * scsi_host_no_list = NULL; struct Scsi_Host * scsi_hostlist = NULL; struct Scsi_Device_Template * scsi_devicelist = NULL; @@ -690,7 +691,8 @@ int next_scsi_host = 0; void scsi_unregister(struct Scsi_Host * sh){ struct Scsi_Host * shpnt; - + Scsi_Host_Name *shn; + if(scsi_hostlist == sh) scsi_hostlist = sh->next; else { @@ -698,6 +700,16 @@ scsi_unregister(struct Scsi_Host * sh){ while(shpnt->next != sh) shpnt = shpnt->next; shpnt->next = shpnt->next->next; } + + /* + * We have to unregister the host from the scsi_host_no_list as well. + * Decide by the host_no not by the name because most host drivers are + * able to handle more than one adapters from the same kind (or family). + */ + for ( shn=scsi_host_no_list; shn && (sh->host_no != shn->host_no); + shn=shn->next); + if (shn) shn->host_registered = 0; + /* else {} : This should not happen, we should panic here... */ /* If we are removing the last host registered, it is safe to reuse * its host number (this avoids "holes" at boot time) (DB) @@ -724,16 +736,50 @@ scsi_unregister(struct Scsi_Host * sh){ struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){ struct Scsi_Host * retval, *shpnt; + Scsi_Host_Name *shn, *shn2; + int new = 1; retval = (struct Scsi_Host *)kmalloc(sizeof(struct Scsi_Host) + j, (tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC); memset(retval, 0, sizeof(struct Scsi_Host) + j); + + /* trying to find a reserved entry (host_no) */ + for (shn = scsi_host_no_list;shn;shn = shn->next) + if (!(shn->host_registered) && shn->loaded_as_module && tpnt->proc_dir && + tpnt->proc_dir->name && !strncmp(tpnt->proc_dir->name, shn->name, strlen(tpnt->proc_dir->name))) { + new = 0; + retval->host_no = shn->host_no; + shn->host_registered = 1; + shn->loaded_as_module = scsi_loadable_module_flag; + break; + } atomic_set(&retval->host_active,0); retval->host_busy = 0; retval->host_failed = 0; if(j > 0xffff) panic("Too many extra bytes requested\n"); retval->extra_bytes = j; retval->loaded_as_module = scsi_loadable_module_flag; - retval->host_no = max_scsi_hosts++; /* never reuse host_no (DB) */ + if (new) { + int len = 0; + shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC); + if (tpnt->proc_dir) + len = strlen(tpnt->proc_dir->name); + shn->name = kmalloc(len+1, GFP_ATOMIC); + if (tpnt->proc_dir) + strncpy(shn->name, tpnt->proc_dir->name, len); + shn->name[len] = 0; + shn->host_no = max_scsi_hosts++; + shn->host_registered = 1; + shn->loaded_as_module = scsi_loadable_module_flag; + shn->next = NULL; + if (scsi_host_no_list) { + for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next) + ; + shn2->next = shn; + } + else + scsi_host_no_list = shn; + retval->host_no = shn->host_no; + } next_scsi_host++; retval->host_queue = NULL; init_waitqueue_head(&retval->host_wait); @@ -747,6 +793,14 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){ retval->max_id = 8; retval->max_lun = 8; + /* + * All drivers right now should be able to handle 12 byte commands. + * Every so often there are requests for 16 byte commands, but individual + * low-level drivers need to certify that they actually do something + * sensible with such commands. + */ + retval->max_cmd_len = 12; + retval->unique_id = 0; retval->io_port = 0; retval->hostt = tpnt; @@ -757,6 +811,7 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){ retval->host_blocked = FALSE; + retval->host_self_blocked = FALSE; #ifdef DEBUG printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j); diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h index 191e4469e..f1b82a5a1 100644 --- a/drivers/scsi/hosts.h +++ b/drivers/scsi/hosts.h @@ -362,6 +362,17 @@ struct Scsi_Host * initialized, as required. */ + /* + * The maximum length of SCSI commands that this host can accept. + * Probably 12 for most host adapters, but could be 16 for others. + * For drivers that don't set this field, a value of 12 is + * assumed. I am leaving this as a number rather than a bit + * because you never know what subsequent SCSI standards might do + * (i.e. could there be a 20 byte or a 24-byte command a few years + * down the road?). + */ + unsigned char max_cmd_len; + int this_id; int can_queue; short cmd_per_lun; @@ -379,6 +390,12 @@ struct Scsi_Host * Host has rejected a command because it was busy. */ unsigned host_blocked:1; + + /* + * Host has requested that no further requests come through for the + * time being. + */ + unsigned host_self_blocked:1; /* * Host uses correct SCSI ordering not PC ordering. The bit is @@ -413,6 +430,20 @@ struct Scsi_Host extern void scsi_free_host_dev(Scsi_Device * SDpnt); extern Scsi_Device * scsi_get_host_dev(struct Scsi_Host * SHpnt); +extern void scsi_unblock_requests(struct Scsi_Host * SHpnt); +extern void scsi_block_requests(struct Scsi_Host * SHpnt); +extern void scsi_report_bus_reset(struct Scsi_Host * SHpnt, int channel); + +typedef struct SHN + { + struct SHN * next; + char * name; + unsigned short host_no; + unsigned short host_registered; + unsigned loaded_as_module; + } Scsi_Host_Name; + +extern Scsi_Host_Name * scsi_host_no_list; extern struct Scsi_Host * scsi_hostlist; extern struct Scsi_Device_Template * scsi_devicelist; @@ -504,6 +535,15 @@ extern void scsi_unregister_module(int, void *); * tackle the character devices first, as there aren't any locking implications * in the block device layer. The block devices will require more work. */ +#ifndef CONFIG_SD_EXTRA_DEVS +#define CONFIG_SD_EXTRA_DEVS 2 +#endif +#ifndef CONFIG_ST_EXTRA_DEVS +#define CONFIG_ST_EXTRA_DEVS 2 +#endif +#ifndef CONFIG_SR_EXTRA_DEVS +#define CONFIG_SR_EXTRA_DEVS 2 +#endif #define SD_EXTRA_DEVS CONFIG_SD_EXTRA_DEVS #define ST_EXTRA_DEVS CONFIG_ST_EXTRA_DEVS #define SR_EXTRA_DEVS CONFIG_SR_EXTRA_DEVS diff --git a/drivers/scsi/mac_esp.c b/drivers/scsi/mac_esp.c index 036ffdf2c..99cfa7e8c 100644 --- a/drivers/scsi/mac_esp.c +++ b/drivers/scsi/mac_esp.c @@ -417,7 +417,9 @@ int mac_esp_detect(Scsi_Host_Template * tpnt) esp->irq = IRQ_MAC_SCSI; request_irq(IRQ_MAC_SCSI, esp_intr, 0, "Mac ESP SCSI", esp); +#if 0 /* conflicts with IOP ADB */ request_irq(IRQ_MAC_SCSIDRQ, fake_drq, 0, "Mac ESP DRQ", esp); +#endif if (macintosh_config->scsi_type == MAC_SCSI_QUADRA) { esp->cfreq = 16500000; @@ -429,8 +431,9 @@ int mac_esp_detect(Scsi_Host_Template * tpnt) } else { /* chipnum == 1 */ esp->irq = IRQ_MAC_SCSIDRQ; - +#if 0 /* conflicts with IOP ADB */ request_irq(IRQ_MAC_SCSIDRQ, esp_intr, 0, "Mac ESP SCSI 2", esp); +#endif esp->cfreq = 25000000; diff --git a/drivers/scsi/mac_scsi.c b/drivers/scsi/mac_scsi.c index 9e5bdf2b2..11a58c478 100644 --- a/drivers/scsi/mac_scsi.c +++ b/drivers/scsi/mac_scsi.c @@ -78,7 +78,6 @@ #define NDEBUG (NDEBUG_ABORT) #endif -#define USE_WRAPPER #define RESET_BOOT #define DRIVER_SETUP @@ -90,30 +89,9 @@ #undef DRIVER_SETUP #endif -/* - * Need to define this to make SCSI work on RBV machines; leave undefined - * to enable interrupts a bit more on other machines - * Changes method of SCSI interrupt disable from software mask to VIA IER! - * (don't know if that's essential) - * - * 990502 (jmt) - not needed (and won't work) on new irq architecture - */ -/* #define RBV_HACK */ - -#ifdef RBV_HACK -#define ENABLE_IRQ() mac_turnon_irq( IRQ_MAC_SCSI ); -#define DISABLE_IRQ() mac_turnoff_irq( IRQ_MAC_SCSI ); -#else #define ENABLE_IRQ() mac_enable_irq( IRQ_MAC_SCSI ); #define DISABLE_IRQ() mac_disable_irq( IRQ_MAC_SCSI ); -#endif - -#define mac_turnon_irq(x) mac_enable_irq(x) -#define mac_turnoff_irq(x) mac_disable_irq(x) -extern void via_scsi_clear(void); - -static void scsi_mac_intr(int irq, void *dummy, struct pt_regs *fp); #ifdef RESET_BOOT static void mac_scsi_reset_boot(struct Scsi_Host *instance); #endif @@ -253,7 +231,7 @@ int macscsi_detect(Scsi_Host_Template * tpnt) if (macintosh_config->scsi_type != MAC_SCSI_OLD) return( 0 ); - tpnt->proc_name = "Mac 5380 SCSI"; + tpnt->proc_name = "mac5380"; /* setup variables */ tpnt->can_queue = @@ -306,11 +284,7 @@ int macscsi_detect(Scsi_Host_Template * tpnt) ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0; if (instance->irq != IRQ_NONE) -#ifdef USE_WRAPPER - if (request_irq(instance->irq, scsi_mac_intr, IRQ_FLG_SLOW, "MacSCSI-5380", NULL)) { -#else - if (request_irq(instance->irq, macscsi_intr, IRQ_FLG_SLOW, "MacSCSI-5380", NULL)) { -#endif + if (request_irq(instance->irq, NCR5380_intr, IRQ_FLG_SLOW, "ncr5380", NCR5380_intr)) { printk("scsi%d: IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq); instance->irq = IRQ_NONE; @@ -337,7 +311,7 @@ int macscsi_detect(Scsi_Host_Template * tpnt) int macscsi_release (struct Scsi_Host *shpnt) { if (shpnt->irq != IRQ_NONE) - free_irq (shpnt->irq, NULL); + free_irq (shpnt->irq, NCR5380_intr); return 0; } @@ -362,7 +336,7 @@ static void mac_scsi_reset_boot(struct Scsi_Host *instance) printk( "Macintosh SCSI: resetting the SCSI bus..." ); /* switch off SCSI IRQ - catch an interrupt without IRQ bit set else */ - mac_turnoff_irq( IRQ_MAC_SCSI ); + mac_disable_irq(IRQ_MAC_SCSI); /* get in phase */ NCR5380_write( TARGET_COMMAND_REG, @@ -380,7 +354,7 @@ static void mac_scsi_reset_boot(struct Scsi_Host *instance) barrier(); /* switch on SCSI IRQ again */ - mac_turnon_irq( IRQ_MAC_SCSI ); + mac_enable_irq(IRQ_MAC_SCSI); printk( " done\n" ); } @@ -399,49 +373,6 @@ void restore_irq(struct pt_regs *regs) restore_flags(flags); } -#ifdef USE_WRAPPER -/* - * SCSI interrupt wrapper - just to make sure it's the proper irq, and - * that we leave the handler in a clean state - */ - -static void scsi_mac_intr (int irq, void *dev_id, struct pt_regs *fp) -{ -#ifndef RBV_HACK - unsigned long flags; -#endif - -#ifdef RBV_HACK - mac_turnoff_irq( IRQ_MAC_SCSI ); -#else - mac_disable_irq( IRQ_MAC_SCSI ); -#endif - - if ( irq == IRQ_MAC_SCSI ) { -#ifndef RBV_HACK - save_flags(flags); - restore_irq(fp); -#endif - NCR5380_intr (irq, dev_id, fp); -#ifndef RBV_HACK - restore_flags(flags); -#endif - } - - /* To be sure the int is not masked */ -#ifdef RBV_HACK - mac_turnon_irq( IRQ_MAC_SCSI ); -#else - mac_enable_irq( IRQ_MAC_SCSI ); -#endif - -#if 1 /* ??? 0 worked */ - /* Clear the IRQ */ - via_scsi_clear(); -#endif -} -#endif - /* * pseudo-DMA transfer functions, copied and modified from Russel King's * ARM 5380 driver (cumana_1) @@ -720,11 +651,7 @@ void scsi_mac_polled (void) printk("SCSI poll\n"); save_flags(flags); cli(); -#ifdef USE_WRAPPER - scsi_mac_intr(IRQ_MAC_SCSI, instance, NULL); -#else - macscsi_intr(IRQ_MAC_SCSI, instance, NULL); -#endif + NCR5380_intr(IRQ_MAC_SCSI, instance, NULL); restore_flags(flags); } #if 0 diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c index 0ede63b87..adafbe43e 100644 --- a/drivers/scsi/mesh.c +++ b/drivers/scsi/mesh.c @@ -253,7 +253,7 @@ mesh_detect(Scsi_Host_Template *tp) continue; } mesh_host->unique_id = nmeshes; -#if !defined(MODULE) && (defined(CONFIG_PMAC) || defined(CONFIG_ALL_PPC)) +#if !defined(MODULE) note_scsi_host(mesh, mesh_host); #endif @@ -305,9 +305,7 @@ mesh_detect(Scsi_Host_Template *tp) if (mesh_sync_period < minper) mesh_sync_period = minper; -#if defined(CONFIG_PMAC) || defined(CONFIG_ALL_PPC) feature_set(mesh, FEATURE_MESH_enable); -#endif mdelay(200); mesh_init(ms); diff --git a/drivers/scsi/mvme16x.c b/drivers/scsi/mvme16x.c index 71121b726..fce0871f1 100644 --- a/drivers/scsi/mvme16x.c +++ b/drivers/scsi/mvme16x.c @@ -40,7 +40,7 @@ int mvme16x_scsi_detect(Scsi_Host_Template *tpnt) if (called) return 0; - tpnt->proc_name = "MVME16x" + tpnt->proc_name = "MVME16x"; options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT; diff --git a/drivers/scsi/pci2000.c b/drivers/scsi/pci2000.c index 83efa5df6..e07417b7e 100644 --- a/drivers/scsi/pci2000.c +++ b/drivers/scsi/pci2000.c @@ -53,7 +53,15 @@ #include "pci2000.h" #include "psi_roy.h" -#include <linux/spinlock.h> +#if LINUX_VERSION_CODE >= LINUXVERSION(2,1,95) +#include <asm/spinlock.h> +#endif +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,93) +#include <linux/bios32.h> +#endif + +struct proc_dir_entry Proc_Scsi_Pci2000 = + { PROC_SCSI_PCI2000, 7, "pci2000", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; //#define DEBUG 1 @@ -120,6 +128,28 @@ static int WaitReady (PADAPTER2000 padapter) return TRUE; } /**************************************************************** + * Name: WaitReadyLong :LOCAL + * + * Description: Wait for controller ready. + * + * Parameters: padapter - Pointer adapter data structure. + * + * Returns: TRUE on not ready. + * + ****************************************************************/ +static int WaitReadyLong (PADAPTER2000 padapter) + { + ULONG z; + + for ( z = 0; z < (5000 * 4); z++ ) + { + if ( !inb_p (padapter->cmd) ) + return FALSE; + udelay (250); + }; + return TRUE; + } +/**************************************************************** * Name: OpDone :LOCAL * * Description: Clean up operation and issue done to caller. @@ -204,7 +234,7 @@ 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 + if ( WaitReadyLong (padapter) ) // wait for adapter ready return DID_TIME_OUT; return DID_OK; } @@ -232,13 +262,23 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) int pun; int bus; int z; +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,95) + int flags; +#else /* version >= v2.1.95 */ unsigned long flags; +#endif /* version >= v2.1.95 */ +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,95) + /* Disable interrupts, if they aren't already disabled. */ + save_flags (flags); + cli (); +#else /* version >= v2.1.95 */ /* * Disable interrupts, if they aren't already disabled and acquire * the I/O spinlock. */ spin_lock_irqsave (&io_request_lock, flags); +#endif /* version >= v2.1.95 */ DEB(printk ("\npci2000 recieved interrupt ")); for ( z = 0; z < NumAdapters; z++ ) // scan for interrupt to process @@ -327,12 +367,20 @@ irqProceed:; OpDone (SCpnt, DID_OK << 16); irq_return:; +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,95) + /* + * Restore the original flags which will enable interrupts + * if and only if they were enabled on entry. + */ + restore_flags (flags); +#else /* version >= v2.1.95 */ /* * Release the I/O spinlock and restore the original flags * which will enable interrupts if and only if they were * enabled on entry. */ spin_unlock_irqrestore (&io_request_lock, flags); +#endif /* version >= v2.1.95 */ } /**************************************************************** * Name: Pci2000_QueueCommand @@ -589,21 +637,37 @@ int Pci2000_Detect (Scsi_Host_Template *tpnt) PADAPTER2000 padapter; int z, zz; int setirq; +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) struct pci_dev *pdev = NULL; +#else + UCHAR pci_bus, pci_device_fn; +#endif +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) if ( !pci_present () ) +#else + if ( !pcibios_present () ) +#endif { printk ("pci2000: PCI BIOS not present\n"); return 0; } +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) while ( (pdev = pci_find_device (VENDOR_PSI, DEVICE_ROY_1, pdev)) != NULL ) +#else + while ( !pcibios_find_device (VENDOR_PSI, DEVICE_ROY_1, found, &pci_bus, &pci_device_fn) ) +#endif { pshost = scsi_register (tpnt, sizeof(ADAPTER2000)); padapter = HOSTDATA(pshost); - padapter->basePort = pdev->resource[1].start; - +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) + padapter->basePort = pdev->base_address[1] & 0xFFFE; +#else + pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &padapter->basePort); + padapter->basePort &= 0xFFFE; +#endif 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; @@ -620,7 +684,11 @@ int Pci2000_Detect (Scsi_Host_Template *tpnt) if ( WaitReady (padapter) ) goto unregister; +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) pshost->irq = pdev->irq; +#else + pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq); +#endif setirq = 1; padapter->irqOwned = 0; for ( z = 0; z < installed; z++ ) // scan for shared interrupts @@ -714,7 +782,11 @@ int Pci2000_Release (struct Scsi_Host *pshost) PADAPTER2000 padapter = HOSTDATA (pshost); if ( padapter->irqOwned ) +#if LINUX_VERSION_CODE < LINUXVERSION(1,3,70) + free_irq (pshost->irq); +#else /* version >= v1.3.70 */ free_irq (pshost->irq, padapter); +#endif /* version >= v1.3.70 */ release_region (pshost->io_port, pshost->n_io_port); scsi_unregister(pshost); return 0; @@ -760,4 +832,3 @@ Scsi_Host_Template driver_template = PCI2000; #include "scsi_module.c" #endif - diff --git a/drivers/scsi/pci2000.h b/drivers/scsi/pci2000.h index 9b80a489b..a3daa5f76 100644 --- a/drivers/scsi/pci2000.h +++ b/drivers/scsi/pci2000.h @@ -200,10 +200,13 @@ int Pci2000_BiosParam (Disk *disk, kdev_t dev, int geom[]); #define NULL 0 #endif +extern struct proc_dir_entry Proc_Scsi_Pci2000; + +#if LINUX_VERSION_CODE >= LINUXVERSION(2,1,75) #define PCI2000 { \ next: NULL, \ module: NULL, \ - proc_name: "pci2000", \ + proc_dir: &Proc_Scsi_Pci2000, \ proc_info: NULL, /* let's not bloat the kernel */ \ name: "PCI-2000 SCSI Intelligent Disk Controller",\ detect: Pci2000_Detect, \ @@ -229,4 +232,27 @@ int Pci2000_BiosParam (Disk *disk, kdev_t dev, int geom[]); use_clustering: DISABLE_CLUSTERING, \ use_new_eh_code: 0 \ } +#else +#define PCI2000 { NULL, NULL, \ + &Proc_Scsi_Pci2000,/* proc_dir_entry */ \ + NULL, \ + "PCI-2000 SCSI Intelligent Disk Controller",\ + Pci2000_Detect, \ + Pci2000_Release, \ + NULL, \ + Pci2000_Command, \ + Pci2000_QueueCommand, \ + Pci2000_Abort, \ + Pci2000_Reset, \ + NULL, \ + Pci2000_BiosParam, \ + 16, \ + -1, \ + 16, \ + 1, \ + 0, \ + 0, \ + DISABLE_CLUSTERING } +#endif + #endif diff --git a/drivers/scsi/pci2220i.c b/drivers/scsi/pci2220i.c index e50ba45dc..80f58cb8e 100644 --- a/drivers/scsi/pci2220i.c +++ b/drivers/scsi/pci2220i.c @@ -24,8 +24,15 @@ * Revisions 1.11 Mar-26-1999 * - Fixed spinlock and PCI configuration. * + * Revision 2.00 December-1-1999 + * - Added code for the PCI-2240I controller + * - Added code for ATAPI devices. + * - Double buffer for scatter/gather support + * ****************************************************************************/ +//#define DEBUG 1 + #include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> @@ -46,18 +53,22 @@ #include "scsi.h" #include "hosts.h" #include "pci2220i.h" +#include "psi_dale.h" -#include <linux/spinlock.h> +#if LINUX_VERSION_CODE >= LINUXVERSION(2,1,95) +#include <asm/spinlock.h> +#endif +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,93) +#include <linux/bios32.h> +#endif -#define PCI2220I_VERSION "1.11" -//#define READ_CMD IDE_COMMAND_READ -//#define WRITE_CMD IDE_COMMAND_WRITE -//#define MAX_BUS_MASTER_BLOCKS 1 // This is the maximum we can bus master +#define PCI2220I_VERSION "2.00" #define READ_CMD IDE_CMD_READ_MULTIPLE #define WRITE_CMD IDE_CMD_WRITE_MULTIPLE #define MAX_BUS_MASTER_BLOCKS SECTORSXFER // This is the maximum we can bus master -//#define DEBUG 1 +struct proc_dir_entry Proc_Scsi_Pci2220i = + { PROC_SCSI_PCI2220I, 8, "pci2220i", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; #ifdef DEBUG #define DEB(x) x @@ -72,10 +83,10 @@ typedef struct { - UCHAR device; // device code UCHAR byte6; // device select register image UCHAR spigot; // spigot number - UCHAR sparebyte; // placeholder + UCHAR spigots[2]; // RAID spigots + UCHAR deviceID[2]; // device ID codes USHORT sectors; // number of sectors per track USHORT heads; // number of heads USHORT cylinders; // number of cylinders for this device @@ -85,12 +96,17 @@ typedef struct ULONG lastsectorlba[2]; // last addressable sector on the drive USHORT raid; // RAID active flag USHORT mirrorRecon; - UCHAR hotRecon; + UCHAR reconOn; USHORT reconCount; + USHORT reconIsStarting; // indicate hot reconstruct is starting + UCHAR cmdDrqInt; // flag for command interrupt + UCHAR packet; // command packet size in bytes } OUR_DEVICE, *POUR_DEVICE; typedef struct { + USHORT bigD; // identity is a PCI-2240I if true, otherwise a PCI-2220I + USHORT atapi; // this interface is for ATAPI devices only 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 @@ -119,16 +135,21 @@ typedef struct USHORT timingPIO; // TRUE if PIO timing is active ULONG timingAddress; // address to use on adapter for current timing mode ULONG irqOwned; // owned IRQ or zero if shared - OUR_DEVICE device[DALE_MAXDRIVES]; - DISK_MIRROR *raidData[8]; + UCHAR numberOfDrives; // saved number of drives on this controller + UCHAR failRegister; // current inverted data in fail register + OUR_DEVICE device[BIGD_MAXDRIVES]; + DISK_MIRROR *raidData[BIGD_MAXDRIVES]; ULONG startSector; USHORT sectorCount; + ULONG readCount; + UCHAR *currentSgBuffer; + ULONG currentSgCount; + USHORT nextSg; UCHAR cmd; Scsi_Cmnd *SCpnt; - VOID *buffer; POUR_DEVICE pdev; // current device opearating on + USHORT devInReconIndex; USHORT expectingIRQ; - USHORT reconIsStarting; // indicate hot reconstruct is starting USHORT reconOn; // Hot reconstruct is to be done. USHORT reconPhase; // Hot reconstruct operation is in progress. ULONG reconSize; @@ -138,6 +159,9 @@ typedef struct struct timer_list reconTimer; struct timer_list timer; UCHAR *kBuffer; + UCHAR reqSense; + UCHAR atapiCdb[16]; + UCHAR atapiSpecial; } ADAPTER2220I, *PADAPTER2220I; #define HOSTDATA(host) ((PADAPTER2220I)&host->hostdata) @@ -152,12 +176,41 @@ typedef struct static struct Scsi_Host *PsiHost[MAXADAPTER] = {NULL,}; // One for each adapter static int NumAdapters = 0; +static int Installed = 0; static SETUP DaleSetup; -static DISK_MIRROR DiskMirror[2]; -static ULONG ModeArray[] = {DALE_DATA_MODE2, DALE_DATA_MODE3, DALE_DATA_MODE4, DALE_DATA_MODE4P}; +static DISK_MIRROR DiskMirror[BIGD_MAXDRIVES]; +static ULONG ModeArray[] = {DALE_DATA_MODE2, DALE_DATA_MODE3, DALE_DATA_MODE4, DALE_DATA_MODE5}; +static ULONG ModeArray2[] = {BIGD_DATA_MODE2, BIGD_DATA_MODE3, BIGD_DATA_MODE4, BIGD_DATA_MODE5}; static void ReconTimerExpiry (unsigned long data); +/******************************************************************************************************* + * Name: Alarm + * + * Description: Sound the for the given device + * + * Parameters: padapter - Pointer adapter data structure. + * device - Device number. + * + * Returns: Nothing. + * + ******************************************************************************************************/ +static void Alarm (PADAPTER2220I padapter, UCHAR device) + { + UCHAR zc; + + if ( padapter->bigD ) + { + zc = device | (FAIL_ANY | FAIL_AUDIBLE); + if ( padapter->failRegister & FAIL_ANY ) + zc |= FAIL_MULTIPLE; + + padapter->failRegister = zc; + outb_p (~zc, padapter->regFail); + } + else + outb_p (0x3C | (1 << device), padapter->regFail); // sound alarm and set fail light + } /**************************************************************** * Name: MuteAlarm :LOCAL * @@ -172,8 +225,16 @@ static void MuteAlarm (PADAPTER2220I padapter) { UCHAR old; - old = (inb_p (padapter->regStatSel) >> 3) | (inb_p (padapter->regStatSel) & 0x83); - outb_p (old | 0x40, padapter->regFail); + if ( padapter->bigD ) + { + padapter->failRegister &= ~FAIL_AUDIBLE; + outb_p (~padapter->failRegister, padapter->regFail); + } + else + { + old = (inb_p (padapter->regStatSel) >> 3) | (inb_p (padapter->regStatSel) & 0x83); + outb_p (old | 0x40, padapter->regFail); + } } /**************************************************************** * Name: WaitReady :LOCAL @@ -214,17 +275,17 @@ static int WaitReadyReset (PADAPTER2220I padapter) ULONG z; UCHAR status; - for ( z = 0; z < (250 * 4); z++ ) // wait up to 1/4 second + for ( z = 0; z < (125 * 16); z++ ) // wait up to 1/4 second { status = inb_p (padapter->regStatCmd); if ( (status & (IDE_STATUS_DRDY | IDE_STATUS_BUSY)) == IDE_STATUS_DRDY ) { - DEB (printk ("\nPCI2220I: Reset took %ld mSec to be ready", z / 4)); + DEB (printk ("\nPCI2220I: Reset took %ld mSec to be ready", z / 8)); return 0; } - udelay (250); + udelay (125); } - DEB (printk ("\nPCI2220I: Reset took more than 1 Second to come ready, Disk Failure")); + DEB (printk ("\nPCI2220I: Reset took more than 2 Seconds to come ready, Disk Failure")); return status; } /**************************************************************** @@ -252,6 +313,52 @@ static int WaitDrq (PADAPTER2220I padapter) return status; } /**************************************************************** + * Name: AtapiWaitReady :LOCAL + * + * Description: Wait for device busy and DRQ to be cleared. + * + * Parameters: padapter - Pointer adapter data structure. + * msec - Number of milliseconds to wait. + * + * Returns: TRUE if drive does not clear busy in time. + * + ****************************************************************/ +static int AtapiWaitReady (PADAPTER2220I padapter, int msec) + { + int z; + + for ( z = 0; z < (msec * 16); z++ ) + { + if ( !(inb_p (padapter->regStatCmd) & (IDE_STATUS_BUSY | IDE_STATUS_DRQ)) ) + return FALSE; + udelay (125); + } + return TRUE; + } +/**************************************************************** + * Name: AtapiWaitDrq :LOCAL + * + * Description: Wait for device ready for data transfer. + * + * Parameters: padapter - Pointer adapter data structure. + * msec - Number of milliseconds to wait. + * + * Returns: TRUE if drive does not assert DRQ in time. + * + ****************************************************************/ +static int AtapiWaitDrq (PADAPTER2220I padapter, int msec) + { + ULONG z; + + for ( z = 0; z < (msec * 16); z++ ) + { + if ( inb_p (padapter->regStatCmd) & IDE_STATUS_DRQ ) + return 0; + udelay (128); + } + return TRUE; + } +/**************************************************************** * Name: HardReset :LOCAL * * Description: Wait for device ready for data transfer. @@ -265,24 +372,113 @@ static int WaitDrq (PADAPTER2220I padapter) ****************************************************************/ static int HardReset (PADAPTER2220I padapter, POUR_DEVICE pdev, UCHAR spigot) { - SelectSpigot (padapter, spigot | 0x80); + DEB (printk ("\npci2220i:RESET spigot = %X devices = %d, %d", spigot, pdev->deviceID[0], pdev->deviceID[1])); + udelay (100000); // just wait 100 mSec to let drives flush + SelectSpigot (padapter, spigot | SEL_IRQ_OFF); outb_p (0x0E, padapter->regAltStat); // reset the suvivor udelay (100); // wait a little outb_p (0x08, padapter->regAltStat); // clear the reset udelay (100); - outb_p (0xA0, padapter->regLba24); //Specify drive - outb_p (pdev->byte6, padapter->regLba24); // select the drive + outb_p (0xA0, padapter->regLba24); // select the master drive if ( WaitReadyReset (padapter) ) + { + DEB (printk ("\npci2220i: master not ready after reset")); return TRUE; + } + outb_p (0xB0, padapter->regLba24); // try the slave drive + if ( (inb_p (padapter->regStatCmd) & (IDE_STATUS_DRDY | IDE_STATUS_BUSY)) == IDE_STATUS_DRDY ) + { + DEB (printk ("\nPCI2220I: initializing slave drive on spigot %X", spigot)); + outb_p (SECTORSXFER, padapter->regSectCount); + WriteCommand (padapter, IDE_CMD_SET_MULTIPLE); + if ( WaitReady (padapter) ) + { + DEB (printk ("\npci2220i: slave not ready after set multiple")); + return TRUE; + } + } + + outb_p (0xA0, padapter->regLba24); // select the drive outb_p (SECTORSXFER, padapter->regSectCount); WriteCommand (padapter, IDE_CMD_SET_MULTIPLE); if ( WaitReady (padapter) ) + { + DEB (printk ("\npci2220i: master not ready after set multiple")); return TRUE; + } return FALSE; } /**************************************************************** + * Name: AtapiReset :LOCAL + * + * Description: Wait for device ready for data transfer. + * + * Parameters: padapter - Pointer adapter data structure. + * pdev - Pointer to device. + * + * Returns: TRUE if drive does not come ready. + * + ****************************************************************/ +static int AtapiReset (PADAPTER2220I padapter, POUR_DEVICE pdev) + { + SelectSpigot (padapter, pdev->spigot); + AtapiDevice (padapter, pdev->byte6); + AtapiCountLo (padapter, 0); + AtapiCountHi (padapter, 0); + WriteCommand (padapter, IDE_COMMAND_ATAPI_RESET); + udelay (125); + if ( AtapiWaitReady (padapter, 1000) ) + return TRUE; + if ( inb_p (padapter->regStatCmd) || (inb_p (padapter->regLba8) != 0x14) || (inb_p (padapter->regLba16) != 0xEB) ) + return TRUE; + return FALSE; + } +/**************************************************************** + * Name: WalkScatGath :LOCAL + * + * Description: Transfer data to/from scatter/gather buffers. + * + * Parameters: padapter - Pointer adapter data structure. + * datain - TRUE if data read. + * length - Number of bytes to transfer. + * + * Returns: Nothing. + * + ****************************************************************/ +static void WalkScatGath (PADAPTER2220I padapter, UCHAR datain, ULONG length) + { + ULONG count; + UCHAR *buffer = padapter->kBuffer; + + while ( length ) + { + count = ( length > padapter->currentSgCount ) ? padapter->currentSgCount : length; + + if ( datain ) + memcpy (padapter->currentSgBuffer, buffer, count); + else + memcpy (buffer, padapter->currentSgBuffer, count); + + padapter->currentSgCount -= count; + if ( !padapter->currentSgCount ) + { + if ( padapter->nextSg < padapter->SCpnt->use_sg ) + { + padapter->currentSgBuffer = ((struct scatterlist *)padapter->SCpnt->request_buffer)[padapter->nextSg].address; + padapter->currentSgCount = ((struct scatterlist *)padapter->SCpnt->request_buffer)[padapter->nextSg].length; + padapter->nextSg++; + } + } + else + padapter->currentSgBuffer += count; + + length -= count; + buffer += count; + } + } +/**************************************************************** * Name: BusMaster :LOCAL * * Description: Do a bus master I/O. @@ -291,34 +487,84 @@ static int HardReset (PADAPTER2220I padapter, POUR_DEVICE pdev, UCHAR spigot) * datain - TRUE if data read. * irq - TRUE if bus master interrupt expected. * - * Returns: TRUE if drive does not assert DRQ in time. + * Returns: Nothing. * ****************************************************************/ static void BusMaster (PADAPTER2220I padapter, UCHAR datain, UCHAR irq) { ULONG zl; - outl (padapter->timingAddress, padapter->regDmaAddrLoc); - outl (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci); - zl = (padapter->sectorCount > MAX_BUS_MASTER_BLOCKS) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount; + zl = ( padapter->sectorCount > MAX_BUS_MASTER_BLOCKS ) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount; padapter->sectorCount -= zl; zl *= (ULONG)BYTES_PER_SECTOR; - padapter->buffer += zl; - outl (zl, padapter->regDmaCount); + if ( datain ) { - outb_p (8, padapter->regDmaDesc); // read operation - if ( irq && !padapter->sectorCount ) - outb_p (5, padapter->regDmaMode); // interrupt on + padapter->readCount = zl; + outb_p (8, padapter->regDmaDesc); // read operation + if ( padapter->bigD ) + { + if ( irq && !padapter->sectorCount ) + outb_p (0x0C, padapter->regDmaMode); // interrupt on + else + outb_p (0x08, padapter->regDmaMode); // no interrupt + } + else + { + if ( irq && !padapter->sectorCount ) + outb_p (0x05, padapter->regDmaMode); // interrupt on + else + outb_p (0x01, padapter->regDmaMode); // no interrupt + } + } + else + { + outb_p (0x00, padapter->regDmaDesc); // write operation + if ( padapter->bigD ) + outb_p (0x08, padapter->regDmaMode); // no interrupt else - outb_p (1, padapter->regDmaMode); // no interrupt + outb_p (0x01, padapter->regDmaMode); // no interrupt + WalkScatGath (padapter, FALSE, zl); + } + + outl (padapter->timingAddress, padapter->regDmaAddrLoc); + outl (virt_to_bus (padapter->kBuffer), padapter->regDmaAddrPci); + outl (zl, padapter->regDmaCount); + outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear + } +/**************************************************************** + * Name: AtapiBusMaster :LOCAL + * + * Description: Do a bus master I/O. + * + * Parameters: padapter - Pointer adapter data structure. + * datain - TRUE if data read. + * length - Number of bytes to transfer. + * + * Returns: Nothing. + * + ****************************************************************/ +static void AtapiBusMaster (PADAPTER2220I padapter, UCHAR datain, ULONG length) + { + outl (padapter->timingAddress, padapter->regDmaAddrLoc); + outl (virt_to_bus (padapter->kBuffer), padapter->regDmaAddrPci); + outl (length, padapter->regDmaCount); + if ( datain ) + { + if ( padapter->readCount ) + WalkScatGath (padapter, TRUE, padapter->readCount); + outb_p (0x08, padapter->regDmaDesc); // read operation + outb_p (0x08, padapter->regDmaMode); // no interrupt + padapter->readCount = length; } else { - outb_p (0, padapter->regDmaDesc); // write operation - outb_p (1, padapter->regDmaMode); // no interrupt + outb_p (0x00, padapter->regDmaDesc); // write operation + outb_p (0x08, padapter->regDmaMode); // no interrupt + if ( !padapter->atapiSpecial ) + WalkScatGath (padapter, FALSE, length); } - outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear + outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear } /**************************************************************** * Name: WriteData :LOCAL @@ -339,9 +585,9 @@ static int WriteData (PADAPTER2220I padapter) if ( padapter->timingPIO ) { zl = (padapter->sectorCount > MAX_BUS_MASTER_BLOCKS) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount; - outsw (padapter->regData, padapter->buffer, zl * (BYTES_PER_SECTOR / 2)); + WalkScatGath (padapter, FALSE, zl * BYTES_PER_SECTOR); + outsw (padapter->regData, padapter->kBuffer, zl * (BYTES_PER_SECTOR / 2)); padapter->sectorCount -= zl; - padapter->buffer += zl * BYTES_PER_SECTOR; } else BusMaster (padapter, 0, 0); @@ -355,31 +601,32 @@ static int WriteData (PADAPTER2220I padapter) * * Description: Write data to device. * - * Parameters: padapter - Pointer adapter data structure. + * Parameters: padapter - Pointer to adapter structure. + * pdev - Pointer to device structure * - * Returns: TRUE if drive does not assert DRQ in time. + * Returns: Index + 1 of drive not failed or zero for OK. * ****************************************************************/ -static int WriteDataBoth (PADAPTER2220I padapter) +static int WriteDataBoth (PADAPTER2220I padapter, POUR_DEVICE pdev) { ULONG zl; UCHAR status0, status1; - SelectSpigot (padapter, 1); + SelectSpigot (padapter, pdev->spigots[0]); status0 = WaitDrq (padapter); if ( !status0 ) { - SelectSpigot (padapter, 2); + SelectSpigot (padapter, pdev->spigots[1]); status1 = WaitDrq (padapter); if ( !status1 ) { - SelectSpigot (padapter, 3); + SelectSpigot (padapter, pdev->spigots[0] | pdev->spigots[1] | padapter->bigD); if ( padapter->timingPIO ) { zl = (padapter->sectorCount > MAX_BUS_MASTER_BLOCKS) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount; - outsw (padapter->regData, padapter->buffer, zl * (BYTES_PER_SECTOR / 2)); + WalkScatGath (padapter, FALSE, zl * BYTES_PER_SECTOR); + outsw (padapter->regData, padapter->kBuffer, zl * (BYTES_PER_SECTOR / 2)); padapter->sectorCount -= zl; - padapter->buffer += zl * BYTES_PER_SECTOR; } else BusMaster (padapter, 0, 0); @@ -388,8 +635,8 @@ static int WriteDataBoth (PADAPTER2220I padapter) } padapter->cmd = 0; // null out the command byte if ( status0 ) - return 1; - return 2; + return 2; + return 1; } /**************************************************************** * Name: IdeCmd :LOCAL @@ -406,7 +653,7 @@ static UCHAR IdeCmd (PADAPTER2220I padapter, POUR_DEVICE pdev) { UCHAR status; - SelectSpigot (padapter, pdev->spigot); // select the spigot + SelectSpigot (padapter, pdev->spigot | padapter->bigD); // select the spigot outb_p (pdev->byte6 | ((UCHAR *)(&padapter->startSector))[3], padapter->regLba24); // select the drive status = WaitReady (padapter); if ( !status ) @@ -429,26 +676,27 @@ static UCHAR IdeCmd (PADAPTER2220I padapter, POUR_DEVICE pdev) * Description: Process an IDE command to both drivers. * * Parameters: padapter - Pointer adapter data structure. + * pdev - Pointer to device structure * - * Returns: Zero if no error or spigot of error. + * Returns: Index + 1 of drive not failed or zero for OK. * ****************************************************************/ -static UCHAR IdeCmdBoth (PADAPTER2220I padapter) +static UCHAR IdeCmdBoth (PADAPTER2220I padapter, POUR_DEVICE pdev) { UCHAR status0; UCHAR status1; - SelectSpigot (padapter, 3); // select the spigots + SelectSpigot (padapter, pdev->spigots[0] | pdev->spigots[1]); // select the spigots outb_p (padapter->pdev->byte6 | ((UCHAR *)(&padapter->startSector))[3], padapter->regLba24);// select the drive - SelectSpigot (padapter, 1); + SelectSpigot (padapter, pdev->spigots[0]); status0 = WaitReady (padapter); if ( !status0 ) { - SelectSpigot (padapter, 2); + SelectSpigot (padapter, pdev->spigots[1]); status1 = WaitReady (padapter); if ( !status1 ) { - SelectSpigot (padapter, 3); + SelectSpigot (padapter, pdev->spigots[0] | pdev->spigots[1] | padapter->bigD); outb_p (padapter->sectorCount, padapter->regSectCount); outb_p (((UCHAR *)(&padapter->startSector))[0], padapter->regLba0); outb_p (((UCHAR *)(&padapter->startSector))[1], padapter->regLba8); @@ -460,8 +708,8 @@ static UCHAR IdeCmdBoth (PADAPTER2220I padapter) } padapter->cmd = 0; // null out the command byte if ( status0 ) - return 1; - return 2; + return 2; + return 1; } /**************************************************************** * Name: OpDone :LOCAL @@ -498,6 +746,7 @@ static void OpDone (PADAPTER2220I padapter, ULONG result) { padapter->cmd = 0; padapter->SCpnt = NULL; + padapter->pdev = NULL; SCpnt->result = result; SCpnt->scsi_done (SCpnt); if ( padapter->reconOn && !padapter->reconTimer.data ) @@ -524,8 +773,8 @@ static ULONG InlineIdentify (PADAPTER2220I padapter, UCHAR spigot, UCHAR device) { PIDENTIFY_DATA pid = (PIDENTIFY_DATA)padapter->kBuffer; - SelectSpigot (padapter, spigot | 0x80); // select the spigot - outb_p (device << 4, padapter->regLba24); // select the drive + SelectSpigot (padapter, spigot | SEL_IRQ_OFF); // select the spigot + outb_p ((device << 4) | 0xA0, padapter->regLba24); // select the drive if ( WaitReady (padapter) ) return 0; WriteCommand (padapter, IDE_COMMAND_IDENTIFY); @@ -535,6 +784,192 @@ static ULONG InlineIdentify (PADAPTER2220I padapter, UCHAR spigot, UCHAR device) return (pid->LBATotalSectors - 1); } /**************************************************************** + * Name: AtapiIdentify :LOCAL + * + * Description: Do an intline inquiry on a drive. + * + * Parameters: padapter - Pointer to host data block. + * pdev - Pointer to device table. + * + * Returns: TRUE on error. + * + ****************************************************************/ +static ULONG AtapiIdentify (PADAPTER2220I padapter, POUR_DEVICE pdev) + { + ATAPI_GENERAL_0 ag0; + USHORT zs; + int z; + + AtapiDevice (padapter, pdev->byte6); + WriteCommand (padapter, IDE_COMMAND_ATAPI_IDENTIFY); + if ( AtapiWaitDrq (padapter, 3000) ) + return TRUE; + + *(USHORT *)&ag0 = inw_p (padapter->regData); + for ( z = 0; z < 255; z++ ) + zs = inw_p (padapter->regData); + + if ( ag0.ProtocolType == 2 ) + { + if ( ag0.CmdDrqType == 1 ) + pdev->cmdDrqInt = TRUE; + switch ( ag0.CmdPacketSize ) + { + case 0: + pdev->packet = 6; + break; + case 1: + pdev->packet = 8; + break; + default: + pdev->packet = 6; + break; + } + return FALSE; + } + return TRUE; + } +/**************************************************************** + * Name: Atapi2Scsi + * + * Description: Convert ATAPI data to SCSI data. + * + * Parameters: padapter - Pointer adapter data structure. + * SCpnt - Pointer to SCSI command structure. + * + * Returns: Nothing. + * + ****************************************************************/ +void Atapi2Scsi (PADAPTER2220I padapter, Scsi_Cmnd *SCpnt) + { + UCHAR *buff = padapter->currentSgBuffer; + + switch ( SCpnt->cmnd[0] ) + { + case SCSIOP_MODE_SENSE: + buff[0] = padapter->kBuffer[1]; + buff[1] = padapter->kBuffer[2]; + buff[2] = padapter->kBuffer[3]; + buff[3] = padapter->kBuffer[7]; + memcpy (&buff[4], &padapter->kBuffer[8], padapter->atapiCdb[8] - 8); + break; + case SCSIOP_INQUIRY: + padapter->kBuffer[2] = 2; + memcpy (buff, padapter->kBuffer, padapter->currentSgCount); + break; + default: + if ( padapter->readCount ) + WalkScatGath (padapter, TRUE, padapter->readCount); + break; + } + } +/**************************************************************** + * Name: Scsi2Atapi + * + * Description: Convert SCSI packet command to Atapi packet command. + * + * Parameters: padapter - Pointer adapter data structure. + * SCpnt - Pointer to SCSI command structure. + * + * Returns: Nothing. + * + ****************************************************************/ +static void Scsi2Atapi (PADAPTER2220I padapter, Scsi_Cmnd *SCpnt) + { + UCHAR *cdb = SCpnt->cmnd; + UCHAR *buff = padapter->currentSgBuffer; + + switch (cdb[0]) + { + case SCSIOP_READ6: + padapter->atapiCdb[0] = SCSIOP_READ; + padapter->atapiCdb[1] = cdb[1] & 0xE0; + padapter->atapiCdb[3] = cdb[1] & 0x1F; + padapter->atapiCdb[4] = cdb[2]; + padapter->atapiCdb[5] = cdb[3]; + padapter->atapiCdb[8] = cdb[4]; + padapter->atapiCdb[9] = cdb[5]; + break; + case SCSIOP_WRITE6: + padapter->atapiCdb[0] = SCSIOP_WRITE; + padapter->atapiCdb[1] = cdb[1] & 0xE0; + padapter->atapiCdb[3] = cdb[1] & 0x1F; + padapter->atapiCdb[4] = cdb[2]; + padapter->atapiCdb[5] = cdb[3]; + padapter->atapiCdb[8] = cdb[4]; + padapter->atapiCdb[9] = cdb[5]; + break; + case SCSIOP_MODE_SENSE: + padapter->atapiCdb[0] = SCSIOP_MODE_SENSE10; + padapter->atapiCdb[2] = cdb[2]; + padapter->atapiCdb[8] = cdb[4] + 4; + break; + + case SCSIOP_MODE_SELECT: + padapter->atapiSpecial = TRUE; + padapter->atapiCdb[0] = SCSIOP_MODE_SELECT10; + padapter->atapiCdb[1] = cdb[1] | 0x10; + memcpy (padapter->kBuffer, buff, 4); + padapter->kBuffer[4] = padapter->kBuffer[5] = 0; + padapter->kBuffer[6] = padapter->kBuffer[7] = 0; + memcpy (&padapter->kBuffer[8], &buff[4], cdb[4] - 4); + padapter->atapiCdb[8] = cdb[4] + 4; + break; + } + } +/**************************************************************** + * Name: AtapiSendCdb + * + * Description: Send the CDB packet to the device. + * + * Parameters: padapter - Pointer adapter data structure. + * pdev - Pointer to device. + * cdb - Pointer to 16 byte SCSI cdb. + * + * Returns: Nothing. + * + ****************************************************************/ +static void AtapiSendCdb (PADAPTER2220I padapter, POUR_DEVICE pdev, CHAR *cdb) + { + DEB (printk ("\nPCI2242I: CDB: %X %X %X %X %X %X %X %X %X %X %X %X", cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9], cdb[10], cdb[11])); + outsw (padapter->regData, cdb, pdev->packet); + } +/**************************************************************** + * Name: AtapiRequestSense + * + * Description: Send the CDB packet to the device. + * + * Parameters: padapter - Pointer adapter data structure. + * pdev - Pointer to device. + * SCpnt - Pointer to SCSI command structure. + * pass - If true then this is the second pass to send cdb. + * + * Returns: TRUE on error. + * + ****************************************************************/ +static int AtapiRequestSense (PADAPTER2220I padapter, POUR_DEVICE pdev, Scsi_Cmnd *SCpnt, UCHAR pass) + { + UCHAR cdb[16] = {SCSIOP_REQUEST_SENSE,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0}; + + DEB (printk ("\nPCI2242I: AUTO REQUEST SENSE")); + cdb[4] = (UCHAR)(sizeof (SCpnt->sense_buffer)); + if ( !pass ) + { + padapter->reqSense = TRUE; + AtapiCountLo (padapter, cdb[4]); + AtapiCountHi (padapter, 0); + outb_p (0, padapter->regError); + WriteCommand (padapter, IDE_COMMAND_ATAPI_PACKET); + if ( pdev->cmdDrqInt ) + return FALSE; + + if ( AtapiWaitDrq (padapter, 500) ) + return TRUE; + } + AtapiSendCdb (padapter, pdev, cdb); + return FALSE; + } +/**************************************************************** * Name: InlineReadSignature :LOCAL * * Description: Do an inline read RAID sigature. @@ -549,10 +984,9 @@ static ULONG InlineIdentify (PADAPTER2220I padapter, UCHAR spigot, UCHAR device) static UCHAR InlineReadSignature (PADAPTER2220I padapter, POUR_DEVICE pdev, int index) { UCHAR status; - UCHAR spigot = 1 << index; ULONG zl = pdev->lastsectorlba[index]; - SelectSpigot (padapter, spigot | 0x80); // select the spigot without interrupts + SelectSpigot (padapter, pdev->spigots[index] | SEL_IRQ_OFF); // select the spigot without interrupts outb_p (pdev->byte6 | ((UCHAR *)&zl)[3], padapter->regLba24); status = WaitReady (padapter); if ( !status ) @@ -591,6 +1025,10 @@ static ULONG DecodeError (PADAPTER2220I padapter, UCHAR status) UCHAR error; padapter->expectingIRQ = 0; +#ifdef DEBUG + printk (" @@@@@@ status: %X @@@@@@@ ", status); + STOP_HERE(); +#endif if ( status & IDE_STATUS_WRITE_FAULT ) { return DID_PARITY << 16; @@ -678,25 +1116,33 @@ static int WriteSignature (PADAPTER2220I padapter, POUR_DEVICE pdev, UCHAR spigo ******************************************************************************************************/ static int InitFailover (PADAPTER2220I padapter, POUR_DEVICE pdev) { - UCHAR spigot; + UCHAR spigot; - DEB (printk ("\npci2220i: Initialize failover process - survivor = %d", padapter->survivor)); + DEB (printk ("\npci2220i: Initialize failover process - survivor = %d", pdev->deviceID[padapter->survivor])); pdev->raid = FALSE; //initializes system for non raid mode - pdev->hotRecon = 0; - padapter->reconOn = FALSE; - spigot = (padapter->survivor) ? 2 : 1; + pdev->reconOn = FALSE; + spigot = pdev->spigots[padapter->survivor]; if ( pdev->DiskMirror[padapter->survivor].status & UCBF_REBUILD ) - return (TRUE); + { + DEB (printk ("\n failed, is survivor")); + return (TRUE); + } if ( HardReset (padapter, pdev, spigot) ) + { + DEB (printk ("\n failed, reset")); return TRUE; + } - outb_p (0x3C | spigot, padapter->regFail); // sound alarm and set fail light + Alarm (padapter, pdev->deviceID[padapter->survivor ^ 1]); pdev->DiskMirror[padapter->survivor].status = UCBF_MIRRORED | UCBF_SURVIVOR; //clear present status if ( WriteSignature (padapter, pdev, spigot, padapter->survivor) ) + { + DEB (printk ("\n failed, write signature")); return TRUE; + } padapter->failinprog = TRUE; return FALSE; } @@ -716,13 +1162,23 @@ static void TimerExpiry (unsigned long data) POUR_DEVICE pdev = padapter->pdev; UCHAR status = IDE_STATUS_BUSY; UCHAR temp, temp1; +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,95) + int flags; +#else /* version >= v2.1.95 */ unsigned long flags; +#endif /* version >= v2.1.95 */ +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,95) + /* Disable interrupts, if they aren't already disabled. */ + save_flags (flags); + cli (); +#else /* version >= v2.1.95 */ /* * Disable interrupts, if they aren't already disabled and acquire * the I/O spinlock. */ spin_lock_irqsave (&io_request_lock, flags); +#endif /* version >= v2.1.95 */ DEB (printk ("\nPCI2220I: Timeout expired ")); if ( padapter->failinprog ) @@ -739,7 +1195,7 @@ static void TimerExpiry (unsigned long data) { case RECON_PHASE_MARKING: case RECON_PHASE_LAST: - padapter->survivor = (pdev->spigot ^ 3) >> 1; + padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 1 : 0; DEB (printk ("\npci2220i: FAILURE 1")); if ( InitFailover (padapter, pdev) ) OpDone (padapter, DID_ERROR << 16); @@ -750,7 +1206,7 @@ static void TimerExpiry (unsigned long data) goto timerExpiryDone; case RECON_PHASE_COPY: - padapter->survivor = (pdev->spigot) >> 1; + padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1; DEB (printk ("\npci2220i: FAILURE 2")); DEB (printk ("\n spig/stat = %X", inb_p (padapter->regStatSel)); if ( InitFailover (padapter, pdev) ) @@ -758,14 +1214,14 @@ static void TimerExpiry (unsigned long data) goto timerExpiryDone; case RECON_PHASE_UPDATE: - padapter->survivor = (pdev->spigot) >> 1; + padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1; DEB (printk ("\npci2220i: FAILURE 3"))); if ( InitFailover (padapter, pdev) ) OpDone (padapter, DID_ERROR << 16); goto timerExpiryDone; case RECON_PHASE_END: - padapter->survivor = (pdev->spigot) >> 1; + padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1; DEB (printk ("\npci2220i: FAILURE 4")); if ( InitFailover (padapter, pdev) ) OpDone (padapter, DID_ERROR << 16); @@ -784,17 +1240,21 @@ static void TimerExpiry (unsigned long data) if ( padapter->cmd == WRITE_CMD ) { DEB (printk ("in RAID write operation")); - if ( inb_p (padapter->regStatSel) & 1 ) + temp = ( pdev->spigot & (SEL_1 | SEL_2) ) ? SEL_1 : SEL_3; + if ( inb_p (padapter->regStatSel) & temp ) { - SelectSpigot (padapter, 0x81 ); // Masking the interrupt during spigot select + DEB (printk ("\npci2220i: Determined A OK")); + SelectSpigot (padapter, temp | SEL_IRQ_OFF); // Masking the interrupt during spigot select temp = inb_p (padapter->regStatCmd); } else temp = IDE_STATUS_BUSY; - if ( inb (padapter->regStatSel) & 2 ) + temp1 = ( pdev->spigot & (SEL_1 | SEL_2) ) ? SEL_2 : SEL_4; + if ( inb (padapter->regStatSel) & temp1 ) { - SelectSpigot (padapter, 0x82 ); // Masking the interrupt during spigot select + DEB (printk ("\npci2220i: Determined B OK")); + SelectSpigot (padapter, temp1 | SEL_IRQ_OFF); // Masking the interrupt during spigot select temp1 = inb_p (padapter->regStatCmd); } else @@ -802,6 +1262,7 @@ static void TimerExpiry (unsigned long data) if ( (temp & IDE_STATUS_BUSY) || (temp1 & IDE_STATUS_BUSY) ) { + DEB (printk ("\npci2220i: Status A: %X B: %X", temp & 0xFF, temp1 & 0xFF)); if ( (temp & IDE_STATUS_BUSY) && (temp1 & IDE_STATUS_BUSY) ) { status = temp; @@ -809,11 +1270,10 @@ static void TimerExpiry (unsigned long data) } else { - if (temp & IDE_STATUS_BUSY) + if ( temp & IDE_STATUS_BUSY ) padapter->survivor = 1; else padapter->survivor = 0; - DEB (printk ("\npci2220i: FAILURE 5")); if ( InitFailover (padapter, pdev) ) { status = inb_p (padapter->regStatCmd); @@ -826,7 +1286,7 @@ static void TimerExpiry (unsigned long data) else { DEB (printk ("in RAID read operation")); - padapter->survivor = (pdev->spigot ^ 3) >> 1; + padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1; DEB (printk ("\npci2220i: FAILURE 6")); if ( InitFailover (padapter, pdev) ) { @@ -847,12 +1307,20 @@ static void TimerExpiry (unsigned long data) OpDone (padapter, DecodeError (padapter, status)); timerExpiryDone:; +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,95) + /* + * Restore the original flags which will enable interrupts + * if and only if they were enabled on entry. + */ + restore_flags (flags); +#else /* version >= v2.1.95 */ /* * Release the I/O spinlock and restore the original flags * which will enable interrupts if and only if they were * enabled on entry. */ spin_unlock_irqrestore (&io_request_lock, flags); +#endif /* version >= v2.1.95 */ } /**************************************************************** * Name: SetReconstruct :LOCAL @@ -871,7 +1339,6 @@ static LONG SetReconstruct (POUR_DEVICE pdev, int index) pdev->DiskMirror[index ^ 1].status = UCBF_MIRRORED | UCBF_REBUILD; pdev->DiskMirror[index ^ 1].reconstructPoint = 0; // start the reconstruct pdev->reconCount = 1990; // mark target drive early - pdev->hotRecon = 1 >> index; return pdev->DiskMirror[index].reconstructPoint; } /**************************************************************** @@ -893,35 +1360,66 @@ static void ReconTimerExpiry (unsigned long data) USHORT minmode; ULONG zl; UCHAR zc; + USHORT z; +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,95) + int flags; +#else /* version >= v2.1.95 */ unsigned long flags; +#endif /* version >= v2.1.95 */ +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,95) + /* Disable interrupts, if they aren't already disabled. */ + save_flags (flags); + cli (); +#else /* version >= v2.1.95 */ /* * Disable interrupts, if they aren't already disabled and acquire * the I/O spinlock. */ spin_lock_irqsave (&io_request_lock, flags); +#endif /* version >= v2.1.95 */ padapter = (PADAPTER2220I)data; if ( padapter->SCpnt ) goto reconTimerExpiry; - pdev = padapter->device; - pid = (PIDENTIFY_DATA)padapter->kBuffer; padapter->reconTimer.data = 0; + for ( z = padapter->devInReconIndex + 1; z < BIGD_MAXDRIVES; z++ ) + { + if ( padapter->device[z].reconOn ) + break; + } + if ( z < BIGD_MAXDRIVES ) + pdev = &padapter->device[z]; + else + { + for ( z = 0; z < BIGD_MAXDRIVES; z++ ) + { + if ( padapter->device[z].reconOn ) + break; + } + if ( z < BIGD_MAXDRIVES ) + pdev = &padapter->device[z]; + else + { + padapter->reconOn = FALSE; + goto reconTimerExpiry; + } + } + + padapter->devInReconIndex = z; + pid = (PIDENTIFY_DATA)padapter->kBuffer; padapter->pdev = pdev; - if ( padapter->reconIsStarting ) + if ( pdev->reconIsStarting ) { - padapter->reconIsStarting = FALSE; - padapter->reconOn = FALSE; - pdev->hotRecon = FALSE; + pdev->reconIsStarting = FALSE; + pdev->reconOn = FALSE; - if ( (pdev->DiskMirror[0].signature == SIGNATURE) && (pdev->DiskMirror[1].signature == SIGNATURE) && + while ( (pdev->DiskMirror[0].signature == SIGNATURE) && (pdev->DiskMirror[1].signature == SIGNATURE) && (pdev->DiskMirror[0].pairIdentifier == (pdev->DiskMirror[1].pairIdentifier ^ 1)) ) { if ( (pdev->DiskMirror[0].status & UCBF_MATCHED) && (pdev->DiskMirror[1].status & UCBF_MATCHED) ) - { - goto reconTimerExpiry; - } + break;; if ( pdev->DiskMirror[0].status & UCBF_SURVIVOR ) // is first drive survivor? testsize = SetReconstruct (pdev, 0); @@ -932,33 +1430,38 @@ static void ReconTimerExpiry (unsigned long data) if ( (pdev->DiskMirror[0].status & UCBF_REBUILD) || (pdev->DiskMirror[1].status & UCBF_REBUILD) ) { if ( pdev->DiskMirror[0].status & UCBF_REBUILD ) - { - pdev->hotRecon = 1; pdev->mirrorRecon = 0; - } else - { - pdev->hotRecon = 2; pdev->mirrorRecon = 1; - } + pdev->reconOn = TRUE; } + break; } - if ( !pdev->hotRecon ) + if ( !pdev->reconOn ) goto reconTimerExpiry; - zc = ((inb_p (padapter->regStatSel) >> 3) | inb_p (padapter->regStatSel)) & 0x83; // mute the alarm - outb_p (zc | pdev->hotRecon | 0x40, padapter->regFail); + if ( padapter->bigD ) + { + padapter->failRegister = 0; + outb_p (~padapter->failRegister, padapter->regFail); + } + else + { + zc = ((inb_p (padapter->regStatSel) >> 3) | inb_p (padapter->regStatSel)) & 0x83; // mute the alarm + outb_p (0xFF, padapter->regFail); + } while ( 1 ) { - if ( HardReset (padapter, pdev, pdev->hotRecon) ) + DEB (printk ("\npci2220i: hard reset issue")); + if ( HardReset (padapter, pdev, pdev->spigots[pdev->mirrorRecon]) ) { DEB (printk ("\npci2220i: sub 1")); break; } - pdev->lastsectorlba[pdev->mirrorRecon] = InlineIdentify (padapter, pdev->hotRecon, 0); + pdev->lastsectorlba[pdev->mirrorRecon] = InlineIdentify (padapter, pdev->spigots[pdev->mirrorRecon], pdev->deviceID[pdev->mirrorRecon] & 1); if ( pdev->lastsectorlba[pdev->mirrorRecon] < testsize ) { @@ -1014,12 +1517,11 @@ static void ReconTimerExpiry (unsigned long data) break; } - if ( !padapter->reconOn ) + if ( !pdev->reconOn ) { - pdev->hotRecon = FALSE; padapter->survivor = pdev->mirrorRecon ^ 1; padapter->reconPhase = RECON_PHASE_FAILOVER; - DEB (printk ("\npci2220i: FAILURE 7")); + DEB (printk ("\npci2220i: FAILURE 7")); InitFailover (padapter, pdev); goto reconTimerExpiry; } @@ -1038,11 +1540,11 @@ static void ReconTimerExpiry (unsigned long data) if ( pdev->reconCount++ > 2000 ) { pdev->reconCount = 0; - if ( WriteSignature (padapter, pdev, pdev->hotRecon, pdev->mirrorRecon) ) + if ( WriteSignature (padapter, pdev, pdev->spigots[pdev->mirrorRecon], pdev->mirrorRecon) ) { padapter->survivor = pdev->mirrorRecon ^ 1; padapter->reconPhase = RECON_PHASE_FAILOVER; - DEB (printk ("\npci2220i: FAILURE 8")); + DEB (printk ("\npci2220i: FAILURE 8")); InitFailover (padapter, pdev); goto reconTimerExpiry; } @@ -1057,30 +1559,30 @@ static void ReconTimerExpiry (unsigned long data) if ( padapter->reconSize ) { - SelectSpigot (padapter, 3); // select the spigots - outb_p (pdev->byte6 | ((UCHAR *)(&zl))[3], padapter->regLba24);// select the drive + SelectSpigot (padapter, pdev->spigots[0] | pdev->spigots[1]); // select the spigots + outb_p (pdev->byte6 | ((UCHAR *)(&zl))[3], padapter->regLba24); // select the drive SelectSpigot (padapter, pdev->spigot); if ( WaitReady (padapter) ) goto reconTimerExpiry; - SelectSpigot (padapter, pdev->hotRecon); + SelectSpigot (padapter, pdev->spigots[pdev->mirrorRecon]); if ( WaitReady (padapter) ) { padapter->survivor = pdev->mirrorRecon ^ 1; padapter->reconPhase = RECON_PHASE_FAILOVER; - DEB (printk ("\npci2220i: FAILURE 9")); + DEB (printk ("\npci2220i: FAILURE 9")); InitFailover (padapter, pdev); goto reconTimerExpiry; } - SelectSpigot (padapter, 3); + SelectSpigot (padapter, pdev->spigots[0] | pdev->spigots[1]); outb_p (padapter->reconSize & 0xFF, padapter->regSectCount); outb_p (((UCHAR *)(&zl))[0], padapter->regLba0); outb_p (((UCHAR *)(&zl))[1], padapter->regLba8); outb_p (((UCHAR *)(&zl))[2], padapter->regLba16); padapter->expectingIRQ = TRUE; padapter->reconPhase = RECON_PHASE_READY; - SelectSpigot (padapter, pdev->hotRecon); + SelectSpigot (padapter, pdev->spigots[pdev->mirrorRecon]); WriteCommand (padapter, WRITE_CMD); StartTimer (padapter); SelectSpigot (padapter, pdev->spigot); @@ -1095,12 +1597,20 @@ static void ReconTimerExpiry (unsigned long data) padapter->reconPhase = RECON_PHASE_LAST; reconTimerExpiry:; +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,95) + /* + * Restore the original flags which will enable interrupts + * if and only if they were enabled on entry. + */ + restore_flags (flags); +#else /* version >= v2.1.95 */ /* * Release the I/O spinlock and restore the original flags * which will enable interrupts if and only if they were * enabled on entry. */ spin_unlock_irqrestore (&io_request_lock, flags); +#endif /* version >= v2.1.95 */ } /**************************************************************** * Name: Irq_Handler :LOCAL @@ -1122,15 +1632,28 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) Scsi_Cmnd *SCpnt; UCHAR status; UCHAR status1; + ATAPI_STATUS statusa; + ATAPI_REASON reasona; + ATAPI_ERROR errora; int z; ULONG zl; +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,95) + int flags; +#else /* version >= v2.1.95 */ unsigned long flags; +#endif /* version >= v2.1.95 */ +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,95) + /* Disable interrupts, if they aren't already disabled. */ + save_flags (flags); + cli (); +#else /* version >= v2.1.95 */ /* * Disable interrupts, if they aren't already disabled and acquire * the I/O spinlock. */ spin_lock_irqsave (&io_request_lock, flags); +#endif /* version >= v2.1.95 */ // DEB (printk ("\npci2220i recieved interrupt\n")); @@ -1155,7 +1678,70 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) padapter = HOSTDATA(shost); pdev = padapter->pdev; SCpnt = padapter->SCpnt; + outb_p (0x08, padapter->regDmaCmdStat); // cancel interrupt from DMA engine + if ( padapter->atapi && SCpnt ) + { + *(char *)&statusa = inb_p (padapter->regStatCmd); // read the device status + *(char *)&reasona = inb_p (padapter->regSectCount); // read the device interrupt reason + + if ( !statusa.bsy ) + { + if ( statusa.drq ) // test for transfer phase + { + if ( !reasona.cod ) // test for data phase + { + z = (ULONG)inb_p (padapter->regLba8) | (ULONG)(inb_p (padapter->regLba16) << 8); + if ( padapter->reqSense ) + insw (padapter->regData, SCpnt->sense_buffer, z / 2); + else + AtapiBusMaster (padapter, reasona.io, z); + goto irq_return; + } + if ( reasona.cod && !reasona.io ) // test for command packet phase + { + if ( padapter->reqSense ) + AtapiRequestSense (padapter, pdev, SCpnt, TRUE); + else + AtapiSendCdb (padapter, pdev, padapter->atapiCdb); + goto irq_return; + } + } + else + { + if ( reasona.io && statusa.drdy ) // test for status phase + { + Atapi2Scsi (padapter, SCpnt); + if ( statusa.check ) + { + *(UCHAR *)&errora = inb_p (padapter->regError); // read the device error + if ( errora.senseKey ) + { + if ( padapter->reqSense || AtapiRequestSense (padapter, pdev, SCpnt, FALSE) ) + OpDone (padapter, DID_ERROR << 16); + } + else + { + if ( errora.ili || errora.abort ) + OpDone (padapter, DID_ERROR << 16); + else + OpDone (padapter, DID_OK << 16); + } + } + else + if ( padapter->reqSense ) + { + DEB (printk ("PCI2242I: Sense codes - %X %X %X ", ((UCHAR *)SCpnt->sense_buffer)[0], ((UCHAR *)SCpnt->sense_buffer)[12], ((UCHAR *)SCpnt->sense_buffer)[13])); + OpDone (padapter, (DRIVER_SENSE << 24) | (DID_OK << 16) | 2); + } + else + OpDone (padapter, DID_OK << 16); + } + } + } + goto irq_return; + } + if ( !padapter->expectingIRQ || !(SCpnt || padapter->reconPhase) ) { DEB(printk ("\npci2220i Unsolicited interrupt\n")); @@ -1163,7 +1749,6 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) goto irq_return; } padapter->expectingIRQ = 0; - outb_p (0x08, padapter->regDmaCmdStat); // cancel interrupt from DMA engine if ( padapter->failinprog ) { @@ -1178,7 +1763,7 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) else { DEB (printk ("\npci2220i: restarting failed opertation.")); - pdev->spigot = (padapter->survivor) ? 2 : 1; + pdev->spigot = (padapter->survivor) ? pdev->spigots[1] : pdev->spigots[0]; del_timer (&padapter->timer); if ( padapter->reconPhase ) OpDone (padapter, DID_OK << 16); @@ -1200,15 +1785,15 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) { if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) ) { - padapter->survivor = (pdev->spigot ^ 3) >> 1; + padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 1 : 0; DEB (printk ("\npci2220i: FAILURE 10")); if ( InitFailover (padapter, pdev) ) OpDone (padapter, DecodeError (padapter, status)); goto irq_return; } - if ( WriteSignature (padapter, pdev, pdev->hotRecon, pdev->mirrorRecon) ) + if ( WriteSignature (padapter, pdev, pdev->spigots[pdev->mirrorRecon], pdev->mirrorRecon) ) { - padapter->survivor = (pdev->spigot) >> 1; + padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1; DEB (printk ("\npci2220i: FAILURE 11")); if ( InitFailover (padapter, pdev) ) OpDone (padapter, DecodeError (padapter, status)); @@ -1228,17 +1813,17 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) OpDone (padapter, DecodeError (padapter, status)); goto irq_return; } - SelectSpigot (padapter, pdev->hotRecon); + SelectSpigot (padapter, pdev->spigots[pdev->mirrorRecon]); if ( WaitDrq (padapter) ) { del_timer (&padapter->timer); - padapter->survivor = (pdev->spigot) >> 1; - DEB (printk ("\npci2220i: FAILURE 12")); + padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1; + DEB (printk ("\npci2220i: FAILURE 12")); if ( InitFailover (padapter, pdev) ) OpDone (padapter, DecodeError (padapter, status)); goto irq_return; } - SelectSpigot (padapter, pdev->spigot | 0x40); + SelectSpigot (padapter, pdev->spigot | SEL_COPY | padapter->bigD); padapter->reconPhase = RECON_PHASE_COPY; padapter->expectingIRQ = TRUE; if ( padapter->timingPIO ) @@ -1247,11 +1832,22 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) } else { - outl (padapter->timingAddress, padapter->regDmaAddrLoc); + if ( (padapter->timingMode > 3) ) + { + if ( padapter->bigD ) + outl (BIGD_DATA_MODE3, padapter->regDmaAddrLoc); + else + outl (DALE_DATA_MODE3, padapter->regDmaAddrLoc); + } + else + outl (padapter->timingAddress, padapter->regDmaAddrLoc); outl (virt_to_bus (padapter->kBuffer), padapter->regDmaAddrPci); outl (padapter->reconSize * BYTES_PER_SECTOR, padapter->regDmaCount); outb_p (8, padapter->regDmaDesc); // read operation - outb_p (1, padapter->regDmaMode); // no interrupt + if ( padapter->bigD ) + outb_p (8, padapter->regDmaMode); // no interrupt + else + outb_p (1, padapter->regDmaMode); // no interrupt outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear } goto irq_return; @@ -1260,13 +1856,14 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) pdev->DiskMirror[pdev->mirrorRecon].reconstructPoint += padapter->reconSize; case RECON_PHASE_UPDATE: - SelectSpigot (padapter, pdev->hotRecon | 0x80); + SelectSpigot (padapter, pdev->spigots[pdev->mirrorRecon] | SEL_IRQ_OFF); status = inb_p (padapter->regStatCmd); // read the device status del_timer (&padapter->timer); if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) ) { - padapter->survivor = (pdev->spigot) >> 1; + padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1; DEB (printk ("\npci2220i: FAILURE 13")); + DEB (printk ("\n status register = %X error = %X", status, inb_p (padapter->regError))); if ( InitFailover (padapter, pdev) ) OpDone (padapter, DecodeError (padapter, status)); goto irq_return; @@ -1279,14 +1876,29 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) del_timer (&padapter->timer); if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) ) { - padapter->survivor = (pdev->spigot) >> 1; - DEB (printk ("\npci2220i: FAILURE 14")); + padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 0 : 1; + DEB (printk ("\npci2220i: FAILURE 14")); if ( InitFailover (padapter, pdev) ) OpDone (padapter, DecodeError (padapter, status)); goto irq_return; } - padapter->reconOn = FALSE; - pdev->hotRecon = 0; + pdev->reconOn = 0; + if ( padapter->bigD ) + { + for ( z = 0; z < padapter->numberOfDrives; z++ ) + { + if ( padapter->device[z].DiskMirror[0].status & UCBF_SURVIVOR ) + { + Alarm (padapter, padapter->device[z].deviceID[0] ^ 2); + MuteAlarm (padapter); + } + if ( padapter->device[z].DiskMirror[1].status & UCBF_SURVIVOR ) + { + Alarm (padapter, padapter->device[z].deviceID[1] ^ 2); + MuteAlarm (padapter); + } + } + } OpDone (padapter, DID_OK << 16); goto irq_return; @@ -1305,9 +1917,9 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) { if ( pdev->raid ) { - padapter->survivor = (pdev->spigot ^ 3) >> 1; + padapter->survivor = ( pdev->spigot == pdev->spigots[0] ) ? 1 : 0; del_timer (&padapter->timer); - DEB (printk ("\npci2220i: FAILURE 15")); + DEB (printk ("\npci2220i: FAILURE 15")); if ( !InitFailover (padapter, pdev) ) goto irq_return; } @@ -1315,10 +1927,9 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) } if ( padapter->timingPIO ) { - zl = (padapter->sectorCount > MAX_BUS_MASTER_BLOCKS) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount; - insw (padapter->regData, padapter->buffer, zl * (BYTES_PER_SECTOR / 2)); - padapter->sectorCount -= zl; - padapter->buffer += zl * BYTES_PER_SECTOR; + insw (padapter->regData, padapter->kBuffer, padapter->readCount / 2); + padapter->sectorCount -= padapter->readCount / BYTES_PER_SECTOR; + WalkScatGath (padapter, TRUE, padapter->readCount); if ( !padapter->sectorCount ) { status = 0; @@ -1326,32 +1937,40 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) } } else + { + if ( padapter->readCount ) + WalkScatGath (padapter, TRUE, padapter->readCount); BusMaster (padapter, 1, 1); + } padapter->expectingIRQ = TRUE; goto irq_return; } + if ( padapter->readCount && !padapter->timingPIO ) + WalkScatGath (padapter, TRUE, padapter->readCount); status = 0; break; case WRITE_CMD: - SelectSpigot (padapter, pdev->spigot | 0x80); - status = inb_p (padapter->regStatCmd); // read the device status if ( pdev->raid ) { - SelectSpigot (padapter, (pdev->spigot ^ 3) | 0x80); - status1 = inb_p (padapter->regStatCmd); // read the device status + SelectSpigot (padapter, pdev->spigots[0] | SEL_IRQ_OFF); + status = inb_p (padapter->regStatCmd); // read the device status + SelectSpigot (padapter, pdev->spigots[1] | SEL_IRQ_OFF); + status1 = inb_p (padapter->regStatCmd); // read the device status } else + SelectSpigot (padapter, pdev->spigot | SEL_IRQ_OFF); + status = inb_p (padapter->regStatCmd); // read the device status status1 = 0; if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) ) { if ( pdev->raid && !(status1 & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT)) ) { - padapter->survivor = (pdev->spigot ^ 3) >> 1; + padapter->survivor = 1; del_timer (&padapter->timer); - SelectSpigot (padapter, pdev->spigot | 0x80); - DEB (printk ("\npci2220i: FAILURE 16 status = %X error = %X", status, inb_p (padapter->regError))); + SelectSpigot (padapter, pdev->spigot | SEL_IRQ_OFF); + DEB (printk ("\npci2220i: FAILURE 16 status = %X error = %X", status, inb_p (padapter->regError))); if ( !InitFailover (padapter, pdev) ) goto irq_return; } @@ -1361,9 +1980,9 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) { if ( status1 & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) ) { - padapter->survivor = pdev->spigot >> 1; + padapter->survivor = 0; del_timer (&padapter->timer); - DEB (printk ("\npci2220i: FAILURE 17 status = %X error = %X", status1, inb_p (padapter->regError))); + DEB (printk ("\npci2220i: FAILURE 17 status = %X error = %X", status1, inb_p (padapter->regError))); if ( !InitFailover (padapter, pdev) ) goto irq_return; status = status1; @@ -1371,15 +1990,15 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) } if ( padapter->sectorCount ) { - status = WriteDataBoth (padapter); + status = WriteDataBoth (padapter, pdev); if ( status ) { - padapter->survivor = (status ^ 3) >> 1; + padapter->survivor = status >> 1; del_timer (&padapter->timer); - DEB (printk ("\npci2220i: FAILURE 18")); + DEB (printk ("\npci2220i: FAILURE 18")); if ( !InitFailover (padapter, pdev) ) goto irq_return; - SelectSpigot (padapter, status | 0x80); + SelectSpigot (padapter, pdev->spigots[status] | SEL_IRQ_OFF); status = inb_p (padapter->regStatCmd); // read the device status break; } @@ -1391,7 +2010,7 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) } if ( padapter->sectorCount ) { - SelectSpigot (padapter, pdev->spigot); + SelectSpigot (padapter, pdev->spigot | padapter->bigD); status = WriteData (padapter); if ( status ) break; @@ -1458,12 +2077,20 @@ static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) OpDone (padapter, zl); irq_return:; +#if LINUX_VERSION_CODE < LINUXVERSION(2,1,95) + /* + * Restore the original flags which will enable interrupts + * if and only if they were enabled on entry. + */ + restore_flags (flags); +#else /* version >= v2.1.95 */ /* * Release the I/O spinlock and restore the original flags * which will enable interrupts if and only if they were * enabled on entry. */ spin_unlock_irqrestore (&io_request_lock, flags); +#endif /* version >= v2.1.95 */ } /**************************************************************** * Name: Pci2220i_QueueCommand @@ -1486,14 +2113,88 @@ int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) PDEVICE_RAID1 pdr; SCpnt->scsi_done = done; - padapter->buffer = SCpnt->request_buffer; padapter->SCpnt = SCpnt; // Save this command data + padapter->readCount = 0; + + if ( SCpnt->use_sg ) + { + padapter->currentSgBuffer = ((struct scatterlist *)SCpnt->request_buffer)[0].address; + padapter->currentSgCount = ((struct scatterlist *)SCpnt->request_buffer)[0].length; + } + else + { + padapter->currentSgBuffer = SCpnt->request_buffer; + padapter->currentSgCount = SCpnt->request_bufflen; + } + padapter->nextSg = 1; + if ( !done ) { printk("pci2220i_queuecommand: %02X: done can't be NULL\n", *cdb); return 0; } + if ( padapter->atapi ) + { + UCHAR zlo, zhi; + + DEB (printk ("\nPCI2242I: ID %d, LUN %d opcode %X ", SCpnt->target, SCpnt->lun, *cdb)); + padapter->pdev = pdev; + if ( !pdev->byte6 || SCpnt->lun ) + { + OpDone (padapter, DID_BAD_TARGET << 16); + return 0; + } + + padapter->atapiSpecial = FALSE; + padapter->reqSense = FALSE; + memset (padapter->atapiCdb, 0, 16); + SelectSpigot (padapter, pdev->spigot); // select the spigot + AtapiDevice (padapter, pdev->byte6); // select the drive + if ( AtapiWaitReady (padapter, 100) ) + { + OpDone (padapter, DID_NO_CONNECT << 16); + return 0; + } + + switch ( cdb[0] ) + { + case SCSIOP_MODE_SENSE: + case SCSIOP_MODE_SELECT: + Scsi2Atapi (padapter, SCpnt); + z = SCpnt->request_bufflen + 4; + break; + case SCSIOP_READ6: + case SCSIOP_WRITE6: + Scsi2Atapi (padapter, SCpnt); + z = SCpnt->request_bufflen; + break; + default: + memcpy (padapter->atapiCdb, cdb, SCpnt->cmd_len); + z = SCpnt->request_bufflen; + break; + } + if ( z > ATAPI_TRANSFER ) + z = ATAPI_TRANSFER; + zlo = (UCHAR)(z & 0xFF); + zhi = (UCHAR)(z >> 8); + + AtapiCountLo (padapter, zlo); + AtapiCountHi (padapter, zhi); + outb_p (0, padapter->regError); + WriteCommand (padapter, IDE_COMMAND_ATAPI_PACKET); + if ( pdev->cmdDrqInt ) + return 0; + + if ( AtapiWaitDrq (padapter, 500) ) + { + OpDone (padapter, DID_ERROR << 16); + return 0; + } + AtapiSendCdb (padapter, pdev, padapter->atapiCdb); + return 0; + } + if ( padapter->reconPhase ) return 0; if ( padapter->reconTimer.data ) @@ -1502,12 +2203,11 @@ int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) padapter->reconTimer.data = 0; } - if ( !pdev->device || SCpnt->lun ) + if ( (SCpnt->target >= padapter->numberOfDrives) || SCpnt->lun ) { OpDone (padapter, DID_BAD_TARGET << 16); return 0; } - switch ( *cdb ) { @@ -1518,7 +2218,15 @@ int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) switch ( cdb[3] ) { case MY_SCSI_REBUILD: - padapter->reconOn = padapter->reconIsStarting = TRUE; + for ( z = 0; z < padapter->numberOfDrives; z++ ) + { + pdev = &padapter->device[z]; + if ( ((pdev->DiskMirror[0].status & UCBF_SURVIVOR) && (pdev->DiskMirror[1].status & UCBF_MIRRORED)) || + ((pdev->DiskMirror[1].status & UCBF_SURVIVOR) && (pdev->DiskMirror[0].status & UCBF_MIRRORED)) ) + { + padapter->reconOn = pdev->reconOn = pdev->reconIsStarting = TRUE; + } + } OpDone (padapter, DID_OK << 16); break; case MY_SCSI_ALARMMUTE: @@ -1535,7 +2243,10 @@ int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) if ( padapter->raidData[z] ) { memcpy (&pdr->DiskRaid1, padapter->raidData[z], sizeof (DISK_MIRROR)); - pdr->TotalSectors = padapter->device[0].blocks; + if ( padapter->raidData[z]->reconstructPoint > padapter->raidData[z ^ 2]->reconstructPoint ) + pdr->TotalSectors = padapter->raidData[z]->reconstructPoint; + else + pdr->TotalSectors = padapter->raidData[z ^ 2]->reconstructPoint; } else memset (pdr, 0, sizeof (DEVICE_RAID1)); @@ -1598,6 +2309,7 @@ int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) while ( padapter->demoFail ) { + pdev = padapter->pdev = &padapter->device[0]; padapter->demoFail = FALSE; if ( !pdev->raid || (pdev->DiskMirror[0].status & UCBF_SURVIVOR) || @@ -1610,7 +2322,7 @@ int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) else padapter->survivor = 0; DEB (printk ("\npci2220i: FAILURE 19")); - if ( InitFailover (padapter, pdev ) ) + if ( InitFailover (padapter, pdev) ) break; return 0; } @@ -1618,14 +2330,14 @@ int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) StartTimer (padapter); if ( pdev->raid && (padapter->cmd == WRITE_CMD) ) { - rc = IdeCmdBoth (padapter); + rc = IdeCmdBoth (padapter, pdev); if ( !rc ) - rc = WriteDataBoth (padapter); + rc = WriteDataBoth (padapter, pdev); if ( rc ) { del_timer (&padapter->timer); padapter->expectingIRQ = 0; - padapter->survivor = (rc ^ 3) >> 1; + padapter->survivor = rc >> 1; DEB (printk ("\npci2220i: FAILURE 20")); if ( InitFailover (padapter, pdev) ) { @@ -1656,7 +2368,6 @@ int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) } return 0; } - static void internal_done(Scsi_Cmnd *SCpnt) { SCpnt->SCp.Status++; @@ -1692,25 +2403,192 @@ int Pci2220i_Command (Scsi_Cmnd *SCpnt) * Returns: Nothing. * ****************************************************************/ -VOID ReadFlash (PADAPTER2220I padapter, VOID *pdata, ULONG base, ULONG length) +static VOID ReadFlash (PADAPTER2220I padapter, VOID *pdata, ULONG base, ULONG length) { ULONG oldremap; UCHAR olddesc; ULONG z; UCHAR *pd = (UCHAR *)pdata; - oldremap = inl (padapter->regRemap); // save values to restore later + oldremap = inl (padapter->regRemap); // save values to restore later olddesc = inb_p (padapter->regDesc); - outl (base | 1, padapter->regRemap); // remap to Flash space as specified - outb_p (0x40, padapter->regDesc); // describe remap region as 8 bit - for ( z = 0; z < length; z++) // get "length" data count - *pd++ = inb_p (padapter->regBase + z); // read in the data + outl (base | 1, padapter->regRemap); // remap to Flash space as specified + outb_p (0x40, padapter->regDesc); // describe remap region as 8 bit + for ( z = 0; z < length; z++) // get "length" data count + *pd++ = inb_p (padapter->regBase + z); // read in the data - outl (oldremap, padapter->regRemap); // restore remap register values + outl (oldremap, padapter->regRemap); // restore remap register values outb_p (olddesc, padapter->regDesc); } /**************************************************************** + * Name: GetRegs + * + * Description: Initialize the regester information. + * + * Parameters: pshost - Pointer to SCSI host data structure. + * bigd - PCI-2240I identifier + * pcidev - Pointer to device data structure. + * pci_bus - PCI bus number. + * pci_device_fn - PCI device and function number. + * + * Returns: TRUE if failure to install. + * + ****************************************************************/ +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) +static USHORT GetRegs (struct Scsi_Host *pshost, BOOL bigd, struct pci_dev *pcidev) +#else +static USHORT GetRegs (struct Scsi_Host *pshost, BOOL bigd, UCHAR pci_bus, UCHAR pci_device_fn) +#endif + { + PADAPTER2220I padapter; + int setirq; + int z; + USHORT zr, zl; + + padapter = HOSTDATA(pshost); + memset (padapter, 0, sizeof (ADAPTER2220I)); + memset (&DaleSetup, 0, sizeof (DaleSetup)); + memset (DiskMirror, 0, sizeof (DiskMirror)); + +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) + zr = pcidev->base_address[1] & 0xFFFE; + zl = pcidev->base_address[2] & 0xFFFE; +#else + pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &zr); + zr &= 0xFFFE; + pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &zl); + zl &= 0xFFFE; +#endif + padapter->basePort = zr; + padapter->regRemap = zr + RTR_LOCAL_REMAP; // 32 bit local space remap + padapter->regDesc = zr + RTR_REGIONS; // 32 bit local region descriptor + padapter->regRange = zr + RTR_LOCAL_RANGE; // 32 bit local range + padapter->regIrqControl = zr + RTR_INT_CONTROL_STATUS; // 16 bit interupt control and status + padapter->regScratchPad = zr + RTR_MAILBOX; // 16 byte scratchpad I/O base address + + padapter->regBase = zl; + padapter->regData = zl + REG_DATA; // data register I/O address + padapter->regError = zl + REG_ERROR; // error register I/O address + padapter->regSectCount = zl + REG_SECTOR_COUNT; // sector count register I/O address + padapter->regLba0 = zl + REG_LBA_0; // least significant byte of LBA + padapter->regLba8 = zl + REG_LBA_8; // next least significant byte of LBA + padapter->regLba16 = zl + REG_LBA_16; // next most significan byte of LBA + padapter->regLba24 = zl + REG_LBA_24; // head and most 4 significant bits of LBA + padapter->regStatCmd = zl + REG_STAT_CMD; // status on read and command on write register + padapter->regStatSel = zl + REG_STAT_SEL; // board status on read and spigot select on write register + padapter->regFail = zl + REG_FAIL; + padapter->regAltStat = zl + REG_ALT_STAT; + + if ( bigd ) + { + padapter->regDmaDesc = zr + RTR_DMA0_DESC_PTR; // address of the DMA discriptor register for direction of transfer + padapter->regDmaCmdStat = zr + RTR_DMA_COMMAND_STATUS; // Byte #0 of DMA command status register + padapter->regDmaAddrPci = zr + RTR_DMA0_PCI_ADDR; // 32 bit register for PCI address of DMA + padapter->regDmaAddrLoc = zr + RTR_DMA0_LOCAL_ADDR; // 32 bit register for local bus address of DMA + padapter->regDmaCount = zr + RTR_DMA0_COUNT; // 32 bit register for DMA transfer count + padapter->regDmaMode = zr + RTR_DMA0_MODE + 1; // 32 bit register for DMA mode control + padapter->bigD = SEL_NEW_SPEED_1; // set spigot speed control bit + } + else + { + padapter->regDmaDesc = zl + RTL_DMA1_DESC_PTR; // address of the DMA discriptor register for direction of transfer + padapter->regDmaCmdStat = zl + RTL_DMA_COMMAND_STATUS + 1; // Byte #1 of DMA command status register + padapter->regDmaAddrPci = zl + RTL_DMA1_PCI_ADDR; // 32 bit register for PCI address of DMA + padapter->regDmaAddrLoc = zl + RTL_DMA1_LOCAL_ADDR; // 32 bit register for local bus address of DMA + padapter->regDmaCount = zl + RTL_DMA1_COUNT; // 32 bit register for DMA transfer count + padapter->regDmaMode = zl + RTL_DMA1_MODE + 1; // 32 bit register for DMA mode control + } + + padapter->numberOfDrives = inb_p (padapter->regScratchPad + BIGD_NUM_DRIVES); + if ( !bigd && !padapter->numberOfDrives ) // if no devices on this board + return TRUE; + +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) + pshost->irq = pcidev->irq; +#else + pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq); +#endif + setirq = 1; + for ( z = 0; z < Installed; 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, SA_SHIRQ, "pci2220i", padapter) < 0 ) + { + if ( request_irq (pshost->irq, Irq_Handler, SA_INTERRUPT | SA_SHIRQ, "pci2220i", padapter) < 0 ) + { + printk ("Unable to allocate IRQ for PCI-2220I controller.\n"); + return TRUE; + } + } + padapter->irqOwned = pshost->irq; // set IRQ as owned + } + if ( padapter->numberOfDrives ) + padapter->kBuffer = kmalloc (SECTORSXFER * BYTES_PER_SECTOR, GFP_DMA | GFP_ATOMIC); + else + padapter->kBuffer = kmalloc (ATAPI_TRANSFER, GFP_DMA | GFP_ATOMIC); + if ( !padapter->kBuffer ) + { + printk ("Unable to allocate DMA buffer for PCI-2220I controller.\n"); +#if LINUX_VERSION_CODE < LINUXVERSION(1,3,70) + free_irq (pshost->irq); +#else /* version >= v1.3.70 */ + free_irq (pshost->irq, padapter); +#endif /* version >= v1.3.70 */ + return TRUE; + } + + PsiHost[Installed] = pshost; // save SCSI_HOST pointer + + pshost->io_port = padapter->basePort; + pshost->n_io_port = 0xFF; + pshost->unique_id = padapter->regBase; + + outb_p (0x01, padapter->regRange); // fix our range register because other drivers want to tromp on it + + padapter->timingMode = inb_p (padapter->regScratchPad + DALE_TIMING_MODE); + if ( padapter->timingMode >= 2 ) + { + if ( bigd ) + padapter->timingAddress = ModeArray2[padapter->timingMode - 2]; + else + padapter->timingAddress = ModeArray[padapter->timingMode - 2]; + } + else + padapter->timingPIO = TRUE; + + ReadFlash (padapter, &DaleSetup, DALE_FLASH_SETUP, sizeof (SETUP)); + ReadFlash (padapter, &DiskMirror, DALE_FLASH_RAID, sizeof (DiskMirror)); + + return FALSE; + } +/**************************************************************** + * Name: SetupFinish + * + * Description: Complete the driver initialization process for a card + * + * Parameters: padapter - Pointer to SCSI host data structure. + * str - Pointer to board type string. + * + * Returns: Nothing. + * + ****************************************************************/ +VOID SetupFinish (PADAPTER2220I padapter, char *str, int irq) + { + init_timer (&padapter->timer); + padapter->timer.function = TimerExpiry; + padapter->timer.data = (unsigned long)padapter; + init_timer (&padapter->reconTimer); + padapter->reconTimer.function = ReconTimerExpiry; + padapter->reconTimer.data = (unsigned long)padapter; + printk("\nPCI-%sI EIDE CONTROLLER: at I/O = %X/%X IRQ = %d\n", str, padapter->basePort, padapter->regBase, irq); + printk("Version %s, Compiled %s %s\n\n", PCI2220I_VERSION, __DATE__, __TIME__); + } +/**************************************************************** * Name: Pci2220i_Detect * * Description: Detect and initialize our boards. @@ -1722,204 +2600,284 @@ VOID ReadFlash (PADAPTER2220I padapter, VOID *pdata, ULONG base, ULONG length) ****************************************************************/ int Pci2220i_Detect (Scsi_Host_Template *tpnt) { - int found = 0; - int installed = 0; struct Scsi_Host *pshost; PADAPTER2220I padapter; + POUR_DEVICE pdev; int unit; int z; - USHORT zs; - USHORT raidon = FALSE; - int setirq; - UCHAR spigot1 = FALSE; - UCHAR spigot2 = FALSE; - struct pci_dev *pdev = NULL; + USHORT raidon; + UCHAR spigot1, spigot2; + UCHAR device; +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) + struct pci_dev *pcidev = NULL; +#else + int found; + UCHAR pci_bus, pci_device_fn; +#endif +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) if ( !pci_present () ) +#else + if ( !pcibios_present () ) +#endif { printk ("pci2220i: PCI BIOS not present\n"); return 0; } - while ( (pdev = pci_find_device (VENDOR_PSI, DEVICE_DALE_1, pdev)) != NULL ) +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) + while ( (pcidev = pci_find_device (VENDOR_PSI, DEVICE_DALE_1, pcidev)) != NULL ) +#else + found = 0; + while ( !pcibios_find_device (VENDOR_PSI, DEVICE_DALE_1, found++, &pci_bus, &pci_device_fn) ) +#endif { pshost = scsi_register (tpnt, sizeof(ADAPTER2220I)); padapter = HOSTDATA(pshost); - memset (padapter, 0, sizeof (ADAPTER2220I)); - - zs = pdev->resource[1].start; - padapter->basePort = zs; - padapter->regRemap = zs + RTR_LOCAL_REMAP; // 32 bit local space remap - padapter->regDesc = zs + RTR_REGIONS; // 32 bit local region descriptor - padapter->regRange = zs + RTR_LOCAL_RANGE; // 32 bit local range - padapter->regIrqControl = zs + RTR_INT_CONTROL_STATUS; // 16 bit interupt control and status - padapter->regScratchPad = zs + RTR_MAILBOX; // 16 byte scratchpad I/O base address - - zs = pdev->resource[2].start; - padapter->regBase = zs; - padapter->regData = zs + REG_DATA; // data register I/O address - padapter->regError = zs + REG_ERROR; // error register I/O address - padapter->regSectCount = zs + REG_SECTOR_COUNT; // sector count register I/O address - padapter->regLba0 = zs + REG_LBA_0; // least significant byte of LBA - padapter->regLba8 = zs + REG_LBA_8; // next least significant byte of LBA - padapter->regLba16 = zs + REG_LBA_16; // next most significan byte of LBA - padapter->regLba24 = zs + REG_LBA_24; // head and most 4 significant bits of LBA - padapter->regStatCmd = zs + REG_STAT_CMD; // status on read and command on write register - padapter->regStatSel = zs + REG_STAT_SEL; // board status on read and spigot select on write register - padapter->regFail = zs + REG_FAIL; - padapter->regAltStat = zs + REG_ALT_STAT; - - padapter->regDmaDesc = zs + RTL_DMA1_DESC_PTR; // address of the DMA discriptor register for direction of transfer - padapter->regDmaCmdStat = zs + RTL_DMA_COMMAND_STATUS + 1; // Byte #1 of DMA command status register - padapter->regDmaAddrPci = zs + RTL_DMA1_PCI_ADDR; // 32 bit register for PCI address of DMA - padapter->regDmaAddrLoc = zs + RTL_DMA1_LOCAL_ADDR; // 32 bit register for local bus address of DMA - padapter->regDmaCount = zs + RTL_DMA1_COUNT; // 32 bit register for DMA transfer count - padapter->regDmaMode = zs + RTL_DMA1_MODE + 1; // 32 bit register for DMA mode control - - if ( !inb_p (padapter->regScratchPad + DALE_NUM_DRIVES) ) // if no devices on this board - goto unregister; - pshost->irq = pdev->irq; - setirq = 1; - for ( z = 0; z < installed; 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, SA_SHIRQ, "pci2220i", padapter) < 0 ) - { - if ( request_irq (pshost->irq, Irq_Handler, SA_INTERRUPT | SA_SHIRQ, "pci2220i", padapter) < 0 ) - { - printk ("Unable to allocate IRQ for PCI-2220I controller.\n"); - goto unregister; - } - } - padapter->irqOwned = pshost->irq; // set IRQ as owned - } - padapter->kBuffer = kmalloc (SECTORSXFER * BYTES_PER_SECTOR, GFP_DMA | GFP_ATOMIC); - if ( !padapter->kBuffer ) - { - printk ("Unable to allocate DMA buffer for PCI-2220I controller.\n"); - free_irq (pshost->irq, padapter); +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) + if ( GetRegs (pshost, FALSE, pcidev) ) +#else + if ( GetRegs (pshost, FALSE, pci_bus, pci_device_fn) ) +#endif goto unregister; - } - PsiHost[installed] = pshost; // save SCSI_HOST pointer - - pshost->io_port = padapter->basePort; - pshost->n_io_port = 0xFF; - pshost->unique_id = padapter->regBase; - pshost->max_id = 4; - - outb_p (0x01, padapter->regRange); // fix our range register because other drivers want to tromp on it - padapter->timingMode = inb_p (padapter->regScratchPad + DALE_TIMING_MODE); - if ( padapter->timingMode >= 2 ) - padapter->timingAddress = ModeArray[padapter->timingMode - 2]; - else - padapter->timingPIO = TRUE; - - ReadFlash (padapter, &DaleSetup, DALE_FLASH_SETUP, sizeof (SETUP)); - for ( z = 0; z < inb_p (padapter->regScratchPad + DALE_NUM_DRIVES); ++z ) + pshost->max_id = padapter->numberOfDrives; + for ( z = 0; z < padapter->numberOfDrives; z++ ) { unit = inb_p (padapter->regScratchPad + DALE_CHANNEL_DEVICE_0 + z) & 0x0F; - padapter->device[z].device = inb_p (padapter->regScratchPad + DALE_SCRATH_DEVICE_0 + unit); - padapter->device[z].byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0); - padapter->device[z].spigot = (UCHAR)(1 << (unit >> 1)); - padapter->device[z].sectors = DaleSetup.setupDevice[unit].sectors; - padapter->device[z].heads = DaleSetup.setupDevice[unit].heads; - padapter->device[z].cylinders = DaleSetup.setupDevice[unit].cylinders; - padapter->device[z].blocks = DaleSetup.setupDevice[unit].blocks; + pdev = &padapter->device[z]; + pdev->byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0); + pdev->spigot = (UCHAR)(1 << (unit >> 1)); + pdev->sectors = DaleSetup.setupDevice[unit].sectors; + pdev->heads = DaleSetup.setupDevice[unit].heads; + pdev->cylinders = DaleSetup.setupDevice[unit].cylinders; + pdev->blocks = DaleSetup.setupDevice[unit].blocks; if ( !z ) { - ReadFlash (padapter, &DiskMirror, DALE_FLASH_RAID, sizeof (DiskMirror)); DiskMirror[0].status = inb_p (padapter->regScratchPad + DALE_RAID_0_STATUS); DiskMirror[1].status = inb_p (padapter->regScratchPad + DALE_RAID_1_STATUS); if ( (DiskMirror[0].signature == SIGNATURE) && (DiskMirror[1].signature == SIGNATURE) && (DiskMirror[0].pairIdentifier == (DiskMirror[1].pairIdentifier ^ 1)) ) { raidon = TRUE; + if ( unit > (unit ^ 2) ) + unit = unit ^ 2; } + else + raidon = FALSE; - memcpy (padapter->device[z].DiskMirror, DiskMirror, sizeof (DiskMirror)); - padapter->raidData[0] = &padapter->device[z].DiskMirror[0]; - padapter->raidData[2] = &padapter->device[z].DiskMirror[1]; + memcpy (pdev->DiskMirror, DiskMirror, sizeof (DiskMirror)); + padapter->raidData[0] = &pdev->DiskMirror[0]; + padapter->raidData[2] = &pdev->DiskMirror[1]; - if ( raidon ) - { - padapter->device[z].lastsectorlba[0] = InlineIdentify (padapter, 1, 0); - padapter->device[z].lastsectorlba[1] = InlineIdentify (padapter, 2, 0); + spigot1 = spigot2 = FALSE; + pdev->spigots[0] = 1; + pdev->spigots[1] = 2; + pdev->lastsectorlba[0] = InlineIdentify (padapter, 1, 0); + pdev->lastsectorlba[1] = InlineIdentify (padapter, 2, 0); - if ( !(DiskMirror[1].status & UCBF_SURVIVOR) && padapter->device[z].lastsectorlba[0] ) - spigot1 = TRUE; - if ( !(DiskMirror[0].status & UCBF_SURVIVOR) && padapter->device[z].lastsectorlba[1] ) - spigot2 = TRUE; - if ( DiskMirror[0].status & UCBF_SURVIVOR & DiskMirror[1].status & UCBF_SURVIVOR ) - spigot1 = TRUE; - - if ( spigot1 && (DiskMirror[0].status & UCBF_REBUILD) ) - InlineReadSignature (padapter, &padapter->device[z], 0); - if ( spigot2 && (DiskMirror[1].status & UCBF_REBUILD) ) - InlineReadSignature (padapter, &padapter->device[z], 1); - - if ( spigot1 && spigot2 ) + if ( !(pdev->DiskMirror[1].status & UCBF_SURVIVOR) && pdev->lastsectorlba[0] ) + spigot1 = TRUE; + if ( !(pdev->DiskMirror[0].status & UCBF_SURVIVOR) && pdev->lastsectorlba[1] ) + spigot2 = TRUE; + if ( pdev->DiskMirror[0].status & DiskMirror[1].status & UCBF_SURVIVOR ) + spigot1 = TRUE; + + if ( spigot1 && (pdev->DiskMirror[0].status & UCBF_REBUILD) ) + InlineReadSignature (padapter, pdev, 0); + if ( spigot2 && (pdev->DiskMirror[1].status & UCBF_REBUILD) ) + InlineReadSignature (padapter, pdev, 1); + + if ( spigot1 && spigot2 && raidon ) + { + pdev->raid = 1; + if ( pdev->DiskMirror[0].status & UCBF_REBUILD ) + pdev->spigot = 2; + else + pdev->spigot = 1; + if ( (pdev->DiskMirror[0].status & UCBF_REBUILD) || (pdev->DiskMirror[1].status & UCBF_REBUILD) ) + padapter->reconOn = pdev->reconOn = pdev->reconIsStarting = TRUE; + } + else + { + if ( spigot1 ) { - padapter->device[z].raid = 1; - if ( DiskMirror[0].status & UCBF_REBUILD ) - padapter->device[z].spigot = 2; - else - padapter->device[z].spigot = 1; - if ( (DiskMirror[0].status & UCBF_REBUILD) || (DiskMirror[1].status & UCBF_REBUILD) ) - { - padapter->reconOn = padapter->reconIsStarting = TRUE; - } + if ( pdev->DiskMirror[0].status & UCBF_REBUILD ) + goto unregister; + pdev->DiskMirror[0].status = UCBF_MIRRORED | UCBF_SURVIVOR; + pdev->spigot = 1; } else { - if ( spigot1 ) - { - if ( DiskMirror[0].status & UCBF_REBUILD ) - goto unregister; - DiskMirror[0].status = UCBF_MIRRORED | UCBF_SURVIVOR; - padapter->device[z].spigot = 1; - } - else + if ( pdev->DiskMirror[1].status & UCBF_REBUILD ) + goto unregister; + pdev->DiskMirror[1].status = UCBF_MIRRORED | UCBF_SURVIVOR; + pdev->spigot = 2; + } + if ( DaleSetup.rebootRebuild && raidon ) + padapter->reconOn = pdev->reconOn = pdev->reconIsStarting = TRUE; + } + + if ( raidon ) + break; + } + } + + SetupFinish (padapter, "2220", pshost->irq); + + if ( ++Installed < MAXADAPTER ) + continue; + break;; +unregister:; + scsi_unregister (pshost); + } + +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) + while ( (pcidev = pci_find_device (VENDOR_PSI, DEVICE_BIGD_1, pcidev)) != NULL ) +#else + found = 0; + while ( !pcibios_find_device (VENDOR_PSI, DEVICE_BIGD_1, found++, &pci_bus, &pci_device_fn) ) +#endif + { + pshost = scsi_register (tpnt, sizeof(ADAPTER2220I)); + padapter = HOSTDATA(pshost); + +#if LINUX_VERSION_CODE > LINUXVERSION(2,1,92) + if ( GetRegs (pshost, TRUE, pcidev) ) +#else + if ( GetRegs (pshost, TRUE, pci_bus, pci_device_fn) ) +#endif + goto unregister1; + + for ( z = 0; z < BIGD_MAXDRIVES; z++ ) + DiskMirror[z].status = inb_p (padapter->regScratchPad + BIGD_RAID_0_STATUS + z); + + pshost->max_id = padapter->numberOfDrives; + padapter->failRegister = inb_p (padapter->regScratchPad + BIGD_ALARM_IMAGE); + for ( z = 0; z < padapter->numberOfDrives; z++ ) + { + unit = inb_p (padapter->regScratchPad + BIGD_DEVICE_0 + z); + pdev = &padapter->device[z]; + pdev->byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0); + pdev->spigot = (UCHAR)(1 << (unit >> 1)); + pdev->sectors = DaleSetup.setupDevice[unit].sectors; + pdev->heads = DaleSetup.setupDevice[unit].heads; + pdev->cylinders = DaleSetup.setupDevice[unit].cylinders; + pdev->blocks = DaleSetup.setupDevice[unit].blocks; + + if ( (DiskMirror[unit].signature == SIGNATURE) && (DiskMirror[unit ^ 2].signature == SIGNATURE) && + (DiskMirror[unit].pairIdentifier == (DiskMirror[unit ^ 2].pairIdentifier ^ 1)) ) + { + raidon = TRUE; + if ( unit > (unit ^ 2) ) + unit = unit ^ 2; + } + else + raidon = FALSE; + + spigot1 = spigot2 = FALSE; + memcpy (&pdev->DiskMirror[0], &DiskMirror[unit], sizeof (DISK_MIRROR)); + memcpy (&pdev->DiskMirror[1], &DiskMirror[unit ^ 2], sizeof (DISK_MIRROR)); + padapter->raidData[unit] = &pdev->DiskMirror[0]; + padapter->raidData[unit ^ 2] = &pdev->DiskMirror[1]; + pdev->spigots[0] = 1 << (unit >> 1); + pdev->spigots[1] = 1 << ((unit ^ 2) >> 1); + pdev->deviceID[0] = unit; + pdev->deviceID[1] = unit ^ 2; + pdev->lastsectorlba[0] = InlineIdentify (padapter, pdev->spigots[0], unit & 1); + pdev->lastsectorlba[1] = InlineIdentify (padapter, pdev->spigots[1], unit & 1); + + if ( !(pdev->DiskMirror[1].status & UCBF_SURVIVOR) && pdev->lastsectorlba[0] ) + spigot1 = TRUE; + if ( !(pdev->DiskMirror[0].status & UCBF_SURVIVOR) && pdev->lastsectorlba[1] ) + spigot2 = TRUE; + if ( pdev->DiskMirror[0].status & pdev->DiskMirror[1].status & UCBF_SURVIVOR ) + spigot1 = TRUE; + + if ( spigot1 && (pdev->DiskMirror[0].status & UCBF_REBUILD) ) + InlineReadSignature (padapter, pdev, 0); + if ( spigot2 && (pdev->DiskMirror[1].status & UCBF_REBUILD) ) + InlineReadSignature (padapter, pdev, 1); + + if ( spigot1 && spigot2 && raidon ) + { + pdev->raid = 1; + if ( pdev->DiskMirror[0].status & UCBF_REBUILD ) + pdev->spigot = pdev->spigots[1]; + else + pdev->spigot = pdev->spigots[0]; + if ( (pdev->DiskMirror[0].status & UCBF_REBUILD) || (pdev->DiskMirror[1].status & UCBF_REBUILD) ) + padapter->reconOn = pdev->reconOn = pdev->reconIsStarting = TRUE; + } + else + { + if ( spigot1 ) + { + if ( pdev->DiskMirror[0].status & UCBF_REBUILD ) + goto unregister1; + pdev->DiskMirror[0].status = UCBF_MIRRORED | UCBF_SURVIVOR; + pdev->spigot = pdev->spigots[0]; + } + else + { + if ( pdev->DiskMirror[1].status & UCBF_REBUILD ) + goto unregister; + pdev->DiskMirror[1].status = UCBF_MIRRORED | UCBF_SURVIVOR; + pdev->spigot = pdev->spigots[1]; + } + if ( DaleSetup.rebootRebuild && raidon ) + padapter->reconOn = pdev->reconOn = pdev->reconIsStarting = TRUE; + } + } + + if ( !padapter->numberOfDrives ) // If no ATA devices then scan ATAPI + { + unit = 0; + for ( spigot1 = 0; spigot1 < 4; spigot1++ ) + { + for ( device = 0; device < 2; device++ ) + { + DEB (printk ("\nPCI2242I: scanning for ID %d ", (spigot1 * 2) + device)); + pdev = &(padapter->device[(spigot1 * 2) + device]); + pdev->byte6 = 0x0A | (device << 4); + pdev->spigot = 1 << spigot1; + if ( !AtapiReset (padapter, pdev) ) + { + DEB (printk (" Device found ")); + if ( !AtapiIdentify (padapter, pdev) ) { - if ( DiskMirror[1].status & UCBF_REBUILD ) - goto unregister; - DiskMirror[1].status = UCBF_MIRRORED | UCBF_SURVIVOR; - padapter->device[z].spigot = 2; + DEB (printk (" Device verified")); + unit++; + continue; } - if ( DaleSetup.rebootRebuil ) - padapter->reconOn = padapter->reconIsStarting = TRUE; } - - break; + pdev->spigot = pdev->byte6 = 0; } } + + if ( unit ) + { + padapter->atapi = TRUE; + padapter->timingAddress = DALE_DATA_MODE3; + outw_p (0x0900, padapter->regIrqControl); // Turn our interrupts on + outw_p (0x0C41, padapter->regDmaMode - 1); // setup for 16 bits, ready enabled, done IRQ enabled, no incriment + outb_p (0xFF, padapter->regFail); // all fail lights and alarm off + pshost->max_id = 8; + } } - - init_timer (&padapter->timer); - padapter->timer.function = TimerExpiry; - padapter->timer.data = (unsigned long)padapter; - init_timer (&padapter->reconTimer); - padapter->reconTimer.function = ReconTimerExpiry; - padapter->reconTimer.data = (unsigned long)padapter; - printk("\nPCI-2220I EIDE CONTROLLER: at I/O = %X/%X IRQ = %d\n", padapter->basePort, padapter->regBase, pshost->irq); - printk("Version %s, Compiled %s %s\n\n", PCI2220I_VERSION, __DATE__, __TIME__); - found++; - if ( ++installed < MAXADAPTER ) + SetupFinish (padapter, "2240", pshost->irq); + + if ( ++Installed < MAXADAPTER ) continue; break;; -unregister:; +unregister1:; scsi_unregister (pshost); - found++; } - - NumAdapters = installed; - return installed; + + NumAdapters = Installed; + return Installed; } /**************************************************************** * Name: Pci2220i_Abort @@ -1933,6 +2891,19 @@ unregister:; ****************************************************************/ int Pci2220i_Abort (Scsi_Cmnd *SCpnt) { + PADAPTER2220I padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure + POUR_DEVICE pdev = &padapter->device[SCpnt->target];// Pointer to device information + + if ( !padapter->SCpnt ) + return SCSI_ABORT_NOT_RUNNING; + + if ( padapter->atapi ) + { + if ( AtapiReset (padapter, pdev) ) + return SCSI_ABORT_ERROR; + OpDone (padapter, DID_ABORT << 16); + return SCSI_ABORT_SUCCESS; + } return SCSI_ABORT_SNOOZE; } /**************************************************************** @@ -1952,6 +2923,15 @@ int Pci2220i_Abort (Scsi_Cmnd *SCpnt) ****************************************************************/ int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) { + PADAPTER2220I padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure + POUR_DEVICE pdev = &padapter->device[SCpnt->target];// Pointer to device information + + if ( padapter->atapi ) + { + if ( AtapiReset (padapter, pdev) ) + return SCSI_RESET_ERROR; + return SCSI_RESET_SUCCESS; + } return SCSI_RESET_PUNT; } /**************************************************************** @@ -1967,6 +2947,7 @@ int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) int Pci2220i_Release (struct Scsi_Host *pshost) { PADAPTER2220I padapter = HOSTDATA (pshost); + USHORT z; if ( padapter->reconOn ) { @@ -1981,11 +2962,29 @@ int Pci2220i_Release (struct Scsi_Host *pshost) } // save RAID status on the board - outb_p (DiskMirror[0].status, padapter->regScratchPad + DALE_RAID_0_STATUS); - outb_p (DiskMirror[1].status, padapter->regScratchPad + DALE_RAID_1_STATUS); + if ( padapter->bigD ) + { + outb_p (padapter->failRegister, padapter->regScratchPad + BIGD_ALARM_IMAGE); + for ( z = 0; z < BIGD_MAXDRIVES; z++ ) + { + if ( padapter->raidData ) + outb_p (padapter->raidData[z]->status, padapter->regScratchPad + BIGD_RAID_0_STATUS + z); + else + outb_p (0, padapter->regScratchPad + BIGD_RAID_0_STATUS); + } + } + else + { + outb_p (padapter->device[0].DiskMirror[0].status, padapter->regScratchPad + DALE_RAID_0_STATUS); + outb_p (padapter->device[0].DiskMirror[1].status, padapter->regScratchPad + DALE_RAID_1_STATUS); + } if ( padapter->irqOwned ) +#if LINUX_VERSION_CODE < LINUXVERSION(1,3,70) + free_irq (pshost->irq); +#else /* version >= v1.3.70 */ free_irq (pshost->irq, padapter); +#endif /* version >= v1.3.70 */ release_region (pshost->io_port, pshost->n_io_port); kfree (padapter->kBuffer); scsi_unregister(pshost); @@ -2011,11 +3010,14 @@ int Pci2220i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[]) { POUR_DEVICE pdev; - pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]); + if ( !(HOSTDATA(disk->device->host))->atapi ) + { + pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]); - geom[0] = pdev->heads; - geom[1] = pdev->sectors; - geom[2] = pdev->cylinders; + geom[0] = pdev->heads; + geom[1] = pdev->sectors; + geom[2] = pdev->cylinders; + } return 0; } diff --git a/drivers/scsi/pci2220i.h b/drivers/scsi/pci2220i.h index 1c75c8c3b..aaa8457fc 100644 --- a/drivers/scsi/pci2220i.h +++ b/drivers/scsi/pci2220i.h @@ -21,265 +21,11 @@ #ifndef _PCI2220I_H #define _PCI2220I_H -#ifndef PSI_EIDE_SCSIOP -#define PSI_EIDE_SCSIOP 1 - #ifndef LINUX_VERSION_CODE #include <linux/version.h> #endif #define LINUXVERSION(v,p,s) (((v)<<16) + ((p)<<8) + (s)) -/************************************************/ -/* 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 - -#include "psi_dale.h" - -/************************************************/ -/* Timeout konstants */ -/************************************************/ -#define TIMEOUT_READY 100 // 100 mSec -#define TIMEOUT_DRQ 300 // 300 mSec -#define TIMEOUT_DATA (3 * HZ) // 3 seconds - -/************************************************/ -/* 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])) ) - -#define SelectSpigot(padapter,spigot) outb_p (spigot, padapter->regStatSel) -#define WriteCommand(padapter,cmd) outb_p (cmd, padapter->regStatCmd) - -/************************************************/ -/* 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_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 - -// 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 -#pragma pack (1) -#pragma align 1 -typedef struct _IDENTIFY_DATA - { - USHORT GeneralConfiguration; // 0 - USHORT NumberOfCylinders; // 1 - USHORT Reserved1; // 2 - USHORT NumberOfHeads; // 3 - USHORT UnformattedBytesPerTrack; // 4 - USHORT UnformattedBytesPerSector; // 5 - USHORT SectorsPerTrack; // 6 - USHORT NumBytesISG; // 7 Byte Len - inter-sector gap - USHORT NumBytesSync; // 8 - sync field - USHORT NumWordsVUS; // 9 Len - Vendor Unique Info - USHORT SerialNumber[10]; // 10 - USHORT BufferType; // 20 - USHORT BufferSectorSize; // 21 - USHORT NumberOfEccBytes; // 22 - USHORT FirmwareRevision[4]; // 23 - USHORT ModelNumber[20]; // 27 - USHORT NumSectorsPerInt :8; // 47 Multiple Mode - Sec/Blk - USHORT Reserved2 :8; // 47 - USHORT DoubleWordMode; // 48 flag for double word mode capable - USHORT VendorUnique1 :8; // 49 - USHORT SupportDMA :1; // 49 DMA supported - USHORT SupportLBA :1; // 49 LBA supported - USHORT SupportIORDYDisable :1; // 49 IORDY can be disabled - USHORT SupportIORDY :1; // 49 IORDY supported - USHORT ReservedPseudoDMA :1; // 49 reserved for pseudo DMA mode support - USHORT Reserved3 :3; // 49 - USHORT Reserved4; // 50 - USHORT Reserved5 :8; // 51 Transfer Cycle Timing - PIO - USHORT PIOCycleTime :8; // 51 Transfer Cycle Timing - PIO - USHORT Reserved6 :8; // 52 - DMA - USHORT DMACycleTime :8; // 52 - DMA - USHORT Valid_54_58 :1; // 53 words 54 - 58 are vaild - USHORT Valid_64_70 :1; // 53 words 64 - 70 are valid - USHORT Reserved7 :14; // 53 - USHORT LogNumCyl; // 54 Current Translation - Num Cyl - USHORT LogNumHeads; // 55 Num Heads - USHORT LogSectorsPerTrack; // 56 Sec/Trk - ULONG LogTotalSectors; // 57 Total Sec - USHORT CurrentNumSecPerInt :8; // 59 current setting for number of sectors per interrupt - USHORT ValidNumSecPerInt :1; // 59 Current setting is valid for number of sectors per interrupt - USHORT Reserved8 :7; // 59 - ULONG LBATotalSectors; // 60 LBA Mode - Sectors - USHORT DMASWordFlags; // 62 - USHORT DMAMWordFlags; // 63 - USHORT AdvancedPIOSupport :8; // 64 Flow control PIO transfer modes supported - USHORT Reserved9 :8; // 64 - USHORT MinMultiDMACycle; // 65 minimum multiword DMA transfer cycle time per word - USHORT RecomendDMACycle; // 66 Manufacturer's recommende multiword DMA transfer cycle time - USHORT MinPIOCycleWithoutFlow; // 67 Minimum PIO transfer cycle time without flow control - USHORT MinPIOCylceWithFlow; // 68 Minimum PIO transfer cycle time with IORDY flow control - USHORT ReservedSpace[256-69]; // 69 - } IDENTIFY_DATA, *PIDENTIFY_DATA; -#pragma pack () -#pragma align 0 -#endif // PSI_EIDE_SCSIOP - // function prototypes int Pci2220i_Detect (Scsi_Host_Template *tpnt); int Pci2220i_Command (Scsi_Cmnd *SCpnt); @@ -293,12 +39,15 @@ int Pci2220i_BiosParam (Disk *disk, kdev_t dev, int geom[]); #define NULL 0 #endif +extern struct proc_dir_entry Proc_Scsi_Pci2220i; + +#if LINUX_VERSION_CODE >= LINUXVERSION(2,1,75) #define PCI2220I { \ next: NULL, \ module: NULL, \ - proc_name: "pci2220i", \ + proc_dir: &Proc_Scsi_Pci2220i, \ proc_info: NULL, /* let's not bloat the kernel */\ - name: "PCI-2220I EIDE Disk Controller", \ + name: "PCI-2220I/PCI-2240I", \ detect: Pci2220i_Detect, \ release: Pci2220i_Release, \ info: NULL, /* let's not bloat the kernel */\ @@ -315,11 +64,34 @@ int Pci2220i_BiosParam (Disk *disk, kdev_t dev, int geom[]); bios_param: Pci2220i_BiosParam, \ can_queue: 1, \ this_id: -1, \ - sg_tablesize: SG_NONE, \ + sg_tablesize: SG_ALL, \ cmd_per_lun: 1, \ present: 0, \ unchecked_isa_dma: 0, \ use_clustering: DISABLE_CLUSTERING, \ use_new_eh_code: 0 \ } +#else +#define PCI2220I { NULL, NULL, \ + &Proc_Scsi_Pci2220i,/* proc_dir_entry */\ + NULL, \ + "PCI-2220I/PCI-2240I", \ + Pci2220i_Detect, \ + Pci2220i_Release, \ + NULL, \ + Pci2220i_Command, \ + Pci2220i_QueueCommand, \ + Pci2220i_Abort, \ + Pci2220i_Reset, \ + NULL, \ + Pci2220i_BiosParam, \ + 1, \ + -1, \ + SG_ALL, \ + 1, \ + 0, \ + 0, \ + DISABLE_CLUSTERING } +#endif + #endif diff --git a/drivers/scsi/pcmcia/.cvsignore b/drivers/scsi/pcmcia/.cvsignore new file mode 100644 index 000000000..f7cf9ab27 --- /dev/null +++ b/drivers/scsi/pcmcia/.cvsignore @@ -0,0 +1,10 @@ +! RCS SCCS CVS CVS.adm +RCSLOG cvslog.* +tags TAGS +.make.state .nse_depinfo +*~ #* .#* ,* _$* *$ +*.old *.bak *.BAK *.orig *.rej .del-* +*.a *.olb *.o *.obj *.so *.exe +*.Z *.elc *.ln +.depend +.*.flags diff --git a/drivers/scsi/pcmcia/Config.in b/drivers/scsi/pcmcia/Config.in new file mode 100644 index 000000000..5416bdef2 --- /dev/null +++ b/drivers/scsi/pcmcia/Config.in @@ -0,0 +1,23 @@ +# +# PCMCIA SCSI adapter configuration +# + +mainmenu_option next_comment +comment 'PCMCIA SCSI adapter support' + +bool 'PCMCIA SCSI adapter support' CONFIG_SCSI_PCMCIA +if [ "$CONFIG_SCSI_PCMCIA" = "y" ]; then + dep_tristate ' Adaptec AHA152X PCMCIA support' CONFIG_PCMCIA_AHA152X m + dep_tristate ' Qlogic PCMCIA support' CONFIG_PCMCIA_QLOGIC m + dep_tristate ' Future Domain PCMCIA support' CONFIG_PCMCIA_FDOMAIN m + if [ "$CONFIG_CARDBUS" = "y" ]; then + dep_tristate ' Adaptec APA1480 CardBus support' CONFIG_PCMCIA_APA1480 m + fi +fi + +if [ "$CONFIG_PCMCIA_QLOGIC" = "y" -o "$CONFIG_PCMCIA_AHA152X" = "y" -o \ + "$CONFIG_PCMCIA_FDOMAIN" = "y" -o "$CONFIG_PCMCIA_APA1480" = "y" ]; then + define_bool CONFIG_PCMCIA_SCSICARD y +fi + +endmenu diff --git a/drivers/scsi/pcmcia/Makefile b/drivers/scsi/pcmcia/Makefile new file mode 100644 index 000000000..d37c6fc5f --- /dev/null +++ b/drivers/scsi/pcmcia/Makefile @@ -0,0 +1,67 @@ +# +# drivers/scsi/pcmcia/Makefile +# +# Makefile for the Linux PCMCIA SCSI drivers. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +MOD_LIST_NAME := PCMCIA_SCSI_MODULES + +obj-y := +obj-m := +obj-n := +obj- := + +vpath %c .. + +CFLAGS_aha152x.o = -DPCMCIA -D__NO_VERSION__ -DAHA152X_STAT +CFLAGS_aic7xxx.o = -DPCMCIA -D__NO_VERSION__ +CFLAGS_fdomain.o = -DPCMCIA -D__NO_VERSION__ +CFLAGS_qlogicfas.o = -DPCMCIA -D__NO_VERSION__ + +# 16-bit client drivers +obj-$(CONFIG_PCMCIA_QLOGIC) += qlogic_cs.o +obj-$(CONFIG_PCMCIA_FDOMAIN) += fdomain_cs.o +obj-$(CONFIG_PCMCIA_AHA152X) += aha152x_cs.o + +# Cardbus client drivers +obj-$(CONFIG_PCMCIA_APA1480) += apa1480_cb.o + +list-multi := qlogic_cs.o fdomain_cs.o aha152x_cs.o apa1480_cb.o +aha152x-objs := aha152x_stub.o aha152x.o +apa1480-objs := apa1480_stub.o aic7xxx.o +fdomain-objs := fdomain_stub.o fdomain.o +qlogic-objs := qlogic_stub.o qlogicfas.o + +# Extract lists of the multi-part drivers. + +multi-y := $(filter $(list-multi), $(obj-y)) +multi-m := $(filter $(list-multi), $(obj-m)) +int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) +int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) + +# Translate to Rules.make lists. + +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(filter-out $(export-objs), $(obj-m)) +MX_OBJS := $(filter $(export-objs), $(obj-m)) +MI_OBJS := $(filter-out $(export-objs), $(int-m)) +MIX_OBJS := $(filter $(export-objs), $(int-m)) + +include $(TOPDIR)/Rules.make + +aha152x_cs.o: $(aha152x-objs) + $(LD) -r -o $@ $(aha152x-objs) + +apa1480_cb.o: $(apa1480-objs) + $(LD) -r -o $@ $(apa1480-objs) + +fdomain_cs.o: $(fdomain-objs) + $(LD) -r -o $@ $(fdomain-objs) + +qlogic_cs.o: $(qlogic-objs) + $(LD) -r -o $@ $(qlogic-objs) diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c new file mode 100644 index 000000000..7450470a1 --- /dev/null +++ b/drivers/scsi/pcmcia/aha152x_stub.c @@ -0,0 +1,437 @@ +/*====================================================================== + + A driver for Adaptec AHA152X-compatible PCMCIA SCSI cards. + + This driver supports the Adaptec AHA-1460, the New Media Bus + Toaster, and the New Media Toast & Jam. + + aha152x_cs.c 1.52 2000/01/11 01:04:31 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/ioport.h> +#include <scsi/scsi.h> +#include <linux/major.h> +#include <linux/blk.h> + +#include <../drivers/scsi/scsi.h> +#include <../drivers/scsi/hosts.h> +#include <scsi/scsi_ioctl.h> +#include <../drivers/scsi/aha152x.h> + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"aha152x_cs.c 1.52 2000/01/11 01:04:31 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Bit map of interrupts to choose from */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +/* SCSI bus setup options */ +static int host_id = 7; +static int reconnect = 1; +static int parity = 1; +static int synchronous = 0; +static int reset_delay = 100; +static int ext_trans = 0; + +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM(host_id, "i"); +MODULE_PARM(reconnect, "i"); +MODULE_PARM(parity, "i"); +MODULE_PARM(synchronous, "i"); +MODULE_PARM(reset_delay, "i"); +MODULE_PARM(ext_trans, "i"); + +/*====================================================================*/ + +typedef struct scsi_info_t { + dev_link_t link; + struct Scsi_Host *host; + int ndev; + dev_node_t node[8]; +} scsi_info_t; + +extern void aha152x_setup(char *str, int *ints); + +static void aha152x_release_cs(u_long arg); +static int aha152x_event(event_t event, int priority, + event_callback_args_t *args); + +static dev_link_t *aha152x_attach(void); +static void aha152x_detach(dev_link_t *); + +static Scsi_Host_Template driver_template = AHA152X; + +static dev_link_t *dev_list = NULL; + +static dev_info_t dev_info = "aha152x_cs"; + +/*====================================================================*/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================*/ + +static dev_link_t *aha152x_attach(void) +{ + scsi_info_t *info; + client_reg_t client_reg; + dev_link_t *link; + int i, ret; + + DEBUG(0, "aha152x_attach()\n"); + + /* Create new SCSI device */ + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) return NULL; + memset(info, 0, sizeof(*info)); + link = &info->link; link->priv = info; + link->release.function = &aha152x_release_cs; + link->release.data = (u_long)link; + + link->io.NumPorts1 = 0x20; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.IOAddrLines = 10; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.Present = PRESENT_OPTION; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.event_handler = &aha152x_event; + client_reg.EventMask = + CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET | + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + aha152x_detach(link); + return NULL; + } + + return link; +} /* aha152x_attach */ + +/*====================================================================*/ + +static void aha152x_detach(dev_link_t *link) +{ + dev_link_t **linkp; + + DEBUG(0, "aha152x_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + if (link->state & DEV_CONFIG) { + aha152x_release_cs((u_long)link); + if (link->state & DEV_STALE_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + } + + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, free bits */ + *linkp = link->next; + kfree(link->priv); + +} /* aha152x_detach */ + +/*====================================================================*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +#define CFG_CHECK(fn, args...) \ +if (CardServices(fn, args) != 0) goto next_entry + +static void aha152x_config_cs(dev_link_t *link) +{ + client_handle_t handle = link->handle; + scsi_info_t *info = link->priv; + tuple_t tuple; + cisparse_t parse; + int i, last_ret, last_fn, ints[8]; + u_char tuple_data[64]; + Scsi_Device *dev; + dev_node_t *node, **tail; + struct Scsi_Host *host; + + DEBUG(0, "aha152x_config(0x%p)\n", link); + + tuple.DesiredTuple = CISTPL_CONFIG; + tuple.TupleData = tuple_data; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + + /* Configure card */ + driver_template.module = &__this_module; + link->state |= DEV_CONFIG; + + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + CS_CHECK(GetFirstTuple, handle, &tuple); + while (1) { + CFG_CHECK(GetTupleData, handle, &tuple); + CFG_CHECK(ParseTuple, handle, &tuple, &parse); + /* For New Media T&J, look for a SCSI window */ + if (parse.cftable_entry.io.win[0].len >= 0x20) + link->io.BasePort1 = parse.cftable_entry.io.win[0].base; + else if ((parse.cftable_entry.io.nwin > 1) && + (parse.cftable_entry.io.win[1].len >= 0x20)) + link->io.BasePort1 = parse.cftable_entry.io.win[1].base; + if ((parse.cftable_entry.io.nwin > 0) && + (link->io.BasePort1 < 0xffff)) { + link->conf.ConfigIndex = parse.cftable_entry.index; + i = CardServices(RequestIO, handle, &link->io); + if (i == CS_SUCCESS) break; + } + next_entry: + CS_CHECK(GetNextTuple, handle, &tuple); + } + + CS_CHECK(RequestIRQ, handle, &link->irq); + CS_CHECK(RequestConfiguration, handle, &link->conf); + + /* A bad hack... */ + release_region(link->io.BasePort1, link->io.NumPorts1); + + /* Set configuration options for the aha152x driver */ + ints[0] = 7; + ints[1] = link->io.BasePort1; + ints[2] = link->irq.AssignedIRQ; + ints[3] = host_id; + ints[4] = reconnect; + ints[5] = parity; + ints[6] = synchronous; + ints[7] = reset_delay; + if (ext_trans) { + ints[8] = ext_trans; ints[0] = 8; + } + aha152x_setup("PCMCIA setup", ints); + + scsi_register_module(MODULE_SCSI_HA, &driver_template); + + tail = &link->dev; + info->ndev = 0; + for (host = scsi_hostlist; host; host = host->next) + if (host->hostt == &driver_template) + for (dev = host->host_queue; dev; dev = dev->next) { + u_long arg[2], id; + kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg); + id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) + + ((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000); + node = &info->node[info->ndev]; + node->minor = 0; + switch (dev->type) { + case TYPE_TAPE: + node->major = SCSI_TAPE_MAJOR; + sprintf(node->dev_name, "st#%04lx", id); + break; + case TYPE_DISK: + case TYPE_MOD: + node->major = SCSI_DISK0_MAJOR; + sprintf(node->dev_name, "sd#%04lx", id); + break; + case TYPE_ROM: + case TYPE_WORM: + node->major = SCSI_CDROM_MAJOR; + sprintf(node->dev_name, "sr#%04lx", id); + break; + default: + node->major = SCSI_GENERIC_MAJOR; + sprintf(node->dev_name, "sg#%04lx", id); + break; + } + *tail = node; tail = &node->next; + info->ndev++; + info->host = dev->host; + } + *tail = NULL; + if (info->ndev == 0) + printk(KERN_INFO "aha152x_cs: no SCSI devices found\n"); + + link->state &= ~DEV_CONFIG_PENDING; + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); + aha152x_release_cs((u_long)link); + return; + +} /* aha152x_config_cs */ + +/*====================================================================*/ + +static void aha152x_release_cs(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + + DEBUG(0, "aha152x_release_cs(0x%p)\n", link); + + if (GET_USE_COUNT(driver_template.module) != 0) { + DEBUG(1, "aha152x_cs: release postponed, " + "device still open\n"); + link->state |= DEV_STALE_CONFIG; + return; + } + + scsi_unregister_module(MODULE_SCSI_HA, &driver_template); + link->dev = NULL; + + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~DEV_CONFIG; + if (link->state & DEV_STALE_LINK) + aha152x_detach(link); + +} /* aha152x_release_cs */ + +/*====================================================================*/ + +static int aha152x_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + scsi_info_t *info = link->priv; + + DEBUG(0, "aha152x_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + link->release.expires = jiffies + HZ/20; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + aha152x_config_cs(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) + CardServices(ReleaseConfiguration, link->handle); + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + Scsi_Cmnd tmp; + CardServices(RequestConfiguration, link->handle, &link->conf); + tmp.host = info->host; + aha152x_host_reset(&tmp); + } + break; + } + return 0; +} /* aha152x_event */ + +/*====================================================================*/ + +static int __init init_aha152x_cs(void) { + servinfo_t serv; + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "aha152x_cs: Card Services release " + "does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &aha152x_attach, &aha152x_detach); + return 0; +} + +static void __exit exit_aha152x_cs(void) { + DEBUG(0, "aha152x_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + aha152x_detach(dev_list); +} + +module_init(init_aha152x_cs); +module_exit(exit_aha152x_cs); + diff --git a/drivers/scsi/pcmcia/apa1480_stub.c b/drivers/scsi/pcmcia/apa1480_stub.c new file mode 100644 index 000000000..a6419f194 --- /dev/null +++ b/drivers/scsi/pcmcia/apa1480_stub.c @@ -0,0 +1,174 @@ +/*====================================================================== + + A driver for the Adaptec APA1480 CardBus SCSI Host Adapter + + apa1480_cb.c 1.19 2000/02/14 22:39:25 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/ioport.h> +#include <scsi/scsi.h> +#include <linux/major.h> +#include <linux/blk.h> + +#include <../drivers/scsi/scsi.h> +#include <../drivers/scsi/hosts.h> +#include <scsi/scsi_ioctl.h> +#include <../drivers/scsi/aic7xxx.h> + +#include <pcmcia/driver_ops.h> + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"apa1480_cb.c 1.19 2000/02/14 22:39:25 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +static int reset = 1; +static int ultra = 0; + +MODULE_PARM(reset, "i"); +MODULE_PARM(ultra, "i"); + +/*====================================================================*/ + +static Scsi_Host_Template driver_template = AIC7XXX; + +extern void aic7xxx_setup(char *, int *); + +static dev_node_t *apa1480_attach(dev_locator_t *loc); +static void apa1480_detach(dev_node_t *node); + +struct driver_operations apa1480_ops = { + "apa1480_cb", apa1480_attach, NULL, NULL, apa1480_detach +}; + +/*====================================================================*/ + +static dev_node_t *apa1480_attach(dev_locator_t *loc) +{ + u_char bus, devfn; + Scsi_Device *dev; + dev_node_t *node; + char s[60]; + int n = 0; + struct Scsi_Host *host; + + if (loc->bus != LOC_PCI) return NULL; + bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; + printk(KERN_INFO "apa1480_attach(bus %d, function %d)\n", + bus, devfn); + + driver_template.module = &__this_module; + + sprintf(s, "no_probe:1,no_reset:%d,ultra:%d", + (reset==0), (ultra!=0)); + aic7xxx_setup(s, NULL); + scsi_register_module(MODULE_SCSI_HA, &driver_template); + + node = kmalloc(7 * sizeof(dev_node_t), GFP_KERNEL); + for (host = scsi_hostlist; host; host = host->next) + if (host->hostt == &driver_template) + for (dev = host->host_queue; dev; dev = dev->next) { + u_long arg[2], id; + kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg); + id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) + + ((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000); + node[n].minor = 0; + switch (dev->type) { + case TYPE_TAPE: + node[n].major = SCSI_TAPE_MAJOR; + sprintf(node[n].dev_name, "st#%04lx", id); + break; + case TYPE_DISK: + case TYPE_MOD: + node[n].major = SCSI_DISK0_MAJOR; + sprintf(node[n].dev_name, "sd#%04lx", id); + break; + case TYPE_ROM: + case TYPE_WORM: + node[n].major = SCSI_CDROM_MAJOR; + sprintf(node[n].dev_name, "sr#%04lx", id); + break; + default: + node[n].major = SCSI_GENERIC_MAJOR; + sprintf(node[n].dev_name, "sg#%04lx", id); + break; + } + if (n) node[n-1].next = &node[n]; + n++; + } + if (n == 0) { + printk(KERN_INFO "apa1480_cs: no SCSI devices found\n"); + scsi_unregister_module(MODULE_SCSI_HA, &driver_template); + kfree(node); + return NULL; + } else + node[n-1].next = NULL; + + MOD_INC_USE_COUNT; + return node; +} + +static void apa1480_detach(dev_node_t *node) +{ + MOD_DEC_USE_COUNT; + scsi_unregister_module(MODULE_SCSI_HA, &driver_template); + kfree(node); +} + +/*====================================================================*/ + +static int __init init_apa1480_cb(void) { + DEBUG(0, "%s\n", version); + register_driver(&apa1480_ops); + return 0; +} + +static void __exit exit_apa1480_cb(void) { + DEBUG(0, "apa1480_cs: unloading\n"); + unregister_driver(&apa1480_ops); +} + +module_init(init_apa1480_cb); +module_exit(exit_apa1480_cb); diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c new file mode 100644 index 000000000..558bf3317 --- /dev/null +++ b/drivers/scsi/pcmcia/fdomain_stub.c @@ -0,0 +1,398 @@ +/*====================================================================== + + A driver for Future Domain-compatible PCMCIA SCSI cards + + fdomain_cs.c 1.41 1999/11/15 06:05:48 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/ioport.h> +#include <scsi/scsi.h> +#include <linux/major.h> +#include <linux/blk.h> + +#include <../drivers/scsi/scsi.h> +#include <../drivers/scsi/hosts.h> +#include <scsi/scsi_ioctl.h> +#include <../drivers/scsi/fdomain.h> + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"fdomain_cs.c 1.41 1999/11/15 06:05:48 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Bit map of interrupts to choose from */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); + +/*====================================================================*/ + +typedef struct scsi_info_t { + dev_link_t link; + int ndev; + dev_node_t node[8]; +} scsi_info_t; + +extern void fdomain_setup(char *str, int *ints); + +static void fdomain_release(u_long arg); +static int fdomain_event(event_t event, int priority, + event_callback_args_t *args); + +static dev_link_t *fdomain_attach(void); +static void fdomain_detach(dev_link_t *); + +static Scsi_Host_Template driver_template = FDOMAIN_16X0; + +static dev_link_t *dev_list = NULL; + +static dev_info_t dev_info = "fdomain_cs"; + +/*====================================================================*/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================*/ + +static dev_link_t *fdomain_attach(void) +{ + scsi_info_t *info; + client_reg_t client_reg; + dev_link_t *link; + int i, ret; + + DEBUG(0, "fdomain_attach()\n"); + + /* Create new SCSI device */ + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) return NULL; + memset(info, 0, sizeof(*info)); + link = &info->link; link->priv = info; + link->release.function = &fdomain_release; + link->release.data = (u_long)link; + + link->io.NumPorts1 = 0x10; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.IOAddrLines = 10; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.Present = PRESENT_OPTION; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.event_handler = &fdomain_event; + client_reg.EventMask = + CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET | + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + fdomain_detach(link); + return NULL; + } + + return link; +} /* fdomain_attach */ + +/*====================================================================*/ + +static void fdomain_detach(dev_link_t *link) +{ + dev_link_t **linkp; + + DEBUG(0, "fdomain_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + if (link->state & DEV_CONFIG) { + fdomain_release((u_long)link); + if (link->state & DEV_STALE_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + } + + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, free bits */ + *linkp = link->next; + kfree(link->priv); + +} /* fdomain_detach */ + +/*====================================================================*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +#define CFG_CHECK(fn, args...) \ +if (CardServices(fn, args) != 0) goto next_entry + +static void fdomain_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + scsi_info_t *info = link->priv; + tuple_t tuple; + cisparse_t parse; + int i, last_ret, last_fn, ints[3]; + u_char tuple_data[64]; + Scsi_Device *dev; + dev_node_t *node, **tail; + struct Scsi_Host *host; + + DEBUG(0, "fdomain_config(0x%p)\n", link); + + tuple.DesiredTuple = CISTPL_CONFIG; + tuple.TupleData = tuple_data; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + + /* Configure card */ + driver_template.module = &__this_module; + link->state |= DEV_CONFIG; + + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + CS_CHECK(GetFirstTuple, handle, &tuple); + while (1) { + CFG_CHECK(GetTupleData, handle, &tuple); + CFG_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigIndex = parse.cftable_entry.index; + link->io.BasePort1 = parse.cftable_entry.io.win[0].base; + i = CardServices(RequestIO, handle, &link->io); + if (i == CS_SUCCESS) break; + next_entry: + CS_CHECK(GetNextTuple, handle, &tuple); + } + + CS_CHECK(RequestIRQ, handle, &link->irq); + CS_CHECK(RequestConfiguration, handle, &link->conf); + + /* A bad hack... */ + release_region(link->io.BasePort1, link->io.NumPorts1); + + /* Set configuration options for the fdomain driver */ + ints[0] = 2; + ints[1] = link->io.BasePort1; + ints[2] = link->irq.AssignedIRQ; + fdomain_setup("PCMCIA setup", ints); + + scsi_register_module(MODULE_SCSI_HA, &driver_template); + + tail = &link->dev; + info->ndev = 0; + for (host = scsi_hostlist; host; host = host->next) + if (host->hostt == &driver_template) + for (dev = host->host_queue; dev; dev = dev->next) { + u_long arg[2], id; + kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg); + id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) + + ((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000); + node = &info->node[info->ndev]; + node->minor = 0; + switch (dev->type) { + case TYPE_TAPE: + node->major = SCSI_TAPE_MAJOR; + sprintf(node->dev_name, "st#%04lx", id); + break; + case TYPE_DISK: + case TYPE_MOD: + node->major = SCSI_DISK0_MAJOR; + sprintf(node->dev_name, "sd#%04lx", id); + break; + case TYPE_ROM: + case TYPE_WORM: + node->major = SCSI_CDROM_MAJOR; + sprintf(node->dev_name, "sr#%04lx", id); + break; + default: + node->major = SCSI_GENERIC_MAJOR; + sprintf(node->dev_name, "sg#%04lx", id); + break; + } + *tail = node; tail = &node->next; + info->ndev++; + } + *tail = NULL; + if (info->ndev == 0) + printk(KERN_INFO "fdomain_cs: no SCSI devices found\n"); + + link->state &= ~DEV_CONFIG_PENDING; + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); + fdomain_release((u_long)link); + return; + +} /* fdomain_config */ + +/*====================================================================*/ + +static void fdomain_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + + DEBUG(0, "fdomain_release(0x%p)\n", link); + + if (GET_USE_COUNT(&__this_module) != 0) { + DEBUG(1, "fdomain_cs: release postponed, " + "device still open\n"); + link->state |= DEV_STALE_CONFIG; + return; + } + + scsi_unregister_module(MODULE_SCSI_HA, &driver_template); + link->dev = NULL; + + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~DEV_CONFIG; + if (link->state & DEV_STALE_LINK) + fdomain_detach(link); + +} /* fdomain_release */ + +/*====================================================================*/ + +static int fdomain_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + + DEBUG(1, "fdomain_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + link->release.expires = jiffies + HZ/20; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + fdomain_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) + CardServices(ReleaseConfiguration, link->handle); + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + CardServices(RequestConfiguration, link->handle, &link->conf); + fdomain_16x0_reset(NULL, 0); + } + break; + } + return 0; +} /* fdomain_event */ + +/*====================================================================*/ + +static int __init init_fdomain_cs(void) { + servinfo_t serv; + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "fdomain_cs: Card Services release " + "does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &fdomain_attach, &fdomain_detach); + return 0; +} + +static void __exit exit_fdomain_cs(void) { + DEBUG(0, "fdomain_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + fdomain_detach(dev_list); +} + +module_init(init_fdomain_cs); +module_exit(exit_fdomain_cs); diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c new file mode 100644 index 000000000..c3f1d03a5 --- /dev/null +++ b/drivers/scsi/pcmcia/qlogic_stub.c @@ -0,0 +1,428 @@ +/*====================================================================== + + A driver for the Qlogic SCSI card + + qlogic_cs.c 1.77 2000/02/01 19:08:09 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <scsi/scsi.h> +#include <linux/major.h> +#include <linux/blk.h> + +#include <../drivers/scsi/scsi.h> +#include <../drivers/scsi/hosts.h> +#include <scsi/scsi_ioctl.h> + +#include <../drivers/scsi/qlogicfas.h> + +#define qlogic_reset(h) qlogicfas_reset(h, 0) + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> +#include <pcmcia/ciscode.h> + +extern void qlogicfas_preset(int port, int irq); + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"qlogic_cs.c 1.77 2000/02/01 19:08:09 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Bit map of interrupts to choose from */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); + +/*====================================================================*/ + +typedef struct scsi_info_t { + dev_link_t link; + u_short manf_id; + int ndev; + dev_node_t node[8]; +} scsi_info_t; + +static void qlogic_release(u_long arg); +static int qlogic_event(event_t event, int priority, + event_callback_args_t *args); + +static dev_link_t *qlogic_attach(void); +static void qlogic_detach(dev_link_t *); + +static Scsi_Host_Template driver_template = QLOGICFAS; + +static dev_link_t *dev_list = NULL; + +static dev_info_t dev_info = "qlogic_cs"; + +/*====================================================================*/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================*/ + +static dev_link_t *qlogic_attach(void) +{ + scsi_info_t *info; + client_reg_t client_reg; + dev_link_t *link; + int i, ret; + + DEBUG(0, "qlogic_attach()\n"); + + /* Create new SCSI device */ + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) return NULL; + memset(info, 0, sizeof(*info)); + link = &info->link; link->priv = info; + link->release.function = &qlogic_release; + link->release.data = (u_long)link; + + link->io.NumPorts1 = 16; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.IOAddrLines = 10; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.Present = PRESENT_OPTION; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.event_handler = &qlogic_event; + client_reg.EventMask = + CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET | + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + qlogic_detach(link); + return NULL; + } + + return link; +} /* qlogic_attach */ + +/*====================================================================*/ + +static void qlogic_detach(dev_link_t *link) +{ + dev_link_t **linkp; + + DEBUG(0, "qlogic_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + if (link->state & DEV_CONFIG) { + qlogic_release((u_long)link); + if (link->state & DEV_STALE_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + } + + if (link->handle) + CardServices(DeregisterClient, link->handle); + + /* Unlink device structure, free bits */ + *linkp = link->next; + kfree(link->priv); + +} /* qlogic_detach */ + +/*====================================================================*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +#define CFG_CHECK(fn, args...) \ +if (CardServices(fn, args) != 0) goto next_entry + +static void qlogic_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + scsi_info_t *info = link->priv; + tuple_t tuple; + cisparse_t parse; + int i, last_ret, last_fn; + u_short tuple_data[32]; + Scsi_Device *dev; + dev_node_t **tail, *node; + struct Scsi_Host *host; + + DEBUG(0, "qlogic_config(0x%p)\n", link); + + tuple.TupleData = (cisdata_t *)tuple_data; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + + tuple.DesiredTuple = CISTPL_MANFID; + if ((CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) && + (CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS)) + info->manf_id = le16_to_cpu(tuple.TupleData[0]); + + /* Configure card */ + driver_template.module = &__this_module; + link->state |= DEV_CONFIG; + + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + CS_CHECK(GetFirstTuple, handle, &tuple); + while (1) { + CFG_CHECK(GetTupleData, handle, &tuple); + CFG_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigIndex = parse.cftable_entry.index; + link->io.BasePort1 = parse.cftable_entry.io.win[0].base; + link->io.NumPorts1 = parse.cftable_entry.io.win[0].len; + if (link->io.BasePort1 != 0) { + i = CardServices(RequestIO, handle, &link->io); + if (i == CS_SUCCESS) break; + } + next_entry: + CS_CHECK(GetNextTuple, handle, &tuple); + } + + CS_CHECK(RequestIRQ, handle, &link->irq); + CS_CHECK(RequestConfiguration, handle, &link->conf); + + if ((info->manf_id == MANFID_MACNICA) || + (info->manf_id == MANFID_PIONEER) || + (info->manf_id == 0x0098)) { + /* set ATAcmd */ + outb(0xb4, link->io.BasePort1+0xd); + outb(0x24, link->io.BasePort1+0x9); + outb(0x04, link->io.BasePort1+0xd); + } + + /* A bad hack... */ + release_region(link->io.BasePort1, link->io.NumPorts1); + + /* The KXL-810AN has a bigger IO port window */ + if (link->io.NumPorts1 == 32) + qlogicfas_preset(link->io.BasePort1+16, link->irq.AssignedIRQ); + else + qlogicfas_preset(link->io.BasePort1, link->irq.AssignedIRQ); + + scsi_register_module(MODULE_SCSI_HA, &driver_template); + + tail = &link->dev; + info->ndev = 0; + for (host = scsi_hostlist; host; host = host->next) + if (host->hostt == &driver_template) + for (dev = host->host_queue; dev; dev = dev->next) { + u_long arg[2], id; + kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg); + id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) + + ((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000); + node = &info->node[info->ndev]; + node->minor = 0; + switch (dev->type) { + case TYPE_TAPE: + node->major = SCSI_TAPE_MAJOR; + sprintf(node->dev_name, "st#%04lx", id); + break; + case TYPE_DISK: + case TYPE_MOD: + node->major = SCSI_DISK0_MAJOR; + sprintf(node->dev_name, "sd#%04lx", id); + break; + case TYPE_ROM: + case TYPE_WORM: + node->major = SCSI_CDROM_MAJOR; + sprintf(node->dev_name, "sr#%04lx", id); + break; + default: + node->major = SCSI_GENERIC_MAJOR; + sprintf(node->dev_name, "sg#%04lx", id); + break; + } + *tail = node; tail = &node->next; + info->ndev++; + } + *tail = NULL; + if (info->ndev == 0) + printk(KERN_INFO "qlogic_cs: no SCSI devices found\n"); + + link->state &= ~DEV_CONFIG_PENDING; + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); + qlogic_release((u_long)link); + return; + +} /* qlogic_config */ + +/*====================================================================*/ + +static void qlogic_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + + DEBUG(0, "qlogic_release(0x%p)\n", link); + + if (GET_USE_COUNT(&__this_module) != 0) { + DEBUG(0, "qlogic_cs: release postponed, device still open\n"); + link->state |= DEV_STALE_CONFIG; + return; + } + + scsi_unregister_module(MODULE_SCSI_HA, &driver_template); + link->dev = NULL; + + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~DEV_CONFIG; + if (link->state & DEV_STALE_LINK) + qlogic_detach(link); + +} /* qlogic_release */ + +/*====================================================================*/ + +static int qlogic_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + + DEBUG(1, "qlogic_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + link->release.expires = jiffies + HZ/20; + add_timer(&link->release); + } + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + qlogic_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) + CardServices(ReleaseConfiguration, link->handle); + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) { + scsi_info_t *info = link->priv; + CardServices(RequestConfiguration, link->handle, &link->conf); + if ((info->manf_id == MANFID_MACNICA) || + (info->manf_id == MANFID_PIONEER) || + (info->manf_id == 0x0098)) { + outb( 0x80, link->io.BasePort1+0xd); + outb( 0x24, link->io.BasePort1+0x9); + outb( 0x04, link->io.BasePort1+0xd); + } + qlogic_reset(NULL); + } + break; + } + return 0; +} /* qlogic_event */ + +/*====================================================================*/ + +static int __init init_qlogic_cs(void) { + servinfo_t serv; + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "qlogic_cs: Card Services release " + "does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &qlogic_attach, &qlogic_detach); + return 0; +} + +static void __exit exit_qlogic_cs(void) { + DEBUG(0, "qlogic_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + qlogic_detach(dev_list); +} + +module_init(init_qlogic_cs); +module_exit(exit_qlogic_cs); diff --git a/drivers/scsi/psi_dale.h b/drivers/scsi/psi_dale.h index bb7c8006b..03da4c3b8 100644 --- a/drivers/scsi/psi_dale.h +++ b/drivers/scsi/psi_dale.h @@ -20,16 +20,32 @@ ****************************************************************************/ /************************************************/ +/* 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 + +/************************************************/ /* Dale PCI setup */ /************************************************/ #define VENDOR_PSI 0x1256 #define DEVICE_DALE_1 0x4401 /* 'D1' */ +#define DEVICE_BIGD_1 0x4201 /* 'B1' */ +#define DEVICE_BIGD_2 0x4202 /* 'B2' */ /************************************************/ /* Misc konstants */ /************************************************/ #define DALE_MAXDRIVES 4 +#define BIGD_MAXDRIVES 8 #define SECTORSXFER 8 +#define ATAPI_TRANSFER 8192 #define BYTES_PER_SECTOR 512 #define DEFAULT_TIMING_MODE 5 @@ -49,90 +65,136 @@ /************************************************/ /* 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 +#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_MODE5 0x00070000L + +#define BIGD_DATA_SLOW 0x00000000L +#define BIGD_DATA_MODE0 0x00000000L +#define BIGD_DATA_MODE2 0x00000000L +#define BIGD_DATA_MODE3 0x00000008L +#define BIGD_DATA_MODE4 0x00000010L +#define BIGD_DATA_MODE5 0x00000020L + +#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 RTR_DMA0_MODE 0x0080 +#define RTR_DMA0_PCI_ADDR 0x0084 +#define RTR_DMA0_LOCAL_ADDR 0x0088 +#define RTR_DMA0_COUNT 0x008C +#define RTR_DMA0_DESC_PTR 0x0090 +#define RTR_DMA1_MODE 0x0094 +#define RTR_DMA1_PCI_ADDR 0x0098 +#define RTR_DMA1_LOCAL_ADDR 0x009C +#define RTR_DMA1_COUNT 0x00A0 +#define RTR_DMA1_DESC_PTR 0x00A4 +#define RTR_DMA_COMMAND_STATUS 0x00A8 +#define RTR_DMA_ARB0 0x00AC +#define RTR_DMA_ARB1 0x00B0 + +#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_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_SCRATCH_DEVICE_0 4 // device type codes +#define DALE_SCRATCH_DEVICE_1 5 +#define DALE_SCRATCH_DEVICE_2 6 +#define DALE_SCRATCH_DEVICE_3 7 -#define DALE_RAID_0_STATUS 8 -#define DALE_RAID_1_STATUS 9 +#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 +#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 */ +/* BigD Scratchpad locations */ /************************************************/ -#define SEL_NONE 0x00 -#define SEL_1 0x01 -#define SEL_2 0x02 +#define BIGD_DEVICE_0 0 // device channel locations +#define BIGD_DEVICE_1 1 +#define BIGD_DEVICE_2 2 +#define BIGD_DEVICE_3 3 + +#define BIGD_DEVICE_4 4 // device type codes +#define BIGD_DEVICE_5 5 +#define BIGD_DEVICE_6 6 +#define BIGD_DEVICE_7 7 + +#define BIGD_ALARM_IMAGE 11 // ~image of alarm fail register +#define BIGD_TIMING_MODE 12 // bus master timing mode (2, 3, 4, 5) +#define BIGD_NUM_DRIVES 13 // number of addressable drives on this board +#define BIGD_RAID_ON 14 // RAID status is on for the whole board +#define BIGD_LAST_ERROR 15 // Last error code from BIOS + +#define BIGD_RAID_0_STATUS 16 +#define BIGD_RAID_1_STATUS 17 +#define BIGD_RAID_2_STATUS 18 +#define BIGD_RAID_3_STATUS 19 +#define BIGD_RAID_4_STATUS 20 +#define BIGD_RAID_5_STATUS 21 +#define BIGD_RAID_6_STATUS 22 +#define BIGD_RAID_7_STATUS 23 /************************************************/ -/* Programmable Interrupt Controller */ +/* Dale cable select bits */ /************************************************/ -#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 +#define SEL_NONE 0x00 +#define SEL_1 0x01 +#define SEL_2 0x02 +#define SEL_3 0x04 +#define SEL_4 0x08 +#define SEL_NEW_SPEED_1 0x20 +#define SEL_COPY 0x40 +#define SEL_IRQ_OFF 0x80 /************************************************/ /* Device/Geometry controls */ @@ -149,6 +211,18 @@ #define DEVICE_DASD_LBA 0x4 // LBA compatible device /************************************************/ +/* BigD fail register bits */ +/************************************************/ +#define FAIL_NONE 0x00 +#define FAIL_0 0x01 +#define FAIL_1 0x02 +#define FAIL_2 0x04 +#define FAIL_MULTIPLE 0x08 +#define FAIL_GOOD 0x20 +#define FAIL_AUDIBLE 0x40 +#define FAIL_ANY 0x80 + +/************************************************/ /* Setup Structure Definitions */ /************************************************/ typedef struct // device setup parameters @@ -168,11 +242,11 @@ typedef struct // master setup structure BOOL promptBIOS; BOOL fastFormat; BOOL shareInterrupt; - BOOL rebootRebuil; + BOOL rebootRebuild; USHORT timingMode; USHORT spare5; USHORT spare6; - SETUP_DEVICE setupDevice[4]; + SETUP_DEVICE setupDevice[BIGD_MAXDRIVES]; } SETUP, *PSETUP; /************************************************/ @@ -210,3 +284,281 @@ typedef struct DEVICE_RAID1 #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 + +/************************************************/ +/* Timeout konstants */ +/************************************************/ +#define TIMEOUT_READY 100 // 100 mSec +#define TIMEOUT_DRQ 300 // 300 mSec +#define TIMEOUT_DATA (3 * HZ) // 3 seconds + +/************************************************/ +/* 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])) ) + +#define SelectSpigot(padapter,spigot) outb_p (spigot, padapter->regStatSel) +#define WriteCommand(padapter,cmd) outb_p (cmd, padapter->regStatCmd) +#define AtapiDevice(padapter,b) outb_p (b, padapter->regLba24); +#define AtapiCountLo(padapter,b) outb_p (b, padapter->regLba8) +#define AtapiCountHi(padapter,b) outb_p (b, padapter->regLba16) + +/************************************************/ +/* 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_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 + +typedef struct _ATAPI_STATUS + { + CHAR check :1; + CHAR reserved1 :1; + CHAR corr :1; + CHAR drq :1; + CHAR dsc :1; + CHAR reserved2 :1; + CHAR drdy :1; + CHAR bsy :1; + } ATAPI_STATUS; + +typedef struct _ATAPI_REASON + { + CHAR cod :1; + CHAR io :1; + CHAR reserved1 :6; + } ATAPI_REASON; + +typedef struct _ATAPI_ERROR + { + CHAR ili :1; + CHAR eom :1; + CHAR abort :1; + CHAR mcr :1; + CHAR senseKey :4; + } ATAPI_ERROR; + +// 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 + +// 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 +#pragma pack (1) +typedef struct _IDENTIFY_DATA + { + USHORT GeneralConfiguration; // 0 + USHORT NumberOfCylinders; // 1 + USHORT Reserved1; // 2 + USHORT NumberOfHeads; // 3 + USHORT UnformattedBytesPerTrack; // 4 + USHORT UnformattedBytesPerSector; // 5 + USHORT SectorsPerTrack; // 6 + USHORT NumBytesISG; // 7 Byte Len - inter-sector gap + USHORT NumBytesSync; // 8 - sync field + USHORT NumWordsVUS; // 9 Len - Vendor Unique Info + USHORT SerialNumber[10]; // 10 + USHORT BufferType; // 20 + USHORT BufferSectorSize; // 21 + USHORT NumberOfEccBytes; // 22 + USHORT FirmwareRevision[4]; // 23 + USHORT ModelNumber[20]; // 27 + USHORT NumSectorsPerInt :8; // 47 Multiple Mode - Sec/Blk + USHORT Reserved2 :8; // 47 + USHORT DoubleWordMode; // 48 flag for double word mode capable + USHORT VendorUnique1 :8; // 49 + USHORT SupportDMA :1; // 49 DMA supported + USHORT SupportLBA :1; // 49 LBA supported + USHORT SupportIORDYDisable :1; // 49 IORDY can be disabled + USHORT SupportIORDY :1; // 49 IORDY supported + USHORT ReservedPsuedoDMA :1; // 49 reserved for pseudo DMA mode support + USHORT Reserved3 :3; // 49 + USHORT Reserved4; // 50 + USHORT Reserved5 :8; // 51 Transfer Cycle Timing - PIO + USHORT PIOCycleTime :8; // 51 Transfer Cycle Timing - PIO + USHORT Reserved6 :8; // 52 - DMA + USHORT DMACycleTime :8; // 52 - DMA + USHORT Valid_54_58 :1; // 53 words 54 - 58 are vaild + USHORT Valid_64_70 :1; // 53 words 64 - 70 are valid + USHORT Reserved7 :14; // 53 + USHORT LogNumCyl; // 54 Current Translation - Num Cyl + USHORT LogNumHeads; // 55 Num Heads + USHORT LogSectorsPerTrack; // 56 Sec/Trk + ULONG LogTotalSectors; // 57 Total Sec + USHORT CurrentNumSecPerInt :8; // 59 current setting for number of sectors per interrupt + USHORT ValidNumSecPerInt :1; // 59 Current setting is valid for number of sectors per interrupt + USHORT Reserved8 :7; // 59 + ULONG LBATotalSectors; // 60 LBA Mode - Sectors + USHORT DMASWordFlags; // 62 + USHORT DMAMWordFlags; // 63 + USHORT AdvancedPIOSupport :8; // 64 Flow control PIO transfer modes supported + USHORT Reserved9 :8; // 64 + USHORT MinMultiDMACycle; // 65 minimum multiword DMA transfer cycle time per word + USHORT RecomendDMACycle; // 66 Manufacturer's recommende multiword DMA transfer cycle time + USHORT MinPIOCycleWithoutFlow; // 67 Minimum PIO transfer cycle time without flow control + USHORT MinPIOCylceWithFlow; // 68 Minimum PIO transfer cycle time with IORDY flow control + USHORT ReservedSpace[256-69]; // 69 + } IDENTIFY_DATA, *PIDENTIFY_DATA; + +// ATAPI configuration bits +typedef struct _ATAPI_GENERAL_0 + { + USHORT CmdPacketSize :2; // Command packet size + USHORT Reserved1 :3; + USHORT CmdDrqType :2; + USHORT Removable :1; + USHORT DeviceType :5; + USHORT Reserved2 :1; + USHORT ProtocolType :2; + } ATAPI_GENERAL_0; + +#pragma pack () diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c index 543a98419..e216ef03a 100644 --- a/drivers/scsi/qla1280.c +++ b/drivers/scsi/qla1280.c @@ -182,6 +182,7 @@ END OF TERMS AND CONDITIONS - Initial Beta Release. *****************************************************************************/ + #ifdef MODULE #include <linux/module.h> #endif @@ -2789,7 +2790,7 @@ qla1280_setup_chip(scsi_qla_host_t *ha) DEBUG(sprintf(debug_buff,"qla1280_setup_chip: loading risc @ =(0x%p),%d,%d(0x%x).\n\r",risc_code_address,cnt,num,risc_address);) DEBUG(qla1280_print(debug_buff)); - printk("qla1280_setup_chip: loading risc @ =code=(0x%p),cnt=%d,seg=%d,addr=0x%x\n\r",risc_code_address,cnt,num,risc_address); + DEBUG(printk("qla1280_setup_chip: loading risc @ =code=(0x%p),cnt=%d,seg=%d,addr=0x%x\n\r",risc_code_address,cnt,num,risc_address)); BCOPY((caddr_t) risc_code_address,(caddr_t) ha->request_ring, (cnt <<1)); mb[0] = MBC_LOAD_RAM; /* mb[0] = MBC_LOAD_RAM_A64; */ @@ -2799,7 +2800,7 @@ qla1280_setup_chip(scsi_qla_host_t *ha) mb[2] = (uint16_t) (ha->request_dma >> 16) & 0xffff; mb[7] = (uint16_t) (MS_64BITS(ha->request_dma) & 0xffff); mb[6] = (uint16_t) (MS_64BITS(ha->request_dma) >> 16) & 0xffff; - printk("qla1280_setup_chip: op=%d 0x%lx = 0x%4x,0x%4x,0x%4x,0x%4x\n",mb[0],ha->request_dma,mb[6],mb[7],mb[2],mb[3]); + DEBUG(printk("qla1280_setup_chip: op=%d 0x%lx = 0x%4x,0x%4x,0x%4x,0x%4x\n",mb[0],ha->request_dma,mb[6],mb[7],mb[2],mb[3])); if( (status = qla1280_mailbox_command(ha, BIT_4|BIT_3|BIT_2|BIT_1|BIT_0, &mb[0])) ) { diff --git a/drivers/scsi/qlogicfas.c b/drivers/scsi/qlogicfas.c index 6196d8eb2..9bc66953d 100644 --- a/drivers/scsi/qlogicfas.c +++ b/drivers/scsi/qlogicfas.c @@ -21,7 +21,7 @@ Version 0.46 1/30/97 - kernel 1.2.0+ Functions as standalone, loadable, and PCMCIA driver, the latter from - Dave Hind's PCMCIA package. + Dave Hinds' PCMCIA package. Redistributable under terms of the GNU Public License @@ -107,7 +107,6 @@ #ifdef PCMCIA #undef QL_INT_ACTIVE_HIGH #define QL_INT_ACTIVE_HIGH 0 -#define MODULE #endif #include <linux/module.h> diff --git a/drivers/scsi/qlogicfc.c b/drivers/scsi/qlogicfc.c index ecdc65cd4..283fe9c3d 100644 --- a/drivers/scsi/qlogicfc.c +++ b/drivers/scsi/qlogicfc.c @@ -64,7 +64,7 @@ /* Configuration section **************************************************** */ /* Set the following macro to 1 to reload the ISP2x00's firmware. This is - version 1.15.37 of the isp2100's firmware and version 2.00.16 of the + version 1.17.30 of the isp2100's firmware and version 2.00.40 of the isp2200's firmware. */ @@ -91,7 +91,7 @@ /* #define TRACE_ISP 1 */ -#define DEFAULT_LOOP_COUNT 10000000 +#define DEFAULT_LOOP_COUNT 1000000000 /* End Configuration section ************************************************ */ @@ -632,6 +632,17 @@ struct init_cb { #define QLOGICFC_MAX_LUN 128 #define QLOGICFC_MAX_LOOP_ID 0x7d +/* the following connection options only apply to the 2200. i have only + * had success with LOOP_ONLY and P2P_ONLY. + */ + +#define LOOP_ONLY 0 +#define P2P_ONLY 1 +#define LOOP_PREFERED 2 +#define P2P_PREFERED 3 + +#define CONNECTION_PREFERENCE LOOP_ONLY + /* adapter_state values */ #define AS_FIRMWARE_DEAD -1 #define AS_LOOP_DOWN 0 @@ -762,7 +773,7 @@ int isp2x00_detect(Scsi_Host_Template * tmpt) hostdata->queued = 0; /* set up the control block */ hostdata->control_block.version = 0x1; - hostdata->control_block.firm_opts = 0x000e; + hostdata->control_block.firm_opts = 0x800e; hostdata->control_block.max_frame_len = 2048; hostdata->control_block.max_iocb = QLOGICFC_REQ_QUEUE_LEN; hostdata->control_block.exec_throttle = QLOGICFC_CMD_PER_LUN; @@ -780,6 +791,8 @@ int isp2x00_detect(Scsi_Host_Template * tmpt) hostdata->control_block.req_queue_addr_lo = virt_to_bus_low32(hostdata->req); hostdata->control_block.req_queue_addr_high = virt_to_bus_high32(hostdata->req); + + hostdata->control_block.add_firm_opts |= CONNECTION_PREFERENCE<<4; hostdata->adapter_state = AS_LOOP_DOWN; hostdata->explore_timer.data = 1; hostdata->host_id = hosts; diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index a7418f18b..d61c832c5 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -1036,7 +1036,7 @@ static inline int load_cmd(Scsi_Cmnd *Cmnd, struct Command_Entry *cmd, int sg_count; sg = (struct scatterlist *) Cmnd->buffer; - sg_count = sbus_map_sg(qpti->sdev, sg, Cmnd->use_sg); + sg_count = sbus_map_sg(qpti->sdev, sg, Cmnd->use_sg, scsi_to_sbus_dma_dir(Cmnd->sc_data_direction)); ds = cmd->dataseg; cmd->segment_cnt = sg_count; @@ -1074,15 +1074,20 @@ static inline int load_cmd(Scsi_Cmnd *Cmnd, struct Command_Entry *cmd, } sg_count -= n; } - } else { + } else if (Cmnd->request_bufflen) { Cmnd->SCp.ptr = (char *)(unsigned long) sbus_map_single(qpti->sdev, Cmnd->request_buffer, - Cmnd->request_bufflen); + Cmnd->request_bufflen, + scsi_to_sbus_dma_dir(Cmnd->sc_data_direction)); cmd->dataseg[0].d_base = (u32) ((unsigned long)Cmnd->SCp.ptr); cmd->dataseg[0].d_count = Cmnd->request_bufflen; cmd->segment_cnt = 1; + } else { + cmd->dataseg[0].d_base = 0; + cmd->dataseg[0].d_count = 0; + cmd->segment_cnt = 1; /* Shouldn't this be 0? */ } /* Committed, record Scsi_Cmd so we can find it later. */ @@ -1409,11 +1414,13 @@ static Scsi_Cmnd *qlogicpti_intr_handler(struct qlogicpti *qpti) if (Cmnd->use_sg) { sbus_unmap_sg(qpti->sdev, (struct scatterlist *)Cmnd->buffer, - Cmnd->use_sg); + Cmnd->use_sg, + scsi_to_sbus_dma_dir(Cmnd->sc_data_direction)); } else { sbus_unmap_single(qpti->sdev, (__u32)((unsigned long)Cmnd->SCp.ptr), - Cmnd->request_bufflen); + Cmnd->request_bufflen, + scsi_to_sbus_dma_dir(Cmnd->sc_data_direction)); } qpti->cmd_count[Cmnd->target]--; sbus_writew(out_ptr, qpti->qregs + MBOX5); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 2d86cbf95..a6e2c14d9 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -224,6 +224,8 @@ void scsi_wait_cmd (Scsi_Cmnd * SCpnt, const void *cmnd , { DECLARE_MUTEX_LOCKED(sem); + if (buffer != NULL && SCpnt->sc_data_direction == SCSI_DATA_NONE) + BUG(); SCpnt->request.sem = &sem; SCpnt->request.rq_status = RQ_SCSI_BUSY; scsi_do_cmd (SCpnt, (void *) cmnd, @@ -248,6 +250,73 @@ static spinlock_t device_request_lock = SPIN_LOCK_UNLOCKED; static spinlock_t scsi_bhqueue_lock = SPIN_LOCK_UNLOCKED; /* + * Function: scsi_allocate_request + * + * Purpose: Allocate a request descriptor. + * + * Arguments: device - device for which we want a request + * + * Lock status: No locks assumed to be held. This function is SMP-safe. + * + * Returns: Pointer to request block. + * + * Notes: With the new queueing code, it becomes important + * to track the difference between a command and a + * request. A request is a pending item in the queue that + * has not yet reached the top of the queue. + */ + +Scsi_Request *scsi_allocate_request(Scsi_Device * device) +{ + Scsi_Request *SRpnt = NULL; + + if (!device) + panic("No device passed to scsi_allocate_request().\n"); + + SRpnt = (Scsi_Request *) kmalloc(sizeof(Scsi_Request), GFP_ATOMIC); + if( SRpnt == NULL ) + { + return NULL; + } + + memset(SRpnt, 0, sizeof(Scsi_Request)); + SRpnt->sr_device = device; + SRpnt->sr_host = device->host; + SRpnt->sr_magic = SCSI_REQ_MAGIC; + SRpnt->sr_data_direction = SCSI_DATA_UNKNOWN; + + return SRpnt; +} + +/* + * Function: scsi_release_request + * + * Purpose: Release a request descriptor. + * + * Arguments: device - device for which we want a request + * + * Lock status: No locks assumed to be held. This function is SMP-safe. + * + * Returns: Pointer to request block. + * + * Notes: With the new queueing code, it becomes important + * to track the difference between a command and a + * request. A request is a pending item in the queue that + * has not yet reached the top of the queue. We still need + * to free a request when we are done with it, of course. + */ +void scsi_release_request(Scsi_Request * req) +{ + if( req->sr_command != NULL ) + { + scsi_release_command(req->sr_command); + req->sr_command = NULL; + } + + kfree(req); +} + +/* * Function: scsi_allocate_device * * Purpose: Allocate a command descriptor. @@ -269,6 +338,9 @@ static spinlock_t scsi_bhqueue_lock = SPIN_LOCK_UNLOCKED; * command block, this function will interrupt and return * NULL in the event that a signal arrives that needs to * be handled. + * + * This function is deprecated, and drivers should be + * rewritten to use Scsi_Request instead of Scsi_Cmnd. */ Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait, @@ -417,6 +489,10 @@ Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait, SCpnt->transfersize = 0; /* No default transfer size */ SCpnt->cmd_len = 0; + SCpnt->sc_data_direction = SCSI_DATA_UNKNOWN; + SCpnt->sc_request = NULL; + SCpnt->sc_magic = SCSI_CMND_MAGIC; + SCpnt->result = 0; SCpnt->underflow = 0; /* Do not flag underflow conditions */ SCpnt->resid = 0; @@ -451,6 +527,9 @@ Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait, * gets hidden in this function. Upper level drivers don't * have any chickens to wave in the air to get things to * work reliably. + * + * This function is deprecated, and drivers should be + * rewritten to use Scsi_Request instead of Scsi_Cmnd. */ void scsi_release_command(Scsi_Cmnd * SCpnt) { @@ -636,6 +715,8 @@ int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt) return rtn; } +devfs_handle_t scsi_devfs_handle = NULL; + /* * scsi_do_cmd sends all the commands out to the low-level driver. It * handles the specifics required for each low level driver - ie queued @@ -643,6 +724,215 @@ int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt) * drivers go for the same host at the same time. */ +void scsi_wait_req (Scsi_Request * SRpnt, const void *cmnd , + void *buffer, unsigned bufflen, + int timeout, int retries) +{ + DECLARE_MUTEX_LOCKED(sem); + + SRpnt->sr_request.sem = &sem; + SRpnt->sr_request.rq_status = RQ_SCSI_BUSY; + scsi_do_req (SRpnt, (void *) cmnd, + buffer, bufflen, scsi_wait_done, timeout, retries); + down (&sem); + SRpnt->sr_request.sem = NULL; + if( SRpnt->sr_command != NULL ) + { + scsi_release_command(SRpnt->sr_command); + SRpnt->sr_command = NULL; + } + +} + +/* + * Function: scsi_do_req + * + * Purpose: Queue a SCSI request + * + * Arguments: SRpnt - command descriptor. + * cmnd - actual SCSI command to be performed. + * buffer - data buffer. + * bufflen - size of data buffer. + * done - completion function to be run. + * timeout - how long to let it run before timeout. + * retries - number of retries we allow. + * + * Lock status: With the new queueing code, this is SMP-safe, and no locks + * need be held upon entry. The old queueing code the lock was + * assumed to be held upon entry. + * + * Returns: Nothing. + * + * Notes: Prior to the new queue code, this function was not SMP-safe. + * Also, this function is now only used for queueing requests + * for things like ioctls and character device requests - this + * is because we essentially just inject a request into the + * queue for the device. Normal block device handling manipulates + * the queue directly. + */ +void scsi_do_req(Scsi_Request * SRpnt, const void *cmnd, + void *buffer, unsigned bufflen, void (*done) (Scsi_Cmnd *), + int timeout, int retries) +{ + Scsi_Device * SDpnt = SRpnt->sr_device; + struct Scsi_Host *host = SDpnt->host; + + ASSERT_LOCK(&io_request_lock, 0); + + SCSI_LOG_MLQUEUE(4, + { + int i; + int target = SDpnt->id; + printk("scsi_do_req (host = %d, channel = %d target = %d, " + "buffer =%p, bufflen = %d, done = %p, timeout = %d, " + "retries = %d)\n" + "command : ", host->host_no, SDpnt->channel, target, buffer, + bufflen, done, timeout, retries); + for (i = 0; i < 10; ++i) + printk("%02x ", ((unsigned char *) cmnd)[i]); + printk("\n"); + }); + + if (!host) { + panic("Invalid or not present host.\n"); + } + + /* + * If the upper level driver is reusing these things, then + * we should release the low-level block now. Another one will + * be allocated later when this request is getting queued. + */ + if( SRpnt->sr_command != NULL ) + { + scsi_release_command(SRpnt->sr_command); + SRpnt->sr_command = NULL; + } + + /* + * We must prevent reentrancy to the lowlevel host driver. This prevents + * it - we enter a loop until the host we want to talk to is not busy. + * Race conditions are prevented, as interrupts are disabled in between the + * time we check for the host being not busy, and the time we mark it busy + * ourselves. + */ + + + /* + * Our own function scsi_done (which marks the host as not busy, disables + * the timeout counter, etc) will be called by us or by the + * scsi_hosts[host].queuecommand() function needs to also call + * the completion function for the high level driver. + */ + + memcpy((void *) SRpnt->sr_cmnd, (const void *) cmnd, + sizeof(SRpnt->sr_cmnd)); + SRpnt->sr_bufflen = bufflen; + SRpnt->sr_buffer = buffer; + SRpnt->sr_allowed = retries; + SRpnt->sr_done = done; + SRpnt->sr_timeout_per_command = timeout; + + memcpy((void *) SRpnt->sr_cmnd, (const void *) cmnd, + sizeof(SRpnt->sr_cmnd)); + + if (SRpnt->sr_cmd_len == 0) + SRpnt->sr_cmd_len = COMMAND_SIZE(SRpnt->sr_cmnd[0]); + + /* + * At this point, we merely set up the command, stick it in the normal + * request queue, and return. Eventually that request will come to the + * top of the list, and will be dispatched. + */ + scsi_insert_special_req(SRpnt, 0); + + SCSI_LOG_MLQUEUE(3, printk("Leaving scsi_do_cmd()\n")); +} + +/* + * Function: scsi_init_cmd_from_req + * + * Purpose: Queue a SCSI command + * Purpose: Initialize a Scsi_Cmnd from a Scsi_Request + * + * Arguments: SCpnt - command descriptor. + * SRpnt - Request from the queue. + * + * Lock status: None needed. + * + * Returns: Nothing. + * + * Notes: Mainly transfer data from the request structure to the + * command structure. The request structure is allocated + * using the normal memory allocator, and requests can pile + * up to more or less any depth. The command structure represents + * a consumable resource, as these are allocated into a pool + * when the SCSI subsystem initializes. The preallocation is + * required so that in low-memory situations a disk I/O request + * won't cause the memory manager to try and write out a page. + * The request structure is generally used by ioctls and character + * devices. + */ +void scsi_init_cmd_from_req(Scsi_Cmnd * SCpnt, Scsi_Request * SRpnt) +{ + struct Scsi_Host *host = SCpnt->host; + + ASSERT_LOCK(&io_request_lock, 0); + + SCpnt->owner = SCSI_OWNER_MIDLEVEL; + SRpnt->sr_command = SCpnt; + + if (!host) { + panic("Invalid or not present host.\n"); + } + + SCpnt->cmd_len = SRpnt->sr_cmd_len; + SCpnt->use_sg = SRpnt->sr_use_sg; + + memcpy((void *) &SCpnt->request, (const void *) &SRpnt->sr_request, + sizeof(SRpnt->sr_request)); + memcpy((void *) SCpnt->data_cmnd, (const void *) SRpnt->sr_cmnd, + sizeof(SCpnt->data_cmnd)); + SCpnt->reset_chain = NULL; + SCpnt->serial_number = 0; + SCpnt->serial_number_at_timeout = 0; + SCpnt->bufflen = SRpnt->sr_bufflen; + SCpnt->buffer = SRpnt->sr_buffer; + SCpnt->flags = 0; + SCpnt->retries = 0; + SCpnt->allowed = SRpnt->sr_allowed; + SCpnt->done = SRpnt->sr_done; + SCpnt->timeout_per_command = SRpnt->sr_timeout_per_command; + + SCpnt->sc_data_direction = SRpnt->sr_data_direction; + + SCpnt->sglist_len = SRpnt->sr_sglist_len; + SCpnt->underflow = SRpnt->sr_underflow; + + SCpnt->sc_request = SRpnt; + + memcpy((void *) SCpnt->cmnd, (const void *) SRpnt->sr_cmnd, + sizeof(SCpnt->cmnd)); + /* Zero the sense buffer. Some host adapters automatically request + * sense on error. 0 is not a valid sense code. + */ + memset((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer); + SCpnt->request_buffer = SRpnt->sr_buffer; + SCpnt->request_bufflen = SRpnt->sr_bufflen; + SCpnt->old_use_sg = SCpnt->use_sg; + if (SCpnt->cmd_len == 0) + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + SCpnt->old_cmd_len = SCpnt->cmd_len; + SCpnt->sc_old_data_direction = SCpnt->sc_data_direction; + + /* Start the timer ticking. */ + + SCpnt->internal_timeout = NORMAL_TIMEOUT; + SCpnt->abort_reason = 0; + SCpnt->result = 0; + + SCSI_LOG_MLQUEUE(3, printk("Leaving scsi_do_cmd()\n")); +} + /* * Function: scsi_do_cmd * @@ -737,6 +1027,7 @@ void scsi_do_cmd(Scsi_Cmnd * SCpnt, const void *cmnd, if (SCpnt->cmd_len == 0) SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); SCpnt->old_cmd_len = SCpnt->cmd_len; + SCpnt->sc_old_data_direction = SCpnt->sc_data_direction; /* Start the timer ticking. */ @@ -996,6 +1287,7 @@ int scsi_retry_command(Scsi_Cmnd * SCpnt) SCpnt->request_bufflen = SCpnt->bufflen; SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; + SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; /* * Zero the sense information from the last time we tried @@ -1017,6 +1309,7 @@ void scsi_finish_command(Scsi_Cmnd * SCpnt) { struct Scsi_Host *host; Scsi_Device *device; + Scsi_Request * SRpnt; unsigned long flags; ASSERT_LOCK(&io_request_lock, 0); @@ -1061,6 +1354,20 @@ void scsi_finish_command(Scsi_Cmnd * SCpnt) /* We can get here with use_sg=0, causing a panic in the upper level (DB) */ SCpnt->use_sg = SCpnt->old_use_sg; + /* + * If there is an associated request structure, copy the data over before we call the + * completion function. + */ + SRpnt = SCpnt->sc_request; + if( SRpnt != NULL ) { + SRpnt->sr_result = SRpnt->sr_command->result; + if( SRpnt->sr_result != 0 ) { + memcpy(SRpnt->sr_sense_buffer, + SRpnt->sr_command->sense_buffer, + sizeof(SRpnt->sr_sense_buffer)); + } + } + SCpnt->done(SCpnt); } @@ -1096,6 +1403,7 @@ void scsi_release_commandblocks(Scsi_Device * SDpnt) kfree((char *) SCpnt); } SDpnt->has_cmdblocks = 0; + SDpnt->queue_depth = 0; spin_unlock_irqrestore(&device_request_lock, flags); } @@ -1168,7 +1476,39 @@ void scsi_build_commandblocks(Scsi_Device * SDpnt) static int proc_scsi_gen_write(struct file * file, const char * buf, unsigned long length, void *data); +void __init scsi_host_no_insert(char *str, int n) +{ + Scsi_Host_Name *shn, *shn2; + int len; + + len = strlen(str); + if (len && (shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC))) { + if ((shn->name = kmalloc(len+1, GFP_ATOMIC))) { + strncpy(shn->name, str, len); + shn->name[len] = 0; + shn->host_no = n; + shn->host_registered = 0; + shn->loaded_as_module = 1; /* numbers shouldn't be freed in any case */ + shn->next = NULL; + if (scsi_host_no_list) { + for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next) + ; + shn2->next = shn; + } + else + scsi_host_no_list = shn; + max_scsi_hosts = n+1; + } + else + kfree((char *) shn); + } +} + #ifndef MODULE /* { */ + +char scsi_host_no_table[20][10] __initdata = {}; +int scsi_host_no_set __initdata = 0; + /* * scsi_dev_init() is our initialization routine, which in turn calls host * initialization, bus scanning, and sd/st initialization routines. @@ -1184,8 +1524,16 @@ int __init scsi_dev_init(void) return; #endif + /* Initialize list of host_no if kernel parameter set */ + if (scsi_host_no_set) { + int i; + for (i = 0;i < sizeof(scsi_host_no_table)/sizeof(scsi_host_no_table[0]);i++) + scsi_host_no_insert(scsi_host_no_table[i], i); + } + /* Yes we're here... */ + scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", 4, NULL); /* * This makes /proc/scsi and /proc/scsi/scsi visible. */ @@ -1535,6 +1883,7 @@ static int proc_scsi_gen_write(struct file * file, const char * buf, * Nobody is using this device any more. * Free all of the command structures. */ + devfs_unregister (scd->de); scsi_release_commandblocks(scd); /* Now we can remove the device structure */ @@ -1834,6 +2183,7 @@ static void scsi_unregister_host(Scsi_Host_Template * tpnt) printk("Attached usage count = %d\n", SDpnt->attached); return; } + devfs_unregister (SDpnt->de); } } @@ -2143,11 +2493,12 @@ static void scsi_dump_status(int level) printk("Dump of scsi host parameters:\n"); i = 0; for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { - printk(" %d %d %d : %d\n", + printk(" %d %d %d : %d %d\n", shpnt->host_failed, shpnt->host_busy, atomic_read(&shpnt->host_active), - shpnt->host_blocked); + shpnt->host_blocked, + shpnt->host_self_blocked); } @@ -2193,19 +2544,24 @@ static void scsi_dump_status(int level) /* Now dump the request lists for each block device */ printk("Dump of pending block device requests\n"); for (i = 0; i < MAX_BLKDEV; i++) { - if (blk_dev[i].request_queue.current_request) { + struct list_head * queue_head; + + queue_head = &blk_dev[i].request_queue.queue_head; + if (!list_empty(queue_head)) { struct request *req; + struct list_head * entry; + printk("%d: ", i); - req = blk_dev[i].request_queue.current_request; - while (req) { + entry = queue_head->next; + do { + req = blkdev_entry_to_request(entry); printk("(%s %d %ld %ld %ld) ", kdevname(req->rq_dev), req->cmd, req->sector, req->nr_sectors, req->current_nr_sectors); - req = req->next; - } + } while ((entry = entry->next) != queue_head); printk("\n"); } } @@ -2216,12 +2572,52 @@ static void scsi_dump_status(int level) } #endif /* CONFIG_PROC_FS */ +static int scsi_host_no_init (char *str) +{ + static int next_no = 0; + char *temp; + +#ifndef MODULE + int len; + scsi_host_no_set = 1; + memset(scsi_host_no_table, 0, sizeof(scsi_host_no_table)); +#endif /* MODULE */ + + while (str) { + temp = str; + while (*temp && (*temp != ':') && (*temp != ',')) + temp++; + if (!*temp) + temp = NULL; + else + *temp++ = 0; +#ifdef MODULE + scsi_host_no_insert(str, next_no); +#else + if (next_no < sizeof(scsi_host_no_table)/sizeof(scsi_host_no_table[0])) { + if ((len = strlen(str)) >= sizeof(scsi_host_no_table[0])) + len = sizeof(scsi_host_no_table[0])-1; + strncpy(scsi_host_no_table[next_no], str, len); + scsi_host_no_table[next_no][len] = 0; + } +#endif /* MODULE */ + str = temp; + next_no++; + } + return 1; +} + +#ifndef MODULE +__setup("scsihosts=", scsi_host_no_init); +#endif + #ifdef MODULE +static char *scsihosts; + +MODULE_PARM(scsihosts, "s"); int init_module(void) { - unsigned long size; - int has_space = 0; struct proc_dir_entry *generic; if( scsi_init_minimal_dma_pool() != 0 ) @@ -2249,6 +2645,8 @@ int init_module(void) scsi_loadable_module_flag = 1; + scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", 4, NULL); + scsi_host_no_init (scsihosts); /* * This is where the processing takes place for most everything * when commands are completed. @@ -2260,8 +2658,21 @@ int init_module(void) void cleanup_module(void) { + Scsi_Host_Name *shn, *shn2 = NULL; + remove_bh(SCSI_BH); + devfs_unregister (scsi_devfs_handle); + for (shn = scsi_host_no_list;shn;shn = shn->next) { + if (shn->name) + kfree(shn->name); + if (shn2) + kfree (shn2); + shn2 = shn; + } + if (shn2) + kfree (shn2); + #ifdef CONFIG_PROC_FS /* No, we're not here anymore. Don't show the /proc/scsi files. */ remove_proc_entry ("scsi/scsi", 0); diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h index df294f2e4..7a073f86e 100644 --- a/drivers/scsi/scsi.h +++ b/drivers/scsi/scsi.h @@ -16,6 +16,7 @@ #define _SCSI_H #include <linux/config.h> /* for CONFIG_SCSI_LOGGING */ +#include <linux/devfs_fs_kernel.h> #include <linux/proc_fs.h> /* @@ -31,6 +32,54 @@ #include <asm/io.h> /* + * These are the values that the SCpnt->sc_data_direction and + * SRpnt->sr_data_direction can take. These need to be set + * The SCSI_DATA_UNKNOWN value is essentially the default. + * In the event that the command creator didn't bother to + * set a value, you will see SCSI_DATA_UNKNOWN. + */ +#define SCSI_DATA_UNKNOWN 0 +#define SCSI_DATA_WRITE 1 +#define SCSI_DATA_READ 2 +#define SCSI_DATA_NONE 3 + +#ifdef CONFIG_PCI +#include <linux/pci.h> +#if ((SCSI_DATA_UNKNOWN == PCI_DMA_BIDIRECTIONAL) && (SCSI_DATA_WRITE == PCI_DMA_TODEVICE) && (SCSI_DATA_READ == PCI_DMA_FROMDEVICE) && (SCSI_DATA_NONE == PCI_DMA_NONE)) +#define scsi_to_pci_dma_dir(scsi_dir) ((int)(scsi_dir)) +#else +extern __inline__ int scsi_to_pci_dma_dir(unsigned char scsi_dir) +{ + if (scsi_dir == SCSI_DATA_UNKNOWN) + return PCI_DMA_BIDIRECTIONAL; + if (scsi_dir == SCSI_DATA_WRITE) + return PCI_DMA_TODEVICE; + if (scsi_dir == SCSI_DATA_READ) + return PCI_DMA_FROMDEVICE; + return PCI_DMA_NONE; +} +#endif +#endif + +#ifdef CONFIG_SBUS +#include <asm/sbus.h> +#if ((SCSI_DATA_UNKNOWN == SBUS_DMA_BIDIRECTIONAL) && (SCSI_DATA_WRITE == SBUS_DMA_TODEVICE) && (SCSI_DATA_READ == SBUS_DMA_FROMDEVICE) && (SCSI_DATA_NONE == SBUS_DMA_NONE)) +#define scsi_to_sbus_dma_dir(scsi_dir) ((int)(scsi_dir)) +#else +extern __inline__ int scsi_to_sbus_dma_dir(unsigned char scsi_dir) +{ + if (scsi_dir == SCSI_DATA_UNKNOWN) + return SBUS_DMA_BIDIRECTIONAL; + if (scsi_dir == SCSI_DATA_WRITE) + return SBUS_DMA_TODEVICE; + if (scsi_dir == SCSI_DATA_READ) + return SBUS_DMA_FROMDEVICE; + return SBUS_DMA_NONE; +} +#endif +#endif + +/* * Some defs, in case these are not defined elsewhere. */ #ifndef TRUE @@ -303,6 +352,7 @@ extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE]; #define SUGGEST_MASK 0xf0 #define MAX_COMMAND_SIZE 12 +#define SCSI_SENSE_BUFFERSIZE 64 /* * SCSI command sets @@ -356,6 +406,10 @@ extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE]; */ typedef struct scsi_device Scsi_Device; typedef struct scsi_cmnd Scsi_Cmnd; +typedef struct scsi_request Scsi_Request; + +#define SCSI_CMND_MAGIC 0xE25C23A5 +#define SCSI_REQ_MAGIC 0x75F6D354 /* * Here is where we prototype most of the mid-layer. @@ -444,10 +498,25 @@ extern void scsi_do_cmd(Scsi_Cmnd *, const void *cmnd, void (*done) (struct scsi_cmnd *), int timeout, int retries); extern void scsi_wait_cmd(Scsi_Cmnd *, const void *cmnd, + void *buffer, unsigned bufflen, + int timeout, int retries); +extern int scsi_dev_init(void); + +/* + * Newer request-based interfaces. + */ +extern Scsi_Request *scsi_allocate_request(Scsi_Device *); +extern void scsi_release_request(Scsi_Request *); +extern void scsi_wait_req(Scsi_Request *, const void *cmnd, void *buffer, unsigned bufflen, int timeout, int retries); -extern int scsi_dev_init(void); +extern void scsi_do_req(Scsi_Request *, const void *cmnd, + void *buffer, unsigned bufflen, + void (*done) (struct scsi_cmnd *), + int timeout, int retries); +extern int scsi_insert_special_req(Scsi_Request * SRpnt, int); +extern void scsi_init_cmd_from_req(Scsi_Cmnd *, Scsi_Request *); /* @@ -466,6 +535,7 @@ extern struct proc_dir_entry *proc_scsi; */ extern void print_command(unsigned char *); extern void print_sense(const char *, Scsi_Cmnd *); +extern void print_req_sense(const char *, Scsi_Request *); extern void print_driverbyte(int scsiresult); extern void print_hostbyte(int scsiresult); extern void print_status (int status); @@ -512,6 +582,7 @@ struct scsi_device { int access_count; /* Count of open channels/mounts */ void *hostdata; /* available to low-level driver */ + devfs_handle_t de; /* directory for the device */ char type; char scsi_level; char vendor[8], model[16], rev[4]; @@ -568,6 +639,39 @@ typedef struct scsi_pointer { volatile int phase; } Scsi_Pointer; +/* + * This is essentially a slimmed down version of Scsi_Cmnd. The point of + * having this is that requests that are injected into the queue as result + * of things like ioctls and character devices shouldn't be using a + * Scsi_Cmnd until such a time that the command is actually at the head + * of the queue and being sent to the driver. + */ +struct scsi_request { + int sr_magic; + int sr_result; /* Status code from lower level driver */ + unsigned char sr_sense_buffer[SCSI_SENSE_BUFFERSIZE]; /* obtained by REQUEST SENSE + * when CHECK CONDITION is + * received on original command + * (auto-sense) */ + + struct Scsi_Host *sr_host; + Scsi_Device *sr_device; + Scsi_Cmnd *sr_command; + struct request sr_request; /* A copy of the command we are + working on */ + unsigned sr_bufflen; /* Size of data buffer */ + void *sr_buffer; /* Data buffer */ + int sr_allowed; + unsigned char sr_data_direction; + unsigned char sr_cmd_len; + unsigned char sr_cmnd[MAX_COMMAND_SIZE]; + void (*sr_done) (struct scsi_cmnd *); /* Mid-level done function */ + int sr_timeout_per_command; + unsigned short sr_use_sg; /* Number of pieces of scatter-gather */ + unsigned short sr_sglist_len; /* size of malloc'd scatter-gather list */ + unsigned sr_underflow; /* Return error if less than + this amount is transfered */ +}; /* * FIXME(eric) - one of the great regrets that I have is that I failed to define @@ -578,6 +682,7 @@ typedef struct scsi_pointer { * go back and retrofit at least some of the elements here with with the prefix. */ struct scsi_cmnd { + int sc_magic; /* private: */ /* * This information is private to the scsi mid-layer. Wrapping it in a @@ -587,6 +692,7 @@ struct scsi_cmnd { unsigned short state; unsigned short owner; Scsi_Device *device; + Scsi_Request *sc_request; struct scsi_cmnd *next; struct scsi_cmnd *reset_chain; @@ -630,6 +736,8 @@ struct scsi_cmnd { unsigned char channel; unsigned char cmd_len; unsigned char old_cmd_len; + unsigned char sc_data_direction; + unsigned char sc_old_data_direction; /* These elements define the operation we are about to perform */ unsigned char cmnd[MAX_COMMAND_SIZE]; @@ -665,7 +773,7 @@ struct scsi_cmnd { struct request request; /* A copy of the command we are working on */ - unsigned char sense_buffer[64]; /* obtained by REQUEST SENSE + unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; /* obtained by REQUEST SENSE * when CHECK CONDITION is * received on original command * (auto-sense) */ diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 773dc0087..7268bc154 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -408,6 +408,7 @@ STATIC int scsi_eh_retry_command(Scsi_Cmnd * SCpnt) SCpnt->request_bufflen = SCpnt->bufflen; SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; + SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; scsi_send_eh_cmnd(SCpnt, SCpnt->timeout_per_command); @@ -464,6 +465,7 @@ STATIC int scsi_request_sense(Scsi_Cmnd * SCpnt) SCpnt->request_bufflen = 256; SCpnt->use_sg = 0; SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + SCpnt->sc_data_direction = SCSI_DATA_READ; scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); @@ -486,6 +488,7 @@ STATIC int scsi_request_sense(Scsi_Cmnd * SCpnt) SCpnt->request_bufflen = SCpnt->bufflen; SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; + SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; /* * Hey, we are done. Let's look to see what happened. @@ -531,6 +534,7 @@ STATIC int scsi_test_unit_ready(Scsi_Cmnd * SCpnt) SCpnt->use_sg = 0; SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); + SCpnt->sc_data_direction = SCSI_DATA_NONE; /* Last chance to have valid sense data */ if (!scsi_sense_valid(SCpnt)) @@ -551,6 +555,7 @@ STATIC int scsi_test_unit_ready(Scsi_Cmnd * SCpnt) SCpnt->request_bufflen = SCpnt->bufflen; SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; + SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; /* * Hey, we are done. Let's look to see what happened. @@ -730,6 +735,7 @@ STATIC void scsi_eh_finish_command(Scsi_Cmnd ** SClist, Scsi_Cmnd * SCpnt) * things. */ SCpnt->use_sg = SCpnt->old_use_sg; + SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; *SClist = SCpnt; } @@ -1245,6 +1251,7 @@ STATIC void scsi_restart_operations(struct Scsi_Host *host) request_queue_t *q; if ((host->can_queue > 0 && (host->host_busy >= host->can_queue)) || (host->host_blocked) + || (host->host_self_blocked) || (SDpnt->device_blocked)) { break; } diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index 56ad67646..abdef85ef 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -105,6 +105,7 @@ static int ioctl_internal_command(Scsi_Device * dev, char *cmd, return -EINTR; } + SCpnt->sc_data_direction = SCSI_DATA_NONE; scsi_wait_cmd(SCpnt, cmd, NULL, 0, timeout, retries); SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", SCpnt->result)); @@ -197,6 +198,7 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic) int inlen, outlen, cmdlen; int needed, buf_needed; int timeout, retries, result; + int data_direction; if (!sic) return -EINVAL; @@ -232,8 +234,21 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic) if (!buf) return -ENOMEM; memset(buf, 0, buf_needed); - } else + if( inlen == 0 ) { + data_direction = SCSI_DATA_WRITE; + } else if (outlen == 0 ) { + data_direction = SCSI_DATA_READ; + } else { + /* + * Can this ever happen? + */ + data_direction = SCSI_DATA_UNKNOWN; + } + + } else { buf = NULL; + data_direction = SCSI_DATA_NONE; + } /* * Obtain the command from the user's address space. @@ -288,6 +303,7 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic) return -EINTR; } + SCpnt->sc_data_direction = data_direction; scsi_wait_cmd(SCpnt, cmd, buf, needed, timeout, retries); /* diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 9ec2fe281..2438aecc6 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -86,6 +86,7 @@ int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int at_head) q = &SCpnt->device->request_queue; SCpnt->request.cmd = SPECIAL; SCpnt->request.special = (void *) SCpnt; + SCpnt->request.q = NULL; /* * We have the option of inserting the head or the tail of the queue. @@ -96,8 +97,7 @@ int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int at_head) spin_lock_irqsave(&io_request_lock, flags); if (at_head) { - SCpnt->request.next = q->current_request; - q->current_request = &SCpnt->request; + list_add(&SCpnt->request.queue, &q->queue_head); } else { /* * FIXME(eric) - we always insert at the tail of the @@ -107,19 +107,75 @@ int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int at_head) * request might not float high enough in the queue * to be scheduled. */ - SCpnt->request.next = NULL; - if (q->current_request == NULL) { - q->current_request = &SCpnt->request; - } else { - struct request *req; + list_add_tail(&SCpnt->request.queue, &q->queue_head); + } - for (req = q->current_request; req; req = req->next) { - if (req->next == NULL) { - req->next = &SCpnt->request; - break; - } - } - } + /* + * Now hit the requeue function for the queue. If the host is + * already busy, so be it - we have nothing special to do. If + * the host can queue it, then send it off. + */ + q->request_fn(q); + spin_unlock_irqrestore(&io_request_lock, flags); + return 0; +} + +/* + * Function: scsi_insert_special_req() + * + * Purpose: Insert pre-formed request into request queue. + * + * Arguments: SRpnt - request that is ready to be queued. + * at_head - boolean. True if we should insert at head + * of queue, false if we should insert at tail. + * + * Lock status: Assumed that lock is not held upon entry. + * + * Returns: Nothing + * + * Notes: This function is called from character device and from + * ioctl types of functions where the caller knows exactly + * what SCSI command needs to be issued. The idea is that + * we merely inject the command into the queue (at the head + * for now), and then call the queue request function to actually + * process it. + */ +int scsi_insert_special_req(Scsi_Request * SRpnt, int at_head) +{ + unsigned long flags; + request_queue_t *q; + + ASSERT_LOCK(&io_request_lock, 0); + + /* + * The SCpnt already contains a request structure - we will doctor the + * thing up with the appropriate values and use that in the actual + * request queue. + */ + q = &SRpnt->sr_device->request_queue; + SRpnt->sr_request.cmd = SPECIAL; + SRpnt->sr_request.special = (void *) SRpnt; + + /* + * We have the option of inserting the head or the tail of the queue. + * Typically we use the tail for new ioctls and so forth. We use the + * head of the queue for things like a QUEUE_FULL message from a + * device, or a host that is unable to accept a particular command. + */ + spin_lock_irqsave(&io_request_lock, flags); + + if (at_head) { + list_add(&SRpnt->sr_request.queue, &q->queue_head); + } else { + /* + * FIXME(eric) - we always insert at the tail of the + * list. Otherwise ioctl commands would always take + * precedence over normal I/O. An ioctl on a busy + * disk might be delayed indefinitely because the + * request might not float high enough in the queue + * to be scheduled. + */ + list_add_tail(&SRpnt->sr_request.queue, &q->queue_head); } /* @@ -172,6 +228,7 @@ int scsi_init_cmd_errh(Scsi_Cmnd * SCpnt) */ SCpnt->old_use_sg = SCpnt->use_sg; SCpnt->old_cmd_len = SCpnt->cmd_len; + SCpnt->sc_old_data_direction = SCpnt->sc_data_direction; memcpy((void *) SCpnt->data_cmnd, (const void *) SCpnt->cmnd, sizeof(SCpnt->cmnd)); SCpnt->buffer = SCpnt->request_buffer; @@ -239,9 +296,8 @@ void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt) * in which case we need to request the blocks that come after * the bad sector. */ - SCpnt->request.next = q->current_request; - q->current_request = &SCpnt->request; SCpnt->request.special = (void *) SCpnt; + list_add(&SCpnt->request.queue, &q->queue_head); } /* @@ -260,7 +316,7 @@ void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt) * use function pointers to pick the right one. */ if (SDpnt->single_lun - && q->current_request == NULL + && list_empty(&q->queue_head) && SDpnt->device_busy == 0) { request_queue_t *q; @@ -270,6 +326,7 @@ void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt) if (((SHpnt->can_queue > 0) && (SHpnt->host_busy >= SHpnt->can_queue)) || (SHpnt->host_blocked) + || (SHpnt->host_self_blocked) || (SDpnt->device_blocked)) { break; } @@ -291,7 +348,8 @@ void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt) for (SDpnt = SHpnt->host_queue; SDpnt; SDpnt = SDpnt->next) { request_queue_t *q; if ((SHpnt->can_queue > 0 && (SHpnt->host_busy >= SHpnt->can_queue)) - || (SHpnt->host_blocked)) { + || (SHpnt->host_blocked) + || (SHpnt->host_self_blocked)) { break; } if (SDpnt->device_blocked || !SDpnt->starved) { @@ -772,6 +830,7 @@ void scsi_request_fn(request_queue_t * q) { struct request *req; Scsi_Cmnd *SCpnt; + Scsi_Request *SRpnt; Scsi_Device *SDpnt; struct Scsi_Host *SHpnt; struct Scsi_Device_Template *STpnt; @@ -802,13 +861,24 @@ void scsi_request_fn(request_queue_t * q) */ while (1 == 1) { /* + * Check this again - each time we loop through we will have + * released the lock and grabbed it again, so each time + * we need to check to see if the queue is plugged or not. + */ + if (SHpnt->in_recovery + || q->plugged) { + return; + } + + /* * If the device cannot accept another request, then quit. */ if (SDpnt->device_blocked) { break; } if ((SHpnt->can_queue > 0 && (SHpnt->host_busy >= SHpnt->can_queue)) - || (SHpnt->host_blocked)) { + || (SHpnt->host_blocked) + || (SHpnt->host_self_blocked)) { /* * If we are unable to process any commands at all for this * device, then we consider it to be starved. What this means @@ -850,18 +920,18 @@ void scsi_request_fn(request_queue_t * q) } /* - * Loop through all of the requests in this queue, and find - * one that is queueable. - */ - req = q->current_request; - - /* * If we couldn't find a request that could be queued, then we * can also quit. */ - if (!req) { + if (list_empty(&q->queue_head)) break; - } + + /* + * Loop through all of the requests in this queue, and find + * one that is queueable. + */ + req = blkdev_entry_next_request(&q->queue_head); + /* * Find the actual device driver associated with this command. * The SPECIAL requests are things like character device or @@ -875,6 +945,14 @@ void scsi_request_fn(request_queue_t * q) if (req->cmd == SPECIAL) { STpnt = NULL; SCpnt = (Scsi_Cmnd *) req->special; + SRpnt = (Scsi_Request *) req->special; + + if( SRpnt->sr_magic == SCSI_REQ_MAGIC ) { + SCpnt = scsi_allocate_device(SRpnt->sr_device, + FALSE, FALSE); + scsi_init_cmd_from_req(SCpnt, SRpnt); + } + } else { STpnt = scsi_get_request_dev(req); if (!STpnt) { @@ -922,8 +1000,7 @@ void scsi_request_fn(request_queue_t * q) * reason to search the list, because all of the commands * in this queue are for the same device. */ - q->current_request = req->next; - SCpnt->request.next = NULL; + blkdev_dequeue_request(req); if (req != &SCpnt->request) { memcpy(&SCpnt->request, req, sizeof(struct request)); @@ -932,7 +1009,6 @@ void scsi_request_fn(request_queue_t * q) * We have copied the data out of the request block - it is now in * a field in SCpnt. Release the request block. */ - req->next = NULL; req->rq_status = RQ_INACTIVE; wake_up(&wait_for_request); } @@ -1005,6 +1081,85 @@ void scsi_request_fn(request_queue_t * q) } /* + * Function: scsi_block_requests() + * + * Purpose: Utility function used by low-level drivers to prevent further + * commands from being queued to the device. + * + * Arguments: SHpnt - Host in question + * + * Returns: Nothing + * + * Lock status: No locks are assumed held. + * + * Notes: There is no timer nor any other means by which the requests + * get unblocked other than the low-level driver calling + * scsi_unblock_requests(). + */ +void scsi_block_requests(struct Scsi_Host * SHpnt) +{ + SHpnt->host_self_blocked = TRUE; +} + +/* + * Function: scsi_unblock_requests() + * + * Purpose: Utility function used by low-level drivers to allow further + * commands from being queued to the device. + * + * Arguments: SHpnt - Host in question + * + * Returns: Nothing + * + * Lock status: No locks are assumed held. + * + * Notes: There is no timer nor any other means by which the requests + * get unblocked other than the low-level driver calling + * scsi_unblock_requests(). + * + * This is done as an API function so that changes to the + * internals of the scsi mid-layer won't require wholesale + * changes to drivers that use this feature. + */ +void scsi_unblock_requests(struct Scsi_Host * SHpnt) +{ + SHpnt->host_self_blocked = FALSE; +} + + +/* + * Function: scsi_report_bus_reset() + * + * Purpose: Utility function used by low-level drivers to report that + * they have observed a bus reset on the bus being handled. + * + * Arguments: SHpnt - Host in question + * channel - channel on which reset was observed. + * + * Returns: Nothing + * + * Lock status: No locks are assumed held. + * + * Notes: This only needs to be called if the reset is one which + * originates from an unknown location. Resets originated + * by the mid-level itself don't need to call this, but there + * should be no harm. + * + * The main purpose of this is to make sure that a CHECK_CONDITION + * is properly treated. + */ +void scsi_report_bus_reset(struct Scsi_Host * SHpnt, int channel) +{ + Scsi_Device *SDloop; + for (SDloop = SHpnt->host_queue; SDloop; SDloop = SDloop->next) { + if (channel == SDloop->channel) { + SDloop->was_reset = 1; + SDloop->expecting_cc_ua = 1; + } + } +} + +/* * FIXME(eric) - these are empty stubs for the moment. I need to re-implement * host blocking from scratch. The theory is that hosts that wish to block * will register/deregister using these functions instead of the old way diff --git a/drivers/scsi/scsi_merge.c b/drivers/scsi/scsi_merge.c index c9a1edb60..d917d9306 100644 --- a/drivers/scsi/scsi_merge.c +++ b/drivers/scsi/scsi_merge.c @@ -307,6 +307,69 @@ recount_segments(Scsi_Cmnd * SCpnt) (((((long)(X)->b_data+(X)->b_size)|((long)(Y)->b_data)) & \ (DMA_CHUNK_SIZE - 1)) == 0) +#ifdef DMA_CHUNK_SIZE +static inline int scsi_new_mergeable(request_queue_t * q, + struct request * req, + struct Scsi_Host *SHpnt, + int max_segments) +{ + /* + * pci_map_sg will be able to merge these two + * into a single hardware sg entry, check if + * we'll have enough memory for the sg list. + * scsi.c allocates for this purpose + * min(64,sg_tablesize) entries. + */ + if (req->nr_segments >= max_segments && + req->nr_segments >= SHpnt->sg_tablesize) + return 0; + req->nr_segments++; + q->nr_segments++; + return 1; +} + +static inline int scsi_new_segment(request_queue_t * q, + struct request * req, + struct Scsi_Host *SHpnt, + int max_segments) +{ + /* + * pci_map_sg won't be able to map these two + * into a single hardware sg entry, so we have to + * check if things fit into sg_tablesize. + */ + if (req->nr_hw_segments >= SHpnt->sg_tablesize || + (req->nr_segments >= max_segments && + req->nr_segments >= SHpnt->sg_tablesize)) + return 0; + if (req->nr_segments >= max_segments) + return 0; + req->nr_hw_segments++; + req->nr_segments++; + q->nr_segments++; + return 1; +} +#else +static inline int scsi_new_segment(request_queue_t * q, + struct request * req, + struct Scsi_Host *SHpnt, + int max_segments) +{ + if (req->nr_segments < SHpnt->sg_tablesize && + req->nr_segments < max_segments) { + /* + * This will form the start of a new segment. Bump the + * counter. + */ + req->nr_segments++; + q->nr_segments++; + return 1; + } else { + return 0; + } +} +#endif + /* * Function: __scsi_merge_fn() * @@ -340,13 +403,14 @@ recount_segments(Scsi_Cmnd * SCpnt) * than to have 4 separate functions all doing roughly the * same thing. */ -__inline static int __scsi_merge_fn(request_queue_t * q, - struct request *req, - struct buffer_head *bh, - int use_clustering, - int dma_host) +__inline static int __scsi_back_merge_fn(request_queue_t * q, + struct request *req, + struct buffer_head *bh, + int max_segments, + int use_clustering, + int dma_host) { - unsigned int sector, count; + unsigned int count; unsigned int segment_size = 0; Scsi_Device *SDpnt; struct Scsi_Host *SHpnt; @@ -354,130 +418,97 @@ __inline static int __scsi_merge_fn(request_queue_t * q, SDpnt = (Scsi_Device *) q->queuedata; SHpnt = SDpnt->host; - count = bh->b_size >> 9; - sector = bh->b_rsector; + if (max_segments > 64) + max_segments = 64; - /* - * We come in here in one of two cases. The first is that we - * are checking to see if we can add the buffer to the end of the - * request, the other is to see if we should add the request to the - * start. - */ - if (req->sector + req->nr_sectors == sector) { - if (use_clustering) { - /* - * See if we can do this without creating another - * scatter-gather segment. In the event that this is a - * DMA capable host, make sure that a segment doesn't span - * the DMA threshold boundary. - */ - if (dma_host && - virt_to_phys(req->bhtail->b_data) - 1 == ISA_DMA_THRESHOLD) { - goto new_end_segment; - } - if (CONTIGUOUS_BUFFERS(req->bhtail, bh)) { + if (use_clustering) { + /* + * See if we can do this without creating another + * scatter-gather segment. In the event that this is a + * DMA capable host, make sure that a segment doesn't span + * the DMA threshold boundary. + */ + if (dma_host && + virt_to_phys(req->bhtail->b_data) - 1 == ISA_DMA_THRESHOLD) { + goto new_end_segment; + } + if (CONTIGUOUS_BUFFERS(req->bhtail, bh)) { #ifdef DMA_SEGMENT_SIZE_LIMITED - if( dma_host - && virt_to_phys(bh->b_data) - 1 >= ISA_DMA_THRESHOLD ) { - segment_size = 0; - count = __count_segments(req, use_clustering, dma_host, &segment_size); - if( segment_size + bh->b_size > PAGE_SIZE ) { - goto new_end_segment; - } + if( dma_host + && virt_to_phys(bh->b_data) - 1 >= ISA_DMA_THRESHOLD ) { + segment_size = 0; + count = __count_segments(req, use_clustering, dma_host, &segment_size); + if( segment_size + bh->b_size > PAGE_SIZE ) { + goto new_end_segment; } -#endif - /* - * This one is OK. Let it go. - */ - return 1; } +#endif + /* + * This one is OK. Let it go. + */ + return 1; } - new_end_segment: + } + new_end_segment: #ifdef DMA_CHUNK_SIZE - if (MERGEABLE_BUFFERS(req->bhtail, bh)) - goto new_mergeable; + if (MERGEABLE_BUFFERS(req->bhtail, bh)) + return scsi_new_mergeable(q, req, SHpnt, max_segments); #endif - goto new_segment; - } else { - if (req->sector - count != sector) { - /* Attempt to merge sector that doesn't belong */ - BUG(); + return scsi_new_segment(q, req, SHpnt, max_segments); +} + +__inline static int __scsi_front_merge_fn(request_queue_t * q, + struct request *req, + struct buffer_head *bh, + int max_segments, + int use_clustering, + int dma_host) +{ + unsigned int count; + unsigned int segment_size = 0; + Scsi_Device *SDpnt; + struct Scsi_Host *SHpnt; + + SDpnt = (Scsi_Device *) q->queuedata; + SHpnt = SDpnt->host; + + if (max_segments > 64) + max_segments = 64; + + if (use_clustering) { + /* + * See if we can do this without creating another + * scatter-gather segment. In the event that this is a + * DMA capable host, make sure that a segment doesn't span + * the DMA threshold boundary. + */ + if (dma_host && + virt_to_phys(bh->b_data) - 1 == ISA_DMA_THRESHOLD) { + goto new_start_segment; } - if (use_clustering) { - /* - * See if we can do this without creating another - * scatter-gather segment. In the event that this is a - * DMA capable host, make sure that a segment doesn't span - * the DMA threshold boundary. - */ - if (dma_host && - virt_to_phys(bh->b_data) - 1 == ISA_DMA_THRESHOLD) { - goto new_start_segment; - } - if (CONTIGUOUS_BUFFERS(bh, req->bh)) { + if (CONTIGUOUS_BUFFERS(bh, req->bh)) { #ifdef DMA_SEGMENT_SIZE_LIMITED - if( dma_host - && virt_to_phys(bh->b_data) - 1 >= ISA_DMA_THRESHOLD ) { - segment_size = bh->b_size; - count = __count_segments(req, use_clustering, dma_host, &segment_size); - if( count != req->nr_segments ) { - goto new_start_segment; - } + if( dma_host + && virt_to_phys(bh->b_data) - 1 >= ISA_DMA_THRESHOLD ) { + segment_size = bh->b_size; + count = __count_segments(req, use_clustering, dma_host, &segment_size); + if( count != req->nr_segments ) { + goto new_start_segment; } -#endif - /* - * This one is OK. Let it go. - */ - return 1; } - } - new_start_segment: -#ifdef DMA_CHUNK_SIZE - if (MERGEABLE_BUFFERS(bh, req->bh)) - goto new_mergeable; #endif - goto new_segment; + /* + * This one is OK. Let it go. + */ + return 1; + } } + new_start_segment: #ifdef DMA_CHUNK_SIZE - new_mergeable: - /* - * pci_map_sg will be able to merge these two - * into a single hardware sg entry, check if - * we'll have enough memory for the sg list. - * scsi.c allocates for this purpose - * min(64,sg_tablesize) entries. - */ - if (req->nr_segments >= 64 && - req->nr_segments >= SHpnt->sg_tablesize) - return 0; - req->nr_segments++; - return 1; - new_segment: - /* - * pci_map_sg won't be able to map these two - * into a single hardware sg entry, so we have to - * check if things fit into sg_tablesize. - */ - if (req->nr_hw_segments >= SHpnt->sg_tablesize || - (req->nr_segments >= 64 && - req->nr_segments >= SHpnt->sg_tablesize)) - return 0; - req->nr_hw_segments++; - req->nr_segments++; - return 1; -#else - new_segment: - if (req->nr_segments < SHpnt->sg_tablesize) { - /* - * This will form the start of a new segment. Bump the - * counter. - */ - req->nr_segments++; - return 1; - } else { - return 0; - } + if (MERGEABLE_BUFFERS(bh, req->bh)) + return scsi_new_mergeable(q, req, SHpnt, max_segments); #endif + return scsi_new_segment(q, req, SHpnt, max_segments); } /* @@ -497,23 +528,34 @@ __inline static int __scsi_merge_fn(request_queue_t * q, * Notes: Optimized for different cases depending upon whether * ISA DMA is in use and whether clustering should be used. */ -#define MERGEFCT(_FUNCTION, _CLUSTER, _DMA) \ -static int _FUNCTION(request_queue_t * q, \ - struct request * req, \ - struct buffer_head * bh) \ -{ \ - int ret; \ - SANITY_CHECK(req, _CLUSTER, _DMA); \ - ret = __scsi_merge_fn(q, req, bh, _CLUSTER, _DMA); \ - return ret; \ +#define MERGEFCT(_FUNCTION, _BACK_FRONT, _CLUSTER, _DMA) \ +static int _FUNCTION(request_queue_t * q, \ + struct request * req, \ + struct buffer_head * bh, \ + int max_segments) \ +{ \ + int ret; \ + SANITY_CHECK(req, _CLUSTER, _DMA); \ + ret = __scsi_ ## _BACK_FRONT ## _merge_fn(q, \ + req, \ + bh, \ + max_segments, \ + _CLUSTER, \ + _DMA); \ + return ret; \ } /* Version with use_clustering 0 and dma_host 1 is not necessary, * since the only use of dma_host above is protected by use_clustering. */ -MERGEFCT(scsi_merge_fn_, 0, 0) -MERGEFCT(scsi_merge_fn_c, 1, 0) -MERGEFCT(scsi_merge_fn_dc, 1, 1) +MERGEFCT(scsi_back_merge_fn_, back, 0, 0) +MERGEFCT(scsi_back_merge_fn_c, back, 1, 0) +MERGEFCT(scsi_back_merge_fn_dc, back, 1, 1) + +MERGEFCT(scsi_front_merge_fn_, front, 0, 0) +MERGEFCT(scsi_front_merge_fn_c, front, 1, 0) +MERGEFCT(scsi_front_merge_fn_dc, front, 1, 1) + /* * Function: __scsi_merge_requests_fn() * @@ -550,6 +592,7 @@ MERGEFCT(scsi_merge_fn_dc, 1, 1) __inline static int __scsi_merge_requests_fn(request_queue_t * q, struct request *req, struct request *next, + int max_segments, int use_clustering, int dma_host) { @@ -559,11 +602,14 @@ __inline static int __scsi_merge_requests_fn(request_queue_t * q, SDpnt = (Scsi_Device *) q->queuedata; SHpnt = SDpnt->host; + if (max_segments > 64) + max_segments = 64; + #ifdef DMA_CHUNK_SIZE /* If it would not fit into prepared memory space for sg chain, * then don't allow the merge. */ - if (req->nr_segments + next->nr_segments - 1 > 64 && + if (req->nr_segments + next->nr_segments - 1 > max_segments && req->nr_segments + next->nr_segments - 1 > SHpnt->sg_tablesize) { return 0; } @@ -619,6 +665,7 @@ __inline static int __scsi_merge_requests_fn(request_queue_t * q, * This one is OK. Let it go. */ req->nr_segments += next->nr_segments - 1; + q->nr_segments--; #ifdef DMA_CHUNK_SIZE req->nr_hw_segments += next->nr_hw_segments - 1; #endif @@ -627,7 +674,7 @@ __inline static int __scsi_merge_requests_fn(request_queue_t * q, } dont_combine: #ifdef DMA_CHUNK_SIZE - if (req->nr_segments + next->nr_segments > 64 && + if (req->nr_segments + next->nr_segments > max_segments && req->nr_segments + next->nr_segments > SHpnt->sg_tablesize) { return 0; } @@ -650,7 +697,8 @@ __inline static int __scsi_merge_requests_fn(request_queue_t * q, * Make sure we can fix something that is the sum of the two. * A slightly stricter test than we had above. */ - if (req->nr_segments + next->nr_segments > SHpnt->sg_tablesize) { + if (req->nr_segments + next->nr_segments > max_segments && + req->nr_segments + next->nr_segments > SHpnt->sg_tablesize) { return 0; } else { /* @@ -683,11 +731,12 @@ __inline static int __scsi_merge_requests_fn(request_queue_t * q, #define MERGEREQFCT(_FUNCTION, _CLUSTER, _DMA) \ static int _FUNCTION(request_queue_t * q, \ struct request * req, \ - struct request * next) \ + struct request * next, \ + int max_segments) \ { \ int ret; \ SANITY_CHECK(req, _CLUSTER, _DMA); \ - ret = __scsi_merge_requests_fn(q, req, next, _CLUSTER, _DMA); \ + ret = __scsi_merge_requests_fn(q, req, next, max_segments, _CLUSTER, _DMA); \ return ret; \ } @@ -1068,7 +1117,7 @@ void initialize_merge_fn(Scsi_Device * SDpnt) * pick a new one. */ #if 0 - if (q->merge_fn != NULL) + if (q->back_merge_fn && q->front_merge_fn) return; #endif /* @@ -1083,19 +1132,23 @@ void initialize_merge_fn(Scsi_Device * SDpnt) * rather than rely upon the default behavior of ll_rw_blk. */ if (!CLUSTERABLE_DEVICE(SHpnt, SDpnt) && SHpnt->unchecked_isa_dma == 0) { - q->merge_fn = scsi_merge_fn_; + q->back_merge_fn = scsi_back_merge_fn_; + q->front_merge_fn = scsi_front_merge_fn_; q->merge_requests_fn = scsi_merge_requests_fn_; SDpnt->scsi_init_io_fn = scsi_init_io_v; } else if (!CLUSTERABLE_DEVICE(SHpnt, SDpnt) && SHpnt->unchecked_isa_dma != 0) { - q->merge_fn = scsi_merge_fn_; + q->back_merge_fn = scsi_back_merge_fn_; + q->front_merge_fn = scsi_front_merge_fn_; q->merge_requests_fn = scsi_merge_requests_fn_; SDpnt->scsi_init_io_fn = scsi_init_io_vd; } else if (CLUSTERABLE_DEVICE(SHpnt, SDpnt) && SHpnt->unchecked_isa_dma == 0) { - q->merge_fn = scsi_merge_fn_c; + q->back_merge_fn = scsi_back_merge_fn_c; + q->front_merge_fn = scsi_front_merge_fn_c; q->merge_requests_fn = scsi_merge_requests_fn_c; SDpnt->scsi_init_io_fn = scsi_init_io_vc; } else if (CLUSTERABLE_DEVICE(SHpnt, SDpnt) && SHpnt->unchecked_isa_dma != 0) { - q->merge_fn = scsi_merge_fn_dc; + q->back_merge_fn = scsi_back_merge_fn_dc; + q->front_merge_fn = scsi_front_merge_fn_dc; q->merge_requests_fn = scsi_merge_requests_fn_dc; SDpnt->scsi_init_io_fn = scsi_init_io_vdc; } diff --git a/drivers/scsi/scsi_obsolete.c b/drivers/scsi/scsi_obsolete.c index ee1041d88..a957a4c01 100644 --- a/drivers/scsi/scsi_obsolete.c +++ b/drivers/scsi/scsi_obsolete.c @@ -231,6 +231,8 @@ static void scsi_request_sense(Scsi_Cmnd * SCpnt) SCpnt->use_sg = 0; SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); SCpnt->result = 0; + SCpnt->sc_data_direction = SCSI_DATA_READ; + /* * Ugly, ugly. The newer interfaces all assume that the lock * isn't held. Mustn't disappoint, or we deadlock the system. @@ -374,6 +376,7 @@ void scsi_old_done(Scsi_Cmnd * SCpnt) if (SCpnt->flags & WAS_SENSE) { SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; + SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; } switch (host_byte(result)) { case DID_OK: @@ -633,6 +636,7 @@ void scsi_old_done(Scsi_Cmnd * SCpnt) SCpnt->request_bufflen = SCpnt->bufflen; SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; + SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; SCpnt->result = 0; /* * Ugly, ugly. The newer interfaces all @@ -649,6 +653,7 @@ void scsi_old_done(Scsi_Cmnd * SCpnt) } if (status == CMD_FINISHED) { + Scsi_Request *SRpnt; #ifdef DEBUG printk("Calling done function - at address %p\n", SCpnt->done); #endif @@ -658,6 +663,7 @@ void scsi_old_done(Scsi_Cmnd * SCpnt) SCpnt->result = result | ((exit & 0xff) << 24); SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; + SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; /* * The upper layers assume the lock isn't held. We mustn't * disappoint them. When the new error handling code is in @@ -665,6 +671,16 @@ void scsi_old_done(Scsi_Cmnd * SCpnt) * it isn't an issue. */ spin_unlock_irq(&io_request_lock); + SRpnt = SCpnt->sc_request; + if( SRpnt != NULL ) { + SRpnt->sr_result = SRpnt->sr_command->result; + if( SRpnt->sr_result != 0 ) { + memcpy(SRpnt->sr_sense_buffer, + SRpnt->sr_command->sense_buffer, + sizeof(SRpnt->sr_sense_buffer)); + } + } + SCpnt->done(SCpnt); spin_lock_irq(&io_request_lock); } diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 645edb67c..40a7cb124 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -455,6 +455,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, int *sparse_lun, Scsi_Device ** SDpnt2, struct Scsi_Host *shpnt, char *scsi_result) { + char devname[64]; unsigned char scsi_cmd[MAX_COMMAND_SIZE]; struct Scsi_Device_Template *sdtpnt; Scsi_Device *SDtail, *SDpnt = *SDpnt2; @@ -462,6 +463,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, int bflags, type = -1; static int ghost_channel=-1, ghost_dev=-1; int org_lun = lun; + extern devfs_handle_t scsi_devfs_handle; SDpnt->host = shpnt; SDpnt->id = dev; @@ -500,6 +502,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, SCpnt->target = SDpnt->id; SCpnt->lun = SDpnt->lun; SCpnt->channel = SDpnt->channel; + SCpnt->sc_data_direction = SCSI_DATA_NONE; scsi_wait_cmd (SCpnt, (void *) scsi_cmd, (void *) NULL, @@ -537,6 +540,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, scsi_cmd[4] = 255; scsi_cmd[5] = 0; SCpnt->cmd_len = 0; + SCpnt->sc_data_direction = SCSI_DATA_READ; scsi_wait_cmd (SCpnt, (void *) scsi_cmd, (void *) scsi_result, @@ -637,6 +641,11 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, print_inquiry(scsi_result); + sprintf (devname, "host%d/bus%d/target%d/lun%d", + SDpnt->host->host_no, SDpnt->channel, SDpnt->id, SDpnt->lun); + if (SDpnt->de) printk ("DEBUG: dir: \"%s\" already exists\n", devname); + else SDpnt->de = devfs_mk_dir (scsi_devfs_handle, devname, 0, NULL); + for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) if (sdtpnt->detect) @@ -696,6 +705,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, scsi_cmd[4] = 0x2a; scsi_cmd[5] = 0; SCpnt->cmd_len = 0; + SCpnt->sc_data_direction = SCSI_DATA_READ; scsi_wait_cmd (SCpnt, (void *) scsi_cmd, (void *) scsi_result, 0x2a, SCSI_TIMEOUT, 3); diff --git a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c index 3379299d8..f6e8939a6 100644 --- a/drivers/scsi/scsi_syms.c +++ b/drivers/scsi/scsi_syms.c @@ -28,16 +28,11 @@ #include "sd.h" #include <scsi/scsicam.h> + /* * This source file contains the symbol table used by scsi loadable * modules. */ - -extern void print_command(unsigned char *command); -extern void print_sense(const char *devclass, Scsi_Cmnd * SCpnt); - -extern const char *const scsi_device_types[]; - EXPORT_SYMBOL(scsi_register_module); EXPORT_SYMBOL(scsi_unregister_module); EXPORT_SYMBOL(scsi_free); @@ -53,6 +48,7 @@ EXPORT_SYMBOL(scsi_command_size); EXPORT_SYMBOL(scsi_ioctl); EXPORT_SYMBOL(print_command); EXPORT_SYMBOL(print_sense); +EXPORT_SYMBOL(print_req_sense); EXPORT_SYMBOL(print_msg); EXPORT_SYMBOL(print_status); EXPORT_SYMBOL(scsi_dma_free_sectors); @@ -67,6 +63,15 @@ EXPORT_SYMBOL(scsi_ioctl_send_command); EXPORT_SYMBOL(scsi_logging_level); #endif +EXPORT_SYMBOL(scsi_allocate_request); +EXPORT_SYMBOL(scsi_release_request); +EXPORT_SYMBOL(scsi_wait_req); +EXPORT_SYMBOL(scsi_do_req); + +EXPORT_SYMBOL(scsi_report_bus_reset); +EXPORT_SYMBOL(scsi_block_requests); +EXPORT_SYMBOL(scsi_unblock_requests); + EXPORT_SYMBOL(scsi_get_host_dev); EXPORT_SYMBOL(scsi_free_host_dev); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index a897c9597..43187600b 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -17,6 +17,8 @@ * * Modified by Jirka Hanika geo@ff.cuni.cz to support more * scsi disks using eight major numbers. + * + * Modified by Richard Gooch rgooch@atnf.csiro.au to support devfs. */ #include <linux/config.h> @@ -307,9 +309,11 @@ static int sd_init_command(Scsi_Cmnd * SCpnt) return 0; } SCpnt->cmnd[0] = WRITE_6; + SCpnt->sc_data_direction = SCSI_DATA_WRITE; break; case READ: SCpnt->cmnd[0] = READ_6; + SCpnt->sc_data_direction = SCSI_DATA_READ; break; default: panic("Unknown sd command %d\n", SCpnt->request.cmd); @@ -489,7 +493,8 @@ static struct gendisk sd_gendisk = NULL, /* block sizes */ 0, /* number */ NULL, /* internal */ - NULL /* next */ + NULL, /* next */ + &sd_fops, /* file operations */ }; static struct gendisk *sd_gendisks = &sd_gendisk; @@ -692,6 +697,7 @@ static int sd_init_onedisk(int i) SCpnt->cmd_len = 0; SCpnt->sense_buffer[0] = 0; SCpnt->sense_buffer[2] = 0; + SCpnt->sc_data_direction = SCSI_DATA_READ; scsi_wait_cmd (SCpnt, (void *) cmd, (void *) buffer, 0/*512*/, SD_TIMEOUT, MAX_RETRIES); @@ -703,6 +709,22 @@ static int sd_init_onedisk(int i) break; } + /* + * If the drive has indicated to us that it doesn't have + * any media in it, don't bother with any of the rest of + * this crap. + */ + if( the_result != 0 + && ((driver_byte(the_result) & DRIVER_SENSE) != 0) + && SCpnt->sense_buffer[2] == UNIT_ATTENTION + && SCpnt->sense_buffer[12] == 0x3A ) { + rscsi_disks[i].capacity = 0x1fffff; + sector_size = 512; + rscsi_disks[i].device->changed = 1; + rscsi_disks[i].ready = 0; + break; + } + /* Look for non-removable devices that return NOT_READY. * Issue command to spin up drive for these cases. */ if (the_result && !rscsi_disks[i].device->removable && @@ -719,8 +741,9 @@ static int sd_init_onedisk(int i) SCpnt->sense_buffer[0] = 0; SCpnt->sense_buffer[2] = 0; + SCpnt->sc_data_direction = SCSI_DATA_READ; scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer, - 512, SD_TIMEOUT, MAX_RETRIES); + 0/*512*/, SD_TIMEOUT, MAX_RETRIES); } spintime = 1; spintime_value = jiffies; @@ -749,6 +772,7 @@ static int sd_init_onedisk(int i) SCpnt->sense_buffer[0] = 0; SCpnt->sense_buffer[2] = 0; + SCpnt->sc_data_direction = SCSI_DATA_READ; scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer, 8, SD_TIMEOUT, MAX_RETRIES); @@ -900,6 +924,7 @@ static int sd_init_onedisk(int i) SCpnt->sense_buffer[2] = 0; /* same code as READCAPA !! */ + SCpnt->sc_data_direction = SCSI_DATA_READ; scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer, 512, SD_TIMEOUT, MAX_RETRIES); @@ -948,7 +973,7 @@ static int sd_init() if (!sd_registered) { for (i = 0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++) { - if (register_blkdev(SD_MAJOR(i), "sd", &sd_fops)) { + if (devfs_register_blkdev(SD_MAJOR(i), "sd", &sd_fops)) { printk("Unable to get major %d for SCSI disk\n", SD_MAJOR(i)); return 1; } @@ -988,6 +1013,15 @@ static int sd_init() sd_gendisks = (struct gendisk *) kmalloc(N_USED_SD_MAJORS * sizeof(struct gendisk), GFP_ATOMIC); for (i = 0; i < N_USED_SD_MAJORS; i++) { + sd_gendisks[i] = sd_gendisk; + sd_gendisks[i].de_arr = kmalloc (SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].de_arr, + GFP_ATOMIC); + memset (sd_gendisks[i].de_arr, 0, + SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].de_arr); + sd_gendisks[i].flags = kmalloc (SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].flags, + GFP_ATOMIC); + memset (sd_gendisks[i].flags, 0, + SCSI_DISKS_PER_MAJOR * sizeof *sd_gendisks[i].flags); sd_gendisks[i].major = SD_MAJOR(i); sd_gendisks[i].major_name = "sd"; sd_gendisks[i].minor_shift = 4; @@ -1061,8 +1095,10 @@ static int sd_detect(Scsi_Device * SDp) return 1; } + static int sd_attach(Scsi_Device * SDp) { + unsigned int devnum; Scsi_Disk *dpnt; int i; @@ -1084,6 +1120,10 @@ static int sd_attach(Scsi_Device * SDp) rscsi_disks[i].has_part_table = 0; sd_template.nr_dev++; SD_GENDISK(i).nr_real++; + devnum = i % SCSI_DISKS_PER_MAJOR; + SD_GENDISK(i).de_arr[devnum] = SDp->de; + if (SDp->removable) + SD_GENDISK(i).flags[devnum] |= GENHD_FL_REMOVABLE; return 0; } @@ -1182,6 +1222,8 @@ static void sd_detach(Scsi_Device * SDp) sd_gendisks->part[index].nr_sects = 0; sd_sizes[index] = 0; } + devfs_register_partitions (&SD_GENDISK (i), + SD_MINOR_NUMBER (start), 1); /* unregister_disk() */ dpnt->has_part_table = 0; dpnt->device = NULL; @@ -1212,7 +1254,7 @@ void cleanup_module(void) scsi_unregister_module(MODULE_SCSI_DEV, &sd_template); for (i = 0; i <= (sd_template.dev_max - 1) / SCSI_DISKS_PER_MAJOR; i++) - unregister_blkdev(SD_MAJOR(i), "sd"); + devfs_unregister_blkdev(SD_MAJOR(i), "sd"); sd_registered--; if (rscsi_disks != NULL) { diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index e092f40a1..2ccf9ac09 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -9,6 +9,8 @@ * Version 2 and 3 extensions to driver: * Copyright (C) 1998, 1999 Douglas Gilbert * + * Modified 19-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support + * * 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) @@ -50,6 +52,7 @@ #include <linux/fcntl.h> #include <linux/init.h> #include <linux/poll.h> + #include <asm/io.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -179,6 +182,7 @@ typedef struct sg_device /* holds the state of each scsi generic device */ wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */ int sg_tablesize; /* adapter's max scatter-gather table size */ Sg_fd * headfp; /* first open fd belonging to this device */ + devfs_handle_t de; kdev_t i_rdev; /* holds device major+minor number */ char exclude; /* opened for exclusive access */ char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ @@ -1075,7 +1079,7 @@ static int sg_init() if (sg_template.dev_noticed == 0) return 0; if(!sg_registered) { - if (register_chrdev(SCSI_GENERIC_MAJOR,"sg",&sg_fops)) + if (devfs_register_chrdev(SCSI_GENERIC_MAJOR,"sg",&sg_fops)) { printk("Unable to get major %d for generic SCSI device\n", SCSI_GENERIC_MAJOR); @@ -1150,6 +1154,10 @@ static int sg_attach(Scsi_Device * scsidp) sdp->sgdebug = 0; sdp->sg_tablesize = scsidp->host ? scsidp->host->sg_tablesize : 0; sdp->i_rdev = MKDEV(SCSI_GENERIC_MAJOR, k); + sdp->de = devfs_register (scsidp->de, "generic", 7, DEVFS_FL_NONE, + SCSI_GENERIC_MAJOR, k, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0, + &sg_fops, NULL); sg_template.nr_dev++; return 0; } @@ -1194,6 +1202,8 @@ static void sg_detach(Scsi_Device * scsidp) SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k)); sdp->device = NULL; } + devfs_unregister (sdp->de); + sdp->de = NULL; scsidp->attached--; sg_template.nr_dev--; /* avoid associated device /dev/sg? being incremented @@ -1219,7 +1229,7 @@ int init_module(void) { void cleanup_module( void) { scsi_unregister_module(MODULE_SCSI_DEV, &sg_template); - unregister_chrdev(SCSI_GENERIC_MAJOR, "sg"); + devfs_unregister_chrdev(SCSI_GENERIC_MAJOR, "sg"); #ifdef CONFIG_PROC_FS sg_proc_cleanup(); diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 365c90660..35f18a53c 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -23,6 +23,8 @@ * Modified by Jens Axboe <axboe@image.dk> - Uniform sr_packet() * interface, capabilities probe additions, ioctl cleanups, etc. * + * Modified by Richard Gooch <rgooch@atnf.csiro.au> to support devfs + * */ #include <linux/module.h> @@ -87,7 +89,6 @@ static int sr_open(struct cdrom_device_info *, int); void get_sectorsize(int); void get_capabilities(int); -void requeue_sr_request(Scsi_Cmnd * SCpnt); static int sr_media_change(struct cdrom_device_info *, int); static int sr_packet(struct cdrom_device_info *, struct cdrom_generic_command *); @@ -325,9 +326,11 @@ static int sr_init_command(Scsi_Cmnd * SCpnt) return 0; } SCpnt->cmnd[0] = WRITE_10; + SCpnt->sc_data_direction = SCSI_DATA_WRITE; break; case READ: SCpnt->cmnd[0] = READ_10; + SCpnt->sc_data_direction = SCSI_DATA_READ; break; default: panic("Unknown sr command %d\n", SCpnt->request.cmd); @@ -462,36 +465,37 @@ void get_sectorsize(int i) unsigned char *buffer; int the_result, retries; int sector_size; - Scsi_Cmnd *SCpnt; + Scsi_Request *SRpnt; buffer = (unsigned char *) scsi_malloc(512); - SCpnt = scsi_allocate_device(scsi_CDs[i].device, 1, FALSE); + SRpnt = scsi_allocate_request(scsi_CDs[i].device); retries = 3; do { cmd[0] = READ_CAPACITY; cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0; memset((void *) &cmd[2], 0, 8); - SCpnt->request.rq_status = RQ_SCSI_BUSY; /* Mark as really busy */ - SCpnt->cmd_len = 0; + SRpnt->sr_request.rq_status = RQ_SCSI_BUSY; /* Mark as really busy */ + SRpnt->sr_cmd_len = 0; memset(buffer, 0, 8); /* Do the command and wait.. */ - scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer, + SRpnt->sr_data_direction = SCSI_DATA_READ; + scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer, 512, SR_TIMEOUT, MAX_RETRIES); - the_result = SCpnt->result; + the_result = SRpnt->sr_result; retries--; } while (the_result && retries); - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; if (the_result) { scsi_CDs[i].capacity = 0x1fffff; @@ -568,7 +572,7 @@ void get_capabilities(int i) cmd[2] = 0x2a; cmd[4] = 128; cmd[3] = cmd[5] = 0; - rc = sr_do_ioctl(i, cmd, buffer, 128, 1); + rc = sr_do_ioctl(i, cmd, buffer, 128, 1, SCSI_DATA_READ); if (-EINVAL == rc) { /* failed, drive has'nt this mode page */ @@ -633,19 +637,19 @@ void get_capabilities(int i) */ static int sr_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command *cgc) { - Scsi_Cmnd *SCpnt; + Scsi_Request *SRpnt; Scsi_Device *device = scsi_CDs[MINOR(cdi->dev)].device; unsigned char *buffer = cgc->buffer; int buflen; /* get the device */ - SCpnt = scsi_allocate_device(device, 1, FALSE); - if (SCpnt == NULL) + SRpnt = scsi_allocate_request(device); + if (SRpnt == NULL) return -ENODEV; /* this just doesn't seem right /axboe */ /* use buffer for ISA DMA */ buflen = (cgc->buflen + 511) & ~511; - if (cgc->buffer && SCpnt->host->unchecked_isa_dma && + if (cgc->buffer && SRpnt->sr_host->unchecked_isa_dma && (virt_to_phys(cgc->buffer) + cgc->buflen - 1 > ISA_DMA_THRESHOLD)) { buffer = scsi_malloc(buflen); if (buffer == NULL) { @@ -658,20 +662,25 @@ static int sr_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command cgc->cmd[1] |= device->lun << 5; /* do the locking and issue the command */ - SCpnt->request.rq_dev = cdi->dev; + SRpnt->sr_request.rq_dev = cdi->dev; /* scsi_wait_cmd sets the command length */ - SCpnt->cmd_len = 0; + SRpnt->sr_cmd_len = 0; + + /* + * FIXME(eric) - need to set the data direction here. + */ + SRpnt->sr_data_direction = SCSI_DATA_UNKNOWN; - scsi_wait_cmd(SCpnt, (void *) cgc->cmd, (void *) buffer, cgc->buflen, + scsi_wait_req(SRpnt, (void *) cgc->cmd, (void *) buffer, cgc->buflen, SR_TIMEOUT, MAX_RETRIES); - if ((cgc->stat = SCpnt->result)) - cgc->sense = (struct request_sense *) SCpnt->sense_buffer; + if ((cgc->stat = SRpnt->sr_result)) + cgc->sense = (struct request_sense *) SRpnt->sr_sense_buffer; /* release */ - SCpnt->request.rq_dev = MKDEV(0, 0); - scsi_release_command(SCpnt); - SCpnt = NULL; + SRpnt->sr_request.rq_dev = MKDEV(0, 0); + scsi_release_request(SRpnt); + SRpnt = NULL; /* write DMA buffer back if used */ if (buffer && (buffer != cgc->buffer)) { @@ -693,7 +702,7 @@ static int sr_init() return 0; if (!sr_registered) { - if (register_blkdev(MAJOR_NR, "sr", &cdrom_fops)) { + if (devfs_register_blkdev(MAJOR_NR, "sr", &cdrom_fops)) { printk("Unable to get major %d for SCSI-CD\n", MAJOR_NR); return 1; } @@ -767,6 +776,11 @@ void sr_finish() sprintf(name, "sr%d", i); strcpy(scsi_CDs[i].cdi.name, name); + scsi_CDs[i].cdi.de = + devfs_register (scsi_CDs[i].device->de, "cd", 2, + DEVFS_FL_DEFAULT, MAJOR_NR, i, + S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, + &cdrom_fops, NULL); register_cdrom(&scsi_CDs[i].cdi); } @@ -828,7 +842,7 @@ int init_module(void) void cleanup_module(void) { scsi_unregister_module(MODULE_SCSI_DEV, &sr_template); - unregister_blkdev(MAJOR_NR, "sr"); + devfs_unregister_blkdev(MAJOR_NR, "sr"); sr_registered--; if (scsi_CDs != NULL) { kfree((char *) scsi_CDs); diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h index e4aad11ab..d3239e647 100644 --- a/drivers/scsi/sr.h +++ b/drivers/scsi/sr.h @@ -36,7 +36,7 @@ typedef struct { extern Scsi_CD *scsi_CDs; -int sr_do_ioctl(int, unsigned char *, void *, unsigned, int); +int sr_do_ioctl(int, unsigned char *, void *, unsigned, int, int); int sr_lock_door(struct cdrom_device_info *, int); int sr_tray_move(struct cdrom_device_info *, int); diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c index 9d08b209b..32e71e06f 100644 --- a/drivers/scsi/sr_ioctl.c +++ b/drivers/scsi/sr_ioctl.c @@ -34,20 +34,21 @@ extern void get_sectorsize(int); error code is. Normally the UNIT_ATTENTION code will automatically clear after one error */ -int sr_do_ioctl(int target, unsigned char *sr_cmd, void *buffer, unsigned buflength, int quiet) +int sr_do_ioctl(int target, unsigned char *sr_cmd, void *buffer, unsigned buflength, int quiet, int readwrite) { - Scsi_Cmnd *SCpnt; + Scsi_Request *SRpnt; Scsi_Device *SDev; struct request *req; int result, err = 0, retries = 0; char *bounce_buffer; SDev = scsi_CDs[target].device; - SCpnt = scsi_allocate_device(scsi_CDs[target].device, 1, FALSE); + SRpnt = scsi_allocate_request(scsi_CDs[target].device); + SRpnt->sr_data_direction = readwrite; /* use ISA DMA buffer if necessary */ - SCpnt->request.buffer = buffer; - if (buffer && SCpnt->host->unchecked_isa_dma && + SRpnt->sr_request.buffer = buffer; + if (buffer && SRpnt->sr_host->unchecked_isa_dma && (virt_to_phys(buffer) + buflength - 1 > ISA_DMA_THRESHOLD)) { bounce_buffer = (char *) scsi_malloc((buflength + 511) & ~511); if (bounce_buffer == NULL) { @@ -62,21 +63,21 @@ int sr_do_ioctl(int target, unsigned char *sr_cmd, void *buffer, unsigned buflen return -ENODEV; - scsi_wait_cmd(SCpnt, (void *) sr_cmd, (void *) buffer, buflength, + scsi_wait_req(SRpnt, (void *) sr_cmd, (void *) buffer, buflength, IOCTL_TIMEOUT, IOCTL_RETRIES); - req = &SCpnt->request; - if (SCpnt->buffer && req->buffer && SCpnt->buffer != req->buffer) { - memcpy(req->buffer, SCpnt->buffer, SCpnt->bufflen); - scsi_free(SCpnt->buffer, (SCpnt->bufflen + 511) & ~511); - SCpnt->buffer = req->buffer; + req = &SRpnt->sr_request; + if (SRpnt->sr_buffer && req->buffer && SRpnt->sr_buffer != req->buffer) { + memcpy(req->buffer, SRpnt->sr_buffer, SRpnt->sr_bufflen); + scsi_free(SRpnt->sr_buffer, (SRpnt->sr_bufflen + 511) & ~511); + SRpnt->sr_buffer = req->buffer; } - result = SCpnt->result; + result = SRpnt->sr_result; /* Minimal error checking. Ignore cases we know about, and report the rest. */ if (driver_byte(result) != 0) { - switch (SCpnt->sense_buffer[2] & 0xf) { + switch (SRpnt->sr_sense_buffer[2] & 0xf) { case UNIT_ATTENTION: scsi_CDs[target].device->changed = 1; if (!quiet) @@ -86,8 +87,8 @@ int sr_do_ioctl(int target, unsigned char *sr_cmd, void *buffer, unsigned buflen err = -ENOMEDIUM; break; case NOT_READY: /* This happens if there is no disc in drive */ - if (SCpnt->sense_buffer[12] == 0x04 && - SCpnt->sense_buffer[13] == 0x01) { + if (SRpnt->sr_sense_buffer[12] == 0x04 && + SRpnt->sr_sense_buffer[13] == 0x01) { /* sense: Logical unit is in process of becoming ready */ if (!quiet) printk(KERN_INFO "sr%d: CDROM not ready yet.\n", target); @@ -104,7 +105,7 @@ int sr_do_ioctl(int target, unsigned char *sr_cmd, void *buffer, unsigned buflen if (!quiet) printk(KERN_INFO "sr%d: CDROM not ready. Make sure there is a disc in the drive.\n", target); #ifdef DEBUG - print_sense("sr", SCpnt); + print_req_sense("sr", SRpnt); #endif err = -ENOMEDIUM; break; @@ -112,8 +113,8 @@ int sr_do_ioctl(int target, unsigned char *sr_cmd, void *buffer, unsigned buflen if (!quiet) printk(KERN_ERR "sr%d: CDROM (ioctl) reports ILLEGAL " "REQUEST.\n", target); - if (SCpnt->sense_buffer[12] == 0x20 && - SCpnt->sense_buffer[13] == 0x00) { + if (SRpnt->sr_sense_buffer[12] == 0x20 && + SRpnt->sr_sense_buffer[13] == 0x00) { /* sense: Invalid command operation code */ err = -EDRIVE_CANT_DO_THIS; } else { @@ -121,20 +122,20 @@ int sr_do_ioctl(int target, unsigned char *sr_cmd, void *buffer, unsigned buflen } #ifdef DEBUG print_command(sr_cmd); - print_sense("sr", SCpnt); + print_req_sense("sr", SRpnt); #endif break; default: printk(KERN_ERR "sr%d: CDROM (ioctl) error, command: ", target); print_command(sr_cmd); - print_sense("sr", SCpnt); + print_req_sense("sr", SRpnt); err = -EIO; } } - result = SCpnt->result; + result = SRpnt->sr_result; /* Wake up a process waiting for device */ - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; return err; } @@ -148,7 +149,7 @@ static int test_unit_ready(int minor) sr_cmd[0] = GPCMD_TEST_UNIT_READY; sr_cmd[1] = ((scsi_CDs[minor].device->lun) << 5); sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0; - return sr_do_ioctl(minor, sr_cmd, NULL, 255, 1); + return sr_do_ioctl(minor, sr_cmd, NULL, 255, 1, SCSI_DATA_NONE); } int sr_tray_move(struct cdrom_device_info *cdi, int pos) @@ -160,7 +161,7 @@ int sr_tray_move(struct cdrom_device_info *cdi, int pos) sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0; sr_cmd[4] = (pos == 0) ? 0x03 /* close */ : 0x02 /* eject */ ; - return sr_do_ioctl(MINOR(cdi->dev), sr_cmd, NULL, 255, 0); + return sr_do_ioctl(MINOR(cdi->dev), sr_cmd, NULL, 255, 0, SCSI_DATA_NONE); } int sr_lock_door(struct cdrom_device_info *cdi, int lock) @@ -237,7 +238,7 @@ int sr_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn) sr_cmd[8] = 24; sr_cmd[9] = 0; - result = sr_do_ioctl(MINOR(cdi->dev), sr_cmd, buffer, 24, 0); + result = sr_do_ioctl(MINOR(cdi->dev), sr_cmd, buffer, 24, 0, SCSI_DATA_READ); memcpy(mcn->medium_catalog_number, buffer + 9, 13); mcn->medium_catalog_number[13] = 0; @@ -266,7 +267,7 @@ int sr_select_speed(struct cdrom_device_info *cdi, int speed) sr_cmd[2] = (speed >> 8) & 0xff; /* MSB for speed (in kbytes/sec) */ sr_cmd[3] = speed & 0xff; /* LSB */ - if (sr_do_ioctl(MINOR(cdi->dev), sr_cmd, NULL, 0, 0)) + if (sr_do_ioctl(MINOR(cdi->dev), sr_cmd, NULL, 0, 0, SCSI_DATA_NONE)) return -EIO; return 0; } @@ -296,7 +297,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg) sr_cmd[8] = 12; /* LSB of length */ sr_cmd[9] = 0; - result = sr_do_ioctl(target, sr_cmd, buffer, 12, 1); + result = sr_do_ioctl(target, sr_cmd, buffer, 12, 1, SCSI_DATA_READ); tochdr->cdth_trk0 = buffer[2]; tochdr->cdth_trk1 = buffer[3]; @@ -317,7 +318,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg) sr_cmd[8] = 12; /* LSB of length */ sr_cmd[9] = 0; - result = sr_do_ioctl(target, sr_cmd, buffer, 12, 0); + result = sr_do_ioctl(target, sr_cmd, buffer, 12, 0, SCSI_DATA_READ); tocentry->cdte_ctrl = buffer[5] & 0xf; tocentry->cdte_adr = buffer[5] >> 4; @@ -390,7 +391,7 @@ int sr_read_cd(int minor, unsigned char *dest, int lba, int format, int blksize) cmd[9] = 0x10; break; } - return sr_do_ioctl(minor, cmd, dest, blksize, 0); + return sr_do_ioctl(minor, cmd, dest, blksize, 0, SCSI_DATA_READ); } /* @@ -428,7 +429,7 @@ int sr_read_sector(int minor, int lba, int blksize, unsigned char *dest) cmd[4] = (unsigned char) (lba >> 8) & 0xff; cmd[5] = (unsigned char) lba & 0xff; cmd[8] = 1; - rc = sr_do_ioctl(minor, cmd, dest, blksize, 0); + rc = sr_do_ioctl(minor, cmd, dest, blksize, 0, SCSI_DATA_READ); return rc; } diff --git a/drivers/scsi/sr_vendor.c b/drivers/scsi/sr_vendor.c index 77be00ea3..a57c25c39 100644 --- a/drivers/scsi/sr_vendor.c +++ b/drivers/scsi/sr_vendor.c @@ -132,7 +132,7 @@ int sr_set_blocklength(int minor, int blocklength) modesel->density = density; modesel->block_length_med = (blocklength >> 8) & 0xff; modesel->block_length_lo = blocklength & 0xff; - if (0 == (rc = sr_do_ioctl(minor, cmd, buffer, sizeof(*modesel), 0))) { + if (0 == (rc = sr_do_ioctl(minor, cmd, buffer, sizeof(*modesel), 0, SCSI_DATA_WRITE))) { scsi_CDs[minor].device->sector_size = blocklength; } #ifdef DEBUG @@ -176,7 +176,7 @@ int sr_cd_check(struct cdrom_device_info *cdi) cmd[1] = (scsi_CDs[minor].device->lun << 5); cmd[8] = 12; cmd[9] = 0x40; - rc = sr_do_ioctl(minor, cmd, buffer, 12, 1); + rc = sr_do_ioctl(minor, cmd, buffer, 12, 1, SCSI_DATA_READ); if (rc != 0) break; if ((buffer[0] << 8) + buffer[1] < 0x0a) { @@ -200,7 +200,7 @@ int sr_cd_check(struct cdrom_device_info *cdi) cmd[0] = 0xde; cmd[1] = (scsi_CDs[minor].device->lun << 5) | 0x03; cmd[2] = 0xb0; - rc = sr_do_ioctl(minor, cmd, buffer, 0x16, 1); + rc = sr_do_ioctl(minor, cmd, buffer, 0x16, 1, SCSI_DATA_READ); if (rc != 0) break; if (buffer[14] != 0 && buffer[14] != 0xb0) { @@ -224,7 +224,7 @@ int sr_cd_check(struct cdrom_device_info *cdi) memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = 0xc7; cmd[1] = (scsi_CDs[minor].device->lun << 5) | 3; - rc = sr_do_ioctl(minor, cmd, buffer, 4, 1); + rc = sr_do_ioctl(minor, cmd, buffer, 4, 1, SCSI_DATA_READ); if (rc == -EINVAL) { printk(KERN_INFO "sr%d: Hmm, seems the drive " "doesn't support multisession CD's\n", minor); @@ -249,7 +249,7 @@ int sr_cd_check(struct cdrom_device_info *cdi) cmd[1] = (scsi_CDs[minor].device->lun << 5); cmd[8] = 0x04; cmd[9] = 0x40; - rc = sr_do_ioctl(minor, cmd, buffer, 0x04, 1); + rc = sr_do_ioctl(minor, cmd, buffer, 0x04, 1, SCSI_DATA_READ); if (rc != 0) { break; } @@ -263,7 +263,7 @@ int sr_cd_check(struct cdrom_device_info *cdi) cmd[6] = rc & 0x7f; /* number of last session */ cmd[8] = 0x0c; cmd[9] = 0x40; - rc = sr_do_ioctl(minor, cmd, buffer, 12, 1); + rc = sr_do_ioctl(minor, cmd, buffer, 12, 1, SCSI_DATA_READ); if (rc != 0) { break; } diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 5817b1424..f897cba9a 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -11,8 +11,10 @@ Copyright 1992 - 2000 Kai Makisara email Kai.Makisara@metla.fi - Last modified: Tue Jan 4 21:43:17 2000 by makisara@kai.makisara.local + Last modified: Sat Feb 19 17:22:34 2000 by makisara@kai.makisara.local Some small formal changes - aeb, 950809 + + Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support */ #include <linux/module.h> @@ -66,15 +68,15 @@ static int buffer_kbs = 0; static int write_threshold_kbs = 0; static int max_buffers = (-1); static int max_sg_segs = 0; -#ifdef MODULE + MODULE_AUTHOR("Kai Makisara"); MODULE_DESCRIPTION("SCSI Tape Driver"); MODULE_PARM(buffer_kbs, "i"); MODULE_PARM(write_threshold_kbs, "i"); MODULE_PARM(max_buffers, "i"); MODULE_PARM(max_sg_segs, "i"); -#else +#ifndef MODULE static struct st_dev_parm { char *name; int *val; @@ -92,7 +94,6 @@ static struct st_dev_parm { "max_sg_segs", &max_sg_segs } }; - #endif @@ -168,16 +169,15 @@ static int st_int_ioctl(struct inode *inode, unsigned int cmd_in, - /* Convert the result to success code */ -static int st_chk_result(Scsi_Cmnd * SCpnt) +static int st_chk_result(Scsi_Request * SRpnt) { - int dev = TAPE_NR(SCpnt->request.rq_dev); - int result = SCpnt->result; - unsigned char *sense = SCpnt->sense_buffer, scode; + int dev = TAPE_NR(SRpnt->sr_request.rq_dev); + int result = SRpnt->sr_result; + unsigned char *sense = SRpnt->sr_sense_buffer, scode; DEB(const char *stp;) - if (!result /* && SCpnt->sense_buffer[0] == 0 */ ) + if (!result) return 0; if (driver_byte(result) & DRIVER_SENSE) @@ -191,11 +191,11 @@ static int st_chk_result(Scsi_Cmnd * SCpnt) if (debugging) { printk(ST_DEB_MSG "st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n", dev, result, - SCpnt->data_cmnd[0], SCpnt->data_cmnd[1], SCpnt->data_cmnd[2], - SCpnt->data_cmnd[3], SCpnt->data_cmnd[4], SCpnt->data_cmnd[5], - SCpnt->request_bufflen); + SRpnt->sr_cmnd[0], SRpnt->sr_cmnd[1], SRpnt->sr_cmnd[2], + SRpnt->sr_cmnd[3], SRpnt->sr_cmnd[4], SRpnt->sr_cmnd[5], + SRpnt->sr_bufflen); if (driver_byte(result) & DRIVER_SENSE) - print_sense("st", SCpnt); + print_req_sense("st", SRpnt); } else ) /* end DEB */ if (!(driver_byte(result) & DRIVER_SENSE) || ((sense[0] & 0x70) == 0x70 && @@ -204,11 +204,11 @@ static int st_chk_result(Scsi_Cmnd * SCpnt) /* scode != UNIT_ATTENTION && */ scode != BLANK_CHECK && scode != VOLUME_OVERFLOW && - SCpnt->data_cmnd[0] != MODE_SENSE && - SCpnt->data_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */ + SRpnt->sr_cmnd[0] != MODE_SENSE && + SRpnt->sr_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */ if (driver_byte(result) & DRIVER_SENSE) { printk(KERN_WARNING "st%d: Error with sense data: ", dev); - print_sense("st", SCpnt); + print_req_sense("st", SRpnt); } else printk(KERN_WARNING "st%d: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n", @@ -219,8 +219,8 @@ static int st_chk_result(Scsi_Cmnd * SCpnt) if ((sense[0] & 0x70) == 0x70 && scode == RECOVERED_ERROR #if ST_RECOVERED_WRITE_FATAL - && SCpnt->data_cmnd[0] != WRITE_6 - && SCpnt->data_cmnd[0] != WRITE_FILEMARKS + && SRpnt->sr_cmnd[0] != WRITE_6 + && SRpnt->sr_cmnd[0] != WRITE_FILEMARKS #endif ) { scsi_tapes[dev].recover_count++; @@ -228,9 +228,9 @@ static int st_chk_result(Scsi_Cmnd * SCpnt) DEB( if (debugging) { - if (SCpnt->data_cmnd[0] == READ_6) + if (SRpnt->sr_cmnd[0] == READ_6) stp = "read"; - else if (SCpnt->data_cmnd[0] == WRITE_6) + else if (SRpnt->sr_cmnd[0] == WRITE_6) stp = "write"; else stp = "ioctl"; @@ -267,13 +267,13 @@ static void st_sleep_done(Scsi_Cmnd * SCpnt) remainder = 0; if ((SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW || remainder > 0) - (STp->buffer)->last_result = SCpnt->result; /* Error */ + (STp->buffer)->midlevel_result = SCpnt->result; /* Error */ else - (STp->buffer)->last_result = INT_MAX; /* OK */ + (STp->buffer)->midlevel_result = INT_MAX; /* OK */ } else - (STp->buffer)->last_result = SCpnt->result; + (STp->buffer)->midlevel_result = SCpnt->result; SCpnt->request.rq_status = RQ_SCSI_DONE; - (STp->buffer)->last_SCpnt = SCpnt; + (STp->buffer)->last_SRpnt = SCpnt->sc_request; DEB( STp->write_pending = 0; ) up(SCpnt->request.sem); @@ -288,47 +288,49 @@ static void st_sleep_done(Scsi_Cmnd * SCpnt) /* Do the scsi command. Waits until command performed if do_wait is true. Otherwise write_behind_check() is used to check that the command has finished. */ -static Scsi_Cmnd * - st_do_scsi(Scsi_Cmnd * SCpnt, Scsi_Tape * STp, unsigned char *cmd, int bytes, - int timeout, int retries, int do_wait) +static Scsi_Request * + st_do_scsi(Scsi_Request * SRpnt, Scsi_Tape * STp, unsigned char *cmd, int bytes, + int direction, int timeout, int retries, int do_wait) { unsigned char *bp; - if (SCpnt == NULL) - SCpnt = scsi_allocate_device(STp->device, 1, TRUE); - if (SCpnt == NULL) { + if (SRpnt == NULL) + SRpnt = scsi_allocate_request(STp->device); + if (SRpnt == NULL) { DEBC( printk(KERN_ERR "st%d: Can't get SCSI request.\n", TAPE_NR(STp->devt)); ); if (signal_pending(current)) - (STp->buffer)->last_result_fatal = (-EINTR); + (STp->buffer)->syscall_result = (-EINTR); else - (STp->buffer)->last_result_fatal = (-EBUSY); + (STp->buffer)->syscall_result = (-EBUSY); return NULL; } - cmd[1] |= (SCpnt->lun << 5) & 0xe0; + cmd[1] |= (SRpnt->sr_device->lun << 5) & 0xe0; init_MUTEX_LOCKED(&STp->sem); - SCpnt->use_sg = (bytes > (STp->buffer)->sg[0].length) ? + SRpnt->sr_use_sg = (bytes > (STp->buffer)->sg[0].length) ? (STp->buffer)->use_sg : 0; - if (SCpnt->use_sg) { + if (SRpnt->sr_use_sg) { bp = (char *) &((STp->buffer)->sg[0]); - if ((STp->buffer)->sg_segs < SCpnt->use_sg) - SCpnt->use_sg = (STp->buffer)->sg_segs; + if ((STp->buffer)->sg_segs < SRpnt->sr_use_sg) + SRpnt->sr_use_sg = (STp->buffer)->sg_segs; } else bp = (STp->buffer)->b_data; - SCpnt->cmd_len = 0; - SCpnt->request.sem = &(STp->sem); - SCpnt->request.rq_status = RQ_SCSI_BUSY; - SCpnt->request.rq_dev = STp->devt; + SRpnt->sr_data_direction = direction; + SRpnt->sr_cmd_len = 0; + SRpnt->sr_request.sem = &(STp->sem); + SRpnt->sr_request.rq_status = RQ_SCSI_BUSY; + SRpnt->sr_request.rq_dev = STp->devt; - scsi_do_cmd(SCpnt, (void *) cmd, bp, bytes, + scsi_do_req(SRpnt, (void *) cmd, bp, bytes, st_sleep_done, timeout, retries); if (do_wait) { - down(SCpnt->request.sem); - (STp->buffer)->last_result_fatal = st_chk_result(SCpnt); + down(SRpnt->sr_request.sem); + SRpnt->sr_request.sem = NULL; + (STp->buffer)->syscall_result = st_chk_result(SRpnt); } - return SCpnt; + return SRpnt; } @@ -348,9 +350,10 @@ static void write_behind_check(Scsi_Tape * STp) ) /* end DEB */ down(&(STp->sem)); + (STp->buffer)->last_SRpnt->sr_request.sem = NULL; - (STp->buffer)->last_result_fatal = st_chk_result((STp->buffer)->last_SCpnt); - scsi_release_command((STp->buffer)->last_SCpnt); + (STp->buffer)->syscall_result = st_chk_result((STp->buffer)->last_SRpnt); + scsi_release_request((STp->buffer)->last_SRpnt); if (STbuffer->writing < STbuffer->buffer_bytes) #if 0 @@ -379,8 +382,8 @@ static void write_behind_check(Scsi_Tape * STp) it messes up the block number). */ static int cross_eof(Scsi_Tape * STp, int forward) { - Scsi_Cmnd *SCpnt; - unsigned char cmd[10]; + Scsi_Request *SRpnt; + unsigned char cmd[MAX_COMMAND_SIZE]; cmd[0] = SPACE; cmd[1] = 0x01; /* Space FileMarks */ @@ -394,18 +397,19 @@ static int cross_eof(Scsi_Tape * STp, int forward) DEBC(printk(ST_DEB_MSG "st%d: Stepping over filemark %s.\n", TAPE_NR(STp->devt), forward ? "forward" : "backward")); - SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_RETRIES, TRUE); - if (!SCpnt) - return (STp->buffer)->last_result_fatal; + SRpnt = st_do_scsi(NULL, STp, cmd, 0, SCSI_DATA_NONE, + STp->timeout, MAX_RETRIES, TRUE); + if (!SRpnt) + return (STp->buffer)->syscall_result; - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; - if ((STp->buffer)->last_result != 0) + if ((STp->buffer)->midlevel_result != 0) printk(KERN_ERR "st%d: Stepping over filemark %s failed.\n", TAPE_NR(STp->devt), forward ? "forward" : "backward"); - return (STp->buffer)->last_result_fatal; + return (STp->buffer)->syscall_result; } @@ -414,17 +418,17 @@ static int flush_write_buffer(Scsi_Tape * STp) { int offset, transfer, blks; int result; - unsigned char cmd[10]; - Scsi_Cmnd *SCpnt; + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Request *SRpnt; ST_partstat *STps; if ((STp->buffer)->writing) { write_behind_check(STp); - if ((STp->buffer)->last_result_fatal) { + if ((STp->buffer)->syscall_result) { DEBC(printk(ST_DEB_MSG "st%d: Async write error (flush) %x.\n", - TAPE_NR(STp->devt), (STp->buffer)->last_result)) - if ((STp->buffer)->last_result == INT_MAX) + TAPE_NR(STp->devt), (STp->buffer)->midlevel_result)) + if ((STp->buffer)->midlevel_result == INT_MAX) return (-ENOSPC); return (-EIO); } @@ -443,7 +447,7 @@ static int flush_write_buffer(Scsi_Tape * STp) memset((STp->buffer)->b_data + offset, 0, transfer - offset); - memset(cmd, 0, 10); + memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = WRITE_6; cmd[1] = 1; blks = transfer / STp->block_size; @@ -451,16 +455,16 @@ static int flush_write_buffer(Scsi_Tape * STp) cmd[3] = blks >> 8; cmd[4] = blks; - SCpnt = st_do_scsi(NULL, STp, cmd, transfer, STp->timeout, - MAX_WRITE_RETRIES, TRUE); - if (!SCpnt) - return (STp->buffer)->last_result_fatal; + SRpnt = st_do_scsi(NULL, STp, cmd, transfer, SCSI_DATA_WRITE, + STp->timeout, MAX_WRITE_RETRIES, TRUE); + if (!SRpnt) + return (STp->buffer)->syscall_result; STps = &(STp->ps[STp->partition]); - if ((STp->buffer)->last_result_fatal != 0) { - if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && - (SCpnt->sense_buffer[2] & 0x40) && - (SCpnt->sense_buffer[2] & 0x0f) == NO_SENSE) { + if ((STp->buffer)->syscall_result != 0) { + if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 && + (SRpnt->sr_sense_buffer[2] & 0x40) && + (SRpnt->sr_sense_buffer[2] & 0x0f) == NO_SENSE) { STp->dirty = 0; (STp->buffer)->buffer_bytes = 0; result = (-ENOSPC); @@ -476,8 +480,8 @@ static int flush_write_buffer(Scsi_Tape * STp) STp->dirty = 0; (STp->buffer)->buffer_bytes = 0; } - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; } return result; } @@ -582,8 +586,9 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) { unsigned short flags; int i, need_dma_buffer, new_session = FALSE; - unsigned char cmd[10]; - Scsi_Cmnd *SCpnt; + int retval; + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Request *SRpnt; Scsi_Tape *STp; ST_mode *STm; ST_partstat *STps; @@ -601,8 +606,14 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) DEB( printk(ST_DEB_MSG "st%d: Device already in use.\n", dev); ) return (-EBUSY); } + STp->in_use = 1; STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0; + if (scsi_tapes[dev].device->host->hostt->module) + __MOD_INC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module); + if (st_template.module) + __MOD_INC_USE_COUNT(st_template.module); + if (mode != STp->current_mode) { DEBC(printk(ST_DEB_MSG "st%d: Mode change from %d to %d.\n", dev, STp->current_mode, mode)); @@ -621,13 +632,14 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) STp->buffer = new_tape_buffer(FALSE, need_dma_buffer); if (STp->buffer == NULL) { printk(KERN_WARNING "st%d: Can't allocate tape buffer.\n", dev); - return (-EBUSY); + retval = (-EBUSY); + goto err_out; } } else STp->buffer = st_buffers[i]; (STp->buffer)->in_use = 1; (STp->buffer)->writing = 0; - (STp->buffer)->last_result_fatal = 0; + (STp->buffer)->syscall_result = 0; (STp->buffer)->use_sg = STp->device->host->sg_tablesize; /* Compute the usable buffer size for this SCSI adapter */ @@ -651,35 +663,27 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) STp->recover_count = 0; DEB( STp->nbr_waits = STp->nbr_finished = 0; ) - if (scsi_tapes[dev].device->host->hostt->module) - __MOD_INC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module); - if (st_template.module) - __MOD_INC_USE_COUNT(st_template.module); - - memset((void *) &cmd[0], 0, 10); + memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE); cmd[0] = TEST_UNIT_READY; - SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES, - TRUE); - if (!SCpnt) { - if (scsi_tapes[dev].device->host->hostt->module) - __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module); - if (st_template.module) - __MOD_DEC_USE_COUNT(st_template.module); - return (STp->buffer)->last_result_fatal; + SRpnt = st_do_scsi(NULL, STp, cmd, 0, SCSI_DATA_NONE, STp->long_timeout, + MAX_READY_RETRIES, TRUE); + if (!SRpnt) { + retval = (STp->buffer)->syscall_result; + goto err_out; } - if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && - (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */ + if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 && + (SRpnt->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */ /* Flush the queued UNIT ATTENTION sense data */ for (i=0; i < 10; i++) { - memset((void *) &cmd[0], 0, 10); + memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE); cmd[0] = TEST_UNIT_READY; - SCpnt = st_do_scsi(SCpnt, STp, cmd, 0, STp->long_timeout, - MAX_READY_RETRIES, TRUE); - if ((SCpnt->sense_buffer[0] & 0x70) != 0x70 || - (SCpnt->sense_buffer[2] & 0x0f) != UNIT_ATTENTION) + SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, SCSI_DATA_NONE, + STp->long_timeout, MAX_READY_RETRIES, TRUE); + if ((SRpnt->sr_sense_buffer[0] & 0x70) != 0x70 || + (SRpnt->sr_sense_buffer[2] & 0x0f) != UNIT_ATTENTION) break; } @@ -700,36 +704,35 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) new_session = TRUE; } - if ((STp->buffer)->last_result_fatal != 0) { + if ((STp->buffer)->syscall_result != 0) { if ((STp->device)->scsi_level >= SCSI_2 && - (SCpnt->sense_buffer[0] & 0x70) == 0x70 && - (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY && - SCpnt->sense_buffer[12] == 0x3a) { /* Check ASC */ + (SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 && + (SRpnt->sr_sense_buffer[2] & 0x0f) == NOT_READY && + SRpnt->sr_sense_buffer[12] == 0x3a) { /* Check ASC */ STp->ready = ST_NO_TAPE; } else STp->ready = ST_NOT_READY; - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; STp->density = 0; /* Clear the erroneous "residue" */ STp->write_prot = 0; STp->block_size = 0; STp->ps[0].drv_file = STp->ps[0].drv_block = (-1); STp->partition = STp->new_partition = 0; STp->door_locked = ST_UNLOCKED; - STp->in_use = 1; return 0; } if (STp->omit_blklims) STp->min_block = STp->max_block = (-1); else { - memset((void *) &cmd[0], 0, 10); + memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE); cmd[0] = READ_BLOCK_LIMITS; - SCpnt = st_do_scsi(SCpnt, STp, cmd, 6, STp->timeout, MAX_READY_RETRIES, - TRUE); + SRpnt = st_do_scsi(SRpnt, STp, cmd, 6, SCSI_DATA_READ, STp->timeout, + MAX_READY_RETRIES, TRUE); - if (!SCpnt->result && !SCpnt->sense_buffer[0]) { + if (!SRpnt->sr_result && !SRpnt->sr_sense_buffer[0]) { STp->max_block = ((STp->buffer)->b_data[1] << 16) | ((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3]; STp->min_block = ((STp->buffer)->b_data[4] << 8) | @@ -745,16 +748,17 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) } } - memset((void *) &cmd[0], 0, 10); + memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE); cmd[0] = MODE_SENSE; cmd[4] = 12; - SCpnt = st_do_scsi(SCpnt, STp, cmd, 12, STp->timeout, MAX_READY_RETRIES, TRUE); + SRpnt = st_do_scsi(SRpnt, STp, cmd, 12, SCSI_DATA_READ, STp->timeout, + MAX_READY_RETRIES, TRUE); - if ((STp->buffer)->last_result_fatal != 0) { + if ((STp->buffer)->syscall_result != 0) { DEBC(printk(ST_DEB_MSG "st%d: No Mode Sense.\n", dev)); STp->block_size = ST_DEFAULT_BLOCK; /* Educated guess (?) */ - (STp->buffer)->last_result_fatal = 0; /* Prevent error propagation */ + (STp->buffer)->syscall_result = 0; /* Prevent error propagation */ STp->drv_write_prot = 0; } else { DEBC(printk(ST_DEB_MSG @@ -779,19 +783,14 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) !enlarge_buffer(STp->buffer, STp->block_size, STp->restr_dma)) { printk(KERN_NOTICE "st%d: Blocksize %d too large for buffer.\n", dev, STp->block_size); - scsi_release_command(SCpnt); - (STp->buffer)->in_use = 0; - STp->buffer = NULL; - if (scsi_tapes[dev].device->host->hostt->module) - __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module); - if (st_template.module) - __MOD_DEC_USE_COUNT(st_template.module); - return (-EIO); + scsi_release_request(SRpnt); + retval = (-EIO); + goto err_out; } STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0; } - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; STp->inited = TRUE; if (STp->block_size > 0) @@ -812,13 +811,8 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) DEBC(printk(ST_DEB_MSG "st%d: Write protected\n", dev)); if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) { - (STp->buffer)->in_use = 0; - STp->buffer = NULL; - if (scsi_tapes[dev].device->host->hostt->module) - __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module); - if (st_template.module) - __MOD_DEC_USE_COUNT(st_template.module); - return (-EROFS); + retval = (-EROFS); + goto err_out; } } @@ -829,13 +823,8 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) DEBC(printk(ST_DEB_MSG "st%d: Updating partition number in status.\n", dev)); if ((STp->partition = find_partition(inode)) < 0) { - (STp->buffer)->in_use = 0; - STp->buffer = NULL; - if (scsi_tapes[dev].device->host->hostt->module) - __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module); - if (st_template.module) - __MOD_DEC_USE_COUNT(st_template.module); - return STp->partition; + retval = STp->partition; + goto err_out; } STp->new_partition = STp->partition; STp->nbr_partitions = 1; /* This guess will be updated when necessary */ @@ -845,15 +834,8 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) STp->density_changed = STp->blksize_changed = FALSE; STp->compression_changed = FALSE; if (!(STm->defaults_for_writes) && - (i = set_mode_densblk(inode, STp, STm)) < 0) { - (STp->buffer)->in_use = 0; - STp->buffer = NULL; - if (scsi_tapes[dev].device->host->hostt->module) - __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module); - if (st_template.module) - __MOD_DEC_USE_COUNT(st_template.module); - return i; - } + (retval = set_mode_densblk(inode, STp, STm)) < 0) + goto err_out; if (STp->default_drvbuffer != 0xff) { if (st_int_ioctl(inode, MTSETDRVBUFFER, STp->default_drvbuffer)) @@ -862,9 +844,21 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) dev, STp->default_drvbuffer); } } - STp->in_use = 1; return 0; + + err_out: + if (STp->buffer != NULL) { + (STp->buffer)->in_use = 0; + STp->buffer = NULL; + } + STp->in_use = 0; + if (scsi_tapes[dev].device->host->hostt->module) + __MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module); + if (st_template.module) + __MOD_DEC_USE_COUNT(st_template.module); + return retval; + } @@ -872,8 +866,8 @@ static int scsi_tape_open(struct inode *inode, struct file *filp) static int scsi_tape_flush(struct file *filp) { int result = 0, result2; - static unsigned char cmd[10]; - Scsi_Cmnd *SCpnt; + static unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Request *SRpnt; Scsi_Tape *STp; ST_mode *STm; ST_partstat *STps; @@ -890,59 +884,62 @@ static int scsi_tape_flush(struct file *filp) STm = &(STp->modes[STp->current_mode]); STps = &(STp->ps[STp->partition]); + if (STps->rw == ST_WRITING && !(STp->device)->was_reset) { + result = flush_write_buffer(STp); + if (result != 0 && result != (-ENOSPC)) + goto out; + } + if (STp->can_partitions && - (result = update_partition(inode)) < 0) { + (result2 = update_partition(inode)) < 0) { DEBC(printk(ST_DEB_MSG "st%d: update_partition at close failed.\n", dev)); + if (result == 0) + result = result2; goto out; } if (STps->rw == ST_WRITING && !(STp->device)->was_reset) { - result = flush_write_buffer(STp); - DEBC(printk(ST_DEB_MSG "st%d: File length %ld bytes.\n", dev, (long) (filp->f_pos)); printk(ST_DEB_MSG "st%d: Async write waits %d, finished %d.\n", dev, STp->nbr_waits, STp->nbr_finished); ) - if (result == 0 || result == (-ENOSPC)) { - - memset(cmd, 0, 10); - cmd[0] = WRITE_FILEMARKS; - cmd[4] = 1 + STp->two_fm; + memset(cmd, 0, MAX_COMMAND_SIZE); + cmd[0] = WRITE_FILEMARKS; + cmd[4] = 1 + STp->two_fm; - SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, - MAX_WRITE_RETRIES, TRUE); - if (!SCpnt) { - result = (STp->buffer)->last_result_fatal; - goto out; - } + SRpnt = st_do_scsi(NULL, STp, cmd, 0, SCSI_DATA_NONE, + STp->timeout, MAX_WRITE_RETRIES, TRUE); + if (!SRpnt) { + result = (STp->buffer)->syscall_result; + goto out; + } - if ((STp->buffer)->last_result_fatal != 0 && - ((SCpnt->sense_buffer[0] & 0x70) != 0x70 || - (SCpnt->sense_buffer[2] & 0x4f) != 0x40 || - ((SCpnt->sense_buffer[0] & 0x80) != 0 && - (SCpnt->sense_buffer[3] | SCpnt->sense_buffer[4] | - SCpnt->sense_buffer[5] | - SCpnt->sense_buffer[6]) == 0))) { - /* Filter out successful write at EOM */ - scsi_release_command(SCpnt); - SCpnt = NULL; - printk(KERN_ERR "st%d: Error on write filemark.\n", dev); - if (result == 0) - result = (-EIO); - } else { - scsi_release_command(SCpnt); - SCpnt = NULL; - if (STps->drv_file >= 0) - STps->drv_file++; - STps->drv_block = 0; - if (STp->two_fm) - cross_eof(STp, FALSE); - STps->eof = ST_FM; - } + if ((STp->buffer)->syscall_result != 0 && + ((SRpnt->sr_sense_buffer[0] & 0x70) != 0x70 || + (SRpnt->sr_sense_buffer[2] & 0x4f) != 0x40 || + ((SRpnt->sr_sense_buffer[0] & 0x80) != 0 && + (SRpnt->sr_sense_buffer[3] | SRpnt->sr_sense_buffer[4] | + SRpnt->sr_sense_buffer[5] | + SRpnt->sr_sense_buffer[6]) == 0))) { + /* Filter out successful write at EOM */ + scsi_release_request(SRpnt); + SRpnt = NULL; + printk(KERN_ERR "st%d: Error on write filemark.\n", dev); + if (result == 0) + result = (-EIO); + } else { + scsi_release_request(SRpnt); + SRpnt = NULL; + if (STps->drv_file >= 0) + STps->drv_file++; + STps->drv_block = 0; + if (STp->two_fm) + cross_eof(STp, FALSE); + STps->eof = ST_FM; } DEBC(printk(ST_DEB_MSG "st%d: Buffer flushed, %d EOF(s) written\n", @@ -1021,9 +1018,9 @@ static ssize_t ssize_t i, do_count, blks, retval, transfer; int write_threshold; int doing_write = 0; - static unsigned char cmd[10]; + static unsigned char cmd[MAX_COMMAND_SIZE]; const char *b_point; - Scsi_Cmnd *SCpnt = NULL; + Scsi_Request *SRpnt = NULL; Scsi_Tape *STp; ST_mode *STm; ST_partstat *STps; @@ -1118,10 +1115,10 @@ static ssize_t if ((STp->buffer)->writing) { write_behind_check(STp); - if ((STp->buffer)->last_result_fatal) { + if ((STp->buffer)->syscall_result) { DEBC(printk(ST_DEB_MSG "st%d: Async write error (write) %x.\n", - dev, (STp->buffer)->last_result)); - if ((STp->buffer)->last_result == INT_MAX) + dev, (STp->buffer)->midlevel_result)); + if ((STp->buffer)->midlevel_result == INT_MAX) STps->eof = ST_EOM_OK; else STps->eof = ST_EOM_ERROR; @@ -1153,7 +1150,7 @@ static ssize_t total = count; - memset(cmd, 0, 10); + memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = WRITE_6; cmd[1] = (STp->block_size != 0); @@ -1175,9 +1172,9 @@ static ssize_t i = append_to_buffer(b_point, STp->buffer, do_count); if (i) { - if (SCpnt != NULL) { - scsi_release_command(SCpnt); - SCpnt = NULL; + if (SRpnt != NULL) { + scsi_release_request(SRpnt); + SRpnt = NULL; } return i; } @@ -1193,23 +1190,23 @@ static ssize_t cmd[3] = blks >> 8; cmd[4] = blks; - SCpnt = st_do_scsi(SCpnt, STp, cmd, transfer, STp->timeout, - MAX_WRITE_RETRIES, TRUE); - if (!SCpnt) - return (STp->buffer)->last_result_fatal; + SRpnt = st_do_scsi(SRpnt, STp, cmd, transfer, SCSI_DATA_WRITE, + STp->timeout, MAX_WRITE_RETRIES, TRUE); + if (!SRpnt) + return (STp->buffer)->syscall_result; - if ((STp->buffer)->last_result_fatal != 0) { + if ((STp->buffer)->syscall_result != 0) { DEBC(printk(ST_DEB_MSG "st%d: Error on write:\n", dev)); - if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && - (SCpnt->sense_buffer[2] & 0x40)) { + if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 && + (SRpnt->sr_sense_buffer[2] & 0x40)) { if (STp->block_size != 0 && - (SCpnt->sense_buffer[0] & 0x80) != 0) - transfer = (SCpnt->sense_buffer[3] << 24) | - (SCpnt->sense_buffer[4] << 16) | - (SCpnt->sense_buffer[5] << 8) | - SCpnt->sense_buffer[6]; + (SRpnt->sr_sense_buffer[0] & 0x80) != 0) + transfer = (SRpnt->sr_sense_buffer[3] << 24) | + (SRpnt->sr_sense_buffer[4] << 16) | + (SRpnt->sr_sense_buffer[5] << 8) | + SRpnt->sr_sense_buffer[6]; else if (STp->block_size == 0 && - (SCpnt->sense_buffer[2] & 0x0f) == + (SRpnt->sr_sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW) transfer = do_count; else @@ -1246,8 +1243,8 @@ static ssize_t retval = (-EIO); } - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; (STp->buffer)->buffer_bytes = 0; STp->dirty = 0; if (count < total) @@ -1271,9 +1268,9 @@ static ssize_t STp->dirty = 1; i = append_to_buffer(b_point, STp->buffer, count); if (i) { - if (SCpnt != NULL) { - scsi_release_command(SCpnt); - SCpnt = NULL; + if (SRpnt != NULL) { + scsi_release_request(SRpnt); + SRpnt = NULL; } return i; } @@ -1281,10 +1278,10 @@ static ssize_t count = 0; } - if (doing_write && (STp->buffer)->last_result_fatal != 0) { - scsi_release_command(SCpnt); - SCpnt = NULL; - return (STp->buffer)->last_result_fatal; + if (doing_write && (STp->buffer)->syscall_result != 0) { + scsi_release_request(SRpnt); + SRpnt = NULL; + return (STp->buffer)->syscall_result; } if (STm->do_async_writes && @@ -1309,14 +1306,15 @@ static ssize_t cmd[4] = blks; DEB( STp->write_pending = 1; ) - SCpnt = st_do_scsi(SCpnt, STp, cmd, (STp->buffer)->writing, STp->timeout, + SRpnt = st_do_scsi(SRpnt, STp, cmd, (STp->buffer)->writing, + SCSI_DATA_WRITE, STp->timeout, MAX_WRITE_RETRIES, FALSE); - if (SCpnt == NULL) - return (STp->buffer)->last_result_fatal; + if (SRpnt == NULL) + return (STp->buffer)->syscall_result; - } else if (SCpnt != NULL) { - scsi_release_command(SCpnt); - SCpnt = NULL; + } else if (SRpnt != NULL) { + scsi_release_request(SRpnt); + SRpnt = NULL; } STps->at_sm &= (total == 0); if (total > 0) @@ -1328,11 +1326,11 @@ static ssize_t /* Read data from the tape. Returns zero in the normal case, one if the eof status has changed, and the negative error code in case of a fatal error. Otherwise updates the buffer and the eof state. */ -static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt) +static long read_tape(struct inode *inode, long count, Scsi_Request ** aSRpnt) { int transfer, blks, bytes; - static unsigned char cmd[10]; - Scsi_Cmnd *SCpnt; + static unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Request *SRpnt; Scsi_Tape *STp; ST_mode *STm; ST_partstat *STps; @@ -1348,7 +1346,7 @@ static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt) if (STps->eof == ST_FM_HIT) return 1; - memset(cmd, 0, 10); + memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = READ_6; cmd[1] = (STp->block_size != 0); if (STp->block_size == 0) @@ -1369,49 +1367,51 @@ static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt) cmd[3] = blks >> 8; cmd[4] = blks; - SCpnt = *aSCpnt; - SCpnt = st_do_scsi(SCpnt, STp, cmd, bytes, STp->timeout, MAX_RETRIES, TRUE); - *aSCpnt = SCpnt; - if (!SCpnt) - return (STp->buffer)->last_result_fatal; + SRpnt = *aSRpnt; + SRpnt = st_do_scsi(SRpnt, STp, cmd, bytes, SCSI_DATA_READ, + STp->timeout, MAX_RETRIES, TRUE); + *aSRpnt = SRpnt; + if (!SRpnt) + return (STp->buffer)->syscall_result; (STp->buffer)->read_pointer = 0; STps->at_sm = 0; /* Something to check */ - if ((STp->buffer)->last_result_fatal) { + if ((STp->buffer)->syscall_result) { retval = 1; DEBC(printk(ST_DEB_MSG "st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", dev, - SCpnt->sense_buffer[0], SCpnt->sense_buffer[1], - SCpnt->sense_buffer[2], SCpnt->sense_buffer[3], - SCpnt->sense_buffer[4], SCpnt->sense_buffer[5], - SCpnt->sense_buffer[6], SCpnt->sense_buffer[7])); - if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */ + SRpnt->sr_sense_buffer[0], SRpnt->sr_sense_buffer[1], + SRpnt->sr_sense_buffer[2], SRpnt->sr_sense_buffer[3], + SRpnt->sr_sense_buffer[4], SRpnt->sr_sense_buffer[5], + SRpnt->sr_sense_buffer[6], SRpnt->sr_sense_buffer[7])); + if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70) { /* extended sense */ - if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) - SCpnt->sense_buffer[2] &= 0xcf; /* No need for EOM in this case */ + if ((SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK) + SRpnt->sr_sense_buffer[2] &= 0xcf; /* No need for EOM in this case */ - if ((SCpnt->sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */ + if ((SRpnt->sr_sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */ /* Compute the residual count */ - if ((SCpnt->sense_buffer[0] & 0x80) != 0) - transfer = (SCpnt->sense_buffer[3] << 24) | - (SCpnt->sense_buffer[4] << 16) | - (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6]; + if ((SRpnt->sr_sense_buffer[0] & 0x80) != 0) + transfer = (SRpnt->sr_sense_buffer[3] << 24) | + (SRpnt->sr_sense_buffer[4] << 16) | + (SRpnt->sr_sense_buffer[5] << 8) | + SRpnt->sr_sense_buffer[6]; else transfer = 0; if (STp->block_size == 0 && - (SCpnt->sense_buffer[2] & 0x0f) == MEDIUM_ERROR) + (SRpnt->sr_sense_buffer[2] & 0x0f) == MEDIUM_ERROR) transfer = bytes; - if (SCpnt->sense_buffer[2] & 0x20) { /* ILI */ + if (SRpnt->sr_sense_buffer[2] & 0x20) { /* ILI */ if (STp->block_size == 0) { if (transfer <= 0) transfer = 0; (STp->buffer)->buffer_bytes = bytes - transfer; } else { - scsi_release_command(SCpnt); - SCpnt = *aSCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = *aSRpnt = NULL; if (transfer == blks) { /* We did not get anything, error */ printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev); if (STps->drv_block >= 0) @@ -1430,7 +1430,7 @@ static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt) if (st_int_ioctl(inode, MTBSR, 1)) return (-EIO); } - } else if (SCpnt->sense_buffer[2] & 0x80) { /* FM overrides EOM */ + } else if (SRpnt->sr_sense_buffer[2] & 0x80) { /* FM overrides EOM */ if (STps->eof != ST_FM_HIT) STps->eof = ST_FM_HIT; else @@ -1443,7 +1443,7 @@ static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt) DEBC(printk(ST_DEB_MSG "st%d: EOF detected (%d bytes read).\n", dev, (STp->buffer)->buffer_bytes)); - } else if (SCpnt->sense_buffer[2] & 0x40) { + } else if (SRpnt->sr_sense_buffer[2] & 0x40) { if (STps->eof == ST_FM) STps->eof = ST_EOD_1; else @@ -1464,7 +1464,7 @@ static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt) "st%d: Tape error while reading.\n", dev)); STps->drv_block = (-1); if (STps->eof == ST_FM && - (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) { + (SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK) { DEBC(printk(ST_DEB_MSG "st%d: Zero returned for first BLANK CHECK after EOF.\n", dev)); @@ -1475,7 +1475,7 @@ static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt) } /* End of extended sense test */ else { /* Non-extended sense */ - retval = (STp->buffer)->last_result_fatal; + retval = (STp->buffer)->syscall_result; } } @@ -1501,7 +1501,7 @@ static ssize_t ssize_t total; ssize_t i, transfer; int special; - Scsi_Cmnd *SCpnt = NULL; + Scsi_Request *SRpnt = NULL; Scsi_Tape *STp; ST_mode *STm; ST_partstat *STps; @@ -1594,10 +1594,10 @@ static ssize_t /* Get new data if the buffer is empty */ if ((STp->buffer)->buffer_bytes == 0) { - special = read_tape(inode, count - total, &SCpnt); + special = read_tape(inode, count - total, &SRpnt); if (special < 0) { /* No need to continue read */ - if (SCpnt != NULL) { - scsi_release_command(SCpnt); + if (SRpnt != NULL) { + scsi_release_request(SRpnt); } return special; } @@ -1616,9 +1616,9 @@ static ssize_t (STp->buffer)->buffer_bytes : count - total; i = from_buffer(STp->buffer, buf, transfer); if (i) { - if (SCpnt != NULL) { - scsi_release_command(SCpnt); - SCpnt = NULL; + if (SRpnt != NULL) { + scsi_release_request(SRpnt); + SRpnt = NULL; } return i; } @@ -1633,9 +1633,9 @@ static ssize_t } /* for (total = 0, special = 0; total < count && !special; ) */ - if (SCpnt != NULL) { - scsi_release_command(SCpnt); - SCpnt = NULL; + if (SRpnt != NULL) { + scsi_release_request(SRpnt); + SRpnt = NULL; } /* Change the eof state if no data from tape or buffer */ @@ -1836,30 +1836,31 @@ static int st_set_options(struct inode *inode, long options) static int st_compression(Scsi_Tape * STp, int state) { int dev; - unsigned char cmd[10]; - Scsi_Cmnd *SCpnt = NULL; + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Request *SRpnt = NULL; if (STp->ready != ST_READY) return (-EIO); /* Read the current page contents */ - memset(cmd, 0, 10); + memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = MODE_SENSE; cmd[1] = 8; cmd[2] = COMPRESSION_PAGE; cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH; - SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE); - if (SCpnt == NULL) - return (STp->buffer)->last_result_fatal; + SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_READ, + STp->timeout, 0, TRUE); + if (SRpnt == NULL) + return (STp->buffer)->syscall_result; - dev = TAPE_NR(SCpnt->request.rq_dev); + dev = TAPE_NR(SRpnt->sr_request.rq_dev); - if ((STp->buffer)->last_result_fatal != 0) { + if ((STp->buffer)->syscall_result != 0) { DEBC(printk(ST_DEB_MSG "st%d: Compression mode page not supported.\n", dev)); - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; return (-EIO); } DEBC(printk(ST_DEB_MSG "st%d: Compression state is %d.\n", dev, @@ -1868,8 +1869,8 @@ static int st_compression(Scsi_Tape * STp, int state) /* Check if compression can be changed */ if (((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCC_MASK) == 0) { DEBC(printk(ST_DEB_MSG "st%d: Compression not supported.\n", dev)); - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; return (-EIO); } @@ -1879,7 +1880,7 @@ static int st_compression(Scsi_Tape * STp, int state) else (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] &= ~DCE_MASK; - memset(cmd, 0, 10); + memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = MODE_SELECT; cmd[1] = 0x10; cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH; @@ -1887,19 +1888,20 @@ static int st_compression(Scsi_Tape * STp, int state) (STp->buffer)->b_data[0] = 0; /* Reserved data length */ (STp->buffer)->b_data[1] = 0; /* Reserved media type byte */ (STp->buffer)->b_data[MODE_HEADER_LENGTH] &= 0x3f; - SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE); + SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE, + STp->timeout, 0, TRUE); - if ((STp->buffer)->last_result_fatal != 0) { + if ((STp->buffer)->syscall_result != 0) { DEBC(printk(ST_DEB_MSG "st%d: Compression change failed.\n", dev)); - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; return (-EIO); } DEBC(printk(ST_DEB_MSG "st%d: Compression state changed to %d.\n", dev, state)); - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; STp->compression_changed = TRUE; return 0; } @@ -1913,11 +1915,12 @@ static int st_int_ioctl(struct inode *inode, long ltmp; int i, ioctl_result; int chg_eof = TRUE; - unsigned char cmd[10]; - Scsi_Cmnd *SCpnt; + unsigned char cmd[MAX_COMMAND_SIZE]; + Scsi_Request *SRpnt; Scsi_Tape *STp; ST_partstat *STps; - int fileno, blkno, at_sm, undone, datalen; + int fileno, blkno, at_sm, undone; + int datalen = 0, direction = SCSI_DATA_NONE; int dev = TAPE_NR(inode->i_rdev); STp = &(scsi_tapes[dev]); @@ -1933,8 +1936,7 @@ static int st_int_ioctl(struct inode *inode, blkno = STps->drv_block; at_sm = STps->at_sm; - memset(cmd, 0, 10); - datalen = 0; + memset(cmd, 0, MAX_COMMAND_SIZE); switch (cmd_in) { case MTFSFM: chg_eof = FALSE; /* Changed from the FSF after this */ @@ -2176,6 +2178,7 @@ static int st_int_ioctl(struct inode *inode, } cmd[0] = MODE_SELECT; cmd[4] = datalen = 12; + direction = SCSI_DATA_WRITE; memset((STp->buffer)->b_data, 0, 12); if (cmd_in == MTSETDRVBUFFER) @@ -2222,15 +2225,16 @@ static int st_int_ioctl(struct inode *inode, return (-ENOSYS); } - SCpnt = st_do_scsi(NULL, STp, cmd, datalen, timeout, MAX_RETRIES, TRUE); - if (!SCpnt) - return (STp->buffer)->last_result_fatal; + SRpnt = st_do_scsi(NULL, STp, cmd, datalen, direction, + timeout, MAX_RETRIES, TRUE); + if (!SRpnt) + return (STp->buffer)->syscall_result; - ioctl_result = (STp->buffer)->last_result_fatal; + ioctl_result = (STp->buffer)->syscall_result; if (!ioctl_result) { /* SCSI command successful */ - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; STps->drv_block = blkno; STps->drv_file = fileno; STps->at_sm = at_sm; @@ -2279,7 +2283,7 @@ static int st_int_ioctl(struct inode *inode, } else { /* SCSI command was not completely successful. Don't return from this block without releasing the SCSI command block! */ - if (SCpnt->sense_buffer[2] & 0x40) { + if (SRpnt->sr_sense_buffer[2] & 0x40) { if (cmd_in != MTBSF && cmd_in != MTBSFM && cmd_in != MTBSR && cmd_in != MTBSS) STps->eof = ST_EOM_OK; @@ -2287,14 +2291,14 @@ static int st_int_ioctl(struct inode *inode, } undone = ( - (SCpnt->sense_buffer[3] << 24) + - (SCpnt->sense_buffer[4] << 16) + - (SCpnt->sense_buffer[5] << 8) + - SCpnt->sense_buffer[6]); + (SRpnt->sr_sense_buffer[3] << 24) + + (SRpnt->sr_sense_buffer[4] << 16) + + (SRpnt->sr_sense_buffer[5] << 8) + + SRpnt->sr_sense_buffer[6]); if (cmd_in == MTWEOF && - (SCpnt->sense_buffer[0] & 0x70) == 0x70 && - (SCpnt->sense_buffer[2] & 0x4f) == 0x40 && - ((SCpnt->sense_buffer[0] & 0x80) == 0 || undone == 0)) { + (SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 && + (SRpnt->sr_sense_buffer[2] & 0x4f) == 0x40 && + ((SRpnt->sr_sense_buffer[0] & 0x80) == 0 || undone == 0)) { ioctl_result = 0; /* EOF written succesfully at EOM */ if (fileno >= 0) fileno++; @@ -2315,7 +2319,7 @@ static int st_int_ioctl(struct inode *inode, STps->drv_block = 0; STps->eof = ST_NOEOF; } else if (cmd_in == MTFSR) { - if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */ + if (SRpnt->sr_sense_buffer[2] & 0x80) { /* Hit filemark */ if (STps->drv_file >= 0) STps->drv_file++; STps->drv_block = 0; @@ -2328,7 +2332,7 @@ static int st_int_ioctl(struct inode *inode, STps->eof = ST_NOEOF; } } else if (cmd_in == MTBSR) { - if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */ + if (SRpnt->sr_sense_buffer[2] & 0x80) { /* Hit filemark */ STps->drv_file--; STps->drv_block = (-1); } else { @@ -2345,14 +2349,14 @@ static int st_int_ioctl(struct inode *inode, } else if (chg_eof) STps->eof = ST_NOEOF; - if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) + if ((SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK) STps->eof = ST_EOD; if (cmd_in == MTLOCK) STp->door_locked = ST_LOCK_FAILS; - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; } return ioctl_result; @@ -2368,14 +2372,14 @@ static int get_location(struct inode *inode, unsigned int *block, int *partition Scsi_Tape *STp; int dev = TAPE_NR(inode->i_rdev); int result; - unsigned char scmd[10]; - Scsi_Cmnd *SCpnt; + unsigned char scmd[MAX_COMMAND_SIZE]; + Scsi_Request *SRpnt; STp = &(scsi_tapes[dev]); if (STp->ready != ST_READY) return (-EIO); - memset(scmd, 0, 10); + memset(scmd, 0, MAX_COMMAND_SIZE); if ((STp->device)->scsi_level < SCSI_2) { scmd[0] = QFA_REQUEST_BLOCK; scmd[4] = 3; @@ -2384,11 +2388,12 @@ static int get_location(struct inode *inode, unsigned int *block, int *partition if (!logical && !STp->scsi2_logical) scmd[1] = 1; } - SCpnt = st_do_scsi(NULL, STp, scmd, 20, STp->timeout, MAX_READY_RETRIES, TRUE); - if (!SCpnt) - return (STp->buffer)->last_result_fatal; + SRpnt = st_do_scsi(NULL, STp, scmd, 20, SCSI_DATA_READ, STp->timeout, + MAX_READY_RETRIES, TRUE); + if (!SRpnt) + return (STp->buffer)->syscall_result; - if ((STp->buffer)->last_result_fatal != 0 || + if ((STp->buffer)->syscall_result != 0 || (STp->device->scsi_level >= SCSI_2 && ((STp->buffer)->b_data[0] & 4) != 0)) { *block = *partition = 0; @@ -2414,8 +2419,8 @@ static int get_location(struct inode *inode, unsigned int *block, int *partition DEBC(printk(ST_DEB_MSG "st%d: Got tape pos. blk %d part %d.\n", dev, *block, *partition)); } - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; return result; } @@ -2432,8 +2437,8 @@ static int set_location(struct inode *inode, unsigned int block, int partition, int result, p; unsigned int blk; int timeout; - unsigned char scmd[10]; - Scsi_Cmnd *SCpnt; + unsigned char scmd[MAX_COMMAND_SIZE]; + Scsi_Request *SRpnt; STp = &(scsi_tapes[dev]); if (STp->ready != ST_READY) @@ -2462,7 +2467,7 @@ static int set_location(struct inode *inode, unsigned int block, int partition, } } - memset(scmd, 0, 10); + memset(scmd, 0, MAX_COMMAND_SIZE); if ((STp->device)->scsi_level < SCSI_2) { scmd[0] = QFA_SEEK_BLOCK; scmd[2] = (block >> 16); @@ -2490,13 +2495,14 @@ static int set_location(struct inode *inode, unsigned int block, int partition, timeout = STp->timeout; #endif - SCpnt = st_do_scsi(NULL, STp, scmd, 20, timeout, MAX_READY_RETRIES, TRUE); - if (!SCpnt) - return (STp->buffer)->last_result_fatal; + SRpnt = st_do_scsi(NULL, STp, scmd, 0, SCSI_DATA_NONE, + timeout, MAX_READY_RETRIES, TRUE); + if (!SRpnt) + return (STp->buffer)->syscall_result; STps->drv_block = STps->drv_file = (-1); STps->eof = ST_NOEOF; - if ((STp->buffer)->last_result_fatal != 0) { + if ((STp->buffer)->syscall_result != 0) { result = (-EIO); if (STp->can_partitions && (STp->device)->scsi_level >= SCSI_2 && @@ -2518,8 +2524,8 @@ static int set_location(struct inode *inode, unsigned int block, int partition, result = 0; } - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; return result; } @@ -2568,27 +2574,28 @@ static int nbr_partitions(struct inode *inode) { int dev = TAPE_NR(inode->i_rdev), result; Scsi_Tape *STp; - Scsi_Cmnd *SCpnt = NULL; - unsigned char cmd[10]; + Scsi_Request *SRpnt = NULL; + unsigned char cmd[MAX_COMMAND_SIZE]; STp = &(scsi_tapes[dev]); if (STp->ready != ST_READY) return (-EIO); - memset((void *) &cmd[0], 0, 10); + memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE); cmd[0] = MODE_SENSE; cmd[1] = 8; /* Page format */ cmd[2] = PART_PAGE; cmd[4] = 200; - SCpnt = st_do_scsi(SCpnt, STp, cmd, 200, STp->timeout, MAX_READY_RETRIES, TRUE); - if (SCpnt == NULL) - return (STp->buffer)->last_result_fatal; + SRpnt = st_do_scsi(SRpnt, STp, cmd, 200, SCSI_DATA_READ, STp->timeout, + MAX_READY_RETRIES, TRUE); + if (SRpnt == NULL) + return (STp->buffer)->syscall_result; - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; - if ((STp->buffer)->last_result_fatal != 0) { + if ((STp->buffer)->syscall_result != 0) { DEBC(printk(ST_DEB_MSG "st%d: Can't read medium partition page.\n", dev)); result = (-EIO); @@ -2608,8 +2615,8 @@ static int partition_tape(struct inode *inode, int size) int dev = TAPE_NR(inode->i_rdev), result; int length; Scsi_Tape *STp; - Scsi_Cmnd *SCpnt = NULL; - unsigned char cmd[10], *bp; + Scsi_Request *SRpnt = NULL; + unsigned char cmd[MAX_COMMAND_SIZE], *bp; if ((result = nbr_partitions(inode)) < 0) return result; @@ -2640,20 +2647,20 @@ static int partition_tape(struct inode *inode, int size) bp[MODE_HEADER_LENGTH] &= 0x3f; bp[MODE_HEADER_LENGTH + 1] = length - 2; - memset(cmd, 0, 10); + memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = MODE_SELECT; cmd[1] = 0x10; cmd[4] = length + MODE_HEADER_LENGTH; - SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->long_timeout, - MAX_READY_RETRIES, TRUE); - if (SCpnt == NULL) - return (STp->buffer)->last_result_fatal; + SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE, + STp->long_timeout, MAX_READY_RETRIES, TRUE); + if (SRpnt == NULL) + return (STp->buffer)->syscall_result; - scsi_release_command(SCpnt); - SCpnt = NULL; + scsi_release_request(SRpnt); + SRpnt = NULL; - if ((STp->buffer)->last_result_fatal != 0) { + if ((STp->buffer)->syscall_result != 0) { printk(KERN_INFO "st%d: Partitioning of tape failed.\n", dev); result = (-EIO); } else @@ -3230,7 +3237,7 @@ static int st_attach(Scsi_Device * SDp) Scsi_Tape *tpnt; ST_mode *STm; ST_partstat *STps; - int i; + int i, mode; if (SDp->type != TYPE_TAPE) return 1; @@ -3247,6 +3254,26 @@ static int st_attach(Scsi_Device * SDp) if (i >= st_template.dev_max) panic("scsi_devices corrupt (st)"); + for (mode = 0; mode < ST_NBR_MODES; ++mode) { + char name[8]; + static char *formats[ST_NBR_MODES] ={"", "l", "m", "a"}; + + /* Rewind entry */ + sprintf (name, "mt%s", formats[mode]); + tpnt->de_r[mode] = + devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT, + MAJOR_NR, i + (mode << 5), + S_IFCHR | S_IRUGO | S_IWUGO, + 0, 0, &st_fops, NULL); + /* No-rewind entry */ + sprintf (name, "mt%sn", formats[mode]); + tpnt->de_n[mode] = + devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT, + MAJOR_NR, i + (mode << 5) + 128, + S_IFCHR | S_IRUGO | S_IWUGO, + 0, 0, &st_fops, NULL); + } + devfs_register_tape (tpnt->de_r[0]); scsi_tapes[i].device = SDp; if (SDp->scsi_level <= 2) scsi_tapes[i].mt_status->mt_type = MT_ISSCSI1; @@ -3336,7 +3363,7 @@ static int st_init() st_buffer_size, st_write_threshold, st_max_buffers, st_max_sg_segs); if (!st_registered) { - if (register_chrdev(SCSI_TAPE_MAJOR, "st", &st_fops)) { + if (devfs_register_chrdev(SCSI_TAPE_MAJOR, "st", &st_fops)) { printk(KERN_ERR "Unable to get major %d for SCSI tapes\n", MAJOR_NR); return 1; @@ -3355,7 +3382,7 @@ static int st_init() GFP_ATOMIC); if (scsi_tapes == NULL) { printk(KERN_ERR "Unable to allocate descriptors for SCSI tapes.\n"); - unregister_chrdev(SCSI_TAPE_MAJOR, "st"); + devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st"); return 1; } @@ -3372,7 +3399,7 @@ static int st_init() for (j=0; j < i; j++) kfree(scsi_tapes[j].mt_status); kfree(scsi_tapes); - unregister_chrdev(SCSI_TAPE_MAJOR, "st"); + devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st"); return 1; } /* Initialize status */ @@ -3385,7 +3412,7 @@ static int st_init() GFP_ATOMIC); if (st_buffers == NULL) { printk(KERN_ERR "Unable to allocate tape buffer pointers.\n"); - unregister_chrdev(SCSI_TAPE_MAJOR, "st"); + devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st"); for (i=0; i < st_template.dev_max; i++) kfree(scsi_tapes[i].mt_status); kfree(scsi_tapes); @@ -3416,11 +3443,17 @@ static int st_init() static void st_detach(Scsi_Device * SDp) { Scsi_Tape *tpnt; - int i; + int i, mode; for (tpnt = scsi_tapes, i = 0; i < st_template.dev_max; i++, tpnt++) if (tpnt->device == SDp) { tpnt->device = NULL; + for (mode = 0; mode < ST_NBR_MODES; ++mode) { + devfs_unregister (tpnt->de_r[mode]); + tpnt->de_r[mode] = NULL; + devfs_unregister (tpnt->de_n[mode]); + tpnt->de_n[mode] = NULL; + } SDp->attached--; st_template.nr_dev--; st_template.dev_noticed--; @@ -3445,7 +3478,7 @@ void cleanup_module(void) int i; scsi_unregister_module(MODULE_SCSI_DEV, &st_template); - unregister_chrdev(SCSI_TAPE_MAJOR, "st"); + devfs_unregister_chrdev(SCSI_TAPE_MAJOR, "st"); st_registered--; if (scsi_tapes != NULL) { for (i=0; i < st_template.dev_max; ++i) diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index 479dca079..e751efc28 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h @@ -8,6 +8,7 @@ #ifndef _SCSI_H #include "scsi.h" #endif +#include <linux/devfs_fs_kernel.h> /* The tape buffer descriptor. */ typedef struct { @@ -18,9 +19,9 @@ typedef struct { int buffer_bytes; int read_pointer; int writing; - int last_result; - int last_result_fatal; - Scsi_Cmnd *last_SCpnt; + int midlevel_result; + int syscall_result; + Scsi_Request *last_SRpnt; unsigned char *b_data; unsigned short use_sg; /* zero or number of segments for this adapter */ unsigned short sg_segs; /* total number of allocated segments */ @@ -85,6 +86,8 @@ typedef struct { /* Mode characteristics */ ST_mode modes[ST_NBR_MODES]; int current_mode; + devfs_handle_t de_r[ST_NBR_MODES]; /* Rewind entries */ + devfs_handle_t de_n[ST_NBR_MODES]; /* No-rewind entries */ /* Status variables */ int partition; diff --git a/drivers/scsi/sun3_NCR5380.c b/drivers/scsi/sun3_NCR5380.c new file mode 100644 index 000000000..510386823 --- /dev/null +++ b/drivers/scsi/sun3_NCR5380.c @@ -0,0 +1,3012 @@ +/* sun3_NCR5380.c -- adapted from atari_NCR5380.c for the sun3 by + Sam Creasey. */ +/* + * NCR 5380 generic driver routines. These should make it *trivial* + * to implement 5380 SCSI drivers under Linux with a non-trantor + * architecture. + * + * Note that these routines also work with NR53c400 family chips. + * + * Copyright 1993, Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * drew@colorado.edu + * +1 (303) 666-5836 + * + * DISTRIBUTION RELEASE 6. + * + * For more information, please consult + * + * NCR 5380 Family + * SCSI Protocol Controller + * Databook + * + * NCR Microelectronics + * 1635 Aeroplaza Drive + * Colorado Springs, CO 80916 + * 1+ (719) 578-3400 + * 1+ (800) 334-5454 + */ + +/* + * ++roman: To port the 5380 driver to the Atari, I had to do some changes in + * this file, too: + * + * - Some of the debug statements were incorrect (undefined variables and the + * like). I fixed that. + * + * - In information_transfer(), I think a #ifdef was wrong. Looking at the + * possible DMA transfer size should also happen for REAL_DMA. I added this + * in the #if statement. + * + * - When using real DMA, information_transfer() should return in a DATAOUT + * phase after starting the DMA. It has nothing more to do. + * + * - The interrupt service routine should run main after end of DMA, too (not + * only after RESELECTION interrupts). Additionally, it should _not_ test + * for more interrupts after running main, since a DMA process may have + * been started and interrupts are turned on now. The new int could happen + * inside the execution of NCR5380_intr(), leading to recursive + * calls. + * + * - I've added a function merge_contiguous_buffers() that tries to + * merge scatter-gather buffers that are located at contiguous + * physical addresses and can be processed with the same DMA setup. + * Since most scatter-gather operations work on a page (4K) of + * 4 buffers (1K), in more than 90% of all cases three interrupts and + * DMA setup actions are saved. + * + * - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA + * and USLEEP, because these were messing up readability and will never be + * needed for Atari SCSI. + * + * - I've revised the NCR5380_main() calling scheme (relax the 'main_running' + * stuff), and 'main' is executed in a bottom half if awoken by an + * interrupt. + * + * - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..." + * constructs. In my eyes, this made the source rather unreadable, so I + * finally replaced that by the *_PRINTK() macros. + * + */ + +/* + * Further development / testing that should be done : + * 1. Test linked command handling code after Eric is ready with + * the high level code. + */ + +#if (NDEBUG & NDEBUG_LISTS) +#define LIST(x,y) \ + { printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); \ + if ((x)==(y)) udelay(5); } +#define REMOVE(w,x,y,z) \ + { printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, \ + (void*)(w), (void*)(x), (void*)(y), (void*)(z)); \ + if ((x)==(y)) udelay(5); } +#else +#define LIST(x,y) +#define REMOVE(w,x,y,z) +#endif + +#ifndef notyet +#undef LINKED +#endif + +/* + * Design + * Issues : + * + * The other Linux SCSI drivers were written when Linux was Intel PC-only, + * and specifically for each board rather than each chip. This makes their + * adaptation to platforms like the Mac (Some of which use NCR5380's) + * more difficult than it has to be. + * + * Also, many of the SCSI drivers were written before the command queuing + * routines were implemented, meaning their implementations of queued + * commands were hacked on rather than designed in from the start. + * + * When I designed the Linux SCSI drivers I figured that + * while having two different SCSI boards in a system might be useful + * for debugging things, two of the same type wouldn't be used. + * Well, I was wrong and a number of users have mailed me about running + * multiple high-performance SCSI boards in a server. + * + * Finally, when I get questions from users, I have no idea what + * revision of my driver they are running. + * + * This driver attempts to address these problems : + * This is a generic 5380 driver. To use it on a different platform, + * one simply writes appropriate system specific macros (ie, data + * transfer - some PC's will use the I/O bus, 68K's must use + * memory mapped) and drops this file in their 'C' wrapper. + * + * As far as command queueing, two queues are maintained for + * each 5380 in the system - commands that haven't been issued yet, + * and commands that are currently executing. This means that an + * unlimited number of commands may be queued, letting + * more commands propagate from the higher driver levels giving higher + * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported, + * allowing multiple commands to propagate all the way to a SCSI-II device + * while a command is already executing. + * + * To solve the multiple-boards-in-the-same-system problem, + * there is a separate instance structure for each instance + * of a 5380 in the system. So, multiple NCR5380 drivers will + * be able to coexist with appropriate changes to the high level + * SCSI code. + * + * A NCR5380_PUBLIC_REVISION macro is provided, with the release + * number (updated for each public release) printed by the + * NCR5380_print_options command, which should be called from the + * wrapper detect function, so that I know what release of the driver + * users are using. + * + * Issues specific to the NCR5380 : + * + * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead + * piece of hardware that requires you to sit in a loop polling for + * the REQ signal as long as you are connected. Some devices are + * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect + * while doing long seek operations. + * + * The workaround for this is to keep track of devices that have + * disconnected. If the device hasn't disconnected, for commands that + * should disconnect, we do something like + * + * while (!REQ is asserted) { sleep for N usecs; poll for M usecs } + * + * Some tweaking of N and M needs to be done. An algorithm based + * on "time to data" would give the best results as long as short time + * to datas (ie, on the same track) were considered, however these + * broken devices are the exception rather than the rule and I'd rather + * spend my time optimizing for the normal case. + * + * Architecture : + * + * At the heart of the design is a coroutine, NCR5380_main, + * which is started when not running by the interrupt handler, + * timer, and queue command function. It attempts to establish + * I_T_L or I_T_L_Q nexuses by removing the commands from the + * issue queue and calling NCR5380_select() if a nexus + * is not established. + * + * Once a nexus is established, the NCR5380_information_transfer() + * phase goes through the various phases as instructed by the target. + * if the target goes into MSG IN and sends a DISCONNECT message, + * the command structure is placed into the per instance disconnected + * queue, and NCR5380_main tries to find more work. If USLEEP + * was defined, and the target is idle for too long, the system + * will try to sleep. + * + * If a command has disconnected, eventually an interrupt will trigger, + * calling NCR5380_intr() which will in turn call NCR5380_reselect + * to reestablish a nexus. This will run main if necessary. + * + * On command termination, the done function will be called as + * appropriate. + * + * SCSI pointers are maintained in the SCp field of SCSI command + * structures, being initialized after the command is connected + * in NCR5380_select, and set as appropriate in NCR5380_information_transfer. + * Note that in violation of the standard, an implicit SAVE POINTERS operation + * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS. + */ + +/* + * Using this file : + * This file a skeleton Linux SCSI driver for the NCR 5380 series + * of chips. To use it, you write an architecture specific functions + * and macros and include this file in your driver. + * + * These macros control options : + * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically + * for commands that return with a CHECK CONDITION status. + * + * LINKED - if defined, linked commands are supported. + * + * REAL_DMA - if defined, REAL DMA is used during the data transfer phases. + * + * SUPPORT_TAGS - if defined, SCSI-2 tagged queuing is used where possible + * + * These macros MUST be defined : + * + * NCR5380_read(register) - read from the specified register + * + * NCR5380_write(register, value) - write to the specific register + * + * Either real DMA *or* pseudo DMA may be implemented + * REAL functions : + * NCR5380_REAL_DMA should be defined if real DMA is to be used. + * Note that the DMA setup functions should return the number of bytes + * that they were able to program the controller for. + * + * Also note that generic i386/PC versions of these macros are + * available as NCR5380_i386_dma_write_setup, + * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual. + * + * NCR5380_dma_write_setup(instance, src, count) - initialize + * NCR5380_dma_read_setup(instance, dst, count) - initialize + * NCR5380_dma_residual(instance); - residual count + * + * PSEUDO functions : + * NCR5380_pwrite(instance, src, count) + * NCR5380_pread(instance, dst, count); + * + * If nothing specific to this implementation needs doing (ie, with external + * hardware), you must also define + * + * NCR5380_queue_command + * NCR5380_reset + * NCR5380_abort + * NCR5380_proc_info + * + * to be the global entry points into the specific driver, ie + * #define NCR5380_queue_command t128_queue_command. + * + * If this is not done, the routines will be defined as static functions + * with the NCR5380* names and the user must provide a globally + * accessible wrapper function. + * + * The generic driver is initialized by calling NCR5380_init(instance), + * after setting the appropriate host specific fields and ID. If the + * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance, + * possible) function may be used. Before the specific driver initialization + * code finishes, NCR5380_print_options should be called. + */ + +static struct Scsi_Host *first_instance = NULL; +static Scsi_Host_Template *the_template = NULL; + +/* Macros ease life... :-) */ +#define SETUP_HOSTDATA(in) \ + struct NCR5380_hostdata *hostdata = \ + (struct NCR5380_hostdata *)(in)->hostdata +#define HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata) + +#define NEXT(cmd) ((Scsi_Cmnd *)((cmd)->host_scribble)) +#define NEXTADDR(cmd) ((Scsi_Cmnd **)&((cmd)->host_scribble)) + +#define HOSTNO instance->host_no +#define H_NO(cmd) (cmd)->host->host_no + +#ifdef SUPPORT_TAGS + +/* + * Functions for handling tagged queuing + * ===================================== + * + * ++roman (01/96): Now I've implemented SCSI-2 tagged queuing. Some notes: + * + * Using consecutive numbers for the tags is no good idea in my eyes. There + * could be wrong re-usings if the counter (8 bit!) wraps and some early + * command has been preempted for a long time. My solution: a bitfield for + * remembering used tags. + * + * There's also the problem that each target has a certain queue size, but we + * cannot know it in advance :-( We just see a QUEUE_FULL status being + * returned. So, in this case, the driver internal queue size assumption is + * reduced to the number of active tags if QUEUE_FULL is returned by the + * target. The command is returned to the mid-level, but with status changed + * to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL + * correctly. + * + * We're also not allowed running tagged commands as long as an untagged + * command is active. And REQUEST SENSE commands after a contingent allegiance + * condition _must_ be untagged. To keep track whether an untagged command has + * been issued, the host->busy array is still employed, as it is without + * support for tagged queuing. + * + * One could suspect that there are possible race conditions between + * is_lun_busy(), cmd_get_tag() and cmd_free_tag(). But I think this isn't the + * case: is_lun_busy() and cmd_get_tag() are both called from NCR5380_main(), + * which already guaranteed to be running at most once. It is also the only + * place where tags/LUNs are allocated. So no other allocation can slip + * between that pair, there could only happen a reselection, which can free a + * tag, but that doesn't hurt. Only the sequence in cmd_free_tag() becomes + * important: the tag bit must be cleared before 'nr_allocated' is decreased. + */ + +/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */ +#undef TAG_NONE +#define TAG_NONE 0xff + +/* For the m68k, the number of bits in 'allocated' must be a multiple of 32! */ +#if (MAX_TAGS % 32) != 0 +#error "MAX_TAGS must be a multiple of 32!" +#endif + +typedef struct { + char allocated[MAX_TAGS/8]; + int nr_allocated; + int queue_size; +} TAG_ALLOC; + +static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */ + + +static void __init init_tags( void ) +{ + int target, lun; + TAG_ALLOC *ta; + + if (!setup_use_tagged_queuing) + return; + + for( target = 0; target < 8; ++target ) { + for( lun = 0; lun < 8; ++lun ) { + ta = &TagAlloc[target][lun]; + memset( &ta->allocated, 0, MAX_TAGS/8 ); + ta->nr_allocated = 0; + /* At the beginning, assume the maximum queue size we could + * support (MAX_TAGS). This value will be decreased if the target + * returns QUEUE_FULL status. + */ + ta->queue_size = MAX_TAGS; + } + } +} + + +/* Check if we can issue a command to this LUN: First see if the LUN is marked + * busy by an untagged command. If the command should use tagged queuing, also + * check that there is a free tag and the target's queue won't overflow. This + * function should be called with interrupts disabled to avoid race + * conditions. + */ + +static int is_lun_busy( Scsi_Cmnd *cmd, int should_be_tagged ) +{ + SETUP_HOSTDATA(cmd->host); + + if (hostdata->busy[cmd->target] & (1 << cmd->lun)) + return( 1 ); + if (!should_be_tagged || + !setup_use_tagged_queuing || !cmd->device->tagged_supported) + return( 0 ); + if (TagAlloc[cmd->target][cmd->lun].nr_allocated >= + TagAlloc[cmd->target][cmd->lun].queue_size ) { + TAG_PRINTK( "scsi%d: target %d lun %d: no free tags\n", + H_NO(cmd), cmd->target, cmd->lun ); + return( 1 ); + } + return( 0 ); +} + + +/* Allocate a tag for a command (there are no checks anymore, check_lun_busy() + * must be called before!), or reserve the LUN in 'busy' if the command is + * untagged. + */ + +static void cmd_get_tag( Scsi_Cmnd *cmd, int should_be_tagged ) +{ + SETUP_HOSTDATA(cmd->host); + + /* If we or the target don't support tagged queuing, allocate the LUN for + * an untagged command. + */ + if (!should_be_tagged || + !setup_use_tagged_queuing || !cmd->device->tagged_supported) { + cmd->tag = TAG_NONE; + hostdata->busy[cmd->target] |= (1 << cmd->lun); + TAG_PRINTK( "scsi%d: target %d lun %d now allocated by untagged " + "command\n", H_NO(cmd), cmd->target, cmd->lun ); + } + else { + TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun]; + + cmd->tag = find_first_zero_bit( &ta->allocated, MAX_TAGS ); + set_bit( cmd->tag, &ta->allocated ); + ta->nr_allocated++; + TAG_PRINTK( "scsi%d: using tag %d for target %d lun %d " + "(now %d tags in use)\n", + H_NO(cmd), cmd->tag, cmd->target, cmd->lun, + ta->nr_allocated ); + } +} + + +/* Mark the tag of command 'cmd' as free, or in case of an untagged command, + * unlock the LUN. + */ + +static void cmd_free_tag( Scsi_Cmnd *cmd ) +{ + SETUP_HOSTDATA(cmd->host); + + if (cmd->tag == TAG_NONE) { + hostdata->busy[cmd->target] &= ~(1 << cmd->lun); + TAG_PRINTK( "scsi%d: target %d lun %d untagged cmd finished\n", + H_NO(cmd), cmd->target, cmd->lun ); + } + else if (cmd->tag >= MAX_TAGS) { + printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n", + H_NO(cmd), cmd->tag ); + } + else { + TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun]; + clear_bit( cmd->tag, &ta->allocated ); + ta->nr_allocated--; + TAG_PRINTK( "scsi%d: freed tag %d for target %d lun %d\n", + H_NO(cmd), cmd->tag, cmd->target, cmd->lun ); + } +} + + +static void free_all_tags( void ) +{ + int target, lun; + TAG_ALLOC *ta; + + if (!setup_use_tagged_queuing) + return; + + for( target = 0; target < 8; ++target ) { + for( lun = 0; lun < 8; ++lun ) { + ta = &TagAlloc[target][lun]; + memset( &ta->allocated, 0, MAX_TAGS/8 ); + ta->nr_allocated = 0; + } + } +} + +#endif /* SUPPORT_TAGS */ + + +/* + * Function: void merge_contiguous_buffers( Scsi_Cmnd *cmd ) + * + * Purpose: Try to merge several scatter-gather requests into one DMA + * transfer. This is possible if the scatter buffers lie on + * physical contiguous addresses. + * + * Parameters: Scsi_Cmnd *cmd + * The command to work on. The first scatter buffer's data are + * assumed to be already transfered into ptr/this_residual. + */ + +static void merge_contiguous_buffers( Scsi_Cmnd *cmd ) +{ + unsigned long endaddr; +#if (NDEBUG & NDEBUG_MERGING) + unsigned long oldlen = cmd->SCp.this_residual; + int cnt = 1; +#endif + + for (endaddr = virt_to_phys(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1; + cmd->SCp.buffers_residual && + virt_to_phys(cmd->SCp.buffer[1].address) == endaddr; ) { + + MER_PRINTK("VTOP(%p) == %08lx -> merging\n", + cmd->SCp.buffer[1].address, endaddr); +#if (NDEBUG & NDEBUG_MERGING) + ++cnt; +#endif + ++cmd->SCp.buffer; + --cmd->SCp.buffers_residual; + cmd->SCp.this_residual += cmd->SCp.buffer->length; + endaddr += cmd->SCp.buffer->length; + } +#if (NDEBUG & NDEBUG_MERGING) + if (oldlen != cmd->SCp.this_residual) + MER_PRINTK("merged %d buffers from %p, new length %08x\n", + cnt, cmd->SCp.ptr, cmd->SCp.this_residual); +#endif +} + +/* + * Function : void initialize_SCp(Scsi_Cmnd *cmd) + * + * Purpose : initialize the saved data pointers for cmd to point to the + * start of the buffer. + * + * Inputs : cmd - Scsi_Cmnd structure to have pointers reset. + */ + +static __inline__ void initialize_SCp(Scsi_Cmnd *cmd) +{ + /* + * Initialize the Scsi Pointer field so that all of the commands in the + * various queues are valid. + */ + + if (cmd->use_sg) { + cmd->SCp.buffer = (struct scatterlist *) cmd->buffer; + cmd->SCp.buffers_residual = cmd->use_sg - 1; + cmd->SCp.ptr = (char *) cmd->SCp.buffer->address; + cmd->SCp.this_residual = cmd->SCp.buffer->length; + /* ++roman: Try to merge some scatter-buffers if they are at + * contiguous physical addresses. + */ + merge_contiguous_buffers( cmd ); + } else { + cmd->SCp.buffer = NULL; + cmd->SCp.buffers_residual = 0; + cmd->SCp.ptr = (char *) cmd->request_buffer; + cmd->SCp.this_residual = cmd->request_bufflen; + } + +} + +#include <linux/config.h> +#include <linux/delay.h> + +#if 1 +static struct { + unsigned char mask; + const char * name;} +signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" }, + { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" }, + { SR_SEL, "SEL" }, {0, NULL}}, +basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}}, +icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"}, + {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"}, + {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"}, + {0, NULL}}, +mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"}, + {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR, + "MODE PARITY INTR"}, {MR_ENABLE_EOP_INTR,"MODE EOP INTR"}, + {MR_MONITOR_BSY, "MODE MONITOR BSY"}, + {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"}, + {0, NULL}}; + +/* + * Function : void NCR5380_print(struct Scsi_Host *instance) + * + * Purpose : print the SCSI bus signals for debugging purposes + * + * Input : instance - which NCR5380 + */ + +static void NCR5380_print(struct Scsi_Host *instance) { + unsigned char status, data, basr, mr, icr, i; + unsigned long flags; + + save_flags(flags); + cli(); + data = NCR5380_read(CURRENT_SCSI_DATA_REG); + status = NCR5380_read(STATUS_REG); + mr = NCR5380_read(MODE_REG); + icr = NCR5380_read(INITIATOR_COMMAND_REG); + basr = NCR5380_read(BUS_AND_STATUS_REG); + restore_flags(flags); + printk("STATUS_REG: %02x ", status); + for (i = 0; signals[i].mask ; ++i) + if (status & signals[i].mask) + printk(",%s", signals[i].name); + printk("\nBASR: %02x ", basr); + for (i = 0; basrs[i].mask ; ++i) + if (basr & basrs[i].mask) + printk(",%s", basrs[i].name); + printk("\nICR: %02x ", icr); + for (i = 0; icrs[i].mask; ++i) + if (icr & icrs[i].mask) + printk(",%s", icrs[i].name); + printk("\nMODE: %02x ", mr); + for (i = 0; mrs[i].mask; ++i) + if (mr & mrs[i].mask) + printk(",%s", mrs[i].name); + printk("\n"); +} + +static struct { + unsigned char value; + const char *name; +} phases[] = { + {PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"}, + {PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"}, + {PHASE_UNKNOWN, "UNKNOWN"}}; + +/* + * Function : void NCR5380_print_phase(struct Scsi_Host *instance) + * + * Purpose : print the current SCSI phase for debugging purposes + * + * Input : instance - which NCR5380 + */ + +static void NCR5380_print_phase(struct Scsi_Host *instance) +{ + unsigned char status; + int i; + + status = NCR5380_read(STATUS_REG); + if (!(status & SR_REQ)) + printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO); + else { + for (i = 0; (phases[i].value != PHASE_UNKNOWN) && + (phases[i].value != (status & PHASE_MASK)); ++i); + printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name); + } +} + +#else /* !NDEBUG */ + +/* dummies... */ +__inline__ void NCR5380_print(struct Scsi_Host *instance) { }; +__inline__ void NCR5380_print_phase(struct Scsi_Host *instance) { }; + +#endif + +/* + * ++roman: New scheme of calling NCR5380_main() + * + * If we're not in an interrupt, we can call our main directly, it cannot be + * already running. Else, we queue it on a task queue, if not 'main_running' + * tells us that a lower level is already executing it. This way, + * 'main_running' needs not be protected in a special way. + * + * queue_main() is a utility function for putting our main onto the task + * queue, if main_running is false. It should be called only from a + * interrupt or bottom half. + */ + +#include <linux/tqueue.h> +#include <linux/interrupt.h> + +static volatile int main_running = 0; +static struct tq_struct NCR5380_tqueue = { + NULL, /* next */ + 0, /* sync */ + (void (*)(void*))NCR5380_main, /* routine, must have (void *) arg... */ + NULL /* data */ +}; + +static __inline__ void queue_main(void) +{ + if (!main_running) { + /* If in interrupt and NCR5380_main() not already running, + queue it on the 'immediate' task queue, to be processed + immediately after the current interrupt processing has + finished. */ + queue_task(&NCR5380_tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + /* else: nothing to do: the running NCR5380_main() will pick up + any newly queued command. */ +} + + +static inline void NCR5380_all_init (void) +{ + static int done = 0; + if (!done) { + INI_PRINTK("scsi : NCR5380_all_init()\n"); + done = 1; + } +} + + +/* + * Function : void NCR58380_print_options (struct Scsi_Host *instance) + * + * Purpose : called by probe code indicating the NCR5380 driver + * options that were selected. + * + * Inputs : instance, pointer to this instance. Unused. + */ + +static void __init NCR5380_print_options (struct Scsi_Host *instance) +{ + printk(" generic options" +#ifdef AUTOSENSE + " AUTOSENSE" +#endif +#ifdef REAL_DMA + " REAL DMA" +#endif +#ifdef PARITY + " PARITY" +#endif +#ifdef SUPPORT_TAGS + " SCSI-2 TAGGED QUEUING" +#endif + ); + printk(" generic release=%d", NCR5380_PUBLIC_RELEASE); +} + +/* + * Function : void NCR5380_print_status (struct Scsi_Host *instance) + * + * Purpose : print commands in the various queues, called from + * NCR5380_abort and NCR5380_debug to aid debugging. + * + * Inputs : instance, pointer to this instance. + */ + +static void NCR5380_print_status (struct Scsi_Host *instance) +{ + char *pr_bfr; + char *start; + int len; + + NCR_PRINT(NDEBUG_ANY); + NCR_PRINT_PHASE(NDEBUG_ANY); + + pr_bfr = (char *) __get_free_page(GFP_ATOMIC); + if (!pr_bfr) { + printk("NCR5380_print_status: no memory for print buffer\n"); + return; + } + len = NCR5380_proc_info(pr_bfr, &start, 0, PAGE_SIZE, HOSTNO, 0); + pr_bfr[len] = 0; + printk("\n%s\n", pr_bfr); + free_page((unsigned long) pr_bfr); +} + + +/******************************************/ +/* + * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED] + * + * *buffer: I/O buffer + * **start: if inout == FALSE pointer into buffer where user read should start + * offset: current offset + * length: length of buffer + * hostno: Scsi_Host host_no + * inout: TRUE - user is writing; FALSE - user is reading + * + * Return the number of bytes read from or written +*/ + +#undef SPRINTF +#define SPRINTF(fmt,args...) \ + do { if (pos + strlen(fmt) + 20 /* slop */ < buffer + length) \ + pos += sprintf(pos, fmt , ## args); } while(0) +static +char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length); + +#ifndef NCR5380_proc_info +static +#endif +int NCR5380_proc_info (char *buffer, char **start, off_t offset, + int length, int hostno, int inout) +{ + char *pos = buffer; + struct Scsi_Host *instance; + struct NCR5380_hostdata *hostdata; + Scsi_Cmnd *ptr; + unsigned long flags; + off_t begin = 0; +#define check_offset() \ + do { \ + if (pos - buffer < offset - begin) { \ + begin += pos - buffer; \ + pos = buffer; \ + } \ + } while (0) + + for (instance = first_instance; instance && HOSTNO != hostno; + instance = instance->next) + ; + if (!instance) + return(-ESRCH); + hostdata = (struct NCR5380_hostdata *)instance->hostdata; + + if (inout) { /* Has data been written to the file ? */ + return(-ENOSYS); /* Currently this is a no-op */ + } + SPRINTF("NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE); + check_offset(); + save_flags(flags); + cli(); + SPRINTF("NCR5380: coroutine is%s running.\n", main_running ? "" : "n't"); + check_offset(); + if (!hostdata->connected) + SPRINTF("scsi%d: no currently connected command\n", HOSTNO); + else + pos = lprint_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected, + pos, buffer, length); + SPRINTF("scsi%d: issue_queue\n", HOSTNO); + check_offset(); + for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = NEXT(ptr)) { + pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length); + check_offset(); + } + + SPRINTF("scsi%d: disconnected_queue\n", HOSTNO); + check_offset(); + for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; + ptr = NEXT(ptr)) { + pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length); + check_offset(); + } + + restore_flags(flags); + *start = buffer + (offset - begin); + if (pos - buffer < offset - begin) + return 0; + else if (pos - buffer - (offset - begin) < length) + return pos - buffer - (offset - begin); + return length; +} + +static char * +lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length) +{ + int i, s; + unsigned char *command; + SPRINTF("scsi%d: destination target %d, lun %d\n", + H_NO(cmd), cmd->target, cmd->lun); + SPRINTF(" command = "); + command = cmd->cmnd; + SPRINTF("%2d (0x%02x)", command[0], command[0]); + for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i) + SPRINTF(" %02x", command[i]); + SPRINTF("\n"); + return pos; +} + + +/* + * Function : void NCR5380_init (struct Scsi_Host *instance) + * + * Purpose : initializes *instance and corresponding 5380 chip. + * + * Inputs : instance - instantiation of the 5380 driver. + * + * Notes : I assume that the host, hostno, and id bits have been + * set correctly. I don't care about the irq and other fields. + * + */ + +static void __init NCR5380_init (struct Scsi_Host *instance, int flags) +{ + int i; + SETUP_HOSTDATA(instance); + + NCR5380_all_init(); + + hostdata->aborted = 0; + hostdata->id_mask = 1 << instance->this_id; + hostdata->id_higher_mask = 0; + for (i = hostdata->id_mask; i <= 0x80; i <<= 1) + if (i > hostdata->id_mask) + hostdata->id_higher_mask |= i; + for (i = 0; i < 8; ++i) + hostdata->busy[i] = 0; +#ifdef SUPPORT_TAGS + init_tags(); +#endif +#if defined (REAL_DMA) + hostdata->dma_len = 0; +#endif + hostdata->targets_present = 0; + hostdata->connected = NULL; + hostdata->issue_queue = NULL; + hostdata->disconnected_queue = NULL; + hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT; + + if (!the_template) { + the_template = instance->hostt; + first_instance = instance; + } + + +#ifndef AUTOSENSE + if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1)) + printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n" + " without AUTOSENSE option, contingent allegiance conditions may\n" + " be incorrectly cleared.\n", HOSTNO); +#endif /* def AUTOSENSE */ + + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + NCR5380_write(MODE_REG, MR_BASE); + NCR5380_write(TARGET_COMMAND_REG, 0); + NCR5380_write(SELECT_ENABLE_REG, 0); +} + +/* + * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd, + * void (*done)(Scsi_Cmnd *)) + * + * Purpose : enqueues a SCSI command + * + * Inputs : cmd - SCSI command, done - function called on completion, with + * a pointer to the command descriptor. + * + * Returns : 0 + * + * Side effects : + * cmd is added to the per instance issue_queue, with minor + * twiddling done to the host specific fields of cmd. If the + * main coroutine is not running, it is restarted. + * + */ + +/* Only make static if a wrapper function is used */ +#ifndef NCR5380_queue_command +static +#endif +int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) +{ + SETUP_HOSTDATA(cmd->host); + Scsi_Cmnd *tmp; + unsigned long flags; + extern int update_timeout(Scsi_Cmnd * SCset, int timeout); + +#if (NDEBUG & NDEBUG_NO_WRITE) + switch (cmd->cmnd[0]) { + case WRITE_6: + case WRITE_10: + printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n", + H_NO(cmd)); + cmd->result = (DID_ERROR << 16); + done(cmd); + return 0; + } +#endif /* (NDEBUG & NDEBUG_NO_WRITE) */ + + +#ifdef NCR5380_STATS +# if 0 + if (!hostdata->connected && !hostdata->issue_queue && + !hostdata->disconnected_queue) { + hostdata->timebase = jiffies; + } +# endif +# ifdef NCR5380_STAT_LIMIT + if (cmd->request_bufflen > NCR5380_STAT_LIMIT) +# endif + switch (cmd->cmnd[0]) + { + case WRITE: + case WRITE_6: + case WRITE_10: + hostdata->time_write[cmd->target] -= (jiffies - hostdata->timebase); + hostdata->bytes_write[cmd->target] += cmd->request_bufflen; + hostdata->pendingw++; + break; + case READ: + case READ_6: + case READ_10: + hostdata->time_read[cmd->target] -= (jiffies - hostdata->timebase); + hostdata->bytes_read[cmd->target] += cmd->request_bufflen; + hostdata->pendingr++; + break; + } +#endif + + /* + * We use the host_scribble field as a pointer to the next command + * in a queue + */ + + NEXT(cmd) = NULL; + cmd->scsi_done = done; + + cmd->result = 0; + + + /* + * Insert the cmd into the issue queue. Note that REQUEST SENSE + * commands are added to the head of the queue since any command will + * clear the contingent allegiance condition that exists and the + * sense data is only guaranteed to be valid while the condition exists. + */ + + save_flags(flags); + cli(); + /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA. + * Otherwise a running NCR5380_main may steal the lock. + * Lock before actually inserting due to fairness reasons explained in + * atari_scsi.c. If we insert first, then it's impossible for this driver + * to release the lock. + * Stop timer for this command while waiting for the lock, or timeouts + * may happen (and they really do), and it's no good if the command doesn't + * appear in any of the queues. + * ++roman: Just disabling the NCR interrupt isn't sufficient here, + * because also a timer int can trigger an abort or reset, which would + * alter queues and touch the lock. + */ + if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { + LIST(cmd, hostdata->issue_queue); + NEXT(cmd) = hostdata->issue_queue; + hostdata->issue_queue = cmd; + } else { + for (tmp = (Scsi_Cmnd *)hostdata->issue_queue; + NEXT(tmp); tmp = NEXT(tmp)) + ; + LIST(cmd, tmp); + NEXT(tmp) = cmd; + } + + restore_flags(flags); + + QU_PRINTK("scsi%d: command added to %s of queue\n", H_NO(cmd), + (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"); + + /* If queue_command() is called from an interrupt (real one or bottom + * half), we let queue_main() do the job of taking care about main. If it + * is already running, this is a no-op, else main will be queued. + * + * If we're not in an interrupt, we can call NCR5380_main() + * unconditionally, because it cannot be already running. + */ + if (in_interrupt() || ((flags >> 8) & 7) >= 6) + queue_main(); + else + NCR5380_main(); + return 0; +} + +/* + * Function : NCR5380_main (void) + * + * Purpose : NCR5380_main is a coroutine that runs as long as more work can + * be done on the NCR5380 host adapters in a system. Both + * NCR5380_queue_command() and NCR5380_intr() will try to start it + * in case it is not running. + * + * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should + * reenable them. This prevents reentrancy and kernel stack overflow. + */ + +static void NCR5380_main (void) +{ + Scsi_Cmnd *tmp, *prev; + struct Scsi_Host *instance = first_instance; + struct NCR5380_hostdata *hostdata = HOSTDATA(instance); + int done; + unsigned long flags; + + /* + * We run (with interrupts disabled) until we're sure that none of + * the host adapters have anything that can be done, at which point + * we set main_running to 0 and exit. + * + * Interrupts are enabled before doing various other internal + * instructions, after we've decided that we need to run through + * the loop again. + * + * this should prevent any race conditions. + * + * ++roman: Just disabling the NCR interrupt isn't sufficient here, + * because also a timer int can trigger an abort or reset, which can + * alter queues and touch the Falcon lock. + */ + + /* Tell int handlers main() is now already executing. Note that + no races are possible here. If an int comes in before + 'main_running' is set here, and queues/executes main via the + task queue, it doesn't do any harm, just this instance of main + won't find any work left to do. */ + if (main_running) + return; + main_running = 1; + + save_flags(flags); + do { + cli(); /* Freeze request queues */ + done = 1; + + if (!hostdata->connected) { + MAIN_PRINTK( "scsi%d: not connected\n", HOSTNO ); + /* + * Search through the issue_queue for a command destined + * for a target that's not busy. + */ +#if (NDEBUG & NDEBUG_LISTS) + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; + tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp)) + ; + if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/ +#endif + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, + prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp) ) { + +#if (NDEBUG & NDEBUG_LISTS) + if (prev != tmp) + printk("MAIN tmp=%p target=%d busy=%d lun=%d\n", + tmp, tmp->target, hostdata->busy[tmp->target], + tmp->lun); +#endif + /* When we find one, remove it from the issue queue. */ + /* ++guenther: possible race with Falcon locking */ + if ( +#ifdef SUPPORT_TAGS + !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE) +#else + !(hostdata->busy[tmp->target] & (1 << tmp->lun)) +#endif + ) { + cli(); /* ++guenther: just to be sure, this must be atomic */ + if (prev) { + REMOVE(prev, NEXT(prev), tmp, NEXT(tmp)); + NEXT(prev) = NEXT(tmp); + } else { + REMOVE(-1, hostdata->issue_queue, tmp, NEXT(tmp)); + hostdata->issue_queue = NEXT(tmp); + } + NEXT(tmp) = NULL; + + /* reenable interrupts after finding one */ + restore_flags(flags); + + /* + * Attempt to establish an I_T_L nexus here. + * On success, instance->hostdata->connected is set. + * On failure, we must add the command back to the + * issue queue so we can keep trying. + */ + MAIN_PRINTK("scsi%d: main(): command for target %d " + "lun %d removed from issue_queue\n", + HOSTNO, tmp->target, tmp->lun); + /* + * REQUEST SENSE commands are issued without tagged + * queueing, even on SCSI-II devices because the + * contingent allegiance condition exists for the + * entire unit. + */ + /* ++roman: ...and the standard also requires that + * REQUEST SENSE command are untagged. + */ + +#ifdef SUPPORT_TAGS + cmd_get_tag( tmp, tmp->cmnd[0] != REQUEST_SENSE ); +#endif + if (!NCR5380_select(instance, tmp, + (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : + TAG_NEXT)) { + break; + } else { + cli(); + LIST(tmp, hostdata->issue_queue); + NEXT(tmp) = hostdata->issue_queue; + hostdata->issue_queue = tmp; +#ifdef SUPPORT_TAGS + cmd_free_tag( tmp ); +#endif + restore_flags(flags); + MAIN_PRINTK("scsi%d: main(): select() failed, " + "returned to issue_queue\n", HOSTNO); + if (hostdata->connected) + break; + } + } /* if target/lun/target queue is not busy */ + } /* for issue_queue */ + } /* if (!hostdata->connected) */ + if (hostdata->connected +#ifdef REAL_DMA + && !hostdata->dma_len +#endif + ) { + restore_flags(flags); + MAIN_PRINTK("scsi%d: main: performing information transfer\n", + HOSTNO); + NCR5380_information_transfer(instance); + MAIN_PRINTK("scsi%d: main: done set false\n", HOSTNO); + done = 0; + } + } while (!done); + + /* Better allow ints _after_ 'main_running' has been cleared, else + an interrupt could believe we'll pick up the work it left for + us, but we won't see it anymore here... */ + main_running = 0; + restore_flags(flags); +} + + +#ifdef REAL_DMA +/* + * Function : void NCR5380_dma_complete (struct Scsi_Host *instance) + * + * Purpose : Called by interrupt handler when DMA finishes or a phase + * mismatch occurs (which would finish the DMA transfer). + * + * Inputs : instance - this instance of the NCR5380. + * + */ + +static void NCR5380_dma_complete( struct Scsi_Host *instance ) +{ + SETUP_HOSTDATA(instance); + int transfered; + unsigned char **data; + volatile int *count; + + if (!hostdata->connected) { + printk(KERN_WARNING "scsi%d: received end of DMA interrupt with " + "no connected cmd\n", HOSTNO); + return; + } + + DMA_PRINTK("scsi%d: real DMA transfer complete, basr 0x%X, sr 0x%X\n", + HOSTNO, NCR5380_read(BUS_AND_STATUS_REG), + NCR5380_read(STATUS_REG)); + + if((sun3scsi_dma_finish())) { + printk("scsi%d: overrun in UDC counter -- not prepared to deal with this!\n", HOSTNO); + printk("please e-mail sammy@oh.verio.com with a description of how this\n"); + printk("error was produced.\n"); + machine_halt(); + } + + /* make sure we're not stuck in a data phase */ + if((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH | + BASR_ACK)) == + (BASR_PHASE_MATCH | BASR_ACK)) { + printk("scsi%d: BASR %02x\n", HOSTNO, NCR5380_read(BUS_AND_STATUS_REG)); + printk("scsi%d: bus stuck in data phase -- probably a + single byte overrun!\n", HOSTNO); + printk("not prepared for this error!\n"); + printk("please e-mail sammy@oh.verio.com with a description of how this\n"); + printk("error was produced.\n"); + machine_halt(); + } + + + + (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); + NCR5380_write(MODE_REG, MR_BASE); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + + transfered = hostdata->dma_len - NCR5380_dma_residual(instance); + hostdata->dma_len = 0; + + data = (unsigned char **) &(hostdata->connected->SCp.ptr); + count = &(hostdata->connected->SCp.this_residual); + *data += transfered; + *count -= transfered; + +} +#endif /* REAL_DMA */ + + +/* + * Function : void NCR5380_intr (int irq) + * + * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses + * from the disconnected queue, and restarting NCR5380_main() + * as required. + * + * Inputs : int irq, irq that caused this interrupt. + * + */ + +static void NCR5380_intr (int irq, void *dev_id, struct pt_regs *regs) +{ + struct Scsi_Host *instance = first_instance; + int done = 1; + unsigned char basr; + + INT_PRINTK("scsi%d: NCR5380 irq triggered\n", HOSTNO); + + /* Look for pending interrupts */ + basr = NCR5380_read(BUS_AND_STATUS_REG); + INT_PRINTK("scsi%d: BASR=%02x\n", HOSTNO, basr); + /* dispatch to appropriate routine if found and done=0 */ + if (basr & BASR_IRQ) { + NCR_PRINT(NDEBUG_INTR); + if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) { + done = 0; + ENABLE_IRQ(); + INT_PRINTK("scsi%d: SEL interrupt\n", HOSTNO); + NCR5380_reselect(instance); + (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); + } + else if (basr & BASR_PARITY_ERROR) { + INT_PRINTK("scsi%d: PARITY interrupt\n", HOSTNO); + (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); + } + else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) { + INT_PRINTK("scsi%d: RESET interrupt\n", HOSTNO); + (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG); + } + else { + /* + * The rest of the interrupt conditions can occur only during a + * DMA transfer + */ + +#if defined(REAL_DMA) + /* + * We should only get PHASE MISMATCH and EOP interrupts if we have + * DMA enabled, so do a sanity check based on the current setting + * of the MODE register. + */ + + if ((NCR5380_read(MODE_REG) & MR_DMA_MODE) && + ((basr & BASR_END_DMA_TRANSFER) || + !(basr & BASR_PHASE_MATCH))) { + + INT_PRINTK("scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO); + NCR5380_dma_complete( instance ); + done = 0; + ENABLE_IRQ(); + } else +#endif /* REAL_DMA */ + { +/* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */ + if (basr & BASR_PHASE_MATCH) + printk(KERN_NOTICE "scsi%d: unknown interrupt, " + "BASR 0x%x, MR 0x%x, SR 0x%x\n", + HOSTNO, basr, NCR5380_read(MODE_REG), + NCR5380_read(STATUS_REG)); + (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); + } + } /* if !(SELECTION || PARITY) */ + } /* BASR & IRQ */ + else { + + printk(KERN_NOTICE "scsi%d: interrupt without IRQ bit set in BASR, " + "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr, + NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG)); + (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); + } + + if (!done) { + INT_PRINTK("scsi%d: in int routine, calling main\n", HOSTNO); + /* Put a call to NCR5380_main() on the queue... */ + queue_main(); + } +} + +#ifdef NCR5380_STATS +static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd* cmd) +{ +# ifdef NCR5380_STAT_LIMIT + if (cmd->request_bufflen > NCR5380_STAT_LIMIT) +# endif + switch (cmd->cmnd[0]) + { + case WRITE: + case WRITE_6: + case WRITE_10: + hostdata->time_write[cmd->target] += (jiffies - hostdata->timebase); + /*hostdata->bytes_write[cmd->target] += cmd->request_bufflen;*/ + hostdata->pendingw--; + break; + case READ: + case READ_6: + case READ_10: + hostdata->time_read[cmd->target] += (jiffies - hostdata->timebase); + /*hostdata->bytes_read[cmd->target] += cmd->request_bufflen;*/ + hostdata->pendingr--; + break; + } +} +#endif + +/* + * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, + * int tag); + * + * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command, + * including ARBITRATION, SELECTION, and initial message out for + * IDENTIFY and queue messages. + * + * Inputs : instance - instantiation of the 5380 driver on which this + * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for + * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for + * the command that is presently connected. + * + * Returns : -1 if selection could not execute for some reason, + * 0 if selection succeeded or failed because the target + * did not respond. + * + * Side effects : + * If bus busy, arbitration failed, etc, NCR5380_select() will exit + * with registers as they should have been on entry - ie + * SELECT_ENABLE will be set appropriately, the NCR5380 + * will cease to drive any SCSI bus signals. + * + * If successful : I_T_L or I_T_L_Q nexus will be established, + * instance->connected will be set to cmd. + * SELECT interrupt will be disabled. + * + * If failed (no target) : cmd->scsi_done() will be called, and the + * cmd->result host byte set to DID_BAD_TARGET. + */ + +static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag) +{ + SETUP_HOSTDATA(instance); + unsigned char tmp[3], phase; + unsigned char *data; + int len; + unsigned long timeout; + unsigned long flags; + + hostdata->restart_select = 0; + NCR_PRINT(NDEBUG_ARBITRATION); + ARB_PRINTK("scsi%d: starting arbitration, id = %d\n", HOSTNO, + instance->this_id); + + /* + * Set the phase bits to 0, otherwise the NCR5380 won't drive the + * data bus during SELECTION. + */ + + save_flags(flags); + cli(); + if (hostdata->connected) { + restore_flags(flags); + return -1; + } + NCR5380_write(TARGET_COMMAND_REG, 0); + + + /* + * Start arbitration. + */ + + NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask); + NCR5380_write(MODE_REG, MR_ARBITRATE); + + restore_flags(flags); + + /* Wait for arbitration logic to complete */ +#if NCR_TIMEOUT + { + unsigned long timeout = jiffies + 2*NCR_TIMEOUT; + + while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS) + && time_before(jiffies, timeout) && !hostdata->connected) + ; + if (time_after_eq(jiffies, timeout)) + { + printk("scsi : arbitration timeout at %d\n", __LINE__); + NCR5380_write(MODE_REG, MR_BASE); + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + return -1; + } + } +#else /* NCR_TIMEOUT */ + while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS) + && !hostdata->connected); +#endif + + ARB_PRINTK("scsi%d: arbitration complete\n", HOSTNO); + + if (hostdata->connected) { + NCR5380_write(MODE_REG, MR_BASE); + return -1; + } + /* + * The arbitration delay is 2.2us, but this is a minimum and there is + * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate + * the integral nature of udelay(). + * + */ + + udelay(3); + + /* Check for lost arbitration */ + if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || + (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) || + (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || + hostdata->connected) { + NCR5380_write(MODE_REG, MR_BASE); + ARB_PRINTK("scsi%d: lost arbitration, deasserting MR_ARBITRATE\n", + HOSTNO); + return -1; + } + + /* after/during arbitration, BSY should be asserted. + IBM DPES-31080 Version S31Q works now */ + /* Tnx to Thomas_Roesch@m2.maus.de for finding this! (Roman) */ + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL | + ICR_ASSERT_BSY ) ; + + if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || + hostdata->connected) { + NCR5380_write(MODE_REG, MR_BASE); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + ARB_PRINTK("scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n", + HOSTNO); + return -1; + } + + /* + * Again, bus clear + bus settle time is 1.2us, however, this is + * a minimum so we'll udelay ceil(1.2) + */ + +#ifdef CONFIG_ATARI_SCSI_TOSHIBA_DELAY + /* ++roman: But some targets (see above :-) seem to need a bit more... */ + udelay(15); +#else + udelay(2); +#endif + + if (hostdata->connected) { + NCR5380_write(MODE_REG, MR_BASE); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + return -1; + } + + ARB_PRINTK("scsi%d: won arbitration\n", HOSTNO); + + /* + * Now that we have won arbitration, start Selection process, asserting + * the host and target ID's on the SCSI bus. + */ + + NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->target))); + + /* + * Raise ATN while SEL is true before BSY goes false from arbitration, + * since this is the only way to guarantee that we'll get a MESSAGE OUT + * phase immediately after selection. + */ + + NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY | + ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL )); + NCR5380_write(MODE_REG, MR_BASE); + + /* + * Reselect interrupts must be turned off prior to the dropping of BSY, + * otherwise we will trigger an interrupt. + */ + + if (hostdata->connected) { + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + return -1; + } + + NCR5380_write(SELECT_ENABLE_REG, 0); + + /* + * The initiator shall then wait at least two deskew delays and release + * the BSY signal. + */ + udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */ + + /* Reset BSY */ + NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA | + ICR_ASSERT_ATN | ICR_ASSERT_SEL)); + + /* + * Something weird happens when we cease to drive BSY - looks + * like the board/chip is letting us do another read before the + * appropriate propagation delay has expired, and we're confusing + * a BSY signal from ourselves as the target's response to SELECTION. + * + * A small delay (the 'C++' frontend breaks the pipeline with an + * unnecessary jump, making it work on my 386-33/Trantor T128, the + * tighter 'C' code breaks and requires this) solves the problem - + * the 1 us delay is arbitrary, and only used because this delay will + * be the same on other platforms and since it works here, it should + * work there. + * + * wingel suggests that this could be due to failing to wait + * one deskew delay. + */ + + udelay(1); + + SEL_PRINTK("scsi%d: selecting target %d\n", HOSTNO, cmd->target); + + /* + * The SCSI specification calls for a 250 ms timeout for the actual + * selection. + */ + + timeout = jiffies + 25; + + /* + * XXX very interesting - we're seeing a bounce where the BSY we + * asserted is being reflected / still asserted (propagation delay?) + * and it's detecting as true. Sigh. + */ + +#if 0 + /* ++roman: If a target conformed to the SCSI standard, it wouldn't assert + * IO while SEL is true. But again, there are some disks out the in the + * world that do that nevertheless. (Somebody claimed that this announces + * reselection capability of the target.) So we better skip that test and + * only wait for BSY... (Famous german words: Der Klügere gibt nach :-) + */ + + while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & + (SR_BSY | SR_IO))); + + if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == + (SR_SEL | SR_IO)) { + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + NCR5380_reselect(instance); + printk (KERN_ERR "scsi%d: reselection after won arbitration?\n", + HOSTNO); + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + return -1; + } +#else + while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY)); +#endif + + /* + * No less than two deskew delays after the initiator detects the + * BSY signal is true, it shall release the SEL signal and may + * change the DATA BUS. -wingel + */ + + udelay(1); + + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); + + if (!(NCR5380_read(STATUS_REG) & SR_BSY)) { + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + if (hostdata->targets_present & (1 << cmd->target)) { + printk(KERN_ERR "scsi%d: weirdness\n", HOSTNO); + if (hostdata->restart_select) + printk(KERN_NOTICE "\trestart select\n"); + NCR_PRINT(NDEBUG_ANY); + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + return -1; + } + cmd->result = DID_BAD_TARGET << 16; +#ifdef NCR5380_STATS + collect_stats(hostdata, cmd); +#endif +#ifdef SUPPORT_TAGS + cmd_free_tag( cmd ); +#endif + cmd->scsi_done(cmd); + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + SEL_PRINTK("scsi%d: target did not respond within 250ms\n", HOSTNO); + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + return 0; + } + + hostdata->targets_present |= (1 << cmd->target); + + /* + * Since we followed the SCSI spec, and raised ATN while SEL + * was true but before BSY was false during selection, the information + * transfer phase should be a MESSAGE OUT phase so that we can send the + * IDENTIFY message. + * + * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG + * message (2 bytes) with a tag ID that we increment with every command + * until it wraps back to 0. + * + * XXX - it turns out that there are some broken SCSI-II devices, + * which claim to support tagged queuing but fail when more than + * some number of commands are issued at once. + */ + + /* Wait for start of REQ/ACK handshake */ + while (!(NCR5380_read(STATUS_REG) & SR_REQ)); + + SEL_PRINTK("scsi%d: target %d selected, going into MESSAGE OUT phase.\n", + HOSTNO, cmd->target); + tmp[0] = IDENTIFY(1, cmd->lun); + +#ifdef SUPPORT_TAGS + if (cmd->tag != TAG_NONE) { + tmp[1] = hostdata->last_message = SIMPLE_QUEUE_TAG; + tmp[2] = cmd->tag; + len = 3; + } else + len = 1; +#else + len = 1; + cmd->tag=0; +#endif /* SUPPORT_TAGS */ + + /* Send message(s) */ + data = tmp; + phase = PHASE_MSGOUT; + NCR5380_transfer_pio(instance, &phase, &len, &data); + SEL_PRINTK("scsi%d: nexus established.\n", HOSTNO); + /* XXX need to handle errors here */ + hostdata->connected = cmd; +#ifndef SUPPORT_TAGS + hostdata->busy[cmd->target] |= (1 << cmd->lun); +#endif + + initialize_SCp(cmd); + + + return 0; +} + +/* + * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance, + * unsigned char *phase, int *count, unsigned char **data) + * + * Purpose : transfers data in given phase using polled I/O + * + * Inputs : instance - instance of driver, *phase - pointer to + * what phase is expected, *count - pointer to number of + * bytes to transfer, **data - pointer to data pointer. + * + * Returns : -1 when different phase is entered without transferring + * maximum number of bytes, 0 if all bytes are transfered or exit + * is in same phase. + * + * Also, *phase, *count, *data are modified in place. + * + * XXX Note : handling for bus free may be useful. + */ + +/* + * Note : this code is not as quick as it could be, however it + * IS 100% reliable, and for the actual data transfer where speed + * counts, we will always do a pseudo DMA or DMA transfer. + */ + +static int NCR5380_transfer_pio( struct Scsi_Host *instance, + unsigned char *phase, int *count, + unsigned char **data) +{ + register unsigned char p = *phase, tmp; + register int c = *count; + register unsigned char *d = *data; + + /* + * The NCR5380 chip will only drive the SCSI bus when the + * phase specified in the appropriate bits of the TARGET COMMAND + * REGISTER match the STATUS REGISTER + */ + + NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p)); + + do { + /* + * Wait for assertion of REQ, after which the phase bits will be + * valid + */ + while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ)); + + HSH_PRINTK("scsi%d: REQ detected\n", HOSTNO); + + /* Check for phase mismatch */ + if ((tmp & PHASE_MASK) != p) { + PIO_PRINTK("scsi%d: phase mismatch\n", HOSTNO); + NCR_PRINT_PHASE(NDEBUG_PIO); + break; + } + + /* Do actual transfer from SCSI bus to / from memory */ + if (!(p & SR_IO)) + NCR5380_write(OUTPUT_DATA_REG, *d); + else + *d = NCR5380_read(CURRENT_SCSI_DATA_REG); + + ++d; + + /* + * The SCSI standard suggests that in MSGOUT phase, the initiator + * should drop ATN on the last byte of the message phase + * after REQ has been asserted for the handshake but before + * the initiator raises ACK. + */ + + if (!(p & SR_IO)) { + if (!((p & SR_MSG) && c > 1)) { + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | + ICR_ASSERT_DATA); + NCR_PRINT(NDEBUG_PIO); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | + ICR_ASSERT_DATA | ICR_ASSERT_ACK); + } else { + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | + ICR_ASSERT_DATA | ICR_ASSERT_ATN); + NCR_PRINT(NDEBUG_PIO); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | + ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK); + } + } else { + NCR_PRINT(NDEBUG_PIO); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK); + } + + while (NCR5380_read(STATUS_REG) & SR_REQ); + + HSH_PRINTK("scsi%d: req false, handshake complete\n", HOSTNO); + +/* + * We have several special cases to consider during REQ/ACK handshaking : + * 1. We were in MSGOUT phase, and we are on the last byte of the + * message. ATN must be dropped as ACK is dropped. + * + * 2. We are in a MSGIN phase, and we are on the last byte of the + * message. We must exit with ACK asserted, so that the calling + * code may raise ATN before dropping ACK to reject the message. + * + * 3. ACK and ATN are clear and the target may proceed as normal. + */ + if (!(p == PHASE_MSGIN && c == 1)) { + if (p == PHASE_MSGOUT && c > 1) + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); + else + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + } + } while (--c); + + PIO_PRINTK("scsi%d: residual %d\n", HOSTNO, c); + + *count = c; + *data = d; + tmp = NCR5380_read(STATUS_REG); + /* The phase read from the bus is valid if either REQ is (already) + * asserted or if ACK hasn't been released yet. The latter is the case if + * we're in MSGIN and all wanted bytes have been received. */ + if ((tmp & SR_REQ) || (p == PHASE_MSGIN && c == 0)) + *phase = tmp & PHASE_MASK; + else + *phase = PHASE_UNKNOWN; + + if (!c || (*phase == p)) + return 0; + else + return -1; +} + +/* + * Function : do_abort (Scsi_Host *host) + * + * Purpose : abort the currently established nexus. Should only be + * called from a routine which can drop into a + * + * Returns : 0 on success, -1 on failure. + */ + +static int do_abort (struct Scsi_Host *host) +{ + unsigned char tmp, *msgptr, phase; + int len; + + /* Request message out phase */ + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); + + /* + * Wait for the target to indicate a valid phase by asserting + * REQ. Once this happens, we'll have either a MSGOUT phase + * and can immediately send the ABORT message, or we'll have some + * other phase and will have to source/sink data. + * + * We really don't care what value was on the bus or what value + * the target sees, so we just handshake. + */ + + while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ); + + NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp)); + + if ((tmp & PHASE_MASK) != PHASE_MSGOUT) { + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | + ICR_ASSERT_ACK); + while (NCR5380_read(STATUS_REG) & SR_REQ); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); + } + + tmp = ABORT; + msgptr = &tmp; + len = 1; + phase = PHASE_MSGOUT; + NCR5380_transfer_pio (host, &phase, &len, &msgptr); + + /* + * If we got here, and the command completed successfully, + * we're about to go into bus free state. + */ + + return len ? -1 : 0; +} + +#if defined(REAL_DMA) +/* + * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance, + * unsigned char *phase, int *count, unsigned char **data) + * + * Purpose : transfers data in given phase using either real + * or pseudo DMA. + * + * Inputs : instance - instance of driver, *phase - pointer to + * what phase is expected, *count - pointer to number of + * bytes to transfer, **data - pointer to data pointer. + * + * Returns : -1 when different phase is entered without transferring + * maximum number of bytes, 0 if all bytes or transfered or exit + * is in same phase. + * + * Also, *phase, *count, *data are modified in place. + * + */ + + +static int NCR5380_transfer_dma( struct Scsi_Host *instance, + unsigned char *phase, int *count, + unsigned char **data) +{ + SETUP_HOSTDATA(instance); + register int c = *count; + register unsigned char p = *phase; + unsigned long flags; + + /* sanity check */ + if(!sun3_dma_setup_done) { + printk("scsi%d: transfer_dma without setup!\n", HOSTNO); + machine_halt(); + } + + hostdata->dma_len = c; + + DMA_PRINTK("scsi%d: initializing DMA for %s, %d bytes %s %p\n", + HOSTNO, (p & SR_IO) ? "reading" : "writing", + c, (p & SR_IO) ? "to" : "from", d); + + /* netbsd turns off ints here, why not be safe and do it too */ + save_flags(flags); + cli(); + + /* send start chain */ + sun3_udc_write(UDC_CHN_START, UDC_CSR); + + if (p & SR_IO) { + NCR5380_write(TARGET_COMMAND_REG, 1); + NCR5380_read(RESET_PARITY_INTERRUPT_REG); + NCR5380_write(INITIATOR_COMMAND_REG, 0); + NCR5380_write(MODE_REG, (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR)); + NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0); + } else { + NCR5380_write(TARGET_COMMAND_REG, 0); + NCR5380_read(RESET_PARITY_INTERRUPT_REG); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_DATA); + NCR5380_write(MODE_REG, (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR)); + NCR5380_write(START_DMA_SEND_REG, 0); + } + + restore_flags(flags); + + sun3_dma_active = 1; + return 0; +} +#endif /* defined(REAL_DMA) */ + +/* + * Function : NCR5380_information_transfer (struct Scsi_Host *instance) + * + * Purpose : run through the various SCSI phases and do as the target + * directs us to. Operates on the currently connected command, + * instance->connected. + * + * Inputs : instance, instance for which we are doing commands + * + * Side effects : SCSI things happen, the disconnected queue will be + * modified if a command disconnects, *instance->connected will + * change. + * + * XXX Note : we need to watch for bus free or a reset condition here + * to recover from an unexpected bus free condition. + */ + +static void NCR5380_information_transfer (struct Scsi_Host *instance) +{ + SETUP_HOSTDATA(instance); + unsigned long flags; + unsigned char msgout = NOP; + int sink = 0; + int len; +#if defined(REAL_DMA) + int transfersize; +#endif + unsigned char *data; + unsigned char phase, tmp, extended_msg[10], old_phase=0xff; + Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected; + + while (1) { + tmp = NCR5380_read(STATUS_REG); + /* We only have a valid SCSI phase when REQ is asserted */ + if (tmp & SR_REQ) { + phase = (tmp & PHASE_MASK); + if (phase != old_phase) { + old_phase = phase; + NCR_PRINT_PHASE(NDEBUG_INFORMATION); + } + + if(phase == PHASE_CMDOUT) { + void *d; + unsigned long count; + + if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) { + count = cmd->SCp.buffer->length; + d = cmd->SCp.buffer->address; + } else { + count = cmd->SCp.this_residual; + d = cmd->SCp.ptr; + } +#ifdef REAL_DMA + /* this command setup for dma yet? */ + if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done + != cmd)) + { + sun3scsi_dma_setup(d, count, + cmd->request.cmd); + sun3_dma_setup_done = cmd; + } +#endif + } + + + if (sink && (phase != PHASE_MSGOUT)) { + NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp)); + + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | + ICR_ASSERT_ACK); + while (NCR5380_read(STATUS_REG) & SR_REQ); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | + ICR_ASSERT_ATN); + sink = 0; + continue; + } + + switch (phase) { + case PHASE_DATAOUT: +#if (NDEBUG & NDEBUG_NO_DATAOUT) + printk("scsi%d: NDEBUG_NO_DATAOUT set, attempted DATAOUT " + "aborted\n", HOSTNO); + sink = 1; + do_abort(instance); + cmd->result = DID_ERROR << 16; + cmd->done(cmd); + return; +#endif + case PHASE_DATAIN: + /* + * If there is no room left in the current buffer in the + * scatter-gather list, move onto the next one. + */ + if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) { + ++cmd->SCp.buffer; + --cmd->SCp.buffers_residual; + cmd->SCp.this_residual = cmd->SCp.buffer->length; + cmd->SCp.ptr = cmd->SCp.buffer->address; + + /* ++roman: Try to merge some scatter-buffers if + * they are at contiguous physical addresses. + */ +// merge_contiguous_buffers( cmd ); + INF_PRINTK("scsi%d: %d bytes and %d buffers left\n", + HOSTNO, cmd->SCp.this_residual, + cmd->SCp.buffers_residual); + } + + /* + * The preferred transfer method is going to be + * PSEUDO-DMA for systems that are strictly PIO, + * since we can let the hardware do the handshaking. + * + * For this to work, we need to know the transfersize + * ahead of time, since the pseudo-DMA code will sit + * in an unconditional loop. + */ + +/* ++roman: I suggest, this should be + * #if def(REAL_DMA) + * instead of leaving REAL_DMA out. + */ + +#if defined(REAL_DMA) +// if (!cmd->device->borken && + if((transfersize = + NCR5380_dma_xfer_len(instance,cmd,phase)) > SUN3_DMA_MINSIZE) { + len = transfersize; + cmd->SCp.phase = phase; + + if (NCR5380_transfer_dma(instance, &phase, + &len, (unsigned char **) &cmd->SCp.ptr)) { + /* + * If the watchdog timer fires, all future + * accesses to this device will use the + * polled-IO. */ + printk(KERN_NOTICE "scsi%d: switching target %d " + "lun %d to slow handshake\n", HOSTNO, + cmd->target, cmd->lun); + cmd->device->borken = 1; + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | + ICR_ASSERT_ATN); + sink = 1; + do_abort(instance); + cmd->result = DID_ERROR << 16; + cmd->done(cmd); + /* XXX - need to source or sink data here, as appropriate */ + } else { +#ifdef REAL_DMA + /* ++roman: When using real DMA, + * information_transfer() should return after + * starting DMA since it has nothing more to + * do. + */ + return; +#else + cmd->SCp.this_residual -= transfersize - len; +#endif + } + } else +#endif /* defined(REAL_DMA) */ + NCR5380_transfer_pio(instance, &phase, + (int *) &cmd->SCp.this_residual, (unsigned char **) + &cmd->SCp.ptr); +#ifdef REAL_DMA + /* if we had intended to dma that command clear it */ + if(sun3_dma_setup_done == cmd) + sun3_dma_setup_done = NULL; +#endif + + break; + case PHASE_MSGIN: + len = 1; + data = &tmp; + NCR5380_write(SELECT_ENABLE_REG, 0); /* disable reselects */ + NCR5380_transfer_pio(instance, &phase, &len, &data); + cmd->SCp.Message = tmp; + + switch (tmp) { + /* + * Linking lets us reduce the time required to get the + * next command out to the device, hopefully this will + * mean we don't waste another revolution due to the delays + * required by ARBITRATION and another SELECTION. + * + * In the current implementation proposal, low level drivers + * merely have to start the next command, pointed to by + * next_link, done() is called as with unlinked commands. + */ +#ifdef LINKED + case LINKED_CMD_COMPLETE: + case LINKED_FLG_CMD_COMPLETE: + /* Accept message by clearing ACK */ + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + + LNK_PRINTK("scsi%d: target %d lun %d linked command " + "complete.\n", HOSTNO, cmd->target, cmd->lun); + + /* Enable reselect interrupts */ + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + /* + * Sanity check : A linked command should only terminate + * with one of these messages if there are more linked + * commands available. + */ + + if (!cmd->next_link) { + printk(KERN_NOTICE "scsi%d: target %d lun %d " + "linked command complete, no next_link\n", + HOSTNO, cmd->target, cmd->lun); + sink = 1; + do_abort (instance); + return; + } + + initialize_SCp(cmd->next_link); + /* The next command is still part of this process; copy it + * and don't free it! */ + cmd->next_link->tag = cmd->tag; + cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); + LNK_PRINTK("scsi%d: target %d lun %d linked request " + "done, calling scsi_done().\n", + HOSTNO, cmd->target, cmd->lun); +#ifdef NCR5380_STATS + collect_stats(hostdata, cmd); +#endif + cmd->scsi_done(cmd); + cmd = hostdata->connected; + break; +#endif /* def LINKED */ + case ABORT: + case COMMAND_COMPLETE: + /* Accept message by clearing ACK */ + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + hostdata->connected = NULL; + QU_PRINTK("scsi%d: command for target %d, lun %d " + "completed\n", HOSTNO, cmd->target, cmd->lun); +#ifdef SUPPORT_TAGS + cmd_free_tag( cmd ); + if (status_byte(cmd->SCp.Status) == QUEUE_FULL) { + /* Turn a QUEUE FULL status into BUSY, I think the + * mid level cannot handle QUEUE FULL :-( (The + * command is retried after BUSY). Also update our + * queue size to the number of currently issued + * commands now. + */ + /* ++Andreas: the mid level code knows about + QUEUE_FULL now. */ + TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun]; + TAG_PRINTK("scsi%d: target %d lun %d returned " + "QUEUE_FULL after %d commands\n", + HOSTNO, cmd->target, cmd->lun, + ta->nr_allocated); + if (ta->queue_size > ta->nr_allocated) + ta->nr_allocated = ta->queue_size; + } +#else + hostdata->busy[cmd->target] &= ~(1 << cmd->lun); +#endif + /* Enable reselect interrupts */ + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + + /* + * I'm not sure what the correct thing to do here is : + * + * If the command that just executed is NOT a request + * sense, the obvious thing to do is to set the result + * code to the values of the stored parameters. + * + * If it was a REQUEST SENSE command, we need some way to + * differentiate between the failure code of the original + * and the failure code of the REQUEST sense - the obvious + * case is success, where we fall through and leave the + * result code unchanged. + * + * The non-obvious place is where the REQUEST SENSE failed + */ + + if (cmd->cmnd[0] != REQUEST_SENSE) + cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); + else if (status_byte(cmd->SCp.Status) != GOOD) + cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + +#ifdef AUTOSENSE + if ((cmd->cmnd[0] != REQUEST_SENSE) && + (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) { + ASEN_PRINTK("scsi%d: performing request sense\n", + HOSTNO); + cmd->cmnd[0] = REQUEST_SENSE; + cmd->cmnd[1] &= 0xe0; + cmd->cmnd[2] = 0; + cmd->cmnd[3] = 0; + cmd->cmnd[4] = sizeof(cmd->sense_buffer); + cmd->cmnd[5] = 0; + cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); + + cmd->use_sg = 0; + /* this is initialized from initialize_SCp + cmd->SCp.buffer = NULL; + cmd->SCp.buffers_residual = 0; + */ + cmd->request_buffer = (char *) cmd->sense_buffer; + cmd->request_bufflen = sizeof(cmd->sense_buffer); + + save_flags(flags); + cli(); + LIST(cmd,hostdata->issue_queue); + NEXT(cmd) = hostdata->issue_queue; + hostdata->issue_queue = (Scsi_Cmnd *) cmd; + restore_flags(flags); + QU_PRINTK("scsi%d: REQUEST SENSE added to head of " + "issue queue\n", H_NO(cmd)); + } else +#endif /* def AUTOSENSE */ + { +#ifdef NCR5380_STATS + collect_stats(hostdata, cmd); +#endif + cmd->scsi_done(cmd); + } + + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + /* + * Restore phase bits to 0 so an interrupted selection, + * arbitration can resume. + */ + NCR5380_write(TARGET_COMMAND_REG, 0); + + while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected) + barrier(); + + return; + case MESSAGE_REJECT: + /* Accept message by clearing ACK */ + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + /* Enable reselect interrupts */ + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + switch (hostdata->last_message) { + case HEAD_OF_QUEUE_TAG: + case ORDERED_QUEUE_TAG: + case SIMPLE_QUEUE_TAG: + /* The target obviously doesn't support tagged + * queuing, even though it announced this ability in + * its INQUIRY data ?!? (maybe only this LUN?) Ok, + * clear 'tagged_supported' and lock the LUN, since + * the command is treated as untagged further on. + */ + cmd->device->tagged_supported = 0; + hostdata->busy[cmd->target] |= (1 << cmd->lun); + cmd->tag = TAG_NONE; + TAG_PRINTK("scsi%d: target %d lun %d rejected " + "QUEUE_TAG message; tagged queuing " + "disabled\n", + HOSTNO, cmd->target, cmd->lun); + break; + } + break; + case DISCONNECT: + /* Accept message by clearing ACK */ + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + save_flags(flags); + cli(); + cmd->device->disconnect = 1; + LIST(cmd,hostdata->disconnected_queue); + NEXT(cmd) = hostdata->disconnected_queue; + hostdata->connected = NULL; + hostdata->disconnected_queue = cmd; + restore_flags(flags); + QU_PRINTK("scsi%d: command for target %d lun %d was " + "moved from connected to the " + "disconnected_queue\n", HOSTNO, + cmd->target, cmd->lun); + /* + * Restore phase bits to 0 so an interrupted selection, + * arbitration can resume. + */ + NCR5380_write(TARGET_COMMAND_REG, 0); + + /* Enable reselect interrupts */ + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + /* Wait for bus free to avoid nasty timeouts */ + while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected) + barrier(); + return; + /* + * The SCSI data pointer is *IMPLICITLY* saved on a disconnect + * operation, in violation of the SCSI spec so we can safely + * ignore SAVE/RESTORE pointers calls. + * + * Unfortunately, some disks violate the SCSI spec and + * don't issue the required SAVE_POINTERS message before + * disconnecting, and we have to break spec to remain + * compatible. + */ + case SAVE_POINTERS: + case RESTORE_POINTERS: + /* Accept message by clearing ACK */ + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + /* Enable reselect interrupts */ + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + break; + case EXTENDED_MESSAGE: +/* + * Extended messages are sent in the following format : + * Byte + * 0 EXTENDED_MESSAGE == 1 + * 1 length (includes one byte for code, doesn't + * include first two bytes) + * 2 code + * 3..length+1 arguments + * + * Start the extended message buffer with the EXTENDED_MESSAGE + * byte, since print_msg() wants the whole thing. + */ + extended_msg[0] = EXTENDED_MESSAGE; + /* Accept first byte by clearing ACK */ + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + + EXT_PRINTK("scsi%d: receiving extended message\n", HOSTNO); + + len = 2; + data = extended_msg + 1; + phase = PHASE_MSGIN; + NCR5380_transfer_pio(instance, &phase, &len, &data); + EXT_PRINTK("scsi%d: length=%d, code=0x%02x\n", HOSTNO, + (int)extended_msg[1], (int)extended_msg[2]); + + if (!len && extended_msg[1] <= + (sizeof (extended_msg) - 1)) { + /* Accept third byte by clearing ACK */ + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + len = extended_msg[1] - 1; + data = extended_msg + 3; + phase = PHASE_MSGIN; + + NCR5380_transfer_pio(instance, &phase, &len, &data); + EXT_PRINTK("scsi%d: message received, residual %d\n", + HOSTNO, len); + + switch (extended_msg[2]) { + case EXTENDED_SDTR: + case EXTENDED_WDTR: + case EXTENDED_MODIFY_DATA_POINTER: + case EXTENDED_EXTENDED_IDENTIFY: + tmp = 0; + } + } else if (len) { + printk(KERN_NOTICE "scsi%d: error receiving " + "extended message\n", HOSTNO); + tmp = 0; + } else { + printk(KERN_NOTICE "scsi%d: extended message " + "code %02x length %d is too long\n", + HOSTNO, extended_msg[2], extended_msg[1]); + tmp = 0; + } + /* Fall through to reject message */ + + /* + * If we get something weird that we aren't expecting, + * reject it. + */ + default: + if (!tmp) { + printk(KERN_DEBUG "scsi%d: rejecting message ", HOSTNO); + print_msg (extended_msg); + printk("\n"); + } else if (tmp != EXTENDED_MESSAGE) + printk(KERN_DEBUG "scsi%d: rejecting unknown " + "message %02x from target %d, lun %d\n", + HOSTNO, tmp, cmd->target, cmd->lun); + else + printk(KERN_DEBUG "scsi%d: rejecting unknown " + "extended message " + "code %02x, length %d from target %d, lun %d\n", + HOSTNO, extended_msg[1], extended_msg[0], + cmd->target, cmd->lun); + + + msgout = MESSAGE_REJECT; + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | + ICR_ASSERT_ATN); + break; + } /* switch (tmp) */ + break; + case PHASE_MSGOUT: + len = 1; + data = &msgout; + hostdata->last_message = msgout; + NCR5380_transfer_pio(instance, &phase, &len, &data); + if (msgout == ABORT) { +#ifdef SUPPORT_TAGS + cmd_free_tag( cmd ); +#else + hostdata->busy[cmd->target] &= ~(1 << cmd->lun); +#endif + hostdata->connected = NULL; + cmd->result = DID_ERROR << 16; +#ifdef NCR5380_STATS + collect_stats(hostdata, cmd); +#endif + cmd->scsi_done(cmd); + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + return; + } + msgout = NOP; + break; + case PHASE_CMDOUT: + len = cmd->cmd_len; + data = cmd->cmnd; + /* + * XXX for performance reasons, on machines with a + * PSEUDO-DMA architecture we should probably + * use the dma transfer function. + */ + NCR5380_transfer_pio(instance, &phase, &len, + &data); + break; + case PHASE_STATIN: + len = 1; + data = &tmp; + NCR5380_transfer_pio(instance, &phase, &len, &data); + cmd->SCp.Status = tmp; + break; + default: + printk("scsi%d: unknown phase\n", HOSTNO); + NCR_PRINT(NDEBUG_ANY); + } /* switch(phase) */ + } /* if (tmp * SR_REQ) */ + } /* while (1) */ +} + +/* + * Function : void NCR5380_reselect (struct Scsi_Host *instance) + * + * Purpose : does reselection, initializing the instance->connected + * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q + * nexus has been reestablished, + * + * Inputs : instance - this instance of the NCR5380. + * + */ + +/* it might eventually prove necessary to do a dma setup on + reselection, but it doesn't seem to be needed now -- sam */ + +static void NCR5380_reselect (struct Scsi_Host *instance) +{ + SETUP_HOSTDATA(instance); + unsigned char target_mask; + unsigned char lun; +#ifdef SUPPORT_TAGS + unsigned char tag; +#endif + unsigned char msg[3]; + Scsi_Cmnd *tmp = NULL, *prev; +/* unsigned long flags; */ + + /* + * Disable arbitration, etc. since the host adapter obviously + * lost, and tell an interrupted NCR5380_select() to restart. + */ + + NCR5380_write(MODE_REG, MR_BASE); + hostdata->restart_select = 1; + + target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask); + + RSL_PRINTK("scsi%d: reselect\n", HOSTNO); + + /* + * At this point, we have detected that our SCSI ID is on the bus, + * SEL is true and BSY was false for at least one bus settle delay + * (400 ns). + * + * We must assert BSY ourselves, until the target drops the SEL + * signal. + */ + + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY); + + while (NCR5380_read(STATUS_REG) & SR_SEL); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + + /* + * Wait for target to go into MSGIN. + */ + + while (!(NCR5380_read(STATUS_REG) & SR_REQ)); + +#if 1 + // acknowledge toggle to MSGIN + NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(PHASE_MSGIN)); + + // peek at the byte without really hitting the bus + msg[0] = NCR5380_read(CURRENT_SCSI_DATA_REG); +#endif + + if (!msg[0] & 0x80) { + printk(KERN_DEBUG "scsi%d: expecting IDENTIFY message, got ", HOSTNO); + print_msg(msg); + do_abort(instance); + return; + } + lun = (msg[0] & 0x07); + + /* + * Find the command corresponding to the I_T_L or I_T_L_Q nexus we + * just reestablished, and remove it from the disconnected queue. + */ + + for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; + tmp; prev = tmp, tmp = NEXT(tmp) ) { + if ((target_mask == (1 << tmp->target)) && (lun == tmp->lun) +#ifdef SUPPORT_TAGS + && (tag == tmp->tag) +#endif + ) { + if (prev) { + REMOVE(prev, NEXT(prev), tmp, NEXT(tmp)); + NEXT(prev) = NEXT(tmp); + } else { + REMOVE(-1, hostdata->disconnected_queue, tmp, NEXT(tmp)); + hostdata->disconnected_queue = NEXT(tmp); + } + NEXT(tmp) = NULL; + break; + } + } + + if (!tmp) { + printk(KERN_WARNING "scsi%d: warning: target bitmask %02x lun %d " +#ifdef SUPPORT_TAGS + "tag %d " +#endif + "not in disconnected_queue.\n", + HOSTNO, target_mask, lun +#ifdef SUPPORT_TAGS + , tag +#endif + ); + /* + * Since we have an established nexus that we can't do anything + * with, we must abort it. + */ + do_abort(instance); + return; + } +#if 1 + /* engage dma setup for the command we just saw */ + { + void *d; + unsigned long count; + + if (!tmp->SCp.this_residual && tmp->SCp.buffers_residual) { + count = tmp->SCp.buffer->length; + d = tmp->SCp.buffer->address; + } else { + count = tmp->SCp.this_residual; + d = tmp->SCp.ptr; + } +#ifdef REAL_DMA + /* setup this command for dma if not already */ + if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done + != tmp)) + { + sun3scsi_dma_setup(d, count, + tmp->request.cmd); + sun3_dma_setup_done = tmp; + } +#endif + } +#endif + + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK); + /* Accept message by clearing ACK */ + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + +#ifdef SUPPORT_TAGS + /* If the phase is still MSGIN, the target wants to send some more + * messages. In case it supports tagged queuing, this is probably a + * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus. + */ + tag = TAG_NONE; + if (phase == PHASE_MSGIN && setup_use_tagged_queuing) { + /* Accept previous IDENTIFY message by clearing ACK */ + NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE ); + len = 2; + data = msg+1; + if (!NCR5380_transfer_pio(instance, &phase, &len, &data) && + msg[1] == SIMPLE_QUEUE_TAG) + tag = msg[2]; + TAG_PRINTK("scsi%d: target mask %02x, lun %d sent tag %d at " + "reselection\n", HOSTNO, target_mask, lun, tag); + } +#endif + + hostdata->connected = tmp; + RSL_PRINTK("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n", + HOSTNO, tmp->target, tmp->lun, tmp->tag); +} + + +/* + * Function : int NCR5380_abort (Scsi_Cmnd *cmd) + * + * Purpose : abort a command + * + * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the + * host byte of the result field to, if zero DID_ABORTED is + * used. + * + * Returns : 0 - success, -1 on failure. + * + * XXX - there is no way to abort the command that is currently + * connected, you have to wait for it to complete. If this is + * a problem, we could implement longjmp() / setjmp(), setjmp() + * called where the loop started in NCR5380_main(). + */ + +#ifndef NCR5380_abort +static +#endif +int NCR5380_abort (Scsi_Cmnd *cmd) +{ + struct Scsi_Host *instance = cmd->host; + SETUP_HOSTDATA(instance); + Scsi_Cmnd *tmp, **prev; + unsigned long flags; + + printk(KERN_NOTICE "scsi%d: aborting command\n", HOSTNO); + print_Scsi_Cmnd (cmd); + + NCR5380_print_status (instance); + + save_flags(flags); + cli(); + + ABRT_PRINTK("scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO, + NCR5380_read(BUS_AND_STATUS_REG), + NCR5380_read(STATUS_REG)); + +#if 1 +/* + * Case 1 : If the command is the currently executing command, + * we'll set the aborted flag and return control so that + * information transfer routine can exit cleanly. + */ + + if (hostdata->connected == cmd) { + + ABRT_PRINTK("scsi%d: aborting connected command\n", HOSTNO); +/* + * We should perform BSY checking, and make sure we haven't slipped + * into BUS FREE. + */ + +/* NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); */ +/* + * Since we can't change phases until we've completed the current + * handshake, we have to source or sink a byte of data if the current + * phase is not MSGOUT. + */ + +/* + * Return control to the executing NCR drive so we can clear the + * aborted flag and get back into our main loop. + */ + + if (do_abort(instance) == 0) { + hostdata->aborted = 1; + hostdata->connected = NULL; + cmd->result = DID_ABORT << 16; +#ifdef SUPPORT_TAGS + cmd_free_tag( cmd ); +#else + hostdata->busy[cmd->target] &= ~(1 << cmd->lun); +#endif + restore_flags(flags); + cmd->scsi_done(cmd); + return SCSI_ABORT_SUCCESS; + } else { +/* restore_flags(flags); */ + printk("scsi%d: abort of connected command failed!\n", HOSTNO); + return SCSI_ABORT_ERROR; + } + } +#endif + +/* + * Case 2 : If the command hasn't been issued yet, we simply remove it + * from the issue queue. + */ + for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue), + tmp = (Scsi_Cmnd *) hostdata->issue_queue; + tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) ) + if (cmd == tmp) { + REMOVE(5, *prev, tmp, NEXT(tmp)); + (*prev) = NEXT(tmp); + NEXT(tmp) = NULL; + tmp->result = DID_ABORT << 16; + restore_flags(flags); + ABRT_PRINTK("scsi%d: abort removed command from issue queue.\n", + HOSTNO); + /* Tagged queuing note: no tag to free here, hasn't been assigned + * yet... */ + tmp->scsi_done(tmp); + return SCSI_ABORT_SUCCESS; + } + +/* + * Case 3 : If any commands are connected, we're going to fail the abort + * and let the high level SCSI driver retry at a later time or + * issue a reset. + * + * Timeouts, and therefore aborted commands, will be highly unlikely + * and handling them cleanly in this situation would make the common + * case of noresets less efficient, and would pollute our code. So, + * we fail. + */ + + if (hostdata->connected) { + restore_flags(flags); + ABRT_PRINTK("scsi%d: abort failed, command connected.\n", HOSTNO); + return SCSI_ABORT_SNOOZE; + } + +/* + * Case 4: If the command is currently disconnected from the bus, and + * there are no connected commands, we reconnect the I_T_L or + * I_T_L_Q nexus associated with it, go into message out, and send + * an abort message. + * + * This case is especially ugly. In order to reestablish the nexus, we + * need to call NCR5380_select(). The easiest way to implement this + * function was to abort if the bus was busy, and let the interrupt + * handler triggered on the SEL for reselect take care of lost arbitrations + * where necessary, meaning interrupts need to be enabled. + * + * When interrupts are enabled, the queues may change - so we + * can't remove it from the disconnected queue before selecting it + * because that could cause a failure in hashing the nexus if that + * device reselected. + * + * Since the queues may change, we can't use the pointers from when we + * first locate it. + * + * So, we must first locate the command, and if NCR5380_select() + * succeeds, then issue the abort, relocate the command and remove + * it from the disconnected queue. + */ + + for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; + tmp = NEXT(tmp)) + if (cmd == tmp) { + restore_flags(flags); + ABRT_PRINTK("scsi%d: aborting disconnected command.\n", HOSTNO); + + if (NCR5380_select (instance, cmd, (int) cmd->tag)) + return SCSI_ABORT_BUSY; + + ABRT_PRINTK("scsi%d: nexus reestablished.\n", HOSTNO); + + do_abort (instance); + + save_flags(flags); + cli(); + for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue), + tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; + tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) ) + if (cmd == tmp) { + REMOVE(5, *prev, tmp, NEXT(tmp)); + *prev = NEXT(tmp); + NEXT(tmp) = NULL; + tmp->result = DID_ABORT << 16; + /* We must unlock the tag/LUN immediately here, since the + * target goes to BUS FREE and doesn't send us another + * message (COMMAND_COMPLETE or the like) + */ +#ifdef SUPPORT_TAGS + cmd_free_tag( tmp ); +#else + hostdata->busy[cmd->target] &= ~(1 << cmd->lun); +#endif + restore_flags(flags); + tmp->scsi_done(tmp); + return SCSI_ABORT_SUCCESS; + } + } + +/* + * Case 5 : If we reached this point, the command was not found in any of + * the queues. + * + * We probably reached this point because of an unlikely race condition + * between the command completing successfully and the abortion code, + * so we won't panic, but we will notify the user in case something really + * broke. + */ + + restore_flags(flags); + printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully\n" + KERN_INFO " before abortion\n", HOSTNO); + + return SCSI_ABORT_NOT_RUNNING; +} + + +/* + * Function : int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int reset_flags) + * + * Purpose : reset the SCSI bus. + * + * Returns : SCSI_RESET_WAKEUP + * + */ + +int NCR5380_reset( Scsi_Cmnd *cmd, unsigned int reset_flags) +{ + SETUP_HOSTDATA(cmd->host); + int i; + unsigned long flags; +#if 1 + Scsi_Cmnd *connected, *disconnected_queue; +#endif + + + NCR5380_print_status (cmd->host); + + /* get in phase */ + NCR5380_write( TARGET_COMMAND_REG, + PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) )); + /* assert RST */ + NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST ); + udelay (40); + /* reset NCR registers */ + NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE ); + NCR5380_write( MODE_REG, MR_BASE ); + NCR5380_write( TARGET_COMMAND_REG, 0 ); + NCR5380_write( SELECT_ENABLE_REG, 0 ); + /* ++roman: reset interrupt condition! otherwise no interrupts don't get + * through anymore ... */ + (void)NCR5380_read( RESET_PARITY_INTERRUPT_REG ); + +#if 1 /* XXX Should now be done by midlevel code, but it's broken XXX */ + /* XXX see below XXX */ + + /* MSch: old-style reset: actually abort all command processing here */ + + /* After the reset, there are no more connected or disconnected commands + * and no busy units; to avoid problems with re-inserting the commands + * into the issue_queue (via scsi_done()), the aborted commands are + * remembered in local variables first. + */ + save_flags(flags); + cli(); + connected = (Scsi_Cmnd *)hostdata->connected; + hostdata->connected = NULL; + disconnected_queue = (Scsi_Cmnd *)hostdata->disconnected_queue; + hostdata->disconnected_queue = NULL; +#ifdef SUPPORT_TAGS + free_all_tags(); +#endif + for( i = 0; i < 8; ++i ) + hostdata->busy[i] = 0; +#ifdef REAL_DMA + hostdata->dma_len = 0; +#endif + restore_flags(flags); + + /* In order to tell the mid-level code which commands were aborted, + * set the command status to DID_RESET and call scsi_done() !!! + * This ultimately aborts processing of these commands in the mid-level. + */ + + if ((cmd = connected)) { + ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd)); + cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16); + cmd->scsi_done( cmd ); + } + + for (i = 0; (cmd = disconnected_queue); ++i) { + disconnected_queue = NEXT(cmd); + NEXT(cmd) = NULL; + cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16); + cmd->scsi_done( cmd ); + } + if (i > 0) + ABRT_PRINTK("scsi: reset aborted %d disconnected command(s)\n", i); + + + /* since all commands have been explicitly terminated, we need to tell + * the midlevel code that the reset was SUCCESSFUL, and there is no + * need to 'wake up' the commands by a request_sense + */ + return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET; +#else /* 1 */ + + /* MSch: new-style reset handling: let the mid-level do what it can */ + + /* ++guenther: MID-LEVEL IS STILL BROKEN. + * Mid-level is supposed to requeue all commands that were active on the + * various low-level queues. In fact it does this, but that's not enough + * because all these commands are subject to timeout. And if a timeout + * happens for any removed command, *_abort() is called but all queues + * are now empty. Abort then gives up the falcon lock, which is fatal, + * since the mid-level will queue more commands and must have the lock + * (it's all happening inside timer interrupt handler!!). + * Even worse, abort will return NOT_RUNNING for all those commands not + * on any queue, so they won't be retried ... + * + * Conclusion: either scsi.c disables timeout for all resetted commands + * immediately, or we loose! As of linux-2.0.20 it doesn't. + */ + + /* After the reset, there are no more connected or disconnected commands + * and no busy units; so clear the low-level status here to avoid + * conflicts when the mid-level code tries to wake up the affected + * commands! + */ + + if (hostdata->issue_queue) + ABRT_PRINTK("scsi%d: reset aborted issued command(s)\n", H_NO(cmd)); + if (hostdata->connected) + ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd)); + if (hostdata->disconnected_queue) + ABRT_PRINTK("scsi%d: reset aborted disconnected command(s)\n", H_NO(cmd)); + + save_flags(flags); + cli(); + hostdata->issue_queue = NULL; + hostdata->connected = NULL; + hostdata->disconnected_queue = NULL; +#ifdef SUPPORT_TAGS + free_all_tags(); +#endif + for( i = 0; i < 8; ++i ) + hostdata->busy[i] = 0; +#ifdef REAL_DMA + hostdata->dma_len = 0; +#endif + restore_flags(flags); + + /* we did no complete reset of all commands, so a wakeup is required */ + return SCSI_RESET_WAKEUP | SCSI_RESET_BUS_RESET; +#endif /* 1 */ +} + +/* Local Variables: */ +/* tab-width: 8 */ +/* End: */ diff --git a/drivers/scsi/sun3_scsi.c b/drivers/scsi/sun3_scsi.c index ba0be94dd..45f504fa3 100644 --- a/drivers/scsi/sun3_scsi.c +++ b/drivers/scsi/sun3_scsi.c @@ -1,6 +1,8 @@ /* * Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl) * + * Sun3 DMA routines added by Sam Creasey (sammy@oh.verio.com) + * * Adapted from mac_scsinew.c: */ /* @@ -44,13 +46,10 @@ */ /* - * $Log: mac_NCR5380.c,v $ + * $Log: sun3_NCR5380.c,v $ */ #define AUTOSENSE -#if 0 -#define PSEUDO_DMA -#endif #include <linux/types.h> #include <linux/stddef.h> @@ -68,6 +67,9 @@ #include <asm/system.h> #include <asm/sun3ints.h> +#include <asm/dvma.h> +/* dma on! */ +#define REAL_DMA #include "scsi.h" #include "hosts.h" @@ -75,17 +77,12 @@ #include "NCR5380.h" #include "constants.h" -#if 0 -#define NDEBUG (NDEBUG_INTR | NDEBUG_PSEUDO_DMA | NDEBUG_ARBITRATION | NDEBUG_SELECTION | NDEBUG_RESELECTION) -#define NCR_TIMEOUT 100 -#else -#define NDEBUG (NDEBUG_ABORT) -#endif - #define USE_WRAPPER #define RESET_BOOT #define DRIVER_SETUP +#define NDEBUG 0 + /* * BUG can be used to trigger a strange code-size related hang on 2.1 kernels */ @@ -94,14 +91,14 @@ #undef DRIVER_SETUP #endif -#define ENABLE_IRQ() sun3_enable_irq( IRQ_SUN3_SCSI ); -#define DISABLE_IRQ() sun3_enable_irq( IRQ_SUN3_SCSI ); +#undef SUPPORT_TAGS + +#define ENABLE_IRQ() enable_irq( IRQ_SUN3_SCSI ); -/* extern void via_scsi_clear(void); */ static void scsi_sun3_intr(int irq, void *dummy, struct pt_regs *fp); -static char sun3scsi_read(struct Scsi_Host *instance, int reg); -static void sun3scsi_write(struct Scsi_Host *instance, int reg, int value); +static inline unsigned char sun3scsi_read(int reg); +static inline void sun3scsi_write(int reg, int value); static int setup_can_queue = -1; static int setup_cmd_per_lun = -1; @@ -111,38 +108,65 @@ static int setup_use_tagged_queuing = -1; #endif static int setup_hostid = -1; -static int polled_scsi_on = 0; +static Scsi_Cmnd *sun3_dma_setup_done = NULL; #define AFTER_RESET_DELAY (HZ/2) -static volatile unsigned char *sun3_scsi_regp = IOBASE_SUN3_SCSI; -/* -static volatile unsigned char *sun3_scsi_drq = NULL; -static volatile unsigned char *sun3_scsi_nodrq = NULL; -*/ +/* ms to wait after hitting dma regs */ +#define SUN3_DMA_DELAY 5 + +/* dvma buffer to allocate -- 32k should hopefully be more than sufficient */ +#define SUN3_DVMA_BUFSIZE 0xe000 + +/* minimum number of bytes to to dma on */ +#define SUN3_DMA_MINSIZE 128 + +static struct proc_dir_entry proc_scsi_sun3_5380 = { + PROC_SCSI_MAC, 13, "Sun3 5380 SCSI", S_IFDIR | S_IRUGO, S_IXUGO, 2 +}; + +static volatile unsigned char *sun3_scsi_regp; +static volatile struct sun3_dma_regs *dregs; +static unsigned char *dmabuf = NULL; /* dma memory buffer */ +static struct sun3_udc_regs *udc_regs = NULL; +static void *sun3_dma_orig_addr = NULL; +static unsigned long sun3_dma_orig_count = 0; +static int sun3_dma_active = 0; /* - * Function : sun3_scsi_setup(char *str, int *ints) - * - * Purpose : booter command line initialization of the overrides array, - * - * Inputs : str - unused, ints - array of integer parameters with ints[0] - * equal to the number of ints. - * - * TODO: make it actually work! - * + * NCR 5380 register access functions */ -void sun3_scsi_setup(char *str, int *ints) { - printk("sun3_scsi_setup() called\n"); - setup_can_queue = -1; - setup_cmd_per_lun = -1; - setup_sg_tablesize = -1; - setup_hostid = -1; -#ifdef SUPPORT_TAGS - setup_use_tagged_queuing = -1; -#endif - printk("sun3_scsi_setup() done\n"); +static inline unsigned char sun3scsi_read(int reg) +{ + return( sun3_scsi_regp[reg] ); +} + +static inline void sun3scsi_write(int reg, int value) +{ + sun3_scsi_regp[reg] = value; +} + +/* dma controller register access functions */ + +static inline unsigned short sun3_udc_read(unsigned char reg) +{ + unsigned short ret; + + dregs->udc_addr = UDC_CSR; + udelay(SUN3_DMA_DELAY); + ret = dregs->udc_data; + udelay(SUN3_DMA_DELAY); + + return ret; +} + +static inline void sun3_udc_write(unsigned short val, unsigned char reg) +{ + dregs->udc_addr = reg; + udelay(SUN3_DMA_DELAY); + dregs->udc_data = val; + udelay(SUN3_DMA_DELAY); } /* @@ -165,7 +189,6 @@ static struct Scsi_Host *default_instance; int sun3scsi_detect(Scsi_Host_Template * tpnt) { unsigned long ioaddr, iopte; - unsigned short *ioptr; int count = 0; static int called = 0; struct Scsi_Host *instance; @@ -173,9 +196,7 @@ int sun3scsi_detect(Scsi_Host_Template * tpnt) if(called) return 0; -printk("sun3scsi_detect(0x%p)\n",tpnt); - - tpnt->proc_name = "Sun3 5380 SCSI"; /* Could you spell "ewww..."? */ + tpnt->proc_dir = &proc_scsi_sun3_5380; /* setup variables */ tpnt->can_queue = @@ -204,7 +225,6 @@ printk("sun3scsi_detect(0x%p)\n",tpnt); if(((iopte & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT) == IOBASE_SUN3_SCSI) { count = 1; -printk("Found ioaddr in pmeg\n"); break; } } @@ -214,22 +234,19 @@ printk("Found ioaddr in pmeg\n"); return 0; } - sun3_scsi_regp = ioaddr; - - /* doing some stuff like resetting DVMA: */ - ioptr = ioaddr; - *(ioptr+8) = 0; - udelay(10); - *(ioptr+9) = 0; - udelay(10); - *(ioptr+12) = 0; - udelay(10); - *(ioptr+12) = 0x7; - udelay(10); - printk("SCSI status reg = %x\n", *(ioptr+12)); - udelay(10); - *(ioptr+13) = 0; - udelay(10); + sun3_scsi_regp = (unsigned char *)ioaddr; + dregs = (struct sun3_dma_regs *)(((unsigned char *)ioaddr) + 8); + + if((dmabuf = sun3_dvma_malloc(SUN3_DVMA_BUFSIZE)) == NULL) { + printk("SUN3 Scsi couldn't allocate DVMA memory!\n"); + return 0; + } + + if((udc_regs = sun3_dvma_malloc(sizeof(struct sun3_udc_regs))) + == NULL) { + printk("SUN3 Scsi couldn't allocate DVMA memory!\n"); + return 0; + } #ifdef SUPPORT_TAGS if (setup_use_tagged_queuing < 0) @@ -239,18 +256,6 @@ printk("Found ioaddr in pmeg\n"); instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); default_instance = instance; -/* - if (macintosh_config->ident == MAC_MODEL_IIFX) { - mac_scsi_regp = via1_regp+0x8000; - mac_scsi_drq = via1_regp+0x6000; - mac_scsi_nodrq = via1_regp+0x12000; - } else { - mac_scsi_regp = via1_regp+0x10000; - mac_scsi_drq = via1_regp+0x6000; - mac_scsi_nodrq = via1_regp+0x12000; - } -*/ - instance->io_port = (unsigned long) ioaddr; instance->irq = IRQ_SUN3_SCSI; @@ -260,15 +265,20 @@ printk("Found ioaddr in pmeg\n"); ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0; - if (instance->irq != IRQ_NONE) - if (sun3_request_irq(instance->irq, sun3scsi_intr, - 0, "Sun3SCSI-5380", NULL)) { - printk("scsi%d: IRQ%d not free, interrupts disabled\n", - instance->host_no, instance->irq); - instance->irq = IRQ_NONE; - } - - printk("scsi%d: generic 5380 at port %lX irq", instance->host_no, instance->io_port); + if (request_irq(instance->irq, scsi_sun3_intr, + 0, "Sun3SCSI-5380", NULL)) { +#ifndef REAL_DMA + printk("scsi%d: IRQ%d not free, interrupts disabled\n", + instance->host_no, instance->irq); + instance->irq = IRQ_NONE; +#else + printk("scsi%d: IRQ%d not free, bailing out\n", + instance->host_no, instance->irq); + return 0; +#endif + } + + printk("scsi%d: Sun3 5380 at port %lX irq", instance->host_no, instance->io_port); if (instance->irq == IRQ_NONE) printk ("s disabled"); else @@ -280,6 +290,12 @@ printk("Found ioaddr in pmeg\n"); NCR5380_print_options(instance); printk("\n"); + dregs->csr = 0; + udelay(SUN3_DMA_DELAY); + dregs->csr = CSR_SCSI | CSR_FIFO | CSR_INTR; + udelay(SUN3_DMA_DELAY); + dregs->fifo_count = 0; + called = 1; return 1; } @@ -342,34 +358,34 @@ const char * sun3scsi_info (struct Scsi_Host *spnt) { return ""; } +// safe bits for the CSR +#define CSR_GOOD 0x060f -/* - * NCR 5380 register access functions - */ - -static char sun3scsi_read(struct Scsi_Host *instance, int reg) +static void scsi_sun3_intr(int irq, void *dummy, struct pt_regs *fp) { -/* -printk("sun3scsi_read(instance=0x%p, reg=0x%x): @0x%p= %d\n",instance,reg,sun3_scsi_regp,sun3_scsi_regp[reg]); -*/ - return( sun3_scsi_regp[reg] ); -} + unsigned short csr = dregs->csr; -static void sun3scsi_write(struct Scsi_Host *instance, int reg, int value) -{ -/* - printk("sun3scsi_write(instance=0x%p, reg=0x%x, value=0x%x)\n", instance, reg, value); -*/ - sun3_scsi_regp[reg] = value; -} + if(csr & ~CSR_GOOD) { + if(csr & CSR_DMA_BUSERR) { + printk("scsi%d: bus error in dma\n", default_instance->host_no); + } -#include "NCR5380.c" + if(csr & CSR_DMA_CONFLICT) { + printk("scsi%d: dma conflict\n", default_instance->host_no); + } + } + + if(csr & (CSR_SDB_INT | CSR_DMA_INT)) + NCR5380_intr(irq, dummy, fp); +} /* * Debug stuff - to be called on NMI, or sysrq key. Use at your own risk; * reentering NCR5380_print_status seems to have ugly side effects */ +/* this doesn't seem to get used at all -- sam */ +#if 0 void sun3_sun3_debug (void) { unsigned long flags; @@ -381,32 +397,141 @@ void sun3_sun3_debug (void) NCR5380_print_status(default_instance); restore_flags(flags); } -#if 0 - polled_scsi_on = 1; -#endif } -/* - * Helper function for interrupt trouble. More ugly side effects here. - */ +#endif + -void scsi_sun3_polled (void) +/* sun3scsi_dma_setup() -- initialize the dma controller for a read/write */ +static unsigned long sun3scsi_dma_setup(void *data, unsigned long count, int write_flag) { - unsigned long flags; - NCR5380_local_declare(); - struct Scsi_Host *instance; + if(write_flag) + memcpy(dmabuf, data, count); + else { + sun3_dma_orig_addr = data; + sun3_dma_orig_count = count; + } + + dregs->fifo_count = 0; + sun3_udc_write(UDC_RESET, UDC_CSR); - instance = default_instance; - NCR5380_setup(instance); - if(NCR5380_read(BUS_AND_STATUS_REG)&BASR_IRQ) - { - printk("SCSI poll\n"); - save_flags(flags); - cli(); - sun3scsi_intr(IRQ_SUN3_SCSI, instance, NULL); - restore_flags(flags); + /* reset fifo */ + dregs->csr &= ~CSR_FIFO; + dregs->csr |= CSR_FIFO; + + /* set direction */ + if(write_flag) + dregs->csr |= CSR_SEND; + else + dregs->csr &= ~CSR_SEND; + + /* byte count for fifo */ + dregs->fifo_count = count; + + sun3_udc_write(UDC_RESET, UDC_CSR); + + /* reset fifo */ + dregs->csr &= ~CSR_FIFO; + dregs->csr |= CSR_FIFO; + + + if(dregs->fifo_count != count) { + printk("scsi%d: fifo_mismatch %04x not %04x\n", + default_instance->host_no, dregs->fifo_count, + (unsigned int) count); + NCR5380_print(default_instance); + } + + /* setup udc */ + udc_regs->addr_hi = ((sun3_dvma_vtop(dmabuf) & 0xff0000) >> 8); + udc_regs->addr_lo = (sun3_dvma_vtop(dmabuf) & 0xffff); + udc_regs->count = count/2; /* count in words */ + udc_regs->mode_hi = UDC_MODE_HIWORD; + if(write_flag) { + if(count & 1) + udc_regs->count++; + udc_regs->mode_lo = UDC_MODE_LSEND; + udc_regs->rsel = UDC_RSEL_SEND; + } else { + udc_regs->mode_lo = UDC_MODE_LRECV; + udc_regs->rsel = UDC_RSEL_RECV; } + + /* announce location of regs block */ + sun3_udc_write(((sun3_dvma_vtop(udc_regs) & 0xff0000) >> 8), + UDC_CHN_HI); + + sun3_udc_write((sun3_dvma_vtop(udc_regs) & 0xffff), UDC_CHN_LO); + + /* set dma master on */ + sun3_udc_write(0xd, UDC_MODE); + + /* interrupt enable */ + sun3_udc_write(UDC_INT_ENABLE, UDC_CSR); + + return count; + +} + +static inline unsigned long sun3scsi_dma_residual(struct Scsi_Host *instance) +{ + unsigned short resid; + + dregs->udc_addr = 0x32; + udelay(SUN3_DMA_DELAY); + resid = dregs->udc_data; + udelay(SUN3_DMA_DELAY); + resid *= 2; + + return (unsigned long) resid; +} + +static inline unsigned long sun3scsi_dma_xfer_len(unsigned long wanted, Scsi_Cmnd *cmd, + int write_flag) +{ + return wanted; } +/* clean up after our dma is done */ +static int sun3scsi_dma_finish(void) +{ + unsigned short count; + int ret = 0; + + count = sun3scsi_dma_residual(default_instance); + + sun3_dma_active = 0; + + /* if we've finished a read, copy out the data we read */ + if(sun3_dma_orig_addr) { + /* check for residual bytes after dma end */ + if(count && (NCR5380_read(BUS_AND_STATUS_REG) & + (BASR_PHASE_MATCH | BASR_ACK))) { + printk("scsi%d: sun3_scsi_finish: read overrun baby... ", default_instance->host_no); + printk("basr now %02x\n", NCR5380_read(BUS_AND_STATUS_REG)); + ret = count; + } + + /* copy in what we dma'd no matter what */ + memcpy(sun3_dma_orig_addr, dmabuf, sun3_dma_orig_count); + sun3_dma_orig_addr = NULL; + + } + + sun3_udc_write(UDC_RESET, UDC_CSR); + + dregs->csr &= ~CSR_SEND; + + /* reset fifo */ + dregs->csr &= ~CSR_FIFO; + dregs->csr |= CSR_FIFO; + + sun3_dma_setup_done = NULL; + + return ret; + +} + +#include "sun3_NCR5380.c" #ifdef MODULE @@ -414,3 +539,4 @@ Scsi_Host_Template driver_template = SUN3_NCR5380; #include "scsi_module.c" #endif + diff --git a/drivers/scsi/sun3_scsi.h b/drivers/scsi/sun3_scsi.h index c677bfbf6..b94f586aa 100644 --- a/drivers/scsi/sun3_scsi.h +++ b/drivers/scsi/sun3_scsi.h @@ -1,6 +1,8 @@ /* * Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl) * + * Sun3 DMA additions by Sam Creasey (sammy@oh.verio.com) + * * Adapted from mac_scsinew.h: */ /* @@ -102,18 +104,272 @@ use_clustering: DISABLE_CLUSTERING \ #define NCR5380_setup(instance) \ _instance = instance -#define NCR5380_read(reg) sun3scsi_read(_instance, reg) -#define NCR5380_write(reg, value) sun3scsi_write(_instance, reg, value) +#define NCR5380_read(reg) sun3scsi_read(reg) +#define NCR5380_write(reg, value) sun3scsi_write(reg, value) #define NCR5380_intr sun3scsi_intr #define NCR5380_queue_command sun3scsi_queue_command -#define NCR5380_abort sun3scsi_abort #define NCR5380_reset sun3scsi_reset +#define NCR5380_abort sun3scsi_abort #define NCR5380_proc_info sun3scsi_proc_info +#define NCR5380_dma_xfer_len(i, cmd, phase) \ + sun3scsi_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1) + +#define NCR5380_dma_write_setup(instance, data, count) sun3scsi_dma_setup(data, count, 1) +#define NCR5380_dma_read_setup(instance, data, count) sun3scsi_dma_setup(data, count, 0) +#define NCR5380_dma_residual sun3scsi_dma_residual #define BOARD_NORMAL 0 #define BOARD_NCR53C400 1 +/* additional registers - mainly DMA control regs */ +/* these start at regbase + 8 -- directly after the NCR regs */ +struct sun3_dma_regs { + unsigned short vmeregs[4]; /* unimpl vme stuff */ + unsigned short udc_data; /* udc dma data reg */ + unsigned short udc_addr; /* uda dma addr reg */ + unsigned short fifo_data; /* fifo data reg, holds extra byte on + odd dma reads */ + unsigned short fifo_count; + unsigned short csr; /* control/status reg */ +}; + +/* ucd chip specific regs - live in dvma space */ +struct sun3_udc_regs { + unsigned short rsel; /* select regs to load */ + unsigned short addr_hi; /* high word of addr */ + unsigned short addr_lo; /* low word */ + unsigned short count; /* words to be xfer'd */ + unsigned short mode_hi; /* high word of channel mode */ + unsigned short mode_lo; /* low word of channel mode */ +}; + +/* addresses of the udc registers */ +#define UDC_MODE 0x38 +#define UDC_CSR 0x2e /* command/status */ +#define UDC_CHN_HI 0x26 /* chain high word */ +#define UDC_CHN_LO 0x22 /* chain lo word */ +#define UDC_CURA_HI 0x1a /* cur reg A high */ +#define UDC_CURA_LO 0x0a /* cur reg A low */ +#define UDC_CURB_HI 0x12 /* cur reg B high */ +#define UDC_CURB_LO 0x02 /* cur reg B low */ +#define UDC_MODE_HI 0x56 /* mode reg high */ +#define UDC_MODE_LO 0x52 /* mode reg low */ +#define UDC_COUNT 0x32 /* words to xfer */ + +/* some udc commands */ +#define UDC_RESET 0 +#define UDC_CHN_START 0xa0 /* start chain */ +#define UDC_INT_ENABLE 0x32 /* channel 1 int on */ + +/* udc mode words */ +#define UDC_MODE_HIWORD 0x40 +#define UDC_MODE_LSEND 0xc2 +#define UDC_MODE_LRECV 0xd2 + +/* udc reg selections */ +#define UDC_RSEL_SEND 0x282 +#define UDC_RSEL_RECV 0x182 + +/* bits in csr reg */ +#define CSR_DMA_ACTIVE 0x8000 +#define CSR_DMA_CONFLICT 0x4000 +#define CSR_DMA_BUSERR 0x2000 + +#define CSR_FIFO_EMPTY 0x400 /* fifo flushed? */ +#define CSR_SDB_INT 0x200 /* sbc interrupt pending */ +#define CSR_DMA_INT 0x100 /* dma interrupt pending */ + +#define CSR_SEND 0x8 /* 1 = send 0 = recv */ +#define CSR_FIFO 0x2 /* reset fifo */ +#define CSR_INTR 0x4 /* interrupt enable */ +#define CSR_SCSI 0x1 + +// debugging printk's, taken from atari_scsi.h +/* Debugging printk definitions: + * + * ARB -> arbitration + * ASEN -> auto-sense + * DMA -> DMA + * HSH -> PIO handshake + * INF -> information transfer + * INI -> initialization + * INT -> interrupt + * LNK -> linked commands + * MAIN -> NCR5380_main() control flow + * NDAT -> no data-out phase + * NWR -> no write commands + * PIO -> PIO transfers + * PDMA -> pseudo DMA (unused on Atari) + * QU -> queues + * RSL -> reselections + * SEL -> selections + * USL -> usleep cpde (unused on Atari) + * LBS -> last byte sent (unused on Atari) + * RSS -> restarting of selections + * EXT -> extended messages + * ABRT -> aborting and resetting + * TAG -> queue tag handling + * MER -> merging of consec. buffers + * + */ + + + +#if NDEBUG & NDEBUG_ARBITRATION +#define ARB_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define ARB_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_AUTOSENSE +#define ASEN_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define ASEN_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_DMA +#define DMA_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define DMA_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_HANDSHAKE +#define HSH_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define HSH_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_INFORMATION +#define INF_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define INF_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_INIT +#define INI_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define INI_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_INTR +#define INT_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define INT_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_LINKED +#define LNK_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define LNK_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_MAIN +#define MAIN_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define MAIN_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_NO_DATAOUT +#define NDAT_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define NDAT_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_NO_WRITE +#define NWR_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define NWR_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_PIO +#define PIO_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define PIO_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_PSEUDO_DMA +#define PDMA_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define PDMA_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_QUEUES +#define QU_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define QU_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_RESELECTION +#define RSL_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define RSL_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_SELECTION +#define SEL_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define SEL_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_USLEEP +#define USL_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define USL_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_LAST_BYTE_SENT +#define LBS_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define LBS_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_RESTART_SELECT +#define RSS_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define RSS_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_EXTENDED +#define EXT_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define EXT_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_ABORT +#define ABRT_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define ABRT_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_TAGS +#define TAG_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define TAG_PRINTK(format, args...) +#endif +#if NDEBUG & NDEBUG_MERGING +#define MER_PRINTK(format, args...) \ + printk(KERN_DEBUG format , ## args) +#else +#define MER_PRINTK(format, args...) +#endif + +/* conditional macros for NCR5380_print_{,phase,status} */ + +#define NCR_PRINT(mask) \ + ((NDEBUG & (mask)) ? NCR5380_print(instance) : (void)0) + +#define NCR_PRINT_PHASE(mask) \ + ((NDEBUG & (mask)) ? NCR5380_print_phase(instance) : (void)0) + +#define NCR_PRINT_STATUS(mask) \ + ((NDEBUG & (mask)) ? NCR5380_print_status(instance) : (void)0) + +#define NDEBUG_ANY 0xffffffff + + + #endif /* ndef HOSTS_C */ #endif /* SUN3_NCR5380_H */ |