xref: /plan9-contrib/sys/src/cmd/usb/lib/parse.c (revision cb26fd3769548fb89b97d7dd28f2dfd3cdc5c7fb)
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <bio.h>
5 #include "usb.h"
6 
7 int
parsedev(Dev * xd,uchar * b,int n)8 parsedev(Dev *xd, uchar *b, int n)
9 {
10 	Usbdev *d;
11 	DDev *dd;
12 	char *hd;
13 
14 	d = xd->usb;
15 	assert(d != nil);
16 	dd = (DDev*)b;
17 	if(usbdebug>1){
18 		hd = hexstr(b, Ddevlen);
19 		fprint(2, "%s: parsedev %s: %s\n", argv0, xd->dir, hd);
20 		free(hd);
21 	}
22 	if(dd->bLength < Ddevlen){
23 		werrstr("short dev descr. (%d < %d)", dd->bLength, Ddevlen);
24 		return -1;
25 	}
26 	if(dd->bDescriptorType != Ddev){
27 		werrstr("%d is not a dev descriptor", dd->bDescriptorType);
28 		return -1;
29 	}
30 	d->csp = CSP(dd->bDevClass, dd->bDevSubClass, dd->bDevProtocol);
31 	d->ep[0]->maxpkt = xd->maxpkt = dd->bMaxPacketSize0;
32 	d->class = dd->bDevClass;
33 	d->nconf = dd->bNumConfigurations;
34 	if(d->nconf == 0)
35 		dprint(2, "%s: %s: no configurations\n", argv0, xd->dir);
36 	d->vid = GET2(dd->idVendor);
37 	d->did = GET2(dd->idProduct);
38 	d->dno = GET2(dd->bcdDev);
39 	d->vsid = dd->iManufacturer;
40 	d->psid = dd->iProduct;
41 	d->ssid = dd->iSerialNumber;
42 	if(n > Ddevlen && usbdebug>1)
43 		fprint(2, "%s: %s: parsedev: %d bytes left",
44 			argv0, xd->dir, n - Ddevlen);
45 	return Ddevlen;
46 }
47 
48 static int
parseiface(Usbdev * d,Conf * c,uchar * b,int n,Iface ** ipp,Altc ** app)49 parseiface(Usbdev *d, Conf *c, uchar *b, int n, Iface **ipp, Altc **app)
50 {
51 	int class, subclass, proto;
52 	int ifid, altid;
53 	DIface *dip;
54 	Iface *ip;
55 
56 	assert(d != nil && c != nil);
57 	if(n < Difacelen){
58 		werrstr("short interface descriptor");
59 		return -1;
60 	}
61 	dip = (DIface *)b;
62 	ifid = dip->bInterfaceNumber;
63 	if(ifid < 0 || ifid >= nelem(c->iface)){
64 		werrstr("bad interface number %d", ifid);
65 		return -1;
66 	}
67 	if(c->iface[ifid] == nil)
68 		c->iface[ifid] = emallocz(sizeof(Iface), 1);
69 	else{
70 		/* hack to avoid unsupported uasp disk interface */
71 		if(dip->bInterfaceClass == Clstorage && dip->bInterfaceProtocol != 0x50)
72 			return 0;
73 	}
74 	ip = c->iface[ifid];
75 	class = dip->bInterfaceClass;
76 	subclass = dip->bInterfaceSubClass;
77 	proto = dip->bInterfaceProtocol;
78 	ip->csp = CSP(class, subclass, proto);
79 	if(d->csp == 0)				/* use csp from 1st iface */
80 		d->csp = ip->csp;		/* if device has none */
81 	if(d->class == 0)
82 		d->class = class;
83 	ip->id = ifid;
84 	if(c == d->conf[0] && ifid == 0)	/* ep0 was already there */
85 		d->ep[0]->iface = ip;
86 	altid = dip->bAlternateSetting;
87 	if(altid < 0 || altid >= nelem(ip->altc)){
88 		werrstr("bad alternate conf. number %d", altid);
89 		return -1;
90 	}
91 	if(ip->altc[altid] == nil)
92 		ip->altc[altid] = emallocz(sizeof(Altc), 1);
93 	*ipp = ip;
94 	*app = ip->altc[altid];
95 	return Difacelen;
96 }
97 
98 extern Ep* mkep(Usbdev *, int);
99 
100 static int
parseendpt(Usbdev * d,Conf * c,Iface * ip,Altc * altc,uchar * b,int n,Ep ** epp)101 parseendpt(Usbdev *d, Conf *c, Iface *ip, Altc *altc, uchar *b, int n, Ep **epp)
102 {
103 	int i, dir, epid;
104 	Ep *ep;
105 	DEp *dep;
106 
107 	assert(d != nil && c != nil && ip != nil && altc != nil);
108 	if(n < Deplen){
109 		werrstr("short endpoint descriptor");
110 		return -1;
111 	}
112 	dep = (DEp *)b;
113 	altc->attrib = dep->bmAttributes;	/* here? */
114 	altc->interval = dep->bInterval;
115 
116 	epid = dep->bEndpointAddress & 0xF;
117 	assert(epid < nelem(d->ep));
118 	if(dep->bEndpointAddress & 0x80)
119 		dir = Ein;
120 	else
121 		dir = Eout;
122 	ep = d->ep[epid];
123 	if(ep == nil){
124 		ep = mkep(d, epid);
125 		ep->dir = dir;
126 	}else if((ep->addr & 0x80) != (dep->bEndpointAddress & 0x80))
127 		ep->dir = Eboth;
128 	ep->maxpkt = GET2(dep->wMaxPacketSize);
129 	ep->ntds = 1 + ((ep->maxpkt >> 11) & 3);
130 	ep->maxpkt &= 0x7FF;
131 	ep->addr = dep->bEndpointAddress;
132 	ep->type = dep->bmAttributes & 0x03;
133 	ep->isotype = (dep->bmAttributes>>2) & 0x03;
134 	ep->conf = c;
135 	ep->iface = ip;
136 	for(i = 0; i < nelem(ip->ep); i++)
137 		if(ip->ep[i] == nil)
138 			break;
139 	if(i == nelem(ip->ep)){
140 		werrstr("parseendpt: bug: too many end points on interface "
141 			"with csp %#lux", ip->csp);
142 		fprint(2, "%s: %r\n", argv0);
143 		return -1;
144 	}
145 	*epp = ip->ep[i] = ep;
146 	return Dep;
147 }
148 
149 static char*
dname(int dtype)150 dname(int dtype)
151 {
152 	switch(dtype){
153 	case Ddev:	return "device";
154 	case Dconf: 	return "config";
155 	case Dstr: 	return "string";
156 	case Diface:	return "interface";
157 	case Dep:	return "endpoint";
158 	case Dreport:	return "report";
159 	case Dphysical:	return "phys";
160 	default:	return "desc";
161 	}
162 }
163 
164 int
parsedesc(Usbdev * d,Conf * c,uchar * b,int n)165 parsedesc(Usbdev *d, Conf *c, uchar *b, int n)
166 {
167 	int	len, nd, tot;
168 	Iface	*ip;
169 	Ep 	*ep;
170 	Altc	*altc;
171 	char	*hd;
172 	int	ok;
173 
174 	assert(d != nil && c != nil);
175 	tot = 0;
176 	ip = nil;
177 	ep = nil;
178 	altc = nil;
179 	for(nd = 0; nd < nelem(d->ddesc); nd++)
180 		if(d->ddesc[nd] == nil)
181 			break;
182 
183 	ok = 1;
184 	while(n > 2 && b[0] != 0 && b[0] <= n){
185 		len = b[0];
186 		if(usbdebug>1){
187 			hd = hexstr(b, len);
188 			fprint(2, "%s:\t\tparsedesc %s %x[%d] %s\n",
189 				argv0, dname(b[1]), b[1], b[0], hd);
190 			free(hd);
191 		}
192 		switch(b[1]){
193 		case Ddev:
194 		case Dconf:
195 			werrstr("unexpected descriptor %d", b[1]);
196 			ddprint(2, "%s\tparsedesc: %r", argv0);
197 			break;
198 		case Diface:
199 			if((ok = parseiface(d, c, b, n, &ip, &altc)) < 0){
200 				ddprint(2, "%s\tparsedesc: %r\n", argv0);
201 				return -1;
202 			}
203 			break;
204 		case Dep:
205 			if(ip == nil || altc == nil){
206 				werrstr("unexpected endpoint descriptor");
207 				break;
208 			}
209 			if(!ok)
210 				break;
211 			if(parseendpt(d, c, ip, altc, b, n, &ep) < 0){
212 				ddprint(2, "%s\tparsedesc: %r\n", argv0);
213 				return -1;
214 			}
215 			break;
216 		default:
217 			if(nd == nelem(d->ddesc)){
218 				fprint(2, "%s: parsedesc: too many "
219 					"device-specific descriptors for device"
220 					" %s %s\n",
221 					argv0, d->vendor, d->product);
222 				break;
223 			}
224 			d->ddesc[nd] = emallocz(sizeof(Desc)+b[0], 0);
225 			d->ddesc[nd]->iface = ip;
226 			d->ddesc[nd]->ep = ep;
227 			d->ddesc[nd]->altc = altc;
228 			d->ddesc[nd]->conf = c;
229 			memmove(&d->ddesc[nd]->data, b, len);
230 			++nd;
231 		}
232 		n -= len;
233 		b += len;
234 		tot += len;
235 	}
236 	return tot;
237 }
238 
239 int
parseconf(Usbdev * d,Conf * c,uchar * b,int n)240 parseconf(Usbdev *d, Conf *c, uchar *b, int n)
241 {
242 	DConf* dc;
243 	int	l;
244 	int	nr;
245 	char	*hd;
246 
247 	assert(d != nil && c != nil);
248 	dc = (DConf*)b;
249 	if(usbdebug>1){
250 		hd = hexstr(b, Dconflen);
251 		fprint(2, "%s:\tparseconf  %s\n", argv0, hd);
252 		free(hd);
253 	}
254 	if(dc->bLength < Dconflen){
255 		werrstr("short configuration descriptor");
256 		return -1;
257 	}
258 	if(dc->bDescriptorType != Dconf){
259 		werrstr("not a configuration descriptor");
260 		return -1;
261 	}
262 	c->cval = dc->bConfigurationValue;
263 	c->attrib = dc->bmAttributes;
264 	c->milliamps = dc->MaxPower*2;
265 	l = GET2(dc->wTotalLength);
266 	if(n < l){
267 		werrstr("truncated configuration info");
268 		return -1;
269 	}
270 	n -= Dconflen;
271 	b += Dconflen;
272 	nr = 0;
273 	if(n > 0 && (nr=parsedesc(d, c, b, n)) < 0)
274 		return -1;
275 	n -= nr;
276 	if(n > 0 && usbdebug>1)
277 		fprint(2, "%s:\tparseconf: %d bytes left\n", argv0, n);
278 	return l;
279 }
280