xref: /plan9-contrib/sys/src/cmd/usb/usbd/usbd.c (revision 3f9c83932f326ae8b6d81b36429957bc06a9813e)
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 
13 Ref	busy;
14 
15 int debug;
16 
17 typedef struct Enum Enum;
18 struct Enum
19 {
20 	Hub *hub;
21 	int port;
22 };
23 
24 void (*dprinter[])(Device *, int, ulong, void *b, int n) = {
25 	[STRING] pstring,
26 	[DEVICE] pdevice,
27 	[0x29] phub,
28 };
29 
30 static void
31 usage(void)
32 {
33 	fprint(2, "usage: usbd\n");
34 	threadexitsall("usage");
35 }
36 
37 void
38 work(void *a)
39 {
40 	int port;
41 	Hub *hub;
42 	Enum *arg;
43 
44 	hub = a;
45 	for (port = 1; port <= hub->nport; port++) {
46 		if (debug)
47 			fprint(2, "enumerate port %H.%d\n", hub, port);
48 		arg = emallocz(sizeof(Enum), 1);
49 		arg->hub = hub;
50 		arg->port = port;
51 		incref(&busy);
52 		threadcreate(enumerate, arg, STACKSIZE);
53 	}
54 
55 	decref(&busy);
56 	for(;;) {
57 		yield();
58 		if (busy.ref == 0)
59 			sleep(2000);
60 	}
61 }
62 
63 void
64 threadmain(int argc, char **argv)
65 {
66 	int i;
67 	Hub *h;
68 
69 	ARGBEGIN{
70 	case 'f':
71 		dontfork=1;
72 		break;
73 	case 'd':
74 		debug++;
75 		break;
76 	case 'v':
77 		verbose = 1;
78 		break;
79 	}ARGEND
80 	if (argc)
81 		usage();
82 	if (access("/dev/usb0", 0) < 0 && bind("#U", "/dev", MAFTER) < 0)
83 		sysfatal("%s: can't bind #U after /dev: %r\n", argv0);
84 
85 	usbfmtinit();
86 	fmtinstall('H', Hfmt);
87 
88 	/* always fork off usb[1—n] */
89 	for(i=1; (h = roothub(i)) != nil; i++) {
90 		incref(&busy);
91 		proccreate(work, h, STACKSIZE);
92 	}
93 
94 	/* usb0 might be handled in this proc */
95 	if((h = roothub(0)) != nil){
96 		incref(&busy);
97 		if (dontfork) {
98 			work(h);
99 		} else {
100 			rfork(RFNOTEG);
101 			proccreate(work, h, STACKSIZE);
102 			/* don't hold window open */
103 			close(0);
104 			close(1);
105 			if(!debug && !verbose)
106 				close(2);
107 		}
108 	}
109 	if (debug)
110 		fprint(2, "done\n");
111 	while (busy.ref)
112 		sleep(100);
113 	threadexits(nil);
114 }
115 
116 void
117 enumerate(void *v)
118 {
119 	int i, port;
120 	Device *d;
121 	Enum *arg;
122 	Hub *h, *nh;
123 
124 	arg = v;
125 	h = arg->hub;
126 	port = arg->port;
127 	free(arg);
128 
129 	for (;;) {
130 		if((portstatus(h, port) & (1<<PORT_CONNECTION)) == 0) {
131 			decref(&busy);
132 			if (verbose)
133 				fprint(2, "usbd: %H: port %d empty\n", h, port);
134 			do {
135 				yield();
136 				if (debugdebug)
137 					fprint(2, "usbd: probing %H.%d\n",
138 						h, port);
139 				sleep(500);
140 			} while((portstatus(h, port) & (1<<PORT_CONNECTION)) == 0);
141 			incref(&busy);
142 		}
143 		if(verbose)
144 			fprint(2, "usbd: %H: port %d attached\n", h, port);
145 		d = configure(h, port);
146 		if(d == nil) {
147 			if(verbose)
148 				fprint(2, "usbd: can't configure port %H.%d\n", h, port);
149 			decref(&busy);
150 			threadexits("configure");
151 		}
152 		if(d->class == Hubclass) {
153 			if(debug)
154 				fprint(2, "usbd: %H.%d: hub %d attached\n",
155 					h, port, d->id);
156 			setconfig(d, 1);
157 			nh = newhub(h, d);
158 			if(nh == nil) {
159 				detach(h, port);
160 				decref(&busy);
161 				threadexits("describehub");
162 			}
163 			if(debug)
164 				fprint(2, "usbd: traversing hub %H\n", nh);
165 			/* TO DO: initialise status endpoint */
166 			for(i=1; i<=nh->nport; i++)
167 				portpower(nh, i, 1);
168 			sleep(nh->pwrms);
169 			for(i=1; i<=nh->nport; i++) {
170 				arg = emallocz(sizeof(Enum), 1);
171 				arg->hub = nh;
172 				arg->port = i;
173 				incref(&busy);
174 				threadcreate(enumerate, arg, STACKSIZE);
175 			}
176 		}else{
177 			if(debug)
178 				fprint(2,
179 					"usbd: %H.%d: %d: not hub, %s speed\n",
180 					h, port, d->id, d->ls?"low":"high");
181 			setconfig(d, 1);	/* TO DO */
182 //unconscionable kludge (testing camera)
183 if(d->class == 10) setup0(d, RH2D|Rinterface, SET_INTERFACE, 10, 0, 0);
184 		}
185 		decref(&busy);
186 		while(portstatus(h, port) & (1<<PORT_CONNECTION)) {
187 			if (debugdebug)
188 				fprint(2, "checking %H.%d\n", h, port);
189 			yield();
190 			if (d->state == Detached) {
191 				if (verbose)
192 					fprint(2,
193 					 "%H: port %d detached by parent hub\n",
194 						h, port);
195 				/* parent hub died */
196 				threadexits(nil);
197 			}
198 		}
199 		if(verbose)
200 			fprint(2, "%H: port %d detached\n", h, port);
201 		detach(h, port);
202 	}
203 }
204 
205 Device*
206 configure(Hub *h, int port)
207 {
208 	Port *p;
209 	Device *d;
210 	int i, s, maxpkt, ls;
211 
212 	portenable(h, port, 1);
213 	sleep(20);
214 	portreset(h, port);
215 	sleep(20);
216 	s = portstatus(h, port);
217 	if (debug)
218 		fprint(2, "%H.%d status %#ux\n", h, port, s);
219 	if ((s & (1<<PORT_CONNECTION)) == 0)
220 		return nil;
221 	if ((s & (1<<PORT_SUSPEND)) == 0) {
222 		if (debug)
223 			fprint(2, "enabling port %H.%d\n", h, port);
224 		portenable(h, port, 1);
225 		s = portstatus(h, port);
226 		if (debug)
227 			fprint(2, "%H.%d status now %#ux\n", h, port, s);
228 	}
229 
230 	ls = (s & (1<<PORT_LOW_SPEED)) != 0;
231 	devspeed(h->dev0, ls);
232 	maxpkt = getmaxpkt(h->dev0);
233 	if(maxpkt < 0) {
234 Error0:
235 		portenable(h, port, 0);
236 		return nil;
237 	}
238 	d = opendev(h->ctlrno, -1);
239 	d->ls = ls;
240 	d->state = Enabled;
241 	d->ep[0]->maxpkt = maxpkt;
242 	if(fprint(d->ctl, "maxpkt 0 %d", maxpkt) < 0) {
243 Error1:
244 		closedev(d);
245 		goto Error0;
246 	}
247 	if(setaddress(h->dev0, d->id) < 0)
248 		goto Error1;
249 	d->state = Assigned;
250 	devspeed(d, ls);
251 	if(describedevice(d) < 0)
252 		goto Error1;
253 
254 	/* read configurations 0 to n */
255 	for(i=0; i<d->nconf; i++) {
256 		if(d->config[i] == nil)
257 			d->config[i] = mallocz(sizeof(*d->config[i]),1);
258 		loadconfig(d, i);
259 	}
260 	for(i=0; i<16; i++)
261 		setdevclass(d, i);
262 	p = &h->port[port-1];
263 	p->d = d;
264 	return d;
265 }
266 
267 void
268 detach(Hub *h, int port)
269 {
270 	Port *p;
271 	Device *d;
272 
273 	p = &h->port[port-1];
274 	if(p->hub != nil) {
275 		freehub(p->hub);
276 		p->hub = nil;
277 	}
278 	d = p->d;
279 	d->state = Detached;	/* return i/o error on access */
280 	closedev(d);
281 }
282