1906943f9SDavid du Colombier #include <u.h>
2906943f9SDavid du Colombier #include <libc.h>
3906943f9SDavid du Colombier #include <thread.h>
4906943f9SDavid du Colombier #include <bio.h>
5906943f9SDavid du Colombier #include "usb.h"
6906943f9SDavid du Colombier
7906943f9SDavid du Colombier int
parsedev(Dev * xd,uchar * b,int n)8906943f9SDavid du Colombier parsedev(Dev *xd, uchar *b, int n)
9906943f9SDavid du Colombier {
10906943f9SDavid du Colombier Usbdev *d;
11906943f9SDavid du Colombier DDev *dd;
12906943f9SDavid du Colombier char *hd;
13906943f9SDavid du Colombier
14906943f9SDavid du Colombier d = xd->usb;
15906943f9SDavid du Colombier assert(d != nil);
16906943f9SDavid du Colombier dd = (DDev*)b;
17906943f9SDavid du Colombier if(usbdebug>1){
18906943f9SDavid du Colombier hd = hexstr(b, Ddevlen);
19906943f9SDavid du Colombier fprint(2, "%s: parsedev %s: %s\n", argv0, xd->dir, hd);
20906943f9SDavid du Colombier free(hd);
21906943f9SDavid du Colombier }
22906943f9SDavid du Colombier if(dd->bLength < Ddevlen){
23906943f9SDavid du Colombier werrstr("short dev descr. (%d < %d)", dd->bLength, Ddevlen);
24906943f9SDavid du Colombier return -1;
25906943f9SDavid du Colombier }
26906943f9SDavid du Colombier if(dd->bDescriptorType != Ddev){
27906943f9SDavid du Colombier werrstr("%d is not a dev descriptor", dd->bDescriptorType);
28906943f9SDavid du Colombier return -1;
29906943f9SDavid du Colombier }
30906943f9SDavid du Colombier d->csp = CSP(dd->bDevClass, dd->bDevSubClass, dd->bDevProtocol);
31906943f9SDavid du Colombier d->ep[0]->maxpkt = xd->maxpkt = dd->bMaxPacketSize0;
32906943f9SDavid du Colombier d->class = dd->bDevClass;
33906943f9SDavid du Colombier d->nconf = dd->bNumConfigurations;
34906943f9SDavid du Colombier if(d->nconf == 0)
35906943f9SDavid du Colombier dprint(2, "%s: %s: no configurations\n", argv0, xd->dir);
36906943f9SDavid du Colombier d->vid = GET2(dd->idVendor);
37906943f9SDavid du Colombier d->did = GET2(dd->idProduct);
3880e9508eSDavid du Colombier d->dno = GET2(dd->bcdDev);
39906943f9SDavid du Colombier d->vsid = dd->iManufacturer;
40906943f9SDavid du Colombier d->psid = dd->iProduct;
41906943f9SDavid du Colombier d->ssid = dd->iSerialNumber;
42906943f9SDavid du Colombier if(n > Ddevlen && usbdebug>1)
43906943f9SDavid du Colombier fprint(2, "%s: %s: parsedev: %d bytes left",
44906943f9SDavid du Colombier argv0, xd->dir, n - Ddevlen);
45906943f9SDavid du Colombier return Ddevlen;
46906943f9SDavid du Colombier }
47906943f9SDavid du Colombier
48906943f9SDavid du Colombier static int
parseiface(Usbdev * d,Conf * c,uchar * b,int n,Iface ** ipp,Altc ** app)49906943f9SDavid du Colombier parseiface(Usbdev *d, Conf *c, uchar *b, int n, Iface **ipp, Altc **app)
50906943f9SDavid du Colombier {
51906943f9SDavid du Colombier int class, subclass, proto;
52906943f9SDavid du Colombier int ifid, altid;
53906943f9SDavid du Colombier DIface *dip;
54906943f9SDavid du Colombier Iface *ip;
55906943f9SDavid du Colombier
56906943f9SDavid du Colombier assert(d != nil && c != nil);
57906943f9SDavid du Colombier if(n < Difacelen){
58906943f9SDavid du Colombier werrstr("short interface descriptor");
59906943f9SDavid du Colombier return -1;
60906943f9SDavid du Colombier }
61906943f9SDavid du Colombier dip = (DIface *)b;
62906943f9SDavid du Colombier ifid = dip->bInterfaceNumber;
63906943f9SDavid du Colombier if(ifid < 0 || ifid >= nelem(c->iface)){
64906943f9SDavid du Colombier werrstr("bad interface number %d", ifid);
65906943f9SDavid du Colombier return -1;
66906943f9SDavid du Colombier }
67906943f9SDavid du Colombier if(c->iface[ifid] == nil)
68906943f9SDavid du Colombier c->iface[ifid] = emallocz(sizeof(Iface), 1);
69*cb26fd37SDavid du Colombier else{
70*cb26fd37SDavid du Colombier /* hack to avoid unsupported uasp disk interface */
71*cb26fd37SDavid du Colombier if(dip->bInterfaceClass == Clstorage && dip->bInterfaceProtocol != 0x50)
72*cb26fd37SDavid du Colombier return 0;
73*cb26fd37SDavid du Colombier }
74906943f9SDavid du Colombier ip = c->iface[ifid];
75906943f9SDavid du Colombier class = dip->bInterfaceClass;
76906943f9SDavid du Colombier subclass = dip->bInterfaceSubClass;
77906943f9SDavid du Colombier proto = dip->bInterfaceProtocol;
78906943f9SDavid du Colombier ip->csp = CSP(class, subclass, proto);
79906943f9SDavid du Colombier if(d->csp == 0) /* use csp from 1st iface */
80906943f9SDavid du Colombier d->csp = ip->csp; /* if device has none */
81906943f9SDavid du Colombier if(d->class == 0)
82906943f9SDavid du Colombier d->class = class;
83906943f9SDavid du Colombier ip->id = ifid;
84906943f9SDavid du Colombier if(c == d->conf[0] && ifid == 0) /* ep0 was already there */
85906943f9SDavid du Colombier d->ep[0]->iface = ip;
86906943f9SDavid du Colombier altid = dip->bAlternateSetting;
87906943f9SDavid du Colombier if(altid < 0 || altid >= nelem(ip->altc)){
88906943f9SDavid du Colombier werrstr("bad alternate conf. number %d", altid);
89906943f9SDavid du Colombier return -1;
90906943f9SDavid du Colombier }
91906943f9SDavid du Colombier if(ip->altc[altid] == nil)
92906943f9SDavid du Colombier ip->altc[altid] = emallocz(sizeof(Altc), 1);
93906943f9SDavid du Colombier *ipp = ip;
94906943f9SDavid du Colombier *app = ip->altc[altid];
95906943f9SDavid du Colombier return Difacelen;
96906943f9SDavid du Colombier }
97906943f9SDavid du Colombier
98906943f9SDavid du Colombier extern Ep* mkep(Usbdev *, int);
99906943f9SDavid du Colombier
100906943f9SDavid du Colombier static int
parseendpt(Usbdev * d,Conf * c,Iface * ip,Altc * altc,uchar * b,int n,Ep ** epp)101906943f9SDavid du Colombier parseendpt(Usbdev *d, Conf *c, Iface *ip, Altc *altc, uchar *b, int n, Ep **epp)
102906943f9SDavid du Colombier {
10316146bc9SDavid du Colombier int i, dir, epid;
104906943f9SDavid du Colombier Ep *ep;
105906943f9SDavid du Colombier DEp *dep;
106906943f9SDavid du Colombier
107906943f9SDavid du Colombier assert(d != nil && c != nil && ip != nil && altc != nil);
108906943f9SDavid du Colombier if(n < Deplen){
109906943f9SDavid du Colombier werrstr("short endpoint descriptor");
110906943f9SDavid du Colombier return -1;
111906943f9SDavid du Colombier }
112906943f9SDavid du Colombier dep = (DEp *)b;
113906943f9SDavid du Colombier altc->attrib = dep->bmAttributes; /* here? */
114906943f9SDavid du Colombier altc->interval = dep->bInterval;
115906943f9SDavid du Colombier
116906943f9SDavid du Colombier epid = dep->bEndpointAddress & 0xF;
117906943f9SDavid du Colombier assert(epid < nelem(d->ep));
118906943f9SDavid du Colombier if(dep->bEndpointAddress & 0x80)
119906943f9SDavid du Colombier dir = Ein;
120906943f9SDavid du Colombier else
121906943f9SDavid du Colombier dir = Eout;
122906943f9SDavid du Colombier ep = d->ep[epid];
123906943f9SDavid du Colombier if(ep == nil){
124906943f9SDavid du Colombier ep = mkep(d, epid);
125906943f9SDavid du Colombier ep->dir = dir;
126906943f9SDavid du Colombier }else if((ep->addr & 0x80) != (dep->bEndpointAddress & 0x80))
127906943f9SDavid du Colombier ep->dir = Eboth;
128906943f9SDavid du Colombier ep->maxpkt = GET2(dep->wMaxPacketSize);
129906943f9SDavid du Colombier ep->ntds = 1 + ((ep->maxpkt >> 11) & 3);
130906943f9SDavid du Colombier ep->maxpkt &= 0x7FF;
131906943f9SDavid du Colombier ep->addr = dep->bEndpointAddress;
132906943f9SDavid du Colombier ep->type = dep->bmAttributes & 0x03;
133906943f9SDavid du Colombier ep->isotype = (dep->bmAttributes>>2) & 0x03;
134906943f9SDavid du Colombier ep->conf = c;
135906943f9SDavid du Colombier ep->iface = ip;
136906943f9SDavid du Colombier for(i = 0; i < nelem(ip->ep); i++)
137906943f9SDavid du Colombier if(ip->ep[i] == nil)
138906943f9SDavid du Colombier break;
139906943f9SDavid du Colombier if(i == nelem(ip->ep)){
14016146bc9SDavid du Colombier werrstr("parseendpt: bug: too many end points on interface "
14116146bc9SDavid du Colombier "with csp %#lux", ip->csp);
142906943f9SDavid du Colombier fprint(2, "%s: %r\n", argv0);
143906943f9SDavid du Colombier return -1;
144906943f9SDavid du Colombier }
145906943f9SDavid du Colombier *epp = ip->ep[i] = ep;
146906943f9SDavid du Colombier return Dep;
147906943f9SDavid du Colombier }
148906943f9SDavid du Colombier
149906943f9SDavid du Colombier static char*
dname(int dtype)150906943f9SDavid du Colombier dname(int dtype)
151906943f9SDavid du Colombier {
152906943f9SDavid du Colombier switch(dtype){
153906943f9SDavid du Colombier case Ddev: return "device";
154906943f9SDavid du Colombier case Dconf: return "config";
155906943f9SDavid du Colombier case Dstr: return "string";
156906943f9SDavid du Colombier case Diface: return "interface";
157906943f9SDavid du Colombier case Dep: return "endpoint";
158906943f9SDavid du Colombier case Dreport: return "report";
159906943f9SDavid du Colombier case Dphysical: return "phys";
160906943f9SDavid du Colombier default: return "desc";
161906943f9SDavid du Colombier }
162906943f9SDavid du Colombier }
163906943f9SDavid du Colombier
164906943f9SDavid du Colombier int
parsedesc(Usbdev * d,Conf * c,uchar * b,int n)165906943f9SDavid du Colombier parsedesc(Usbdev *d, Conf *c, uchar *b, int n)
166906943f9SDavid du Colombier {
16716146bc9SDavid du Colombier int len, nd, tot;
168906943f9SDavid du Colombier Iface *ip;
169906943f9SDavid du Colombier Ep *ep;
170906943f9SDavid du Colombier Altc *altc;
171906943f9SDavid du Colombier char *hd;
172*cb26fd37SDavid du Colombier int ok;
173906943f9SDavid du Colombier
174906943f9SDavid du Colombier assert(d != nil && c != nil);
175906943f9SDavid du Colombier tot = 0;
176906943f9SDavid du Colombier ip = nil;
177906943f9SDavid du Colombier ep = nil;
178906943f9SDavid du Colombier altc = nil;
179906943f9SDavid du Colombier for(nd = 0; nd < nelem(d->ddesc); nd++)
180906943f9SDavid du Colombier if(d->ddesc[nd] == nil)
181906943f9SDavid du Colombier break;
182906943f9SDavid du Colombier
183*cb26fd37SDavid du Colombier ok = 1;
184906943f9SDavid du Colombier while(n > 2 && b[0] != 0 && b[0] <= n){
185906943f9SDavid du Colombier len = b[0];
186906943f9SDavid du Colombier if(usbdebug>1){
187906943f9SDavid du Colombier hd = hexstr(b, len);
188906943f9SDavid du Colombier fprint(2, "%s:\t\tparsedesc %s %x[%d] %s\n",
189906943f9SDavid du Colombier argv0, dname(b[1]), b[1], b[0], hd);
190906943f9SDavid du Colombier free(hd);
191906943f9SDavid du Colombier }
192906943f9SDavid du Colombier switch(b[1]){
193906943f9SDavid du Colombier case Ddev:
194906943f9SDavid du Colombier case Dconf:
195906943f9SDavid du Colombier werrstr("unexpected descriptor %d", b[1]);
196906943f9SDavid du Colombier ddprint(2, "%s\tparsedesc: %r", argv0);
197906943f9SDavid du Colombier break;
198906943f9SDavid du Colombier case Diface:
199*cb26fd37SDavid du Colombier if((ok = parseiface(d, c, b, n, &ip, &altc)) < 0){
200906943f9SDavid du Colombier ddprint(2, "%s\tparsedesc: %r\n", argv0);
201906943f9SDavid du Colombier return -1;
202906943f9SDavid du Colombier }
203906943f9SDavid du Colombier break;
204906943f9SDavid du Colombier case Dep:
205906943f9SDavid du Colombier if(ip == nil || altc == nil){
206906943f9SDavid du Colombier werrstr("unexpected endpoint descriptor");
207906943f9SDavid du Colombier break;
208906943f9SDavid du Colombier }
209*cb26fd37SDavid du Colombier if(!ok)
210*cb26fd37SDavid du Colombier break;
211906943f9SDavid du Colombier if(parseendpt(d, c, ip, altc, b, n, &ep) < 0){
212906943f9SDavid du Colombier ddprint(2, "%s\tparsedesc: %r\n", argv0);
213906943f9SDavid du Colombier return -1;
214906943f9SDavid du Colombier }
215906943f9SDavid du Colombier break;
216906943f9SDavid du Colombier default:
217906943f9SDavid du Colombier if(nd == nelem(d->ddesc)){
21816146bc9SDavid du Colombier fprint(2, "%s: parsedesc: too many "
21916146bc9SDavid du Colombier "device-specific descriptors for device"
22016146bc9SDavid du Colombier " %s %s\n",
22116146bc9SDavid du Colombier argv0, d->vendor, d->product);
222906943f9SDavid du Colombier break;
223906943f9SDavid du Colombier }
224906943f9SDavid du Colombier d->ddesc[nd] = emallocz(sizeof(Desc)+b[0], 0);
225906943f9SDavid du Colombier d->ddesc[nd]->iface = ip;
226906943f9SDavid du Colombier d->ddesc[nd]->ep = ep;
227906943f9SDavid du Colombier d->ddesc[nd]->altc = altc;
228906943f9SDavid du Colombier d->ddesc[nd]->conf = c;
229906943f9SDavid du Colombier memmove(&d->ddesc[nd]->data, b, len);
230906943f9SDavid du Colombier ++nd;
231906943f9SDavid du Colombier }
232906943f9SDavid du Colombier n -= len;
233906943f9SDavid du Colombier b += len;
234906943f9SDavid du Colombier tot += len;
235906943f9SDavid du Colombier }
236906943f9SDavid du Colombier return tot;
237906943f9SDavid du Colombier }
238906943f9SDavid du Colombier
239906943f9SDavid du Colombier int
parseconf(Usbdev * d,Conf * c,uchar * b,int n)240906943f9SDavid du Colombier parseconf(Usbdev *d, Conf *c, uchar *b, int n)
241906943f9SDavid du Colombier {
242906943f9SDavid du Colombier DConf* dc;
243906943f9SDavid du Colombier int l;
244906943f9SDavid du Colombier int nr;
245906943f9SDavid du Colombier char *hd;
246906943f9SDavid du Colombier
247906943f9SDavid du Colombier assert(d != nil && c != nil);
248906943f9SDavid du Colombier dc = (DConf*)b;
249906943f9SDavid du Colombier if(usbdebug>1){
250906943f9SDavid du Colombier hd = hexstr(b, Dconflen);
251906943f9SDavid du Colombier fprint(2, "%s:\tparseconf %s\n", argv0, hd);
252906943f9SDavid du Colombier free(hd);
253906943f9SDavid du Colombier }
254906943f9SDavid du Colombier if(dc->bLength < Dconflen){
255906943f9SDavid du Colombier werrstr("short configuration descriptor");
256906943f9SDavid du Colombier return -1;
257906943f9SDavid du Colombier }
258906943f9SDavid du Colombier if(dc->bDescriptorType != Dconf){
259906943f9SDavid du Colombier werrstr("not a configuration descriptor");
260906943f9SDavid du Colombier return -1;
261906943f9SDavid du Colombier }
262906943f9SDavid du Colombier c->cval = dc->bConfigurationValue;
263906943f9SDavid du Colombier c->attrib = dc->bmAttributes;
264906943f9SDavid du Colombier c->milliamps = dc->MaxPower*2;
265906943f9SDavid du Colombier l = GET2(dc->wTotalLength);
266906943f9SDavid du Colombier if(n < l){
267906943f9SDavid du Colombier werrstr("truncated configuration info");
268906943f9SDavid du Colombier return -1;
269906943f9SDavid du Colombier }
270906943f9SDavid du Colombier n -= Dconflen;
271906943f9SDavid du Colombier b += Dconflen;
272906943f9SDavid du Colombier nr = 0;
273906943f9SDavid du Colombier if(n > 0 && (nr=parsedesc(d, c, b, n)) < 0)
274906943f9SDavid du Colombier return -1;
275906943f9SDavid du Colombier n -= nr;
276906943f9SDavid du Colombier if(n > 0 && usbdebug>1)
277906943f9SDavid du Colombier fprint(2, "%s:\tparseconf: %d bytes left\n", argv0, n);
278906943f9SDavid du Colombier return l;
279906943f9SDavid du Colombier }
280