xref: /plan9/sys/src/cmd/usb/usbd/usbd.c (revision a8482e058adfb9039bb8aaa40ea4270fc64ff0a4)
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "usb.h"
5 
6 #include "dat.h"
7 #include "fns.h"
8 
9 #define STACKSIZE 128*1024
10 
11 static int dontfork;
12 static int usbnum;
13 
14 Ref	busy;
15 
16 int debug;
17 
18 typedef struct Enum Enum;
19 struct Enum
20 {
21 	Hub *hub;
22 	int port;
23 };
24 
25 void (*dprinter[])(Device *, int, ulong, void *b, int n) = {
26 	[STRING] pstring,
27 	[DEVICE] pdevice,
28 	[0x29] phub,
29 };
30 
31 static void
32 usage(void)
33 {
34 	fprint(2, "usage: usbd [-DfV] [-d mask] [-u root-hub]\n");
35 	threadexitsall("usage");
36 }
37 
38 /*
39  * based on libthread's threadsetname, but drags in less library code.
40  * actually just sets the arguments displayed.
41  */
42 void
43 procsetname(char *fmt, ...)
44 {
45 	int fd;
46 	char *cmdname;
47 	char buf[32];
48 	va_list arg;
49 
50 	va_start(arg, fmt);
51 	cmdname = vsmprint(fmt, arg);
52 	va_end(arg);
53 	if (cmdname == nil)
54 		return;
55 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
56 	if((fd = open(buf, OWRITE)) >= 0){
57 		write(fd, cmdname, strlen(cmdname)+1);
58 		close(fd);
59 	}
60 	free(cmdname);
61 }
62 
63 void
64 work(void *a)
65 {
66 	int port;
67 	char name[100];
68 	Hub *hub;
69 	Enum *arg;
70 
71 	hub = a;
72 	snprint(name, sizeof name, "%H", hub);
73 	procsetname(name);
74 
75 	for(port = 1; port <= hub->nport; port++){
76 		if (debug)
77 			fprint(2, "enumerate port %H.%d\n", hub, port);
78 		arg = emallocz(sizeof(Enum), 1);
79 		arg->hub = hub;
80 		arg->port = port;
81 		incref(&busy);
82 		threadcreate(enumerate, arg, STACKSIZE);
83 	}
84 
85 	decref(&busy);
86 	for(;;) {
87 		yield();
88 		if (busy.ref == 0)
89 			sleep(2000);
90 	}
91 }
92 
93 void
94 realmain(void *)
95 {
96 	int i;
97 	Hub *h;
98 
99 	if (!dontfork) {
100 		/* don't hold window open */
101 		close(0);
102 		close(1);
103 		open("/dev/null", OREAD);
104 		open("/dev/null", OWRITE);
105 		if(!debug && !verbose) {
106 			close(2);
107 			open("/dev/null", OWRITE);
108 		}
109 	}
110 	if(usbnum < 0){
111 		/* always fork off usb[1—n] */
112 		for(i=1; (h = roothub(i)) != nil; i++) {
113 			incref(&busy);
114 			proccreate(work, h, STACKSIZE);
115 		}
116 		usbnum = 0;
117 	}
118 	/* usb0 might be handled in this proc */
119 	if((h = roothub(usbnum)) != nil){
120 		incref(&busy);
121 		if (dontfork)
122 			work(h);
123 		else
124 			proccreate(work, h, STACKSIZE);
125 	}
126 	if (debug)
127 		fprint(2, "done\n");
128 	while (busy.ref)
129 		sleep(100);
130 	threadexits(nil);
131 }
132 
133 void
134 threadmain(int argc, char **argv)
135 {
136 	usbnum = -1;
137 	ARGBEGIN{
138 	case 'd':
139 		debug = atoi(EARGF(usage()));
140 		break;
141 	case 'D':
142 		debugdebug++;
143 		break;
144 	case 'f':
145 		dontfork = 1;
146 		break;
147 	case 'u':
148 		usbnum = atoi(EARGF(usage()));
149 		break;
150 	case 'V':
151 		verbose = 1;
152 		break;
153 	}ARGEND
154 	if(argc)
155 		usage();
156 	if(access("/dev/usb0", 0) < 0 && bind("#U", "/dev", MBEFORE) < 0)
157 		sysfatal("%s: can't bind -b #U /dev: %r", argv0);
158 
159 	usbfmtinit();
160 	fmtinstall('H', Hfmt);
161 
162 	if (dontfork)
163 		realmain(nil);
164 	else
165 		procrfork(realmain, nil, STACKSIZE, RFNOTEG | RFFDG);
166 	threadexits(nil);
167 }
168 
169 void
170 enumerate(void *v)
171 {
172 	int i, port;
173 	Device *d;
174 	Enum *arg;
175 	Hub *h, *nh;
176 
177 	arg = v;
178 	h = arg->hub;
179 	port = arg->port;
180 	free(arg);
181 
182 	for(;;){
183 		if((portstatus(h, port) & (1<<PORT_CONNECTION)) == 0){
184 			decref(&busy);
185 			if(verbose)
186 				fprint(2, "usbd: %H: port %d empty\n", h, port);
187 			do{
188 				yield();
189 				if(debugdebug)
190 					fprint(2, "usbd: probing %H.%d\n",
191 						h, port);
192 				sleep(500);
193 			}while((portstatus(h, port) & (1<<PORT_CONNECTION)) == 0);
194 			incref(&busy);
195 		}
196 		if(verbose)
197 			fprint(2, "usbd: %H: port %d attached\n", h, port);
198 		d = configure(h, port);
199 		if(d == nil){
200 			if(verbose)
201 				fprint(2, "usbd: can't configure port %H.%d\n", h, port);
202 			decref(&busy);
203 			threadexits("configure");
204 		}
205 		if(d->class == Hubclass){
206 			if(debug)
207 				fprint(2, "usbd: %H.%d: hub %d attached\n",
208 					h, port, d->id);
209 			setconfig(d, 1);
210 			nh = newhub(h, d);
211 			if(nh == nil) {
212 				detach(h, port);
213 				decref(&busy);
214 				threadexits("describehub");
215 			}
216 			if(debug)
217 				fprint(2, "usbd: traversing hub %H\n", nh);
218 			/* TO DO: initialise status endpoint */
219 			for(i=1; i<=nh->nport; i++)
220 				portpower(nh, i, 1);
221 			sleep(nh->pwrms);
222 			for(i=1; i<=nh->nport; i++) {
223 				arg = emallocz(sizeof(Enum), 1);
224 				arg->hub = nh;
225 				arg->port = i;
226 				incref(&busy);
227 				threadcreate(enumerate, arg, STACKSIZE);
228 			}
229 		}else{
230 			if(debug)
231 				fprint(2,
232 					"usbd: %H.%d: %d: not hub, %s speed\n",
233 					h, port, d->id, d->ls?"low":"high");
234 			setconfig(d, 1);	/* TO DO */
235 //unconscionable kludge (testing camera)
236 if(d->class == 10) setup0(d, RH2D|Rinterface, SET_INTERFACE, 10, 0, 0);
237 		}
238 		decref(&busy);
239 		while(portstatus(h, port) & (1<<PORT_CONNECTION)) {
240 			if (debugdebug)
241 				fprint(2, "checking %H.%d\n", h, port);
242 			yield();
243 			if (d->state == Detached) {
244 				if (verbose)
245 					fprint(2,
246 					 "%H: port %d detached by parent hub\n",
247 						h, port);
248 				/* parent hub died */
249 				threadexits(nil);
250 			}
251 		}
252 		if(verbose)
253 			fprint(2, "%H: port %d detached\n", h, port);
254 		detach(h, port);
255 	}
256 }
257 
258 Device*
259 configure(Hub *h, int port)
260 {
261 	Port *p;
262 	Device *d;
263 	int i, s, maxpkt, ls;
264 
265 	portenable(h, port, 1);
266 	sleep(20);
267 	portreset(h, port);
268 	sleep(20);
269 	s = portstatus(h, port);
270 	if (debug)
271 		fprint(2, "%H.%d status %#ux\n", h, port, s);
272 	if ((s & (1<<PORT_CONNECTION)) == 0)
273 		return nil;
274 	if ((s & (1<<PORT_SUSPEND)) == 0) {
275 		if (debug)
276 			fprint(2, "enabling port %H.%d\n", h, port);
277 		portenable(h, port, 1);
278 		s = portstatus(h, port);
279 		if (debug)
280 			fprint(2, "%H.%d status now %#ux\n", h, port, s);
281 	}
282 
283 	ls = (s & (1<<PORT_LOW_SPEED)) != 0;
284 	devspeed(h->dev0, ls);
285 	maxpkt = getmaxpkt(h->dev0);
286 	if(debugdebug)
287 		fprint(2, "%H.%d maxpkt: %d\n", h, port, maxpkt);
288 	if(maxpkt < 0){
289 Error0:
290 		portenable(h, port, 0);
291 		return nil;
292 	}
293 	d = opendev(h->ctlrno, -1);
294 	d->ls = ls;
295 	d->state = Enabled;
296 	d->ep[0]->maxpkt = maxpkt;
297 	if(fprint(d->ctl, "maxpkt 0 %d", maxpkt) < 0){
298 Error1:
299 		closedev(d);
300 		goto Error0;
301 	}
302 	if(setaddress(h->dev0, d->id) < 0)
303 		goto Error1;
304 	d->state = Assigned;
305 	devspeed(d, ls);
306 	if(describedevice(d) < 0)
307 		goto Error1;
308 
309 	/* read configurations 0 to n */
310 	for(i=0; i<d->nconf; i++){
311 		if(d->config[i] == nil)
312 			d->config[i] = mallocz(sizeof(*d->config[i]),1);
313 		loadconfig(d, i);
314 	}
315 	for(i=0; i<16; i++)
316 		setdevclass(d, i);
317 	p = &h->port[port-1];
318 	p->d = d;
319 	return d;
320 }
321 
322 void
323 detach(Hub *h, int port)
324 {
325 	Port *p;
326 	Device *d;
327 
328 	p = &h->port[port-1];
329 	if(p->hub != nil) {
330 		freehub(p->hub);
331 		p->hub = nil;
332 	}
333 	d = p->d;
334 	d->state = Detached;	/* return i/o error on access */
335 	closedev(d);
336 }
337