From d18484417b6d2f8acc96a6660af0dfc2e171dad6 Mon Sep 17 00:00:00 2001 From: David Gwynne Date: Thu, 9 Dec 2004 11:49:56 +0000 Subject: code to let us cause a detach and reattach of a device from software ok deraadt@ --- sys/dev/usb/uhub.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++- sys/dev/usb/usbdivar.h | 3 +- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/sys/dev/usb/uhub.c b/sys/dev/usb/uhub.c index f7e0b5bc69b..56033142051 100644 --- a/sys/dev/usb/uhub.c +++ b/sys/dev/usb/uhub.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uhub.c,v 1.26 2004/07/11 05:32:38 deraadt Exp $ */ +/* $OpenBSD: uhub.c,v 1.27 2004/12/09 11:49:55 dlg 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 $ */ @@ -83,6 +83,7 @@ struct uhub_softc { }; Static usbd_status uhub_explore(usbd_device_handle hub); +Static usbd_status uhub_reset_device(usbd_device_handle, int); Static void uhub_intr(usbd_xfer_handle, usbd_private_handle,usbd_status); #if defined(__FreeBSD__) @@ -211,6 +212,7 @@ USB_ATTACH(uhub) dev->hub = hub; dev->hub->hubsoftc = sc; hub->explore = uhub_explore; + hub->reset_device = uhub_reset_device; hub->hubdesc = hubdesc; DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, " @@ -323,6 +325,84 @@ USB_ATTACH(uhub) USB_ATTACH_ERROR_RETURN; } +Static usbd_status +uhub_reset_device(usbd_device_handle dev, int port) +{ + struct uhub_softc *sc = dev->hub->hubsoftc; + struct usbd_port *up; + int status, change; + int speed; + usbd_status err; + + DPRINTFN(10, ("uhub_reset_device port=%d\n", port)); + + if (!sc->sc_running) + return (USBD_NOT_STARTED); + + up = &dev->hub->ports[port-1]; + + /* Disconnect */ + DPRINTF(("uhub_reset_device: disconnecting device on port %d\n", port)); + usb_disconnect_port(up, USBDEV(sc->sc_dev)); + usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION); + + /* Wait for maximum device power up time. */ + usbd_delay_ms(dev, USB_PORT_POWERUP_DELAY); + + /* Reset port, which implies enabling it. */ + err = usbd_reset_port(dev, port, &up->status); + if (err) { + printf("%s: port %d reset failed\n", + USBDEVNAME(sc->sc_dev), port); + return(err); + } + + /* connect */ + err = usbd_get_port_status(dev, port, &up->status); + if (err) { + DPRINTF(("uhub_reset_device: get port status failed, " + "error=%s\n", usbd_errstr(err))); + return (err); + } + status = UGETW(up->status.wPortStatus); + change = UGETW(up->status.wPortChange); + + /* 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(USBDEV(sc->sc_dev), 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. + */ + printf("%s: device problem, disabling port %d\n", + USBDEVNAME(sc->sc_dev), 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); +} + usbd_status uhub_explore(usbd_device_handle dev) { diff --git a/sys/dev/usb/usbdivar.h b/sys/dev/usb/usbdivar.h index fa323578859..be9534447f7 100644 --- a/sys/dev/usb/usbdivar.h +++ b/sys/dev/usb/usbdivar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: usbdivar.h,v 1.19 2003/07/08 13:19:09 nate Exp $ */ +/* $OpenBSD: usbdivar.h,v 1.20 2004/12/09 11:49:55 dlg Exp $ */ /* $NetBSD: usbdivar.h,v 1.70 2002/07/11 21:14:36 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbdivar.h,v 1.11 1999/11/17 22:33:51 n_hibma Exp $ */ @@ -86,6 +86,7 @@ struct usbd_port { struct usbd_hub { usbd_status (*explore)(usbd_device_handle hub); + usbd_status (*reset_device)(usbd_device_handle, int); void *hubsoftc; usb_hub_descriptor_t hubdesc; struct usbd_port ports[1]; -- cgit v1.2.3