]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/nusb/usbd/hub.c
devshr: changed #σc to contain directories
[plan9front.git] / sys / src / cmd / nusb / usbd / hub.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "usb.h"
5 #include "dat.h"
6 #include "fns.h"
7
8 Hub *hubs;
9 QLock hublock;
10 static int nhubs;
11 static int mustdump;
12 static int pollms = Pollms;
13 static Lock masklck;
14
15 static char *dsname[] = { "disabled", "attached", "configed" };
16
17 int
18 getdevnb(uvlong *maskp)
19 {
20         int i;
21
22         lock(&masklck);
23         for(i = 0; i < 8 * sizeof *maskp; i++)
24                 if((*maskp & (1ULL<<i)) == 0){
25                         *maskp |= 1ULL<<i;
26                         unlock(&masklck);
27                         return i;
28                 }
29         unlock(&masklck);
30         return -1;
31 }
32
33 void
34 putdevnb(uvlong *maskp, int id)
35 {
36         lock(&masklck);
37         if(id >= 0)
38                 *maskp &= ~(1ULL<<id);
39         unlock(&masklck);
40 }
41
42 static int
43 hubfeature(Hub *h, int port, int f, int on)
44 {
45         int cmd;
46
47         if(on)
48                 cmd = Rsetfeature;
49         else
50                 cmd = Rclearfeature;
51         return usbcmd(h->dev, Rh2d|Rclass|Rother, cmd, f, port, nil, 0);
52 }
53
54 /*
55  * This may be used to detect overcurrent on the hub
56  */
57 static void
58 checkhubstatus(Hub *h)
59 {
60         uchar buf[4];
61         int sts;
62
63         if(h->isroot)   /* not for root hubs */
64                 return;
65         if(usbcmd(h->dev, Rd2h|Rclass|Rdev, Rgetstatus, 0, 0, buf, 4) < 0){
66                 dprint(2, "%s: get hub status: %r\n", h->dev->dir);
67                 return;
68         }
69         sts = GET2(buf);
70         dprint(2, "hub %s: status %#ux\n", h->dev->dir, sts);
71 }
72
73 static int
74 confighub(Hub *h)
75 {
76         int type;
77         uchar buf[128]; /* room for extra descriptors */
78         int i;
79         Usbdev *d;
80         DHub *dd;
81         Port *pp;
82         int nr;
83         int nmap;
84         uchar *PortPwrCtrlMask;
85         int offset;
86         int mask;
87
88         d = h->dev->usb;
89         for(i = 0; i < nelem(d->ddesc); i++)
90                 if(d->ddesc[i] == nil)
91                         break;
92                 else if(d->ddesc[i]->data.bDescriptorType == Dhub){
93                         dd = (DHub*)&d->ddesc[i]->data;
94                         nr = Dhublen;
95                         goto Config;
96                 }
97         type = Rd2h|Rclass|Rdev;
98         nr = usbcmd(h->dev, type, Rgetdesc, Dhub<<8|0, 0, buf, sizeof buf);
99         if(nr < Dhublen){
100                 dprint(2, "%s: %s: getdesc hub: %r\n", argv0, h->dev->dir);
101                 return -1;
102         }
103         dd = (DHub*)buf;
104 Config:
105         h->nport = dd->bNbrPorts;
106         nmap = 1 + h->nport/8;
107         if(nr < 7 + 2*nmap){
108                 fprint(2, "%s: %s: descr. too small\n", argv0, h->dev->dir);
109                 return -1;
110         }
111         h->port = emallocz((h->nport+1)*sizeof(Port), 1);
112         h->pwrms = dd->bPwrOn2PwrGood*2;
113         if(h->pwrms < Powerdelay)
114                 h->pwrms = Powerdelay;
115         h->maxcurrent = dd->bHubContrCurrent;
116         h->pwrmode = dd->wHubCharacteristics[0] & 3;
117         h->compound = (dd->wHubCharacteristics[0] & (1<<2))!=0;
118         h->leds = (dd->wHubCharacteristics[0] & (1<<7)) != 0;
119         PortPwrCtrlMask = dd->DeviceRemovable + nmap;
120         for(i = 1; i <= h->nport; i++){
121                 pp = &h->port[i];
122                 offset = i/8;
123                 mask = 1<<(i%8);
124                 pp->removable = (dd->DeviceRemovable[offset] & mask) != 0;
125                 pp->pwrctl = (PortPwrCtrlMask[offset] & mask) != 0;
126         }
127         return 0;
128 }
129
130 static void
131 configroothub(Hub *h)
132 {
133         Dev *d;
134         char buf[128];
135         char *p;
136         int nr;
137
138         d = h->dev;
139         h->nport = 2;
140         h->maxpkt = 8;
141         seek(d->cfd, 0, 0);
142         nr = read(d->cfd, buf, sizeof(buf)-1);
143         if(nr < 0)
144                 goto Done;
145         buf[nr] = 0;
146
147         p = strstr(buf, "ports ");
148         if(p == nil)
149                 fprint(2, "%s: %s: no port information\n", argv0, d->dir);
150         else
151                 h->nport = atoi(p+6);
152         p = strstr(buf, "maxpkt ");
153         if(p == nil)
154                 fprint(2, "%s: %s: no maxpkt information\n", argv0, d->dir);
155         else
156                 h->maxpkt = atoi(p+7);
157 Done:
158         h->port = emallocz((h->nport+1)*sizeof(Port), 1);
159         dprint(2, "%s: %s: ports %d maxpkt %d\n", argv0, d->dir, h->nport, h->maxpkt);
160 }
161
162 Hub*
163 newhub(char *fn, Dev *d)
164 {
165         Hub *h;
166         int i;
167         Usbdev *ud;
168
169         h = emallocz(sizeof(Hub), 1);
170         h->isroot = (d == nil);
171         if(h->isroot){
172                 h->dev = opendev(fn);
173                 if(h->dev == nil){
174                         fprint(2, "%s: opendev: %s: %r", argv0, fn);
175                         goto Fail;
176                 }
177                 if(opendevdata(h->dev, ORDWR) < 0){
178                         fprint(2, "%s: opendevdata: %s: %r\n", argv0, fn);
179                         goto Fail;
180                 }
181                 configroothub(h);       /* never fails */
182         }else{
183                 h->dev = d;
184                 if(confighub(h) < 0){
185                         fprint(2, "%s: %s: config: %r\n", argv0, fn);
186                         goto Fail;
187                 }
188         }
189         if(h->dev == nil){
190                 fprint(2, "%s: opendev: %s: %r\n", argv0, fn);
191                 goto Fail;
192         }
193         devctl(h->dev, "hub");
194         ud = h->dev->usb;
195         if(h->isroot)
196                 devctl(h->dev, "info roothub csp %#08ux ports %d",
197                         0x000009, h->nport);
198         else{
199                 devctl(h->dev, "info hub csp %#08ulx ports %d %q %q",
200                         ud->csp, h->nport, ud->vendor, ud->product);
201                 for(i = 1; i <= h->nport; i++)
202                         if(hubfeature(h, i, Fportpower, 1) < 0)
203                                 fprint(2, "%s: %s: power: %r\n", argv0, fn);
204                 sleep(h->pwrms);
205                 for(i = 1; i <= h->nport; i++)
206                         if(h->leds != 0)
207                                 hubfeature(h, i, Fportindicator, 1);
208         }
209         h->next = hubs;
210         hubs = h;
211         nhubs++;
212         dprint(2, "%s: hub %#p allocated:", argv0, h);
213         dprint(2, " ports %d pwrms %d max curr %d pwrm %d cmp %d leds %d\n",
214                 h->nport, h->pwrms, h->maxcurrent,
215                 h->pwrmode, h->compound, h->leds);
216         incref(h->dev);
217         return h;
218 Fail:
219         if(d != nil)
220                 devctl(d, "detach");
221         free(h->port);
222         free(h);
223         dprint(2, "%s: hub %#p failed to start:", argv0, h);
224         return nil;
225 }
226
227 static void portdetach(Hub *h, int p);
228
229 /*
230  * If during enumeration we get an I/O error the hub is gone or
231  * in pretty bad shape. Because of retries of failed usb commands
232  * (and the sleeps they include) it can take a while to detach all
233  * ports for the hub. This detaches all ports and makes the hub void.
234  * The parent hub will detect a detach (probably right now) and
235  * close it later.
236  */
237 static void
238 hubfail(Hub *h)
239 {
240         int i;
241
242         for(i = 1; i <= h->nport; i++)
243                 portdetach(h, i);
244         h->failed = 1;
245 }
246
247 static void
248 closehub(Hub *h)
249 {
250         Hub **hl;
251
252         dprint(2, "%s: closing hub %#p\n", argv0, h);
253         for(hl = &hubs; *hl != nil; hl = &(*hl)->next)
254                 if(*hl == h)
255                         break;
256         if(*hl == nil)
257                 sysfatal("closehub: no hub");
258         *hl = h->next;
259         nhubs--;
260         hubfail(h);             /* detach all ports */
261         free(h->port);
262         assert(h->dev != nil);
263         devctl(h->dev, "detach");
264         closedev(h->dev);
265         free(h);
266 }
267
268 static int
269 portstatus(Hub *h, int p)
270 {
271         Dev *d;
272         uchar buf[4];
273         int t;
274         int sts;
275         int dbg;
276
277         dbg = usbdebug;
278         if(dbg != 0 && dbg < 4)
279                 usbdebug = 1;   /* do not be too chatty */
280         d = h->dev;
281         t = Rd2h|Rclass|Rother;
282         if(usbcmd(d, t, Rgetstatus, 0, p, buf, sizeof(buf)) < 0)
283                 sts = -1;
284         else
285                 sts = GET2(buf);
286         usbdebug = dbg;
287         return sts;
288 }
289
290 static char*
291 stsstr(int sts)
292 {
293         static char s[80];
294         char *e;
295
296         e = s;
297         if(sts&PSsuspend)
298                 *e++ = 'z';
299         if(sts&PSreset)
300                 *e++ = 'r';
301         if(sts&PSslow)
302                 *e++ = 'l';
303         if(sts&PShigh)
304                 *e++ = 'h';
305         if(sts&PSchange)
306                 *e++ = 'c';
307         if(sts&PSenable)
308                 *e++ = 'e';
309         if(sts&PSstatuschg)
310                 *e++ = 's';
311         if(sts&PSpresent)
312                 *e++ = 'p';
313         if(e == s)
314                 *e++ = '-';
315         *e = 0;
316         return s;
317 }
318
319 static int
320 getmaxpkt(Dev *d, int islow)
321 {
322         uchar buf[64];  /* More room to try to get device-specific descriptors */
323         DDev *dd;
324
325         dd = (DDev*)buf;
326         if(islow)
327                 dd->bMaxPacketSize0 = 8;
328         else
329                 dd->bMaxPacketSize0 = 64;
330         if(usbcmd(d, Rd2h|Rstd|Rdev, Rgetdesc, Ddev<<8|0, 0, buf, sizeof(buf)) < 0)
331                 return -1;
332         return dd->bMaxPacketSize0;
333 }
334
335 /*
336  * BUG: does not consider max. power avail.
337  */
338 static Dev*
339 portattach(Hub *h, int p, int sts)
340 {
341         Dev *d;
342         Port *pp;
343         Dev *nd;
344         char fname[80];
345         char buf[40];
346         char *sp;
347         int mp;
348         int nr;
349
350         d = h->dev;
351         pp = &h->port[p];
352         nd = nil;
353         pp->state = Pattached;
354         dprint(2, "%s: %s: port %d attach sts %#ux\n", argv0, d->dir, p, sts);
355         sleep(Connectdelay);
356         if(hubfeature(h, p, Fportenable, 1) < 0)
357                 dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
358         sleep(Enabledelay);
359         if(hubfeature(h, p, Fportreset, 1) < 0){
360                 dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p);
361                 goto Fail;
362         }
363         sleep(Resetdelay);
364         sts = portstatus(h, p);
365         if(sts < 0)
366                 goto Fail;
367         if((sts & PSenable) == 0){
368                 dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
369                 hubfeature(h, p, Fportenable, 1);
370                 sts = portstatus(h, p);
371                 if((sts & PSenable) == 0)
372                         goto Fail;
373         }
374         sp = "full";
375         if(sts & PSslow)
376                 sp = "low";
377         if(sts & PShigh)
378                 sp = "high";
379         dprint(2, "%s: %s: port %d: attached status %#ux\n", argv0, d->dir, p, sts);
380
381         if(devctl(d, "newdev %s %d", sp, p) < 0){
382                 fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p);
383                 goto Fail;
384         }
385         seek(d->cfd, 0, 0);
386         nr = read(d->cfd, buf, sizeof(buf)-1);
387         if(nr == 0){
388                 fprint(2, "%s: %s: port %d: newdev: eof\n", argv0, d->dir, p);
389                 goto Fail;
390         }
391         if(nr < 0){
392                 fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p);
393                 goto Fail;
394         }
395         buf[nr] = 0;
396         snprint(fname, sizeof(fname), "/dev/usb/%s", buf);
397         nd = opendev(fname);
398         if(nd == nil){
399                 fprint(2, "%s: %s: port %d: opendev: %r\n", argv0, d->dir, p);
400                 goto Fail;
401         }
402         if(usbdebug > 2)
403                 devctl(nd, "debug 1");
404         if(opendevdata(nd, ORDWR) < 0){
405                 fprint(2, "%s: %s: opendevdata: %r\n", argv0, nd->dir);
406                 goto Fail;
407         }
408         if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetaddress, nd->id, 0, nil, 0) < 0){
409                 dprint(2, "%s: %s: port %d: setaddress: %r\n", argv0, d->dir, p);
410                 goto Fail;
411         }
412         if(devctl(nd, "address") < 0){
413                 dprint(2, "%s: %s: port %d: set address: %r\n", argv0, d->dir, p);
414                 goto Fail;
415         }
416
417         mp=getmaxpkt(nd, strcmp(sp, "low") == 0);
418         if(mp < 0){
419                 dprint(2, "%s: %s: port %d: getmaxpkt: %r\n", argv0, d->dir, p);
420                 goto Fail;
421         }else{
422                 dprint(2, "%s; %s: port %d: maxpkt %d\n", argv0, d->dir, p, mp);
423                 devctl(nd, "maxpkt %d", mp);
424         }
425         if((sts & PSslow) != 0 && strcmp(sp, "full") == 0)
426                 dprint(2, "%s: %s: port %d: %s is full speed when port is low\n",
427                         argv0, d->dir, p, nd->dir);
428         if(configdev(nd) < 0){
429                 dprint(2, "%s: %s: port %d: configdev: %r\n", argv0, d->dir, p);
430                 goto Fail;
431         }
432         /*
433          * We always set conf #1. BUG.
434          */
435         if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){
436                 dprint(2, "%s: %s: port %d: setconf: %r\n", argv0, d->dir, p);
437                 unstall(nd, nd, Eout);
438                 if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0)
439                         goto Fail;
440         }
441         dprint(2, "%s: %U", argv0, nd);
442         pp->state = Pconfiged;
443         dprint(2, "%s: %s: port %d: configed: %s\n",
444                         argv0, d->dir, p, nd->dir);
445         return pp->dev = nd;
446 Fail:
447         pp->state = Pdisabled;
448         pp->sts = 0;
449         if(pp->hub != nil)
450                 pp->hub = nil;  /* hub closed by enumhub */
451         hubfeature(h, p, Fportenable, 0);
452         if(nd != nil)
453                 devctl(nd, "detach");
454         closedev(nd);
455         return nil;
456 }
457
458 static void
459 portdetach(Hub *h, int p)
460 {
461         Dev *d;
462         Port *pp;
463         d = h->dev;
464         pp = &h->port[p];
465
466         /*
467          * Clear present, so that we detect an attach on reconnects.
468          */
469         pp->sts &= ~(PSpresent|PSenable);
470
471         if(pp->state == Pdisabled)
472                 return;
473         pp->state = Pdisabled;
474         dprint(2, "%s: %s: port %d: detached\n", argv0, d->dir, p);
475
476         if(pp->hub != nil){
477                 closehub(pp->hub);
478                 pp->hub = nil;
479         }
480         if(pp->devmaskp != nil)
481                 putdevnb(pp->devmaskp, pp->devnb);
482         pp->devmaskp = nil;
483         if(pp->dev != nil){
484                 devctl(pp->dev, "detach");
485                 closedev(pp->dev);
486                 pp->dev = nil;
487         }
488 }
489
490 /*
491  * The next two functions are included to
492  * perform a port reset asked for by someone (usually a driver).
493  * This must be done while no other device is in using the
494  * configuration address and with care to keep the old address.
495  * To keep drivers decoupled from usbd they write the reset request
496  * to the #u/usb/epN.0/ctl file and then exit.
497  * This is unfortunate because usbd must now poll twice as much.
498  *
499  * An alternative to this reset process would be for the driver to detach
500  * the device. The next function could see that, issue a port reset, and
501  * then restart the driver once to see if it's a temporary error.
502  *
503  * The real fix would be to use interrupt endpoints for non-root hubs
504  * (would probably make some hubs fail) and add an events file to
505  * the kernel to report events to usbd. This is a severe change not
506  * yet implemented.
507  */
508 static int
509 portresetwanted(Hub *h, int p)
510 {
511         char buf[5];
512         Port *pp;
513         Dev *nd;
514
515         pp = &h->port[p];
516         nd = pp->dev;
517         if(nd != nil && nd->cfd >= 0 && pread(nd->cfd, buf, 5, 0LL) == 5)
518                 return strncmp(buf, "reset", 5) == 0;
519         else
520                 return 0;
521 }
522
523 static void
524 portreset(Hub *h, int p)
525 {
526         int sts;
527         Dev *d, *nd;
528         Port *pp;
529
530         d = h->dev;
531         pp = &h->port[p];
532         nd = pp->dev;
533         dprint(2, "%s: %s: port %d: resetting\n", argv0, d->dir, p);
534         if(hubfeature(h, p, Fportreset, 1) < 0){
535                 dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p);
536                 goto Fail;
537         }
538         sleep(Resetdelay);
539         sts = portstatus(h, p);
540         if(sts < 0)
541                 goto Fail;
542         if((sts & PSenable) == 0){
543                 dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
544                 hubfeature(h, p, Fportenable, 1);
545                 sts = portstatus(h, p);
546                 if((sts & PSenable) == 0)
547                         goto Fail;
548         }
549         nd = pp->dev;
550         opendevdata(nd, ORDWR);
551         if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetaddress, nd->id, 0, nil, 0) < 0){
552                 dprint(2, "%s: %s: port %d: setaddress: %r\n", argv0, d->dir, p);
553                 goto Fail;
554         }
555         if(devctl(nd, "address") < 0){
556                 dprint(2, "%s: %s: port %d: set address: %r\n", argv0, d->dir, p);
557                 goto Fail;
558         }
559         if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){
560                 dprint(2, "%s: %s: port %d: setconf: %r\n", argv0, d->dir, p);
561                 unstall(nd, nd, Eout);
562                 if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0)
563                         goto Fail;
564         }
565         if(nd->dfd >= 0)
566                 close(nd->dfd);
567         return;
568 Fail:
569         pp->state = Pdisabled;
570         pp->sts = 0;
571         if(pp->hub != nil)
572                 pp->hub = nil;  /* hub closed by enumhub */
573         hubfeature(h, p, Fportenable, 0);
574         if(nd != nil)
575                 devctl(nd, "detach");
576         closedev(nd);
577 }
578
579 static int
580 portgone(Port *pp, int sts)
581 {
582         if(sts < 0)
583                 return 1;
584         /*
585          * If it was enabled and it's not now then it may be reconnect.
586          * We pretend it's gone and later we'll see it as attached.
587          */
588         if((pp->sts & PSenable) != 0 && (sts & PSenable) == 0)
589                 return 1;
590         return (pp->sts & PSpresent) != 0 && (sts & PSpresent) == 0;
591 }
592
593 static int
594 enumhub(Hub *h, int p)
595 {
596         int sts;
597         Dev *d;
598         Port *pp;
599         int onhubs;
600
601         if(h->failed)
602                 return 0;
603         d = h->dev;
604         if(usbdebug > 3)
605                 fprint(2, "%s: %s: port %d enumhub\n", argv0, d->dir, p);
606
607         sts = portstatus(h, p);
608         if(sts < 0){
609                 hubfail(h);             /* avoid delays on detachment */
610                 return -1;
611         }
612         pp = &h->port[p];
613         onhubs = nhubs;
614         if((sts & PSsuspend) != 0){
615                 if(hubfeature(h, p, Fportenable, 1) < 0)
616                         dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
617                 sleep(Enabledelay);
618                 sts = portstatus(h, p);
619                 fprint(2, "%s: %s: port %d: resumed (sts %#ux)\n", argv0, d->dir, p, sts);
620         }
621         if((pp->sts & PSpresent) == 0 && (sts & PSpresent) != 0){
622                 if(portattach(h, p, sts) != nil)
623                         if(startdev(pp) < 0)
624                                 portdetach(h, p);
625         }else if(portgone(pp, sts)){
626                 removedev(pp);
627                 portdetach(h, p);
628         }else if(portresetwanted(h, p))
629                 portreset(h, p);
630         else if(pp->sts != sts){
631                 dprint(2, "%s: %s port %d: sts %s %#x ->",
632                         argv0, d->dir, p, stsstr(pp->sts), pp->sts);
633                 dprint(2, " %s %#x\n",stsstr(sts), sts);
634         }
635         pp->sts = sts;
636         if(onhubs != nhubs)
637                 return -1;
638         return 0;
639 }
640
641 static void
642 dump(void)
643 {
644         Hub *h;
645         int i;
646
647         mustdump = 0;
648         for(h = hubs; h != nil; h = h->next)
649                 for(i = 1; i <= h->nport; i++)
650                         fprint(2, "%s: hub %#p %s port %d: %U",
651                                 argv0, h, h->dev->dir, i, h->port[i].dev);
652
653 }
654
655 void
656 work(void)
657 {
658         char *fn;
659         Hub *h;
660         int i;
661
662         hubs = nil;
663         while((fn = rendezvous(work, nil)) != nil){
664                 dprint(2, "%s: %s starting\n", argv0, fn);
665                 h = newhub(fn, nil);
666                 if(h == nil)
667                         fprint(2, "%s: %s: newhub failed: %r\n", argv0, fn);
668                 free(fn);
669         }
670         /*
671          * Enumerate (and acknowledge after first enumeration).
672          * Do NOT perform enumeration concurrently for the same
673          * controller. new devices attached respond to a default
674          * address (0) after reset, thus enumeration has to work
675          * one device at a time at least before addresses have been
676          * assigned.
677          * Do not use hub interrupt endpoint because we
678          * have to poll the root hub(s) in any case.
679          */
680         for(;;){
681                 qlock(&hublock);
682 Again:
683                 for(h = hubs; h != nil; h = h->next)
684                         for(i = 1; i <= h->nport; i++)
685                                 if(enumhub(h, i) < 0){
686                                         /* changes in hub list; repeat */
687                                         goto Again;
688                                 }
689                 qunlock(&hublock);
690                 sleep(pollms);
691                 if(mustdump)
692                         dump();
693         }
694 }