📄 sdmylex.c
字号:
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. */ while(waserror()) ; sleep(ccb, done32, ccb); poperror(); /* * 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 to 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 == SDok) d = 0; n -= d; r->rlen = n; /* * If there was a check-condition, save the * ccb for a possible request-sense command. */ if(sdstat == SDcheck){ if(r->flags & SDnosense){ lock(&ctlr->cachelock); if(ctlr->cache[target]) ccbfree(ctlr, ctlr->cache[target]); ctlr->cache[target] = (Ccb*)ccb; unlock(&ctlr->cachelock); return SDcheck; } n = 8+ccb->sense[7]; if(n > sizeof(r->sense)-1) n = sizeof(r->sense)-1; memmove(r->sense, ccb->sense, n); r->flags |= SDvalidsense; } ccbfree(ctlr, (Ccb*)ccb); if(btstat){ if(btstat == 0x11) return SDtimeout; return SDeio; } return sdstat;}static voidmylex32interrupt(Ureg*, void* arg){ ulong pa; Ctlr *ctlr; Ccb32 *ccb; Mbox32 *mb, *mbox; int port, rinterrupt, rstatus; ctlr = arg; port = ctlr->port; /* * Save and clear the interrupt(s). The only * interrupts expected are Cmdc, which is ignored, * and Imbl which means something completed. * There's one spurious interrupt left over from * initialisation, ignore it. */ rinterrupt = inb(port+Rinterrupt); rstatus = inb(port+Rstatus); outb(port+Rcontrol, Rint); if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++) print("%s: interrupt 0x%2.2ux\n", ctlr->sdev->name, rinterrupt); if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) print("%s: command invalid\n", ctlr->sdev->name); /* * Look for something in the mail. * If there is, free the mailbox and wakeup whoever. */ mb = ctlr->mb; for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){ pa = (mbox->ccb[3]<<24) |(mbox->ccb[2]<<16) |(mbox->ccb[1]<<8) |mbox->ccb[0]; if(ctlr->pcidev) ccb = BPA2K(pa, ctlr->pcidev->tbdf); else ccb = BPA2K(pa, BUSUNKNOWN); mbox->code = 0; ccb->done = 1; wakeup(ccb); ctlr->mbix++; if(ctlr->mbix >= NMbox+NMbox) ctlr->mbix = NMbox; }}static intmylexrio(SDreq* r){ Ctlr *ctlr; ctlr = r->unit->dev->ctlr; if((r->unit->subno) == ctlr->id) r->status = SDtimeout; else if(ctlr->bus == 24) r->status = mylex24rio(r); else r->status = mylex32rio(r); return r->status;}/* * 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 voidissueio(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; } if(datalen){ len = 0; while(len < datalen){ if(inb(port+Rstatus) & Dirrdy){ data[len] = inb(port+Rdatain); len++; } if(inb(port+Rinterrupt) & Cmdc) return; } }}/* * Issue a command to a controller, wait for it to complete then * try to reset the interrupt. Should only be called at initialisation. */static intissue(Ctlr* ctlr, uchar* cmd, int cmdlen, uchar* data, int datalen){ int port; uchar rinterrupt, rstatus; static Lock mylexissuelock; port = ctlr->port; ilock(&ctlr->issuelock); issueio(port, cmd, cmdlen, data, datalen); while(!((rinterrupt = inb(port+Rinterrupt)) & Cmdc)) ; rstatus = inb(port+Rstatus); outb(port+Rcontrol, Rint); iunlock(&ctlr->issuelock); if((rinterrupt & Cmdc) && (rstatus & Cmdinv)) return 0; return 1;}static SDev*mylexprobe(int port, int irq){ SDev *sdev; Ctlr *ctlr; uchar cmd[6], data[256]; int clen, dlen, timeo; if(ioalloc(port, 0x3, 0, "mylex") < 0) return nil; ctlr = nil; /* * 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)){buggery: if(ctlr != nil) free(ctlr); iofree(port); return nil; } if((ctlr = malloc(sizeof(Ctlr))) == nil) goto buggery; ctlr->port = port; ctlr->irq = irq; ctlr->bus = 24; ctlr->wide = 0; /* * Try to determine if this is a 32-bit MultiMaster 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] = 4; clen = 2; dlen = 256; if(issue(ctlr, cmd, clen, data, dlen)){ if(data[0] == 'E') ctlr->bus = 32; ctlr->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)) goto buggery; } /* * If the BIOS is enabled on the AHA-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; clen = 1; dlen = 4; if(issue(ctlr, cmd, clen, data, dlen) == 0) goto buggery; if(data[0] >= 0x43){ cmd[0] = Cextbios; clen = 1; dlen = 2; if(issue(ctlr, cmd, clen, data, dlen) == 0) goto buggery; /* * 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]; clen = 3; if(issue(ctlr, cmd, clen, 0, 0) == 0) goto buggery; } } /* * Get the id, DMA and IRQ info from the board. This will * cause an interrupt which will hopefully not cause any * trouble because the interrupt number isn't known yet. * This is necessary as the DMA won't be set up if the * board has the BIOS disabled. * * If the IRQ is already known, this must be a 32-bit PCI * or EISA card, in which case the returned DMA and IRQ can * be ignored. */ cmd[0] = Cinquire; clen = 1; dlen = 3; if(issue(ctlr, cmd, clen, data, dlen) == 0) goto buggery; ctlr->id = data[2] & 0x07; if(ctlr->irq < 0){ switch(data[0]){ /* DMA Arbitration Priority */ case 0x80: /* Channel 7 */ outb(0xD6, 0xC3); outb(0xD4, 0x03); break; case 0x40: /* Channel 6 */ outb(0xD6, 0xC2); outb(0xD4, 0x02); break; case 0x20: /* Channel 5 */ outb(0xD6, 0xC1); outb(0xD4, 0x01); break; case 0x01: /* Channel 0 */ outb(0x0B, 0xC0); outb(0x0A, 0x00); break; default: break; } switch(data[1]){ /* Interrupt Channel */ case 0x40: ctlr->irq = 15; break; case 0x20: ctlr->irq = 14; break; case 0x08: ctlr->irq = 12; break; case 0x04: ctlr->irq = 11; break; case 0x02: ctlr->irq = 10; break; case 0x01: ctlr->irq = 9; break; default: goto buggery; } } if((sdev = malloc(sizeof(SDev))) == nil) goto buggery; sdev->ifc = &sdmylexifc; sdev->ctlr = ctlr; ctlr->sdev = sdev; if(!ctlr->wide) sdev->nunit = 8; else sdev->nunit = 16; return sdev;}static int mylexport[8] = { 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0x000, 0x000,};static int mylexirq[8] = { 9, 10, 11, 12, 14, 15, 0, 0,};static SDev*mylexpnp(void){ Pcidev *p; Ctlr *ctlr; int cfg, i, x; SDev *sdev, *head, *tail; p = nil; head = tail = nil; while(p = pcimatch(p, 0x104B, 0)){ if((sdev = mylexprobe(p->mem[0].bar & ~0x01, p->intl)) == nil) continue; ctlr = sdev->ctlr; ctlr->pcidev = p; if(head != nil) tail->next = sdev; else head = sdev; tail = sdev; } if(strncmp(KADDR(0xFFFD9), "EISA", 4)) return head; for(cfg = 0x1000; cfg < MaxEISA*0x1000; cfg += 0x1000){ x = 0; for(i = 0; i < 4; i++) x |= inb(cfg+CfgEISA+i)<<(i*8); if(x != 0x0142B30A && x != 0x0242B30A) continue; x = inb(cfg+0xC8C); if((sdev = mylexprobe(mylexport[x & 0x07], -1)) == nil) continue; if(head != nil) tail->next = sdev; else head = sdev; tail = sdev; } return head;}static SDev*mylexid(SDev* sdev){ return scsiid(sdev, &sdmylexifc);}static intmylex24enable(Ctlr* ctlr){ ulong p; Ccb24 *ccb, *ccbp; uchar cmd[6], *v; int len; len = (sizeof(Mbox24)*NMbox*2)+(sizeof(Ccb24)*NCcb); v = xspanalloc(len, 32, 0); if(!PADDR24(ctlr, sizeof(Ctlr)) || !PADDR24(v, len)) return 0; ctlr->mb = v; v += sizeof(Mbox24)*NMbox*2; ccb = (Ccb24*)v; for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){ ccbp->ccb = ctlr->ccb; ctlr->ccb = (Ccb*)ccbp; } /* * Initialise the software controller and * set the board scanning the mailboxes. */ ctlr->mbix = NMbox; cmd[0] = Cinitialise; cmd[1] = NMbox; p = K2BPA(ctlr->mb, BUSUNKNOWN); cmd[2] = p>>16; cmd[3] = p>>8; cmd[4] = p; len = 5; return issue(ctlr, cmd, len, 0, 0);}static intmylex32enable(Ctlr* ctlr){ ulong p; Ccb32 *ccb, *ccbp; uchar cmd[6], *v; v = xspanalloc((sizeof(Mbox32)*NMbox*2)+(sizeof(Ccb32)*NCcb), 32, 0); ctlr->mb = 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; } /* * Attempt wide mode setup. */ if(ctlr->wide){ cmd[0] = Cwide; cmd[1] = 1; if(!issue(ctlr, cmd, 2, 0, 0)) ctlr->wide = 0; } /* * Initialise the software controller and * set the board scanning the mailboxes. */ ctlr->mbix = NMbox; cmd[0] = Ciem; cmd[1] = NMbox; if(ctlr->pcidev) p = K2BPA(ctlr->mb, ctlr->tbdf); else p = K2BPA(ctlr->mb, BUSUNKNOWN); cmd[2] = p; cmd[3] = p>>8; cmd[4] = p>>16; cmd[5] = p>>24; return issue(ctlr, cmd, 6, 0, 0);}static intmylexenable(SDev* sdev){ int tbdf; Ctlr *ctlr; void (*interrupt)(Ureg*, void*); char name[NAMELEN]; ctlr = sdev->ctlr; if(ctlr->cache == nil){ if((ctlr->cache = malloc(sdev->nunit*sizeof(Ccb*))) == nil) return 0; } tbdf = BUSUNKNOWN; if(ctlr->bus == 32){ if(ctlr->pcidev){ tbdf = ctlr->pcidev->tbdf; pcisetbme(ctlr->pcidev); } if(!mylex32enable(ctlr)) return 0; interrupt = mylex32interrupt; } else if(mylex24enable(ctlr)) interrupt = mylex24interrupt; else return 0; snprint(name, NAMELEN, "sd%c (%s)", sdev->idno, sdev->ifc->name); intrenable(ctlr->irq, interrupt, ctlr, tbdf, name); return 1;}SDifc sdmylexifc = { "mylex", /* name */ mylexpnp, /* pnp */ nil, /* legacy */ mylexid, /* id */ mylexenable, /* enable */ nil, /* disable */ scsiverify, /* verify */ scsionline, /* online */ mylexrio, /* rio */ nil, /* rctl */ nil, /* wctl */ scsibio, /* bio */};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -