📄 scsibuslogic.c
字号:
/* * Fill in the ccb. */ ccb->opcode = Ordl; if(dbytes) n = *dbytes; else n = 0; if(n == 0) ccb->datadir = CCBdataout|CCBdatain; else if(rw == SCSIread) ccb->datadir = CCBdatain; else ccb->datadir = CCBdataout; ccb->cdblen = cbytes; ccb->datalen[0] = n; ccb->datalen[1] = n>>8; ccb->datalen[2] = n>>16; ccb->datalen[3] = n>>24; p = PADDR(data); ccb->dataptr[0] = p; ccb->dataptr[1] = p>>8; ccb->dataptr[2] = p>>16; ccb->dataptr[3] = p>>24; ccb->targetid = id; ccb->luntag = lun; if(t->ok && (t->inquiry[7] & 0x02)){ if(ctlr->wide) ccb->datadir |= SQTag|TagEnable; else ccb->luntag |= SQTag|TagEnable; } memmove(ccb->cdb, cmd, cbytes); ccb->btstat = ccb->sdstat = 0; ccb->ccbctl = 0; /* * There's one more mbox than there there is * ccb so there is always one free. */ lock(&ctlr->mboxlock); mb = ctlr->mb; mb += ctlr->mbox; p = PADDR(ccb); mb->ccb[0] = p; mb->ccb[1] = p>>8; mb->ccb[2] = p>>16; mb->ccb[3] = p>>24; mb->code = Mbostart; ctlr->mbox++; if(ctlr->mbox >= NMbox) ctlr->mbox = 0; /* * This command does not require Hardy * and doesn't generate a Cmdc interrupt. */ ccb->done = 0; outb(ctlr->port+Rcpr, Cstart); unlock(&ctlr->mboxlock); /* * Wait for the request to complete and return the status. * Since the buffer is not reference counted cannot return * until the DMA is done writing into the buffer so the caller * cannot free the buffer prematurely. */ sleep(ccb, done32, ccb); /* * Save the status and patch up the number of * bytes actually transferred. * There's a firmware bug on some 956C controllers * which causes the return count from a successful * READ CAPACITY not be updated, so fix it here. */ sdstat = ccb->sdstat; btstat = ccb->btstat; d = ccb->datalen[0]; d |= (ccb->datalen[1]<<8); d |= (ccb->datalen[2]<<16); d |= (ccb->datalen[3]<<24); if(ccb->cdb[0] == 0x25 && sdstat == STok) d = 0; n -= d; if(dbytes) *dbytes = n; /* * If there was a check-condition, save the * ccb for a possible request-sense command. */ if(sdstat == STcheck){ lock(&ctlr->cachelock); if(ctlr->cache[id]) ccbfree(ctlr, ctlr->cache[id]); ctlr->cache[id] = (Ccb*)ccb; unlock(&ctlr->cachelock); return STcheck; } ccbfree(ctlr, (Ccb*)ccb); if(btstat){ if(btstat == 0x11) return STtimeout; return STharderr; } return sdstat;}static voidinterrupt32(Ureg*, void* arg){ Ctlr *ctlr; ulong port; uchar rinterrupt, rstatus; Mbox32 *mb, *mbox; Ccb32 *ccb; ctlr = arg; /* * Save and clear the interrupt(s). The only * interrupts expected are Cmdc, which is ignored, * and Imbl which means something completed. */ port = ctlr->port; rinterrupt = inb(port+Rinterrupt); rstatus = inb(port+Rstatus); if((rinterrupt & ~(Cmdc|Imbl)) != Intv) print("scsi#%d: interrupt 0x%2.2ux\n", ctlr->ctlrno, rinterrupt); if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) print("scsi#%d: command invalid\n", ctlr->ctlrno); /* * Look for something in the mail. * If there is, save the status, free the mailbox * and wakeup whoever. */ mb = ctlr->mb; for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){ ccb = (Ccb*)(KZERO |(mbox->ccb[3]<<24) |(mbox->ccb[2]<<16) |(mbox->ccb[1]<<8) |mbox->ccb[0]); mbox->code = 0; ccb->done = 1; wakeup(ccb); ctlr->mbix++; if(ctlr->mbix >= NMbox+NMbox) ctlr->mbix = NMbox; } outb(port+Rcontrol, Rint);}static Lock cmdlock[MaxScsi];/* * Issue a command to a controller. The command and its length is * contained in cmd and cmdlen. If any data is to be * returned, datalen should be non-zero, and the returned data * will be placed in data. * If Cmdc is set, bail out, the invalid command will be handled * when the interrupt is processed. */static intissueio(int port, uchar* cmd, int cmdlen, uchar* data, int datalen){ int len; if(cmd[0] != Cstart && cmd[0] != Ceombri){ while(!(inb(port+Rstatus) & Hardy)) ; } outb(port+Rcpr, cmd[0]); len = 1; while(len < cmdlen){ if(!(inb(port+Rstatus) & Cprbsy)){ outb(port+Rcpr, cmd[len]); len++; } if(inb(port+Rinterrupt) & Cmdc) return 0; } len = 0; if(datalen){ while(len < datalen){ if(inb(port+Rstatus) & Dirrdy){ data[len] = inb(port+Rdatain); len++; } if(inb(port+Rinterrupt) & Cmdc) break; } } return len;}/* * Issue a command to a controller, wait for it to complete then * reset the interrupt. * Should only be called at initialisation. */static intissue(int ctlrno, int port, uchar* cmd, int cmdlen, uchar* data, int datalen){ int len; uchar rinterrupt, rstatus; ilock(&cmdlock[ctlrno]); len = issueio(port, cmd, cmdlen, data, datalen); while(!((rinterrupt = inb(port+Rinterrupt)) & Cmdc)) ; rstatus = inb(port+Rstatus); outb(port+Rcontrol, Rint); if((rinterrupt & Cmdc) && (rstatus & Cmdinv)){ iunlock(&cmdlock[ctlrno]); return -1; } iunlock(&cmdlock[ctlrno]); return len;}Scsiiobuslogic24(Ctlr* ctlr, ISAConf* isa){ ulong p; Ccb24 *ccb, *ccbp; Bbuf *bb; uchar cmd[6], *v; int i, len; len = (sizeof(Mbox24)*NMbox*2)+(sizeof(Ccb24)*NCcb)+(sizeof(Bbuf)*NBbuf); v = ialloc(len, 0); if(!PADDR24(ctlr, sizeof(Ctlr)) || !PADDR24(v, len)){ print("scsi#%d: %s: 24-bit allocation failed\n", ctlr->ctlrno, isa->type); return 0; } ctlr->mb = (Mbox*)v; v += sizeof(Mbox24)*NMbox*2; ccb = (Ccb24*)v; for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){ ccbp->ccb = ctlr->ccb; ctlr->ccb = (Ccb*)ccbp; } v += sizeof(Ccb24)*NCcb; for(i = 0; i < NBbuf; i++){ bb = (Bbuf*)v; bb->buf = ialloc(RBUFSIZE, 32); if(bb->buf == nil || !PADDR24(bb->buf, RBUFSIZE)){ print("scsi#%d: %s: 24-bit bb allocation failed (%d)\n", ctlr->ctlrno, isa->type, i); break; } bb->next = ctlr->bbuf; ctlr->bbuf = bb; v += sizeof(Bbuf); } /* * Initialise the software controller and * set the board scanning the mailboxes. */ ctlr->mbix = NMbox; cmd[0] = Cinitialise; cmd[1] = NMbox; p = PADDR(ctlr->mb); cmd[2] = p>>16; cmd[3] = p>>8; cmd[4] = p; if(issue(ctlr->ctlrno, ctlr->port, cmd, 5, 0, 0) >= 0){ ctlrxx[ctlr->ctlrno] = ctlr; setvec(Int0vec+isa->irq, interrupt24, ctlr); return scsiio24; } print("scsi#%d: %s: mbox24 init failed\n", ctlr->ctlrno, isa->type); return 0;}Scsiiobuslogic32(Ctlr* ctlr, ISAConf* isa){ ulong p; Ccb32 *ccb, *ccbp; uchar cmd[6], *v; v = ialloc((sizeof(Mbox32)*NMbox*2)+(sizeof(Ccb32)*NCcb), 0); ctlr->mb = (Mbox*)v; v += sizeof(Mbox32)*NMbox*2; ccb = (Ccb32*)v; for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){ /* * Fill in some stuff that doesn't change. */ ccbp->senselen = sizeof(ccbp->sense); p = PADDR(ccbp->sense); ccbp->senseptr[0] = p; ccbp->senseptr[1] = p>>8; ccbp->senseptr[2] = p>>16; ccbp->senseptr[3] = p>>24; ccbp->ccb = ctlr->ccb; ctlr->ccb = (Ccb*)ccbp; } /* * Wide mode setup. */ if(ctlr->wide){ cmd[0] = Cwide; cmd[1] = 1; if(issue(ctlr->ctlrno, ctlr->port, cmd, 2, 0, 0) < 0) print("scsi#%d: %s: wide init failed\n", ctlr->ctlrno, isa->type); } /* * Initialise the software controller and * set the board scanning the mailboxes. */ ctlr->mbix = NMbox; cmd[0] = Ciem; cmd[1] = NMbox; p = PADDR(ctlr->mb); cmd[2] = p; cmd[3] = p>>8; cmd[4] = p>>16; cmd[5] = p>>24; if(issue(ctlr->ctlrno, ctlr->port, cmd, 6, 0, 0) >= 0){ ctlrxx[ctlr->ctlrno] = ctlr; /* intrenable(VectorPIC+isa->irq, interrupt32, ctlr, ctlr->tbdf); */ setvec(Int0vec+isa->irq, interrupt32, ctlr); return scsiio32; } print("scsi#%d: %s: mbox32 init failed\n", ctlr->ctlrno, isa->type); return 0;}typedef struct Adapter { int port; Pcidev* pcidev;} Adapter;static Msgbuf* adapter;static voidbuslogicpci(void){ Msgbuf *mb; Adapter *ap; Pcidev *p; p = nil; while(p = pcimatch(p, 0x104B, 0)){ mb = mballoc(sizeof(Adapter), 0, Mxxx); ap = (Adapter*)mb->data; ap->port = p->mem[0].bar & ~0x01; ap->pcidev = p; mb->next = adapter; adapter = mb; }}Scsiiobuslogic(int ctlrno, ISAConf* isa){ Scsiio io; Ctlr *ctlr; Adapter *ap; Pcidev *pcidev; Msgbuf *mb, **mbb; uchar cmd[6], data[256]; int bus, cmdlen, datalen, port, timeo, wide; static int scandone; if(scandone == 0){ buslogicpci(); scandone = 1; } /* * Any adapter matches if no isa->port is supplied, * otherwise the ports must match. */ port = 0; pcidev = nil; mbb = &adapter; for(mb = *mbb; mb != nil; mb = mb->next){ ap = (Adapter*)mb->data; if(isa->port == 0 || isa->port == ap->port){ port = ap->port; pcidev = ap->pcidev; *mbb = mb->next; mbfree(mb); break; } mbb = &mb->next; } if(port == 0){ if(isa->port == 0) return nil; port = isa->port; } isa->port = port; /* * Attempt to hard-reset the board and reset * the SCSI bus. If the board state doesn't settle to * idle with mailbox initialisation required, either * it isn't a compatible board or it's broken. * If the controller has SCAM set this can take a while. */ outb(port+Rcontrol, Rhard|Rsbus); for(timeo = 0; timeo < 100; timeo++){ if(inb(port+Rstatus) == (Inreq|Hardy)) break; delay(100); } if(inb(port+Rstatus) != (Inreq|Hardy)){ print("scsi#%d: %s: port 0x%ux failed to hard-reset 0x%ux\n", ctlrno, isa->type, port, inb(port+Rstatus)); return 0; } /* * Try to determine if this is a Buslogic 32-bit controller * by attempting to obtain the extended inquiry information; * this command is not implemented on Adaptec 154xx * controllers. If successful, the first byte of the returned * data is the host adapter bus type, 'E' for 32-bit EISA, * PCI and VLB buses. */ cmd[0] = Ciesi; cmd[1] = 14; cmdlen = 2; datalen = 256; bus = 24; wide = 0; datalen = issue(ctlrno, port, cmd, cmdlen, data, datalen); if(datalen >= 0){ if(data[0] == 'E') bus = 32; wide = data[0x0D] & 0x01; } else{ /* * Inconceivable though it may seem, a hard controller reset is * necessary here to clear out the command queue. Every board seems to * lock-up in a different way if you give an invalid command and then * try to clear out the command/parameter and/or data-in register. * Soft reset doesn't do the job either. Fortunately no serious * initialisation has been done yet so there's nothing to tidy up. */ outb(port+Rcontrol, Rhard); for(timeo = 0; timeo < 100; timeo++){ if(inb(port+Rstatus) == (Inreq|Hardy)) break; delay(100); } if(inb(port+Rstatus) != (Inreq|Hardy)){ print("scsi#%d: %s: port 0x%ux Ciesi 0x%ux\n", ctlrno, isa->type, port, inb(port+Rstatus)); return 0; } } /* * If the BIOS is enabled on the 1542C/CF and BIOS options for support of drives * > 1Gb, dynamic scanning of the SCSI bus or more than 2 drives under DOS 5.0 * are enabled, the BIOS disables accepting Cmbinit to protect against running * with drivers which don't support those options. In order to unlock the * interface it is necessary to read a lock-code using Cextbios and write it back * using Cmbienable; the lock-code is non-zero. */ cmd[0] = Cinquiry; cmdlen = 1; datalen = 4; if(issue(ctlrno, port, cmd, cmdlen, data, datalen) < 0){ print("scsi#%d: %s: Cinquiry\n", ctlrno, isa->type); return 0; } if(data[0] >= 0x43){ cmd[0] = Cextbios; cmdlen = 1; datalen = 2; if(issue(ctlrno, port, cmd, cmdlen, data, datalen) < 0){ print("scsi#%d: %s: Cextbios\n", ctlrno, isa->type); return 0; } /* * Lock-code returned in data[1]. If it's non-zero write it back * along with bit 0 of byte 0 cleared to enable mailbox initialisation. */ if(data[1]){ cmd[0] = Cmbienable; cmd[1] = 0; cmd[2] = data[1]; cmdlen = 3; if(issue(ctlrno, port, cmd, cmdlen, 0, 0) < 0){ print("scsi#%d: %s: Cmbienable\n", ctlrno, isa->type); return 0; } } } /* * Get the DMA, IRQ and adapter SCSI ID from the board. * This is necessary as the DMA won't be set up if the it's * not a PCI adapter and the BIOS is disabled. */ cmd[0] = Cinquire; cmdlen = 1; datalen = 3; if(issue(ctlrno, port, cmd, cmdlen, data, datalen) < 0){ print("scsi#%d: %s: can't inquire configuration\n", ctlrno, isa->type); return 0; } if(pcidev && pcidev->intl) isa->irq = pcidev->intl; else{ switch(data[0]){ /* DMA Arbitration Priority */ case 0x80: /* Channel 7 */ outb(0xD6, 0xC3); outb(0xD4, 0x03); isa->dma = 7; break; case 0x40: /* Channel 6 */ outb(0xD6, 0xC2); outb(0xD4, 0x02); isa->dma = 6; break; case 0x20: /* Channel 5 */ outb(0xD6, 0xC1); outb(0xD4, 0x01); isa->dma = 5; break; case 0x01: /* Channel 0 */ outb(0x0B, 0xC0); outb(0x0A, 0x00); isa->dma = 0; break; default: /* * No DMA channel will show for 32-bit EISA/VLB * cards which don't have ISA DMA compatibility set. * Carry on regardless. */ isa->dma = -1; break; } switch(data[1]){ /* Interrupt Channel */ case 0x40: isa->irq = 15; break; case 0x20: isa->irq = 14; break; case 0x08: isa->irq = 12; break; case 0x04: isa->irq = 11; break; case 0x02: isa->irq = 10; break; case 0x01: isa->irq = 9; break; default: print("scsi#%d: %s: invalid irq #%2.2ux\n", ctlrno, isa->type, data[1]); return 0; } } /* * Allocate and start to initialise the software controller. */ ctlr = ialloc(sizeof(Ctlr), 0); ctlr->port = isa->port; ctlr->id = data[2] & 0x07; ctlr->ctlrno = ctlrno; ctlr->bus = bus; ctlr->wide = wide; if(pcidev) ctlr->tbdf = pcidev->tbdf; else ctlr->tbdf = BUSUNKNOWN; if(bus == 24) io = buslogic24(ctlr, isa); else io = buslogic32(ctlr, isa); if(io){ if(ctrls == 0) cmd_install("scsi", "-- scsi stats", cmd_scsi); ctrls |= 1<<ctlrno; } return io;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -