Diff for /src/sys/bus/usb/uhub.c between versions 1.3 and 1.4

version 1.3, 2003/08/07 21:16:47 version 1.4, 2003/12/30 01:01:44
Line 1 Line 1
/*        $NetBSD: uhub.c,v 1.47 2000/09/24 02:08:38 augustss Exp $  *//*
/*        $FreeBSD: src/sys/dev/usb/uhub.c,v 1.21.2.7 2002/11/06 20:23:50 joe Exp $  */ * $NetBSD: uhub.c,v 1.64 2003/02/08 03:32:51 ichiro Exp $
/*        $DragonFly$     */ * $FreeBSD: src/sys/dev/usb/uhub.c,v 1.54 2003/08/24 17:55:55 obrien Exp $
  * $DragonFly$
  */
   
 /*  /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.   * Copyright (c) 1998 The NetBSD Foundation, Inc.
Line 40 Line 42
  */   */
   
 /*  /*
 * USB spec: http://www.usb.org/developers/docs.htm * USB spec: http://www.usb.org/developers/docs/usbspec.zip
  */   */
   
 #include <sys/param.h>  #include <sys/param.h>
Line 54 Line 56
 #include <sys/module.h>  #include <sys/module.h>
 #include <sys/bus.h>  #include <sys/bus.h>
 #include "bus_if.h"  #include "bus_if.h"
 #include <sys/sysctl.h>  
 #endif  #endif
   #include <sys/sysctl.h>
   
 #include <machine/bus.h>  #include <machine/bus.h>
   
Line 69 Line 71
 #ifdef USB_DEBUG  #ifdef USB_DEBUG
 #define DPRINTF(x)      if (uhubdebug) logprintf x  #define DPRINTF(x)      if (uhubdebug) logprintf x
 #define DPRINTFN(n,x)   if (uhubdebug>(n)) logprintf x  #define DPRINTFN(n,x)   if (uhubdebug>(n)) logprintf x
static int    uhubdebug = 0;int     uhubdebug = 0;
 SYSCTL_NODE(_hw_usb, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB uhub");  SYSCTL_NODE(_hw_usb, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB uhub");
 SYSCTL_INT(_hw_usb_uhub, OID_AUTO, debug, CTLFLAG_RW,  SYSCTL_INT(_hw_usb_uhub, OID_AUTO, debug, CTLFLAG_RW,
        &uhubdebug, 0, "uhub debug level");           &uhubdebug, 0, "uhub debug level");
 #else  #else
 #define DPRINTF(x)  #define DPRINTF(x)
 #define DPRINTFN(n,x)  #define DPRINTFN(n,x)
