From 4b04b212e5c631597a1f0954ac2e88a70967c96b Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Thu, 8 Sep 1994 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1994-09-08 --- pc/dat.h | 15 ++ pc/fns.h | 9 ++ pc/io.h | 9 +- pc/scsi.c | 355 +++++++++++++++++++++++++++++++++++++++------- port/devns16552.c | 52 +++++-- port/portdat.h | 2 + 6 files changed, 381 insertions(+), 61 deletions(-) diff --git a/pc/dat.h b/pc/dat.h index 4019b6d3918219148ddc8d7a3dd490c7aecca8fe..00e0c6b244c81b6aa6712a8c8ca86d30f84f0758 100644 --- a/pc/dat.h +++ b/pc/dat.h @@ -187,6 +187,21 @@ struct ISAConf { uchar ea[6]; }; +enum { + MaxScsi = 4, + NTarget = 8, +}; + +struct Target { + int ctlrno; + int target; + uchar* inq; + uchar* scratch; + + int ok; +}; +typedef int (*Scsiio)(Target*, int, uchar*, int, void*, int*); + #define MAXPCMCIA 8 /* maximum number of PCMCIA cards */ #define BOOTLINE ((char *)0x80000100) /* bootline passed by boot program */ diff --git a/pc/fns.h b/pc/fns.h index 69f058f51f4ecc3944223b12f3b9ec92fd11c6c1..1fad7cb51c9a9884fcbb07e3f0b7a346a97680e6 100644 --- a/pc/fns.h +++ b/pc/fns.h @@ -118,3 +118,12 @@ Block* iallocb(int); void* ifroute(ulong); ulong ifunroute(ulong); void parseip(char*, char*); + +#define scsialloc(n) mallocz((n)+512, 0) +#define scsifree(p) free(p) + +void addscsicard(char*, Scsiio (*)(int, ISAConf*)); +int scsistart(Target*, char, int); +int scsireqsense(Target*, char, int); +int scsicap(Target*, char, ulong*, ulong*); +int scsibio(Target*, char, int, void*, long, long, long); diff --git a/pc/io.h b/pc/io.h index 132dd0ddecc1f5c3da321f033c916dde645baf95..db8e54968b2ab7c8671260afdea28fe561fde02e 100644 --- a/pc/io.h +++ b/pc/io.h @@ -18,10 +18,17 @@ enum Floppyvec= Int0vec+6, /* floppy interrupts */ Parallelvec= Int0vec+7, /* parallel port interrupts */ Int1vec= Int0vec+8, - Ethervec= Int0vec+10, /* ethernet interrupt */ + Vector9= Int0vec+9, /* unassigned */ + Vector10= Int0vec+10, /* unassigned, usually ethernet */ + Vector11= Int0vec+11, /* unassigned, usually scsi */ Mousevec= Int0vec+12, /* mouse interrupt */ Matherr2vec= Int0vec+13, /* math coprocessor */ Hardvec= Int0vec+14, /* hard disk */ + Vector15= Int0vec+15, /* unassigned */ Syscallvec= 64, }; + +enum { + MaxEISA = 16, +}; diff --git a/pc/scsi.c b/pc/scsi.c index c2bc14283c34ac333000776fe6869683850726be..f5c97fe35577e48d3da80d9a5d1cf73626e93539 100644 --- a/pc/scsi.c +++ b/pc/scsi.c @@ -4,81 +4,340 @@ #include "dat.h" #include "fns.h" #include "io.h" +#include "ureg.h" #include "../port/error.h" +#include "../port/netif.h" -/* - * Known to devscsi.c. -int scsidebugs[8]; -int scsiownid = CtlrID; - */ +enum { + Ninq = 255, + Nscratch = 255, + + CMDreqsense = 0x03, + CMDinquire = 0x12, +}; + +typedef struct { + ISAConf; + Scsiio io; + + Target target[NTarget]; +} Ctlr; + +static Ctlr *scsi[MaxScsi]; + +static struct { + char *type; + Scsiio (*reset)(int, ISAConf*); +} cards[MaxScsi+1]; void -initscsi(void) +addscsicard(char *t, Scsiio (*r)(int, ISAConf*)) { -} + static int ncard; -/* - * 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; + if(ncard == MaxScsi) + panic("too many scsi cards\n"); + cards[ncard].type = t; + cards[ncard].reset = r; + ncard++; +} void -scsibufreset(ulong datasize) +scsireset(void) +{ + Ctlr *ctlr; + int ctlrno, n, t; + + for(ctlr = 0, ctlrno = 0; ctlrno < MaxScsi; ctlrno++){ + if(ctlr == 0) + ctlr = malloc(sizeof(Ctlr)); + memset(ctlr, 0, sizeof(Ctlr)); + if(isaconfig("scsi", ctlrno, ctlr) == 0) + continue; + for(n = 0; cards[n].type; n++){ + if(strcmp(cards[n].type, ctlr->type)) + continue; + if((ctlr->io = (*cards[n].reset)(ctlrno, ctlr)) == 0) + break; + + print("scsi%d: %s: port %lux irq %d addr %lux size %d\n", + ctlrno, ctlr->type, ctlr->port, + ctlr->irq, ctlr->mem, ctlr->size); + + for(t = 0; t < NTarget; t++){ + ctlr->target[t].ctlrno = ctlrno; + ctlr->target[t].target = t; + ctlr->target[t].inq = xalloc(Ninq); + ctlr->target[t].scratch = xalloc(Nscratch); + } + + scsi[ctlrno] = ctlr; + ctlr = 0; + break; + } + } + if(ctlr) + free(ctlr); +} + +int +scsiexec(Target *tp, int rw, uchar *cmd, int cbytes, void *data, int *dbytes) { - bufdatasize = datasize; + return (*scsi[tp->ctlrno]->io)(tp, rw, cmd, cbytes, data, dbytes); } -Scsibuf * -scsibuf(void) +Target* +scsiunit(int ctlr, int unit) { - Scsibuf *b; + Target *t; - b = smalloc(sizeof(*b)); - b->virt = smalloc(bufdatasize); - b->phys = (void *)(PADDR(b->virt)); - return b; + if(ctlr < 0 || ctlr >= MaxScsi || scsi[ctlr] == 0) + return 0; + if(unit < 0 || unit >= NTarget) + return 0; + t = &scsi[ctlr]->target[unit]; + if(t->ok == 0) + return 0; + return t; } -void -scsifree(Scsibuf *b) +static void +scsiprobe(Ctlr *ctlr) { - free(b->virt); - free(b); + Target *tp; + uchar cmd[6]; + int i, s, nbytes; + + for(i = 0; i < NTarget; i++) { + tp = &ctlr->target[i]; + + /* + * Test unit ready + */ + memset(cmd, 0, sizeof(cmd)); + s = scsiexec(tp, SCSIread, cmd, sizeof(cmd), 0, 0); + if(s < 0) + continue; + + /* + * Determine if the drive exists and is not ready or + * is simply not responding + */ + if((s = scsireqsense(tp, 0, 0)) != STok){ + print("scsi%d: unit %d unavailable, status %d\n", tp->ctlrno, i, s); + continue; + } + + /* + * Inquire to find out what the device is + * Drivers then use the result to attach to targets + */ + memset(tp->inq, 0, Ninq); + cmd[0] = CMDinquire; + cmd[4] = Ninq; + nbytes = Ninq; + s = scsiexec(tp, SCSIread, cmd, sizeof(cmd), tp->inq, &nbytes); + if(s < 0) { + print("scsi%d: unit %d inquire failed, status %d\n", tp->ctlrno, i, s); + continue; + } + print("scsi%d: unit %d %s\n", tp->ctlrno, i, tp->inq+8); + tp->ok = 1; + } } -/* - * Hack for devvid - */ -Scsibuf * -scsialloc(ulong n) +static void +inventory(void) { - Scsibuf *b; + int i; + static Lock ilock; + static int inited; + + lock(&ilock); + if(inited) { + unlock(&ilock); + return; + } + inited = 1; + unlock(&ilock); - b = smalloc(sizeof(*b)); - b->virt = smalloc(n); - b->phys = (void *)(PADDR(b->virt)); - return b; + for(i = 0; i < MaxScsi; i++){ + if(scsi[i]) + scsiprobe(scsi[i]); + } } -extern int (*aha1542reset(void))(Scsi*, int); -extern int (*ultra14freset(void))(Scsi*, int); +int +scsiinv(int devno, int type, Target **rt, uchar **inq, char *id) +{ + Target *t; + int ctlr, unit; + + inventory(); -static int (*exec)(Scsi*, int); + for(;;){ + ctlr = devno/NTarget; + unit = devno%NTarget; + if(ctlr >= MaxScsi || scsi[ctlr] == 0) + return -1; + + t = &scsi[ctlr]->target[unit]; + devno++; + if(t->ok && (t->inq[0]&0x0F) == type){ + *rt = t; + *inq = t->inq; + sprint(id, "scsi%d: unit %d", ctlr, unit); + return devno; + } + } + return -1; +} int -scsiexec(Scsi *p, int rflag) +scsistart(Target *t, char lun, int s) { - if(exec == 0) - error(Enonexist); - return (*exec)(p, rflag); + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = 0x1b; + cmd[1] = lun<<5; + cmd[4] = s ? 1 : 0; + return scsiexec(t, SCSIread, cmd, sizeof(cmd), 0, 0); } -void -resetscsi(void) +int +scsicap(Target *t, char lun, ulong *size, ulong *bsize) { - if(exec = aha1542reset()) - return; - exec = ultra14freset(); + int s, nbytes; + uchar cmd[10], *d; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = 0x25; + cmd[1] = lun<<5; + + d = malloc(8); + if(d == 0) + return -1; + + nbytes = 8; + s = scsiexec(t, SCSIread, cmd, sizeof(cmd), d, &nbytes); + if(s < 0) { + free(d); + return s; + } + *size = (d[0]<<24)|(d[1]<<16)|(d[2]<<8)|(d[3]<<0); + *bsize = (d[4]<<24)|(d[5]<<16)|(d[6]<<8)|(d[7]<<0); + free(d); + return 0; +} + +int +scsibio(Target *t, char lun, int dir, void *b, long n, long bsize, long bno) +{ + uchar cmd[10]; + int s, cdbsiz, nbytes; + + memset(cmd, 0, sizeof cmd); + if(bno <= 0x1fffff && n < 256) { + cmd[0] = 0x0A; + if(dir == SCSIread) + cmd[0] = 0x08; + cmd[1] = (lun<<5) | bno >> 16; + cmd[2] = bno >> 8; + cmd[3] = bno; + cmd[4] = n; + cdbsiz = 6; + } + else { + cmd[0] = 0x2A; + if(dir == SCSIread) + cmd[0] = 0x28; + cmd[1] = (lun<<5); + cmd[2] = bno >> 24; + cmd[3] = bno >> 16; + cmd[4] = bno >> 8; + cmd[5] = bno; + cmd[7] = n>>8; + cmd[8] = n; + cdbsiz = 10; + } + nbytes = n*bsize; + s = scsiexec(t, dir, cmd, cdbsiz, b, &nbytes); + if(s < 0) { + scsireqsense(t, lun, 0); + return -1; + } + return nbytes; } + +static char *key[] = +{ + "no sense", + "recovered error", + "not ready", + "medium error", + "hardware error", + "illegal request", + "unit attention", + "data protect", + "blank check", + "vendor specific", + "copy aborted", + "aborted command", + "equal", + "volume overflow", + "miscompare", + "reserved" +}; + +int +scsireqsense(Target *tp, char lun, int quiet) +{ + char *s; + int sr, try, nbytes; + uchar cmd[6], *sense; + + sense = tp->scratch; + + for(try = 0; try < 5; try++) { + memset(cmd, 0, sizeof(cmd)); + cmd[0] = CMDreqsense; + cmd[1] = lun<<5; + cmd[4] = Nscratch; + memset(sense, 0, sizeof(sense)); + + nbytes = Nscratch; + sr = scsiexec(tp, SCSIread, cmd, sizeof(cmd), sense, &nbytes); + if(sr != STok) + return sr; + + /* + * Unit attention. We can handle that. + */ + if((sense[2] & 0x0F) == 0x00 || (sense[2] & 0x0F) == 0x06) + return STok; + + /* + * Recovered error. Why bother telling me. + */ + if((sense[2] & 0x0F) == 0x01) + return STok; + + /* + * Unit is becoming ready + */ + if(sense[12] != 0x04 || sense[13] != 0x01) + break; + + delay(5000); + } + + if(quiet) + return STcheck; + + s = key[sense[2]&0xf]; + print("scsi%d: unit %d reqsense: '%s' code #%2.2ux #%2.2ux\n", + tp->ctlrno, tp->target, s, sense[12], sense[13]); + return STcheck; +} + diff --git a/port/devns16552.c b/port/devns16552.c index bceee69920eff9ce1faa0d686879affb7712ab9e..3425bad367a058c08a0412e104c3a0183b8bb22c 100644 --- a/port/devns16552.c +++ b/port/devns16552.c @@ -228,26 +228,28 @@ ns16552break(Uart *p, int ms) void ns16552fifoon(Uart *p) { - ulong i, x, c; + ulong i, x; if(p->nofifo) return; x = splhi(); - /* empty buffer */ - c = 0; /* c is to to avoid an unused warning */ - for(i = 0; i < 16; i++) - c = uartrdreg(p, Data); - USED(c); - ns16552intr(p->dev); + /* reset fifos */ + uartwrreg(p, Fifoctl, Fclear); + /* empty buffer and interrupt conditions */ + for(i = 0; i < 16; i++){ + if(uartrdreg(p, Istat)) + ; + if(uartrdreg(p, Data)) + ; + } + /* turn on fifo */ p->fifoon = 1; uartwrreg(p, Fifoctl, Fena|Ftrig); - ns16552intr(p->dev); - USED(c); if((p->istat & Fenabd) == 0){ /* didn't work, must be an earlier chip type */ p->nofifo = 1; @@ -482,6 +484,22 @@ ns16552special(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, p->opens++; } +/* + * reset the interface + */ +void +ns16552shake(Uart *p) +{ + int xonoff, modem; + + xonoff = p->xonoff; + modem = p->modem; + ns16552disable(p); + ns16552enable(p); + p->xonoff = xonoff; + ns16552mflow(p, modem); +} + /* * handle an interrupt to a single uart */ @@ -489,13 +507,23 @@ void ns16552intr(int dev) { uchar ch; - int s, l, loops; + int s, l, loops, shakes; Uart *p = uart[dev]; + shakes = 0; for(loops = 0;; loops++){ - if(loops > 100000) - panic("ns16552intr"); p->istat = s = uartrdreg(p, Istat); + if(loops > 50000){ + if(shakes++ < 3){ + /* try resetting the interface */ + ns16552shake(p); + continue; + } + print("lstat %ux mstat %ux istat %ux iena %ux ferr %d oerr %d", + uartrdreg(p, Lstat), uartrdreg(p, Mstat), + s, uartrdreg(p, Iena), p->frame, p->overrun); + panic("ns16552intr"); + } switch(s&0x3f){ case 6: /* receiver line status */ l = uartrdreg(p, Lstat); diff --git a/port/portdat.h b/port/portdat.h index 17212e3a4e759d3d106609ffe92d83c74b0a2dad..476bbf1343b4e52e20390098e93334e44db3f383 100644 --- a/port/portdat.h +++ b/port/portdat.h @@ -667,6 +667,8 @@ enum /* Things scsi */ enum { + STtimeout =-3, + STownid =-2, STharderr =-1, /* Command status error returns */ STok = 0, STcheck = 2,