xref: /inferno-os/appl/lib/usb/usb.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1#
2# Copyright © 2002 Vita Nuova Holdings Limited
3#
4implement Usb;
5
6include "sys.m";
7	sys: Sys;
8
9include "usb.m";
10
11include "string.m";
12	str: String;
13
14Proto: adt {
15	proto: int;
16	name: string;
17};
18
19SubClass: adt {
20	subclass: int;
21	name: string;
22	proto: array of Proto;
23};
24
25Class: adt {
26	class: int;
27	name: string;
28	subclass: array of SubClass;
29};
30
31classes := array [] of {
32	Class(Usb->CL_AUDIO, "audio",
33		array [] of {
34			SubClass(1, "control", nil),
35			SubClass(2, "stream", nil),
36			SubClass(3, "midi", nil),
37		}
38	),
39	Class(Usb->CL_COMMS, "comms",
40		array [] of {
41			SubClass(1, "abstract",
42				array [] of {
43					Proto(1, "AT"),
44				}
45			)
46		}
47	),
48	Class(Usb->CL_HID, "hid",
49		array [] of {
50			SubClass(1, "boot",
51				array [] of {
52					Proto(1, "kbd"),
53					Proto(2, "mouse"),
54				}
55			)
56		}
57	),
58	Class(Usb->CL_PRINTER, "printer",
59		array [] of {
60			SubClass(1, "printer",
61				array [] of {
62					Proto(1, "uni"),
63					Proto(2, "bi"),
64				}
65			)
66		}
67	),
68	Class(Usb->CL_HUB, "hub",
69		array [] of {
70			SubClass(1, "hub", nil),
71		}
72	),
73	Class(Usb->CL_DATA, "data", nil),
74	Class(Usb->CL_MASS, "mass",
75		array [] of {
76			SubClass(1, "rbc",
77				array [] of {
78					Proto(0, "cbi-cc"),
79					Proto(1, "cbi-nocc"),
80					Proto(16r50, "bulkonly"),
81				}
82			),
83			SubClass(2, "sff-8020i/mmc-2",
84				array [] of {
85					Proto(0, "cbi-cc"),
86					Proto(1, "cbi-nocc"),
87					Proto(16r50, "bulkonly"),
88				}
89			),
90			SubClass(3, "qic-157",
91				array [] of {
92					Proto(0, "cbi-cc"),
93					Proto(1, "cbi-nocc"),
94					Proto(16r50, "bulkonly"),
95				}
96			),
97			SubClass(4, "ufi",
98				array [] of {
99					Proto(0, "cbi-cc"),
100					Proto(1, "cbi-nocc"),
101					Proto(16r50, "bulkonly"),
102				}
103			),
104			SubClass(5, "sff-8070i",
105				array [] of {
106					Proto(0, "cbi-cc"),
107					Proto(1, "cbi-nocc"),
108					Proto(16r50, "bulkonly"),
109				}
110			),
111			SubClass(6, "scsi",
112				array [] of {
113					Proto(0, "cbi-cc"),
114					Proto(1, "cbi-nocc"),
115					Proto(16r50, "bulkonly"),
116				}
117			),
118		}
119	),
120};
121
122get2(b: array of byte): int
123{
124	return int b[0] | (int b[1] << 8);
125}
126
127put2(buf: array of byte, v: int)
128{
129	buf[0] = byte v;
130	buf[1] = byte (v >> 8);
131}
132
133get4(b: array of byte): int
134{
135	return int b[0] | (int b[1] << 8) | (int b[2] << 16) | (int b[3] << 24);
136}
137
138put4(buf: array of byte, v: int)
139{
140	buf[0] = byte v;
141	buf[1] = byte (v >> 8);
142	buf[2] = byte (v >> 16);
143	buf[3] = byte (v >> 24);
144}
145
146bigget2(b: array of byte): int
147{
148	return int b[1] | (int b[0] << 8);
149}
150
151bigput2(buf: array of byte, v: int)
152{
153	buf[1] = byte v;
154	buf[0] = byte (v >> 8);
155}
156
157bigget4(b: array of byte): int
158{
159	return int b[3] | (int b[2] << 8) | (int b[1] << 16) | (int b[0] << 24);
160}
161
162bigput4(buf: array of byte, v: int)
163{
164	buf[3] = byte v;
165	buf[2] = byte (v >> 8);
166	buf[1] = byte (v >> 16);
167	buf[0] = byte (v >> 24);
168}
169
170strtol(s: string, base: int): (int, string)
171{
172	if (str == nil)
173		str = load String String->PATH;
174	if (base != 0)
175		return str->toint(s, base);
176	if (len s >= 2 && (s[0:2] == "0X" || s[0:2] == "0x"))
177		return str->toint(s[2:], 16);
178	if (len s > 0 && s[0:1] == "0")
179		return str->toint(s[1:], 8);
180	return str->toint(s, 10);
181}
182
183memset(buf: array of byte, v: int)
184{
185	for (x := 0; x < len buf; x++)
186		buf[x] = byte v;
187}
188
189setupreq(setupfd: ref Sys->FD, typ, req, value, index: int, outbuf: array of byte, count: int): int
190{
191	additional: int;
192	if (outbuf != nil) {
193		additional = len outbuf;
194		# if there is an outbuf, then the count sent must be length of the payload
195		# this assumes that RH2D is set
196		count = additional;
197	}
198	else
199		additional = 0;
200	buf := array[8 + additional] of byte;
201	buf[0] = byte typ;
202	buf[1] = byte req;
203	put2(buf[2:], value);
204	put2(buf[4:], index);
205	put2(buf[6:], count);
206	if (additional)
207		buf[8:] = outbuf;
208	rv := sys->write(setupfd, buf, len buf);
209	if (rv < 0)
210		return -1;
211	if (rv != len buf)
212		return -1;
213	return rv;
214}
215
216setupreply(setupfd: ref Sys->FD, buf: array of byte): int
217{
218	nb := sys->read(setupfd, buf, len buf);
219	return nb;
220}
221
222setup(setupfd: ref Sys->FD, typ, req, value, index: int, outbuf: array of byte, inbuf: array of byte): int
223{
224	count: int;
225	if (inbuf != nil)
226		count = len inbuf;
227	else
228		count = 0;
229	if (setupreq(setupfd, typ, req, value, index, outbuf, count) < 0)
230		return -1;
231	if (count == 0)
232		return 0;
233	return setupreply(setupfd, inbuf);
234}
235
236get_descriptor(fd: ref Sys->FD, rtyp: int, dtyp: int, dindex: int, langid: int, buf: array of byte): int
237{
238	nr := -1;
239	if (setupreq(fd, RD2H | rtyp | Rdevice, GET_DESCRIPTOR, (dtyp << 8) | dindex, langid, nil, len buf) < 0
240		|| (nr = setupreply(fd, buf)) < 1)
241		return -1;
242	return nr;
243}
244
245get_standard_descriptor(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int
246{
247	return get_descriptor(fd, Rstandard, dtyp, index, 0, buf);
248}
249
250get_class_descriptor(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int
251{
252	return get_descriptor(fd, Rclass, dtyp, index, 0, buf);
253}
254
255get_vendor_descriptor(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int
256{
257	return get_descriptor(fd, Rvendor, dtyp, index, 0, buf);
258}
259
260get_status(fd: ref Sys->FD, port: int): int
261{
262	buf := array [4] of byte;
263	if (setupreq(fd, RD2H | Rclass | Rother, GET_STATUS, 0, port, nil, len buf) < 0
264	 	|| setupreply(fd, buf) < len buf)
265		return -1;
266	return get2(buf);
267}
268
269set_address(fd: ref Sys->FD, address: int): int
270{
271	return setupreq(fd, RH2D | Rstandard | Rdevice, SET_ADDRESS, address, 0, nil, 0);
272}
273
274set_configuration(fd: ref Sys->FD, n: int): int
275{
276	return setupreq(fd, RH2D | Rstandard | Rdevice, SET_CONFIGURATION, n, 0, nil, 0);
277}
278
279setclear_feature(fd: ref Sys->FD, rtyp: int, value: int, index: int, on: int): int
280{
281	req: int;
282	if (on)
283		req = SET_FEATURE;
284	else
285		req = CLEAR_FEATURE;
286	return setupreq(fd, RH2D | rtyp, req, value, index, nil, 0);
287}
288
289parse_conf(b: array of byte): ref Configuration
290{
291	if (len b < DCONFLEN)
292		return nil;
293	conf := ref Configuration;
294	conf.id = int b[5];
295	conf.iface = array[int b[4]] of Interface;
296	conf.attr = int b[7];
297	conf.powerma = int b[8] * 2;
298	return conf;
299}
300
301parse_iface(conf: ref Configuration, b: array of byte): ref AltInterface
302{
303	if (len b < DINTERLEN || conf == nil)
304		return nil;
305	id := int b[2];
306	if (id >= len conf.iface)
307		return nil;
308	ai := ref AltInterface;
309	ai.id = int b[3];
310	if (int b[4] != 0)
311		ai.ep = array [int b[4]] of ref Endpt;
312	ai.class = int b[5];
313	ai.subclass = int b[6];
314	ai.proto = int b[7];
315	conf.iface[id].altiface = ai :: conf.iface[id].altiface;
316	return ai;
317}
318
319parse_endpt(conf: ref Configuration, ai: ref AltInterface, b: array of byte): ref Endpt
320{
321	if (len b < DENDPLEN || conf == nil || ai == nil || ai.ep == nil)
322		return nil;
323	for (i := 0; i < len ai.ep; i++)
324		if (ai.ep[i] == nil)
325			break;
326	if (i >= len ai.ep)
327		return nil;
328	ep := ref Endpt;
329	ai.ep[i] = ep;
330	ep.addr = int b[2];
331	ep.attr = int b[3];
332	ep.d2h = ep.addr & 16r80;
333	ep.etype = int b[3] & 3;
334	ep.isotype = (int b[3] >> 2) & 3;
335	ep.maxpkt = get2(b[4:]);
336	ep.interval = int b[6];
337	return ep;
338}
339
340get_parsed_configuration_descriptor(fd: ref Sys->FD, n: int): ref Configuration
341{
342	conf: ref Configuration;
343	altiface: ref AltInterface;
344
345	b := array [256] of byte;
346	nr := get_standard_descriptor(fd, CONFIGURATION, n, b);
347	if (nr < 0)
348		return nil;
349	conf = nil;
350	altiface = nil;
351	for (i := 0; nr - i > 2 && b[i] > byte 0 && int b[i] <= nr - i; i += int b[i]) {
352		ni := i + int b[i];
353		case int b[i + 1] {
354		Usb->CONFIGURATION =>
355			conf = parse_conf(b[i: ni]);
356			if (conf == nil)
357				return nil;
358		Usb->INTERFACE =>
359			altiface = parse_iface(conf, b[i: ni]);
360			if (altiface == nil)
361				return nil;
362		Usb->ENDPOINT =>
363			if (parse_endpt(conf, altiface, b[i: ni]) == nil)
364				return nil;
365		}
366	}
367	if (i < nr)
368		sys->print("usb: residue at end of descriptors\n");
369	return conf;
370}
371
372get_parsed_device_descriptor(fd: ref Sys->FD): ref Device
373{
374	b := array [256] of byte;
375	nr := get_standard_descriptor(fd, DEVICE, 0, b);
376	if (nr < DDEVLEN) {
377		if (nr == 8 || nr == 16) {
378			memset(b[nr: DDEVLEN - 1], 0);
379			b[DDEVLEN - 1] = byte 1;
380			nr = DDEVLEN;
381		}
382		else
383			return nil;
384	}
385	dev := ref Device;
386	dev.usbmajor = int b[3];
387	dev.usbminor = int b[2];
388	dev.class = int b[4];
389	dev.subclass = int b[5];
390	dev.proto = int b[6];
391	dev.maxpkt0 = int b[7];
392	dev.vid = get2(b[8:]);
393	dev.did = get2(b[10:]);
394	dev.relmajor = int b[13];
395	dev.relminor = int b[12];
396	dev.nconf = int b[17];
397	return dev;
398}
399
400dump_configuration(fd: ref Sys->FD, conf: ref Configuration)
401{
402	sys->fprint(fd, "configuration %d attr 0x%.x powerma %d\n", conf.id, conf.attr, conf.powerma);
403	for (i := 0; i < len conf.iface; i++) {
404		sys->fprint(fd, "\tinterface %d\n", i);
405		ail := conf.iface[i].altiface;
406		while (ail != nil) {
407			ai := hd ail;
408			sys->fprint(fd, "\t\t%d class %d subclass %d proto %d [%s]\n",
409				ai.id, ai.class, ai.subclass, ai.proto,
410				sclass(ai.class, ai.subclass, ai.proto));
411			for (e := 0; e < len ai.ep; e++) {
412				if (ai.ep[e] == nil) {
413					sys->fprint(fd, "\t\t\t missing descriptor\n");
414					continue;
415				}
416				sys->fprint(fd, "\t\t\t0x%.2ux attr 0x%.x maxpkt %d interval %d\n",
417					ai.ep[e].addr, ai.ep[e].attr, ai.ep[e].maxpkt, ai.ep[e].interval);
418			}
419			ail = tl ail;
420		}
421	}
422sys->fprint(fd, "done dumping\n");
423}
424
425sclass(class, subclass, proto: int): string
426{
427	for (c := 0; c < len classes; c++)
428		if (classes[c].class == class)
429			break;
430	if (c >= len classes)
431		return sys->sprint("%d.%d.%d", class, subclass, proto);
432	if (classes[c].subclass == nil)
433		return sys->sprint("%s.%d.%d", classes[c].name, subclass, proto);
434	for (sc := 0; sc < len classes[c].subclass; sc++)
435		if (classes[c].subclass[sc].subclass == subclass)
436			break;
437	if (sc >= len classes[c].subclass)
438		return sys->sprint("%s.%d.%d", classes[c].name, subclass, proto);
439	if (classes[c].subclass[sc].proto == nil)
440		return sys->sprint("%s.%s.%d", classes[c].name, classes[c].subclass[sc].name, proto);
441	for (p := 0; p < len classes[c].subclass[sc].proto; p++)
442		if (classes[c].subclass[sc].proto[p].proto == proto)
443			break;
444	if (p >= len classes[c].subclass[sc].proto)
445		return sys->sprint("%s.%s.%d", classes[c].name, classes[c].subclass[sc].name, proto);
446	return sys->sprint("%s.%s.%s", classes[c].name, classes[c].subclass[sc].name,
447		classes[c].subclass[sc].proto[p].name);
448}
449
450init()
451{
452	sys = load Sys Sys->PATH;
453}
454