cec updates. Reference: /n/patches.lsub.org/patch/cec Date: Wed Apr 25 00:00:13 CES 2012 Signed-off-by: quanstro@quanstro.net --- /sys/src/cmd/cec/Protocol Mon Apr 23 17:20:22 2012 +++ /sys/src/cmd/cec/Protocol Thu Jan 7 18:18:18 2010 @@ -52,11 +52,12 @@ network. The packet sent is of the form Tdiscover = { - [dest] 0xffffffff, + [dest] mac of destination interface or 0xffffffff, [src] mac of local interface, [etype] service type, [type] Tdiscover, - [seq] 0, + [conn] don't care + [seq] seq != 0, [len] 0, [data] 0, } @@ -65,10 +66,13 @@ Tdiscover in that data and len may be set. The contents of data is application specific. +Servers typically send an unsolicited Toffer on startup to allow clients +to reconnect automaticly. Seq should be 0 for unsolicited Toffers. + 3. Initializing a connection. Tinit[abc] A connection is initialized by the following conversation: In addition -to the fields set for the Tdiscover packet, he client sends a packet +to the fields set for the Tdiscover packet, the client sends a packet of type Tinita with the conntag of its choice. The server responds to Tinita with a packet of type Tinitb. And finally the client sents a Tinitc packet back to the server, completing the connection. @@ -78,7 +82,7 @@ Data is sent from the client to the console server with the Tdata packet. The seq field is incremented for each data packet sent. Thus data packets may be transmitted if lost. The data is whatever data the client has to -send to the server, up to 1500 bytes. Typically, however, each keystroke +send to the server, up to 255 bytes. Typically, however, each keystroke is sent in a seperate packet. The len field is the length of the data. --- /sys/src/cmd/cec/cec.c Thu Apr 12 12:25:26 2012 +++ /sys/src/cmd/cec/cec.c Sat Nov 26 01:01:41 2011 @@ -1,12 +1,14 @@ /* - * cec — coraid ethernet console - * Copyright © Coraid, Inc. 2006-2008. + * cec — simple ethernet console + * Copyright © Coraid, Inc. 2006-2009 * All Rights Reserved. */ #include #include #include /* really! */ #include +#include +#include #include "cec.h" enum { @@ -18,41 +20,34 @@ Tdiscover, Toffer, Treset, - + Hdrsz = 18, - Eaddrlen = 6, + Timeout = 7500, }; -typedef struct{ +typedef struct { uchar ea[Eaddrlen]; int major; char name[28]; } Shelf; int conn(int); -void gettingkilled(int); int pickone(void); void probe(void); -void sethdr(Pkt *, int); -int shelfidx(void); - -Shelf *con; -Shelf tab[1000]; +uchar bcast[Eaddrlen] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +uchar ea[Eaddrlen]; +uchar ea0[Eaddrlen]; +uchar contag; +char esc = ''; char *host; +int ntab; +char pflag; +int shelf = -1; char *srv; char *svc; - -char pflag; - -int ntab; -int shelf = -1; - -uchar bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -uchar contag; -uchar esc = ''; -uchar ea[Eaddrlen]; -uchar unsetea[Eaddrlen]; +Shelf *con; +Shelf tab[1000]; extern int fd; /* set in netopen */ @@ -100,8 +95,7 @@ void usage(void) { - fprint(2, "usage: cec [-dp] [-c esc] [-e ea] [-h host] [-s shelf] " - "[-S srv] interface\n"); + fprint(2, "usage: cec [-dp] [-c esc] [-e ea] [-s shelf] [-h host] [-S srv] interface\n"); exits0("usage"); } @@ -116,12 +110,19 @@ int nilea(uchar *ea) { - return memcmp(ea, unsetea, Eaddrlen) == 0; + return memcmp(ea, ea0, sizeof ea0) == 0; +} + +int +specific(void) +{ + return shelf != -1 || host != 0 || !nilea(ea); } void main(int argc, char **argv) { + char *p, *v; int r, n; ARGBEGIN{ @@ -129,16 +130,20 @@ srv = EARGF(usage()); break; case 'c': - esc = tolower(*(EARGF(usage()))) - 'a' + 1; - if(esc == 0 || esc >= ' ') + esc = toupper(*(EARGF(usage()))) - 'A' + 1; + if(esc < 1 || esc > 0x19) usage(); break; case 'd': debug++; break; case 'e': - if(parseether(ea, EARGF(usage())) == -1) + p = EARGF(usage()); + if(v = csgetvalue(nil, "sys", p, "ether", 0)) + p = v; + if(parseether(ea, p) == -1) usage(); + free(v); pflag = 1; break; case 'h': @@ -157,7 +162,6 @@ *argv = "/net/ether0"; else if(argc != 1) usage(); - fmtinstall('E', eipfmt); if(srv != nil) dosrv(srv); @@ -170,7 +174,7 @@ probe(); for(;;){ n = 0; - if(shelf == -1 && host == 0 && nilea(ea)) + if(!specific()) n = pickone(); rawon(); conn(n); @@ -182,7 +186,7 @@ exits0("host not found"); if(!nilea(ea)) exits0("ea not found"); - } else if(shelf != -1 || host || !nilea(ea)) + }else if(specific()) exits0(""); } } @@ -232,8 +236,8 @@ int tcmp(void *a, void *b) { - Shelf *s, *t; int d; + Shelf *s, *t; s = a; t = b; @@ -249,49 +253,61 @@ probe(void) { char *sh, *other; - int n; + int n, i; Pkt q; Shelf *p; - do { - ntab = 0; - memset(q.dst, 0xff, Eaddrlen); - memset(q.src, 0, Eaddrlen); - q.etype = htons(Etype); - q.type = Tdiscover; - q.len = 0; - q.conn = 0; - q.seq = 0; - netsend(&q, 60); - timewait(Iowait); - while((n = netget(&q, sizeof q)) >= 0){ - if((n <= 0 && didtimeout()) || ntab == nelem(tab)) +top: + ntab = 0; + memset(q.dst, 0xff, Eaddrlen); + memset(q.src, 0, Eaddrlen); + q.etype = htons(Etype); + q.type = Tdiscover; + q.len = 0; + q.conn = 0; + q.seq = 1; /* protocol extension */ + netsend(&q, 60); + timewait(Iowait); + while((n = netget(&q, sizeof q)) >= 0){ + if((n <= 0 && didtimeout()) || ntab == nelem(tab)) + break; + if(n < 60 || q.len == 0 || q.type != Toffer) + continue; + q.data[q.len] = 0; + sh = strtok((char *)q.data, " \t"); + if(sh == nil) + continue; + if(!nilea(ea) && memcmp(ea, q.src, Eaddrlen)) + continue; + if(shelf != -1 && atoi(sh) != shelf) + continue; + other = strtok(nil, "\x1"); + if(other == 0) + other = ""; + if(host && strcmp(host, other)) + continue; + p = tab + ntab++; + memcpy(p->ea, q.src, Eaddrlen); + p->major = atoi(sh); + p->name[0] = 0; + if(p->name) + snprint(p->name, sizeof p->name, "%s", other); + if(specific()) + break; + + /* dedup; prefer hostnames */ + for(i = 0; i < ntab-1; i++) + if(memcmp(p->ea, tab[i].ea, Eaddrlen) == 0){ + if(strlen(p->name) > 0) + memcpy(tab+i, p, sizeof *p); + ntab--; break; - if(n < 60 || q.len == 0 || q.type != Toffer) - continue; - q.data[q.len] = 0; - sh = strtok((char *)q.data, " \t"); - if(sh == nil) - continue; - if(!nilea(ea) && memcmp(ea, q.src, Eaddrlen) != 0) - continue; - if(shelf != -1 && atoi(sh) != shelf) - continue; - other = strtok(nil, "\x1"); - if(other == 0) - other = ""; - if(host && strcmp(host, other) != 0) - continue; - p = tab + ntab++; - memcpy(p->ea, q.src, Eaddrlen); - p->major = atoi(sh); - p->name[0] = 0; - if(p->name) - snprint(p->name, sizeof p->name, "%s", other); - } - alarm(0); - } while (ntab == 0 && pflag); + } + } + alarm(0); if(ntab == 0){ + if(pflag) + goto top; fprint(2, "none found.\n"); exits0("none found"); } @@ -303,8 +319,11 @@ { int i; - for(i = 0; i < ntab; i++) + for(i = 0; i < ntab; i++){ + if(i > 0 && tcmp(tab - 1, tab) == 0) + continue; print("%2d %5d %E %s\n", i, tab[i].major, tab[i].ea, tab[i].name); + } } int @@ -320,18 +339,15 @@ case 1: if(buf[0] == '\n') continue; - /* fall through */ case 2: if(buf[0] == 'p'){ probe(); break; } if(buf[0] == 'q') - /* fall through */ case 0: case -1: exits0(0); - break; } if(isdigit(buf[0])){ buf[n] = 0; @@ -367,21 +383,26 @@ } int -ethopen(void) +ethopen(int force) { + int t, w; Pkt tpk, rpk; - int i, n; contag = (getpid() >> 8) ^ (getpid() & 0xff); sethdr(&tpk, Tinita); sethdr(&rpk, 0); - for(i = 0; i < 3 && rpk.type != Tinitb; i++){ + t = time(0); + while(rpk.type != Tinitb){ netsend(&tpk, 60); - timewait(Iowait); - n = netget(&rpk, 1000); + w = 30*Iowait; + if(t - time(0) > 6){ + if(!force) + break; + w = Iowait; + } + timewait(w); + netget(&rpk, 1000); alarm(0); - if(n < 0) - return -1; } if(rpk.type != Tinitb) return -1; @@ -415,7 +436,7 @@ } /* - * this is a bit too aggressive. it really needs to replace only \n\r with \n. + * this is a bit too agressive. it really needs to replace only \n\r with \n. */ static uchar crbuf[256]; @@ -425,106 +446,132 @@ int i, j, c; j = 0; - for(i = 0; i < n; i++) - if((c = buf[i]) != '\r') - crbuf[j++] = c; + for(i = 0; i < n; i++){ + if((c = buf[i]) == '\r') + continue; + crbuf[j++] = c; + } write(fd, crbuf, j); } int doloop(void) { - int unacked, retries, set[2]; - uchar c, tseq, rseq; - uchar ea[Eaddrlen]; - Pkt tpk, spk; + uchar c, tseq, rseq, ea[Eaddrlen]; + int l, unacked, set[2]; + Pkt tpk, spk, ppk; Mux *m; memmove(ea, con->ea, Eaddrlen); - retries = 0; - unacked = 0; tseq = 0; rseq = -1; set[0] = 0; set[1] = fd; top: - if ((m = mux(set)) == 0) + if((m = mux(set)) == 0) exits0("mux: %r"); - for (; ; ) - switch (muxread(m, &spk)) { - case -1: - if (unacked == 0) - break; - if (retries-- == 0) { - fprint(2, "Connection timed out\n"); - muxfree(m); + unacked = 0; + sethdr(&ppk, Tdata); + for(;;) switch(muxread(m, &spk)){ + case -1: + break; + case Ftimedout: + if(ppk.len > 0){ + tpk = ppk; + tpk.seq = ++tseq; + unacked = tpk.len; + netsend(&tpk, Hdrsz+tpk.len); + muxtimeout(m, Timeout); + ppk.len = 0; + }else{ + unacked = 0; + muxtimeout(m, 0); + } + break; + case Ftimeout: + netsend(&tpk, Hdrsz+unacked); + break; + case Fkbd: + c = spk.data[0]; + if(c == esc) { + muxfree(m); + switch(escape()) { + case 'q': + tpk.len = 0; + tpk.type = Treset; + netsend(&tpk, 60); return 0; + case '.': + goto top; + case 'i': + if((m = mux(set)) == 0) + exits0("mux: %r"); + break; } - netsend(&tpk, Hdrsz + unacked); - break; - case Fkbd: - c = spk.data[0]; - if (c == esc) { - muxfree(m); - switch (escape()) { - case 'q': - tpk.len = 0; - tpk.type = Treset; - netsend(&tpk, 60); - return 0; - case '.': - goto top; - case 'i': - if ((m = mux(set)) == 0) - exits0("mux: %r"); - break; - } - } + } + if(unacked>0 || ppk.len>0){ + l = spk.len; + if(ppk.len + l > 255) + l = 255 - ppk.len; + memmove(ppk.data + ppk.len, spk.data, l); + ppk.len += l; + } + if(unacked == 0){ sethdr(&tpk, Tdata); - memcpy(tpk.data, spk.data, spk.len); + memmove(tpk.data, spk.data, spk.len); tpk.len = spk.len; tpk.seq = ++tseq; unacked = spk.len; - retries = 2; - netsend(&tpk, Hdrsz + spk.len); - break; - case Fcec: - if (memcmp(spk.src, ea, Eaddrlen) != 0 || - ntohs(spk.etype) != Etype) - continue; - if (spk.type == Toffer && - memcmp(spk.dst, bcast, Eaddrlen) != 0) { - muxfree(m); - return 1; - } - if (spk.conn != contag) - continue; - switch (spk.type) { - case Tdata: - if (spk.seq == rseq) - break; - nocrwrite(1, spk.data, spk.len); - memmove(spk.dst, spk.src, Eaddrlen); - memset(spk.src, 0, Eaddrlen); - spk.type = Tack; - spk.len = 0; - rseq = spk.seq; - netsend(&spk, 60); - break; - case Tack: - if (spk.seq == tseq) - unacked = 0; + netsend(&tpk, Hdrsz+spk.len); + muxtimeout(m, Timeout); + } + break; + case Fcec: + if(memcmp(spk.src, ea, 6) != 0 || ntohs(spk.etype) != Etype) + continue; +// if(spk.type == Toffer && memcmp(spk.dst, bcast, 6) != 0){ + if(spk.type == Toffer && spk.seq == 0){ + muxfree(m); + return 1; + } + if(spk.conn != contag) + continue; + switch(spk.type){ + case Tdata: + if(spk.seq == rseq) break; - case Treset: - muxfree(m); - return 1; + nocrwrite(1, spk.data, spk.len); + memmove(spk.dst, spk.src, Eaddrlen); + memset(spk.src, 0, Eaddrlen); + spk.type = Tack; + spk.len = 0; + rseq = spk.seq; + netsend(&spk, 60); + break; + case Tack: + if(spk.seq == tseq){ + muxtimeout(m, 0); + unacked = 0; + if(ppk.len > 0){ + tpk = ppk; + tpk.seq = ++tseq; + unacked = spk.len; + netsend(&tpk, Hdrsz+spk.len); + muxtimeout(m, Timeout); + ppk.len = 0; + } } break; - case Ffatal: + case Treset: muxfree(m); - fprint(2, "kbd read error\n"); - exits0("fatal"); + return 1; } + break; + case Ffatal: + muxfree(m); + fprint(2, "kbd read error\n"); + exits0("fatal"); + } } int @@ -536,10 +583,14 @@ if(con) ethclose(); con = tab + n; - if(ethopen() < 0){ + if(debug) + fprint(2, "ethopen->\n"); + if(ethopen(!specific()) < 0){ fprint(2, "connection failed\n"); return 0; } + if(debug) + fprint(2, "ethopen<-\n"); r = doloop(); if(r <= 0) return r; --- /sys/src/cmd/cec/cec.h Thu Apr 12 12:25:26 2012 +++ /sys/src/cmd/cec/cec.h Wed Jul 29 02:40:06 2009 @@ -1,6 +1,10 @@ +enum { + Eaddrlen = 6, +}; + typedef struct { - uchar dst[6]; - uchar src[6]; + uchar dst[Eaddrlen]; + uchar src[Eaddrlen]; ushort etype; uchar type; uchar conn; @@ -12,6 +16,8 @@ enum { Fkbd, Fcec, + Ftimeout, + Ftimedout, Ffatal, }; @@ -27,6 +33,7 @@ Mux *mux(int fd[2]); void muxfree(Mux*); int muxread(Mux*, Pkt*); +void muxtimeout(Mux*, int); int netget(void *, int); int netopen(char *name); --- /sys/src/cmd/cec/mux.c Thu Apr 12 12:25:26 2012 +++ /sys/src/cmd/cec/mux.c Wed Jul 29 02:33:56 2009 @@ -19,6 +19,11 @@ Muxproc p[2]; int pfd[2]; int inuse; + int to; + int retries; + int step; + vlong t0; + vlong t1; }; static Mux smux = { @@ -100,11 +105,63 @@ return m; } +#define MSEC() (nsec()/1000000) + +static void +timestamp(Mux *m) +{ + m->t0 = MSEC(); +} + int -muxread(Mux *m, Pkt *p) +timeout(Mux *m) { - if(read(m->pfd[0], &m->m, sizeof m->m) == -1) + vlong t; + + if(m->to == 0) return -1; + t = m->t0 + m->step; + if(t > m->t1) + t = m->t1; + t -= MSEC(); + if(t < 5) + t = 0; + return t; +} + +int +muxread(Mux *m, Pkt *p) +{ + int r, t; + + t = timeout(m); + r = -1; + if(t != 0){ + if(t > 0) + alarm(t); + r = read(m->pfd[0], &m->m, sizeof m->m); + if(t > 0) + alarm(0); + } + if(r == -1){ + timestamp(m); + if(m->t0 + 5 >= m->t1) + return Ftimedout; + m->retries++; + if(m->step < 1000) + m->step <<= 1; + return Ftimeout; + } memcpy(p, &m->m.p, sizeof *p); return m->m.type; +} + +void +muxtimeout(Mux *m, int to) +{ + m->retries = 0; + timestamp(m); + m->step = 75; + m->to = to; + m->t1 = m->t0 + to; } --- /sys/src/cmd/cec/plan9.c Thu Apr 12 12:25:26 2012 +++ /sys/src/cmd/cec/plan9.c Wed Mar 12 15:55:09 2008 @@ -1,4 +1,3 @@ -/* Copyright © Coraid, Inc. 2006. All rights reserved. */ #include #include #include "cec.h" @@ -84,6 +83,7 @@ dump(p, len); } if (len < 60) - len = 60; /* mintu */ + len = 60; return write(fd, p, len); } + --- /sys/man/8/cec Thu Apr 12 12:25:01 2012 +++ /sys/man/8/cec Sat Aug 8 16:40:08 2009 @@ -21,43 +21,55 @@ .B -s .I shelf ] [ -.I interface +.B interface ] .SH DESCRIPTION -.I Cec -uses raw Ethernet packets to connect to a CEC server for console access. -All clients share the same session. -Coraid appliances and Coraid Plan 9 kernels can currently be CEC servers. +The +.I cec +command uses raw ethernet frames to connect to a CEC +server to simulate a serial connection. The client and +protocol provide for packet acknowledgement and +retransmission. +Coraid appliances and Coraid Plan 9 kernels can currently be +CEC servers. Each uses CEC to provide console access. .PP -.I Cec -starts by probing the specified network interface +A +.I cec +process starts by probing the specified network interface for available CEC servers. The default is -.BR /net/ether0 . +.I /net/ether0 . Only one .I cec -process may be run per Ethernet interface. +process may be run per ethernet interface. The +.B -S +flag posts its argument to +.B /srv +to allow sharing. If the server is specified with the .BR -b , .BR -h , or .B -s -options, communication will +flag, the connection will proceed immediately upon discovery of the first CEC server -with the specified address. -Otherwise, a selection prompt -will be displayed showing the discovered CEC servers +with the specified address. Otherwise, a selection prompt +will be displayed showing the discovered CEC servers available for communication. Unless the .B -p -option is specified, +flag is specified, .I cec exits if no matching servers are found. -The selection prompt accepts +.IP +.EX + 0 5 003048865f1e shelf5 + [#qp]: +.EE .LP -.TF "\fInumber " +The selection prompt accepts .TP -.I number +.I n Connect to server -.I number +.I n (from the first column), .TP .B p @@ -65,89 +77,57 @@ .TP .B q Quit. -.PD .PP Note the selection number is not the shelf address but the .IR cec -generated sequence number printed in the leftmost column. .PP -Once connected to a CEC server, typing the escape character -will drop the user into an escape prompt where the user may type -.L q -to quit the connection, -.L i -to send the escape character -across the connection, or -.L . -to continue the connection. +Once connected to a cec server, entering the escape sequence +will drop the user into an escape prompt where the user may +type 'q' to quit the connection, 'i' to send the escape sequence +across the connection, or '.' to continue the connection. +The escape sequence is printed on connection. +If the connection is unexpectedly lost, +the initial probe process outlined above is rerun. .SS Options -.TP -.B -c -Set the escape character to -.RI control- esc . -The default setting is control-\e. +.TP 1i +.BI -c " esc" +Set the escape caracter to +.BI ctl - +.IR esc . +The default setting is +.BR ctl -\e. .TP .B -d -Print debugging information. +Output debugging information. .TP -.B -e -Connect to the server with Ethernet address -.IR ea ; -implies -.BR -p . +.BI -e " ea" +Connect to the server with ethernet address +.IR ea . This option implies +.IR -p . .TP -.B -h -Connect to the server +.BI -h " host" +Connect to the server named .IR host . -Note that this name might not be the same as the contents of -.L /dev/sysname -on the target system. +Note this name may not be the same as the value of +.LR /dev/sysname . .TP .B -p -Persist: continue trying to connect even if +Persistence. Continue trying to connect even if there are no matching servers. This is useful when -connecting to a CPU server before it boots. +connecting to a cpu server before it boots. .TP -.B -s -Connect to the server at address +.BI -s " shelf" +Connect to the server at shelf address .IR shelf . -.TP -.B -S -Post the CEC connection as -.BI /srv/ srv -to allow sharing. -.PP -If the -.BR -e , -.BR -s , -or -.B -h -options are given, -.I cec -will exit upon closing the connection. -Otherwise, -.I cec -will return to the selection prompt upon connection close. -.SH EXAMPLES -.IP -.EX -; cec '#l1/ether1' - 0 1 003048679b89 -[#qp]: 0 - -SR shelf 1> -SR shelf 1> >>> q - 0 1 003048679b89 -[#qp]: q -; -.EE -.SH SOURCE -.B /sys/src/cmd/cec -.\" .SH "SEE ALSO" -.\" .IR cec (3) +.SH "SEE ALSO" +.IR cec (3). .SH BUGS The CEC protocol should be integrated with the console server. -The arbitration between the keyboard and network is suboptimal. +.PP +CEC should multiplex network connections as +.B /net/tcp +does. .PP Early boot information and very late crash information from servers may be lost due to timing quirks.