diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2015-06-29 18:27:41 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2015-06-29 18:27:41 +0000 |
commit | c0a19be517314a589b041d3eb33a5944dc22a1aa (patch) | |
tree | cc56c3d513a32e6bc1375b3e7e564b3def6edb6f | |
parent | 441ccb55b8d160a4fadbd625f604f64c4f7d05b1 (diff) |
Move the logic to handle a "connect status change" from uhub_explore()
into its own functions.
Needed for upcoming "port link state" change handling.
Tested by sobrado@
-rw-r--r-- | sys/dev/usb/uhub.c | 258 |
1 files changed, 134 insertions, 124 deletions
diff --git a/sys/dev/usb/uhub.c b/sys/dev/usb/uhub.c index ce8d0c82d70..0057888bf1f 100644 --- a/sys/dev/usb/uhub.c +++ b/sys/dev/usb/uhub.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uhub.c,v 1.85 2015/06/22 10:29:18 mpi Exp $ */ +/* $OpenBSD: uhub.c,v 1.86 2015/06/29 18:27:40 mpi Exp $ */ /* $NetBSD: uhub.c,v 1.64 2003/02/08 03:32:51 ichiro Exp $ */ /* $FreeBSD: src/sys/dev/usb/uhub.c,v 1.18 1999/11/17 22:33:43 n_hibma Exp $ */ @@ -53,6 +53,8 @@ #define DPRINTF(x...) #endif +#define DEVNAME(sc) ((sc)->sc_dev.dv_xname) + struct uhub_softc { struct device sc_dev; /* base device */ struct usbd_device *sc_hub; /* USB device */ @@ -70,6 +72,7 @@ struct uhub_softc { int uhub_explore(struct usbd_device *hub); void uhub_intr(struct usbd_xfer *, void *, usbd_status); +int uhub_port_connect(struct uhub_softc *, int, int, int); /* * We need two attachment points: @@ -355,30 +358,26 @@ uhub_explore(struct usbd_device *dev) { struct uhub_softc *sc = dev->hub->hubsoftc; struct usbd_port *up; - usbd_status err; - int speed; + int status, change; int port; - int change, status, reconnect; - if (usbd_is_dying(dev)) + if (usbd_is_dying(sc->sc_hub)) return (EIO); if (!sc->sc_running) return (ENXIO); /* Ignore hubs that are too deep. */ - if (dev->depth > USB_HUB_MAX_DEPTH) + if (sc->sc_hub->depth > USB_HUB_MAX_DEPTH) return (EOPNOTSUPP); - for (port = 1; port <= dev->hub->nports; port++) { - up = &dev->hub->ports[port-1]; + for (port = 1; port <= sc->sc_hub->hub->nports; port++) { + up = &sc->sc_hub->hub->ports[port-1]; - reconnect = up->reattach; - up->reattach = 0; change = 0; status = 0; - if ((sc->sc_status & (1 << port)) || reconnect) { + if ((sc->sc_status & (1 << port)) || up->reattach) { sc->sc_status &= ~(1 << port); if (usbd_get_port_status(dev, port, &up->status)) @@ -390,8 +389,14 @@ uhub_explore(struct usbd_device *dev) sc->sc_dev.dv_xname, port, status, change); } + if (up->reattach) { + change |= UPS_C_CONNECT_STATUS; + up->reattach = 0; + } + if (change & UPS_C_PORT_ENABLED) { - usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE); + usbd_clear_port_feature(sc->sc_hub, port, + UHF_C_PORT_ENABLE); if (change & UPS_C_CONNECT_STATUS) { /* Ignore the port error if the device vanished. */ @@ -406,130 +411,30 @@ uhub_explore(struct usbd_device *dev) sc->sc_dev.dv_xname, port); if (up->restartcnt++ < USBD_RESTART_MAX) - goto disco; + change |= UPS_C_CONNECT_STATUS; else printf("%s: port error, giving up " "port %d\n", sc->sc_dev.dv_xname, port); } } - if (!reconnect && !(change & UPS_C_CONNECT_STATUS)) { - /* No status change, just do recursive explore. */ - if (up->device != NULL && up->device->hub != NULL) - up->device->hub->explore(up->device); - continue; - } - - /* We have a connect status change, handle it. */ - usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION); - - /* - * If there is already a device on the port the change status - * must mean that is has disconnected. Looking at the - * current connect status is not enough to figure this out - * since a new unit may have been connected before we handle - * the disconnect. - */ - disco: - if (up->device != NULL) { - /* Disconnected */ - usbd_detach(up->device, &sc->sc_dev); - up->device = NULL; - usbd_clear_port_feature(dev, port, - UHF_C_PORT_CONNECTION); - } - if (!(status & UPS_CURRENT_CONNECT_STATUS)) { - /* Nothing connected, just ignore it. */ - continue; - } - - /* Connected */ - if (!(status & (UPS_PORT_POWER|UPS_PORT_POWER_SS))) { - printf("%s: connected port %d has no power\n", - sc->sc_dev.dv_xname, port); - continue; - } - - /* Wait for maximum device power up time. */ - usbd_delay_ms(dev, USB_PORT_POWERUP_DELAY); - - /* Reset port, which implies enabling it. */ - if (usbd_reset_port(dev, port)) { - printf("%s: port %d reset failed\n", - sc->sc_dev.dv_xname, port); - continue; - } - /* Get port status again, it might have changed during reset */ - if (usbd_get_port_status(dev, port, &up->status)) - continue; - - status = UGETW(up->status.wPortStatus); - change = UGETW(up->status.wPortChange); - DPRINTF("%s: port %d status=0x%04x change=0x%04x\n", - sc->sc_dev.dv_xname, port, status, change); - - if (!(status & UPS_CURRENT_CONNECT_STATUS)) { - /* Nothing connected, just ignore it. */ - DPRINTF("%s: port %d, device disappeared after reset\n", - sc->sc_dev.dv_xname, port); - continue; - } - /* - * Figure out device speed. This is a bit tricky because - * UPS_PORT_POWER_SS and UPS_LOW_SPEED share the same bit. - */ - if ((status & UPS_PORT_POWER) == 0) - status &= ~UPS_PORT_POWER_SS; - - if (status & UPS_HIGH_SPEED) - speed = USB_SPEED_HIGH; - else if (status & UPS_LOW_SPEED) - speed = USB_SPEED_LOW; - else { - /* - * If there is no power bit set, it is certainly - * a Super Speed device, so use the speed of its - * parent hub. - */ - if (status & UPS_PORT_POWER) - speed = USB_SPEED_FULL; - else - speed = sc->sc_hub->speed; - } - - /* - * Reduce the speed, otherwise we won't setup the proper - * transfer methods. - */ - if (speed > sc->sc_hub->speed) - speed = sc->sc_hub->speed; + if (change & UPS_C_CONNECT_STATUS) { + if (uhub_port_connect(sc, port, status, change)) + continue; - /* Get device info and set its address. */ - err = usbd_new_device(&sc->sc_dev, dev->bus, - dev->depth + 1, speed, port, up); - /* XXX retry a few times? */ - if (err) { - DPRINTF("%s: usbd_new_device failed, error=%s\n", - sc->sc_dev.dv_xname, usbd_errstr(err)); - /* Avoid addressing problems by disabling. */ - /* usbd_reset_port(dev, port, &up->status); */ - - /* - * The unit refused to accept a new address, or had - * some other serious problem. Since we cannot leave - * at 0 we have to disable the port instead. - */ - printf("%s: device problem, disabling port %d\n", - sc->sc_dev.dv_xname, port); - usbd_clear_port_feature(dev, port, UHF_PORT_ENABLE); - } else { /* The port set up succeeded, reset error count. */ up->restartcnt = 0; + } - if (up->device->hub) - up->device->hub->explore(up->device); + if (change & UPS_C_PORT_LINK_STATE) { + usbd_clear_port_feature(sc->sc_hub, port, + UHF_C_PORT_LINK_STATE); } + + /* Recursive explore. */ + if (up->device != NULL && up->device->hub != NULL) + up->device->hub->explore(up->device); } return (0); @@ -600,3 +505,108 @@ uhub_intr(struct usbd_xfer *xfer, void *addr, usbd_status status) usb_needs_explore(sc->sc_hub, 0); } } + +int +uhub_port_connect(struct uhub_softc *sc, int port, int status, int change) +{ + struct usbd_port *up = &sc->sc_hub->hub->ports[port-1]; + int speed; + + /* We have a connect status change, handle it. */ + usbd_clear_port_feature(sc->sc_hub, port, UHF_C_PORT_CONNECTION); + + /* + * If there is already a device on the port the change status + * must mean that is has disconnected. Looking at the + * current connect status is not enough to figure this out + * since a new unit may have been connected before we handle + * the disconnect. + */ + if (up->device != NULL) { + /* Disconnected */ + usbd_detach(up->device, &sc->sc_dev); + up->device = NULL; + } + + /* Nothing connected, just ignore it. */ + if ((status & UPS_CURRENT_CONNECT_STATUS) == 0) + return (0); + + /* Connected */ + if ((status & (UPS_PORT_POWER|UPS_PORT_POWER_SS)) == 0) { + printf("%s: connected port %d has no power\n", DEVNAME(sc), + port); + return (-1); + } + + /* Wait for maximum device power up time. */ + usbd_delay_ms(sc->sc_hub, USB_PORT_POWERUP_DELAY); + + /* Reset port, which implies enabling it. */ + if (usbd_reset_port(sc->sc_hub, port)) { + printf("%s: port %d reset failed\n", DEVNAME(sc), port); + return (-1); + } + /* Get port status again, it might have changed during reset */ + if (usbd_get_port_status(sc->sc_hub, port, &up->status)) + return (-1); + + status = UGETW(up->status.wPortStatus); + change = UGETW(up->status.wPortChange); + DPRINTF("%s: port %d status=0x%04x change=0x%04x\n", DEVNAME(sc), + port, status, change); + + /* Nothing connected, just ignore it. */ + if ((status & UPS_CURRENT_CONNECT_STATUS) == 0) { + DPRINTF("%s: port %d, device disappeared after reset\n", + DEVNAME(sc), port); + return (-1); + } + + /* + * Figure out device speed. This is a bit tricky because + * UPS_PORT_POWER_SS and UPS_LOW_SPEED share the same bit. + */ + if ((status & UPS_PORT_POWER) == 0) + status &= ~UPS_PORT_POWER_SS; + + if (status & UPS_HIGH_SPEED) + speed = USB_SPEED_HIGH; + else if (status & UPS_LOW_SPEED) + speed = USB_SPEED_LOW; + else { + /* + * If there is no power bit set, it is certainly + * a Super Speed device, so use the speed of its + * parent hub. + */ + if (status & UPS_PORT_POWER) + speed = USB_SPEED_FULL; + else + speed = sc->sc_hub->speed; + } + + /* + * Reduce the speed, otherwise we won't setup the proper + * transfer methods. + */ + if (speed > sc->sc_hub->speed) + speed = sc->sc_hub->speed; + + /* Get device info and set its address. */ + if (usbd_new_device(&sc->sc_dev, sc->sc_hub->bus, sc->sc_hub->depth + 1, + speed, port, up)) { + /* + * The unit refused to accept a new address, or had + * some other serious problem. Since we cannot leave + * at 0 we have to disable the port instead. + */ + printf("%s: device problem, disabling port %d\n", DEVNAME(sc), + port); + usbd_clear_port_feature(sc->sc_hub, port, UHF_PORT_ENABLE); + + return (-1); + } + + return (0); +} |