Line 90  Static usbd_status uhub_explore(usbd_dev Line 92  Static usbd_status uhub_explore(usbd_dev
 Static void uhub_intr(usbd_xfer_handle, usbd_private_handle,usbd_status);  Static void uhub_intr(usbd_xfer_handle, usbd_private_handle,usbd_status);
   
 #if defined(__FreeBSD__)  #if defined(__FreeBSD__)
   Static bus_driver_added_t uhub_driver_added;
 Static bus_child_detached_t uhub_child_detached;  Static bus_child_detached_t uhub_child_detached;
 #endif  #endif
   
   
/* /*
  * We need two attachment points:   * We need two attachment points:
  * hub to usb and hub to hub   * hub to usb and hub to hub
  * Every other driver only connects to hubs   * Every other driver only connects to hubs
Line 104  Static bus_child_detached_t uhub_child_d Line 107  Static bus_child_detached_t uhub_child_d
 USB_DECLARE_DRIVER(uhub);  USB_DECLARE_DRIVER(uhub);
   
 /* Create the driver instance for the hub connected to hub case */  /* Create the driver instance for the hub connected to hub case */
struct cfattach uhub_uhub_ca = {CFATTACH_DECL(uhub_uhub, sizeof(struct uhub_softc),
        sizeof(struct uhub_softc), uhub_match, uhub_attach,    uhub_match, uhub_attach, uhub_detach, uhub_activate);
        uhub_detach, uhub_activate 
}; 
 #elif defined(__FreeBSD__)  #elif defined(__FreeBSD__)
 USB_DECLARE_DRIVER_INIT(uhub,  USB_DECLARE_DRIVER_INIT(uhub,
                           DEVMETHOD(bus_driver_added, uhub_driver_added),
                         DEVMETHOD(bus_child_detached, uhub_child_detached),                          DEVMETHOD(bus_child_detached, uhub_child_detached),
                         DEVMETHOD(device_suspend, bus_generic_suspend),                          DEVMETHOD(device_suspend, bus_generic_suspend),
                         DEVMETHOD(device_resume, bus_generic_resume),                          DEVMETHOD(device_resume, bus_generic_resume),
                         DEVMETHOD(device_shutdown, bus_generic_shutdown)                          DEVMETHOD(device_shutdown, bus_generic_shutdown)
                         );                          );
                        
 /* Create the driver instance for the hub connected to usb case. */  /* Create the driver instance for the hub connected to usb case. */
 devclass_t uhubroot_devclass;  devclass_t uhubroot_devclass;
   
 Static device_method_t uhubroot_methods[] = {  Static device_method_t uhubroot_methods[] = {
         DEVMETHOD(device_probe, uhub_match),          DEVMETHOD(device_probe, uhub_match),
         DEVMETHOD(device_attach, uhub_attach),          DEVMETHOD(device_attach, uhub_attach),
   
         /* detach is not allowed for a root hub */          /* detach is not allowed for a root hub */
         DEVMETHOD(device_suspend, bus_generic_suspend),          DEVMETHOD(device_suspend, bus_generic_suspend),
         DEVMETHOD(device_resume, bus_generic_resume),          DEVMETHOD(device_resume, bus_generic_resume),
Line 140  USB_MATCH(uhub) Line 143  USB_MATCH(uhub)
 {  {
         USB_MATCH_START(uhub, uaa);          USB_MATCH_START(uhub, uaa);
         usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);          usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);
        
         DPRINTFN(5,("uhub_match, dd=%p\n", dd));          DPRINTFN(5,("uhub_match, dd=%p\n", dd));
        /*         /*
          * The subclass for hubs seems to be 0 for some and 1 for others,           * The subclass for hubs seems to be 0 for some and 1 for others,
          * so we just ignore the subclass.           * so we just ignore the subclass.
          */           */
Line 155  USB_ATTACH(uhub) Line 158  USB_ATTACH(uhub)
 {  {
         USB_ATTACH_START(uhub, sc, uaa);          USB_ATTACH_START(uhub, sc, uaa);
         usbd_device_handle dev = uaa->device;          usbd_device_handle dev = uaa->device;
        char devinfo[1024];        char *devinfo;
         usbd_status err;          usbd_status err;
         struct usbd_hub *hub;          struct usbd_hub *hub;
         usb_device_request_t req;          usb_device_request_t req;
Line 163  USB_ATTACH(uhub) Line 166  USB_ATTACH(uhub)
         int p, port, nports, nremov, pwrdly;          int p, port, nports, nremov, pwrdly;
         usbd_interface_handle iface;          usbd_interface_handle iface;
         usb_endpoint_descriptor_t *ed;          usb_endpoint_descriptor_t *ed;
        
         devinfo = malloc(1024, M_TEMP, M_NOWAIT);
         if (devinfo == NULL) {
                 USB_ATTACH_ERROR_RETURN;
         }
         DPRINTFN(1,("uhub_attach\n"));          DPRINTFN(1,("uhub_attach\n"));
         sc->sc_hub = dev;          sc->sc_hub = dev;
         usbd_devinfo(dev, 1, devinfo);          usbd_devinfo(dev, 1, devinfo);
Line 174  USB_ATTACH(uhub) Line 181  USB_ATTACH(uhub)
         if (err) {          if (err) {
                 DPRINTF(("%s: configuration failed, error=%s\n",                  DPRINTF(("%s: configuration failed, error=%s\n",
                          USBDEVNAME(sc->sc_dev), usbd_errstr(err)));                           USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
                   free(devinfo, M_TEMP);
                 USB_ATTACH_ERROR_RETURN;                  USB_ATTACH_ERROR_RETURN;
         }          }
   
         if (dev->depth > USB_HUB_MAX_DEPTH) {          if (dev->depth > USB_HUB_MAX_DEPTH) {
                 printf("%s: hub depth (%d) exceeded, hub ignored\n",                  printf("%s: hub depth (%d) exceeded, hub ignored\n",
                        USBDEVNAME(sc->sc_dev), USB_HUB_MAX_DEPTH);                         USBDEVNAME(sc->sc_dev), USB_HUB_MAX_DEPTH);
                   free(devinfo, M_TEMP);
                 USB_ATTACH_ERROR_RETURN;                  USB_ATTACH_ERROR_RETURN;
         }          }
   
         /* Get hub descriptor. */          /* Get hub descriptor. */
         req.bmRequestType = UT_READ_CLASS_DEVICE;          req.bmRequestType = UT_READ_CLASS_DEVICE;
         req.bRequest = UR_GET_DESCRIPTOR;          req.bRequest = UR_GET_DESCRIPTOR;
        USETW(req.wValue, 0);        USETW2(req.wValue, (dev->address > 1 ? UDESC_HUB : 0), 0);
         USETW(req.wIndex, 0);          USETW(req.wIndex, 0);
         USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);          USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
         DPRINTFN(1,("usb_init_hub: getting hub descriptor\n"));          DPRINTFN(1,("usb_init_hub: getting hub descriptor\n"));
Line 199  USB_ATTACH(uhub) Line 208  USB_ATTACH(uhub)
         if (err) {          if (err) {
                 DPRINTF(("%s: getting hub descriptor failed, error=%s\n",                  DPRINTF(("%s: getting hub descriptor failed, error=%s\n",
                          USBDEVNAME(sc->sc_dev), usbd_errstr(err)));                           USBDEVNAME(sc->sc_dev), usbd_errstr(err)));
                   free(devinfo, M_TEMP);
                 USB_ATTACH_ERROR_RETURN;                  USB_ATTACH_ERROR_RETURN;
         }          }
   
Line 211  USB_ATTACH(uhub) Line 221  USB_ATTACH(uhub)
   
         hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),          hub = malloc(sizeof(*hub) + (nports-1) * sizeof(struct usbd_port),
                      M_USBDEV, M_NOWAIT);                       M_USBDEV, M_NOWAIT);
        if (hub == NULL)        if (hub == NULL) {
                 free(devinfo, M_TEMP);
                 USB_ATTACH_ERROR_RETURN;                  USB_ATTACH_ERROR_RETURN;
           }
         dev->hub = hub;          dev->hub = hub;
         dev->hub->hubsoftc = sc;          dev->hub->hubsoftc = sc;
         hub->explore = uhub_explore;          hub->explore = uhub_explore;
         hub->hubdesc = hubdesc;          hub->hubdesc = hubdesc;
        
         DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, "          DPRINTFN(1,("usbhub_init_hub: selfpowered=%d, parent=%p, "
                     "parent->selfpowered=%d\n",                      "parent->selfpowered=%d\n",
                  dev->self_powered, dev->powersrc->parent,                   dev->self_powered, dev->powersrc->parent,
                 dev->powersrc->parent ?                  dev->powersrc->parent ?
                  dev->powersrc->parent->self_powered : 0));                   dev->powersrc->parent->self_powered : 0));
   
         if (!dev->self_powered && dev->powersrc->parent != NULL &&          if (!dev->self_powered && dev->powersrc->parent != NULL &&
Line 248  USB_ATTACH(uhub) Line 260  USB_ATTACH(uhub)
         }          }
   
         err = usbd_open_pipe_intr(iface, ed->bEndpointAddress,          err = usbd_open_pipe_intr(iface, ed->bEndpointAddress,
                  USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_status,                   USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_status,
                   sizeof(sc->sc_status), uhub_intr, UHUB_INTR_INTERVAL);                    sizeof(sc->sc_status), uhub_intr, UHUB_INTR_INTERVAL);
         if (err) {          if (err) {
                printf("%s: cannot open interrupt pipe\n",                 printf("%s: cannot open interrupt pipe\n",
                        USBDEVNAME(sc->sc_dev));                         USBDEVNAME(sc->sc_dev));
                 goto bad;                  goto bad;
         }          }
Line 259  USB_ATTACH(uhub) Line 271  USB_ATTACH(uhub)
         /* Wait with power off for a while. */          /* Wait with power off for a while. */
         usbd_delay_ms(dev, USB_POWER_DOWN_TIME);          usbd_delay_ms(dev, USB_POWER_DOWN_TIME);
   
           usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, dev, USBDEV(sc->sc_dev));
   
         /*          /*
          * To have the best chance of success we do things in the exact same           * To have the best chance of success we do things in the exact same
          * order as Windoze98.  This should not be necessary, but some           * order as Windoze98.  This should not be necessary, but some
Line 276  USB_ATTACH(uhub) Line 290  USB_ATTACH(uhub)
          *  For all ports           *  For all ports
          *     get port status           *     get port status
          *     if device connected           *     if device connected
            *        wait 100 ms
          *        turn on reset           *        turn on reset
          *        wait           *        wait
          *        clear C_PORT_RESET           *        clear C_PORT_RESET
Line 304  USB_ATTACH(uhub) Line 319  USB_ATTACH(uhub)
                 /* Turn the power on. */                  /* Turn the power on. */
                 err = usbd_set_port_feature(dev, port, UHF_PORT_POWER);                  err = usbd_set_port_feature(dev, port, UHF_PORT_POWER);
                 if (err)                  if (err)
                        printf("%s: port %d power on failed, %s\n",                         printf("%s: port %d power on failed, %s\n",
                                USBDEVNAME(sc->sc_dev), port,                                 USBDEVNAME(sc->sc_dev), port,
                                usbd_errstr(err));                                 usbd_errstr(err));
                 DPRINTF(("usb_init_port: turn on port %d power\n", port));                  DPRINTF(("usb_init_port: turn on port %d power\n", port));
