Plan 9 from Bell Labs’s /usr/web/sources/patch/saved/cpu-cec/devcec.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 * 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;
}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.