diff options
Diffstat (limited to 'sys/dev/usb/uhub.c')
-rw-r--r-- | sys/dev/usb/uhub.c | 355 |
1 files changed, 181 insertions, 174 deletions
diff --git a/sys/dev/usb/uhub.c b/sys/dev/usb/uhub.c index b182009..8a44380 100644 --- a/sys/dev/usb/uhub.c +++ b/sys/dev/usb/uhub.c @@ -1,12 +1,13 @@ -/* $NetBSD: uhub.c,v 1.5 1998/08/02 22:30:52 augustss Exp $ */ +/* $NetBSD: uhub.c,v 1.13 1998/12/30 18:06:25 augustss Exp $ */ /* FreeBSD $Id$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * - * Author: Lennart Augustsson <augustss@carlstedt.se> - * Carlstedt Research & Technology + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (augustss@carlstedt.se) at + * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,8 +38,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <dev/usb/usb_port.h> - #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> @@ -61,6 +60,7 @@ #define DPRINTF(x) if (usbdebug) printf x #define DPRINTFN(n,x) if (usbdebug>(n)) printf x extern int usbdebug; +extern char *usbd_error_strs[]; #else #define DPRINTF(x) #define DPRINTFN(n,x) @@ -74,73 +74,27 @@ struct uhub_softc { u_char sc_running; }; -#if defined(__NetBSD__) -int uhub_match __P((struct device *, struct cfdata *, void *)); -void uhub_attach __P((struct device *, struct device *, void *)); -void uhub_detach __P((struct device *)); -#elif defined(__FreeBSD__) -static device_probe_t uhub_match; -static device_attach_t uhub_attach; -static device_detach_t uhub_detach; -#endif - -usbd_status uhub_init_port __P((int, struct usbd_port *, usbd_device_handle)); -void uhub_disconnect __P((struct usbd_port *up, int portno)); +usbd_status uhub_init_port __P((struct usbd_port *)); +void uhub_disconnect __P((struct usbd_port *up)); usbd_status uhub_explore __P((usbd_device_handle hub)); void uhub_intr __P((usbd_request_handle, usbd_private_handle, usbd_status)); /*void uhub_disco __P((void *));*/ -#if defined(__NetBSD__) -extern struct cfdriver uhub_cd; - -struct cfattach uhub_ca = { - sizeof(struct uhub_softc), uhub_match, uhub_attach -}; +USB_DECLARE_DRIVER_NAME(usb, uhub); +/* FIXME what does FreeBSD need? */ +#if defined(__NetBSD__) struct cfattach uhub_uhub_ca = { sizeof(struct uhub_softc), uhub_match, uhub_attach }; - -#elif defined(__FreeBSD__) -static devclass_t uhub_devclass; - -static device_method_t uhub_methods[] = { - DEVMETHOD(device_probe, uhub_match), - DEVMETHOD(device_attach, uhub_attach), - DEVMETHOD(device_detach, uhub_detach), - {0,0} -}; - -static driver_t uhub_driver = { - "usb", /* this is silly, but necessary. The uhub - * implements a usb bus on top of a usb bus, - * but the problem is that name of the driver - * is used a the name of the device class it - * implements. - */ - uhub_methods, - DRIVER_TYPE_MISC, - sizeof(struct uhub_softc) -}; #endif -#if defined(__NetBSD__) -int -uhub_match(parent, match, aux) - struct device *parent; - struct cfdata *match; - void *aux; -{ - struct usb_attach_arg *uaa = aux; -#elif defined(__FreeBSD__) -static int -uhub_match(device_t device) +USB_MATCH(uhub) { - struct usb_attach_arg *uaa = device_get_ivars(device); -#endif + USB_MATCH_START(uhub, uaa); usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device); - + DPRINTFN(5,("uhub_match, dd=%p\n", dd)); /* * The subclass for hubs seems to be 0 for some and 1 for others, @@ -151,52 +105,36 @@ uhub_match(device_t device) return (UMATCH_NONE); } -#if defined(__NetBSD__) -void -uhub_attach(parent, self, aux) - struct device *parent; - struct device *self; - void *aux; +USB_ATTACH(uhub) { - struct uhub_softc *sc = (struct uhub_softc *)self; - struct usb_attach_arg *uaa = aux; -#elif defined(__FreeBSD__) -static int -uhub_attach(device_t self) -{ - struct uhub_softc *sc = device_get_softc(self); - struct usb_attach_arg *uaa = device_get_ivars(self); -#endif + USB_ATTACH_START(uhub, sc, uaa); usbd_device_handle dev = uaa->device; char devinfo[1024]; usbd_status r; struct usbd_hub *hub; usb_device_request_t req; usb_hub_descriptor_t hubdesc; - int port, nports; + int p, port, nports, nremov; usbd_interface_handle iface; usb_endpoint_descriptor_t *ed; - - DPRINTFN(10,("uhub_attach\n")); + + DPRINTFN(1,("uhub_attach\n")); sc->sc_hub = dev; usbd_devinfo(dev, 1, devinfo); -#if defined(__FreeBSD__) - usb_device_set_desc(self, devinfo); - printf("%s%d", device_get_name(self), device_get_unit(self)); -#endif - printf(": %s\n", devinfo); - sc->sc_dev = self; + USB_ATTACH_SETUP; + printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); - r = usbd_set_config_no(dev, 0, 1); + r = usbd_set_config_index(dev, 0, 1); if (r != USBD_NORMAL_COMPLETION) { - DEVICE_ERROR(sc->sc_dev, ("configuration failed, error=%d\n", r)); - ATTACH_ERROR_RETURN; + DPRINTF(("%s: configuration failed, error=%d(%s)\n", + USBDEVNAME(sc->sc_dev), r, usbd_error_strs[r])); + USB_ATTACH_ERROR_RETURN; } if (dev->depth > USB_HUB_MAX_DEPTH) { - DEVICE_ERROR(sc->sc_dev, ("hub depth (%d) exceeded, hub ignored\n", - USB_HUB_MAX_DEPTH)); - ATTACH_ERROR_RETURN; + printf("%s: hub depth (%d) exceeded, hub ignored\n", + USBDEVNAME(sc->sc_dev), USB_HUB_MAX_DEPTH); + USB_ATTACH_ERROR_RETURN; } /* Get hub descriptor. */ @@ -206,49 +144,62 @@ uhub_attach(device_t self) USETW(req.wIndex, 0); USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE); DPRINTFN(1,("usb_init_hub: getting hub descriptor\n")); - /* XXX not correct for hubs with >7 ports */ r = usbd_do_request(dev, &req, &hubdesc); + nports = hubdesc.bNbrPorts; + if (r == USBD_NORMAL_COMPLETION && nports > 7) { + USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8); + r = usbd_do_request(dev, &req, &hubdesc); + } if (r != USBD_NORMAL_COMPLETION) { - DEVICE_ERROR(sc->sc_dev, ("getting hub descriptor failed, error=%d\n", r)); - ATTACH_ERROR_RETURN; + DPRINTF(("%s: getting hub descriptor failed, error=%d(%s)\n", + USBDEVNAME(sc->sc_dev), r, usbd_error_strs[r])); + USB_ATTACH_ERROR_RETURN; } - /* XXX block should be moved down to avoid memory leaking (or an overdose of free()'s) */ - nports = hubdesc.bNbrPorts; + for (nremov = 0, port = 1; port <= nports; port++) + if (!UHD_NOT_REMOV(&hubdesc, port)) + nremov++; + printf("%s: %d port%s with %d removable, %s powered\n", + USBDEVNAME(sc->sc_dev), nports, nports != 1 ? "s" : "", + nremov, dev->self_powered ? "self" : "bus"); + + hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port), M_USB, M_NOWAIT); if (hub == 0) - ATTACH_ERROR_RETURN; + USB_ATTACH_ERROR_RETURN; dev->hub = hub; - dev->hub->hubdata = sc; + dev->hub->hubsoftc = sc; hub->explore = uhub_explore; hub->hubdesc = hubdesc; - hub->nports = nports; - - DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, parent->selfpowered=%d\n", + + DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, " + "parent->selfpowered=%d\n", dev->self_powered, dev->powersrc->parent, dev->powersrc->parent ? dev->powersrc->parent->self_powered : 0)); if (!dev->self_powered && dev->powersrc->parent && !dev->powersrc->parent->self_powered) { - DEVICE_ERROR(sc->sc_dev, ("bus powered hub connected to bus powered hub, ignored\n")); - ATTACH_ERROR_RETURN; + printf("%s: bus powered hub connected to bus powered hub, " + "ignored\n", + USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; } /* Set up interrupt pipe. */ r = usbd_device2interface_handle(dev, 0, &iface); if (r != USBD_NORMAL_COMPLETION) { - DEVICE_ERROR(sc->sc_dev, ("no interface handle\n")); - ATTACH_ERROR_RETURN; + printf("%s: no interface handle\n", USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; } ed = usbd_interface2endpoint_descriptor(iface, 0); if (ed == 0) { - DEVICE_ERROR(sc->sc_dev, ("no endpoint descriptor\n")); - ATTACH_ERROR_RETURN; + printf("%s: no endpoint descriptor\n", USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; } if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { - DEVICE_ERROR(sc->sc_dev, ("bad interrupt endpoint\n")); - ATTACH_ERROR_RETURN; + printf("%s: bad interrupt endpoint\n", USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; } r = usbd_open_pipe_intr(iface, ed->bEndpointAddress,USBD_SHORT_XFER_OK, @@ -256,82 +207,91 @@ uhub_attach(device_t self) sizeof(sc->sc_status), uhub_intr); if (r != USBD_NORMAL_COMPLETION) { - DEVICE_ERROR(sc->sc_dev, ("cannot open interrupt pipe\n")); - ATTACH_ERROR_RETURN; + printf("%s: cannot open interrupt pipe\n", + USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; } - for (port = 1; port <= nports; port++) { - r = uhub_init_port(port, &hub->ports[port-1], dev); + /* Wait with power off for a while. */ + usbd_delay_ms(dev, USB_POWER_DOWN_TIME); + + for (p = 0; p < nports; p++) { + struct usbd_port *up = &hub->ports[p]; + up->device = 0; + up->parent = dev; + up->portno = p+1; + r = uhub_init_port(up); if (r != USBD_NORMAL_COMPLETION) - DEVICE_ERROR(sc->sc_dev, ("init of port %d failed\n", port)); + printf("%s: init of port %d failed\n", + USBDEVNAME(sc->sc_dev), up->portno); } sc->sc_running = 1; - ATTACH_SUCCESS_RETURN; + USB_ATTACH_SUCCESS_RETURN; } -#if defined(__NetBSD__) -static int -uhub_detach(self) - struct device *self; -{ - struct uhub_softc *sc = (struct uhub_softc *)self; -#elif defined(__FreeBSD__) +#if defined(__FreeBSD__) static int uhub_detach(device_t self) { struct uhub_softc *sc = device_get_softc(self); -#endif int nports = sc->sc_hub->hub->hubdesc.bNbrPorts; - int port; + int p; - for (port = 1; port <= nports; port++) { - if (sc->sc_hub->hub->ports[port-1].device) - uhub_disconnect(&sc->sc_hub->hub->ports[port-1], port); + for (p = 0; p < nports; p++) { + struct usbd_port *up = &sc->sc_hub->hub->ports[p]; + if (up->device) + uhub_disconnect(up); } free(sc->sc_hub->hub, M_USB); return 0; } +#endif usbd_status -uhub_init_port(port, uport, dev) - int port; - struct usbd_port *uport; - usbd_device_handle dev; +uhub_init_port(up) + struct usbd_port *up; { + int port = up->portno; + usbd_device_handle dev = up->parent; usbd_status r; u_int16_t pstatus; - uport->device = 0; - uport->parent = dev; - r = usbd_get_port_status(dev, port, &uport->status); + r = usbd_get_port_status(dev, port, &up->status); if (r != USBD_NORMAL_COMPLETION) - return r; - pstatus = UGETW(uport->status.wPortStatus); - DPRINTF(("usbd_init_port: adding hub port=%d status=0x%04x change=0x%04x\n", - port, pstatus, UGETW(uport->status.wPortChange))); + return (r); + pstatus = UGETW(up->status.wPortStatus); + DPRINTF(("usbd_init_port: adding hub port=%d status=0x%04x " + "change=0x%04x\n", + port, pstatus, UGETW(up->status.wPortChange))); if ((pstatus & UPS_PORT_POWER) == 0) { /* Port lacks power, turn it on */ + + /* First let the device go through a good power cycle, */ + usbd_delay_ms(dev, USB_PORT_POWER_DOWN_TIME); + + /* then turn the power on. */ r = usbd_set_port_feature(dev, port, UHF_PORT_POWER); if (r != USBD_NORMAL_COMPLETION) return (r); - r = usbd_get_port_status(dev, port, &uport->status); + r = usbd_get_port_status(dev, port, &up->status); if (r != USBD_NORMAL_COMPLETION) return (r); - DPRINTF(("usb_init_port: turn on port %d power status=0x%04x change=0x%04x\n", - port, UGETW(uport->status.wPortStatus), - UGETW(uport->status.wPortChange))); + DPRINTF(("usb_init_port: turn on port %d power status=0x%04x " + "change=0x%04x\n", + port, UGETW(up->status.wPortStatus), + UGETW(up->status.wPortChange))); /* Wait for stable power. */ - usbd_delay_ms(dev->bus, dev->hub->hubdesc.bPwrOn2PwrGood * - UHD_PWRON_FACTOR); + usbd_delay_ms(dev, dev->hub->hubdesc.bPwrOn2PwrGood * + UHD_PWRON_FACTOR); } if (dev->self_powered) /* Self powered hub, give ports maximum current. */ - uport->power = USB_MAX_POWER; + up->power = USB_MAX_POWER; else - uport->power = USB_MIN_POWER; + up->power = USB_MIN_POWER; return (USBD_NORMAL_COMPLETION); } @@ -340,7 +300,7 @@ uhub_explore(dev) usbd_device_handle dev; { usb_hub_descriptor_t *hd = &dev->hub->hubdesc; - struct uhub_softc *sc = dev->hub->hubdata; + struct uhub_softc *sc = dev->hub->hubsoftc; struct usbd_port *up; usbd_status r; int port; @@ -359,15 +319,35 @@ uhub_explore(dev) up = &dev->hub->ports[port-1]; r = usbd_get_port_status(dev, port, &up->status); if (r != USBD_NORMAL_COMPLETION) { - DPRINTF(("uhub_explore: get port status failed, error=%d\n", - r)); + DPRINTF(("uhub_explore: get port status failed, " + "error=%d(%s)\n", + r, usbd_error_strs[r])); continue; } status = UGETW(up->status.wPortStatus); change = UGETW(up->status.wPortChange); DPRINTFN(5, ("uhub_explore: port %d status 0x%04x 0x%04x\n", port, status, change)); - if (!(change & UPS_CURRENT_CONNECT_STATUS)) { + if (change & UPS_C_PORT_ENABLED) { + usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE); + if (status & UPS_PORT_ENABLED) { + printf("%s: illegal enable change, port %d\n", + USBDEVNAME(sc->sc_dev), port); + } else { + /* Port error condition. */ + if (up->restartcnt++ < USBD_RESTART_MAX) { + printf("%s: port error, restarting " + "port %d\n", + USBDEVNAME(sc->sc_dev), port); + goto disco; + } else { + printf("%s: port error, giving up " + "port %d\n", + USBDEVNAME(sc->sc_dev), port); + } + } + } + if (!(change & UPS_C_CONNECT_STATUS)) { /* No status change, just do recursive explore. */ if (up->device && up->device->hub) up->device->hub->explore(up->device); @@ -384,11 +364,13 @@ uhub_explore(dev) * since a new unit may have been connected before we handle * the disconnect. */ + disco: if (up->device) { /* Disconnected */ - DPRINTF(("uhub_explore: device %d disappeared on port %d\n", + DPRINTF(("uhub_explore: device %d disappeared " + "on port %d\n", up->device->address, port)); - uhub_disconnect(up, port); + uhub_disconnect(up); usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION); } @@ -396,23 +378,24 @@ uhub_explore(dev) continue; /* Connected */ + up->restartcnt = 0; + /* Wait for maximum device power up time. */ - usbd_delay_ms(dev->bus, USB_PORT_POWERUP_DELAY); + usbd_delay_ms(dev, USB_PORT_POWERUP_DELAY); + /* Reset port, which implies enabling it. */ if (usbd_reset_port(dev, port, &up->status) != USBD_NORMAL_COMPLETION) continue; - /* Wait for power to settle in device. */ - usbd_delay_ms(dev->bus, USB_POWER_SETTLE); - /* Get device info and set its address. */ r = usbd_new_device(&sc->sc_dev, dev->bus, dev->depth + 1, status & UPS_LOW_SPEED, port, up); /* XXX retry a few times? */ if (r != USBD_NORMAL_COMPLETION) { - DPRINTFN(-1,("uhub_explore: usb_new_device failed, error=%d\n", r)); + DPRINTFN(-1,("uhub_explore: usb_new_device failed, " + "error=%d(%s)\n", r, usbd_error_strs[r])); /* Avoid addressing problems by disabling. */ /* usbd_reset_port(dev, port, &up->status); */ /* XXX @@ -426,12 +409,13 @@ uhub_explore(dev) * address, and since we cannot leave * at 0 we have to disable the port * instead. */ - /* - DEVICE_ERROR(*parent, ("device problem, disable port %d\n", - port)); - */ + printf("%s: device problem, disabling " + "port %d\n", + USBDEVNAME(sc->sc_dev), port); usbd_clear_port_feature(dev, port, UHF_PORT_ENABLE); + /* Make sure we don't try to restart it. */ + up->restartcnt = USBD_RESTART_MAX; } } else { if (up->device->hub) @@ -442,21 +426,28 @@ uhub_explore(dev) } void -uhub_disconnect(up, portno) +uhub_disconnect(up) struct usbd_port *up; - int portno; { usbd_device_handle dev = up->device; usbd_pipe_handle p, n; - usb_hub_descriptor_t *hd; - struct usbd_port *spi; - int i, port; + int i; + struct softc { bdevice sc_dev; }; /* all softc begin like this */ DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n", - up, dev, portno)); - - DEVICE_MSG(dev->bdev, ("device addr %d%s on hub addr %d, port %d disconnected\n", - dev->address, dev->hub ? " (hub)" : "", up->parent->address, portno)); + up, dev, up->portno)); + + printf("%s: at %s port %d (addr %d) disconnected\n", + USBDEVNAME(((struct softc *)dev->softc)->sc_dev), + USBDEVNAME(((struct uhub_softc *)up->parent->softc)->sc_dev), + up->portno, dev->address); + + if (!dev->cdesc) { + /* Partially attached device, just drop it. */ + dev->bus->devices[dev->address] = 0; + up->device = 0; + return; + } for (i = 0; i < dev->cdesc->bNumInterface; i++) { for (p = LIST_FIRST(&dev->ifaces[i].pipes); p; p = n) { @@ -468,8 +459,26 @@ uhub_disconnect(up, portno) } } - /* clean up the kindergarten, get rid of the kids */ - usbd_remove_device(dev, up); + /* XXX Free all data structures and disable further I/O. */ + if (dev->hub) { + struct usbd_port *rup; + int p, nports; + + DPRINTFN(3,("usb_disconnect: hub, recursing\n")); + nports = dev->hub->hubdesc.bNbrPorts; + for(p = 0; p < nports; p++) { + rup = &dev->hub->ports[p]; + if (rup->device) + uhub_disconnect(rup); + } + } + + dev->bus->devices[dev->address] = 0; + up->device = 0; + /* XXX free */ +#if defined(__FreeBSD__) + device_delete_child(device_get_parent(((struct softc *)dev->softc)->sc_dev), ((struct softc *)dev->softc)->sc_dev); +#endif } void @@ -481,14 +490,12 @@ uhub_intr(reqh, addr, status) struct uhub_softc *sc = addr; DPRINTFN(5,("uhub_intr: sc=%p\n", sc)); -#if 0 if (status != USBD_NORMAL_COMPLETION) - usbd_clear_endpoint_stall(sc->sc_ipipe); + usbd_clear_endpoint_stall_async(sc->sc_ipipe); else -#endif usb_needs_explore(sc->sc_hub->bus); } #if defined(__FreeBSD__) -DRIVER_MODULE(uhub, usb, uhub_driver, uhub_devclass, usb_driver_load, 0); +DRIVER_MODULE(uhub, usb, uhub_driver, uhub_devclass, usbd_driver_load, 0); #endif |