Line 320  USB_ATTACH(uhub) Line 335  USB_ATTACH(uhub)
   
  bad:   bad:
         free(hub, M_USBDEV);          free(hub, M_USBDEV);
           free(devinfo, M_TEMP);
         dev->hub = 0;          dev->hub = 0;
         USB_ATTACH_ERROR_RETURN;          USB_ATTACH_ERROR_RETURN;
 }  }
Line 331  uhub_explore(usbd_device_handle dev) Line 347  uhub_explore(usbd_device_handle dev)
         struct uhub_softc *sc = dev->hub->hubsoftc;          struct uhub_softc *sc = dev->hub->hubsoftc;
         struct usbd_port *up;          struct usbd_port *up;
         usbd_status err;          usbd_status err;
           int speed;
         int port;          int port;
         int change, status;          int change, status;
   
Line 353  uhub_explore(usbd_device_handle dev) Line 370  uhub_explore(usbd_device_handle dev)
                 }                  }
                 status = UGETW(up->status.wPortStatus);                  status = UGETW(up->status.wPortStatus);
                 change = UGETW(up->status.wPortChange);                  change = UGETW(up->status.wPortChange);
                DPRINTFN(3,("uhub_explore: port %d status 0x%04x 0x%04x\n",                DPRINTFN(3,("uhub_explore: %s port %d status 0x%04x 0x%04x\n",
                            port, status, change));                            USBDEVNAME(sc->sc_dev), port, status, change));
                 if (change & UPS_C_PORT_ENABLED) {                  if (change & UPS_C_PORT_ENABLED) {
                         DPRINTF(("uhub_explore: C_PORT_ENABLED\n"));                          DPRINTF(("uhub_explore: C_PORT_ENABLED\n"));
                         usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);                          usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
Line 380  uhub_explore(usbd_device_handle dev) Line 397  uhub_explore(usbd_device_handle dev)
                         DPRINTFN(3,("uhub_explore: port=%d !C_CONNECT_"                          DPRINTFN(3,("uhub_explore: port=%d !C_CONNECT_"
                                     "STATUS\n", port));                                      "STATUS\n", port));
                         /* No status change, just do recursive explore. */                          /* No status change, just do recursive explore. */
                        if (up->device && up->device->hub)                        if (up->device != NULL && up->device->hub != NULL)
                                 up->device->hub->explore(up->device);                                  up->device->hub->explore(up->device);
   #if 0 && defined(DIAGNOSTIC)
                           if (up->device == NULL &&
                               (status & UPS_CURRENT_CONNECT_STATUS))
                                   printf("%s: connected, no device\n",
                                          USBDEVNAME(sc->sc_dev));
   #endif
                         continue;                          continue;
                 }                  }
   
