usbd_status usbd_reset_port(usbd_device_handle dev, int port, usb_port_status_t *ps) { usb_device_request_t req; usbd_status err; int n; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UHF_PORT_RESET); USETW(req.wIndex, port); USETW(req.wLength, 0); err = usbd_do_request(dev, &req, 0); DPRINTFN(1,("usbd_reset_port: port %d reset done, error=%s\n", port, usbd_errstr(err))); if (err) return (err); n = 10; do { /* Wait for device to recover from reset. */ usbd_delay_ms(dev, USB_PORT_RESET_DELAY); err = usbd_get_port_status(dev, port, ps); if (err) { DPRINTF(("usbd_reset_port: get status failed %d\n", err)); return (err); } /* If the device disappeared, just give up. */ if (!(UGETW(ps->wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) return (USBD_NORMAL_COMPLETION); } while ((UGETW(ps->wPortChange) & UPS_C_PORT_RESET) == 0 && --n > 0); if (n == 0) return (USBD_TIMEOUT); err = usbd_clear_port_feature(dev, port, UHF_C_PORT_RESET); #ifdef USB_DEBUG if (err) DPRINTF(("usbd_reset_port: clear port feature failed %d\n", err)); #endif /* Wait for the device to recover from reset. */ usbd_delay_ms(dev, USB_PORT_RESET_RECOVERY); return (err); }
usbd_status uhub_explore(usbd_device_handle dev) { usb_hub_descriptor_t *hd = &dev->hub->hubdesc; device_t self = dev->hub->hubdev; struct uhub_softc *sc = device_get_softc(self); struct usbd_port *up; usbd_status err; int speed; int port; int change, status; DPRINTFN(10, ("uhub_explore dev=%p addr=%d\n", dev, dev->address)); if (!sc->sc_running) return (USBD_NOT_STARTED); /* Ignore hubs that are too deep. */ if (dev->depth > USB_HUB_MAX_DEPTH) return (USBD_TOO_DEEP); for(port = 1; port <= hd->bNbrPorts; port++) { up = &dev->hub->ports[port-1]; err = usbd_get_port_status(dev, port, &up->status); if (err) { DPRINTF(("uhub_explore: get port status failed, " "error=%s\n", usbd_errstr(err))); continue; } status = UGETW(up->status.wPortStatus); change = UGETW(up->status.wPortChange); DPRINTFN(3,("uhub_explore: %s port %d status 0x%04x 0x%04x\n", device_get_nameunit(self), port, status, change)); if (change & UPS_C_PORT_ENABLED) { DPRINTF(("uhub_explore: C_PORT_ENABLED 0x%x\n", change)); usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE); if (change & UPS_C_CONNECT_STATUS) { /* Ignore the port error if the device vanished. */ } else if (status & UPS_PORT_ENABLED) { device_printf(self, "illegal enable change, port %d\n", port); } else { /* Port error condition. */ if (up->restartcnt) /* no message first time */ device_printf(self, "port error, restarting " "port %d\n", port); if (up->restartcnt++ < USBD_RESTART_MAX) goto disco; else device_printf(self, "port error, giving up " "port %d\n", port); } } if (!(change & UPS_C_CONNECT_STATUS)) { DPRINTFN(3,("uhub_explore: port=%d !C_CONNECT_" "STATUS\n", port)); /* No status change, just do recursive explore. */ if (up->device != NULL && up->device->hub != NULL) up->device->hub->explore(up->device); #if 0 && defined(DIAGNOSTIC) if (up->device == NULL && (status & UPS_CURRENT_CONNECT_STATUS)) device_printf(self, "connected, no device\n"); #endif continue; } /* We have a connect status change, handle it. */ DPRINTF(("uhub_explore: status change hub=%d port=%d\n", dev->address, port)); usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION); /*usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);*/ /* * 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 */ DPRINTF(("uhub_explore: device addr=%d disappeared " "on port %d\n", up->device->address, port)); usb_disconnect_port(up, self); usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION); } if (!(status & UPS_CURRENT_CONNECT_STATUS)) { /* Nothing connected, just ignore it. */ DPRINTFN(3,("uhub_explore: port=%d !CURRENT_CONNECT" "_STATUS\n", port)); continue; } /* Connected */ if (!(status & UPS_PORT_POWER)) device_printf(self, "strange, connected port %d has no power\n", port); /* 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, &up->status)) { device_printf(self, "port %d reset failed\n", port); continue; } /* Get port status again, it might have changed during reset */ err = usbd_get_port_status(dev, port, &up->status); if (err) { DPRINTF(("uhub_explore: get port status failed, " "error=%s\n", usbd_errstr(err))); continue; } status = UGETW(up->status.wPortStatus); change = UGETW(up->status.wPortChange); if (!(status & UPS_CURRENT_CONNECT_STATUS)) { /* Nothing connected, just ignore it. */ #ifdef DIAGNOSTIC device_printf(self, "port %d, device disappeared after reset\n", port); #endif continue; } #if 0 if (UHUB_IS_HIGH_SPEED(sc) && !(status & UPS_HIGH_SPEED)) { device_printf(self, "port %d, transaction translation not " "implemented, low/full speed device ignored\n", port); continue; } #endif /* Figure out device speed */ if (status & UPS_HIGH_SPEED) speed = USB_SPEED_HIGH; else if (status & UPS_LOW_SPEED) speed = USB_SPEED_LOW; else speed = USB_SPEED_FULL; /* Get device info and set its address. */ err = usbd_new_device(self, dev->bus, dev->depth + 1, speed, port, up); /* XXX retry a few times? */ if (err) { DPRINTFN(-1,("uhub_explore: usb_new_device failed, " "error=%s\n", 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. */ device_printf(self, "device problem (%s), disabling port %d\n", usbd_errstr(err), 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); } } return (USBD_NORMAL_COMPLETION); }
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); }
int uhub_explore(struct usbd_device *dev) { struct uhub_softc *sc = dev->hub->hubsoftc; struct usbd_port *up; int status, change; int port; if (usbd_is_dying(sc->sc_hub)) return (EIO); if (!sc->sc_running) return (ENXIO); /* Ignore hubs that are too deep. */ if (sc->sc_hub->depth > USB_HUB_MAX_DEPTH) return (EOPNOTSUPP); for (port = 1; port <= sc->sc_hub->hub->nports; port++) { up = &sc->sc_hub->hub->ports[port-1]; change = 0; status = 0; if ((sc->sc_status & (1 << port)) || up->reattach) { sc->sc_status &= ~(1 << port); 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 (up->reattach) { change |= UPS_C_CONNECT_STATUS; up->reattach = 0; } if (change & UPS_C_PORT_ENABLED) { 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. */ } else if (status & UPS_PORT_ENABLED) { printf("%s: illegal enable change, port %d\n", sc->sc_dev.dv_xname, port); } else { /* Port error condition. */ if (up->restartcnt) /* no message first time */ printf("%s: port error, restarting " "port %d\n", sc->sc_dev.dv_xname, port); if (up->restartcnt++ < USBD_RESTART_MAX) change |= UPS_C_CONNECT_STATUS; else printf("%s: port error, giving up " "port %d\n", sc->sc_dev.dv_xname, port); } } if (change & UPS_C_CONNECT_STATUS) { if (uhub_port_connect(sc, port, status, change)) continue; /* The port set up succeeded, reset error count. */ up->restartcnt = 0; } 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); }
/* **************************************************************** * Varre as portas de uma unidade * **************************************************************** */ int uhub_explore (struct usbd_device *dev) { struct usb_hub_descriptor *hd = &dev->hub->hubdesc; struct uhub_softc *sc = dev->hub->hubsoftc; struct usbd_port *up; int err, speed; int port, change, status; #ifdef USB_MSG printf ("uhub_explore (%s)\n", sc->sc_dev->nameunit); printf ("uhub_explore: dev= %P addr = %d\n", dev, dev->address); #endif USB_MSG if (sc->sc_running == 0) return (USBD_NOT_STARTED); if (dev->depth > USB_HUB_MAX_DEPTH) return (USBD_TOO_DEEP); for (port = 1; port <= hd->bNbrPorts; port++) { up = &dev->hub->ports[port - 1]; if (err = usbd_get_port_status (dev, port, &up->status)) { printf ( "uhub_explore (%s): erro ao ler o estado da porta %d (%s)\n", sc->sc_dev->nameunit, port, usbd_errstr (err) ); continue; } status = UGETW (up->status.wPortStatus); change = UGETW (up->status.wPortChange); #ifdef USB_MSG printf ( "uhub_explore (%s) porta %d status = 0x%04X change = 0x%04X\n", sc->sc_dev->nameunit, port, status, change ); #endif USB_MSG if (change & UPS_C_PORT_ENABLED) { #ifdef USB_MSG printf ("uhub_explore: C_PORT_ENABLED\n"); #endif USB_MSG usbd_clear_port_feature (dev, port, UHF_C_PORT_ENABLE); if (change & UPS_C_CONNECT_STATUS) { /* Ignore the port error if the device vanished. */ #ifdef USB_MSG printf ( "uhub_explore (%s): ignorando erro, porta %d\n", sc->sc_dev->nameunit, port ); #endif USB_MSG } elif (status & UPS_PORT_ENABLED) { printf ( "uhub_explore (%s): mudança inválida de habilitação, porta %d\n", sc->sc_dev->nameunit, port ); } else { /* Port error condition */ if (up->restartcnt) /* no message first time */