static int ugen_do_port_feature(struct usb_fifo *f, uint8_t port_no, uint8_t set, uint16_t feature) { struct usb_device *udev = f->udev; struct usb_hub *hub; int err; err = priv_check(curthread, PRIV_DRIVER); if (err) { return (err); } if (port_no == 0) { return (EINVAL); } if ((udev == NULL) || (udev->hub == NULL)) { return (EINVAL); } hub = udev->hub; if (port_no > hub->nports) { return (EINVAL); } if (set) err = usbd_req_set_port_feature(udev, NULL, port_no, feature); else err = usbd_req_clear_port_feature(udev, NULL, port_no, feature); if (err) return (ENXIO); /* failure */ return (0); /* success */ }
static int ugen_set_power_mode(struct usb_fifo *f, int mode) { struct usb_device *udev = f->udev; int err; uint8_t old_mode; if ((udev == NULL) || (udev->parent_hub == NULL)) { return (EINVAL); } err = priv_check(curthread, PRIV_DRIVER); if (err) return (err); /* get old power mode */ old_mode = udev->power_mode; /* if no change, then just return */ if (old_mode == mode) return (0); switch (mode) { case USB_POWER_MODE_OFF: /* get the device unconfigured */ err = ugen_set_config(f, USB_UNCONFIG_INDEX); if (err) { DPRINTFN(0, "Could not unconfigure " "device (ignored)\n"); } /* clear port enable */ err = usbd_req_clear_port_feature(udev->parent_hub, NULL, udev->port_no, UHF_PORT_ENABLE); break; case USB_POWER_MODE_ON: case USB_POWER_MODE_SAVE: break; case USB_POWER_MODE_RESUME: #if USB_HAVE_POWERD /* let USB-powerd handle resume */ USB_BUS_LOCK(udev->bus); udev->pwr_save.write_refs++; udev->pwr_save.last_xfer_time = ticks; USB_BUS_UNLOCK(udev->bus); /* set new power mode */ usbd_set_power_mode(udev, USB_POWER_MODE_SAVE); /* wait for resume to complete */ usb_pause_mtx(NULL, hz / 4); /* clear write reference */ USB_BUS_LOCK(udev->bus); udev->pwr_save.write_refs--; USB_BUS_UNLOCK(udev->bus); #endif mode = USB_POWER_MODE_SAVE; break; case USB_POWER_MODE_SUSPEND: #if USB_HAVE_POWERD /* let USB-powerd handle suspend */ USB_BUS_LOCK(udev->bus); udev->pwr_save.last_xfer_time = ticks - (256 * hz); USB_BUS_UNLOCK(udev->bus); #endif mode = USB_POWER_MODE_SAVE; break; default: return (EINVAL); } if (err) return (ENXIO); /* I/O failure */ /* if we are powered off we need to re-enumerate first */ if (old_mode == USB_POWER_MODE_OFF) { if (udev->flags.usb_mode == USB_MODE_HOST) { if (udev->re_enumerate_wait == 0) udev->re_enumerate_wait = 1; } /* set power mode will wake up the explore thread */ } /* set new power mode */ usbd_set_power_mode(udev, mode); return (0); /* success */ }
static int ugen_set_power_mode(struct usb_fifo *f, int mode) { struct usb_device *udev = f->udev; int err; uint8_t old_mode; if ((udev == NULL) || (udev->parent_hub == NULL)) { return (EINVAL); } err = priv_check(curthread, PRIV_DRIVER); if (err) return (err); /* get old power mode */ old_mode = udev->power_mode; /* if no change, then just return */ if (old_mode == mode) return (0); switch (mode) { case USB_POWER_MODE_OFF: /* get the device unconfigured */ err = ugen_set_config(f, USB_UNCONFIG_INDEX); if (err) { DPRINTFN(0, "Could not unconfigure " "device (ignored)\n"); } /* clear port enable */ err = usbd_req_clear_port_feature(udev->parent_hub, NULL, udev->port_no, UHF_PORT_ENABLE); break; case USB_POWER_MODE_ON: case USB_POWER_MODE_SAVE: break; case USB_POWER_MODE_RESUME: err = usbd_req_clear_port_feature(udev->parent_hub, NULL, udev->port_no, UHF_PORT_SUSPEND); mode = USB_POWER_MODE_SAVE; break; case USB_POWER_MODE_SUSPEND: err = usbd_req_set_port_feature(udev->parent_hub, NULL, udev->port_no, UHF_PORT_SUSPEND); mode = USB_POWER_MODE_SAVE; break; default: return (EINVAL); } if (err) return (ENXIO); /* I/O failure */ /* if we are powered off we need to re-enumerate first */ if (old_mode == USB_POWER_MODE_OFF) { err = ugen_re_enumerate(f); if (err) return (err); } /* set new power mode */ usbd_set_power_mode(udev, mode); return (0); /* success */ }
/*------------------------------------------------------------------------* * usbd_req_reset_port * * This function will instruct an USB HUB to perform a reset sequence * on the specified port number. * * Returns: * 0: Success. The USB device should now be at address zero. * Else: Failure. No USB device is present and the USB port should be * disabled. *------------------------------------------------------------------------*/ usb_error_t usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) { struct usb_port_status ps; usb_error_t err; uint16_t n; #if USB_DEBUG uint16_t pr_poll_delay; uint16_t pr_recovery_delay; #endif err = usbd_req_set_port_feature(udev, mtx, port, UHF_PORT_RESET); if (err) { goto done; } #if USB_DEBUG /* range check input parameters */ pr_poll_delay = usb_pr_poll_delay; if (pr_poll_delay < 1) { pr_poll_delay = 1; } else if (pr_poll_delay > 1000) { pr_poll_delay = 1000; } pr_recovery_delay = usb_pr_recovery_delay; if (pr_recovery_delay > 1000) { pr_recovery_delay = 1000; } #endif n = 0; while (1) { #if USB_DEBUG /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay)); n += pr_poll_delay; #else /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_DELAY)); n += USB_PORT_RESET_DELAY; #endif err = usbd_req_get_port_status(udev, mtx, &ps, port); if (err) { goto done; } /* if the device disappeared, just give up */ if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) { goto done; } /* check if reset is complete */ if (UGETW(ps.wPortChange) & UPS_C_PORT_RESET) { break; } /* check for timeout */ if (n > 1000) { n = 0; break; } } /* clear port reset first */ err = usbd_req_clear_port_feature( udev, mtx, port, UHF_C_PORT_RESET); if (err) { goto done; } /* check for timeout */ if (n == 0) { err = USB_ERR_TIMEOUT; goto done; } #if USB_DEBUG /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_recovery_delay)); #else /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_RECOVERY)); #endif done: DPRINTFN(2, "port %d reset returning error=%s\n", port, usbd_errstr(err)); return (err); }