Line 404  uhub_explore(usbd_device_handle dev) Line 427  uhub_explore(usbd_device_handle dev)
                         DPRINTF(("uhub_explore: device addr=%d disappeared "                          DPRINTF(("uhub_explore: device addr=%d disappeared "
                                  "on port %d\n", up->device->address, port));                                   "on port %d\n", up->device->address, port));
                         usb_disconnect_port(up, USBDEV(sc->sc_dev));                          usb_disconnect_port(up, USBDEV(sc->sc_dev));
                        usbd_clear_port_feature(dev, port,                         usbd_clear_port_feature(dev, port,
                                                 UHF_C_PORT_CONNECTION);                                                  UHF_C_PORT_CONNECTION);
                 }                  }
                 if (!(status & UPS_CURRENT_CONNECT_STATUS)) {                  if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
Line 425  uhub_explore(usbd_device_handle dev) Line 448  uhub_explore(usbd_device_handle dev)
   
                 /* Reset port, which implies enabling it. */                  /* Reset port, which implies enabling it. */
                 if (usbd_reset_port(dev, port, &up->status)) {                  if (usbd_reset_port(dev, port, &up->status)) {
                        printf("uhub_explore: port=%d reset failed\n",                        printf("%s: port %d reset failed\n",
                                 port);                               USBDEVNAME(sc->sc_dev), 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
                         printf("%s: port %d, device disappeared after reset\n",
                                USBDEVNAME(sc->sc_dev), port);
 #endif
                         continue;                          continue;
                 }                  }
   
                   /* 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. */                  /* Get device info and set its address. */
                err = usbd_new_device(USBDEV(sc->sc_dev), dev->bus,                 err = usbd_new_device(USBDEV(sc->sc_dev), dev->bus,
                          dev->depth + 1, status & UPS_LOW_SPEED,                     dev->depth + 1, speed, port, up);
                          port, up); 
                 /* XXX retry a few times? */                  /* XXX retry a few times? */
                 if (err) {                  if (err) {
                         DPRINTFN(-1,("uhub_explore: usb_new_device failed, "                          DPRINTFN(-1,("uhub_explore: usb_new_device failed, "
Line 441  uhub_explore(usbd_device_handle dev) Line 487  uhub_explore(usbd_device_handle dev)
                         /* Avoid addressing problems by disabling. */                          /* Avoid addressing problems by disabling. */
                         /* usbd_reset_port(dev, port, &up->status); */                          /* usbd_reset_port(dev, port, &up->status); */
   
                        /*                         /*
                          * The unit refused to accept a new address, or had                           * The unit refused to accept a new address, or had
                          * some other serious problem.  Since we cannot leave                           * some other serious problem.  Since we cannot leave
                          * at 0 we have to disable the port instead.                           * at 0 we have to disable the port instead.
Line 472  uhub_activate(device_ptr_t self, enum de Line 518  uhub_activate(device_ptr_t self, enum de
         switch (act) {          switch (act) {
         case DVACT_ACTIVATE:          case DVACT_ACTIVATE:
                 return (EOPNOTSUPP);                  return (EOPNOTSUPP);
                 break;  
   
         case DVACT_DEACTIVATE:          case DVACT_DEACTIVATE:
                 if (hub == NULL) /* malfunctioning hub */                  if (hub == NULL) /* malfunctioning hub */
Line 520  USB_DETACH(uhub) Line 565  USB_DETACH(uhub)
                 if (rup->device)                  if (rup->device)
                         usb_disconnect_port(rup, self);                          usb_disconnect_port(rup, self);
         }          }
        
         usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_hub,
                            USBDEV(sc->sc_dev));
   
         free(hub, M_USBDEV);          free(hub, M_USBDEV);
         sc->sc_hub->hub = NULL;          sc->sc_hub->hub = NULL;
Line 540  uhub_child_detached(device_t self, devic Line 587  uhub_child_detached(device_t self, devic
        int port;         int port;
        int i;         int i;
   
       if (!devhub->hub)         if (!devhub->hub)
                /* should never happen; children are only created after init */                 /* should never happen; children are only created after init */
                panic("hub not fully initialised, but child deleted?");                 panic("hub not fully initialised, but child deleted?");
   
Line 557  uhub_child_detached(device_t self, devic Line 604  uhub_child_detached(device_t self, devic
                }                 }
        }         }
 }  }
   
   Static void
   uhub_driver_added(device_t _dev, driver_t *_driver)
   {
           /* Don't do anything, as reprobing does not work currently. We should
            * really call through to usbd_new_device or a function along those
            * lines that reinitialises the device if it is not owned by any
            * driver. But this is complicated. Manual replugging by the user is
            * easier.
            */
   
            ;
   }
 #endif  #endif
   
   
Line 572  uhub_intr(usbd_xfer_handle xfer, usbd_pr Line 632  uhub_intr(usbd_xfer_handle xfer, usbd_pr
         struct uhub_softc *sc = addr;          struct uhub_softc *sc = addr;
   
         DPRINTFN(5,("uhub_intr: sc=%p\n", sc));          DPRINTFN(5,("uhub_intr: sc=%p\n", sc));
        if (status != USBD_NORMAL_COMPLETION)        if (status == USBD_STALLED)
                 usbd_clear_endpoint_stall_async(sc->sc_ipipe);                  usbd_clear_endpoint_stall_async(sc->sc_ipipe);
        else if (status == USBD_NORMAL_COMPLETION)
        usb_needs_explore(sc->sc_hub->bus);                usb_needs_explore(sc->sc_hub);
 }  }
   
 #if defined(__FreeBSD__)  #if defined(__FreeBSD__)

Removed from v.1.3  
changed lines
  Added in v.1.4