📄 scsibuslogic.c
字号:
/* * Buslogic BT-* SCSI Host Adapter in both 24-bit and 32-bit mode. * 24-bit mode works for Adaptec 154xx series too. * * To do: * tidy the PCI probe and do EISA; * allocate more Ccb's as needed, up to NMbox-1; * add nmbox and nccb to Ctlr struct for the above; * 64-bit LUN/explicit wide support necessary? * */#include "all.h"#include "io.h"#include "mem.h"enum { /* registers */ Rcontrol = 0x00, /* WO: control register */ Rstatus = 0x00, /* RO: status register */ Rcpr = 0x01, /* WO: command/parameter register */ Rdatain = 0x01, /* RO: data-in register */ Rinterrupt = 0x02, /* RO: interrupt register */};enum { /* Rcontrol */ Rsbus = 0x10, /* SCSI Bus Reset */ Rint = 0x20, /* Interrupt Reset */ Rsoft = 0x40, /* Soft Reset */ Rhard = 0x80, /* Hard Reset */};enum { /* Rstatus */ Cmdinv = 0x01, /* Command Invalid */ Dirrdy = 0x04, /* Data In Register Ready */ Cprbsy = 0x08, /* Command/Parameter-Out Register Busy */ Hardy = 0x10, /* Host Adapter Ready */ Inreq = 0x20, /* Initialisation Required */ Dfail = 0x40, /* Diagnostic Failure */ Dact = 0x80, /* Diagnostic Active */};enum { /* Rcpr */ Cinitialise = 0x01, /* Initialise Mailbox */ Cstart = 0x02, /* Start Mailbox Command */ Cinquiry = 0x04, /* Adapter Anquiry */ Ceombri = 0x05, /* Enable OMBR Interrupt */ Cinquire = 0x0B, /* Inquire Configuration */ Cextbios = 0x28, /* AHA-1542: Return extended BIOS information */ Cmbienable = 0x29, /* AHA-1542: Mailbox interface enable */ Ciem = 0x81, /* Initialise Extended Mailbox */ Ciesi = 0x8D, /* Inquire Extended Setup Information */ Cerrm = 0x8F, /* Enable strict round-robin mode */ Cwide = 0x96, /* Wide CCB */};enum { /* Rinterrupt */ Imbl = 0x01, /* Incoming Mailbox Loaded */ Mbor = 0x02, /* Mailbox Out Ready */ Cmdc = 0x04, /* Command Complete */ Rsts = 0x08, /* SCSI Reset State */ Intv = 0x80, /* Interrupt Valid */};typedef struct { uchar code; /* action/completion code */ uchar ccb[3]; /* CCB pointer (MSB, ..., LSB) */} Mbox24;typedef struct { uchar ccb[4]; /* CCB pointer (LSB, ..., MSB) */ uchar btstat; /* BT-7[45]7[SD] status */ uchar sdstat; /* SCSI device status */ uchar pad; uchar code; /* action/completion code */} Mbox32;typedef union { Mbox24; Mbox32;} Mbox;enum { /* mailbox commands */ Mbfree = 0x00, /* Mailbox not in use */ Mbostart = 0x01, /* Start a mailbox command */ Mboabort = 0x02, /* Abort a mailbox command */ Mbiok = 0x01, /* CCB completed without error */ Mbiabort = 0x02, /* CCB aborted at request of host */ Mbinx = 0x03, /* Aborted CCB not found */ Mbierror = 0x04, /* CCB completed with error */};typedef struct Ccb24 Ccb24;typedef struct Ccb32 Ccb32;typedef union Ccb Ccb;typedef struct Ccb24 { uchar opcode; /* Operation code */ uchar datadir; /* Data direction control */ uchar cdblen; /* Length of CDB */ uchar senselen; /* Length of sense area */ uchar datalen[3]; /* Data length (MSB, ..., LSB) */ uchar dataptr[3]; /* Data pointer (MSB, ..., LSB) */ uchar linkptr[3]; /* Link pointer (MSB, ..., LSB) */ uchar linkid; /* command linking identifier */ uchar btstat; /* BT-* adapter status */ uchar sdstat; /* SCSI device status */ uchar reserved[2]; /* */ uchar cs[12+0xFF]; /* Command descriptor block + Sense bytes */ Rendez; int done; /* command completed */ Ccb* ccb; /* link on free list */} Ccb24;typedef struct Ccb32 { uchar opcode; /* Operation code */ uchar datadir; /* Data direction control */ uchar cdblen; /* Length of CDB */ uchar senselen; /* Length of sense area */ uchar datalen[4]; /* Data length (LSB, ..., MSB) */ uchar dataptr[4]; /* Data pointer (LSB, ..., MSB) */ uchar reserved[2]; uchar btstat; /* BT-* adapter status */ uchar sdstat; /* SCSI device status */ uchar targetid; /* Target ID */ uchar luntag; /* LUN & tag */ uchar cdb[12]; /* Command descriptor block */ uchar ccbctl; /* CCB control */ uchar linkid; /* command linking identifier */ uchar linkptr[4]; /* Link pointer (LSB, ..., MSB) */ uchar senseptr[4]; /* Sense pointer (LSB, ..., MSB) */ uchar sense[0xFF]; /* Sense bytes */ Rendez; int done; /* command completed */ Ccb* ccb; /* link on free list */} Ccb32;typedef union Ccb { Ccb24; Ccb32;} Ccb;enum { /* opcode */ OInitiator = 0x00, /* initiator CCB */ Ordl = 0x03, /* initiator CCB with residual data length returned */};enum { /* datadir */ CCBdatain = 0x08, /* inbound data transfer, length is checked */ CCBdataout = 0x10, /* outbound data transfer, length is checked */};enum { /* btstat */ Eok = 0x00, /* CCB completed normally with no errors */};enum { /* luntag */ TagEnable = 0x20, /* Tag enable */ SQTag = 0x00, /* Simple Queue Tag */ HQTag = 0x40, /* Head of Queue Tag */ OQTag = 0x80, /* Ordered Queue Tag */};enum { /* CCB control */ NoDisc = 0x08, /* No disconnect */ NoUnd = 0x10, /* No underrrun error report */ NoData = 0x20, /* No data transfer */ NoStat = 0x40, /* No CCB status if zero */ NoIntr = 0x80, /* No Interrupts */};typedef struct Bbuf Bbuf;struct Bbuf { Bbuf* next; uchar* data; /* original data */ uchar* buf; /* bounce buffer */};typedef struct { ulong port; /* I/O port */ int id; /* adapter SCSI id */ int ctlrno; int bus; /* 24 or 32 -bit */ int wide; int tbdf; Lock ccblock; QLock ccbq; Rendez ccbr; Lock mboxlock; Mbox* mb; /* mailbox out + mailbox in */ int mbox; /* current mailbox out index into mb */ int mbix; /* current mailbox in index into mb */ Lock cachelock; Ccb* ccb; /* list of free Ccb's */ Ccb* cache[NTarget]; /* last completed Ccb */ Lock bblock; Bbuf* bbuf; /* list of free 24-bit bounce buffers */ QLock bbq; Rendez bbr;} Ctlr;/* * The number of mailboxes should be a multiple of 8 (4 for Mbox32) * to ensure the boundary between the out and in mailboxes doesn't * straddle a cache-line boundary. * The number of Ccb's should be less than the number of mailboxes to * ensure no queueing is necessary on mailbox allocation. * NBbuf should minimally be the actual number of targets plus some * slop for queuing. Since 24-bit controllers are never wide and it's * unlikely anyone would fully populate the controller, 8 is probably * enough. */enum { NMbox = 8*8, /* number of Mbox's */ NCcb = NMbox-1, /* number of Ccb's */ NBbuf = 8, /* number of 24-bit bounce buffers */};#define PADDR24(a, n) ((PADDR(a)+(n)) <= (1<<24))static Ctlr *ctlrxx[MaxScsi];static int ctrls;static voidcmd_scsi(int, char**){ int i, j; Ctlr *ctlr; Mbox24 *mb24; Mbox32 *mb32; for(i=0; i<MaxScsi; i++) { if(!(ctrls & (1<<i))) continue; print("ctlr %d:\n", i); ctlr = ctlrxx[i]; print(" mbox %2.2d\n", ctlr->mbox); print(" mbix %2.2d\n", ctlr->mbix); if(ctlr->bus == 24){ for(j=0; j<NMbox+NMbox; j++) { mb24 = &ctlr->mb[j]; print(" mbox %2.2d: code #%x\tpaddr #%ux\n", j, mb24->code, (mb24->ccb[0]<<16)|(mb24->ccb[1]<<8)|mb24->ccb[2]); prflush(); } } else { for(j=0; j<NMbox+NMbox; j++) { mb32 = &ctlr->mb[j]; print(" mbox %2.2d: code #%x\tpaddr #%ux\tbt#%ux\tsd#%ux\n", j, mb32->code, (mb32->ccb[3]<<24) |(mb32->ccb[2]<<16) |(mb32->ccb[1]<<8) |mb32->ccb[0], mb32->btstat, mb32->sdstat); prflush(); } } }}static voidccbfree(Ctlr* ctlr, Ccb* ccb){ lock(&ctlr->ccblock); if(ctlr->bus == 24) ((Ccb24*)ccb)->ccb = ctlr->ccb; else ((Ccb32*)ccb)->ccb = ctlr->ccb; if(ctlr->ccb == nil) wakeup(&ctlr->ccbr); ctlr->ccb = ccb; unlock(&ctlr->ccblock);}static intccbavailable(void* a){ return ((Ctlr*)a)->ccb != nil;}static Ccb*ccballoc(Ctlr* ctlr){ Ccb *ccb; for(;;){ lock(&ctlr->ccblock); if(ccb = ctlr->ccb){ if(ctlr->bus == 24) ctlr->ccb = ((Ccb24*)ccb)->ccb; else ctlr->ccb = ((Ccb32*)ccb)->ccb; unlock(&ctlr->ccblock); break; } unlock(&ctlr->ccblock); qlock(&ctlr->ccbq); sleep(&ctlr->ccbr, ccbavailable, ctlr); qunlock(&ctlr->ccbq); } return ccb;}static voidbbfree(Ctlr* ctlr, Bbuf *bb){ lock(&ctlr->bblock); bb->next = ctlr->bbuf; if(ctlr->bbuf == nil) wakeup(&ctlr->bbr); ctlr->bbuf = bb; unlock(&ctlr->bblock);}static intbbavailable(void* a){ return ((Ctlr*)a)->bbuf != nil;}static Bbuf*bballoc(Ctlr* ctlr){ Bbuf *bb; for(;;){ lock(&ctlr->bblock); if(bb = ctlr->bbuf){ ctlr->bbuf = bb->next; unlock(&ctlr->bblock); break; } unlock(&ctlr->bblock); qlock(&ctlr->bbq); sleep(&ctlr->bbr, bbavailable, ctlr); qunlock(&ctlr->bbq); } return bb;}static intdone24(void* arg){ return ((Ccb24*)arg)->done;}static intscsiio24(Target* t, int rw, uchar* cmd, int cbytes, void* data, int* dbytes){ Ctlr *ctlr; Ccb24 *ccb; Mbox24 *mb; Bbuf *bb; ulong p; int d, id, n, btstat, sdstat; uchar *sense; uchar lun; if((ctlr = ctlrxx[t->ctlrno]) == nil || ctlr->port == 0) return STharderr; id = t->targetno; if(ctlr->id == id) return STownid; lun = (cmd[1]>>5) & 0x07; /* * Ctlr->cache holds the last completed Ccb for this target if it * returned 'check condition'. * If this command is a request-sense and there is valid sense data * from the last completed Ccb, return it immediately. */ lock(&ctlr->cachelock); if(ccb = ctlr->cache[id]){ ctlr->cache[id] = nil; if(cmd[0] == 0x03 && ccb->sdstat == STcheck && lun == ((ccb->cs[1]>>5) & 0x07)){ unlock(&ctlr->cachelock); if(dbytes){ sense = &ccb->cs[ccb->cdblen]; n = 8+sense[7]; if(n > *dbytes) n = *dbytes; memmove(data, sense, n); *dbytes = n; } ccbfree(ctlr, (Ccb*)ccb); return STok; } } unlock(&ctlr->cachelock); if(ccb == nil) ccb = ccballoc(ctlr); /* * Check if the transfer is to memory above the 24-bit limit the * controller can address. If it is, try to allocate a temporary * buffer as a staging area. */ if(dbytes) n = *dbytes; else n = 0; if(n && !PADDR24(data, n)){ bb = bballoc(ctlr); bb->data = data; if(rw == SCSIwrite) memmove(bb->buf, data, n); data = bb->buf; } else bb = nil; /* * Fill in the ccb. */ ccb->opcode = Ordl; ccb->datadir = (id<<5)|lun; if(n == 0) ccb->datadir |= CCBdataout|CCBdatain; else if(rw == SCSIread) ccb->datadir |= CCBdatain; else ccb->datadir |= CCBdataout; ccb->cdblen = cbytes; ccb->senselen = 0xFF; ccb->datalen[0] = n>>16; ccb->datalen[1] = n>>8; ccb->datalen[2] = n; p = PADDR(data); ccb->dataptr[0] = p>>16; ccb->dataptr[1] = p>>8; ccb->dataptr[2] = p; ccb->linkptr[0] = ccb->linkptr[1] = ccb->linkptr[2] = 0; ccb->linkid = 0; ccb->btstat = ccb->sdstat = 0; ccb->reserved[0] = ccb->reserved[1] = 0; memmove(ccb->cs, cmd, cbytes); /* * 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>>16; mb->ccb[1] = p>>8; mb->ccb[2] = p; 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, done24, 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]<<16; d |= ccb->datalen[1]<<8; d |= ccb->datalen[2]; if(ccb->cs[0] == 0x25 && sdstat == STok) d = 0; n -= d; if(dbytes) *dbytes = n; /* * Tidy things up if a staging area was used for the data, */ if(bb != nil){ if(sdstat == STok && btstat == 0 && rw == SCSIread) memmove(bb->data, data, n); bbfree(ctlr, bb); } /* * 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 voidinterrupt24(Ureg*, void* arg){ Ctlr *ctlr; ulong port; uchar rinterrupt, rstatus; Mbox24 *mb, *mbox; Ccb24 *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[0]<<16) |(mbox->ccb[1]<<8) |mbox->ccb[2]); mbox->code = 0; ccb->done = 1; wakeup(ccb); ctlr->mbix++; if(ctlr->mbix >= NMbox+NMbox) ctlr->mbix = NMbox; } outb(port+Rcontrol, Rint);}static intdone32(void* arg){ return ((Ccb32*)arg)->done;}static intscsiio32(Target* t, int rw, uchar* cmd, int cbytes, void* data, int* dbytes){ Ctlr *ctlr; Ccb32 *ccb; Mbox32 *mb; ulong p; int d, id, n, btstat, sdstat; uchar lun; if((ctlr = ctlrxx[t->ctlrno]) == nil || ctlr->port == 0) return STharderr; id = t->targetno; if(ctlr->id == id) return STownid; lun = (cmd[1]>>5) & 0x07; /* * Ctlr->cache holds the last completed Ccb for this target if it * returned 'check condition'. * If this command is a request-sense and there is valid sense data * from the last completed Ccb, return it immediately. */ lock(&ctlr->cachelock); if(ccb = ctlr->cache[id]){ ctlr->cache[id] = nil; if(cmd[0] == 0x03 && ccb->sdstat == STcheck && lun == (ccb->luntag & 0x07)){ unlock(&ctlr->cachelock); if(dbytes){ n = 8+ccb->sense[7]; if(n > *dbytes) n = *dbytes; memmove(data, ccb->sense, n); *dbytes = n; } ccbfree(ctlr, (Ccb*)ccb); return STok; } } unlock(&ctlr->cachelock); if(ccb == nil) ccb = ccballoc(ctlr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -