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