📄 wd7000.c
字号:
ulong flags; short i;#ifdef WD7000_DEBUG short free_scbs = 0;#endif save_flags (flags); cli (); for (i = 0; i < MAX_SCBS; i++) if (! scbs[i].used) { scbs[i].used = 1; scb = &(scbs[i]); break; }#ifdef WD7000_DEBUG for (i = 0; i < MAX_SCBS; i++) free_scbs += scbs[i].used ? 0 : 1; printk ("wd7000_%s: allocating scb (0x%08x), %d scbs free\n", __FUNCTION__, (int) scb, free_scbs);#endif restore_flags (flags); return (scb);}static inline void scb_free (Scb *scb){ short i; ulong flags; save_flags (flags); cli (); for (i = 0; i < MAX_SCBS; i++) if (&(scbs[i]) == scb) { memset ((void *) &(scbs[i]), 0, sizeof (Scb)); break; } if (i == MAX_SCBS) printk ("wd7000_%s: trying to free alien scb (0x%08x)...\n", __FUNCTION__, (int) scb);#ifdef WD7000_DEBUG else printk ("wd7000_%s: freeing scb (0x%08x)\n", __FUNCTION__, (int) scb);#endif 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; ulong flags; unchar start_ogmb; Mailbox *ogmbs = host->mb.ogmb; int *next_ogmb = &(host->next_ogmb);#ifdef WD7000_DEBUG printk ("wd7000_%s: 0x%08x", __FUNCTION__, (int) 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%08x", (int) 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; wd7000_command_out (host, &start_ogmb, 1);#ifdef WD7000_DEBUG printk (", awaiting interrupt.\n");#endif return (1);}int make_code (uint hosterr, uint 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 ("%s: 0x%08x\n", __FUNCTION__, (int) SCpnt);#endif SCpnt->SCp.phase = 0;}static inline void wd7000_intr_ack (Adapter *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 ("%s: irq = %d, host = 0x%08x\n", __FUNCTION__, irq, (int) host);#endif flag = inb (host->iobase + ASC_INTR_STAT);#ifdef WD7000_DEBUG printk ("%s: intr stat = 0x%02x\n", __FUNCTION__, 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 ("%s: phantom interrupt...\n", __FUNCTION__);#endif wd7000_intr_ack (host); return; } if (flag & MB_INTR) { /* The interrupt is for a mailbox */ if (! (flag & IMB_INTR)) {#ifdef WD7000_DEBUG printk ("%s: free outgoing mailbox\n", __FUNCTION__);#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 ("%s: unsolicited interrupt 0x%02x\n", __FUNCTION__, icmb_status);#endif wd7000_intr_ack (host); return; } /* Aaaargh! (Zaga) */ scb = (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; scb_free (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 ("%s: return from interrupt handler\n", __FUNCTION__);#endif}void do_wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs){#if (LINUX_VERSION_CODE >= 0x020100) ulong flags; spin_lock_irqsave (&io_request_lock, flags);#endif wd7000_intr_handle (irq, dev_id, regs);#if (LINUX_VERSION_CODE >= 0x020100) spin_unlock_irqrestore (&io_request_lock, flags);#endif}int wd7000_queuecommand (Scsi_Cmnd *SCpnt, void (*done) (Scsi_Cmnd *)){ register Scb *scb; register Sgb *sgb; register Adapter *host = (Adapter *) SCpnt->host->hostdata; if ((scb = scb_alloc ()) == NULL) { printk ("%s: Cannot allocate SCB!\n", __FUNCTION__); return (0); } SCpnt->scsi_done = done; SCpnt->SCp.phase = 1; SCpnt->host_scribble = (unchar *) scb; scb->idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7); scb->direc = 0x40; /* Disable direction check */ scb->SCpnt = SCpnt; /* so we can find stuff later */ scb->host = host; memcpy (scb->cdb, SCpnt->cmnd, SCpnt->cmd_len); if (SCpnt->use_sg) { struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; uint i; if (SCpnt->host->sg_tablesize == SG_NONE) panic ("%s: scatter/gather not supported.\n", __FUNCTION__);#ifdef WD7000_DEBUG else 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){ if (! wd7000_queuecommand (SCpnt, wd7000_scsi_done)) return (-1); 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]; ulong timeout; /* * 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. */ icb.type = code; any2scsi (icb.len, sizeof (buf)); any2scsi (icb.ptr, (int) &buf); icb.phase = 1; mail_out (host, (Scb *) &icb); /* * Wait up to 2 seconds for completion. */ for (timeout = jiffies + WAITnexttimeout; icb.phase && (jiffies < timeout); ) barrier (); if (icb.phase) { printk ("%s: timed out.\n", __FUNCTION__); return (0); } if (make_code (icb.vue | (icb.status << 8), 0)) { printk ("%s: failed (0x%02x,0x%02x)\n", __FUNCTION__, 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 ("%s: WAIT timed out.\n", __FUNCTION__); return (0); /* 0 = not ok */ } if ((diag = inb (host->iobase + ASC_INTR_STAT)) != 1) { printk ("%s: ", __FUNCTION__); 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 (! wd7000_command_out (host, (unchar *) &init_cmd, sizeof (init_cmd))) { printk ("%s: adapter initialization failed.\n", __FUNCTION__);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -