@@ 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;
+}
+
@@ 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;
@@ 483,19 485,45 @@ ns16552special(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*,
}
/*
+ * 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
*/
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);