Annotation of sys/dev/usb/uhub.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: uhub.c,v 1.47 2007/06/14 10:11:16 mbalmer Exp $ */
2: /* $NetBSD: uhub.c,v 1.64 2003/02/08 03:32:51 ichiro Exp $ */
3: /* $FreeBSD: src/sys/dev/usb/uhub.c,v 1.18 1999/11/17 22:33:43 n_hibma Exp $ */
4:
5: /*
6: * Copyright (c) 1998 The NetBSD Foundation, Inc.
7: * All rights reserved.
8: *
9: * This code is derived from software contributed to The NetBSD Foundation
10: * by Lennart Augustsson (lennart@augustsson.net) at
11: * Carlstedt Research & Technology.
12: *
13: * Redistribution and use in source and binary forms, with or without
14: * modification, are permitted provided that the following conditions
15: * are met:
16: * 1. Redistributions of source code must retain the above copyright
17: * notice, this list of conditions and the following disclaimer.
18: * 2. Redistributions in binary form must reproduce the above copyright
19: * notice, this list of conditions and the following disclaimer in the
20: * documentation and/or other materials provided with the distribution.
21: * 3. All advertising materials mentioning features or use of this software
22: * must display the following acknowledgement:
23: * This product includes software developed by the NetBSD
24: * Foundation, Inc. and its contributors.
25: * 4. Neither the name of The NetBSD Foundation nor the names of its
26: * contributors may be used to endorse or promote products derived
27: * from this software without specific prior written permission.
28: *
29: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
30: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
33: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39: * POSSIBILITY OF SUCH DAMAGE.
40: */
41:
42: /*
43: * USB spec: http://www.usb.org/developers/docs/usbspec.zip
44: */
45:
46: #include <sys/param.h>
47: #include <sys/systm.h>
48: #include <sys/kernel.h>
49: #include <sys/malloc.h>
50: #include <sys/device.h>
51: #include <sys/proc.h>
52:
53: #include <machine/bus.h>
54:
55: #include <dev/usb/usb.h>
56: #include <dev/usb/usbdi.h>
57: #include <dev/usb/usbdi_util.h>
58: #include <dev/usb/usbdivar.h>
59:
60: #define UHUB_INTR_INTERVAL 255 /* ms */
61:
62: #ifdef UHUB_DEBUG
63: #define DPRINTF(x) do { if (uhubdebug) printf x; } while (0)
64: #define DPRINTFN(n,x) do { if (uhubdebug>(n)) printf x; } while (0)
65: int uhubdebug = 0;
66: #else
67: #define DPRINTF(x)
68: #define DPRINTFN(n,x)
69: #endif
70:
71: struct uhub_softc {
72: struct device sc_dev; /* base device */
73: usbd_device_handle sc_hub; /* USB device */
74: usbd_pipe_handle sc_ipipe; /* interrupt pipe */
75: u_int8_t sc_status[1]; /* XXX more ports */
76: u_char sc_running;
77: };
78: #define UHUB_PROTO(sc) ((sc)->sc_hub->ddesc.bDeviceProtocol)
79: #define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB)
80: #define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT)
81:
82: usbd_status uhub_explore(usbd_device_handle hub);
83: void uhub_intr(usbd_xfer_handle, usbd_private_handle,usbd_status);
84:
85: /*
86: * We need two attachment points:
87: * hub to usb and hub to hub
88: * Every other driver only connects to hubs
89: */
90:
91: int uhub_match(struct device *, void *, void *);
92: void uhub_attach(struct device *, struct device *, void *);
93: int uhub_detach(struct device *, int);
94: int uhub_activate(struct device *, enum devact);
95:
96: struct cfdriver uhub_cd = {
97: NULL, "uhub", DV_DULL
98: };
99:
100: const struct cfattach uhub_ca = {
101: sizeof(struct uhub_softc),
102: uhub_match,
103: uhub_attach,
104: uhub_detach,
105: uhub_activate,
106: };
107:
108: struct cfattach uhub_uhub_ca = {
109: sizeof(struct uhub_softc), uhub_match, uhub_attach,
110: uhub_detach, uhub_activate
111: };
112:
113: int
114: uhub_match(struct device *parent, void *match, void *aux)
115: {
116: struct usb_attach_arg *uaa = aux;
117: usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);
118:
119: DPRINTFN(5,("uhub_match, dd=%p\n", dd));
120: /*
121: * The subclass for hubs seems to be 0 for some and 1 for others,
122: * so we just ignore the subclass.
123: */
124: if (uaa->iface == NULL && dd->bDeviceClass == UDCLASS_HUB)
125: return (UMATCH_DEVCLASS_DEVSUBCLASS);
126: return (UMATCH_NONE);
127: }
128:
129: void
130: uhub_attach(struct device *parent, struct device *self, void *aux)
131: {
132: struct uhub_softc *sc = (struct uhub_softc *)self;
133: struct usb_attach_arg *uaa = aux;
134: usbd_device_handle dev = uaa->device;
135: char *devinfop;
136: usbd_status err;
137: struct usbd_hub *hub = NULL;
138: usb_device_request_t req;
139: usb_hub_descriptor_t hubdesc;
140: int p, port, nports, nremov, pwrdly;
141: usbd_interface_handle iface;
142: usb_endpoint_descriptor_t *ed;
143: struct usbd_tt *tts = NULL;
144:
145: DPRINTFN(1,("uhub_attach\n"));
146: sc->sc_hub = dev;
147:
148: devinfop = usbd_devinfo_alloc(dev, 0);
149: printf(": %s\n", devinfop);
150: usbd_devinfo_free(devinfop);
151:
152: err = usbd_set_config_index(dev, 0, 1);
153: if (err) {
154: DPRINTF(("%s: configuration failed, error=%s\n",
155: sc->sc_dev.dv_xname, usbd_errstr(err)));
156: return;
157: }
158:
159: if (dev->depth > USB_HUB_MAX_DEPTH) {
160: printf("%s: hub depth (%d) exceeded, hub ignored\n",
161: sc->sc_dev.dv_xname, USB_HUB_MAX_DEPTH);
162: return;
163: }
164:
165: /* Get hub descriptor. */
166: req.bmRequestType = UT_READ_CLASS_DEVICE;
167: req.bRequest = UR_GET_DESCRIPTOR;
168: USETW2(req.wValue, UDESC_HUB, 0);
169: USETW(req.wIndex, 0);
170: USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
171: DPRINTFN(1,("usb_init_hub: getting hub descriptor\n"));
172: err = usbd_do_request(dev, &req, &hubdesc);
173: nports = hubdesc.bNbrPorts;
174: if (!err && nports > 7) {
175: USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8);
176: err = usbd_do_request(dev, &req, &hubdesc);
177: }
178: if (err) {
179: DPRINTF(("%s: getting hub descriptor failed, error=%s\n",
180: sc->sc_dev.dv_xname, usbd_errstr(err)));
181: return;
182: }
183:
184: for (nremov = 0, port = 1; port <= nports; port++)
185: if (!UHD_NOT_REMOV(&hubdesc, port))
186: nremov++;
187:
188: #ifdef UHUB_DEBUG
189: printf("%s: %d port%s with %d removable, %s powered",
190: sc->sc_dev.dv_xname, nports, nports != 1 ? "s" : "",
191: nremov, dev->self_powered ? "self" : "bus");
192:
193: if (dev->depth > 0 && UHUB_IS_HIGH_SPEED(sc)) {
194: printf(", %s transaction translator%s",
195: UHUB_IS_SINGLE_TT(sc) ? "single" : "multiple",
196: UHUB_IS_SINGLE_TT(sc) ? "" : "s");
197: }
198: printf("\n");
199: #endif
200:
201: if (nports == 0) {
202: printf("%s: no ports, hub ignored\n", sc->sc_dev.dv_xname);
203: goto bad;
204: }
205:
206: hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),
207: M_USBDEV, M_NOWAIT);
208: if (hub == NULL)
209: return;
210: dev->hub = hub;
211: dev->hub->hubsoftc = sc;
212: hub->explore = uhub_explore;
213: hub->hubdesc = hubdesc;
214:
215: DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, "
216: "parent->selfpowered=%d\n",
217: dev->self_powered, dev->powersrc->parent,
218: dev->powersrc->parent ?
219: dev->powersrc->parent->self_powered : 0));
220:
221: if (!dev->self_powered && dev->powersrc->parent != NULL &&
222: !dev->powersrc->parent->self_powered) {
223: printf("%s: bus powered hub connected to bus powered hub, "
224: "ignored\n", sc->sc_dev.dv_xname);
225: goto bad;
226: }
227:
228: /* Set up interrupt pipe. */
229: err = usbd_device2interface_handle(dev, 0, &iface);
230: if (err) {
231: printf("%s: no interface handle\n", sc->sc_dev.dv_xname);
232: goto bad;
233: }
234: ed = usbd_interface2endpoint_descriptor(iface, 0);
235: if (ed == NULL) {
236: printf("%s: no endpoint descriptor\n", sc->sc_dev.dv_xname);
237: goto bad;
238: }
239: if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
240: printf("%s: bad interrupt endpoint\n", sc->sc_dev.dv_xname);
241: goto bad;
242: }
243:
244: err = usbd_open_pipe_intr(iface, ed->bEndpointAddress,
245: USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_status,
246: sizeof(sc->sc_status), uhub_intr, UHUB_INTR_INTERVAL);
247: if (err) {
248: printf("%s: cannot open interrupt pipe\n",
249: sc->sc_dev.dv_xname);
250: goto bad;
251: }
252:
253: /* Wait with power off for a while. */
254: usbd_delay_ms(dev, USB_POWER_DOWN_TIME);
255:
256: usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, dev, &sc->sc_dev);
257:
258: /*
259: * To have the best chance of success we do things in the exact same
260: * order as Windoze98. This should not be necessary, but some
261: * devices do not follow the USB specs to the letter.
262: *
263: * These are the events on the bus when a hub is attached:
264: * Get device and config descriptors (see attach code)
265: * Get hub descriptor (see above)
266: * For all ports
267: * turn on power
268: * wait for power to become stable
269: * (all below happens in explore code)
270: * For all ports
271: * clear C_PORT_CONNECTION
272: * For all ports
273: * get port status
274: * if device connected
275: * wait 100 ms
276: * turn on reset
277: * wait
278: * clear C_PORT_RESET
279: * get port status
280: * proceed with device attachment
281: */
282:
283: if (UHUB_IS_HIGH_SPEED(sc)) {
284: tts = malloc((UHUB_IS_SINGLE_TT(sc) ? 1 : nports) *
285: sizeof (struct usbd_tt), M_USBDEV, M_NOWAIT);
286: if (!tts)
287: goto bad;
288: }
289: /* Set up data structures */
290: for (p = 0; p < nports; p++) {
291: struct usbd_port *up = &hub->ports[p];
292: up->device = NULL;
293: up->parent = dev;
294: up->portno = p+1;
295: if (dev->self_powered)
296: /* Self powered hub, give ports maximum current. */
297: up->power = USB_MAX_POWER;
298: else
299: up->power = USB_MIN_POWER;
300: up->restartcnt = 0;
301: up->reattach = 0;
302: if (UHUB_IS_HIGH_SPEED(sc)) {
303: up->tt = &tts[UHUB_IS_SINGLE_TT(sc) ? 0 : p];
304: up->tt->hub = hub;
305: } else {
306: up->tt = NULL;
307: }
308: }
309:
310: /* XXX should check for none, individual, or ganged power? */
311:
312: pwrdly = dev->hub->hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR
313: + USB_EXTRA_POWER_UP_TIME;
314: for (port = 1; port <= nports; port++) {
315: /* Turn the power on. */
316: err = usbd_set_port_feature(dev, port, UHF_PORT_POWER);
317: if (err)
318: printf("%s: port %d power on failed, %s\n",
319: sc->sc_dev.dv_xname, port,
320: usbd_errstr(err));
321: DPRINTF(("usb_init_port: turn on port %d power\n", port));
322: }
323:
324: /* Wait for stable power. Root hubs delay in their event thread. */
325: if (dev->powersrc->parent != NULL)
326: usbd_delay_ms(dev, pwrdly);
327:
328: /* The usual exploration will finish the setup. */
329:
330: sc->sc_running = 1;
331:
332: return;
333:
334: bad:
335: if (hub)
336: free(hub, M_USBDEV);
337: dev->hub = NULL;
338: }
339:
340: usbd_status
341: uhub_explore(usbd_device_handle dev)
342: {
343: usb_hub_descriptor_t *hd = &dev->hub->hubdesc;
344: struct uhub_softc *sc = dev->hub->hubsoftc;
345: struct usbd_port *up;
346: usbd_status err;
347: int speed;
348: int port;
349: int change, status, reconnect;
350:
351: DPRINTFN(10, ("uhub_explore dev=%p addr=%d\n", dev, dev->address));
352:
353: if (!sc->sc_running)
354: return (USBD_NOT_STARTED);
355:
356: /* Ignore hubs that are too deep. */
357: if (dev->depth > USB_HUB_MAX_DEPTH)
358: return (USBD_TOO_DEEP);
359:
360: for(port = 1; port <= hd->bNbrPorts; port++) {
361: up = &dev->hub->ports[port-1];
362: err = usbd_get_port_status(dev, port, &up->status);
363: if (err) {
364: DPRINTF(("uhub_explore: get port status failed, "
365: "error=%s\n", usbd_errstr(err)));
366: continue;
367: }
368: status = UGETW(up->status.wPortStatus);
369: change = UGETW(up->status.wPortChange);
370: reconnect = up->reattach;
371: up->reattach = 0;
372: DPRINTFN(3,("uhub_explore: %s port %d status 0x%04x 0x%04x\n",
373: sc->sc_dev.dv_xname, port, status, change));
374: if (change & UPS_C_PORT_ENABLED) {
375: DPRINTF(("uhub_explore: C_PORT_ENABLED\n"));
376: usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
377: if (change & UPS_C_CONNECT_STATUS) {
378: /* Ignore the port error if the device
379: vanished. */
380: } else if (status & UPS_PORT_ENABLED) {
381: printf("%s: illegal enable change, port %d\n",
382: sc->sc_dev.dv_xname, port);
383: } else {
384: /* Port error condition. */
385: if (up->restartcnt) /* no message first time */
386: printf("%s: port error, restarting "
387: "port %d\n",
388: sc->sc_dev.dv_xname, port);
389:
390: if (up->restartcnt++ < USBD_RESTART_MAX)
391: goto disco;
392: else
393: printf("%s: port error, giving up "
394: "port %d\n",
395: sc->sc_dev.dv_xname, port);
396: }
397: }
398: if (!reconnect && !(change & UPS_C_CONNECT_STATUS)) {
399: DPRINTFN(3,("uhub_explore: port=%d !C_CONNECT_"
400: "STATUS\n", port));
401: /* No status change, just do recursive explore. */
402: if (up->device != NULL && up->device->hub != NULL)
403: up->device->hub->explore(up->device);
404: #if 0 && defined(DIAGNOSTIC)
405: if (up->device == NULL &&
406: (status & UPS_CURRENT_CONNECT_STATUS))
407: printf("%s: connected, no device\n",
408: sc->sc_dev.dv_xname);
409: #endif
410: continue;
411: }
412:
413: /* We have a connect status change, handle it. */
414:
415: DPRINTF(("uhub_explore: status change hub=%d port=%d\n",
416: dev->address, port));
417: usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION);
418: /*usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);*/
419: /*
420: * If there is already a device on the port the change status
421: * must mean that is has disconnected. Looking at the
422: * current connect status is not enough to figure this out
423: * since a new unit may have been connected before we handle
424: * the disconnect.
425: */
426: disco:
427: if (up->device != NULL) {
428: /* Disconnected */
429: DPRINTF(("uhub_explore: device addr=%d disappeared "
430: "on port %d\n", up->device->address, port));
431: usb_disconnect_port(up, &sc->sc_dev);
432: usbd_clear_port_feature(dev, port,
433: UHF_C_PORT_CONNECTION);
434: }
435: if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
436: /* Nothing connected, just ignore it. */
437: DPRINTFN(3,("uhub_explore: port=%d !CURRENT_CONNECT"
438: "_STATUS\n", port));
439: continue;
440: }
441:
442: /* Connected */
443:
444: if (!(status & UPS_PORT_POWER))
445: printf("%s: strange, connected port %d has no power\n",
446: sc->sc_dev.dv_xname, port);
447:
448: /* Wait for maximum device power up time. */
449: usbd_delay_ms(dev, USB_PORT_POWERUP_DELAY);
450:
451: /* Reset port, which implies enabling it. */
452: if (usbd_reset_port(dev, port, &up->status)) {
453: printf("%s: port %d reset failed\n",
454: sc->sc_dev.dv_xname, port);
455: continue;
456: }
457: /* Get port status again, it might have changed during reset */
458: err = usbd_get_port_status(dev, port, &up->status);
459: if (err) {
460: DPRINTF(("uhub_explore: get port status failed, "
461: "error=%s\n", usbd_errstr(err)));
462: continue;
463: }
464: status = UGETW(up->status.wPortStatus);
465: change = UGETW(up->status.wPortChange);
466: if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
467: /* Nothing connected, just ignore it. */
468: #ifdef UHUB_DEBUG
469: printf("%s: port %d, device disappeared after reset\n",
470: sc->sc_dev.dv_xname, port);
471: #endif
472: continue;
473: }
474:
475: /* Figure out device speed */
476: if (status & UPS_HIGH_SPEED)
477: speed = USB_SPEED_HIGH;
478: else if (status & UPS_LOW_SPEED)
479: speed = USB_SPEED_LOW;
480: else
481: speed = USB_SPEED_FULL;
482: /* Get device info and set its address. */
483: err = usbd_new_device(&sc->sc_dev, dev->bus,
484: dev->depth + 1, speed, port, up);
485: /* XXX retry a few times? */
486: if (err) {
487: DPRINTFN(-1,("uhub_explore: usbd_new_device failed, "
488: "error=%s\n", usbd_errstr(err)));
489: /* Avoid addressing problems by disabling. */
490: /* usbd_reset_port(dev, port, &up->status); */
491:
492: /*
493: * The unit refused to accept a new address, or had
494: * some other serious problem. Since we cannot leave
495: * at 0 we have to disable the port instead.
496: */
497: printf("%s: device problem, disabling port %d\n",
498: sc->sc_dev.dv_xname, port);
499: usbd_clear_port_feature(dev, port, UHF_PORT_ENABLE);
500: } else {
501: /* The port set up succeeded, reset error count. */
502: up->restartcnt = 0;
503:
504: if (up->device->hub)
505: up->device->hub->explore(up->device);
506: }
507: }
508: return (USBD_NORMAL_COMPLETION);
509: }
510:
511: int
512: uhub_activate(struct device *self, enum devact act)
513: {
514: struct uhub_softc *sc = (struct uhub_softc *)self;
515: struct usbd_hub *hub = sc->sc_hub->hub;
516: usbd_device_handle dev;
517: int nports, port, i;
518:
519: switch (act) {
520: case DVACT_ACTIVATE:
521: break;
522:
523: case DVACT_DEACTIVATE:
524: if (hub == NULL) /* malfunctioning hub */
525: break;
526: nports = hub->hubdesc.bNbrPorts;
527: for(port = 0; port < nports; port++) {
528: dev = hub->ports[port].device;
529: if (dev != NULL && dev->subdevs != NULL) {
530: for (i = 0; dev->subdevs[i] != NULL; i++)
531: config_deactivate(dev->subdevs[i]);
532: }
533: }
534: break;
535: }
536: return (0);
537: }
538:
539: /*
540: * Called from process context when the hub is gone.
541: * Detach all devices on active ports.
542: */
543: int
544: uhub_detach(struct device *self, int flags)
545: {
546: struct uhub_softc *sc = (struct uhub_softc *)self;
547: struct usbd_hub *hub = sc->sc_hub->hub;
548: struct usbd_port *rup;
549: int port, nports;
550:
551: DPRINTF(("uhub_detach: sc=%p flags=%d\n", sc, flags));
552:
553: if (hub == NULL) /* Must be partially working */
554: return (0);
555:
556: usbd_abort_pipe(sc->sc_ipipe);
557: usbd_close_pipe(sc->sc_ipipe);
558:
559: nports = hub->hubdesc.bNbrPorts;
560: for(port = 0; port < nports; port++) {
561: rup = &hub->ports[port];
562: if (rup->device)
563: usb_disconnect_port(rup, self);
564: }
565:
566: usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_hub,
567: &sc->sc_dev);
568:
569: if (hub->ports[0].tt)
570: free(hub->ports[0].tt, M_USBDEV);
571: free(hub, M_USBDEV);
572: sc->sc_hub->hub = NULL;
573:
574: return (0);
575: }
576:
577: /*
578: * Hub interrupt.
579: * This an indication that some port has changed status.
580: * Notify the bus event handler thread that we need
581: * to be explored again.
582: */
583: void
584: uhub_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
585: {
586: struct uhub_softc *sc = addr;
587:
588: DPRINTFN(5,("uhub_intr: sc=%p\n", sc));
589: if (status == USBD_STALLED)
590: usbd_clear_endpoint_stall_async(sc->sc_ipipe);
591: else if (status == USBD_NORMAL_COMPLETION)
592: usb_needs_explore(sc->sc_hub);
593: }
CVSweb