Date: Wed, 12 Dec 2007 22:45:26 GMT From: Hans Petter Selasky <hselasky@FreeBSD.org> To: Perforce Change Reviews <perforce@FreeBSD.org> Subject: PERFORCE change 130745 for review Message-ID: <200712122245.lBCMjQPL078789@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=130745 Change 130745 by hselasky@hselasky_laptop001 on 2007/12/12 22:45:07 This commit is related to USB device side support. o In general: The code does almost the same like before only that some functions have been refactored. o Some small changes to support USB Device Mode. See use of "USB_MODE_DEVICE" in the code. o Number of ports is now stored in "hub->nports". Affected files ... .. //depot/projects/usb/src/sys/dev/usb/uhub.c#25 edit Differences ... ==== //depot/projects/usb/src/sys/dev/usb/uhub.c#25 (text+ko) ==== @@ -69,12 +69,17 @@ #define DPRINTF(...) do { } while (0) #endif +struct uhub_current_state { + uint16_t port_change; + uint16_t port_status; +}; + struct uhub_softc { + struct uhub_current_state sc_st;/* current state */ device_t sc_dev; /* base device */ struct usbd_device *sc_udev; /* USB device */ struct usbd_xfer *sc_xfer[2]; /* interrupt xfer */ uint8_t sc_flags; -#define UHUB_FLAG_RUNNING 0x01 #define UHUB_FLAG_INTR_STALL 0x02 uint8_t sc_name[32]; }; @@ -179,7 +184,8 @@ * event handler thread that we need * to be explored again: */ - usb_needs_explore(sc->sc_udev); + usb_needs_explore(sc->sc_udev->bus, + USB_BUS_EXPLORE_TREE); case USBD_ST_SETUP: if (sc->sc_flags & UHUB_FLAG_INTR_STALL) { @@ -200,225 +206,306 @@ } } -static struct usbd_device * -uhub_port_to_sub_device(struct usbd_device *udev, struct usbd_port *up) -{ - if ((udev == NULL) || (up == NULL)) { - /* be NULL safe */ - return (NULL); - } - if (up->device_addr == USB_START_ADDR) { - /* nothing to do */ - return (NULL); - } - return (udev->bus->devices[up->device_addr]); -} - +/*------------------------------------------------------------------------* + * uhub_explore_sub - subroutine + * + * Return values: + * 0: Success + * Else: A control transaction failed + *------------------------------------------------------------------------*/ static usbd_status_t -uhub_explore_sub(device_t dev, struct usbd_device *udev, struct usbd_port *up) +uhub_explore_sub(struct uhub_softc *sc, struct usbd_port *up) { + struct usbd_bus *bus; struct usbd_device *child; - uint8_t refcount = usb_driver_added_refcount; - usbd_status_t err = 0; + uint8_t refcount; + usbd_status_t err; + + bus = sc->sc_udev->bus; + err = 0; - child = uhub_port_to_sub_device(udev, up); + /* get driver added refcount from USB bus */ + refcount = bus->driver_added_refcount; + /* get device assosiated with the given port */ + child = usbd_bus_port_get_device(bus, up); if (child == NULL) { /* nothing to do */ - return (0); + goto done; } /* check if probe and attach should be done */ if (child->driver_added_refcount != refcount) { child->driver_added_refcount = refcount; - err = usbd_probe_and_attach(dev, child); + err = usbd_probe_and_attach(child, + USB_IFACE_INDEX_ANY); + if (err) { + goto done; + } + } + /* start control transfer, if device mode */ + + if (child->flags.usb_mode == USB_MODE_DEVICE) { + usbd_default_transfer_setup(child); } /* if a HUB becomes present, do a recursive HUB explore */ if (child->hub) { - (child->hub->explore) (child); + err = (child->hub->explore) (child); } +done: + return (err); +} + +/*------------------------------------------------------------------------* + * uhub_read_port_status - factored out code + *------------------------------------------------------------------------*/ +static usbd_status_t +uhub_read_port_status(struct uhub_softc *sc, uint8_t portno) +{ + usb_port_status_t ps; + usbd_status_t err; + + err = usbreq_get_port_status( + sc->sc_udev, &usb_global_lock, &ps, portno); + + /* update status regardless of error */ + + sc->sc_st.port_status = UGETW(ps.wPortStatus); + sc->sc_st.port_change = UGETW(ps.wPortChange); + + /* debugging print */ + + DPRINTF(sc, 3, "port %d, wPortStatus=0x%04x, " + "wPortChange=0x%04x, err=%s\n", + portno, sc->sc_st.port_status, + sc->sc_st.port_change, usbd_errstr(err)); return (err); } +/*------------------------------------------------------------------------* + * uhub_reattach_port + * + * Returns: + * 0: Success + * Else: A control transaction failed + *------------------------------------------------------------------------*/ static usbd_status_t -uhub_explore(struct usbd_device *udev) +uhub_reattach_port(struct uhub_softc *sc, uint8_t portno) { - usb_hub_descriptor_t *hd = &udev->hub->hubdesc; - struct uhub_softc *sc = udev->hub->hubsoftc; struct usbd_device *child; - struct usbd_port *up; + struct usbd_device *udev; usbd_status_t err; - uint16_t change; - uint16_t status; - uint8_t portno; + uint8_t timeout; uint8_t speed; - uint8_t x; + uint8_t usb_mode; + + DPRINTF(sc, 0, "reattaching port %d\n", portno); + + err = 0; + timeout = 0; + udev = sc->sc_udev; + child = usbd_bus_port_get_device(udev->bus, + udev->hub->ports + portno - 1); + +repeat: + + /* first clear the port connection change bit */ + + err = usbreq_clear_port_feature + (udev, &usb_global_lock, portno, UHF_C_PORT_CONNECTION); + + if (err) { + goto error; + } + /* detach any existing devices */ + + if (child) { + usbd_detach_device(child, USB_IFACE_INDEX_ANY, 1); + usbd_free_device(child); + child = NULL; + } + /* get fresh status */ - DPRINTF(sc, 10, "udev=%p addr=%d\n", udev, udev->address); + err = uhub_read_port_status(sc, portno); + if (err) { + goto error; + } + /* check if nothing is connected to the port */ - if (!(sc->sc_flags & UHUB_FLAG_RUNNING)) { - return (USBD_NOT_STARTED); + if (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS)) { + goto error; } - /* ignore hubs that are too deep */ - if (udev->depth > USB_HUB_MAX_DEPTH) { - return (USBD_TOO_DEEP); + /* check if there is no power on the port and print a warning */ + + if (!(sc->sc_st.port_status & UPS_PORT_POWER)) { + DPRINTF(sc, 0, "WARNING: strange, connected port %d " + "has no power\n", portno); } - for (x = 0; x < hd->bNbrPorts; x++) { - up = udev->hub->ports + x; - portno = x + 1; - err = usbreq_get_port_status - (udev, &usb_global_lock, &up->status, portno); - if (err) { - DPRINTF(sc, 0, "get port status failed, " - "error=%s\n", usbd_errstr(err)); - continue; - } - status = UGETW(up->status.wPortStatus); - change = UGETW(up->status.wPortChange); - DPRINTF(sc, 3, "port %d status 0x%04x 0x%04x\n", - portno, status, change); - if (change & UPS_C_PORT_ENABLED) { - DPRINTF(sc, 0, "C_PORT_ENABLED 0x%x\n", change); - usbreq_clear_port_feature - (udev, &usb_global_lock, portno, UHF_C_PORT_ENABLE); - if (change & UPS_C_CONNECT_STATUS) { - /* - * ignore the port error if the device - * vanished - */ - } else if (status & UPS_PORT_ENABLED) { - DPRINTF(sc, -1, "illegal enable change, " - "port %d\n", portno); - } else { - /* port error condition */ - if (up->restartcnt) { /* no message first time */ - DPRINTF(sc, -1, "port error, restarting " - "port %d\n", portno); - } - if (up->restartcnt++ < USBD_RESTART_MAX) { - goto disconnect; - } else { - DPRINTF(sc, -1, "port error, giving up " - "port %d\n", portno); - } - } - } - if (!(change & UPS_C_CONNECT_STATUS)) { - DPRINTF(sc, 3, "port=%d !C_CONNECT_" - "STATUS\n", portno); + /* check if the device is in Host Mode */ + + if (!(sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)) { - /* no status change, just do recursive explore */ - err = uhub_explore_sub(sc->sc_dev, udev, up); - continue; - } - /* we have a connect status change, handle it */ + DPRINTF(sc, 0, "Port %d is in Host Mode\n", portno); - DPRINTF(sc, 0, "status change hub=%d port=%d\n", - udev->address, portno); - usbreq_clear_port_feature - (udev, &usb_global_lock, portno, UHF_C_PORT_CONNECTION); - /* - * usbreq_clear_port_feature (udev, &usb_global_lock, - * portno, 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. - */ -disconnect: - child = uhub_port_to_sub_device(udev, up); - if (child) { - /* disconnected */ - DPRINTF(sc, 0, "device addr=%d disappeared " - "on port %d\n", child->address, - portno); - usbd_free_device(child, 1); - usbreq_clear_port_feature - (udev, &usb_global_lock, portno, UHF_C_PORT_CONNECTION); - } - if (!(status & UPS_CURRENT_CONNECT_STATUS)) { - /* nothing connected, just ignore it */ - DPRINTF(sc, 3, "port=%d !CURRENT_CONNECT_STATUS\n", - portno); - continue; - } - /* connected */ + /* USB Host Mode */ - if (!(status & UPS_PORT_POWER)) { - DPRINTF(sc, -1, "strange, connected port %d " - "has no power\n", portno); - } /* wait for maximum device power up time */ + usbd_delay_ms(udev, USB_PORT_POWERUP_DELAY); /* reset port, which implies enabling it */ + err = usbreq_reset_port - (udev, &usb_global_lock, &up->status, portno); + (udev, &usb_global_lock, portno); if (err) { DPRINTF(sc, -1, "port %d reset " "failed, error=%s\n", portno, usbd_errstr(err)); - continue; + goto error; } /* get port status again, it might have changed during reset */ - err = usbreq_get_port_status - (udev, &usb_global_lock, &up->status, portno); + err = uhub_read_port_status(sc, portno); if (err) { - DPRINTF(sc, 0, "get port status failed, " - "error=%s\n", usbd_errstr(err)); - continue; + goto error; + } + /* check if something changed during port reset */ + + if ((sc->sc_st.port_change & UPS_C_CONNECT_STATUS) || + (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS))) { + if (timeout) { + DPRINTF(sc, -1, "giving up port reset " + "- device vanished!\n"); + goto error; + } + timeout = 1; + goto repeat; } - status = UGETW(up->status.wPortStatus); - change = UGETW(up->status.wPortChange); - if (!(status & UPS_CURRENT_CONNECT_STATUS)) { - /* nothing connected, just ignore it */ - DPRINTF(sc, 1, "port %d, device disappeared " - "after reset\n", portno); - continue; + } else { + DPRINTF(sc, 0, "Port %d is in Device Mode\n", portno); + } + + /* + * Figure out the device speed + */ + speed = + (sc->sc_st.port_status & UPS_HIGH_SPEED) ? USB_SPEED_HIGH : + (sc->sc_st.port_status & UPS_LOW_SPEED) ? USB_SPEED_LOW : USB_SPEED_FULL; + + /* + * Figure out the device mode + * + * NOTE: This part is currently FreeBSD specific. + */ + usb_mode = + (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE) ? + USB_MODE_DEVICE : USB_MODE_HOST; + + /* need to create a new child */ + + child = usbd_alloc_device(sc->sc_dev, udev->bus, udev, + udev->depth + 1, portno - 1, portno, speed, usb_mode); + if (child == NULL) { + DPRINTF(sc, -1, "could not allocate new device!\n"); + goto error; + } + return (0); /* success */ + +error: + if (child) { + usbd_detach_device(child, USB_IFACE_INDEX_ANY, 1); + usbd_free_device(child); + child = NULL; + } + if (err == 0) { + err = usbreq_clear_port_feature + (sc->sc_udev, &usb_global_lock, portno, UHF_PORT_ENABLE); + } + if (err) { + DPRINTF(sc, -1, "device problem (%s), " + "disabling port %d\n", usbd_errstr(err), portno); + } + return (err); +} + +/*------------------------------------------------------------------------* + * uhub_explore + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static usbd_status_t +uhub_explore(struct usbd_device *udev) +{ + struct usbd_hub *hub; + struct uhub_softc *sc; + struct usbd_port *up; + usbd_status_t err; + uint8_t portno; + uint8_t x; + + hub = udev->hub; + sc = hub->hubsoftc; + + DPRINTF(sc, 10, "udev=%p addr=%d\n", udev, udev->address); + + /* ignore hubs that are too deep */ + if (udev->depth > USB_HUB_MAX_DEPTH) { + return (USBD_TOO_DEEP); + } + for (x = 0; x != hub->nports; x++) { + up = hub->ports + x; + portno = x + 1; + + err = uhub_read_port_status(sc, portno); + if (err) { + /* most likely the HUB is gone */ + break; } - /* figure out device speed */ - speed = - (status & UPS_HIGH_SPEED) ? USB_SPEED_HIGH : - (status & UPS_LOW_SPEED) ? USB_SPEED_LOW : USB_SPEED_FULL; + if (sc->sc_st.port_change & UPS_C_PORT_ENABLED) { + err = usbreq_clear_port_feature( + udev, &usb_global_lock, portno, UHF_C_PORT_ENABLE); + if (err) { + /* most likely the HUB is gone */ + break; + } + if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) { + /* + * Ignore the port error if the device + * has vanished ! + */ + } else if (sc->sc_st.port_status & UPS_PORT_ENABLED) { + DPRINTF(sc, -1, "illegal enable change, " + "port %d\n", portno); + } else { - /* get device info and set its address */ - err = usbd_new_device(sc->sc_dev, udev->bus, udev, - udev->depth + 1, speed, x, portno); - if (err == 0) { - err = uhub_explore_sub(sc->sc_dev, udev, up); + if (up->restartcnt == USBD_RESTART_MAX) { + /* XXX could try another speed ? */ + DPRINTF(sc, -1, "port error, giving up " + "port %d\n", portno); + } else { + sc->sc_st.port_change |= UPS_C_CONNECT_STATUS; + up->restartcnt++; + } + } + } + if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) { + err = uhub_reattach_port(sc, portno); + if (err) { + /* most likely the HUB is gone */ + break; + } } + err = uhub_explore_sub(sc, up); if (err) { - DPRINTF(sc, -1, "usb_new_device failed, " - "error=%s\n", usbd_errstr(err)); - /* Avoid addressing problems by disabling. */ - /* - * usbreq_reset_port (udev, &usb_global_lock, - * &up->status, portno); - */ - - /* - * 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. - */ - DPRINTF(sc, -1, "device problem (%s), " - "disabling port %d\n", usbd_errstr(err), portno); - usbreq_clear_port_feature - (udev, &usb_global_lock, portno, UHF_PORT_ENABLE); + /* no device(s) present */ continue; } - /* - * The port setup succeeded, reset error count and do - * recursive explore, if any: - */ + /* explore succeeded - reset restart counter */ up->restartcnt = 0; - } return (USBD_NORMAL_COMPLETION); } @@ -433,9 +520,8 @@ return (UMATCH_NONE); } /* - * the subclass for hubs, is ignored, - * because it is 0 for some - * and 1 for others + * The subclass for USB HUBs is ignored because it is 0 for some + * and 1 for others. */ if ((uaa->iface == NULL) && (dd->bDeviceClass == UDCLASS_HUB)) { @@ -484,18 +570,18 @@ DPRINTF(sc, 1, "depth=%d selfpowered=%d, parent=%p, " "parent->selfpowered=%d\n", udev->depth, - udev->self_powered, + udev->flags.self_powered, parent_hub, parent_hub ? - parent_hub->self_powered : 0); + parent_hub->flags.self_powered : 0); if (udev->depth > USB_HUB_MAX_DEPTH) { DPRINTF(sc, -1, "hub depth, %d, exceeded. HUB ignored!\n", USB_HUB_MAX_DEPTH); goto error; } - if (!udev->self_powered && parent_hub && - (!parent_hub->self_powered)) { + if (!udev->flags.self_powered && parent_hub && + (!parent_hub->flags.self_powered)) { DPRINTF(sc, -1, "bus powered hub connected to " "bus powered hub. HUB ignored!\n"); goto error; @@ -547,11 +633,11 @@ /* initialize HUB structure */ hub->hubsoftc = sc; hub->explore = &uhub_explore; - hub->hubdesc = hubdesc; + hub->nports = hubdesc.bNbrPorts; hub->hubudev = udev; /* if self powered hub, give ports maximum current */ - if (udev->self_powered) { + if (udev->flags.self_powered) { hub->portpower = USB_MAX_POWER; } else { hub->portpower = USB_MIN_POWER; @@ -599,11 +685,11 @@ pwrdly = ((hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + USB_EXTRA_POWER_UP_TIME); - for (x = 0; x < nports; x++) { + for (x = 0; x != nports; x++) { /* set up data structures */ struct usbd_port *up = hub->ports + x; - up->device_addr = USB_START_ADDR; + up->device_index = 0; up->restartcnt = 0; portno = x + 1; @@ -628,11 +714,7 @@ device_printf(dev, "%d port%s with %d " "removable, %s powered\n", nports, (nports != 1) ? "s" : "", - removable, udev->self_powered ? "self" : "bus"); - - /* the usual exploration will finish the setup */ - - sc->sc_flags |= UHUB_FLAG_RUNNING; + removable, udev->flags.self_powered ? "self" : "bus"); /* start the interrupt endpoint */ @@ -670,16 +752,17 @@ if (hub == NULL) { /* must be partially working */ return (0); } - for (x = 0; x < hub->hubdesc.bNbrPorts; x++) { + for (x = 0; x != hub->nports; x++) { - child = uhub_port_to_sub_device(sc->sc_udev, hub->ports + x); + child = usbd_bus_port_get_device(sc->sc_udev->bus, hub->ports + x); /* * Subdevices are not freed, because the caller of - * uhub_detach() will do that. The function we are calling - * is NULL safe. + * uhub_detach() will do that. The function we are + * calling is NULL safe. */ - usbd_free_device(child, 0); + usbd_detach_device(child, USB_IFACE_INDEX_ANY, 0); + usbd_free_device(child); } usbd_transfer_unsetup(sc->sc_xfer, 2); @@ -696,59 +779,77 @@ return; } +struct hub_result { + struct usbd_device *udev; + uint8_t portno; + uint8_t iface_index; +}; + +static void +uhub_find_iface_index(struct usbd_hub *hub, device_t child, + struct hub_result *res) +{ + struct usbd_interface *iface; + struct usbd_device *udev; + uint8_t nports; + uint8_t x; + uint8_t i; + + nports = hub->nports; + for (x = 0; x != nports; x++) { + udev = usbd_bus_port_get_device(hub->hubudev->bus, + hub->ports + x); + if (!udev) { + continue; + } + if (udev->global_dev == child) { + res->iface_index = 0; + res->udev = udev; + res->portno = x + 1; + return; + } + for (i = 0; i != USB_MAX_INTERFACES; i++) { + iface = usbd_get_iface(udev, i); + if (iface && + (iface->subdev == child)) { + res->iface_index = i; + res->udev = udev; + res->portno = x + 1; + return; + } + } + } + res->iface_index = 0; + res->udev = NULL; + res->portno = 0; + return; +} + static int uhub_child_location_string(device_t parent, device_t child, char *buf, size_t buflen) { struct uhub_softc *sc = device_get_softc(parent); struct usbd_hub *hub = sc->sc_udev->hub; - struct usbd_device *udev; - uint8_t x; - uint8_t nports; - uint8_t iface_index; + struct hub_result res; mtx_lock(&usb_global_lock); - - nports = hub->hubdesc.bNbrPorts; - for (x = 0; x < nports; x++) { - udev = uhub_port_to_sub_device(sc->sc_udev, hub->ports + x); - if (udev) { - device_t *subdev = - &udev->subdevs[0]; - device_t *subdev_end = - &udev->subdevs_end[0]; - - iface_index = 0; - - while (subdev < subdev_end) { - if (subdev[0] == child) { - goto found; - } - subdev++; - iface_index++; - } + uhub_find_iface_index(hub, child, &res); + if (!res.udev) { + DPRINTF(sc, 0, "device not on hub\n"); + if (buflen) { + buf[0] = '\0'; } + goto done; } - - mtx_unlock(&usb_global_lock); - - DPRINTF(sc, 0, "device not on hub\n"); - - if (buflen) { - buf[0] = '\0'; - } - return (0); - - -found: - - if (udev->probed == USBD_PROBED_IFACE_AND_FOUND) { + if (res.udev->probed == USBD_PROBED_IFACE_AND_FOUND) { snprintf(buf, buflen, "port=%i interface=%i", - x + 1, iface_index); + res.portno, res.iface_index); } else { - snprintf(buf, buflen, "port=%i", x + 1); + snprintf(buf, buflen, "port=%i", res.portno); } +done: mtx_unlock(&usb_global_lock); return (0); @@ -761,71 +862,44 @@ struct uhub_softc *sc = device_get_softc(parent); struct usbd_hub *hub = sc->sc_udev->hub; struct usbd_interface *iface; - struct usbd_device *udev; - uint8_t x; - uint8_t nports; - uint8_t iface_index; + struct hub_result res; mtx_lock(&usb_global_lock); - - nports = hub->hubdesc.bNbrPorts; - for (x = 0; x < nports; x++) { - udev = uhub_port_to_sub_device(sc->sc_udev, hub->ports + x); - if (udev) { - device_t *subdev = - &udev->subdevs[0]; - device_t *subdev_end = - &udev->subdevs_end[0]; - - iface_index = 0; - - while (subdev < subdev_end) { - if (subdev[0] == child) { - goto found; - } - subdev++; - iface_index++; - } + uhub_find_iface_index(hub, child, &res); + if (!res.udev) { + DPRINTF(sc, 0, "device not on hub\n"); + if (buflen) { + buf[0] = '\0'; } + goto done; } + iface = usbd_get_iface(res.udev, res.iface_index); - mtx_unlock(&usb_global_lock); - - DPRINTF(sc, 0, "device not on hub\n"); - - if (buflen) { - buf[0] = '\0'; - } - return (0); - -found: - - iface = usbd_get_iface(udev, iface_index); - - if ((udev->probed == USBD_PROBED_IFACE_AND_FOUND) && + if ((res.udev->probed == USBD_PROBED_IFACE_AND_FOUND) && iface && iface->idesc) { snprintf(buf, buflen, "vendor=0x%04x product=0x%04x " "devclass=0x%02x devsubclass=0x%02x " "sernum=\"%s\" " "intclass=0x%02x intsubclass=0x%02x", - UGETW(udev->ddesc.idVendor), - UGETW(udev->ddesc.idProduct), - udev->ddesc.bDeviceClass, - udev->ddesc.bDeviceSubClass, - &udev->serial[0], + UGETW(res.udev->ddesc.idVendor), + UGETW(res.udev->ddesc.idProduct), + res.udev->ddesc.bDeviceClass, + res.udev->ddesc.bDeviceSubClass, + res.udev->serial, iface->idesc->bInterfaceClass, iface->idesc->bInterfaceSubClass); } else { snprintf(buf, buflen, "vendor=0x%04x product=0x%04x " "devclass=0x%02x devsubclass=0x%02x " "sernum=\"%s\"", - UGETW(udev->ddesc.idVendor), - UGETW(udev->ddesc.idProduct), - udev->ddesc.bDeviceClass, - udev->ddesc.bDeviceSubClass, - &udev->serial[0]); + UGETW(res.udev->ddesc.idVendor), + UGETW(res.udev->ddesc.idProduct), + res.udev->ddesc.bDeviceClass, + res.udev->ddesc.bDeviceSubClass, + res.udev->serial); } +done: mtx_unlock(&usb_global_lock); return (0);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200712122245.lBCMjQPL078789>