]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/nusb/usbd/hub.c
merge
[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                 detachdev(pp);
485
486                 devctl(pp->dev, "detach");
487                 closedev(pp->dev);
488                 pp->dev = nil;
489         }
490 }
491
492 /*
493  * The next two functions are included to
494  * perform a port reset asked for by someone (usually a driver).
495  * This must be done while no other device is in using the
496  * configuration address and with care to keep the old address.
497  * To keep drivers decoupled from usbd they write the reset request
498  * to the #u/usb/epN.0/ctl file and then exit.
499  * This is unfortunate because usbd must now poll twice as much.
500  *
501  * An alternative to this reset process would be for the driver to detach
502  * the device. The next function could see that, issue a port reset, and
503  * then restart the driver once to see if it's a temporary error.
504  *
505  * The real fix would be to use interrupt endpoints for non-root hubs
506  * (would probably make some hubs fail) and add an events file to
507  * the kernel to report events to usbd. This is a severe change not
508  * yet implemented.
509  */
510 static int
511 portresetwanted(Hub *h, int p)
512 {
513         char buf[5];
514         Port *pp;
515         Dev *nd;
516
517         pp = &h->port[p];
518         nd = pp->dev;
519         if(nd != nil && nd->cfd >= 0 && pread(nd->cfd, buf, 5, 0LL) == 5)
520                 return strncmp(buf, "reset", 5) == 0;
521         else
522                 return 0;
523 }
524
525 static void
526 portreset(Hub *h, int p)
527 {
528         int sts;
529         Dev *d, *nd;
530         Port *pp;
531
532         d = h->dev;
533         pp = &h->port[p];
534         nd = pp->dev;
535         dprint(2, "%s: %s: port %d: resetting\n", argv0, d->dir, p);
536         if(hubfeature(h, p, Fportreset, 1) < 0){
537                 dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p);
538                 goto Fail;
539         }
540         sleep(Resetdelay);
541         sts = portstatus(h, p);
542         if(sts < 0)
543                 goto Fail;
544         if((sts & PSenable) == 0){
545                 dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
546                 hubfeature(h, p, Fportenable, 1);
547                 sts = portstatus(h, p);
548                 if((sts & PSenable) == 0)
549                         goto Fail;
550         }
551         nd = pp->dev;
552         opendevdata(nd, ORDWR);
553         if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetaddress, nd->id, 0, nil, 0) < 0){
554                 dprint(2, "%s: %s: port %d: setaddress: %r\n", argv0, d->dir, p);
555                 goto Fail;
556         }
557         if(devctl(nd, "address") < 0){
558                 dprint(2, "%s: %s: port %d: set address: %r\n", argv0, d->dir, p);
559                 goto Fail;
560         }
561         if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){
562                 dprint(2, "%s: %s: port %d: setconf: %r\n", argv0, d->dir, p);
563                 unstall(nd, nd, Eout);
564                 if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0)
565                         goto Fail;
566         }
567         if(nd->dfd >= 0){
568                 close(nd->dfd);
569                 nd->dfd = -1;
570         }
571         return;
572 Fail:
573         pp->state = Pdisabled;
574         pp->sts = 0;
575         if(pp->hub != nil)
576                 pp->hub = nil;  /* hub closed by enumhub */
577         hubfeature(h, p, Fportenable, 0);
578         if(nd != nil)
579                 devctl(nd, "detach");
580         closedev(nd);
581 }
582
583 static int
584 portgone(Port *pp, int sts)
585 {
586         if(sts < 0)
587                 return 1;
588         /*
589          * If it was enabled and it's not now then it may be reconnect.
590          * We pretend it's gone and later we'll see it as attached.
591          */
592         if((pp->sts & PSenable) != 0 && (sts & PSenable) == 0)
593                 return 1;
594         return (pp->sts & PSpresent) != 0 && (sts & PSpresent) == 0;
595 }
596
597 static int
598 enumhub(Hub *h, int p)
599 {
600         int sts;
601         Dev *d;
602         Port *pp;
603         int onhubs;
604
605         if(h->failed)
606                 return 0;
607         d = h->dev;
608         if(usbdebug > 3)
609                 fprint(2, "%s: %s: port %d enumhub\n", argv0, d->dir, p);
610
611         sts = portstatus(h, p);
612         if(sts < 0){
613                 hubfail(h);             /* avoid delays on detachment */
614                 return -1;
615         }
616         pp = &h->port[p];
617         onhubs = nhubs;
618         if((sts & PSsuspend) != 0){
619                 if(hubfeature(h, p, Fportenable, 1) < 0)
620                         dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
621                 sleep(Enabledelay);
622                 sts = portstatus(h, p);
623                 fprint(2, "%s: %s: port %d: resumed (sts %#ux)\n", argv0, d->dir, p, sts);
624         }
625         if((pp->sts & PSpresent) == 0 && (sts & PSpresent) != 0){
626                 if(portattach(h, p, sts) != nil)
627                         if(attachdev(pp) < 0)
628                                 portdetach(h, p);
629         }else if(portgone(pp, sts)){
630                 portdetach(h, p);
631         }else if(portresetwanted(h, p))
632                 portreset(h, p);
633         else if(pp->sts != sts){
634                 dprint(2, "%s: %s port %d: sts %s %#x ->",
635                         argv0, d->dir, p, stsstr(pp->sts), pp->sts);
636                 dprint(2, " %s %#x\n",stsstr(sts), sts);
637         }
638         pp->sts = sts;
639         if(onhubs != nhubs)
640                 return -1;
641         return 0;
642 }
643
644 static void
645 dump(void)
646 {
647         Hub *h;
648         int i;
649
650         mustdump = 0;
651         for(h = hubs; h != nil; h = h->next)
652                 for(i = 1; i <= h->nport; i++)
653                         fprint(2, "%s: hub %#p %s port %d: %U",
654                                 argv0, h, h->dev->dir, i, h->port[i].dev);
655
656 }
657
658 void
659 work(void)
660 {
661         char *fn;
662         Hub *h;
663         int i;
664
665         hubs = nil;
666         while((fn = rendezvous(work, nil)) != nil){
667                 dprint(2, "%s: %s starting\n", argv0, fn);
668                 h = newhub(fn, nil);
669                 if(h == nil)
670                         fprint(2, "%s: %s: newhub failed: %r\n", argv0, fn);
671                 free(fn);
672         }
673
674         if(hubs == nil)
675                 return;
676
677         /*
678          * Enumerate (and acknowledge after first enumeration).
679          * Do NOT perform enumeration concurrently for the same
680          * controller. new devices attached respond to a default
681          * address (0) after reset, thus enumeration has to work
682          * one device at a time at least before addresses have been
683          * assigned.
684          * Do not use hub interrupt endpoint because we
685          * have to poll the root hub(s) in any case.
686          */
687         for(;;){
688                 qlock(&hublock);
689 Again:
690                 for(h = hubs; h != nil; h = h->next)
691                         for(i = 1; i <= h->nport; i++)
692                                 if(enumhub(h, i) < 0){
693                                         /* changes in hub list; repeat */
694                                         goto Again;
695                                 }
696                 qunlock(&hublock);
697                 sleep(pollms);
698                 if(mustdump)
699                         dump();
700         }
701 }