usb/ 775 0 0 0 11255750624 74645ustar00nemosysusb/audio/ 775 0 0 0 11255750546 105705ustar00nemosysusb/audio/audio.c 664 0 0 23301 11177300144 12040ustar00nemosys/* * USB audio driver for Plan 9 * This needs a full rewrite. * As it is, it does not check for all errors, * mixes the audio data structures with the usb configuration, * may cross nil pointers, and is hard to debug and fix. * Also, it does not issue a dettach request to the endpoint * after the device is unplugged. This means that the old * endpoint would still be around until manually reclaimed. */ #include #include #include #include "usb.h" #include "audio.h" #include "audioctl.h" #define STACKSIZE 16*1024 extern char* srvpost; char * mntpt; Channel *controlchan; int verbose; int setrec = 0; int defaultspeed[2] = {44100, 44100}; Dev *buttondev; Dev *epdev[2]; static void audio_endpoint(Dev *, Desc *dd) { byte *b = (uchar*)&dd->data; int n = dd->data.bLength; char *hd; switch(b[2]){ case 0x01: if(usbdebug){ fprint(2, "CS_ENDPOINT for attributes 0x%x, lockdelayunits %d, lockdelay %#ux, ", b[3], b[4], b[5] | (b[6]<<8)); if(b[3] & has_setspeed) fprint(2, "has sampling-frequency control"); else fprint(2, "does not have sampling-frequency control"); if(b[3] & 0x1<<1) fprint(2, ", has pitch control"); else fprint(2, ", does not have pitch control"); if(b[3] & 0x1<<7) fprint(2, ", max packets only"); fprint(2, "\n"); } if(dd->conf == nil) sysfatal("conf == nil"); if(dd->iface == nil) sysfatal("iface == nil"); if(dd->altc == nil) sysfatal("alt == nil"); if(dd->altc->aux == nil) dd->altc->aux= mallocz(sizeof(Audioalt),1); ((Audioalt*)dd->altc->aux)->caps |= b[3]; break; case 0x02: if(usbdebug){ fprint(2, "CS_INTERFACE FORMAT_TYPE %d, channels %d, subframesize %d, resolution %d, freqtype %d, ", b[3], b[4], b[5], b[6], b[7]); fprint(2, "freq0 %d, freq1 %d\n", b[8] | (b[9]<<8) | (b[10]<<16), b[11] | (b[12]<<8) | (b[13]<<16)); } break; default: if(usbdebug){ hd = hexstr(b, n); fprint(2, "CS_INTERFACE: %s\n", hd); free(hd); } } } enum { None, Volumeset, Volumeget, Altset, Altget, Speedget, }; void controlproc(void *) { /* Proc that looks after /dev/usb/%d/ctl */ int i, nf; char *req, *args[8]; Audiocontrol *c; long value[8]; Channel *replchan; while(req = recvp(controlchan)){ int rec; nf = tokenize(req, args, nelem(args)); if(nf < 3) sysfatal("controlproc: not enough arguments"); replchan = (Channel*)strtol(args[0], nil, 0); if(strcmp(args[2], "playback") == 0) rec = Play; else if(strcmp(args[2], "record") == 0) rec = Record; else{ /* illegal request */ dprint(2, "%s must be record or playback", args[2]); if(replchan) chanprint(replchan, "%s must be record or playback", args[2]); free(req); continue; } c = nil; for(i = 0; i < Ncontrol; i++){ c = &controls[rec][i]; if(strcmp(args[1], c->name) == 0) break; } if(i == Ncontrol){ dprint(2, "Illegal control name: %s", args[1]); if(replchan) chanprint(replchan, "Illegal control name: %s", args[1]); }else if(!c->settable){ dprint(2, "%s %s is not settable", args[1], args[2]); if(replchan) chanprint(replchan, "%s %s is not settable", args[1], args[2]); }else if(nf < 4){ dprint(2, "insufficient arguments for %s %s", args[1], args[2]); if(replchan) chanprint(replchan, "insufficient arguments for %s %s", args[1], args[2]); }else if(ctlparse(args[3], c, value) < 0){ if(replchan) chanprint(replchan, "parse error in %s %s", args[1], args[2]); }else{ dprint(2, "controlproc: setcontrol %s %s %s\n", rec?"in":"out", args[1], args[3]); if(setcontrol(rec, args[1], value) < 0){ if(replchan) chanprint(replchan, "setting %s %s failed", args[1], args[2]); }else{ if(replchan) chanprint(replchan, "ok"); } ctlevent(); } free(req); } } void buttonproc(void *) { int i, fd, b; char err[32]; byte buf[1]; Audiocontrol *c; fd = buttondev->dfd; c = &controls[Play][Volume_control]; for(;;){ if((b = read(fd, buf, 1)) < 0){ rerrstr(err, sizeof err); if(strcmp(err, "interrupted") == 0){ dprint(2, "read interrupted\n"); continue; } sysfatal("read %s/data: %r", buttondev->dir); } if(b == 0 || buf[0] == 0){ continue; }else if(buf[0] == 1){ if(c->chans == 0) c->value[0] += c->step; else for(i = 1; i < 8; i++) if(c->chans & 1 << i) c->value[i] += c->step; chanprint(controlchan, "0 volume playback %A", c); }else if(buf[0] == 2){ if(c->chans == 0) c->value[0] -= c->step; else for(i = 1; i < 8; i++) if(c->chans & 1 << i) c->value[i] -= c->step; chanprint(controlchan, "0 volume playback %A", c); }else if(usbdebug){ fprint(2, "button"); for(i = 0; i < b; i++) fprint(2, " %#2.2x", buf[i]); fprint(2, "\n"); } } } void usage(void) { fprint(2, "usage: usbaudio [-dpV] [-m mountpoint] [-s srvname] " "[-v volume] [dev]\n"); threadexitsall("usage"); } void threadmain(int argc, char **argv) { char *devdir; int i; long value[8], volume[8]; Audiocontrol *c; char *p; extern int attachok; Ep *ep; int csps[] = { Audiocsp, 0}; devdir = nil; volume[0] = Undef; for(i = 0; i<8; i++) value[i] = 0; fmtinstall('A', Aconv); fmtinstall('U', Ufmt); quotefmtinstall(); ARGBEGIN{ case 'd': usbdebug++; verbose++; break; case 'm': mntpt = EARGF(usage()); break; case 'p': attachok++; break; case 's': srvpost = EARGF(usage()); break; case 'v': volume[0] = strtol(EARGF(usage()), &p, 0); for(i = 1; i < 8; i++) volume[i] = volume[0]; break; case 'V': verbose++; break; default: usage(); }ARGEND switch(argc){ case 0: break; case 1: devdir = argv[0]; break; default: usage(); } if(devdir == nil) if(finddevs(matchdevcsp, csps, &devdir, 1) < 1){ fprint(2, "No usb audio\n"); threadexitsall("usbaudio not found"); } ad = opendev(devdir); if(ad == nil) sysfatal("opendev: %r"); if(configdev(ad) < 0) sysfatal("configdev: %r"); for(i = 0; i < nelem(ad->usb->ddesc); i++) if(ad->usb->ddesc[i] != nil) switch(ad->usb->ddesc[i]->data.bDescriptorType){ case AUDIO_INTERFACE: audio_interface(ad, ad->usb->ddesc[i]); break; case AUDIO_ENDPOINT: audio_endpoint(ad, ad->usb->ddesc[i]); break; } controlchan = chancreate(sizeof(char*), 8); for(i = 0; i < nelem(ad->usb->ep); i++) if((ep = ad->usb->ep[i]) != nil){ if(ep->iface->csp == CSP(Claudio, 2, 0) && ep->dir == Eout) endpt[0] = ep->id; if(ep->iface->csp == CSP(Claudio, 2, 0) && ep->dir == Ein) endpt[1] = ep->id; if(buttonendpt<0 && Class(ep->iface->csp) == Clhid) buttonendpt = ep->id; } if(endpt[0] != -1){ if(verbose) fprint(2, "usb/audio: playback on ep %d\n", endpt[0]); interface[0] = ad->usb->ep[endpt[0]]->iface->id; } if(endpt[1] != -1){ if(verbose) fprint(2, "usb/audio: record on ep %d\n", endpt[0]); interface[1] = ad->usb->ep[endpt[1]]->iface->id; } if(verbose && buttonendpt >= 0) fprint(2, "usb/audio: buttons on ep %d\n", buttonendpt); if(endpt[Play] >= 0){ if(verbose) fprint(2, "Setting default play parameters: %d Hz, %d channels at %d bits\n", defaultspeed[Play], 2, 16); if(findalt(Play, 2, 16, defaultspeed[Play]) < 0){ if(findalt(Play, 2, 16, 48000) < 0) sysfatal("Can't configure playout for %d or %d Hz", defaultspeed[Play], 48000); fprint(2, "Warning, can't configure playout for %d Hz, configuring for %d Hz instead\n", defaultspeed[Play], 48000); defaultspeed[Play] = 48000; } value[0] = 2; if(setcontrol(Play, "channels", value) == Undef) sysfatal("Can't set play channels"); value[0] = 16; if(setcontrol(Play, "resolution", value) == Undef) sysfatal("Can't set play resolution"); } if(endpt[Record] >= 0){ setrec = 1; if(verbose) fprint(2, "Setting default record parameters: " "%d Hz, %d channels at %d bits\n", defaultspeed[Record], 2, 16); i = 2; while(findalt(Record, i, 16, defaultspeed[Record]) < 0) if(i == 2 && controls[Record][Channel_control].max == 1){ fprint(2, "Warning, can't configure stereo " "recording, configuring mono instead\n"); i = 1; }else break; if(findalt(Record, i, 16, 48000) < 0){ endpt[Record] = -1; /* disable recording */ setrec = 0; fprint(2, "Warning, can't configure record for %d Hz or %d Hz\n", defaultspeed[Record], 48000); }else fprint(2, "Warning, can't configure record for %d Hz, " "configuring for %d Hz instead\n", defaultspeed[Record], 48000); defaultspeed[Record] = 48000; if(setrec){ value[0] = i; if(setcontrol(Record, "channels", value) == Undef) sysfatal("Can't set record channels"); value[0] = 16; if(setcontrol(Record, "resolution", value) == Undef) sysfatal("Can't set record resolution"); } } getcontrols(); /* Get the initial value of all controls */ value[0] = defaultspeed[Play]; if(endpt[Play] >= 0 && setcontrol(Play, "speed", value) < 0) sysfatal("can't set play speed"); value[0] = defaultspeed[Record]; if(endpt[Record] >= 0 && setcontrol(Record, "speed", value) < 0) fprint(2, "%s: can't set record speed\n", argv0); value[0] = 0; setcontrol(Play, "mute", value); if(volume[0] != Undef){ c = &controls[Play][Volume_control]; if(*p == '%' && c->min != Undef) for(i = 0; i < 8; i++) volume[i] = (volume[i]*c->max + (100-volume[i])*c->min)/100; if(c->settable) setcontrol(Play, "volume", volume); c = &controls[Record][Volume_control]; if(c->settable && setrec) setcontrol(Record, "volume", volume); } if(buttonendpt > 0){ buttondev = openep(ad, buttonendpt); if(buttondev == nil) sysfatal("openep: buttons: %r"); if(opendevdata(buttondev, OREAD) < 0) sysfatal("open buttons fd: %r"); proccreate(buttonproc, nil, STACKSIZE); } proccreate(controlproc, nil, STACKSIZE); proccreate(serve, nil, STACKSIZE); threadexits(nil); } ustar00nemosysusb/audio/audio.h 664 0 0 4002 11152121043 12012ustar00nemosysenum { master_chan = 0x00, Speed_control = 0x00, /* Items below are defined by USB standard: */ Mute_control = 0x01, Volume_control = 0x02, Bass_control = 0x03, Mid_control = 0x04, Treble_control = 0x05, Equalizer_control = 0x06, Agc_control = 0x07, Delay_control = 0x08, Bassboost_control = 0x09, Loudness_control = 0x0a, /* Items below are defined by implementation: */ Channel_control = 0x0b, Resolution_control = 0x0c, Ncontrol, Selector_control = 0x0d, sampling_freq_control = 0x01, Audiocsp = 0x000101, /* audio.control.0 */ AUDIO_INTERFACE = 0x24, AUDIO_ENDPOINT = 0x25, }; #define AS_GENERAL 1 #define FORMAT_TYPE 2 #define FORMAT_SPECIFIC 3 #define PCM 1 #define PCM8 2 #define IEEE_FLOAT 3 #define ALAW 4 #define MULAW 5 #define SAMPLING_FREQ_CONTROL 0x01 typedef struct Audioalt Audioalt; struct Audioalt { int nchan; int res; int subframesize; int minfreq, maxfreq; /* continuous freqs */ int freqs[8]; /* discrete freqs */ int caps; /* see below for meanings */ }; enum { /* Audioalt->caps bits */ has_setspeed = 0x1, /* has a speed_set command */ has_pitchset = 0x2, /* has a pitch_set command */ has_contfreq = 0x4, /* frequency continuously variable */ has_discfreq = 0x8, /* discrete set of frequencies */ onefreq = 0x10, /* only one frequency */ maxpkt_only = 0x80, /* packets must be padded to max size */ }; typedef uchar byte; extern int setrec; extern int verbose; extern int defaultspeed[2]; extern Dev *ad; extern Dev *buttondev; extern Channel *controlchan; extern Dev *epdev[2]; void audio_interface(Dev *d, Desc *dd); void setalt(Dev *d, int endpt, int value); int getalt(Dev *d, int endpt); int setspeed(int rec, int speed); int setcontrol(int rec, char *name, long *value); int getspecialcontrol(int rec, int ctl, int req, long *value); int getcontrol(int rec, char *name, long *value); int findalt(int rec, int nchan, int res, int speed); void getcontrols(void); void serve(void *); int nbchanprint(Channel *c, char *fmt, ...); int Aconv(Fmt *fp); /* illegal request */ dprint(2, "%s must be record or playback", args[2]); if(replchan) chanprint(replchan, "%s must be record or playback", args[2]); free(req); continue; } c = nil; for(i = 0; i < Ncontrol; i++){ c = &controls[rec][i]; if(strcmp(args[1], c->name) == 0) break; } if(i == Ncontrol){ dprint(2, "Illegal control name: %s", args[1]); if(replchan) chanprint(replchan, "Illegal control name: %s", args[1]); }else if(!c->settable){ dprint(2, "%s %s is not susb/audio/audioctl.c 664 0 0 42706 11171061713 12555ustar00nemosys#include #include #include #include "usb.h" #include "audio.h" #include "audioctl.h" int endpt[2] = {-1, -1}; int interface[2] = {-1, -1}; int featureid[2] = {-1, -1}; int selectorid[2] = {-1, -1}; int mixerid[2] = {-1, -1}; int curalt[2] = {-1, -1}; int buttonendpt = -1; int id; Dev *ad; Audiocontrol controls[2][Ncontrol] = { { [Speed_control] = { "speed", 0, {0}, 0, 44100, Undef}, [Mute_control] = { "mute", 0, {0}, 0, 0, Undef}, [Volume_control] = { "volume", 0, {0}, 0, 0, Undef}, [Bass_control] = { "bass", 0, {0}, 0, 0, Undef}, [Mid_control] = { "mid", 0, {0}, 0, 0, Undef}, [Treble_control] = { "treble", 0, {0}, 0, 0, Undef}, [Equalizer_control] = { "equalizer", 0, {0}, 0, 0, Undef}, [Agc_control] = { "agc", 0, {0}, 0, 0, Undef}, [Delay_control] = { "delay", 0, {0}, 0, 0, Undef}, [Bassboost_control] = { "bassboost", 0, {0}, 0, 0, Undef}, [Loudness_control] = { "loudness", 0, {0}, 0, 0, Undef}, [Channel_control] = { "channels", 0, {0}, 0, 2, Undef}, [Resolution_control] = { "resolution", 0, {0}, 0, 16, Undef}, // [Selector_control] = { "selector", 0, {0}, 0, 0, Undef}, }, { [Speed_control] = { "speed", 0, {0}, 0, 44100, Undef}, [Mute_control] = { "mute", 0, {0}, 0, 0, Undef}, [Volume_control] = { "volume", 0, {0}, 0, 0, Undef}, [Bass_control] = { "bass", 0, {0}, 0, 0, Undef}, [Mid_control] = { "mid", 0, {0}, 0, 0, Undef}, [Treble_control] = { "treble", 0, {0}, 0, 0, Undef}, [Equalizer_control] = { "equalizer", 0, {0}, 0, 0, Undef}, [Agc_control] = { "agc", 0, {0}, 0, 0, Undef}, [Delay_control] = { "delay", 0, {0}, 0, 0, Undef}, [Bassboost_control] = { "bassboost", 0, {0}, 0, 0, Undef}, [Loudness_control] = { "loudness", 0, {0}, 0, 0, Undef}, [Channel_control] = { "channels", 0, {0}, 0, 2, Undef}, [Resolution_control] = { "resolution", 0, {0}, 0, 16, Undef}, // [Selector_control] = { "selector", 0, {0}, 0, 0, Undef}, } }; int setaudioalt(int rec, Audiocontrol *c, int control) { dprint(2, "setcontrol %s: Set alt %d\n", c->name, control); curalt[rec] = control; if(usbcmd(ad, Rh2d|Rstd|Riface, Rsetiface, control, interface[rec], nil, 0) < 0){ dprint(2, "setcontrol: setupcmd %s failed\n", c->name); return -1; } return control; } int findalt(int rec, int nchan, int res, int speed) { Ep *ep; Audioalt *a; Altc *da; int i, j, k, retval; retval = -1; controls[rec][Channel_control].min = 1000000; controls[rec][Channel_control].max = 0; controls[rec][Channel_control].step = Undef; controls[rec][Resolution_control].min = 1000000; controls[rec][Resolution_control].max = 0; controls[rec][Resolution_control].step = Undef; for(i = 0; i < nelem(ad->usb->ep); i++){ if((ep = ad->usb->ep[i]) == nil) continue; if(ep->iface == nil){ fprint(2, "\tno interface\n"); return 0; } if(ep->iface->csp != CSP(Claudio, 2, 0)) continue; if((rec == Play && (ep->addr & 0x80)) || (rec == Record && (ep->addr & 0x80) == 0)) continue; for(j = 0; j < 16; j++){ if((da = ep->iface->altc[j]) == nil || (a = da->aux) == nil) continue; if(a->nchan < controls[rec][Channel_control].min) controls[rec][Channel_control].min = a->nchan; if(a->nchan > controls[rec][Channel_control].max) controls[rec][Channel_control].max = a->nchan; if(a->res < controls[rec][Resolution_control].min) controls[rec][Resolution_control].min = a->res; if(a->res > controls[rec][Resolution_control].max) controls[rec][Resolution_control].max = a->res; controls[rec][Channel_control].settable = 1; controls[rec][Channel_control].readable = 1; controls[rec][Resolution_control].settable = 1; controls[rec][Resolution_control].readable = 1; controls[rec][Speed_control].settable = 1; controls[rec][Speed_control].readable = 1; if(a->nchan == nchan && a->res == res){ if(speed == Undef) retval = j; else if(a->caps & (has_discfreq|onefreq)){ for(k = 0; k < nelem(a->freqs); k++){ if(a->freqs[k] == speed){ retval = j; break; } } }else{ if(speed >= a->minfreq && speed <= a->maxfreq) retval = j; } } } } if(usbdebug && retval < 0) fprint(2, "findalt(%d, %d, %d, %d) failed\n", rec, nchan, res, speed); return retval; } int setspeed(int rec, int speed) { int ps, n, no, dist, i; Audioalt *a; Altc *da; Ep *ep; uchar buf[3]; if(rec == Record && !setrec) return Undef; if(curalt[rec] < 0){ fprint(2, "Must set channels and resolution before speed\n"); return Undef; } if(endpt[rec] < 0) sysfatal("endpt[%s] not set", rec?"Record":"Playback"); ep = ad->usb->ep[endpt[rec]]; if(ep->iface == nil) sysfatal("no interface"); if(curalt[rec] < 0) sysfatal("curalt[%s] not set", rec?"Record":"Playback"); da = ep->iface->altc[curalt[rec]]; a = da->aux; if(a->caps & onefreq){ dprint(2, "setspeed %d: onefreq\n", speed); /* speed not settable, but packet size must still be set */ speed = a->freqs[0]; }else if(a->caps & has_contfreq){ dprint(2, "setspeed %d: contfreq\n", speed); if(speed < a->minfreq) speed = a->minfreq; else if(speed > a->maxfreq) speed = a->maxfreq; dprint(2, "Setting continuously variable %s speed to %d\n", rec?"record":"playback", speed); }else if(a->caps & has_discfreq){ dprint(2, "setspeed %d: discfreq\n", speed); dist = 1000000; no = -1; for(i = 0; a->freqs[i] > 0; i++) if(abs(a->freqs[i] - speed) < dist){ dist = abs(a->freqs[i] - speed); no = i; } if(no == -1){ dprint(2, "no = -1\n"); return Undef; } speed = a->freqs[no]; dprint(2, "Setting discreetly variable %s speed to %d\n", rec?"record":"playback", speed); }else{ dprint(2, "can't happen\n?"); return Undef; } if(a->caps & has_setspeed){ dprint(2, "Setting %s speed to %d Hz;", rec?"record":"playback", speed); buf[0] = speed; buf[1] = speed >> 8; buf[2] = speed >> 16; n = endpt[rec]; if(rec) n |= 0x80; if(usbcmd(ad, Rh2d|Rclass|Rep, Rsetcur, sampling_freq_control<<8, n, buf, 3) < 0){ fprint(2, "Error in setupcmd\n"); return Undef; } if((n=usbcmd(ad, Rd2h|Rclass|Rep, Rgetcur, sampling_freq_control<<8, n, buf, 3)) < 0){ fprint(2, "Error in setupreq\n"); return Undef; } if(n != 3) fprint(2, "Error in setupreply: %d\n", n); else{ n = buf[0] | buf[1] << 8 | buf[2] << 16; if(buf[2] || n == 0){ dprint(2, "Speed out of bounds %d (0x%x)\n", n, n); }else if(n != speed && ad->usb->vid == 0x077d && (ad->usb->did == 0x0223 || ad->usb->did == 0x07af)){ /* Griffin iMic responds incorrectly to sample rate inquiry */ dprint(2, " reported as %d (iMic bug?);", n); }else speed = n; } dprint(2, " speed now %d Hz;", speed); } ps = ((speed * da->interval + 999) / 1000) * controls[rec][Channel_control].value[0] * controls[rec][Resolution_control].value[0]/8; if(ps > ep->maxpkt){ fprint(2, "%s: setspeed(rec %d, speed %d): packet size %d > " "maximum packet size %d\n", argv0, rec, speed, ps, ep->maxpkt); return Undef; } dprint(2, "Configuring %s endpoint for %d Hz\n", rec?"record":"playback", speed); epdev[rec] = openep(ad, endpt[rec]); if(epdev[rec] == nil) sysfatal("openep rec %d: %r", rec); devctl(epdev[rec], "pollival %d", da->interval); devctl(epdev[rec], "samplesz %ld", controls[rec][Channel_control].value[0] * controls[rec][Resolution_control].value[0]/8); devctl(epdev[rec], "hz %d", speed); /* NO: the client uses the endpoint file directly if(opendevdata(epdev[rec], rec ? OREAD : OWRITE) < 0) sysfatal("openep rec %d: %r", rec); */ return speed; } long getspeed(int rec, int which) { int i, n; Audioalt *a; Altc *da; Ep *ep; uchar buf[3]; int r; if(curalt[rec] < 0){ fprint(2, "Must set channels and resolution before getspeed\n"); return Undef; } if(endpt[rec] < 0) sysfatal("endpt[%s] not set", rec?"Record":"Playback"); dprint(2, "getspeed: endpt[%d] == %d\n", rec, endpt[rec]); ep = ad->usb->ep[endpt[rec]]; if(ep->iface == nil) sysfatal("no interface"); if(curalt[rec] < 0) sysfatal("curalt[%s] not set", rec?"Record":"Playback"); da = ep->iface->altc[curalt[rec]]; a = da->aux; if(a->caps & onefreq){ dprint(2, "getspeed: onefreq\n"); if(which == Rgetres) return Undef; return a->freqs[0]; /* speed not settable */ } if(a->caps & has_setspeed){ dprint(2, "getspeed: has_setspeed, ask\n"); n = endpt[rec]; if(rec) n |= 0x80; r = Rd2h|Rclass|Rep; if(usbcmd(ad,r,which,sampling_freq_control<<8, n, buf, 3) < 0) return Undef; if(n == 3){ if(buf[2]){ dprint(2, "Speed out of bounds\n"); if((a->caps & has_discfreq) && (buf[0] | buf[1] << 8) < 8) return a->freqs[buf[0] | buf[1] << 8]; } return buf[0] | buf[1] << 8 | buf[2] << 16; } dprint(2, "getspeed: n = %d\n", n); } if(a->caps & has_contfreq){ dprint(2, "getspeed: has_contfreq\n"); if(which == Rgetcur) return controls[rec][Speed_control].value[0]; if(which == Rgetmin) return a->minfreq; if(which == Rgetmax) return a->maxfreq; if(which == Rgetres) return 1; } if(a->caps & has_discfreq){ dprint(2, "getspeed: has_discfreq\n"); if(which == Rgetcur) return controls[rec][Speed_control].value[0]; if(which == Rgetmin) return a->freqs[0]; for(i = 0; i < 8 && a->freqs[i] > 0; i++) ; if(which == Rgetmax) return a->freqs[i-1]; if(which == Rgetres) return Undef; } dprint(2, "can't happen\n?"); return Undef; } int setcontrol(int rec, char *name, long *value) { int i, ctl, m; byte buf[3]; int type, req, control, index, count; Audiocontrol *c; c = nil; for(ctl = 0; ctl < Ncontrol; ctl++){ c = &controls[rec][ctl]; if(strcmp(name, c->name) == 0) break; } if(ctl == Ncontrol){ dprint(2, "setcontrol: control not found\n"); return -1; } if(c->settable == 0){ dprint(2, "setcontrol: control %d.%d not settable\n", rec, ctl); if(c->chans){ for(i = 0; i < 8; i++) if((c->chans & 1 << i) && c->value[i] != value[i]) return -1; return 0; } if(c->value[0] != value[0]) return -1; return 0; } if(c->chans){ value[0] = 0; // set to average m = 0; for(i = 1; i < 8; i++) if(c->chans & 1 << i){ if(c->min != Undef && value[i] < c->min) value[i] = c->min; if(c->max != Undef && value[i] > c->max) value[i] = c->max; value[0] += value[i]; m++; }else value[i] = Undef; if(m) value[0] /= m; }else{ if(c->min != Undef && value[0] < c->min) value[0] = c->min; if(c->max != Undef && value[0] > c->max) value[0] = c->max; } req = Rsetcur; count = 1; switch(ctl){ default: dprint(2, "setcontrol: can't happen\n"); return -1; case Speed_control: if((rec != Record || setrec) && (value[0] = setspeed(rec, value[0])) < 0) return -1; c->value[0] = value[0]; return 0; case Equalizer_control: /* not implemented */ return -1; case Resolution_control: control = findalt(rec, controls[rec][Channel_control].value[0], value[0], defaultspeed[rec]); if(control < 0 || setaudioalt(rec, c, control) < 0){ dprint(2, "setcontrol: can't find setting for %s\n", c->name); return -1; } c->value[0] = value[0]; controls[rec][Speed_control].value[0] = defaultspeed[rec]; return 0; case Volume_control: case Delay_control: count = 2; /* fall through */ case Mute_control: case Bass_control: case Mid_control: case Treble_control: case Agc_control: case Bassboost_control: case Loudness_control: type = Rh2d|Rclass|Riface; control = ctl<<8; index = featureid[rec]<<8; break; case Selector_control: type = Rh2d|Rclass|Riface; control = 0; index = selectorid[rec]<<8; break; case Channel_control: control = findalt(rec, value[0], controls[rec][Resolution_control].value[0], defaultspeed[rec]); if(control < 0 || setaudioalt(rec, c, control) < 0){ dprint(2, "setcontrol: can't find setting for %s\n", c->name); return -1; } c->value[0] = value[0]; controls[rec][Speed_control].value[0] = defaultspeed[rec]; return 0; } if(c->chans){ for(i = 1; i < 8; i++) if(c->chans & 1 << i){ switch(count){ case 2: buf[1] = value[i] >> 8; case 1: buf[0] = value[i]; } if(usbcmd(ad, type, req, control | i, index, buf, count) < 0){ dprint(2, "setcontrol: setupcmd %s failed\n", controls[rec][ctl].name); return -1; } c->value[i] = value[i]; } }else{ switch(count){ case 2: buf[1] = value[0] >> 8; case 1: buf[0] = value[0]; } if(usbcmd(ad, type, req, control, index, buf, count) < 0){ dprint(2, "setcontrol: setupcmd %s failed\n", c->name); return -1; } } c->value[0] = value[0]; return 0; } int getspecialcontrol(int rec, int ctl, int req, long *value) { byte buf[3]; int m, n, i; int type, control, index, count, signedbyte; short svalue; count = 1; signedbyte = 0; switch(ctl){ default: return Undef; case Speed_control: value[0] = getspeed(rec, req); return 0; case Channel_control: case Resolution_control: if(req == Rgetmin) value[0] = controls[rec][ctl].min; if(req == Rgetmax) value[0] = controls[rec][ctl].max; if(req == Rgetres) value[0] = controls[rec][ctl].step; if(req == Rgetcur) value[0] = controls[rec][ctl].value[0]; return 0; case Volume_control: case Delay_control: count = 2; /* fall through */ case Bass_control: case Mid_control: case Treble_control: case Equalizer_control: signedbyte = 1; type = Rd2h|Rclass|Riface; control = ctl<<8; index = featureid[rec]<<8; break; case Selector_control: type = Rd2h|Rclass|Riface; control = 0; index = selectorid[rec]<<8; break; case Mute_control: case Agc_control: case Bassboost_control: case Loudness_control: if(req != Rgetcur) return Undef; type = Rd2h|Rclass|Riface; control = ctl<<8; index = featureid[rec]<<8; break; } if(controls[rec][ctl].chans){ m = 0; value[0] = 0; // set to average for(i = 1; i < 8; i++){ value[i] = Undef; if(controls[rec][ctl].chans & 1 << i){ n=usbcmd(ad, type,req, control|i,index,buf,count); if(n < 0) return Undef; if(n != count) return -1; switch (count){ case 2: svalue = buf[1] << 8 | buf[0]; if(req == Rgetcur){ value[i] = svalue; value[0] += svalue; m++; }else value[0] = svalue; break; case 1: svalue = buf[0]; if(signedbyte && (svalue&0x80)) svalue |= 0xFF00; if(req == Rgetcur){ value[i] = svalue; value[0] += svalue; m++; }else value[0] = svalue; } } } if(m) value[0] /= m; return 0; } value[0] = Undef; if(usbcmd(ad, type, req, control, index, buf, count) != count) return -1; switch (count){ case 2: svalue = buf[1] << 8 | buf[0]; value[0] = svalue; break; case 1: svalue = buf[0]; if(signedbyte && (svalue&0x80)) svalue |= 0xFF00; value[0] = svalue; } return 0; } int getcontrol(int rec, char *name, long *value) { int i; for(i = 0; i < Ncontrol; i++){ if(strcmp(name, controls[rec][i].name) == 0) break; } if(i == Ncontrol) return -1; if(controls[rec][i].readable == 0) return -1; if(getspecialcontrol(rec, i, Rgetcur, value) < 0) return -1; memmove(controls[rec][i].value, value, sizeof controls[rec][i].value); return 0; } void getcontrols(void) { int rec, ctl, i; Audiocontrol *c; long v[8]; for(rec = 0; rec < 2; rec++){ if(rec == Record && !setrec) continue; for(ctl = 0; ctl < Ncontrol; ctl++){ c = &controls[rec][ctl]; if(c->readable){ if(verbose) fprint(2, "%s %s control", rec?"Record":"Playback", controls[rec][ctl].name); c->min = (getspecialcontrol(rec, ctl, Rgetmin, v) < 0) ? Undef : v[0]; if(verbose && c->min != Undef) fprint(2, ", min %ld", c->min); c->max = (getspecialcontrol(rec, ctl, Rgetmax, v) < 0) ? Undef : v[0]; if(verbose && c->max != Undef) fprint(2, ", max %ld", c->max); c->step = (getspecialcontrol(rec, ctl, Rgetres, v) < 0) ? Undef : v[0]; if(verbose && c->step != Undef) fprint(2, ", step %ld", c->step); if(getspecialcontrol(rec, ctl, Rgetcur, c->value) == 0){ if(verbose){ if(c->chans){ fprint(2, ", values"); for(i = 1; i < 8; i++) if(c->chans & 1 << i) fprint(2, "[%d] %ld ", i, c->value[i]); }else fprint(2, ", value %ld", c->value[0]); } } if(verbose) fprint(2, "\n"); }else{ c->min = Undef; c->max = Undef; c->step = Undef; c->value[0] = Undef; dprint(2, "%s %s control not settable\n", rec?"Playback":"Record", controls[rec][ctl].name); } } } } int ctlparse(char *s, Audiocontrol *c, long *v) { int i, j, nf, m; char *vals[9]; char *p; long val; nf = tokenize(s, vals, nelem(vals)); if(nf <= 0) return -1; if(c->chans){ j = 0; m = 0; SET(val); v[0] = 0; // will compute average of v[i] for(i = 1; i < 8; i++) if(c->chans & 1 << i){ if(j < nf){ val = strtol(vals[j], &p, 0); if(val == 0 && *p != '\0' && *p != '%') return -1; if(*p == '%' && c->min != Undef) val = (val*c->max + (100-val)*c->min)/100; j++; } v[i] = val; v[0] += val; m++; }else v[i] = Undef; if(m) v[0] /= m; }else{ val = strtol(vals[0], &p, 0); if(*p == '%' && c->min != Undef) val = (val*c->max + (100-val)*c->min)/100; v[0] = val; } return 0; } int Aconv(Fmt *fp) { char str[256]; Audiocontrol *c; int fst, i; char *p; c = va_arg(fp->args, Audiocontrol*); p = str; if(c->chans){ fst = 1; for(i = 1; i < 8; i++) if(c->chans & 1 << i){ p = seprint(p, str+sizeof str, "%s%ld", fst?"'":" ", c->value[i]); fst = 0; } seprint(p, str+sizeof str, "'"); }else seprint(p, str+sizeof str, "%ld", c->value[0]); return fmtstrcpy(fp, str); } eed; } long getspeed(int rec, int which) { int i, n; Auusb/audio/audioctl.h 664 0 0 1152 11152121044 12521ustar00nemosysenum{ Undef = 0x80000000, Play = 0, Record = 1, }; typedef struct Audiocontrol Audiocontrol; struct Audiocontrol { char *name; uchar readable; uchar settable; uchar chans; /* 0 is master, non-zero is bitmap */ long value[8]; /* 0 is master; value[0] == Undef -> all values Undef */ long min, max, step; }; extern Audiocontrol controls[2][Ncontrol]; extern int endpt[2]; extern int interface[2]; extern int featureid[2]; extern int selectorid[2]; extern int mixerid[2]; extern int buttonendpt; int ctlparse(char *s, Audiocontrol *c, long *v); void ctlevent(void); #pragma varargck type "A" Audiocontrol* getspeed: n = %d\n", n); } if(a->caps & has_contfreq){ dprint(2, "getspeed: has_contfreq\n"); if(which == Rgetcur) return controls[rec][Speed_control].value[0]; if(which == Rgetmin) return a->minfreq; if(which == Rgetmax) return a->maxfreq; if(which == Rgetres) return 1; } if(a->caps & has_discfreq){ dprint(2, "getspeed: has_discfreq\n"); if(which == Rgetcur) return contusb/audio/audiofs.c 664 0 0 42454 11152121045 12375ustar00nemosys#include #include #include #include #include #include #include "usb.h" #include "audio.h" #include "audioctl.h" int attachok; #define STACKSIZE 16*1024 enum { OPERM = 0x3, /* mask of all permission types in open mode */ }; typedef struct Fid Fid; typedef struct Audioctldata Audioctldata; typedef struct Worker Worker; struct Audioctldata { long offoff; /* offset of the offset for audioctl */ long values[2][Ncontrol][8]; /* last values transmitted */ char *s; int ns; }; enum { Busy = 0x01, Open = 0x02, Eof = 0x04, }; struct Fid { QLock; int fid; Dir *dir; ushort flags; short readers; void *fiddata; /* file specific per-fid data (used for audioctl) */ Fid *next; }; struct Worker { Fid *fid; ushort tag; Fcall *rhdr; Dir *dir; Channel *eventc; Worker *next; }; enum { /* Event channel messages for worker */ Work = 0x01, Check = 0x02, Flush = 0x03, }; enum { Qdir, Qvolume, Qaudioctl, Qaudiostat, Nqid, }; Dir dirs[] = { [Qdir] = {0,0,{Qdir, 0,QTDIR},0555|DMDIR,0,0,0, ".", nil,nil,nil}, [Qvolume] = {0,0,{Qvolume, 0,QTFILE},0666,0,0,0, "volume", nil,nil,nil}, [Qaudioctl] = {0,0,{Qaudioctl, 0,QTFILE},0666,0,0,0, "audioctl",nil,nil,nil}, [Qaudiostat] = {0,0,{Qaudiostat,0,QTFILE},0666,0,0,0, "audiostat",nil,nil,nil}, }; int messagesize = 4*1024+IOHDRSZ; uchar mdata[8*1024+IOHDRSZ]; uchar mbuf[8*1024+IOHDRSZ]; Fcall thdr; Fcall rhdr; Worker *workers; char srvfile[64], mntdir[64], epdata[64], audiofile[64]; int mfd[2], p[2]; char user[32]; char *srvpost; Channel *procchan; Channel *replchan; Fid *fids; Fid* newfid(int); void io(void *); void usage(void); extern char *mntpt; char *rflush(Fid*), *rauth(Fid*), *rattach(Fid*), *rwalk(Fid*), *ropen(Fid*), *rcreate(Fid*), *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*), *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*), *rversion(Fid*); char *(*fcalls[])(Fid*) = { [Tflush] rflush, [Tversion] rversion, [Tauth] rauth, [Tattach] rattach, [Twalk] rwalk, [Topen] ropen, [Tcreate] rcreate, [Tread] rread, [Twrite] rwrite, [Tclunk] rclunk, [Tremove] rremove, [Tstat] rstat, [Twstat] rwstat, }; char Eperm[] = "permission denied"; char Enotdir[] = "not a directory"; char Enoauth[] = "no authentication in ramfs"; char Enotexist[] = "file does not exist"; char Einuse[] = "file in use"; char Eexist[] = "file exists"; char Enotowner[] = "not owner"; char Eisopen[] = "file already open for I/O"; char Excl[] = "exclusive use file already open"; char Ename[] = "illegal name"; char Ebadctl[] = "unknown control message"; int notifyf(void *, char *s) { if(strncmp(s, "interrupt", 9) == 0) return 1; return 0; } void post(char *name, char *envname, int srvfd) { int fd; char buf[32]; fd = create(name, OWRITE, attachok?0666:0600); if(fd < 0) return; sprint(buf, "%d",srvfd); if(write(fd, buf, strlen(buf)) != strlen(buf)) sysfatal("srv write"); close(fd); putenv(envname, name); } /* * BUG: If audio is later used on a different name space, the * audio/audioin files are not there because of the bind trick. * We should actually implement those files despite the binds. * If audio is used from within the same ns nothing would change, * otherwise, whoever would mount the audio files could still * play/record audio (unlike now). */ void serve(void *) { int i; ulong t; if(pipe(p) < 0) sysfatal("pipe failed"); mfd[0] = p[0]; mfd[1] = p[0]; atnotify(notifyf, 1); strcpy(user, getuser()); t = time(nil); for(i = 0; i < Nqid; i++){ dirs[i].uid = user; dirs[i].gid = user; dirs[i].muid = user; dirs[i].atime = t; dirs[i].mtime = t; } if(mntpt == nil){ snprint(mntdir, sizeof(mntdir), "/dev"); mntpt = mntdir; } if(usbdebug) fmtinstall('F', fcallfmt); procrfork(io, nil, STACKSIZE, RFFDG|RFNAMEG); close(p[0]); /* don't deadlock if child fails */ if(srvpost){ sprint(srvfile, "/srv/%s", srvpost); remove(srvfile); post(srvfile, "usbaudio", p[1]); } if(mount(p[1], -1, mntpt, MBEFORE, "") < 0) sysfatal("mount failed"); if(endpt[Play] >= 0 && devctl(epdev[Play], "name audio") < 0) fprint(2, "audio: name audio: %r\n"); if(endpt[Record] >= 0 && devctl(epdev[Record], "name audioin") < 0) fprint(2, "audio: name audioin: %r\n"); threadexits(nil); } char* rversion(Fid*) { Fid *f; if(thdr.msize < 256) return "max messagesize too small"; if(thdr.msize < messagesize) messagesize = thdr.msize; rhdr.msize = messagesize; if(strncmp(thdr.version, "9P2000", 6) != 0) return "unknown 9P version"; else rhdr.version = "9P2000"; for(f = fids; f; f = f->next) if(f->flags & Busy) rclunk(f); return nil; } char* rauth(Fid*) { return "usbaudio: no authentication required"; } char* rflush(Fid *) { Worker *w; int waitflush; do { waitflush = 0; for(w = workers; w; w = w->next) if(w->tag == thdr.oldtag){ waitflush++; nbsendul(w->eventc, thdr.oldtag << 16 | Flush); } if(waitflush) sleep(50); } while(waitflush); dprint(2, "flush done on tag %d\n", thdr.oldtag); return 0; } char* rattach(Fid *f) { f->flags |= Busy; f->dir = &dirs[Qdir]; rhdr.qid = f->dir->qid; if(attachok == 0 && strcmp(thdr.uname, user) != 0) return Eperm; return 0; } static Fid* doclone(Fid *f, int nfid) { Fid *nf; nf = newfid(nfid); if(nf->flags & Busy) return nil; nf->flags |= Busy; nf->flags &= ~Open; nf->dir = f->dir; return nf; } char* dowalk(Fid *f, char *name) { int t; if(strcmp(name, ".") == 0) return nil; if(strcmp(name, "..") == 0){ f->dir = &dirs[Qdir]; return nil; } if(f->dir != &dirs[Qdir]) return Enotexist; for(t = 1; t < Nqid; t++){ if(strcmp(name, dirs[t].name) == 0){ f->dir = &dirs[t]; return nil; } } return Enotexist; } char* rwalk(Fid *f) { Fid *nf; char *rv; int i; Dir *savedir; if(f->flags & Open) return Eisopen; rhdr.nwqid = 0; nf = nil; savedir = f->dir; /* clone if requested */ if(thdr.newfid != thdr.fid){ nf = doclone(f, thdr.newfid); if(nf == nil) return "new fid in use"; f = nf; } /* if it's just a clone, return */ if(thdr.nwname == 0 && nf != nil) return nil; /* walk each element */ rv = nil; for(i = 0; i < thdr.nwname; i++){ rv = dowalk(f, thdr.wname[i]); if(rv != nil){ if(nf != nil) rclunk(nf); else f->dir = savedir; break; } rhdr.wqid[i] = f->dir->qid; } rhdr.nwqid = i; /* we only error out if no walk */ if(i > 0) rv = nil; return rv; } Audioctldata * allocaudioctldata(void) { int i, j, k; Audioctldata *a; a = emallocz(sizeof(Audioctldata), 1); for(i = 0; i < 2; i++) for(j=0; j < Ncontrol; j++) for(k=0; k < 8; k++) a->values[i][j][k] = Undef; return a; } char * ropen(Fid *f) { if(f->flags & Open) return Eisopen; if(thdr.mode != OREAD && (f->dir->mode & 0x2) == 0) return Eperm; qlock(f); if(f->dir == &dirs[Qaudioctl] && f->fiddata == nil) f->fiddata = allocaudioctldata(); qunlock(f); rhdr.iounit = 0; rhdr.qid = f->dir->qid; f->flags |= Open; return nil; } char * rcreate(Fid*) { return Eperm; } int readtopdir(Fid*, uchar *buf, long off, int cnt, int blen) { int i, m, n; long pos; n = 0; pos = 0; for(i = 1; i < Nqid; i++){ m = convD2M(&dirs[i], &buf[n], blen-n); if(off <= pos){ if(m <= BIT16SZ || m > cnt) break; n += m; cnt -= m; } pos += m; } return n; } enum { Chunk = 1024, }; int makeaudioctldata(Fid *f) { int rec, ctl, i, diff; long *actls; /* 8 of them */ char *p, *e; Audiocontrol *c; Audioctldata *a; if((a = f->fiddata) == nil) sysfatal("fiddata"); if((p = a->s) == nil) a->s = p = emallocz(Chunk, 0); e = p + Chunk - 1; /* e must point *at* last byte, not *after* */ for(rec = 0; rec < 2; rec++) for(ctl = 0; ctl < Ncontrol; ctl++){ c = &controls[rec][ctl]; actls = a->values[rec][ctl]; diff = 0; if(c->chans){ for(i = 1; i < 8; i++) if((c->chans & 1<value[i] != actls[i]) diff = 1; }else if(c->value[0] != actls[0]) diff = 1; if(diff){ p = seprint(p, e, "%s %s %A", c->name, rec? "in": "out", c); memmove(actls, c->value, sizeof c->value); if(c->min != Undef){ p = seprint(p, e, " %ld %ld", c->min, c->max); if(c->step != Undef) p = seprint(p, e, " %ld", c->step); } p = seprint(p, e, "\n"); } } assert(strlen(a->s) < Chunk); a->ns = p - a->s; return a->ns; } void readproc(void *x) { int n, cnt; ulong event; vlong off; uchar *mdata; Audioctldata *a; Fcall *rhdr; Fid *f; Worker *w; w = x; mdata = emallocz(8*1024+IOHDRSZ, 0); while(event = recvul(w->eventc)){ if(event != Work) continue; f = w->fid; rhdr = w->rhdr; a = f->fiddata; off = rhdr->offset; cnt = rhdr->count; assert(a->offoff == off); /* f is already locked */ for(;;){ qunlock(f); event = recvul(w->eventc); qlock(f); ddprint(2, "readproc unblocked fid %d %lld\n", f->fid, f->dir->qid.path); switch (event & 0xffff){ case Work: sysfatal("readproc phase error"); case Check: if(f->fiddata && makeaudioctldata(f) == 0) continue; break; case Flush: if((event >> 16) == rhdr->tag){ ddprint(2, "readproc flushing fid %d, tag %d\n", f->fid, rhdr->tag); goto flush; } continue; } if(f->fiddata){ rhdr->data = a->s; rhdr->count = a->ns; break; } yield(); } if(rhdr->count > cnt) rhdr->count = cnt; if(rhdr->count) f->flags &= ~Eof; ddprint(2, "readproc:->%F\n", rhdr); n = convS2M(rhdr, mdata, messagesize); if(write(mfd[1], mdata, n) != n) sysfatal("mount write"); flush: w->tag = NOTAG; f->readers--; assert(f->readers == 0); free(rhdr); w->rhdr = nil; qunlock(f); sendp(procchan, w); } threadexits(nil); } char* rread(Fid *f) { int i, n, cnt, rec, div; vlong off; char *p; Audiocontrol *c; Audioctldata *a; Worker *w; static char buf[1024]; rhdr.count = 0; off = thdr.offset; cnt = thdr.count; if(cnt > messagesize - IOHDRSZ) cnt = messagesize - IOHDRSZ; rhdr.data = (char*)mbuf; if(f->dir == &dirs[Qdir]){ n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ); rhdr.count = n; return nil; } if(f->dir == &dirs[Qvolume]){ p = buf; n = sizeof buf; for(rec = 0; rec < 2; rec++){ c = &controls[rec][Volume_control]; if(c->readable){ div = c->max - c->min; i = snprint(p, n, "audio %s %ld\n", rec? "in": "out", (c->min != Undef? 100*(c->value[0]-c->min)/(div? div: 1): c->value[0])); p += i; n -= i; } c = &controls[rec][Treble_control]; if(c->readable){ div = c->max - c->min; i = snprint(p, n, "treb %s %ld\n", rec? "in": "out", (c->min != Undef? 100*(c->value[0]-c->min)/(div? div: 1): c->value[0])); p += i; n -= i; } c = &controls[rec][Bass_control]; if(c->readable){ div = c->max - c->min; i = snprint(p, n, "bass %s %ld\n", rec? "in": "out", (c->min != Undef? 100*(c->value[0]-c->min)/(div? div: 1): c->value[0])); p += i; n -= i; } c = &controls[rec][Speed_control]; if(c->readable){ i = snprint(p, n, "speed %s %ld\n", rec? "in": "out", c->value[0]); p += i; n -= i; } } n = sizeof buf - n; if(off > n) rhdr.count = 0; else{ rhdr.data = buf + off; rhdr.count = n - off; if(rhdr.count > cnt) rhdr.count = cnt; } return nil; } if(f->dir == &dirs[Qaudioctl]){ Fcall *hdr; qlock(f); a = f->fiddata; if(off - a->offoff < 0){ /* there was a seek */ a->offoff = off; a->ns = 0; } do { if(off - a->offoff < a->ns){ rhdr.data = a->s + (off - a->offoff); rhdr.count = a->ns - (off - a->offoff); if(rhdr.count > cnt) rhdr.count = cnt; qunlock(f); return nil; } if(a->offoff != off){ a->ns = 0; a->offoff = off; rhdr.count = 0; qunlock(f); return nil; } } while(makeaudioctldata(f) != 0); assert(a->offoff == off); /* Wait for data off line */ f->readers++; w = nbrecvp(procchan); if(w == nil){ w = emallocz(sizeof(Worker), 1); w->eventc = chancreate(sizeof(ulong), 1); w->next = workers; workers = w; proccreate(readproc, w, 4096); } hdr = emallocz(sizeof(Fcall), 0); w->fid = f; w->tag = thdr.tag; assert(w->rhdr == nil); w->rhdr = hdr; hdr->count = cnt; hdr->offset = off; hdr->type = thdr.type+1; hdr->fid = thdr.fid; hdr->tag = thdr.tag; sendul(w->eventc, Work); return (char*)~0; } return Eperm; } char* rwrite(Fid *f) { long cnt, value; char *lines[2*Ncontrol], *fields[4], *subfields[9], *err, *p; int nlines, i, nf, nnf, rec, ctl; Audiocontrol *c; Worker *w; static char buf[256]; rhdr.count = 0; cnt = thdr.count; if(cnt > messagesize - IOHDRSZ) cnt = messagesize - IOHDRSZ; err = nil; if(f->dir == &dirs[Qvolume] || f->dir == &dirs[Qaudioctl]){ thdr.data[cnt] = '\0'; nlines = getfields(thdr.data, lines, 2*Ncontrol, 1, "\n"); for(i = 0; i < nlines; i++){ dprint(2, "line: %s\n", lines[i]); nf = tokenize(lines[i], fields, 4); if(nf == 0) continue; if(nf == 3) if(strcmp(fields[1], "in") == 0 || strcmp(fields[1], "record") == 0) rec = 1; else if(strcmp(fields[1], "out") == 0 || strcmp(fields[1], "playback") == 0) rec = 0; else{ dprint(2, "bad1\n"); return Ebadctl; } else if(nf == 2) rec = 0; else{ dprint(2, "bad2 %d\n", nf); return Ebadctl; } c = nil; if(strcmp(fields[0], "audio") == 0) /* special case */ fields[0] = "volume"; for(ctl = 0; ctl < Ncontrol; ctl++){ c = &controls[rec][ctl]; if(strcmp(fields[0], c->name) == 0) break; } if(ctl == Ncontrol){ dprint(2, "bad3\n"); return Ebadctl; } if(f->dir == &dirs[Qvolume] && ctl != Speed_control && c->min != Undef && c->max != Undef){ nnf = tokenize(fields[nf-1], subfields, nelem(subfields)); if(nnf <= 0 || nnf > 8){ dprint(2, "bad4\n"); return Ebadctl; } p = buf; for(i = 0; i < nnf; i++){ value = strtol(subfields[i], nil, 0); value = ((100 - value)*c->min + value*c->max) / 100; if(p == buf){ dprint(2, "rwrite: %s %s '%ld", c->name, rec? "record": "playback", value); }else dprint(2, " %ld", value); if(p == buf) p = seprint(p, buf+sizeof buf, "0x%p %s %s '%ld", replchan, c->name, rec? "record": "playback", value); else p = seprint(p, buf+sizeof buf, " %ld", value); } dprint(2, "'\n"); seprint(p, buf+sizeof buf-1, "'"); chanprint(controlchan, buf); }else{ dprint(2, "rwrite: %s %s %q", c->name, rec? "record": "playback", fields[nf-1]); chanprint(controlchan, "0x%p %s %s %q", replchan, c->name, rec? "record": "playback", fields[nf-1]); } p = recvp(replchan); if(p){ if(strcmp(p, "ok") == 0){ free(p); p = nil; } if(err == nil) err = p; } } for(w = workers; w; w = w->next) nbsendul(w->eventc, Qaudioctl << 16 | Check); rhdr.count = thdr.count; return err; } return Eperm; } char * rclunk(Fid *f) { Audioctldata *a; qlock(f); f->flags &= ~(Open|Busy); assert(f->readers ==0); if(f->fiddata){ a = f->fiddata; if(a->s) free(a->s); free(a); f->fiddata = nil; } qunlock(f); return 0; } char * rremove(Fid *) { return Eperm; } char * rstat(Fid *f) { Audioctldata *a; if(f->dir == &dirs[Qaudioctl]){ qlock(f); if(f->fiddata == nil) f->fiddata = allocaudioctldata(); a = f->fiddata; if(a->ns == 0) makeaudioctldata(f); f->dir->length = a->offoff + a->ns; qunlock(f); } rhdr.nstat = convD2M(f->dir, mbuf, messagesize - IOHDRSZ); rhdr.stat = mbuf; return 0; } char * rwstat(Fid*) { return Eperm; } Fid * newfid(int fid) { Fid *f, *ff; ff = nil; for(f = fids; f; f = f->next) if(f->fid == fid) return f; else if(ff == nil && (f->flags & Busy) == 0) ff = f; if(ff == nil){ ff = emallocz(sizeof *ff, 1); ff->next = fids; fids = ff; } ff->fid = fid; ff->flags &= ~(Busy|Open); ff->dir = nil; return ff; } void io(void *) { char *err, e[32]; int n; close(p[1]); procchan = chancreate(sizeof(Channel*), 8); replchan = chancreate(sizeof(char*), 0); for(;;){ /* * reading from a pipe or a network device * will give an error after a few eof reads * however, we cannot tell the difference * between a zero-length read and an interrupt * on the processes writing to us, * so we wait for the error */ n = read9pmsg(mfd[0], mdata, messagesize); if(n == 0) continue; if(n < 0){ rerrstr(e, sizeof e); if(strcmp(e, "interrupted") == 0){ dprint(2, "read9pmsg interrupted\n"); continue; } return; } if(convM2S(mdata, n, &thdr) == 0) continue; ddprint(2, "io:<-%F\n", &thdr); rhdr.data = (char*)mdata + messagesize; if(!fcalls[thdr.type]) err = "bad fcall type"; else err = (*fcalls[thdr.type])(newfid(thdr.fid)); if(err == (char*)~0) continue; /* handled off line */ if(err){ rhdr.type = Rerror; rhdr.ename = err; }else{ rhdr.type = thdr.type + 1; rhdr.fid = thdr.fid; } rhdr.tag = thdr.tag; ddprint(2, "io:->%F\n", &rhdr); n = convS2M(&rhdr, mdata, messagesize); if(write(mfd[1], mdata, n) != n) sysfatal("mount write"); } } int newid(void) { int rv; static int id; static Lock idlock; lock(&idlock); rv = ++id; unlock(&idlock); return rv; } void ctlevent(void) { Worker *w; for(w = workers; w; w = w->next) nbsendul(w->eventc, Qaudioctl << 16 | Check); } ong *actls; /* 8 of them */ char *p, *e; Audiocontrol *c; Audioctldata *a; if((a = f->fiddata) == nil) sysfatal("fiddata"); if((p = a->s) == nil) a->s = p = emallocz(Chunk, 0); e = p + Chunk - 1; /* eusb/audio/audiosub.c 664 0 0 17517 11152121045 12560ustar00nemosys#include #include #include #include "usb.h" #include "audio.h" #include "audioctl.h" typedef struct Namelist Namelist; struct Namelist { short index; char *name; }; Namelist terminal_types[] = { { 0x100, "USB Terminal, undefined type"}, { 0x101, "USB Streaming"}, { 0x201, "Microphone"}, { 0x301, "Speaker"}, { 0x603, "Line connector"}, { 0x605, "S/PDIF"}, { 0, nil } }; units[2][8]; /* rec and play units */ nunits[2]; /* number in use */ char * namefor(Namelist *list, int item) { while(list->name){ if(list->index == item) return list->name; list++; } return ""; } static int findunit(int nr) { int rec, i; for(rec = 0; rec < 2; rec++) for(i = 0; i < nunits[rec]; i++) if(units[rec][i] == nr) return rec; return -1; } void audio_interface(Dev *, Desc *dd) { byte *b = (uchar*)&dd->data; byte *bb = b; int nb = dd->data.bLength; int ctl, ch, u, x; byte *p; int class, subclass; Audioalt *aa; char *hd; class = Class(dd->iface->csp); subclass = Subclass(dd->iface->csp); dprint(2, "%d.%d: ", class, subclass); switch (subclass){ case 1: // control switch (b[2]){ case 0x01: dprint(2, "Class-Specific AC Interface Header Descriptor\n"); dprint(2, "\tAudioDevClass release (bcd)%c%c%c%c, " "TotalLength %d, InCollection %d aInterfaceNr %d\n", '0'+((b[4]>>4)&0xf), '0'+(b[4]&0xf), '0'+((b[3]>>4)&0xf), '0'+(b[3]&0xf), b[5]|(b[6]<<8), b[7], b[8]); break; case 0x02: // input dprint(2, "Audio Input Terminal Descriptor\n"); dprint(2, "\tbTerminalId %d, wTerminalType " "0x%x (%s), bAssocTerminal %d bNrChannels %d, " "wChannelConfig %d, iChannelNames %d iTerminal %d\n", b[3], b[4]|(b[5]<<8), namefor(terminal_types, b[4]|(b[5]<<8)), b[6], b[7], b[8]|(b[9]<<8), b[10], b[11]); if((b[4]|b[5]<<8) == 0x101){ if(verbose) fprint(2, "Audio output unit %d\n", b[3]); /* USB streaming input: play interface */ units[Play][nunits[Play]++] = b[3]; }else{ if(verbose) fprint(2, "Dev can record from %s\n", namefor(terminal_types, b[4]|(b[5]<<8))); /* Non-USB input: record interface */ units[Record][nunits[Record]++] = b[3]; } break; case 0x03: // output if(usbdebug){ fprint(2, "Audio Output Terminal Descriptor\n"); fprint(2, "\tbTerminalId %d, wTerminalType 0x%x (%s), bAssocTerminal %d bSourceId %d, iTerminal %d\n", b[3], b[4]|(b[5]<<8), namefor(terminal_types, b[4]|(b[5]<<8)), b[6], b[7], b[8]); } if((b[4]|b[5]<<8) == 0x101){ if(verbose) fprint(2, "Audio input unit %d\n", b[3]); /* USB streaming output: record interface */ units[Record][nunits[Record]++] = b[3]; if(verbose) fprint(2, "Dev can play to %s\n", namefor(terminal_types, b[4]|(b[5]<<8))); /* Non-USB output: play interface */ units[Play][nunits[Play]++] = b[3]; } break; case 0x04: if(verbose) fprint(2, "Audio Mixer Unit %d\n", b[3]); if(usbdebug){ fprint(2, "\t%d bytes:", nb); for(ctl = 0; ctl < nb; ctl++) fprint(2, " 0x%2.2x", b[ctl]); fprint(2, "\n\tbUnitId %d, bNrInPins %d", b[3], b[4]); } if(b[4]){ dprint(2, ", baSourceIDs: [%d", b[5]); u = findunit(b[5]); for(ctl = 1; ctl < b[4]; ctl++){ if(u < 0) u = findunit(b[5+ctl]); else if((x = findunit(b[5+ctl])) >= 0 && u != x && verbose) fprint(2, "\tMixer %d for I & O \n", b[3]); dprint(2, ", %d", b[5+ctl]); } dprint(2, "]\n"); if(u >= 0){ units[u][nunits[u]++] = b[3]; if(mixerid[u] >= 0) fprint(2, "Second mixer (%d, %d) on %s\n", mixerid[u], b[3], u?"record":"playback"); mixerid[u] = b[3]; } if(usbdebug){ fprint(2, "Channels %d, config %d, ", b[ctl+5], b[ctl+5+1] | b[ctl+5+2] << 8); x = b[ctl+5] * b[4]; fprint(2, "programmable: %d bits, 0x", x); x = (x + 7) >> 3; while(x--) fprint(2, "%2.2x", b[ctl+x+5+4]); } } break; case 0x05: if(verbose) fprint(2, "Audio Selector Unit %d\n", b[3]); dprint(2, "\tbUnitId %d, bNrInPins %d", b[3], b[4]); if(b[4]){ u = findunit(b[5]); dprint(2, ", baSourceIDs: %s [%d", u?"record":"playback", b[5]); for(ctl = 1; ctl < b[4]; ctl++){ if(u < 0) u = findunit(b[5+ctl]); else if((x = findunit(b[5+ctl])) >= 0 && u != x && verbose) fprint(2, "\tSelector %d for I & O\n", b[3]); dprint(2, ", %d", b[5+ctl]); } dprint(2, "]\n"); if(u >= 0){ units[u][nunits[u]++] = b[3]; if(selectorid[u] >= 0) fprint(2, "Second selector (%d, %d) on %s\n", selectorid[u], b[3], u?"record":"playback"); selectorid[u] = b[3]; controls[u][Selector_control].readable = 1; controls[u][Selector_control].settable = 1; controls[u][Selector_control].chans = 0; } } break; case 0x06: // feature if(verbose) fprint(2, "Audio Feature Unit %d", b[3]); dprint(2, "\tbUnitId %d, bSourceId %d, bControlSize %d\n", b[3], b[4], b[5]); u = findunit(b[4]); if(u >= 0){ if(verbose) fprint(2, " for %s\n", u?"Record":"Playback"); units[u][nunits[u]++] = b[3]; if(featureid[u] >= 0) if(verbose) fprint(2, "Second feature unit (%d, %d)" " on %s\n", featureid[u], b[3], u?"record":"playback"); featureid[u] = b[3]; }else if(verbose) fprint(2, ", not known what for\n"); p = b + 6; for(ctl = 1; ctl < 0x0b; ctl++) if((1<<(ctl-1)) & (b[6] | ((b[5]>1)?(b[7]<<8):0))){ if(verbose) fprint(2, "\t%s control on master channel\n", controls[0][ctl].name); if(u >= 0){ controls[u][ctl].readable = 1; controls[u][ctl].settable = 1; controls[u][ctl].chans = 0; } } p += (b[5]>1)?2:1; for(ch = 0; ch < (nb - 8)/b[5]; ch++){ for(ctl = 1; ctl < 0x0b; ctl++) if((1<<(ctl-1)) & (p[0] | ((b[5]>1)?(p[1]<<8):0))){ if(verbose) fprint(2, "\t%s control on channel %d\n", controls[0][ctl].name, ch+1); if(u >= 0){ controls[u][ctl].readable = 1; controls[u][ctl].settable = 1; controls[u][ctl].chans |= 1 <<(ch+1); } } p += (b[5]>1)?2:1; } break; default: hd = hexstr(bb, nb); fprint(2, "audio control unknown: %s\n", hd); free(hd); } break; case 2: // stream switch (b[2]){ case 0x01: dprint(2, "Audio stream for TerminalID %d, delay %d, format_tag %#ux\n", b[3], b[4], b[5] | (b[6]<<8)); break; case 0x02: aa = (Audioalt *)dd->altc->aux; if(aa == nil){ aa = mallocz(sizeof(Audioalt), 1); dd->altc->aux = aa; } if(verbose){ if(b[4] <= 2) fprint(2, "Interface %d: %s, %d bits, ", dd->iface->id, (b[4] == 1)?"mono":"stereo", b[6]); else fprint(2, "Interface %d, %d channels, %d bits, ", dd->iface->id, b[4], b[6]); } if(b[7] == 0){ if(verbose) fprint(2, "frequency variable between %d and %d\n", b[8] | b[9]<<8 | b[10]<<16, b[11] | b[12]<<8 | b[13]<<16); aa->minfreq = b[8] | b[9]<<8 | b[10]<<16; aa->maxfreq = b[11] | b[12]<<8 | b[13]<<16; aa->caps |= has_contfreq; }else{ if(verbose) fprint(2, "discrete frequencies are:"); for(ch = 0; ch < b[7] && ch < 8; ch++){ aa->freqs[ch] = b[8+3*ch] | b[9+3*ch]<<8 | b[10+3*ch]<<16; if(verbose) fprint(2, " %d", b[8+3*ch] | b[9+3*ch]<<8 | b[10+3*ch]<<16); } if(ch < 8) aa->freqs[ch] = -1; if(verbose) fprint(2, "\n"); if(ch > 1) aa->caps |= has_discfreq; /* more than one frequency */ else aa->caps |= onefreq; /* only one frequency */ } aa->nchan = b[4]; aa->res = b[6]; aa->subframesize = b[5]; break; default: if(usbdebug){ hd = hexstr(bb, nb); fprint(2, "audio stream unknown: %s\n", hd); free(hd); } } break; case 3: // midi default: if(usbdebug){ hd = hexstr(bb, nb); fprint(2, "Unknown audio stream type: CS_INTERFACE: %s\n", hd); free(hd); } } } usb/audio/mkfile 664 0 0 516 11152121046 11723ustar00nemosys #include #include #include #include #include "scsireq.h" #include "usb.h" #include "usbfs.h" #include "ums.h" enum { Qdir = 0, Qctl, Qraw, Qdata, Qmax, }; typedef struct Dirtab Dirtab; struct Dirtab { char *name; int mode; }; static Dirtab dirtab[] = { [Qdir] "/", DMDIR|0555, [Qctl] "ctl", 0444, [Qraw] "raw", 0640, [Qdata] "data", 0640, }; /* * These are used by scuzz scsireq */ int exabyte, force6bytecmds; long maxiosize = MaxIOsize; static int diskdebug; static int getmaxlun(Dev *dev) { uchar max; int r; max = 0; r = Rd2h|Rclass|Riface; if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){ dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir); }else dprint(2, "disk: %s: maxlun %d\n", dev->dir, max); return max; } static int umsreset(Ums *ums) { int r; r = Rh2d|Rclass|Riface; if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){ fprint(2, "disk: reset: %r\n"); return -1; } return 0; } static int umsrecover(Ums *ums) { if(umsreset(ums) < 0) return -1; if(unstall(ums->dev, ums->epin, Ein) < 0) dprint(2, "disk: unstall epin: %r\n"); /* do we need this when epin == epout? */ if(unstall(ums->dev, ums->epout, Eout) < 0) dprint(2, "disk: unstall epout: %r\n"); return 0; } static void umsfatal(Ums *ums) { int i; devctl(ums->dev, "detach"); for(i = 0; i < ums->maxlun; i++) usbfsdel(&ums->lun[i].fs); } static int umscapacity(Umsc *lun) { uchar data[32]; lun->blocks = 0; lun->capacity = 0; lun->lbsize = 0; if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data) < 0) return -1; lun->blocks = GETBELONG(data); lun->lbsize = GETBELONG(data+4); if(lun->blocks == 0xFFFFFFFF){ if(SRrcapacity16(lun, data) < 0){ lun->lbsize = 0; lun->blocks = 0; return -1; }else{ lun->lbsize = GETBELONG(data + 8); lun->blocks = (uvlong)GETBELONG(data)<<32 | GETBELONG(data + 4); } } lun->blocks++; /* SRcapacity returns LBA of last block */ lun->capacity = (vlong)lun->blocks * lun->lbsize; return 0; } static int umsinit(Ums *ums) { uchar i; Umsc *lun; int some; umsreset(ums); ums->maxlun = getmaxlun(ums->dev); ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1); some = 0; for(i = 0; i <= ums->maxlun; i++){ lun = &ums->lun[i]; lun->ums = ums; lun->umsc = lun; lun->lun = i; lun->flags = Fopen | Fusb | Frw10; if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0) continue; if(lun->inquiry[0] != 0x00){ /* not a disk */ fprint(2, "%s: lun %d is not a disk (type %#02x)\n", argv0, i, lun->inquiry[0]); continue; } SRstart(lun, 1); /* * we ignore the device type reported by inquiry. * Some devices return a wrong value but would still work. */ some++; lun->inq = smprint("%.48s", (char *)lun->inquiry+8); umscapacity(lun); } if(some == 0){ devctl(ums->dev, "detach"); return -1; } return 0; } /* * called by SR*() commands provided by scuzz's scsireq */ long umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status) { Cbw cbw; Csw csw; int n; Ums *ums; ums = umsc->ums; memcpy(cbw.signature, "USBC", 4); cbw.tag = ++ums->seq; cbw.datalen = data->count; cbw.flags = data->write? CbwDataOut: CbwDataIn; cbw.lun = umsc->lun; if(cmd->count < 1 || cmd->count > 16) print("%s: umsrequest: bad cmd count: %ld\n", argv0, cmd->count); cbw.len = cmd->count; assert(cmd->count <= sizeof(cbw.command)); memcpy(cbw.command, cmd->p, cmd->count); memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count); werrstr(""); /* we use %r later even for n == 0 */ if(diskdebug){ fprint(2, "disk: cmd: tag %#lx: ", cbw.tag); for(n = 0; n < cbw.len; n++) fprint(2, " %2.2x", cbw.command[n]&0xFF); fprint(2, " datalen: %ld\n", cbw.datalen); } if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){ fprint(2, "disk: cmd: %r\n"); goto Fail; } if(data->count != 0){ if(data->write) n = write(ums->epout->dfd, data->p, data->count); else{ memset(data->p, data->count, 0); n = read(ums->epin->dfd, data->p, data->count); } if(diskdebug) if(n < 0) fprint(2, "disk: data: %r\n"); else fprint(2, "disk: data: %d bytes\n", n); if(n <= 0) if(data->write == 0) unstall(ums->dev, ums->epin, Ein); } n = read(ums->epin->dfd, &csw, CswLen); if(n <= 0){ /* n == 0 means "stalled" */ unstall(ums->dev, ums->epin, Ein); n = read(ums->epin->dfd, &csw, CswLen); } if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){ dprint(2, "disk: read n=%d: status: %r\n", n); goto Fail; } if(csw.tag != cbw.tag){ dprint(2, "disk: status tag mismatch\n"); goto Fail; } if(csw.status >= CswPhaseErr){ dprint(2, "disk: phase error\n"); goto Fail; } if(diskdebug){ fprint(2, "status: %2.2ux residue: %ld\n", csw.status, csw.dataresidue); if(cbw.command[0] == ScmdRsense){ fprint(2, "sense data:"); for(n = 0; n < data->count - csw.dataresidue; n++) fprint(2, " %2.2x", data->p[n]); fprint(2, "\n"); } } switch(csw.status){ case CswOk: *status = STok; break; case CswFailed: *status = STcheck; break; default: dprint(2, "disk: phase error\n"); goto Fail; } ums->nerrs = 0; return data->count - csw.dataresidue; Fail: *status = STharderr; if(ums->nerrs++ > 15){ fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir); umsfatal(ums); }else umsrecover(ums); return -1; } static int dwalk(Usbfs *fs, Fid *fid, char *name) { int i; Qid qid; qid = fid->qid; if((qid.type & QTDIR) == 0){ werrstr("walk in non-directory"); return -1; } if(strcmp(name, "..") == 0) return 0; for(i = 1; i < nelem(dirtab); i++) if(strcmp(name, dirtab[i].name) == 0){ qid.path = i | fs->qid; qid.vers = 0; qid.type = dirtab[i].mode >> 24; fid->qid = qid; return 0; } werrstr(Enotfound); return -1; } static void dostat(Usbfs *fs, int path, Dir *d) { Dirtab *t; Umsc *lun; t = &dirtab[path]; d->qid.path = path; d->qid.type = t->mode >> 24; d->mode = t->mode; d->name = t->name; lun = fs->aux; if(path == Qdata) d->length = lun->capacity; else d->length = 0; } static int dirgen(Usbfs *fs, Qid, int i, Dir *d, void*) { i++; /* skip dir */ if(i >= Qmax) return -1; else{ dostat(fs, i, d); d->qid.path |= fs->qid; return 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; Umsc *lun; path = fid->qid.path & ~fs->qid; lun = fs->aux; switch(path){ case Qraw: lun->phase = Pcmd; break; } return 0; } /* * Upon SRread/SRwrite errors we assume the medium may have changed, * and ask again for the capacity of the media. * BUG: How to proceed to avoid confussing dossrv?? */ static long dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset) { long bno, nb, len, off, n; ulong path; char buf[1024], *p; char *s; char *e; Umsc *lun; Ums *ums; Qid q; q = fid->qid; path = fid->qid.path & ~fs->qid; ums = fs->dev->aux; lun = fs->aux; qlock(ums); switch(path){ case Qdir: count = usbdirread(fs, q, data, count, offset, dirgen, nil); break; case Qctl: e = buf + sizeof(buf); s = seprint(buf, e, "%s lun %ld: ", fs->dev->dir, lun - &ums->lun[0]); if(lun->flags & Finqok) s = seprint(s, e, "inquiry %s ", lun->inq); if(lun->blocks > 0) s = seprint(s, e, "geometry %llud %ld", lun->blocks, lun->lbsize); s = seprint(s, e, "\n"); count = usbreadbuf(data, count, offset, buf, s - buf); break; case Qraw: if(lun->lbsize <= 0 && umscapacity(lun) < 0){ qunlock(ums); return -1; } switch(lun->phase){ case Pcmd: qunlock(ums); werrstr("phase error"); return -1; case Pdata: lun->data.p = (uchar*)data; lun->data.count = count; lun->data.write = 0; count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); lun->phase = Pstatus; if(count < 0){ lun->lbsize = 0; /* medium may have changed */ qunlock(ums); return -1; } break; case Pstatus: n = snprint(buf, sizeof buf, "%11.0ud ", lun->status); count = usbreadbuf(data, count, 0LL, buf, n); lun->phase = Pcmd; break; } break; case Qdata: if(lun->lbsize <= 0 && umscapacity(lun) < 0){ qunlock(ums); return -1; } bno = offset / lun->lbsize; nb = (offset + count + lun->lbsize - 1) / lun->lbsize - bno; if(bno + nb > lun->blocks) nb = lun->blocks - bno; if(bno >= lun->blocks || nb == 0){ count = 0; break; } if(nb * lun->lbsize > maxiosize) nb = maxiosize / lun->lbsize; p = emallocz(nb * lun->lbsize, 0); /* could use a static buffer */ lun->offset = offset / lun->lbsize; n = SRread(lun, p, nb * lun->lbsize); if(n < 0){ free(p); lun->lbsize = 0; /* medium may have changed */ qunlock(ums); return -1; } len = count; off = offset % lun->lbsize; if(off + len > n) len = n - off; count = len; memmove(data, p + off, len); free(p); break; } qunlock(ums); return count; } static long dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong offset) { int bno, nb, len, off; ulong path; char *p; Ums *ums; Umsc *lun; char *data; ums = fs->dev->aux; lun = fs->aux; path = fid->qid.path & ~fs->qid; data = buf; qlock(ums); switch(path){ default: qunlock(ums); werrstr(Eperm); return -1; case Qraw: if(lun->lbsize <= 0 && umscapacity(lun) < 0){ qunlock(ums); return -1; } switch(lun->phase){ case Pcmd: if(count != 6 && count != 10){ qunlock(ums); werrstr("bad command length"); return -1; } memmove(lun->rawcmd, data, count); lun->cmd.p = lun->rawcmd; lun->cmd.count = count; lun->cmd.write = 1; lun->phase = Pdata; break; case Pdata: lun->data.p = (uchar*)data; lun->data.count = count; lun->data.write = 1; count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); lun->phase = Pstatus; if(count < 0){ lun->lbsize = 0; /* medium may have changed */ qunlock(ums); return -1; } break; case Pstatus: lun->phase = Pcmd; qunlock(ums); werrstr("phase error"); return -1; } break; case Qdata: if(lun->lbsize <= 0 && umscapacity(lun) < 0){ qunlock(ums); return -1; } bno = offset / lun->lbsize; nb = (offset + count + lun->lbsize-1) / lun->lbsize - bno; if(bno + nb > lun->blocks) nb = lun->blocks - bno; if(bno >= lun->blocks || nb == 0){ count = 0; break; } if(nb * lun->lbsize > maxiosize) nb = maxiosize / lun->lbsize; p = emallocz(nb * lun->lbsize, 0); off = offset % lun->lbsize; len = count; if(off || (len % lun->lbsize) != 0){ lun->offset = offset / lun->lbsize; count = SRread(lun, p, nb * lun->lbsize); if(count < 0){ free(p); lun->lbsize = 0; /* medium may have changed */ qunlock(ums); return -1; } if(off + len > count) len = count - off; } memmove(p+off, data, len); lun->offset = offset / lun->lbsize; count = SRwrite(lun, p, nb * lun->lbsize); if(count < 0){ free(p); lun->lbsize = 0; /* medium may have changed */ qunlock(ums); return -1; } if(off+len > count) len = count - off; count = len; free(p); break; } qunlock(ums); return count; } int findendpoints(Ums *ums) { Ep *ep; Usbdev *ud; ulong csp; ulong sc; int i; int epin, epout; epin = epout = -1; ud = ums->dev->usb; for(i = 0; i < nelem(ud->ep); i++){ if((ep = ud->ep[i]) == nil) continue; csp = ep->iface->csp; sc = Subclass(csp); if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk))) continue; if(sc != Subatapi && sc != Sub8070 && sc != Subscsi) fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc); if(ep->type == Ebulk){ if(ep->dir == Eboth || ep->dir == Ein) if(epin == -1) epin = ep->id; if(ep->dir == Eboth || ep->dir == Eout) if(epout == -1) epout = ep->id; } } dprint(2, "disk: ep ids: in %d out %d\n", epin, epout); if(epin == -1 || epout == -1) return -1; ums->epin = openep(ums->dev, epin); if(ums->epin == nil){ fprint(2, "disk: openep %d: %r\n", epin); return -1; } if(epout == epin){ incref(ums->epin); ums->epout = ums->epin; }else ums->epout = openep(ums->dev, epout); if(ums->epout == nil){ fprint(2, "disk: openep %d: %r\n", epout); closedev(ums->epin); return -1; } if(ums->epin == ums->epout) opendevdata(ums->epin, ORDWR); else{ opendevdata(ums->epin, OREAD); opendevdata(ums->epout, OWRITE); } if(ums->epin->dfd < 0 || ums->epout->dfd < 0){ fprint(2, "disk: open i/o ep data: %r\n"); closedev(ums->epin); closedev(ums->epout); return -1; } dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir); devctl(ums->epin, "timeout 2000"); devctl(ums->epout, "timeout 2000"); if(usbdebug > 1 || diskdebug > 2){ devctl(ums->epin, "debug 1"); devctl(ums->epout, "debug 1"); devctl(ums->dev, "debug 1"); } return 0; } static int usage(void) { werrstr("usage: usb/disk [-d]"); return -1; } static void umsdevfree(void *a) { Ums *ums = a; if(ums == nil) return; closedev(ums->epin); closedev(ums->epout); ums->epin = ums->epout = nil; free(ums->lun); free(ums); } static Usbfs diskfs = { .walk = dwalk, .open = dopen, .read = dread, .write = dwrite, .stat = dstat, }; int diskmain(Dev *dev, int argc, char **argv) { Ums *ums; Umsc *lun; int i; ARGBEGIN{ case 'd': scsidebug(diskdebug); diskdebug++; break; default: return usage(); }ARGEND if(argc != 0) return usage(); ums = dev->aux = emallocz(sizeof(Ums), 1); ums->maxlun = -1; ums->dev = dev; dev->free = umsdevfree; if(findendpoints(ums) < 0){ werrstr("disk: endpoints not found"); return -1; } if(umsinit(ums) < 0){ dprint(2, "disk: umsinit: %r\n"); return -1; } for(i = 0; i <= ums->maxlun; i++){ lun = &ums->lun[i]; lun->fs = diskfs; snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", dev->id, i); lun->fs.dev = dev; incref(dev); lun->fs.aux = lun; usbfsadd(&lun->fs); } closedev(dev); return 0; } if(data->count != 0){ usb/disk/main.c 664 0 0 2125 11176074553 11510ustar00nemosys/* * usb/disk - usb mass storage file server */ #include #include #include #include #include "scsireq.h" #include "usb.h" #include "usbfs.h" #include "ums.h" enum { Arglen = 80, }; static void usage(void) { fprint(2, "usage: %s [-Dd] [-m mnt] [-s srv] [dev...]\n", argv0); threadexitsall("usage"); } static int csps[] = { CSP(Clstorage,Subatapi,Protobulk), CSP(Clstorage,Sub8070,Protobulk), CSP(Clstorage,Subscsi,Protobulk), 0, }; void threadmain(int argc, char **argv) { char args[Arglen]; char *as; char *ae; char *srv; char *mnt; srv = nil; mnt = "/n/disk"; quotefmtinstall(); ae = args+sizeof(args); as = seprint(args, ae, "disk"); ARGBEGIN{ case 'D': usbfsdebug++; break; case 'd': usbdebug++; as = seprint(as, ae, " -d"); break; case 'm': mnt = EARGF(usage()); break; case 's': srv = EARGF(usage()); default: usage(); }ARGEND rfork(RFNOTEG); threadsetgrp(threadid()); fmtinstall('U', Ufmt); usbfsinit(srv, mnt, &usbdirfs, MBEFORE); startdevs(args, argv, argc, matchdevcsp, csps, diskmain); threadexits(nil); } IR) == 0){ werrstr("walk in non-directory"); return -1; } if(strcmp(name, "..") == 0) return 0; for(i = 1; i < nelem(dirtab); i++) if(strcmp(name, dirtab[i].name) == 0){ qid.path = i | fs->qid; qid.vers = 0; qid.type = dirtab[i].mode >> 24; fid->qid = qid; return 0; } werrstr(Enotfound); return -1; } static void dostat(Usbfs *fs, int path, Dir *d) { Dirtab *t; Umsc *lun; t = &dirtab[pathusb/disk/mkfile 664 0 0 747 11200127040 11554ustar00nemosysscsierrs.c usb/disk/mkscsierrs 775 0 0 643 11200127524 12477ustar00nemosys#!/bin/rc cat < #include typedef struct Err Err; struct Err { int n; char *s; }; static Err scsierrs[] = { EOF grep '^[0-9a-c][0-9a-c][0-9a-c][0-9a-c][ ]' /sys/lib/scsicodes | sed -e 's/^(....) (.*)/ {0x\1, "\2"},\n/' cat < #include typedef struct Err Err; struct Err { int n; char *s; }; static Err scsierrs[] = { {0x0000, "no additional sense information"}, {0x0001, "filemark detected"}, {0x0002, "end-of-partition/medium detected"}, {0x0003, "setmark detected"}, {0x0004, "beginning-of-partition/medium detected"}, {0x0005, "end-of-data detected"}, {0x0006, "i/o process terminated"}, {0x0011, "audio play operation in progress"}, {0x0012, "audio play operation paused"}, {0x0013, "audio play operation successfully completed"}, {0x0014, "audio play operation stopped due to error"}, {0x0015, "no current audio status to return"}, {0x0016, "operation in progress"}, {0x0017, "cleaning requested"}, {0x0100, "no index/sector signal"}, {0x0200, "no seek complete"}, {0x0300, "peripheral device write fault"}, {0x0301, "no write current"}, {0x0302, "excessive write errors"}, {0x0400, "logical unit not ready, cause not reportable"}, {0x0401, "logical unit is in process of becoming ready"}, {0x0402, "logical unit not ready, initializing cmd. required"}, {0x0403, "logical unit not ready, manual intervention required"}, {0x0404, "logical unit not ready, format in progress"}, {0x0405, "logical unit not ready, rebuild in progress"}, {0x0406, "logical unit not ready, recalculation in progress"}, {0x0407, "logical unit not ready, operation in progress"}, {0x0408, "logical unit not ready, long write in progress"}, {0x0409, "logical unit not ready, self-test in progress"}, {0x0410, "auxiliary memory code 2 (99-148) [proposed]"}, {0x0500, "logical unit does not respond to selection"}, {0x0600, "no reference position found"}, {0x0700, "multiple peripheral devices selected"}, {0x0800, "logical unit communication failure"}, {0x0801, "logical unit communication time-out"}, {0x0802, "logical unit communication parity error"}, {0x0803, "logical unit communication crc error (ultra-dma/32)"}, {0x0804, "unreachable copy target"}, {0x0900, "track following error"}, {0x0901, "tracking servo failure"}, {0x0902, "focus servo failure"}, {0x0903, "spindle servo failure"}, {0x0904, "head select fault"}, {0x0a00, "error log overflow"}, {0x0b00, "warning"}, {0x0b01, "warning - specified temperature exceeded"}, {0x0b02, "warning - enclosure degraded"}, {0x0c00, "write error"}, {0x0c01, "write error - recovered with auto reallocation"}, {0x0c02, "write error - auto reallocation failed"}, {0x0c03, "write error - recommend reassignment"}, {0x0c04, "compression check miscompare error"}, {0x0c05, "data expansion occurred during compression"}, {0x0c06, "block not compressible"}, {0x0c07, "write error - recovery needed"}, {0x0c08, "write error - recovery failed"}, {0x0c09, "write error - loss of streaming"}, {0x0c0a, "write error - padding blocks added"}, {0x0c0b, "auxiliary memory code 4 (99-148) [proposed]"}, {0x1000, "id crc or ecc error"}, {0x1100, "unrecovered read error"}, {0x1101, "read retries exhausted"}, {0x1102, "error too long to correct"}, {0x1103, "multiple read errors"}, {0x1104, "unrecovered read error - auto reallocate failed"}, {0x1105, "l-ec uncorrectable error"}, {0x1106, "circ unrecovered error"}, {0x1107, "data re-synchronization error"}, {0x1108, "incomplete block read"}, {0x1109, "no gap found"}, {0x110a, "miscorrected error"}, {0x110b, "unrecovered read error - recommend reassignment"}, {0x110c, "unrecovered read error - recommend rewrite the data"}, {0x1110, "error reading isrc number"}, {0x1111, "read error - loss of streaming"}, {0x1112, "auxiliary memory code 3 (99-148) [proposed]"}, {0x1200, "address mark not found for id field"}, {0x1300, "address mark not found for data field"}, {0x1400, "recorded entity not found"}, {0x1401, "record not found"}, {0x1402, "filemark or setmark not found"}, {0x1403, "end-of-data not found"}, {0x1404, "block sequence error"}, {0x1405, "record not found - recommend reassignment"}, {0x1406, "record not found - data auto-reallocated"}, {0x1500, "random positioning error"}, {0x1501, "mechanical positioning error"}, {0x1502, "positioning error detected by read of medium"}, {0x1600, "data synchronization mark error"}, {0x1601, "data sync error - data rewritten"}, {0x1602, "data sync error - recommend rewrite"}, {0x1603, "data sync error - data auto-reallocated"}, {0x1604, "data sync error - recommend reassignment"}, {0x1700, "recovered data with no error correction applied"}, {0x1701, "recovered data with retries"}, {0x1702, "recovered data with positive head offset"}, {0x1703, "recovered data with negative head offset"}, {0x1704, "recovered data with retries and/or circ applied"}, {0x1705, "recovered data using previous sector id"}, {0x1706, "recovered data without ecc - data auto-reallocated"}, {0x1707, "recovered data without ecc - recommend reassignment"}, {0x1708, "recovered data without ecc - recommend rewrite"}, {0x1709, "recovered data without ecc - data rewritten"}, {0x1800, "recovered data with error correction applied"}, {0x1801, "recovered data with error corr. & retries applied"}, {0x1802, "recovered data - data auto-reallocated"}, {0x1803, "recovered data with circ"}, {0x1804, "recovered data with l-ec"}, {0x1805, "recovered data - recommend reassignment"}, {0x1806, "recovered data - recommend rewrite"}, {0x1807, "recovered data with ecc - data rewritten"}, {0x1900, "defect list error"}, {0x1901, "defect list not available"}, {0x1902, "defect list error in primary list"}, {0x1903, "defect list error in grown list"}, {0x1a00, "parameter list length error"}, {0x1b00, "synchronous data transfer error"}, {0x1c00, "defect list not found"}, {0x1c01, "primary defect list not found"}, {0x1c02, "grown defect list not found"}, {0x2000, "invalid command operation code"}, {0x2001, "access controls code 1 (99-314) [proposed]"}, {0x2002, "access controls code 2 (99-314) [proposed]"}, {0x2003, "access controls code 3 (99-314) [proposed]"}, {0x2100, "logical block address out of range"}, {0x2101, "invalid element address"}, {0x2200, "illegal function (use 20 00, 24 00, or 26 00)"}, {0x2400, "invalid field in cdb"}, {0x2401, "cdb decryption error"}, {0x2500, "logical unit not supported"}, {0x2600, "invalid field in parameter list"}, {0x2601, "parameter not supported"}, {0x2602, "parameter value invalid"}, {0x2603, "threshold parameters not supported"}, {0x2604, "invalid release of persistent reservation"}, {0x2605, "data decryption error"}, {0x2606, "too many target descriptors"}, {0x2607, "unsupported target descriptor type code"}, {0x2608, "too many segment descriptors"}, {0x2609, "unsupported segment descriptor type code"}, {0x260a, "unexpected inexact segment"}, {0x260b, "inline data length exceeded"}, {0x260c, "invalid operation for copy source or destination"}, {0x2700, "write protected"}, {0x2701, "hardware write protected"}, {0x2702, "logical unit software write protected"}, {0x2703, "associated write protect"}, {0x2704, "persistent write protect"}, {0x2705, "permanent write protect"}, {0x2800, "not ready to ready change, medium may have changed"}, {0x2801, "import or export element accessed"}, {0x2900, "power on, reset, or bus device reset occurred"}, {0x2901, "power on occurred"}, {0x2902, "scsi bus reset occurred"}, {0x2903, "bus device reset function occurred"}, {0x2904, "device internal reset"}, {0x2905, "transceiver mode changed to single-ended"}, {0x2906, "transceiver mode changed to lvd"}, {0x2a00, "parameters changed"}, {0x2a01, "mode parameters changed"}, {0x2a02, "log parameters changed"}, {0x2a03, "reservations preempted"}, {0x2a04, "reservations released"}, {0x2a05, "registrations preempted"}, {0x2b00, "copy cannot execute since host cannot disconnect"}, {0x2c00, "command sequence error"}, {0x2c01, "too many windows specified"}, {0x2c02, "invalid combination of windows specified"}, {0x2c03, "current program area is not empty"}, {0x2c04, "current program area is empty"}, {0x2c05, "illegal power condition request"}, {0x3000, "incompatible medium installed"}, {0x3001, "cannot read medium - unknown format"}, {0x3002, "cannot read medium - incompatible format"}, {0x3003, "cleaning cartridge installed"}, {0x3004, "cannot write medium - unknown format"}, {0x3005, "cannot write medium - incompatible format"}, {0x3006, "cannot format medium - incompatible medium"}, {0x3007, "cleaning failure"}, {0x3008, "cannot write - application code mismatch"}, {0x3009, "current session not fixated for append"}, {0x3100, "medium format corrupted"}, {0x3101, "format command failed"}, {0x3200, "no defect spare location available"}, {0x3201, "defect list update failure"}, {0x3300, "tape length error"}, {0x3400, "enclosure failure"}, {0x3500, "enclosure services failure"}, {0x3501, "unsupported enclosure function"}, {0x3502, "enclosure services unavailable"}, {0x3503, "enclosure services transfer failure"}, {0x3504, "enclosure services transfer refused"}, {0x3600, "ribbon, ink, or toner failure"}, {0x3700, "rounded parameter"}, {0x3800, "event status notification"}, {0x3802, "esn - power management class event"}, {0x3804, "esn - media class event"}, {0x3806, "esn - device busy class event"}, {0x3900, "saving parameters not supported"}, {0x3a00, "medium not present"}, {0x3a01, "medium not present - tray closed"}, {0x3a02, "medium not present - tray open"}, {0x3a03, "medium not present - loadable"}, {0x3a04, "medium not present - medium auxiliary memory accessible"}, {0x3b00, "sequential positioning error"}, {0x3b01, "tape position error at beginning-of-medium"}, {0x3b02, "tape position error at end-of-medium"}, {0x3b03, "tape or electronic vertical forms unit not ready"}, {0x3b04, "slew failure"}, {0x3b05, "paper jam"}, {0x3b06, "failed to sense top-of-form"}, {0x3b07, "failed to sense bottom-of-form"}, {0x3b08, "reposition error"}, {0x3b09, "read past end of medium"}, {0x3b0a, "read past beginning of medium"}, {0x3b0b, "position past end of medium"}, {0x3b0c, "position past beginning of medium"}, {0x3b11, "medium magazine not accessible"}, {0x3b12, "medium magazine removed"}, {0x3b13, "medium magazine inserted"}, {0x3b14, "medium magazine locked"}, {0x3b15, "medium magazine unlocked"}, {0x3b16, "mechanical positioning or changer error"}, {0x4000, "ram failure (should use 40 nn)"}, {0x4100, "data path failure (should use 40 nn)"}, {0x4200, "power-on or self-test failure (should use 40 nn)"}, {0x4300, "message error"}, {0x4400, "internal target failure"}, {0x4500, "select or reselect failure"}, {0x4600, "unsuccessful soft reset"}, {0x4700, "scsi parity error"}, {0x4701, "data phase crc error detected"}, {0x4702, "scsi parity error detected during st data phase"}, {0x4703, "information unit crc error detected"}, {0x4704, "asynchronous information protection error detected"}, {0x4800, "initiator detected error message received"}, {0x4900, "invalid message error"}, {0x4a00, "command phase error"}, {0x4b00, "data phase error"}, {0x4c00, "logical unit failed self-configuration"}, {0x5000, "write append error"}, {0x5001, "write append position error"}, {0x5002, "position error related to timing"}, {0x5100, "erase failure"}, {0x5200, "cartridge fault"}, {0x5300, "media load or eject failed"}, {0x5301, "unload tape failure"}, {0x5302, "medium removal prevented"}, {0x5400, "scsi to host system interface failure"}, {0x5500, "system resource failure"}, {0x5501, "system buffer full"}, {0x5502, "insufficient reservation resources"}, {0x5503, "insufficient resources"}, {0x5504, "insufficient registration resources"}, {0x5505, "access controls code 4 (99-314) [proposed]"}, {0x5506, "auxiliary memory code 1 (99-148) [proposed]"}, {0x5700, "unable to recover table-of-contents"}, {0x5800, "generation does not exist"}, {0x5900, "updated block read"}, {0x5a00, "operator request or state change input"}, {0x5a01, "operator medium removal request"}, {0x5a02, "operator selected write protect"}, {0x5a03, "operator selected write permit"}, {0x5b00, "log exception"}, {0x5b01, "threshold condition met"}, {0x5b02, "log counter at maximum"}, {0x5b03, "log list codes exhausted"}, {0x5c00, "rpl status change"}, {0x5c01, "spindles synchronized"}, {0x5c02, "spindles not synchronized"}, {0x6000, "lamp failure"}, {0x6100, "video acquisition error"}, {0x6101, "unable to acquire video"}, {0x6102, "out of focus"}, {0x6200, "scan head positioning error"}, {0x6300, "end of user area encountered on this track"}, {0x6301, "packet does not fit in available space"}, {0x6400, "illegal mode for this track"}, {0x6401, "invalid packet size"}, {0x6500, "voltage fault"}, {0x6600, "automatic document feeder cover up"}, {0x6601, "automatic document feeder lift up"}, {0x6602, "document jam in automatic document feeder"}, {0x6603, "document miss feed automatic in document feeder"}, {0x6700, "configuration failure"}, {0x6701, "configuration of incapable logical units failed"}, {0x6702, "add logical unit failed"}, {0x6703, "modification of logical unit failed"}, {0x6704, "exchange of logical unit failed"}, {0x6705, "remove of logical unit failed"}, {0x6706, "attachment of logical unit failed"}, {0x6707, "creation of logical unit failed"}, {0x6708, "assign failure occurred"}, {0x6709, "multiply assigned logical unit"}, {0x6800, "logical unit not configured"}, {0x6900, "data loss on logical unit"}, {0x6901, "multiple logical unit failures"}, {0x6902, "parity/data mismatch"}, {0x6a00, "informational, refer to log"}, {0x6b00, "state change has occurred"}, {0x6b01, "redundancy level got better"}, {0x6b02, "redundancy level got worse"}, {0x6c00, "rebuild failure occurred"}, {0x7100, "decompression exception long algorithm id"}, {0x7200, "session fixation error"}, {0x7201, "session fixation error writing lead-in"}, {0x7202, "session fixation error writing lead-out"}, {0x7203, "session fixation error - incomplete track in session"}, {0x7204, "empty or partially written reserved track"}, {0x7205, "no more track reservations allowed"}, {0x7300, "cd control error"}, {0x7301, "power calibration area almost full"}, {0x7302, "power calibration area is full"}, {0x7303, "power calibration area error"}, {0x7304, "program memory area update failure"}, {0x7305, "program memory area is full"}, {0x7306, "rma/pma is full"}, }; char* scsierrmsg(int n) { int i; for(i = 0; i < nelem(scsierrs); i++) if(scsierrs[i].n == n) return scsierrs[i].s; return "scsi error"; } "recovered data with negative head offset"}, {0x1704, "recovered data with retries and/or circ applied"}, {0x1705, "recovered data using previous sector id"}, {0x1706, "recovered data without ecc - data auto-reallocated"}, {0x1707, "recovered data without ecc - recommend reassignment"}, {0x1708, "recovered data without ecc - recommend rewrite"}, {0x1709, "recovered data without ecc - data rewritten"}, {0x1800, "recovered data with errousb/disk/scsireq.c 664 0 0 44131 11176010273 12245ustar00nemosys/* * This is /sys/src/cmd/scuzz/scsireq.c * changed to add more debug support, to keep * disk compiling without a scuzz that includes these changes. * Also, this includes minor tweaks for usb: * we set req.lun/unit to rp->lun/unit in SRreqsense * we set the rp->sense[0] bit Sd0valid in SRreqsense * This does not use libdisk to retrieve the scsi error to make * user we see the diagnostics if we boot with debug enabled. * */ #include #include /* * BUGS: * no luns * and incomplete in many other ways */ #include "scsireq.h" enum { Debug = 0, }; /* * exabyte tape drives, at least old ones like the 8200 and 8505, * are dumb: you have to read the exact block size on the tape, * they don't take 10-byte SCSI commands, and various other fine points. */ extern int exabyte, force6bytecmds; static int debug = Debug; static char *scmdnames[256] = { [ScmdTur] "Tur", [ScmdRewind] "Rewind", [ScmdRsense] "Rsense", [ScmdFormat] "Format", [ScmdRblimits] "Rblimits", [ScmdRead] "Read", [ScmdWrite] "Write", [ScmdSeek] "Seek", [ScmdFmark] "Fmark", [ScmdSpace] "Space", [ScmdInq] "Inq", [ScmdMselect6] "Mselect6", [ScmdMselect10] "Mselect10", [ScmdMsense6] "Msense6", [ScmdMsense10] "Msense10", [ScmdStart] "Start", [ScmdRcapacity] "Rcapacity", [ScmdRcapacity16] "Rcap16", [ScmdExtread] "Extread", [ScmdExtwrite] "Extwrite", [ScmdExtseek] "Extseek", [ScmdSynccache] "Synccache", [ScmdRTOC] "RTOC", [ScmdRdiscinfo] "Rdiscinfo", [ScmdRtrackinfo] "Rtrackinfo", [ScmdReserve] "Reserve", [ScmdBlank] "Blank", [ScmdCDpause] "CDpause", [ScmdCDstop] "CDstop", [ScmdCDplay] "CDplay", [ScmdCDload] "CDload", [ScmdCDscan] "CDscan", [ScmdCDstatus] "CDstatus", [Scmdgetconf] "getconf", }; long SRready(ScsiReq *rp) { uchar cmd[6]; memset(cmd, 0, sizeof cmd); rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = cmd; rp->data.count = 0; rp->data.write = 1; return SRrequest(rp); } long SRrewind(ScsiReq *rp) { uchar cmd[6]; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdRewind; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = cmd; rp->data.count = 0; rp->data.write = 1; if(SRrequest(rp) >= 0){ rp->offset = 0; return 0; } return -1; } long SRreqsense(ScsiReq *rp) { uchar cmd[6]; ScsiReq req; long status; if(rp->status == Status_SD){ rp->status = STok; return 0; } memset(cmd, 0, sizeof cmd); cmd[0] = ScmdRsense; cmd[4] = sizeof(req.sense); memset(&req, 0, sizeof(req)); if(rp->flags&Fusb) req.flags |= Fusb; req.lun = rp->lun; req.unit = rp->unit; req.fd = rp->fd; req.umsc = rp->umsc; req.cmd.p = cmd; req.cmd.count = sizeof cmd; req.data.p = rp->sense; req.data.count = sizeof(rp->sense); req.data.write = 0; status = SRrequest(&req); rp->status = req.status; if(status != -1) rp->sense[0] |= Sd0valid; return status; } long SRformat(ScsiReq *rp) { uchar cmd[6]; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdFormat; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = cmd; rp->data.count = 6; rp->data.write = 0; return SRrequest(rp); } long SRrblimits(ScsiReq *rp, uchar *list) { uchar cmd[6]; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdRblimits; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = list; rp->data.count = 6; rp->data.write = 0; return SRrequest(rp); } static int dirdevrw(ScsiReq *rp, uchar *cmd, long nbytes) { long n; n = nbytes / rp->lbsize; if(rp->offset <= Max24off && n <= 256 && (rp->flags & Frw10) == 0){ PUTBE24(cmd+1, rp->offset); cmd[4] = n; cmd[5] = 0; return 6; } cmd[0] |= ScmdExtread; cmd[1] = 0; PUTBELONG(cmd+2, rp->offset); cmd[6] = 0; cmd[7] = n>>8; cmd[8] = n; cmd[9] = 0; return 10; } static int seqdevrw(ScsiReq *rp, uchar *cmd, long nbytes) { long n; /* don't set Cmd1sili; we want the ILI bit instead of a fatal error */ cmd[1] = rp->flags&Fbfixed? Cmd1fixed: 0; n = nbytes / rp->lbsize; PUTBE24(cmd+2, n); cmd[5] = 0; return 6; } long SRread(ScsiReq *rp, void *buf, long nbytes) { uchar cmd[10]; long n; if((nbytes % rp->lbsize) || nbytes > maxiosize){ rp->status = Status_BADARG; return -1; } /* set up scsi read cmd */ cmd[0] = ScmdRead; if(rp->flags & Fseqdev) rp->cmd.count = seqdevrw(rp, cmd, nbytes); else rp->cmd.count = dirdevrw(rp, cmd, nbytes); rp->cmd.p = cmd; rp->data.p = buf; rp->data.count = nbytes; rp->data.write = 0; /* issue it */ n = SRrequest(rp); if(n != -1){ /* it worked? */ rp->offset += n / rp->lbsize; return n; } /* request failed; maybe we just read a short record? */ if (exabyte) { fprint(2, "read error\n"); rp->status = STcheck; return n; } if(rp->status != Status_SD || !(rp->sense[0] & Sd0valid)) return -1; /* compute # of bytes not read */ n = GETBELONG(rp->sense+3) * rp->lbsize; if(!(rp->flags & Fseqdev)) return -1; /* device is a tape or something similar */ if (rp->sense[2] == Sd2filemark || rp->sense[2] == 0x08 || rp->sense[2] & Sd2ili && n > 0) rp->data.count = nbytes - n; else return -1; n = rp->data.count; if (!rp->readblock++ || debug) fprint(2, "SRread: tape data count %ld%s\n", n, (rp->sense[2] & Sd2ili? " with ILI": "")); rp->status = STok; rp->offset += n / rp->lbsize; return n; } long SRwrite(ScsiReq *rp, void *buf, long nbytes) { uchar cmd[10]; long n; if((nbytes % rp->lbsize) || nbytes > maxiosize){ rp->status = Status_BADARG; return -1; } /* set up scsi write cmd */ cmd[0] = ScmdWrite; if(rp->flags & Fseqdev) rp->cmd.count = seqdevrw(rp, cmd, nbytes); else rp->cmd.count = dirdevrw(rp, cmd, nbytes); rp->cmd.p = cmd; rp->data.p = buf; rp->data.count = nbytes; rp->data.write = 1; /* issue it */ if((n = SRrequest(rp)) == -1){ if (exabyte) { fprint(2, "write error\n"); rp->status = STcheck; return n; } if(rp->status != Status_SD || rp->sense[2] != Sd2eom) return -1; if(rp->sense[0] & Sd0valid){ n -= GETBELONG(rp->sense+3) * rp->lbsize; rp->data.count = nbytes - n; } else rp->data.count = nbytes; n = rp->data.count; } rp->offset += n / rp->lbsize; return n; } long SRseek(ScsiReq *rp, long offset, int type) { uchar cmd[10]; switch(type){ case 0: break; case 1: offset += rp->offset; if(offset >= 0) break; /*FALLTHROUGH*/ default: rp->status = Status_BADARG; return -1; } memset(cmd, 0, sizeof cmd); if(offset <= Max24off && (rp->flags & Frw10) == 0){ cmd[0] = ScmdSeek; PUTBE24(cmd+1, offset & Max24off); rp->cmd.count = 6; }else{ cmd[0] = ScmdExtseek; PUTBELONG(cmd+2, offset); rp->cmd.count = 10; } rp->cmd.p = cmd; rp->data.p = cmd; rp->data.count = 0; rp->data.write = 1; SRrequest(rp); if(rp->status == STok) return rp->offset = offset; return -1; } long SRfilemark(ScsiReq *rp, ulong howmany) { uchar cmd[6]; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdFmark; PUTBE24(cmd+2, howmany); rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = cmd; rp->data.count = 0; rp->data.write = 1; return SRrequest(rp); } long SRspace(ScsiReq *rp, uchar code, long howmany) { uchar cmd[6]; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdSpace; cmd[1] = code; PUTBE24(cmd+2, howmany); rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = cmd; rp->data.count = 0; rp->data.write = 1; /* * what about rp->offset? */ return SRrequest(rp); } long SRinquiry(ScsiReq *rp) { uchar cmd[6]; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdInq; cmd[4] = sizeof rp->inquiry; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; memset(rp->inquiry, 0, sizeof rp->inquiry); rp->data.p = rp->inquiry; rp->data.count = sizeof rp->inquiry; rp->data.write = 0; if(SRrequest(rp) >= 0){ rp->flags |= Finqok; return 0; } rp->flags &= ~Finqok; return -1; } long SRmodeselect6(ScsiReq *rp, uchar *list, long nbytes) { uchar cmd[6]; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdMselect6; if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2) cmd[1] = 0x10; cmd[4] = nbytes; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = list; rp->data.count = nbytes; rp->data.write = 1; return SRrequest(rp); } long SRmodeselect10(ScsiReq *rp, uchar *list, long nbytes) { uchar cmd[10]; memset(cmd, 0, sizeof cmd); if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2) cmd[1] = 0x10; cmd[0] = ScmdMselect10; cmd[7] = nbytes>>8; cmd[8] = nbytes; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = list; rp->data.count = nbytes; rp->data.write = 1; return SRrequest(rp); } long SRmodesense6(ScsiReq *rp, uchar page, uchar *list, long nbytes) { uchar cmd[6]; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdMsense6; cmd[2] = page; cmd[4] = nbytes; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = list; rp->data.count = nbytes; rp->data.write = 0; return SRrequest(rp); } long SRmodesense10(ScsiReq *rp, uchar page, uchar *list, long nbytes) { uchar cmd[10]; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdMsense10; cmd[2] = page; cmd[7] = nbytes>>8; cmd[8] = nbytes; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = list; rp->data.count = nbytes; rp->data.write = 0; return SRrequest(rp); } long SRstart(ScsiReq *rp, uchar code) { uchar cmd[6]; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdStart; cmd[4] = code; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = cmd; rp->data.count = 0; rp->data.write = 1; return SRrequest(rp); } long SRrcapacity(ScsiReq *rp, uchar *data) { uchar cmd[10]; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdRcapacity; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = data; rp->data.count = 8; rp->data.write = 0; return SRrequest(rp); } long SRrcapacity16(ScsiReq *rp, uchar *data) { uchar cmd[16]; uint i; i = 32; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdRcapacity16; cmd[1] = 0x10; cmd[10] = i>>24; cmd[11] = i>>16; cmd[12] = i>>8; cmd[13] = i; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = data; rp->data.count = i; rp->data.write = 0; return SRrequest(rp); } void scsidebug(int d) { debug = d; if(debug) fprint(2, "scsidebug on\n"); } static long request(int fd, ScsiPtr *cmd, ScsiPtr *data, int *status) { long n, r; char buf[16]; /* this was an experiment but it seems to be a good idea */ *status = STok; /* send SCSI command */ if(write(fd, cmd->p, cmd->count) != cmd->count){ fprint(2, "scsireq: write cmd: %r\n"); *status = Status_SW; return -1; } /* read or write actual data */ werrstr(""); if(data->write) n = write(fd, data->p, data->count); else { n = read(fd, data->p, data->count); if (n < 0) memset(data->p, 0, data->count); else if (n < data->count) memset(data->p + n, 0, data->count - n); } if (n != data->count && n <= 0) { if (debug) fprint(2, "request: tried to %s %ld bytes of data for cmd 0x%x but got %r\n", (data->write? "write": "read"), data->count, cmd->p[0]); } else if (n != data->count && (data->write || debug)) fprint(2, "request: %s %ld of %ld bytes of actual data\n", (data->write? "wrote": "read"), n, data->count); /* read status */ buf[0] = '\0'; r = read(fd, buf, sizeof buf-1); if(exabyte && r <= 0 || !exabyte && r < 0){ fprint(2, "scsireq: read status: %r\n"); *status = Status_SW; return -1; } if (r >= 0) buf[r] = '\0'; *status = atoi(buf); if(n < 0 && (exabyte || *status != STcheck)) fprint(2, "scsireq: status 0x%2.2uX: data transfer: %r\n", *status); return n; } static char* seprintcmd(char *s, char* e, char *cmd, int count, int args) { uint c; if(count < 6) return seprint(s, e, ""); c = cmd[0]; if(scmdnames[c] != nil) s = seprint(s, e, "%s", scmdnames[c]); else s = seprint(s, e, "cmd:%#02uX", c); if(args != 0) switch(c){ case ScmdRsense: case ScmdInq: case ScmdMselect6: case ScmdMsense6: s = seprint(s, e, " sz %d", cmd[4]); break; case ScmdSpace: s = seprint(s, e, " code %d", cmd[1]); break; case ScmdStart: s = seprint(s, e, " code %d", cmd[4]); break; } return s; } static char* seprintdata(char *s, char *se, uchar *p, int count) { int i; if(count == 0) return s; for(i = 0; i < 20 && i < count; i++) s = seprint(s, se, " %02x", p[i]); return s; } static void SRdumpReq(ScsiReq *rp) { char buf[128]; char *s; char *se; se = buf+sizeof(buf); s = seprint(buf, se, "lun %d ", rp->lun); s = seprintcmd(s, se, (char*)rp->cmd.p, rp->cmd.count, 1); s = seprint(s, se, " [%ld]", rp->data.count); if(rp->cmd.write) seprintdata(s, se, rp->data.p, rp->data.count); fprint(2, "scsi⇒ %s\n", buf); } static void SRdumpRep(ScsiReq *rp) { char buf[128]; char *s; char *se; se = buf+sizeof(buf); s = seprint(buf, se, "lun %d ", rp->lun); s = seprintcmd(s, se, (char*)rp->cmd.p, rp->cmd.count, 0); switch(rp->status){ case STok: s = seprint(s, se, " good [%ld] ", rp->data.count); if(rp->cmd.write == 0) s = seprintdata(s, se, rp->data.p, rp->data.count); break; case STnomem: s = seprint(s, se, " buffer allocation failed"); break; case STharderr: s = seprint(s, se, " controller error"); break; case STtimeout: s = seprint(s, se, " bus timeout"); break; case STcheck: s = seprint(s, se, " check condition"); break; case STcondmet: s = seprint(s, se, " condition met/good"); break; case STbusy: s = seprint(s, se, " busy"); break; case STintok: s = seprint(s, se, " intermediate/good"); break; case STintcondmet: s = seprint(s, se, " intermediate/condition met/good"); break; case STresconf: s = seprint(s, se, " reservation conflict"); break; case STterminated: s = seprint(s, se, " command terminated"); break; case STqfull: s = seprint(s, se, " queue full"); break; default: s = seprint(s, se, " sts=%#x", rp->status); } USED(s); fprint(2, "scsi← %s\n", buf); } static char* scsierr(ScsiReq *rp) { int ec; switch(rp->status){ case 0: return ""; case Status_SD: ec = (rp->sense[12] << 8) | rp->sense[13]; return scsierrmsg(ec); case Status_SW: return "software error"; case Status_BADARG: return "bad argument"; case Status_RO: return "device is read only"; default: return "unknown"; } } static void SRdumpErr(ScsiReq *rp) { char buf[128]; char *se; se = buf+sizeof(buf); seprintcmd(buf, se, (char*)rp->cmd.p, rp->cmd.count, 0); print("\t%s status: %s\n", buf, scsierr(rp)); } long SRrequest(ScsiReq *rp) { long n; int status; retry: if(debug) SRdumpReq(rp); if(rp->flags&Fusb) n = umsrequest(rp->umsc, &rp->cmd, &rp->data, &status); else n = request(rp->fd, &rp->cmd, &rp->data, &status); rp->status = status; if(status == STok) rp->data.count = n; if(debug) SRdumpRep(rp); switch(status){ case STok: break; case STcheck: if(rp->cmd.p[0] != ScmdRsense && SRreqsense(rp) != -1) rp->status = Status_SD; if(debug || exabyte) SRdumpErr(rp); werrstr("%s", scsierr(rp)); return -1; case STbusy: sleep(1000); goto retry; default: if(debug || exabyte) SRdumpErr(rp); werrstr("%s", scsierr(rp)); return -1; } return n; } int SRclose(ScsiReq *rp) { if((rp->flags & Fopen) == 0){ rp->status = Status_BADARG; return -1; } close(rp->fd); rp->flags = 0; return 0; } static int dirdevopen(ScsiReq *rp) { ulong blocks; uchar data[8]; if(SRstart(rp, 1) == -1 || SRrcapacity(rp, data) == -1) return -1; rp->lbsize = GETBELONG(data+4); blocks = GETBELONG(data); if(blocks == 0xffffffff){ if(SRrcapacity16(rp, data) == -1) return -1; rp->lbsize = GETBELONG(data + 8); blocks = (vlong)GETBELONG(data)<<32 | GETBELONG(data + 4); } /* some newer dev's don't support 6-byte commands */ if(blocks > Max24off && !force6bytecmds) rp->flags |= Frw10; return 0; } static int seqdevopen(ScsiReq *rp) { uchar mode[16], limits[6]; if(SRrblimits(rp, limits) == -1) return -1; if(limits[1] == 0 && limits[2] == limits[4] && limits[3] == limits[5]){ rp->flags |= Fbfixed; rp->lbsize = limits[4]<<8 | limits[5]; return 0; } /* * On some older hardware the optional 10-byte * modeselect command isn't implemented. */ if (force6bytecmds) rp->flags |= Fmode6; if(!(rp->flags & Fmode6)){ /* try 10-byte command first */ memset(mode, 0, sizeof mode); mode[3] = 0x10; /* device-specific param. */ mode[7] = 8; /* block descriptor length */ /* * exabytes can't handle this, and * modeselect(10) is optional. */ if(SRmodeselect10(rp, mode, sizeof mode) != -1){ rp->lbsize = 1; return 0; /* success */ } /* can't do 10-byte commands, back off to 6-byte ones */ rp->flags |= Fmode6; } /* 6-byte command */ memset(mode, 0, sizeof mode); mode[2] = 0x10; /* device-specific param. */ mode[3] = 8; /* block descriptor length */ /* * bsd sez exabytes need this bit (NBE: no busy enable) in * vendor-specific page (0), but so far we haven't needed it. mode[12] |= 8; */ if(SRmodeselect6(rp, mode, 4+8) == -1) return -1; rp->lbsize = 1; return 0; } static int wormdevopen(ScsiReq *rp) { long status; uchar list[MaxDirData]; if (SRstart(rp, 1) == -1 || (status = SRmodesense10(rp, Allmodepages, list, sizeof list)) == -1) return -1; /* nbytes = list[0]<<8 | list[1]; */ /* # of bytes of block descriptors of 8 bytes each; not even 1? */ if((list[6]<<8 | list[7]) < 8) rp->lbsize = 2048; else /* last 3 bytes of block 0 descriptor */ rp->lbsize = GETBE24(list+13); return status; } int SRopenraw(ScsiReq *rp, char *unit) { char name[128]; if(rp->flags & Fopen){ rp->status = Status_BADARG; return -1; } memset(rp, 0, sizeof *rp); rp->unit = unit; sprint(name, "%s/raw", unit); if((rp->fd = open(name, ORDWR)) == -1){ rp->status = STtimeout; return -1; } rp->flags = Fopen; return 0; } int SRopen(ScsiReq *rp, char *unit) { if(SRopenraw(rp, unit) == -1) return -1; SRready(rp); if(SRinquiry(rp) >= 0){ switch(rp->inquiry[0]){ default: fprint(2, "unknown device type 0x%.2x\n", rp->inquiry[0]); rp->status = Status_SW; break; case 0x00: /* Direct access (disk) */ case 0x05: /* CD-ROM */ case 0x07: /* rewriteable MO */ if(dirdevopen(rp) == -1) break; return 0; case 0x01: /* Sequential eg: tape */ rp->flags |= Fseqdev; if(seqdevopen(rp) == -1) break; return 0; case 0x02: /* Printer */ rp->flags |= Fprintdev; return 0; case 0x04: /* Worm */ rp->flags |= Fwormdev; if(wormdevopen(rp) == -1) break; return 0; case 0x08: /* medium-changer */ rp->flags |= Fchanger; return 0; } } SRclose(rp); return -1; } dMselect10; cmd[7] = nbytes>>8; cmd[8] = nbytes; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p = list; rp->data.count = nbytes; rp->data.write = 1; return SRrequest(rp); } long SRmodesense6(ScsiReq *rp, uchar page, uchar *list, long nbytes) { uchar cmd[6]; memset(cmd, 0, sizeof cmd); cmd[0] = ScmdMsense6; cmd[2] = page; cmd[4] = nbytes; rp->cmd.p = cmd; rp->cmd.count = sizeof cmd; rp->data.p usb/disk/scsireq.h 664 0 0 16033 11176010054 12247ustar00nemosys/* * This is /sys/src/cmd/scuzz/scsireq.c * changed to add more debug support, to keep * disk compiling without a scuzz that includes these changes. */ /* this file is also included by usb/disk and cdfs */ typedef struct Umsc Umsc; #pragma incomplete Umsc enum { /* fundamental constants/defaults */ NTargetID = 8, /* number of target IDs */ CtlrID = 7, /* default controller target ID */ MaxDirData = 255, /* max. direct data returned */ LBsize = 512, /* default logical-block size */ }; typedef struct { uchar *p; long count; uchar write; } ScsiPtr; typedef struct { int flags; char *unit; /* unit directory */ int lun; ulong lbsize; ulong offset; /* in blocks of lbsize bytes */ int fd; Umsc *umsc; /* lun */ ScsiPtr cmd; ScsiPtr data; int status; /* returned status */ uchar sense[MaxDirData]; /* returned sense data */ uchar inquiry[MaxDirData]; /* returned inquiry data */ int readblock; /* flag: read a block since open */ } ScsiReq; enum { /* software flags */ Fopen = 0x0001, /* open */ Fseqdev = 0x0002, /* sequential-access device */ Fwritten = 0x0004, /* device written */ Fronly = 0x0008, /* device is read-only */ Fwormdev = 0x0010, /* write-once read-multiple device */ Fprintdev = 0x0020, /* printer */ Fbfixed = 0x0040, /* fixed block size */ Fchanger = 0x0080, /* medium-changer device */ Finqok = 0x0100, /* inquiry data is OK */ Fmode6 = 0x0200, /* use 6-byte modeselect */ Frw10 = 0x0400, /* use 10-byte read/write */ Fusb = 0x0800, /* USB transparent scsi */ }; enum { STnomem =-4, /* buffer allocation failed */ STharderr =-3, /* controller error of some kind */ STtimeout =-2, /* bus timeout */ STok = 0, /* good */ STcheck = 0x02, /* check condition */ STcondmet = 0x04, /* condition met/good */ STbusy = 0x08, /* busy */ STintok = 0x10, /* intermediate/good */ STintcondmet = 0x14, /* intermediate/condition met/good */ STresconf = 0x18, /* reservation conflict */ STterminated = 0x22, /* command terminated */ STqfull = 0x28, /* queue full */ }; enum { /* status */ Status_SD = 0x80, /* sense-data available */ Status_SW = 0x83, /* internal software error */ Status_BADARG = 0x84, /* bad argument to request */ Status_RO = 0x85, /* device is read-only */ }; enum { /* SCSI command codes */ ScmdTur = 0x00, /* test unit ready */ ScmdRewind = 0x01, /* rezero/rewind */ ScmdRsense = 0x03, /* request sense */ ScmdFormat = 0x04, /* format unit */ ScmdRblimits = 0x05, /* read block limits */ ScmdRead = 0x08, /* read */ ScmdWrite = 0x0A, /* write */ ScmdSeek = 0x0B, /* seek */ ScmdFmark = 0x10, /* write filemarks */ ScmdSpace = 0x11, /* space forward/backward */ ScmdInq = 0x12, /* inquiry */ ScmdMselect6 = 0x15, /* mode select */ ScmdMselect10 = 0x55, /* mode select */ ScmdMsense6 = 0x1A, /* mode sense */ ScmdMsense10 = 0x5A, /* mode sense */ ScmdStart = 0x1B, /* start/stop unit */ ScmdRcapacity = 0x25, /* read capacity */ ScmdRcapacity16 = 0x9e, /* long read capacity */ ScmdExtread = 0x28, /* extended read */ ScmdExtwrite = 0x2A, /* extended write */ ScmdExtseek = 0x2B, /* extended seek */ ScmdSynccache = 0x35, /* flush cache */ ScmdRTOC = 0x43, /* read TOC data */ ScmdRdiscinfo = 0x51, /* read disc information */ ScmdRtrackinfo = 0x52, /* read track information */ ScmdReserve = 0x53, /* reserve track */ ScmdBlank = 0xA1, /* blank *-RW media */ ScmdCDpause = 0x4B, /* pause/resume */ ScmdCDstop = 0x4E, /* stop play/scan */ ScmdCDplay = 0xA5, /* play audio */ ScmdCDload = 0xA6, /* load/unload */ ScmdCDscan = 0xBA, /* fast forward/reverse */ ScmdCDstatus = 0xBD, /* mechanism status */ Scmdgetconf = 0x46, /* get configuration */ ScmdEInitialise = 0x07, /* initialise element status */ ScmdMMove = 0xA5, /* move medium */ ScmdEStatus = 0xB8, /* read element status */ ScmdMExchange = 0xA6, /* exchange medium */ ScmdEposition = 0x2B, /* position to element */ ScmdReadDVD = 0xAD, /* read dvd structure */ ScmdReportKey = 0xA4, /* read dvd key */ ScmdSendKey = 0xA3, /* write dvd key */ ScmdClosetracksess= 0x5B, ScmdRead12 = 0xA8, ScmdSetcdspeed = 0xBB, ScmdReadcd = 0xBE, /* vendor-specific */ ScmdFwaddr = 0xE2, /* first writeable address */ ScmdTreserve = 0xE4, /* reserve track */ ScmdTinfo = 0xE5, /* read track info */ ScmdTwrite = 0xE6, /* write track */ ScmdMload = 0xE7, /* medium load/unload */ ScmdFixation = 0xE9, /* fixation */ }; enum { /* sense data byte 0 */ Sd0valid = 0x80, /* valid sense data present */ /* sense data byte 2 */ /* incorrect-length indicator, difference in bytes 3—6 */ Sd2ili = 0x20, Sd2eom = 0x40, /* end of medium (tape) */ Sd2filemark = 0x80, /* at a filemark (tape) */ /* command byte 1 */ Cmd1fixed = 1, /* use fixed-length blocks */ Cmd1sili = 2, /* don't set Sd2ili */ /* limit of block #s in 24-bit ccbs */ Max24off = (1<<21) - 1, /* 2⁲ⁱ - 1 */ /* mode pages */ Allmodepages = 0x3F, }; /* p arguments should be of type uchar* */ #define GETBELONG(p) ((ulong)(p)[0]<<24 | (ulong)(p)[1]<<16 | (p)[2]<<8 | (p)[3]) #define PUTBELONG(p, ul) ((p)[0] = (ul)>>24, (p)[1] = (ul)>>16, \ (p)[2] = (ul)>>8, (p)[3] = (ul)) #define GETBE24(p) ((ulong)(p)[0]<<16 | (p)[1]<<8 | (p)[2]) #define PUTBE24(p, ul) ((p)[0] = (ul)>>16, (p)[1] = (ul)>>8, (p)[2] = (ul)) extern long maxiosize; long SRready(ScsiReq*); long SRrewind(ScsiReq*); long SRreqsense(ScsiReq*); long SRformat(ScsiReq*); long SRrblimits(ScsiReq*, uchar*); long SRread(ScsiReq*, void*, long); long SRwrite(ScsiReq*, void*, long); long SRseek(ScsiReq*, long, int); long SRfilemark(ScsiReq*, ulong); long SRspace(ScsiReq*, uchar, long); long SRinquiry(ScsiReq*); long SRmodeselect6(ScsiReq*, uchar*, long); long SRmodeselect10(ScsiReq*, uchar*, long); long SRmodesense6(ScsiReq*, uchar, uchar*, long); long SRmodesense10(ScsiReq*, uchar, uchar*, long); long SRstart(ScsiReq*, uchar); long SRrcapacity(ScsiReq*, uchar*); long SRrcapacity16(ScsiReq*, uchar*); long SRblank(ScsiReq*, uchar, uchar); /* MMC CD-R/CD-RW commands */ long SRsynccache(ScsiReq*); long SRTOC(ScsiReq*, void*, int, uchar, uchar); long SRrdiscinfo(ScsiReq*, void*, int); long SRrtrackinfo(ScsiReq*, void*, int, int); long SRcdpause(ScsiReq*, int); /* MMC CD audio commands */ long SRcdstop(ScsiReq*); long SRcdload(ScsiReq*, int, int); long SRcdplay(ScsiReq*, int, long, long); long SRcdstatus(ScsiReq*, uchar*, int); long SRgetconf(ScsiReq*, uchar*, int); /* old CD-R/CD-RW commands */ long SRfwaddr(ScsiReq*, uchar, uchar, uchar, uchar*); long SRtreserve(ScsiReq*, long); long SRtinfo(ScsiReq*, uchar, uchar*); long SRwtrack(ScsiReq*, void*, long, uchar, uchar); long SRmload(ScsiReq*, uchar); long SRfixation(ScsiReq*, uchar); long SReinitialise(ScsiReq*); /* CHANGER commands */ long SRestatus(ScsiReq*, uchar, uchar*, int); long SRmmove(ScsiReq*, int, int, int, int); long SRrequest(ScsiReq*); int SRclose(ScsiReq*); int SRopenraw(ScsiReq*, char*); int SRopen(ScsiReq*, char*); void makesense(ScsiReq*); long umsrequest(struct Umsc*, ScsiPtr*, ScsiPtr*, int*); void scsidebug(int); char* scsierrmsg(int n); != -1){ rp->lbsize = 1; return 0; /* success */ } /* can't do 10-byte commands, back off to 6-byte ones */ rp->flags |= Fmode6; } /* 6-byte command */ memset(mode, 0, sizeof mode); mode[2] = 0x10; /* device-specific param. */ mode[3] = 8; /* block descriptor length */ /* * bsd sez exabytes need this bit (NBE: no busy enable) in * vendor-specific page (0), but so far we haven't needed it. mode[12] |= 8; */ if(SRmodeselect6(rp, mode, 4+8) == -1) returnusb/disk/ums.h 664 0 0 3156 11203555423 11371ustar00nemosys/* * mass storage transport protocols and subclasses, * from usb mass storage class specification overview rev 1.2 */ typedef struct Umsc Umsc; typedef struct Ums Ums; typedef struct Cbw Cbw; /* command block wrapper */ typedef struct Csw Csw; /* command status wrapper */ enum { Protocbi = 0, /* control/bulk/interrupt; mainly floppies */ Protocb = 1, /* " with no interrupt; mainly floppies */ Protobulk = 0x50, /* bulk only */ Subrbc = 1, /* reduced blk cmds */ Subatapi = 2, /* cd/dvd using sff-8020i or mmc-2 cmd blks */ Subqic = 3, /* QIC-157 tapes */ Subufi = 4, /* floppy */ Sub8070 = 5, /* removable media, atapi-like */ Subscsi = 6, /* scsi transparent cmd set */ Subisd200 = 7, /* ISD200 ATA */ Subdev = 0xff, /* use device's value */ Umsreset = 0xFF, Getmaxlun = 0xFE, MaxIOsize = 256*1024, /* max. I/O size */ // Maxlun = 256, Maxlun = 32, CMreset = 1, Pcmd = 0, Pdata, Pstatus, CbwLen = 31, CbwDataIn = 0x80, CbwDataOut = 0x00, CswLen = 13, CswOk = 0, CswFailed = 1, CswPhaseErr = 2, }; /* these are 600 bytes each; ScsiReq is not tiny */ struct Umsc { ScsiReq; uvlong blocks; vlong capacity; uchar rawcmd[10]; uchar phase; char *inq; Ums *ums; Usbfs fs; }; struct Ums { QLock; Dev *dev; Dev *epin; Dev *epout; Umsc *lun; uchar maxlun; int seq; int nerrs; }; /* * USB transparent SCSI devices */ struct Cbw { char signature[4]; /* "USBC" */ long tag; long datalen; uchar flags; uchar lun; uchar len; char command[16]; }; struct Csw { char signature[4]; /* "USBS" */ long tag; long dataresidue; uchar status; }; int diskmain(Dev*, int, char**); 0 0 16033 11176010054 12247ustar00nemosysusb/ether/ 775 0 0 0 11255750546 105765ustar00nemosysusb/ether/asix.c 664 0 0 23556 11200337277 11731ustar00nemosys/* * Asix USB ether adapters * I got no documentation for it, thus the bits * come from other systems; it's likely this is * doing more than needed in some places and * less than required in others. */ #include #include #include #include #include "usb.h" #include "usbfs.h" #include "ether.h" enum { /* Asix commands */ Cswmii = 0x06, /* set sw mii */ Crmii = 0x07, /* read mii reg */ Cwmii = 0x08, /* write mii reg */ Chwmii = 0x0a, /* set hw mii */ Creeprom = 0x0b, /* read eeprom */ Cwdis = 0x0e, /* write disable */ Cwena = 0x0d, /* write enable */ Crrxctl = 0x0f, /* read rx ctl */ Cwrxctl = 0x10, /* write rx ctl */ Cwipg = 0x12, /* write ipg */ Crmac = 0x13, /* read mac addr */ Crphy = 0x19, /* read phy id */ Cwmedium = 0x1b, /* write medium mode */ Crgpio = 0x1e, /* read gpio */ Cwgpio = 0x1f, /* write gpios */ Creset = 0x20, /* reset */ Cwphy = 0x22, /* select phy */ /* reset codes */ Rclear = 0x00, Rprte = 0x04, Rprl = 0x08, Riprl = 0x20, Rippd = 0x40, Gpiogpo1en = 0x04, /* gpio1 enable */, Gpiogpo1 = 0x08, /* gpio1 value */ Gpiogpo2en = 0x10, /* gpio2 enable */ Gpiogpo2 = 0x20, /* gpio2 value */ Gpiorse = 0x80, /* gpio reload serial eeprom */ Pmask = 0x1F, Pembed = 0x10, /* embedded phy */ Mfd = 0x002, /* media */ Mac = 0x004, Mrfc = 0x010, Mtfc = 0x020, Mjfe = 0x040, Mre = 0x100, Mps = 0x200, Mall772 = Mfd|Mrfc|Mtfc|Mps|Mac|Mre, Mall178 = Mps|Mfd|Mac|Mrfc|Mtfc|Mjfe|Mre, Ipgdflt = 0x15|0x0c|0x12, /* default ipg0, 1, 2 */ Rxctlso = 0x80, Rxctlab = 0x08, Rxctlsep = 0x04, Rxctlamall = 0x02, /* all multicast */ Rxctlprom = 0x01, /* promiscuous */ /* MII */ Miibmcr = 0x00, /* basic mode ctrl reg. */ Bmcrreset = 0x8000, /* reset */ Bmcranena = 0x1000, /* auto neg. enable */ Bmcrar = 0x0200, /* announce restart */ Miiad = 0x04, /* advertise reg. */ Adcsma = 0x0001, Ad1000f = 0x0200, Ad1000h = 0x0100, Ad10h = 0x0020, Ad10f = 0x0040, Ad100h = 0x0080, Ad100f = 0x0100, Adpause = 0x0400, Adall = Ad10h|Ad10f|Ad100h|Ad100f, Miimctl = 0x14, /* marvell ctl */ Mtxdly = 0x02, Mrxdly = 0x80, Mtxrxdly = 0x82, Miic1000 = 0x09, }; static int asixset(Dev *d, int c, int v) { int r; int ec; r = Rh2d|Rvendor|Rdev; ec = usbcmd(d, r, c, v, 0, nil, 0); if(ec < 0) deprint(2, "%s: asixset %x %x: %r\n", argv0, c, v); return ec; } static int asixget(Dev *d, int c, uchar *buf, int l) { int r; int ec; r = Rd2h|Rvendor|Rdev; ec = usbcmd(d, r, c, 0, 0, buf, l); if(ec < 0) deprint(2, "%s: asixget %x: %r\n", argv0, c); return ec; } static int getgpio(Dev *d) { uchar c; if(asixget(d, Crgpio, &c, 1) < 0) return -1; return c; } static int getphy(Dev *d) { uchar buf[2]; if(asixget(d, Crphy, buf, sizeof(buf)) < 0) return -1; deprint(2, "%s: phy addr %#ux\n", argv0, buf[1]); return buf[1]; } static int getrxctl(Dev *d) { uchar buf[2]; int r; memset(buf, 0, sizeof(buf)); if(asixget(d, Crrxctl, buf, sizeof(buf)) < 0) return -1; r = GET2(buf); deprint(2, "%s: rxctl %#x\n", argv0, r); return r; } static int getmac(Dev *d, uchar buf[]) { if(asixget(d, Crmac, buf, Eaddrlen) < 0) return -1; return Eaddrlen; } static int miiread(Dev *d, int phy, int reg) { int r; uchar v[2]; r = Rd2h|Rvendor|Rdev; if(usbcmd(d, r, Crmii, phy, reg, v, 2) < 0){ dprint(2, "%s: miiwrite: %r\n", argv0); return -1; } r = GET2(v); if(r == 0xFFFF) return -1; return r; } static int miiwrite(Dev *d, int phy, int reg, int val) { int r; uchar v[2]; if(asixset(d, Cswmii, 0) < 0) return -1; r = Rh2d|Rvendor|Rdev; PUT2(v, val); if(usbcmd(d, r, Cwmii, phy, reg, v, 2) < 0){ deprint(2, "%s: miiwrite: %#x %#x %r\n", argv0, reg, val); return -1; } if(asixset(d, Chwmii, 0) < 0) return -1; return 0; } static int eepromread(Dev *d, int i) { int r; int ec; uchar buf[2]; r = Rd2h|Rvendor|Rdev; ec = usbcmd(d, r, Creeprom, i, 0, buf, sizeof(buf)); if(ec < 0) deprint(2, "%s: eepromread %d: %r\n", argv0, i); ec = GET2(buf); deprint(2, "%s: eeprom %#x = %#x\n", argv0, i, ec); if(ec == 0xFFFF) ec = -1; return ec; } /* * No doc. we are doing what Linux does as closely * as we can. */ static int ctlrinit(Ether *ether) { Dev *d; int i; int bmcr; int gpio; int ee17; int rc; d = ether->dev; switch(ether->cid){ default: fprint(2, "%s: card known but not implemented\n", argv0); return -1; case A88178: deprint(2, "%s: setting up A88178\n", argv0); gpio = getgpio(d); if(gpio < 0) return -1; deprint(2, "%s: gpio sts %#x\n", argv0, gpio); asixset(d, Cwena, 0); ee17 = eepromread(d, 0x0017); asixset(d, Cwdis, 0); asixset(d, Cwgpio, Gpiorse|Gpiogpo1|Gpiogpo1en); if((ee17 >> 8) != 1){ asixset(d, Cwgpio, 0x003c); asixset(d, Cwgpio, 0x001c); asixset(d, Cwgpio, 0x003c); }else{ asixset(d, Cwgpio, Gpiogpo1en); asixset(d, Cwgpio, Gpiogpo1|Gpiogpo1en); } asixset(d, Creset, Rclear); sleep(150); asixset(d, Creset, Rippd|Rprl); sleep(150); asixset(d, Cwrxctl, 0); if(getmac(d, ether->addr) < 0) return -1; ether->phy = getphy(d); if(ee17 < 0 || (ee17 & 0x7) == 0){ miiwrite(d, ether->phy, Miimctl, Mtxrxdly); sleep(60); } miiwrite(d, ether->phy, Miibmcr, Bmcrreset|Bmcranena); miiwrite(d, ether->phy, Miiad, Adall|Adcsma|Adpause); miiwrite(d, ether->phy, Miic1000, Ad1000f); bmcr = miiread(d, ether->phy, Miibmcr); if((bmcr & Bmcranena) != 0){ bmcr |= Bmcrar; miiwrite(d, ether->phy, Miibmcr, bmcr); } asixset(d, Cwmedium, Mall178); asixset(d, Cwrxctl, Rxctlso|Rxctlab); break; case A88772: deprint(2, "%s: setting up A88772\n", argv0); if(asixset(d, Cwgpio, Gpiorse|Gpiogpo2|Gpiogpo2en) < 0) return -1; ether->phy = getphy(d); dprint(2, "%s: phy %#x\n", argv0, ether->phy); if((ether->phy & Pmask) == Pembed){ /* embedded 10/100 ethernet */ rc = asixset(d, Cwphy, 1); }else rc = asixset(d, Cwphy, 0); if(rc < 0) return -1; if(asixset(d, Creset, Rippd|Rprl) < 0) return -1; sleep(150); if((ether->phy & Pmask) == Pembed) rc = asixset(d, Creset, Riprl); else rc = asixset(d, Creset, Rprte); if(rc < 0) return -1; sleep(150); rc = getrxctl(d); deprint(2, "%s: rxctl is %#x\n", argv0, rc); if(asixset(d, Cwrxctl, 0) < 0) return -1; if(getmac(d, ether->addr) < 0) return -1; if(asixset(d, Creset, Rprl) < 0) return -1; sleep(150); if(asixset(d, Creset, Riprl|Rprl) < 0) return -1; sleep(150); miiwrite(d, ether->phy, Miibmcr, Bmcrreset); miiwrite(d, ether->phy, Miiad, Adall|Adcsma); bmcr = miiread(d, ether->phy, Miibmcr); if((bmcr & Bmcranena) != 0){ bmcr |= Bmcrar; miiwrite(d, ether->phy, Miibmcr, bmcr); } if(asixset(d, Cwmedium, Mall772) < 0) return -1; if(asixset(d, Cwipg, Ipgdflt) < 0) return -1; if(asixset(d, Cwrxctl, Rxctlso|Rxctlab) < 0) return -1; deprint(2, "%s: final rxctl: %#x\n", argv0, getrxctl(d)); break; } if(etherdebug){ fprint(2, "%s: ether: phy %#x addr ", argv0, ether->phy); for(i = 0; i < sizeof(ether->addr); i++) fprint(2, "%02x", ether->addr[i]); fprint(2, "\n"); } return 0; } static long asixbread(Ether *e, Buf *bp) { ulong nr; ulong hd; Buf *rbp; rbp = e->aux; if(rbp == nil || rbp->ndata < 4){ rbp->rp = rbp->data; rbp->ndata = read(e->epin->dfd, rbp->rp, sizeof(bp->data)); if(rbp->ndata < 0) return -1; } if(rbp->ndata < 4){ werrstr("short frame"); deprint(2, "%s: asixbread got %d bytes\n", argv0, rbp->ndata); rbp->ndata = 0; return 0; } hd = GET4(rbp->rp); nr = hd & 0xFFFF; hd = (hd>>16) & 0xFFFF; if(nr != (~hd & 0xFFFF)){ if(0)deprint(2, "%s: asixread: bad header %#ulx %#ulx\n", argv0, nr, (~hd & 0xFFFF)); werrstr("bad usb packet header"); rbp->ndata = 0; return 0; } rbp->rp += 4; if(nr < 6 || nr > Epktlen){ if(nr < 6) werrstr("short frame"); else werrstr("long frame"); deprint(2, "%s: asixbread %r (%ld)\n", argv0, nr); rbp->ndata = 0; return 0; } bp->rp = bp->data + Hdrsize; memmove(bp->rp, rbp->rp, nr); bp->ndata = nr; rbp->rp += 4 + nr; rbp->ndata -= (4 + nr); return bp->ndata; } static long asixbwrite(Ether *e, Buf *bp) { ulong len; long n; deprint(2, "%s: asixbwrite %d bytes\n", argv0, bp->ndata); assert(bp->rp - bp->data >= Hdrsize); bp->ndata &= 0xFFFF; len = (0xFFFF0000 & ~(bp->ndata<<16)) | bp->ndata; bp->rp -= 4; PUT4(bp->rp, len); bp->ndata += 4; if((bp->ndata % e->epout->maxpkt) == 0){ PUT4(bp->rp+bp->ndata, 0xFFFF0000); bp->ndata += 4; } n = write(e->epout->dfd, bp->rp, bp->ndata); deprint(2, "%s: asixbwrite wrote %ld bytes\n", argv0, n); if(n <= 0) return n; return n; } static int asixpromiscuous(Ether *e, int on) { int rxctl; deprint(2, "%s: aixprompiscuous %d\n", argv0, on); rxctl = getrxctl(e->dev); if(on != 0) rxctl |= Rxctlprom; else rxctl &= ~Rxctlprom; return asixset(e->dev, Cwrxctl, rxctl); } static int asixmulticast(Ether *e, uchar *addr, int on) { int rxctl; USED(addr); USED(on); /* BUG: should write multicast filter */ rxctl = getrxctl(e->dev); if(e->nmcasts != 0) rxctl |= Rxctlamall; else rxctl &= ~Rxctlamall; deprint(2, "%s: asixmulticast %d\n", argv0, e->nmcasts); return asixset(e->dev, Cwrxctl, rxctl); } static void asixfree(Ether *ether) { deprint(2, "%s: aixfree %#p\n", argv0, ether); free(ether->aux); ether->aux = nil; } int asixreset(Ether *ether) { Cinfo *ip; Dev *dev; dev = ether->dev; for(ip = cinfo; ip->vid != 0; ip++) if(ip->vid == dev->usb->vid && ip->did == dev->usb->did){ ether->cid = ip->cid; if(ctlrinit(ether) < 0){ deprint(2, "%s: init failed: %r\n", argv0); return -1; } deprint(2, "%s: asix reset done\n", argv0); ether->aux = emallocz(sizeof(Buf), 1); ether->bread = asixbread; ether->bwrite = asixbwrite; ether->free = asixfree; ether->promiscuous = asixpromiscuous; ether->multicast = asixmulticast; ether->mbps = 100; /* BUG */ return 0; } return -1; } usb/ether/cdc.c 664 0 0 2121 11200040775 11451ustar00nemosys/* * Standard usb ethernet communications device. */ #include #include #include #include #include "usb.h" #include "usbfs.h" #include "ether.h" static int okclass(Iface *iface) { return Class(iface->csp) == Clcomms && Subclass(iface->csp) == Scether; } static int getmac(Ether *ether) { int i; Usbdev *ud; uchar *b; Desc *dd; char *mac; ud = ether->dev->usb; for(i = 0; i < nelem(ud->ddesc); i++) if((dd = ud->ddesc[i]) != nil && okclass(dd->iface)){ b = (uchar*)&dd->data; if(b[1] == Dfunction && b[2] == Fnether){ mac = loaddevstr(ether->dev, b[3]); if(mac != nil && strlen(mac) != 12){ free(mac); mac = nil; } if(mac != nil){ parseaddr(ether->addr, mac); free(mac); return 0; } } } return -1; } int cdcreset(Ether *ether) { /* * Assume that all communication devices are going to * be std. ethernet communication devices. Specific controllers * must have been probed first. * NB: This ignores unions. */ if(ether->dev->usb->class == Clcomms) return getmac(ether); return -1; } lab = 0x08, Rxctlsep = 0x04, Rxctlamall = 0x02, /* all multicast */ Rxctlprom = 0x01, /* promiscuous */ /* MII */ Miibmcr = 0x00, /* basic mode ctrl reg. */ Bmcrreset = 0x8000, /* reset */ Bmcranena = 0x1000, /* auto neg. enable */ Bmcrar = 0x0200, /* announce restart */ Miiad = 0x04, /* advertise reg. */ Adcsma = 0x0001, Ad1000f = 0x0200, Ad1000h = 0x0100, Ad10h = 0x0020, Ad10f = 0x0040usb/ether/clether.c 664 0 0 36577 11175633617 12432ustar00nemosys#include #include #include #include #include #include #include <9p.h> #include "usb.h" typedef struct Tab Tab; typedef struct Qbuf Qbuf; typedef struct Dq Dq; typedef struct Conn Conn; typedef struct Ehdr Ehdr; typedef struct Stats Stats; enum { SC_ACM = 2, SC_ETHER = 6, FUNCTION = 0x24, FN_HEADER = 0, FN_UNION = 6, FN_ETHER = 15, }; enum { Qroot, Qiface, Qclone, Qstats, Qaddr, Qndir, Qctl, Qdata, Qtype, Qmax, }; struct Tab { char *name; ulong mode; }; Tab tab[] = { "/", DMDIR|0555, "etherU", DMDIR|0555, /* calling it *ether* makes snoopy(8) happy */ "clone", 0666, "stats", 0666, "addr", 0444, "%ud", DMDIR|0555, "ctl", 0666, "data", 0666, "type", 0444, }; struct Qbuf { Qbuf *next; int ndata; uchar data[]; }; struct Dq { QLock l; Dq *next; Req *r; Req **rt; Qbuf *q; Qbuf **qt; int nb; }; struct Conn { QLock l; int used; int type; int prom; Dq *dq; }; struct Ehdr { uchar d[6]; uchar s[6]; uchar type[2]; }; struct Stats { int in; int out; }; Conn conn[32]; int nconn = 0; int debug; ulong time0; int usbfdin = -1; int usbfdout = -1; Stats stats; uchar macaddr[6]; int iunion[8][2]; int niunion; int maxpacket = 64; char *uname; #define PATH(type, n) ((type)|((n)<<8)) #define TYPE(path) (((uint)(path) & 0x000000FF)>>0) #define NUM(path) (((uint)(path) & 0xFFFFFF00)>>8) #define NUMCONN(c) (((long)(c)-(long)&conn[0])/sizeof(conn[0])) static int receivepacket(void *buf, int len); static void fillstat(Dir *d, uvlong path) { Tab *t; memset(d, 0, sizeof(*d)); d->uid = estrdup9p(uname); d->gid = estrdup9p(uname); d->qid.path = path; d->atime = d->mtime = time0; t = &tab[TYPE(path)]; d->name = smprint(t->name, NUM(path)); d->qid.type = t->mode>>24; d->mode = t->mode; } static void fsattach(Req *r) { if(r->ifcall.aname && r->ifcall.aname[0]){ respond(r, "invalid attach specifier"); return; } if(uname == nil) uname = estrdup9p(r->ifcall.uname); r->fid->qid.path = PATH(Qroot, 0); r->fid->qid.type = QTDIR; r->fid->qid.vers = 0; r->ofcall.qid = r->fid->qid; respond(r, nil); } static void fsstat(Req *r) { fillstat(&r->d, r->fid->qid.path); respond(r, nil); } static int rootgen(int i, Dir *d, void*) { i += Qroot+1; if(i == Qiface){ fillstat(d, i); return 0; } return -1; } static int ifacegen(int i, Dir *d, void*) { i += Qiface+1; if(i < Qndir){ fillstat(d, i); return 0; } i -= Qndir; if(i < nconn){ fillstat(d, PATH(Qndir, i)); return 0; } return -1; } static int ndirgen(int i, Dir *d, void *aux) { i += Qndir+1; if(i < Qmax){ fillstat(d, PATH(i, NUMCONN(aux))); return 0; } return -1; } static char* fswalk1(Fid *fid, char *name, Qid *qid) { int i, n; char buf[32]; ulong path; path = fid->qid.path; if(!(fid->qid.type&QTDIR)) return "walk in non-directory"; if(strcmp(name, "..") == 0){ switch(TYPE(path)){ case Qroot: return nil; case Qiface: qid->path = PATH(Qroot, 0); qid->type = tab[Qroot].mode>>24; return nil; case Qndir: qid->path = PATH(Qiface, 0); qid->type = tab[Qiface].mode>>24; return nil; default: return "bug in fswalk1"; } } for(i = TYPE(path)+1; ipath = PATH(i, n); qid->type = tab[i].mode>>24; return nil; } break; } if(strcmp(name, tab[i].name) == 0){ qid->path = PATH(i, NUM(path)); qid->type = tab[i].mode>>24; return nil; } if(tab[i].mode&DMDIR) break; } return "directory entry not found"; } static void matchrq(Dq *d) { Req *r; Qbuf *b; while(r = d->r){ int n; if((b = d->q) == nil) break; if((d->q = b->next) == nil) d->qt = &d->q; if((d->r = (Req*)r->aux) == nil) d->rt = &d->r; n = r->ifcall.count; if(n > b->ndata) n = b->ndata; memmove(r->ofcall.data, b->data, n); free(b); r->ofcall.count = n; respond(r, nil); } } static void readconndata(Req *r) { Dq *d; d = r->fid->aux; qlock(&d->l); if(d->q==nil && d->nb){ qunlock(&d->l); r->ofcall.count = 0; respond(r, nil); return; } // enqueue request r->aux = nil; *d->rt = r; d->rt = (Req**)&r->aux; matchrq(d); qunlock(&d->l); } static void writeconndata(Req *r) { char e[ERRMAX]; Dq *d; void *p; int n; d = r->fid->aux; p = r->ifcall.data; n = r->ifcall.count; if((n == 11) && memcmp(p, "nonblocking", n)==0){ d->nb = 1; goto out; } n = write(usbfdout, p, n); if(n < 0){ rerrstr(e, sizeof(e)); respond(r, e); return; } /* * this may not work with all CDC devices. the * linux driver sends one more random byte * instead of a zero byte transaction. maybe we * should do the same? */ if(n % maxpacket == 0) write(usbfdout, "", 0); if(receivepacket(p, n) == 0) stats.out++; out: r->ofcall.count = n; respond(r, nil); } static char* mac2str(uchar *m) { int i; char *t = "0123456789abcdef"; static char buf[13]; buf[13] = 0; for(i=0; i<6; i++){ buf[i*2] = t[m[i]>>4]; buf[i*2+1] = t[m[i]&0xF]; } return buf; } static int str2mac(uchar *m, char *s) { int i; if(strlen(s) != 12) return -1; for(i=0; i<12; i++){ uchar v; if(s[i] >= 'A' && s[i] <= 'F'){ v = 10 + s[i] - 'A'; } else if(s[i] >= 'a' && s[i] <= 'f'){ v = 10 + s[i] - 'a'; } else if(s[i] >= '0' && s[i] <= '9'){ v = s[i] - '0'; } else { v = 0; } if(i&1){ m[i/2] |= v; } else { m[i/2] = v<<4; } } return 0; } static void fsread(Req *r) { char buf[200]; char e[ERRMAX]; ulong path; path = r->fid->qid.path; switch(TYPE(path)){ default: snprint(e, sizeof e, "bug in fsread path=%lux", path); respond(r, e); break; case Qroot: dirread9p(r, rootgen, nil); respond(r, nil); break; case Qiface: dirread9p(r, ifacegen, nil); respond(r, nil); break; case Qstats: snprint(buf, sizeof(buf), "in: %d\n" "out: %d\n" "mbps: %d\n" "addr: %s\n", stats.in, stats.out, 10, mac2str(macaddr)); readstr(r, buf); respond(r, nil); break; case Qaddr: readstr(r, mac2str(macaddr)); respond(r, nil); break; case Qndir: dirread9p(r, ndirgen, &conn[NUM(path)]); respond(r, nil); break; case Qctl: snprint(buf, sizeof(buf), "%11d ", NUM(path)); readstr(r, buf); respond(r, nil); break; case Qtype: snprint(buf, sizeof(buf), "%11d ", conn[NUM(path)].type); readstr(r, buf); respond(r, nil); break; case Qdata: readconndata(r); break; } } static void fswrite(Req *r) { char e[ERRMAX]; ulong path; char *p; int n; path = r->fid->qid.path; switch(TYPE(path)){ case Qctl: n = r->ifcall.count; p = (char*)r->ifcall.data; if((n == 11) && memcmp(p, "promiscuous", 11)==0) conn[NUM(path)].prom = 1; if((n > 8) && memcmp(p, "connect ", 8)==0){ char x[12]; if(n - 8 >= sizeof(x)){ respond(r, "invalid control msg"); return; } p += 8; memcpy(x, p, n-8); x[n-8] = 0; conn[NUM(path)].type = atoi(p); } r->ofcall.count = n; respond(r, nil); break; case Qdata: writeconndata(r); break; default: snprint(e, sizeof e, "bug in fswrite path=%lux", path); respond(r, e); } } static void fsopen(Req *r) { static int need[4] = { 4, 2, 6, 1 }; ulong path; int i, n; Tab *t; Dq *d; Conn *c; /* * lib9p already handles the blatantly obvious. * we just have to enforce the permissions we have set. */ path = r->fid->qid.path; t = &tab[TYPE(path)]; n = need[r->ifcall.mode&3]; if((n&t->mode) != n){ respond(r, "permission denied"); return; } d = nil; r->fid->aux = nil; switch(TYPE(path)){ case Qclone: for(i=0; i= nconn) nconn = i+1; path = PATH(Qctl, i); goto CaseConn; } respond(r, "out of connections"); return; case Qdata: d = emalloc9p(sizeof(*d)); memset(d, 0, sizeof(*d)); d->qt = &d->q; d->rt = &d->r; r->fid->aux = d; case Qndir: case Qctl: case Qtype: CaseConn: c = &conn[NUM(path)]; qlock(&c->l); if(c->used++ == 0){ c->type = 0; c->prom = 0; } if(d != nil){ d->next = c->dq; c->dq = d; } qunlock(&c->l); break; } r->fid->qid.path = path; r->ofcall.qid.path = path; respond(r, nil); } static void fsflush(Req *r) { Req *o, **p; Fid *f; Dq *d; o = r->oldreq; f = o->fid; if(TYPE(f->qid.path) == Qdata){ d = f->aux; qlock(&d->l); for(p=&d->r; *p; p=(Req**)&((*p)->aux)){ if(*p == o){ if((*p = (Req*)o->aux) == nil) d->rt = p; r->oldreq = nil; respond(o, "interrupted"); break; } } qunlock(&d->l); } respond(r, nil); } static void fsdestroyfid(Fid *fid) { Conn *c; Qbuf *b; Dq **x, *d; if(TYPE(fid->qid.path) >= Qndir){ c = &conn[NUM(fid->qid.path)]; qlock(&c->l); if(d = fid->aux){ fid->aux = nil; for(x=&c->dq; *x; x=&((*x)->next)){ if(*x == d){ *x = d->next; break; } } qlock(&d->l); while(b = d->q){ d->q = b->next; free(b); } qunlock(&d->l); } if(TYPE(fid->qid.path) == Qctl) c->prom = 0; c->used--; qunlock(&c->l); } } static char* finddevice(int *ctrlno, int *id) { static char buf[80]; int fd; int i, j; for(i=0; i<16; i++){ for(j=0; j<128; j++){ int csp; char line[80]; char *p; int n; snprint(buf, sizeof(buf), "/dev/usb%d/%d/status", i, j); fd = open(buf, OREAD); buf[strlen(buf)-7] = 0; if(fd < 0) break; n = read(fd, line, sizeof(line)-1); close(fd); if(n <= 0) continue; line[n] = 0; p = line; if(strncmp(p, "Enabled ", 8) == 0) p += 8; csp = atol(p); if(Class(csp) != Clcomms) continue; switch(Subclass(csp)){ default: continue; case SC_ACM: case SC_ETHER: break; } if(debug) fprint(2, "found matching device %s\n", buf); if(ctrlno) *ctrlno = i; if(id) *id = j; return buf; } } return nil; } static char* usbgetstr(Dev *d, int i, int lang) { byte b[200]; byte *rb; char *s; Rune r; int l; int n; setupreq(d->ep[0], RD2H|Rdevice, GET_DESCRIPTOR, STRING<<8|i, lang, sizeof(b)); if((n = setupreply(d->ep[0], b, sizeof(b))) < 0) return nil; if(n <= 2) return nil; if(n & 1) return nil; s = malloc(n*UTFmax+1); n = (n - 2)/2; rb = (byte*)b + 2; for(l=0; --n >= 0; rb += 2){ r = GET2(rb); l += runetochar(s+l, &r); } s[l] = 0; return s; } static void etherfunc(Device *d, int, ulong csp, void *bb, int nb) { int class, subclass; uchar *b = bb; char *s; class = Class(csp); subclass = Subclass(csp); if(class != CL_COMMS || subclass != SC_ETHER) return; switch(b[2]){ case FN_HEADER: pcs_raw("header: ", bb, nb); break; case FN_ETHER: pcs_raw("ether: ", bb, nb); if(s = usbgetstr(d, b[3], 0)){ str2mac(macaddr, s); free(s); } break; case FN_UNION: pcs_raw("union: ", bb, nb); if(niunion < nelem(iunion)){ iunion[niunion][0] = b[3]; iunion[niunion][1] = b[4]; niunion++; } break; default: pcs_raw("unknown: ", bb, nb); } } int findendpoints(Device *d, int *epin, int *epout) { int i, j, k; *epin = *epout = -1; niunion = 0; memset(macaddr, 0, 6); for(i=0; inconf; i++){ if (d->config[i] == nil) d->config[i] = mallocz(sizeof(*d->config[0]), 1); loadconfig(d, i); } if(niunion <= 0) return -1; for(i=0; iep); i++){ Endpt *ep; if((ep = d->ep[i]) == nil) continue; if(ep->type != Ebulk) continue; if(ep->iface == nil) continue; if(Class(ep->iface->csp) != CL_DATA) continue; for(j=0; jiface->interface) continue; if(ep->conf == nil) continue; for(k=0; kconf->nif; k++){ if(iunion[j][0] != ep->conf->iface[k]->interface) continue; if(Class(ep->conf->iface[k]->csp) != CL_COMMS) continue; if(Subclass(ep->conf->iface[k]->csp) != SC_ETHER) continue; if(ep->addr & 0x80){ if(*epin == -1) *epin = ep->addr&0xF; } else { if(*epout == -1) *epout = ep->addr&0xF; } if (*epin != -1 && *epout != -1){ maxpacket = ep->maxpkt; for(i=0; i<2; i++){ if(ep->iface->dalt[i] == nil) continue; setupreq(d->ep[0], RH2D|Rinterface, SET_INTERFACE, i, ep->iface->interface, 0); break; } return 0; } break; } break; } } return -1; } static int inote(void *, char *msg) { if(strstr(msg, "interrupt")) return 1; return 0; } static int receivepacket(void *buf, int len) { int i; int t; Ehdr *h; if(len < sizeof(*h)) return -1; h = (Ehdr*)buf; t = (h->type[0]<<8)|h->type[1]; for(i=0; il); if(!c->used) goto next; if(c->type > 0) if(c->type != t) goto next; if(!c->prom && !(h->d[0]&1)) if(memcmp(h->d, macaddr, 6)) goto next; for(d=c->dq; d; d=d->next){ int n; n = len; if(c->type == -2 && n > 64) n = 64; b = emalloc9p(sizeof(*b) + n); b->ndata = n; memcpy(b->data, buf, n); qlock(&d->l); // enqueue buffer b->next = nil; *d->qt = b; d->qt = &b->next; matchrq(d); qunlock(&d->l); } next: qunlock(&c->l); } return 0; } static void usbreadproc(void *) { char err[ERRMAX]; uchar buf[4*1024]; atnotify(inote, 1); threadsetname("usbreadproc"); for(;;){ int n; n = read(usbfdin, buf, sizeof(buf)); if(n < 0){ rerrstr(err, sizeof(err)); if(strstr(err, "interrupted")) continue; fprint(2, "usbreadproc: %s\n", err); threadexitsall(err); } if(n == 0) continue; if(receivepacket(buf, n) == 0) stats.in++; } } void (*dprinter[])(Device *, int, ulong, void *b, int n) = { [STRING] pstring, [DEVICE] pdevice, [FUNCTION] etherfunc, }; Srv fs = { .attach= fsattach, .destroyfid= fsdestroyfid, .walk1= fswalk1, .open= fsopen, .read= fsread, .write= fswrite, .stat= fsstat, .flush= fsflush, }; static void usage(void) { fprint(2, "usage: %s [-dD] [-m mtpt] [-s srv] [ctrlno n]\n", argv0); exits("usage"); } void threadmain(int argc, char **argv) { char *srv, *mtpt, *r; char s[64]; int ctrlno, id, epin, epout; Device *d; srv = nil; mtpt = "/net"; ARGBEGIN { case 'd': debug = 1; break; case 'D': chatty9p++; break; case 'm': mtpt = EARGF(usage()); break; case 's': srv = EARGF(usage()); break; default: usage(); } ARGEND; if(argc != 0 && argc != 2) usage(); if(argc == 2){ ctrlno = atoi(argv[0]); id = atoi(argv[1]); r = smprint("/dev/usb%d/%d", ctrlno, id); } else { r = finddevice(&ctrlno, &id); if(r == nil){ fprint(2, "no device found\n"); return; } } if(debug) fprint(2, "using %d %d %s\n", ctrlno, id, r); if((d = opendev(ctrlno, id)) == nil){ fprint(2, "opendev failed: %r\n"); exits("opendev"); } if(describedevice(d) < 0){ fprint(2, "describedevice failed: %r\n"); exits("describedevice"); } if(findendpoints(d, &epin, &epout) < 0){ fprint(2, "no endpoints found!\n"); exits("findendpoints"); } if(debug) fprint(2, "endpoints in %d, out %d, maxpacket %d\n", epin, epout, maxpacket); fprint(d->ctl, "ep %d bulk r %d 24", epin, maxpacket); fprint(d->ctl, "ep %d bulk w %d 24", epout, maxpacket); closedev(d); sprint(s, "%s/ep%ddata", r, epin); if((usbfdin = open(s, OREAD)) < 0){ fprint(2, "cant open receiving endpoint: %r\n"); exits("open"); } sprint(s, "%s/ep%ddata", r, epout); if((usbfdout = open(s, OWRITE)) < 0){ fprint(2, "cant open transmitting endpoint: %r\n"); exits("open"); } proccreate(usbreadproc, nil, 8*1024); atnotify(inote, 1); time0 = time(0); threadpostmountsrv(&fs, srv, mtpt, MBEFORE); } i/2] |= v; } else { m[i/2] = v<<4; } } return 0; } static void fsread(Req *r) { char buf[200]; char e[ERRMAX]; ulongusb/ether/ether.c 664 0 0 53437 11255747100 12075ustar00nemosys/* * usb/ether - usb ethernet adapter. * BUG: This should use /dev/etherfile to * use the kernel ether device code. */ #include #include #include #include #include "usb.h" #include "usbfs.h" #include "ether.h" typedef struct Dirtab Dirtab; enum { /* Qids. Maintain order (relative to dirtabs structs) */ Qroot = 0, Qclone, Qaddr, Qifstats, Qstats, Qndir, Qndata, Qnctl, Qnifstats, Qnstats, Qntype, Qmax, }; struct Dirtab { char *name; int qid; int mode; }; typedef int (*Resetf)(Ether*); /* * Controllers by vid/vid. Used to locate * specific adapters that do not implement cdc ethernet * Keep null terminated. */ Cinfo cinfo[] = { /* Asix controllers. * Only A88178 and A881772 are implemented. * Others are easy to add by borrowing code * from other systems. */ {0x077b, 0x2226, A8817x}, {0x0b95, 0x1720, A8817x}, {0x0557, 0x2009, A8817x}, {0x0411, 0x003d, A8817x}, {0x0411, 0x006e, A88178}, {0x6189, 0x182d, A8817x}, {0x07aa, 0x0017, A8817x}, {0x1189, 0x0893, A8817x}, {0x1631, 0x6200, A8817x}, {0x04f1, 0x3008, A8817x}, {0x0b95, 0x1780, A88178}, /* Geoff */ {0x13b1, 0x0018, A88772}, {0x1557, 0x7720, A88772}, {0x07d1, 0x3c05, A88772}, {0x2001, 0x3c05, A88772}, {0x1737, 0x0039, A88178}, {0x050d, 0x5055, A88178}, {0x05ac, 0x1402, A88772}, /* Apple */ {0x0b95, 0x772a, A88772}, {0x14ea, 0xab11, A88178}, {0x0db0, 0xa877, A88772}, {0, 0, 0}, }; /* * Each etherU%d is the root of our file system, * which is added to the usb root directory. We only * have to concern ourselfs with each /etherU%d subtree. * * NB: Maintain order in dirtabs, relative to the Qids enum. */ static Dirtab rootdirtab[] = { "/", Qroot, DMDIR|0555, /* etherU%d */ "clone", Qclone, 0666, "addr", Qaddr, 0444, "ifstats", Qifstats, 0444, "stats", Qstats, 0444, /* one dir per connection here */ nil, 0, 0, }; static Dirtab conndirtab[] = { "%d", Qndir, DMDIR|0555, "data", Qndata, 0666, "ctl", Qnctl, 0666, "ifstats", Qnifstats, 0444, "stats", Qnstats, 0444, "type", Qntype, 0444, nil, 0, }; int etherdebug; Resetf ethers[] = { asixreset, cdcreset, /* keep last */ }; static int qtype(vlong q) { return q&0xFF; } static int qnum(vlong q) { return (q >> 8) & 0xFFFFFF; } static uvlong mkqid(int n, int t) { uvlong q; q = (n&0xFFFFFF) << 8 | t&0xFF; return q; } static void freebuf(Ether *e, Buf *bp) { if(0)deprint(2, "%s: freebuf %#p\n", argv0, bp); if(bp != nil){ qlock(e); e->nbufs--; qunlock(e); sendp(e->bc, bp); } } static Buf* allocbuf(Ether *e) { Buf *bp; bp = nbrecvp(e->bc); if(bp == nil){ qlock(e); if(e->nabufs < Nconns){ bp = emallocz(sizeof(Buf), 1); e->nabufs++; setmalloctag(bp, getcallerpc(&e)); deprint(2, "%s: %d buffers\n", argv0, e->nabufs); } qunlock(e); } if(bp == nil) bp = recvp(e->bc); bp->rp = bp->data + Hdrsize; bp->ndata = 0; if(0)deprint(2, "%s: allocbuf %#p\n", argv0, bp); qlock(e); e->nbufs++; qunlock(e); return bp; } static Conn* newconn(Ether *e) { int i; Conn *c; qlock(e); for(i = 0; i < nelem(e->conns); i++){ c = e->conns[i]; if(c == nil || c->ref == 0){ if(c == nil){ c = emallocz(sizeof(Conn), 1); c->rc = chancreate(sizeof(Buf*), 2); c->nb = i; } c->ref = 1; if(i == e->nconns) e->nconns++; e->conns[i] = c; deprint(2, "%s: newconn %d\n", argv0, i); qunlock(e); return c; } } qunlock(e); return nil; } static char* seprintaddr(char *s, char *se, uchar *addr) { int i; for(i = 0; i < Eaddrlen; i++) s = seprint(s, se, "%02x", addr[i]); return s; } void dumpframe(char *tag, void *p, int n) { Etherpkt *ep; char buf[128]; char *s, *se; int i; ep = p; if(n < Eaddrlen * 2 + 2){ fprint(2, "short packet (%d bytes)\n", n); return; } se = buf+sizeof(buf); s = seprint(buf, se, "%s [%d]: ", tag, n); s = seprintaddr(s, se, ep->s); s = seprint(s, se, " -> "); s = seprintaddr(s, se, ep->d); s = seprint(s, se, " type 0x%02ux%02ux ", ep->type[0], ep->type[1]); n -= Eaddrlen * 2 + 2; for(i = 0; i < n && i < 16; i++) s = seprint(s, se, "%02x", ep->data[i]); if(n >= 16) fprint(2, "%s...\n", buf); else fprint(2, "%s\n", buf); } static char* seprintstats(char *s, char *se, Ether *e) { qlock(e); s = seprint(s, se, "in: %ld\n", e->nin); s = seprint(s, se, "out: %ld\n", e->nout); s = seprint(s, se, "input errs: %ld\n", e->nierrs); s = seprint(s, se, "output errs: %ld\n", e->noerrs); s = seprint(s, se, "mbps: %d\n", e->mbps); s = seprint(s, se, "prom: %ld\n", e->prom.ref); s = seprint(s, se, "addr: "); s = seprintaddr(s, se, e->addr); s = seprint(s, se, "\n"); qunlock(e); return s; } static char* seprintifstats(char *s, char *se, Ether *e) { int i; Conn *c; qlock(e); s = seprint(s, se, "ctlr id: %#x\n", e->cid); s = seprint(s, se, "phy: %#x\n", e->phy); s = seprint(s, se, "exiting: %s\n", e->exiting ? "y" : "n"); s = seprint(s, se, "conns: %d\n", e->nconns); s = seprint(s, se, "allocated bufs: %d\n", e->nabufs); s = seprint(s, se, "used bufs: %d\n", e->nbufs); for(i = 0; i < nelem(e->conns); i++){ c = e->conns[i]; if(c == nil) continue; if(c->ref == 0) s = seprint(s, se, "c[%d]: free\n", i); else{ s = seprint(s, se, "c[%d]: refs %ld t %#x h %d p %d\n", c->nb, c->ref, c->type, c->headersonly, c->prom); } } qunlock(e); return s; } static void etherdump(Ether *e) { char buf[256]; if(etherdebug == 0) return; seprintifstats(buf, buf+sizeof(buf), e); fprint(2, "%s: ether %#p:\n%s\n", argv0, e, buf); } static Conn* getconn(Ether *e, int i, int idleok) { Conn *c; qlock(e); if(i < 0 || i >= e->nconns) c = nil; else{ c = e->conns[i]; if(idleok == 0 && c != nil && c->ref == 0) c = nil; } qunlock(e); return c; } static void filldir(Usbfs *fs, Dir *d, Dirtab *tab, int cn) { d->qid.path = mkqid(cn, tab->qid); d->qid.path |= fs->qid; d->mode = tab->mode; if((d->mode & DMDIR) != 0) d->qid.type = QTDIR; else d->qid.type = QTFILE; if(tab->qid == Qndir) sprint(d->name, "%d", cn); else d->name = tab->name; } static int rootdirgen(Usbfs *fs, Qid, int i, Dir *d, void *) { Ether *e; Dirtab *tab; int cn; e = fs->aux; i++; /* skip root */ cn = 0; if(i < nelem(rootdirtab) - 1) /* null terminated */ tab = &rootdirtab[i]; else{ cn = i - nelem(rootdirtab) + 1; if(cn < e->nconns) tab = &conndirtab[0]; else return -1; } filldir(fs, d, tab, cn); return 0; } static int conndirgen(Usbfs *fs, Qid q, int i, Dir *d, void *) { Dirtab *tab; i++; /* skip root */ if(i < nelem(conndirtab) - 1) /* null terminated */ tab = &conndirtab[i]; else return -1; filldir(fs, d, tab, qnum(q.path)); return 0; } static int fswalk(Usbfs *fs, Fid *fid, char *name) { int cn, i; char *es; Dirtab *tab; Ether *e; Qid qid; e = fs->aux; qid = fid->qid; qid.path &= ~fs->qid; if((qid.type & QTDIR) == 0){ werrstr("walk in non-directory"); return -1; } if(strcmp(name, "..") == 0){ /* must be /etherU%d; i.e. our root dir. */ fid->qid.path = mkqid(0, Qroot) | fs->qid; fid->qid.vers = 0; fid->qid.type = QTDIR; return 0; } switch(qtype(qid.path)){ case Qroot: if(name[0] >= '0' && name[0] <= '9'){ es = name; cn = strtoul(name, &es, 10); if(cn >= e->nconns || *es != 0){ werrstr(Enotfound); return -1; } fid->qid.path = mkqid(cn, Qndir) | fs->qid; fid->qid.vers = 0; return 0; } /* fall */ case Qndir: if(qtype(qid.path) == Qroot) tab = rootdirtab; else tab = conndirtab; cn = qnum(qid.path); for(i = 0; tab[i].name != nil; tab++) if(strcmp(tab[i].name, name) == 0){ fid->qid.path = mkqid(cn, tab[i].qid)|fs->qid; fid->qid.vers = 0; if((tab[i].mode & DMDIR) != 0) fid->qid.type = QTDIR; else fid->qid.type = QTFILE; return 0; } break; default: sysfatal("usb: ether: fswalk bug"); } return -1; } static Dirtab* qdirtab(vlong q) { int i, qt; Dirtab *tab; qt = qtype(q); if(qt < nelem(rootdirtab) - 1){ /* null terminated */ tab = rootdirtab; i = qt; }else{ tab = conndirtab; i = qt - (nelem(rootdirtab) - 1); assert(i < nelem(conndirtab) - 1); } return &tab[i]; } static int fsstat(Usbfs *fs, Qid qid, Dir *d) { filldir(fs, d, qdirtab(qid.path), qnum(qid.path)); return 0; } static int fsopen(Usbfs *fs, Fid *fid, int omode) { int qt; vlong qid; Conn *c; Dirtab *tab; Ether *e; qid = fid->qid.path & ~fs->qid; e = fs->aux; qt = qtype(qid); tab = qdirtab(qid); omode &= 3; if(omode != OREAD && (tab->mode&0222) == 0){ werrstr(Eperm); return -1; } switch(qt){ case Qclone: c = newconn(e); if(c == nil){ werrstr("no more connections"); return -1; } fid->qid.type = QTFILE; fid->qid.path = mkqid(c->nb, Qnctl)|fs->qid; fid->qid.vers = 0; break; case Qndata: case Qnctl: case Qnifstats: case Qnstats: case Qntype: c = getconn(e, qnum(qid), 1); if(c == nil) sysfatal("usb: ether: fsopen bug"); incref(c); break; } etherdump(e); return 0; } static int prom(Ether *e, int set) { if(e->promiscuous != nil) return e->promiscuous(e, set); return 0; } static void fsclunk(Usbfs *fs, Fid *fid) { int qt; vlong qid; Buf *bp; Conn *c; Ether *e; e = fs->aux; qid = fid->qid.path & ~fs->qid; qt = qtype(qid); switch(qt){ case Qndata: case Qnctl: case Qnifstats: case Qnstats: case Qntype: if(fid->omode != ONONE){ c = getconn(e, qnum(qid), 0); if(c == nil) sysfatal("usb: ether: fsopen bug"); if(decref(c) == 0){ while((bp = nbrecvp(c->rc)) != nil) freebuf(e, bp); qlock(e); if(c->prom != 0) if(decref(&e->prom) == 0) prom(e, 0); c->prom = c->type = 0; qunlock(e); } } break; } etherdump(e); } int parseaddr(uchar *m, char *s) { int i, n; uchar v; if(strlen(s) < 12) return -1; if(strlen(s) > 12 && strlen(s) < 17) return -1; for(i = n = 0; i < strlen(s); i++){ if(s[i] == ':') continue; if(s[i] >= 'A' && s[i] <= 'F') v = 10 + s[i] - 'A'; else if(s[i] >= 'a' && s[i] <= 'f') v = 10 + s[i] - 'a'; else if(s[i] >= '0' && s[i] <= '9') v = s[i] - '0'; else return -1; if(n&1) m[n/2] |= v; else m[n/2] = v<<4; n++; } return 0; } static long fsread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset) { int cn, qt; char *s, *se; char buf[128]; Buf *bp; Conn *c; Ether *e; Qid q; q = fid->qid; q.path &= ~fs->qid; e = fs->aux; s = buf; se = buf+sizeof(buf); qt = qtype(q.path); cn = qnum(q.path); switch(qt){ case Qroot: count = usbdirread(fs, q, data, count, offset, rootdirgen, nil); break; case Qaddr: s = seprintaddr(s, se, e->addr); count = usbreadbuf(data, count, offset, buf, s - buf); break; case Qnifstats: /* BUG */ case Qifstats: s = seprintifstats(s, se, e); if(e->seprintstats != nil) s = e->seprintstats(s, se, e); count = usbreadbuf(data, count, offset, buf, s - buf); break; case Qnstats: /* BUG */ case Qstats: s = seprintstats(s, se, e); count = usbreadbuf(data, count, offset, buf, s - buf); break; case Qndir: count = usbdirread(fs, q, data, count, offset, conndirgen, nil); break; case Qndata: c = getconn(e, cn, 0); if(c == nil){ werrstr(Eio); return -1; } bp = recvp(c->rc); if(bp == nil) return -1; if(etherdebug > 1) dumpframe("etherin", bp->rp, bp->ndata); count = usbreadbuf(data, count, 0LL, bp->rp, bp->ndata); freebuf(e, bp); break; case Qnctl: s = seprint(s, se, "%11d ", cn); count = usbreadbuf(data, count, offset, buf, s - buf); break; case Qntype: c = getconn(e, cn, 0); if(c == nil) s = seprint(s, se, "%11d ", 0); else s = seprint(s, se, "%11d ", c->type); count = usbreadbuf(data, count, offset, buf, s - buf); break; default: sysfatal("usb: ether: fsread bug"); } return count; } static int typeinuse(Ether *e, int t) { int i; for(i = 0; i < e->nconns; i++) if(e->conns[i]->ref > 0 && e->conns[i]->type == t) return 1; return 0; } static int isloopback(Ether *e, Buf *) { return e->prom.ref > 0; /* BUG: also loopbacks and broadcasts */ } static int etherctl(Ether *e, Conn *c, char *buf) { uchar addr[Eaddrlen]; int t; deprint(2, "%s: etherctl: %s\n", argv0, buf); if(strncmp(buf, "connect ", 8) == 0){ t = atoi(buf+8); qlock(e); if(typeinuse(e, t)){ werrstr("type already in use"); qunlock(e); return -1; } c->type = atoi(buf+8); qunlock(e); return 0; } if(strncmp(buf, "nonblocking", 11) == 0){ if(buf[11] == '\n' || buf[11] == 0) e->nblock = 1; else e->nblock = atoi(buf + 12); deprint(2, "%s: nblock %d\n", argv0, e->nblock); return 0; } if(strncmp(buf, "promiscuous", 11) == 0){ if(c->prom == 0) incref(&e->prom); c->prom = 1; return prom(e, 1); } if(strncmp(buf, "headersonly", 11) == 0){ c->headersonly = 1; return 0; } if(strncmp(buf, "addmulti ", 9) == 0 || strncmp(buf, "remmulti ", 9) == 0){ if(parseaddr(addr, buf+9) < 0){ werrstr("bad address"); return -1; } if(e->multicast == nil) return 0; if(strncmp(buf, "add", 3) == 0){ e->nmcasts++; return e->multicast(e, addr, 1); }else{ e->nmcasts--; return e->multicast(e, addr, 0); } } if(e->ctl != nil) return e->ctl(e, buf); werrstr(Ebadctl); return -1; } static long etherbread(Ether *e, Buf *bp) { deprint(2, "%s: etherbread\n", argv0); bp->rp = bp->data + Hdrsize; bp->ndata = -1; bp->ndata = read(e->epin->dfd, bp->rp, sizeof(bp->data)-Hdrsize); if(bp->ndata < 0){ deprint(2, "%s: etherbread: %r\n", argv0); /* keep { and } */ }else deprint(2, "%s: etherbread: got %d bytes\n", argv0, bp->ndata); return bp->ndata; } static long etherbwrite(Ether *e, Buf *bp) { long n; deprint(2, "%s: etherbwrite %d bytes\n", argv0, bp->ndata); n = write(e->epout->dfd, bp->rp, bp->ndata); if(n < 0){ deprint(2, "%s: etherbwrite: %r\n", argv0); /* keep { and } */ }else deprint(2, "%s: etherbwrite wrote %ld bytes\n", argv0, n); if(n <= 0) return n; if((bp->ndata % e->epout->maxpkt) == 0){ deprint(2, "%s: short pkt write\n", argv0); write(e->epout->dfd, "", 1); } return n; } static long fswrite(Usbfs *fs, Fid *fid, void *data, long count, vlong) { int cn, qt; char buf[128]; Buf *bp; Conn *c; Ether *e; Qid q; q = fid->qid; q.path &= ~fs->qid; e = fs->aux; qt = qtype(q.path); cn = qnum(q.path); switch(qt){ case Qndata: c = getconn(e, cn, 0); if(c == nil){ werrstr(Eio); return -1; } bp = allocbuf(e); if(count > sizeof(bp->data)-Hdrsize) count = sizeof(bp->data)-Hdrsize; memmove(bp->rp, data, count); bp->ndata = count; if(etherdebug > 1) dumpframe("etherout", bp->rp, bp->ndata); if(e->nblock == 0) sendp(e->wc, bp); else if(nbsendp(e->wc, bp) == 0){ deprint(2, "%s: (out) packet lost\n", argv0); freebuf(e, bp); } break; case Qnctl: c = getconn(e, cn, 0); if(c == nil){ werrstr(Eio); return -1; } if(count > sizeof(buf) - 1) count = sizeof(buf) - 1; memmove(buf, data, count); buf[count] = 0; if(etherctl(e, c, buf) < 0) return -1; break; default: sysfatal("usb: ether: fsread bug"); } return count; } static int openeps(Ether *e, int epin, int epout) { e->epin = openep(e->dev, epin); if(e->epin == nil){ fprint(2, "ether: in: openep %d: %r\n", epin); return -1; } if(epout == epin){ incref(e->epin); e->epout = e->epin; }else e->epout = openep(e->dev, epout); if(e->epout == nil){ fprint(2, "ether: out: openep %d: %r\n", epout); closedev(e->epin); return -1; } if(e->epin == e->epout) opendevdata(e->epin, ORDWR); else{ opendevdata(e->epin, OREAD); opendevdata(e->epout, OWRITE); } if(e->epin->dfd < 0 || e->epout->dfd < 0){ fprint(2, "ether: open i/o ep data: %r\n"); closedev(e->epin); closedev(e->epout); return -1; } dprint(2, "ether: ep in %s maxpkt %d; ep out %s maxpkt %d\n", e->epin->dir, e->epin->maxpkt, e->epout->dir, e->epout->maxpkt); /* time outs are not activated for I/O endpoints */ if(usbdebug > 2 || etherdebug > 2){ devctl(e->epin, "debug 1"); devctl(e->epout, "debug 1"); devctl(e->dev, "debug 1"); } return 0; } static int usage(void) { werrstr("usage: usb/ether [-d]"); return -1; } static Usbfs etherfs = { .walk = fswalk, .open = fsopen, .read = fsread, .write = fswrite, .stat = fsstat, .clunk = fsclunk, }; static void shutdownchan(Channel *c) { Buf *bp; while((bp=nbrecvp(c)) != nil) free(bp); chanfree(c); } static void etherfree(Ether *e) { int i; Buf *bp; if(e->free != nil) e->free(e); closedev(e->epin); closedev(e->epout); if(e->rc == nil){ /* not really started */ free(e); return; } for(i = 0; i < e->nconns; i++) if(e->conns[i] != nil){ while((bp = nbrecvp(e->conns[i]->rc)) != nil) free(bp); chanfree(e->conns[i]->rc); free(e->conns[i]); } shutdownchan(e->bc); shutdownchan(e->rc); shutdownchan(e->wc); e->epin = e->epout = nil; free(e); } static void etherdevfree(void *a) { Ether *e = a; if(e != nil) etherfree(e); } /* must return 1 if c wants bp; 0 if not */ static int cwantsbp(Conn *c, Buf *bp) { if(c->ref != 0 && (c->prom != 0 || c->type < 0 || c->type == bp->type)) return 1; return 0; } static void etherwriteproc(void *a) { Ether *e = a; Buf *bp; Channel *wc; wc = e->wc; while(e->exiting == 0){ bp = recvp(wc); if(bp == nil || e->exiting != 0){ free(bp); break; } e->nout++; if(e->bwrite(e, bp) < 0) e->noerrs++; if(isloopback(e, bp) && e->exiting == 0) sendp(e->rc, bp); /* send to input queue */ else freebuf(e, bp); } deprint(2, "%s: writeproc exiting\n", argv0); closedev(e->dev); } static void setbuftype(Buf *bp) { uchar *p; bp->type = 0; if(bp->ndata >= Ehdrsize){ p = bp->rp + Eaddrlen*2; bp->type = p[0]<<8 | p[1]; } } static void etherexiting(Ether *e) { devctl(e->dev, "detach"); e->exiting = 1; close(e->epin->dfd); e->epin->dfd = -1; close(e->epout->dfd); e->epout->dfd = -1; nbsend(e->wc, nil); } static void etherreadproc(void *a) { int i, n, nwants; Buf *bp, *dbp; Ether *e = a; while(e->exiting == 0){ bp = nbrecvp(e->rc); if(bp == nil){ bp = allocbuf(e); /* leak() may think we leak */ if(e->bread(e, bp) < 0){ freebuf(e, bp); break; } if(bp->ndata == 0){ /* may be a short packet; continue */ if(0)dprint(2, "%s: read: short\n", argv0); freebuf(e, bp); continue; }else setbuftype(bp); } e->nin++; nwants = 0; for(i = 0; i < e->nconns; i++) nwants += cwantsbp(e->conns[i], bp); for(i = 0; nwants > 0 && i < e->nconns; i++) if(cwantsbp(e->conns[i], bp)){ n = bp->ndata; if(e->conns[i]->type == -2 && n > 64) n = 64; if(nwants-- == 1){ bp->ndata = n; dbp = bp; bp = nil; }else{ dbp = allocbuf(e); memmove(dbp->rp, bp->rp, n); dbp->ndata = n; dbp->type = bp->type; } if(nbsendp(e->conns[i]->rc, dbp) == 0){ e->nierrs++; freebuf(e, dbp); } } freebuf(e, bp); } deprint(2, "%s: writeproc exiting\n", argv0); etherexiting(e); closedev(e->dev); } static void setalt(Dev *d, int ifcid, int altid) { if(usbcmd(d, Rh2d|Rstd|Riface, Rsetiface, altid, ifcid, nil, 0) < 0) dprint(2, "%s: setalt ifc %d alt %d: %r\n", argv0, ifcid, altid); } static int ifaceinit(Ether *e, Iface *ifc, int *ei, int *eo) { Ep *ep; int epin, epout, i; if(ifc == nil) return -1; epin = epout = -1; for(i = 0; (epin < 0 || epout < 0) && i < nelem(ifc->ep); i++) if((ep = ifc->ep[i]) != nil && ep->type == Ebulk){ if(ep->dir == Eboth || ep->dir == Ein) if(epin == -1) epin = ep->id; if(ep->dir == Eboth || ep->dir == Eout) if(epout == -1) epout = ep->id; } if(epin == -1 || epout == -1) return -1; dprint(2, "ether: ep ids: in %d out %d\n", epin, epout); for(i = 0; i < nelem(ifc->altc); i++) if(ifc->altc[i] != nil) setalt(e->dev, ifc->id, i); *ei = epin; *eo = epout; return 0; } static int etherinit(Ether *e, int *ei, int *eo) { int ctlid, datid, i, j; Conf *c; Desc *desc; Iface *ctlif, *datif; Usbdev *ud; *ei = *eo = -1; ud = e->dev->usb; /* look for union descriptor with ethernet ctrl interface */ for(i = 0; i < nelem(ud->ddesc); i++){ if((desc = ud->ddesc[i]) == nil) continue; if(desc->data.bLength < 5 || desc->data.bbytes[0] != Cdcunion) continue; ctlid = desc->data.bbytes[1]; datid = desc->data.bbytes[2]; if((c = desc->conf) == nil) continue; ctlif = datif = nil; for(j = 0; j < nelem(c->iface); j++){ if(c->iface[j] == nil) continue; if(c->iface[j]->id == ctlid) ctlif = c->iface[j]; if(c->iface[j]->id == datid) datif = c->iface[j]; if(datif != nil && ctlif != nil){ if(Subclass(ctlif->csp) == Scether && ifaceinit(e, datif, ei, eo) != -1) return 0; break; } } } /* try any other one that seems to be ok */ for(i = 0; i < nelem(ud->conf); i++) if((c = ud->conf[i]) != nil) for(j = 0; j < nelem(c->iface); j++) if(ifaceinit(e, c->iface[j], ei, eo) != -1) return 0; dprint(2, "%s: no valid endpoints", argv0); return -1; } int ethermain(Dev *dev, int argc, char **argv) { int epin, epout, i; Ether *e; ARGBEGIN{ case 'd': if(etherdebug == 0) fprint(2, "ether debug on\n"); etherdebug++; break; default: return usage(); }ARGEND if(argc != 0) return usage(); e = dev->aux = emallocz(sizeof(Ether), 1); e->dev = dev; dev->free = etherdevfree; for(i = 0; i < nelem(ethers); i++) if(ethers[i](e) == 0) break; if(i == nelem(ethers)) return -1; if(e->init == nil) e->init = etherinit; if(e->init(e, &epin, &epout) < 0) return -1; if(e->bwrite == nil) e->bwrite = etherbwrite; if(e->bread == nil) e->bread = etherbread; if(openeps(e, epin, epout) < 0) return -1; e->fs = etherfs; snprint(e->fs.name, sizeof(e->fs.name), "etherU%d", dev->id); e->fs.dev = dev; e->fs.aux = e; e->bc = chancreate(sizeof(Buf*), Nconns); e->rc = chancreate(sizeof(Buf*), Nconns/2); e->wc = chancreate(sizeof(Buf*), Nconns*2); incref(e->dev); proccreate(etherwriteproc, e, 16*1024); incref(e->dev); proccreate(etherreadproc, e, 16*1024); deprint(2, "%s: dev ref %ld\n", argv0, dev->ref); usbfsadd(&e->fs); return 0; } uf *) { return e->prom.ref > 0; /* BUG: also loopbacks and broadcasts */ } static int etherctl(Ether *e, Conn *c, char *buf) { uchar addr[Eaddrlen]; int t; deprint(2, "%s: etherctl: %s\n", argv0, buf); if(strncmp(buf, usb/ether/ether.h 664 0 0 4627 11255024374 12060ustar00nemosystypedef struct Ether Ether; typedef struct Etherops Etherops; typedef struct Conn Conn; typedef struct Cinfo Cinfo; typedef struct Buf Buf; typedef struct Etherpkt Etherpkt; enum { /* controller ids */ Cdc = 0, A8817x, /* Asis */ A88178, A88179, A88772, Eaddrlen = 6, Epktlen = 1514, Ehdrsize = 2*Eaddrlen + 2, Maxpkt = 2000, /* no jumbo packets here */ Nconns = 8, /* max number of connections */ Nbufs = 8, /* max number of buffers */ Scether = 6, /* ethernet cdc subclass */ Fnheader = 0, /* Functions */ Fnunion = 6, Fnether = 15, Cdcunion = 6, /* CDC Union descriptor subtype */ }; struct Buf { int type; int ndata; uchar* rp; uchar data[Hdrsize+Maxpkt]; }; struct Conn { Ref; /* one per file in use */ int nb; int type; int headersonly; int prom; Channel*rc; /* [2] of Buf* */ }; struct Etherops { int (*init)(Ether*, int *epin, int *epout); long (*bread)(Ether*, Buf*); long (*bwrite)(Ether*, Buf*); int (*ctl)(Ether*, char*); int (*promiscuous)(Ether*, int); int (*multicast)(Ether*, uchar*, int); char* (*seprintstats)(char*, char*, Ether*); void (*free)(Ether*); void* aux; }; struct Ether { QLock; QLock wlck; /* write one at a time */ int epinid; /* epin address */ int epoutid; /* epout address */ Dev* dev; Dev* epin; Dev* epout; int cid; /* ctlr id */ int phy; /* phy id */ Ref prom; /* nb. of promiscuous conns */ int exiting; /* shutting down */ int wrexited; /* write process died */ uchar addr[Eaddrlen]; /* mac */ int nconns; /* nb. of entries used in... */ Conn* conns[Nconns]; /* connections */ int nabufs; /* nb. of allocated buffers */ int nbufs; /* nb. of buffers in use */ int nblock; /* nonblocking (output)? */ long nin; long nout; long nierrs; long noerrs; int mbps; int nmcasts; Channel*rc; /* read channel (of Buf*) */ Channel*wc; /* write channel (of Buf*) */ Channel*bc; /* free buf. chan. (of Buf*) */ Etherops; Usbfs fs; }; struct Cinfo { int vid; /* usb vendor id */ int did; /* usb device/product id */ int cid; /* controller id assigned by us */ }; struct Etherpkt { uchar d[Eaddrlen]; uchar s[Eaddrlen]; uchar type[2]; uchar data[1500]; }; int ethermain(Dev *dev, int argc, char **argv); int asixreset(Ether*); int cdcreset(Ether*); int parseaddr(uchar *m, char *s); void dumpframe(char *tag, void *p, int n); extern Cinfo cinfo[]; extern int etherdebug; #define deprint if(etherdebug)fprint usb/ether/main.c 664 0 0 3006 11200237533 11647ustar00nemosys/* * usb/ether - usb ethernet adapter. * BUG: This should use /dev/etherfile to * use the kernel ether device code. */ #include #include #include #include #include "usb.h" #include "usbfs.h" #include "ether.h" enum { Arglen = 80, }; static void usage(void) { fprint(2, "usage: %s [-Dd] [-m mnt] [-s srv] [dev...]\n", argv0); threadexitsall("usage"); } /* * Ether devices may be weird. * Be optimistic and try to use any communication * device or one of the `vendor specific class' devices * that we know are ethernets. */ static int matchether(char *info, void*) { Cinfo *ip; char buf[50]; /* * I have an ether reporting comms.0.0 */ if(strstr(info, "comms") != nil) return 0; for(ip = cinfo; ip->vid != 0; ip++){ snprint(buf, sizeof(buf), "vid %#06x did %#06x", ip->vid, ip->did); if(strstr(info, buf) != nil) return 0; } return -1; } void threadmain(int argc, char **argv) { char args[Arglen]; char *as; char *ae; char *srv; char *mnt; srv = nil; mnt = "/net"; quotefmtinstall(); ae = args+sizeof(args); as = seprint(args, ae, "ether"); ARGBEGIN{ case 'D': usbfsdebug++; break; case 'd': usbdebug++; as = seprint(as, ae, " -d"); break; case 'm': mnt = EARGF(usage()); break; case 's': srv = EARGF(usage()); default: usage(); }ARGEND rfork(RFNOTEG); threadsetgrp(threadid()); fmtinstall('U', Ufmt); usbfsinit(srv, mnt, &usbdirfs, MAFTER|MCREATE); startdevs(args, argv, argc, matchether, nil, ethermain); threadexits(nil); } exiting != 0){ free(bp); break; } e->nout++; if(e->bwrite(e, bp) < 0) e->noerrs++; if(isloopback(e, bp) && e->exiting == 0) sendp(e->rc, bp); /* send to input queue */ else freebuf(e, bp); } deprint(2, "%s: writeproc exiting\n", argv0); closedev(e->dev); } static void setbuftype(Buf *bp) { uchar *p; bp->type = 0; if(bp->ndata >= Ehdrsize){ p = bp->rp + Eaddrlen*2; bp->type = p[0]<<8 | p[1]; } } static void etherexiting(Ether *e) { devctl(e->dev, "detach"); e->eusb/ether/mkfile 664 0 0 575 11231031610 11731ustar00nemosys #include #include #include "usb.h" #include "usbfs.h" #include "ibuddy.h" enum { /* Qids. Maintain order (relative to dirtabs structs) */ Qroot = 0, Qctl, Qmax, }; typedef struct Dirtab Dirtab; struct Dirtab { char *name; int mode; }; static Dirtab dirtab[] = { [Qroot] "/", DMDIR|0555, [Qctl] "ctl", 0664, }; int ibuddydebug; /* actions are written to the control end point */ /* 8th byte holds the ibuddy's actions */ /* WARNING: a bit set to 1 means OFF, 0 means ON */ /* DO NOT activate IBRight and IBLeft simultaneosly */ /* DO NOT activate IBOpen and IBClose simultaneosly */ /* IF YOU DO SO, YOU WILL DESTROY THE MOTORS */ /* THE CODE IS EXTRA PARANOID REGARDING THAT */ uchar ibuddymsg[8] = {0x55, 0x53, 0x42, 0x43, 0x00, 0x40, 0x02, 0x00}; uchar ibuddysetup[8] = {0x22, 0x09, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00}; static int usage(void) { werrstr("usage: usb/ibuddy [-d]"); return -1; } static int checkmotors(uchar aux) { if((aux & (1< 7) return -1; if(on) aux = bud->status & ~(1<status | (1<dev, Rh2d|Rclass|Riface, Rsetconf, 2, 1, ibuddymsg, 8); ibuddymsg[7] = IBOff; if(ret < 0){ werrstr("io error"); return -1; } bud->status = aux; dsprint(2, "ibuddy: ibuddycmd: command done, status: %b\n", aux); return ret; } static int ibuddylights(Ibuddy *bud, int light , int on) { switch(light){ case IBRed: case IBGreen: case IBBlue: case IBHeart: return ibuddycmd(bud, light, on); default: werrstr(Ebadctl); return -1; } } static int ibuddyhips(Ibuddy *bud, int right) { int ret; uchar aux; if(right){ aux = bud->status | (1<status | (1<dev, Rh2d|Rclass|Riface, Rsetconf, 2, 1, ibuddymsg, 8); ibuddymsg[7] = IBOff; if(ret < 0){ werrstr("io error"); return -1; } bud->status = aux; sleep(IBTwisttime); dsprint(2, "ibuddy: ibuddyhips: command done: %b\n", aux); return ret; } static int ibuddywings(Ibuddy *bud, int open) { int ret; uchar aux; if(open){ aux = bud->status | (1<status | (1<dev, Rh2d|Rclass|Riface, Rsetconf, 2, 1, ibuddymsg, 8); ibuddymsg[7] = IBOff; if(ret < 0){ werrstr("io error"); return -1; } bud->status = aux; sleep(IBTwisttime); dsprint(2, "ibuddy: ibuddywings: command done: %b\n", aux); return ret; } /* test the interface's functions */ static void ibuddytest(Ibuddy *bud) { int i; for(i=0; i<20; i++){ ibuddyhips(bud, 1); ibuddyhips(bud, 0); } sleep(5*1000); for(i=0; i<20; i++){ ibuddywings(bud, 1); ibuddywings(bud, 0); } sleep(5*1000); ibuddylights(bud, IBRed, 1); sleep(1000); ibuddylights(bud, IBGreen, 1); sleep(1000); ibuddylights(bud, IBBlue, 1); sleep(1000); ibuddylights(bud, IBHeart, 1); sleep(1000); ibuddylights(bud, IBRed, 0); sleep(1000); ibuddylights(bud, IBGreen, 0); sleep(1000); ibuddylights(bud, IBBlue, 0); sleep(1000); ibuddylights(bud, IBHeart, 0); } static char * ibuddydumpstate(Ibuddy *bud, char * buf, int len) { int w; w = 0; if((bud->status & (1<status & (1<status & (1<status & (1<status & (1<status & (1<status & (1<status & (1<qid; if((qid.type & QTDIR) == 0){ werrstr("walk in non-directory"); return -1; } if(qid.path != (Qroot | fs->qid)){ werrstr("qid must be Qroot!"); return -1; } if(strcmp(name, "..") == 0 || strcmp(name, ".") == 0){ /*can only be the root */ fid->qid.path = Qroot | fs->qid; fid->qid.vers = 0; fid->qid.type = QTDIR; return 0; } if(strcmp(name, "ctl") == 0){ qid.path = Qctl | fs->qid; qid.vers = 0; qid.type = dirtab[Qctl].mode >> 24; fid->qid = qid; return 0; } werrstr(Enotfound); return -1; } static int dstat(Usbfs *fs, Qid qid, Dir *d) { int path; Ibuddy *bud; Dirtab *t; path = qid.path & ~fs->qid; t = &dirtab[path]; d->qid.path = path; d->qid.type = t->mode >> 24; d->mode = t->mode; /* if it's the root, the name is the FS' name */ bud = fs->aux; if(strcmp(t->name, "/") == 0) d->name = t->name; else snprint(d->name, Namesz, t->name, bud->fs.name); d->length = 0; d->qid.path |= fs->qid; return 0; } static int dopen(Usbfs *fs, Fid *fid, int) { ulong path; path = fid->qid.path & ~fs->qid; switch(path){ case Qroot: dsprint(2, "ibuddy, opened root\n"); break; case Qctl: dsprint(2, "ibuddy, opened ctl\n"); break; default: werrstr(Enotfound); return -1; } 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 *) { Dirtab *tab; i++; /* skip root */ if(i < nelem(dirtab)) tab = &dirtab[i]; else return -1; filldir(fs, d, tab, i); return 0; } static long dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset) { ulong path; char *buf, *err; /* change */ char *e; Qid q; Ibuddy *bud; q = fid->qid; path = fid->qid.path & ~fs->qid; bud = fs->aux; buf = emallocz(255, 1); err = emallocz(255, 1); qlock(bud); switch(path){ case Qroot: count = usbdirread(fs, q, data, count, offset, dirgen, nil); break; case Qctl: if(offset != 0){ count = 0; break; } e = ibuddydumpstate(bud, buf, 255); count = usbreadbuf(data, count, 0, buf, e - buf); break; default: werrstr(Ebadfid); count = -1; } qunlock(bud); free(err); free(buf); return count; } static int ibuddyprocessline(Ibuddy *bud, char *cmd) { char *tok[3]; int n; int light; n = tokenize(cmd, tok, 3); if(n != 2) return -1; if(strcmp(tok[0], "red") == 0) light = IBRed; else if(strcmp(tok[0], "blue") == 0) light = IBBlue; else if(strcmp(tok[0], "green") == 0) light = IBGreen; else if(strcmp(tok[0], "heart") == 0) light = IBHeart; else light = -1; if(light != -1){ if(strcmp(tok[1], "on") == 0) return ibuddylights(bud, light , 1); else if(strcmp(tok[1], "off") == 0) return ibuddylights(bud, light , 0); else{ werrstr(Ebadctl); return -1; } } if(strcmp(tok[0], "hips") == 0){ if(strcmp(tok[1], "right") == 0) return ibuddyhips(bud, 1); else if(strcmp(tok[1], "left") == 0) return ibuddyhips(bud, 0); else{ werrstr(Ebadctl); return -1; } } if(strcmp(tok[0], "wings") == 0){ if(strcmp(tok[1], "open") == 0) return ibuddywings(bud, 1); else if(strcmp(tok[1], "close") == 0) return ibuddywings(bud, 0); else{ werrstr(Ebadctl); return -1; } } werrstr(Ebadctl); return -1; } static int ibuddyprocesscmds(Ibuddy *bud, char *cmd) { char *tok[6]; int n; int i; n = gettokens(cmd, tok, 6, "\n"); for(i=0; i < n; i++){ if(ibuddyprocessline(bud, tok[i]) < 0) return -1; } return 0; } static long dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong) { ulong path; char *cmd; Ibuddy *bud; bud = fs->aux; path = fid->qid.path & ~fs->qid; qlock(bud); switch(path){ case Qctl: cmd = emallocz(count+1, 1); memmove(cmd, buf, count); cmd[count] = 0; if(ibuddyprocesscmds(bud, cmd) < 0){ /* errstr written in the call */ qunlock(bud); free(cmd); return -1; } free(cmd); break; default: werrstr(Eperm); count = -1; } qunlock(bud); return count; } static Usbfs ibuddylfs = { .walk = dwalk, .open = dopen, .read = dread, .write= dwrite, .stat = dstat, }; static void ibuddydevfree(void *a) { Ibuddy *bud = a; if(bud == nil) return; dsprint(2, "ibuddy: ibuddydevfree: freeing\n"); bud->dev = nil; free(bud); } int ibuddymain(Dev *dev, int argc, char* argv[]) { Ibuddy *bud; int res; ARGBEGIN{ case 'd': ibuddydebug++; break; default: return usage(); }ARGEND if(argc != 0) return usage(); bud = dev->aux = emallocz(sizeof(Ibuddy), 1); bud->dev = dev; dev->free = ibuddydevfree; /* reset the ibuddy */ res = usbcmd(dev, Rh2d|Rclass|Riface, Rsetconf, 2, 1, ibuddysetup, 8); if(res < 0){ dsprint(2, "ibuddy: ibuddymain: reset usbcmd returned: %d\n", res); return -1; } /* set to: right, close wings, and no lights */ bud->status = IBOff; ibuddywings(bud, 0); ibuddyhips(bud, 1); ibuddylights(bud, IBRed, 0); ibuddylights(bud, IBGreen, 0); ibuddylights(bud, IBBlue, 0); ibuddylights(bud, IBHeart, 0); bud->fs = ibuddylfs; snprint(bud->fs.name, sizeof(bud->fs.name), "ibuddy%d", dev->id); bud->fs.dev = dev; incref(dev); bud->fs.aux = bud; usbfsadd(&bud->fs); closedev(dev); return 0; } multaneosly */ /* DO NOT activate IBOpen and IBClose simultaneosly */ /* IF YOU DO SO, YOU WILL DESTROY THE MOTORS */ /* THE CODE IS EXTRA PARANOID REGARDING THAT */ uchar ibuddymsg[8] = {0x55, 0x53, 0x42, 0x43, 0x00, 0x40, 0x02, 0x00}; uchar ibuddysetup[8] = {0x22, 0x09, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00}; static int usage(void) { werrstr("usage: usb/ibuddy [-d]"); return -1; } static int checkmotors(uchar aux) { if((aux & (1<status | (1< #include #include #include "usb.h" #include "usbfs.h" #include "ibuddy.h" typedef struct Parg Parg; enum { Arglen = 80, }; static void usage(void) { fprint(2, "usage: %s [-d] [-a n] [dev...]\n", argv0); threadexitsall("usage"); } /* returns 0 if it matches with the dev */ static int matchibuddy(char *info, void*) { char buf[50]; snprint(buf, sizeof(buf), "vid %#06x did %#06x", Ibuddyvid, Ibuddydid); dsprint(2, "ibuddy: %s %s\n", buf, info); if(strstr(info, buf) != nil){ return 0; } return -1; } void threadmain(int argc, char **argv) { char args[Arglen]; char *mnt; char *srv; char *as; char *ae; mnt = "/dev"; srv = nil; quotefmtinstall(); ae = args+sizeof(args); as = seprint(args, ae, "ibuddy"); ARGBEGIN{ case 'D': usbfsdebug++; break; case 'd': usbdebug++; as = seprint(as, ae, " -d"); break; case 'm': mnt = EARGF(usage()); break; case 's': srv = EARGF(usage()); break; default: usage(); }ARGEND; rfork(RFNOTEG); fmtinstall('U', Ufmt); threadsetgrp(threadid()); usbfsinit(srv, mnt, &usbdirfs, MAFTER|MCREATE); startdevs(args, argv, argc, matchibuddy, nil, ibuddymain); threadexits(nil); } ydumpstate(Ibuddy *bud, char * buf, int len) { int w; w = 0; if((bud->status & (1<status & (1<status & (1< #include #include #include "usb.h" #include "hid.h" enum { Awakemsg=0xdeaddead, Diemsg = 0xbeefbeef, }; typedef struct KDev KDev; typedef struct Kin Kin; struct KDev { Dev* dev; /* usb device*/ Dev* ep; /* endpoint to get events */ Kin* in; /* used to send events to kernel */ Channel*repeatc; /* only for keyboard */ int accel; /* only for mouse */ }; /* * Kbdin and mousein files must be shared among all instances. */ struct Kin { int ref; int fd; char* name; }; /* * Map for the logitech bluetooth mouse with 8 buttons and wheels. * { ptr ->mouse} * { 0x01, 0x01 }, // left * { 0x04, 0x02 }, // middle * { 0x02, 0x04 }, // right * { 0x40, 0x08 }, // up * { 0x80, 0x10 }, // down * { 0x10, 0x08 }, // side up * { 0x08, 0x10 }, // side down * { 0x20, 0x02 }, // page * besides wheel and regular up/down report the 4th byte as 1/-1 */ /* * key code to scan code; for the page table used by * the logitech bluetooth keyboard. */ static char sctab[256] = { [0x00] 0x0, 0x0, 0x0, 0x0, 0x1e, 0x30, 0x2e, 0x20, [0x08] 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, [0x10] 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14, [0x18] 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x2, 0x3, [0x20] 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, [0x28] 0x1c, 0x1, 0xe, 0xf, 0x39, 0xc, 0xd, 0x1a, [0x30] 0x1b, 0x2b, 0x2b, 0x27, 0x28, 0x29, 0x33, 0x34, [0x38] 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, [0x40] 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0x63, 0x46, [0x48] 0x77, 0x52, 0x47, 0x49, 0x53, 0x4f, 0x51, 0x4d, [0x50] 0x4b, 0x50, 0x48, 0x45, 0x35, 0x37, 0x4a, 0x4e, [0x58] 0x1c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47, [0x60] 0x48, 0x49, 0x52, 0x53, 0x56, 0x7f, 0x74, 0x75, [0x68] 0x55, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, [0x70] 0x78, 0x79, 0x7a, 0x7b, 0x0, 0x0, 0x0, 0x0, [0x78] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71, [0x80] 0x73, 0x72, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x0, [0x88] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0x90] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0x98] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0xa0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0xa8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0xb0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0xb8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0xc0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0xc8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0xd0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0xd8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0xe0] 0x1d, 0x2a, 0x38, 0x7d, 0x61, 0x36, 0x64, 0x7e, [0xe8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x73, 0x72, 0x71, [0xf0] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0xf8] 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, }; static QLock inlck; static Kin kbdin = { .ref = 0, .name = "#Ι/kbin", .fd = -1, }; static Kin ptrin = { .ref = 0, .name = "#m/mousein", .fd = -1, }; static int kbdebug; static void kbfatal(KDev *kd, char *sts) { Dev *dev; if(sts != nil) fprint(2, "kb: fatal: %s\n", sts); else fprint(2, "kb: exiting\n"); if(kd->repeatc != nil) nbsendul(kd->repeatc, Diemsg); dev = kd->dev; kd->dev = nil; if(kd->ep != nil) closedev(kd->ep); kd->ep = nil; devctl(dev, "detach"); closedev(dev); /* * free(kd); done by closedev. */ threadexits(sts); } static int scale(KDev *f, int x) { int sign = 1; if(x < 0){ sign = -1; x = -x; } switch(x){ case 0: case 1: case 2: case 3: break; case 4: x = 6 + (f->accel>>2); break; case 5: x = 9 + (f->accel>>1); break; default: x *= MaxAcc; break; } return sign*x; } /* * ps2 mouse is processed mostly at interrupt time. * for usb we do what we can. */ static void sethipri(void) { char fn[30]; int fd; snprint(fn, sizeof(fn), "/proc/%d/ctl", getpid()); fd = open(fn, OWRITE); if(fd < 0) return; fprint(fd, "pri 13"); close(fd); } static void ptrwork(void* a) { static char maptab[] = {0x0, 0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7}; int x, y, b, c, ptrfd; int mfd; char buf[32]; char mbuf[80]; KDev* f = a; int hipri; hipri = 0; ptrfd = f->ep->dfd; mfd = f->in->fd; if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf) kbfatal(f, "weird mouse maxpkt"); for(;;){ memset(buf, 0, sizeof buf); if(f->ep == nil) kbfatal(f, nil); c = read(ptrfd, buf, f->ep->maxpkt); assert(f->dev != nil); assert(f->ep != nil); if(c < 0) dprint(2, "kb: mouse: %s: read: %r\n", f->ep->dir); if(c <= 0) kbfatal(f, nil); if(c < 3) continue; if(f->accel){ x = scale(f, buf[1]); y = scale(f, buf[2]); }else{ x = buf[1]; y = buf[2]; } b = maptab[buf[0] & 0x7]; if(c > 3 && buf[3] == 1) /* up */ b |= 0x08; if(c > 3 && buf[3] == -1) /* down */ b |= 0x10; if(kbdebug) fprint(2, "kb: m%11d %11d %11d\n", x, y, b); seprint(mbuf, mbuf+sizeof(mbuf), "m%11d %11d %11d", x, y,b); if(write(mfd, mbuf, strlen(mbuf)) < 0) kbfatal(f, "mousein i/o"); if(hipri == 0){ sethipri(); hipri = 1; } } } static void stoprepeat(KDev *f) { sendul(f->repeatc, Awakemsg); } static void startrepeat(KDev *f, uchar esc1, uchar sc) { ulong c; if(esc1) c = SCesc1 << 8 | (sc & 0xff); else c = sc; sendul(f->repeatc, c); } static void putscan(int kbinfd, uchar esc, uchar sc) { uchar s[2] = {SCesc1, 0}; if(sc == 0x41){ kbdebug++; return; } if(sc == 0x42){ kbdebug = 0; return; } if(kbdebug) fprint(2, "sc: %x %x\n", (esc? SCesc1: 0), sc); s[1] = sc; if(esc && sc != 0) write(kbinfd, s, 2); else if(sc != 0) write(kbinfd, s+1, 1); } static void repeatproc(void* a) { KDev *f; Channel *repeatc; int kbdinfd; ulong l, t, i; uchar esc1, sc; /* * too many jumps here. * Rewrite instead of debug, if needed. */ f = a; repeatc = f->repeatc; kbdinfd = f->in->fd; l = Awakemsg; Repeat: if(l == Diemsg) goto Abort; while(l == Awakemsg) l = recvul(repeatc); if(l == Diemsg) goto Abort; esc1 = l >> 8; sc = l; t = 160; for(;;){ for(i = 0; i < t; i += 5){ if(l = nbrecvul(repeatc)) goto Repeat; sleep(5); } putscan(kbdinfd, esc1, sc); t = 30; } Abort: chanfree(repeatc); threadexits("aborted"); } #define hasesc1(sc) (((sc) > 0x47) || ((sc) == 0x38)) static void putmod(int fd, uchar mods, uchar omods, uchar mask, uchar esc, uchar sc) { /* BUG: Should be a single write */ if((mods&mask) && !(omods&mask)) putscan(fd, esc, sc); if(!(mods&mask) && (omods&mask)) putscan(fd, esc, Keyup|sc); } /* * This routine diffs the state with the last known state * and invents the scan codes that would have been sent * by a non-usb keyboard in that case. This also requires supplying * the extra esc1 byte as well as keyup flags. * The aim is to allow future addition of other keycode pages * for other keyboards. */ static uchar putkeys(KDev *f, uchar buf[], uchar obuf[], int n, uchar dk) { int i, j; uchar uk; int fd; fd = f->in->fd; putmod(fd, buf[0], obuf[0], Mctrl, 0, SCctrl); putmod(fd, buf[0], obuf[0], (1<dev->usb->ep[eid]->iface->id; return usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0); } /* * Try to recover from a babble error. A port reset is the only way out. * BUG: we should be careful not to reset a bundle with several devices. */ static void recoverkb(KDev *f) { int i; char buf[5]; close(f->dev->dfd); /* it's for usbd now */ devctl(f->dev, "reset"); for(i = 0; i < 10; i++){ sleep(500); if(opendevdata(f->dev, ORDWR) >= 0){ setbootproto(f, f->ep->id); break; } /* else usbd still working... */ } } static void kbdwork(void *a) { int c, i, kbdfd, nerrs; uchar dk, buf[64], lbuf[64]; char err[128]; KDev *f = a; kbdfd = f->ep->dfd; if(f->ep->maxpkt < 3 || f->ep->maxpkt > sizeof buf) kbfatal(f, "weird maxpkt"); f->repeatc = chancreate(sizeof(ulong), 0); if(f->repeatc == nil) kbfatal(f, "chancreate failed"); proccreate(repeatproc, f, Stack); memset(lbuf, 0, sizeof lbuf); dk = nerrs = 0; for(;;){ memset(buf, 0, sizeof buf); c = read(kbdfd, buf, f->ep->maxpkt); assert(f->dev != nil); assert(f->ep != nil); if(c < 0){ rerrstr(err, sizeof(err)); dprint(2, "kb: %s: read: %s\n", f->ep->dir, err); if(strstr(err, "babble") != 0 && ++nerrs < 3){ recoverkb(f); continue; } } if(c <= 0) kbfatal(f, nil); if(c < 3) continue; if(kbdbusy(buf + 2, c - 2)) continue; if(usbdebug > 1 || kbdebug > 1){ fprint(2, "kbd mod %x: ", buf[0]); for(i = 2; i < c; i++) fprint(2, "kc %x ", buf[i]); fprint(2, "\n"); } dk = putkeys(f, buf, lbuf, f->ep->maxpkt, dk); memmove(lbuf, buf, c); nerrs = 0; } } static void freekdev(void *a) { KDev *kd; kd = a; if(kd->in != nil){ qlock(&inlck); if(--kd->in->ref == 0){ close(kd->in->fd); kd->in->fd = -1; } qunlock(&inlck); } dprint(2, "freekdev\n"); free(kd); } static void kbstart(Dev *d, Ep *ep, Kin *in, void (*f)(void*), int accel) { KDev *kd; qlock(&inlck); if(in->fd < 0){ in->fd = open(in->name, OWRITE); if(in->fd < 0){ fprint(2, "kb: %s: %r\n", in->name); qunlock(&inlck); return; } } in->ref++; /* for kd->in = in */ qunlock(&inlck); kd = d->aux = emallocz(sizeof(KDev), 1); d->free = freekdev; kd->in = in; kd->dev = d; if(setbootproto(kd, ep->id) < 0){ fprint(2, "kb: %s: bootproto: %r\n", d->dir); return; } kd->accel = accel; kd->ep = openep(d, ep->id); if(kd->ep == nil){ fprint(2, "kb: %s: openep %d: %r\n", d->dir, ep->id); return; } if(opendevdata(kd->ep, OREAD) < 0){ fprint(2, "kb: %s: opendevdata: %r\n", kd->ep->dir); closedev(kd->ep); kd->ep = nil; return; } incref(d); proccreate(f, kd, Stack); } static int usage(void) { werrstr("usage: usb/kb [-dkmn] [-a n]"); return -1; } int kbmain(Dev *d, int argc, char* argv[]) { int i; int kena; int pena; int accel; char *as; Usbdev *ud; Ep *ep; kena = pena = 1; accel = 0; ARGBEGIN{ case 'a': as = ARGF(); if(as == nil) return usage(); accel = strtol(as, nil, 0); break; case 'd': kbdebug++; break; case 'k': kena = 1; pena = 0; break; case 'm': kena = 0; pena = 1; break; default: return usage(); }ARGEND; if(argc != 0){ return usage(); } ud = d->usb; d->aux = nil; dprint(2, "kb: main: dev %s ref %ld\n", d->dir, d->ref); for(i = 0; i < nelem(ud->ep); i++){ if((ep = ud->ep[i]) == nil) break; if(kena && ep->type == Eintr && ep->dir == Ein) if(ep->iface->csp == KbdCSP) kbstart(d, ep, &kbdin, kbdwork, accel); if(pena && ep->type == Eintr && ep->dir == Ein) if(ep->iface->csp == PtrCSP) kbstart(d, ep, &ptrin, ptrwork, accel); } closedev(d); return 0; } 0x3, [0x20] 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, [0x28] 0x1c, 0x1, 0xe, 0xf, 0x39, 0xc, 0xd, 0x1a, [0x30] 0x1b, 0x2b, 0x2b, 0x27, 0x28, 0x29, 0x33, 0x34, [0x38] 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, [0x40] 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0x63, 0x46, [0x48] 0x77, 0x52, 0x47, 0x49, 0x53, 0x4f, 0x51, 0x4d, [0x50] 0x4b, 0x50, 0x48, 0x45, 0x35, 0x37, 0x4a, 0x4e, [0x58] 0x1c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47, [0x60] 0x48, 0x49, 0x52, 0x53, 0x56, 0x7f, 0x74, 0x75, [usb/kb/main.c 664 0 0 1743 11176073566 11162ustar00nemosys#include #include #include #include "usb.h" #include "hid.h" typedef struct Parg Parg; enum { Ndevs = 10, Arglen = 80, Nargs = 10, }; static void usage(void) { fprint(2, "usage: %s [-dkm] [-a n] [dev...]\n", argv0); threadexitsall("usage"); } void threadmain(int argc, char **argv) { char args[Arglen]; char *as; char *ae; int accel; int pena; int csps[] = { KbdCSP, PtrCSP, 0 }; quotefmtinstall(); pena = 1; ae = args+sizeof(args); as = seprint(args, ae, "kb"); ARGBEGIN{ case 'a': accel = strtol(EARGF(usage()), nil, 0); as = seprint(as, ae, " -a %d", accel); break; case 'd': usbdebug++; /* fall */ case 'k': as = seprint(as, ae, " -k"); pena = 0; break; case 'm': as = seprint(as, ae, " -m"); pena = 1; break; default: usage(); }ARGEND; rfork(RFNOTEG); fmtinstall('U', Ufmt); threadsetgrp(threadid()); if(pena == 0) csps[1] = 0; startdevs(args, argv, argc, matchdevcsp, csps, kbmain); threadexits(nil); } (KDev *f, int x) { int sign usb/kb/mkfile 664 0 0 573 11152121031 11213ustar00nemosys #include #include #include "usb.h" /* * epN.M -> N */ static int nameid(char *s) { char *r; char nm[20]; r = strrchr(s, 'p'); if(r == nil) return -1; strecpy(nm, nm+sizeof(nm), r+1); r = strchr(nm, '.'); if(r == nil) return -1; *r = 0; return atoi(nm); } Dev* openep(Dev *d, int id) { char *mode; /* How many modes? */ Ep *ep; Altc *ac; Dev *epd; Usbdev *ud; char name[40]; if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0) return nil; if(d->cfd < 0 || d->usb == nil){ werrstr("device not configured"); return nil; } ud = d->usb; if(id < 0 || id >= nelem(ud->ep) || ud->ep[id] == nil){ werrstr("bad enpoint number"); return nil; } ep = ud->ep[id]; mode = "rw"; if(ep->dir == Ein) mode = "r"; if(ep->dir == Eout) mode = "w"; snprint(name, sizeof(name), "/dev/usb/ep%d.%d", d->id, id); if(access(name, AEXIST) == 0){ dprint(2, "%s: %s already exists; trying to open\n", argv0, name); epd = opendev(name); if(epd != nil) epd->maxpkt = ep->maxpkt; /* guess */ return epd; } if(devctl(d, "new %d %d %s", id, ep->type, mode) < 0){ dprint(2, "%s: %s: new: %r\n", argv0, d->dir); return nil; } epd = opendev(name); if(epd == nil) return nil; epd->id = id; if(devctl(epd, "maxpkt %d", ep->maxpkt) < 0) fprint(2, "%s: %s: openep: maxpkt: %r\n", argv0, epd->dir); else dprint(2, "%s: %s: maxpkt %d\n", argv0, epd->dir, ep->maxpkt); epd->maxpkt = ep->maxpkt; ac = ep->iface->altc[0]; if(ep->ntds > 1 && devctl(epd, "ntds %d", ep->ntds) < 0) fprint(2, "%s: %s: openep: ntds: %r\n", argv0, epd->dir); else dprint(2, "%s: %s: ntds %d\n", argv0, epd->dir, ep->ntds); /* * For iso endpoints and high speed interrupt endpoints the pollival is * actually 2ⁿ and not n. * The kernel usb driver must take that into account. * It's simpler this way. */ if(ac != nil && (ep->type == Eintr || ep->type == Eiso) && ac->interval != 0) if(devctl(epd, "pollival %d", ac->interval) < 0) fprint(2, "%s: %s: openep: pollival: %r\n", argv0, epd->dir); return epd; } Dev* opendev(char *fn) { Dev *d; int l; if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0) return nil; d = emallocz(sizeof(Dev), 1); incref(d); l = strlen(fn); d->dfd = -1; /* * +30 to allocate extra size to concat "/" * we should probably remove that feature from the manual * and from the code after checking out that nobody relies on * that. */ d->dir = emallocz(l + 30, 0); strcpy(d->dir, fn); strcpy(d->dir+l, "/ctl"); d->cfd = open(d->dir, ORDWR|OCEXEC); d->dir[l] = 0; d->id = nameid(fn); if(d->cfd < 0){ werrstr("can't open endpoint %s: %r", d->dir); free(d->dir); free(d); return nil; } dprint(2, "%s: opendev %#p %s\n", argv0, d, fn); return d; } int opendevdata(Dev *d, int mode) { char buf[80]; /* more than enough for a usb path */ seprint(buf, buf+sizeof(buf), "%s/data", d->dir); d->dfd = open(buf, mode|OCEXEC); return d->dfd; } enum { /* Max device conf is also limited by max control request size * as limited by the kernel usb.h. * (both limits are arbitrary). */ Maxdevconf = 16 * 1024 }; int loaddevconf(Dev *d, int n) { uchar *buf; int nr; int type; if(n >= nelem(d->usb->conf)){ werrstr("loaddevconf: bug: out of configurations in device"); fprint(2, "%s: %r\n", argv0); return -1; } buf = emallocz(Maxdevconf, 0); type = Rd2h|Rstd|Rdev; nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, Maxdevconf); if(nr < Dconflen){ free(buf); return -1; } if(d->usb->conf[n] == nil) d->usb->conf[n] = emallocz(sizeof(Conf), 1); nr = parseconf(d->usb, d->usb->conf[n], buf, nr); free(buf); return nr; } Ep* mkep(Usbdev *d, int id) { Ep *ep; d->ep[id] = ep = emallocz(sizeof(Ep), 1); ep->id = id; return ep; } static char* mkstr(uchar *b, int n) { Rune r; char *us; char *s; char *e; if(n <= 2 || (n & 1) != 0) return strdup("none"); n = (n - 2)/2; b += 2; us = s = emallocz(n*UTFmax+1, 0); e = s + n*UTFmax+1; for(; --n >= 0; b += 2){ r = GET2(b); s = seprint(s, e, "%C", r); } return us; } char* loaddevstr(Dev *d, int sid) { uchar buf[128]; int type; int nr; if(sid == 0) return estrdup("none"); type = Rd2h|Rstd|Rdev; nr=usbcmd(d, type, Rgetdesc, Dstr<<8|sid, 0, buf, sizeof(buf)); return mkstr(buf, nr); } int loaddevdesc(Dev *d) { uchar buf[Ddevlen+255]; int nr; int type; Ep *ep0; type = Rd2h|Rstd|Rdev; nr = sizeof(buf); memset(buf, 0, Ddevlen); if((nr=usbcmd(d, type, Rgetdesc, Ddev<<8|0, 0, buf, nr)) < 0) return -1; /* * Several hubs are returning descriptors of 17 bytes, not 18. * We accept them and leave number of configurations as zero. * (a get configuration descriptor also fails for them!) */ if(nr < Ddevlen){ print("%s: %s: warning: device with short descriptor\n", argv0, d->dir); if(nr < Ddevlen-1){ werrstr("short device descriptor (%d bytes)", nr); return -1; } } d->usb = emallocz(sizeof(Usbdev), 1); ep0 = mkep(d->usb, 0); ep0->dir = Eboth; ep0->type = Econtrol; ep0->maxpkt = d->maxpkt = 8; /* a default */ nr = parsedev(d, buf, nr); if(nr >= 0){ d->usb->vendor = loaddevstr(d, d->usb->vsid); if(strcmp(d->usb->vendor, "none") != 0){ d->usb->product = loaddevstr(d, d->usb->psid); d->usb->serial = loaddevstr(d, d->usb->ssid); } } return nr; } int configdev(Dev *d) { int i; if(d->dfd < 0) opendevdata(d, ORDWR); if(loaddevdesc(d) < 0) return -1; for(i = 0; i < d->usb->nconf; i++) if(loaddevconf(d, i) < 0) return -1; return 0; } static void closeconf(Conf *c) { int i; int a; if(c == nil) return; for(i = 0; i < nelem(c->iface); i++) if(c->iface[i] != nil){ for(a = 0; a < nelem(c->iface[i]->altc); a++) free(c->iface[i]->altc[a]); free(c->iface[i]); } free(c); } void closedev(Dev *d) { int i; Usbdev *ud; if(d==nil || decref(d) != 0) return; dprint(2, "%s: closedev %#p %s\n", argv0, d, d->dir); if(d->free != nil) d->free(d->aux); if(d->cfd >= 0) close(d->cfd); if(d->dfd >= 0) close(d->dfd); d->cfd = d->dfd = -1; free(d->dir); d->dir = nil; ud = d->usb; d->usb = nil; if(ud != nil){ free(ud->vendor); free(ud->product); free(ud->serial); for(i = 0; i < nelem(ud->ep); i++) free(ud->ep[i]); for(i = 0; i < nelem(ud->ddesc); i++) free(ud->ddesc[i]); for(i = 0; i < nelem(ud->conf); i++) closeconf(ud->conf[i]); free(ud); } free(d); } static char* reqstr(int type, int req) { char *s; static char* ds[] = { "dev", "if", "ep", "oth" }; static char buf[40]; if(type&Rd2h) s = seprint(buf, buf+sizeof(buf), "d2h"); else s = seprint(buf, buf+sizeof(buf), "h2d"); if(type&Rclass) s = seprint(s, buf+sizeof(buf), "|cls"); else if(type&Rvendor) s = seprint(s, buf+sizeof(buf), "|vnd"); else s = seprint(s, buf+sizeof(buf), "|std"); s = seprint(s, buf+sizeof(buf), "|%s", ds[type&3]); switch(req){ case Rgetstatus: s = seprint(s, buf+sizeof(buf), " getsts"); break; case Rclearfeature: s = seprint(s, buf+sizeof(buf), " clrfeat"); break; case Rsetfeature: s = seprint(s, buf+sizeof(buf), " setfeat"); break; case Rsetaddress: s = seprint(s, buf+sizeof(buf), " setaddr"); break; case Rgetdesc: s = seprint(s, buf+sizeof(buf), " getdesc"); break; case Rsetdesc: s = seprint(s, buf+sizeof(buf), " setdesc"); break; case Rgetconf: s = seprint(s, buf+sizeof(buf), " getcnf"); break; case Rsetconf: s = seprint(s, buf+sizeof(buf), " setcnf"); break; case Rgetiface: s = seprint(s, buf+sizeof(buf), " getif"); break; case Rsetiface: s = seprint(s, buf+sizeof(buf), " setif"); break; } USED(s); return buf; } static int cmdreq(Dev *d, int type, int req, int value, int index, uchar *data, int count) { int ndata, n; uchar *wp; uchar buf[8]; char *hd, *rs; assert(d != nil); if(data == nil){ wp = buf; ndata = 0; }else{ ndata = count; wp = emallocz(8+ndata, 0); } wp[0] = type; wp[1] = req; PUT2(wp+2, value); PUT2(wp+4, index); PUT2(wp+6, count); if(data != nil) memmove(wp+8, data, ndata); if(usbdebug>2){ hd = hexstr(wp, ndata+8); rs = reqstr(type, req); fprint(2, "%s: %s val %d|%d idx %d cnt %d out[%d] %s\n", d->dir, rs, value>>8, value&0xFF, index, count, ndata+8, hd); free(hd); } n = write(d->dfd, wp, 8+ndata); if(wp != buf) free(wp); if(n < 0) return -1; if(n != 8+ndata){ dprint(2, "%s: cmd: short write: %d\n", argv0, n); return -1; } return n; } static int cmdrep(Dev *d, void *buf, int nb) { char *hd; nb = read(d->dfd, buf, nb); if(nb >0 && usbdebug > 2){ hd = hexstr(buf, nb); fprint(2, "%s: in[%d] %s\n", d->dir, nb, hd); free(hd); } return nb; } int usbcmd(Dev *d, int type, int req, int value, int index, uchar *data, int count) { int r; int i; int nerr; char err[64]; /* * Some devices do not respond to commands some times. * Others even report errors but later work just fine. Retry. */ r = -1; *err = 0; for(i = nerr = 0; i < Uctries; i++){ if(type&Rd2h) r = cmdreq(d, type, req, value, index, nil, count); else r = cmdreq(d, type, req, value, index, data, count); if(r > 0){ if((type&Rd2h) == 0) break; r = cmdrep(d, data, count); if(r > 0) break; if(r == 0) werrstr("no data from device"); } nerr++; if(*err == 0) rerrstr(err, sizeof(err)); sleep(Ucdelay); } if(r > 0 && i >= 2){ /* let the user know the device is not in good shape */ fprint(2, "%s: usbcmd: %s: required %d attempts (%s)\n", argv0, d->dir, i, err); } return r; } int unstall(Dev *dev, Dev *ep, int dir) { int r; if(dir == Ein) dir = 0x80; else dir = 0; r = Rh2d|Rstd|Rep; if(usbcmd(dev, r, Rclearfeature, Fhalt, ep->id|dir, nil, 0)<0){ werrstr("unstall: %s: %r", ep->dir); return -1; } if(devctl(ep, "clrhalt") < 0){ werrstr("clrhalt: %s: %r", ep->dir); return -1; } return 0; } /* * To be sure it uses a single write. */ int devctl(Dev *dev, char *fmt, ...) { char buf[128]; va_list arg; char *e; va_start(arg, fmt); e = vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); return write(dev->cfd, buf, e-buf); } usb/lib/devs.c 664 0 0 6114 11220630066 11326ustar00nemosys#include #include #include #include "usb.h" typedef struct Parg Parg; enum { Ndevs = 32, Arglen = 500, Nargs = 64, Stack = 16 * 1024, }; struct Parg { char* args; Dev* dev; int (*f)(Dev*,int,char**); Channel*rc; }; static void workproc(void *a) { Parg *pa; char args[Arglen]; char *argv[Nargs]; int argc; Channel *rc; Dev *d; int (*f)(Dev*,int,char**); pa = a; strecpy(args, args+sizeof(args), pa->args); /* don't leak */ d = pa->dev; f = pa->f; rc = pa->rc; free(pa->args); free(pa); argc = tokenize(args, argv, nelem(argv)-1); argv[argc] = nil; if(f(d, argc, argv) < 0){ closedev(d); fprint(2, "%s: devmain: %r\n", argv0); sendul(rc, -1); threadexits("devmain: %r"); } sendul(rc, 0); threadexits(nil); } int matchdevcsp(char *info, void *a) { char sbuf[40]; int *csps; csps = a; for(; *csps != 0; csps++){ snprint(sbuf, sizeof(sbuf), "csp %#08ux", *csps); if(strstr(info, sbuf) != nil) return 0; } return -1; } int finddevs(int (*matchf)(char*,void*), void *farg, char** dirs, int ndirs) { int fd; char fbuf[40]; char dbuf[512]; Dir *d; int nd, nr; int n; int i; char *nm; fd = open("/dev/usb", OREAD); if(fd < 0) sysfatal("/dev/usb: %r"); nd = dirreadall(fd, &d); close(fd); if(nd < 2) sysfatal("/dev/usb: no devs"); for(i = n = 0; i < nd && n < ndirs; i++){ nm = d[i].name; if(strcmp(nm, "ctl") == 0 || strstr(nm, ".0") == nil) continue; snprint(fbuf, sizeof(fbuf), "/dev/usb/%s/ctl", nm); fd = open(fbuf, OREAD); if(fd < 0) continue; /* may be gone */ nr = read(fd, dbuf, sizeof(dbuf)-1); close(fd); if(nr < 0) continue; dbuf[nr] = 0; if(strstr(dbuf, "enabled ") != nil && strstr(dbuf, " busy") == nil) if(matchf(dbuf, farg) == 0) dirs[n++] = smprint("/dev/usb/%s", nm); } free(d); if(usbdebug > 1) for(nd = 0; nd < n; nd++) fprint(2, "finddevs: %s\n", dirs[nd]); return n; } void startdevs(char *args, char *argv[], int argc, int (*mf)(char*,void*), void*ma, int (*df)(Dev*,int,char**)) { int i, ndirs, ndevs; char *dirs[Ndevs]; char **dp; Parg *parg; Dev *dev; Channel *rc; if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0) sysfatal("#u: %r"); if(argc > 0){ ndirs = argc; dp = argv; }else{ dp = dirs; ndirs = finddevs(mf, ma, dp, Ndevs); if(ndirs == nelem(dirs)) fprint(2, "%s: too many devices\n", argv0); } ndevs = 0; rc = chancreate(sizeof(ulong), 0); if(rc == nil) sysfatal("no memory"); for(i = 0; i < ndirs; i++){ fprint(2, "%s: startdevs: opening #%d %s\n", argv0, i, dp[i]); dev = opendev(dp[i]); if(dev != nil){ if(configdev(dev) < 0){ fprint(2, "%s: %s: config: %r\n", argv0, dp[i]); closedev(dev); }else{ dprint(2, "%s: %U", argv0, dev); parg = emallocz(sizeof(Parg), 0); parg->args = estrdup(args); parg->dev = dev; parg->rc = rc; parg->f = df; proccreate(workproc, parg, Stack); if(recvul(rc) == 0) ndevs++; } }else fprint(2, "%s: %s: %r\n", argv0, dp[i]); if(dp != argv) free(dirs[i]); } chanfree(rc); if(ndevs == 0) sysfatal("no device found"); } seconf(d->usb, d->usb->conf[n], buf, nr); free(buf); return nr; } Ep* mkep(Usbdev *d, int id) { Ep *ep; d->ep[id] = ep = emallocz(sizeof(Ep), 1); ep->id = id; return ep; } static char* mkstr(uchar *b, int n) { Rune r; char *us; char *s; char *e; if(n <= 2 || (n & 1) != 0) return strdup("none"); n = (n - 2)/2; b += 2; us = s = emallocz(n*UTFmax+1, 0); e = s + n*UTFmax+1; for(; --n >= 0; b += 2){ r = GET2(b); usb/lib/dump.c 664 0 0 6553 11220630131 11332ustar00nemosys#include #include #include #include #include "usb.h" int usbdebug; static char *edir[] = {"in", "out", "inout"}; static char *etype[] = {"ctl", "iso", "bulk", "intr"}; static char* cnames[] = { "none", "audio", "comms", "hid", "", "", "", "printer", "storage", "hub", "data" }; static char* devstates[] = { "detached", "attached", "enabled", "assigned", "configured" }; char* classname(int c) { static char buf[30]; if(c >= 0 && c < nelem(cnames)) return cnames[c]; else{ seprint(buf, buf+30, "%d", c); return buf; } } char* hexstr(void *a, int n) { int i; uchar *b; char *dbuff; char *s; char *e; b = a; dbuff = s = emallocz(1024, 0); *s = 0; e = s+1024; for(i=0; i < n; i++) s = seprint(s, e, " %.2ux", b[i]); if(s == e) fprint(2, "%s: usb/lib: hexdump: bug: small buffer\n", argv0); return dbuff; } static char* seprintiface(char *s, char *e, Iface *i) { int j; Altc *a; Ep *ep; char *eds, *ets; s = seprint(s, e, "\t\tiface csp %s.%uld.%uld\n", classname(Class(i->csp)), Subclass(i->csp), Proto(i->csp)); for(j = 0; j < Naltc; j++){ a=i->altc[j]; if(a == nil) break; s = seprint(s, e, "\t\t alt %d attr %d ival %d", j, a->attrib, a->interval); if(a->aux != nil) s = seprint(s, e, " devspec %p\n", a->aux); else s = seprint(s, e, "\n"); } for(j = 0; j < Nep; j++){ ep = i->ep[j]; if(ep == nil) break; eds = ets = ""; if(ep->dir <= nelem(edir)) eds = edir[ep->dir]; if(ep->type <= nelem(etype)) ets = etype[ep->type]; s = seprint(s, e, "\t\t ep id %d addr %d dir %s type %s" " itype %d maxpkt %d ntds %d\n", ep->id, ep->addr, eds, ets, ep->isotype, ep->maxpkt, ep->ntds); } return s; } static char* seprintconf(char *s, char *e, Usbdev *d, int ci) { int i; Conf *c; char *hd; c = d->conf[ci]; s = seprint(s, e, "\tconf: cval %d attrib %x %d mA\n", c->cval, c->attrib, c->milliamps); for(i = 0; i < Niface; i++) if(c->iface[i] == nil) break; else s = seprintiface(s, e, c->iface[i]); for(i = 0; i < Nddesc; i++) if(d->ddesc[i] == nil) break; else if(d->ddesc[i]->conf == c){ hd = hexstr((uchar*)&d->ddesc[i]->data, d->ddesc[i]->data.bLength); s = seprint(s, e, "\t\tdev desc %x[%d]: %s\n", d->ddesc[i]->data.bDescriptorType, d->ddesc[i]->data.bLength, hd); free(hd); } return s; } int Ufmt(Fmt *f) { int i; Dev *d; Usbdev *ud; char buf[1024]; char *s, *e; s = buf; e = buf+sizeof(buf); d = va_arg(f->args, Dev*); if(d == nil) return fmtprint(f, "\n"); s = seprint(s, e, "%s", d->dir); ud = d->usb; if(ud == nil) return fmtprint(f, "%s %ld refs\n", buf, d->ref); s = seprint(s, e, " csp %s.%uld.%uld", classname(Class(ud->csp)), Subclass(ud->csp), Proto(ud->csp)); s = seprint(s, e, " vid %#ux did %#ux", ud->vid, ud->did); s = seprint(s, e, " refs %ld\n", d->ref); s = seprint(s, e, "\t%s %s %s\n", ud->vendor, ud->product, ud->serial); for(i = 0; i < Nconf; i++){ if(ud->conf[i] == nil) break; else s = seprintconf(s, e, ud, i); } return fmtprint(f, "%s", buf); } char* estrdup(char *s) { char *d; d = strdup(s); if(d == nil) sysfatal("strdup: %r"); setmalloctag(d, getcallerpc(&s)); return d; } void* emallocz(ulong size, int zero) { void *x; x = malloc(size); if(x == nil) sysfatal("malloc: %r"); if(zero) memset(x, 0, size); setmalloctag(x, getcallerpc(&size)); return x; } nil) memmove(wp+8, data, ndata); if(usbdebug>2){ hd = hexstr(wp, ndata+8); rs = reqstr(type, req); fprint(2, "%s: %s val %d|%d idx %d cnt %dusb/lib/fs.c 664 0 0 30777 11200322560 11024ustar00nemosys/* * Framework for USB devices that provide a file tree. * The main process (usbd or the driver's main proc) * calls fsinit() to start FS operation. * * One or more drivers call fsstart/fsend to register * or unregister their operations for their subtrees. * * root dir has qids with 0 in high 32 bits. * for other files we keep the device id in there. * The low 32 bits for directories at / must be 0. */ #include #include #include #include #include "usb.h" #include "usbfs.h" #undef dprint #define dprint if(usbfsdebug)fprint typedef struct Rpc Rpc; enum { Nproc = 3, /* max nb. of cached FS procs */ Nofid = ~0, /* null value for fid number */ Notag = ~0, /* null value for tags */ Dietag = 0xdead, /* fake tag to ask outproc to die */ Stack = 16 * 1024, /* Fsproc requests */ Run = 0, /* call f(r) */ Exit, /* terminate */ }; struct Rpc { Fcall t; Fcall r; Fid* fid; int flushed; Rpc* next; char data[Bufsize]; }; int usbfsdebug; char Enotfound[] = "file not found"; char Etoosmall[] = "parameter too small"; char Eio[] = "i/o error"; char Eperm[] = "permission denied"; char Ebadcall[] = "unknown fs call"; char Ebadfid[] = "fid not found"; char Einuse[] = "fid already in use"; char Eisopen[] = "it is already open"; char Ebadctl[] = "unknown control request"; static char *user; static ulong epoch; static ulong msgsize = Msgsize; static int fsfd = -1; static Channel *outc; /* of Rpc* */ static QLock rpclck; /* protect vars in this block */ static Fid *freefids; static Fid *fids; static Rpc *freerpcs; static Rpc *rpcs; static Channel*procc; static Channel*endc; static Usbfs* fsops; static void fsioproc(void*); static void schedproc(void*) { Channel *proc[Nproc]; int nproc; Channel *p; Alt a[] = { {procc, &proc[0], CHANSND}, {endc, &p, CHANRCV}, {nil, nil, CHANEND} }; memset(proc, 0, sizeof(proc)); nproc = 0; for(;;){ if(nproc == 0){ proc[0] = chancreate(sizeof(Rpc*), 0); proccreate(fsioproc, proc[0], Stack); nproc++; } switch(alt(a)){ case 0: proc[0] = nil; if(nproc > 1){ proc[0] = proc[nproc-1]; proc[nproc-1] = nil; } nproc--; break; case 1: if(nproc < nelem(proc)) proc[nproc++] = p; else sendp(p, nil); break; default: sysfatal("alt"); } } } static void dump(void) { Rpc *rpc; Fid *fid; qlock(&rpclck); fprint(2, "dump:\n"); for(rpc = rpcs; rpc != nil; rpc = rpc->next) fprint(2, "rpc %#p %F next %#p\n", rpc, &rpc->t, rpc->next); for(fid = fids; fid != nil; fid = fid->next) fprint(2, "fid %d qid %#llux omode %d aux %#p\n", fid->fid, fid->qid.path, fid->omode, fid->aux); fprint(2, "\n"); qunlock(&rpclck); } static Rpc* newrpc(void) { Rpc *r; qlock(&rpclck); r = freerpcs; if(r != nil) freerpcs = r->next; else r = emallocz(sizeof(Rpc), 0); r->next = rpcs; rpcs = r; r->t.tag = r->r.tag = Notag; r->t.fid = r->r.fid = Nofid; r->t.type = r->r.type = 0; r->flushed = 0; r->fid = nil; r->r.data = (char*)r->data; qunlock(&rpclck); return r; } static void freerpc(Rpc *r) { Rpc **l; if(r == nil) return; qlock(&rpclck); for(l = &rpcs; *l != nil && *l != r; l = &(*l)->next) ; assert(*l == r); *l = r->next; r->next = freerpcs; freerpcs = r; r->t.type = 0; r->t.tag = 0x77777777; qunlock(&rpclck); } static void flushrpc(int tag) { Rpc *r; qlock(&rpclck); for(r = rpcs; r != nil; r = r->next) if(r->t.tag == tag){ r->flushed = 1; break; } qunlock(&rpclck); } static Fid* getfid(int fid, int alloc) { Fid *f; qlock(&rpclck); for(f = fids; f != nil && f->fid != fid; f = f->next) ; if(f != nil && alloc != 0){ /* fid in use */ qunlock(&rpclck); return nil; } if(f == nil && alloc != 0){ if(freefids != nil){ f = freefids; freefids = freefids->next; }else f = emallocz(sizeof(Fid), 1); f->fid = fid; f->aux = nil; f->omode = ONONE; f->next = fids; fids = f; } qunlock(&rpclck); return f; } static void freefid(Fid *f) { Fid **l; if(f == nil) return; if(fsops->clunk != nil) fsops->clunk(fsops, f); qlock(&rpclck); for(l = &fids; *l != nil && *l != f; l = &(*l)->next) ; assert(*l == f); *l = f->next; f->next = freefids; freefids = f; qunlock(&rpclck); } static Rpc* fserror(Rpc *rpc, char* fmt, ...) { va_list arg; char *c; va_start(arg, fmt); c = (char*)rpc->data; vseprint(c, c+sizeof(rpc->data), fmt, arg); va_end(arg); rpc->r.type = Rerror; rpc->r.ename = (char*)rpc->data; return rpc; } static Rpc* fsversion(Rpc *r) { if(r->t.msize < 256) return fserror(r, Etoosmall); if(strncmp(r->t.version, "9P2000", 6) != 0) return fserror(r, "wrong version"); if(r->t.msize < msgsize) msgsize = r->t.msize; r->r.msize = msgsize; r->r.version = "9P2000"; return r; } static Rpc* fsattach(Rpc *r) { static int already; /* Reload user because at boot it could be still none */ user=getuser(); if(already++ > 0 && strcmp(r->t.uname, user) != 0) return fserror(r, Eperm); if(r->fid == nil) return fserror(r, Einuse); r->r.qid.type = QTDIR; r->r.qid.path = fsops->qid; r->r.qid.vers = 0; r->fid->qid = r->r.qid; return r; } static Rpc* fswalk(Rpc *r) { int i; Fid *nfid; Fid *ofid; if(r->fid->omode != ONONE) return fserror(r, Eisopen); nfid = nil; ofid = r->fid; if(r->t.newfid != r->t.fid){ nfid = getfid(r->t.newfid, 1); if(nfid == nil) return fserror(r, Einuse); nfid->qid = r->fid->qid; if(fsops->clone != nil) fsops->clone(fsops, ofid, nfid); else nfid->aux = r->fid->aux; r->fid = nfid; } r->r.nwqid = 0; for(i = 0; i < r->t.nwname; i++) if(fsops->walk(fsops, r->fid, r->t.wname[i]) < 0) break; else r->r.wqid[i] = r->fid->qid; r->r.nwqid = i; if(i != r->t.nwname && r->t.nwname > 0){ if(nfid != nil) freefid(nfid); r->fid = ofid; } if(i == 0 && r->t.nwname > 0) return fserror(r, "%r"); return r; } static void fsioproc(void* a) { Channel *p = a; Rpc *rpc; long rc; Fcall *t; Fcall *r; Fid *fid; dprint(2, "%s: fsioproc pid %d\n", argv0, getpid()); while((rpc = recvp(p)) != nil){ t = &rpc->t; r = &rpc->r; fid = rpc->fid; rc = -1; dprint(2, "%s: fsioproc pid %d: req %d\n", argv0, getpid(), t->type); switch(t->type){ case Topen: rc = fsops->open(fsops, fid, t->mode); if(rc >= 0){ r->iounit = 0; r->qid = fid->qid; fid->omode = t->mode & 3; } break; case Tread: rc = fsops->read(fsops,fid,r->data,t->count,t->offset); if(rc >= 0){ if(rc > t->count) print("%s: bug: read %ld bytes > %ud wanted\n", argv0, rc, t->count); r->count = rc; } break; case Twrite: rc = fsops->write(fsops,fid,t->data,t->count,t->offset); r->count = rc; break; default: sysfatal("fsioproc: bad type"); } if(rc < 0) sendp(outc, fserror(rpc, "%r")); else sendp(outc, rpc); sendp(endc, p); } chanfree(p); dprint(2, "%s: fsioproc %d exiting\n", argv0, getpid()); threadexits(nil); } static Rpc* fsopen(Rpc *r) { Channel *p; if(r->fid->omode != ONONE) return fserror(r, Eisopen); if((r->t.mode & 3) != OREAD && (r->fid->qid.type & QTDIR) != 0) return fserror(r, Eperm); p = recvp(procc); sendp(p, r); return nil; } int usbdirread(Usbfs*f, Qid q, char *data, long cnt, vlong off, Dirgen gen, void *arg) { Dir d; char name[Namesz]; int i; int n; int nd; memset(&d, 0, sizeof(d)); d.name = name; d.uid = d.gid = d.muid = user; d.atime = time(nil); d.mtime = epoch; d.length = 0; for(i = n = 0; gen(f, q, i, &d, arg) >= 0; i++){ if(usbfsdebug > 1) fprint(2, "%s: dir %d q %#llux: %D\n", argv0, i, q.path, &d); nd = convD2M(&d, (uchar*)data+n, cnt-n); if(nd <= BIT16SZ) break; if(off > 0) off -= nd; else n += nd; d.name = name; d.uid = d.gid = d.muid = user; d.atime = time(nil); d.mtime = epoch; d.length = 0; } return n; } long usbreadbuf(void *data, long count, vlong offset, void *buf, long n) { if(offset >= n) return 0; if(offset + count > n) count = n - offset; memmove(data, (char*)buf + offset, count); return count; } static Rpc* fsread(Rpc *r) { Channel *p; if(r->fid->omode != OREAD && r->fid->omode != ORDWR) return fserror(r, Eperm); p = recvp(procc); sendp(p, r); return nil; } static Rpc* fswrite(Rpc *r) { Channel *p; if(r->fid->omode != OWRITE && r->fid->omode != ORDWR) return fserror(r, Eperm); p = recvp(procc); sendp(p, r); return nil; } static Rpc* fsclunk(Rpc *r) { freefid(r->fid); return r; } static Rpc* fsno(Rpc *r) { return fserror(r, Eperm); } static Rpc* fsstat(Rpc *r) { Dir d; char name[Namesz]; memset(&d, 0, sizeof(d)); d.name = name; d.uid = d.gid = d.muid = user; d.atime = time(nil); d.mtime = epoch; d.length = 0; if(fsops->stat(fsops, r->fid->qid, &d) < 0) return fserror(r, "%r"); r->r.stat = (uchar*)r->data; r->r.nstat = convD2M(&d, (uchar*)r->data, msgsize); return r; } static Rpc* fsflush(Rpc *r) { /* * Flag it as flushed and respond. * Either outproc will reply to the flushed request * before responding to flush, or it will never reply to it. * Note that we do NOT abort the ongoing I/O. * That might leave the affected endpoints in a failed * state. Instead, we pretend the request is aborted. * * Only open, read, and write are processed * by auxiliary processes and other requests wil never be * flushed in practice. */ flushrpc(r->t.oldtag); return r; } Rpc* (*fscalls[])(Rpc*) = { [Tversion] fsversion, [Tauth] fsno, [Tattach] fsattach, [Twalk] fswalk, [Topen] fsopen, [Tcreate] fsno, [Tread] fsread, [Twrite] fswrite, [Tclunk] fsclunk, [Tremove] fsno, [Tstat] fsstat, [Twstat] fsno, [Tflush] fsflush, }; static void outproc(void*) { static uchar buf[Bufsize]; Rpc *rpc; int nw; static int once = 0; if(once++ != 0) sysfatal("more than one outproc"); for(;;){ do rpc = recvp(outc); while(rpc == nil); /* a delayed reply */ if(rpc->t.tag == Dietag) break; if(rpc->flushed){ dprint(2, "outproc: tag %d flushed\n", rpc->t.tag); freerpc(rpc); continue; } dprint(2, "-> %F\n", &rpc->r); nw = convS2M(&rpc->r, buf, sizeof(buf)); if(nw == sizeof(buf)) fprint(2, "%s: outproc: buffer is too small\n", argv0); if(nw <= BIT16SZ) fprint(2, "%s: conS2M failed\n", argv0); else if(write(fsfd, buf, nw) != nw){ fprint(2, "%s: outproc: write: %r", argv0); /* continue and let the reader abort us */ } if(usbfsdebug > 1) dump(); freerpc(rpc); } dprint(2, "%s: outproc: exiting\n", argv0); } static void usbfs(void*) { Rpc *rpc; int nr; static int once = 0; if(once++ != 0) sysfatal("more than one usbfs proc"); outc = chancreate(sizeof(Rpc*), 1); procc = chancreate(sizeof(Channel*), 0); endc = chancreate(sizeof(Channel*), 0); if(outc == nil || procc == nil || endc == nil) sysfatal("chancreate: %r"); threadcreate(schedproc, nil, Stack); proccreate(outproc, nil, Stack); for(;;){ rpc = newrpc(); do{ nr = read9pmsg(fsfd, rpc->data, sizeof(rpc->data)); }while(nr == 0); if(nr < 0){ dprint(2, "%s: usbfs: read: '%r'", argv0); if(fsops->end != nil) fsops->end(fsops); else closedev(fsops->dev); rpc->t.tag = Dietag; sendp(outc, rpc); break; } if(convM2S((uchar*)rpc->data, nr, &rpc->t) <=0){ dprint(2, "%s: convM2S failed\n", argv0); freerpc(rpc); continue; } dprint(2, "<- %F\n", &rpc->t); rpc->r.tag = rpc->t.tag; rpc->r.type = rpc->t.type + 1; rpc->r.fid = rpc->t.fid; if(fscalls[rpc->t.type] == nil){ sendp(outc, fserror(rpc, Ebadcall)); continue; } if(rpc->t.fid != Nofid){ if(rpc->t.type == Tattach) rpc->fid = getfid(rpc->t.fid, 1); else rpc->fid = getfid(rpc->t.fid, 0); if(rpc->fid == nil){ sendp(outc, fserror(rpc, Ebadfid)); continue; } } sendp(outc, fscalls[rpc->t.type](rpc)); } dprint(2, "%s: ubfs: eof: exiting\n", argv0); } void usbfsinit(char* srv, char *mnt, Usbfs *f, int flag) { int fd[2]; int sfd; int afd; char sfile[40]; fsops = f; if(pipe(fd) < 0) sysfatal("pipe: %r"); user = getuser(); epoch = time(nil); fmtinstall('D', dirfmt); fmtinstall('M', dirmodefmt); fmtinstall('F', fcallfmt); fsfd = fd[1]; procrfork(usbfs, nil, Stack, RFNAMEG); /* no RFFDG */ if(srv != nil){ snprint(sfile, sizeof(sfile), "#s/%s", srv); remove(sfile); sfd = create(sfile, OWRITE, 0660); if(sfd < 0) sysfatal("post: %r"); snprint(sfile, sizeof(sfile), "%d", fd[0]); if(write(sfd, sfile, strlen(sfile)) != strlen(sfile)) sysfatal("post: %r"); close(sfd); } if(mnt != nil){ sfd = dup(fd[0], -1); /* debug */ afd = fauth(sfd, ""); if(afd >= 0) sysfatal("authentication required??"); if(mount(sfd, -1, mnt, flag, "") < 0) sysfatal("mount: %r"); } close(fd[0]); } usb/lib/fsdir.c 664 0 0 15541 11152121034 11512ustar00nemosys#include #include #include #include #include "usb.h" #include "usbfs.h" typedef struct Rpc Rpc; enum { Incr = 3, /* increments for fs array */ Dtop = 0, /* high 32 bits for / */ Qdir = 0, /* low 32 bits for /devdir */ }; QLock fslck; static Usbfs** fs; static int nfs; static int fsused; static int exitonclose = 1; void usbfsexits(int y) { exitonclose = y; } static int qiddev(uvlong path) { return (int)(path>>32) & 0xFF; } static int qidfile(uvlong path) { return (int)(path & 0xFFFFFFFFULL); } static uvlong mkqid(int qd, int qf) { return ((uvlong)qd << 32) | (uvlong)qf; } void usbfsdirdump(void) { int i; qlock(&fslck); fprint(2, "%s: fs list: (%d used %d total)\n", argv0, fsused, nfs); for(i = 1; i < nfs; i++) if(fs[i] != nil) if(fs[i]->dev != nil) fprint(2, "%s\t%s dev %#p refs %ld\n", argv0, fs[i]->name, fs[i]->dev, fs[i]->dev->ref); else fprint(2, "%s:\t%s\n", argv0, fs[i]->name); qunlock(&fslck); } void usbfsadd(Usbfs *dfs) { int i, j; dprint(2, "%s: fsadd %s\n", argv0, dfs->name); qlock(&fslck); for(i = 1; i < nfs; i++) if(fs[i] == nil) break; if(i >= nfs){ if((nfs%Incr) == 0){ fs = realloc(fs, sizeof(Usbfs*) * (nfs+Incr)); if(fs == nil) sysfatal("realloc: %r"); for(j = nfs; j < nfs+Incr; j++) fs[j] = nil; } if(nfs == 0) /* do not use entry 0 */ nfs++; fs[nfs++] = dfs; }else fs[i] = dfs; dfs->qid = mkqid(i, 0); fsused++; qunlock(&fslck); } static void usbfsdelnth(int i) { if(fs[i] != nil){ dprint(2, "%s: fsdel %s", argv0, fs[i]->name); if(fs[i]->dev != nil){ dprint(2, " dev %#p ref %ld\n", fs[i]->dev, fs[i]->dev->ref); }else dprint(2, "no dev\n"); if(fs[i]->end != nil) fs[i]->end(fs[i]); else closedev(fs[i]->dev); fsused--; } fs[i] = nil; if(fsused == 0 && exitonclose != 0){ fprint(2, "%s: all file systems gone: exiting\n", argv0); threadexitsall(nil); } } void usbfsdel(Usbfs *dfs) { int i; qlock(&fslck); for(i = 0; i < nfs; i++) if(dfs == nil || fs[i] == dfs){ usbfsdelnth(i); if(dfs != nil) break; } qunlock(&fslck); } static void fsend(Usbfs*) { dprint(2, "%s: fsend\n", argv0); usbfsdel(nil); } void usbfsgone(char *dir) { int i; qlock(&fslck); /* devices may have more than one fs */ for(i = 0; i < nfs; i++) if(fs[i] != nil && fs[i]->dev != nil) if(strcmp(fs[i]->dev->dir, dir) == 0) usbfsdelnth(i); qunlock(&fslck); } static void fsclone(Usbfs*, Fid *o, Fid *n) { int qd; Dev *dev; void (*xfsclone)(Usbfs *fs, Fid *of, Fid *nf); xfsclone = nil; dev = nil; qd = qiddev(o->qid.path); qlock(&fslck); if(qd != Dtop && fs[qd] != nil && fs[qd]->clone != nil){ dev = fs[qd]->dev; if(dev != nil) incref(dev); xfsclone = fs[qd]->clone; } qunlock(&fslck); if(xfsclone != nil){ xfsclone(fs[qd], o, n); } if(dev != nil) closedev(dev); } static int fswalk(Usbfs*, Fid *fid, char *name) { Qid q; int qd, qf; int i; int rc; Dev *dev; Dir d; int (*xfswalk)(Usbfs *fs, Fid *f, char *name); q = fid->qid; qd = qiddev(q.path); qf = qidfile(q.path); q.type = QTDIR; q.vers = 0; if(strcmp(name, "..") == 0) if(qd == Dtop || qf == Qdir){ q.path = mkqid(Dtop, Qdir); fid->qid = q; return 0; } if(qd != 0){ qlock(&fslck); if(fs[qd] == nil){ qunlock(&fslck); werrstr(Eio); return -1; } dev = fs[qd]->dev; if(dev != nil) incref(dev); xfswalk = fs[qd]->walk; qunlock(&fslck); rc = xfswalk(fs[qd], fid, name); if(dev != nil) closedev(dev); return rc; } qlock(&fslck); for(i = 0; i < nfs; i++) if(fs[i] != nil && strcmp(name, fs[i]->name) == 0){ q.path = mkqid(i, Qdir); fs[i]->stat(fs[i], q, &d); /* may be a file */ fid->qid = d.qid; qunlock(&fslck); return 0; } qunlock(&fslck); werrstr(Enotfound); return -1; } static int fsopen(Usbfs*, Fid *fid, int mode) { int qd; int rc; Dev *dev; int (*xfsopen)(Usbfs *fs, Fid *f, int mode); qd = qiddev(fid->qid.path); if(qd == Dtop) return 0; qlock(&fslck); if(fs[qd] == nil){ qunlock(&fslck); werrstr(Eio); return -1; } dev = fs[qd]->dev; if(dev != nil) incref(dev); xfsopen = fs[qd]->open; qunlock(&fslck); if(xfsopen != nil) rc = xfsopen(fs[qd], fid, mode); else rc = 0; if(dev != nil) closedev(dev); return rc; } static int dirgen(Usbfs*, Qid, int n, Dir *d, void *) { int i; Dev *dev; char *nm; qlock(&fslck); for(i = 0; i < nfs; i++) if(fs[i] != nil && n-- == 0){ d->qid.type = QTDIR; d->qid.path = mkqid(i, Qdir); d->qid.vers = 0; dev = fs[i]->dev; if(dev != nil) incref(dev); nm = d->name; fs[i]->stat(fs[i], d->qid, d); d->name = nm; strncpy(d->name, fs[i]->name, Namesz); if(dev != nil) closedev(dev); qunlock(&fslck); return 0; } qunlock(&fslck); return -1; } static long fsread(Usbfs*, Fid *fid, void *data, long cnt, vlong off) { int qd; int rc; Dev *dev; Qid q; long (*xfsread)(Usbfs *fs, Fid *f, void *data, long count, vlong ); q = fid->qid; qd = qiddev(q.path); if(qd == Dtop) return usbdirread(nil, q, data, cnt, off, dirgen, nil); qlock(&fslck); if(fs[qd] == nil){ qunlock(&fslck); werrstr(Eio); return -1; } dev = fs[qd]->dev; if(dev != nil) incref(dev); xfsread = fs[qd]->read; qunlock(&fslck); rc = xfsread(fs[qd], fid, data, cnt, off); if(dev != nil) closedev(dev); return rc; } static long fswrite(Usbfs*, Fid *fid, void *data, long cnt, vlong off) { int qd; int rc; Dev *dev; long (*xfswrite)(Usbfs *fs, Fid *f, void *data, long count, vlong ); qd = qiddev(fid->qid.path); if(qd == Dtop) sysfatal("fswrite: not for usbd /"); qlock(&fslck); if(fs[qd] == nil){ qunlock(&fslck); werrstr(Eio); return -1; } dev = fs[qd]->dev; if(dev != nil) incref(dev); xfswrite = fs[qd]->write; qunlock(&fslck); rc = xfswrite(fs[qd], fid, data, cnt, off); if(dev != nil) closedev(dev); return rc; } static void fsclunk(Usbfs*, Fid* fid) { int qd; Dev *dev; void (*xfsclunk)(Usbfs *fs, Fid *f); dev = nil; qd = qiddev(fid->qid.path); qlock(&fslck); if(qd != Dtop && fs[qd] != nil){ dev=fs[qd]->dev; if(dev != nil) incref(dev); xfsclunk = fs[qd]->clunk; }else xfsclunk = nil; qunlock(&fslck); if(xfsclunk != nil){ xfsclunk(fs[qd], fid); } if(dev != nil) closedev(dev); } static int fsstat(Usbfs*, Qid qid, Dir *d) { int qd; int rc; Dev *dev; int (*xfsstat)(Usbfs *fs, Qid q, Dir *d); qd = qiddev(qid.path); if(qd == Dtop){ d->qid = qid; d->name = "usb"; d->length = 0; d->mode = 0555|DMDIR; return 0; } qlock(&fslck); if(fs[qd] == nil){ qunlock(&fslck); werrstr(Eio); return -1; } xfsstat = fs[qd]->stat; dev = fs[qd]->dev; if(dev != nil) incref(dev); qunlock(&fslck); rc = xfsstat(fs[qd], qid, d); if(dev != nil) closedev(dev); return rc; } Usbfs usbdirfs = { .walk = fswalk, .clone = fsclone, .clunk = fsclunk, .open = fsopen, .read = fsread, .write = fswrite, .stat = fsstat, .end = fsend, }; nue; } dprint(2, "-> %F\n", &rpc->r); nw = convS2M(&rpc->r, buf, sizeof(buf)); if(nw == sizeof(buf)) fprint(2, "%s: outproc: buffer is too small\n",usb/lib/mkfile 664 0 0 513 11221120570 11363ustar00nemosys #include #include #include #include "usb.h" int parsedev(Dev *xd, uchar *b, int n) { Usbdev *d; DDev *dd; char *hd; d = xd->usb; assert(d != nil); dd = (DDev*)b; if(usbdebug>1){ hd = hexstr(b, Ddevlen); fprint(2, "%s: parsedev %s: %s\n", argv0, xd->dir, hd); free(hd); } if(dd->bLength < Ddevlen){ werrstr("short dev descr. (%d < %d)", dd->bLength, Ddevlen); return -1; } if(dd->bDescriptorType != Ddev){ werrstr("%d is not a dev descriptor", dd->bDescriptorType); return -1; } d->csp = CSP(dd->bDevClass, dd->bDevSubClass, dd->bDevProtocol); d->ep[0]->maxpkt = xd->maxpkt = dd->bMaxPacketSize0; d->class = dd->bDevClass; d->nconf = dd->bNumConfigurations; if(d->nconf == 0) dprint(2, "%s: %s: no configurations\n", argv0, xd->dir); d->vid = GET2(dd->idVendor); d->did = GET2(dd->idProduct); d->vsid = dd->iManufacturer; d->psid = dd->iProduct; d->ssid = dd->iSerialNumber; if(n > Ddevlen && usbdebug>1) fprint(2, "%s: %s: parsedev: %d bytes left", argv0, xd->dir, n - Ddevlen); return Ddevlen; } static int parseiface(Usbdev *d, Conf *c, uchar *b, int n, Iface **ipp, Altc **app) { int class, subclass, proto; int ifid, altid; DIface *dip; Iface *ip; assert(d != nil && c != nil); if(n < Difacelen){ werrstr("short interface descriptor"); return -1; } dip = (DIface *)b; ifid = dip->bInterfaceNumber; if(ifid < 0 || ifid >= nelem(c->iface)){ werrstr("bad interface number %d", ifid); return -1; } if(c->iface[ifid] == nil) c->iface[ifid] = emallocz(sizeof(Iface), 1); ip = c->iface[ifid]; class = dip->bInterfaceClass; subclass = dip->bInterfaceSubClass; proto = dip->bInterfaceProtocol; ip->csp = CSP(class, subclass, proto); if(d->csp == 0) /* use csp from 1st iface */ d->csp = ip->csp; /* if device has none */ if(d->class == 0) d->class = class; ip->id = ifid; if(c == d->conf[0] && ifid == 0) /* ep0 was already there */ d->ep[0]->iface = ip; altid = dip->bAlternateSetting; if(altid < 0 || altid >= nelem(ip->altc)){ werrstr("bad alternate conf. number %d", altid); return -1; } if(ip->altc[altid] == nil) ip->altc[altid] = emallocz(sizeof(Altc), 1); *ipp = ip; *app = ip->altc[altid]; return Difacelen; } extern Ep* mkep(Usbdev *, int); static int parseendpt(Usbdev *d, Conf *c, Iface *ip, Altc *altc, uchar *b, int n, Ep **epp) { int i, dir, epid; Ep *ep; DEp *dep; assert(d != nil && c != nil && ip != nil && altc != nil); if(n < Deplen){ werrstr("short endpoint descriptor"); return -1; } dep = (DEp *)b; altc->attrib = dep->bmAttributes; /* here? */ altc->interval = dep->bInterval; epid = dep->bEndpointAddress & 0xF; assert(epid < nelem(d->ep)); if(dep->bEndpointAddress & 0x80) dir = Ein; else dir = Eout; ep = d->ep[epid]; if(ep == nil){ ep = mkep(d, epid); ep->dir = dir; }else if((ep->addr & 0x80) != (dep->bEndpointAddress & 0x80)) ep->dir = Eboth; ep->maxpkt = GET2(dep->wMaxPacketSize); ep->ntds = 1 + ((ep->maxpkt >> 11) & 3); ep->maxpkt &= 0x7FF; ep->addr = dep->bEndpointAddress; ep->type = dep->bmAttributes & 0x03; ep->isotype = (dep->bmAttributes>>2) & 0x03; ep->conf = c; ep->iface = ip; for(i = 0; i < nelem(ip->ep); i++) if(ip->ep[i] == nil) break; if(i == nelem(ip->ep)){ werrstr("parseendpt: bug: too many end points on interface " "with csp %#lux", ip->csp); fprint(2, "%s: %r\n", argv0); return -1; } *epp = ip->ep[i] = ep; return Dep; } static char* dname(int dtype) { switch(dtype){ case Ddev: return "device"; case Dconf: return "config"; case Dstr: return "string"; case Diface: return "interface"; case Dep: return "endpoint"; case Dreport: return "report"; case Dphysical: return "phys"; default: return "desc"; } } int parsedesc(Usbdev *d, Conf *c, uchar *b, int n) { int len, nd, tot; Iface *ip; Ep *ep; Altc *altc; char *hd; assert(d != nil && c != nil); tot = 0; ip = nil; ep = nil; altc = nil; for(nd = 0; nd < nelem(d->ddesc); nd++) if(d->ddesc[nd] == nil) break; while(n > 2 && b[0] != 0 && b[0] <= n){ len = b[0]; if(usbdebug>1){ hd = hexstr(b, len); fprint(2, "%s:\t\tparsedesc %s %x[%d] %s\n", argv0, dname(b[1]), b[1], b[0], hd); free(hd); } switch(b[1]){ case Ddev: case Dconf: werrstr("unexpected descriptor %d", b[1]); ddprint(2, "%s\tparsedesc: %r", argv0); break; case Diface: if(parseiface(d, c, b, n, &ip, &altc) < 0){ ddprint(2, "%s\tparsedesc: %r\n", argv0); return -1; } break; case Dep: if(ip == nil || altc == nil){ werrstr("unexpected endpoint descriptor"); break; } if(parseendpt(d, c, ip, altc, b, n, &ep) < 0){ ddprint(2, "%s\tparsedesc: %r\n", argv0); return -1; } break; default: if(nd == nelem(d->ddesc)){ fprint(2, "%s: parsedesc: too many " "device-specific descriptors for device" " %s %s\n", argv0, d->vendor, d->product); break; } d->ddesc[nd] = emallocz(sizeof(Desc)+b[0], 0); d->ddesc[nd]->iface = ip; d->ddesc[nd]->ep = ep; d->ddesc[nd]->altc = altc; d->ddesc[nd]->conf = c; memmove(&d->ddesc[nd]->data, b, len); ++nd; } n -= len; b += len; tot += len; } return tot; } int parseconf(Usbdev *d, Conf *c, uchar *b, int n) { DConf* dc; int l; int nr; char *hd; assert(d != nil && c != nil); dc = (DConf*)b; if(usbdebug>1){ hd = hexstr(b, Dconflen); fprint(2, "%s:\tparseconf %s\n", argv0, hd); free(hd); } if(dc->bLength < Dconflen){ werrstr("short configuration descriptor"); return -1; } if(dc->bDescriptorType != Dconf){ werrstr("not a configuration descriptor"); return -1; } c->cval = dc->bConfigurationValue; c->attrib = dc->bmAttributes; c->milliamps = dc->MaxPower*2; l = GET2(dc->wTotalLength); if(n < l){ werrstr("truncated configuration info"); return -1; } n -= Dconflen; b += Dconflen; nr = 0; if(n > 0 && (nr=parsedesc(d, c, b, n)) < 0) return -1; n -= nr; if(n > 0 && usbdebug>1) fprint(2, "%s:\tparseconf: %d bytes left\n", argv0, n); return l; } usb/lib/usb.h 664 0 0 17301 11220631332 11200ustar00nemosystypedef struct Altc Altc; typedef struct Conf Conf; typedef struct DConf DConf; typedef struct DDesc DDesc; typedef struct DDev DDev; typedef struct DEp DEp; typedef struct DIface DIface; typedef struct Desc Desc; typedef struct Dev Dev; typedef struct Ep Ep; typedef struct Iface Iface; typedef struct Usbdev Usbdev; enum { /* fundamental constants */ Nep = 16, /* max. endpoints per usb device & per interface */ /* tunable parameters */ Nconf = 16, /* max. configurations per usb device */ Nddesc = 8*Nep, /* max. device-specific descriptors per usb device */ Niface = 16, /* max. interfaces per configuration */ Naltc = 16, /* max. alt configurations per interface */ Uctries = 4, /* no. of tries for usbcmd */ Ucdelay = 50, /* delay before retrying */ /* request type */ Rh2d = 0<<7, /* host to device */ Rd2h = 1<<7, /* device to host */ Rstd = 0<<5, /* types */ Rclass = 1<<5, Rvendor = 2<<5, Rdev = 0, /* recipients */ Riface = 1, Rep = 2, /* endpoint */ Rother = 3, /* standard requests */ Rgetstatus = 0, Rclearfeature = 1, Rsetfeature = 3, Rsetaddress = 5, Rgetdesc = 6, Rsetdesc = 7, Rgetconf = 8, Rsetconf = 9, Rgetiface = 10, Rsetiface = 11, Rsynchframe = 12, Rgetcur = 0x81, Rgetmin = 0x82, Rgetmax = 0x83, Rgetres = 0x84, Rsetcur = 0x01, Rsetmin = 0x02, Rsetmax = 0x03, Rsetres = 0x04, /* dev classes */ Clnone = 0, /* not in usb */ Claudio = 1, Clcomms = 2, Clhid = 3, Clprinter = 7, Clstorage = 8, Clhub = 9, Cldata = 10, /* standard descriptor sizes */ Ddevlen = 18, Dconflen = 9, Difacelen = 9, Deplen = 7, /* descriptor types */ Ddev = 1, Dconf = 2, Dstr = 3, Diface = 4, Dep = 5, Dreport = 0x22, Dfunction = 0x24, Dphysical = 0x23, /* feature selectors */ Fdevremotewakeup = 1, Fhalt = 0, /* device state */ Detached = 0, Attached, Enabled, Assigned, Configured, /* endpoint direction */ Ein = 0, Eout, Eboth, /* endpoint type */ Econtrol = 0, Eiso = 1, Ebulk = 2, Eintr = 3, /* endpoint isotype */ Eunknown = 0, Easync = 1, Eadapt = 2, Esync = 3, /* config attrib */ Cbuspowered = 1<<7, Cselfpowered = 1<<6, Cremotewakeup = 1<<5, /* report types */ Tmtype = 3<<2, Tmitem = 0xF0, Tmain = 0<<2, Tinput = 0x80, Toutput = 0x90, Tfeature = 0xB0, Tcoll = 0xA0, Tecoll = 0xC0, Tglobal = 1<<2, Tusagepage = 0x00, Tlmin = 0x10, Tlmax = 0x20, Tpmin = 0x30, Tpmax = 0x40, Tunitexp = 0x50, Tunit = 0x60, Trepsize = 0x70, TrepID = 0x80, Trepcount = 0x90, Tpush = 0xA0, Tpop = 0xB0, Tlocal = 2<<2, Tusage = 0x00, Tumin = 0x10, Tumax = 0x20, Tdindex = 0x30, Tdmin = 0x40, Tdmax = 0x50, Tsindex = 0x70, Tsmin = 0x80, Tsmax = 0x90, Tsetdelim = 0xA0, Treserved = 3<<2, Tlong = 0xFE, }; /* * Usb device (when used for ep0s) or endpoint. * RC: One ref because of existing, another one per ogoing I/O. * per-driver resources (including FS if any) are released by aux * once the last ref is gone. This may include other Devs using * to access endpoints for actual I/O. */ struct Dev { Ref; char* dir; /* path for the endpoint dir */ int id; /* usb id for device or ep. number */ int dfd; /* descriptor for the data file */ int cfd; /* descriptor for the control file */ int maxpkt; /* cached from usb description */ Ref nerrs; /* number of errors in requests */ Usbdev* usb; /* USB description */ void* aux; /* for the device driver */ void (*free)(void*); /* idem. to release aux */ }; /* * device description as reported by USB (unpacked). */ struct Usbdev { ulong csp; /* USB class/subclass/proto */ int vid; /* vendor id */ int did; /* product (device) id */ char* vendor; char* product; char* serial; int vsid; int psid; int ssid; int class; /* from descriptor */ int nconf; /* from descriptor */ Conf* conf[Nconf]; /* configurations */ Ep* ep[Nep]; /* all endpoints in device */ Desc* ddesc[Nddesc]; /* (raw) device specific descriptors */ }; struct Ep { uchar addr; /* endpt address, 0-15 (|0x80 if Ein) */ uchar dir; /* direction, Ein/Eout */ uchar type; /* Econtrol, Eiso, Ebulk, Eintr */ uchar isotype; /* Eunknown, Easync, Eadapt, Esync */ int id; int maxpkt; /* max. packet size */ int ntds; /* nb. of Tds per µframe */ Conf* conf; /* the endpoint belongs to */ Iface* iface; /* the endpoint belongs to */ }; struct Altc { int attrib; int interval; void* aux; /* for the driver program */ }; struct Iface { int id; /* interface number */ ulong csp; /* USB class/subclass/proto */ Altc* altc[Naltc]; Ep* ep[Nep]; void* aux; /* for the driver program */ }; struct Conf { int cval; /* value for set configuration */ int attrib; int milliamps; /* maximum power in this config. */ Iface* iface[Niface]; }; /* * Device-specific descriptors. * They show up mixed with other descriptors * within a configuration. * These are unknown to the library but handed to the driver. */ struct Desc { Conf* conf; /* where this descriptor was read */ Iface* iface; /* last iface before desc in conf. */ Ep* ep; /* last endpt before desc in conf. */ Altc* altc; /* last alt.c. before desc in conf. */ DDesc data; /* unparsed standard USB descriptor */ }; struct DDesc { uchar bLength; uchar bDescriptorType; uchar bbytes[1]; /* extra bytes allocated here to keep the rest of it */ }; /* * layout of standard descriptor types */ struct DDev { uchar bLength; uchar bDescriptorType; uchar bcdUSB[2]; uchar bDevClass; uchar bDevSubClass; uchar bDevProtocol; uchar bMaxPacketSize0; uchar idVendor[2]; uchar idProduct[2]; uchar bcdDev[2]; uchar iManufacturer; uchar iProduct; uchar iSerialNumber; uchar bNumConfigurations; }; struct DConf { uchar bLength; uchar bDescriptorType; uchar wTotalLength[2]; uchar bNumInterfaces; uchar bConfigurationValue; uchar iConfiguration; uchar bmAttributes; uchar MaxPower; }; struct DIface { uchar bLength; uchar bDescriptorType; uchar bInterfaceNumber; uchar bAlternateSetting; uchar bNumEndpoints; uchar bInterfaceClass; uchar bInterfaceSubClass; uchar bInterfaceProtocol; uchar iInterface; }; struct DEp { uchar bLength; uchar bDescriptorType; uchar bEndpointAddress; uchar bmAttributes; uchar wMaxPacketSize[2]; uchar bInterval; }; #define Class(csp) ((csp) & 0xff) #define Subclass(csp) (((csp)>>8) & 0xff) #define Proto(csp) (((csp)>>16) & 0xff) #define CSP(c, s, p) ((c) | (s)<<8 | (p)<<16) #define GET2(p) (((p)[1] & 0xFF)<<8 | ((p)[0] & 0xFF)) #define PUT2(p,v) {(p)[0] = (v); (p)[1] = (v)>>8;} #define GET4(p) (((p)[3]&0xFF)<<24 | ((p)[2]&0xFF)<<16 | \ ((p)[1]&0xFF)<<8 | ((p)[0]&0xFF)) #define PUT4(p,v) {(p)[0] = (v); (p)[1] = (v)>>8; \ (p)[2] = (v)>>16; (p)[3] = (v)>>24;} #define dprint if(usbdebug)fprint #define ddprint if(usbdebug > 1)fprint #pragma varargck type "U" Dev* #pragma varargck argpos devctl 2 int Ufmt(Fmt *f); char* classname(int c); void closedev(Dev *d); int configdev(Dev *d); int devctl(Dev *dev, char *fmt, ...); void* emallocz(ulong size, int zero); char* estrdup(char *s); int matchdevcsp(char *info, void *a); int finddevs(int (*matchf)(char*,void*), void *farg, char** dirs, int ndirs); char* hexstr(void *a, int n); int loaddevconf(Dev *d, int n); int loaddevdesc(Dev *d); char* loaddevstr(Dev *d, int sid); Dev* opendev(char *fn); int opendevdata(Dev *d, int mode); Dev* openep(Dev *d, int id); int parseconf(Usbdev *d, Conf *c, uchar *b, int n); int parsedesc(Usbdev *d, Conf *c, uchar *b, int n); int parsedev(Dev *xd, uchar *b, int n); void startdevs(char *args, char *argv[], int argc, int (*mf)(char*,void*), void*ma, int (*df)(Dev*,int,char**)); int unstall(Dev *dev, Dev *ep, int dir); int usbcmd(Dev *d, int type, int req, int value, int index, uchar *data, int count); extern int usbdebug; /* more messages for bigger values */ Dconf: werrstr("unexpected descriptor %d", b[1]); ddprint(2, "%s\tparsedesc: %r", argv0); break; case Diface: if(parseiface(d, c, b, n, &ip, &altc) < 0){ ddprint(2, "%s\tparsedesc: %r\n", argv0); return -1; } break; case Dep: if(ip == nil || altc == nil){ werrstr("unexpected endpousb/lib/usbfs.h 664 0 0 2537 11177617175 11542ustar00nemosystypedef struct Usbfs Usbfs; typedef struct Fid Fid; enum { Hdrsize = 128, /* plenty of room for headers */ Msgsize = 8 * 1024, Bufsize = Hdrsize + Msgsize, Namesz = 40, Errmax = 128, ONONE = ~0, /* omode in Fid when not open */ }; struct Fid { int fid; Qid qid; int omode; Fid* next; void* aux; }; struct Usbfs { char name[Namesz]; uvlong qid; Dev* dev; void* aux; int (*walk)(Usbfs *fs, Fid *f, char *name); void (*clone)(Usbfs *fs, Fid *of, Fid *nf); void (*clunk)(Usbfs *fs, Fid *f); int (*open)(Usbfs *fs, Fid *f, int mode); long (*read)(Usbfs *fs, Fid *f, void *data, long count, vlong offset); long (*write)(Usbfs *fs, Fid*f, void *data, long count, vlong offset); int (*stat)(Usbfs *fs, Qid q, Dir *d); void (*end)(Usbfs *fs); }; typedef int (*Dirgen)(Usbfs*, Qid, int, Dir*, void*); long usbreadbuf(void *data, long count, vlong offset, void *buf, long n); void usbfsadd(Usbfs *dfs); void usbfsdel(Usbfs *dfs); int usbdirread(Usbfs*f, Qid q, char *data, long cnt, vlong off, Dirgen gen, void *arg); void usbfsinit(char* srv, char *mnt, Usbfs *f, int flag); void usbfsdirdump(void); extern char Enotfound[]; extern char Etoosmall[]; extern char Eio[]; extern char Eperm[]; extern char Ebadcall[]; extern char Ebadfid[]; extern char Einuse[]; extern char Eisopen[]; extern char Ebadctl[]; extern Usbfs usbdirfs; extern int usbfsdebug; usb/mkfile 664 0 0 1441 11255750624 10656ustar00nemosys/n/sources/contrib/nemo/u.tar 0x02, Rsetmax = 0x03, Rsetres = 0x04, /* dev classes */ Clnone = 0, /* not in usb */ Claudio = 1, Clcomms = 2, Clhid = 3, Clprinter = 7, Clstorage = 8, Clhub = 9, Cldata = 10, /* standard descriptor siusb/print/ 775 0 0 0 11255750546 106235ustar00nemosysusb/print/main.c 664 0 0 1255 11200563213 11675ustar00nemosys/* * usb/print - usb printer */ #include #include #include #include "usb.h" enum { Arglen = 80, }; static void usage(void) { fprint(2, "usage: %s [-d] [dev...]\n", argv0); threadexitsall("usage"); } static int csps[] = { 0x020107, 0 }; extern int printmain(Dev*, int, char**); void threadmain(int argc, char **argv) { char args[Arglen]; char *ae; quotefmtinstall(); ae = args+sizeof(args); seprint(args, ae, "print"); ARGBEGIN{ case 'd': usbdebug++; break; default: usage(); }ARGEND rfork(RFNOTEG); threadsetgrp(threadid()); fmtinstall('U', Ufmt); startdevs(args, argv, argc, matchdevcsp, csps, printmain); threadexits(nil); } * descriptor for the control file */ int maxpkt; /* cached from usb description */ Ref nerrs; /* number of errors in requests */ Usbdev* usb; /* USB description */ void* aux; /* for the device driver */ void (*free)(void*); /* idem. to release aux */ }; /* * device description as reported by USB (unpacked). */ struct Usbdev {usb/print/mkfile 664 0 0 346 11200563132 11757ustar00nemosys #include #include #include "usb.h" enum { Qdir = 0, Qctl, Qraw, Qdata, Qmax, }; int findendpoints(Dev *dev) { Ep *ep; Dev *d; Usbdev *ud; int i; int epout; epout = -1; ud = dev->usb; for(i = 0; i < nelem(ud->ep); i++){ if((ep = ud->ep[i]) == nil) break; if(ep->iface->csp != 0x020107) continue; if(ep->type == Ebulk && (ep->dir == Eboth || ep->dir == Eout)) if(epout == -1) epout = ep->id; } dprint(2, "print: ep ids: out %d\n", epout); if(epout == -1) return -1; d = openep(dev, epout); if(d == nil){ fprint(2, "print: openep %d: %r\n", epout); return -1; } opendevdata(d, OWRITE); if(d->dfd < 0){ fprint(2, "print: open i/o ep data: %r\n"); closedev(d); return -1; } dprint(2, "print: ep out %s\n", d->dir); if(usbdebug > 1) devctl(d, "debug 1"); devctl(d, "name lp%d", dev->id); return 0; } static int usage(void) { werrstr("usage: usb/print"); return -1; } int printmain(Dev *dev, int argc, char **argv) { ARGBEGIN{ default: return usage(); }ARGEND if(argc != 0) return usage(); if(findendpoints(dev) < 0){ werrstr("print: endpoints not found"); return -1; } return 0; } ]&0xFF)<<16 | \ ((p)[1]&0xFF)<<8usb/probe 775 0 0 444 11152121017 10463ustar00nemosys#!/bin/rc rfork e test -e /dev/usb || bind -a '#u' /dev || { echo no '#u/usb' >[1=2] exit nousb } awk 'BEGIN{ep="";} $1 ~ /ep[0-9]+\.0/ && $2 == "enabled" && $NF ~ /busy|idle/ { ep=$1; next; } { if(ep != ""){ printf("%s %s\n", ep, $0); ep=""; } } ' /dev/usb/ctl exit '' usb/serial/ 775 0 0 0 11255750546 112655ustar00paureasysusb/serial/main.c 664 0 0 2061 11214501024 12330ustar00paureasys#include #include #include #include "usb.h" #include "usbfs.h" #include "serial.h" #include "ucons.h" #include "prolific.h" typedef struct Parg Parg; enum { Arglen = 80, }; static void usage(void) { fprint(2, "usage: %s [-d] [-a n] [dev...]\n", argv0); threadexitsall("usage"); } static int matchserial(char *info, void*) { if(uconsmatch(info) == 0 || plmatch(info) == 0) return 0; return -1; } void threadmain(int argc, char **argv) { char *mnt, *srv, *as, *ae; char args[Arglen]; mnt = "/dev"; srv = nil; quotefmtinstall(); ae = args + sizeof args; as = seprint(args, ae, "serial"); ARGBEGIN{ case 'D': usbfsdebug++; break; case 'd': usbdebug++; as = seprint(as, ae, " -d"); break; case 'm': mnt = EARGF(usage()); break; case 's': srv = EARGF(usage()); break; default: usage(); break; }ARGEND; rfork(RFNOTEG); fmtinstall('U', Ufmt); threadsetgrp(threadid()); usbfsinit(srv, mnt, &usbdirfs, MAFTER|MCREATE); startdevs(args, argv, argc, matchserial, nil, serialmain); threadexits(nil); } , Usbfs *f, int flag); void usbfsdirdump(void); extern char Enotfound[]; extern char Etoosmall[]; extern char Eio[]; extern char Eperm[]; extern char Ebadcall[]; extern char Ebadfid[]; extern char Einuse[]; extern char Eisopen[]; extern char Ebadctl[]; extern Usbfs usbdirfs; extern int usbfsdebug; usb/serial/mkfile 664 0 0 662 11214504044 12424ustar00paureasys #include #include #include "usb.h" #include "usbfs.h" #include "serial.h" #include "prolific.h" Cinfo plinfo[] = { { PL2303Vid, PL2303Did }, { PL2303Vid, PL2303DidRSAQ2 }, { PL2303Vid, PL2303DidDCU11 }, { PL2303Vid, PL2303DidRSAQ3 }, { PL2303Vid, PL2303DidPHAROS }, { PL2303Vid, PL2303DidALDIGA }, { PL2303Vid, PL2303DidMMX }, { PL2303Vid, PL2303DidGPRS }, { IODATAVid, IODATADid }, { IODATAVid, IODATADidRSAQ5 }, { ATENVid, ATENDid }, { ATENVid2, ATENDid }, { ELCOMVid, ELCOMDid }, { ELCOMVid, ELCOMDidUCSGT }, { ITEGNOVid, ITEGNODid }, { ITEGNOVid, ITEGNODid2080 }, { MA620Vid, MA620Did }, { RATOCVid, RATOCDid }, { TRIPPVid, TRIPPDid }, { RADIOSHACKVid,RADIOSHACKDid }, { DCU10Vid, DCU10Did }, { SITECOMVid, SITECOMDid }, { ALCATELVid, ALCATELDid }, { SAMSUNGVid, SAMSUNGDid }, { SIEMENSVid, SIEMENSDidSX1 }, { SIEMENSVid, SIEMENSDidX65 }, { SIEMENSVid, SIEMENSDidX75 }, { SIEMENSVid, SIEMENSDidEF81 }, { SYNTECHVid, SYNTECHDid }, { NOKIACA42Vid, NOKIACA42Did }, { CA42CA42Vid, CA42CA42Did }, { SAGEMVid, SAGEMDid }, { LEADTEKVid, LEADTEK9531Did }, { SPEEDDRAGONVid,SPEEDDRAGONDid }, { DATAPILOTU2Vid,DATAPILOTU2Did }, { BELKINVid, BELKINDid }, { ALCORVid, ALCORDid }, { WS002INVid, WS002INDid }, { COREGAVid, COREGADid }, { YCCABLEVid, YCCABLEDid }, { SUPERIALVid, SUPERIALDid }, { HPVid, HPLD220Did }, { 0, 0 }, }; int plmatch(char *info) { Cinfo *ip; char buf[50]; for(ip = plinfo; ip->vid != 0; ip++){ snprint(buf, sizeof buf, "vid %#06x did %#06x", ip->vid, ip->did); dsprint(2, "serial: %s %s", buf, info); if(strstr(info, buf) != nil) return 0; } return -1; } static void statusreader(void *u); static void dumpbuf(uchar *buf, int bufsz) { int i; for(i=0; idev, Rd2h | Rvendor | Rdev, VendorReadReq, val, index, buf, 1); dsprint(2, "serial: vendorread res:%d\n", res); return res; } static int vendorwrite(Serial *ser, int val, int index) { int res; dsprint(2, "serial: vendorwrite val: 0x%x idx:%d\n", val, index); res = usbcmd(ser->dev, Rh2d | Rvendor | Rdev, VendorWriteReq, val, index, nil, 0); dsprint(2, "serial: vendorwrite res:%d\n", res); return res; } static int plgetparam(Serial *ser) { uchar buf[7]; int res; res = usbcmd(ser->dev, Rd2h | Rclass | Riface, GetLineReq, 0, 0, buf, sizeof buf); ser->baud = GET4(buf); /* * with the Pl9 interface it is not possible to set `1.5' as stop bits * for the prologic: * 0 is 1 stop bit * 1 is 1.5 stop bits * 2 is 2 stop bits */ if(buf[4] == 1) fprint(2, "warning, stop bit set to 1.5 unsupported"); else if(buf[4] == 0) ser->stop = 1; else if(buf[4] == 2) ser->stop = 2; ser->parity = buf[5]; ser->bits = buf[6]; dsprint(2, "serial: getparam: "); if(serialdebug) dumpbuf(buf, sizeof buf); dsprint(2, "serial: getparam res: %d\n", res); return res; } static int plmodemctl(Serial *ser, int set) { if(set == 0){ ser->mctl = 0; vendorwrite(ser, 0x0, 0x0); return 0; } ser->mctl = 1; if(ser->type == TypeHX) vendorwrite(ser, 0x0, Dcr0InitX); else vendorwrite(ser, 0x0, Dcr0InitH); return 0; } static int plsetparam(Serial *ser) { uchar buf[7]; int res; PUT4(buf, ser->baud); if(ser->stop == 1) buf[4] = 0; else if(ser->stop == 2) buf[4] = 2; /* see comment in getparam */ buf[5] = ser->parity; buf[6] = ser->bits; dsprint(2, "serial: setparam: "); if(serialdebug) dumpbuf(buf, sizeof buf); res = usbcmd(ser->dev, Rh2d | Rclass | Riface, SetLineReq, 0, 0, buf, sizeof buf); plmodemctl(ser, ser->mctl); plgetparam(ser); /* make sure our state corresponds */ dsprint(2, "serial: setparam res: %d\n", res); return res; } static int plinit(Serial *ser) { ulong csp; char *st; uchar *buf; buf = emallocz(VendorReqSize, 1); qlock(ser); serialreset(ser); csp = ser->dev->usb->csp; if(Class(csp) == 0x02) ser->type = Type0; else if(ser->dev->maxpkt == 0x40) ser->type = TypeHX; else if(Class(csp) == 0x00 || Class(csp) == 0xFF) ser->type = Type1; if(ser->type != ser->dev->usb->psid) fprint(2, "serial: warning, heuristics: %#ux and psid: " "%#ux, not a match\n", ser->type, ser->dev->usb->psid); dsprint(2, "serial: type %d\n", ser->type); vendorread(ser, 0x8484, 0, buf); vendorwrite(ser, 0x0404, 0); vendorread(ser, 0x8484, 0, buf); vendorread(ser, 0x8383, 0, buf); vendorread(ser, 0x8484, 0, buf); vendorwrite(ser, 0x0404, 1); vendorread(ser, 0x8484, 0, buf); vendorread(ser, 0x8383, 0, buf); vendorwrite(ser, 0, 1); vendorwrite(ser, 1, 0); if(ser->type == TypeHX) vendorwrite(ser, 2, Dcr2InitX); else vendorwrite(ser, 2, Dcr2InitH); plgetparam(ser); qunlock(ser); free(buf); st = emallocz(255, 1); qlock(ser); if(serialdebug) serdumpst(ser, st, 255); dsprint(2, st); qunlock(ser); free(st); /* ser gets freed by closedev, the process has a reference */ incref(ser->dev); proccreate(statusreader, ser, 8*1024); return 0; } static int plsetbreak(Serial *ser, int val) { return usbcmd(ser->dev, Rh2d | Rclass | Riface, (val != 0? BreakOn: BreakOff), val, 0, nil, 0); } static int plclearpipes(Serial *ser) { if(ser->type == TypeHX){ vendorwrite(ser, 8, 0x0); vendorwrite(ser, 9, 0x0); }else{ if(unstall(ser->dev, ser->epout, Eout) < 0) dprint(2, "disk: unstall epout: %r\n"); if(unstall(ser->dev, ser->epin, Ein) < 0) dprint(2, "disk: unstall epin: %r\n"); if(unstall(ser->dev, ser->epintr, Ein) < 0) dprint(2, "disk: unstall epintr: %r\n"); } return 0; } static int setctlline(Serial *ser, uchar val) { return usbcmd(ser->dev, Rh2d | Rclass | Riface, SetCtlReq, val, 0, nil, 0); } static void composectl(Serial *ser) { if(ser->rts) ser->ctlstate |= CtlRTS; else ser->ctlstate &= ~CtlRTS; if(ser->dtr) ser->ctlstate |= CtlDTR; else ser->ctlstate &= ~CtlDTR; } int plsendlines(Serial *ser) { int res; dsprint(2, "serial: sendlines: %#2.2x\n", ser->ctlstate); composectl(ser); res = setctlline(ser, ser->ctlstate); dsprint(2, "serial: getparam res: %d\n", res); return 0; } static int plreadstatus(Serial *ser) { int nr, dfd; char err[40]; uchar buf[10]; qlock(ser); dsprint(2, "serial: reading from interrupt\n"); dfd = ser->epintr->dfd; qunlock(ser); nr = read(dfd, buf, sizeof buf); qlock(ser); snprint(err, sizeof err, "%r"); dsprint(2, "serial: interrupt read %d %r\n", nr); if(nr < 0 && strstr(err, "timed out") != nil){ dsprint(2, "serial: need to recover, status read %d %r\n", nr); if(serialrecover(ser, err) < 0){ qunlock(ser); return -1; } } if(nr < 0) dsprint(2, "serial: reading status: %r"); else if(nr >= sizeof buf - 1){ ser->dcd = buf[8] & DcdStatus; ser->dsr = buf[8] & DsrStatus; ser->cts = buf[8] & BreakerrStatus; ser->ring = buf[8] & RingStatus; ser->cts = buf[8] & CtsStatus; if (buf[8] & FrerrStatus) ser->nframeerr++; if (buf[8] & ParerrStatus) ser->nparityerr++; if (buf[8] & OvererrStatus) ser->novererr++; } else dsprint(2, "serial: bad status read %d\n", nr); dsprint(2, "serial: finished read from interrupt %d\n", nr); qunlock(ser); return 0; } static void statusreader(void *u) { Serial *ser; ser = u; threadsetname("statusreaderproc"); while(plreadstatus(ser) > 0) ; closedev(ser->dev); } Serialops plops = { .init = plinit, .getparam = plgetparam, .setparam = plsetparam, .clearpipes = plclearpipes, .sendlines = plsendlines, .modemctl = plmodemctl, .setbreak = plsetbreak, }; usb/serial/prolific.h 664 0 0 6206 11214504315 13233ustar00paureasysenum { /* flavours of the device */ Type0, Type1, TypeHX, /* usbcmd parameters */ SetLineReq = 0x20, SetCtlReq = 0x22, CtlDTR = 0x01, CtlRTS = 0x02, BreakReq = 0x23, BreakOn = 0xffff, BreakOff = 0x0000, GetLineReq = 0x21, VendorWriteReq = 0x01, /* BUG: is this a standard request? */ VendorReadReq = 0x01, VendorReqSize = 10, /* status read from interrupt endpoint */ DcdStatus = 0x01, DsrStatus = 0x02, BreakerrStatus= 0x04, RingStatus = 0x08, FrerrStatus = 0x10, ParerrStatus = 0x20, OvererrStatus = 0x40, CtsStatus = 0x80, /* * flow control bits, Dcr0InitH * I think, composed this list from various drivers and specs. * FlowOutCts = 0x0001, * FlowOutDsr = 0x0002, * FlowInDsr = 0x0004, * FlowInDtr = 0x0008, * FlowInRts = 0x0010, * FlowOutRts = 0x0020, * FlowOutXon = 0x0080, * FlowInXon = 0x0100, */ Dcr0InitH = 0x0041, Dcr0InitX = 0x0061, Dcr1InitH = 0x0080, Dcr1InitX = 0x0000, Dcr2InitH = 0x0024, Dcr2InitX = 0x0044, }; enum { PL2303Vid = 0x067b, PL2303Did = 0x2303, PL2303DidRSAQ2= 0x04bb, PL2303DidDCU11= 0x1234, PL2303DidPHAROS=0xaaa0, PL2303DidRSAQ3= 0xaaa2, PL2303DidALDIGA=0x0611, PL2303DidMMX = 0x0612, PL2303DidGPRS = 0x0609, ATENVid = 0x0557, ATENVid2 = 0x0547, ATENDid = 0x2008, IODATAVid = 0x04bb, IODATADid = 0x0a03, IODATADidRSAQ5= 0x0a0e, ELCOMVid = 0x056e, ELCOMDid = 0x5003, ELCOMDidUCSGT = 0x5004, ITEGNOVid = 0x0eba, ITEGNODid = 0x1080, ITEGNODid2080 = 0x2080, MA620Vid = 0x0df7, MA620Did = 0x0620, RATOCVid = 0x0584, RATOCDid = 0xb000, TRIPPVid = 0x2478, TRIPPDid = 0x2008, RADIOSHACKVid = 0x1453, RADIOSHACKDid = 0x4026, DCU10Vid = 0x0731, DCU10Did = 0x0528, SITECOMVid = 0x6189, SITECOMDid = 0x2068, /* Alcatel OT535/735 USB cable */ ALCATELVid = 0x11f7, ALCATELDid = 0x02df, /* Samsung I330 phone cradle */ SAMSUNGVid = 0x04e8, SAMSUNGDid = 0x8001, SIEMENSVid = 0x11f5, SIEMENSDidSX1 = 0x0001, SIEMENSDidX65 = 0x0003, SIEMENSDidX75 = 0x0004, SIEMENSDidEF81= 0x0005, SYNTECHVid = 0x0745, SYNTECHDid = 0x0001, /* Nokia CA-42 Cable */ NOKIACA42Vid = 0x078b, NOKIACA42Did = 0x1234, /* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */ CA42CA42Vid = 0x10b5, CA42CA42Did = 0xac70, SAGEMVid = 0x079b, SAGEMDid = 0x0027, /* Leadtek GPS 9531 (ID 0413:2101) */ LEADTEKVid = 0x0413, LEADTEK9531Did= 0x2101, /* USB GSM cable from Speed Dragon Multimedia, Ltd */ SPEEDDRAGONVid= 0x0e55, SPEEDDRAGONDid= 0x110b, /* DATAPILOT Universal-2 Phone Cable */ BELKINVid = 0x050d, BELKINDid = 0x0257, /* Belkin "F5U257" Serial Adapter */ DATAPILOTU2Vid= 0x0731, DATAPILOTU2Did= 0x2003, ALCORVid = 0x058F, ALCORDid = 0x9720, /* Willcom WS002IN Data Driver (by NetIndex Inc.) */, WS002INVid = 0x11f6, WS002INDid = 0x2001, /* Corega CG-USBRS232R Serial Adapter */, COREGAVid = 0x07aa, COREGADid = 0x002a, /* Y.C. Cable U.S.A., Inc - USB to RS-232 */, YCCABLEVid = 0x05ad, YCCABLEDid = 0x0fba, /* "Superial" USB - Serial */, SUPERIALVid = 0x5372, SUPERIALDid = 0x2303, /* Hewlett-Packard LD220-HP POS Pole Display */, HPVid = 0x03f0, HPLD220Did = 0x3524, }; extern Serialops plops; int plmatch(char *info); static void statusreader(void *u); static void dumpbuf(uchar *buf, int bufsz) { int i; for(i=0; idev, Rd2h | Rvendor | Rdev, Veusb/serial/serial.c 664 0 0 27644 11231311774 12733ustar00paureasys#include #include #include #include #include "usb.h" #include "usbfs.h" #include "serial.h" #include "prolific.h" #include "ucons.h" /* * BUG: This device is not really prepared to use different * serial implementations. That part must be rewritten. * * BUG: An error on the device does not make the driver exit. * It probably should. */ int serialdebug; int debugport; 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, [Qctl] "ctl", 0444, [Qdata] "data", 0640, }; 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 */ static void serialdrain(Serial *ser) { uint baud; baud = ser->baud; /* wait for the 256-byte pipe to clear */ sleep(10 + 256/((1 + baud)*1000)); ser->clearpipes(ser); } int serialreset(Serial *ser) { /* cmd for reset */ fprint(2, "serial: error, resetting\n"); serialdrain(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){ 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++; 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][2]; 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; nw = write(p->epout->dfd, &x, 1); if(nw != 1){ serialrecover(p, ""); return -1; } break; } if (nop) fprint(2, "serial: %c, unsupported nop ctl\n", c); } if(drain) serialdrain(p); if(lines && !set) p->sendlines(p); else if(set && 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 = ser->init(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 *) { Dirtab *tab; i++; /* skip root */ if(i < nelem(dirtab)) tab = &dirtab[i]; else return -1; filldir(fs, d, tab, i); return 0; } static long dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset) { int dfd; long rcount; ulong path; char *buf, *err; /* change */ char *e; Qid q; Serial *ser; q = fid->qid; path = fid->qid.path & ~fs->qid; ser = fs->aux; buf = emallocz(255, 1); err = emallocz(255, 1); qlock(ser); switch(path){ case Qroot: count = usbdirread(fs, q, data, count, offset, dirgen, nil); break; case Qdata: if(debugport && count > 8) count = 8; if(0)dsprint(2, "serial: reading from data\n"); do { dfd = ser->epin->dfd; qunlock(ser); err[0] = 0; rcount = read(dfd, data, count); qlock(ser); if(rcount < 0) snprint(err, 255, "%r"); } while(rcount < 0 && strstr(err, "timed out") != nil); if(rcount < 0){ dsprint(2, "serial: need to recover, data read %ld %r\n", count); serialrecover(ser, err); } if(0)dsprint(2, "serial: read from bulk %ld\n", rcount); count = rcount; break; case Qctl: if(offset != 0){ count = 0; break; } e = serdumpst(ser, buf, 255); count = usbreadbuf(data, count, 0, buf, e - buf); break; } qunlock(ser); free(err); free(buf); return count; } static long dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong) { int nw; ulong path; char *cmd; char err[40]; Serial *ser; ser = fs->aux; path = fid->qid.path & ~fs->qid; qlock(ser); switch(path){ case Qdata: nw = write(ser->epout->dfd, buf, count); if(nw != count){ dsprint(2, "serial: need to recover, status read %d %r\n", nw); snprint(err, sizeof err, "%r"); serialrecover(ser, err); } count = nw; 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 2000"); /* probably a bug */ devctl(ser->epout, "timeout 2000"); 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); } if(debugport){ /* * Debug port uses 8 as maxpkt. * There should be a Serialops op to * customize the device at this point. */ devctl(ser->epin, "maxpkt 8"); devctl(ser->epout, "maxpkt 8"); } 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; Usbdev *ud; epintr = epin = epout = -1; ud = ser->dev->usb; for(i = 0; i < nelem(ud->ep); i++){ if((ep = ud->ep[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, "disk: 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; } static int usage(void) { werrstr("usage: usb/serial [-dkmn] [-a n]"); 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; 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); ser->dev = dev; dev->free = serdevfree; snprint(buf, sizeof buf, "vid %#06x did %#06x", dev->usb->vid, dev->usb->did); ser->fs = serialfs; debugport = 0; if(plmatch(buf) == 0){ ser->hasepintr = 1; ser->Serialops = plops; } else if(uconsmatch(buf) == 0){ ser->Serialops = uconsops; debugport = 1; } if(findendpoints(ser) < 0){ werrstr("serial: endpoints not found"); return -1; } if(serinit(ser) < 0){ dprint(2, "serial: serinit: %r\n"); return -1; } snprint(ser->fs.name, sizeof(ser->fs.name), "eiaU%d", dev->id); ser->fs.dev = dev; incref(dev); ser->fs.aux = ser; usbfsadd(&ser->fs); closedev(dev); return 0; } ase 'b': drain++; p->baud = n; set++; break; case 'c': p->dcd = n; // liusb/serial/serial.h 664 0 0 3061 11224607203 12700ustar00paureasystypedef struct Serialops Serialops; typedef struct Serial Serial; struct Serialops { int (*init)(Serial*); int (*getparam)(Serial*); int (*setparam)(Serial*); int (*clearpipes)(Serial*); int (*sendlines)(Serial*); int (*modemctl)(Serial*, int); int (*setbreak)(Serial*, int); int (*readstatus)(Serial*); }; struct Serial { QLock; Dev *dev; /* usb device*/ Dev *ep; /* endpoint to get events */ Dev *epintr; Dev *epin; Dev *epout; Usbfs fs; int type; int recover; int hasepintr; uchar ctlstate; /* serial parameters */ uint baud; int stop; int mctl; int parity; int bits; int fifo; int limit; int rts; int cts; int dsr; int dcd; int dtr; vlong timer; int blocked; /* for sw flow ctl. BUG: not implemented yet */ int nbreakerr; int ring; int nframeerr; int nparityerr; int novererr; int enabled; Serialops; }; enum { /* soft flow control chars */ CTLS = 023, CTLQ = 021, }; /* * !hget http://lxr.linux.no/source/drivers/usb/serial/pl2303.h|htmlfmt * !hget http://lxr.linux.no/source/drivers/usb/serial/pl2303.c|htmlfmt */ int serialmain(Dev *d, int argc, char *argv[]); typedef struct Cinfo Cinfo; struct Cinfo { int vid; /* usb vendor id */ int did; /* usb device/product id */ int cid; /* controller id assigned by us */ }; extern Cinfo plinfo[]; extern Cinfo uconsinfo[]; extern int serialdebug; #define dsprint if(serialdebug)fprint int serialrecover(Serial *ser, char *err); int serialreset(Serial *ser); char *serdumpst(Serial *ser, char *buf, int bufsz); int serialnop(Serial *); int serialnopctl(Serial *, int); en(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 = sepusb/serial/ucons.c 664 0 0 1241 11214505347 12546ustar00paureasys#include #include #include #include "usb.h" #include "usbfs.h" #include "serial.h" #include "ucons.h" Cinfo uconsinfo[] = { { Net20DCVid, Net20DCDid }, { 0, 0 }, }; int uconsmatch(char *info) { Cinfo *ip; char buf[50]; for(ip = uconsinfo; ip->vid != 0; ip++){ snprint(buf, sizeof buf, "vid %#06x did %#06x", ip->vid, ip->did); dsprint(2, "serial: %s %s", buf, info); if(strstr(info, buf) != nil) return 0; } return -1; } Serialops uconsops = { .init = serialnop, .getparam = serialnop, .setparam = serialnop, .clearpipes = serialnop, .sendlines = serialnop, .modemctl = serialnopctl, .setbreak = serialnopctl, }; e, 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? *usb/serial/ucons.h 664 0 0 214 11214504351 12524ustar00paureasys enum { Net20DCVid = 0x0525, /* Ajays usb debug cable */ Net20DCDid = 0x127a, }; int uconsmatch(char *info); extern Serialops uconsops; usb/usbd/ 775 0 0 0 11255750546 104245ustar00nemosysusb/usbd/dev.c 664 0 0 12325 11220622624 11354ustar00nemosys/* * Framework for USB devices. * Some of them may be embedded into usbd and some of * them may exist as /bin/usb/* binaries on their own. * * When embedded, devmain() is given a ref of an already * configured and open Dev. If devmain() * does not fail it should release this ref when done and * use incref to add further refs to it. */ #include #include #include #include "usb.h" #include "usbd.h" extern Devtab devtab[]; static char* cputype; static int cspmatch(Devtab *dt, int dcsp) { int i; int csp; for(i = 0; i < nelem(dt->csps); i++) if((csp=dt->csps[i]) != 0) if(csp == dcsp) return 1; else if((csp&DCL) && (csp&~DCL) == Class(dcsp)) return 1; return 0; } static int devmatch(Devtab *dt, Usbdev *d) { int i; int c; Conf *cp; if(dt->vid != -1 && d->vid != dt->vid) return 0; if(dt->did != -1 && d->did != dt->did) return 0; if(cspmatch(dt, d->csp)) return 1; for(c = 0; c < Nconf; c++) if((cp=d->conf[c]) != nil) for(i = 0; i < Niface; i++) if(cp->iface[i] != nil) if(cspmatch(dt, cp->iface[i]->csp)) return 1; return 0; } /* We can't use procexec to execute drivers, because * procexec mounts #| at /mnt/temp and we do *not* * have /mnt/temp at boot time. * Instead, we use access to guess if we can execute the file. * and reply as procexec. Be careful that the child inherits * all the shared state of the thread library. It should run unnoticed. */ static void xexec(Channel *c, char *nm, char *args[]) { int pid; if(access(nm, AEXEC) == 0){ pid = rfork(RFFDG|RFREND|RFPROC); switch(pid){ case 0: exec(nm, args); _exits("exec"); case -1: break; default: sendul(c, pid); threadexits(nil); } } } typedef struct Sarg Sarg; struct Sarg{ Port *pp; Devtab* dt; Channel*rc; char fname[80]; char args[128]; char *argv[40]; }; static void startdevproc(void *a) { Sarg *sa = a; Dev *d; Devtab *dt; int argc; char *args, **argv; char *fname; d = sa->pp->dev; dt = sa->dt; args = sa->args; argv = sa->argv; fname = sa->fname; threadsetgrp(threadid()); if(dt->args != nil) strncpy(args, dt->args, sizeof(sa->args)); else args[0] = 0; dprint(2, "%s: start: %s %s\n", argv0, dt->name, args); argv[0] = dt->name; argc = 1; if(args[0] != 0) argc += tokenize(args, argv+1, nelem(sa->argv)-2); argv[argc] = nil; if(dt->init == nil){ if(d->dfd > 0 ){ close(d->dfd); d->dfd = -1; } rfork(RFCFDG); open("/dev/null", OREAD); open("/dev/cons", OWRITE); open("/dev/cons", OWRITE); xexec(sa->rc, argv[0], argv); snprint(fname, sizeof(sa->fname), "/bin/usb/%s", dt->name); xexec(sa->rc, fname, argv); snprint(fname, sizeof(sa->fname), "/boot/%s", dt->name); xexec(sa->rc, fname, argv); if(cputype == nil) cputype = getenv("cputype"); if(cputype != nil){ snprint(fname, sizeof(sa->fname), "/%s/bin/%s", cputype, dt->name); argv[0] = fname; xexec(sa->rc, fname, argv); } fprint(2, "%s: %s: not found. can't exec\n", argv0, dt->name); sendul(sa->rc, -1); threadexits("exec"); }else{ sa->pp->dev = opendev(d->dir); sendul(sa->rc, 0); if(dt->init(d, argc, argv) < 0){ fprint(2, "%s: %s: %r\n", argv0, dt->name); closedev(d); } free(sa); } threadexits(nil); } static void writeinfo(Dev *d) { char buf[128]; char *s; char *se; Usbdev *ud; Conf *c; Iface *ifc; int i, j; ud = d->usb; s = buf; se = buf+sizeof(buf); s = seprint(s, se, "info %s csp %#08ulx", classname(ud->class), ud->csp); for(i = 0; i < ud->nconf; i++){ c = ud->conf[i]; if(c == nil) break; for(j = 0; j < nelem(c->iface); j++){ ifc = c->iface[j]; if(ifc == nil) break; if(ifc->csp != ud->csp) s = seprint(s, se, " csp %#08ulx", ifc->csp); } } s = seprint(s, se, " vid %06#x did %06#x", ud->vid, ud->did); seprint(s, se, " %q %q", ud->vendor, ud->product); devctl(d, "%s", buf); } int startdev(Port *pp) { Dev *d; Usbdev *ud; Devtab *dt; Sarg *sa; Channel *rc; d = pp->dev; assert(d); ud = d->usb; assert(ud != nil); writeinfo(d); if(ud->class == Clhub){ /* * Hubs are handled directly by this process avoiding * concurrent operation so that at most one device * has the config address in use. * We cancel kernel debug for these eps. too chatty. */ pp->hub = newhub(d->dir, d); if(pp->hub == nil) fprint(2, "%s: %s: %r\n", argv0, d->dir); else fprint(2, "usb/hub... "); if(usbdebug > 1) devctl(d, "debug 0"); /* polled hubs are chatty */ return pp->hub == nil ? -1 : 0; } for(dt = devtab; dt->name != nil; dt++) if(devmatch(dt, ud)) break; /* * From here on the device is for the driver. * When we return pp->dev contains a Dev just for us * with only the ctl open. Both devs are released on the last closedev: * driver's upon I/O errors and ours upon port dettach. */ if(dt->name == nil){ dprint(2, "%s: no configured entry for %s (csp %#08lx)\n", argv0, d->dir, ud->csp); close(d->dfd); d->dfd = -1; return 0; } sa = emallocz(sizeof(Sarg), 1); sa->pp = pp; sa->dt = dt; rc = sa->rc = chancreate(sizeof(ulong), 1); procrfork(startdevproc, sa, Stack, RFNOTEG); if(recvul(rc) != 0) free(sa); chanfree(rc); fprint(2, "usb/%s... ", dt->name); sleep(Spawndelay); /* in case we re-spawn too fast */ return 0; } : not implemented yet */ int nbreakerr; int ring; int nframeerr; int nparityerr; int novererr; int enabled; Serialops; }; enum { /* soft flow control chars */ CTLS = 023, CTLQ = 021, }; /* * !hget http://lxr.linux.no/source/drivers/usb/serial/pl2303.h|htmlfmt * !hget http://lxr.linuxusb/usbd/devtab.c 664 0 0 1067 11251431216 12023ustar00nemosys/* machine generated. do not edit */ #include #include #include #include "usb.h" #include "usbd.h" extern int kbmain(Dev*, int, char**); extern int diskmain(Dev*, int, char**); extern int ethermain(Dev*, int, char**); Devtab devtab[] = { /* device, entrypoint, {csp, csp, csp csp}, vid, did */ {"kb", kbmain, {0x010103, 0x020103, 0, 0, }, -1, -1, ""}, {"disk", diskmain, {DCL|8, 0, 0, 0, }, -1, -1, ""}, {"ether", ethermain, {DCL|2, 0, 0, 0, }, -1, -1, ""}, {nil, nil, {0, 0, 0, 0, }, -1, -1, nil}, }; /* end of machine generated */ 664 0 0 1241 11214505347 12546ustar00paureasysusb/usbd/mkdev 775 0 0 3401 11152121021 11426ustar00nemosys#!/bin/rc rfork e DB=usbdb HDR=../lib/usb.h subs=`{ grep '^ Cl.*' $HDR | sed -e 's/.*Cl([a-z]+)[ ]+=[ ]+([0-9]+).*/-e s.\1,.\2,./' | tr A-Z a-z } cat< #include #include #include "usb.h" #include "usbd.h" EOF awk ' collect && /^[^ \t]/{ collect = 0; } $0 ~ /^(embed|auto)/{ section = $0; collect = 1; next; } collect { if(section ~ "embed"){ printf("extern int %smain(Dev*, int, char**);\n", $1); } } ' $DB cat < i) args = args " " $j; else args = $j } for(i = 2; i <= NF; i++){ if($i ~ "^csp="){ ncsp++; sub("csp=", "", $i); printf("%s, ", $i); } else if($i ~ "^subclass="){ ncsp++; sub("subclass=", "", $i); printf("DSC|%s, ", $i); } else if($i ~ "^class="){ ncsp++; sub("class=", "", $i); printf("DCL|%s, ", $i); } else if($i ~ "^proto="){ ncsp++; sub("proto=", "", $i); printf("DPT|%s, ", $i); } else if($i ~ "^vid="){ sub("vid=", "", $i); vid=$i } else if($i ~ "did="){ sub("did=", "", $i); did=$i } } for(i = ncsp; i < 4; i++) printf("0, "); printf("}, %s, %s, \"%s\"},\n", vid, did, args); } ' $DB | sed $subs cat<$target usb/usbd/usbd.c 664 0 0 44230 11255747750 11553ustar00nemosys#include #include #include #include #include "usb.h" #include "usbfs.h" #include "usbd.h" static Channel *portc; static int win; static int verbose; int mainstacksize = Stack; static Hub *hubs; static int nhubs; static int mustdump; static int pollms = Pollms; static char *dsname[] = { "disabled", "attached", "configed" }; static int hubfeature(Hub *h, int port, int f, int on) { int cmd; if(on) cmd = Rsetfeature; else cmd = Rclearfeature; return usbcmd(h->dev, Rh2d|Rclass|Rother, cmd, f, port, nil, 0); } /* * This may be used to detect overcurrent on the hub */ static void checkhubstatus(Hub *h) { uchar buf[4]; int sts; if(h->isroot) /* not for root hubs */ return; if(usbcmd(h->dev, Rd2h|Rclass|Rdev, Rgetstatus, 0, 0, buf, 4) < 0){ dprint(2, "%s: get hub status: %r\n", h->dev->dir); return; } sts = GET2(buf); dprint(2, "hub %s: status %#ux\n", h->dev->dir, sts); } static int confighub(Hub *h) { int type; uchar buf[128]; /* room for extra descriptors */ int i; Usbdev *d; DHub *dd; Port *pp; int nr; int nmap; uchar *PortPwrCtrlMask; int offset; int mask; d = h->dev->usb; for(i = 0; i < nelem(d->ddesc); i++) if(d->ddesc[i] == nil) break; else if(d->ddesc[i]->data.bDescriptorType == Dhub){ dd = (DHub*)&d->ddesc[i]->data; nr = Dhublen; goto Config; } type = Rd2h|Rclass|Rdev; nr = usbcmd(h->dev, type, Rgetdesc, Dhub<<8|0, 0, buf, sizeof buf); if(nr < Dhublen){ dprint(2, "%s: %s: getdesc hub: %r\n", argv0, h->dev->dir); return -1; } dd = (DHub*)buf; Config: h->nport = dd->bNbrPorts; nmap = 1 + h->nport/8; if(nr < 7 + 2*nmap){ fprint(2, "%s: %s: descr. too small\n", argv0, h->dev->dir); return -1; } h->port = emallocz((h->nport+1)*sizeof(Port), 1); h->pwrms = dd->bPwrOn2PwrGood*2; if(h->pwrms < Powerdelay) h->pwrms = Powerdelay; h->maxcurrent = dd->bHubContrCurrent; h->pwrmode = dd->wHubCharacteristics[0] & 3; h->compound = (dd->wHubCharacteristics[0] & (1<<2))!=0; h->leds = (dd->wHubCharacteristics[0] & (1<<7)) != 0; PortPwrCtrlMask = dd->DeviceRemovable + nmap; for(i = 1; i <= h->nport; i++){ pp = &h->port[i]; offset = i/8; mask = 1<<(i%8); pp->removable = (dd->DeviceRemovable[offset] & mask) != 0; pp->pwrctl = (PortPwrCtrlMask[offset] & mask) != 0; } return 0; } static void configroothub(Hub *h) { Dev *d; char buf[128]; char *p; int nr; d = h->dev; h->nport = 2; h->maxpkt = 8; seek(d->cfd, 0, 0); nr = read(d->cfd, buf, sizeof(buf)-1); if(nr < 0) goto Done; buf[nr] = 0; p = strstr(buf, "ports "); if(p == nil) fprint(2, "%s: %s: no port information\n", argv0, d->dir); else h->nport = atoi(p+6); p = strstr(buf, "maxpkt "); if(p == nil) fprint(2, "%s: %s: no maxpkt information\n", argv0, d->dir); else h->maxpkt = atoi(p+7); Done: h->port = emallocz((h->nport+1)*sizeof(Port), 1); dprint(2, "%s: %s: ports %d maxpkt %d\n", argv0, d->dir, h->nport, h->maxpkt); } Hub* newhub(char *fn, Dev *d) { Hub *h; int i; Usbdev *ud; h = emallocz(sizeof(Hub), 1); h->isroot = (d == nil); if(h->isroot){ h->dev = opendev(fn); if(h->dev == nil){ fprint(2, "%s: opendev: %s: %r", argv0, fn); goto Fail; } if(opendevdata(h->dev, ORDWR) < 0){ fprint(2, "%s: opendevdata: %s: %r\n", argv0, fn); goto Fail; } configroothub(h); /* never fails */ }else{ h->dev = d; if(confighub(h) < 0){ fprint(2, "%s: %s: config: %r\n", argv0, fn); goto Fail; } } if(h->dev == nil){ fprint(2, "%s: opendev: %s: %r\n", argv0, fn); goto Fail; } devctl(h->dev, "hub"); ud = h->dev->usb; if(h->isroot) devctl(h->dev, "info roothub csp %#08ux ports %d", 0x000009, h->nport); else{ devctl(h->dev, "info hub csp %#08ulx ports %d %q %q", ud->csp, h->nport, ud->vendor, ud->product); for(i = 1; i <= h->nport; i++) if(hubfeature(h, i, Fportpower, 1) < 0) fprint(2, "%s: %s: power: %r\n", argv0, fn); sleep(h->pwrms); for(i = 1; i <= h->nport; i++) if(h->leds != 0) hubfeature(h, i, Fportindicator, 1); } h->next = hubs; hubs = h; nhubs++; dprint(2, "%s: hub %#p allocated:", argv0, h); dprint(2, " ports %d pwrms %d max curr %d pwrm %d cmp %d leds %d\n", h->nport, h->pwrms, h->maxcurrent, h->pwrmode, h->compound, h->leds); incref(h->dev); return h; Fail: if(d != nil) devctl(d, "detach"); free(h->port); free(h); dprint(2, "%s: hub %#p failed to start:", argv0, h); return nil; } static void portdetach(Hub *h, int p); /* * If during enumeration we get an I/O error the hub is gone or * in pretty bad shape. Because of retries of failed usb commands * (and the sleeps they include) it can take a while to detach all * ports for the hub. This detaches all ports and makes the hub void. * The parent hub will detect a detach (probably right now) and * close it later. */ static void hubfail(Hub *h) { int i; for(i = 1; i <= h->nport; i++) portdetach(h, i); h->failed = 1; } static void closehub(Hub *h) { Hub **hl; dprint(2, "%s: closing hub %#p\n", argv0, h); for(hl = &hubs; *hl != nil; hl = &(*hl)->next) if(*hl == h) break; if(*hl == nil) sysfatal("closehub: no hub"); *hl = h->next; nhubs--; hubfail(h); /* detach all ports */ free(h->port); assert(h->dev != nil); devctl(h->dev, "detach"); closedev(h->dev); free(h); } static int portstatus(Hub *h, int p) { Dev *d; uchar buf[4]; int t; int sts; int dbg; dbg = usbdebug; if(dbg != 0 && dbg < 4) usbdebug = 1; /* do not be too chatty */ d = h->dev; t = Rd2h|Rclass|Rother; if(usbcmd(d, t, Rgetstatus, 0, p, buf, sizeof(buf)) < 0) sts = -1; else sts = GET2(buf); usbdebug = dbg; return sts; } static char* stsstr(int sts) { static char s[80]; char *e; e = s; if(sts&PSsuspend) *e++ = 'z'; if(sts&PSreset) *e++ = 'r'; if(sts&PSslow) *e++ = 'l'; if(sts&PShigh) *e++ = 'h'; if(sts&PSchange) *e++ = 'c'; if(sts&PSenable) *e++ = 'e'; if(sts&PSstatuschg) *e++ = 's'; if(sts&PSpresent) *e++ = 'p'; if(e == s) *e++ = '-'; *e = 0; return s; } static int getmaxpkt(Dev *d, int islow) { uchar buf[64]; /* More room to try to get device-specific descriptors */ DDev *dd; dd = (DDev*)buf; if(islow) dd->bMaxPacketSize0 = 8; else dd->bMaxPacketSize0 = 64; if(usbcmd(d, Rd2h|Rstd|Rdev, Rgetdesc, Ddev<<8|0, 0, buf, sizeof(buf)) < 0) return -1; return dd->bMaxPacketSize0; } /* * BUG: does not consider max. power avail. */ static Dev* portattach(Hub *h, int p, int sts) { Dev *d; Port *pp; Dev *nd; char fname[80]; char buf[40]; char *sp; int mp; int nr; d = h->dev; pp = &h->port[p]; nd = nil; pp->state = Pattached; dprint(2, "%s: %s: port %d attach sts %#ux\n", argv0, d->dir, p, sts); sleep(Connectdelay); if(hubfeature(h, p, Fportenable, 1) < 0) dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p); sleep(Enabledelay); if(hubfeature(h, p, Fportreset, 1) < 0){ dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p); goto Fail; } sleep(Resetdelay); sts = portstatus(h, p); if(sts < 0) goto Fail; if((sts & PSenable) == 0){ dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p); hubfeature(h, p, Fportenable, 1); sts = portstatus(h, p); if((sts & PSenable) == 0) goto Fail; } sp = "full"; if(sts & PSslow) sp = "low"; if(sts & PShigh) sp = "high"; dprint(2, "%s: %s: port %d: attached status %#ux\n", argv0, d->dir, p, sts); if(devctl(d, "newdev %s %d", sp, p) < 0){ fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p); goto Fail; } seek(d->cfd, 0, 0); nr = read(d->cfd, buf, sizeof(buf)-1); if(nr == 0){ fprint(2, "%s: %s: port %d: newdev: eof\n", argv0, d->dir, p); goto Fail; } if(nr < 0){ fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p); goto Fail; } buf[nr] = 0; snprint(fname, sizeof(fname), "/dev/usb/%s", buf); nd = opendev(fname); if(nd == nil){ fprint(2, "%s: %s: port %d: opendev: %r\n", argv0, d->dir, p); goto Fail; } if(usbdebug > 2) devctl(nd, "debug 1"); if(opendevdata(nd, ORDWR) < 0){ fprint(2, "%s: %s: opendevdata: %r\n", argv0, nd->dir); goto Fail; } if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetaddress, nd->id, 0, nil, 0) < 0){ dprint(2, "%s: %s: port %d: setaddress: %r\n", argv0, d->dir, p); goto Fail; } if(devctl(nd, "address") < 0){ dprint(2, "%s: %s: port %d: set address: %r\n", argv0, d->dir, p); goto Fail; } mp=getmaxpkt(nd, strcmp(sp, "low") == 0); if(mp < 0){ dprint(2, "%s: %s: port %d: getmaxpkt: %r\n", argv0, d->dir, p); goto Fail; }else{ dprint(2, "%s; %s: port %d: maxpkt %d\n", argv0, d->dir, p, mp); devctl(nd, "maxpkt %d", mp); } if((sts & PSslow) != 0 && strcmp(sp, "full") == 0) dprint(2, "%s: %s: port %d: %s is full speed when port is low\n", argv0, d->dir, p, nd->dir); if(configdev(nd) < 0){ dprint(2, "%s: %s: port %d: configdev: %r\n", argv0, d->dir, p); goto Fail; } /* * We always set conf #1. BUG. */ if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){ dprint(2, "%s: %s: port %d: setconf: %r\n", argv0, d->dir, p); unstall(nd, nd, Eout); if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0) goto Fail; } dprint(2, "%s: %U", argv0, nd); pp->state = Pconfiged; dprint(2, "%s: %s: port %d: configed: %s\n", argv0, d->dir, p, nd->dir); return pp->dev = nd; Fail: pp->state = Pdisabled; pp->sts = 0; if(pp->hub != nil) pp->hub = nil; /* hub closed by enumhub */ hubfeature(h, p, Fportenable, 0); if(nd != nil) devctl(nd, "detach"); closedev(nd); return nil; } static void portdetach(Hub *h, int p) { Dev *d; Port *pp; extern void usbfsgone(char*); d = h->dev; pp = &h->port[p]; /* * Clear present, so that we detect an attach on reconnects. */ pp->sts &= ~(PSpresent|PSenable); if(pp->state == Pdisabled) return; pp->state = Pdisabled; dprint(2, "%s: %s: port %d: detached\n", argv0, d->dir, p); if(pp->hub != nil){ closehub(pp->hub); pp->hub = nil; } if(pp->dev != nil){ devctl(pp->dev, "detach"); usbfsgone(pp->dev->dir); closedev(pp->dev); pp->dev = nil; } } /* * The next two functions are included to * perform a port reset asked for by someone (usually a driver). * This must be done while no other device is in using the * configuration address and with care to keep the old address. * To keep drivers decoupled from usbd they write the reset request * to the #u/usb/epN.0/ctl file and then exit. * This is unfortunate because usbd must now poll twice as much. * * An alternative to this reset process would be for the driver to detach * the device. The next function could see that, issue a port reset, and * then restart the driver once to see if it's a temporary error. * * The real fix would be to use interrupt endpoints for non-root hubs * (would probably make some hubs fail) and add an events file to * the kernel to report events to usbd. This is a severe change not * yet implemented. */ static int portresetwanted(Hub *h, int p) { char buf[5]; Port *pp; Dev *nd; pp = &h->port[p]; nd = pp->dev; if(nd != nil && nd->cfd >= 0 && pread(nd->cfd, buf, 5, 0LL) == 5) return strncmp(buf, "reset", 5) == 0; else return 0; } static void portreset(Hub *h, int p) { int sts; Dev *d, *nd; Port *pp; d = h->dev; pp = &h->port[p]; nd = pp->dev; dprint(2, "%s: %s: port %d: resetting\n", argv0, d->dir, p); if(hubfeature(h, p, Fportreset, 1) < 0){ dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p); goto Fail; } sleep(Resetdelay); sts = portstatus(h, p); if(sts < 0) goto Fail; if((sts & PSenable) == 0){ dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p); hubfeature(h, p, Fportenable, 1); sts = portstatus(h, p); if((sts & PSenable) == 0) goto Fail; } nd = pp->dev; opendevdata(nd, ORDWR); if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetaddress, nd->id, 0, nil, 0) < 0){ dprint(2, "%s: %s: port %d: setaddress: %r\n", argv0, d->dir, p); goto Fail; } if(devctl(nd, "address") < 0){ dprint(2, "%s: %s: port %d: set address: %r\n", argv0, d->dir, p); goto Fail; } if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){ dprint(2, "%s: %s: port %d: setconf: %r\n", argv0, d->dir, p); unstall(nd, nd, Eout); if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0) goto Fail; } if(nd->dfd >= 0) close(nd->dfd); return; Fail: pp->state = Pdisabled; pp->sts = 0; if(pp->hub != nil) pp->hub = nil; /* hub closed by enumhub */ hubfeature(h, p, Fportenable, 0); if(nd != nil) devctl(nd, "detach"); closedev(nd); } static int portgone(Port *pp, int sts) { if(sts < 0) return 1; /* * If it was enabled and it's not now then it may be reconnect. * We pretend it's gone and later we'll see it as attached. */ if((pp->sts & PSenable) != 0 && (sts & PSenable) == 0) return 1; return (pp->sts & PSpresent) != 0 && (sts & PSpresent) == 0; } static int enumhub(Hub *h, int p) { int sts; Dev *d; Port *pp; int onhubs; if(h->failed) return 0; d = h->dev; if(usbdebug > 3) fprint(2, "%s: %s: port %d enumhub\n", argv0, d->dir, p); sts = portstatus(h, p); if(sts < 0){ hubfail(h); /* avoid delays on detachment */ return -1; } pp = &h->port[p]; onhubs = nhubs; if((sts & PSsuspend) != 0){ if(hubfeature(h, p, Fportenable, 1) < 0) dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p); sleep(Enabledelay); sts = portstatus(h, p); fprint(2, "%s: %s: port %d: resumed (sts %#ux)\n", argv0, d->dir, p, sts); } if((pp->sts & PSpresent) == 0 && (sts & PSpresent) != 0){ if(portattach(h, p, sts) != nil) if(startdev(pp) < 0) portdetach(h, p); }else if(portgone(pp, sts)) portdetach(h, p); else if(portresetwanted(h, p)) portreset(h, p); else if(pp->sts != sts){ dprint(2, "%s: %s port %d: sts %s %#x ->", argv0, d->dir, p, stsstr(pp->sts), pp->sts); dprint(2, " %s %#x\n",stsstr(sts), sts); } pp->sts = sts; if(onhubs != nhubs) return -1; return 0; } static void dump(void) { Hub *h; int i; mustdump = 0; for(h = hubs; h != nil; h = h->next) for(i = 1; i <= h->nport; i++) fprint(2, "%s: hub %#p %s port %d: %U", argv0, h, h->dev->dir, i, h->port[i].dev); usbfsdirdump(); } static void work(void *a) { Channel *portc; char *fn; Hub *h; int i; portc = a; hubs = nil; /* * Receive requests for root hubs */ while((fn = recvp(portc)) != nil){ dprint(2, "%s: %s starting\n", argv0, fn); h = newhub(fn, nil); if(h == nil) fprint(2, "%s: %s: %r\n", argv0, fn); free(fn); } /* * Enumerate (and acknowledge after first enumeration). * Do NOT perform enumeration concurrently for the same * controller. new devices attached respond to a default * address (0) after reset, thus enumeration has to work * one device at a time at least before addresses have been * assigned. * Do not use hub interrupt endpoint because we * have to poll the root hub(s) in any case. */ for(;;){ Again: for(h = hubs; h != nil; h = h->next) for(i = 1; i <= h->nport; i++) if(enumhub(h, i) < 0){ /* changes in hub list; repeat */ goto Again; } if(portc != nil){ sendp(portc, nil); portc = nil; } sleep(pollms); if(mustdump) dump(); } } static int cfswalk(Usbfs*, Fid *, char *) { werrstr(Enotfound); return -1; } static int cfsopen(Usbfs*, Fid *, int) { return 0; } static long cfsread(Usbfs*, Fid *, void *, long , vlong ) { return 0; } static void setdrvargs(char *name, char *args) { Devtab *dt; extern Devtab devtab[]; for(dt = devtab; dt->name != nil; dt++) if(strstr(dt->name, name) != nil) dt->args = estrdup(args); } static long cfswrite(Usbfs*, Fid *, void *data, long cnt, vlong ) { char buf[80]; char *toks[4]; if(cnt > sizeof(buf)) cnt = sizeof(buf) - 1; strncpy(buf, data, cnt); buf[cnt] = 0; if(cnt > 0 && buf[cnt-1] == '\n') buf[cnt-1] = 0; if(strncmp(buf, "dump", 4) == 0){ mustdump = 1; return cnt; } if(strncmp(buf, "reset", 5) == 0){ werrstr("reset not implemented"); return -1; } if(tokenize(buf, toks, nelem(toks)) != 2){ werrstr("usage: debug|fsdebug n"); return -1; } if(strcmp(toks[0], "debug") == 0) usbdebug = atoi(toks[1]); else if(strcmp(toks[0], "fsdebug") == 0) usbfsdebug = atoi(toks[1]); else if(strcmp(toks[0], "kbargs") == 0) setdrvargs("kb", toks[1]); else if(strcmp(toks[0], "diskargs") == 0) setdrvargs("disk", toks[1]); else{ werrstr("unkown ctl '%s'", buf); return -1; } fprint(2, "%s: debug %d fsdebug %d\n", argv0, usbdebug, usbfsdebug); return cnt; } static int cfsstat(Usbfs* fs, Qid qid, Dir *d) { d->qid = qid; d->qid.path |= fs->qid; d->qid.type = 0; d->qid.vers = 0; d->name = "usbdctl"; d->length = 0; d->mode = 0664; return 0; } static Usbfs ctlfs = { .walk = cfswalk, .open = cfsopen, .read = cfsread, .write = cfswrite, .stat = cfsstat }; static void args(void) { char *s; s = getenv("usbdebug"); if(s != nil) usbdebug = atoi(s); free(s); s = getenv("usbfsdebug"); if(s != nil) usbfsdebug = atoi(s); free(s); s = getenv("kbargs"); if(s != nil) setdrvargs("kb", s); free(s); s = getenv("diskargs"); if(s != nil) setdrvargs("disk", s); free(s); } static void usage(void) { fprint(2, "usage: %s [-Dd] [-s srv] [-m mnt] [dev...]\n", argv0); threadexitsall("usage"); } extern void usbfsexits(int); void threadmain(int argc, char **argv) { int i; Dir *d; int fd; int nd; char *err; char *srv; char *mnt; srv = "usb"; mnt = "/dev"; ARGBEGIN{ case 'D': usbfsdebug++; break; case 'd': usbdebug++; break; case 's': srv = EARGF(usage()); break; case 'i': pollms = atoi(EARGF(usage())); break; case 'm': mnt = EARGF(usage()); break; default: usage(); }ARGEND; if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0) sysfatal("#u: %r"); args(); fmtinstall('U', Ufmt); quotefmtinstall(); rfork(RFNOTEG); portc = chancreate(sizeof(char *), 0); if(portc == nil) sysfatal("chancreate"); proccreate(work, portc, Stack); if(argc == 0){ fd = open("/dev/usb", OREAD); if(fd < 0) sysfatal("/dev/usb: %r"); nd = dirreadall(fd, &d); close(fd); if(nd < 2) sysfatal("/dev/usb: no hubs"); for(i = 0; i < nd; i++) if(strcmp(d[i].name, "ctl") != 0) sendp(portc, smprint("/dev/usb/%s", d[i].name)); free(d); }else for(i = 0; i < argc; i++) sendp(portc, strdup(argv[i])); sendp(portc, nil); err = recvp(portc); chanfree(portc); usbfsexits(0); usbfsinit(srv, mnt, &usbdirfs, MAFTER); snprint(ctlfs.name, sizeof(ctlfs.name), "usbdctl"); usbfsadd(&ctlfs); threadexits(err); } t address: %r\n", argv0, d->dir, p); goto Fail; } mp=getmaxpkt(nd, strcmp(sp, "low") == 0); if(mp < 0){ dprint(2, "%s: %s: port %d: getmaxpkt: %r\n", argv0, d->dir, p); goto Fail; }else{ dprint(2, "%s; %s: port %d: maxpkt %d\n", argv0, d->dir, p, mp); devctl(nd, "maxpkt %d", mp); } if((sts & PSslow) != 0 && strcmp(sp, "full") == 0) dprint(usb/usbd/usbd.h 664 0 0 5424 11251231337 11523ustar00nemosystypedef struct Hub Hub; typedef struct Port Port; typedef struct DHub DHub; typedef struct Devtab Devtab; typedef struct Usbfs Usbfs; enum { Stack = 32*1024, Dhub = 0x29, /* hub descriptor type */ Dhublen = 9, /* hub descriptor length */ /* hub class feature selectors */ Fhublocalpower = 0, Fhubovercurrent = 1, Fportconnection = 0, Fportenable = 1, Fportsuspend = 2, Fportovercurrent = 3, Fportreset = 4, Fportpower = 8, Fportlowspeed = 9, Fcportconnection = 16, Fcportenable = 17, Fcportsuspend = 18, Fcportovercurrent= 19, Fcportreset = 20, Fportindicator = 22, /* Port status and status change bits * Constants at /sys/src/9/pc/usb.h starting with HP- * must have the same values or root hubs won't work. */ PSpresent = 0x0001, PSenable = 0x0002, PSsuspend = 0x0004, PSovercurrent = 0x0008, PSreset = 0x0010, PSpower = 0x0100, PSslow = 0x0200, PShigh = 0x0400, PSstatuschg = 0x10000, /* PSpresent changed */ PSchange = 0x20000, /* PSenable changed */ /* port/device state */ Pdisabled = 0, /* must be 0 */ Pattached, Pconfiged, /* Delays, timeouts (ms) */ Spawndelay = 1000, /* how often may we re-spawn a driver */ Connectdelay = 1000, /* how much to wait after a connect */ Resetdelay = 20, /* how much to wait after a reset */ Enabledelay = 20, /* how much to wait after an enable */ Powerdelay = 100, /* after powering up ports */ Pollms = 250, /* port poll interval */ Chgdelay = 100, /* waiting for port become stable */ Chgtmout = 1000, /* ...but at most this much */ /* * device tab for embedded usb drivers. */ DCL = 0x01000000, /* csp identifies just class */ DSC = 0x02000000, /* csp identifies just subclass */ DPT = 0x04000000, /* csp identifies just proto */ }; struct Hub { uchar pwrmode; uchar compound; uchar pwrms; /* time to wait in ms */ uchar maxcurrent; /* after powering port*/ int leds; /* has port indicators? */ int maxpkt; uchar nport; Port *port; int failed; /* I/O error while enumerating */ int isroot; /* set if root hub */ Dev *dev; /* for this hub */ Hub *next; /* in list of hubs */ }; struct Port { int state; /* state of the device */ int sts; /* old port status */ uchar removable; uchar pwrctl; Dev *dev; /* attached device (if non-nil) */ Hub *hub; /* non-nil if hub attached */ }; /* USB HUB descriptor */ struct DHub { uchar bLength; uchar bDescriptorType; uchar bNbrPorts; uchar wHubCharacteristics[2]; uchar bPwrOn2PwrGood; uchar bHubContrCurrent; uchar DeviceRemovable[1]; /* variable length */ }; struct Devtab { char *name; int (*init)(Dev*, int, char**); /* nil if external */ int csps[4]; int vid; int did; char *args; }; Hub* newhub(char *fn, Dev *d); int startdev(Port *pp); void threadmain(int argc, char **argv); extern Usbfs usbdfsops; goto Fail; } if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){ dprint(2, "%s: %s: port %d: setconf: %r\n", argv0, d->dir, p); unstall(nd, nd, Eout); if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0) goto usb/usbd/usbdb 664 0 0 343 11232036641 11412ustar00nemosys# only kb, disk, and ether are prepared for embedding. # others are not yet converted to sit in the usbd device driver library embed kb csp=0x010103 csp=0x020103 args= disk class=storage args= ether class=comms args= auto usb/usbfat: 775 0 0 2111 11152121020 10735ustar00nemosys#!/bin/rc # usbfat: [disk [mtpt]] - mount a USB disk's MS FAT file system rfork e disk = () mtpt = /n/usb test -e /dev/usb || bind -a '#u' /dev || { echo no '#u/usb' >[1=2] exit nousb } test -e /dev/usbdctl || mount -a /srv/usb /dev || { echo cannot mount /srv/usb >[1=2] exit nousbd } disks=() mtpt=() switch ($#*) { case 0 ; case 1 disks = $1 case 2 disks = $1 mtpt = $2 case * echo usage: $0 ' [disk [mtpt]]' >[1=2] exit usage } if (~ $#disks 0){ if(! test -e /dev/sdU*/data){ echo no usb disks >[1=2] exit nodisk } disks = `{echo /dev/sdU*/data} } for(d in $disks){ if(~ $d sdU*.[0-9]*) d=/dev/$d/data if(test -e $d){ name=`{echo $d | sed 's/.*(sdU[0-9]+\.[0-9]+).*/\1/'} if(~ $#mtpt 0) mnt=/n/$name if not mnt=$mtpt # don't mount it if it seems to be already mounted. if(! test -e $mnt/*) if(grep -s geometry /dev/$name/ctl){ blk = `{disk/fdisk -p $d | awk '/^part dos / {print $3}'} if (! ~ $#blk 0 && ~ $blk [0-9]*) d=$d:$blk mount -c <{dossrv -sf $d >[2]/dev/null} $mnt && echo $mnt } } if not echo $d does not exist } exit '' ;;){ Again: for(h = hubs; h != nil; h = h->next) for(i = 1; i <= h->nport; i++) if(enumhub(h, i) < 0){ /* changes in hub list; repeat */ goto Again; } if(portc != nil){ sendp(portc, nil); portc = nil; } sleep(pollms); if(mustdump) dump(); } } static int cfswalk(Usbfs*, Fid *, char *) { werrstr(Enotfound); return -1; } static int cfsopen(Usbfs*, Fid *, int) { return 0; } static long cfsre