📄 wd7000.c
字号:
{ register unsigned long flags; save_flags (flags); cli (); memset (scb, 0, sizeof (Scb)); scb->next = scbfree; scbfree = scb; freescbs++; restore_flags (flags);}static inline void init_scbs (void){ int i; unsigned long flags; save_flags (flags); cli (); scbfree = &(scbs[0]); memset (scbs, 0, sizeof (scbs)); for (i = 0; i < MAX_SCBS - 1; i++) { scbs[i].next = &(scbs[i + 1]); scbs[i].SCpnt = NULL; } scbs[MAX_SCBS - 1].next = NULL; scbs[MAX_SCBS - 1].SCpnt = NULL; restore_flags (flags);}static int mail_out (Adapter *host, Scb *scbptr)/* * Note: this can also be used for ICBs; just cast to the parm type. */{ register int i, ogmb; register unsigned long flags; unchar start_ogmb; Mailbox *ogmbs = host->mb.ogmb; int *next_ogmb = &(host->next_ogmb);#ifdef WD7000_DEBUG printk ("wd7000_mail_out: 0x%06lx", (long) scbptr);#endif /* We first look for a free outgoing mailbox */ save_flags (flags); cli (); ogmb = *next_ogmb; for (i = 0; i < OGMB_CNT; i++) { if (ogmbs[ogmb].status == 0) {#ifdef WD7000_DEBUG printk (" using OGMB 0x%x", ogmb);#endif ogmbs[ogmb].status = 1; any2scsi ((unchar *) ogmbs[ogmb].scbptr, (int) scbptr); *next_ogmb = (ogmb + 1) % OGMB_CNT; break; } else ogmb = (++ogmb) % OGMB_CNT; } restore_flags (flags);#ifdef WD7000_DEBUG printk (", scb is 0x%06lx", (long) scbptr);#endif if (i >= OGMB_CNT) { /* * Alternatively, we might issue the "interrupt on free OGMB", * and sleep, but it must be ensured that it isn't the init * task running. Instead, this version assumes that the caller * will be persistent, and try again. Since it's the adapter * that marks OGMB's free, waiting even with interrupts off * should work, since they are freed very quickly in most cases. */#ifdef WD7000_DEBUG printk (", no free OGMBs.\n");#endif return (0); } wd7000_enable_intr (host); start_ogmb = START_OGMB | ogmb; command_out (host, &start_ogmb, 1);#ifdef WD7000_DEBUG printk (", awaiting interrupt.\n");#endif return (1);}int make_code (unsigned hosterr, unsigned scsierr){#ifdef WD7000_DEBUG int in_error = hosterr;#endif switch ((hosterr >> 8) & 0xff) { case 0: /* Reserved */ hosterr = DID_ERROR; break; case 1: /* Command Complete, no errors */ hosterr = DID_OK; break; case 2: /* Command complete, error logged in scb status (scsierr) */ hosterr = DID_OK; break; case 4: /* Command failed to complete - timeout */ hosterr = DID_TIME_OUT; break; case 5: /* Command terminated; Bus reset by external device */ hosterr = DID_RESET; break; case 6: /* Unexpected Command Received w/ host as target */ hosterr = DID_BAD_TARGET; break; case 80: /* Unexpected Reselection */ case 81: /* Unexpected Selection */ hosterr = DID_BAD_INTR; break; case 82: /* Abort Command Message */ hosterr = DID_ABORT; break; case 83: /* SCSI Bus Software Reset */ case 84: /* SCSI Bus Hardware Reset */ hosterr = DID_RESET; break; default: /* Reserved */ hosterr = DID_ERROR; }#ifdef WD7000_DEBUG if (scsierr || hosterr) printk ("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n", scsierr, in_error, hosterr);#endif return (scsierr | (hosterr << 16));}static void wd7000_scsi_done (Scsi_Cmnd *SCpnt){#ifdef WD7000_DEBUG printk ("wd7000_scsi_done: 0x%06lx\n", (long) SCpnt);#endif SCpnt->SCp.phase = 0;}#define wd7000_intr_ack(host) outb (0, host->iobase + ASC_INTR_ACK)void wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs){ register int flag, icmb, errstatus, icmb_status; register int host_error, scsi_error; register Scb *scb; /* for SCSI commands */ register IcbAny *icb; /* for host commands */ register Scsi_Cmnd *SCpnt; Adapter *host = (Adapter *) wd7000_host[irq - IRQ_MIN]->hostdata; /* This MUST be set!!! */ Mailbox *icmbs = host->mb.icmb; host->int_counter++;#ifdef WD7000_DEBUG printk ("wd7000_intr_handle: irq = %d, host = 0x%06lx\n", irq, (long) host);#endif flag = inb (host->iobase + ASC_INTR_STAT);#ifdef WD7000_DEBUG printk ("wd7000_intr_handle: intr stat = 0x%02x\n", flag);#endif if (!(inb (host->iobase + ASC_STAT) & INT_IM)) { /* NB: these are _very_ possible if IRQ 15 is being used, since * it's the "garbage collector" on the 2nd 8259 PIC. Specifically, * any interrupt signal into the 8259 which can't be identified * comes out as 7 from the 8259, which is 15 to the host. Thus, it * is a good thing the WD7000 has an interrupt status port, so we * can sort these out. Otherwise, electrical noise and other such * problems would be indistinguishable from valid interrupts... */#ifdef WD7000_DEBUG printk ("wd7000_intr_handle: phantom interrupt...\n");#endif wd7000_intr_ack (host); return; } if (flag & MB_INTR) { /* The interrupt is for a mailbox */ if (!(flag & IMB_INTR)) {#ifdef WD7000_DEBUG printk ("wd7000_intr_handle: free outgoing mailbox\n");#endif /* * If sleep_on() and the "interrupt on free OGMB" command are * used in mail_out(), wake_up() should correspondingly be called * here. For now, we don't need to do anything special. */ wd7000_intr_ack (host); return; } else { /* The interrupt is for an incoming mailbox */ icmb = flag & MB_MASK; icmb_status = icmbs[icmb].status; if (icmb_status & 0x80) { /* unsolicited - result in ICMB */#ifdef WD7000_DEBUG printk ("wd7000_intr_handle: unsolicited interrupt 0x%02x\n", icmb_status);#endif wd7000_intr_ack (host); return; } /* Aaaargh! (Zaga) */ scb = bus_to_virt(scsi2int ((unchar *) icmbs[icmb].scbptr)); icmbs[icmb].status = 0; if (!(scb->op & ICB_OP_MASK)) { /* an SCB is done */ SCpnt = scb->SCpnt; if (--(SCpnt->SCp.phase) <= 0) { /* all scbs are done */ host_error = scb->vue | (icmb_status << 8); scsi_error = scb->status; errstatus = make_code (host_error, scsi_error); SCpnt->result = errstatus; free_scb (scb); SCpnt->scsi_done (SCpnt); } } else { /* an ICB is done */ icb = (IcbAny *) scb; icb->status = icmb_status; icb->phase = 0; } } /* incoming mailbox */ } wd7000_intr_ack (host);#ifdef WD7000_DEBUG printk ("wd7000_intr_handle: return from interrupt handler\n");#endif}void do_wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs){ unsigned long flags; spin_lock_irqsave(&io_request_lock, flags); wd7000_intr_handle(irq, dev_id, regs); spin_unlock_irqrestore(&io_request_lock, flags);}int wd7000_queuecommand (Scsi_Cmnd *SCpnt, void (*done) (Scsi_Cmnd *)){ register Scb *scb; register Sgb *sgb; register unchar *cdb = (unchar *) SCpnt->cmnd; register unchar idlun; register short cdblen; Adapter *host = (Adapter *) SCpnt->host->hostdata; cdblen = SCpnt->cmd_len; idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7); SCpnt->scsi_done = done; SCpnt->SCp.phase = 1; scb = alloc_scbs (1); scb->idlun = idlun; memcpy (scb->cdb, cdb, cdblen); scb->direc = 0x40; /* Disable direction check */ scb->SCpnt = SCpnt; /* so we can find stuff later */ SCpnt->host_scribble = (unchar *) scb; scb->host = host; if (SCpnt->use_sg) { struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; unsigned i; if (SCpnt->host->sg_tablesize == SG_NONE) { panic ("wd7000_queuecommand: scatter/gather not supported.\n"); }#ifdef WD7000_DEBUG printk ("Using scatter/gather with %d elements.\n", SCpnt->use_sg);#endif sgb = scb->sgb; scb->op = 1; any2scsi (scb->dataptr, (int) sgb); any2scsi (scb->maxlen, SCpnt->use_sg * sizeof (Sgb)); for (i = 0; i < SCpnt->use_sg; i++) { any2scsi (sgb[i].ptr, (int) sg[i].address); any2scsi (sgb[i].len, sg[i].length); } } else { scb->op = 0; any2scsi (scb->dataptr, (int) SCpnt->request_buffer); any2scsi (scb->maxlen, SCpnt->request_bufflen); } while (!mail_out (host, scb)); /* keep trying */ return (1);}int wd7000_command (Scsi_Cmnd *SCpnt){ wd7000_queuecommand (SCpnt, wd7000_scsi_done); while (SCpnt->SCp.phase > 0) barrier (); /* phase counts scbs down to 0 */ return (SCpnt->result);}int wd7000_diagnostics (Adapter *host, int code){ static IcbDiag icb = {ICB_OP_DIAGNOSTICS}; static unchar buf[256]; unsigned long timeout; icb.type = code; any2scsi (icb.len, sizeof (buf)); any2scsi (icb.ptr, (int) &buf); icb.phase = 1; /* * This routine is only called at init, so there should be OGMBs * available. I'm assuming so here. If this is going to * fail, I can just let the timeout catch the failure. */ mail_out (host, (struct scb *) &icb); timeout = jiffies + WAITnexttimeout; /* wait up to 2 seconds */ while (icb.phase && time_before(jiffies, timeout)) barrier (); /* wait for completion */ if (icb.phase) { printk ("wd7000_diagnostics: timed out.\n"); return (0); } if (make_code (icb.vue | (icb.status << 8), 0)) { printk ("wd7000_diagnostics: failed (0x%02x,0x%02x)\n", icb.vue, icb.status); return (0); } return (1);}int wd7000_init (Adapter *host){ InitCmd init_cmd = { INITIALIZATION, 7, host->bus_on, host->bus_off, 0, { 0, 0, 0 }, OGMB_CNT, ICMB_CNT }; int diag; /* * Reset the adapter - only. The SCSI bus was initialized at power-up, * and we need to do this just so we control the mailboxes, etc. */ outb (ASC_RES, host->iobase + ASC_CONTROL); delay (1); /* reset pulse: this is 10ms, only need 25us */ outb (0, host->iobase + ASC_CONTROL); host->control = 0; /* this must always shadow ASC_CONTROL */ if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { printk ("wd7000_init: WAIT timed out.\n"); return (0); /* 0 = not ok */ } if ((diag = inb (host->iobase + ASC_INTR_STAT)) != 1) { printk ("wd7000_init: "); switch (diag) { case 2: printk ("RAM failure.\n"); break; case 3: printk ("FIFO R/W failed\n"); break; case 4: printk ("SBIC register R/W failed\n"); break; case 5: printk ("Initialization D-FF failed.\n"); break; case 6: printk ("Host IRQ D-FF failed.\n"); break; case 7: printk ("ROM checksum error.\n"); break; default: printk ("diagnostic code 0x%02Xh received.\n", diag); } return (0); } /* Clear mailboxes */ memset (&(host->mb), 0, sizeof (host->mb)); /* Execute init command */ any2scsi ((unchar *) & (init_cmd.mailboxes), (int) &(host->mb)); if (!command_out (host, (unchar *) &init_cmd, sizeof (init_cmd))) { printk ("wd7000_init: adapter initialization failed.\n"); return (0); } if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) { printk ("wd7000_init: WAIT timed out.\n"); return (0); } if (request_irq (host->irq, do_wd7000_intr_handle, SA_INTERRUPT, "wd7000", NULL)) { printk ("wd7000_init: can't get IRQ %d.\n", host->irq); return (0); } if (request_dma (host->dma, "wd7000")) { printk ("wd7000_init: can't get DMA channel %d.\n", host->dma); free_irq (host->irq, NULL); return (0); } wd7000_enable_dma (host); wd7000_enable_intr (host); if (!wd7000_diagnostics (host, ICB_DIAG_FULL)) { free_dma (host->dma); free_irq (host->irq, NULL); return (0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -