909 lines · 17.5 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * This part takes care of locking except for initialization and * other threads created by the hw dep. drivers. */ #include <u.h>#include <libc.h>#include <ctype.h>#include <thread.h>#include "usb.h"#include "usbfs.h"#include "serial.h"#include "prolific.h"#include "ucons.h"#include "ftdi.h"#include "silabs.h" int serialdebug; enum {	/* Qids. Maintain order (relative to dirtabs structs) */	Qroot	= 0,	Qctl,	Qdata,	Qmax,}; typedef struct Dirtab Dirtab;struct Dirtab {	char	*name;	int	mode;}; static Dirtab dirtab[] = {	[Qroot]	"/",		DMDIR|0555,	[Qdata]	"%s",		0660,	[Qctl]	"%sctl",	0664,}; static int sdebug; static voidserialfatal(Serial *ser){	Serialport *p;	int i; 	dsprint(2, "serial: fatal error, detaching\n");	devctl(ser->dev, "detach"); 	for(i = 0; i < ser->nifcs; i++){		p = &ser->p[i];		usbfsdel(&p->fs);		if(p->w4data != nil)			chanclose(p->w4data);		if(p->gotdata != nil)			chanclose(p->gotdata);		if(p->readc)			chanclose(p->readc);	}} /* I sleep with the lock... only way to drain in general */static voidserialdrain(Serialport *p){	Serial *ser;	uint baud, pipesize; 	ser = p->s;	baud = p->baud; 	if(p->baud == ~0)		return;	if(ser->maxwtrans < 256)		pipesize = 256;	else		pipesize = ser->maxwtrans;	/* wait for the at least 256-byte pipe to clear */	sleep(10 + pipesize/((1 + baud)*1000));	if(ser->clearpipes != nil)		ser->clearpipes(p);} intserialreset(Serial *ser){	Serialport *p;	int i, res; 	res = 0;	/* cmd for reset */	for(i = 0; i < ser->nifcs; i++){		p = &ser->p[i];		serialdrain(p);	}	if(ser->reset != nil)		res = ser->reset(ser, nil);	return res;} /* call this if something goes wrong, must be qlocked */intserialrecover(Serial *ser, Serialport *p, Dev *ep, char *err){	if(p != nil)		dprint(2, "serial[%d], %s: %s, level %d\n", p->interfc,			p->name, err, ser->recover);	else		dprint(2, "serial[%s], global error, level %d\n",			ser->p[0].name, ser->recover);	ser->recover++;	if(strstr(err, "detached") != nil)		return -1;	if(ser->recover < 3){		if(p != nil){				if(ep != nil){				if(ep == p->epintr)					unstall(ser->dev, p->epintr, Ein);				if(ep == p->epin)					unstall(ser->dev, p->epin, Ein);				if(ep == p->epout)					unstall(ser->dev, p->epout, Eout);				return 0;			} 			if(p->epintr != nil)				unstall(ser->dev, p->epintr, Ein);			if(p->epin != nil)				unstall(ser->dev, p->epin, Ein);			if(p->epout != nil)				unstall(ser->dev, p->epout, Eout);		}		return 0;	}	if(ser->recover > 4 && ser->recover < 8)		serialfatal(ser);	if(ser->recover > 8){		ser->reset(ser, p);		return 0;	}	if(serialreset(ser) < 0)		return -1;	return 0;} static intserialctl(Serialport *p, char *cmd){	Serial *ser;	int c, i, n, nf, nop, nw, par, drain, set, lines;	char *f[16];	uchar x; 	ser = p->s;	drain = set = lines = 0;	nf = tokenize(cmd, f, nelem(f));	for(i = 0; i < nf; i++){		if(strncmp(f[i], "break", 5) == 0){			if(ser->setbreak != nil)				ser->setbreak(p, 1);			continue;		} 		nop = 0;		n = atoi(f[i]+1);		c = *f[i];		if (isascii(c) && isupper(c))			c = tolower(c);		switch(c){		case 'b':			drain++;			p->baud = n;			set++;			break;		case 'c':			p->dcd = n;			// lines++;			++nop;			break;		case 'd':			p->dtr = n;			lines++;			break;		case 'e':			p->dsr = n;			// lines++;			++nop;			break;		case 'f':		/* flush the pipes */			drain++;			break;		case 'h':		/* hangup?? */			p->rts = p->dtr = 0;			lines++;			fprint(2, "serial: %c, unsure ctl\n", c);			break;		case 'i':			++nop;			break;		case 'k':			drain++;			ser->setbreak(p, 1);			sleep(n);			ser->setbreak(p, 0);			break;		case 'l':			drain++;			p->bits = n;			set++;			break;		case 'm':			drain++;			if(ser->modemctl != nil)				ser->modemctl(p, n);			if(n == 0)				p->cts = 0;			break;		case 'n':			p->blocked = n;			++nop;			break;		case 'p':		/* extended... */			if(strlen(f[i]) != 2)				return -1;			drain++;			par = f[i][1];			if(par == 'n')				p->parity = 0;			else if(par == 'o')				p->parity = 1;			else if(par == 'e')				p->parity = 2;			else if(par == 'm')	/* mark parity */				p->parity = 3;			else if(par == 's')	/* space parity */				p->parity = 4;			else				return -1;			set++;			break;		case 'q':			// drain++;			p->limit = n;			++nop;			break;		case 'r':			drain++;			p->rts = n;			lines++;			break;		case 's':			drain++;			p->stop = n;			set++;			break;		case 'w':			/* ?? how do I put this */			p->timer = n * 100000LL;			++nop;			break;		case 'x':			if(n == 0)				x = CTLS;			else				x = CTLQ;			if(ser->wait4write != nil)				nw = ser->wait4write(p, &x, 1);			else				nw = write(p->epout->dfd, &x, 1);			if(nw != 1){				serialrecover(ser, p, p->epout, "");				return -1;			}			break;		}		/*		 * don't print.  the condition is harmless and the print		 * splatters all over the display.		 */		USED(nop);		if (0 && nop)			fprint(2, "serial: %c, unsupported nop ctl\n", c);	}	if(drain)		serialdrain(p);	if(lines && !set){		if(ser->sendlines != nil && ser->sendlines(p) < 0)			return -1;	} else if(set){		if(ser->setparam != nil && ser->setparam(p) < 0)			return -1;	}	ser->recover = 0;	return 0;} char *pformat = "noems"; char *serdumpst(Serialport *p, char *buf, int bufsz){	char *e, *s;	Serial *ser; 	ser = p->s; 	e = buf + bufsz;	s = seprint(buf, e, "b%d ", p->baud);	s = seprint(s, e, "c%d ", p->dcd);	/* unimplemented */	s = seprint(s, e, "d%d ", p->dtr);	s = seprint(s, e, "e%d ", p->dsr);	/* unimplemented */	s = seprint(s, e, "l%d ", p->bits);	s = seprint(s, e, "m%d ", p->mctl);	if(p->parity >= 0 || p->parity < strlen(pformat))		s = seprint(s, e, "p%c ", pformat[p->parity]);	else		s = seprint(s, e, "p%c ", '?');	s = seprint(s, e, "r%d ", p->rts);	s = seprint(s, e, "s%d ", p->stop);	s = seprint(s, e, "i%d ", p->fifo);	s = seprint(s, e, "\ndev(%d) ", 0);	s = seprint(s, e, "type(%d)  ", ser->type);	s = seprint(s, e, "framing(%d) ", p->nframeerr);	s = seprint(s, e, "overruns(%d) ", p->novererr);	s = seprint(s, e, "berr(%d) ", p->nbreakerr);	s = seprint(s, e, " serr(%d)\n", p->nparityerr);	return s;} static intserinit(Serialport *p){	int res;	res = 0;	Serial *ser; 	ser = p->s; 	if(ser->init != nil)		res = ser->init(p);	if(ser->getparam != nil)		ser->getparam(p);	p->nframeerr = p->nparityerr = p->nbreakerr = p->novererr = 0; 	return res;} static intdwalk(Usbfs *fs, Fid *fid, char *name){	int i;	char *dname;	Qid qid;	Serialport *p; 	qid = fid->qid;	if((qid.type & QTDIR) == 0){		werrstr("walk in non-directory");		return -1;	} 	if(strcmp(name, "..") == 0){		/* must be /eiaU%d; i.e. our root dir. */		fid->qid.path = Qroot | fs->qid;		fid->qid.vers = 0;		fid->qid.type = QTDIR;		return 0;	} 	p = fs->aux;	for(i = 1; i < nelem(dirtab); i++){		dname = smprint(dirtab[i].name, p->name);		if(strcmp(name, dname) == 0){			qid.path = i | fs->qid;			qid.vers = 0;			qid.type = dirtab[i].mode >> 24;			fid->qid = qid;			free(dname);			return 0;		} else			free(dname);	}	werrstr(Enotfound);	return -1;} static voiddostat(Usbfs *fs, int path, Dir *d){	Dirtab *t;	Serialport *p; 	t = &dirtab[path];	d->qid.path = path;	d->qid.type = t->mode >> 24;	d->mode = t->mode;	p = fs->aux; 	if(strcmp(t->name, "/") == 0)		d->name = t->name;	else		snprint(d->name, Namesz, t->name, p->fs.name);	d->length = 0;} static intdstat(Usbfs *fs, Qid qid, Dir *d){	int path; 	path = qid.path & ~fs->qid;	dostat(fs, path, d);	d->qid.path |= fs->qid;	return 0;} static intdopen(Usbfs *fs, Fid *fid, int){	ulong path;	Serialport *p; 	path = fid->qid.path & ~fs->qid;	p = fs->aux;	switch(path){		/* BUG: unneeded? */	case Qdata:		dsprint(2, "serial, opened data\n");		break;	case Qctl:		dsprint(2, "serial, opened ctl\n");		if(p->isjtag)			return 0;		serialctl(p, "l8 i1");	/* default line parameters */		break;	}	return 0;}  static voidfilldir(Usbfs *fs, Dir *d, Dirtab *tab, int i, void *v){	Serialport *p; 	p = v;	d->qid.path = i | fs->qid;	d->mode = tab->mode;	if((d->mode & DMDIR) != 0)		d->qid.type = QTDIR;	else		d->qid.type = QTFILE;	sprint(d->name, tab->name, p->name);	/* hope it fits */} static intdirgen(Usbfs *fs, Qid, int i, Dir *d, void *p){	i++;				/* skip root */	if(i >= nelem(dirtab))		return -1;	filldir(fs, d, &dirtab[i], i, p);	return 0;} enum {	Serbufsize	= 256,}; static longdread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset){	int dfd;	long rcount;	ulong path;	char *e, *buf, *err;	/* change */	Qid q;	Serialport *p;	Serial *ser;	static int errrun, good; 	q = fid->qid;	path = fid->qid.path & ~fs->qid;	p = fs->aux;	ser = p->s; 	buf = emallocz(Serbufsize, 1);	err = emallocz(Serbufsize, 1);	qlock(ser);	switch(path){	case Qroot:

Lines 1–500 of 909.