From 164fd3484a38879c422ecba4abf692891bdfe0fd Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Thu, 14 Jan 1993 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1993-01-14 --- pc/adaptec.c | 548 +++++++++++++++++++++++++++++++++++++++++++++++++++ pc/devhard.c | 2 +- pc/main.c | 2 +- 3 files changed, 550 insertions(+), 2 deletions(-) create mode 100644 pc/adaptec.c diff --git a/pc/adaptec.c b/pc/adaptec.c new file mode 100644 index 0000000000000000000000000000000000000000..bd2ae34647a399efcda31b90569266fec59dbf9a --- /dev/null +++ b/pc/adaptec.c @@ -0,0 +1,548 @@ +/* + * Adaptec AHA-154[02]B Intelligent Host Adapter. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "ureg.h" + +/* + * Uniprocessors can't (don't need to) lock. + */ +#define lock(l) +#define unlock(l) + +enum { + NCtlr = 1, + NTarget = 8, /* targets per controller */ + + Port = 0x330, /* factory defaults: I/O port */ + CtlrID = 7, /* adapter SCSI id */ + Irq = 11, /* interrupt request level */ +}; + +enum { /* registers */ + Rc = 0, /* WO: control */ + Rs = 0, /* RO: status */ + Rcdo = 1, /* WO: command/data out */ + Rdi = 1, /* RO: data in */ + Rif = 2, /* RO: interrupt flags */ +}; + +enum { /* Rc */ + Scrst = 0x10, /* SCSI bus reset */ + Irst = 0x20, /* interrupt reset */ + Srst = 0x40, /* soft reset */ + Hrst = 0x80, /* hard reset */ +}; + +enum { /* Rs */ + Invdcmd = 0x01, /* invalid host adapter command */ + Df = 0x04, /* data in port full */ + Cdf = 0x08, /* command/data port full */ + Idle = 0x10, /* SCSI host adapter idle */ + Init = 0x20, /* mailbox initialisation required */ + Diagf = 0x40, /* internal diagnostic failure */ + Stst = 0x80, /* self testing in progress */ +}; + +enum { /* Rcdo */ + Cnop = 0x00, /* no operation */ + Cmbinit = 0x01, /* mailbox initialisation */ + Cstart = 0x02, /* start SCSI command */ + Cbios = 0x03, /* start PC/AT BIOS command */ + Cinquiry = 0x04, /* adapter inquiry */ + Cmboie = 0x05, /* enable mailbox out available interrupt */ + Cselection = 0x06, /* set selection timeout */ + Cbuson = 0x07, /* set bus-on time */ + Cbusoff = 0x08, /* set bus-off time */ + Ctransfer = 0x09, /* set transfer speed */ + Cdevices = 0x0A, /* return installed devices */ + Cconfiguration = 0x0B, /* return configuration data */ + Ctenable = 0x0C, /* enable target mode */ + Csetup = 0x0D, /* return setup data */ + Cwbuff = 0x1A, /* write adapter channel 2 buffer */ + Crbuff = 0x1B, /* read adapter channel 2 buffer */ + Cwfifo = 0x1C, /* write adapter FIFO buffer */ + Crfifo = 0x1D, /* read adapter FIFO buffer */ + Cecho = 0x1F, /* ECHO command data */ + Cdiag = 0x20, /* adapter diagnostic */ + Coptions = 0x21, /* set host adapter options */ +}; + +enum { /* Rif */ + Mbif = 0x01, /* mailbox in full */ + Mboa = 0x02, /* mailbox out available */ + Hacc = 0x04, /* host adapter command complete */ + Scrd = 0x08, /* SCSI reset detected */ + Ai = 0x80, /* any interrupt */ +}; + +typedef struct { + uchar cmd; /* command */ + uchar msb; /* CCB pointer MSB */ + uchar mid; /* CCB pointer MID */ + uchar lsb; /* CCB pointer LSB */ +} Mbox; + +enum { /* mailbox commands */ + Mbfree = 0x00, /* mailbox is free */ + + Mbostart = 0x01, /* start SCSI or adapter command */ + Mboabort = 0x02, /* abort SCSI or adapter command */ + + Mbiok = 0x01, /* CCB completed without error */ + Mbiabort = 0x02, /* CCB aborted by host */ + Mbinx = 0x03, /* aborted CCB not found */ + Mbierror = 0x04, /* CCB completed with error */ +}; + +typedef struct { + uchar op; /* command control block operation code */ + uchar ctl; /* address and direction control */ + uchar cmdlen; /* SCSI command length */ + uchar reqlen; /* request sense allocation length */ + uchar datalen[3]; /* data length (MSB, MID, LSB) */ + uchar dataptr[3]; /* data pointer (MSB, MID, LSB) */ + uchar linkptr[3]; /* link pointer (MSB, MID, LSB) */ + uchar linkid; /* command linking identifier */ + uchar hastat; /* host adapter status */ + uchar tarstat; /* target device status */ + uchar reserved[2]; + uchar cs[12+0xFF]; /* SCSI command and sense bytes */ +} Ccb; + +enum { /* op */ + OInitiator = 0x00, /* initiator CCB */ + OTarget = 0x01, /* target CCB */ + Osg = 0x02, /* initiator CCB with scatter/gather */ + Ordl = 0x03, /* initiator CCB, residual data length returned */ + Osgrdl = 0x04, /* initiator CCB, both of the above */ + Obdr = 0x81, /* bus device reset */ +}; + +enum { /* ctl */ + CCBdatain = 0x08, /* inbound data transfer, length is checked */ + CCBdataout = 0x10, /* outbound data transfer, length is checked */ +}; + +enum { /* hastat */ + Eok = 0x00, /* no host adapter detected error */ + Etimeout = 0x11, /* selection timeout */ + Elength = 0x12, /* data over/under run */ + Ebusfree = 0x13, /* unexpected bus free */ + Ephase = 0x14, /* target bus phase sequence error */ + Eopcode = 0x16, /* invalid CCB opcode */ + Elink = 0x17, /* linked CCB does not have same LUN */ + Edirection = 0x18, /* invalid target direction received from host */ + Eduplicate = 0x19, /* duplicate CCB received in target mode */ + Esegment = 0x1A, /* invalid CCB or segment list parameter */ +}; + +enum { /* tarstat */ + Sgood = 0x00, /* good status */ + Scheck = 0x02, /* check status */ + Sbusy = 0x08, /* LUN busy */ +}; + +typedef struct Target Target; +typedef struct Ctlr Ctlr; + +struct Target { + QLock; + Rendez; + + Ccb ccb; + ulong paddr; /* physical address of ccb */ + uchar *sense; /* address of returned sense data */ + + int done; + Target *active; /* link on active list */ +}; + +struct Ctlr { + Lock; + + ulong port; /* I/O port */ + ulong id; /* adapter SCSI id */ + + uchar cmd[5]; /* adapter command out */ + uchar cmdlen; /* adapter command out length */ + uchar data[256]; /* adapter command data in */ + uchar datalen; /* adapter command data in length */ + + Mbox mb[NTarget+NTarget]; /* mailbox out + mailbox in */ + + Mbox *mbox; /* current mailbox out index into mb */ + Mbox *mbix; /* current mailbox in index into mb */ + + Target target[NTarget]; + Target *active; /* link on active list */ +}; + +static Ctlr softctlr[NCtlr]; + +/* + * Issue a command to the controller. The command and its length is + * contained in ctlr->cmd and ctlr->cmdlen. If any data is to be + * returned, ctlr->datalen should be non-0, and the returned data will + * be placed in ctlr->data. + * If we see Hacc set, bail out, we'll process + * the invalid command at interrupt time. + */ +static void +issue(Ctlr *ctlr) +{ + int len; + + len = 0; + while(len < ctlr->cmdlen){ + if((inb(ctlr->port+Rs) & Cdf) == 0){ + outb(ctlr->port+Rcdo, ctlr->cmd[len]); + len++; + } + + if(inb(ctlr->port+Rif) & Hacc) + return; + } + + if(ctlr->datalen){ + len = 0; + while(len < ctlr->datalen){ + if(inb(ctlr->port+Rs) & Df){ + ctlr->data[len] = inb(ctlr->port+Rdi); + len++; + } + + if(inb(ctlr->port+Rif) & Hacc) + return; + } + } +} + +static int +done(void *arg) +{ + return ((Target*)arg)->done; +} + +static int +scsiio(int bus, Scsi *p, int rw) +{ + Ctlr *ctlr; + Target *tp; + ushort status; + ulong len, s; + + /* + * Wait for the target to become free, + * then set it up. The Adaptec will allow us to + * queue multiple transactions per target, but + * gives no guarantee about ordering, so we just + * allow one per target. + */ + ctlr = &softctlr[bus]; + tp = &ctlr->target[p->target]; + + qlock(tp); + if(waserror()){ + qunlock(tp); + nexterror(); + } + + /* + * If this is a request-sense and we have valid sense data + * from the last command, return it immediately. + * A pox on these weird enum names and the WD33C93A status + * codes. + */ + if(p->cmd.base[0] == ScsiExtsens && tp->sense){ + len = 8+tp->sense[7]; + memmove(p->data.ptr, tp->sense, len); + p->data.ptr += len; + tp->sense = 0; + + qunlock(tp); + poperror(); + return 0x6000; + } + tp->sense = 0; + + /* + * Fill in the ccb. + */ + tp->ccb.op = Ordl; + tp->ccb.ctl = (p->target<<5)|p->lun; + + len = p->cmd.lim - p->cmd.base; + tp->ccb.cmdlen = len; + memmove(tp->ccb.cs, p->cmd.base, len); + + tp->ccb.reqlen = 0xFF; + + len = p->data.lim - p->data.base; + tp->ccb.datalen[0] = (len>>16) & 0xFF; + tp->ccb.datalen[1] = (len>>8) & 0xFF; + tp->ccb.datalen[2] = len; + if(len == 0) + tp->ccb.ctl |= CCBdataout|CCBdatain; + else if(rw == ScsiIn) + tp->ccb.ctl |= CCBdatain; + else + tp->ccb.ctl |= CCBdataout; + + len = PADDR(p->data.base); + tp->ccb.dataptr[0] = (len>>16) & 0xFF; + tp->ccb.dataptr[1] = (len>>8) & 0xFF; + tp->ccb.dataptr[2] = len; + + tp->ccb.linkptr[0] = tp->ccb.linkptr[1] = tp->ccb.linkptr[2] = 0; + tp->ccb.linkid = 0; + + tp->ccb.hastat = tp->ccb.tarstat = 0; + tp->ccb.reserved[0] = tp->ccb.reserved[1] = 0; + + /* + * Link the target onto the beginning of the + * ctlr active list and start the request. + * The interrupt routine has to be able to take + * requests off the queue in any order. + */ + s = splhi(); + lock(ctlr); + + tp->done = 0; + tp->active = ctlr->active; + ctlr->active = tp; + + ctlr->mbox->msb = (tp->paddr>>16) & 0xFF; + ctlr->mbox->mid = (tp->paddr>>8) & 0xFF; + ctlr->mbox->lsb = tp->paddr & 0xFF; + ctlr->mbox->cmd = Mbostart; + + ctlr->cmd[0] = Cstart; + ctlr->cmdlen = 1; + ctlr->datalen = 0; + issue(ctlr); + + ctlr->mbox++; + if(ctlr->mbox >= &ctlr->mb[NTarget]) + ctlr->mbox = ctlr->mb; + + unlock(ctlr); + splx(s); + + /* + * Wait for the request to complete + * and return the status. + */ + tsleep(tp, done, tp, 60*5*1000); + if(done(tp) == 0) + print("adaptec%d: timeout cmd=#%2.2ux\n", p->target, tp->ccb.cs[0]); + + if((status = (tp->ccb.hastat<<8)) == 0) + status = 0x6000; + status |= tp->ccb.tarstat; + len = (tp->ccb.datalen[0]<<16)|(tp->ccb.datalen[1]<<8)|tp->ccb.datalen[2]; + p->data.ptr = p->data.lim - len; + + /* + * If the command returned sense data, keep a note + * of where it is for a subsequent request-sense command. + */ + if(tp->ccb.tarstat == Scheck && tp->ccb.hastat == Eok) + tp->sense = &tp->ccb.cs[tp->ccb.cmdlen]; + + qunlock(tp); + poperror(); + + return status; +} + +int +scsiexec(Scsi *p, int rw) +{ + USED(rw); + + if(p->target == CtlrID) + return 0x6002; + + return p->status = scsiio(0, p, rw); +} + +static void +interrupt(Ureg *ur) +{ + Ctlr *ctlr = &softctlr[0]; + uchar rif, rs; + Target *tp, **l; + ulong paddr; + + USED(ur); + + lock(ctlr); + + /* + * Save and clear the interrupt(s). The only + * interrupts expected are Hacc, which we ignore, + * and Mbif which means something completed. + */ + rif = inb(ctlr->port+Rif); + rs = inb(ctlr->port+Rs); + outb(ctlr->port+Rc, Irst); + if(rif & ~(Ai|Hacc|Mbif)) + print("adaptec%d: interrupt #%2.2ux\n", 0, rif); + if((rif & Hacc) && (rs & Invdcmd)) + print("adaptec%d: invdcmd #%2.2ux, len %d\n", 0, ctlr->cmd[0], ctlr->cmdlen); + + /* + * Look for something in the mail. + * If there is, try to find the recipient from the + * ccb address, take it off the active list and + * wakeup whoever. + */ + while(ctlr->mbix->cmd){ + paddr = (ctlr->mbix->msb<<16)|(ctlr->mbix->mid<<8)|ctlr->mbix->lsb; + l = &ctlr->active; + for(tp = *l; tp; tp = tp->active){ + if(tp->paddr == paddr) + break; + l = &tp->active; + } + if(tp == 0) + panic("adaptec%d: no target for ccb #%lux\n", 0, paddr); + *l = tp->active; + + ctlr->mbix->cmd = 0; + + tp->done = 1; + wakeup(tp); + + ctlr->mbix++; + if(ctlr->mbix >= &ctlr->mb[NTarget+NTarget]) + ctlr->mbix = &ctlr->mb[NTarget]; + } + + unlock(ctlr); +} + +static void +reset(Ctlr *ctlr, ulong port, ulong id) +{ + ulong paddr; + int i; + + /* + * Initialise the software controller and set the board + * scanning the mailboxes. + * Need code here to tidy things up if we're + * resetting after being active. + */ + memset(ctlr, 0, sizeof(Ctlr)); + ctlr->port = port; + ctlr->id = id; + for(i = 0; i < NTarget; i++){ + ctlr->target[i].paddr = PADDR(&ctlr->target[i].ccb); + ctlr->mbox = ctlr->mb; + ctlr->mbix = &ctlr->mb[NTarget]; + } + + ctlr->cmd[0] = Cmbinit; + paddr = PADDR(ctlr->mb); + ctlr->cmd[1] = NTarget; + ctlr->cmd[2] = (paddr>>16) & 0xFF; + ctlr->cmd[3] = (paddr>>8) & 0xFF; + ctlr->cmd[4] = paddr & 0xFF; + ctlr->cmdlen = 5; + ctlr->datalen = 0; + issue(ctlr); +} + +void +resetscsi(void) +{ + uchar rs; + ulong port; + + /* + * For the moment assume the factory default settings. + */ + port = Port; + + /* + * Attempt to soft-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 an Adaptec or it's broken. + */ + outb(port+Rc, Srst|Scrst); + delay(500); + if((rs = inb(port+Rs)) != (Init|Idle)){ + print("adaptec%d: reset status #%2.2ux\n", 0, rs); + return; + } + + setvec(Int0vec+Irq, interrupt); + reset(&softctlr[0], port, CtlrID); +} + +/* + * Known to devscsi.c. + */ +int scsidebugs[8]; +int scsiownid = CtlrID; + +void +initscsi(void) +{ +} + +/* + * Quick hack. Need to do a better job of dynamic initialisation + * for machines with peculiar memory/cache restictions. + * Also, what about 16Mb address limit on the Adaptec? + */ +static ulong bufdatasize; + +void +scsibufreset(ulong datasize) +{ + bufdatasize = datasize; +} + +Scsibuf * +scsibuf(void) +{ + Scsibuf *b; + + b = smalloc(sizeof(*b)); + b->virt = smalloc(bufdatasize); + b->phys = (void *)(PADDR(b->virt)); + return b; +} + +void +scsifree(Scsibuf *b) +{ + free(b->virt); + free(b); +} + +/* + * Hack for devvid + */ +Scsibuf * +scsialloc(ulong n) +{ + Scsibuf *b; + + b = smalloc(sizeof(*b)); + b->virt = smalloc(n); + b->phys = (void *)(PADDR(b->virt)); + return b; +} diff --git a/pc/devhard.c b/pc/devhard.c index c31f428a304b35f052a0393976d0a188301f32cf..5a22656dc50d50d17eb11ae187829f4492aed3fb 100644 --- a/pc/devhard.c +++ b/pc/devhard.c @@ -233,7 +233,7 @@ hardattach(char *spec) qunlock(dp); poperror(); } - return devattach('w', spec); + return devattach('H', spec); } Chan* diff --git a/pc/main.c b/pc/main.c index 9ae53c199b0c62e696096f07e416c17f52a25949..7398b5273c762ee66cacb61322bdc591e700cda0 100644 --- a/pc/main.c +++ b/pc/main.c @@ -205,7 +205,7 @@ bootargs(ulong base) sprint(buf, "local!#f/fd%ddisk", atoi(cp+3)); av[ac++] = pusharg(buf); } else if(strncmp(cp, "hd!", 3) == 0){ - sprint(buf, "local!#w/hd%ddisk", atoi(cp+3)); + sprint(buf, "local!#h/hd%ddisk", atoi(cp+3)); av[ac++] = pusharg(buf); }