xref: /plan9/sys/src/cmd/usb/lib/parse.c (revision 16146bc9f7f774945213b79ebee114e739fe0b99)
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
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);
38906943f9SDavid du Colombier 	d->vsid = dd->iManufacturer;
39906943f9SDavid du Colombier 	d->psid = dd->iProduct;
40906943f9SDavid du Colombier 	d->ssid = dd->iSerialNumber;
41906943f9SDavid du Colombier 	if(n > Ddevlen && usbdebug>1)
42906943f9SDavid du Colombier 		fprint(2, "%s: %s: parsedev: %d bytes left",
43906943f9SDavid du Colombier 			argv0, xd->dir, n - Ddevlen);
44906943f9SDavid du Colombier 	return Ddevlen;
45906943f9SDavid du Colombier }
46906943f9SDavid du Colombier 
47906943f9SDavid du Colombier static int
48906943f9SDavid du Colombier parseiface(Usbdev *d, Conf *c, uchar *b, int n, Iface **ipp, Altc **app)
49906943f9SDavid du Colombier {
50906943f9SDavid du Colombier 	int class, subclass, proto;
51906943f9SDavid du Colombier 	int ifid, altid;
52906943f9SDavid du Colombier 	DIface *dip;
53906943f9SDavid du Colombier 	Iface *ip;
54906943f9SDavid du Colombier 
55906943f9SDavid du Colombier 	assert(d != nil && c != nil);
56906943f9SDavid du Colombier 	if(n < Difacelen){
57906943f9SDavid du Colombier 		werrstr("short interface descriptor");
58906943f9SDavid du Colombier 		return -1;
59906943f9SDavid du Colombier 	}
60906943f9SDavid du Colombier 	dip = (DIface *)b;
61906943f9SDavid du Colombier 	ifid = dip->bInterfaceNumber;
62906943f9SDavid du Colombier 	if(ifid < 0 || ifid >= nelem(c->iface)){
63906943f9SDavid du Colombier 		werrstr("bad interface number %d", ifid);
64906943f9SDavid du Colombier 		return -1;
65906943f9SDavid du Colombier 	}
66906943f9SDavid du Colombier 	if(c->iface[ifid] == nil)
67906943f9SDavid du Colombier 		c->iface[ifid] = emallocz(sizeof(Iface), 1);
68906943f9SDavid du Colombier 	ip = c->iface[ifid];
69906943f9SDavid du Colombier 	class = dip->bInterfaceClass;
70906943f9SDavid du Colombier 	subclass = dip->bInterfaceSubClass;
71906943f9SDavid du Colombier 	proto = dip->bInterfaceProtocol;
72906943f9SDavid du Colombier 	ip->csp = CSP(class, subclass, proto);
73906943f9SDavid du Colombier 	if(d->csp == 0)				/* use csp from 1st iface */
74906943f9SDavid du Colombier 		d->csp = ip->csp;		/* if device has none */
75906943f9SDavid du Colombier 	if(d->class == 0)
76906943f9SDavid du Colombier 		d->class = class;
77906943f9SDavid du Colombier 	ip->id = ifid;
78906943f9SDavid du Colombier 	if(c == d->conf[0] && ifid == 0)	/* ep0 was already there */
79906943f9SDavid du Colombier 		d->ep[0]->iface = ip;
80906943f9SDavid du Colombier 	altid = dip->bAlternateSetting;
81906943f9SDavid du Colombier 	if(altid < 0 || altid >= nelem(ip->altc)){
82906943f9SDavid du Colombier 		werrstr("bad alternate conf. number %d", altid);
83906943f9SDavid du Colombier 		return -1;
84906943f9SDavid du Colombier 	}
85906943f9SDavid du Colombier 	if(ip->altc[altid] == nil)
86906943f9SDavid du Colombier 		ip->altc[altid] = emallocz(sizeof(Altc), 1);
87906943f9SDavid du Colombier 	*ipp = ip;
88906943f9SDavid du Colombier 	*app = ip->altc[altid];
89906943f9SDavid du Colombier 	return Difacelen;
90906943f9SDavid du Colombier }
91906943f9SDavid du Colombier 
92906943f9SDavid du Colombier extern Ep* mkep(Usbdev *, int);
93906943f9SDavid du Colombier 
94906943f9SDavid du Colombier static int
95906943f9SDavid du Colombier parseendpt(Usbdev *d, Conf *c, Iface *ip, Altc *altc, uchar *b, int n, Ep **epp)
96906943f9SDavid du Colombier {
97*16146bc9SDavid du Colombier 	int i, dir, epid;
98906943f9SDavid du Colombier 	Ep *ep;
99906943f9SDavid du Colombier 	DEp *dep;
100906943f9SDavid du Colombier 
101906943f9SDavid du Colombier 	assert(d != nil && c != nil && ip != nil && altc != nil);
102906943f9SDavid du Colombier 	if(n < Deplen){
103906943f9SDavid du Colombier 		werrstr("short endpoint descriptor");
104906943f9SDavid du Colombier 		return -1;
105906943f9SDavid du Colombier 	}
106906943f9SDavid du Colombier 	dep = (DEp *)b;
107906943f9SDavid du Colombier 	altc->attrib = dep->bmAttributes;	/* here? */
108906943f9SDavid du Colombier 	altc->interval = dep->bInterval;
109906943f9SDavid du Colombier 
110906943f9SDavid du Colombier 	epid = dep->bEndpointAddress & 0xF;
111906943f9SDavid du Colombier 	assert(epid < nelem(d->ep));
112906943f9SDavid du Colombier 	if(dep->bEndpointAddress & 0x80)
113906943f9SDavid du Colombier 		dir = Ein;
114906943f9SDavid du Colombier 	else
115906943f9SDavid du Colombier 		dir = Eout;
116906943f9SDavid du Colombier 	ep = d->ep[epid];
117906943f9SDavid du Colombier 	if(ep == nil){
118906943f9SDavid du Colombier 		ep = mkep(d, epid);
119906943f9SDavid du Colombier 		ep->dir = dir;
120906943f9SDavid du Colombier 	}else if((ep->addr & 0x80) != (dep->bEndpointAddress & 0x80))
121906943f9SDavid du Colombier 		ep->dir = Eboth;
122906943f9SDavid du Colombier 	ep->maxpkt = GET2(dep->wMaxPacketSize);
123906943f9SDavid du Colombier 	ep->ntds = 1 + ((ep->maxpkt >> 11) & 3);
124906943f9SDavid du Colombier 	ep->maxpkt &= 0x7FF;
125906943f9SDavid du Colombier 	ep->addr = dep->bEndpointAddress;
126906943f9SDavid du Colombier 	ep->type = dep->bmAttributes & 0x03;
127906943f9SDavid du Colombier 	ep->isotype = (dep->bmAttributes>>2) & 0x03;
128906943f9SDavid du Colombier 	ep->conf = c;
129906943f9SDavid du Colombier 	ep->iface = ip;
130906943f9SDavid du Colombier 	for(i = 0; i < nelem(ip->ep); i++)
131906943f9SDavid du Colombier 		if(ip->ep[i] == nil)
132906943f9SDavid du Colombier 			break;
133906943f9SDavid du Colombier 	if(i == nelem(ip->ep)){
134*16146bc9SDavid du Colombier 		werrstr("parseendpt: bug: too many end points on interface "
135*16146bc9SDavid du Colombier 			"with csp %#lux", ip->csp);
136906943f9SDavid du Colombier 		fprint(2, "%s: %r\n", argv0);
137906943f9SDavid du Colombier 		return -1;
138906943f9SDavid du Colombier 	}
139906943f9SDavid du Colombier 	*epp = ip->ep[i] = ep;
140906943f9SDavid du Colombier 	return Dep;
141906943f9SDavid du Colombier }
142906943f9SDavid du Colombier 
143906943f9SDavid du Colombier static char*
144906943f9SDavid du Colombier dname(int dtype)
145906943f9SDavid du Colombier {
146906943f9SDavid du Colombier 	switch(dtype){
147906943f9SDavid du Colombier 	case Ddev:	return "device";
148906943f9SDavid du Colombier 	case Dconf: 	return "config";
149906943f9SDavid du Colombier 	case Dstr: 	return "string";
150906943f9SDavid du Colombier 	case Diface:	return "interface";
151906943f9SDavid du Colombier 	case Dep:	return "endpoint";
152906943f9SDavid du Colombier 	case Dreport:	return "report";
153906943f9SDavid du Colombier 	case Dphysical:	return "phys";
154906943f9SDavid du Colombier 	default:	return "desc";
155906943f9SDavid du Colombier 	}
156906943f9SDavid du Colombier }
157906943f9SDavid du Colombier 
158906943f9SDavid du Colombier int
159906943f9SDavid du Colombier parsedesc(Usbdev *d, Conf *c, uchar *b, int n)
160906943f9SDavid du Colombier {
161*16146bc9SDavid du Colombier 	int	len, nd, tot;
162906943f9SDavid du Colombier 	Iface	*ip;
163906943f9SDavid du Colombier 	Ep 	*ep;
164906943f9SDavid du Colombier 	Altc	*altc;
165906943f9SDavid du Colombier 	char	*hd;
166906943f9SDavid du Colombier 
167906943f9SDavid du Colombier 	assert(d != nil && c != nil);
168906943f9SDavid du Colombier 	tot = 0;
169906943f9SDavid du Colombier 	ip = nil;
170906943f9SDavid du Colombier 	ep = nil;
171906943f9SDavid du Colombier 	altc = nil;
172906943f9SDavid du Colombier 	for(nd = 0; nd < nelem(d->ddesc); nd++)
173906943f9SDavid du Colombier 		if(d->ddesc[nd] == nil)
174906943f9SDavid du Colombier 			break;
175906943f9SDavid du Colombier 
176906943f9SDavid du Colombier 	while(n > 2 && b[0] != 0 && b[0] <= n){
177906943f9SDavid du Colombier 		len = b[0];
178906943f9SDavid du Colombier 		if(usbdebug>1){
179906943f9SDavid du Colombier 			hd = hexstr(b, len);
180906943f9SDavid du Colombier 			fprint(2, "%s:\t\tparsedesc %s %x[%d] %s\n",
181906943f9SDavid du Colombier 				argv0, dname(b[1]), b[1], b[0], hd);
182906943f9SDavid du Colombier 			free(hd);
183906943f9SDavid du Colombier 		}
184906943f9SDavid du Colombier 		switch(b[1]){
185906943f9SDavid du Colombier 		case Ddev:
186906943f9SDavid du Colombier 		case Dconf:
187906943f9SDavid du Colombier 			werrstr("unexpected descriptor %d", b[1]);
188906943f9SDavid du Colombier 			ddprint(2, "%s\tparsedesc: %r", argv0);
189906943f9SDavid du Colombier 			break;
190906943f9SDavid du Colombier 		case Diface:
191906943f9SDavid du Colombier 			if(parseiface(d, c, b, n, &ip, &altc) < 0){
192906943f9SDavid du Colombier 				ddprint(2, "%s\tparsedesc: %r\n", argv0);
193906943f9SDavid du Colombier 				return -1;
194906943f9SDavid du Colombier 			}
195906943f9SDavid du Colombier 			break;
196906943f9SDavid du Colombier 		case Dep:
197906943f9SDavid du Colombier 			if(ip == nil || altc == nil){
198906943f9SDavid du Colombier 				werrstr("unexpected endpoint descriptor");
199906943f9SDavid du Colombier 				break;
200906943f9SDavid du Colombier 			}
201906943f9SDavid du Colombier 			if(parseendpt(d, c, ip, altc, b, n, &ep) < 0){
202906943f9SDavid du Colombier 				ddprint(2, "%s\tparsedesc: %r\n", argv0);
203906943f9SDavid du Colombier 				return -1;
204906943f9SDavid du Colombier 			}
205906943f9SDavid du Colombier 			break;
206906943f9SDavid du Colombier 		default:
207906943f9SDavid du Colombier 			if(nd == nelem(d->ddesc)){
208*16146bc9SDavid du Colombier 				fprint(2, "%s: parsedesc: too many "
209*16146bc9SDavid du Colombier 					"device-specific descriptors for device"
210*16146bc9SDavid du Colombier 					" %s %s\n",
211*16146bc9SDavid du Colombier 					argv0, d->vendor, d->product);
212906943f9SDavid du Colombier 				break;
213906943f9SDavid du Colombier 			}
214906943f9SDavid du Colombier 			d->ddesc[nd] = emallocz(sizeof(Desc)+b[0], 0);
215906943f9SDavid du Colombier 			d->ddesc[nd]->iface = ip;
216906943f9SDavid du Colombier 			d->ddesc[nd]->ep = ep;
217906943f9SDavid du Colombier 			d->ddesc[nd]->altc = altc;
218906943f9SDavid du Colombier 			d->ddesc[nd]->conf = c;
219906943f9SDavid du Colombier 			memmove(&d->ddesc[nd]->data, b, len);
220906943f9SDavid du Colombier 			++nd;
221906943f9SDavid du Colombier 		}
222906943f9SDavid du Colombier 		n -= len;
223906943f9SDavid du Colombier 		b += len;
224906943f9SDavid du Colombier 		tot += len;
225906943f9SDavid du Colombier 	}
226906943f9SDavid du Colombier 	return tot;
227906943f9SDavid du Colombier }
228906943f9SDavid du Colombier 
229906943f9SDavid du Colombier int
230906943f9SDavid du Colombier parseconf(Usbdev *d, Conf *c, uchar *b, int n)
231906943f9SDavid du Colombier {
232906943f9SDavid du Colombier 	DConf* dc;
233906943f9SDavid du Colombier 	int	l;
234906943f9SDavid du Colombier 	int	nr;
235906943f9SDavid du Colombier 	char	*hd;
236906943f9SDavid du Colombier 
237906943f9SDavid du Colombier 	assert(d != nil && c != nil);
238906943f9SDavid du Colombier 	dc = (DConf*)b;
239906943f9SDavid du Colombier 	if(usbdebug>1){
240906943f9SDavid du Colombier 		hd = hexstr(b, Dconflen);
241906943f9SDavid du Colombier 		fprint(2, "%s:\tparseconf  %s\n", argv0, hd);
242906943f9SDavid du Colombier 		free(hd);
243906943f9SDavid du Colombier 	}
244906943f9SDavid du Colombier 	if(dc->bLength < Dconflen){
245906943f9SDavid du Colombier 		werrstr("short configuration descriptor");
246906943f9SDavid du Colombier 		return -1;
247906943f9SDavid du Colombier 	}
248906943f9SDavid du Colombier 	if(dc->bDescriptorType != Dconf){
249906943f9SDavid du Colombier 		werrstr("not a configuration descriptor");
250906943f9SDavid du Colombier 		return -1;
251906943f9SDavid du Colombier 	}
252906943f9SDavid du Colombier 	c->cval = dc->bConfigurationValue;
253906943f9SDavid du Colombier 	c->attrib = dc->bmAttributes;
254906943f9SDavid du Colombier 	c->milliamps = dc->MaxPower*2;
255906943f9SDavid du Colombier 	l = GET2(dc->wTotalLength);
256906943f9SDavid du Colombier 	if(n < l){
257906943f9SDavid du Colombier 		werrstr("truncated configuration info");
258906943f9SDavid du Colombier 		return -1;
259906943f9SDavid du Colombier 	}
260906943f9SDavid du Colombier 	n -= Dconflen;
261906943f9SDavid du Colombier 	b += Dconflen;
262906943f9SDavid du Colombier 	nr = 0;
263906943f9SDavid du Colombier 	if(n > 0 && (nr=parsedesc(d, c, b, n)) < 0)
264906943f9SDavid du Colombier 		return -1;
265906943f9SDavid du Colombier 	n -= nr;
266906943f9SDavid du Colombier 	if(n > 0 && usbdebug>1)
267906943f9SDavid du Colombier 		fprint(2, "%s:\tparseconf: %d bytes left\n", argv0, n);
268906943f9SDavid du Colombier 	return l;
269906943f9SDavid du Colombier }
270