1b2495906SDavid du Colombier /* 2b2495906SDavid du Colombier * This part takes care of locking except for initialization and 3b2495906SDavid du Colombier * other threads created by the hw dep. drivers. 4b2495906SDavid du Colombier * BUG: An error on the device does not make the driver exit. 5b2495906SDavid du Colombier * It probably should. 6b2495906SDavid du Colombier */ 7b2495906SDavid du Colombier 85e4f5c78SDavid du Colombier #include <u.h> 95e4f5c78SDavid du Colombier #include <libc.h> 105e4f5c78SDavid du Colombier #include <ctype.h> 115e4f5c78SDavid du Colombier #include <thread.h> 125e4f5c78SDavid du Colombier #include "usb.h" 135e4f5c78SDavid du Colombier #include "usbfs.h" 145e4f5c78SDavid du Colombier #include "serial.h" 154d52e0f0SDavid du Colombier #include "prolific.h" 164d52e0f0SDavid du Colombier #include "ucons.h" 1780e9508eSDavid du Colombier #include "ftdi.h" 184d52e0f0SDavid du Colombier 195e4f5c78SDavid du Colombier int serialdebug; 205e4f5c78SDavid du Colombier 215e4f5c78SDavid du Colombier enum { 225e4f5c78SDavid du Colombier /* Qids. Maintain order (relative to dirtabs structs) */ 235e4f5c78SDavid du Colombier Qroot = 0, 245e4f5c78SDavid du Colombier Qctl, 255e4f5c78SDavid du Colombier Qdata, 265e4f5c78SDavid du Colombier Qmax, 275e4f5c78SDavid du Colombier }; 285e4f5c78SDavid du Colombier 295e4f5c78SDavid du Colombier typedef struct Dirtab Dirtab; 305e4f5c78SDavid du Colombier struct Dirtab { 315e4f5c78SDavid du Colombier char *name; 325e4f5c78SDavid du Colombier int mode; 335e4f5c78SDavid du Colombier }; 345e4f5c78SDavid du Colombier 355e4f5c78SDavid du Colombier static Dirtab dirtab[] = { 365e4f5c78SDavid du Colombier [Qroot] "/", DMDIR|0555, 37b2495906SDavid du Colombier [Qdata] "eiaU", 0660, 38b2495906SDavid du Colombier [Qctl] "eiaUctl", 0664, 395e4f5c78SDavid du Colombier }; 405e4f5c78SDavid du Colombier 415e4f5c78SDavid du Colombier static int sdebug; 425e4f5c78SDavid du Colombier 434d52e0f0SDavid du Colombier int 444d52e0f0SDavid du Colombier serialnop(Serial *) 454d52e0f0SDavid du Colombier { 464d52e0f0SDavid du Colombier return 0; 474d52e0f0SDavid du Colombier } 484d52e0f0SDavid du Colombier 494d52e0f0SDavid du Colombier int 504d52e0f0SDavid du Colombier serialnopctl(Serial *, int) 514d52e0f0SDavid du Colombier { 524d52e0f0SDavid du Colombier return 0; 534d52e0f0SDavid du Colombier } 544d52e0f0SDavid du Colombier 555e4f5c78SDavid du Colombier static void 565e4f5c78SDavid du Colombier serialfatal(Serial *ser) 575e4f5c78SDavid du Colombier { 585e4f5c78SDavid du Colombier dsprint(2, "serial: fatal error, detaching\n"); 595e4f5c78SDavid du Colombier devctl(ser->dev, "detach"); 605e4f5c78SDavid du Colombier usbfsdel(&ser->fs); 615e4f5c78SDavid du Colombier } 625e4f5c78SDavid du Colombier 6380e9508eSDavid du Colombier /* I sleep with the lock... only way to drain in general */ 645e4f5c78SDavid du Colombier static void 655e4f5c78SDavid du Colombier serialdrain(Serial *ser) 665e4f5c78SDavid du Colombier { 6780e9508eSDavid du Colombier uint baud, pipesize; 685e4f5c78SDavid du Colombier 6980e9508eSDavid du Colombier if(ser->maxwrite < 256) 7080e9508eSDavid du Colombier pipesize = 256; 7180e9508eSDavid du Colombier else 7280e9508eSDavid du Colombier pipesize = ser->maxwrite; 735e4f5c78SDavid du Colombier baud = ser->baud; 7480e9508eSDavid du Colombier /* wait for the at least 256-byte pipe to clear */ 7580e9508eSDavid du Colombier sleep(10 + pipesize/((1 + baud)*1000)); 7680e9508eSDavid du Colombier if(ser->clearpipes != nil) 775e4f5c78SDavid du Colombier ser->clearpipes(ser); 785e4f5c78SDavid du Colombier } 795e4f5c78SDavid du Colombier 805e4f5c78SDavid du Colombier int 815e4f5c78SDavid du Colombier serialreset(Serial *ser) 825e4f5c78SDavid du Colombier { 835e4f5c78SDavid du Colombier /* cmd for reset */ 845e4f5c78SDavid du Colombier serialdrain(ser); 8580e9508eSDavid du Colombier if(ser->reset != nil) 8680e9508eSDavid du Colombier ser->reset(ser); 875e4f5c78SDavid du Colombier return 0; 885e4f5c78SDavid du Colombier } 895e4f5c78SDavid du Colombier 905e4f5c78SDavid du Colombier /* call this if something goes wrong */ 915e4f5c78SDavid du Colombier int 925e4f5c78SDavid du Colombier serialrecover(Serial *ser, char *err) 935e4f5c78SDavid du Colombier { 945e4f5c78SDavid du Colombier if(strstr(err, "detached") != nil) 955e4f5c78SDavid du Colombier return -1; 9616146bc9SDavid du Colombier if(ser->recover > 1) 975e4f5c78SDavid du Colombier serialfatal(ser); 9816146bc9SDavid du Colombier ser->recover++; 995e4f5c78SDavid du Colombier if(serialreset(ser) < 0) 1005e4f5c78SDavid du Colombier return -1; 10116146bc9SDavid du Colombier ser->recover = 0; 1025e4f5c78SDavid du Colombier return 0; 1035e4f5c78SDavid du Colombier } 1045e4f5c78SDavid du Colombier 1055e4f5c78SDavid du Colombier static int 1065e4f5c78SDavid du Colombier serialctl(Serial *p, char *cmd) 1075e4f5c78SDavid du Colombier { 1085e4f5c78SDavid du Colombier int c, i, n, nf, nop, nw, par, drain, set, lines; 1095e4f5c78SDavid du Colombier char *f[16]; 1105e4f5c78SDavid du Colombier uchar x; 1115e4f5c78SDavid du Colombier 1125e4f5c78SDavid du Colombier drain = set = lines = 0; 1135e4f5c78SDavid du Colombier nf = tokenize(cmd, f, nelem(f)); 1145e4f5c78SDavid du Colombier for(i = 0; i < nf; i++){ 1155e4f5c78SDavid du Colombier if(strncmp(f[i], "break", 5) == 0){ 11680e9508eSDavid du Colombier if(p->setbreak != nil) 1175e4f5c78SDavid du Colombier p->setbreak(p, 1); 1185e4f5c78SDavid du Colombier continue; 1195e4f5c78SDavid du Colombier } 1205e4f5c78SDavid du Colombier 1215e4f5c78SDavid du Colombier nop = 0; 1225e4f5c78SDavid du Colombier n = atoi(f[i]+1); 1235e4f5c78SDavid du Colombier c = *f[i]; 1245e4f5c78SDavid du Colombier if (isascii(c) && isupper(c)) 1255e4f5c78SDavid du Colombier c = tolower(c); 1265e4f5c78SDavid du Colombier switch(c){ 1275e4f5c78SDavid du Colombier case 'b': 1285e4f5c78SDavid du Colombier drain++; 1295e4f5c78SDavid du Colombier p->baud = n; 1305e4f5c78SDavid du Colombier set++; 1315e4f5c78SDavid du Colombier break; 1325e4f5c78SDavid du Colombier case 'c': 1335e4f5c78SDavid du Colombier p->dcd = n; 1345e4f5c78SDavid du Colombier // lines++; 1355e4f5c78SDavid du Colombier ++nop; 1365e4f5c78SDavid du Colombier break; 1375e4f5c78SDavid du Colombier case 'd': 1385e4f5c78SDavid du Colombier p->dtr = n; 1395e4f5c78SDavid du Colombier lines++; 1405e4f5c78SDavid du Colombier break; 1415e4f5c78SDavid du Colombier case 'e': 1425e4f5c78SDavid du Colombier p->dsr = n; 1435e4f5c78SDavid du Colombier // lines++; 1445e4f5c78SDavid du Colombier ++nop; 1455e4f5c78SDavid du Colombier break; 1465e4f5c78SDavid du Colombier case 'f': /* flush the pipes */ 1475e4f5c78SDavid du Colombier drain++; 1485e4f5c78SDavid du Colombier break; 1495e4f5c78SDavid du Colombier case 'h': /* hangup?? */ 1505e4f5c78SDavid du Colombier p->rts = p->dtr = 0; 1515e4f5c78SDavid du Colombier lines++; 1525e4f5c78SDavid du Colombier fprint(2, "serial: %c, unsure ctl\n", c); 1535e4f5c78SDavid du Colombier break; 1545e4f5c78SDavid du Colombier case 'i': 1555e4f5c78SDavid du Colombier ++nop; 1565e4f5c78SDavid du Colombier break; 1575e4f5c78SDavid du Colombier case 'k': 1585e4f5c78SDavid du Colombier drain++; 1595e4f5c78SDavid du Colombier p->setbreak(p, 1); 1605e4f5c78SDavid du Colombier sleep(n); 1615e4f5c78SDavid du Colombier p->setbreak(p, 0); 1625e4f5c78SDavid du Colombier break; 1635e4f5c78SDavid du Colombier case 'l': 1645e4f5c78SDavid du Colombier drain++; 1655e4f5c78SDavid du Colombier p->bits = n; 1665e4f5c78SDavid du Colombier set++; 1675e4f5c78SDavid du Colombier break; 1685e4f5c78SDavid du Colombier case 'm': 1695e4f5c78SDavid du Colombier drain++; 17080e9508eSDavid du Colombier if(p->modemctl != nil) 1715e4f5c78SDavid du Colombier p->modemctl(p, n); 1725e4f5c78SDavid du Colombier if(n == 0) 1735e4f5c78SDavid du Colombier p->cts = 0; 1745e4f5c78SDavid du Colombier break; 1755e4f5c78SDavid du Colombier case 'n': 1765e4f5c78SDavid du Colombier p->blocked = n; 1775e4f5c78SDavid du Colombier ++nop; 1785e4f5c78SDavid du Colombier break; 1795e4f5c78SDavid du Colombier case 'p': /* extended... */ 1805e4f5c78SDavid du Colombier if(strlen(f[i]) != 2) 1815e4f5c78SDavid du Colombier return -1; 1825e4f5c78SDavid du Colombier drain++; 18380e9508eSDavid du Colombier par = f[i][1]; 1845e4f5c78SDavid du Colombier if(par == 'n') 1855e4f5c78SDavid du Colombier p->parity = 0; 1865e4f5c78SDavid du Colombier else if(par == 'o') 1875e4f5c78SDavid du Colombier p->parity = 1; 1885e4f5c78SDavid du Colombier else if(par == 'e') 1895e4f5c78SDavid du Colombier p->parity = 2; 1905e4f5c78SDavid du Colombier else if(par == 'm') /* mark parity */ 1915e4f5c78SDavid du Colombier p->parity = 3; 1925e4f5c78SDavid du Colombier else if(par == 's') /* space parity */ 1935e4f5c78SDavid du Colombier p->parity = 4; 1945e4f5c78SDavid du Colombier else 1955e4f5c78SDavid du Colombier return -1; 1965e4f5c78SDavid du Colombier set++; 1975e4f5c78SDavid du Colombier break; 1985e4f5c78SDavid du Colombier case 'q': 1995e4f5c78SDavid du Colombier // drain++; 2005e4f5c78SDavid du Colombier p->limit = n; 2015e4f5c78SDavid du Colombier ++nop; 2025e4f5c78SDavid du Colombier break; 2035e4f5c78SDavid du Colombier case 'r': 2045e4f5c78SDavid du Colombier drain++; 2055e4f5c78SDavid du Colombier p->rts = n; 2065e4f5c78SDavid du Colombier lines++; 2075e4f5c78SDavid du Colombier break; 2085e4f5c78SDavid du Colombier case 's': 2095e4f5c78SDavid du Colombier drain++; 2105e4f5c78SDavid du Colombier p->stop = n; 2115e4f5c78SDavid du Colombier set++; 2125e4f5c78SDavid du Colombier break; 2135e4f5c78SDavid du Colombier case 'w': 2145e4f5c78SDavid du Colombier /* ?? how do I put this */ 2155e4f5c78SDavid du Colombier p->timer = n * 100000LL; 2165e4f5c78SDavid du Colombier ++nop; 2175e4f5c78SDavid du Colombier break; 2185e4f5c78SDavid du Colombier case 'x': 2195e4f5c78SDavid du Colombier if(n == 0) 2205e4f5c78SDavid du Colombier x = CTLS; 2215e4f5c78SDavid du Colombier else 2225e4f5c78SDavid du Colombier x = CTLQ; 22380e9508eSDavid du Colombier if(p->wait4write != nil) 22480e9508eSDavid du Colombier nw = p->wait4write(p, &x, 1); 22580e9508eSDavid du Colombier else 2265e4f5c78SDavid du Colombier nw = write(p->epout->dfd, &x, 1); 2275e4f5c78SDavid du Colombier if(nw != 1){ 2285e4f5c78SDavid du Colombier serialrecover(p, ""); 2295e4f5c78SDavid du Colombier return -1; 2305e4f5c78SDavid du Colombier } 2315e4f5c78SDavid du Colombier break; 2325e4f5c78SDavid du Colombier } 233*ecc2a7c8SDavid du Colombier /* 234*ecc2a7c8SDavid du Colombier * don't print. the condition is harmless and the print 235*ecc2a7c8SDavid du Colombier * splatters all over the display. 236*ecc2a7c8SDavid du Colombier */ 237*ecc2a7c8SDavid du Colombier USED(nop); 238*ecc2a7c8SDavid du Colombier if (0 && nop) 2395e4f5c78SDavid du Colombier fprint(2, "serial: %c, unsupported nop ctl\n", c); 2405e4f5c78SDavid du Colombier } 2415e4f5c78SDavid du Colombier if(drain) 2425e4f5c78SDavid du Colombier serialdrain(p); 24380e9508eSDavid du Colombier if(lines && !set){ 24480e9508eSDavid du Colombier if(p->sendlines != nil && p->sendlines(p) < 0) 2455e4f5c78SDavid du Colombier return -1; 24680e9508eSDavid du Colombier } else if(set){ 24780e9508eSDavid du Colombier if(p->setparam != nil && p->setparam(p) < 0) 24880e9508eSDavid du Colombier return -1; 24980e9508eSDavid du Colombier } 2505e4f5c78SDavid du Colombier return 0; 2515e4f5c78SDavid du Colombier } 2525e4f5c78SDavid du Colombier 2535e4f5c78SDavid du Colombier char *pformat = "noems"; 2545e4f5c78SDavid du Colombier 2555e4f5c78SDavid du Colombier char * 2564d52e0f0SDavid du Colombier serdumpst(Serial *ser, char *buf, int bufsz) 2575e4f5c78SDavid du Colombier { 2585e4f5c78SDavid du Colombier char *e, *s; 2595e4f5c78SDavid du Colombier 2605e4f5c78SDavid du Colombier e = buf + bufsz; 2615e4f5c78SDavid du Colombier s = seprint(buf, e, "b%d ", ser->baud); 2625e4f5c78SDavid du Colombier s = seprint(s, e, "c%d ", ser->dcd); /* unimplemented */ 2635e4f5c78SDavid du Colombier s = seprint(s, e, "d%d ", ser->dtr); 2645e4f5c78SDavid du Colombier s = seprint(s, e, "e%d ", ser->dsr); /* unimplemented */ 2655e4f5c78SDavid du Colombier s = seprint(s, e, "l%d ", ser->bits); 2665e4f5c78SDavid du Colombier s = seprint(s, e, "m%d ", ser->mctl); 2675e4f5c78SDavid du Colombier if(ser->parity >= 0 || ser->parity < strlen(pformat)) 2685e4f5c78SDavid du Colombier s = seprint(s, e, "p%c ", pformat[ser->parity]); 2695e4f5c78SDavid du Colombier else 2705e4f5c78SDavid du Colombier s = seprint(s, e, "p%c ", '?'); 2715e4f5c78SDavid du Colombier s = seprint(s, e, "r%d ", ser->rts); 2725e4f5c78SDavid du Colombier s = seprint(s, e, "s%d ", ser->stop); 2735e4f5c78SDavid du Colombier s = seprint(s, e, "i%d ", ser->fifo); 2745e4f5c78SDavid du Colombier s = seprint(s, e, "\ndev(%d) ", 0); 2755e4f5c78SDavid du Colombier s = seprint(s, e, "type(%d) ", ser->type); 2765e4f5c78SDavid du Colombier s = seprint(s, e, "framing(%d) ", ser->nframeerr); 2775e4f5c78SDavid du Colombier s = seprint(s, e, "overruns(%d) ", ser->novererr); 2785e4f5c78SDavid du Colombier s = seprint(s, e, "berr(%d) ", ser->nbreakerr); 2795e4f5c78SDavid du Colombier s = seprint(s, e, " serr(%d) ", ser->nparityerr); 2805e4f5c78SDavid du Colombier return s; 2815e4f5c78SDavid du Colombier } 2825e4f5c78SDavid du Colombier 2835e4f5c78SDavid du Colombier static int 2845e4f5c78SDavid du Colombier serinit(Serial *ser) 2855e4f5c78SDavid du Colombier { 2865e4f5c78SDavid du Colombier int res; 2875e4f5c78SDavid du Colombier 28880e9508eSDavid du Colombier res = 0; 28980e9508eSDavid du Colombier if(ser->init != nil) 2905e4f5c78SDavid du Colombier res = ser->init(ser); 29180e9508eSDavid du Colombier if(ser->getparam != nil) 29280e9508eSDavid du Colombier ser->getparam(ser); 2935e4f5c78SDavid du Colombier ser->nframeerr = ser->nparityerr = ser->nbreakerr = ser->novererr = 0; 2945e4f5c78SDavid du Colombier return res; 2955e4f5c78SDavid du Colombier } 2965e4f5c78SDavid du Colombier 2975e4f5c78SDavid du Colombier static int 2985e4f5c78SDavid du Colombier dwalk(Usbfs *fs, Fid *fid, char *name) 2995e4f5c78SDavid du Colombier { 3005e4f5c78SDavid du Colombier int i; 3015e4f5c78SDavid du Colombier char *dname; 3025e4f5c78SDavid du Colombier Qid qid; 3035e4f5c78SDavid du Colombier Serial *ser; 3045e4f5c78SDavid du Colombier 3055e4f5c78SDavid du Colombier qid = fid->qid; 3065e4f5c78SDavid du Colombier if((qid.type & QTDIR) == 0){ 3075e4f5c78SDavid du Colombier werrstr("walk in non-directory"); 3085e4f5c78SDavid du Colombier return -1; 3095e4f5c78SDavid du Colombier } 3105e4f5c78SDavid du Colombier 3115e4f5c78SDavid du Colombier if(strcmp(name, "..") == 0){ 3125e4f5c78SDavid du Colombier /* must be /eiaU%d; i.e. our root dir. */ 3135e4f5c78SDavid du Colombier fid->qid.path = Qroot | fs->qid; 3145e4f5c78SDavid du Colombier fid->qid.vers = 0; 3155e4f5c78SDavid du Colombier fid->qid.type = QTDIR; 3165e4f5c78SDavid du Colombier return 0; 3175e4f5c78SDavid du Colombier } 3185e4f5c78SDavid du Colombier 3195e4f5c78SDavid du Colombier ser = fs->aux; 3205e4f5c78SDavid du Colombier for(i = 1; i < nelem(dirtab); i++){ 3215e4f5c78SDavid du Colombier dname = smprint(dirtab[i].name, ser->fs.name); 3225e4f5c78SDavid du Colombier if(strcmp(name, dname) == 0){ 3235e4f5c78SDavid du Colombier qid.path = i | fs->qid; 3245e4f5c78SDavid du Colombier qid.vers = 0; 3255e4f5c78SDavid du Colombier qid.type = dirtab[i].mode >> 24; 3265e4f5c78SDavid du Colombier fid->qid = qid; 3275e4f5c78SDavid du Colombier free(dname); 3285e4f5c78SDavid du Colombier return 0; 3295e4f5c78SDavid du Colombier } else 3305e4f5c78SDavid du Colombier free(dname); 3315e4f5c78SDavid du Colombier } 3325e4f5c78SDavid du Colombier werrstr(Enotfound); 3335e4f5c78SDavid du Colombier return -1; 3345e4f5c78SDavid du Colombier } 3355e4f5c78SDavid du Colombier 3365e4f5c78SDavid du Colombier static void 3375e4f5c78SDavid du Colombier dostat(Usbfs *fs, int path, Dir *d) 3385e4f5c78SDavid du Colombier { 3395e4f5c78SDavid du Colombier Dirtab *t; 3405e4f5c78SDavid du Colombier Serial *ser; 3415e4f5c78SDavid du Colombier 3425e4f5c78SDavid du Colombier t = &dirtab[path]; 3435e4f5c78SDavid du Colombier d->qid.path = path; 3445e4f5c78SDavid du Colombier d->qid.type = t->mode >> 24; 3455e4f5c78SDavid du Colombier d->mode = t->mode; 3465e4f5c78SDavid du Colombier ser = fs->aux; 3475e4f5c78SDavid du Colombier 3485e4f5c78SDavid du Colombier if(strcmp(t->name, "/") == 0) 3495e4f5c78SDavid du Colombier d->name = t->name; 3505e4f5c78SDavid du Colombier else 3515e4f5c78SDavid du Colombier snprint(d->name, Namesz, t->name, ser->fs.name); 3525e4f5c78SDavid du Colombier d->length = 0; 3535e4f5c78SDavid du Colombier } 3545e4f5c78SDavid du Colombier 3555e4f5c78SDavid du Colombier static int 3565e4f5c78SDavid du Colombier dstat(Usbfs *fs, Qid qid, Dir *d) 3575e4f5c78SDavid du Colombier { 3585e4f5c78SDavid du Colombier int path; 3595e4f5c78SDavid du Colombier 3605e4f5c78SDavid du Colombier path = qid.path & ~fs->qid; 3615e4f5c78SDavid du Colombier dostat(fs, path, d); 3625e4f5c78SDavid du Colombier d->qid.path |= fs->qid; 3635e4f5c78SDavid du Colombier return 0; 3645e4f5c78SDavid du Colombier } 3655e4f5c78SDavid du Colombier 3665e4f5c78SDavid du Colombier static int 3675e4f5c78SDavid du Colombier dopen(Usbfs *fs, Fid *fid, int) 3685e4f5c78SDavid du Colombier { 3695e4f5c78SDavid du Colombier ulong path; 3705e4f5c78SDavid du Colombier // Serial *ser; 3715e4f5c78SDavid du Colombier 3725e4f5c78SDavid du Colombier path = fid->qid.path & ~fs->qid; 3735e4f5c78SDavid du Colombier // ser = fs->aux; 3745e4f5c78SDavid du Colombier switch(path){ /* BUG: unneeded? */ 3755e4f5c78SDavid du Colombier case Qdata: 3764d52e0f0SDavid du Colombier dsprint(2, "serial, opened data\n"); 3775e4f5c78SDavid du Colombier break; 3785e4f5c78SDavid du Colombier case Qctl: 3794d52e0f0SDavid du Colombier dsprint(2, "serial, opened ctl\n"); 3805e4f5c78SDavid du Colombier break; 3815e4f5c78SDavid du Colombier } 3825e4f5c78SDavid du Colombier return 0; 3835e4f5c78SDavid du Colombier } 3845e4f5c78SDavid du Colombier 3855e4f5c78SDavid du Colombier 3865e4f5c78SDavid du Colombier static void 3875e4f5c78SDavid du Colombier filldir(Usbfs *fs, Dir *d, Dirtab *tab, int i) 3885e4f5c78SDavid du Colombier { 3895e4f5c78SDavid du Colombier d->qid.path = i | fs->qid; 3905e4f5c78SDavid du Colombier d->mode = tab->mode; 3915e4f5c78SDavid du Colombier if((d->mode & DMDIR) != 0) 3925e4f5c78SDavid du Colombier d->qid.type = QTDIR; 3935e4f5c78SDavid du Colombier else 3945e4f5c78SDavid du Colombier d->qid.type = QTFILE; 3955e4f5c78SDavid du Colombier d->name = tab->name; 3965e4f5c78SDavid du Colombier } 3975e4f5c78SDavid du Colombier 3985e4f5c78SDavid du Colombier static int 3995e4f5c78SDavid du Colombier dirgen(Usbfs *fs, Qid, int i, Dir *d, void *) 4005e4f5c78SDavid du Colombier { 4015e4f5c78SDavid du Colombier i++; /* skip root */ 40280e9508eSDavid du Colombier if(i >= nelem(dirtab)) 4035e4f5c78SDavid du Colombier return -1; 40480e9508eSDavid du Colombier filldir(fs, d, &dirtab[i], i); 4055e4f5c78SDavid du Colombier return 0; 4065e4f5c78SDavid du Colombier } 4075e4f5c78SDavid du Colombier 408bfb6eab9SDavid du Colombier enum { 409bfb6eab9SDavid du Colombier Serbufsize = 255, 410bfb6eab9SDavid du Colombier }; 411bfb6eab9SDavid du Colombier 4125e4f5c78SDavid du Colombier static long 4135e4f5c78SDavid du Colombier dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset) 4145e4f5c78SDavid du Colombier { 4155e4f5c78SDavid du Colombier int dfd; 4165e4f5c78SDavid du Colombier long rcount; 4175e4f5c78SDavid du Colombier ulong path; 41880e9508eSDavid du Colombier char *e, *buf, *err; /* change */ 4195e4f5c78SDavid du Colombier Qid q; 4205e4f5c78SDavid du Colombier Serial *ser; 421*ecc2a7c8SDavid du Colombier static int errrun, good; 4225e4f5c78SDavid du Colombier 4235e4f5c78SDavid du Colombier q = fid->qid; 4245e4f5c78SDavid du Colombier path = fid->qid.path & ~fs->qid; 4255e4f5c78SDavid du Colombier ser = fs->aux; 4265e4f5c78SDavid du Colombier 427bfb6eab9SDavid du Colombier buf = emallocz(Serbufsize, 1); 428bfb6eab9SDavid du Colombier err = emallocz(Serbufsize, 1); 4295e4f5c78SDavid du Colombier qlock(ser); 4305e4f5c78SDavid du Colombier switch(path){ 4315e4f5c78SDavid du Colombier case Qroot: 4325e4f5c78SDavid du Colombier count = usbdirread(fs, q, data, count, offset, dirgen, nil); 4335e4f5c78SDavid du Colombier break; 4345e4f5c78SDavid du Colombier case Qdata: 43580e9508eSDavid du Colombier if(count > ser->maxread) 43680e9508eSDavid du Colombier count = ser->maxread; 43780e9508eSDavid du Colombier dsprint(2, "serial: reading from data\n"); 4385e4f5c78SDavid du Colombier do { 4394d52e0f0SDavid du Colombier err[0] = 0; 44080e9508eSDavid du Colombier dfd = ser->epin->dfd; 44180e9508eSDavid du Colombier if(usbdebug >= 3) 44280e9508eSDavid du Colombier dsprint(2, "serial: reading: %ld\n", count); 44380e9508eSDavid du Colombier 444*ecc2a7c8SDavid du Colombier assert(count > 0); 44580e9508eSDavid du Colombier if(ser->wait4data != nil) 44680e9508eSDavid du Colombier rcount = ser->wait4data(ser, data, count); 44780e9508eSDavid du Colombier else{ 44880e9508eSDavid du Colombier qunlock(ser); 4495e4f5c78SDavid du Colombier rcount = read(dfd, data, count); 4505e4f5c78SDavid du Colombier qlock(ser); 45180e9508eSDavid du Colombier } 45225fc6993SDavid du Colombier /* 45325fc6993SDavid du Colombier * if we encounter a long run of continuous read 45425fc6993SDavid du Colombier * errors, do something drastic so that our caller 45525fc6993SDavid du Colombier * doesn't just spin its wheels forever. 45625fc6993SDavid du Colombier */ 45725fc6993SDavid du Colombier if(rcount < 0) { 458bfb6eab9SDavid du Colombier snprint(err, Serbufsize, "%r"); 459*ecc2a7c8SDavid du Colombier ++errrun; 460*ecc2a7c8SDavid du Colombier sleep(20); 461*ecc2a7c8SDavid du Colombier if (good > 0 && errrun > 10000) { 46225fc6993SDavid du Colombier /* the line has been dropped; give up */ 46325fc6993SDavid du Colombier qunlock(ser); 464*ecc2a7c8SDavid du Colombier fprint(2, "%s: line %s is gone: %r\n", 465*ecc2a7c8SDavid du Colombier argv0, ser->fs.name); 46625fc6993SDavid du Colombier threadexitsall("serial line gone"); 46725fc6993SDavid du Colombier } 468*ecc2a7c8SDavid du Colombier } else { 46925fc6993SDavid du Colombier errrun = 0; 470*ecc2a7c8SDavid du Colombier good++; 471*ecc2a7c8SDavid du Colombier } 47280e9508eSDavid du Colombier if(usbdebug >= 3) 47380e9508eSDavid du Colombier dsprint(2, "serial: read: %s %ld\n", err, rcount); 4745e4f5c78SDavid du Colombier } while(rcount < 0 && strstr(err, "timed out") != nil); 4755e4f5c78SDavid du Colombier 47680e9508eSDavid du Colombier dsprint(2, "serial: read from bulk %ld, %10.10s\n", rcount, err); 4775e4f5c78SDavid du Colombier if(rcount < 0){ 4785e4f5c78SDavid du Colombier dsprint(2, "serial: need to recover, data read %ld %r\n", 4795e4f5c78SDavid du Colombier count); 4805e4f5c78SDavid du Colombier serialrecover(ser, err); 4815e4f5c78SDavid du Colombier } 48280e9508eSDavid du Colombier dsprint(2, "serial: read from bulk %ld\n", rcount); 4835e4f5c78SDavid du Colombier count = rcount; 4845e4f5c78SDavid du Colombier break; 4855e4f5c78SDavid du Colombier case Qctl: 48680e9508eSDavid du Colombier if(offset != 0) 4875e4f5c78SDavid du Colombier count = 0; 48880e9508eSDavid du Colombier else { 489bfb6eab9SDavid du Colombier e = serdumpst(ser, buf, Serbufsize); 4905e4f5c78SDavid du Colombier count = usbreadbuf(data, count, 0, buf, e - buf); 49180e9508eSDavid du Colombier } 4925e4f5c78SDavid du Colombier break; 4935e4f5c78SDavid du Colombier } 4945e4f5c78SDavid du Colombier qunlock(ser); 4955e4f5c78SDavid du Colombier free(err); 4965e4f5c78SDavid du Colombier free(buf); 4975e4f5c78SDavid du Colombier return count; 4985e4f5c78SDavid du Colombier } 4995e4f5c78SDavid du Colombier 5005e4f5c78SDavid du Colombier static long 50180e9508eSDavid du Colombier altwrite(Serial *ser, uchar *buf, long count) 50280e9508eSDavid du Colombier { 50380e9508eSDavid du Colombier int nw, dfd; 50480e9508eSDavid du Colombier char err[128]; 50580e9508eSDavid du Colombier 50680e9508eSDavid du Colombier do{ 50780e9508eSDavid du Colombier if(ser->wait4write != nil) 50880e9508eSDavid du Colombier /* unlocked inside later */ 50980e9508eSDavid du Colombier nw = ser->wait4write(ser, buf, count); 51080e9508eSDavid du Colombier else{ 51180e9508eSDavid du Colombier dfd = ser->epout->dfd; 51280e9508eSDavid du Colombier qunlock(ser); 51380e9508eSDavid du Colombier nw = write(dfd, buf, count); 51480e9508eSDavid du Colombier qlock(ser); 51580e9508eSDavid du Colombier } 51680e9508eSDavid du Colombier rerrstr(err, sizeof err); 51780e9508eSDavid du Colombier } while(nw < 0 && strstr(err, "timed out") != nil); 51880e9508eSDavid du Colombier 51980e9508eSDavid du Colombier if(nw != count){ 52080e9508eSDavid du Colombier dsprint(2, "serial: need to recover, status in write %d %r\n", 52180e9508eSDavid du Colombier nw); 52280e9508eSDavid du Colombier snprint(err, sizeof err, "%r"); 52380e9508eSDavid du Colombier serialrecover(ser, err); 52480e9508eSDavid du Colombier } 52580e9508eSDavid du Colombier return nw; 52680e9508eSDavid du Colombier } 52780e9508eSDavid du Colombier 52880e9508eSDavid du Colombier static long 5295e4f5c78SDavid du Colombier dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong) 5305e4f5c78SDavid du Colombier { 5315e4f5c78SDavid du Colombier ulong path; 5325e4f5c78SDavid du Colombier char *cmd; 5335e4f5c78SDavid du Colombier Serial *ser; 5345e4f5c78SDavid du Colombier 5355e4f5c78SDavid du Colombier ser = fs->aux; 5365e4f5c78SDavid du Colombier path = fid->qid.path & ~fs->qid; 5375e4f5c78SDavid du Colombier 5385e4f5c78SDavid du Colombier qlock(ser); 5395e4f5c78SDavid du Colombier switch(path){ 5405e4f5c78SDavid du Colombier case Qdata: 54180e9508eSDavid du Colombier count = altwrite(ser, (uchar *)buf, count); 5425e4f5c78SDavid du Colombier break; 5435e4f5c78SDavid du Colombier case Qctl: 5445e4f5c78SDavid du Colombier cmd = emallocz(count+1, 1); 5455e4f5c78SDavid du Colombier memmove(cmd, buf, count); 5465e4f5c78SDavid du Colombier cmd[count] = 0; 5475e4f5c78SDavid du Colombier if(serialctl(ser, cmd) < 0){ 5485e4f5c78SDavid du Colombier qunlock(ser); 5495e4f5c78SDavid du Colombier werrstr(Ebadctl); 5505e4f5c78SDavid du Colombier free(cmd); 5515e4f5c78SDavid du Colombier return -1; 5525e4f5c78SDavid du Colombier } 5535e4f5c78SDavid du Colombier free(cmd); 5545e4f5c78SDavid du Colombier break; 5555e4f5c78SDavid du Colombier default: 5565e4f5c78SDavid du Colombier qunlock(ser); 5575e4f5c78SDavid du Colombier werrstr(Eperm); 5585e4f5c78SDavid du Colombier return -1; 5595e4f5c78SDavid du Colombier } 5605e4f5c78SDavid du Colombier qunlock(ser); 5615e4f5c78SDavid du Colombier return count; 5625e4f5c78SDavid du Colombier } 5635e4f5c78SDavid du Colombier 5645e4f5c78SDavid du Colombier static int 5655e4f5c78SDavid du Colombier openeps(Serial *ser, int epin, int epout, int epintr) 5665e4f5c78SDavid du Colombier { 5675e4f5c78SDavid du Colombier ser->epin = openep(ser->dev, epin); 5685e4f5c78SDavid du Colombier if(ser->epin == nil){ 5695e4f5c78SDavid du Colombier fprint(2, "serial: openep %d: %r\n", epin); 5705e4f5c78SDavid du Colombier return -1; 5715e4f5c78SDavid du Colombier } 5725e4f5c78SDavid du Colombier ser->epout = openep(ser->dev, epout); 5735e4f5c78SDavid du Colombier if(ser->epout == nil){ 5745e4f5c78SDavid du Colombier fprint(2, "serial: openep %d: %r\n", epout); 5755e4f5c78SDavid du Colombier closedev(ser->epin); 5765e4f5c78SDavid du Colombier return -1; 5775e4f5c78SDavid du Colombier } 5784d52e0f0SDavid du Colombier 57980e9508eSDavid du Colombier devctl(ser->epin, "timeout 1000"); 58080e9508eSDavid du Colombier devctl(ser->epout, "timeout 1000"); 5814d52e0f0SDavid du Colombier 5824d52e0f0SDavid du Colombier if(ser->hasepintr){ 5835e4f5c78SDavid du Colombier ser->epintr = openep(ser->dev, epintr); 5845e4f5c78SDavid du Colombier if(ser->epintr == nil){ 5855e4f5c78SDavid du Colombier fprint(2, "serial: openep %d: %r\n", epintr); 5865e4f5c78SDavid du Colombier closedev(ser->epin); 5875e4f5c78SDavid du Colombier closedev(ser->epout); 5885e4f5c78SDavid du Colombier return -1; 5895e4f5c78SDavid du Colombier } 5904d52e0f0SDavid du Colombier opendevdata(ser->epintr, OREAD); 59180e9508eSDavid du Colombier devctl(ser->epintr, "timeout 1000"); 5924d52e0f0SDavid du Colombier } 5935e4f5c78SDavid du Colombier 59480e9508eSDavid du Colombier if(ser->seteps!= nil) 59580e9508eSDavid du Colombier ser->seteps(ser); 5965e4f5c78SDavid du Colombier opendevdata(ser->epin, OREAD); 5975e4f5c78SDavid du Colombier opendevdata(ser->epout, OWRITE); 59880e9508eSDavid du Colombier if(ser->epin->dfd < 0 || ser->epout->dfd < 0 || 59980e9508eSDavid du Colombier (ser->hasepintr && ser->epintr->dfd < 0)){ 6005e4f5c78SDavid du Colombier fprint(2, "serial: open i/o ep data: %r\n"); 6015e4f5c78SDavid du Colombier closedev(ser->epin); 6025e4f5c78SDavid du Colombier closedev(ser->epout); 6034d52e0f0SDavid du Colombier if(ser->hasepintr) 6045e4f5c78SDavid du Colombier closedev(ser->epintr); 6055e4f5c78SDavid du Colombier return -1; 6065e4f5c78SDavid du Colombier } 6075e4f5c78SDavid du Colombier return 0; 6085e4f5c78SDavid du Colombier } 6095e4f5c78SDavid du Colombier 6105e4f5c78SDavid du Colombier static int 6115e4f5c78SDavid du Colombier findendpoints(Serial *ser) 6125e4f5c78SDavid du Colombier { 6135e4f5c78SDavid du Colombier int i, epin, epout, epintr; 61480e9508eSDavid du Colombier Ep *ep, **eps; 6155e4f5c78SDavid du Colombier Usbdev *ud; 6165e4f5c78SDavid du Colombier 6175e4f5c78SDavid du Colombier epintr = epin = epout = -1; 6185e4f5c78SDavid du Colombier ud = ser->dev->usb; 61980e9508eSDavid du Colombier 62080e9508eSDavid du Colombier /* 62180e9508eSDavid du Colombier * interfc 0 means start from the start which is equiv to 62280e9508eSDavid du Colombier * iterate through endpoints probably, could be done better 62380e9508eSDavid du Colombier */ 62480e9508eSDavid du Colombier if(ser->interfc == 0) 62580e9508eSDavid du Colombier eps = ud->ep; 62680e9508eSDavid du Colombier else 62780e9508eSDavid du Colombier eps = ser->dev->usb->conf[0]->iface[ser->interfc]->ep; 62880e9508eSDavid du Colombier 62980e9508eSDavid du Colombier for(i = 0; i < Niface; i++){ 63080e9508eSDavid du Colombier if((ep = eps[i]) == nil) 6315e4f5c78SDavid du Colombier continue; 63280e9508eSDavid du Colombier if(ser->hasepintr && ep->type == Eintr && 63380e9508eSDavid du Colombier ep->dir == Ein && epintr == -1) 6345e4f5c78SDavid du Colombier epintr = ep->id; 6355e4f5c78SDavid du Colombier if(ep->type == Ebulk){ 6365e4f5c78SDavid du Colombier if(ep->dir == Ein && epin == -1) 6375e4f5c78SDavid du Colombier epin = ep->id; 6385e4f5c78SDavid du Colombier if(ep->dir == Eout && epout == -1) 6395e4f5c78SDavid du Colombier epout = ep->id; 6405e4f5c78SDavid du Colombier } 6415e4f5c78SDavid du Colombier } 6425e4f5c78SDavid du Colombier dprint(2, "serial: ep ids: in %d out %d intr %d\n", epin, epout, epintr); 6434d52e0f0SDavid du Colombier if(epin == -1 || epout == -1 || (ser->hasepintr && epintr == -1)) 6445e4f5c78SDavid du Colombier return -1; 6454d52e0f0SDavid du Colombier 6465e4f5c78SDavid du Colombier if(openeps(ser, epin, epout, epintr) < 0) 6475e4f5c78SDavid du Colombier return -1; 6485e4f5c78SDavid du Colombier 64980e9508eSDavid du Colombier dprint(2, "serial: ep in %s out %s\n", ser->epin->dir, ser->epout->dir); 6504d52e0f0SDavid du Colombier if(ser->hasepintr) 65180e9508eSDavid du Colombier dprint(2, "serial: ep intr %s\n", ser->epintr->dir); 6525e4f5c78SDavid du Colombier 6535e4f5c78SDavid du Colombier if(usbdebug > 1 || serialdebug > 2){ 6545e4f5c78SDavid du Colombier devctl(ser->epin, "debug 1"); 6555e4f5c78SDavid du Colombier devctl(ser->epout, "debug 1"); 6564d52e0f0SDavid du Colombier if(ser->hasepintr) 6575e4f5c78SDavid du Colombier devctl(ser->epintr, "debug 1"); 6585e4f5c78SDavid du Colombier devctl(ser->dev, "debug 1"); 6595e4f5c78SDavid du Colombier } 6605e4f5c78SDavid du Colombier return 0; 6615e4f5c78SDavid du Colombier } 6625e4f5c78SDavid du Colombier 66380e9508eSDavid du Colombier /* keep in sync with main.c */ 6645e4f5c78SDavid du Colombier static int 6655e4f5c78SDavid du Colombier usage(void) 6665e4f5c78SDavid du Colombier { 66780e9508eSDavid du Colombier werrstr("usage: usb/serial [-dD] [-m mtpt] [-s srv]"); 6685e4f5c78SDavid du Colombier return -1; 6695e4f5c78SDavid du Colombier } 6705e4f5c78SDavid du Colombier 6715e4f5c78SDavid du Colombier static void 6725e4f5c78SDavid du Colombier serdevfree(void *a) 6735e4f5c78SDavid du Colombier { 6745e4f5c78SDavid du Colombier Serial *ser = a; 6755e4f5c78SDavid du Colombier 6765e4f5c78SDavid du Colombier if(ser == nil) 6775e4f5c78SDavid du Colombier return; 6784d52e0f0SDavid du Colombier if(ser->hasepintr) 6795e4f5c78SDavid du Colombier closedev(ser->epintr); 6805e4f5c78SDavid du Colombier closedev(ser->epin); 6815e4f5c78SDavid du Colombier closedev(ser->epout); 6825e4f5c78SDavid du Colombier ser->epintr = ser->epin = ser->epout = nil; 68380e9508eSDavid du Colombier chanfree(ser->w4data); 68480e9508eSDavid du Colombier chanfree(ser->gotdata); 68580e9508eSDavid du Colombier chanfree(ser->w4empty); 6865e4f5c78SDavid du Colombier free(ser); 6875e4f5c78SDavid du Colombier } 6885e4f5c78SDavid du Colombier 6895e4f5c78SDavid du Colombier static Usbfs serialfs = { 6905e4f5c78SDavid du Colombier .walk = dwalk, 6915e4f5c78SDavid du Colombier .open = dopen, 6925e4f5c78SDavid du Colombier .read = dread, 6935e4f5c78SDavid du Colombier .write= dwrite, 6945e4f5c78SDavid du Colombier .stat = dstat, 6955e4f5c78SDavid du Colombier }; 6965e4f5c78SDavid du Colombier 6975e4f5c78SDavid du Colombier int 6985e4f5c78SDavid du Colombier serialmain(Dev *dev, int argc, char* argv[]) 6995e4f5c78SDavid du Colombier { 7005e4f5c78SDavid du Colombier Serial *ser; 7014d52e0f0SDavid du Colombier char buf[50]; 7025e4f5c78SDavid du Colombier 7035e4f5c78SDavid du Colombier ARGBEGIN{ 7045e4f5c78SDavid du Colombier case 'd': 7055e4f5c78SDavid du Colombier serialdebug++; 7065e4f5c78SDavid du Colombier break; 7075e4f5c78SDavid du Colombier default: 7085e4f5c78SDavid du Colombier return usage(); 7095e4f5c78SDavid du Colombier }ARGEND 7105e4f5c78SDavid du Colombier if(argc != 0) 7115e4f5c78SDavid du Colombier return usage(); 7125e4f5c78SDavid du Colombier 7135e4f5c78SDavid du Colombier ser = dev->aux = emallocz(sizeof(Serial), 1); 714bfb6eab9SDavid du Colombier /* BUG: could this go wrong? channel leaks? */ 71580e9508eSDavid du Colombier ser->w4data = chancreate(sizeof(ulong), 0); 71680e9508eSDavid du Colombier ser->gotdata = chancreate(sizeof(ulong), 0); 71780e9508eSDavid du Colombier ser->w4empty = chancreate(sizeof(ulong), 0); 718bfb6eab9SDavid du Colombier ser->maxread = ser->maxwrite = sizeof ser->data; 7195e4f5c78SDavid du Colombier ser->dev = dev; 7205e4f5c78SDavid du Colombier dev->free = serdevfree; 7214d52e0f0SDavid du Colombier 7224d52e0f0SDavid du Colombier snprint(buf, sizeof buf, "vid %#06x did %#06x", 7234d52e0f0SDavid du Colombier dev->usb->vid, dev->usb->did); 7244d52e0f0SDavid du Colombier ser->fs = serialfs; 7254d52e0f0SDavid du Colombier if(plmatch(buf) == 0){ 7264d52e0f0SDavid du Colombier ser->hasepintr = 1; 7274d52e0f0SDavid du Colombier ser->Serialops = plops; 72880e9508eSDavid du Colombier } else if(uconsmatch(buf) == 0) 7294d52e0f0SDavid du Colombier ser->Serialops = uconsops; 73080e9508eSDavid du Colombier else if(ftmatch(ser, buf) == 0) 73180e9508eSDavid du Colombier ser->Serialops = ftops; 73280e9508eSDavid du Colombier else { 73380e9508eSDavid du Colombier werrstr("serial: no serial devices found"); 734bfb6eab9SDavid du Colombier closedev(dev); 7355e4f5c78SDavid du Colombier return -1; 7365e4f5c78SDavid du Colombier } 7375e4f5c78SDavid du Colombier 73880e9508eSDavid du Colombier if(findendpoints(ser) < 0){ 73980e9508eSDavid du Colombier werrstr("serial: no endpoints found"); 740bfb6eab9SDavid du Colombier closedev(dev); 74180e9508eSDavid du Colombier return -1; 74280e9508eSDavid du Colombier } 7435e4f5c78SDavid du Colombier if(serinit(ser) < 0){ 7445e4f5c78SDavid du Colombier dprint(2, "serial: serinit: %r\n"); 745bfb6eab9SDavid du Colombier closedev(dev); 7465e4f5c78SDavid du Colombier return -1; 7475e4f5c78SDavid du Colombier } 7485e4f5c78SDavid du Colombier 74980e9508eSDavid du Colombier snprint(ser->fs.name, sizeof ser->fs.name, "eiaU%d", dev->id); 75080e9508eSDavid du Colombier fprint(2, "%s\n", ser->fs.name); 7515e4f5c78SDavid du Colombier ser->fs.dev = dev; 7525e4f5c78SDavid du Colombier incref(dev); 7535e4f5c78SDavid du Colombier ser->fs.aux = ser; 7545e4f5c78SDavid du Colombier usbfsadd(&ser->fs); 7555e4f5c78SDavid du Colombier 7565e4f5c78SDavid du Colombier closedev(dev); 7575e4f5c78SDavid du Colombier return 0; 7585e4f5c78SDavid du Colombier } 759