123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758 |
- /*
- * This part takes care of locking except for initialization and
- * other threads created by the hw dep. drivers.
- * BUG: An error on the device does not make the driver exit.
- * It probably should.
- */
- #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"
- 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] "eiaU", 0660,
- [Qctl] "eiaUctl", 0664,
- };
- static int sdebug;
- int
- serialnop(Serial *)
- {
- return 0;
- }
- int
- serialnopctl(Serial *, int)
- {
- return 0;
- }
- static void
- serialfatal(Serial *ser)
- {
- dsprint(2, "serial: fatal error, detaching\n");
- devctl(ser->dev, "detach");
- usbfsdel(&ser->fs);
- }
- /* I sleep with the lock... only way to drain in general */
- static void
- serialdrain(Serial *ser)
- {
- uint baud, pipesize;
- if(ser->maxwrite < 256)
- pipesize = 256;
- else
- pipesize = ser->maxwrite;
- baud = ser->baud;
- /* wait for the at least 256-byte pipe to clear */
- sleep(10 + pipesize/((1 + baud)*1000));
- if(ser->clearpipes != nil)
- ser->clearpipes(ser);
- }
- int
- serialreset(Serial *ser)
- {
- /* cmd for reset */
- serialdrain(ser);
- if(ser->reset != nil)
- ser->reset(ser);
- return 0;
- }
- /* call this if something goes wrong */
- int
- serialrecover(Serial *ser, char *err)
- {
- if(strstr(err, "detached") != nil)
- return -1;
- if(ser->recover > 1)
- serialfatal(ser);
- ser->recover++;
- if(serialreset(ser) < 0)
- return -1;
- ser->recover = 0;
- return 0;
- }
- static int
- serialctl(Serial *p, char *cmd)
- {
- int c, i, n, nf, nop, nw, par, drain, set, lines;
- char *f[16];
- uchar x;
- drain = set = lines = 0;
- nf = tokenize(cmd, f, nelem(f));
- for(i = 0; i < nf; i++){
- if(strncmp(f[i], "break", 5) == 0){
- if(p->setbreak != nil)
- p->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++;
- p->setbreak(p, 1);
- sleep(n);
- p->setbreak(p, 0);
- break;
- case 'l':
- drain++;
- p->bits = n;
- set++;
- break;
- case 'm':
- drain++;
- if(p->modemctl != nil)
- p->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(p->wait4write != nil)
- nw = p->wait4write(p, &x, 1);
- else
- nw = write(p->epout->dfd, &x, 1);
- if(nw != 1){
- serialrecover(p, "");
- 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(p->sendlines != nil && p->sendlines(p) < 0)
- return -1;
- } else if(set){
- if(p->setparam != nil && p->setparam(p) < 0)
- return -1;
- }
- return 0;
- }
- char *pformat = "noems";
- char *
- serdumpst(Serial *ser, char *buf, int bufsz)
- {
- char *e, *s;
- e = buf + bufsz;
- s = seprint(buf, e, "b%d ", ser->baud);
- s = seprint(s, e, "c%d ", ser->dcd); /* unimplemented */
- s = seprint(s, e, "d%d ", ser->dtr);
- s = seprint(s, e, "e%d ", ser->dsr); /* unimplemented */
- s = seprint(s, e, "l%d ", ser->bits);
- s = seprint(s, e, "m%d ", ser->mctl);
- if(ser->parity >= 0 || ser->parity < strlen(pformat))
- s = seprint(s, e, "p%c ", pformat[ser->parity]);
- else
- s = seprint(s, e, "p%c ", '?');
- s = seprint(s, e, "r%d ", ser->rts);
- s = seprint(s, e, "s%d ", ser->stop);
- s = seprint(s, e, "i%d ", ser->fifo);
- s = seprint(s, e, "\ndev(%d) ", 0);
- s = seprint(s, e, "type(%d) ", ser->type);
- s = seprint(s, e, "framing(%d) ", ser->nframeerr);
- s = seprint(s, e, "overruns(%d) ", ser->novererr);
- s = seprint(s, e, "berr(%d) ", ser->nbreakerr);
- s = seprint(s, e, " serr(%d) ", ser->nparityerr);
- return s;
- }
- static int
- serinit(Serial *ser)
- {
- int res;
- res = 0;
- if(ser->init != nil)
- res = ser->init(ser);
- if(ser->getparam != nil)
- ser->getparam(ser);
- ser->nframeerr = ser->nparityerr = ser->nbreakerr = ser->novererr = 0;
- return res;
- }
- static int
- dwalk(Usbfs *fs, Fid *fid, char *name)
- {
- int i;
- char *dname;
- Qid qid;
- Serial *ser;
- 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;
- }
- ser = fs->aux;
- for(i = 1; i < nelem(dirtab); i++){
- dname = smprint(dirtab[i].name, ser->fs.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 void
- dostat(Usbfs *fs, int path, Dir *d)
- {
- Dirtab *t;
- Serial *ser;
- t = &dirtab[path];
- d->qid.path = path;
- d->qid.type = t->mode >> 24;
- d->mode = t->mode;
- ser = fs->aux;
- if(strcmp(t->name, "/") == 0)
- d->name = t->name;
- else
- snprint(d->name, Namesz, t->name, ser->fs.name);
- d->length = 0;
- }
- static int
- dstat(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 int
- dopen(Usbfs *fs, Fid *fid, int)
- {
- ulong path;
- // Serial *ser;
- path = fid->qid.path & ~fs->qid;
- // ser = fs->aux;
- switch(path){ /* BUG: unneeded? */
- case Qdata:
- dsprint(2, "serial, opened data\n");
- break;
- case Qctl:
- dsprint(2, "serial, opened ctl\n");
- break;
- }
- return 0;
- }
- static void
- filldir(Usbfs *fs, Dir *d, Dirtab *tab, int i)
- {
- d->qid.path = i | fs->qid;
- d->mode = tab->mode;
- if((d->mode & DMDIR) != 0)
- d->qid.type = QTDIR;
- else
- d->qid.type = QTFILE;
- d->name = tab->name;
- }
- static int
- dirgen(Usbfs *fs, Qid, int i, Dir *d, void *)
- {
- i++; /* skip root */
- if(i >= nelem(dirtab))
- return -1;
- filldir(fs, d, &dirtab[i], i);
- return 0;
- }
- enum {
- Serbufsize = 255,
- };
- static long
- dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
- {
- int dfd;
- long rcount;
- ulong path;
- char *e, *buf, *err; /* change */
- Qid q;
- Serial *ser;
- static int errrun, good;
- q = fid->qid;
- path = fid->qid.path & ~fs->qid;
- ser = fs->aux;
- buf = emallocz(Serbufsize, 1);
- err = emallocz(Serbufsize, 1);
- qlock(ser);
- switch(path){
- case Qroot:
- count = usbdirread(fs, q, data, count, offset, dirgen, nil);
- break;
- case Qdata:
- if(count > ser->maxread)
- count = ser->maxread;
- dsprint(2, "serial: reading from data\n");
- do {
- err[0] = 0;
- dfd = ser->epin->dfd;
- if(usbdebug >= 3)
- dsprint(2, "serial: reading: %ld\n", count);
- assert(count > 0);
- if(ser->wait4data != nil)
- rcount = ser->wait4data(ser, data, count);
- else{
- qunlock(ser);
- rcount = read(dfd, data, count);
- qlock(ser);
- }
- /*
- * if we encounter a long run of continuous read
- * errors, do something drastic so that our caller
- * doesn't just spin its wheels forever.
- */
- if(rcount < 0) {
- snprint(err, Serbufsize, "%r");
- ++errrun;
- sleep(20);
- if (good > 0 && errrun > 10000) {
- /* the line has been dropped; give up */
- qunlock(ser);
- fprint(2, "%s: line %s is gone: %r\n",
- argv0, ser->fs.name);
- threadexitsall("serial line gone");
- }
- } else {
- errrun = 0;
- good++;
- }
- if(usbdebug >= 3)
- dsprint(2, "serial: read: %s %ld\n", err, rcount);
- } while(rcount < 0 && strstr(err, "timed out") != nil);
- dsprint(2, "serial: read from bulk %ld, %10.10s\n", rcount, err);
- if(rcount < 0){
- dsprint(2, "serial: need to recover, data read %ld %r\n",
- count);
- serialrecover(ser, err);
- }
- dsprint(2, "serial: read from bulk %ld\n", rcount);
- count = rcount;
- break;
- case Qctl:
- if(offset != 0)
- count = 0;
- else {
- e = serdumpst(ser, buf, Serbufsize);
- count = usbreadbuf(data, count, 0, buf, e - buf);
- }
- break;
- }
- qunlock(ser);
- free(err);
- free(buf);
- return count;
- }
- static long
- altwrite(Serial *ser, uchar *buf, long count)
- {
- int nw, dfd;
- char err[128];
- do{
- if(ser->wait4write != nil)
- /* unlocked inside later */
- nw = ser->wait4write(ser, buf, count);
- else{
- dfd = ser->epout->dfd;
- qunlock(ser);
- nw = write(dfd, buf, count);
- qlock(ser);
- }
- rerrstr(err, sizeof err);
- } while(nw < 0 && strstr(err, "timed out") != nil);
- if(nw != count){
- dsprint(2, "serial: need to recover, status in write %d %r\n",
- nw);
- snprint(err, sizeof err, "%r");
- serialrecover(ser, err);
- }
- return nw;
- }
- static long
- dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong)
- {
- ulong path;
- char *cmd;
- Serial *ser;
- ser = fs->aux;
- path = fid->qid.path & ~fs->qid;
- qlock(ser);
- switch(path){
- case Qdata:
- count = altwrite(ser, (uchar *)buf, count);
- break;
- case Qctl:
- cmd = emallocz(count+1, 1);
- memmove(cmd, buf, count);
- cmd[count] = 0;
- if(serialctl(ser, cmd) < 0){
- qunlock(ser);
- werrstr(Ebadctl);
- free(cmd);
- return -1;
- }
- free(cmd);
- break;
- default:
- qunlock(ser);
- werrstr(Eperm);
- return -1;
- }
- qunlock(ser);
- return count;
- }
- static int
- openeps(Serial *ser, int epin, int epout, int epintr)
- {
- ser->epin = openep(ser->dev, epin);
- if(ser->epin == nil){
- fprint(2, "serial: openep %d: %r\n", epin);
- return -1;
- }
- ser->epout = openep(ser->dev, epout);
- if(ser->epout == nil){
- fprint(2, "serial: openep %d: %r\n", epout);
- closedev(ser->epin);
- return -1;
- }
- devctl(ser->epin, "timeout 1000");
- devctl(ser->epout, "timeout 1000");
- if(ser->hasepintr){
- ser->epintr = openep(ser->dev, epintr);
- if(ser->epintr == nil){
- fprint(2, "serial: openep %d: %r\n", epintr);
- closedev(ser->epin);
- closedev(ser->epout);
- return -1;
- }
- opendevdata(ser->epintr, OREAD);
- devctl(ser->epintr, "timeout 1000");
- }
- if(ser->seteps!= nil)
- ser->seteps(ser);
- opendevdata(ser->epin, OREAD);
- opendevdata(ser->epout, OWRITE);
- if(ser->epin->dfd < 0 || ser->epout->dfd < 0 ||
- (ser->hasepintr && ser->epintr->dfd < 0)){
- fprint(2, "serial: open i/o ep data: %r\n");
- closedev(ser->epin);
- closedev(ser->epout);
- if(ser->hasepintr)
- closedev(ser->epintr);
- return -1;
- }
- return 0;
- }
- static int
- findendpoints(Serial *ser)
- {
- int i, epin, epout, epintr;
- Ep *ep, **eps;
- Usbdev *ud;
- epintr = epin = epout = -1;
- ud = ser->dev->usb;
- /*
- * interfc 0 means start from the start which is equiv to
- * iterate through endpoints probably, could be done better
- */
- if(ser->interfc == 0)
- eps = ud->ep;
- else
- eps = ser->dev->usb->conf[0]->iface[ser->interfc]->ep;
- for(i = 0; i < Niface; i++){
- if((ep = eps[i]) == nil)
- continue;
- if(ser->hasepintr && ep->type == Eintr &&
- ep->dir == Ein && epintr == -1)
- epintr = ep->id;
- if(ep->type == Ebulk){
- if(ep->dir == Ein && epin == -1)
- epin = ep->id;
- if(ep->dir == Eout && epout == -1)
- epout = ep->id;
- }
- }
- dprint(2, "serial: ep ids: in %d out %d intr %d\n", epin, epout, epintr);
- if(epin == -1 || epout == -1 || (ser->hasepintr && epintr == -1))
- return -1;
- if(openeps(ser, epin, epout, epintr) < 0)
- return -1;
- dprint(2, "serial: ep in %s out %s\n", ser->epin->dir, ser->epout->dir);
- if(ser->hasepintr)
- dprint(2, "serial: ep intr %s\n", ser->epintr->dir);
- if(usbdebug > 1 || serialdebug > 2){
- devctl(ser->epin, "debug 1");
- devctl(ser->epout, "debug 1");
- if(ser->hasepintr)
- devctl(ser->epintr, "debug 1");
- devctl(ser->dev, "debug 1");
- }
- return 0;
- }
- /* keep in sync with main.c */
- static int
- usage(void)
- {
- werrstr("usage: usb/serial [-dD] [-m mtpt] [-s srv]");
- return -1;
- }
- static void
- serdevfree(void *a)
- {
- Serial *ser = a;
- if(ser == nil)
- return;
- if(ser->hasepintr)
- closedev(ser->epintr);
- closedev(ser->epin);
- closedev(ser->epout);
- ser->epintr = ser->epin = ser->epout = nil;
- chanfree(ser->w4data);
- chanfree(ser->gotdata);
- chanfree(ser->w4empty);
- free(ser);
- }
- static Usbfs serialfs = {
- .walk = dwalk,
- .open = dopen,
- .read = dread,
- .write= dwrite,
- .stat = dstat,
- };
- int
- serialmain(Dev *dev, int argc, char* argv[])
- {
- Serial *ser;
- char buf[50];
- ARGBEGIN{
- case 'd':
- serialdebug++;
- break;
- default:
- return usage();
- }ARGEND
- if(argc != 0)
- return usage();
- ser = dev->aux = emallocz(sizeof(Serial), 1);
- /* BUG: could this go wrong? channel leaks? */
- ser->w4data = chancreate(sizeof(ulong), 0);
- ser->gotdata = chancreate(sizeof(ulong), 0);
- ser->w4empty = chancreate(sizeof(ulong), 0);
- ser->maxread = ser->maxwrite = sizeof ser->data;
- ser->dev = dev;
- dev->free = serdevfree;
- snprint(buf, sizeof buf, "vid %#06x did %#06x",
- dev->usb->vid, dev->usb->did);
- ser->fs = serialfs;
- if(plmatch(buf) == 0){
- ser->hasepintr = 1;
- ser->Serialops = plops;
- } else if(uconsmatch(buf) == 0)
- ser->Serialops = uconsops;
- else if(ftmatch(ser, buf) == 0)
- ser->Serialops = ftops;
- else {
- werrstr("serial: no serial devices found");
- closedev(dev);
- return -1;
- }
- if(findendpoints(ser) < 0){
- werrstr("serial: no endpoints found");
- closedev(dev);
- return -1;
- }
- if(serinit(ser) < 0){
- dprint(2, "serial: serinit: %r\n");
- closedev(dev);
- return -1;
- }
- snprint(ser->fs.name, sizeof ser->fs.name, "eiaU%d", dev->id);
- fprint(2, "%s\n", ser->fs.name);
- ser->fs.dev = dev;
- incref(dev);
- ser->fs.aux = ser;
- usbfsadd(&ser->fs);
- closedev(dev);
- return 0;
- }
|