cec console for cpu servers. useful when there's no serial available. Notes: Wed Sep 19 19:14:16 EDT 2007 geoff deferred. Reference: /n/sources/patch/saved/cpu-cec Date: Sun Mar 18 23:23:24 CET 2007 Signed-off-by: quanstro@quanstro.net Reviewed-by: geoff --- /sys/src/9/port/portfns.h Sun Mar 18 23:21:35 2007 +++ /sys/src/9/port/portfns.h Sun Mar 18 23:21:31 2007 @@ -22,6 +22,7 @@ int canpage(Proc*); int canqlock(QLock*); int canrlock(RWlock*); +void cecputs(char*, int); void chandevinit(void); void chandevreset(void); void chandevshutdown(void); --- /sys/src/9/port/devcec.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/port/devcec.c Sun Mar 18 23:21:45 2007 @@ -0,0 +1,818 @@ +/* + * Coraid Ethernet Console driver + * kick (CEC) driver + * Also requires hooks into devcons.c + */ + +#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" + +enum { + Ncbuf = 8192, + Ncmask = Ncbuf-1, + Namelen = 128, +}; + +enum{ + Tinita = 0, + Tinitb, + Tinitc, + Tdata, + Tack, + Tdiscover, + Toffer, + Treset, +}; + +enum{ + Cunused = 0, + Cinitb, // sent initb + Clogin, // login state + Copen, // up +}; + +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[1500]; +}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 -- cecputs while !islo() + 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; + +/* + * Since this code is in the output chain of procedures for console + * output, we can't use the general printf functions. See the ones + * at the bottom of this file. It assumes the serial port. + */ + +static int cecprint(char *, ...); +extern int parseether(uchar *, char *); +extern Chan * chandial(char *, char *, char *, Chan **); + +enum { + Qdir = 0, + Qstat, + Qctl, + Qdbg, + Qcfg, + CMsetshelf, + CMsetname, + CMtraceon, + CMtraceoff, + CMsetpasswd, + CMcecon, + CMcecoff, + + Nconns = 20, +}; + +static If ifs[4]; +static char name[Namelen]; +static int shelf = -1; +static Conn conn[Nconns]; +static int tflag; // trace flag +static char passwd[Namelen]; +static int xmit; +static int rsnd; +static Rendez trendez; +static uchar broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +Dirtab cecdir[] = { + ".", { Qdir, 0, QTDIR }, 0, DMDIR | 0555, + "cecstat", { Qstat}, 1, 0444, + "cecctl", { Qctl}, 1, 0222, + "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, +}; + +static void +getaddr(char *path, uchar *ea) +{ + char buf[6*2+1]; + Chan *c; + int n; + + snprint(up->genbuf, sizeof up->genbuf, "%s/addr", path); + c = namec(up->genbuf, Aopen, OREAD, 0); + if(waserror()) { + cclose(c); + nexterror(); + } + n = devtab[c->type]->read(c, buf, sizeof buf-1, 0); + if(n != 6*2) + error("getaddr"); + buf[n] = 0; + if(parseether(ea, buf) < 0) + error("parseether failure"); + poperror(); + cclose(c); +} + +static char *types[] = { + "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) // full + return; + cp->cbuf[cp->w] = c; + cp->w = (cp->w+1)&Ncmask; +} + + +static void +trace(Block *bp) +{ + Pkt *p; + int type; + + 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) // set header for response +{ + Block *bp; + Pkt *np; + + len += 18; + 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) // put on output queue +{ + 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 = nil; + cp->to = 0; + cp->retries = 0; +} + +static void +start(Conn *cp) +{ + char buf[250]; + int n, c; + + if(cp->bp != nil) + return; + n = 0; + ilock(cp); + while(n < sizeof buf){ + c = cbget(cp); + if(c == -1) + break; + buf[n] = c; + n++; + } + iunlock(cp); + if(n == 0) + return; + senddata(cp, buf, n); +} + +void +cecputs(char *str, int n) +{ + int i, c, ien; + Conn *cp; + extern int panicking; + + if(panicking || active.exiting) + return; + ien = islo(); + for(cp = conn; cp < &conn[Nconns]; cp++){ + ilock(cp); + if(cp->state == Copen){ + for (i = 0; i < n; i++){ + c = str[i]; + if(c == '\n') + cbput(cp, '\r'); + cbput(cp, c); + } + } + iunlock(cp); + if(ien){ + qlock(cp); + start(cp); + qunlock(cp); + }else{ + cp->stalled = 1; + wakeup(&trendez); + } + } +} + +static void +conputs(Conn *c, char *s) +{ + for(; *s; s++) + cbput(c, *s); +} + +static void +cectimer(void *) +{ + Conn *cp; + + for(;;){ + tsleep(&trendez, return0, 0, 500); + 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 = nil; + cp->state = Cunused; + }else + resend(cp); + } + }else if(cp->stalled){ + cp->stalled = 0; + start(cp); + } + qunlock(cp); + } + } +} + +static void +discover(If *ifc, Pkt *p) // tell about us +{ + Block *bp; + Pkt *np; + uchar *addr; + + 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 * +findconn(uchar *ea, uchar cno) // return locked connection object +{ + Conn *cp, *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) +{ + Pkt *np; + int i; + Block *bp; + + // 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(p->seq == cp->rcvseq) + return; + + // process message + + 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) // connection request +{ + Pkt *np; + Block *bp; + + 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) // reader of incoming frames +{ + Block *bp; + If *ifc; + Pkt *p; + Conn *cp; + + ifc = vp; + if(waserror()) + goto kexit; + + discover(ifc, 0); + for(;;){ + bp = ifc->d->bread(ifc->dc, ETHERMAXTU, 0); + 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: + // connection request + 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: + // data packet arrived + incoming(cp, ifc, p); + break; + case Tack: + // ack for one I sent arrived + if(cp->state == Clogin || cp->state == Copen){ + ack(cp); + start(cp); + } + break; + case Tdiscover: + // someone wanting to know about us + 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(); + } + +kexit: + 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 int +cecstat(Chan *c, uchar *dp, int 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 char * cstate[] = { "unused", "initb", "login", "open" }; +enum { SIZE = READSTR*2 }; + +static long +cecread(Chan *c, void *a, long n, vlong offset) +{ + char *p; + Conn *cp; + int j; + If *ifc; + + switch((int)c->qid.path){ + case Qdir: + return devdirread(c, a, n, cecdir, nelem(cecdir), devgen); + case Qstat: + p = malloc(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 %08ulx\n", + cp->ea, cp->cno, cstate[cp->state], cp->idle, + cp->to, cp->retries, 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: + p = mallocz(SIZE, 1); + 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 +cecon(char *path) +{ + Chan *dc, *cc; + uchar ea[6]; + char buf[64]; + If *ifc, *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, nelem(up->genbuf), "can't dial %s", buf); + error(up->genbuf); + } + ifc->d = devtab[cc->type]; + ifc->cc = cc; + ifc->dc = dc; + strncpy(ifc->path, path, nelem(ifc->path)); + memmove(ifc->ea, ea, 6); + snprint(up->genbuf, nelem(up->genbuf), "cec@%s\n", path); + kproc(up->genbuf, cecrdr, ifc); +} + +static void +cecoff(char *path) +{ + If *ifc, *e; + + ifc = ifs; + e = ifc+nelem(ifs); + for(; ifc < e; ifc++) + if(ifc->d && strcmp(path, ifc->path) == 0) + break; + if(ifc == e) + error("cec not found"); + cclose(ifc->cc); + cclose(ifc->dc); +} + +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; + default: + cmderror(cb, "bad control message"); + break; + } + free(cb); + poperror(); + return n; + } + error(Egreg); + return 0; +} + +Dev cecdevtab = { + L'©', + "cethcon", + + devreset, + devinit, + devshutdown, + cecattach, + cecwalk, + cecstat, + cecopen, + devcreate, + cecclose, + cecread, + devbread, + cecwrite, + devbwrite, + devremove, + devwstat, + devpower, + devconfig, +}; + +static int +cecprint(char *fmt, ...) +{ + int n; + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof(buf), fmt, arg)-buf; + va_end(arg); + uartputs(buf, n); + return n; +} + --- /sys/src/9/port/devcons.c Sun Mar 18 23:22:04 2007 +++ /sys/src/9/port/devcons.c Sun Mar 18 23:21:56 2007 @@ -177,7 +177,7 @@ uartputs(str, n); return; } - + cecputs(str, n); while(n > 0) { t = memchr(str, '\n', n); if(t && !kbd.raw) { @@ -264,6 +264,7 @@ if(screenputs != nil && iprintscreenputs) screenputs(buf, n); uartputs(buf, n); + cecputs(buf, n); if(locked) unlock(&iprintlock); splx(s); @@ -321,7 +322,7 @@ 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); + n = sprint(buf, "%s %lud: ", up->text, up->pid); va_start(arg, fmt); n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf; va_end(arg); @@ -339,6 +340,36 @@ } static void +echocec(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){ + cecputs(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) + cecputs(ebuf, p - ebuf); +} + + +static void echoscreen(char *buf, int n) { char *e, *p; @@ -478,6 +509,7 @@ echoscreen(buf, n); if(serialoq) echoserialoq(buf, n); + echocec(buf, n); } /*