cons/ 755 0 0 0 11231562730 77445ustar00nemonemocons/Diffs 644 0 0 22221 11213215210 10725ustar00nemonemoThe actual change is devcons.c several queues in portdat are replaced by function interfaces in portfns Remaining files are the ones I had to change to use the new console interface. diff /n/dumpplanb/2009/0608/sys/src/9/port/devuart.c /sys/src/9/port/devuart.c 218,219c218,219 < kbdq = p->iq; < serialoq = p->oq; --- > addkbdq(p->iq, -1); > addconsdev(p->oq, uartputs, 2, 0); diff /n/dumpplanb/2009/0608/sys/src/9/port/portdat.h /sys/src/9/port/portdat.h 768,769d767 < extern Queue* kbdq; < extern Queue* kprintoq; 773d770 < extern Queue* serialoq; diff /n/dumpplanb/2009/0608/sys/src/9/port/rdb.c /sys/src/9/port/rdb.c 62,63c62 < serialoq = nil; /* turn off serial console */ < kprintoq = nil; /* turn off /dev/kprint if active */ --- > delconsdevs(); /* turn off serial console and kprint */ diff /n/dumpplanb/2009/0608/sys/src/9/port/devcons.c /sys/src/9/port/devcons.c 8d7 < 10a10,28 > enum > { > Nconsdevs = 64, /* max number of consoles */ > > /* Condevs flags */ > Ciprint = 2, /* call this fn from iprint */ > Cntorn = 4, /* change \n to \r\n */ > }; > > typedef struct Consdev Consdev; > > struct Consdev > { > Chan* c; /* external file */ > Queue* q; /* i/o queue, if any */ > void (*fn)(char*, int); /* i/o function when no queue */ > int flags; > }; > 14,19c32,33 < Queue* kbdq; /* unprocessed console input */ < Queue* lineq; /* processed console input */ < Queue* serialoq; /* serial console output */ < Queue* kprintoq; /* console output, for /dev/kprint */ < ulong kprintinuse; /* test and set whether /dev/kprint is open */ < int iprintscreenputs = 1; --- > static void cscreenputs(char*, int); > static void kmesgputs(char *, int); 20a35,53 > static Lock consdevslck; > static int nconsdevs = 3; > static Consdev consdevs[Nconsdevs] = /* keep this order */ > { > {nil, nil, kmesgputs, 0}, /* kmesg */ > {nil, nil, cscreenputs, Ciprint}, /* kprint */ > {nil, nil, uartputs, Ciprint|Cntorn}, /* serial */ > }; > > static int nkbdqs; > static int nkbdprocs; > static Queue* kbdqs[Nconsdevs]; > static int kbdprocs[Nconsdevs]; > static Queue* kbdq; /* unprocessed console input */ > static Queue* lineq; /* processed console input */ > static Queue* serialoq; /* serial console output */ > static Queue* kprintoq; /* console output, for /dev/kprint */ > static ulong kprintinuse; /* test and set whether /dev/kprint is open */ > 35c68,71 < /* a place to save up characters at interrupt time before dumping them in the queue */ --- > /* > * A place to save up characters at interrupt time > * before dumping them in the queue > */ 69a106,133 > /* To keep the rest of the kernel unware of new consdevs by now */ > static void > cscreenputs(char *s, int n) > { > if(screenputs != nil) > screenputs(s, n); > } > > int > addconsdev(Queue *q, void (*fn)(char*,int), int i, int flags) > { > Consdev *c; > > ilock(&consdevslck); > if(i < 0) > i = nconsdevs++; > else > flags |= consdevs[i].flags; > if(nconsdevs == Nconsdevs) > panic("Nconsdevs too small"); > c = &consdevs[i]; > c->flags = flags; > c->q = q; > c->fn = fn; > iunlock(&consdevslck); > return i; > } > 70a135,204 > delconsdevs(void) > { > nconsdevs = 2; /* throw away serial consoles and kprint */ > consdevs[1].q = nil; > } > > static void > conskbdqproc(void *a) > { > char buf[64]; > Queue *q; > int nr; > > q = a; > while((nr = qread(q, buf, sizeof(buf))) > 0) > qwrite(kbdq, buf, nr); > pexit("hangup", 1); > } > > static void > kickkbdq(void) > { > int i; > > if(up != nil && nkbdqs > 1 && nkbdprocs != nkbdqs){ > lock(&consdevslck); > if(nkbdprocs == nkbdqs){ > unlock(&consdevslck); > return; > } > for(i = 0; i < nkbdqs; i++) > if(kbdprocs[i] == 0){ > kbdprocs[i] = 1; > kproc("conskbdq", conskbdqproc, kbdqs[i]); > } > unlock(&consdevslck); > } > } > > int > addkbdq(Queue *q, int i) > { > int n; > > ilock(&consdevslck); > if(i < 0) > i = nkbdqs++; > if(nkbdqs == Nconsdevs) > panic("Nconsdevs too small"); > kbdqs[i] = q; > n = nkbdqs; > iunlock(&consdevslck); > switch(n){ > case 1: > /* if there's just one, pull directly from it. */ > kbdq = q; > break; > case 2: > /* later we'll merge bytes from all kbdqs into a single kbdq */ > kbdq = qopen(4*1024, 0, 0, 0); > if(kbdq == nil) > panic("no kbdq"); > /* fall */ > default: > kickkbdq(); > } > return i; > } > > void 82,83c216,221 < if(serialoq) < return qlen(serialoq) > 0; --- > int i; > Queue *q; > > for(i = 0; i < nconsdevs; i++) > if((q = consdevs[i].q) != nil && qlen(q) > 0) > return 1; 136a275,291 > static void > consputs(Consdev *c, char *s, int n, int usewrite) > { > Chan *cc; > Queue *q; > > if((cc = c->c) != nil && usewrite) > devtab[cc->type]->write(cc, s, n, 0); > else if((q = c->q) != nil && !qisclosed(q)) > if(usewrite) > qwrite(q, s, n); > else > qiwrite(q, s, n); > else if(c->fn != nil) > c->fn(s, n); > } > 145a301,303 > Consdev *c; > int i; > int len; 146a305 > char *s; 152,188c311,327 < /* < * how many different output devices do we need? < */ < kmesgputs(str, n); < < /* < * if someone is reading /dev/kprint, < * put the message there. < * if not and there's an attached bit mapped display, < * put the message there. < * < * if there's a serial line being used as a console, < * put the message there. < */ < if(kprintoq != nil && !qisclosed(kprintoq)){ < if(usewrite) < qwrite(kprintoq, str, n); < else < qiwrite(kprintoq, str, n); < }else if(screenputs != nil) < screenputs(str, n); < < if(serialoq == nil){ < uartputs(str, n); < return; < } < < while(n > 0) { < t = memchr(str, '\n', n); < if(t && !kbd.raw) { < m = t-str; < if(usewrite){ < qwrite(serialoq, str, m); < qwrite(serialoq, "\r\n", 2); < } else { < qiwrite(serialoq, str, m); < qiwrite(serialoq, "\r\n", 2); --- > for(i = 0; i < nconsdevs; i++){ > c = &consdevs[i]; > len = n; > s = str; > while(len > 0){ > t = nil; > if((c->flags&Cntorn) && !kbd.raw) > t = memchr(s, '\n', len); > if(t != nil && !kbd.raw){ > m = t-s; > consputs(c, s, m, usewrite); > consputs(c, "\r\n", 2, usewrite); > len -= m+1; > s = t+1; > }else{ > consputs(c, s, len, usewrite); > break; 190,197d328 < n -= m+1; < str = t+1; < } else { < if(usewrite) < qwrite(serialoq, str, n); < else < qiwrite(serialoq, str, n); < break; 252a384 > int i; 262,264c394,396 < if(screenputs != nil && iprintscreenputs) < screenputs(buf, n); < uartputs(buf, n); --- > for(i = 0; i < nconsdevs; i++) > if((consdevs[i].flags&Ciprint) != 0) > consdevs[i].fn(buf, n); 279c411 < kprintoq = nil; /* don't try to write to /dev/kprint */ --- > consdevs[1].q = nil; /* don't try to write to /dev/kprint */ 353,407d484 < echoscreen(char *buf, int n) < { < char *e, *p; < char ebuf[128]; < int x; < < p = ebuf; < e = ebuf + sizeof(ebuf) - 4; < while(n-- > 0){ < if(p >= e){ < screenputs(ebuf, p - ebuf); < p = ebuf; < } < x = *buf++; < if(x == 0x15){ < *p++ = '^'; < *p++ = 'U'; < *p++ = '\n'; < } else < *p++ = x; < } < if(p != ebuf) < screenputs(ebuf, p - ebuf); < } < < static void < echoserialoq(char *buf, int n) < { < char *e, *p; < char ebuf[128]; < int x; < < p = ebuf; < e = ebuf + sizeof(ebuf) - 4; < while(n-- > 0){ < if(p >= e){ < qiwrite(serialoq, ebuf, p - ebuf); < p = ebuf; < } < x = *buf++; < if(x == '\n'){ < *p++ = '\r'; < *p++ = '\n'; < } else if(x == 0x15){ < *p++ = '^'; < *p++ = 'U'; < *p++ = '\n'; < } else < *p++ = x; < } < if(p != ebuf) < qiwrite(serialoq, ebuf, p - ebuf); < } < < static void 484,491c561,564 < qproduce(kbdq, buf, n); < if(kbd.raw) < return; < kmesgputs(buf, n); < if(screenputs != nil) < echoscreen(buf, n); < if(serialoq) < echoserialoq(buf, n); --- > if(kbdq != nil) > qproduce(kbdq, buf, n); > if(kbd.raw == 0) > putstrn(buf, n); 667a741 > kickkbdq(); 709a784 > consdevs[1].q = kprintoq; diff /n/dumpplanb/2009/0608/sys/src/9/port/portfns.h /sys/src/9/port/portfns.h 5a6,7 > int addconsdev(Queue*, void (*fn)(char*,int), int, int); > int addkbdq(Queue*, int); 57a60 > void delconsdevs(void); diff /n/dumpplanb/2009/0608/sys/src/9/pc/kbd.c /sys/src/9/pc/kbd.c 11a12,13 > static Queue *kbdq; > 609c611 < --- > addkbdq(kbdq, -1); diff /n/dumpplanb/2009/0608/sys/src/9/pc/vga.c /sys/src/9/pc/vga.c 23a24 > static int usingvgascreenputs; 199,200c200,201 < < screenputs = vgascreenputs; --- > usingvgascreenputs = 1; > addconsdev(nil, vgascreenputs, 1, 0); 253c254 < if(scr->vaddr == nil || screenputs != vgascreenputs) --- > if(scr->vaddr == nil || usingvgascreenputs == 0) diff /n/dumpplanb/2009/0608/sys/src/9/pc/cga.c /sys/src/9/pc/cga.c 126c126 < screenputs = cgascreenputs; --- > addconsdev(nil, cgascreenputs, 1, 0); diff /n/dumpplanb/2009/0608/sys/src/9/pc/fns.h /sys/src/9/pc/fns.h 148d147 < void (*screenputs)(char*, int); diff /n/dumpplanb/2009/0608/sys/src/9/pc/sd53c8xx.c /sys/src/9/pc/sd53c8xx.c 363c363 < screenputs(debugbuf, endp - debugbuf); --- > putstrn(debugbuf, endp - debugbuf); diff /n/dumpplanb/2009/0608/sys/src/9/pc/main.c /sys/src/9/pc/main.c 696c696 < serialoq = nil; --- > delconsdevs(); cons/cga.c 644 0 0 3475 11231562730 10653ustar00nemonemo#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" enum { Black, Blue, Green, Cyan, Red, Magenta, Brown, Grey, Bright = 0x08, Blinking = 0x80, Yellow = Bright|Brown, White = Bright|Grey, }; enum { Width = 80*2, Height = 25, Attr = (Black<<4)|Grey, /* high nibble background * low foreground */ }; #define CGASCREENBASE ((uchar*)KADDR(0xB8000)) static int cgapos; static Lock cgascreenlock; static uchar cgaregr(int index) { outb(0x3D4, index); return inb(0x3D4+1) & 0xFF; } static void cgaregw(int index, int data) { outb(0x3D4, index); outb(0x3D4+1, data); } static void movecursor(void) { cgaregw(0x0E, (cgapos/2>>8) & 0xFF); cgaregw(0x0F, cgapos/2 & 0xFF); CGASCREENBASE[cgapos+1] = Attr; } static void cgascreenputc(int c) { int i; uchar *p; if(c == '\n'){ cgapos = cgapos/Width; cgapos = (cgapos+1)*Width; } else if(c == '\t'){ i = 8 - ((cgapos/2)&7); while(i-->0) cgascreenputc(' '); } else if(c == '\b'){ if(cgapos >= 2) cgapos -= 2; cgascreenputc(' '); cgapos -= 2; } else{ CGASCREENBASE[cgapos++] = c; CGASCREENBASE[cgapos++] = Attr; } if(cgapos >= Width*Height){ memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1)); p = &CGASCREENBASE[Width*(Height-1)]; for(i=0; i 0) cgascreenputc(*s++); unlock(&cgascreenlock); } void screeninit(void) { cgapos = cgaregr(0x0E)<<8; cgapos |= cgaregr(0x0F); cgapos *= 2; addconsdev(nil, cgascreenputs, 1, 0); } mesgputs, 0}, /* kmesg */ > {nil, nil, cscreenputs, Ciprint}, /* kprint */ > {nil, nil, uartputs, Ciprint|Cntorn}, /* serial */ > }; > > static int nkbdqs; > static int nkbdprocs; > static Quecons/devcons.c 644 0 0 60171 11231562730 11576ustar00nemonemo#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "pool.h" #include enum { Nconsdevs = 64, /* max number of consoles */ /* Condevs flags */ Ciprint = 2, /* call this fn from iprint */ Cntorn = 4, /* change \n to \r\n */ }; typedef struct Consdev Consdev; struct Consdev { Chan* c; /* external file */ Queue* q; /* i/o queue, if any */ void (*fn)(char*, int); /* i/o function when no queue */ int flags; }; void (*consdebug)(void) = nil; void (*screenputs)(char*, int) = nil; static void cscreenputs(char*, int); static void kmesgputs(char *, int); static Lock consdevslck; static int nconsdevs = 3; static Consdev consdevs[Nconsdevs] = /* keep this order */ { {nil, nil, kmesgputs, 0}, /* kmesg */ {nil, nil, cscreenputs, Ciprint}, /* kprint */ {nil, nil, uartputs, Ciprint|Cntorn}, /* serial */ }; static int nkbdqs; static int nkbdprocs; static Queue* kbdqs[Nconsdevs]; static int kbdprocs[Nconsdevs]; static Queue* kbdq; /* unprocessed console input */ static Queue* lineq; /* processed console input */ static Queue* serialoq; /* serial console output */ static Queue* kprintoq; /* console output, for /dev/kprint */ static ulong kprintinuse; /* test and set whether /dev/kprint is open */ int panicking; static struct { QLock; int raw; /* true if we shouldn't process input */ Ref ctl; /* number of opens to the control file */ int x; /* index into line */ char line[1024]; /* current input line */ int count; int ctlpoff; /* * A place to save up characters at interrupt time * before dumping them in the queue */ Lock lockputc; char istage[1024]; char *iw; char *ir; char *ie; } kbd = { .iw = kbd.istage, .ir = kbd.istage, .ie = kbd.istage + sizeof(kbd.istage), }; char *sysname; vlong fasthz; static void seedrand(void); static int readtime(ulong, char*, int); static int readbintime(char*, int); static int writetime(char*, int); static int writebintime(char*, int); enum { CMhalt, CMreboot, CMpanic, }; Cmdtab rebootmsg[] = { CMhalt, "halt", 1, CMreboot, "reboot", 0, CMpanic, "panic", 0, }; /* To keep the rest of the kernel unware of new consdevs by now */ static void cscreenputs(char *s, int n) { if(screenputs != nil) screenputs(s, n); } int addconsdev(Queue *q, void (*fn)(char*,int), int i, int flags) { Consdev *c; ilock(&consdevslck); if(i < 0) i = nconsdevs; else flags |= consdevs[i].flags; if(nconsdevs == Nconsdevs) panic("Nconsdevs too small"); c = &consdevs[i]; c->flags = flags; c->q = q; c->fn = fn; if(i == nconsdevs) nconsdevs++; iunlock(&consdevslck); return i; } void delconsdevs(void) { nconsdevs = 2; /* throw away serial consoles and kprint */ consdevs[1].q = nil; } static void conskbdqproc(void *a) { char buf[64]; Queue *q; int nr; q = a; while((nr = qread(q, buf, sizeof(buf))) > 0) qwrite(kbdq, buf, nr); pexit("hangup", 1); } static void kickkbdq(void) { int i; if(up != nil && nkbdqs > 1 && nkbdprocs != nkbdqs){ lock(&consdevslck); if(nkbdprocs == nkbdqs){ unlock(&consdevslck); return; } for(i = 0; i < nkbdqs; i++) if(kbdprocs[i] == 0){ kbdprocs[i] = 1; kproc("conskbdq", conskbdqproc, kbdqs[i]); } unlock(&consdevslck); } } int addkbdq(Queue *q, int i) { int n; ilock(&consdevslck); if(i < 0) i = nkbdqs++; if(nkbdqs == Nconsdevs) panic("Nconsdevs too small"); kbdqs[i] = q; n = nkbdqs; iunlock(&consdevslck); switch(n){ case 1: /* if there's just one, pull directly from it. */ kbdq = q; break; case 2: /* later we'll merge bytes from all kbdqs into a single kbdq */ kbdq = qopen(4*1024, 0, 0, 0); if(kbdq == nil) panic("no kbdq"); /* fall */ default: kickkbdq(); } return i; } void printinit(void) { lineq = qopen(2*1024, 0, nil, nil); if(lineq == nil) panic("printinit"); qnoblock(lineq, 1); } int consactive(void) { int i; Queue *q; for(i = 0; i < nconsdevs; i++) if((q = consdevs[i].q) != nil && qlen(q) > 0) return 1; return 0; } void prflush(void) { ulong now; now = m->ticks; while(consactive()) if(m->ticks - now >= HZ) break; } /* * Log console output so it can be retrieved via /dev/kmesg. * This is good for catching boot-time messages after the fact. */ struct { Lock lk; char buf[16384]; uint n; } kmesg; static void kmesgputs(char *str, int n) { uint nn, d; ilock(&kmesg.lk); /* take the tail of huge writes */ if(n > sizeof kmesg.buf){ d = n - sizeof kmesg.buf; str += d; n -= d; } /* slide the buffer down to make room */ nn = kmesg.n; if(nn + n >= sizeof kmesg.buf){ d = nn + n - sizeof kmesg.buf; if(d) memmove(kmesg.buf, kmesg.buf+d, sizeof kmesg.buf-d); nn -= d; } /* copy the data in */ memmove(kmesg.buf+nn, str, n); nn += n; kmesg.n = nn; iunlock(&kmesg.lk); } static void consputs(Consdev *c, char *s, int n, int usewrite) { Chan *cc; Queue *q; if((cc = c->c) != nil && usewrite) devtab[cc->type]->write(cc, s, n, 0); else if((q = c->q) != nil && !qisclosed(q)) if(usewrite) qwrite(q, s, n); else qiwrite(q, s, n); else if(c->fn != nil) c->fn(s, n); } /* * Print a string on the console. Convert \n to \r\n for serial * line consoles. Locking of the queues is left up to the screen * or uart code. Multi-line messages to serial consoles may get * interspersed with other messages. */ static void putstrn0(char *str, int n, int usewrite) { Consdev *c; int i; int len; int m; char *s; char *t; if(!islo()) usewrite = 0; for(i = 0; i < nconsdevs; i++){ c = &consdevs[i]; len = n; s = str; while(len > 0){ t = nil; if((c->flags&Cntorn) && !kbd.raw) t = memchr(s, '\n', len); if(t != nil && !kbd.raw){ m = t-s; consputs(c, s, m, usewrite); consputs(c, "\r\n", 2, usewrite); len -= m+1; s = t+1; }else{ consputs(c, s, len, usewrite); break; } } } } void putstrn(char *str, int n) { putstrn0(str, n, 0); } int noprint; int print(char *fmt, ...) { int n; va_list arg; char buf[PRINTSIZE]; if(noprint) return -1; va_start(arg, fmt); n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); putstrn(buf, n); return n; } /* * Want to interlock iprints to avoid interlaced output on * multiprocessor, but don't want to deadlock if one processor * dies during print and another has something important to say. * Make a good faith effort. */ static Lock iprintlock; static int iprintcanlock(Lock *l) { int i; for(i=0; i<1000; i++){ if(canlock(l)) return 1; if(l->m == MACHP(m->machno)) return 0; microdelay(100); } return 0; } int iprint(char *fmt, ...) { int i; int n, s, locked; va_list arg; char buf[PRINTSIZE]; s = splhi(); va_start(arg, fmt); n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); locked = iprintcanlock(&iprintlock); for(i = 0; i < nconsdevs; i++) if((consdevs[i].flags&Ciprint) != 0) consdevs[i].fn(buf, n); if(locked) unlock(&iprintlock); splx(s); return n; } void panic(char *fmt, ...) { int n, s; va_list arg; char buf[PRINTSIZE]; consdevs[1].q = nil; /* don't try to write to /dev/kprint */ if(panicking) for(;;); panicking = 1; s = splhi(); strcpy(buf, "panic: "); va_start(arg, fmt); n = vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf; va_end(arg); iprint("%s\n", buf); if(consdebug) (*consdebug)(); splx(s); prflush(); buf[n] = '\n'; putstrn(buf, n+1); dumpstack(); exit(1); } /* libmp at least contains a few calls to sysfatal; simulate with panic */ void sysfatal(char *fmt, ...) { char err[256]; va_list arg; va_start(arg, fmt); vseprint(err, err + sizeof err, fmt, arg); va_end(arg); panic("sysfatal: %s", err); } void _assert(char *fmt) { panic("assert failed at %#p: %s", getcallerpc(&fmt), fmt); } int pprint(char *fmt, ...) { int n; Chan *c; va_list arg; char buf[2*PRINTSIZE]; if(up == nil || up->fgrp == nil) return 0; c = up->fgrp->fd[2]; if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR)) return 0; n = snprint(buf, sizeof buf, "%s %lud: ", up->text, up->pid); va_start(arg, fmt); n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); if(waserror()) return 0; devtab[c->type]->write(c, buf, n, c->offset); poperror(); lock(c); c->offset += n; unlock(c); return n; } static void echo(char *buf, int n) { static int ctrlt, pid; int x; char *e, *p; if(n == 0) return; e = buf+n; for(p = buf; p < e; p++){ switch(*p){ case 0x10: /* ^P */ if(cpuserver && !kbd.ctlpoff){ active.exiting = 1; return; } break; case 0x14: /* ^T */ ctrlt++; if(ctrlt > 2) ctrlt = 2; continue; } if(ctrlt != 2) continue; /* ^T escapes */ ctrlt = 0; switch(*p){ case 'S': x = splhi(); dumpstack(); procdump(); splx(x); return; case 's': dumpstack(); return; case 'x': xsummary(); ixsummary(); mallocsummary(); // memorysummary(); pagersummary(); return; case 'd': if(consdebug == nil) consdebug = rdb; else consdebug = nil; print("consdebug now %#p\n", consdebug); return; case 'D': if(consdebug == nil) consdebug = rdb; consdebug(); return; case 'p': x = spllo(); procdump(); splx(x); return; case 'q': scheddump(); return; case 'k': killbig("^t ^t k"); return; case 'r': exit(0); return; } } if(kbdq != nil) qproduce(kbdq, buf, n); if(kbd.raw == 0) putstrn(buf, n); } /* * Called by a uart interrupt for console input. * * turn '\r' into '\n' before putting it into the queue. */ int kbdcr2nl(Queue*, int ch) { char *next; ilock(&kbd.lockputc); /* just a mutex */ if(ch == '\r' && !kbd.raw) ch = '\n'; next = kbd.iw+1; if(next >= kbd.ie) next = kbd.istage; if(next != kbd.ir){ *kbd.iw = ch; kbd.iw = next; } iunlock(&kbd.lockputc); return 0; } /* * Put character, possibly a rune, into read queue at interrupt time. * Called at interrupt time to process a character. */ int kbdputc(Queue*, int ch) { int i, n; char buf[3]; Rune r; char *next; if(kbd.ir == nil) return 0; /* in case we're not inited yet */ ilock(&kbd.lockputc); /* just a mutex */ r = ch; n = runetochar(buf, &r); for(i = 0; i < n; i++){ next = kbd.iw+1; if(next >= kbd.ie) next = kbd.istage; if(next == kbd.ir) break; *kbd.iw = buf[i]; kbd.iw = next; } iunlock(&kbd.lockputc); return 0; } /* * we save up input characters till clock time to reduce * per character interrupt overhead. */ static void kbdputcclock(void) { char *iw; /* this amortizes cost of qproduce */ if(kbd.iw != kbd.ir){ iw = kbd.iw; if(iw < kbd.ir){ echo(kbd.ir, kbd.ie-kbd.ir); kbd.ir = kbd.istage; } if(kbd.ir != iw){ echo(kbd.ir, iw-kbd.ir); kbd.ir = iw; } } } enum{ Qdir, Qbintime, Qcons, Qconsctl, Qcputime, Qdrivers, Qkmesg, Qkprint, Qhostdomain, Qhostowner, Qnull, Qosversion, Qpgrpid, Qpid, Qppid, Qrandom, Qreboot, Qswap, Qsysname, Qsysstat, Qtime, Quser, Qzero, }; enum { VLNUMSIZE= 22, }; static Dirtab consdir[]={ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, "bintime", {Qbintime}, 24, 0664, "cons", {Qcons}, 0, 0660, "consctl", {Qconsctl}, 0, 0220, "cputime", {Qcputime}, 6*NUMSIZE, 0444, "drivers", {Qdrivers}, 0, 0444, "hostdomain", {Qhostdomain}, DOMLEN, 0664, "hostowner", {Qhostowner}, 0, 0664, "kmesg", {Qkmesg}, 0, 0440, "kprint", {Qkprint, 0, QTEXCL}, 0, DMEXCL|0440, "null", {Qnull}, 0, 0666, "osversion", {Qosversion}, 0, 0444, "pgrpid", {Qpgrpid}, NUMSIZE, 0444, "pid", {Qpid}, NUMSIZE, 0444, "ppid", {Qppid}, NUMSIZE, 0444, "random", {Qrandom}, 0, 0444, "reboot", {Qreboot}, 0, 0664, "swap", {Qswap}, 0, 0664, "sysname", {Qsysname}, 0, 0664, "sysstat", {Qsysstat}, 0, 0666, "time", {Qtime}, NUMSIZE+3*VLNUMSIZE, 0664, "user", {Quser}, 0, 0666, "zero", {Qzero}, 0, 0444, }; int readnum(ulong off, char *buf, ulong n, ulong val, int size) { char tmp[64]; snprint(tmp, sizeof(tmp), "%*lud", size-1, val); tmp[size-1] = ' '; if(off >= size) return 0; if(off+n > size) n = size-off; memmove(buf, tmp+off, n); return n; } int readstr(ulong off, char *buf, ulong n, char *str) { int size; size = strlen(str); if(off >= size) return 0; if(off+n > size) n = size-off; memmove(buf, str+off, n); return n; } static void consinit(void) { todinit(); randominit(); /* * at 115200 baud, the 1024 char buffer takes 56 ms to process, * processing it every 22 ms should be fine */ addclock0link(kbdputcclock, 22); kickkbdq(); } static Chan* consattach(char *spec) { return devattach('c', spec); } static Walkqid* conswalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen); } static int consstat(Chan *c, uchar *dp, int n) { return devstat(c, dp, n, consdir, nelem(consdir), devgen); } static Chan* consopen(Chan *c, int omode) { c->aux = nil; c = devopen(c, omode, consdir, nelem(consdir), devgen); switch((ulong)c->qid.path){ case Qconsctl: incref(&kbd.ctl); break; case Qkprint: if(tas(&kprintinuse) != 0){ c->flag &= ~COPEN; error(Einuse); } if(kprintoq == nil){ kprintoq = qopen(8*1024, Qcoalesce, 0, 0); if(kprintoq == nil){ c->flag &= ~COPEN; error(Enomem); } qnoblock(kprintoq, 1); consdevs[1].q = kprintoq; }else qreopen(kprintoq); c->iounit = qiomaxatomic; break; } return c; } static void consclose(Chan *c) { switch((ulong)c->qid.path){ /* last close of control file turns off raw */ case Qconsctl: if(c->flag&COPEN){ if(decref(&kbd.ctl) == 0) kbd.raw = 0; } break; /* close of kprint allows other opens */ case Qkprint: if(c->flag & COPEN){ kprintinuse = 0; qhangup(kprintoq, nil); } break; } } static long consread(Chan *c, void *buf, long n, vlong off) { ulong l; Mach *mp; char *b, *bp, ch; char tmp[256]; /* must be >= 18*NUMSIZE (Qswap) */ int i, k, id, send; vlong offset = off; if(n <= 0) return n; switch((ulong)c->qid.path){ case Qdir: return devdirread(c, buf, n, consdir, nelem(consdir), devgen); case Qcons: qlock(&kbd); if(waserror()) { qunlock(&kbd); nexterror(); } while(!qcanread(lineq)){ if(qread(kbdq, &ch, 1) == 0) continue; send = 0; if(ch == 0){ /* flush output on rawoff -> rawon */ if(kbd.x > 0) send = !qcanread(kbdq); }else if(kbd.raw){ kbd.line[kbd.x++] = ch; send = !qcanread(kbdq); }else{ switch(ch){ case '\b': if(kbd.x > 0) kbd.x--; break; case 0x15: /* ^U */ kbd.x = 0; break; case '\n': case 0x04: /* ^D */ send = 1; default: if(ch != 0x04) kbd.line[kbd.x++] = ch; break; } } if(send || kbd.x == sizeof kbd.line){ qwrite(lineq, kbd.line, kbd.x); kbd.x = 0; } } n = qread(lineq, buf, n); qunlock(&kbd); poperror(); return n; case Qcputime: k = offset; if(k >= 6*NUMSIZE) return 0; if(k+n > 6*NUMSIZE) n = 6*NUMSIZE - k; /* easiest to format in a separate buffer and copy out */ for(i=0; i<6 && NUMSIZE*itime[i]; if(i == TReal) l = MACHP(0)->ticks - l; l = TK2MS(l); readnum(0, tmp+NUMSIZE*i, NUMSIZE, l, NUMSIZE); } memmove(buf, tmp+k, n); return n; case Qkmesg: /* * This is unlocked to avoid tying up a process * that's writing to the buffer. kmesg.n never * gets smaller, so worst case the reader will * see a slurred buffer. */ if(off >= kmesg.n) n = 0; else{ if(off+n > kmesg.n) n = kmesg.n - off; memmove(buf, kmesg.buf+off, n); } return n; case Qkprint: return qread(kprintoq, buf, n); case Qpgrpid: return readnum((ulong)offset, buf, n, up->pgrp->pgrpid, NUMSIZE); case Qpid: return readnum((ulong)offset, buf, n, up->pid, NUMSIZE); case Qppid: return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE); case Qtime: return readtime((ulong)offset, buf, n); case Qbintime: return readbintime(buf, n); case Qhostowner: return readstr((ulong)offset, buf, n, eve); case Qhostdomain: return readstr((ulong)offset, buf, n, hostdomain); case Quser: return readstr((ulong)offset, buf, n, up->user); case Qnull: return 0; case Qsysstat: b = smalloc(conf.nmach*(NUMSIZE*11+1) + 1); /* +1 for NUL */ bp = b; for(id = 0; id < 32; id++) { if(active.machs & (1<cs, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, mp->intr, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, mp->syscall, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, mp->pfault, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, mp->tlbfault, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, mp->tlbpurge, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, mp->load, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, (mp->perf.avg_inidle*100)/mp->perf.period, NUMSIZE); bp += NUMSIZE; readnum(0, bp, NUMSIZE, (mp->perf.avg_inintr*100)/mp->perf.period, NUMSIZE); bp += NUMSIZE; *bp++ = '\n'; } } if(waserror()){ free(b); nexterror(); } n = readstr((ulong)offset, buf, n, b); free(b); poperror(); return n; case Qswap: snprint(tmp, sizeof tmp, "%lud memory\n" "%d pagesize\n" "%lud kernel\n" "%lud/%lud user\n" "%lud/%lud swap\n" "%lud/%lud kernel malloc\n" "%lud/%lud kernel draw\n", conf.npage*BY2PG, BY2PG, conf.npage-conf.upages, palloc.user-palloc.freecount, palloc.user, conf.nswap-swapalloc.free, conf.nswap, mainmem->cursize, mainmem->maxsize, imagmem->cursize, imagmem->maxsize); return readstr((ulong)offset, buf, n, tmp); case Qsysname: if(sysname == nil) return 0; return readstr((ulong)offset, buf, n, sysname); case Qrandom: return randomread(buf, n); case Qdrivers: b = malloc(READSTR); if(b == nil) error(Enomem); n = 0; for(i = 0; devtab[i] != nil; i++) n += snprint(b+n, READSTR-n, "#%C %s\n", devtab[i]->dc, devtab[i]->name); if(waserror()){ free(b); nexterror(); } n = readstr((ulong)offset, buf, n, b); free(b); poperror(); return n; case Qzero: memset(buf, 0, n); return n; case Qosversion: snprint(tmp, sizeof tmp, "2000"); n = readstr((ulong)offset, buf, n, tmp); return n; default: print("consread %#llux\n", c->qid.path); error(Egreg); } return -1; /* never reached */ } static long conswrite(Chan *c, void *va, long n, vlong off) { char buf[256], ch; long l, bp; char *a; Mach *mp; int id, fd; Chan *swc; ulong offset; Cmdbuf *cb; Cmdtab *ct; a = va; offset = off; switch((ulong)c->qid.path){ case Qcons: /* * Can't page fault in putstrn, so copy the data locally. */ l = n; while(l > 0){ bp = l; if(bp > sizeof buf) bp = sizeof buf; memmove(buf, a, bp); putstrn0(buf, bp, 1); a += bp; l -= bp; } break; case Qconsctl: if(n >= sizeof(buf)) n = sizeof(buf)-1; strncpy(buf, a, n); buf[n] = 0; for(a = buf; a;){ if(strncmp(a, "rawon", 5) == 0){ kbd.raw = 1; /* clumsy hack - wake up reader */ ch = 0; qwrite(kbdq, &ch, 1); } else if(strncmp(a, "rawoff", 6) == 0){ kbd.raw = 0; } else if(strncmp(a, "ctlpon", 6) == 0){ kbd.ctlpoff = 0; } else if(strncmp(a, "ctlpoff", 7) == 0){ kbd.ctlpoff = 1; } if(a = strchr(a, ' ')) a++; } break; case Qtime: if(!iseve()) error(Eperm); return writetime(a, n); case Qbintime: if(!iseve()) error(Eperm); return writebintime(a, n); case Qhostowner: return hostownerwrite(a, n); case Qhostdomain: return hostdomainwrite(a, n); case Quser: return userwrite(a, n); case Qnull: break; case Qreboot: if(!iseve()) error(Eperm); cb = parsecmd(a, n); if(waserror()) { free(cb); nexterror(); } ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg)); switch(ct->index) { case CMhalt: reboot(nil, 0, 0); break; case CMreboot: rebootcmd(cb->nf-1, cb->f+1); break; case CMpanic: *(ulong*)0=0; panic("/dev/reboot"); } poperror(); free(cb); break; case Qsysstat: for(id = 0; id < 32; id++) { if(active.machs & (1<cs = 0; mp->intr = 0; mp->syscall = 0; mp->pfault = 0; mp->tlbfault = 0; mp->tlbpurge = 0; } } break; case Qswap: if(n >= sizeof buf) error(Egreg); memmove(buf, va, n); /* so we can NUL-terminate */ buf[n] = 0; /* start a pager if not already started */ if(strncmp(buf, "start", 5) == 0){ kickpager(); break; } if(!iseve()) error(Eperm); if(buf[0]<'0' || '9'= sizeof buf) error(Ebadarg); strncpy(buf, a, n); buf[n] = 0; if(buf[n-1] == '\n') buf[n-1] = 0; kstrdup(&sysname, buf); break; default: print("conswrite: %#llux\n", c->qid.path); error(Egreg); } return n; } Dev consdevtab = { 'c', "cons", devreset, consinit, devshutdown, consattach, conswalk, consstat, consopen, devcreate, consclose, consread, devbread, conswrite, devbwrite, devremove, devwstat, }; static ulong randn; static void seedrand(void) { if(!waserror()){ randomread((void*)&randn, sizeof(randn)); poperror(); } } int nrand(int n) { if(randn == 0) seedrand(); randn = randn*1103515245 + 12345 + MACHP(0)->ticks; return (randn>>16) % n; } int rand(void) { nrand(1); return randn; } static uvlong uvorder = 0x0001020304050607ULL; static uchar* le2vlong(vlong *to, uchar *f) { uchar *t, *o; int i; t = (uchar*)to; o = (uchar*)&uvorder; for(i = 0; i < sizeof(vlong); i++) t[o[i]] = f[i]; return f+sizeof(vlong); } static uchar* vlong2le(uchar *t, vlong from) { uchar *f, *o; int i; f = (uchar*)&from; o = (uchar*)&uvorder; for(i = 0; i < sizeof(vlong); i++) t[i] = f[o[i]]; return t+sizeof(vlong); } static long order = 0x00010203; static uchar* le2long(long *to, uchar *f) { uchar *t, *o; int i; t = (uchar*)to; o = (uchar*)ℴ for(i = 0; i < sizeof(long); i++) t[o[i]] = f[i]; return f+sizeof(long); } static uchar* long2le(uchar *t, long from) { uchar *f, *o; int i; f = (uchar*)&from; o = (uchar*)ℴ for(i = 0; i < sizeof(long); i++) t[i] = f[o[i]]; return t+sizeof(long); } char *Ebadtimectl = "bad time control"; /* * like the old #c/time but with added info. Return * * secs nanosecs fastticks fasthz */ static int readtime(ulong off, char *buf, int n) { vlong nsec, ticks; long sec; char str[7*NUMSIZE]; nsec = todget(&ticks); if(fasthz == 0LL) fastticks((uvlong*)&fasthz); sec = nsec/1000000000ULL; snprint(str, sizeof(str), "%*lud %*llud %*llud %*llud ", NUMSIZE-1, sec, VLNUMSIZE-1, nsec, VLNUMSIZE-1, ticks, VLNUMSIZE-1, fasthz); return readstr(off, buf, n, str); } /* * set the time in seconds */ static int writetime(char *buf, int n) { char b[13]; long i; vlong now; if(n >= sizeof(b)) error(Ebadtimectl); strncpy(b, buf, n); b[n] = 0; i = strtol(b, 0, 0); if(i <= 0) error(Ebadtimectl); now = i*1000000000LL; todset(now, 0, 0); return n; } /* * read binary time info. all numbers are little endian. * ticks and nsec are syncronized. */ static int readbintime(char *buf, int n) { int i; vlong nsec, ticks; uchar *b = (uchar*)buf; i = 0; if(fasthz == 0LL) fastticks((uvlong*)&fasthz); nsec = todget(&ticks); if(n >= 3*sizeof(uvlong)){ vlong2le(b+2*sizeof(uvlong), fasthz); i += sizeof(uvlong); } if(n >= 2*sizeof(uvlong)){ vlong2le(b+sizeof(uvlong), ticks); i += sizeof(uvlong); } if(n >= 8){ vlong2le(b, nsec); i += sizeof(vlong); } return i; } /* * set any of the following * - time in nsec * - nsec trim applied over some seconds * - clock frequency */ static int writebintime(char *buf, int n) { uchar *p; vlong delta; long period; n--; p = (uchar*)buf + 1; switch(*buf){ case 'n': if(n < sizeof(vlong)) error(Ebadtimectl); le2vlong(&delta, p); todset(delta, 0, 0); break; case 'd': if(n < sizeof(vlong)+sizeof(long)) error(Ebadtimectl); p = le2vlong(&delta, p); le2long(&period, p); todset(-1, delta, period); break; case 'f': if(n < sizeof(uvlong)) error(Ebadtimectl); le2vlong(&fasthz, p); todsetfreq(fasthz); break; } return n; } */ if(kbd.x > 0) send = !qcanread(kbdq); }else if(kbd.raw){ kbd.line[kbd.x++] = ch; send = !qcanread(kbdq); }else{ switch(ch){ case '\b': if(kbd.x > 0) kbd.x--; break; case 0x15: /* ^U */ kbd.x = 0; break; case '\n': case 0x04: /* ^D */ send = 1; default: if(ch != 0x04) kbd.line[kbd.x++] = ch; brcons/devuart.c 644 0 0 27775 11231562730 11624ustar00nemonemo#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "../port/netif.h" enum { /* soft flow control chars */ CTLS= 023, CTLQ= 021, }; extern Dev uartdevtab; extern PhysUart* physuart[]; static Uart* uartlist; static Uart** uart; static int uartnuart; static Dirtab *uartdir; static int uartndir; static Timer *uarttimer; struct Uartalloc { Lock; Uart *elist; /* list of enabled interfaces */ } uartalloc; static void uartclock(void); static void uartflow(void*); /* * enable/disable uart and add/remove to list of enabled uarts */ static Uart* uartenable(Uart *p) { Uart **l; if(p->iq == nil){ if((p->iq = qopen(8*1024, 0, uartflow, p)) == nil) return nil; } else qreopen(p->iq); if(p->oq == nil){ if((p->oq = qopen(8*1024, 0, uartkick, p)) == nil){ qfree(p->iq); p->iq = nil; return nil; } } else qreopen(p->oq); p->ir = p->istage; p->iw = p->istage; p->ie = &p->istage[Stagesize]; p->op = p->ostage; p->oe = p->ostage; p->hup_dsr = p->hup_dcd = 0; p->dsr = p->dcd = 0; /* assume we can send */ p->cts = 1; p->ctsbackoff = 0; if(p->bits == 0) uartctl(p, "l8"); if(p->stop == 0) uartctl(p, "s1"); if(p->parity == 0) uartctl(p, "pn"); if(p->baud == 0) uartctl(p, "b9600"); (*p->phys->enable)(p, 1); lock(&uartalloc); for(l = &uartalloc.elist; *l; l = &(*l)->elist){ if(*l == p) break; } if(*l == 0){ p->elist = uartalloc.elist; uartalloc.elist = p; } p->enabled = 1; unlock(&uartalloc); return p; } static void uartdisable(Uart *p) { Uart **l; (*p->phys->disable)(p); lock(&uartalloc); for(l = &uartalloc.elist; *l; l = &(*l)->elist){ if(*l == p){ *l = p->elist; break; } } p->enabled = 0; unlock(&uartalloc); } void uartmouse(Uart* p, int (*putc)(Queue*, int), int setb1200) { qlock(p); if(p->opens++ == 0 && uartenable(p) == nil){ qunlock(p); error(Enodev); } if(setb1200) uartctl(p, "b1200"); p->putc = putc; p->special = 1; qunlock(p); } void uartsetmouseputc(Uart* p, int (*putc)(Queue*, int)) { qlock(p); if(p->opens == 0 || p->special == 0){ qunlock(p); error(Enodev); } p->putc = putc; qunlock(p); } static void setlength(int i) { Uart *p; if(i > 0){ p = uart[i]; if(p && p->opens && p->iq) uartdir[1+3*i].length = qlen(p->iq); } else for(i = 0; i < uartnuart; i++){ p = uart[i]; if(p && p->opens && p->iq) uartdir[1+3*i].length = qlen(p->iq); } } /* * set up the '#t' directory */ static void uartreset(void) { int i; Dirtab *dp; Uart *p, *tail; tail = nil; for(i = 0; physuart[i] != nil; i++){ if(physuart[i]->pnp == nil) continue; if((p = physuart[i]->pnp()) == nil) continue; if(uartlist != nil) tail->next = p; else uartlist = p; for(tail = p; tail->next != nil; tail = tail->next) uartnuart++; uartnuart++; } if(uartnuart) uart = xalloc(uartnuart*sizeof(Uart*)); uartndir = 1 + 3*uartnuart; uartdir = xalloc(uartndir * sizeof(Dirtab)); if (uart == nil || uartdir == nil) panic("uartreset: no memory"); dp = uartdir; strcpy(dp->name, "."); mkqid(&dp->qid, 0, 0, QTDIR); dp->length = 0; dp->perm = DMDIR|0555; dp++; p = uartlist; for(i = 0; i < uartnuart; i++){ /* 3 directory entries per port */ sprint(dp->name, "eia%d", i); dp->qid.path = NETQID(i, Ndataqid); dp->perm = 0660; dp++; sprint(dp->name, "eia%dctl", i); dp->qid.path = NETQID(i, Nctlqid); dp->perm = 0660; dp++; sprint(dp->name, "eia%dstatus", i); dp->qid.path = NETQID(i, Nstatqid); dp->perm = 0444; dp++; uart[i] = p; p->dev = i; if(p->console || p->special){ if(uartenable(p) != nil){ if(p->console){ addkbdq(p->iq, -1); addconsdev(p->oq, uartputs, 2, 0); p->putc = kbdcr2nl; } p->opens++; } } p = p->next; } if(uartnuart){ /* * at 115200 baud, the 1024 char buffer takes 56 ms to process, * processing it every 22 ms should be fine. */ uarttimer = addclock0link(uartclock, 22); } } static Chan* uartattach(char *spec) { return devattach('t', spec); } static Walkqid* uartwalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, uartdir, uartndir, devgen); } static int uartstat(Chan *c, uchar *dp, int n) { if(NETTYPE(c->qid.path) == Ndataqid) setlength(NETID(c->qid.path)); return devstat(c, dp, n, uartdir, uartndir, devgen); } static Chan* uartopen(Chan *c, int omode) { Uart *p; c = devopen(c, omode, uartdir, uartndir, devgen); switch(NETTYPE(c->qid.path)){ case Nctlqid: case Ndataqid: p = uart[NETID(c->qid.path)]; qlock(p); if(p->opens++ == 0 && uartenable(p) == nil){ qunlock(p); c->flag &= ~COPEN; error(Enodev); } qunlock(p); break; } c->iounit = qiomaxatomic; return c; } static int uartdrained(void* arg) { Uart *p; p = arg; return qlen(p->oq) == 0 && p->op == p->oe; } static void uartdrainoutput(Uart *p) { if(!p->enabled) return; p->drain = 1; if(waserror()){ p->drain = 0; nexterror(); } sleep(&p->r, uartdrained, p); poperror(); } static void uartclose(Chan *c) { Uart *p; if(c->qid.type & QTDIR) return; if((c->flag & COPEN) == 0) return; switch(NETTYPE(c->qid.path)){ case Ndataqid: case Nctlqid: p = uart[NETID(c->qid.path)]; qlock(p); if(--(p->opens) == 0){ qclose(p->iq); ilock(&p->rlock); p->ir = p->iw = p->istage; iunlock(&p->rlock); /* */ qhangup(p->oq, nil); if(!waserror()){ uartdrainoutput(p); poperror(); } qclose(p->oq); uartdisable(p); p->dcd = p->dsr = p->dohup = 0; } qunlock(p); break; } } static long uartread(Chan *c, void *buf, long n, vlong off) { Uart *p; ulong offset = off; if(c->qid.type & QTDIR){ setlength(-1); return devdirread(c, buf, n, uartdir, uartndir, devgen); } p = uart[NETID(c->qid.path)]; switch(NETTYPE(c->qid.path)){ case Ndataqid: return qread(p->iq, buf, n); case Nctlqid: return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); case Nstatqid: return (*p->phys->status)(p, buf, n, offset); } return 0; } int uartctl(Uart *p, char *cmd) { char *f[16]; int i, n, nf; nf = tokenize(cmd, f, nelem(f)); for(i = 0; i < nf; i++){ if(strncmp(f[i], "break", 5) == 0){ (*p->phys->dobreak)(p, 0); continue; } n = atoi(f[i]+1); switch(*f[i]){ case 'B': case 'b': uartdrainoutput(p); if((*p->phys->baud)(p, n) < 0) return -1; break; case 'C': case 'c': p->hup_dcd = n; break; case 'D': case 'd': uartdrainoutput(p); (*p->phys->dtr)(p, n); break; case 'E': case 'e': p->hup_dsr = n; break; case 'f': case 'F': if(p->oq != nil) qflush(p->oq); break; case 'H': case 'h': if(p->iq != nil) qhangup(p->iq, 0); if(p->oq != nil) qhangup(p->oq, 0); break; case 'i': case 'I': uartdrainoutput(p); (*p->phys->fifo)(p, n); break; case 'K': case 'k': uartdrainoutput(p); (*p->phys->dobreak)(p, n); break; case 'L': case 'l': uartdrainoutput(p); if((*p->phys->bits)(p, n) < 0) return -1; break; case 'm': case 'M': uartdrainoutput(p); (*p->phys->modemctl)(p, n); break; case 'n': case 'N': if(p->oq != nil) qnoblock(p->oq, n); break; case 'P': case 'p': uartdrainoutput(p); if((*p->phys->parity)(p, *(f[i]+1)) < 0) return -1; break; case 'Q': case 'q': if(p->iq != nil) qsetlimit(p->iq, n); if(p->oq != nil) qsetlimit(p->oq, n); break; case 'R': case 'r': uartdrainoutput(p); (*p->phys->rts)(p, n); break; case 'S': case 's': uartdrainoutput(p); if((*p->phys->stop)(p, n) < 0) return -1; break; case 'W': case 'w': if(uarttimer == nil || n < 1) return -1; uarttimer->tns = (vlong)n * 100000LL; break; case 'X': case 'x': if(p->enabled){ ilock(&p->tlock); p->xonoff = n; iunlock(&p->tlock); } break; } } return 0; } static long uartwrite(Chan *c, void *buf, long n, vlong) { Uart *p; char *cmd; if(c->qid.type & QTDIR) error(Eperm); p = uart[NETID(c->qid.path)]; switch(NETTYPE(c->qid.path)){ case Ndataqid: qlock(p); if(waserror()){ qunlock(p); nexterror(); } n = qwrite(p->oq, buf, n); qunlock(p); poperror(); break; case Nctlqid: cmd = malloc(n+1); memmove(cmd, buf, n); cmd[n] = 0; qlock(p); if(waserror()){ qunlock(p); free(cmd); nexterror(); } /* let output drain */ if(uartctl(p, cmd) < 0) error(Ebadarg); qunlock(p); poperror(); free(cmd); break; } return n; } static int uartwstat(Chan *c, uchar *dp, int n) { Dir d; Dirtab *dt; if(!iseve()) error(Eperm); if(QTDIR & c->qid.type) error(Eperm); if(NETTYPE(c->qid.path) == Nstatqid) error(Eperm); dt = &uartdir[1 + 3 * NETID(c->qid.path)]; n = convM2D(dp, n, &d, nil); if(n == 0) error(Eshortstat); if(d.mode != ~0UL) dt[0].perm = dt[1].perm = d.mode; return n; } void uartpower(int on) { Uart *p; for(p = uartlist; p != nil; p = p->next) { if(p->phys->power) (*p->phys->power)(p, on); } } Dev uartdevtab = { 't', "uart", uartreset, devinit, devshutdown, uartattach, uartwalk, uartstat, uartopen, devcreate, uartclose, uartread, devbread, uartwrite, devbwrite, devremove, uartwstat, uartpower, }; /* * restart input if it's off */ static void uartflow(void *v) { Uart *p; p = v; if(p->modem) (*p->phys->rts)(p, 1); } /* * put some bytes into the local queue to avoid calling * qconsume for every character */ int uartstageoutput(Uart *p) { int n; n = qconsume(p->oq, p->ostage, Stagesize); if(n <= 0) return 0; p->op = p->ostage; p->oe = p->ostage + n; return n; } /* * restart output */ void uartkick(void *v) { Uart *p = v; if(p->blocked) return; ilock(&p->tlock); (*p->phys->kick)(p); iunlock(&p->tlock); if(p->drain && uartdrained(p)){ p->drain = 0; wakeup(&p->r); } } /* * Move data from the interrupt staging area to * the input Queue. */ static void uartstageinput(Uart *p) { int n; uchar *ir, *iw; while(p->ir != p->iw){ ir = p->ir; if(p->ir > p->iw){ iw = p->ie; p->ir = p->istage; } else{ iw = p->iw; p->ir = p->iw; } if((n = qproduce(p->iq, ir, iw - ir)) < 0){ p->serr++; (*p->phys->rts)(p, 0); } else if(n == 0) p->berr++; } } /* * receive a character at interrupt time */ void uartrecv(Uart *p, char ch) { uchar *next; /* software flow control */ if(p->xonoff){ if(ch == CTLS){ p->blocked = 1; }else if(ch == CTLQ){ p->blocked = 0; p->ctsbackoff = 2; /* clock gets output going again */ } } /* receive the character */ if(p->putc) p->putc(p->iq, ch); else if (p->iw) { /* maybe the line isn't enabled yet */ ilock(&p->rlock); next = p->iw + 1; if(next == p->ie) next = p->istage; if(next == p->ir) uartstageinput(p); if(next != p->ir){ *p->iw = ch; p->iw = next; } iunlock(&p->rlock); } } /* * we save up input characters till clock time to reduce * per character interrupt overhead. */ static void uartclock(void) { Uart *p; lock(&uartalloc); for(p = uartalloc.elist; p; p = p->elist){ /* this hopefully amortizes cost of qproduce to many chars */ if(p->iw != p->ir){ ilock(&p->rlock); uartstageinput(p); iunlock(&p->rlock); } /* hang up if requested */ if(p->dohup){ qhangup(p->iq, 0); qhangup(p->oq, 0); p->dohup = 0; } /* this adds hysteresis to hardware/software flow control */ if(p->ctsbackoff){ ilock(&p->tlock); if(p->ctsbackoff){ if(--(p->ctsbackoff) == 0) (*p->phys->kick)(p); } iunlock(&p->tlock); } } unlock(&uartalloc); } /* * polling console input, output */ Uart* consuart; int uartgetc(void) { if(consuart == nil || consuart->phys->getc == nil) return -1; return consuart->phys->getc(consuart); } void uartputc(int c) { if(consuart == nil || consuart->phys->putc == nil) return; consuart->phys->putc(consuart, c); } void uartputs(char *s, int n) { char *e; if(consuart == nil || consuart->phys->putc == nil) return; e = s+n; for(; sphys->putc(consuart, '\r'); consuart->phys->putc(consuart, *s); } } cons/devusbcons.c 644 0 0 36224 11231562730 12312ustar00nemonemo#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "usb.h" #include "usbehci.h" /* * USB debug port support for Net20DC Ajays debug cable. * Still WIP. * * ehci must avoid reset of the ctlr whose capio is at * ehcidebugcapio, if not nil. * * Note that using this requires being able to use vmap() to map ehci * registers for I/O. This means that MMU code must be initialized. * * CAUTION: The debug device has two ports but they are NOT * the same. If you put the "USB 2.0 debug cable" text in front of you * the port to the right is the one to attach to the debug port. The port * on the left is the one to attach to the host used for debugging. * Thigs are worse. Power for the "debug cable" is taken only from the * port attached to the target machine. * This means the device will malfunction unless you do the plug/unplug * start serial dance in the wrong order. * * For the machine I tried, I plug the debugged port and cold start the * machine. * Then I do this each time I want to start again: * - unplug the debugging port * - warm reboot the debugged machine * - wait until bootfile prompt (device has its power) * - plug the debugging port * - start usb/serail * - con /dev/eiaUX/data * * If the debug device seems to be stuck I unplug the debugged port * to remove power from the device and make it restart. * * This is so clumsy that the bug might be ours, but I wouldn't bet. * * The usb/serial driver talking to us must be sure that maxpkt is * set to 8 bytes. Otherwise, the debug device hangs. It's picky this cable. * * There's also some problem in the usb/serial kludge for con'ing this: * we miss some messages. However, this could be due to the debugging * machine being too slow. */ typedef struct Ctlr Ctlr; enum { Pdebugport = 0x0A, /* ehci debug port PCI cap. */ Ddebug = 0x0A, /* debug descriptor type */ Ddebuglen = 4, /* debug descriptor length */ Debugmode = 6, /* debug mode feature */ Daddr = 127, /* debug device address */ Tokin = 0x69, Tokout = 0xE1, Toksetup = 0x2D, Ack = 0xD2, Nak = 0x5A, Nyet = 0x96, Stall = 0x1E, Data0 = 0xC3 << Pspidshift, Data1 = 0x4B << Pspidshift, Read = 0, /* mode for ucio() */ Write = 1, }; struct Ctlr { Lock; Pcidev* pcidev; Ecapio* capio; /* Capability i/o regs */ Eopio* opio; /* Operational i/o regs */ Edbgio* dbgio; /* Debug port i/o regs */ /* software */ int port; /* port number */ int epin; /* input endpoint address */ int epout; /* output endpoint address */ int sc; /* start char in buf */ int ec; /* end char in buf */ char buf[8]; /* read buffer */ Queue* iq; /* input queue */ Queue* oq; /* output queue */ int consid; /* console id */ }; #undef dprint #define dprint if(debug)iprint Ecapio* ehcidebugcapio; int ehcidebugport; static int debug = 1; static Ctlr ctlr; /* one console at most */ static void ehcirun(Ctlr *ctlr, int on) { int i; Eopio *opio; dprint("usbcons: %s", on ? "starting" : "halting"); opio = ctlr->opio; if(on) opio->cmd |= Crun; else opio->cmd = Cstop; for(i = 0; i < 100; i++) if(on == 0 && (opio->sts & Shalted) != 0) break; else if(on != 0 && (opio->sts & Shalted) == 0) break; else delay(1); if(i == 100) dprint("usbcons: %s cmd timed out\n", on ? "run" : "halt"); dprint(" sts %#ulx\n", opio->sts); } static int rpid(ulong csw) { return (csw >> Prpidshift) & Prpidmask; } static int spid(ulong csw) { return (csw >> Pspidshift) & Pspidmask; } /* * Perform I/O. * This returns -1 upon errors but -2 upon naks after (re)trying. * The caller could probably retry even more upon naks. */ static int ucio(Edbgio *dbgio, int pid, void *data, int len, int iswrite, int tmout) { static char *wstr[] = {"rd", "wr" }; int i; uchar *b; int ntries; int once; int ppid; b = data; if(len > 8) panic("usbcons ucio bug. len > 0"); if(len > 0 && data == nil) panic("usbcons ucio bug. len but not data"); ntries = 1000; once = 1; Again: dbgio->csw = (dbgio->csw & ~Clen) | len; dbgio->pid = pid; if(once && debug > 1){ once = 0; dprint("usbcons: %s: csw %#ulx pid %#ux [%d] ", wstr[iswrite], dbgio->csw, pid, len); if(iswrite){ for(i = 0; i < len; i++) dprint("%02ux ", b[i]); } } memset(dbgio->data, 0, sizeof(dbgio->data)); if(iswrite){ if(len > 0) memmove(dbgio->data, data, len); dbgio->csw |= Cwrite; }else dbgio->csw &= ~Cwrite; dbgio->csw |= Cgo; for(i = 0; (dbgio->csw&Cdone) == 0; i++) if(tmout != 0 && i > 100000) return -1; dbgio->csw |= Cdone; /* acknowledge */ if((dbgio->csw & Cfailed) != 0){ dprint(" err csw %#ulx\n", dbgio->csw); return -1; } ppid = rpid(dbgio->pid); if((ppid == Nak || ppid == Nyet) && --ntries > 0){ microdelay(10); goto Again; } if(ntries == 0) dprint(" naks"); if(ppid != Ack && ppid != spid(dbgio->pid)){ dprint(" bad pid %#x\n", ppid); len = -1; if(ppid == Nak) len = -2; } if(iswrite == 0 && len > 0){ if((dbgio->csw&Clen) < len) len = dbgio->csw&Clen; if(len > 0) memmove(data, dbgio->data, len); } if(debug > 1){ dprint("-> [%d] ", len); if(iswrite == 0){ for(i = 0; i < len; i++) dprint("%02ux ", b[i]); } dprint(" csw %#ulx\n", dbgio->csw); } return len; } /* * BUG: This is not a generic usb cmd tool. * If you call this be sure it works for the type of call you are making. * This is just for what this driver uses. Compare this with the * general purpose ehci control transfer dance. */ static int uccmd(Edbgio *dbgio, int type, int req, int val, int idx, void *data, int cnt) { uchar buf[Rsetuplen]; int r; assert(cnt >= 0 && cnt <= nelem(dbgio->data)); dprint("usbcons: cmd t %#x r %#x v %#x i %#x c %d\n", type, req, val, idx, cnt); buf[Rtype] = type; buf[Rreq] = req; PUT2(buf+Rvalue, val); PUT2(buf+Rindex, idx); PUT2(buf+Rcount, cnt); if(ucio(dbgio, Data0|Toksetup, buf, Rsetuplen, Write, 100) < 0) return -1; if((type&Rd2h) == 0 && cnt > 0) panic("out debug command with data"); r = ucio(dbgio, Data1|Tokin, data, cnt, Read, 100); if((type&Rd2h) != 0) ucio(dbgio, Data1|Tokout, nil, 0, Write, 100); return r; } static ulong uctoggle(Edbgio *dbgio) { return (dbgio->pid ^ Ptoggle) & Ptogglemask; } static int ucread(Ctlr *ctlr, void *data, int cnt) { Edbgio *dbgio; int r; ilock(ctlr); dbgio = ctlr->dbgio; dbgio->addr = (Daddr << Adevshift) | (ctlr->epin << Aepshift); r = ucio(dbgio, uctoggle(dbgio)|Tokin, data, cnt, Read, 10); if(r < 0) uctoggle(dbgio); /* leave toggle as it was */ iunlock(ctlr); return r; } static int ucwrite(Ctlr *ctlr, void *data, int cnt) { Edbgio *dbgio; int r; ilock(ctlr); dbgio = ctlr->dbgio; dbgio->addr = (Daddr << Adevshift) | (ctlr->epout << Aepshift); r = ucio(dbgio, uctoggle(dbgio)|Tokout, data, cnt, Write, 10); if(r < 0) uctoggle(dbgio); /* leave toggle as it was */ iunlock(ctlr); return r; } /* * Set the device address to 127 and enable it. * The device might have the address hardwired to 127. * We try to set it up in any case but ignore most errors. */ static int ucconfigdev(Ctlr *ctlr) { uchar desc[8]; Edbgio *dbgio; int r; dbgio = ctlr->dbgio; dprint("usbcons: setting up device address to %d\n", Daddr); dbgio->addr = (0 << Adevshift) | (0 << Aepshift); if(uccmd(dbgio, Rh2d|Rstd|Rdev, Rsetaddr, Daddr, 0, nil, 0) < 0) print("usbcons: debug device: can't set address to %d\n", Daddr); else dprint("usbcons: device address set to %d\n", Daddr); dbgio->addr = (Daddr << Adevshift) | (0 << Aepshift); dprint("usbcons: reading debug descriptor\n"); r = uccmd(dbgio, Rd2h|Rstd|Rdev, Rgetdesc, Ddebug << 8, 0, desc, 4); if(r < Ddebuglen || desc[1] != Ddebug){ print("usbcons: debug device: can't get debug descriptor\n"); dbgio->csw &= ~(Cowner|Cbusy); return -1; } dprint("usbcons: setting up debug mode\n"); if(uccmd(dbgio, Rh2d|Rstd|Rdev, Rsetfeature, Debugmode, 0, nil, 0) < 0){ print("usbcons: debug device: can't set debug mode\n"); dbgio->csw &= ~(Cowner|Cbusy); return -1; } ctlr->epin = desc[2] & ~0x80; /* clear direction bit from ep. addr */; ctlr->epout = desc[3]; print("#u/usb/ep%d.0: ehci debug port: in ep%d.%d out: ep%d.%d\n", Daddr, Daddr, ctlr->epin, Daddr, ctlr->epout); return 0; } /* * Ctlr already ilocked. */ static void portreset(Ctlr *ctlr, int port) { Eopio *opio; ulong s; int i; opio = ctlr->opio; s = opio->portsc[port-1]; dprint("usbcons %#p port %d reset: sts %#ulx\n", ctlr->capio, port, s); s &= ~(Psenable|Psreset); opio->portsc[port-1] = s|Psreset; for(i = 0; i < 10; i++){ delay(10); if((opio->portsc[port-1] & Psreset) == 0) break; } opio->portsc[port-1] &= ~Psreset; delay(50); dprint("usbcons %#p port %d after reset: sts %#ulx\n", ctlr->capio, port, s); } /* * Ctlr already ilocked. */ static void portenable(Ctlr *ctlr, int port) { Eopio *opio; ulong s; opio = ctlr->opio; s = opio->portsc[port-1]; dprint("usbcons: port %d enable: sts %#ulx\n", port, s); if(s & (Psstatuschg | Pschange)) opio->portsc[port-1] = s; opio->portsc[port-1] |= Psenable; delay(100); dprint("usbcons: port %d after enable: sts %#ulx\n", port, s); } static int ucattachdev(Ctlr *ctlr) { Eopio *opio; Edbgio *dbgio; int i; ulong s; int port; ilock(ctlr); if(ehcidebugcapio != nil){ iunlock(ctlr); return -1; } /* reclaim port */ dbgio = ctlr->dbgio; dbgio->csw |= Cowner; dbgio->csw &= ~(Cenable|Cbusy); opio = ctlr->opio; opio->cmd &= ~(Chcreset|Ciasync|Cpse|Case); opio->config = Callmine; /* reclaim all ports */ ehcirun(ctlr, 1); delay(100); ctlr->port = (ctlr->capio->parms >> Cdbgportshift) & Cdbgportmask; port = ctlr->port; if(port < 1 || port > (ctlr->capio->parms & Cnports)){ print("usbcons: debug port out of range\n"); dbgio->csw &= ~(Cowner|Cbusy); iunlock(ctlr); return -1; } dprint("usbcons: debug port: %d\n", port); opio->portsc[port-1] = Pspower; for(i = 0; i < 200; i++){ s = opio->portsc[port-1]; if(s & (Psstatuschg | Pschange)){ opio->portsc[port-1] = s; dprint("usbcons: port sts %#ulx\n", s); } if(s & Pspresent){ portreset(ctlr, port); break; } delay(1); } if((opio->portsc[port-1] & Pspresent) == 0 || i == 200){ print("usbcons: no debug device (attached to another port?)\n"); dbgio->csw &= ~(Cowner|Cbusy); iunlock(ctlr); return -1; } ehcidebugcapio = ctlr->capio; /* this ehci must avoid reset */ ehcidebugport = port; /* and this port must not be available */ dbgio->csw |= Cowner|Cenable|Cbusy; opio->portsc[port-1] &= ~Psenable; delay(100); ehcirun(ctlr, 0); // portenable(ctlr, port); // dbgio->csw |= Cowner|Cenable|Cbusy; delay(100); iunlock(ctlr); return 0; } static int ucreset(Ctlr *ctlr) { Eopio *opio; int i; dprint("ucreset\n"); /* * Turn off legacy mode. */ ehcirun(ctlr, 0); pcicfgw16(ctlr->pcidev, 0xc0, 0x2000); /* clear high 32 bits of address signals if it's 64 bits. * This is probably not needed but it does not hurt. */ opio = ctlr->opio; if((ctlr->capio->capparms & C64) != 0){ dprint("ehci: 64 bits\n"); opio->seg = 0; } opio->cmd |= Chcreset; /* controller reset */ for(i = 0; i < 100; i++){ if((opio->cmd & Chcreset) == 0) break; delay(1); } if(i == 100){ print("usbcons: controller reset timed out\n"); return -1; } dprint("ucreset done\n"); return 0; } static int ucinit(Pcidev *p, uint ptr) { uintptr io; uint off; Ecapio *capio; io = p->mem[0].bar & ~0xF; if(io == 0){ print("usbcons: failed to map registers\n"); return -1; } off = pcicfgr16(p, ptr+2) & 0xFFF; capio = ctlr.capio = vmap(io, p->mem[0].size); ctlr.opio = (Eopio*)((uintptr)capio + (capio->cap & 0xFF)); ctlr.dbgio = (Edbgio*)((uintptr)capio + off); ctlr.pcidev = p; pcisetbme(p); pcisetpms(p, 0); if((ctlr.dbgio->csw & Cbusy) != 0){ print("usbcons: debug port already in use\n"); return -1; } print("usbcons: port %#p: ehci debug port\n", ctlr.dbgio); if(ucreset(&ctlr) < 0 || ucattachdev(&ctlr) < 0 || ucconfigdev(&ctlr) < 0) return -1; return 0; } /* * Polling interface. */ int usbgetc(void) { int nr; if(ehcidebugcapio == nil) return -1; if(ctlr.sc == ctlr.ec){ ctlr.sc = ctlr.ec = 0; nr = ucread(&ctlr, ctlr.buf, sizeof(ctlr.buf)); if(nr > 0) ctlr.ec += nr; } if(ctlr.sc < ctlr.ec) return ctlr.buf[ctlr.sc++]; return -1; } void usbputc(int c) { char buf[1]; if(ehcidebugcapio == nil) return; buf[0] = c; ucwrite(&ctlr, buf, 1); } /* * Put 8 chars at a time. * Ignore errors (this device seems to be flaky). * Some times (for some packets) the device keeps on * sending naks. This does not seem to depend on toggles or * pids or packet content. It's supposed we don't need to send * unstalls here. But it really looks like we do need then. * Time to use a sniffer to see what windows does * with the device?? */ void usbputs(char *s, int n) { int nw; if(ehcidebugcapio == nil) return; for(; n > 0; n -= nw){ nw = n; if(nw > 8) nw = 8; ucwrite(&ctlr, s, nw); s += nw; } } static Lock lck; static void usbclock(void) { char buf[80]; int n; lock(&lck); usbputs(".", 1); if((n = qconsume(ctlr.oq, buf, sizeof(buf))) > 0) usbputs(buf, n); unlock(&lck); // while((n = ucread(&ctlr, buf, sizeof(buf))) > 0) // qproduce(ctlr.iq, buf, n); } /* * Queue interface * The debug port does not seem to * issue interrupts for us. We must poll for completion and * also to check for input. * By now this might suffice but it's probably wrong. */ static void usbconsreset(void) { if(ehcidebugcapio == nil) return; return; ctlr.oq = qopen(8*1024, 0, nil, nil); if(ctlr.oq == nil) return; debug = 0; addconsdev(ctlr.oq, usbputs, ctlr.consid, 0); addclock0link(usbclock, 10); ctlr.iq = qopen(8*1024, 0, nil, nil); if(ctlr.iq == nil) return; addkbdq(ctlr.iq, -1); } /* * Scan the bus and enable the first debug port found. * Perhaps we should search all for a ctlr specified by port * e.g., console=usb port=xxxx * or by some other means. */ void usbconsole(void) { static int already = 0; uint ptr; Pcidev *p; char *s; if(already++ != 0) return; if((s = getconf("console")) == nil || strncmp(s, "usb", 3) != 0) if(debug == 0) return; dprint("usb console..."); p = nil; while ((p = pcimatch(p, 0, 0)) != nil) { /* * Find EHCI controllers (Programming Interface = 0x20). */ if(p->ccrb != Pcibcserial || p->ccru != Pciscusb || p->ccrp != 0x20) continue; if((pcicfgr16(p, PciPSR) & 0x0010) == 0) continue; /* * We have extended caps. search list for ehci debug port. */ ptr = pcicfgr32(p, 0x34); while(ptr >= 0x40 && (ptr & ~0xFC) == 0){ if(pcicfgr8(p, ptr) == Pdebugport){ dprint("found\n"); if(ucinit(p, ptr) >= 0){ ctlr.consid = addconsdev(nil, usbputs, -1, 0); print("1"); print("2"); print("3"); print("4"); print("5"); print("6"); print("7"); print("Plan 9 usb console\n"); } return; } ptr = pcicfgr8(p, ptr+1); } } dprint("not found\n"); } /* * This driver does not really serve any file. However, we want to * setup the console at reset time. */ static Chan* usbconsattach(char*) { error(Eperm); return nil; } Dev usbconsdevtab = { L'↔', "usbcons", usbconsreset, devinit, devshutdown, usbconsattach, /* all others set to nil. Attachments are not allowed */ }; ++) dprint("%02ux ", b[i]); } dprint(" csw %#ulx\n", dbgio->csw); } return len; } /* * BUG: This is not a generic usb cmd tool. * If you call this be sure it works for the type of call you are making. * This is just for what this driver uses. Compare this with the * general purpose ehci control transfer dance. */ static int uccmd(Edbgio *dbgio, cons/fns.h 644 0 0 11171 11231562730 10724ustar00nemonemo#include "../port/portfns.h" void aamloop(int); Dirtab* addarchfile(char*, int, long(*)(Chan*,void*,long,vlong), long(*)(Chan*,void*,long,vlong)); void archinit(void); int bios32call(BIOS32ci*, u16int[3]); int bios32ci(BIOS32si*, BIOS32ci*); void bios32close(BIOS32si*); BIOS32si* bios32open(char*); void bootargs(void*); ulong cankaddr(ulong); void clockintr(Ureg*, void*); int (*cmpswap)(long*, long, long); int cmpswap486(long*, long, long); void (*coherence)(void); void cpuid(char*, int*, int*); int cpuidentify(void); void cpuidprint(void); void (*cycles)(uvlong*); void delay(int); int dmacount(int); int dmadone(int); void dmaend(int); int dmainit(int, int); long dmasetup(int, void*, long, int); #define evenaddr(x) /* x86 doesn't care */ void fpclear(void); void fpenv(FPsave*); void fpinit(void); void fpoff(void); void fprestore(FPsave*); void fpsave(FPsave*); ulong fpstatus(void); ulong getcr0(void); ulong getcr2(void); ulong getcr3(void); ulong getcr4(void); char* getconf(char*); void guesscpuhz(int); void halt(void); int i8042auxcmd(int); int i8042auxcmds(uchar*, int); void i8042auxenable(void (*)(int, int)); void i8042reset(void); void i8250console(void); void* i8250alloc(int, int, int); void i8250mouse(char*, int (*)(Queue*, int), int); void i8250setmouseputc(char*, int (*)(Queue*, int)); void i8253enable(void); void i8253init(void); void i8253link(void); uvlong i8253read(uvlong*); void i8253timerset(uvlong); int i8259disable(int); int i8259enable(Vctl*); void i8259init(void); int i8259isr(int); void i8259on(void); void i8259off(void); int i8259vecno(int); void idle(void); void idlehands(void); int inb(int); void insb(int, void*, int); ushort ins(int); void inss(int, void*, int); ulong inl(int); void insl(int, void*, int); int intrdisable(int, void (*)(Ureg *, void *), void*, int, char*); void intrenable(int, void (*)(Ureg*, void*), void*, int, char*); void introff(void); void intron(void); void invlpg(ulong); void iofree(int); void ioinit(void); int iounused(int, int); int ioalloc(int, int, int, char*); int ioreserve(int, int, int, char*); int iprint(char*, ...); int isaconfig(char*, int, ISAConf*); void* kaddr(ulong); void kbdenable(void); void kbdinit(void); #define kmapinval() void lgdt(ushort[3]); void lidt(ushort[3]); void links(void); void ltr(ulong); void mach0init(void); void mathinit(void); void mb386(void); void mb586(void); void meminit(void); void memorysummary(void); void mfence(void); #define mmuflushtlb(pdb) putcr3(pdb) void mmuinit(void); ulong* mmuwalk(ulong*, ulong, int, int); uchar nvramread(int); void nvramwrite(int, uchar); void outb(int, int); void outsb(int, void*, int); void outs(int, ushort); void outss(int, void*, int); void outl(int, ulong); void outsl(int, void*, int); ulong paddr(void*); ulong pcibarsize(Pcidev*, int); void pcibussize(Pcidev*, ulong*, ulong*); int pcicfgr8(Pcidev*, int); int pcicfgr16(Pcidev*, int); int pcicfgr32(Pcidev*, int); void pcicfgw8(Pcidev*, int, int); void pcicfgw16(Pcidev*, int, int); void pcicfgw32(Pcidev*, int, int); void pciclrbme(Pcidev*); void pciclrioe(Pcidev*); void pciclrmwi(Pcidev*); int pcigetpms(Pcidev*); void pcihinv(Pcidev*); uchar pciipin(Pcidev*, uchar); Pcidev* pcimatch(Pcidev*, int, int); Pcidev* pcimatchtbdf(int); void pcireset(void); int pciscan(int, Pcidev**); void pcisetbme(Pcidev*); void pcisetioe(Pcidev*); void pcisetmwi(Pcidev*); int pcisetpms(Pcidev*, int); void pcmcisread(PCMslot*); int pcmcistuple(int, int, int, void*, int); PCMmap* pcmmap(int, ulong, int, int); int pcmspecial(char*, ISAConf*); int (*_pcmspecial)(char *, ISAConf *); void pcmspecialclose(int); void (*_pcmspecialclose)(int); void pcmunmap(int, PCMmap*); int pdbmap(ulong*, ulong, ulong, int); void procrestore(Proc*); void procsave(Proc*); void procsetup(Proc*); void putcr3(ulong); void putcr4(ulong); void* rampage(void); void rdmsr(int, vlong*); void realmode(Ureg*); void screeninit(void); void syncclock(void); void* tmpmap(Page*); void tmpunmap(void*); void touser(void*); void trapenable(int, void (*)(Ureg*, void*), void*, char*); void trapinit(void); void trapinit0(void); int tas(void*); uvlong tscticks(uvlong*); ulong umbmalloc(ulong, int, int); void umbfree(ulong, int); ulong umbrwmalloc(ulong, int, int); void umbrwfree(ulong, int); ulong upaalloc(int, int); void upafree(ulong, int); void upareserve(ulong, int); void usbconsole(void); #define userureg(ur) (((ur)->cs & 0xFFFF) == UESEL) void vectortable(void); void* vmap(ulong, int); int vmapsync(ulong); void vunmap(void*, int); void wrmsr(int, vlong); int xchgw(ushort*, int); #define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1])) #define KADDR(a) kaddr(a) #define PADDR(a) paddr((void*)(a)) #define dcflush(a, b) nt("ucreset\n"); /* * Turn off legacy mode. */ ehcirun(ctlr, 0); pcicfgw16(ctlr->pcidev, 0xc0, 0x2000); /* clear high 32 bits of address signals if it's 64 bits. * This is probably not needed but it does not hurt. */ opio = ctlr->opio; if((ctlr->capio->capparms & C64) != 0){ dprint("ehci: 64 bits\n"); opio->seg = 0; } opio->cmd |= Chcreset; /* controller reset */ focons/kbd.c 644 0 0 32022 11231562730 10667ustar00nemonemo/* * keyboard input */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" static Queue *kbdq; enum { Data= 0x60, /* data port */ Status= 0x64, /* status port */ Inready= 0x01, /* input character ready */ Outbusy= 0x02, /* output busy */ Sysflag= 0x04, /* system flag */ Cmddata= 0x08, /* cmd==0, data==1 */ Inhibit= 0x10, /* keyboard/mouse inhibited */ Minready= 0x20, /* mouse character ready */ Rtimeout= 0x40, /* general timeout */ Parity= 0x80, Cmd= 0x64, /* command port (write only) */ Spec= 0xF800, /* Unicode private space */ PF= Spec|0x20, /* num pad function key */ View= Spec|0x00, /* view (shift window up) */ KF= 0xF000, /* function key (begin Unicode private space) */ Shift= Spec|0x60, Break= Spec|0x61, Ctrl= Spec|0x62, Latin= Spec|0x63, Caps= Spec|0x64, Num= Spec|0x65, Middle= Spec|0x66, Altgr= Spec|0x67, Kmouse= Spec|0x100, No= 0x00, /* peter */ Home= KF|13, Up= KF|14, Pgup= KF|15, Print= KF|16, Left= KF|17, Right= KF|18, End= KF|24, Down= View, Pgdown= KF|19, Ins= KF|20, Del= 0x7F, Scroll= KF|21, Nscan= 128, }; /* * The codes at 0x79 and 0x7b are produed by the PFU Happy Hacking keyboard. * A 'standard' keyboard doesn't produce anything above 0x58. */ Rune kbtab[Nscan] = { [0x00] No, 0x1b, '1', '2', '3', '4', '5', '6', [0x08] '7', '8', '9', '0', '-', '=', '\b', '\t', [0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', [0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's', [0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', [0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v', [0x30] 'b', 'n', 'm', ',', '.', '/', Shift, '*', [0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, [0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7', [0x48] '8', '9', '-', '4', '5', '6', '+', '1', [0x50] '2', '3', '0', '.', No, No, No, KF|11, [0x58] KF|12, No, No, No, No, No, No, No, [0x60] No, No, No, No, No, No, No, No, [0x68] No, No, No, No, No, No, No, No, [0x70] No, No, No, No, No, No, No, No, [0x78] No, View, No, Up, No, No, No, No, }; Rune kbtabshift[Nscan] = { [0x00] No, 0x1b, '!', '@', '#', '$', '%', '^', [0x08] '&', '*', '(', ')', '_', '+', '\b', '\t', [0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', [0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S', [0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', [0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V', [0x30] 'B', 'N', 'M', '<', '>', '?', Shift, '*', [0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5, [0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7', [0x48] '8', '9', '-', '4', '5', '6', '+', '1', [0x50] '2', '3', '0', '.', No, No, No, KF|11, [0x58] KF|12, No, No, No, No, No, No, No, [0x60] No, No, No, No, No, No, No, No, [0x68] No, No, No, No, No, No, No, No, [0x70] No, No, No, No, No, No, No, No, [0x78] No, Up, No, Up, No, No, No, No, }; Rune kbtabesc1[Nscan] = { [0x00] No, No, No, No, No, No, No, No, [0x08] No, No, No, No, No, No, No, No, [0x10] No, No, No, No, No, No, No, No, [0x18] No, No, No, No, '\n', Ctrl, No, No, [0x20] No, No, No, No, No, No, No, No, [0x28] No, No, Shift, No, No, No, No, No, [0x30] No, No, No, No, No, '/', No, Print, [0x38] Altgr, No, No, No, No, No, No, No, [0x40] No, No, No, No, No, No, Break, Home, [0x48] Up, Pgup, No, Left, No, Right, No, End, [0x50] Down, Pgdown, Ins, Del, No, No, No, No, [0x58] No, No, No, No, No, No, No, No, [0x60] No, No, No, No, No, No, No, No, [0x68] No, No, No, No, No, No, No, No, [0x70] No, No, No, No, No, No, No, No, [0x78] No, Up, No, No, No, No, No, No, }; Rune kbtabaltgr[Nscan] = { [0x00] No, No, No, No, No, No, No, No, [0x08] No, No, No, No, No, No, No, No, [0x10] No, No, No, No, No, No, No, No, [0x18] No, No, No, No, '\n', Ctrl, No, No, [0x20] No, No, No, No, No, No, No, No, [0x28] No, No, Shift, No, No, No, No, No, [0x30] No, No, No, No, No, '/', No, Print, [0x38] Altgr, No, No, No, No, No, No, No, [0x40] No, No, No, No, No, No, Break, Home, [0x48] Up, Pgup, No, Left, No, Right, No, End, [0x50] Down, Pgdown, Ins, Del, No, No, No, No, [0x58] No, No, No, No, No, No, No, No, [0x60] No, No, No, No, No, No, No, No, [0x68] No, No, No, No, No, No, No, No, [0x70] No, No, No, No, No, No, No, No, [0x78] No, Up, No, No, No, No, No, No, }; Rune kbtabctrl[Nscan] = { [0x00] No, '', '', '', '', '', '', '', [0x08] '', '', '', '', ' ', '', '\b', '\t', [0x10] '', '', '', '', '', '', '', '\t', [0x18] '', '', '', '', '\n', Ctrl, '', '', [0x20] '', '', '', '\b', '\n', ' ', ' ', '', [0x28] '', No, Shift, '', '', '', '', '', [0x30] '', '', ' ', ' ', '', '', Shift, '\n', [0x38] Latin, No, Ctrl, '', '', '', '', '', [0x40] '', '', ' ', ' ', '', '', '', '', [0x48] '', '', ' ', '', '', '', ' ', '', [0x50] '', '', '', '', No, No, No, '', [0x58] ' ', No, No, No, No, No, No, No, [0x60] No, No, No, No, No, No, No, No, [0x68] No, No, No, No, No, No, No, No, [0x70] No, No, No, No, No, No, No, No, [0x78] No, '', No, '\b', No, No, No, No, }; enum { /* controller command byte */ Cscs1= (1<<6), /* scan code set 1 */ Cauxdis= (1<<5), /* mouse disable */ Ckbddis= (1<<4), /* kbd disable */ Csf= (1<<2), /* system flag */ Cauxint= (1<<1), /* mouse interrupt enable */ Ckbdint= (1<<0), /* kbd interrupt enable */ }; int mouseshifted; void (*kbdmouse)(int); static Lock i8042lock; static uchar ccc; static void (*auxputc)(int, int); static int nokbd = 1; /* * wait for output no longer busy */ static int outready(void) { int tries; for(tries = 0; (inb(Status) & Outbusy); tries++){ if(tries > 500) return -1; delay(2); } return 0; } /* * wait for input */ static int inready(void) { int tries; for(tries = 0; !(inb(Status) & Inready); tries++){ if(tries > 500) return -1; delay(2); } return 0; } /* * ask 8042 to reset the machine */ void i8042reset(void) { int i, x; if(nokbd) return; *((ushort*)KADDR(0x472)) = 0x1234; /* BIOS warm-boot flag */ /* * newer reset the machine command */ outready(); outb(Cmd, 0xFE); outready(); /* * Pulse it by hand (old somewhat reliable) */ x = 0xDF; for(i = 0; i < 5; i++){ x ^= 1; outready(); outb(Cmd, 0xD1); outready(); outb(Data, x); /* toggle reset */ delay(100); } } int i8042auxcmd(int cmd) { unsigned int c; int tries; c = 0; tries = 0; ilock(&i8042lock); do{ if(tries++ > 2) break; if(outready() < 0) break; outb(Cmd, 0xD4); if(outready() < 0) break; outb(Data, cmd); if(outready() < 0) break; if(inready() < 0) break; c = inb(Data); } while(c == 0xFE || c == 0); iunlock(&i8042lock); if(c != 0xFA){ print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd); return -1; } return 0; } int i8042auxcmds(uchar *cmd, int ncmd) { int i; ilock(&i8042lock); for(i=0; iesc1 = 1; return; } else if(c == 0xe1){ kbscan->esc2 = 2; return; } keyup = c & 0x80; c &= 0x7f; if(c > sizeof kbtab){ c |= keyup; if(c != 0xFF) /* these come fairly often: CAPSLOCK U Y */ print("unknown key %ux\n", c); return; } if(kbscan->esc1){ c = kbtabesc1[c]; kbscan->esc1 = 0; } else if(kbscan->esc2){ kbscan->esc2--; return; } else if(kbscan->shift) c = kbtabshift[c]; else if(kbscan->altgr) c = kbtabaltgr[c]; else if(kbscan->ctl) c = kbtabctrl[c]; else c = kbtab[c]; if(kbscan->caps && c<='z' && c>='a') c += 'A' - 'a'; /* * keyup only important for shifts */ if(keyup){ switch(c){ case Latin: kbscan->alt = 0; break; case Shift: kbscan->shift = 0; mouseshifted = 0; if(kdebug) print("shiftclr\n"); break; case Ctrl: kbscan->ctl = 0; break; case Altgr: kbscan->altgr = 0; break; case Kmouse|1: case Kmouse|2: case Kmouse|3: case Kmouse|4: case Kmouse|5: kbscan->buttons &= ~(1<<(c-Kmouse-1)); if(kbdmouse) kbdmouse(kbscan->buttons); break; } return; } /* * normal character */ if(!(c & (Spec|KF))){ if(kbscan->ctl) if(kbscan->alt && c == Del) exit(0); if(!kbscan->collecting){ kbdputc(kbdq, c); return; } kbscan->kc[kbscan->nk++] = c; c = latin1(kbscan->kc, kbscan->nk); if(c < -1) /* need more keystrokes */ return; if(c != -1) /* valid sequence */ kbdputc(kbdq, c); else /* dump characters */ for(i=0; ink; i++) kbdputc(kbdq, kbscan->kc[i]); kbscan->nk = 0; kbscan->collecting = 0; return; } else { switch(c){ case Caps: kbscan->caps ^= 1; return; case Num: kbscan->num ^= 1; return; case Shift: kbscan->shift = 1; if(kdebug) print("shift\n"); mouseshifted = 1; return; case Latin: kbscan->alt = 1; /* * VMware and Qemu use Ctl-Alt as the key combination * to make the VM give up keyboard and mouse focus. * This has the unfortunate side effect that when you * come back into focus, Plan 9 thinks you want to type * a compose sequence (you just typed alt). * * As a clumsy hack around this, we look for ctl-alt * and don't treat it as the start of a compose sequence. */ if(!kbscan->ctl){ kbscan->collecting = 1; kbscan->nk = 0; } return; case Ctrl: kbscan->ctl = 1; return; case Altgr: kbscan->altgr = 1; return; case Kmouse|1: case Kmouse|2: case Kmouse|3: case Kmouse|4: case Kmouse|5: kbscan->buttons |= 1<<(c-Kmouse-1); if(kbdmouse) kbdmouse(kbscan->buttons); return; case KF|11: kdebug = 1; break; case KF|12: kdebug = 0; break; } } kbdputc(kbdq, c); } /* * keyboard interrupt */ static void i8042intr(Ureg*, void*) { int s, c; /* * get status */ ilock(&i8042lock); s = inb(Status); if(!(s&Inready)){ iunlock(&i8042lock); return; } /* * get the character */ c = inb(Data); iunlock(&i8042lock); /* * if it's the aux port... */ if(s & Minready){ if(auxputc != nil) auxputc(c, kbscans[0].shift); /* internal source */ return; } kbdputsc(c, 0); /* internal source */ } void i8042auxenable(void (*putc)(int, int)) { char *err = "i8042: aux init failed\n"; /* enable kbd/aux xfers and interrupts */ ccc &= ~Cauxdis; ccc |= Cauxint; ilock(&i8042lock); if(outready() < 0) print(err); outb(Cmd, 0x60); /* write control register */ if(outready() < 0) print(err); outb(Data, ccc); if(outready() < 0) print(err); outb(Cmd, 0xA8); /* auxilliary device enable */ if(outready() < 0){ iunlock(&i8042lock); return; } auxputc = putc; intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux"); iunlock(&i8042lock); } static char *initfailed = "i8042: kbdinit failed\n"; static int outbyte(int port, int c) { outb(port, c); if(outready() < 0) { print(initfailed); return -1; } return 0; } void kbdinit(void) { int c, try; /* wait for a quiescent controller */ try = 1000; while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) { if(c & Inready) inb(Data); delay(1); } if (try <= 0) { print(initfailed); return; } /* get current controller command byte */ outb(Cmd, 0x20); if(inready() < 0){ print("i8042: kbdinit can't read ccc\n"); ccc = 0; } else ccc = inb(Data); /* enable kbd xfers and interrupts */ ccc &= ~Ckbddis; ccc |= Csf | Ckbdint | Cscs1; if(outready() < 0) { print(initfailed); return; } nokbd = 0; /* disable mouse */ if (outbyte(Cmd, 0x60) < 0 || outbyte(Data, ccc) < 0) print("i8042: kbdinit mouse disable failed\n"); } void kbdenable(void) { kbdq = qopen(4*1024, 0, 0, 0); if(kbdq == nil) panic("kbdinit"); qnoblock(kbdq, 1); addkbdq(kbdq, -1); ioalloc(Data, 1, 0, "kbd"); ioalloc(Cmd, 1, 0, "kbd"); intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd"); } void kbdputmap(ushort m, ushort scanc, Rune r) { if(scanc >= Nscan) error(Ebadarg); switch(m) { default: error(Ebadarg); case 0: kbtab[scanc] = r; break; case 1: kbtabshift[scanc] = r; break; case 2: kbtabesc1[scanc] = r; break; case 3: kbtabaltgr[scanc] = r; break; case 4: kbtabctrl[scanc] = r; break; } } int kbdgetmap(uint offset, int *t, int *sc, Rune *r) { if ((int)offset < 0) error(Ebadarg); *t = offset/Nscan; *sc = offset%Nscan; switch(*t) { default: return 0; case 0: *r = kbtab[*sc]; return 1; case 1: *r = kbtabshift[*sc]; return 1; case 2: *r = kbtabesc1[*sc]; return 1; case 3: *r = kbtabaltgr[*sc]; return 1; case 4: *r = kbtabctrl[*sc]; return 1; } } , No, [0x20] No, No, No, No, No, No, No, No, [0x28] No, No, Shift, No, No, No, No, No, [0x30] No, No, No, No, No, '/', No, Print, [0x38] Altgr, No, No, No, No, No, No, No, [0x40] No, No, No, No, No, No, Break, Home, [0x48] Up, Pgup, No, Left, No, Right, No, End, [0x50] Down, Pgdown, Ins, Del, No, No, No, No, [0x58] No, No, No, No, No, No, No, No, [0x60] No, No, No, No, No, No, No, No, [0x68] No, No, No, No, No, No, No, No, [0x70] No, No, No, No, No, No, No, No, [0x78] No, Up, No, No, No, Ncons/main.c 644 0 0 36106 11231562730 11062ustar00nemonemo#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "init.h" #include "pool.h" #include "reboot.h" Mach *m; /* * Where configuration info is left for the loaded programme. * This will turn into a structure as more is done by the boot loader * (e.g. why parse the .ini file twice?). * There are 3584 bytes available at CONFADDR. */ #define BOOTLINE ((char*)CONFADDR) #define BOOTLINELEN 64 #define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN)) #define BOOTARGSLEN (4096-0x200-BOOTLINELEN) #define MAXCONF 64 char bootdisk[KNAMELEN]; Conf conf; char *confname[MAXCONF]; char *confval[MAXCONF]; int nconf; uchar *sp; /* user stack of init proc */ int delaylink; static void options(void) { long i, n; char *cp, *line[MAXCONF], *p, *q; /* * parse configuration args from dos file plan9.ini */ cp = BOOTARGS; /* where b.com leaves its config */ cp[BOOTARGSLEN-1] = 0; /* * Strip out '\r', change '\t' -> ' '. */ p = cp; for(q = cp; *q; q++){ if(*q == '\r') continue; if(*q == '\t') *q = ' '; *p++ = *q; } *p = 0; n = getfields(cp, line, MAXCONF, 1, "\n"); for(i = 0; i < n; i++){ if(*line[i] == '#') continue; cp = strchr(line[i], '='); if(cp == nil) continue; *cp++ = '\0'; confname[nconf] = line[i]; confval[nconf] = cp; nconf++; } } extern void mmuinit0(void); extern void (*i8237alloc)(void); void main(void) { mach0init(); options(); ioinit(); i8250console(); quotefmtinstall(); screeninit(); print("\nPlan 9\n"); trapinit0(); mmuinit0(); kbdinit(); i8253init(); cpuidentify(); meminit(); confinit(); archinit(); xinit(); if(i8237alloc != nil) i8237alloc(); trapinit(); printinit(); cpuidprint(); mmuinit(); if(arch->intrinit) /* launches other processors on an mp */ arch->intrinit(); // usbconsole(); /* requires pci which requires malloc, and vmap */ timersinit(); mathinit(); kbdenable(); if(arch->clockenable) arch->clockenable(); procinit0(); initseg(); if(delaylink){ bootlinks(); pcimatch(0, 0, 0); }else links(); conf.monitor = 1; chandevreset(); pageinit(); i8253link(); swapinit(); userinit(); active.thunderbirdsarego = 1; schedinit(); } void mach0init(void) { conf.nmach = 1; MACHP(0) = (Mach*)CPU0MACH; m->pdb = (ulong*)CPU0PDB; m->gdt = (Segdesc*)CPU0GDT; machinit(); active.machs = 1; active.exiting = 0; } void machinit(void) { int machno; ulong *pdb; Segdesc *gdt; machno = m->machno; pdb = m->pdb; gdt = m->gdt; memset(m, 0, sizeof(Mach)); m->machno = machno; m->pdb = pdb; m->gdt = gdt; m->perf.period = 1; /* * For polled uart output at boot, need * a default delay constant. 100000 should * be enough for a while. Cpuidentify will * calculate the real value later. */ m->loopconst = 100000; } void init0(void) { int i; char buf[2*KNAMELEN]; up->nerrlab = 0; spllo(); /* * These are o.k. because rootinit is null. * Then early kproc's will have a root and dot. */ up->slash = namec("#/", Atodir, 0, 0); pathclose(up->slash->path); up->slash->path = newpath("/"); up->dot = cclone(up->slash); chandevinit(); if(!waserror()){ snprint(buf, sizeof(buf), "%s %s", arch->id, conffile); ksetenv("terminal", buf, 0); ksetenv("cputype", "386", 0); if(cpuserver) ksetenv("service", "cpu", 0); else ksetenv("service", "terminal", 0); for(i = 0; i < nconf; i++){ if(confname[i][0] != '*') ksetenv(confname[i], confval[i], 0); ksetenv(confname[i], confval[i], 1); } poperror(); } kproc("alarm", alarmkproc, 0); touser(sp); } void userinit(void) { void *v; Proc *p; Segment *s; Page *pg; p = newproc(); p->pgrp = newpgrp(); p->egrp = smalloc(sizeof(Egrp)); p->egrp->ref = 1; p->fgrp = dupfgrp(nil); p->rgrp = newrgrp(); p->procmode = 0640; kstrdup(&eve, ""); kstrdup(&p->text, "*init*"); kstrdup(&p->user, eve); p->fpstate = FPinit; fpoff(); /* * Kernel Stack * * N.B. make sure there's enough space for syscall to check * for valid args and * 4 bytes for gotolabel's return PC */ p->sched.pc = (ulong)init0; p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD); /* * User Stack * * N.B. cannot call newpage() with clear=1, because pc kmap * requires up != nil. use tmpmap instead. */ s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG); p->seg[SSEG] = s; pg = newpage(0, 0, USTKTOP-BY2PG); v = tmpmap(pg); memset(v, 0, BY2PG); segpage(s, pg); bootargs(v); tmpunmap(v); /* * Text */ s = newseg(SG_TEXT, UTZERO, 1); s->flushme++; p->seg[TSEG] = s; pg = newpage(0, 0, UTZERO); memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl)); segpage(s, pg); v = tmpmap(pg); memset(v, 0, BY2PG); memmove(v, initcode, sizeof initcode); tmpunmap(v); ready(p); } uchar * pusharg(char *p) { int n; n = strlen(p)+1; sp -= n; memmove(sp, p, n); return sp; } void bootargs(void *base) { int i, ac; uchar *av[32]; uchar **lsp; char *cp = BOOTLINE; char buf[64]; sp = (uchar*)base + BY2PG - MAXSYSARG*BY2WD; ac = 0; av[ac++] = pusharg("/386/9dos"); /* when boot is changed to only use rc, this code can go away */ cp[BOOTLINELEN-1] = 0; buf[0] = 0; if(strncmp(cp, "fd", 2) == 0){ sprint(buf, "local!#f/fd%lddisk", strtol(cp+2, 0, 0)); av[ac++] = pusharg(buf); } else if(strncmp(cp, "sd", 2) == 0){ sprint(buf, "local!#S/sd%c%c/fs", *(cp+2), *(cp+3)); av[ac++] = pusharg(buf); } else if(strncmp(cp, "ether", 5) == 0) av[ac++] = pusharg("-n"); /* 4 byte word align stack */ sp = (uchar*)((ulong)sp & ~3); /* build argc, argv on stack */ sp -= (ac+1)*sizeof(sp); lsp = (uchar**)sp; for(i = 0; i < ac; i++) *lsp++ = av[i] + ((USTKTOP - BY2PG) - (ulong)base); *lsp = 0; sp += (USTKTOP - BY2PG) - (ulong)base - sizeof(ulong); } char* getconf(char *name) { int i; for(i = 0; i < nconf; i++) if(cistrcmp(confname[i], name) == 0) return confval[i]; return 0; } static void writeconf(void) { char *p, *q; int n; p = getconfenv(); if(waserror()) { free(p); nexterror(); } /* convert to name=value\n format */ for(q=p; *q; q++) { q += strlen(q); *q = '='; q += strlen(q); *q = '\n'; } n = q - p + 1; if(n >= BOOTARGSLEN) error("kernel configuration too large"); memset(BOOTLINE, 0, BOOTLINELEN); memmove(BOOTARGS, p, n); poperror(); free(p); } void confinit(void) { char *p; int i, userpcnt; ulong kpages; if(p = getconf("*kernelpercent")) userpcnt = 100 - strtol(p, 0, 0); else userpcnt = 0; conf.npage = 0; for(i=0; i 2000) conf.nproc = 2000; conf.nimage = 200; conf.nswap = conf.nproc*80; conf.nswppo = 4096; if(cpuserver) { if(userpcnt < 10) userpcnt = 70; kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Hack for the big boys. Only good while physmem < 4GB. * Give the kernel fixed max + enough to allocate the * page pool. * This is an overestimate as conf.upages < conf.npages. * The patch of nimage is a band-aid, scanning the whole * page list in imagereclaim just takes too long. */ if(kpages > (64*MB + conf.npage*sizeof(Page))/BY2PG){ kpages = (64*MB + conf.npage*sizeof(Page))/BY2PG; conf.nimage = 2000; kpages += (conf.nproc*KSTACK)/BY2PG; } } else { if(userpcnt < 10) { if(conf.npage*BY2PG < 16*MB) userpcnt = 40; else userpcnt = 60; } kpages = conf.npage - (conf.npage*userpcnt)/100; /* * Make sure terminals with low memory get at least * 4MB on the first Image chunk allocation. */ if(conf.npage*BY2PG < 16*MB) imagmem->minarena = 4*1024*1024; } /* * can't go past the end of virtual memory * (ulong)-KZERO is 2^32 - KZERO */ if(kpages > ((ulong)-KZERO)/BY2PG) kpages = ((ulong)-KZERO)/BY2PG; conf.upages = conf.npage - kpages; conf.ialloc = (kpages/2)*BY2PG; /* * Guess how much is taken by the large permanent * datastructures. Mntcache and Mntrpc are not accounted for * (probably ~300KB). */ kpages *= BY2PG; kpages -= conf.upages*sizeof(Page) + conf.nproc*sizeof(Proc) + conf.nimage*sizeof(Image) + conf.nswap + conf.nswppo*sizeof(Page); mainmem->maxsize = kpages; if(p != nil || !cpuserver){ /* * give terminals lots of image memory, too; the dynamic * allocation will balance the load properly, hopefully. * be careful with 32-bit overflow. */ imagmem->maxsize = kpages; } } static char* mathmsg[] = { nil, /* handled below */ "denormalized operand", "division by zero", "numeric overflow", "numeric underflow", "precision loss", }; static void mathnote(void) { int i; ulong status; char *msg, note[ERRMAX]; status = up->fpsave.status; /* * Some attention should probably be paid here to the * exception masks and error summary. */ msg = "unknown exception"; for(i = 1; i <= 5; i++){ if(!((1<fpsave.pc, status); postnote(up, 1, note, NDebug); } /* * math coprocessor error */ static void matherror(Ureg *ur, void*) { /* * a write cycle to port 0xF0 clears the interrupt latch attached * to the error# line from the 387 */ if(!(m->cpuiddx & 0x01)) outb(0xF0, 0xFF); /* * save floating point state to check out error */ fpenv(&up->fpsave); mathnote(); if((ur->pc & 0xf0000000) == KZERO) panic("fp: status %ux fppc=0x%lux pc=0x%lux", up->fpsave.status, up->fpsave.pc, ur->pc); } /* * math coprocessor emulation fault */ static void mathemu(Ureg *ureg, void*) { if(up->fpstate & FPillegal){ /* someone did floating point in a note handler */ postnote(up, 1, "sys: floating point in note handler", NDebug); return; } switch(up->fpstate){ case FPinit: fpinit(); up->fpstate = FPactive; break; case FPinactive: /* * Before restoring the state, check for any pending * exceptions, there's no way to restore the state without * generating an unmasked exception. * More attention should probably be paid here to the * exception masks and error summary. */ if((up->fpsave.status & ~up->fpsave.control) & 0x07F){ mathnote(); break; } fprestore(&up->fpsave); up->fpstate = FPactive; break; case FPactive: panic("math emu pid %ld %s pc 0x%lux", up->pid, up->text, ureg->pc); break; } } /* * math coprocessor segment overrun */ static void mathover(Ureg*, void*) { pexit("math overrun", 0); } void mathinit(void) { trapenable(VectorCERR, matherror, 0, "matherror"); if(X86FAMILY(m->cpuidax) == 3) intrenable(IrqIRQ13, matherror, 0, BUSUNKNOWN, "matherror"); trapenable(VectorCNA, mathemu, 0, "mathemu"); trapenable(VectorCSO, mathover, 0, "mathover"); } /* * set up floating point for a new process */ void procsetup(Proc*p) { p->fpstate = FPinit; fpoff(); } void procrestore(Proc *p) { uvlong t; if(p->kp) return; cycles(&t); p->pcycles -= t; } /* * Save the mach dependent part of the process state. */ void procsave(Proc *p) { uvlong t; cycles(&t); p->pcycles += t; if(p->fpstate == FPactive){ if(p->state == Moribund) fpclear(); else{ /* * Fpsave() stores without handling pending * unmasked exeptions. Postnote() can't be called * here as sleep() already has up->rlock, so * the handling of pending exceptions is delayed * until the process runs again and generates an * emulation fault to activate the FPU. */ fpsave(&p->fpsave); } p->fpstate = FPinactive; } /* * While this processor is in the scheduler, the process could run * on another processor and exit, returning the page tables to * the free list where they could be reallocated and overwritten. * When this processor eventually has to get an entry from the * trashed page tables it will crash. * * If there's only one processor, this can't happen. * You might think it would be a win not to do this in that case, * especially on VMware, but it turns out not to matter. */ mmuflushtlb(PADDR(m->pdb)); } static void shutdown(int ispanic) { int ms, once; lock(&active); if(ispanic) active.ispanic = ispanic; else if(m->machno == 0 && (active.machs & (1<machno)) == 0) active.ispanic = 0; once = active.machs & (1<machno); active.machs &= ~(1<machno); active.exiting = 1; unlock(&active); if(once) iprint("cpu%d: exiting\n", m->machno); spllo(); for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){ delay(TK2MS(2)); if(active.machs == 0 && consactive() == 0) break; } if(getconf("*debug")) delay(5*60*1000); if(active.ispanic){ if(!cpuserver) for(;;) halt(); delay(10000); }else delay(1000); } void reboot(void *entry, void *code, ulong size) { void (*f)(ulong, ulong, ulong); ulong *pdb; writeconf(); shutdown(0); /* * should be the only processor running now */ print("shutting down...\n"); delay(200); splhi(); /* turn off buffered serial console */ delconsdevs(); /* shutdown devices */ chandevshutdown(); /* * Modify the machine page table to directly map the low 4MB of memory * This allows the reboot code to turn off the page mapping */ pdb = m->pdb; pdb[PDX(0)] = pdb[PDX(KZERO)]; mmuflushtlb(PADDR(pdb)); /* setup reboot trampoline function */ f = (void*)REBOOTADDR; memmove(f, rebootcode, sizeof(rebootcode)); print("rebooting...\n"); /* off we go - never to return */ coherence(); (*f)(PADDR(entry), PADDR(code), size); } void exit(int ispanic) { shutdown(ispanic); arch->reset(); } int isaconfig(char *class, int ctlrno, ISAConf *isa) { char cc[32], *p; int i; snprint(cc, sizeof cc, "%s%d", class, ctlrno); p = getconf(cc); if(p == nil) return 0; isa->type = ""; isa->nopt = tokenize(p, isa->opt, NISAOPT); for(i = 0; i < isa->nopt; i++){ p = isa->opt[i]; if(cistrncmp(p, "type=", 5) == 0) isa->type = p + 5; else if(cistrncmp(p, "port=", 5) == 0) isa->port = strtoul(p+5, &p, 0); else if(cistrncmp(p, "irq=", 4) == 0) isa->irq = strtoul(p+4, &p, 0); else if(cistrncmp(p, "dma=", 4) == 0) isa->dma = strtoul(p+4, &p, 0); else if(cistrncmp(p, "mem=", 4) == 0) isa->mem = strtoul(p+4, &p, 0); else if(cistrncmp(p, "size=", 5) == 0) isa->size = strtoul(p+5, &p, 0); else if(cistrncmp(p, "freq=", 5) == 0) isa->freq = strtoul(p+5, &p, 0); } return 1; } int cistrcmp(char *a, char *b) { int ac, bc; for(;;){ ac = *a++; bc = *b++; if(ac >= 'A' && ac <= 'Z') ac = 'a' + (ac - 'A'); if(bc >= 'A' && bc <= 'Z') bc = 'a' + (bc - 'A'); ac -= bc; if(ac) return ac; if(bc == 0) break; } return 0; } int cistrncmp(char *a, char *b, int n) { unsigned ac, bc; while(n > 0){ ac = *a++; bc = *b++; n--; if(ac >= 'A' && ac <= 'Z') ac = 'a' + (ac - 'A'); if(bc >= 'A' && bc <= 'Z') bc = 'a' + (bc - 'A'); ac -= bc; if(ac) return ac; if(bc == 0) break; } return 0; } /* * put the processor in the halt state if we've no processes to run. * an interrupt will get us going again. */ void idlehands(void) { if(conf.nmach == 1) halt(); } go away */ cp[BOOTLINELEN-1] = 0; buf[0] = 0; if(strncmp(cp, "fd", 2) == 0){ sprint(buf, "local!#f/fd%lddisk", strtol(cp+2, 0, 0)); av[ac++] = pusharg(buf); } else if(strncmp(cp, "sd", 2) == 0){ sprint(buf, "local!#S/sd%c%c/fs", *(cp+2), *(cp+3)); av[ac++] = pusharg(buf); } else if(strncmp(cp, "ether", 5) == 0) av[ac++] = pusharg("-n"); /* 4 byte word align stack */ sp = (uchar*)((ulong)sp & ~3); /* build argc, argv oncons/pack 755 0 0 353 11231562622 10571ustar00nemonemo#!/bin/rc d=/usr/nemo/src/9/cons port=(devuart.c portdat.h rdb.c devcons.c portfns.h) pc=(devusbcons.c kbd.c vga.c cga.c fns.h sd53c8xx.c main.c) for(f in $port) cp /sys/src/9/port/$f $d/$f for(f in $pc) cp /sys/src/9/pc/$f $d/$f cons/portdat.h 644 0 0 52460 11231562730 11621ustar00nemonemotypedef struct Alarms Alarms; typedef struct Block Block; typedef struct Chan Chan; typedef struct Cmdbuf Cmdbuf; typedef struct Cmdtab Cmdtab; typedef struct Confmem Confmem; typedef struct Dev Dev; typedef struct Dirtab Dirtab; typedef struct Edf Edf; typedef struct Egrp Egrp; typedef struct Evalue Evalue; typedef struct Fgrp Fgrp; typedef struct DevConf DevConf; typedef struct Image Image; typedef struct Log Log; typedef struct Logflag Logflag; typedef struct Mntcache Mntcache; typedef struct Mount Mount; typedef struct Mntrpc Mntrpc; typedef struct Mntwalk Mntwalk; typedef struct Mnt Mnt; typedef struct Mhead Mhead; typedef struct Note Note; typedef struct Page Page; typedef struct Path Path; typedef struct Palloc Palloc; typedef struct Pallocmem Pallocmem; typedef struct Perf Perf; typedef struct PhysUart PhysUart; typedef struct Pgrp Pgrp; typedef struct Physseg Physseg; typedef struct Proc Proc; typedef struct Pte Pte; typedef struct QLock QLock; typedef struct Queue Queue; typedef struct Ref Ref; typedef struct Rendez Rendez; typedef struct Rgrp Rgrp; typedef struct RWlock RWlock; typedef struct Sargs Sargs; typedef struct Schedq Schedq; typedef struct Segment Segment; typedef struct Sema Sema; typedef struct Timer Timer; typedef struct Timers Timers; typedef struct Uart Uart; typedef struct Waitq Waitq; typedef struct Walkqid Walkqid; typedef struct Watchdog Watchdog; typedef int Devgen(Chan*, char*, Dirtab*, int, int, Dir*); #pragma incomplete DevConf #pragma incomplete Edf #pragma incomplete Mntcache #pragma incomplete Mntrpc #pragma incomplete Queue #pragma incomplete Timers #include struct Ref { Lock; long ref; }; struct Rendez { Lock; Proc *p; }; struct QLock { Lock use; /* to access Qlock structure */ Proc *head; /* next process waiting for object */ Proc *tail; /* last process waiting for object */ int locked; /* flag */ }; struct RWlock { Lock use; Proc *head; /* list of waiting processes */ Proc *tail; ulong wpc; /* pc of writer */ Proc *wproc; /* writing proc */ int readers; /* number of readers */ int writer; /* number of writers */ }; struct Alarms { QLock; Proc *head; }; struct Sargs { ulong args[MAXSYSARG]; }; /* * Access types in namec & channel flags */ enum { Aaccess, /* as in stat, wstat */ Abind, /* for left-hand-side of bind */ Atodir, /* as in chdir */ Aopen, /* for i/o */ Amount, /* to be mounted or mounted upon */ Acreate, /* is to be created */ Aremove, /* will be removed by caller */ COPEN = 0x0001, /* for i/o */ CMSG = 0x0002, /* the message channel for a mount */ /*rsc CCREATE = 0x0004, /* permits creation if c->mnt */ CCEXEC = 0x0008, /* close on exec */ CFREE = 0x0010, /* not in use */ CRCLOSE = 0x0020, /* remove on close */ CCACHE = 0x0080, /* client cache */ }; /* flag values */ enum { BINTR = (1<<0), BFREE = (1<<1), Bipck = (1<<2), /* ip checksum */ Budpck = (1<<3), /* udp checksum */ Btcpck = (1<<4), /* tcp checksum */ Bpktck = (1<<5), /* packet checksum */ }; struct Block { long ref; Block* next; Block* list; uchar* rp; /* first unconsumed byte */ uchar* wp; /* first empty byte */ uchar* lim; /* 1 past the end of the buffer */ uchar* base; /* start of the buffer */ void (*free)(Block*); ushort flag; ushort checksum; /* IP checksum of complete packet (minus media header) */ }; #define BLEN(s) ((s)->wp - (s)->rp) #define BALLOC(s) ((s)->lim - (s)->base) struct Chan { Ref; /* the Lock in this Ref is also Chan's lock */ Chan* next; /* allocation */ Chan* link; vlong offset; /* in fd */ vlong devoffset; /* in underlying device; see read */ ushort type; ulong dev; ushort mode; /* read/write */ ushort flag; Qid qid; int fid; /* for devmnt */ ulong iounit; /* chunk size for i/o; 0==default */ Mhead* umh; /* mount point that derived Chan; used in unionread */ Chan* umc; /* channel in union; held for union read */ QLock umqlock; /* serialize unionreads */ int uri; /* union read index */ int dri; /* devdirread index */ uchar* dirrock; /* directory entry rock for translations */ int nrock; int mrock; QLock rockqlock; int ismtpt; Mntcache*mcp; /* Mount cache pointer */ Mnt* mux; /* Mnt for clients using me for messages */ union { void* aux; Qid pgrpid; /* for #p/notepg */ ulong mid; /* for ns in devproc */ }; Chan* mchan; /* channel to mounted server */ Qid mqid; /* qid of root of mount point */ Path* path; }; struct Path { Ref; char *s; Chan **mtpt; /* mtpt history */ int len; /* strlen(s) */ int alen; /* allocated length of s */ int mlen; /* number of path elements */ int malen; /* allocated length of mtpt */ }; struct Dev { int dc; char* name; void (*reset)(void); void (*init)(void); void (*shutdown)(void); Chan* (*attach)(char*); Walkqid*(*walk)(Chan*, Chan*, char**, int); int (*stat)(Chan*, uchar*, int); Chan* (*open)(Chan*, int); void (*create)(Chan*, char*, int, ulong); void (*close)(Chan*); long (*read)(Chan*, void*, long, vlong); Block* (*bread)(Chan*, long, ulong); long (*write)(Chan*, void*, long, vlong); long (*bwrite)(Chan*, Block*, ulong); void (*remove)(Chan*); int (*wstat)(Chan*, uchar*, int); void (*power)(int); /* power mgt: power(1) => on, power (0) => off */ int (*config)(int, char*, DevConf*); /* returns nil on error */ }; struct Dirtab { char name[KNAMELEN]; Qid qid; vlong length; long perm; }; struct Walkqid { Chan *clone; int nqid; Qid qid[1]; }; enum { NSMAX = 1000, NSLOG = 7, NSCACHE = (1<ref; channels on this mount point incref(c->mchan) == Mnt.c */ Chan *c; /* Channel to file service */ Proc *rip; /* Reader in progress */ Mntrpc *queue; /* Queue of pending requests on this channel */ ulong id; /* Multiplexer id for channel check */ Mnt *list; /* Free list */ int flags; /* cache */ int msize; /* data + IOHDRSZ */ char *version; /* 9P version */ Queue *q; /* input queue */ }; enum { NUser, /* note provided externally */ NExit, /* deliver note quietly */ NDebug, /* print debug message */ }; struct Note { char msg[ERRMAX]; int flag; /* whether system posted it */ }; enum { PG_NOFLUSH = 0, PG_TXTFLUSH = 1, /* flush dcache and invalidate icache */ PG_DATFLUSH = 2, /* flush both i & d caches (UNUSED) */ PG_NEWCOL = 3, /* page has been recolored */ PG_MOD = 0x01, /* software modified bit */ PG_REF = 0x02, /* software referenced bit */ }; struct Page { Lock; ulong pa; /* Physical address in memory */ ulong va; /* Virtual address for user */ ulong daddr; /* Disc address on swap */ ushort ref; /* Reference count */ char modref; /* Simulated modify/reference bits */ char color; /* Cache coloring */ char cachectl[MAXMACH]; /* Cache flushing control for putmmu */ Image *image; /* Associated text or swap image */ Page *next; /* Lru free list */ Page *prev; Page *hash; /* Image hash chains */ }; struct Swapalloc { Lock; /* Free map lock */ int free; /* currently free swap pages */ uchar* swmap; /* Base of swap map in memory */ uchar* alloc; /* Round robin allocator */ uchar* last; /* Speed swap allocation */ uchar* top; /* Top of swap map */ Rendez r; /* Pager kproc idle sleep */ ulong highwater; /* Pager start threshold */ ulong headroom; /* Space pager frees under highwater */ }swapalloc; struct Image { Ref; Chan *c; /* channel to text file */ Qid qid; /* Qid for page cache coherence */ Qid mqid; Chan *mchan; ushort type; /* Device type of owning channel */ Segment *s; /* TEXT segment for image if running */ Image *hash; /* Qid hash chains */ Image *next; /* Free list */ int notext; /* no file associated */ }; struct Pte { Page *pages[PTEPERTAB]; /* Page map for this chunk of pte */ Page **first; /* First used entry */ Page **last; /* Last used entry */ }; /* Segment types */ enum { SG_TYPE = 07, /* Mask type of segment */ SG_TEXT = 00, SG_DATA = 01, SG_BSS = 02, SG_STACK = 03, SG_SHARED = 04, SG_PHYSICAL = 05, SG_RONLY = 0040, /* Segment is read only */ SG_CEXEC = 0100, /* Detach at exec */ }; #define PG_ONSWAP 1 #define onswap(s) (((ulong)s)&PG_ONSWAP) #define pagedout(s) (((ulong)s)==0 || onswap(s)) #define swapaddr(s) (((ulong)s)&~PG_ONSWAP) #define SEGMAXSIZE (SEGMAPSIZE*PTEMAPMEM) struct Physseg { ulong attr; /* Segment attributes */ char *name; /* Attach name */ ulong pa; /* Physical address */ ulong size; /* Maximum segment size in pages */ Page *(*pgalloc)(Segment*, ulong); /* Allocation if we need it */ void (*pgfree)(Page*); }; struct Sema { Rendez; long *addr; int waiting; Sema *next; Sema *prev; }; struct Segment { Ref; QLock lk; ushort steal; /* Page stealer lock */ ushort type; /* segment type */ ulong base; /* virtual base */ ulong top; /* virtual top */ ulong size; /* size in pages */ ulong fstart; /* start address in file for demand load */ ulong flen; /* length of segment in file */ int flushme; /* maintain icache for this segment */ Image *image; /* text in file attached to this segment */ Physseg *pseg; ulong* profile; /* Tick profile area */ Pte **map; int mapsize; Pte *ssegmap[SSEGMAPSIZE]; Lock semalock; Sema sema; ulong mark; /* portcountrefs */ }; enum { RENDLOG = 5, RENDHASH = 1<rendhash[(s)&((1<mnthash[(qid).path&((1< variadic */ }; /* * routines to access UART hardware */ struct PhysUart { char* name; Uart* (*pnp)(void); void (*enable)(Uart*, int); void (*disable)(Uart*); void (*kick)(Uart*); void (*dobreak)(Uart*, int); int (*baud)(Uart*, int); int (*bits)(Uart*, int); int (*stop)(Uart*, int); int (*parity)(Uart*, int); void (*modemctl)(Uart*, int); void (*rts)(Uart*, int); void (*dtr)(Uart*, int); long (*status)(Uart*, void*, long, long); void (*fifo)(Uart*, int); void (*power)(Uart*, int); int (*getc)(Uart*); /* polling versions, for iprint, rdb */ void (*putc)(Uart*, int); }; enum { Stagesize= 2048 }; /* * software UART */ struct Uart { void* regs; /* hardware stuff */ void* saveregs; /* place to put registers on power down */ char* name; /* internal name */ ulong freq; /* clock frequency */ int bits; /* bits per character */ int stop; /* stop bits */ int parity; /* even, odd or no parity */ int baud; /* baud rate */ PhysUart*phys; int console; /* used as a serial console */ int special; /* internal kernel device */ Uart* next; /* list of allocated uarts */ QLock; int type; /* ?? */ int dev; int opens; int enabled; Uart *elist; /* next enabled interface */ int perr; /* parity errors */ int ferr; /* framing errors */ int oerr; /* rcvr overruns */ int berr; /* no input buffers */ int serr; /* input queue overflow */ /* buffers */ int (*putc)(Queue*, int); Queue *iq; Queue *oq; Lock rlock; uchar istage[Stagesize]; uchar *iw; uchar *ir; uchar *ie; Lock tlock; /* transmit */ uchar ostage[Stagesize]; uchar *op; uchar *oe; int drain; int modem; /* hardware flow control on */ int xonoff; /* software flow control on */ int blocked; int cts, dsr, dcd; /* keep track of modem status */ int ctsbackoff; int hup_dsr, hup_dcd; /* send hangup upstream? */ int dohup; Rendez r; }; extern Uart* consuart; /* * performance timers, all units in perfticks */ struct Perf { ulong intrts; /* time of last interrupt */ ulong inintr; /* time since last clock tick in interrupt handlers */ ulong avg_inintr; /* avg time per clock tick in interrupt handlers */ ulong inidle; /* time since last clock tick in idle loop */ ulong avg_inidle; /* avg time per clock tick in idle loop */ ulong last; /* value of perfticks() at last clock tick */ ulong period; /* perfticks() per clock tick */ }; struct Watchdog { void (*enable)(void); /* watchdog enable */ void (*disable)(void); /* watchdog disable */ void (*restart)(void); /* watchdog restart */ void (*stat)(char*, char*); /* watchdog statistics */ }; /* queue state bits, Qmsg, Qcoalesce, and Qkick can be set in qopen */ enum { /* Queue.state */ Qstarve = (1<<0), /* consumer starved */ Qmsg = (1<<1), /* message stream */ Qclosed = (1<<2), /* queue has been closed/hungup */ Qflow = (1<<3), /* producer flow controlled */ Qcoalesce = (1<<4), /* coallesce packets on read */ Qkick = (1<<5), /* always call the kick routine after qwrite */ }; #define DEVDOTDOT -1 #pragma varargck type "I" uchar* #pragma varargck type "V" uchar* #pragma varargck type "E" uchar* #pragma varargck type "M" uchar* timer programmed in ns from now */ Tperiodic, /* periodic timer, period in ns */ }; struct Timer { /* Public interface */ int tmode; /* See above */ vlong tns; /* meaning defined by mode */ void (*tf)cons/portfns.h 644 0 0 25705 11231562730 11641ustar00nemonemovoid _assert(char*); void accounttime(void); Timer* addclock0link(void (*)(void), int); int addphysseg(Physseg*); void addbootfile(char*, uchar*, ulong); int addconsdev(Queue*, void (*fn)(char*,int), int, int); int addkbdq(Queue*, int); void addwatchdog(Watchdog*); Block* adjustblock(Block*, int); void alarmkproc(void*); Block* allocb(int); int anyhigher(void); int anyready(void); Image* attachimage(int, Chan*, ulong, ulong); Page* auxpage(void); Block* bl2mem(uchar*, Block*, int); int blocklen(Block*); void bootlinks(void); void cachedel(Image*, ulong); void cachepage(Page*, Image*); void callwithureg(void(*)(Ureg*)); char* chanpath(Chan*); int canlock(Lock*); int canpage(Proc*); int canqlock(QLock*); int canrlock(RWlock*); void chandevinit(void); void chandevreset(void); void chandevshutdown(void); void chanfree(Chan*); void checkalarms(void); void checkb(Block*, char*); void cinit(void); Chan* cclone(Chan*); void cclose(Chan*); void ccloseq(Chan*); void closeegrp(Egrp*); void closefgrp(Fgrp*); void closepgrp(Pgrp*); void closergrp(Rgrp*); long clrfpintr(void); void cmderror(Cmdbuf*, char*); int cmount(Chan**, Chan*, int, char*); void confinit(void); int consactive(void); void (*consdebug)(void); void copen(Chan*); Block* concatblock(Block*); Block* copyblock(Block*, int); void copypage(Page*, Page*); void countpagerefs(ulong*, int); int cread(Chan*, uchar*, int, vlong); void cunmount(Chan*, Chan*); void cupdate(Chan*, uchar*, int, vlong); void cwrite(Chan*, uchar*, int, vlong); ulong dbgpc(Proc*); long decref(Ref*); int decrypt(void*, void*, int); void delay(int); void delconsdevs(void); Proc* dequeueproc(Schedq*, Proc*); Chan* devattach(int, char*); Block* devbread(Chan*, long, ulong); long devbwrite(Chan*, Block*, ulong); Chan* devclone(Chan*); int devconfig(int, char *, DevConf *); void devcreate(Chan*, char*, int, ulong); void devdir(Chan*, Qid, char*, vlong, char*, long, Dir*); long devdirread(Chan*, char*, long, Dirtab*, int, Devgen*); Devgen devgen; void devinit(void); int devno(int, int); Chan* devopen(Chan*, int, Dirtab*, int, Devgen*); void devpermcheck(char*, ulong, int); void devpower(int); void devremove(Chan*); void devreset(void); void devshutdown(void); int devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*); Walkqid* devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*); int devwstat(Chan*, uchar*, int); void drawactive(int); void drawcmap(void); void dumpaproc(Proc*); void dumpregs(Ureg*); void dumpstack(void); Fgrp* dupfgrp(Fgrp*); int duppage(Page*); void dupswap(Page*); void edfinit(Proc*); char* edfadmit(Proc*); int edfready(Proc*); void edfrecord(Proc*); void edfrun(Proc*, int); void edfstop(Proc*); void edfyield(void); int emptystr(char*); int encrypt(void*, void*, int); void envcpy(Egrp*, Egrp*); int eqchan(Chan*, Chan*, int); int eqchantdqid(Chan*, int, int, Qid, int); int eqqid(Qid, Qid); void error(char*); long execregs(ulong, ulong, ulong); void exhausted(char*); void exit(int); uvlong fastticks(uvlong*); uvlong fastticks2ns(uvlong); uvlong fastticks2us(uvlong); int fault(ulong, int); void fdclose(int, int); Chan* fdtochan(int, int, int, int); int findmount(Chan**, Mhead**, int, int, Qid); int fixfault(Segment*, ulong, int, int); void flushmmu(void); void forceclosefgrp(void); void forkchild(Proc*, Ureg*); void forkret(void); void free(void*); void freeb(Block*); void freeblist(Block*); int freebroken(void); void freepte(Segment*, Pte*); void getcolor(ulong, ulong*, ulong*, ulong*); ulong getmalloctag(void*); ulong getrealloctag(void*); void gotolabel(Label*); char* getconfenv(void); int haswaitq(void*); long hostdomainwrite(char*, int); long hostownerwrite(char*, int); void hzsched(void); Block* iallocb(int); void iallocsummary(void); long ibrk(ulong, int); void ilock(Lock*); void iunlock(Lock*); long incref(Ref*); void initseg(void); int iprint(char*, ...); void isdir(Chan*); int iseve(void); int islo(void); Segment* isoverlap(Proc*, ulong, int); int ispages(void*); int isphysseg(char*); void ixsummary(void); int kbdcr2nl(Queue*, int); int kbdgetmap(uint, int*, int*, Rune*); int kbdputc(Queue*, int); void kbdputmap(ushort, ushort, Rune); void kickpager(void); void killbig(char*); void kproc(char*, void(*)(void*), void*); void kprocchild(Proc*, void (*)(void*), void*); void (*kproftimer)(ulong); void ksetenv(char*, char*, int); void kstrcpy(char*, char*, int); void kstrdup(char**, char*); long latin1(Rune*, int); int lock(Lock*); void logopen(Log*); void logclose(Log*); char* logctl(Log*, int, char**, Logflag*); void logn(Log*, int, void*, int); long logread(Log*, void*, ulong, long); void log(Log*, int, char*, ...); Cmdtab* lookupcmd(Cmdbuf*, Cmdtab*, int); Page* lookpage(Image*, ulong); #define MS2NS(n) (((vlong)(n))*1000000LL) void machinit(void); void* mallocz(ulong, int); void* malloc(ulong); void* mallocalign(ulong, ulong, long, ulong); void mallocsummary(void); Block* mem2bl(uchar*, int); void mfreeseg(Segment*, ulong, int); void microdelay(int); uvlong mk64fract(uvlong, uvlong); void mkqid(Qid*, vlong, ulong, int); void mmurelease(Proc*); void mmuswitch(Proc*); Chan* mntauth(Chan*, char*); long mntversion(Chan*, char*, int, int); void mouseresize(void); void mountfree(Mount*); ulong ms2tk(ulong); ulong msize(void*); ulong ms2tk(ulong); uvlong ms2fastticks(ulong); void mul64fract(uvlong*, uvlong, uvlong); void muxclose(Mnt*); Chan* namec(char*, int, int, ulong); void nameerror(char*, char*); Chan* newchan(void); int newfd(Chan*); Mhead* newmhead(Chan*); Mount* newmount(Mhead*, Chan*, int, char*); Page* newpage(int, Segment **, ulong); Path* newpath(char*); Pgrp* newpgrp(void); Rgrp* newrgrp(void); Proc* newproc(void); void nexterror(void); int notify(Ureg*); int nrand(int); uvlong ns2fastticks(uvlong); int okaddr(ulong, ulong, int); int openmode(ulong); Block* packblock(Block*); Block* padblock(Block*, int); void pagechainhead(Page*); void pageinit(void); ulong pagenumber(Page*); void pagersummary(void); void panic(char*, ...); Cmdbuf* parsecmd(char *a, int n); void pathclose(Path*); ulong perfticks(void); void pexit(char*, int); void pgrpcpy(Pgrp*, Pgrp*); void pgrpnote(ulong, char*, long, int); void pio(Segment *, ulong, ulong, Page **); #define poperror() up->nerrlab-- void portcountpagerefs(ulong*, int); int postnote(Proc*, int, char*, int); int pprint(char*, ...); int preempted(void); void prflush(void); void printinit(void); ulong procalarm(ulong); void procctl(Proc*); void procdump(void); int procfdprint(Chan*, int, int, char*, int); int procindex(ulong); void procinit0(void); void procflushseg(Segment*); void procpriority(Proc*, int, int); Proc* proctab(int); extern void (*proctrace)(Proc*, int, vlong); void procwired(Proc*, int); Pte* ptealloc(void); Pte* ptecpy(Pte*); int pullblock(Block**, int); Block* pullupblock(Block*, int); Block* pullupqueue(Queue*, int); void putimage(Image*); void putmhead(Mhead*); void putmmu(ulong, ulong, Page*); void putpage(Page*); void putseg(Segment*); void putstrn(char*, int); void putswap(Page*); ulong pwait(Waitmsg*); void qaddlist(Queue*, Block*); Block* qbread(Queue*, int); long qbwrite(Queue*, Block*); Queue* qbypass(void (*)(void*, Block*), void*); int qcanread(Queue*); void qclose(Queue*); int qconsume(Queue*, void*, int); Block* qcopy(Queue*, int, ulong); int qdiscard(Queue*, int); void qflush(Queue*); void qfree(Queue*); int qfull(Queue*); Block* qget(Queue*); void qhangup(Queue*, char*); int qisclosed(Queue*); int qiwrite(Queue*, void*, int); int qlen(Queue*); void qlock(QLock*); Queue* qopen(int, int, void (*)(void*), void*); int qpass(Queue*, Block*); int qpassnolim(Queue*, Block*); int qproduce(Queue*, void*, int); void qputback(Queue*, Block*); long qread(Queue*, void*, int); Block* qremove(Queue*); void qreopen(Queue*); void qsetlimit(Queue*, int); void qunlock(QLock*); int qwindow(Queue*); int qwrite(Queue*, void*, int); void qnoblock(Queue*, int); int rand(void); void randominit(void); ulong randomread(void*, ulong); void rdb(void); int readnum(ulong, char*, ulong, ulong, int); int readstr(ulong, char*, ulong, char*); void ready(Proc*); void rebootcmd(int, char**); void reboot(void*, void*, ulong); void relocateseg(Segment*, ulong); void renameuser(char*, char*); void resched(char*); void resrcwait(char*); int return0(void*); void rlock(RWlock*); long rtctime(void); void runlock(RWlock*); Proc* runproc(void); void savefpregs(FPsave*); void sched(void); void scheddump(void); void schedinit(void); void (*screenputs)(char*, int); long seconds(void); ulong segattach(Proc*, ulong, char *, ulong, ulong); void segclock(ulong); void segpage(Segment*, Page*); int setcolor(ulong, ulong, ulong, ulong); void setkernur(Ureg*, Proc*); int setlabel(Label*); void setmalloctag(void*, ulong); void setrealloctag(void*, ulong); void setregisters(Ureg*, char*, char*, int); void setswapchan(Chan*); char* skipslash(char*); void sleep(Rendez*, int(*)(void*), void*); void* smalloc(ulong); int splhi(void); int spllo(void); void splx(int); void splxpc(int); char* srvname(Chan*); int swapcount(ulong); int swapfull(void); void swapinit(void); void timeradd(Timer*); void timerdel(Timer*); void timersinit(void); void timerintr(Ureg*, Tval); void timerset(Tval); ulong tk2ms(ulong); #define TK2MS(x) ((x)*(1000/HZ)) uvlong tod2fastticks(vlong); vlong todget(vlong*); void todsetfreq(vlong); void todinit(void); void todset(vlong, vlong, int); Block* trimblock(Block*, int, int); void tsleep(Rendez*, int (*)(void*), void*, ulong); int uartctl(Uart*, char*); int uartgetc(void); void uartkick(void*); void uartmouse(Uart*, int (*)(Queue*, int), int); void uartsetmouseputc(Uart*, int (*)(Queue*, int)); void uartputc(int); void uartputs(char*, int); void uartrecv(Uart*, char); int uartstageoutput(Uart*); void unbreak(Proc*); void uncachepage(Page*); long unionread(Chan*, void*, long); void unlock(Lock*); uvlong us2fastticks(uvlong); void userinit(void); ulong userpc(void); long userwrite(char*, int); void validaddr(ulong, ulong, int); void validname(char*, int); char* validnamedup(char*, int); void validstat(uchar*, int); void* vmemchr(void*, int, int); Proc* wakeup(Rendez*); int walk(Chan**, char**, int, int, int*); void wlock(RWlock*); void wunlock(RWlock*); void* xalloc(ulong); void* xallocz(ulong, int); void xfree(void*); void xhole(ulong, ulong); void xinit(void); int xmerge(void*, void*); void* xspanalloc(ulong, int, ulong); void xsummary(void); void yield(void); Segment* data2txt(Segment*); Segment* dupseg(Segment**, int, int); Segment* newseg(int, ulong, ulong); Segment* seg(Proc*, ulong, int); void hnputv(void*, uvlong); void hnputl(void*, uint); void hnputs(void*, ushort); uvlong nhgetv(void*); uint nhgetl(void*); ushort nhgets(void*); ulong µs(void); void _xinc(long*); long _xdec(long*); long lcycles(void); #pragma varargck argpos iprint 1 cclose(Chan*); void ccloseq(Chan*); void closeegrp(Egrp*)cons/rdb.c 644 0 0 3173 11231562730 10663ustar00nemonemo#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #define DBG if(0)scrprint #pragma varargck argpos scrprint 1 static Ureg ureg; static void scrprint(char *fmt, ...) { char buf[128]; va_list va; int n; va_start(va, fmt); n = vseprint(buf, buf+sizeof buf, fmt, va)-buf; va_end(va); putstrn(buf, n); } static char* getline(void) { static char buf[128]; int i, c; for(;;){ for(i=0; i 4){ mesg(Rerr, Ecount); break; } a = addr(min+0); scrprint("mput %.8lux\n", a); memmove(a, min+5, n); mesg(Rmput, mout); break; * */ default: DBG("unknown %c\n", *req); iprint("Eunknown message\n"); break; } } } void rdb(void) { splhi(); iprint("rdb..."); callwithureg(talkrdb); } se(int, int); Chan* fdtochan(int, int, int, int); int findmount(Chan**, Mhead**, int, int, Qid); int fixfault(Segment*, ulong, int, int); void flushmmu(void); void forceclosefgrp(void); void forkchild(Proc*, Ureg*); void forkret(void); void free(void*); void freeb(Block*); void freeblist(Block*); int freebroken(void); void freepte(Segment*, Pte*); void getcolor(ulong, ulongcons/sd53c8xx.c 644 0 0 154016 11231562730 11550ustar00nemonemo/* * NCR/Symbios/LSI Logic 53c8xx driver for Plan 9 * Nigel Roles (nigel@9fs.org) * * 27/5/02 Fixed problems with transfers >= 256 * 512 * * 13/3/01 Fixed microcode to support targets > 7 * * 01/12/00 Removed previous comments. Fixed a small problem in * mismatch recovery for targets with synchronous offsets of >=16 * connected to >=875s. Thanks, Jean. * * Known problems * * Read/write mismatch recovery may fail on 53c1010s. Really need to get a manual. */ #define MAXTARGET 16 /* can be 8 or 16 */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/sd.h" extern SDifc sd53c8xxifc; /**********************************/ /* Portable configuration macros */ /**********************************/ //#define BOOTDEBUG //#define ASYNC_ONLY //#define INTERNAL_SCLK //#define ALWAYS_DO_WDTR #define WMR_DEBUG /**********************************/ /* CPU specific macros */ /**********************************/ #define PRINTPREFIX "sd53c8xx: " #ifdef BOOTDEBUG #define KPRINT oprint #define IPRINT intrprint #define DEBUG(n) 1 #define IFLUSH() iflush() #else static int idebug = 1; #define KPRINT if(0) iprint #define IPRINT if(idebug) iprint #define DEBUG(n) (0) #define IFLUSH() #endif /* BOOTDEBUG */ /*******************************/ /* General */ /*******************************/ #ifndef DMASEG #define DMASEG(x) PCIWADDR(x) #define legetl(x) (*(ulong*)(x)) #define lesetl(x,v) (*(ulong*)(x) = (v)) #define swabl(a,b,c) #else #endif /*DMASEG */ #define DMASEG_TO_KADDR(x) KADDR((x)-PCIWINDOW) #define KPTR(x) ((x) == 0 ? 0 : DMASEG_TO_KADDR(x)) #define MEGA 1000000L #ifdef INTERNAL_SCLK #define SCLK (33 * MEGA) #else #define SCLK (40 * MEGA) #endif /* INTERNAL_SCLK */ #define ULTRA_NOCLOCKDOUBLE_SCLK (80 * MEGA) #define MAXSYNCSCSIRATE (5 * MEGA) #define MAXFASTSYNCSCSIRATE (10 * MEGA) #define MAXULTRASYNCSCSIRATE (20 * MEGA) #define MAXULTRA2SYNCSCSIRATE (40 * MEGA) #define MAXASYNCCORERATE (25 * MEGA) #define MAXSYNCCORERATE (25 * MEGA) #define MAXFASTSYNCCORERATE (50 * MEGA) #define MAXULTRASYNCCORERATE (80 * MEGA) #define MAXULTRA2SYNCCORERATE (160 * MEGA) #define X_MSG 1 #define X_MSG_SDTR 1 #define X_MSG_WDTR 3 struct na_patch { unsigned lwoff; unsigned char type; }; typedef struct Ncr { uchar scntl0; /* 00 */ uchar scntl1; uchar scntl2; uchar scntl3; uchar scid; /* 04 */ uchar sxfer; uchar sdid; uchar gpreg; uchar sfbr; /* 08 */ uchar socl; uchar ssid; uchar sbcl; uchar dstat; /* 0c */ uchar sstat0; uchar sstat1; uchar sstat2; uchar dsa[4]; /* 10 */ uchar istat; /* 14 */ uchar istatpad[3]; uchar ctest0; /* 18 */ uchar ctest1; uchar ctest2; uchar ctest3; uchar temp[4]; /* 1c */ uchar dfifo; /* 20 */ uchar ctest4; uchar ctest5; uchar ctest6; uchar dbc[3]; /* 24 */ uchar dcmd; /* 27 */ uchar dnad[4]; /* 28 */ uchar dsp[4]; /* 2c */ uchar dsps[4]; /* 30 */ uchar scratcha[4]; /* 34 */ uchar dmode; /* 38 */ uchar dien; uchar dwt; uchar dcntl; uchar adder[4]; /* 3c */ uchar sien0; /* 40 */ uchar sien1; uchar sist0; uchar sist1; uchar slpar; /* 44 */ uchar slparpad0; uchar macntl; uchar gpcntl; uchar stime0; /* 48 */ uchar stime1; uchar respid; uchar respidpad0; uchar stest0; /* 4c */ uchar stest1; uchar stest2; uchar stest3; uchar sidl; /* 50 */ uchar sidlpad[3]; uchar sodl; /* 54 */ uchar sodlpad[3]; uchar sbdl; /* 58 */ uchar sbdlpad[3]; uchar scratchb[4]; /* 5c */ } Ncr; typedef struct Movedata { uchar dbc[4]; uchar pa[4]; } Movedata; typedef enum NegoState { NeitherDone, WideInit, WideResponse, WideDone, SyncInit, SyncResponse, BothDone } NegoState; typedef enum State { Allocated, Queued, Active, Done } State; typedef struct Dsa Dsa; struct Dsa { uchar stateb; uchar result; uchar dmablks; uchar flag; /* setbyte(state,3,...) */ union { ulong dmancr; /* For block transfer: NCR order (little-endian) */ uchar dmaaddr[4]; }; uchar target; /* Target */ uchar pad0[3]; uchar lun; /* Logical Unit Number */ uchar pad1[3]; uchar scntl3; uchar sxfer; uchar pad2[2]; uchar next[4]; /* chaining for SCRIPT (NCR byte order) */ Dsa *freechain; /* chaining for freelist */ Rendez; uchar scsi_id_buf[4]; Movedata msg_out_buf; Movedata cmd_buf; Movedata data_buf; Movedata status_buf; uchar msg_out[10]; /* enough to include SDTR */ uchar status; int p9status; uchar parityerror; }; typedef enum Feature { BigFifo = 1, /* 536 byte fifo */ BurstOpCodeFetch = 2, /* burst fetch opcodes */ Prefetch = 4, /* prefetch 8 longwords */ LocalRAM = 8, /* 4K longwords of local RAM */ Differential = 16, /* Differential support */ Wide = 32, /* Wide capable */ Ultra = 64, /* Ultra capable */ ClockDouble = 128, /* Has clock doubler */ ClockQuad = 256, /* Has clock quadrupler (same as Ultra2) */ Ultra2 = 256, } Feature; typedef enum Burst { Burst2 = 0, Burst4 = 1, Burst8 = 2, Burst16 = 3, Burst32 = 4, Burst64 = 5, Burst128 = 6 } Burst; typedef struct Variant { ushort did; uchar maxrid; /* maximum allowed revision ID */ char *name; Burst burst; /* codings for max burst */ uchar maxsyncoff; /* max synchronous offset */ uchar registers; /* number of 32 bit registers */ unsigned feature; } Variant; static unsigned char cf2[] = { 6, 2, 3, 4, 6, 8, 12, 16 }; #define NULTRA2SCF (sizeof(cf2)/sizeof(cf2[0])) #define NULTRASCF (NULTRA2SCF - 2) #define NSCF (NULTRASCF - 1) typedef struct Controller { Lock; struct { uchar scntl3; uchar stest2; } bios; uchar synctab[NULTRA2SCF - 1][8];/* table of legal tpfs */ NegoState s[MAXTARGET]; uchar scntl3[MAXTARGET]; uchar sxfer[MAXTARGET]; uchar cap[MAXTARGET]; /* capabilities byte from Identify */ ushort capvalid; /* bit per target for validity of cap[] */ ushort wide; /* bit per target set if wide negotiated */ ulong sclk; /* clock speed of controller */ uchar clockmult; /* set by synctabinit */ uchar ccf; /* CCF bits */ uchar tpf; /* best tpf value for this controller */ uchar feature; /* requested features */ int running; /* is the script processor running? */ int ssm; /* single step mode */ Ncr *n; /* pointer to registers */ Variant *v; /* pointer to variant type */ ulong *script; /* where the real script is */ ulong scriptpa; /* where the real script is */ Pcidev* pcidev; SDev* sdev; struct { Lock; uchar head[4]; /* head of free list (NCR byte order) */ Dsa *freechain; } dsalist; QLock q[MAXTARGET]; /* queues for each target */ } Controller; #define SYNCOFFMASK(c) (((c)->v->maxsyncoff * 2) - 1) #define SSIDMASK(c) (((c)->v->feature & Wide) ? 15 : 7) /* ISTAT */ enum { Abrt = 0x80, Srst = 0x40, Sigp = 0x20, Sem = 0x10, Con = 0x08, Intf = 0x04, Sip = 0x02, Dip = 0x01 }; /* DSTAT */ enum { Dfe = 0x80, Mdpe = 0x40, Bf = 0x20, Abrted = 0x10, Ssi = 0x08, Sir = 0x04, Iid = 0x01 }; /* SSTAT */ enum { DataOut, DataIn, Cmd, Status, ReservedOut, ReservedIn, MessageOut, MessageIn }; static void setmovedata(Movedata*, ulong, ulong); static void advancedata(Movedata*, long); static int bios_set_differential(Controller *c); static char *phase[] = { "data out", "data in", "command", "status", "reserved out", "reserved in", "message out", "message in" }; #ifdef BOOTDEBUG #define DEBUGSIZE 10240 char debugbuf[DEBUGSIZE]; char *debuglast; static void intrprint(char *format, ...) { if (debuglast == 0) debuglast = debugbuf; debuglast = vseprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1)); } static void iflush() { int s; char *endp; s = splhi(); if (debuglast == 0) debuglast = debugbuf; if (debuglast == debugbuf) { splx(s); return; } endp = debuglast; splx(s); putstrn(debugbuf, endp - debugbuf); s = splhi(); memmove(debugbuf, endp, debuglast - endp); debuglast -= endp - debugbuf; splx(s); } static void oprint(char *format, ...) { int s; iflush(); s = splhi(); if (debuglast == 0) debuglast = debugbuf; debuglast = vseprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1)); splx(s); iflush(); } #endif #include "sd53c8xx.i" /* * We used to use a linked list of Dsas with nil as the terminator, * but occasionally the 896 card seems not to notice that the 0 * is really a 0, and then it tries to reference the Dsa at address 0. * To address this, we use a sentinel dsa that links back to itself * and has state A_STATE_END. If the card takes an iteration or * two to notice that the state says A_STATE_END, that's no big * deal. Clearly this isn't the right approach, but I'm just * stumped. Even with this, we occasionally get prints about * "WSR set", usually with about the same frequency that the * card used to walk past 0. */ static Dsa *dsaend; static Dsa* dsaallocnew(Controller *c) { Dsa *d; /* c->dsalist must be ilocked */ d = xalloc(sizeof *d); if (d == nil) panic("sd53c8xx dsaallocnew: no memory"); lesetl(d->next, legetl(c->dsalist.head)); lesetl(&d->stateb, A_STATE_FREE); coherence(); lesetl(c->dsalist.head, DMASEG(d)); coherence(); return d; } static Dsa * dsaalloc(Controller *c, int target, int lun) { Dsa *d; ilock(&c->dsalist); if ((d = c->dsalist.freechain) != 0) { if (DEBUG(1)) IPRINT(PRINTPREFIX "%d/%d: reused dsa %lux\n", target, lun, (ulong)d); } else { d = dsaallocnew(c); if (DEBUG(1)) IPRINT(PRINTPREFIX "%d/%d: allocated dsa %lux\n", target, lun, (ulong)d); } c->dsalist.freechain = d->freechain; lesetl(&d->stateb, A_STATE_ALLOCATED); iunlock(&c->dsalist); d->target = target; d->lun = lun; return d; } static void dsafree(Controller *c, Dsa *d) { ilock(&c->dsalist); d->freechain = c->dsalist.freechain; c->dsalist.freechain = d; lesetl(&d->stateb, A_STATE_FREE); iunlock(&c->dsalist); } static void dsadump(Controller *c) { Dsa *d; u32int *a; iprint("dsa controller list: c=%p head=%.8lux\n", c, legetl(c->dsalist.head)); for(d=KPTR(legetl(c->dsalist.head)); d != dsaend; d=KPTR(legetl(d->next))){ if(d == (void*)-1){ iprint("\t dsa %p\n", d); break; } a = (u32int*)d; iprint("\tdsa %p %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux\n", a, a[0], a[1], a[2], a[3], a[4], a[5]); } /* a = KPTR(c->scriptpa+E_dsa_addr); iprint("dsa_addr: %.8ux %.8ux %.8ux %.8ux %.8ux\n", a[0], a[1], a[2], a[3], a[4]); a = KPTR(c->scriptpa+E_issue_addr); iprint("issue_addr: %.8ux %.8ux %.8ux %.8ux %.8ux\n", a[0], a[1], a[2], a[3], a[4]); a = KPTR(c->scriptpa+E_issue_test_begin); e = KPTR(c->scriptpa+E_issue_test_end); iprint("issue_test code (at offset %.8ux):\n", E_issue_test_begin); i = 0; for(; adsalist.head)); d != dsaend; d = KPTR(legetl(d->next))) { if (d->target != 0xff && d->target != target) continue; if (lun != 0xff && d->lun != lun) continue; if (state != 0xff && d->stateb != state) continue; break; } return d; } static void dumpncrregs(Controller *c, int intr) { int i; Ncr *n = c->n; int depth = c->v->registers / 4; if (intr) { IPRINT("sa = %.8lux\n", c->scriptpa); } else { KPRINT("sa = %.8lux\n", c->scriptpa); } for (i = 0; i < depth; i++) { int j; for (j = 0; j < 4; j++) { int k = j * depth + i; uchar *p; /* display little-endian to make 32-bit values readable */ p = (uchar*)n+k*4; if (intr) { IPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80); } else { KPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80); } USED(p); } if (intr) { IPRINT("\n"); } else { KPRINT("\n"); } } } static int chooserate(Controller *c, int tpf, int *scfp, int *xferpp) { /* find lowest entry >= tpf */ int besttpf = 1000; int bestscfi = 0; int bestxferp = 0; int scf, xferp; int maxscf; if (c->v->feature & Ultra2) maxscf = NULTRA2SCF; else if (c->v->feature & Ultra) maxscf = NULTRASCF; else maxscf = NSCF; /* * search large clock factors first since this should * result in more reliable transfers */ for (scf = maxscf; scf >= 1; scf--) { for (xferp = 0; xferp < 8; xferp++) { unsigned char v = c->synctab[scf - 1][xferp]; if (v == 0) continue; if (v >= tpf && v < besttpf) { besttpf = v; bestscfi = scf; bestxferp = xferp; } } } if (besttpf == 1000) return 0; if (scfp) *scfp = bestscfi; if (xferpp) *xferpp = bestxferp; return besttpf; } static void synctabinit(Controller *c) { int scf; unsigned long scsilimit; int xferp; unsigned long cr, sr; int tpf; int fast; int maxscf; if (c->v->feature & Ultra2) maxscf = NULTRA2SCF; else if (c->v->feature & Ultra) maxscf = NULTRASCF; else maxscf = NSCF; /* * for chips with no clock doubler, but Ultra capable (e.g. 860, or interestingly the * first spin of the 875), assume 80MHz * otherwise use the internal (33 Mhz) or external (40MHz) default */ if ((c->v->feature & Ultra) != 0 && (c->v->feature & (ClockDouble | ClockQuad)) == 0) c->sclk = ULTRA_NOCLOCKDOUBLE_SCLK; else c->sclk = SCLK; /* * otherwise, if the chip is Ultra capable, but has a slow(ish) clock, * invoke the doubler */ if (SCLK <= 40000000) { if (c->v->feature & ClockDouble) { c->sclk *= 2; c->clockmult = 1; } else if (c->v->feature & ClockQuad) { c->sclk *= 4; c->clockmult = 1; } else c->clockmult = 0; } else c->clockmult = 0; /* derive CCF from sclk */ /* woebetide anyone with SCLK < 16.7 or > 80MHz */ if (c->sclk <= 25 * MEGA) c->ccf = 1; else if (c->sclk <= 3750000) c->ccf = 2; else if (c->sclk <= 50 * MEGA) c->ccf = 3; else if (c->sclk <= 75 * MEGA) c->ccf = 4; else if ((c->v->feature & ClockDouble) && c->sclk <= 80 * MEGA) c->ccf = 5; else if ((c->v->feature & ClockQuad) && c->sclk <= 120 * MEGA) c->ccf = 6; else if ((c->v->feature & ClockQuad) && c->sclk <= 160 * MEGA) c->ccf = 7; for (scf = 1; scf < maxscf; scf++) { /* check for legal core rate */ /* round up so we run slower for safety */ cr = (c->sclk * 2 + cf2[scf] - 1) / cf2[scf]; if (cr <= MAXSYNCCORERATE) { scsilimit = MAXSYNCSCSIRATE; fast = 0; } else if (cr <= MAXFASTSYNCCORERATE) { scsilimit = MAXFASTSYNCSCSIRATE; fast = 1; } else if ((c->v->feature & Ultra) && cr <= MAXULTRASYNCCORERATE) { scsilimit = MAXULTRASYNCSCSIRATE; fast = 2; } else if ((c->v->feature & Ultra2) && cr <= MAXULTRA2SYNCCORERATE) { scsilimit = MAXULTRA2SYNCSCSIRATE; fast = 3; } else continue; for (xferp = 11; xferp >= 4; xferp--) { int ok; int tp; /* calculate scsi rate - round up again */ /* start from sclk for accuracy */ int totaldivide = xferp * cf2[scf]; sr = (c->sclk * 2 + totaldivide - 1) / totaldivide; if (sr > scsilimit) break; /* * now work out transfer period * round down now so that period is pessimistic */ tp = (MEGA * 1000) / sr; /* * bounds check it */ if (tp < 25 || tp > 255 * 4) continue; /* * spot stupid special case for Ultra or Ultra2 * while working out factor */ if (tp == 25) tpf = 10; else if (tp == 50) tpf = 12; else if (tp < 52) continue; else tpf = tp / 4; /* * now check tpf looks sensible * given core rate */ switch (fast) { case 0: /* scf must be ccf for SCSI 1 */ ok = tpf >= 50 && scf == c->ccf; break; case 1: ok = tpf >= 25 && tpf < 50; break; case 2: /* * must use xferp of 4, or 5 at a pinch * for an Ultra transfer */ ok = xferp <= 5 && tpf >= 12 && tpf < 25; break; case 3: ok = xferp == 4 && (tpf == 10 || tpf == 11); break; default: ok = 0; } if (!ok) continue; c->synctab[scf - 1][xferp - 4] = tpf; } } #ifndef NO_ULTRA2 if (c->v->feature & Ultra2) tpf = 10; else #endif if (c->v->feature & Ultra) tpf = 12; else tpf = 25; for (; tpf < 256; tpf++) { if (chooserate(c, tpf, &scf, &xferp) == tpf) { unsigned tp = tpf == 10 ? 25 : (tpf == 12 ? 50 : tpf * 4); unsigned long khz = (MEGA + tp - 1) / (tp); KPRINT(PRINTPREFIX "tpf=%d scf=%d.%.1d xferp=%d mhz=%ld.%.3ld\n", tpf, cf2[scf] / 2, (cf2[scf] & 1) ? 5 : 0, xferp + 4, khz / 1000, khz % 1000); USED(khz); if (c->tpf == 0) c->tpf = tpf; /* note lowest value for controller */ } } } static void synctodsa(Dsa *dsa, Controller *c) { /* KPRINT("synctodsa(dsa=%lux, target=%d, scntl3=%.2lx sxfer=%.2x)\n", dsa, dsa->target, c->scntl3[dsa->target], c->sxfer[dsa->target]); */ dsa->scntl3 = c->scntl3[dsa->target]; dsa->sxfer = c->sxfer[dsa->target]; } static void setsync(Dsa *dsa, Controller *c, int target, uchar ultra, uchar scf, uchar xferp, uchar reqack) { c->scntl3[target] = (c->scntl3[target] & 0x08) | (((scf << 4) | c->ccf | (ultra << 7)) & ~0x08); c->sxfer[target] = (xferp << 5) | reqack; c->s[target] = BothDone; if (dsa) { synctodsa(dsa, c); c->n->scntl3 = c->scntl3[target]; c->n->sxfer = c->sxfer[target]; } } static void setasync(Dsa *dsa, Controller *c, int target) { setsync(dsa, c, target, 0, c->ccf, 0, 0); } static void setwide(Dsa *dsa, Controller *c, int target, uchar wide) { c->scntl3[target] = wide ? (1 << 3) : 0; setasync(dsa, c, target); c->s[target] = WideDone; } static int buildsdtrmsg(uchar *buf, uchar tpf, uchar offset) { *buf++ = X_MSG; *buf++ = 3; *buf++ = X_MSG_SDTR; *buf++ = tpf; *buf = offset; return 5; } static int buildwdtrmsg(uchar *buf, uchar expo) { *buf++ = X_MSG; *buf++ = 2; *buf++ = X_MSG_WDTR; *buf = expo; return 4; } static void start(Controller *c, long entry) { ulong p; if (c->running) panic(PRINTPREFIX "start called while running"); c->running = 1; p = c->scriptpa + entry; lesetl(c->n->dsp, p); coherence(); if (c->ssm) c->n->dcntl |= 0x4; /* start DMA in SSI mode */ } static void ncrcontinue(Controller *c) { if (c->running) panic(PRINTPREFIX "ncrcontinue called while running"); /* set the start DMA bit to continue execution */ c->running = 1; coherence(); c->n->dcntl |= 0x4; } static void softreset(Controller *c) { Ncr *n = c->n; n->istat = Srst; /* software reset */ n->istat = 0; /* general initialisation */ n->scid = (1 << 6) | 7; /* respond to reselect, ID 7 */ n->respid = 1 << 7; /* response ID = 7 */ #ifdef INTERNAL_SCLK n->stest1 = 0x80; /* disable external scsi clock */ #else n->stest1 = 0x00; #endif n->stime0 = 0xdd; /* about 0.5 second timeout on each device */ n->scntl0 |= 0x8; /* Enable parity checking */ /* continued setup */ n->sien0 = 0x8f; n->sien1 = 0x04; n->dien = 0x7d; n->stest3 = 0x80; /* TolerANT enable */ c->running = 0; if (c->v->feature & BigFifo) n->ctest5 = (1 << 5); n->dmode = c->v->burst << 6; /* set burst length bits */ if (c->v->burst & 4) n->ctest5 |= (1 << 2); /* including overflow into ctest5 bit 2 */ if (c->v->feature & Prefetch) n->dcntl |= (1 << 5); /* prefetch enable */ else if (c->v->feature & BurstOpCodeFetch) n->dmode |= (1 << 1); /* burst opcode fetch */ if (c->v->feature & Differential) { /* chip capable */ if ((c->feature & Differential) || bios_set_differential(c)) { /* user enabled, or some evidence bios set differential */ if (n->sstat2 & (1 << 2)) print(PRINTPREFIX "can't go differential; wrong cable\n"); else { n->stest2 = (1 << 5); print(PRINTPREFIX "differential mode set\n"); } } } if (c->clockmult) { n->stest1 |= (1 << 3); /* power up doubler */ delay(2); n->stest3 |= (1 << 5); /* stop clock */ n->stest1 |= (1 << 2); /* enable doubler */ n->stest3 &= ~(1 << 5); /* start clock */ /* pray */ } } static void msgsm(Dsa *dsa, Controller *c, int msg, int *cont, int *wakeme) { uchar histpf, hisreqack; int tpf; int scf, xferp; int len; Ncr *n = c->n; switch (c->s[dsa->target]) { case SyncInit: switch (msg) { case A_SIR_MSG_SDTR: /* reply to my SDTR */ histpf = n->scratcha[2]; hisreqack = n->scratcha[3]; KPRINT(PRINTPREFIX "%d: SDTN response %d %d\n", dsa->target, histpf, hisreqack); if (hisreqack == 0) setasync(dsa, c, dsa->target); else { /* hisreqack should be <= c->v->maxsyncoff */ tpf = chooserate(c, histpf, &scf, &xferp); KPRINT(PRINTPREFIX "%d: SDTN: using %d %d\n", dsa->target, tpf, hisreqack); setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack); } *cont = -2; return; case A_SIR_EV_PHASE_SWITCH_AFTER_ID: /* target ignored ATN for message after IDENTIFY - not SCSI-II */ KPRINT(PRINTPREFIX "%d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target); KPRINT(PRINTPREFIX "%d: SDTN: async\n", dsa->target); setasync(dsa, c, dsa->target); *cont = E_to_decisions; return; case A_SIR_MSG_REJECT: /* rejection of my SDTR */ KPRINT(PRINTPREFIX "%d: SDTN: rejected SDTR\n", dsa->target); //async: KPRINT(PRINTPREFIX "%d: SDTN: async\n", dsa->target); setasync(dsa, c, dsa->target); *cont = -2; return; } break; case WideInit: switch (msg) { case A_SIR_MSG_WDTR: /* reply to my WDTR */ KPRINT(PRINTPREFIX "%d: WDTN: response %d\n", dsa->target, n->scratcha[2]); setwide(dsa, c, dsa->target, n->scratcha[2]); *cont = -2; return; case A_SIR_EV_PHASE_SWITCH_AFTER_ID: /* target ignored ATN for message after IDENTIFY - not SCSI-II */ KPRINT(PRINTPREFIX "%d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target); setwide(dsa, c, dsa->target, 0); *cont = E_to_decisions; return; case A_SIR_MSG_REJECT: /* rejection of my SDTR */ KPRINT(PRINTPREFIX "%d: WDTN: rejected WDTR\n", dsa->target); setwide(dsa, c, dsa->target, 0); *cont = -2; return; } break; case NeitherDone: case WideDone: case BothDone: switch (msg) { case A_SIR_MSG_WDTR: { uchar hiswide, mywide; hiswide = n->scratcha[2]; mywide = (c->v->feature & Wide) != 0; KPRINT(PRINTPREFIX "%d: WDTN: target init %d\n", dsa->target, hiswide); if (hiswide < mywide) mywide = hiswide; KPRINT(PRINTPREFIX "%d: WDTN: responding %d\n", dsa->target, mywide); setwide(dsa, c, dsa->target, mywide); len = buildwdtrmsg(dsa->msg_out, mywide); setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len); *cont = E_response; c->s[dsa->target] = WideResponse; return; } case A_SIR_MSG_SDTR: #ifdef ASYNC_ONLY *cont = E_reject; return; #else /* target decides to renegotiate */ histpf = n->scratcha[2]; hisreqack = n->scratcha[3]; KPRINT(PRINTPREFIX "%d: SDTN: target init %d %d\n", dsa->target, histpf, hisreqack); if (hisreqack == 0) { /* he wants asynchronous */ setasync(dsa, c, dsa->target); tpf = 0; } else { /* he wants synchronous */ tpf = chooserate(c, histpf, &scf, &xferp); if (hisreqack > c->v->maxsyncoff) hisreqack = c->v->maxsyncoff; KPRINT(PRINTPREFIX "%d: using %d %d\n", dsa->target, tpf, hisreqack); setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack); } /* build my SDTR message */ len = buildsdtrmsg(dsa->msg_out, tpf, hisreqack); setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len); *cont = E_response; c->s[dsa->target] = SyncResponse; return; #endif } break; case WideResponse: switch (msg) { case A_SIR_EV_RESPONSE_OK: c->s[dsa->target] = WideDone; KPRINT(PRINTPREFIX "%d: WDTN: response accepted\n", dsa->target); *cont = -2; return; case A_SIR_MSG_REJECT: setwide(dsa, c, dsa->target, 0); KPRINT(PRINTPREFIX "%d: WDTN: response REJECTed\n", dsa->target); *cont = -2; return; } break; case SyncResponse: switch (msg) { case A_SIR_EV_RESPONSE_OK: c->s[dsa->target] = BothDone; KPRINT(PRINTPREFIX "%d: SDTN: response accepted (%s)\n", dsa->target, phase[n->sstat1 & 7]); *cont = -2; return; /* chf */ case A_SIR_MSG_REJECT: setasync(dsa, c, dsa->target); KPRINT(PRINTPREFIX "%d: SDTN: response REJECTed\n", dsa->target); *cont = -2; return; } break; } KPRINT(PRINTPREFIX "%d: msgsm: state %d msg %d\n", dsa->target, c->s[dsa->target], msg); *wakeme = 1; return; } static void calcblockdma(Dsa *d, ulong base, ulong count) { ulong blocks; if (DEBUG(3)) blocks = 0; else { blocks = count / A_BSIZE; if (blocks > 255) blocks = 255; } d->dmablks = blocks; d->dmaaddr[0] = base; d->dmaaddr[1] = base >> 8; d->dmaaddr[2] = base >> 16; d->dmaaddr[3] = base >> 24; setmovedata(&d->data_buf, base + blocks * A_BSIZE, count - blocks * A_BSIZE); d->flag = legetl(d->data_buf.dbc) == 0; } static ulong read_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa) { ulong dbc; uchar dfifo = n->dfifo; int inchip; dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; if (n->ctest5 & (1 << 5)) inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff; else inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f; if (inchip) { IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: DMA FIFO = %d\n", dsa->target, dsa->lun, inchip); } if (n->sxfer & SYNCOFFMASK(c)) { /* SCSI FIFO */ uchar fifo = n->sstat1 >> 4; if (c->v->maxsyncoff > 8) fifo |= (n->sstat2 & (1 << 4)); if (fifo) { inchip += fifo; IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SCSI FIFO = %d\n", dsa->target, dsa->lun, fifo); } } else { if (n->sstat0 & (1 << 7)) { inchip++; IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SIDL full\n", dsa->target, dsa->lun); } if (n->sstat2 & (1 << 7)) { inchip++; IPRINT(PRINTPREFIX "%d/%d: read_mismatch_recover: SIDL msb full\n", dsa->target, dsa->lun); } } USED(inchip); return dbc; } static ulong write_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa) { ulong dbc; uchar dfifo = n->dfifo; int inchip; dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; USED(dsa); if (n->ctest5 & (1 << 5)) inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff; else inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f; #ifdef WMR_DEBUG if (inchip) { IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: DMA FIFO = %d\n", dsa->target, dsa->lun, inchip); } #endif if (n->sstat0 & (1 << 5)) { inchip++; #ifdef WMR_DEBUG IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODL full\n", dsa->target, dsa->lun); #endif } if (n->sstat2 & (1 << 5)) { inchip++; #ifdef WMR_DEBUG IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODL msb full\n", dsa->target, dsa->lun); #endif } if (n->sxfer & SYNCOFFMASK(c)) { /* synchronous SODR */ if (n->sstat0 & (1 << 6)) { inchip++; #ifdef WMR_DEBUG IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODR full\n", dsa->target, dsa->lun); #endif } if (n->sstat2 & (1 << 6)) { inchip++; #ifdef WMR_DEBUG IPRINT(PRINTPREFIX "%d/%d: write_mismatch_recover: SODR msb full\n", dsa->target, dsa->lun); #endif } } /* clear the dma fifo */ n->ctest3 |= (1 << 2); /* wait till done */ while ((n->dstat & Dfe) == 0) ; return dbc + inchip; } static void sd53c8xxinterrupt(Ureg *ur, void *a) { uchar istat, dstat; ushort sist; int wakeme = 0; int cont = -1; Dsa *dsa; ulong dsapa; Controller *c = a; Ncr *n = c->n; USED(ur); if (DEBUG(1)) { IPRINT(PRINTPREFIX "int\n"); } ilock(c); istat = n->istat; if (istat & Intf) { Dsa *d; int wokesomething = 0; if (DEBUG(1)) { IPRINT(PRINTPREFIX "Intfly\n"); } n->istat = Intf; /* search for structures in A_STATE_DONE */ for (d = KPTR(legetl(c->dsalist.head)); d != dsaend; d = KPTR(legetl(d->next))) { if (d->stateb == A_STATE_DONE) { d->p9status = d->status; if (DEBUG(1)) { IPRINT(PRINTPREFIX "waking up dsa %lux\n", (ulong)d); } wakeup(d); wokesomething = 1; } } if (!wokesomething) { IPRINT(PRINTPREFIX "nothing to wake up\n"); } } if ((istat & (Sip | Dip)) == 0) { if (DEBUG(1)) { IPRINT(PRINTPREFIX "int end %x\n", istat); } iunlock(c); return; } sist = (n->sist1<<8)|n->sist0; /* BUG? can two-byte read be inconsistent? */ dstat = n->dstat; dsapa = legetl(n->dsa); /* * Can't compute dsa until we know that dsapa is valid. */ if(dsapa < -KZERO) dsa = (Dsa*)DMASEG_TO_KADDR(dsapa); else{ dsa = nil; /* * happens at startup on some cards but we * don't actually deref dsa because none of the * flags we are about are set. * still, print in case that changes and we're * about to dereference nil. */ iprint("sd53c8xxinterrupt: dsa=%.8lux istat=%ux sist=%ux dstat=%ux\n", dsapa, istat, sist, dstat); } c->running = 0; if (istat & Sip) { if (DEBUG(1)) { IPRINT("sist = %.4x\n", sist); } if (sist & 0x80) { ulong addr; ulong sa; ulong dbc; ulong tbc; int dmablks; ulong dmaaddr; addr = legetl(n->dsp); sa = addr - c->scriptpa; if (DEBUG(1) || DEBUG(2)) { IPRINT(PRINTPREFIX "%d/%d: Phase Mismatch sa=%.8lux\n", dsa->target, dsa->lun, sa); } /* * now recover */ if (sa == E_data_in_mismatch) { /* * though this is a failure in the residue, there may have been blocks * as well. if so, dmablks will not have been zeroed, since the state * was not saved by the microcode. */ dbc = read_mismatch_recover(c, n, dsa); tbc = legetl(dsa->data_buf.dbc) - dbc; dsa->dmablks = 0; n->scratcha[2] = 0; advancedata(&dsa->data_buf, tbc); if (DEBUG(1) || DEBUG(2)) { IPRINT(PRINTPREFIX "%d/%d: transferred = %ld residue = %ld\n", dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc)); } cont = E_data_mismatch_recover; } else if (sa == E_data_in_block_mismatch) { dbc = read_mismatch_recover(c, n, dsa); tbc = A_BSIZE - dbc; /* recover current state from registers */ dmablks = n->scratcha[2]; dmaaddr = legetl(n->scratchb); /* we have got to dmaaddr + tbc */ /* we have dmablks * A_BSIZE - tbc + residue left to do */ /* so remaining transfer is */ IPRINT("in_block_mismatch: dmaaddr = 0x%lux tbc=%lud dmablks=%d\n", dmaaddr, tbc, dmablks); calcblockdma(dsa, dmaaddr + tbc, dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc)); /* copy changes into scratch registers */ IPRINT("recalc: dmablks %d dmaaddr 0x%lx pa 0x%lx dbc %ld\n", dsa->dmablks, legetl(dsa->dmaaddr), legetl(dsa->data_buf.pa), legetl(dsa->data_buf.dbc)); n->scratcha[2] = dsa->dmablks; lesetl(n->scratchb, dsa->dmancr); cont = E_data_block_mismatch_recover; } else if (sa == E_data_out_mismatch) { dbc = write_mismatch_recover(c, n, dsa); tbc = legetl(dsa->data_buf.dbc) - dbc; dsa->dmablks = 0; n->scratcha[2] = 0; advancedata(&dsa->data_buf, tbc); if (DEBUG(1) || DEBUG(2)) { IPRINT(PRINTPREFIX "%d/%d: transferred = %ld residue = %ld\n", dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc)); } cont = E_data_mismatch_recover; } else if (sa == E_data_out_block_mismatch) { dbc = write_mismatch_recover(c, n, dsa); tbc = legetl(dsa->data_buf.dbc) - dbc; /* recover current state from registers */ dmablks = n->scratcha[2]; dmaaddr = legetl(n->scratchb); /* we have got to dmaaddr + tbc */ /* we have dmablks blocks - tbc + residue left to do */ /* so remaining transfer is */ IPRINT("out_block_mismatch: dmaaddr = %lux tbc=%lud dmablks=%d\n", dmaaddr, tbc, dmablks); calcblockdma(dsa, dmaaddr + tbc, dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc)); /* copy changes into scratch registers */ n->scratcha[2] = dsa->dmablks; lesetl(n->scratchb, dsa->dmancr); cont = E_data_block_mismatch_recover; } else if (sa == E_id_out_mismatch) { /* * target switched phases while attention held during * message out. The possibilities are: * 1. It didn't like the last message. This is indicated * by the new phase being message_in. Use script to recover * * 2. It's not SCSI-II compliant. The new phase will be other * than message_in. We should also indicate that the device * is asynchronous, if it's the SDTR that got ignored * * For now, if the phase switch is not to message_in, and * and it happens after IDENTIFY and before SDTR, we * notify the negotiation state machine. */ ulong lim = legetl(dsa->msg_out_buf.dbc); uchar p = n->sstat1 & 7; dbc = write_mismatch_recover(c, n, dsa); tbc = lim - dbc; IPRINT(PRINTPREFIX "%d/%d: msg_out_mismatch: %lud/%lud sent, phase %s\n", dsa->target, dsa->lun, tbc, lim, phase[p]); if (p != MessageIn && tbc == 1) { msgsm(dsa, c, A_SIR_EV_PHASE_SWITCH_AFTER_ID, &cont, &wakeme); } else cont = E_id_out_mismatch_recover; } else if (sa == E_cmd_out_mismatch) { /* * probably the command count is longer than the device wants ... */ ulong lim = legetl(dsa->cmd_buf.dbc); uchar p = n->sstat1 & 7; dbc = write_mismatch_recover(c, n, dsa); tbc = lim - dbc; IPRINT(PRINTPREFIX "%d/%d: cmd_out_mismatch: %lud/%lud sent, phase %s\n", dsa->target, dsa->lun, tbc, lim, phase[p]); USED(p, tbc); cont = E_to_decisions; } else { IPRINT(PRINTPREFIX "%d/%d: ma sa=%.8lux wanted=%s got=%s\n", dsa->target, dsa->lun, sa, phase[n->dcmd & 7], phase[n->sstat1 & 7]); dumpncrregs(c, 1); dsa->p9status = SDeio; /* chf */ wakeme = 1; } } /*else*/ if (sist & 0x400) { if (DEBUG(0)) { IPRINT(PRINTPREFIX "%d/%d Sto\n", dsa->target, dsa->lun); } dsa->p9status = SDtimeout; dsa->stateb = A_STATE_DONE; coherence(); softreset(c); cont = E_issue_check; wakeme = 1; } if (sist & 0x1) { IPRINT(PRINTPREFIX "%d/%d: parity error\n", dsa->target, dsa->lun); dsa->parityerror = 1; } if (sist & 0x4) { IPRINT(PRINTPREFIX "%s%d lun %d: unexpected disconnect\n", c->sdev->name, dsa->target, dsa->lun); dumpncrregs(c, 1); //wakeme = 1; dsa->p9status = SDeio; } } if (istat & Dip) { if (DEBUG(1)) { IPRINT("dstat = %.2x\n", dstat); } /*else*/ if (dstat & Ssi) { ulong w = legetl(n->dsp) - c->scriptpa; IPRINT("[%lux]", w); USED(w); cont = -2; /* restart */ } if (dstat & Sir) { switch (legetl(n->dsps)) { case A_SIR_MSG_IO_COMPLETE: dsa->p9status = dsa->status; wakeme = 1; break; case A_SIR_MSG_SDTR: case A_SIR_MSG_WDTR: case A_SIR_MSG_REJECT: case A_SIR_EV_RESPONSE_OK: msgsm(dsa, c, legetl(n->dsps), &cont, &wakeme); break; case A_SIR_MSG_IGNORE_WIDE_RESIDUE: /* back up one in the data transfer */ IPRINT(PRINTPREFIX "%d/%d: ignore wide residue %d, WSR = %d\n", dsa->target, dsa->lun, n->scratcha[1], n->scntl2 & 1); if (dsa->flag == 2) { IPRINT(PRINTPREFIX "%d/%d: transfer over; residue ignored\n", dsa->target, dsa->lun); } else { calcblockdma(dsa, legetl(dsa->dmaaddr) - 1, dsa->dmablks * A_BSIZE + legetl(dsa->data_buf.dbc) + 1); } cont = -2; break; case A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT: IPRINT(PRINTPREFIX "%d: not msg_in after reselect (%s)", n->ssid & SSIDMASK(c), phase[n->sstat1 & 7]); dsa = dsafind(c, n->ssid & SSIDMASK(c), -1, A_STATE_DISCONNECTED); dumpncrregs(c, 1); wakeme = 1; break; case A_SIR_NOTIFY_LOAD_STATE: IPRINT(PRINTPREFIX ": load_state dsa=%p\n", dsa); if (dsa == (void*)KZERO || dsa == (void*)-1) { dsadump(c); dumpncrregs(c, 1); panic("bad dsa in load_state"); } cont = -2; break; case A_SIR_NOTIFY_MSG_IN: IPRINT(PRINTPREFIX "%d/%d: msg_in %d\n", dsa->target, dsa->lun, n->sfbr); cont = -2; break; case A_SIR_NOTIFY_DISC: IPRINT(PRINTPREFIX "%d/%d: disconnect:", dsa->target, dsa->lun); goto dsadump; case A_SIR_NOTIFY_STATUS: IPRINT(PRINTPREFIX "%d/%d: status\n", dsa->target, dsa->lun); cont = -2; break; case A_SIR_NOTIFY_COMMAND: IPRINT(PRINTPREFIX "%d/%d: commands\n", dsa->target, dsa->lun); cont = -2; break; case A_SIR_NOTIFY_DATA_IN: IPRINT(PRINTPREFIX "%d/%d: data in a %lx b %lx\n", dsa->target, dsa->lun, legetl(n->scratcha), legetl(n->scratchb)); cont = -2; break; case A_SIR_NOTIFY_BLOCK_DATA_IN: IPRINT(PRINTPREFIX "%d/%d: block data in: a2 %x b %lx\n", dsa->target, dsa->lun, n->scratcha[2], legetl(n->scratchb)); cont = -2; break; case A_SIR_NOTIFY_DATA_OUT: IPRINT(PRINTPREFIX "%d/%d: data out\n", dsa->target, dsa->lun); cont = -2; break; case A_SIR_NOTIFY_DUMP: IPRINT(PRINTPREFIX "%d/%d: dump\n", dsa->target, dsa->lun); dumpncrregs(c, 1); cont = -2; break; case A_SIR_NOTIFY_DUMP2: IPRINT(PRINTPREFIX "%d/%d: dump2:", dsa->target, dsa->lun); IPRINT(" sa %lux", legetl(n->dsp) - c->scriptpa); IPRINT(" dsa %lux", legetl(n->dsa)); IPRINT(" sfbr %ux", n->sfbr); IPRINT(" a %lux", legetl(n->scratcha)); IPRINT(" b %lux", legetl(n->scratchb)); IPRINT(" ssid %ux", n->ssid); IPRINT("\n"); cont = -2; break; case A_SIR_NOTIFY_WAIT_RESELECT: IPRINT(PRINTPREFIX "wait reselect\n"); cont = -2; break; case A_SIR_NOTIFY_RESELECT: IPRINT(PRINTPREFIX "reselect: ssid %.2x sfbr %.2x at %ld\n", n->ssid, n->sfbr, TK2MS(m->ticks)); cont = -2; break; case A_SIR_NOTIFY_ISSUE: IPRINT(PRINTPREFIX "%d/%d: issue dsa=%p end=%p:", dsa->target, dsa->lun, dsa, dsaend); dsadump: IPRINT(" tgt=%d", dsa->target); IPRINT(" time=%ld", TK2MS(m->ticks)); IPRINT("\n"); cont = -2; break; case A_SIR_NOTIFY_ISSUE_CHECK: IPRINT(PRINTPREFIX "issue check\n"); cont = -2; break; case A_SIR_NOTIFY_SIGP: IPRINT(PRINTPREFIX "responded to SIGP\n"); cont = -2; break; case A_SIR_NOTIFY_DUMP_NEXT_CODE: { ulong *dsp = c->script + (legetl(n->dsp)-c->scriptpa)/4; int x; IPRINT(PRINTPREFIX "code at %lux", dsp - c->script); for (x = 0; x < 6; x++) { IPRINT(" %.8lux", dsp[x]); } IPRINT("\n"); USED(dsp); cont = -2; break; } case A_SIR_NOTIFY_WSR: IPRINT(PRINTPREFIX "%d/%d: WSR set\n", dsa->target, dsa->lun); cont = -2; break; case A_SIR_NOTIFY_LOAD_SYNC: IPRINT(PRINTPREFIX "%d/%d: scntl=%.2x sxfer=%.2x\n", dsa->target, dsa->lun, n->scntl3, n->sxfer); cont = -2; break; case A_SIR_NOTIFY_RESELECTED_ON_SELECT: if (DEBUG(2)) { IPRINT(PRINTPREFIX "%d/%d: reselected during select\n", dsa->target, dsa->lun); } cont = -2; break; case A_error_reselected: /* dsa isn't valid here */ iprint(PRINTPREFIX "reselection error\n"); dumpncrregs(c, 1); for (dsa = KPTR(legetl(c->dsalist.head)); dsa != dsaend; dsa = KPTR(legetl(dsa->next))) { IPRINT(PRINTPREFIX "dsa target %d lun %d state %d\n", dsa->target, dsa->lun, dsa->stateb); } break; default: IPRINT(PRINTPREFIX "%d/%d: script error %ld\n", dsa->target, dsa->lun, legetl(n->dsps)); dumpncrregs(c, 1); wakeme = 1; } } /*else*/ if (dstat & Iid) { int i, target, lun; ulong addr, dbc, *v; addr = legetl(n->dsp); if(dsa){ target = dsa->target; lun = dsa->lun; }else{ target = -1; lun = -1; } dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0]; // if(dsa == nil) idebug++; IPRINT(PRINTPREFIX "%d/%d: Iid pa=%.8lux sa=%.8lux dbc=%lux\n", target, lun, addr, addr - c->scriptpa, dbc); addr = (ulong)c->script + addr - c->scriptpa; addr -= 64; addr &= ~63; v = (ulong*)addr; for(i=0; i<8; i++){ IPRINT("%.8lux: %.8lux %.8lux %.8lux %.8lux\n", addr, v[0], v[1], v[2], v[3]); addr += 4*4; v += 4; } USED(addr, dbc); if(dsa == nil){ dsadump(c); dumpncrregs(c, 1); panic("bad dsa"); } dsa->p9status = SDeio; wakeme = 1; } /*else*/ if (dstat & Bf) { IPRINT(PRINTPREFIX "%d/%d: Bus Fault\n", dsa->target, dsa->lun); dumpncrregs(c, 1); dsa->p9status = SDeio; wakeme = 1; } } if (cont == -2) ncrcontinue(c); else if (cont >= 0) start(c, cont); if (wakeme){ if(dsa->p9status == SDnostatus) dsa->p9status = SDeio; wakeup(dsa); } iunlock(c); if (DEBUG(1)) { IPRINT(PRINTPREFIX "int end 1\n"); } } static int done(void *arg) { return ((Dsa *)arg)->p9status != SDnostatus; } static void setmovedata(Movedata *d, ulong pa, ulong bc) { d->pa[0] = pa; d->pa[1] = pa>>8; d->pa[2] = pa>>16; d->pa[3] = pa>>24; d->dbc[0] = bc; d->dbc[1] = bc>>8; d->dbc[2] = bc>>16; d->dbc[3] = bc>>24; } static void advancedata(Movedata *d, long v) { lesetl(d->pa, legetl(d->pa) + v); lesetl(d->dbc, legetl(d->dbc) - v); } static void dumpwritedata(uchar *data, int datalen) { int i; uchar *bp; if (!DEBUG(0)){ USED(data, datalen); return; } if (datalen) { KPRINT(PRINTPREFIX "write:"); for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++) { KPRINT("%.2ux", *bp); } if (i < datalen) { KPRINT("..."); } KPRINT("\n"); } } static void dumpreaddata(uchar *data, int datalen) { int i; uchar *bp; if (!DEBUG(0)){ USED(data, datalen); return; } if (datalen) { KPRINT(PRINTPREFIX "read:"); for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++) { KPRINT("%.2ux", *bp); } if (i < datalen) { KPRINT("..."); } KPRINT("\n"); } } static void busreset(Controller *c) { int x, ntarget; /* bus reset */ c->n->scntl1 |= (1 << 3); delay(500); c->n->scntl1 &= ~(1 << 3); if(!(c->v->feature & Wide)) ntarget = 8; else ntarget = MAXTARGET; for (x = 0; x < ntarget; x++) { setwide(0, c, x, 0); #ifndef ASYNC_ONLY c->s[x] = NeitherDone; #endif } c->capvalid = 0; } static void reset(Controller *c) { /* should wakeup all pending tasks */ softreset(c); busreset(c); } static int sd53c8xxrio(SDreq* r) { Dsa *d; uchar *bp; Controller *c; uchar target_expo, my_expo; int bc, check, i, status, target; if((target = r->unit->subno) == 0x07) return r->status = SDtimeout; /* assign */ c = r->unit->dev->ctlr; check = 0; d = dsaalloc(c, target, r->lun); qlock(&c->q[target]); /* obtain access to target */ docheck: /* load the transfer control stuff */ d->scsi_id_buf[0] = 0; d->scsi_id_buf[1] = c->sxfer[target]; d->scsi_id_buf[2] = target; d->scsi_id_buf[3] = c->scntl3[target]; synctodsa(d, c); bc = 0; d->msg_out[bc] = 0x80 | r->lun; #ifndef NO_DISCONNECT d->msg_out[bc] |= (1 << 6); #endif bc++; /* work out what to do about negotiation */ switch (c->s[target]) { default: KPRINT(PRINTPREFIX "%d: strange nego state %d\n", target, c->s[target]); c->s[target] = NeitherDone; /* fall through */ case NeitherDone: if ((c->capvalid & (1 << target)) == 0) break; target_expo = (c->cap[target] >> 5) & 3; my_expo = (c->v->feature & Wide) != 0; if (target_expo < my_expo) my_expo = target_expo; #ifdef ALWAYS_DO_WDTR bc += buildwdtrmsg(d->msg_out + bc, my_expo); KPRINT(PRINTPREFIX "%d: WDTN: initiating expo %d\n", target, my_expo); c->s[target] = WideInit; break; #else if (my_expo) { bc += buildwdtrmsg(d->msg_out + bc, (c->v->feature & Wide) ? 1 : 0); KPRINT(PRINTPREFIX "%d: WDTN: initiating expo %d\n", target, my_expo); c->s[target] = WideInit; break; } KPRINT(PRINTPREFIX "%d: WDTN: narrow\n", target); /* fall through */ #endif case WideDone: if (c->cap[target] & (1 << 4)) { KPRINT(PRINTPREFIX "%d: SDTN: initiating %d %d\n", target, c->tpf, c->v->maxsyncoff); bc += buildsdtrmsg(d->msg_out + bc, c->tpf, c->v->maxsyncoff); c->s[target] = SyncInit; break; } KPRINT(PRINTPREFIX "%d: SDTN: async only\n", target); c->s[target] = BothDone; break; case BothDone: break; } setmovedata(&d->msg_out_buf, DMASEG(d->msg_out), bc); setmovedata(&d->cmd_buf, DMASEG(r->cmd), r->clen); calcblockdma(d, r->data ? DMASEG(r->data) : 0, r->dlen); if (DEBUG(0)) { KPRINT(PRINTPREFIX "%d/%d: exec: ", target, r->lun); for (bp = r->cmd; bp < &r->cmd[r->clen]; bp++) { KPRINT("%.2ux", *bp); } KPRINT("\n"); if (!r->write) { KPRINT(PRINTPREFIX "%d/%d: exec: limit=(%d)%ld\n", target, r->lun, d->dmablks, legetl(d->data_buf.dbc)); } else dumpwritedata(r->data, r->dlen); } setmovedata(&d->status_buf, DMASEG(&d->status), 1); d->p9status = SDnostatus; d->parityerror = 0; coherence(); d->stateb = A_STATE_ISSUE; /* start operation */ coherence(); ilock(c); if (c->ssm) c->n->dcntl |= 0x10; /* single step */ if (c->running) { c->n->istat = Sigp; } else { start(c, E_issue_check); } iunlock(c); while(waserror()) ; tsleep(d, done, d, 600 * 1000); poperror(); if (!done(d)) { KPRINT(PRINTPREFIX "%d/%d: exec: Timed out\n", target, r->lun); dumpncrregs(c, 0); dsafree(c, d); reset(c); qunlock(&c->q[target]); r->status = SDtimeout; return r->status = SDtimeout; /* assign */ } if((status = d->p9status) == SDeio) c->s[target] = NeitherDone; if (d->parityerror) { status = SDeio; } /* * adjust datalen */ r->rlen = r->dlen; if (DEBUG(0)) { KPRINT(PRINTPREFIX "%d/%d: exec: before rlen adjust: dmablks %d flag %d dbc %lud\n", target, r->lun, d->dmablks, d->flag, legetl(d->data_buf.dbc)); } r->rlen = r->dlen; if (d->flag != 2) { r->rlen -= d->dmablks * A_BSIZE; r->rlen -= legetl(d->data_buf.dbc); } if(!r->write) dumpreaddata(r->data, r->rlen); if (DEBUG(0)) { KPRINT(PRINTPREFIX "%d/%d: exec: p9status=%d status %d rlen %ld\n", target, r->lun, d->p9status, status, r->rlen); } /* * spot the identify */ if ((c->capvalid & (1 << target)) == 0 && (status == SDok || status == SDcheck) && r->cmd[0] == 0x12 && r->dlen >= 8) { c->capvalid |= 1 << target; bp = r->data; c->cap[target] = bp[7]; KPRINT(PRINTPREFIX "%d: capabilities %.2x\n", target, bp[7]); } if(!check && status == SDcheck && !(r->flags & SDnosense)){ check = 1; r->write = 0; memset(r->cmd, 0, sizeof(r->cmd)); r->cmd[0] = 0x03; r->cmd[1] = r->lun<<5; r->cmd[4] = sizeof(r->sense)-1; r->clen = 6; r->data = r->sense; r->dlen = sizeof(r->sense)-1; /* * Clear out the microcode state * so the Dsa can be re-used. */ lesetl(&d->stateb, A_STATE_ALLOCATED); coherence(); goto docheck; } qunlock(&c->q[target]); dsafree(c, d); if(status == SDok && check){ status = SDcheck; r->flags |= SDvalidsense; } if(DEBUG(0)) KPRINT(PRINTPREFIX "%d: r flags %8.8uX status %d rlen %ld\n", target, r->flags, status, r->rlen); if(r->flags & SDvalidsense){ if(!DEBUG(0)) KPRINT(PRINTPREFIX "%d: r flags %8.8uX status %d rlen %ld\n", target, r->flags, status, r->rlen); for(i = 0; i < r->rlen; i++) KPRINT(" %2.2uX", r->sense[i]); KPRINT("\n"); } return r->status = status; } static void cribbios(Controller *c) { c->bios.scntl3 = c->n->scntl3; c->bios.stest2 = c->n->stest2; print(PRINTPREFIX "%s: bios scntl3(%.2x) stest2(%.2x)\n", c->sdev->name, c->bios.scntl3, c->bios.stest2); } static int bios_set_differential(Controller *c) { /* Concept lifted from FreeBSD - thanks Gerard */ /* basically, if clock conversion factors are set, then there is * evidence the bios had a go at the chip, and if so, it would * have set the differential enable bit in stest2 */ return (c->bios.scntl3 & 7) != 0 && (c->bios.stest2 & 0x20) != 0; } #define NCR_VID 0x1000 #define NCR_810_DID 0x0001 #define NCR_820_DID 0x0002 /* don't know enough about this one to support it */ #define NCR_825_DID 0x0003 #define NCR_815_DID 0x0004 #define SYM_810AP_DID 0x0005 #define SYM_860_DID 0x0006 #define SYM_896_DID 0x000b #define SYM_895_DID 0x000c #define SYM_885_DID 0x000d /* ditto */ #define SYM_875_DID 0x000f /* ditto */ #define SYM_1010_DID 0x0020 #define SYM_1011_DID 0x0021 #define SYM_875J_DID 0x008f static Variant variant[] = { { NCR_810_DID, 0x0f, "NCR53C810", Burst16, 8, 24, 0 }, { NCR_810_DID, 0x1f, "SYM53C810ALV", Burst16, 8, 24, Prefetch }, { NCR_810_DID, 0xff, "SYM53C810A", Burst16, 8, 24, Prefetch }, { SYM_810AP_DID, 0xff, "SYM53C810AP", Burst16, 8, 24, Prefetch }, { NCR_815_DID, 0xff, "NCR53C815", Burst16, 8, 24, BurstOpCodeFetch }, { NCR_825_DID, 0x0f, "NCR53C825", Burst16, 8, 24, Wide|BurstOpCodeFetch|Differential }, { NCR_825_DID, 0xff, "SYM53C825A", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide }, { SYM_860_DID, 0x0f, "SYM53C860", Burst16, 8, 24, Prefetch|Ultra }, { SYM_860_DID, 0xff, "SYM53C860LV", Burst16, 8, 24, Prefetch|Ultra }, { SYM_875_DID, 0x01, "SYM53C875r1", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra }, { SYM_875_DID, 0xff, "SYM53C875", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble }, { SYM_875J_DID, 0xff, "SYM53C875j", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble }, { SYM_885_DID, 0xff, "SYM53C885", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|ClockDouble }, { SYM_895_DID, 0xff, "SYM53C895", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, { SYM_896_DID, 0xff, "SYM53C896", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, { SYM_1010_DID, 0xff, "SYM53C1010", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, { SYM_1011_DID, 0xff, "SYM53C1010", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 }, }; static int xfunc(Controller *c, enum na_external x, unsigned long *v) { switch (x) { default: print("xfunc: can't find external %d\n", x); return 0; case X_scsi_id_buf: *v = offsetof(Dsa, scsi_id_buf[0]); break; case X_msg_out_buf: *v = offsetof(Dsa, msg_out_buf); break; case X_cmd_buf: *v = offsetof(Dsa, cmd_buf); break; case X_data_buf: *v = offsetof(Dsa, data_buf); break; case X_status_buf: *v = offsetof(Dsa, status_buf); break; case X_dsa_head: *v = DMASEG(&c->dsalist.head[0]); break; case X_ssid_mask: *v = SSIDMASK(c); break; } return 1; } static int na_fixup(Controller *c, ulong pa_reg, struct na_patch *patch, int patches, int (*externval)(Controller*, int, ulong*)) { int p; int v; ulong *script, pa_script; unsigned long lw, lv; script = c->script; pa_script = c->scriptpa; for (p = 0; p < patches; p++) { switch (patch[p].type) { case 1: /* script relative */ script[patch[p].lwoff] += pa_script; break; case 2: /* register i/o relative */ script[patch[p].lwoff] += pa_reg; break; case 3: /* data external */ lw = script[patch[p].lwoff]; v = (lw >> 8) & 0xff; if (!(*externval)(c, v, &lv)) return 0; v = lv & 0xff; script[patch[p].lwoff] = (lw & 0xffff00ffL) | (v << 8); break; case 4: /* 32 bit external */ lw = script[patch[p].lwoff]; if (!(*externval)(c, lw, &lv)) return 0; script[patch[p].lwoff] = lv; break; case 5: /* 24 bit external */ lw = script[patch[p].lwoff]; if (!(*externval)(c, lw & 0xffffff, &lv)) return 0; script[patch[p].lwoff] = (lw & 0xff000000L) | (lv & 0xffffffL); break; } } return 1; } static SDev* sd53c8xxpnp(void) { char *cp; Pcidev *p; Variant *v; int ba, nctlr; void *scriptma; Controller *ctlr; SDev *sdev, *head, *tail; ulong regpa, *script, scriptpa; void *regva, *scriptva; if(cp = getconf("*maxsd53c8xx")) nctlr = strtoul(cp, 0, 0); else nctlr = 32; p = nil; head = tail = nil; while((p = pcimatch(p, NCR_VID, 0)) != nil && nctlr > 0){ for(v = variant; v < &variant[nelem(variant)]; v++){ if(p->did == v->did && p->rid <= v->maxrid) break; } if(v >= &variant[nelem(variant)]) { print("no match\n"); continue; } print(PRINTPREFIX "%s rev. 0x%2.2x intr=%d command=%4.4uX\n", v->name, p->rid, p->intl, p->pcr); regpa = p->mem[1].bar; ba = 2; if(regpa & 0x04){ if(p->mem[2].bar) continue; ba++; } if(regpa == 0) print("regpa 0\n"); regpa &= ~0xF; regva = vmap(regpa, p->mem[1].size); if(regva == 0) continue; script = nil; scriptpa = 0; scriptva = nil; scriptma = nil; if((v->feature & LocalRAM) && sizeof(na_script) <= 4096){ scriptpa = p->mem[ba].bar; if((scriptpa & 0x04) && p->mem[ba+1].bar){ vunmap(regva, p->mem[1].size); continue; } scriptpa &= ~0x0F; scriptva = vmap(scriptpa, p->mem[ba].size); if(scriptva) script = scriptva; } if(scriptpa == 0){ /* * Either the map failed, or this chip does not have * local RAM. It will need a copy of the microcode. */ scriptma = malloc(sizeof(na_script)); if(scriptma == nil){ vunmap(regva, p->mem[1].size); continue; } scriptpa = DMASEG(scriptma); script = scriptma; } ctlr = malloc(sizeof(Controller)); sdev = malloc(sizeof(SDev)); if(ctlr == nil || sdev == nil){ buggery: if(ctlr) free(ctlr); if(sdev) free(sdev); if(scriptma) free(scriptma); else if(scriptva) vunmap(scriptva, p->mem[ba].size); if(regva) vunmap(regva, p->mem[1].size); continue; } if(dsaend == nil) dsaend = xalloc(sizeof *dsaend); if(dsaend == nil) panic("sd53c8xxpnp: no memory"); lesetl(&dsaend->stateb, A_STATE_END); // lesetl(dsaend->next, DMASEG(dsaend)); coherence(); lesetl(ctlr->dsalist.head, DMASEG(dsaend)); coherence(); ctlr->dsalist.freechain = 0; ctlr->n = regva; ctlr->v = v; ctlr->script = script; memmove(ctlr->script, na_script, sizeof(na_script)); /* * Because we don't yet have an abstraction for the * addresses as seen from the controller side (and on * the 386 it doesn't matter), the following two lines * are different between the 386 and alpha copies of * this driver. */ ctlr->scriptpa = scriptpa; if(!na_fixup(ctlr, regpa, na_patches, NA_PATCHES, xfunc)){ print("script fixup failed\n"); goto buggery; } swabl(ctlr->script, ctlr->script, sizeof(na_script)); ctlr->pcidev = p; sdev->ifc = &sd53c8xxifc; sdev->ctlr = ctlr; sdev->idno = '0'; if(!(v->feature & Wide)) sdev->nunit = 8; else sdev->nunit = MAXTARGET; ctlr->sdev = sdev; if(head != nil) tail->next = sdev; else head = sdev; tail = sdev; nctlr--; } return head; } static int sd53c8xxenable(SDev* sdev) { Pcidev *pcidev; Controller *ctlr; char name[32]; ctlr = sdev->ctlr; pcidev = ctlr->pcidev; pcisetbme(pcidev); ilock(ctlr); synctabinit(ctlr); cribbios(ctlr); reset(ctlr); snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); intrenable(pcidev->intl, sd53c8xxinterrupt, ctlr, pcidev->tbdf, name); iunlock(ctlr); return 1; } SDifc sd53c8xxifc = { "53c8xx", /* name */ sd53c8xxpnp, /* pnp */ nil, /* legacy */ sd53c8xxenable, /* enable */ nil, /* disable */ scsiverify, /* verify */ scsionline, /* online */ sd53c8xxrio, /* rio */ nil, /* rctl */ nil, /* wctl */ scsibio, /* bio */ nil, /* probe */ nil, /* clear */ nil, /* rtopctl */ nil, /* wtopctl */ }; Timed out\n", target, r->lun); dumpncrregs(c, 0); dsafree(c, d); reset(c); qunlock(&c->q[target]); r->status = SDtimeout; return r->status = SDtimeout; /* assign */ } if((status = d->p9status) == SDeio) c->s[target] = NeitherDone; if (d->parityerror) { status = SDeio; } /* * adjust datalen */ r->rlen = r->dlen; if (DEBUG(0)) { KPRINT(PRINTPREFIX "%d/%d: exec: before rlen adjust: dmablks %d flag %d dbc %lud\n", target, r->lun, d->dmablks, d->flag, legetl(d-cons/vga.c 644 0 0 12131 11231562730 10703ustar00nemonemo#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #define Image IMAGE #include #include #include #include "screen.h" static Memimage* back; static Memimage *conscol; static Point curpos; static Rectangle window; static int *xp; static int xbuf[256]; Lock vgascreenlock; int drawdebug; static int usingvgascreenputs; void vgaimageinit(ulong chan) { if(back == nil){ back = allocmemimage(Rect(0,0,1,1), chan); /* RSC BUG */ if(back == nil) panic("back alloc"); /* RSC BUG */ back->flags |= Frepl; back->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); memfillcolor(back, DBlack); } if(conscol == nil){ conscol = allocmemimage(Rect(0,0,1,1), chan); /* RSC BUG */ if(conscol == nil) panic("conscol alloc"); /* RSC BUG */ conscol->flags |= Frepl; conscol->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); memfillcolor(conscol, DWhite); } } static void vgascroll(VGAscr* scr) { int h, o; Point p; Rectangle r; h = scr->memdefont->height; o = 8*h; r = Rpt(window.min, Pt(window.max.x, window.max.y-o)); p = Pt(window.min.x, window.min.y+o); memimagedraw(scr->gscreen, r, scr->gscreen, p, nil, p, S); r = Rpt(Pt(window.min.x, window.max.y-o), window.max); memimagedraw(scr->gscreen, r, back, ZP, nil, ZP, S); curpos.y -= o; } static void vgascreenputc(VGAscr* scr, char* buf, Rectangle *flushr) { Point p; int h, w, pos; Rectangle r; // drawdebug = 1; if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) xp = xbuf; h = scr->memdefont->height; switch(buf[0]){ case '\n': if(curpos.y+h >= window.max.y){ vgascroll(scr); *flushr = window; } curpos.y += h; vgascreenputc(scr, "\r", flushr); break; case '\r': xp = xbuf; curpos.x = window.min.x; break; case '\t': p = memsubfontwidth(scr->memdefont, " "); w = p.x; if(curpos.x >= window.max.x-4*w) vgascreenputc(scr, "\n", flushr); pos = (curpos.x-window.min.x)/w; pos = 4-(pos%4); *xp++ = curpos.x; r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y + h); memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S); curpos.x += pos*w; break; case '\b': if(xp <= xbuf) break; xp--; r = Rect(*xp, curpos.y, curpos.x, curpos.y+h); memimagedraw(scr->gscreen, r, back, back->r.min, nil, ZP, S); combinerect(flushr, r); curpos.x = *xp; break; case '\0': break; default: p = memsubfontwidth(scr->memdefont, buf); w = p.x; if(curpos.x >= window.max.x-w) vgascreenputc(scr, "\n", flushr); *xp++ = curpos.x; r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h); memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S); memimagestring(scr->gscreen, curpos, conscol, ZP, scr->memdefont, buf); combinerect(flushr, r); curpos.x += w; } // drawdebug = 0; } static void vgascreenputs(char* s, int n) { int i, gotdraw; Rune r; char buf[4]; VGAscr *scr; Rectangle flushr; scr = &vgascreen[0]; if(!islo()){ /* * Don't deadlock trying to * print in an interrupt. */ if(!canlock(&vgascreenlock)) return; } else lock(&vgascreenlock); /* * Be nice to hold this, but not going to deadlock * waiting for it. Just try and see. */ gotdraw = canqlock(&drawlock); flushr = Rect(10000, 10000, -10000, -10000); while(n > 0){ i = chartorune(&r, s); if(i == 0){ s++; --n; continue; } memmove(buf, s, i); buf[i] = 0; n -= i; s += i; vgascreenputc(scr, buf, &flushr); } flushmemscreen(flushr); if(gotdraw) qunlock(&drawlock); unlock(&vgascreenlock); } void vgascreenwin(VGAscr* scr) { int h, w; h = scr->memdefont->height; w = scr->memdefont->info[' '].width; window = insetrect(scr->gscreen->r, 48); window.max.x = window.min.x+((window.max.x-window.min.x)/w)*w; window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h; curpos = window.min; usingvgascreenputs = 1; addconsdev(nil, vgascreenputs, 1, 0); } /* * Supposedly this is the way to turn DPMS * monitors off using just the VGA registers. * Unfortunately, it seems to mess up the video mode * on the cards I've tried. */ void vgablank(VGAscr*, int blank) { uchar seq1, crtc17; if(blank) { seq1 = 0x00; crtc17 = 0x80; } else { seq1 = 0x20; crtc17 = 0x00; } outs(Seqx, 0x0100); /* synchronous reset */ seq1 |= vgaxi(Seqx, 1) & ~0x20; vgaxo(Seqx, 1, seq1); crtc17 |= vgaxi(Crtx, 0x17) & ~0x80; delay(10); vgaxo(Crtx, 0x17, crtc17); outs(Crtx, 0x0300); /* end synchronous reset */ } void addvgaseg(char *name, ulong pa, ulong size) { Physseg seg; memset(&seg, 0, sizeof seg); seg.attr = SG_PHYSICAL; seg.name = name; seg.pa = pa; seg.size = size; addphysseg(&seg); } void cornerstring(char *s) { int h, w; VGAscr *scr; Rectangle r; Point p; scr = &vgascreen[0]; if(scr->vaddr == nil || usingvgascreenputs == 0) return; p = memsubfontwidth(scr->memdefont, s); w = p.x; h = scr->memdefont->height; r = Rect(0, 0, w, h); memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S); memimagestring(scr->gscreen, r.min, conscol, ZP, scr->memdefont, s); // flushmemscreen(r); } 0xffffff, &lv)) return 0; script[patch[p].lwoff] = (lw & 0xff000000L) | (lv & 0xffffffL); break; } } return 1; } static SDev* sd53c8xxpnp(void) { char *cp; Pcidev *p; Variant *v; int ba, nctlr; void *scriptma; Controller *ctlr; SDev *sdev, *head, *tail; ulong regpa, *script, scriptpa; void *regva, *scriptva; if(cp = getconf("*maxsd53c8xx")) nctlr = strtoul(cp, 0, 0); else nctlr = 32; p =