/* * Coraid ethernet console — serial replacement. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "../port/error.h" #include "../port/netif.h" extern Dev cecdevtab; enum { Namelen = 128, Ncbuf = 8192, Ncmask = Ncbuf-1, Nconns = 20, Size = READSTR, }; enum { Hdrsz = 18, Tinita = 0, Tinitb, Tinitc, Tdata, Tack, Tdiscover, Toffer, Treset, Tlast, Cunused = 0, Cinitb, Clogin, Copen, }; static char *cstate[] = { "unused", "initb", "login", "open" }; typedef struct { Chan *dc; Chan *cc; Dev *d; uchar ea[6]; char path[32]; } If; typedef struct { uchar dst[6]; uchar src[6]; uchar etype[2]; uchar type; uchar conn; uchar seq; uchar len; uchar data[0x100]; } Pkt; typedef struct { QLock; Lock; uchar ea[6]; /* along with cno, the key to the connection */ uchar cno; /* connection number on remote host */ uchar stalled; /* cectimer needs to kick it */ int state; /* connection state */ int idle; /* idle ticks */ int to; /* ticks to timeout */ int retries; /* remaining retries */ Block *bp; /* unacked message */ If *ifc; /* interface for this connection */ uchar sndseq; /* sequence number of last sent message */ uchar rcvseq; /* sequence number of last rcv'd message */ char cbuf[Ncbuf]; /* curcular buffer */ int r, w; /* indexes into cbuf */ int pwi; /* index into passwd; */ char passwd[32]; /* password typed by connection */ } Conn; extern int parseether(uchar *, char *); extern Chan *chandial(char *, char *, char *, Chan **); enum { Qdir = 0, Qstat, Qctl, Qdbg, Qcfg, CMreset, CMsetshelf, CMsetname, CMtraceon, CMtraceoff, CMsetpasswd, CMcecon, CMcecoff, CMwrite, }; static If ifs[4]; static char name[Namelen]; static int shelf = -1; static Conn conn[Nconns]; static int tflag; static char passwd[Namelen]; static int xmit; static int rsnd; static Rendez trendez; static int tcond; static uchar broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static Dirtab cecdir[] = { ".", { Qdir, 0, QTDIR }, 0, DMDIR | 0555, "cecstat", { Qstat}, 1, 0444, "cecctl", { Qctl}, 1, 0777, // "cecctl", { Qctl}, 1, 0664, "cecdbg", { Qdbg }, 1, 0444, "ceccfg", { Qcfg }, 1, 0444, }; static Cmdtab ceccmd[] = { CMsetname, "name", 2, CMtraceon, "traceon", 1, CMtraceoff, "traceoff", 1, CMsetpasswd, "password", 2, CMcecon, "cecon", 2, CMcecoff, "cecoff", 2, CMsetshelf, "shelf", 2, CMwrite, "write", -1, }; /* * Avoid outputting debugging to ourselves. Assumes a serial port. */ static int cecprint(char *fmt, ...) { char buf[PRINTSIZE]; int n; va_list arg; va_start(arg, fmt); n = vseprint(buf, buf+sizeof buf, fmt, arg)-buf; va_end(arg); uartputs(buf, n); return n; } static void getaddr(char *path, uchar *ea) { char buf[6*2]; int n; Chan *c; snprint(up->genbuf, sizeof up->genbuf, "%s/addr", path); c = namec(up->genbuf, Aopen, OREAD, 0); if(waserror()) { cclose(c); nexterror(); } n = c->dev->read(c, buf, sizeof buf, 0); if(n != sizeof buf) error("getaddr"); if(parseether(ea, buf) < 0) error("parseether failure"); poperror(); cclose(c); } static char *types[Tlast+1] = { "Tinita", "Tinitb", "Tinitc", "Tdata", "Tack", "Tdiscover", "Toffer", "Treset", "*GOK*", }; static int cbget(Conn *cp) { int c; if(cp->r == cp->w) return -1; c = cp->cbuf[cp->r]; cp->r = cp->r+1 & Ncmask; return c; } static void cbput(Conn *cp, int c) { if(cp->r == (cp->w+1 & Ncmask)) return; cp->cbuf[cp->w] = c; cp->w = cp->w+1 & Ncmask; } static void trace(Block *bp) { uint type; Pkt *p; if(tflag == 0) return; p = (Pkt *)bp->rp; type = p->type; if(type > Treset) type = Treset+1; cecprint("%E > %E) seq %d, type %s, len %d, conn %d\n", p->src, p->dst, p->seq, types[type], p->len, p->conn); } static Block* sethdr(If *ifc, uchar *ea, Pkt **npp, int len) { Block *bp; Pkt *np; len += Hdrsz; if(len < 60) len = 60; bp = allocb(len); bp->wp = bp->rp+len; np = (Pkt *)bp->rp; memmove(np->dst, ea, 6); memmove(np->src, ifc->ea, 6); np->etype[0] = 0xbc; np->etype[1] = 0xbc; np->seq = 0; *npp = np; return bp; } static void send(Conn *cp, Block *bp) { Block *nbp; if(cp->bp != nil) panic("cecsend: cp->bp not nil\n"); nbp = allocb(BLEN(bp)); memmove(nbp->wp, bp->rp, BLEN(bp)); nbp->wp += BLEN(bp); cp->bp = nbp; trace(bp); cp->ifc->d->bwrite(cp->ifc->dc, bp, 0); xmit++; cp->to = 4; cp->retries = 3; xmit++; } static void senddata(Conn *cp, void *data, int len) { Block *bp; Pkt *p; bp = sethdr(cp->ifc, cp->ea, &p, len); memmove(p->data, data, len); p->len = len; p->seq = ++cp->sndseq; p->conn = cp->cno; p->type = Tdata; send(cp, bp); } static void resend(Conn *cp) { Block *nbp; rsnd++; nbp = allocb(BLEN(cp->bp)); memmove(nbp->wp, cp->bp->rp, BLEN(cp->bp)); nbp->wp += BLEN(cp->bp); trace(nbp); cp->ifc->d->bwrite(cp->ifc->dc, nbp, 0); cp->to = 4; } static void reset(If *ifc, uchar conn) { Block *bp; Pkt *p; bp = sethdr(ifc, ifc->ea, &p, 0); p->type = Treset; p->conn = conn; trace(bp); ifc->d->bwrite(ifc->dc, bp, 0); } static void ack(Conn *cp) { if(cp->bp) freeb(cp->bp); cp->bp = 0; cp->to = 0; cp->retries = 0; } static void start(Conn *cp) { char buf[250]; int n, c; if(cp->bp) return; ilock(cp); for(n = 0; n < sizeof buf; n++){ if((c = cbget(cp)) == -1) break; buf[n] = c; } iunlock(cp); if(n != 0) senddata(cp, buf, n); } static void cecputs(char *str, int n) { int i, c, wake; Conn *cp; wake = 0; for(cp = conn; cp < conn+Nconns; cp++){ ilock(cp); if(cp->state == Copen){ for (i = 0; i < n; i++){ c = str[i]; if(c == 0) continue; /* BOTCH */ if(c == '\n') cbput(cp, '\r'); cbput(cp, c); } cp->stalled = 1; wake = 1; } iunlock(cp); } if(wake){ tcond = 1; wakeup(&trendez); } } static void conputs(Conn *c, char *s) { for(; *s; s++) cbput(c, *s); } static int icansleep(void*) { return tcond != 0; } static void cectimer(void *) { Conn *cp; for(;;){ tsleep(&trendez, icansleep, 0, 250); tcond = 0; for(cp = conn; cp < conn + Nconns; cp++){ qlock(cp); if(cp->bp != nil){ if(--cp->to <= 0){ if(--cp->retries <= 0){ freeb(cp->bp); cp->bp = 0; // cp->state = Cunused; }else resend(cp); } }else if(cp->stalled){ cp->stalled = 0; start(cp); } qunlock(cp); } } } static int cecqlen(void*) { int n; Conn *c; n = 0; for(c = conn; c < conn+Nconns; c++){ if(!canqlock(c)) continue; if(c->bp) n += BLEN(c->bp); if(c->r > c->w) n += c->r-c->w; else n += c->w-c->r; qunlock(c); } if(n){ tcond = 1; wakeup(&trendez); } return n; } static void discover(If *ifc, Pkt *p) { uchar *addr; Block *bp; Pkt *np; if(p) addr = p->src; else addr = broadcast; bp = sethdr(ifc, addr, &np, 0); np->type = Toffer; np->len = snprint((char *)np->data, sizeof np->data, "%d %s", shelf, name); trace(bp); ifc->d->bwrite(ifc->dc, bp, 0); } static Conn* connidx(int cno) { Conn *c; for(c = conn; c < conn + Nconns; c++) if(cno == c->cno){ qlock(c); return c; } return nil; } static Conn* findconn(uchar *ea, uchar cno) { Conn *cp, *ncp; ncp = nil; for(cp = conn; cp < conn + Nconns; cp++){ if(ncp == nil && cp->state == Cunused) ncp = cp; if(memcmp(ea, cp->ea, 6) == 0 && cno == cp->cno){ qlock(cp); return cp; } } if(ncp != nil) qlock(ncp); return ncp; } static void checkpw(Conn *cp, char *str, int len) { int i, c; if(passwd[0] == 0) return; for(i = 0; i < len; i++){ c = str[i]; if(c != '\n' && c != '\r'){ if(cp->pwi < (sizeof cp->passwd)-1) cp->passwd[cp->pwi++] = c; cbput(cp, '#'); cecprint("%c", c); continue; } /* is newline; check password */ cp->passwd[cp->pwi] = 0; if(strcmp(cp->passwd, passwd) == 0){ cp->state = Copen; cp->pwi = 0; print("\r\n%E logged in\r\n", cp->ea); }else{ conputs(cp, "\r\nBad password\r\npassword: "); cp->pwi = 0; } } start(cp); } static void incoming(Conn *cp, If *ifc, Pkt *p) { int i; Block *bp; Pkt *np; /* ack it no matter what its sequence number */ bp = sethdr(ifc, p->src, &np, 0); np->type = Tack; np->seq = p->seq; np->conn = cp->cno; np->len = 0; trace(bp); ifc->d->bwrite(ifc->dc, bp, 0); if(cp->state == Cunused){ /* connection */ discover(ifc, p); return; } if(p->seq == cp->rcvseq) return; cp->rcvseq = p->seq; if(cp->state == Copen){ for (i = 0; i < p->len; i++) kbdcr2nl(nil, (char)p->data[i]); }else if(cp->state == Clogin) checkpw(cp, (char *)p->data, p->len); } static void inita(Conn *ncp, If *ifc, Pkt *p) { Block *bp; Pkt *np; ncp->ifc = ifc; ncp->state = Cinitb; memmove(ncp->ea, p->src, 6); ncp->cno = p->conn; bp = sethdr(ifc, p->src, &np, 0); np->type = Tinitb; np->conn = ncp->cno; np->len = 0; send(ncp, bp); } static void cecrdr(void *vp) { Block *bp; Conn *cp; If *ifc; Pkt *p; ifc = vp; if(waserror()) goto exit; discover(ifc, 0); for(;;){ bp = ifc->d->bread(ifc->dc, 1514, 0); // do we care about making the MTU non magic? if(bp == nil) nexterror(); p = (Pkt *)bp->rp; if(p->etype[0] != 0xbc || p->etype[1] != 0xbc){ freeb(bp); continue; } trace(bp); cp = findconn(p->src, p->conn); if(cp == nil){ cecprint("cec: out of connection structures\n"); freeb(bp); continue; } if (waserror()){ freeb(bp); qunlock(cp); continue; } switch(p->type){ case Tinita: if(cp->bp){ cecprint("cec: reset with bp!? ask quanstro\n"); freeb(cp->bp); cp->bp = 0; } inita(cp, ifc, p); break; case Tinitb: cecprint("cec: unexpected initb\n"); break; case Tinitc: if(cp->state == Cinitb){ ack(cp); if(cp->passwd[0]){ cp->state = Clogin; conputs(cp, "password: "); start(cp); }else cp->state = Copen; } break; case Tdata: incoming(cp, ifc, p); break; case Tack: if(cp->state == Clogin || cp->state == Copen){ ack(cp); start(cp); } break; case Tdiscover: discover(ifc, p); break; case Toffer: // cecprint("cec: unexpected offer\n"); from ourselves. break; case Treset: if(cp->bp) freeb(cp->bp); cp->bp = 0; cp->state = Cunused; break; default: cecprint("bad cec type: %d\n", p->type); break; } nexterror(); } exit: for(cp = conn; cp < conn+nelem(conn); cp++) if(cp->ifc == ifc){ if(cp->bp) freeb(cp->bp); memset(cp, 0, sizeof *cp); break; } memset(ifc, 0, sizeof *ifc); pexit("cec exiting", 1); } static Chan * cecattach(char *spec) { Chan *c; static QLock q; static int inited; qlock(&q); if(inited == 0){ kproc("cectimer", cectimer, nil); inited++; } qunlock(&q); c = devattach(L'©', spec); c->qid.path = Qdir; return c; } static Walkqid* cecwalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, cecdir, nelem(cecdir), devgen); } static long cecstat(Chan *c, uchar *dp, long n) { return devstat(c, dp, n, cecdir, nelem(cecdir), devgen); } static Chan* cecopen(Chan *c, int omode) { return devopen(c, omode, cecdir, nelem(cecdir), devgen); } static void cecclose(Chan*) { } static long cecread(Chan *c, void *a, long n, vlong offset) { char *p; int j; Conn *cp; If *ifc; switch((int)c->qid.path){ case Qdir: return devdirread(c, a, n, cecdir, nelem(cecdir), devgen); case Qstat: p = smalloc(Size); j = 0; for(cp = conn; cp < conn+Nconns; cp++) if(cp->state != Cunused) j += snprint(p+j, Size-j, "%E %3d %-6s %12d %d %d %.8lux\n", cp->ea, cp->cno, cstate[cp->state], cp->idle, cp->to, cp->retries, (uintptr)cp->bp); n = readstr(offset, a, n, p); free(p); return n; case Qdbg: cecprint("xmit %d, rsnd %d\n", xmit, rsnd); return 0; case Qcfg: case Qctl: p = smalloc(Size); j = 0; for(ifc = ifs; ifc < ifs+nelem(ifs); ifc++) if(ifc->d) j += snprint(p+j, Size-j, "%s\n", ifc->path); n = readstr(offset, a, n, p); free(p); return n; } error(Egreg); return 0; } /* static void cecfixup(void) { char *p; int len; p = malloc(128*1024); snprint(p, 6, "write "); len = readfile("/dev/kmesg", p+6, 128*1024-6); writefile("#©/cecctl", p, len+6); free(p); } */ static void cecon(char *path) { char buf[64]; uchar ea[6]; Chan *dc, *cc; If *ifc, *nifc; nifc = nil; for(ifc = ifs; ifc < ifs+nelem(ifs); ifc++) if(ifc->d == nil) nifc = ifc; else if(strcmp(ifc->path, path) == 0) return; ifc = nifc; if(ifc == nil) error("out of interface structures"); getaddr(path, ea); snprint(buf, sizeof buf, "%s!0xbcbc", path); dc = chandial(buf, nil, nil, &cc); if(dc == nil || cc == nil){ if (cc) cclose(cc); if (dc) cclose(dc); snprint(up->genbuf, sizeof up->genbuf, "can't dial %s", buf); error(up->genbuf); } ifc->d = cc->dev; ifc->cc = cc; ifc->dc = dc; strncpy(ifc->path, path, sizeof ifc->path); memmove(ifc->ea, ea, 6); snprint(up->genbuf, sizeof up->genbuf, "cec:%s", path); kproc(up->genbuf, cecrdr, ifc); } static void cecoff(char *path) { int all, n; If *ifc, *e; all = strcmp(path, "*") == 0; n = 0; ifc = ifs; e = ifc+nelem(ifs); for(; ifc < e; ifc++) if(ifc->d && (all || strcmp(path, ifc->path) == 0)){ cclose(ifc->cc); cclose(ifc->dc); memset(ifc, 0, sizeof *ifc); n++; } if(all + n == 0) error("cec not found"); } static void rst(Conn *c) { if(c == nil) error("no such index"); if(c->bp) freeb(c->bp); c->bp = 0; c->state = Cunused; qunlock(c); } static long cecwrite(Chan *c, void *a, long n, vlong) { Cmdbuf *cb; Cmdtab *cp; if(c->qid.path == Qctl){ cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } cp = lookupcmd(cb, ceccmd, nelem(ceccmd)); switch(cp->index){ case CMsetname: strecpy(name, name+sizeof name-1, cb->f[1]); break; case CMtraceon: tflag = 1; break; case CMtraceoff: tflag = 0; break; case CMsetpasswd: strcpy(passwd, cb->f[1]); break; case CMcecon: cecon(cb->f[1]); break; case CMcecoff: cecoff(cb->f[1]); break; case CMsetshelf: shelf = atoi(cb->f[1]); break; case CMwrite: cecputs((char*)a+6,n-6); break; case CMreset: rst(connidx(atoi(cb->f[1]))); break; default: cmderror(cb, "bad control message"); break; } free(cb); poperror(); return n; } error(Egreg); return 0; } static void cecinit(void) { addconsdev(nil, cecputs, -1, 0); } Dev cecdevtab = { L'©', "cec", devreset, cecinit, devshutdown, cecattach, cecwalk, cecstat, cecopen, devcreate, cecclose, cecread, devbread, cecwrite, devbwrite, devremove, devwstat, devpower, devconfig, };