Index: head/sys/compat/linuxkpi/common/src/linux_usb.c =================================================================== --- head/sys/compat/linuxkpi/common/src/linux_usb.c (revision 368658) +++ head/sys/compat/linuxkpi/common/src/linux_usb.c (revision 368659) @@ -1,1725 +1,1720 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2007 Luigi Rizzo - Universita` di Pisa. All rights reserved. * Copyright (c) 2007 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ struct usb_linux_softc { LIST_ENTRY(usb_linux_softc) sc_attached_list; device_t sc_fbsd_dev; struct usb_device *sc_fbsd_udev; struct usb_interface *sc_ui; struct usb_driver *sc_udrv; }; /* prototypes */ static device_probe_t usb_linux_probe; static device_attach_t usb_linux_attach; static device_detach_t usb_linux_detach; static device_suspend_t usb_linux_suspend; static device_resume_t usb_linux_resume; static usb_callback_t usb_linux_isoc_callback; static usb_callback_t usb_linux_non_isoc_callback; static usb_complete_t usb_linux_wait_complete; static uint16_t usb_max_isoc_frames(struct usb_device *); static int usb_start_wait_urb(struct urb *, usb_timeout_t, uint16_t *); static const struct usb_device_id *usb_linux_lookup_id( const struct usb_device_id *, struct usb_attach_arg *); static struct usb_driver *usb_linux_get_usb_driver(struct usb_linux_softc *); static int usb_linux_create_usb_device(struct usb_device *, device_t); static void usb_linux_cleanup_interface(struct usb_device *, struct usb_interface *); static void usb_linux_complete(struct usb_xfer *); static int usb_unlink_urb_sub(struct urb *, uint8_t); /*------------------------------------------------------------------------* * FreeBSD USB interface *------------------------------------------------------------------------*/ static LIST_HEAD(, usb_linux_softc) usb_linux_attached_list; static LIST_HEAD(, usb_driver) usb_linux_driver_list; static device_method_t usb_linux_methods[] = { /* Device interface */ DEVMETHOD(device_probe, usb_linux_probe), DEVMETHOD(device_attach, usb_linux_attach), DEVMETHOD(device_detach, usb_linux_detach), DEVMETHOD(device_suspend, usb_linux_suspend), DEVMETHOD(device_resume, usb_linux_resume), DEVMETHOD_END }; static driver_t usb_linux_driver = { .name = "usb_linux", .methods = usb_linux_methods, .size = sizeof(struct usb_linux_softc), }; static devclass_t usb_linux_devclass; DRIVER_MODULE(usb_linux, uhub, usb_linux_driver, usb_linux_devclass, NULL, 0); MODULE_VERSION(usb_linux, 1); /*------------------------------------------------------------------------* * usb_linux_lookup_id * * This functions takes an array of "struct usb_device_id" and tries * to match the entries with the information in "struct usb_attach_arg". * If it finds a match the matching entry will be returned. * Else "NULL" will be returned. *------------------------------------------------------------------------*/ static const struct usb_device_id * usb_linux_lookup_id(const struct usb_device_id *id, struct usb_attach_arg *uaa) { if (id == NULL) { goto done; } /* * Keep on matching array entries until we find one with * "match_flags" equal to zero, which indicates the end of the * array: */ for (; id->match_flags; id++) { if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && (id->idVendor != uaa->info.idVendor)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && (id->idProduct != uaa->info.idProduct)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && (id->bcdDevice_lo > uaa->info.bcdDevice)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && (id->bcdDevice_hi < uaa->info.bcdDevice)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && (id->bDeviceClass != uaa->info.bDeviceClass)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && (id->bDeviceSubClass != uaa->info.bDeviceSubClass)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && (id->bDeviceProtocol != uaa->info.bDeviceProtocol)) { continue; } if ((uaa->info.bDeviceClass == 0xFF) && !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS | USB_DEVICE_ID_MATCH_INT_PROTOCOL))) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && (id->bInterfaceClass != uaa->info.bInterfaceClass)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && (id->bInterfaceSubClass != uaa->info.bInterfaceSubClass)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && (id->bInterfaceProtocol != uaa->info.bInterfaceProtocol)) { continue; } /* we found a match! */ return (id); } done: return (NULL); } /*------------------------------------------------------------------------* * usb_linux_probe * * This function is the FreeBSD probe callback. It is called from the * FreeBSD USB stack through the "device_probe_and_attach()" function. *------------------------------------------------------------------------*/ static int usb_linux_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct usb_driver *udrv; int err = ENXIO; if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } mtx_lock(&Giant); LIST_FOREACH(udrv, &usb_linux_driver_list, linux_driver_list) { if (usb_linux_lookup_id(udrv->id_table, uaa)) { err = 0; break; } } mtx_unlock(&Giant); return (err); } /*------------------------------------------------------------------------* * usb_linux_get_usb_driver * * This function returns the pointer to the "struct usb_driver" where * the Linux USB device driver "struct usb_device_id" match was found. * We apply a lock before reading out the pointer to avoid races. *------------------------------------------------------------------------*/ static struct usb_driver * usb_linux_get_usb_driver(struct usb_linux_softc *sc) { struct usb_driver *udrv; mtx_lock(&Giant); udrv = sc->sc_udrv; mtx_unlock(&Giant); return (udrv); } /*------------------------------------------------------------------------* * usb_linux_attach * * This function is the FreeBSD attach callback. It is called from the * FreeBSD USB stack through the "device_probe_and_attach()" function. * This function is called when "usb_linux_probe()" returns zero. *------------------------------------------------------------------------*/ static int usb_linux_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct usb_linux_softc *sc = device_get_softc(dev); struct usb_driver *udrv; const struct usb_device_id *id = NULL; mtx_lock(&Giant); LIST_FOREACH(udrv, &usb_linux_driver_list, linux_driver_list) { id = usb_linux_lookup_id(udrv->id_table, uaa); if (id) break; } mtx_unlock(&Giant); if (id == NULL) { return (ENXIO); } if (usb_linux_create_usb_device(uaa->device, dev) != 0) return (ENOMEM); device_set_usb_desc(dev); sc->sc_fbsd_udev = uaa->device; sc->sc_fbsd_dev = dev; sc->sc_udrv = udrv; sc->sc_ui = usb_ifnum_to_if(uaa->device, uaa->info.bIfaceNum); if (sc->sc_ui == NULL) { return (EINVAL); } if (udrv->probe) { if ((udrv->probe) (sc->sc_ui, id)) { return (ENXIO); } } mtx_lock(&Giant); LIST_INSERT_HEAD(&usb_linux_attached_list, sc, sc_attached_list); mtx_unlock(&Giant); /* success */ return (0); } /*------------------------------------------------------------------------* * usb_linux_detach * * This function is the FreeBSD detach callback. It is called from the * FreeBSD USB stack through the "device_detach()" function. *------------------------------------------------------------------------*/ static int usb_linux_detach(device_t dev) { struct usb_linux_softc *sc = device_get_softc(dev); struct usb_driver *udrv = NULL; mtx_lock(&Giant); if (sc->sc_attached_list.le_prev) { LIST_REMOVE(sc, sc_attached_list); sc->sc_attached_list.le_prev = NULL; udrv = sc->sc_udrv; sc->sc_udrv = NULL; } mtx_unlock(&Giant); if (udrv && udrv->disconnect) { (udrv->disconnect) (sc->sc_ui); } /* * Make sure that we free all FreeBSD USB transfers belonging to * this Linux "usb_interface", hence they will most likely not be * needed any more. */ usb_linux_cleanup_interface(sc->sc_fbsd_udev, sc->sc_ui); return (0); } /*------------------------------------------------------------------------* * usb_linux_suspend * * This function is the FreeBSD suspend callback. Usually it does nothing. *------------------------------------------------------------------------*/ static int usb_linux_suspend(device_t dev) { struct usb_linux_softc *sc = device_get_softc(dev); struct usb_driver *udrv = usb_linux_get_usb_driver(sc); int err; if (udrv && udrv->suspend) { err = (udrv->suspend) (sc->sc_ui, 0); } return (0); } /*------------------------------------------------------------------------* * usb_linux_resume * * This function is the FreeBSD resume callback. Usually it does nothing. *------------------------------------------------------------------------*/ static int usb_linux_resume(device_t dev) { struct usb_linux_softc *sc = device_get_softc(dev); struct usb_driver *udrv = usb_linux_get_usb_driver(sc); int err; if (udrv && udrv->resume) { err = (udrv->resume) (sc->sc_ui); } return (0); } /*------------------------------------------------------------------------* * Linux emulation layer *------------------------------------------------------------------------*/ /*------------------------------------------------------------------------* * usb_max_isoc_frames * * The following function returns the maximum number of isochronous * frames that we support per URB. It is not part of the Linux USB API. *------------------------------------------------------------------------*/ static uint16_t usb_max_isoc_frames(struct usb_device *dev) { ; /* indent fix */ switch (usbd_get_speed(dev)) { case USB_SPEED_LOW: case USB_SPEED_FULL: return (USB_MAX_FULL_SPEED_ISOC_FRAMES); default: return (USB_MAX_HIGH_SPEED_ISOC_FRAMES); } } /*------------------------------------------------------------------------* * usb_submit_urb * * This function is used to queue an URB after that it has been * initialized. If it returns non-zero, it means that the URB was not * queued. *------------------------------------------------------------------------*/ int usb_submit_urb(struct urb *urb, uint16_t mem_flags) { struct usb_host_endpoint *uhe; uint8_t do_unlock; int err; if (urb == NULL) return (-EINVAL); do_unlock = mtx_owned(&Giant) ? 0 : 1; if (do_unlock) mtx_lock(&Giant); if (urb->endpoint == NULL) { err = -EINVAL; goto done; } /* * Check to see if the urb is in the process of being killed * and stop a urb that is in the process of being killed from * being re-submitted (e.g. from its completion callback * function). */ if (urb->kill_count != 0) { err = -EPERM; goto done; } uhe = urb->endpoint; /* * Check that we have got a FreeBSD USB transfer that will dequeue * the URB structure and do the real transfer. If there are no USB * transfers, then we return an error. */ if (uhe->bsd_xfer[0] || uhe->bsd_xfer[1]) { /* we are ready! */ TAILQ_INSERT_TAIL(&uhe->bsd_urb_list, urb, bsd_urb_list); urb->status = -EINPROGRESS; usbd_transfer_start(uhe->bsd_xfer[0]); usbd_transfer_start(uhe->bsd_xfer[1]); err = 0; } else { /* no pipes have been setup yet! */ urb->status = -EINVAL; err = -EINVAL; } done: if (do_unlock) mtx_unlock(&Giant); return (err); } /*------------------------------------------------------------------------* * usb_unlink_urb * * This function is used to stop an URB after that it is been * submitted, but before the "complete" callback has been called. On *------------------------------------------------------------------------*/ int usb_unlink_urb(struct urb *urb) { return (usb_unlink_urb_sub(urb, 0)); } static void usb_unlink_bsd(struct usb_xfer *xfer, struct urb *urb, uint8_t drain) { if (xfer == NULL) return; if (!usbd_transfer_pending(xfer)) return; if (xfer->priv_fifo == (void *)urb) { if (drain) { mtx_unlock(&Giant); usbd_transfer_drain(xfer); mtx_lock(&Giant); } else { usbd_transfer_stop(xfer); } usbd_transfer_start(xfer); } } static int usb_unlink_urb_sub(struct urb *urb, uint8_t drain) { struct usb_host_endpoint *uhe; uint16_t x; uint8_t do_unlock; int err; if (urb == NULL) return (-EINVAL); do_unlock = mtx_owned(&Giant) ? 0 : 1; if (do_unlock) mtx_lock(&Giant); if (drain) urb->kill_count++; if (urb->endpoint == NULL) { err = -EINVAL; goto done; } uhe = urb->endpoint; if (urb->bsd_urb_list.tqe_prev) { /* not started yet, just remove it from the queue */ TAILQ_REMOVE(&uhe->bsd_urb_list, urb, bsd_urb_list); urb->bsd_urb_list.tqe_prev = NULL; urb->status = -ECONNRESET; urb->actual_length = 0; for (x = 0; x < urb->number_of_packets; x++) { urb->iso_frame_desc[x].actual_length = 0; } if (urb->complete) { (urb->complete) (urb); } } else { /* * If the URB is not on the URB list, then check if one of * the FreeBSD USB transfer are processing the current URB. * If so, re-start that transfer, which will lead to the * termination of that URB: */ usb_unlink_bsd(uhe->bsd_xfer[0], urb, drain); usb_unlink_bsd(uhe->bsd_xfer[1], urb, drain); } err = 0; done: if (drain) urb->kill_count--; if (do_unlock) mtx_unlock(&Giant); return (err); } /*------------------------------------------------------------------------* * usb_clear_halt * * This function must always be used to clear the stall. Stall is when * an USB endpoint returns a stall message to the USB host controller. * Until the stall is cleared, no data can be transferred. *------------------------------------------------------------------------*/ int usb_clear_halt(struct usb_device *dev, struct usb_host_endpoint *uhe) { struct usb_config cfg[1]; struct usb_endpoint *ep; uint8_t type; uint8_t addr; if (uhe == NULL) return (-EINVAL); type = uhe->desc.bmAttributes & UE_XFERTYPE; addr = uhe->desc.bEndpointAddress; memset(cfg, 0, sizeof(cfg)); cfg[0].type = type; cfg[0].endpoint = addr & UE_ADDR; cfg[0].direction = addr & (UE_DIR_OUT | UE_DIR_IN); ep = usbd_get_endpoint(dev, uhe->bsd_iface_index, cfg); if (ep == NULL) return (-EINVAL); usbd_clear_data_toggle(dev, ep); return (usb_control_msg(dev, &dev->ep0, UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT, UF_ENDPOINT_HALT, addr, NULL, 0, 1000)); } /*------------------------------------------------------------------------* * usb_start_wait_urb * * This is an internal function that is used to perform synchronous * Linux USB transfers. *------------------------------------------------------------------------*/ static int usb_start_wait_urb(struct urb *urb, usb_timeout_t timeout, uint16_t *p_actlen) { int err; uint8_t do_unlock; /* you must have a timeout! */ if (timeout == 0) { timeout = 1; } urb->complete = &usb_linux_wait_complete; urb->timeout = timeout; urb->transfer_flags |= URB_WAIT_WAKEUP; urb->transfer_flags &= ~URB_IS_SLEEPING; do_unlock = mtx_owned(&Giant) ? 0 : 1; if (do_unlock) mtx_lock(&Giant); err = usb_submit_urb(urb, 0); if (err) goto done; /* * the URB might have completed before we get here, so check that by * using some flags! */ while (urb->transfer_flags & URB_WAIT_WAKEUP) { urb->transfer_flags |= URB_IS_SLEEPING; cv_wait(&urb->cv_wait, &Giant); urb->transfer_flags &= ~URB_IS_SLEEPING; } err = urb->status; done: if (do_unlock) mtx_unlock(&Giant); if (p_actlen != NULL) { if (err) *p_actlen = 0; else *p_actlen = urb->actual_length; } return (err); } /*------------------------------------------------------------------------* * usb_control_msg * * The following function performs a control transfer sequence one any * control, bulk or interrupt endpoint, specified by "uhe". A control * transfer means that you transfer an 8-byte header first followed by * a data-phase as indicated by the 8-byte header. The "timeout" is * given in milliseconds. * * Return values: * 0: Success * < 0: Failure * > 0: Actual length *------------------------------------------------------------------------*/ int usb_control_msg(struct usb_device *dev, struct usb_host_endpoint *uhe, uint8_t request, uint8_t requesttype, uint16_t value, uint16_t index, void *data, uint16_t size, usb_timeout_t timeout) { struct usb_device_request req; struct urb *urb; int err; uint16_t actlen; uint8_t type; uint8_t addr; req.bmRequestType = requesttype; req.bRequest = request; USETW(req.wValue, value); USETW(req.wIndex, index); USETW(req.wLength, size); if (uhe == NULL) { return (-EINVAL); } type = (uhe->desc.bmAttributes & UE_XFERTYPE); addr = (uhe->desc.bEndpointAddress & UE_ADDR); if (type != UE_CONTROL) { return (-EINVAL); } if (addr == 0) { /* * The FreeBSD USB stack supports standard control * transfers on control endpoint zero: */ err = usbd_do_request_flags(dev, NULL, &req, data, USB_SHORT_XFER_OK, &actlen, timeout); if (err) { err = -EPIPE; } else { err = actlen; } return (err); } if (dev->flags.usb_mode != USB_MODE_HOST) { /* not supported */ return (-EINVAL); } err = usb_setup_endpoint(dev, uhe, 1 /* dummy */ ); /* * NOTE: we need to allocate real memory here so that we don't * transfer data to/from the stack! * * 0xFFFF is a FreeBSD specific magic value. */ urb = usb_alloc_urb(0xFFFF, size); urb->dev = dev; urb->endpoint = uhe; memcpy(urb->setup_packet, &req, sizeof(req)); if (size && (!(req.bmRequestType & UT_READ))) { /* move the data to a real buffer */ memcpy(USB_ADD_BYTES(urb->setup_packet, sizeof(req)), data, size); } err = usb_start_wait_urb(urb, timeout, &actlen); if (req.bmRequestType & UT_READ) { if (actlen) { bcopy(USB_ADD_BYTES(urb->setup_packet, sizeof(req)), data, actlen); } } usb_free_urb(urb); if (err == 0) { err = actlen; } return (err); } /*------------------------------------------------------------------------* * usb_set_interface * * The following function will select which alternate setting of an * USB interface you plan to use. By default alternate setting with * index zero is selected. Note that "iface_no" is not the interface * index, but rather the value of "bInterfaceNumber". *------------------------------------------------------------------------*/ int usb_set_interface(struct usb_device *dev, uint8_t iface_no, uint8_t alt_index) { struct usb_interface *p_ui = usb_ifnum_to_if(dev, iface_no); int err; if (p_ui == NULL) return (-EINVAL); if (alt_index >= p_ui->num_altsetting) return (-EINVAL); usb_linux_cleanup_interface(dev, p_ui); err = -usbd_set_alt_interface_index(dev, p_ui->bsd_iface_index, alt_index); if (err == 0) { p_ui->cur_altsetting = p_ui->altsetting + alt_index; } return (err); } /*------------------------------------------------------------------------* * usb_setup_endpoint * * The following function is an extension to the Linux USB API that * allows you to set a maximum buffer size for a given USB endpoint. * The maximum buffer size is per URB. If you don't call this function * to set a maximum buffer size, the endpoint will not be functional. * Note that for isochronous endpoints the maximum buffer size must be * a non-zero dummy, hence this function will base the maximum buffer * size on "wMaxPacketSize". *------------------------------------------------------------------------*/ int usb_setup_endpoint(struct usb_device *dev, struct usb_host_endpoint *uhe, usb_size_t bufsize) { struct usb_config cfg[2]; uint8_t type = uhe->desc.bmAttributes & UE_XFERTYPE; uint8_t addr = uhe->desc.bEndpointAddress; if (uhe->fbsd_buf_size == bufsize) { /* optimize */ return (0); } usbd_transfer_unsetup(uhe->bsd_xfer, 2); uhe->fbsd_buf_size = bufsize; if (bufsize == 0) { return (0); } memset(cfg, 0, sizeof(cfg)); if (type == UE_ISOCHRONOUS) { /* * Isochronous transfers are special in that they don't fit * into the BULK/INTR/CONTROL transfer model. */ cfg[0].type = type; cfg[0].endpoint = addr & UE_ADDR; cfg[0].direction = addr & (UE_DIR_OUT | UE_DIR_IN); cfg[0].callback = &usb_linux_isoc_callback; cfg[0].bufsize = 0; /* use wMaxPacketSize */ cfg[0].frames = usb_max_isoc_frames(dev); cfg[0].flags.proxy_buffer = 1; #if 0 /* * The Linux USB API allows non back-to-back * isochronous frames which we do not support. If the * isochronous frames are not back-to-back we need to * do a copy, and then we need a buffer for * that. Enable this at your own risk. */ cfg[0].flags.ext_buffer = 1; #endif cfg[0].flags.short_xfer_ok = 1; bcopy(cfg, cfg + 1, sizeof(*cfg)); /* Allocate and setup two generic FreeBSD USB transfers */ if (usbd_transfer_setup(dev, &uhe->bsd_iface_index, uhe->bsd_xfer, cfg, 2, uhe, &Giant)) { return (-EINVAL); } } else { if (bufsize > (1 << 22)) { /* limit buffer size */ bufsize = (1 << 22); } /* Allocate and setup one generic FreeBSD USB transfer */ cfg[0].type = type; cfg[0].endpoint = addr & UE_ADDR; cfg[0].direction = addr & (UE_DIR_OUT | UE_DIR_IN); cfg[0].callback = &usb_linux_non_isoc_callback; cfg[0].bufsize = bufsize; cfg[0].flags.ext_buffer = 1; /* enable zero-copy */ cfg[0].flags.proxy_buffer = 1; cfg[0].flags.short_xfer_ok = 1; if (usbd_transfer_setup(dev, &uhe->bsd_iface_index, uhe->bsd_xfer, cfg, 1, uhe, &Giant)) { return (-EINVAL); } } return (0); } /*------------------------------------------------------------------------* * usb_linux_create_usb_device * * The following function is used to build up a per USB device * structure tree, that mimics the Linux one. The root structure * is returned by this function. *------------------------------------------------------------------------*/ static int usb_linux_create_usb_device(struct usb_device *udev, device_t dev) { struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev); struct usb_descriptor *desc; struct usb_interface_descriptor *id; struct usb_endpoint_descriptor *ed; struct usb_interface *p_ui = NULL; struct usb_host_interface *p_uhi = NULL; struct usb_host_endpoint *p_uhe = NULL; usb_size_t size; uint16_t niface_total; uint16_t nedesc; uint16_t iface_no_curr; uint16_t iface_index; uint8_t pass; uint8_t iface_no; /* * We do two passes. One pass for computing necessary memory size * and one pass to initialize all the allocated memory structures. */ for (pass = 0; pass < 2; pass++) { iface_no_curr = 0xFFFF; niface_total = 0; iface_index = 0; nedesc = 0; desc = NULL; /* * Iterate over all the USB descriptors. Use the USB config * descriptor pointer provided by the FreeBSD USB stack. */ while ((desc = usb_desc_foreach(cd, desc))) { /* * Build up a tree according to the descriptors we * find: */ switch (desc->bDescriptorType) { case UDESC_DEVICE: break; case UDESC_ENDPOINT: ed = (void *)desc; if ((ed->bLength < sizeof(*ed)) || (iface_index == 0)) break; if (p_uhe) { bcopy(ed, &p_uhe->desc, sizeof(p_uhe->desc)); p_uhe->bsd_iface_index = iface_index - 1; TAILQ_INIT(&p_uhe->bsd_urb_list); p_uhe++; } if (p_uhi) { (p_uhi - 1)->desc.bNumEndpoints++; } nedesc++; break; case UDESC_INTERFACE: id = (void *)desc; if (id->bLength < sizeof(*id)) break; if (p_uhi) { bcopy(id, &p_uhi->desc, sizeof(p_uhi->desc)); p_uhi->desc.bNumEndpoints = 0; p_uhi->endpoint = p_uhe; p_uhi->string = ""; p_uhi->bsd_iface_index = iface_index; p_uhi++; } iface_no = id->bInterfaceNumber; niface_total++; if (iface_no_curr != iface_no) { if (p_ui) { p_ui->altsetting = p_uhi - 1; p_ui->cur_altsetting = p_uhi - 1; - p_ui->num_altsetting = 1; p_ui->bsd_iface_index = iface_index; p_ui->linux_udev = udev; p_ui++; } iface_no_curr = iface_no; iface_index++; - } else { - if (p_ui) { - (p_ui - 1)->num_altsetting++; - } } break; default: break; } } if (pass == 0) { size = (sizeof(*p_uhe) * nedesc) + (sizeof(*p_ui) * iface_index) + (sizeof(*p_uhi) * niface_total); p_uhe = malloc(size, M_USBDEV, M_WAITOK | M_ZERO); p_ui = (void *)(p_uhe + nedesc); p_uhi = (void *)(p_ui + iface_index); udev->linux_iface_start = p_ui; udev->linux_iface_end = p_ui + iface_index; udev->linux_endpoint_start = p_uhe; udev->linux_endpoint_end = p_uhe + nedesc; udev->devnum = device_get_unit(dev); bcopy(&udev->ddesc, &udev->descriptor, sizeof(udev->descriptor)); bcopy(udev->ctrl_ep.edesc, &udev->ep0.desc, sizeof(udev->ep0.desc)); } } return (0); } /*------------------------------------------------------------------------* * usb_alloc_urb * * This function should always be used when you allocate an URB for * use with the USB Linux stack. In case of an isochronous transfer * you must specifiy the maximum number of "iso_packets" which you * plan to transfer per URB. This function is always blocking, and * "mem_flags" are not regarded like on Linux. *------------------------------------------------------------------------*/ struct urb * usb_alloc_urb(uint16_t iso_packets, uint16_t mem_flags) { struct urb *urb; usb_size_t size; if (iso_packets == 0xFFFF) { /* * FreeBSD specific magic value to ask for control transfer * memory allocation: */ size = sizeof(*urb) + sizeof(struct usb_device_request) + mem_flags; } else { size = sizeof(*urb) + (iso_packets * sizeof(urb->iso_frame_desc[0])); } urb = malloc(size, M_USBDEV, M_WAITOK | M_ZERO); cv_init(&urb->cv_wait, "URBWAIT"); if (iso_packets == 0xFFFF) { urb->setup_packet = (void *)(urb + 1); urb->transfer_buffer = (void *)(urb->setup_packet + sizeof(struct usb_device_request)); } else { urb->number_of_packets = iso_packets; } return (urb); } /*------------------------------------------------------------------------* * usb_find_host_endpoint * * The following function will return the Linux USB host endpoint * structure that matches the given endpoint type and endpoint * value. If no match is found, NULL is returned. This function is not * part of the Linux USB API and is only used internally. *------------------------------------------------------------------------*/ struct usb_host_endpoint * usb_find_host_endpoint(struct usb_device *dev, uint8_t type, uint8_t ep) { struct usb_host_endpoint *uhe; struct usb_host_endpoint *uhe_end; struct usb_host_interface *uhi; struct usb_interface *ui; uint8_t ea; uint8_t at; uint8_t mask; if (dev == NULL) { return (NULL); } if (type == UE_CONTROL) { mask = UE_ADDR; } else { mask = (UE_DIR_IN | UE_DIR_OUT | UE_ADDR); } ep &= mask; /* * Iterate over all the interfaces searching the selected alternate * setting only, and all belonging endpoints. */ for (ui = dev->linux_iface_start; ui != dev->linux_iface_end; ui++) { uhi = ui->cur_altsetting; if (uhi) { uhe_end = uhi->endpoint + uhi->desc.bNumEndpoints; for (uhe = uhi->endpoint; uhe != uhe_end; uhe++) { ea = uhe->desc.bEndpointAddress; at = uhe->desc.bmAttributes; if (((ea & mask) == ep) && ((at & UE_XFERTYPE) == type)) { return (uhe); } } } } if ((type == UE_CONTROL) && ((ep & UE_ADDR) == 0)) { return (&dev->ep0); } return (NULL); } /*------------------------------------------------------------------------* * usb_altnum_to_altsetting * * The following function returns a pointer to an alternate setting by * index given a "usb_interface" pointer. If the alternate setting by * index does not exist, NULL is returned. And alternate setting is a * variant of an interface, but usually with slightly different * characteristics. *------------------------------------------------------------------------*/ struct usb_host_interface * usb_altnum_to_altsetting(const struct usb_interface *intf, uint8_t alt_index) { if (alt_index >= intf->num_altsetting) { return (NULL); } return (intf->altsetting + alt_index); } /*------------------------------------------------------------------------* * usb_ifnum_to_if * * The following function searches up an USB interface by * "bInterfaceNumber". If no match is found, NULL is returned. *------------------------------------------------------------------------*/ struct usb_interface * usb_ifnum_to_if(struct usb_device *dev, uint8_t iface_no) { struct usb_interface *p_ui; for (p_ui = dev->linux_iface_start; p_ui != dev->linux_iface_end; p_ui++) { if ((p_ui->num_altsetting > 0) && (p_ui->altsetting->desc.bInterfaceNumber == iface_no)) { return (p_ui); } } return (NULL); } /*------------------------------------------------------------------------* * usb_buffer_alloc *------------------------------------------------------------------------*/ void * usb_buffer_alloc(struct usb_device *dev, usb_size_t size, uint16_t mem_flags, uint8_t *dma_addr) { return (malloc(size, M_USBDEV, M_WAITOK | M_ZERO)); } /*------------------------------------------------------------------------* * usbd_get_intfdata *------------------------------------------------------------------------*/ void * usbd_get_intfdata(struct usb_interface *intf) { return (intf->bsd_priv_sc); } /*------------------------------------------------------------------------* * usb_linux_register * * The following function is used by the "USB_DRIVER_EXPORT()" macro, * and is used to register a Linux USB driver, so that its * "usb_device_id" structures gets searched a probe time. This * function is not part of the Linux USB API, and is for internal use * only. *------------------------------------------------------------------------*/ void usb_linux_register(void *arg) { struct usb_driver *drv = arg; mtx_lock(&Giant); LIST_INSERT_HEAD(&usb_linux_driver_list, drv, linux_driver_list); mtx_unlock(&Giant); usb_needs_explore_all(); } /*------------------------------------------------------------------------* * usb_linux_deregister * * The following function is used by the "USB_DRIVER_EXPORT()" macro, * and is used to deregister a Linux USB driver. This function will * ensure that all driver instances belonging to the Linux USB device * driver in question, gets detached before the driver is * unloaded. This function is not part of the Linux USB API, and is * for internal use only. *------------------------------------------------------------------------*/ void usb_linux_deregister(void *arg) { struct usb_driver *drv = arg; struct usb_linux_softc *sc; repeat: mtx_lock(&Giant); LIST_FOREACH(sc, &usb_linux_attached_list, sc_attached_list) { if (sc->sc_udrv == drv) { mtx_unlock(&Giant); device_detach(sc->sc_fbsd_dev); goto repeat; } } LIST_REMOVE(drv, linux_driver_list); mtx_unlock(&Giant); } /*------------------------------------------------------------------------* * usb_linux_free_device * * The following function is only used by the FreeBSD USB stack, to * cleanup and free memory after that a Linux USB device was attached. *------------------------------------------------------------------------*/ void usb_linux_free_device(struct usb_device *dev) { struct usb_host_endpoint *uhe; struct usb_host_endpoint *uhe_end; int err; uhe = dev->linux_endpoint_start; uhe_end = dev->linux_endpoint_end; while (uhe != uhe_end) { err = usb_setup_endpoint(dev, uhe, 0); uhe++; } err = usb_setup_endpoint(dev, &dev->ep0, 0); free(dev->linux_endpoint_start, M_USBDEV); } /*------------------------------------------------------------------------* * usb_buffer_free *------------------------------------------------------------------------*/ void usb_buffer_free(struct usb_device *dev, usb_size_t size, void *addr, uint8_t dma_addr) { free(addr, M_USBDEV); } /*------------------------------------------------------------------------* * usb_free_urb *------------------------------------------------------------------------*/ void usb_free_urb(struct urb *urb) { if (urb == NULL) { return; } /* make sure that the current URB is not active */ usb_kill_urb(urb); /* destroy condition variable */ cv_destroy(&urb->cv_wait); /* just free it */ free(urb, M_USBDEV); } /*------------------------------------------------------------------------* * usb_init_urb * * The following function can be used to initialize a custom URB. It * is not recommended to use this function. Use "usb_alloc_urb()" * instead. *------------------------------------------------------------------------*/ void usb_init_urb(struct urb *urb) { if (urb == NULL) { return; } memset(urb, 0, sizeof(*urb)); } /*------------------------------------------------------------------------* * usb_kill_urb *------------------------------------------------------------------------*/ void usb_kill_urb(struct urb *urb) { usb_unlink_urb_sub(urb, 1); } /*------------------------------------------------------------------------* * usb_set_intfdata * * The following function sets the per Linux USB interface private * data pointer. It is used by most Linux USB device drivers. *------------------------------------------------------------------------*/ void usb_set_intfdata(struct usb_interface *intf, void *data) { intf->bsd_priv_sc = data; } /*------------------------------------------------------------------------* * usb_linux_cleanup_interface * * The following function will release all FreeBSD USB transfers * associated with a Linux USB interface. It is for internal use only. *------------------------------------------------------------------------*/ static void usb_linux_cleanup_interface(struct usb_device *dev, struct usb_interface *iface) { struct usb_host_interface *uhi; struct usb_host_interface *uhi_end; struct usb_host_endpoint *uhe; struct usb_host_endpoint *uhe_end; int err; uhi = iface->altsetting; uhi_end = iface->altsetting + iface->num_altsetting; while (uhi != uhi_end) { uhe = uhi->endpoint; uhe_end = uhi->endpoint + uhi->desc.bNumEndpoints; while (uhe != uhe_end) { err = usb_setup_endpoint(dev, uhe, 0); uhe++; } uhi++; } } /*------------------------------------------------------------------------* * usb_linux_wait_complete * * The following function is used by "usb_start_wait_urb()" to wake it * up, when an USB transfer has finished. *------------------------------------------------------------------------*/ static void usb_linux_wait_complete(struct urb *urb) { if (urb->transfer_flags & URB_IS_SLEEPING) { cv_signal(&urb->cv_wait); } urb->transfer_flags &= ~URB_WAIT_WAKEUP; } /*------------------------------------------------------------------------* * usb_linux_complete *------------------------------------------------------------------------*/ static void usb_linux_complete(struct usb_xfer *xfer) { struct urb *urb; urb = usbd_xfer_get_priv(xfer); usbd_xfer_set_priv(xfer, NULL); if (urb->complete) { (urb->complete) (urb); } } /*------------------------------------------------------------------------* * usb_linux_isoc_callback * * The following is the FreeBSD isochronous USB callback. Isochronous * frames are USB packets transferred 1000 or 8000 times per second, * depending on whether a full- or high- speed USB transfer is * used. *------------------------------------------------------------------------*/ static void usb_linux_isoc_callback(struct usb_xfer *xfer, usb_error_t error) { usb_frlength_t max_frame = xfer->max_frame_size; usb_frlength_t offset; usb_frcount_t x; struct urb *urb = usbd_xfer_get_priv(xfer); struct usb_host_endpoint *uhe = usbd_xfer_softc(xfer); struct usb_iso_packet_descriptor *uipd; DPRINTF("\n"); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (urb->bsd_isread) { /* copy in data with regard to the URB */ offset = 0; for (x = 0; x < urb->number_of_packets; x++) { uipd = urb->iso_frame_desc + x; if (uipd->length > xfer->frlengths[x]) { if (urb->transfer_flags & URB_SHORT_NOT_OK) { /* XXX should be EREMOTEIO */ uipd->status = -EPIPE; } else { uipd->status = 0; } } else { uipd->status = 0; } uipd->actual_length = xfer->frlengths[x]; if (!xfer->flags.ext_buffer) { usbd_copy_out(xfer->frbuffers, offset, USB_ADD_BYTES(urb->transfer_buffer, uipd->offset), uipd->actual_length); } offset += max_frame; } } else { for (x = 0; x < urb->number_of_packets; x++) { uipd = urb->iso_frame_desc + x; uipd->actual_length = xfer->frlengths[x]; uipd->status = 0; } } urb->actual_length = xfer->actlen; /* check for short transfer */ if (xfer->actlen < xfer->sumlen) { /* short transfer */ if (urb->transfer_flags & URB_SHORT_NOT_OK) { /* XXX should be EREMOTEIO */ urb->status = -EPIPE; } else { urb->status = 0; } } else { /* success */ urb->status = 0; } /* call callback */ usb_linux_complete(xfer); case USB_ST_SETUP: tr_setup: if (xfer->priv_fifo == NULL) { /* get next transfer */ urb = TAILQ_FIRST(&uhe->bsd_urb_list); if (urb == NULL) { /* nothing to do */ return; } TAILQ_REMOVE(&uhe->bsd_urb_list, urb, bsd_urb_list); urb->bsd_urb_list.tqe_prev = NULL; x = xfer->max_frame_count; if (urb->number_of_packets > x) { /* XXX simply truncate the transfer */ urb->number_of_packets = x; } } else { DPRINTF("Already got a transfer\n"); /* already got a transfer (should not happen) */ urb = usbd_xfer_get_priv(xfer); } urb->bsd_isread = (uhe->desc.bEndpointAddress & UE_DIR_IN) ? 1 : 0; if (xfer->flags.ext_buffer) { /* set virtual address to load */ usbd_xfer_set_frame_data(xfer, 0, urb->transfer_buffer, 0); } if (!(urb->bsd_isread)) { /* copy out data with regard to the URB */ offset = 0; for (x = 0; x < urb->number_of_packets; x++) { uipd = urb->iso_frame_desc + x; usbd_xfer_set_frame_len(xfer, x, uipd->length); if (!xfer->flags.ext_buffer) { usbd_copy_in(xfer->frbuffers, offset, USB_ADD_BYTES(urb->transfer_buffer, uipd->offset), uipd->length); } offset += uipd->length; } } else { /* * compute the transfer length into the "offset" * variable */ offset = urb->number_of_packets * max_frame; /* setup "frlengths" array */ for (x = 0; x < urb->number_of_packets; x++) { uipd = urb->iso_frame_desc + x; usbd_xfer_set_frame_len(xfer, x, max_frame); } } usbd_xfer_set_priv(xfer, urb); xfer->flags.force_short_xfer = 0; xfer->timeout = urb->timeout; xfer->nframes = urb->number_of_packets; usbd_transfer_submit(xfer); return; default: /* Error */ if (xfer->error == USB_ERR_CANCELLED) { urb->status = -ECONNRESET; } else { urb->status = -EPIPE; /* stalled */ } /* Set zero for "actual_length" */ urb->actual_length = 0; /* Set zero for "actual_length" */ for (x = 0; x < urb->number_of_packets; x++) { urb->iso_frame_desc[x].actual_length = 0; urb->iso_frame_desc[x].status = urb->status; } /* call callback */ usb_linux_complete(xfer); if (xfer->error == USB_ERR_CANCELLED) { /* we need to return in this case */ return; } goto tr_setup; } } /*------------------------------------------------------------------------* * usb_linux_non_isoc_callback * * The following is the FreeBSD BULK/INTERRUPT and CONTROL USB * callback. It dequeues Linux USB stack compatible URB's, transforms * the URB fields into a FreeBSD USB transfer, and defragments the USB * transfer as required. When the transfer is complete the "complete" * callback is called. *------------------------------------------------------------------------*/ static void usb_linux_non_isoc_callback(struct usb_xfer *xfer, usb_error_t error) { enum { REQ_SIZE = sizeof(struct usb_device_request) }; struct urb *urb = usbd_xfer_get_priv(xfer); struct usb_host_endpoint *uhe = usbd_xfer_softc(xfer); uint8_t *ptr; usb_frlength_t max_bulk = usbd_xfer_max_len(xfer); uint8_t data_frame = xfer->flags_int.control_xfr ? 1 : 0; DPRINTF("\n"); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (xfer->flags_int.control_xfr) { /* don't transfer the setup packet again: */ usbd_xfer_set_frame_len(xfer, 0, 0); } if (urb->bsd_isread && (!xfer->flags.ext_buffer)) { /* copy in data with regard to the URB */ usbd_copy_out(xfer->frbuffers + data_frame, 0, urb->bsd_data_ptr, xfer->frlengths[data_frame]); } urb->bsd_length_rem -= xfer->frlengths[data_frame]; urb->bsd_data_ptr += xfer->frlengths[data_frame]; urb->actual_length += xfer->frlengths[data_frame]; /* check for short transfer */ if (xfer->actlen < xfer->sumlen) { urb->bsd_length_rem = 0; /* short transfer */ if (urb->transfer_flags & URB_SHORT_NOT_OK) { urb->status = -EPIPE; } else { urb->status = 0; } } else { /* check remainder */ if (urb->bsd_length_rem > 0) { goto setup_bulk; } /* success */ urb->status = 0; } /* call callback */ usb_linux_complete(xfer); case USB_ST_SETUP: tr_setup: /* get next transfer */ urb = TAILQ_FIRST(&uhe->bsd_urb_list); if (urb == NULL) { /* nothing to do */ return; } TAILQ_REMOVE(&uhe->bsd_urb_list, urb, bsd_urb_list); urb->bsd_urb_list.tqe_prev = NULL; usbd_xfer_set_priv(xfer, urb); xfer->flags.force_short_xfer = 0; xfer->timeout = urb->timeout; if (xfer->flags_int.control_xfr) { /* * USB control transfers need special handling. * First copy in the header, then copy in data! */ if (!xfer->flags.ext_buffer) { usbd_copy_in(xfer->frbuffers, 0, urb->setup_packet, REQ_SIZE); usbd_xfer_set_frame_len(xfer, 0, REQ_SIZE); } else { /* set virtual address to load */ usbd_xfer_set_frame_data(xfer, 0, urb->setup_packet, REQ_SIZE); } ptr = urb->setup_packet; /* setup data transfer direction and length */ urb->bsd_isread = (ptr[0] & UT_READ) ? 1 : 0; urb->bsd_length_rem = ptr[6] | (ptr[7] << 8); } else { /* setup data transfer direction */ urb->bsd_length_rem = urb->transfer_buffer_length; urb->bsd_isread = (uhe->desc.bEndpointAddress & UE_DIR_IN) ? 1 : 0; } urb->bsd_data_ptr = urb->transfer_buffer; urb->actual_length = 0; setup_bulk: if (max_bulk > urb->bsd_length_rem) { max_bulk = urb->bsd_length_rem; } /* check if we need to force a short transfer */ if ((max_bulk == urb->bsd_length_rem) && (urb->transfer_flags & URB_ZERO_PACKET) && (!xfer->flags_int.control_xfr)) { xfer->flags.force_short_xfer = 1; } /* check if we need to copy in data */ if (xfer->flags.ext_buffer) { /* set virtual address to load */ usbd_xfer_set_frame_data(xfer, data_frame, urb->bsd_data_ptr, max_bulk); } else if (!urb->bsd_isread) { /* copy out data with regard to the URB */ usbd_copy_in(xfer->frbuffers + data_frame, 0, urb->bsd_data_ptr, max_bulk); usbd_xfer_set_frame_len(xfer, data_frame, max_bulk); } if (xfer->flags_int.control_xfr) { if (max_bulk > 0) { xfer->nframes = 2; } else { xfer->nframes = 1; } } else { xfer->nframes = 1; } usbd_transfer_submit(xfer); return; default: if (xfer->error == USB_ERR_CANCELLED) { urb->status = -ECONNRESET; } else { urb->status = -EPIPE; } /* Set zero for "actual_length" */ urb->actual_length = 0; /* call callback */ usb_linux_complete(xfer); if (xfer->error == USB_ERR_CANCELLED) { /* we need to return in this case */ return; } goto tr_setup; } } /*------------------------------------------------------------------------* * usb_fill_bulk_urb *------------------------------------------------------------------------*/ void usb_fill_bulk_urb(struct urb *urb, struct usb_device *udev, struct usb_host_endpoint *uhe, void *buf, int length, usb_complete_t callback, void *arg) { urb->dev = udev; urb->endpoint = uhe; urb->transfer_buffer = buf; urb->transfer_buffer_length = length; urb->complete = callback; urb->context = arg; } /*------------------------------------------------------------------------* * usb_bulk_msg * * NOTE: This function can also be used for interrupt endpoints! * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ int usb_bulk_msg(struct usb_device *udev, struct usb_host_endpoint *uhe, void *data, int len, uint16_t *pactlen, usb_timeout_t timeout) { struct urb *urb; int err; if (uhe == NULL) return (-EINVAL); if (len < 0) return (-EINVAL); err = usb_setup_endpoint(udev, uhe, 4096 /* bytes */); if (err) return (err); urb = usb_alloc_urb(0, 0); usb_fill_bulk_urb(urb, udev, uhe, data, len, usb_linux_wait_complete, NULL); err = usb_start_wait_urb(urb, timeout, pactlen); usb_free_urb(urb); return (err); } MODULE_DEPEND(linuxkpi, usb, 1, 1, 1); static void usb_linux_init(void *arg) { /* register our function */ usb_linux_free_device_p = &usb_linux_free_device; } SYSINIT(usb_linux_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb_linux_init, NULL); SYSUNINIT(usb_linux_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb_linux_unload, NULL); Index: head/sys/dev/usb/usb_device.c =================================================================== --- head/sys/dev/usb/usb_device.c (revision 368658) +++ head/sys/dev/usb/usb_device.c (revision 368659) @@ -1,3088 +1,3092 @@ /* $FreeBSD$ */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2008-2020 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if USB_HAVE_UGEN #include #endif #include "usbdevs.h" #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #include #if USB_HAVE_UGEN #include #include #endif #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ /* function prototypes */ static int sysctl_hw_usb_template(SYSCTL_HANDLER_ARGS); static void usb_init_endpoint(struct usb_device *, uint8_t, struct usb_endpoint_descriptor *, struct usb_endpoint_ss_comp_descriptor *, struct usb_endpoint *); static void usb_unconfigure(struct usb_device *, uint8_t); static void usb_detach_device_sub(struct usb_device *, device_t *, char **, uint8_t); static uint8_t usb_probe_and_attach_sub(struct usb_device *, struct usb_attach_arg *); static void usb_init_attach_arg(struct usb_device *, struct usb_attach_arg *); static void usb_suspend_resume_sub(struct usb_device *, device_t, uint8_t); static usb_proc_callback_t usbd_clear_stall_proc; static usb_error_t usb_config_parse(struct usb_device *, uint8_t, uint8_t); #if USB_HAVE_DEVCTL static void usb_notify_addq(const char *type, struct usb_device *); #endif #if USB_HAVE_UGEN static void usb_fifo_free_wrap(struct usb_device *, uint8_t, uint8_t); static void usb_cdev_create(struct usb_device *); static void usb_cdev_free(struct usb_device *); #endif /* This variable is global to allow easy access to it: */ #ifdef USB_TEMPLATE int usb_template = USB_TEMPLATE; #else int usb_template = -1; #endif SYSCTL_PROC(_hw_usb, OID_AUTO, template, CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, NULL, 0, sysctl_hw_usb_template, "I", "Selected USB device side template"); /*------------------------------------------------------------------------* * usb_trigger_reprobe_on_off * * This function sets the pull up resistors for all ports currently * operating in device mode either on (when on_not_off is 1), or off * (when it's 0). *------------------------------------------------------------------------*/ static void usb_trigger_reprobe_on_off(int on_not_off) { struct usb_port_status ps; struct usb_bus *bus; struct usb_device *udev; usb_error_t err; int do_unlock, max; max = devclass_get_maxunit(usb_devclass_ptr); while (max >= 0) { mtx_lock(&usb_ref_lock); bus = devclass_get_softc(usb_devclass_ptr, max); max--; if (bus == NULL || bus->devices == NULL || bus->devices[USB_ROOT_HUB_ADDR] == NULL) { mtx_unlock(&usb_ref_lock); continue; } udev = bus->devices[USB_ROOT_HUB_ADDR]; if (udev->refcount == USB_DEV_REF_MAX) { mtx_unlock(&usb_ref_lock); continue; } udev->refcount++; mtx_unlock(&usb_ref_lock); do_unlock = usbd_enum_lock(udev); if (do_unlock > 1) { do_unlock = 0; goto next; } err = usbd_req_get_port_status(udev, NULL, &ps, 1); if (err != 0) { DPRINTF("usbd_req_get_port_status() " "failed: %s\n", usbd_errstr(err)); goto next; } if ((UGETW(ps.wPortStatus) & UPS_PORT_MODE_DEVICE) == 0) goto next; if (on_not_off) { err = usbd_req_set_port_feature(udev, NULL, 1, UHF_PORT_POWER); if (err != 0) { DPRINTF("usbd_req_set_port_feature() " "failed: %s\n", usbd_errstr(err)); } } else { err = usbd_req_clear_port_feature(udev, NULL, 1, UHF_PORT_POWER); if (err != 0) { DPRINTF("usbd_req_clear_port_feature() " "failed: %s\n", usbd_errstr(err)); } } next: mtx_lock(&usb_ref_lock); if (do_unlock) usbd_enum_unlock(udev); if (--(udev->refcount) == 0) cv_broadcast(&udev->ref_cv); mtx_unlock(&usb_ref_lock); } } /*------------------------------------------------------------------------* * usb_trigger_reprobe_all * * This function toggles the pull up resistors for all ports currently * operating in device mode, causing the host machine to reenumerate them. *------------------------------------------------------------------------*/ static void usb_trigger_reprobe_all(void) { /* * Set the pull up resistors off for all ports in device mode. */ usb_trigger_reprobe_on_off(0); /* * According to the DWC OTG spec this must be at least 3ms. */ usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_POWER_DOWN_TIME)); /* * Set the pull up resistors back on. */ usb_trigger_reprobe_on_off(1); } static int sysctl_hw_usb_template(SYSCTL_HANDLER_ARGS) { int error, val; val = usb_template; error = sysctl_handle_int(oidp, &val, 0, req); if (error != 0 || req->newptr == NULL || usb_template == val) return (error); usb_template = val; if (usb_template < 0) { usb_trigger_reprobe_on_off(0); } else { usb_trigger_reprobe_all(); } return (0); } /* English is default language */ static int usb_lang_id = 0x0009; static int usb_lang_mask = 0x00FF; SYSCTL_INT(_hw_usb, OID_AUTO, usb_lang_id, CTLFLAG_RWTUN, &usb_lang_id, 0, "Preferred USB language ID"); SYSCTL_INT(_hw_usb, OID_AUTO, usb_lang_mask, CTLFLAG_RWTUN, &usb_lang_mask, 0, "Preferred USB language mask"); static const char* statestr[USB_STATE_MAX] = { [USB_STATE_DETACHED] = "DETACHED", [USB_STATE_ATTACHED] = "ATTACHED", [USB_STATE_POWERED] = "POWERED", [USB_STATE_ADDRESSED] = "ADDRESSED", [USB_STATE_CONFIGURED] = "CONFIGURED", }; const char * usb_statestr(enum usb_dev_state state) { return ((state < USB_STATE_MAX) ? statestr[state] : "UNKNOWN"); } const char * usb_get_manufacturer(struct usb_device *udev) { return (udev->manufacturer ? udev->manufacturer : "Unknown"); } const char * usb_get_product(struct usb_device *udev) { return (udev->product ? udev->product : ""); } const char * usb_get_serial(struct usb_device *udev) { return (udev->serial ? udev->serial : ""); } /*------------------------------------------------------------------------* * usbd_get_ep_by_addr * * This function searches for an USB ep by endpoint address and * direction. * * Returns: * NULL: Failure * Else: Success *------------------------------------------------------------------------*/ struct usb_endpoint * usbd_get_ep_by_addr(struct usb_device *udev, uint8_t ea_val) { struct usb_endpoint *ep = udev->endpoints; struct usb_endpoint *ep_end = udev->endpoints + udev->endpoints_max; enum { EA_MASK = (UE_DIR_IN | UE_DIR_OUT | UE_ADDR), }; /* * According to the USB specification not all bits are used * for the endpoint address. Keep defined bits only: */ ea_val &= EA_MASK; /* * Iterate across all the USB endpoints searching for a match * based on the endpoint address: */ for (; ep != ep_end; ep++) { if (ep->edesc == NULL) { continue; } /* do the mask and check the value */ if ((ep->edesc->bEndpointAddress & EA_MASK) == ea_val) { goto found; } } /* * The default endpoint is always present and is checked separately: */ if ((udev->ctrl_ep.edesc != NULL) && ((udev->ctrl_ep.edesc->bEndpointAddress & EA_MASK) == ea_val)) { ep = &udev->ctrl_ep; goto found; } return (NULL); found: return (ep); } /*------------------------------------------------------------------------* * usbd_get_endpoint * * This function searches for an USB endpoint based on the information * given by the passed "struct usb_config" pointer. * * Return values: * NULL: No match. * Else: Pointer to "struct usb_endpoint". *------------------------------------------------------------------------*/ struct usb_endpoint * usbd_get_endpoint(struct usb_device *udev, uint8_t iface_index, const struct usb_config *setup) { struct usb_endpoint *ep = udev->endpoints; struct usb_endpoint *ep_end = udev->endpoints + udev->endpoints_max; uint8_t index = setup->ep_index; uint8_t ea_mask; uint8_t ea_val; uint8_t type_mask; uint8_t type_val; DPRINTFN(10, "udev=%p iface_index=%d address=0x%x " "type=0x%x dir=0x%x index=%d\n", udev, iface_index, setup->endpoint, setup->type, setup->direction, setup->ep_index); /* check USB mode */ if (setup->usb_mode != USB_MODE_DUAL && udev->flags.usb_mode != setup->usb_mode) { /* wrong mode - no endpoint */ return (NULL); } /* setup expected endpoint direction mask and value */ if (setup->direction == UE_DIR_RX) { ea_mask = (UE_DIR_IN | UE_DIR_OUT); ea_val = (udev->flags.usb_mode == USB_MODE_DEVICE) ? UE_DIR_OUT : UE_DIR_IN; } else if (setup->direction == UE_DIR_TX) { ea_mask = (UE_DIR_IN | UE_DIR_OUT); ea_val = (udev->flags.usb_mode == USB_MODE_DEVICE) ? UE_DIR_IN : UE_DIR_OUT; } else if (setup->direction == UE_DIR_ANY) { /* match any endpoint direction */ ea_mask = 0; ea_val = 0; } else { /* match the given endpoint direction */ ea_mask = (UE_DIR_IN | UE_DIR_OUT); ea_val = (setup->direction & (UE_DIR_IN | UE_DIR_OUT)); } /* setup expected endpoint address */ if (setup->endpoint == UE_ADDR_ANY) { /* match any endpoint address */ } else { /* match the given endpoint address */ ea_mask |= UE_ADDR; ea_val |= (setup->endpoint & UE_ADDR); } /* setup expected endpoint type */ if (setup->type == UE_BULK_INTR) { /* this will match BULK and INTERRUPT endpoints */ type_mask = 2; type_val = 2; } else if (setup->type == UE_TYPE_ANY) { /* match any endpoint type */ type_mask = 0; type_val = 0; } else { /* match the given endpoint type */ type_mask = UE_XFERTYPE; type_val = (setup->type & UE_XFERTYPE); } /* * Iterate across all the USB endpoints searching for a match * based on the endpoint address. Note that we are searching * the endpoints from the beginning of the "udev->endpoints" array. */ for (; ep != ep_end; ep++) { if ((ep->edesc == NULL) || (ep->iface_index != iface_index)) { continue; } /* do the masks and check the values */ if (((ep->edesc->bEndpointAddress & ea_mask) == ea_val) && ((ep->edesc->bmAttributes & type_mask) == type_val)) { if (!index--) { goto found; } } } /* * Match against default endpoint last, so that "any endpoint", "any * address" and "any direction" returns the first endpoint of the * interface. "iface_index" and "direction" is ignored: */ if ((udev->ctrl_ep.edesc != NULL) && ((udev->ctrl_ep.edesc->bEndpointAddress & ea_mask) == ea_val) && ((udev->ctrl_ep.edesc->bmAttributes & type_mask) == type_val) && (!index)) { ep = &udev->ctrl_ep; goto found; } return (NULL); found: return (ep); } /*------------------------------------------------------------------------* * usbd_interface_count * * This function stores the number of USB interfaces excluding * alternate settings, which the USB config descriptor reports into * the unsigned 8-bit integer pointed to by "count". * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_interface_count(struct usb_device *udev, uint8_t *count) { if (udev->cdesc == NULL) { *count = 0; return (USB_ERR_NOT_CONFIGURED); } *count = udev->ifaces_max; return (USB_ERR_NORMAL_COMPLETION); } /*------------------------------------------------------------------------* * usb_init_endpoint * * This function will initialise the USB endpoint structure pointed to by * the "endpoint" argument. The structure pointed to by "endpoint" must be * zeroed before calling this function. *------------------------------------------------------------------------*/ static void usb_init_endpoint(struct usb_device *udev, uint8_t iface_index, struct usb_endpoint_descriptor *edesc, struct usb_endpoint_ss_comp_descriptor *ecomp, struct usb_endpoint *ep) { const struct usb_bus_methods *methods; usb_stream_t x; methods = udev->bus->methods; (methods->endpoint_init) (udev, edesc, ep); /* initialise USB endpoint structure */ ep->edesc = edesc; ep->ecomp = ecomp; ep->iface_index = iface_index; /* setup USB stream queues */ for (x = 0; x != USB_MAX_EP_STREAMS; x++) { TAILQ_INIT(&ep->endpoint_q[x].head); ep->endpoint_q[x].command = &usbd_pipe_start; } /* the pipe is not supported by the hardware */ if (ep->methods == NULL) return; /* check for SUPER-speed streams mode endpoint */ if (udev->speed == USB_SPEED_SUPER && ecomp != NULL && (edesc->bmAttributes & UE_XFERTYPE) == UE_BULK && (UE_GET_BULK_STREAMS(ecomp->bmAttributes) != 0)) { usbd_set_endpoint_mode(udev, ep, USB_EP_MODE_STREAMS); } else { usbd_set_endpoint_mode(udev, ep, USB_EP_MODE_DEFAULT); } /* clear stall, if any */ if (methods->clear_stall != NULL) { USB_BUS_LOCK(udev->bus); (methods->clear_stall) (udev, ep); USB_BUS_UNLOCK(udev->bus); } } /*-----------------------------------------------------------------------* * usb_endpoint_foreach * * This function will iterate all the USB endpoints except the control * endpoint. This function is NULL safe. * * Return values: * NULL: End of USB endpoints * Else: Pointer to next USB endpoint *------------------------------------------------------------------------*/ struct usb_endpoint * usb_endpoint_foreach(struct usb_device *udev, struct usb_endpoint *ep) { struct usb_endpoint *ep_end; /* be NULL safe */ if (udev == NULL) return (NULL); ep_end = udev->endpoints + udev->endpoints_max; /* get next endpoint */ if (ep == NULL) ep = udev->endpoints; else ep++; /* find next allocated ep */ while (ep != ep_end) { if (ep->edesc != NULL) return (ep); ep++; } return (NULL); } /*------------------------------------------------------------------------* * usb_wait_pending_refs * * This function will wait for any USB references to go away before * returning. This function is used before freeing a USB device. *------------------------------------------------------------------------*/ static void usb_wait_pending_refs(struct usb_device *udev) { #if USB_HAVE_UGEN DPRINTF("Refcount = %d\n", (int)udev->refcount); mtx_lock(&usb_ref_lock); udev->refcount--; while (1) { /* wait for any pending references to go away */ if (udev->refcount == 0) { /* prevent further refs being taken, if any */ udev->refcount = USB_DEV_REF_MAX; break; } cv_wait(&udev->ref_cv, &usb_ref_lock); } mtx_unlock(&usb_ref_lock); #endif } /*------------------------------------------------------------------------* * usb_unconfigure * * This function will free all USB interfaces and USB endpoints belonging * to an USB device. * * Flag values, see "USB_UNCFG_FLAG_XXX". *------------------------------------------------------------------------*/ static void usb_unconfigure(struct usb_device *udev, uint8_t flag) { uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); /* detach all interface drivers */ usb_detach_device(udev, USB_IFACE_INDEX_ANY, flag); #if USB_HAVE_UGEN /* free all FIFOs except control endpoint FIFOs */ usb_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, flag); /* * Free all cdev's, if any. */ usb_cdev_free(udev); #endif #if USB_HAVE_COMPAT_LINUX /* free Linux compat device, if any */ if (udev->linux_endpoint_start != NULL) { usb_linux_free_device_p(udev); udev->linux_endpoint_start = NULL; } #endif usb_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_FREE); /* free "cdesc" after "ifaces" and "endpoints", if any */ if (udev->cdesc != NULL) { if (udev->flags.usb_mode != USB_MODE_DEVICE) usbd_free_config_desc(udev, udev->cdesc); udev->cdesc = NULL; } /* set unconfigured state */ udev->curr_config_no = USB_UNCONFIG_NO; udev->curr_config_index = USB_UNCONFIG_INDEX; if (do_unlock) usbd_enum_unlock(udev); } /*------------------------------------------------------------------------* * usbd_set_config_index * * This function selects configuration by index, independent of the * actual configuration number. This function should not be used by * USB drivers. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_set_config_index(struct usb_device *udev, uint8_t index) { struct usb_status ds; struct usb_config_descriptor *cdp; uint16_t power; uint16_t max_power; uint8_t selfpowered; uint8_t do_unlock; usb_error_t err; DPRINTFN(6, "udev=%p index=%d\n", udev, index); /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); usb_unconfigure(udev, 0); if (index == USB_UNCONFIG_INDEX) { /* * Leave unallocated when unconfiguring the * device. "usb_unconfigure()" will also reset * the current config number and index. */ err = usbd_req_set_config(udev, NULL, USB_UNCONFIG_NO); if (udev->state == USB_STATE_CONFIGURED) usb_set_device_state(udev, USB_STATE_ADDRESSED); goto done; } /* get the full config descriptor */ if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* save some memory */ err = usbd_req_get_descriptor_ptr(udev, &cdp, (UDESC_CONFIG << 8) | index); } else { /* normal request */ err = usbd_req_get_config_desc_full(udev, NULL, &cdp, index); } if (err) { goto done; } /* set the new config descriptor */ udev->cdesc = cdp; /* Figure out if the device is self or bus powered. */ selfpowered = 0; if ((!udev->flags.uq_bus_powered) && (cdp->bmAttributes & UC_SELF_POWERED) && (udev->flags.usb_mode == USB_MODE_HOST)) { /* May be self powered. */ if (cdp->bmAttributes & UC_BUS_POWERED) { /* Must ask device. */ err = usbd_req_get_device_status(udev, NULL, &ds); if (err) { DPRINTFN(0, "could not read " "device status: %s\n", usbd_errstr(err)); } else if (UGETW(ds.wStatus) & UDS_SELF_POWERED) { selfpowered = 1; } DPRINTF("status=0x%04x \n", UGETW(ds.wStatus)); } else selfpowered = 1; } DPRINTF("udev=%p cdesc=%p (addr %d) cno=%d attr=0x%02x, " "selfpowered=%d, power=%d\n", udev, cdp, udev->address, cdp->bConfigurationValue, cdp->bmAttributes, selfpowered, cdp->bMaxPower * 2); /* Check if we have enough power. */ power = cdp->bMaxPower * 2; if (udev->parent_hub) { max_power = udev->parent_hub->hub->portpower; } else { max_power = USB_MAX_POWER; } if (power > max_power) { DPRINTFN(0, "power exceeded %d > %d\n", power, max_power); err = USB_ERR_NO_POWER; goto done; } /* Only update "self_powered" in USB Host Mode */ if (udev->flags.usb_mode == USB_MODE_HOST) { udev->flags.self_powered = selfpowered; } udev->power = power; udev->curr_config_no = cdp->bConfigurationValue; udev->curr_config_index = index; usb_set_device_state(udev, USB_STATE_CONFIGURED); /* Set the actual configuration value. */ err = usbd_req_set_config(udev, NULL, cdp->bConfigurationValue); if (err) { goto done; } err = usb_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_ALLOC); if (err) { goto done; } err = usb_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_INIT); if (err) { goto done; } #if USB_HAVE_UGEN /* create device nodes for each endpoint */ usb_cdev_create(udev); #endif done: DPRINTF("error=%s\n", usbd_errstr(err)); if (err) { usb_unconfigure(udev, 0); } if (do_unlock) usbd_enum_unlock(udev); return (err); } /*------------------------------------------------------------------------* * usb_config_parse * * This function will allocate and free USB interfaces and USB endpoints, * parse the USB configuration structure and initialise the USB endpoints * and interfaces. If "iface_index" is not equal to * "USB_IFACE_INDEX_ANY" then the "cmd" parameter is the * alternate_setting to be selected for the given interface. Else the * "cmd" parameter is defined by "USB_CFG_XXX". "iface_index" can be * "USB_IFACE_INDEX_ANY" or a valid USB interface index. This function * is typically called when setting the configuration or when setting * an alternate interface. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_config_parse(struct usb_device *udev, uint8_t iface_index, uint8_t cmd) { struct usb_idesc_parse_state ips; struct usb_interface_descriptor *id; struct usb_endpoint_descriptor *ed; struct usb_interface *iface; struct usb_endpoint *ep; usb_error_t err; uint8_t ep_curr; uint8_t ep_max; uint8_t temp; uint8_t do_init; uint8_t alt_index; if (iface_index != USB_IFACE_INDEX_ANY) { /* parameter overload */ alt_index = cmd; cmd = USB_CFG_INIT; } else { /* not used */ alt_index = 0; } err = 0; DPRINTFN(5, "iface_index=%d cmd=%d\n", iface_index, cmd); if (cmd == USB_CFG_FREE) goto cleanup; if (cmd == USB_CFG_INIT) { sx_assert(&udev->enum_sx, SA_LOCKED); /* check for in-use endpoints */ ep = udev->endpoints; ep_max = udev->endpoints_max; while (ep_max--) { /* look for matching endpoints */ if ((iface_index == USB_IFACE_INDEX_ANY) || (iface_index == ep->iface_index)) { if (ep->refcount_alloc != 0) { /* * This typically indicates a * more serious error. */ err = USB_ERR_IN_USE; } else { /* reset endpoint */ memset(ep, 0, sizeof(*ep)); /* make sure we don't zero the endpoint again */ ep->iface_index = USB_IFACE_INDEX_ANY; } } ep++; } if (err) return (err); } memset(&ips, 0, sizeof(ips)); ep_curr = 0; ep_max = 0; while ((id = usb_idesc_foreach(udev->cdesc, &ips))) { iface = udev->ifaces + ips.iface_index; /* check for specific interface match */ if (cmd == USB_CFG_INIT) { if ((iface_index != USB_IFACE_INDEX_ANY) && (iface_index != ips.iface_index)) { /* wrong interface */ do_init = 0; } else if (alt_index != ips.iface_index_alt) { /* wrong alternate setting */ do_init = 0; } else { /* initialise interface */ do_init = 1; } } else do_init = 0; /* check for new interface */ if (ips.iface_index_alt == 0) { /* update current number of endpoints */ ep_curr = ep_max; } + /* update number of alternate settings, if any */ + if (iface_index == USB_IFACE_INDEX_ANY) + iface->num_altsetting = ips.iface_index_alt + 1; + /* check for init */ if (do_init) { /* setup the USB interface structure */ iface->idesc = id; /* set alternate index */ iface->alt_index = alt_index; /* set default interface parent */ if (iface_index == USB_IFACE_INDEX_ANY) { iface->parent_iface_index = USB_IFACE_INDEX_ANY; } } DPRINTFN(5, "found idesc nendpt=%d\n", id->bNumEndpoints); ed = (struct usb_endpoint_descriptor *)id; temp = ep_curr; /* iterate all the endpoint descriptors */ while ((ed = usb_edesc_foreach(udev->cdesc, ed))) { /* check if endpoint limit has been reached */ if (temp >= USB_MAX_EP_UNITS) { DPRINTF("Endpoint limit reached\n"); break; } ep = udev->endpoints + temp; if (do_init) { void *ecomp; ecomp = usb_ed_comp_foreach(udev->cdesc, (void *)ed); if (ecomp != NULL) DPRINTFN(5, "Found endpoint companion descriptor\n"); usb_init_endpoint(udev, ips.iface_index, ed, ecomp, ep); } temp ++; /* find maximum number of endpoints */ if (ep_max < temp) ep_max = temp; } } /* NOTE: It is valid to have no interfaces and no endpoints! */ if (cmd == USB_CFG_ALLOC) { udev->ifaces_max = ips.iface_index; #if (USB_HAVE_FIXED_IFACE == 0) udev->ifaces = NULL; if (udev->ifaces_max != 0) { udev->ifaces = malloc(sizeof(*iface) * udev->ifaces_max, M_USB, M_WAITOK | M_ZERO); if (udev->ifaces == NULL) { err = USB_ERR_NOMEM; goto done; } } #endif #if (USB_HAVE_FIXED_ENDPOINT == 0) if (ep_max != 0) { udev->endpoints = malloc(sizeof(*ep) * ep_max, M_USB, M_WAITOK | M_ZERO); if (udev->endpoints == NULL) { err = USB_ERR_NOMEM; goto done; } } else { udev->endpoints = NULL; } #endif USB_BUS_LOCK(udev->bus); udev->endpoints_max = ep_max; /* reset any ongoing clear-stall */ udev->ep_curr = NULL; USB_BUS_UNLOCK(udev->bus); } #if (USB_HAVE_FIXED_IFACE == 0) || (USB_HAVE_FIXED_ENDPOINT == 0) done: #endif if (err) { if (cmd == USB_CFG_ALLOC) { cleanup: USB_BUS_LOCK(udev->bus); udev->endpoints_max = 0; /* reset any ongoing clear-stall */ udev->ep_curr = NULL; USB_BUS_UNLOCK(udev->bus); #if (USB_HAVE_FIXED_IFACE == 0) free(udev->ifaces, M_USB); udev->ifaces = NULL; #endif #if (USB_HAVE_FIXED_ENDPOINT == 0) free(udev->endpoints, M_USB); udev->endpoints = NULL; #endif udev->ifaces_max = 0; } } return (err); } /*------------------------------------------------------------------------* * usbd_set_alt_interface_index * * This function will select an alternate interface index for the * given interface index. The interface should not be in use when this * function is called. That means there should not be any open USB * transfers. Else an error is returned. If the alternate setting is * already set this function will simply return success. This function * is called in Host mode and Device mode! * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_set_alt_interface_index(struct usb_device *udev, uint8_t iface_index, uint8_t alt_index) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); usb_error_t err; uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (iface == NULL) { err = USB_ERR_INVAL; goto done; } if (iface->alt_index == alt_index) { /* * Optimise away duplicate setting of * alternate setting in USB Host Mode! */ err = 0; goto done; } #if USB_HAVE_UGEN /* * Free all generic FIFOs for this interface, except control * endpoint FIFOs: */ usb_fifo_free_wrap(udev, iface_index, 0); #endif err = usb_config_parse(udev, iface_index, alt_index); if (err) { goto done; } if (iface->alt_index != alt_index) { /* the alternate setting does not exist */ err = USB_ERR_INVAL; goto done; } err = usbd_req_set_alt_interface_no(udev, NULL, iface_index, iface->idesc->bAlternateSetting); done: if (do_unlock) usbd_enum_unlock(udev); return (err); } /*------------------------------------------------------------------------* * usbd_set_endpoint_stall * * This function is used to make a BULK or INTERRUPT endpoint send * STALL tokens in USB device mode. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_set_endpoint_stall(struct usb_device *udev, struct usb_endpoint *ep, uint8_t do_stall) { struct usb_xfer *xfer; usb_stream_t x; uint8_t et; uint8_t was_stalled; if (ep == NULL) { /* nothing to do */ DPRINTF("Cannot find endpoint\n"); /* * Pretend that the clear or set stall request is * successful else some USB host stacks can do * strange things, especially when a control endpoint * stalls. */ return (0); } et = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((et != UE_BULK) && (et != UE_INTERRUPT)) { /* * Should not stall control * nor isochronous endpoints. */ DPRINTF("Invalid endpoint\n"); return (0); } USB_BUS_LOCK(udev->bus); /* store current stall state */ was_stalled = ep->is_stalled; /* check for no change */ if (was_stalled && do_stall) { /* if the endpoint is already stalled do nothing */ USB_BUS_UNLOCK(udev->bus); DPRINTF("No change\n"); return (0); } /* set stalled state */ ep->is_stalled = 1; if (do_stall || (!was_stalled)) { if (!was_stalled) { for (x = 0; x != USB_MAX_EP_STREAMS; x++) { /* lookup the current USB transfer, if any */ xfer = ep->endpoint_q[x].curr; if (xfer != NULL) { /* * The "xfer_stall" method * will complete the USB * transfer like in case of a * timeout setting the error * code "USB_ERR_STALLED". */ (udev->bus->methods->xfer_stall) (xfer); } } } (udev->bus->methods->set_stall) (udev, ep, &do_stall); } if (!do_stall) { ep->toggle_next = 0; /* reset data toggle */ ep->is_stalled = 0; /* clear stalled state */ (udev->bus->methods->clear_stall) (udev, ep); /* start the current or next transfer, if any */ for (x = 0; x != USB_MAX_EP_STREAMS; x++) { usb_command_wrapper(&ep->endpoint_q[x], ep->endpoint_q[x].curr); } } USB_BUS_UNLOCK(udev->bus); return (0); } /*------------------------------------------------------------------------* * usb_reset_iface_endpoints - used in USB device side mode *------------------------------------------------------------------------*/ usb_error_t usb_reset_iface_endpoints(struct usb_device *udev, uint8_t iface_index) { struct usb_endpoint *ep; struct usb_endpoint *ep_end; ep = udev->endpoints; ep_end = udev->endpoints + udev->endpoints_max; for (; ep != ep_end; ep++) { if ((ep->edesc == NULL) || (ep->iface_index != iface_index)) { continue; } /* simulate a clear stall from the peer */ usbd_set_endpoint_stall(udev, ep, 0); } return (0); } /*------------------------------------------------------------------------* * usb_detach_device_sub * * This function will try to detach an USB device. If it fails a panic * will result. * * Flag values, see "USB_UNCFG_FLAG_XXX". *------------------------------------------------------------------------*/ static void usb_detach_device_sub(struct usb_device *udev, device_t *ppdev, char **ppnpinfo, uint8_t flag) { device_t dev; char *pnpinfo; int err; dev = *ppdev; if (dev) { /* * NOTE: It is important to clear "*ppdev" before deleting * the child due to some device methods being called late * during the delete process ! */ *ppdev = NULL; if (!rebooting) { device_printf(dev, "at %s, port %d, addr %d " "(disconnected)\n", device_get_nameunit(udev->parent_dev), udev->port_no, udev->address); } if (device_is_attached(dev)) { if (udev->flags.peer_suspended) { err = DEVICE_RESUME(dev); if (err) { device_printf(dev, "Resume failed\n"); } } } /* detach and delete child */ if (device_delete_child(udev->parent_dev, dev)) { goto error; } } pnpinfo = *ppnpinfo; if (pnpinfo != NULL) { *ppnpinfo = NULL; free(pnpinfo, M_USBDEV); } return; error: /* Detach is not allowed to fail in the USB world */ panic("usb_detach_device_sub: A USB driver would not detach\n"); } /*------------------------------------------------------------------------* * usb_detach_device * * The following function will detach the matching interfaces. * This function is NULL safe. * * Flag values, see "USB_UNCFG_FLAG_XXX". *------------------------------------------------------------------------*/ void usb_detach_device(struct usb_device *udev, uint8_t iface_index, uint8_t flag) { struct usb_interface *iface; uint8_t i; if (udev == NULL) { /* nothing to do */ return; } DPRINTFN(4, "udev=%p\n", udev); sx_assert(&udev->enum_sx, SA_LOCKED); /* * First detach the child to give the child's detach routine a * chance to detach the sub-devices in the correct order. * Then delete the child using "device_delete_child()" which * will detach all sub-devices from the bottom and upwards! */ if (iface_index != USB_IFACE_INDEX_ANY) { i = iface_index; iface_index = i + 1; } else { i = 0; iface_index = USB_IFACE_MAX; } /* do the detach */ for (; i != iface_index; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) { /* looks like the end of the USB interfaces */ break; } usb_detach_device_sub(udev, &iface->subdev, &iface->pnpinfo, flag); } } /*------------------------------------------------------------------------* * usb_probe_and_attach_sub * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static uint8_t usb_probe_and_attach_sub(struct usb_device *udev, struct usb_attach_arg *uaa) { struct usb_interface *iface; device_t dev; int err; iface = uaa->iface; if (iface->parent_iface_index != USB_IFACE_INDEX_ANY) { /* leave interface alone */ return (0); } dev = iface->subdev; if (dev) { /* clean up after module unload */ if (device_is_attached(dev)) { /* already a device there */ return (0); } /* clear "iface->subdev" as early as possible */ iface->subdev = NULL; if (device_delete_child(udev->parent_dev, dev)) { /* * Panic here, else one can get a double call * to device_detach(). USB devices should * never fail on detach! */ panic("device_delete_child() failed\n"); } } if (uaa->temp_dev == NULL) { /* create a new child */ uaa->temp_dev = device_add_child(udev->parent_dev, NULL, -1); if (uaa->temp_dev == NULL) { device_printf(udev->parent_dev, "Device creation failed\n"); return (1); /* failure */ } device_set_ivars(uaa->temp_dev, uaa); device_quiet(uaa->temp_dev); } /* * Set "subdev" before probe and attach so that "devd" gets * the information it needs. */ iface->subdev = uaa->temp_dev; if (device_probe_and_attach(iface->subdev) == 0) { /* * The USB attach arguments are only available during probe * and attach ! */ uaa->temp_dev = NULL; device_set_ivars(iface->subdev, NULL); if (udev->flags.peer_suspended) { err = DEVICE_SUSPEND(iface->subdev); if (err) device_printf(iface->subdev, "Suspend failed\n"); } return (0); /* success */ } else { /* No USB driver found */ iface->subdev = NULL; } return (1); /* failure */ } /*------------------------------------------------------------------------* * usbd_set_parent_iface * * Using this function will lock the alternate interface setting on an * interface. It is typically used for multi interface drivers. In USB * device side mode it is assumed that the alternate interfaces all * have the same endpoint descriptors. The default parent index value * is "USB_IFACE_INDEX_ANY". Then the alternate setting value is not * locked. *------------------------------------------------------------------------*/ void usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index, uint8_t parent_index) { struct usb_interface *iface; if (udev == NULL || iface_index == parent_index) { /* nothing to do */ return; } iface = usbd_get_iface(udev, iface_index); if (iface != NULL) iface->parent_iface_index = parent_index; } static void usb_init_attach_arg(struct usb_device *udev, struct usb_attach_arg *uaa) { memset(uaa, 0, sizeof(*uaa)); uaa->device = udev; uaa->usb_mode = udev->flags.usb_mode; uaa->port = udev->port_no; uaa->dev_state = UAA_DEV_READY; uaa->info.idVendor = UGETW(udev->ddesc.idVendor); uaa->info.idProduct = UGETW(udev->ddesc.idProduct); uaa->info.bcdDevice = UGETW(udev->ddesc.bcdDevice); uaa->info.bDeviceClass = udev->ddesc.bDeviceClass; uaa->info.bDeviceSubClass = udev->ddesc.bDeviceSubClass; uaa->info.bDeviceProtocol = udev->ddesc.bDeviceProtocol; uaa->info.bConfigIndex = udev->curr_config_index; uaa->info.bConfigNum = udev->curr_config_no; } /*------------------------------------------------------------------------* * usb_probe_and_attach * * This function is called from "uhub_explore_sub()", * "usb_handle_set_config()" and "usb_handle_request()". * * Returns: * 0: Success * Else: A control transfer failed *------------------------------------------------------------------------*/ usb_error_t usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index) { struct usb_attach_arg uaa; struct usb_interface *iface; uint8_t i; uint8_t j; uint8_t do_unlock; if (udev == NULL) { DPRINTF("udev == NULL\n"); return (USB_ERR_INVAL); } /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (udev->curr_config_index == USB_UNCONFIG_INDEX) { /* do nothing - no configuration has been set */ goto done; } /* setup USB attach arguments */ usb_init_attach_arg(udev, &uaa); /* * If the whole USB device is targeted, invoke the USB event * handler(s): */ if (iface_index == USB_IFACE_INDEX_ANY) { if (usb_test_quirk(&uaa, UQ_MSC_DYMO_EJECT) != 0 && usb_dymo_eject(udev, 0) == 0) { /* success, mark the udev as disappearing */ uaa.dev_state = UAA_DEV_EJECTING; } EVENTHANDLER_INVOKE(usb_dev_configured, udev, &uaa); if (uaa.dev_state != UAA_DEV_READY) { /* leave device unconfigured */ usb_unconfigure(udev, 0); goto done; } } /* Check if only one interface should be probed: */ if (iface_index != USB_IFACE_INDEX_ANY) { i = iface_index; j = i + 1; } else { i = 0; j = USB_IFACE_MAX; } /* Do the probe and attach */ for (; i != j; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) { /* * Looks like the end of the USB * interfaces ! */ DPRINTFN(2, "end of interfaces " "at %u\n", i); break; } if (iface->idesc == NULL) { /* no interface descriptor */ continue; } uaa.iface = iface; uaa.info.bInterfaceClass = iface->idesc->bInterfaceClass; uaa.info.bInterfaceSubClass = iface->idesc->bInterfaceSubClass; uaa.info.bInterfaceProtocol = iface->idesc->bInterfaceProtocol; uaa.info.bIfaceIndex = i; uaa.info.bIfaceNum = iface->idesc->bInterfaceNumber; uaa.driver_info = 0; /* reset driver_info */ DPRINTFN(2, "iclass=%u/%u/%u iindex=%u/%u\n", uaa.info.bInterfaceClass, uaa.info.bInterfaceSubClass, uaa.info.bInterfaceProtocol, uaa.info.bIfaceIndex, uaa.info.bIfaceNum); usb_probe_and_attach_sub(udev, &uaa); /* * Remove the leftover child, if any, to enforce that * a new nomatch devd event is generated for the next * interface if no driver is found: */ if (uaa.temp_dev == NULL) continue; if (device_delete_child(udev->parent_dev, uaa.temp_dev)) DPRINTFN(0, "device delete child failed\n"); uaa.temp_dev = NULL; } done: if (do_unlock) usbd_enum_unlock(udev); return (0); } /*------------------------------------------------------------------------* * usb_suspend_resume_sub * * This function is called when the suspend or resume methods should * be executed on an USB device. *------------------------------------------------------------------------*/ static void usb_suspend_resume_sub(struct usb_device *udev, device_t dev, uint8_t do_suspend) { int err; if (dev == NULL) { return; } if (!device_is_attached(dev)) { return; } if (do_suspend) { err = DEVICE_SUSPEND(dev); } else { err = DEVICE_RESUME(dev); } if (err) { device_printf(dev, "%s failed\n", do_suspend ? "Suspend" : "Resume"); } } /*------------------------------------------------------------------------* * usb_suspend_resume * * The following function will suspend or resume the USB device. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usb_suspend_resume(struct usb_device *udev, uint8_t do_suspend) { struct usb_interface *iface; uint8_t i; if (udev == NULL) { /* nothing to do */ return (0); } DPRINTFN(4, "udev=%p do_suspend=%d\n", udev, do_suspend); sx_assert(&udev->sr_sx, SA_LOCKED); USB_BUS_LOCK(udev->bus); /* filter the suspend events */ if (udev->flags.peer_suspended == do_suspend) { USB_BUS_UNLOCK(udev->bus); /* nothing to do */ return (0); } udev->flags.peer_suspended = do_suspend; USB_BUS_UNLOCK(udev->bus); /* do the suspend or resume */ for (i = 0; i != USB_IFACE_MAX; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) { /* looks like the end of the USB interfaces */ break; } usb_suspend_resume_sub(udev, iface->subdev, do_suspend); } return (0); } /*------------------------------------------------------------------------* * usbd_clear_stall_proc * * This function performs generic USB clear stall operations. *------------------------------------------------------------------------*/ static void usbd_clear_stall_proc(struct usb_proc_msg *_pm) { struct usb_udev_msg *pm = (void *)_pm; struct usb_device *udev = pm->udev; /* Change lock */ USB_BUS_UNLOCK(udev->bus); USB_MTX_LOCK(&udev->device_mtx); /* Start clear stall callback */ usbd_transfer_start(udev->ctrl_xfer[1]); /* Change lock */ USB_MTX_UNLOCK(&udev->device_mtx); USB_BUS_LOCK(udev->bus); } /*------------------------------------------------------------------------* * usb_get_langid * * This function tries to figure out the USB string language to use. *------------------------------------------------------------------------*/ void usb_get_langid(struct usb_device *udev) { uint8_t *scratch_ptr; uint8_t do_unlock; int err; /* * Workaround for buggy USB devices. * * It appears that some string-less USB chips will crash and * disappear if any attempts are made to read any string * descriptors. * * Try to detect such chips by checking the strings in the USB * device descriptor. If no strings are present there we * simply disable all USB strings. */ /* Protect scratch area */ do_unlock = usbd_ctrl_lock(udev); scratch_ptr = udev->scratch.data; if (udev->flags.no_strings) { err = USB_ERR_INVAL; } else if (udev->ddesc.iManufacturer || udev->ddesc.iProduct || udev->ddesc.iSerialNumber) { /* read out the language ID string */ err = usbd_req_get_string_desc(udev, NULL, (char *)scratch_ptr, 4, 0, USB_LANGUAGE_TABLE); } else { err = USB_ERR_INVAL; } if (err || (scratch_ptr[0] < 4)) { udev->flags.no_strings = 1; } else { uint16_t langid; uint16_t pref; uint16_t mask; uint8_t x; /* load preferred value and mask */ pref = usb_lang_id; mask = usb_lang_mask; /* align length correctly */ scratch_ptr[0] &= ~1U; /* fix compiler warning */ langid = 0; /* search for preferred language */ for (x = 2; x < scratch_ptr[0]; x += 2) { langid = UGETW(scratch_ptr + x); if ((langid & mask) == pref) break; } if (x >= scratch_ptr[0]) { /* pick the first language as the default */ DPRINTFN(1, "Using first language\n"); langid = UGETW(scratch_ptr + 2); } DPRINTFN(1, "Language selected: 0x%04x\n", langid); udev->langid = langid; } if (do_unlock) usbd_ctrl_unlock(udev); } /*------------------------------------------------------------------------* * usb_alloc_device * * This function allocates a new USB device. This function is called * when a new device has been put in the powered state, but not yet in * the addressed state. Get initial descriptor, set the address, get * full descriptor and get strings. * * Return values: * 0: Failure * Else: Success *------------------------------------------------------------------------*/ struct usb_device * usb_alloc_device(device_t parent_dev, struct usb_bus *bus, struct usb_device *parent_hub, uint8_t depth, uint8_t port_index, uint8_t port_no, enum usb_dev_speed speed, enum usb_hc_mode mode) { struct usb_attach_arg uaa; struct usb_device *udev; struct usb_device *adev; struct usb_device *hub; usb_error_t err; uint8_t device_index; uint8_t config_index; uint8_t config_quirk; uint8_t set_config_failed; DPRINTF("parent_dev=%p, bus=%p, parent_hub=%p, depth=%u, " "port_index=%u, port_no=%u, speed=%u, usb_mode=%u\n", parent_dev, bus, parent_hub, depth, port_index, port_no, speed, mode); /* * Find an unused device index. In USB Host mode this is the * same as the device address. * * Device index zero is not used and device index 1 should * always be the root hub. */ for (device_index = USB_ROOT_HUB_ADDR; (device_index != bus->devices_max) && (bus->devices[device_index] != NULL); device_index++) /* nop */; if (device_index == bus->devices_max) { device_printf(bus->bdev, "No free USB device index for new device\n"); return (NULL); } if (depth > 0x10) { device_printf(bus->bdev, "Invalid device depth\n"); return (NULL); } udev = malloc(sizeof(*udev), M_USB, M_WAITOK | M_ZERO); #if (USB_HAVE_MALLOC_WAITOK == 0) if (udev == NULL) { return (NULL); } #endif /* initialise our SX-lock */ sx_init_flags(&udev->enum_sx, "USB config SX lock", SX_DUPOK); sx_init_flags(&udev->sr_sx, "USB suspend and resume SX lock", SX_NOWITNESS); sx_init_flags(&udev->ctrl_sx, "USB control transfer SX lock", SX_DUPOK); cv_init(&udev->ctrlreq_cv, "WCTRL"); cv_init(&udev->ref_cv, "UGONE"); /* initialise our mutex */ mtx_init(&udev->device_mtx, "USB device mutex", NULL, MTX_DEF); /* initialise generic clear stall */ udev->cs_msg[0].hdr.pm_callback = &usbd_clear_stall_proc; udev->cs_msg[0].udev = udev; udev->cs_msg[1].hdr.pm_callback = &usbd_clear_stall_proc; udev->cs_msg[1].udev = udev; /* initialise some USB device fields */ udev->parent_hub = parent_hub; udev->parent_dev = parent_dev; udev->port_index = port_index; udev->port_no = port_no; udev->depth = depth; udev->bus = bus; udev->address = USB_START_ADDR; /* default value */ udev->plugtime = (usb_ticks_t)ticks; /* * We need to force the power mode to "on" because there are plenty * of USB devices out there that do not work very well with * automatic suspend and resume! */ udev->power_mode = usbd_filter_power_mode(udev, USB_POWER_MODE_ON); udev->pwr_save.last_xfer_time = ticks; /* we are not ready yet */ udev->refcount = 1; /* set up default endpoint descriptor */ udev->ctrl_ep_desc.bLength = sizeof(udev->ctrl_ep_desc); udev->ctrl_ep_desc.bDescriptorType = UDESC_ENDPOINT; udev->ctrl_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT; udev->ctrl_ep_desc.bmAttributes = UE_CONTROL; udev->ctrl_ep_desc.wMaxPacketSize[0] = USB_MAX_IPACKET; udev->ctrl_ep_desc.wMaxPacketSize[1] = 0; udev->ctrl_ep_desc.bInterval = 0; /* set up default endpoint companion descriptor */ udev->ctrl_ep_comp_desc.bLength = sizeof(udev->ctrl_ep_comp_desc); udev->ctrl_ep_comp_desc.bDescriptorType = UDESC_ENDPOINT_SS_COMP; udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; udev->speed = speed; udev->flags.usb_mode = mode; /* search for our High Speed USB HUB, if any */ adev = udev; hub = udev->parent_hub; while (hub) { if (hub->speed == USB_SPEED_HIGH) { udev->hs_hub_addr = hub->address; udev->parent_hs_hub = hub; udev->hs_port_no = adev->port_no; break; } adev = hub; hub = hub->parent_hub; } /* init the default endpoint */ usb_init_endpoint(udev, 0, &udev->ctrl_ep_desc, &udev->ctrl_ep_comp_desc, &udev->ctrl_ep); /* set device index */ udev->device_index = device_index; #if USB_HAVE_UGEN /* Create ugen name */ snprintf(udev->ugen_name, sizeof(udev->ugen_name), USB_GENERIC_NAME "%u.%u", device_get_unit(bus->bdev), device_index); LIST_INIT(&udev->pd_list); /* Create the control endpoint device */ udev->ctrl_dev = usb_make_dev(udev, NULL, 0, 0, FREAD|FWRITE, UID_ROOT, GID_OPERATOR, 0600); /* Create a link from /dev/ugenX.X to the default endpoint */ if (udev->ctrl_dev != NULL) make_dev_alias(udev->ctrl_dev->cdev, "%s", udev->ugen_name); #endif /* Initialise device */ if (bus->methods->device_init != NULL) { err = (bus->methods->device_init) (udev); if (err != 0) { DPRINTFN(0, "device init %d failed " "(%s, ignored)\n", device_index, usbd_errstr(err)); goto done; } } /* set powered device state after device init is complete */ usb_set_device_state(udev, USB_STATE_POWERED); if (udev->flags.usb_mode == USB_MODE_HOST) { err = usbd_req_set_address(udev, NULL, device_index); /* * This is the new USB device address from now on, if * the set address request didn't set it already. */ if (udev->address == USB_START_ADDR) udev->address = device_index; /* * We ignore any set-address errors, hence there are * buggy USB devices out there that actually receive * the SETUP PID, but manage to set the address before * the STATUS stage is ACK'ed. If the device responds * to the subsequent get-descriptor at the new * address, then we know that the set-address command * was successful. */ if (err) { DPRINTFN(0, "set address %d failed " "(%s, ignored)\n", udev->address, usbd_errstr(err)); } } else { /* We are not self powered */ udev->flags.self_powered = 0; /* Set unconfigured state */ udev->curr_config_no = USB_UNCONFIG_NO; udev->curr_config_index = USB_UNCONFIG_INDEX; /* Setup USB descriptors */ err = (usb_temp_setup_by_index_p) (udev, usb_template); if (err) { DPRINTFN(0, "setting up USB template failed - " "usb_template(4) not loaded?\n"); goto done; } } usb_set_device_state(udev, USB_STATE_ADDRESSED); /* setup the device descriptor and the initial "wMaxPacketSize" */ err = usbd_setup_device_desc(udev, NULL); if (err != 0) { /* try to enumerate two more times */ err = usbd_req_re_enumerate(udev, NULL); if (err != 0) { err = usbd_req_re_enumerate(udev, NULL); if (err != 0) { goto done; } } } /* * Setup temporary USB attach args so that we can figure out some * basic quirks for this device. */ usb_init_attach_arg(udev, &uaa); if (usb_test_quirk(&uaa, UQ_BUS_POWERED)) { udev->flags.uq_bus_powered = 1; } if (usb_test_quirk(&uaa, UQ_NO_STRINGS)) { udev->flags.no_strings = 1; } usb_get_langid(udev); /* assume 100mA bus powered for now. Changed when configured. */ udev->power = USB_MIN_POWER; /* fetch the vendor and product strings from the device */ usb_set_device_strings(udev); if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* USB device mode setup is complete */ err = 0; goto config_done; } /* * Most USB devices should attach to config index 0 by * default */ if (usb_test_quirk(&uaa, UQ_CFG_INDEX_0)) { config_index = 0; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_1)) { config_index = 1; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_2)) { config_index = 2; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_3)) { config_index = 3; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_4)) { config_index = 4; config_quirk = 1; } else { config_index = 0; config_quirk = 0; } set_config_failed = 0; repeat_set_config: DPRINTF("setting config %u\n", config_index); /* get the USB device configured */ err = usbd_set_config_index(udev, config_index); if (err) { if (udev->ddesc.bNumConfigurations != 0) { if (!set_config_failed) { set_config_failed = 1; /* XXX try to re-enumerate the device */ err = usbd_req_re_enumerate(udev, NULL); if (err == 0) goto repeat_set_config; } DPRINTFN(0, "Failure selecting configuration index %u:" "%s, port %u, addr %u (ignored)\n", config_index, usbd_errstr(err), udev->port_no, udev->address); } /* * Some USB devices do not have any configurations. Ignore any * set config failures! */ err = 0; goto config_done; } if (!config_quirk && config_index + 1 < udev->ddesc.bNumConfigurations) { if ((udev->cdesc->bNumInterface < 2) && usbd_get_no_descriptors(udev->cdesc, UDESC_ENDPOINT) == 0) { DPRINTFN(0, "Found no endpoints, trying next config\n"); config_index++; goto repeat_set_config; } #if USB_HAVE_MSCTEST if (config_index == 0) { /* * Try to figure out if we have an * auto-install disk there: */ if (usb_iface_is_cdrom(udev, 0)) { DPRINTFN(0, "Found possible auto-install " "disk (trying next config)\n"); config_index++; goto repeat_set_config; } } #endif } #if USB_HAVE_MSCTEST if (set_config_failed == 0 && config_index == 0 && usb_test_quirk(&uaa, UQ_MSC_NO_SYNC_CACHE) == 0 && usb_test_quirk(&uaa, UQ_MSC_NO_GETMAXLUN) == 0) { /* * Try to figure out if there are any MSC quirks we * should apply automatically: */ err = usb_msc_auto_quirk(udev, 0); if (err != 0) { set_config_failed = 1; goto repeat_set_config; } } #endif config_done: DPRINTF("new dev (addr %d), udev=%p, parent_hub=%p\n", udev->address, udev, udev->parent_hub); /* register our device - we are ready */ usb_bus_port_set_device(bus, parent_hub ? parent_hub->hub->ports + port_index : NULL, udev, device_index); #if USB_HAVE_UGEN /* Symlink the ugen device name */ udev->ugen_symlink = usb_alloc_symlink(udev->ugen_name); /* Announce device */ printf("%s: <%s %s> at %s\n", udev->ugen_name, usb_get_manufacturer(udev), usb_get_product(udev), device_get_nameunit(udev->bus->bdev)); #endif #if USB_HAVE_DEVCTL usb_notify_addq("ATTACH", udev); #endif done: if (err) { /* * Free USB device and all subdevices, if any. */ usb_free_device(udev, 0); udev = NULL; } return (udev); } #if USB_HAVE_UGEN struct usb_fs_privdata * usb_make_dev(struct usb_device *udev, const char *devname, int ep, int fi, int rwmode, uid_t uid, gid_t gid, int mode) { struct usb_fs_privdata* pd; struct make_dev_args args; char buffer[32]; /* Store information to locate ourselves again later */ pd = malloc(sizeof(struct usb_fs_privdata), M_USBDEV, M_WAITOK | M_ZERO); pd->bus_index = device_get_unit(udev->bus->bdev); pd->dev_index = udev->device_index; pd->ep_addr = ep; pd->fifo_index = fi; pd->mode = rwmode; /* Now, create the device itself */ if (devname == NULL) { devname = buffer; snprintf(buffer, sizeof(buffer), USB_DEVICE_DIR "/%u.%u.%u", pd->bus_index, pd->dev_index, pd->ep_addr); } /* Setup arguments for make_dev_s() */ make_dev_args_init(&args); args.mda_devsw = &usb_devsw; args.mda_uid = uid; args.mda_gid = gid; args.mda_mode = mode; args.mda_si_drv1 = pd; if (make_dev_s(&args, &pd->cdev, "%s", devname) != 0) { DPRINTFN(0, "Failed to create device %s\n", devname); free(pd, M_USBDEV); return (NULL); } return (pd); } void usb_destroy_dev_sync(struct usb_fs_privdata *pd) { DPRINTFN(1, "Destroying device at ugen%d.%d\n", pd->bus_index, pd->dev_index); /* * Destroy character device synchronously. After this * all system calls are returned. Can block. */ destroy_dev(pd->cdev); free(pd, M_USBDEV); } void usb_destroy_dev(struct usb_fs_privdata *pd) { struct usb_bus *bus; if (pd == NULL) return; mtx_lock(&usb_ref_lock); bus = devclass_get_softc(usb_devclass_ptr, pd->bus_index); mtx_unlock(&usb_ref_lock); if (bus == NULL) { usb_destroy_dev_sync(pd); return; } /* make sure we can re-use the device name */ delist_dev(pd->cdev); USB_BUS_LOCK(bus); LIST_INSERT_HEAD(&bus->pd_cleanup_list, pd, pd_next); /* get cleanup going */ usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), &bus->cleanup_msg[0], &bus->cleanup_msg[1]); USB_BUS_UNLOCK(bus); } static void usb_cdev_create(struct usb_device *udev) { struct usb_config_descriptor *cd; struct usb_endpoint_descriptor *ed; struct usb_descriptor *desc; struct usb_fs_privdata* pd; int inmode, outmode, inmask, outmask, mode; uint8_t ep; KASSERT(LIST_FIRST(&udev->pd_list) == NULL, ("stale cdev entries")); DPRINTFN(2, "Creating device nodes\n"); if (usbd_get_mode(udev) == USB_MODE_DEVICE) { inmode = FWRITE; outmode = FREAD; } else { /* USB_MODE_HOST */ inmode = FREAD; outmode = FWRITE; } inmask = 0; outmask = 0; desc = NULL; /* * Collect all used endpoint numbers instead of just * generating 16 static endpoints. */ cd = usbd_get_config_descriptor(udev); while ((desc = usb_desc_foreach(cd, desc))) { /* filter out all endpoint descriptors */ if ((desc->bDescriptorType == UDESC_ENDPOINT) && (desc->bLength >= sizeof(*ed))) { ed = (struct usb_endpoint_descriptor *)desc; /* update masks */ ep = ed->bEndpointAddress; if (UE_GET_DIR(ep) == UE_DIR_OUT) outmask |= 1 << UE_GET_ADDR(ep); else inmask |= 1 << UE_GET_ADDR(ep); } } /* Create all available endpoints except EP0 */ for (ep = 1; ep < 16; ep++) { mode = (inmask & (1 << ep)) ? inmode : 0; mode |= (outmask & (1 << ep)) ? outmode : 0; if (mode == 0) continue; /* no IN or OUT endpoint */ pd = usb_make_dev(udev, NULL, ep, 0, mode, UID_ROOT, GID_OPERATOR, 0600); if (pd != NULL) LIST_INSERT_HEAD(&udev->pd_list, pd, pd_next); } } static void usb_cdev_free(struct usb_device *udev) { struct usb_fs_privdata* pd; DPRINTFN(2, "Freeing device nodes\n"); while ((pd = LIST_FIRST(&udev->pd_list)) != NULL) { KASSERT(pd->cdev->si_drv1 == pd, ("privdata corrupt")); LIST_REMOVE(pd, pd_next); usb_destroy_dev(pd); } } #endif /*------------------------------------------------------------------------* * usb_free_device * * This function is NULL safe and will free an USB device and its * children devices, if any. * * Flag values: Reserved, set to zero. *------------------------------------------------------------------------*/ void usb_free_device(struct usb_device *udev, uint8_t flag) { struct usb_bus *bus; if (udev == NULL) return; /* already freed */ DPRINTFN(4, "udev=%p port=%d\n", udev, udev->port_no); bus = udev->bus; /* set DETACHED state to prevent any further references */ usb_set_device_state(udev, USB_STATE_DETACHED); #if USB_HAVE_DEVCTL usb_notify_addq("DETACH", udev); #endif #if USB_HAVE_UGEN if (!rebooting) { printf("%s: <%s %s> at %s (disconnected)\n", udev->ugen_name, usb_get_manufacturer(udev), usb_get_product(udev), device_get_nameunit(bus->bdev)); } /* Destroy UGEN symlink, if any */ if (udev->ugen_symlink) { usb_free_symlink(udev->ugen_symlink); udev->ugen_symlink = NULL; } usb_destroy_dev(udev->ctrl_dev); #endif if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* stop receiving any control transfers (Device Side Mode) */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); } /* the following will get the device unconfigured in software */ usb_unconfigure(udev, USB_UNCFG_FLAG_FREE_EP0); /* final device unregister after all character devices are closed */ usb_bus_port_set_device(bus, udev->parent_hub ? udev->parent_hub->hub->ports + udev->port_index : NULL, NULL, USB_ROOT_HUB_ADDR); /* unsetup any leftover default USB transfers */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); /* template unsetup, if any */ (usb_temp_unsetup_p) (udev); /* * Make sure that our clear-stall messages are not queued * anywhere: */ USB_BUS_LOCK(udev->bus); usb_proc_mwait(USB_BUS_CS_PROC(udev->bus), &udev->cs_msg[0], &udev->cs_msg[1]); USB_BUS_UNLOCK(udev->bus); /* wait for all references to go away */ usb_wait_pending_refs(udev); sx_destroy(&udev->enum_sx); sx_destroy(&udev->sr_sx); sx_destroy(&udev->ctrl_sx); cv_destroy(&udev->ctrlreq_cv); cv_destroy(&udev->ref_cv); mtx_destroy(&udev->device_mtx); #if USB_HAVE_UGEN KASSERT(LIST_FIRST(&udev->pd_list) == NULL, ("leaked cdev entries")); #endif /* Uninitialise device */ if (bus->methods->device_uninit != NULL) (bus->methods->device_uninit) (udev); /* free device */ free(udev->serial, M_USB); free(udev->manufacturer, M_USB); free(udev->product, M_USB); free(udev, M_USB); } /*------------------------------------------------------------------------* * usbd_get_iface * * This function is the safe way to get the USB interface structure * pointer by interface index. * * Return values: * NULL: Interface not present. * Else: Pointer to USB interface structure. *------------------------------------------------------------------------*/ struct usb_interface * usbd_get_iface(struct usb_device *udev, uint8_t iface_index) { struct usb_interface *iface = udev->ifaces + iface_index; if (iface_index >= udev->ifaces_max) return (NULL); return (iface); } /*------------------------------------------------------------------------* * usbd_find_descriptor * * This function will lookup the first descriptor that matches the * criteria given by the arguments "type" and "subtype". Descriptors * will only be searched within the interface having the index * "iface_index". If the "id" argument points to an USB descriptor, * it will be skipped before the search is started. This allows * searching for multiple descriptors using the same criteria. Else * the search is started after the interface descriptor. * * Return values: * NULL: End of descriptors * Else: A descriptor matching the criteria *------------------------------------------------------------------------*/ void * usbd_find_descriptor(struct usb_device *udev, void *id, uint8_t iface_index, uint8_t type, uint8_t type_mask, uint8_t subtype, uint8_t subtype_mask) { struct usb_descriptor *desc; struct usb_config_descriptor *cd; struct usb_interface *iface; cd = usbd_get_config_descriptor(udev); if (cd == NULL) { return (NULL); } if (id == NULL) { iface = usbd_get_iface(udev, iface_index); if (iface == NULL) { return (NULL); } id = usbd_get_interface_descriptor(iface); if (id == NULL) { return (NULL); } } desc = (void *)id; while ((desc = usb_desc_foreach(cd, desc))) { if (desc->bDescriptorType == UDESC_INTERFACE) { break; } if (((desc->bDescriptorType & type_mask) == type) && ((desc->bDescriptorSubtype & subtype_mask) == subtype)) { return (desc); } } return (NULL); } /*------------------------------------------------------------------------* * usb_devinfo * * This function will dump information from the device descriptor * belonging to the USB device pointed to by "udev", to the string * pointed to by "dst_ptr" having a maximum length of "dst_len" bytes * including the terminating zero. *------------------------------------------------------------------------*/ void usb_devinfo(struct usb_device *udev, char *dst_ptr, uint16_t dst_len) { struct usb_device_descriptor *udd = &udev->ddesc; uint16_t bcdDevice; uint16_t bcdUSB; bcdUSB = UGETW(udd->bcdUSB); bcdDevice = UGETW(udd->bcdDevice); if (udd->bDeviceClass != 0xFF) { snprintf(dst_ptr, dst_len, "%s %s, class %d/%d, rev %x.%02x/" "%x.%02x, addr %d", usb_get_manufacturer(udev), usb_get_product(udev), udd->bDeviceClass, udd->bDeviceSubClass, (bcdUSB >> 8), bcdUSB & 0xFF, (bcdDevice >> 8), bcdDevice & 0xFF, udev->address); } else { snprintf(dst_ptr, dst_len, "%s %s, rev %x.%02x/" "%x.%02x, addr %d", usb_get_manufacturer(udev), usb_get_product(udev), (bcdUSB >> 8), bcdUSB & 0xFF, (bcdDevice >> 8), bcdDevice & 0xFF, udev->address); } } #ifdef USB_VERBOSE /* * Descriptions of of known vendors and devices ("products"). */ struct usb_knowndev { uint16_t vendor; uint16_t product; uint32_t flags; const char *vendorname; const char *productname; }; #define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */ #include "usbdevs.h" #include "usbdevs_data.h" #endif /* USB_VERBOSE */ void usb_set_device_strings(struct usb_device *udev) { struct usb_device_descriptor *udd = &udev->ddesc; #ifdef USB_VERBOSE const struct usb_knowndev *kdp; #endif char *temp_ptr; size_t temp_size; uint16_t vendor_id; uint16_t product_id; uint8_t do_unlock; /* Protect scratch area */ do_unlock = usbd_ctrl_lock(udev); temp_ptr = (char *)udev->scratch.data; temp_size = sizeof(udev->scratch.data); vendor_id = UGETW(udd->idVendor); product_id = UGETW(udd->idProduct); /* cleanup old strings, if any */ free(udev->serial, M_USB); free(udev->manufacturer, M_USB); free(udev->product, M_USB); /* zero the string pointers */ udev->serial = NULL; udev->manufacturer = NULL; udev->product = NULL; /* get serial number string */ usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size, udev->ddesc.iSerialNumber); udev->serial = strdup(temp_ptr, M_USB); /* get manufacturer string */ usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size, udev->ddesc.iManufacturer); usb_trim_spaces(temp_ptr); if (temp_ptr[0] != '\0') udev->manufacturer = strdup(temp_ptr, M_USB); /* get product string */ usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size, udev->ddesc.iProduct); usb_trim_spaces(temp_ptr); if (temp_ptr[0] != '\0') udev->product = strdup(temp_ptr, M_USB); #ifdef USB_VERBOSE if (udev->manufacturer == NULL || udev->product == NULL) { for (kdp = usb_knowndevs; kdp->vendorname != NULL; kdp++) { if (kdp->vendor == vendor_id && (kdp->product == product_id || (kdp->flags & USB_KNOWNDEV_NOPROD) != 0)) break; } if (kdp->vendorname != NULL) { /* XXX should use pointer to knowndevs string */ if (udev->manufacturer == NULL) { udev->manufacturer = strdup(kdp->vendorname, M_USB); } if (udev->product == NULL && (kdp->flags & USB_KNOWNDEV_NOPROD) == 0) { udev->product = strdup(kdp->productname, M_USB); } } } #endif /* Provide default strings if none were found */ if (udev->manufacturer == NULL) { snprintf(temp_ptr, temp_size, "vendor 0x%04x", vendor_id); udev->manufacturer = strdup(temp_ptr, M_USB); } if (udev->product == NULL) { snprintf(temp_ptr, temp_size, "product 0x%04x", product_id); udev->product = strdup(temp_ptr, M_USB); } if (do_unlock) usbd_ctrl_unlock(udev); } /* * Returns: * See: USB_MODE_XXX */ enum usb_hc_mode usbd_get_mode(struct usb_device *udev) { return (udev->flags.usb_mode); } /* * Returns: * See: USB_SPEED_XXX */ enum usb_dev_speed usbd_get_speed(struct usb_device *udev) { return (udev->speed); } uint32_t usbd_get_isoc_fps(struct usb_device *udev) { ; /* indent fix */ switch (udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: return (1000); default: return (8000); } } struct usb_device_descriptor * usbd_get_device_descriptor(struct usb_device *udev) { if (udev == NULL) return (NULL); /* be NULL safe */ return (&udev->ddesc); } struct usb_config_descriptor * usbd_get_config_descriptor(struct usb_device *udev) { if (udev == NULL) return (NULL); /* be NULL safe */ return (udev->cdesc); } /*------------------------------------------------------------------------* * usb_test_quirk - test a device for a given quirk * * Return values: * 0: The USB device does not have the given quirk. * Else: The USB device has the given quirk. *------------------------------------------------------------------------*/ uint8_t usb_test_quirk(const struct usb_attach_arg *uaa, uint16_t quirk) { uint8_t found; uint8_t x; if (quirk == UQ_NONE) return (0); /* search the automatic per device quirks first */ for (x = 0; x != USB_MAX_AUTO_QUIRK; x++) { if (uaa->device->autoQuirk[x] == quirk) return (1); } /* search global quirk table, if any */ found = (usb_test_quirk_p) (&uaa->info, quirk); return (found); } struct usb_interface_descriptor * usbd_get_interface_descriptor(struct usb_interface *iface) { if (iface == NULL) return (NULL); /* be NULL safe */ return (iface->idesc); } uint8_t usbd_get_interface_altindex(struct usb_interface *iface) { return (iface->alt_index); } uint8_t usbd_get_bus_index(struct usb_device *udev) { return ((uint8_t)device_get_unit(udev->bus->bdev)); } uint8_t usbd_get_device_index(struct usb_device *udev) { return (udev->device_index); } #if USB_HAVE_DEVCTL static void usb_notify_addq(const char *type, struct usb_device *udev) { struct usb_interface *iface; struct sbuf *sb; int i; /* announce the device */ sb = sbuf_new_auto(); sbuf_printf(sb, #if USB_HAVE_UGEN "ugen=%s " "cdev=%s " #endif "vendor=0x%04x " "product=0x%04x " "devclass=0x%02x " "devsubclass=0x%02x " "sernum=\"%s\" " "release=0x%04x " "mode=%s " "port=%u " #if USB_HAVE_UGEN "parent=%s" #endif "", #if USB_HAVE_UGEN udev->ugen_name, udev->ugen_name, #endif UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, usb_get_serial(udev), UGETW(udev->ddesc.bcdDevice), (udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device", udev->port_no #if USB_HAVE_UGEN , udev->parent_hub != NULL ? udev->parent_hub->ugen_name : device_get_nameunit(device_get_parent(udev->bus->bdev)) #endif ); sbuf_finish(sb); devctl_notify("USB", "DEVICE", type, sbuf_data(sb)); sbuf_delete(sb); /* announce each interface */ for (i = 0; i < USB_IFACE_MAX; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) break; /* end of interfaces */ if (iface->idesc == NULL) continue; /* no interface descriptor */ sb = sbuf_new_auto(); sbuf_printf(sb, #if USB_HAVE_UGEN "ugen=%s " "cdev=%s " #endif "vendor=0x%04x " "product=0x%04x " "devclass=0x%02x " "devsubclass=0x%02x " "sernum=\"%s\" " "release=0x%04x " "mode=%s " "interface=%d " "endpoints=%d " "intclass=0x%02x " "intsubclass=0x%02x " "intprotocol=0x%02x", #if USB_HAVE_UGEN udev->ugen_name, udev->ugen_name, #endif UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, usb_get_serial(udev), UGETW(udev->ddesc.bcdDevice), (udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device", iface->idesc->bInterfaceNumber, iface->idesc->bNumEndpoints, iface->idesc->bInterfaceClass, iface->idesc->bInterfaceSubClass, iface->idesc->bInterfaceProtocol); sbuf_finish(sb); devctl_notify("USB", "INTERFACE", type, sbuf_data(sb)); sbuf_delete(sb); } } #endif #if USB_HAVE_UGEN /*------------------------------------------------------------------------* * usb_fifo_free_wrap * * This function will free the FIFOs. * * Description of "flag" argument: If the USB_UNCFG_FLAG_FREE_EP0 flag * is set and "iface_index" is set to "USB_IFACE_INDEX_ANY", we free * all FIFOs. If the USB_UNCFG_FLAG_FREE_EP0 flag is not set and * "iface_index" is set to "USB_IFACE_INDEX_ANY", we free all non * control endpoint FIFOs. If "iface_index" is not set to * "USB_IFACE_INDEX_ANY" the flag has no effect. *------------------------------------------------------------------------*/ static void usb_fifo_free_wrap(struct usb_device *udev, uint8_t iface_index, uint8_t flag) { struct usb_fifo *f; uint16_t i; /* * Free any USB FIFOs on the given interface: */ for (i = 0; i != USB_FIFO_MAX; i++) { f = udev->fifo[i]; if (f == NULL) { continue; } /* Check if the interface index matches */ if (iface_index == f->iface_index) { if (f->methods != &usb_ugen_methods) { /* * Don't free any non-generic FIFOs in * this case. */ continue; } if ((f->dev_ep_index == 0) && (f->fs_xfer == NULL)) { /* no need to free this FIFO */ continue; } } else if (iface_index == USB_IFACE_INDEX_ANY) { if ((f->methods == &usb_ugen_methods) && (f->dev_ep_index == 0) && (!(flag & USB_UNCFG_FLAG_FREE_EP0)) && (f->fs_xfer == NULL)) { /* no need to free this FIFO */ continue; } } else { /* no need to free this FIFO */ continue; } /* free this FIFO */ usb_fifo_free(f); } } #endif /*------------------------------------------------------------------------* * usb_peer_can_wakeup * * Return values: * 0: Peer cannot do resume signalling. * Else: Peer can do resume signalling. *------------------------------------------------------------------------*/ uint8_t usb_peer_can_wakeup(struct usb_device *udev) { const struct usb_config_descriptor *cdp; cdp = udev->cdesc; if ((cdp != NULL) && (udev->flags.usb_mode == USB_MODE_HOST)) { return (cdp->bmAttributes & UC_REMOTE_WAKEUP); } return (0); /* not supported */ } void usb_set_device_state(struct usb_device *udev, enum usb_dev_state state) { KASSERT(state < USB_STATE_MAX, ("invalid udev state")); DPRINTF("udev %p state %s -> %s\n", udev, usb_statestr(udev->state), usb_statestr(state)); #if USB_HAVE_UGEN mtx_lock(&usb_ref_lock); #endif udev->state = state; #if USB_HAVE_UGEN mtx_unlock(&usb_ref_lock); #endif if (udev->bus->methods->device_state_change != NULL) (udev->bus->methods->device_state_change) (udev); } enum usb_dev_state usb_get_device_state(struct usb_device *udev) { if (udev == NULL) return (USB_STATE_DETACHED); return (udev->state); } uint8_t usbd_device_attached(struct usb_device *udev) { return (udev->state > USB_STATE_DETACHED); } /* * The following function locks enumerating the given USB device. If * the lock is already grabbed this function returns zero. Else a * a value of one is returned. */ uint8_t usbd_enum_lock(struct usb_device *udev) { if (sx_xlocked(&udev->enum_sx)) return (0); sx_xlock(&udev->enum_sx); sx_xlock(&udev->sr_sx); /* * NEWBUS LOCK NOTE: We should check if any parent SX locks * are locked before locking Giant. Else the lock can be * locked multiple times. */ mtx_lock(&Giant); return (1); } #if USB_HAVE_UGEN /* * This function is the same like usbd_enum_lock() except a value of * 255 is returned when a signal is pending: */ uint8_t usbd_enum_lock_sig(struct usb_device *udev) { if (sx_xlocked(&udev->enum_sx)) return (0); if (sx_xlock_sig(&udev->enum_sx)) return (255); if (sx_xlock_sig(&udev->sr_sx)) { sx_xunlock(&udev->enum_sx); return (255); } mtx_lock(&Giant); return (1); } #endif /* The following function unlocks enumerating the given USB device. */ void usbd_enum_unlock(struct usb_device *udev) { mtx_unlock(&Giant); sx_xunlock(&udev->enum_sx); sx_xunlock(&udev->sr_sx); } /* The following function locks suspend and resume. */ void usbd_sr_lock(struct usb_device *udev) { sx_xlock(&udev->sr_sx); /* * NEWBUS LOCK NOTE: We should check if any parent SX locks * are locked before locking Giant. Else the lock can be * locked multiple times. */ mtx_lock(&Giant); } /* The following function unlocks suspend and resume. */ void usbd_sr_unlock(struct usb_device *udev) { mtx_unlock(&Giant); sx_xunlock(&udev->sr_sx); } /* * The following function checks the enumerating lock for the given * USB device. */ uint8_t usbd_enum_is_locked(struct usb_device *udev) { return (sx_xlocked(&udev->enum_sx)); } /* * The following function is used to serialize access to USB control * transfers and the USB scratch area. If the lock is already grabbed * this function returns zero. Else a value of one is returned. */ uint8_t usbd_ctrl_lock(struct usb_device *udev) { if (sx_xlocked(&udev->ctrl_sx)) return (0); sx_xlock(&udev->ctrl_sx); /* * We need to allow suspend and resume at this point, else the * control transfer will timeout if the device is suspended! */ if (usbd_enum_is_locked(udev)) usbd_sr_unlock(udev); return (1); } void usbd_ctrl_unlock(struct usb_device *udev) { sx_xunlock(&udev->ctrl_sx); /* * Restore the suspend and resume lock after we have unlocked * the USB control transfer lock to avoid LOR: */ if (usbd_enum_is_locked(udev)) usbd_sr_lock(udev); } /* * The following function is used to set the per-interface specific * plug and play information. The string referred to by the pnpinfo * argument can safely be freed after calling this function. The * pnpinfo of an interface will be reset at device detach or when * passing a NULL argument to this function. This function * returns zero on success, else a USB_ERR_XXX failure code. */ usb_error_t usbd_set_pnpinfo(struct usb_device *udev, uint8_t iface_index, const char *pnpinfo) { struct usb_interface *iface; iface = usbd_get_iface(udev, iface_index); if (iface == NULL) return (USB_ERR_INVAL); if (iface->pnpinfo != NULL) { free(iface->pnpinfo, M_USBDEV); iface->pnpinfo = NULL; } if (pnpinfo == NULL || pnpinfo[0] == 0) return (0); /* success */ iface->pnpinfo = strdup(pnpinfo, M_USBDEV); if (iface->pnpinfo == NULL) return (USB_ERR_NOMEM); return (0); /* success */ } usb_error_t usbd_add_dynamic_quirk(struct usb_device *udev, uint16_t quirk) { uint8_t x; for (x = 0; x != USB_MAX_AUTO_QUIRK; x++) { if (udev->autoQuirk[x] == 0 || udev->autoQuirk[x] == quirk) { udev->autoQuirk[x] = quirk; return (0); /* success */ } } return (USB_ERR_NOMEM); } /* * The following function is used to select the endpoint mode. It * should not be called outside enumeration context. */ usb_error_t usbd_set_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep, uint8_t ep_mode) { usb_error_t error; uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (udev->bus->methods->set_endpoint_mode != NULL) { error = (udev->bus->methods->set_endpoint_mode) ( udev, ep, ep_mode); } else if (ep_mode != USB_EP_MODE_DEFAULT) { error = USB_ERR_INVAL; } else { error = 0; } /* only set new mode regardless of error */ ep->ep_mode = ep_mode; if (do_unlock) usbd_enum_unlock(udev); return (error); } uint8_t usbd_get_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep) { return (ep->ep_mode); } Index: head/sys/dev/usb/usb_request.c =================================================================== --- head/sys/dev/usb/usb_request.c (revision 368658) +++ head/sys/dev/usb/usb_request.c (revision 368659) @@ -1,2325 +1,2336 @@ /* $FreeBSD$ */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. All rights reserved. - * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2008-2020 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ static int usb_no_cs_fail; SYSCTL_INT(_hw_usb, OID_AUTO, no_cs_fail, CTLFLAG_RWTUN, &usb_no_cs_fail, 0, "USB clear stall failures are ignored, if set"); static int usb_full_ddesc; SYSCTL_INT(_hw_usb, OID_AUTO, full_ddesc, CTLFLAG_RWTUN, &usb_full_ddesc, 0, "USB always read complete device descriptor, if set"); #ifdef USB_DEBUG #ifdef USB_REQ_DEBUG /* The following structures are used in connection to fault injection. */ struct usb_ctrl_debug { int bus_index; /* target bus */ int dev_index; /* target address */ int ds_fail; /* fail data stage */ int ss_fail; /* fail status stage */ int ds_delay; /* data stage delay in ms */ int ss_delay; /* status stage delay in ms */ int bmRequestType_value; int bRequest_value; }; struct usb_ctrl_debug_bits { uint16_t ds_delay; uint16_t ss_delay; uint8_t ds_fail:1; uint8_t ss_fail:1; uint8_t enabled:1; }; /* The default is to disable fault injection. */ static struct usb_ctrl_debug usb_ctrl_debug = { .bus_index = -1, .dev_index = -1, .bmRequestType_value = -1, .bRequest_value = -1, }; SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_bus_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.bus_index, 0, "USB controller index to fail"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_dev_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.dev_index, 0, "USB device address to fail"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.ds_fail, 0, "USB fail data stage"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.ss_fail, 0, "USB fail status stage"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_delay, CTLFLAG_RWTUN, &usb_ctrl_debug.ds_delay, 0, "USB data stage delay in ms"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_delay, CTLFLAG_RWTUN, &usb_ctrl_debug.ss_delay, 0, "USB status stage delay in ms"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rt_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.bmRequestType_value, 0, "USB bmRequestType to fail"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rv_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.bRequest_value, 0, "USB bRequest to fail"); /*------------------------------------------------------------------------* * usbd_get_debug_bits * * This function is only useful in USB host mode. *------------------------------------------------------------------------*/ static void usbd_get_debug_bits(struct usb_device *udev, struct usb_device_request *req, struct usb_ctrl_debug_bits *dbg) { int temp; memset(dbg, 0, sizeof(*dbg)); /* Compute data stage delay */ temp = usb_ctrl_debug.ds_delay; if (temp < 0) temp = 0; else if (temp > (16*1024)) temp = (16*1024); dbg->ds_delay = temp; /* Compute status stage delay */ temp = usb_ctrl_debug.ss_delay; if (temp < 0) temp = 0; else if (temp > (16*1024)) temp = (16*1024); dbg->ss_delay = temp; /* Check if this control request should be failed */ if (usbd_get_bus_index(udev) != usb_ctrl_debug.bus_index) return; if (usbd_get_device_index(udev) != usb_ctrl_debug.dev_index) return; temp = usb_ctrl_debug.bmRequestType_value; if ((temp != req->bmRequestType) && (temp >= 0) && (temp <= 255)) return; temp = usb_ctrl_debug.bRequest_value; if ((temp != req->bRequest) && (temp >= 0) && (temp <= 255)) return; temp = usb_ctrl_debug.ds_fail; if (temp) dbg->ds_fail = 1; temp = usb_ctrl_debug.ss_fail; if (temp) dbg->ss_fail = 1; dbg->enabled = 1; } #endif /* USB_REQ_DEBUG */ #endif /* USB_DEBUG */ /*------------------------------------------------------------------------* * usbd_do_request_callback * * This function is the USB callback for generic USB Host control * transfers. *------------------------------------------------------------------------*/ void usbd_do_request_callback(struct usb_xfer *xfer, usb_error_t error) { ; /* workaround for a bug in "indent" */ DPRINTF("st=%u\n", USB_GET_STATE(xfer)); switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: usbd_transfer_submit(xfer); break; default: cv_signal(&xfer->xroot->udev->ctrlreq_cv); break; } } /*------------------------------------------------------------------------* * usb_do_clear_stall_callback * * This function is the USB callback for generic clear stall requests. *------------------------------------------------------------------------*/ void usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_device_request req; struct usb_device *udev; struct usb_endpoint *ep; struct usb_endpoint *ep_end; struct usb_endpoint *ep_first; usb_stream_t x; uint8_t to; udev = xfer->xroot->udev; USB_BUS_LOCK(udev->bus); /* round robin endpoint clear stall */ ep = udev->ep_curr; ep_end = udev->endpoints + udev->endpoints_max; ep_first = udev->endpoints; to = udev->endpoints_max; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: tr_transferred: /* reset error counter */ udev->clear_stall_errors = 0; if (ep == NULL) goto tr_setup; /* device was unconfigured */ if (ep->edesc && ep->is_stalled) { ep->toggle_next = 0; ep->is_stalled = 0; /* some hardware needs a callback to clear the data toggle */ usbd_clear_stall_locked(udev, ep); for (x = 0; x != USB_MAX_EP_STREAMS; x++) { /* start the current or next transfer, if any */ usb_command_wrapper(&ep->endpoint_q[x], ep->endpoint_q[x].curr); } } ep++; case USB_ST_SETUP: tr_setup: if (to == 0) break; /* no endpoints - nothing to do */ if ((ep < ep_first) || (ep >= ep_end)) ep = ep_first; /* endpoint wrapped around */ if (ep->edesc && ep->is_stalled) { /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = ep->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* copy in the transfer */ usbd_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); /* set length */ usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); xfer->nframes = 1; USB_BUS_UNLOCK(udev->bus); usbd_transfer_submit(xfer); USB_BUS_LOCK(udev->bus); break; } ep++; to--; goto tr_setup; default: if (error == USB_ERR_CANCELLED) break; DPRINTF("Clear stall failed.\n"); /* * Some VMs like VirtualBox always return failure on * clear-stall which we sometimes should just ignore. */ if (usb_no_cs_fail) goto tr_transferred; if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) goto tr_setup; if (error == USB_ERR_TIMEOUT) { udev->clear_stall_errors = USB_CS_RESET_LIMIT; DPRINTF("Trying to re-enumerate.\n"); usbd_start_re_enumerate(udev); } else { udev->clear_stall_errors++; if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) { DPRINTF("Trying to re-enumerate.\n"); usbd_start_re_enumerate(udev); } } goto tr_setup; } /* store current endpoint */ udev->ep_curr = ep; USB_BUS_UNLOCK(udev->bus); } static usb_handle_req_t * usbd_get_hr_func(struct usb_device *udev) { /* figure out if there is a Handle Request function */ if (udev->flags.usb_mode == USB_MODE_DEVICE) return (usb_temp_get_desc_p); else if (udev->parent_hub == NULL) return (udev->bus->methods->roothub_exec); else return (NULL); } /*------------------------------------------------------------------------* * usbd_do_request_flags and usbd_do_request * * Description of arguments passed to these functions: * * "udev" - this is the "usb_device" structure pointer on which the * request should be performed. It is possible to call this function * in both Host Side mode and Device Side mode. * * "mtx" - if this argument is non-NULL the mutex pointed to by it * will get dropped and picked up during the execution of this * function, hence this function sometimes needs to sleep. If this * argument is NULL it has no effect. * * "req" - this argument must always be non-NULL and points to an * 8-byte structure holding the USB request to be done. The USB * request structure has a bit telling the direction of the USB * request, if it is a read or a write. * * "data" - if the "wLength" part of the structure pointed to by "req" * is non-zero this argument must point to a valid kernel buffer which * can hold at least "wLength" bytes. If "wLength" is zero "data" can * be NULL. * * "flags" - here is a list of valid flags: * * o USB_SHORT_XFER_OK: allows the data transfer to be shorter than * specified * * o USB_DELAY_STATUS_STAGE: allows the status stage to be performed * at a later point in time. This is tunable by the "hw.usb.ss_delay" * sysctl. This flag is mostly useful for debugging. * * o USB_USER_DATA_PTR: treat the "data" pointer like a userland * pointer. * * "actlen" - if non-NULL the actual transfer length will be stored in * the 16-bit unsigned integer pointed to by "actlen". This * information is mostly useful when the "USB_SHORT_XFER_OK" flag is * used. * * "timeout" - gives the timeout for the control transfer in * milliseconds. A "timeout" value less than 50 milliseconds is * treated like a 50 millisecond timeout. A "timeout" value greater * than 30 seconds is treated like a 30 second timeout. This USB stack * does not allow control requests without a timeout. * * NOTE: This function is thread safe. All calls to "usbd_do_request_flags" * will be serialized by the use of the USB device enumeration lock. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, struct usb_device_request *req, void *data, uint16_t flags, uint16_t *actlen, usb_timeout_t timeout) { #ifdef USB_REQ_DEBUG struct usb_ctrl_debug_bits dbg; #endif usb_handle_req_t *hr_func; struct usb_xfer *xfer; const void *desc; int err = 0; usb_ticks_t start_ticks; usb_ticks_t delta_ticks; usb_ticks_t max_ticks; uint16_t length; uint16_t temp; uint16_t acttemp; uint8_t do_unlock; if (timeout < 50) { /* timeout is too small */ timeout = 50; } if (timeout > 30000) { /* timeout is too big */ timeout = 30000; } length = UGETW(req->wLength); DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x " "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n", udev, req->bmRequestType, req->bRequest, req->wValue[1], req->wValue[0], req->wIndex[1], req->wIndex[0], req->wLength[1], req->wLength[0]); /* Check if the device is still alive */ if (udev->state < USB_STATE_POWERED) { DPRINTF("usb device has gone\n"); return (USB_ERR_NOT_CONFIGURED); } /* * Set "actlen" to a known value in case the caller does not * check the return value: */ if (actlen) *actlen = 0; #if (USB_HAVE_USER_IO == 0) if (flags & USB_USER_DATA_PTR) return (USB_ERR_INVAL); #endif if ((mtx != NULL) && (mtx != &Giant)) { USB_MTX_UNLOCK(mtx); USB_MTX_ASSERT(mtx, MA_NOTOWNED); } /* * Serialize access to this function: */ do_unlock = usbd_ctrl_lock(udev); hr_func = usbd_get_hr_func(udev); if (hr_func != NULL) { DPRINTF("Handle Request function is set\n"); desc = NULL; temp = 0; if (!(req->bmRequestType & UT_READ)) { if (length != 0) { DPRINTFN(1, "The handle request function " "does not support writing data!\n"); err = USB_ERR_INVAL; goto done; } } /* The root HUB code needs the BUS lock locked */ USB_BUS_LOCK(udev->bus); err = (hr_func) (udev, req, &desc, &temp); USB_BUS_UNLOCK(udev->bus); if (err) goto done; if (length > temp) { if (!(flags & USB_SHORT_XFER_OK)) { err = USB_ERR_SHORT_XFER; goto done; } length = temp; } if (actlen) *actlen = length; if (length > 0) { #if USB_HAVE_USER_IO if (flags & USB_USER_DATA_PTR) { if (copyout(desc, data, length)) { err = USB_ERR_INVAL; goto done; } } else #endif memcpy(data, desc, length); } goto done; /* success */ } /* * Setup a new USB transfer or use the existing one, if any: */ usbd_ctrl_transfer_setup(udev); xfer = udev->ctrl_xfer[0]; if (xfer == NULL) { /* most likely out of memory */ err = USB_ERR_NOMEM; goto done; } #ifdef USB_REQ_DEBUG /* Get debug bits */ usbd_get_debug_bits(udev, req, &dbg); /* Check for fault injection */ if (dbg.enabled) flags |= USB_DELAY_STATUS_STAGE; #endif USB_XFER_LOCK(xfer); if (flags & USB_DELAY_STATUS_STAGE) xfer->flags.manual_status = 1; else xfer->flags.manual_status = 0; if (flags & USB_SHORT_XFER_OK) xfer->flags.short_xfer_ok = 1; else xfer->flags.short_xfer_ok = 0; xfer->timeout = timeout; start_ticks = ticks; max_ticks = USB_MS_TO_TICKS(timeout); usbd_copy_in(xfer->frbuffers, 0, req, sizeof(*req)); usbd_xfer_set_frame_len(xfer, 0, sizeof(*req)); while (1) { temp = length; if (temp > usbd_xfer_max_len(xfer)) { temp = usbd_xfer_max_len(xfer); } #ifdef USB_REQ_DEBUG if (xfer->flags.manual_status) { if (usbd_xfer_frame_len(xfer, 0) != 0) { /* Execute data stage separately */ temp = 0; } else if (temp > 0) { if (dbg.ds_fail) { err = USB_ERR_INVAL; break; } if (dbg.ds_delay > 0) { usb_pause_mtx( xfer->xroot->xfer_mtx, USB_MS_TO_TICKS(dbg.ds_delay)); /* make sure we don't time out */ start_ticks = ticks; } } } #endif usbd_xfer_set_frame_len(xfer, 1, temp); if (temp > 0) { if (!(req->bmRequestType & UT_READ)) { #if USB_HAVE_USER_IO if (flags & USB_USER_DATA_PTR) { USB_XFER_UNLOCK(xfer); err = usbd_copy_in_user(xfer->frbuffers + 1, 0, data, temp); USB_XFER_LOCK(xfer); if (err) { err = USB_ERR_INVAL; break; } } else #endif usbd_copy_in(xfer->frbuffers + 1, 0, data, temp); } usbd_xfer_set_frames(xfer, 2); } else { if (usbd_xfer_frame_len(xfer, 0) == 0) { if (xfer->flags.manual_status) { #ifdef USB_REQ_DEBUG if (dbg.ss_fail) { err = USB_ERR_INVAL; break; } if (dbg.ss_delay > 0) { usb_pause_mtx( xfer->xroot->xfer_mtx, USB_MS_TO_TICKS(dbg.ss_delay)); /* make sure we don't time out */ start_ticks = ticks; } #endif xfer->flags.manual_status = 0; } else { break; } } usbd_xfer_set_frames(xfer, 1); } usbd_transfer_start(xfer); while (usbd_transfer_pending(xfer)) { cv_wait(&udev->ctrlreq_cv, xfer->xroot->xfer_mtx); } err = xfer->error; if (err) { break; } /* get actual length of DATA stage */ if (xfer->aframes < 2) { acttemp = 0; } else { acttemp = usbd_xfer_frame_len(xfer, 1); } /* check for short packet */ if (temp > acttemp) { temp = acttemp; length = temp; } if (temp > 0) { if (req->bmRequestType & UT_READ) { #if USB_HAVE_USER_IO if (flags & USB_USER_DATA_PTR) { USB_XFER_UNLOCK(xfer); err = usbd_copy_out_user(xfer->frbuffers + 1, 0, data, temp); USB_XFER_LOCK(xfer); if (err) { err = USB_ERR_INVAL; break; } } else #endif usbd_copy_out(xfer->frbuffers + 1, 0, data, temp); } } /* * Clear "frlengths[0]" so that we don't send the setup * packet again: */ usbd_xfer_set_frame_len(xfer, 0, 0); /* update length and data pointer */ length -= temp; data = USB_ADD_BYTES(data, temp); if (actlen) { (*actlen) += temp; } /* check for timeout */ delta_ticks = ticks - start_ticks; if (delta_ticks > max_ticks) { if (!err) { err = USB_ERR_TIMEOUT; } } if (err) { break; } } if (err) { /* * Make sure that the control endpoint is no longer * blocked in case of a non-transfer related error: */ usbd_transfer_stop(xfer); } USB_XFER_UNLOCK(xfer); done: if (do_unlock) usbd_ctrl_unlock(udev); if ((mtx != NULL) && (mtx != &Giant)) USB_MTX_LOCK(mtx); switch (err) { case USB_ERR_NORMAL_COMPLETION: case USB_ERR_SHORT_XFER: case USB_ERR_STALLED: case USB_ERR_CANCELLED: break; default: DPRINTF("error=%s - waiting a bit for TT cleanup\n", usbd_errstr(err)); usb_pause_mtx(mtx, hz / 16); break; } return ((usb_error_t)err); } /*------------------------------------------------------------------------* * usbd_do_request_proc - factored out code * * This function is factored out code. It does basically the same like * usbd_do_request_flags, except it will check the status of the * passed process argument before doing the USB request. If the * process is draining the USB_ERR_IOERROR code will be returned. It * is assumed that the mutex associated with the process is locked * when calling this function. *------------------------------------------------------------------------*/ usb_error_t usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc, struct usb_device_request *req, void *data, uint16_t flags, uint16_t *actlen, usb_timeout_t timeout) { usb_error_t err; uint16_t len; /* get request data length */ len = UGETW(req->wLength); /* check if the device is being detached */ if (usb_proc_is_gone(pproc)) { err = USB_ERR_IOERROR; goto done; } /* forward the USB request */ err = usbd_do_request_flags(udev, pproc->up_mtx, req, data, flags, actlen, timeout); done: /* on failure we zero the data */ /* on short packet we zero the unused data */ if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) { if (err) memset(data, 0, len); else if (actlen && *actlen != len) memset(((uint8_t *)data) + *actlen, 0, len - *actlen); } return (err); } /*------------------------------------------------------------------------* * usbd_req_reset_port * * This function will instruct a 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; uint16_t status; uint16_t change; DPRINTF("\n"); /* clear any leftover port reset changes first */ usbd_req_clear_port_feature( udev, mtx, port, UHF_C_PORT_RESET); /* assert port reset on the given port */ err = usbd_req_set_port_feature( udev, mtx, port, UHF_PORT_RESET); /* check for errors */ if (err) goto done; n = 0; while (1) { /* 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; err = usbd_req_get_port_status(udev, mtx, &ps, port); if (err) goto done; status = UGETW(ps.wPortStatus); change = UGETW(ps.wPortChange); /* if the device disappeared, just give up */ if (!(status & UPS_CURRENT_CONNECT_STATUS)) goto done; /* check if reset is complete */ if (change & UPS_C_PORT_RESET) break; /* * Some Virtual Machines like VirtualBox 4.x fail to * generate a port reset change event. Check if reset * is no longer asserted. */ if (!(status & UPS_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; } /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery)); done: DPRINTFN(2, "port %d reset returning error=%s\n", port, usbd_errstr(err)); return (err); } /*------------------------------------------------------------------------* * usbd_req_warm_reset_port * * This function will instruct an USB HUB to perform a warm reset * sequence on the specified port number. This kind of reset is not * mandatory for LOW-, FULL- and HIGH-speed USB HUBs and is targeted * for SUPER-speed USB HUBs. * * Returns: * 0: Success. The USB device should now be available again. * Else: Failure. No USB device is present and the USB port should be * disabled. *------------------------------------------------------------------------*/ usb_error_t usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) { struct usb_port_status ps; usb_error_t err; uint16_t n; uint16_t status; uint16_t change; DPRINTF("\n"); err = usbd_req_get_port_status(udev, mtx, &ps, port); if (err) goto done; status = UGETW(ps.wPortStatus); switch (UPS_PORT_LINK_STATE_GET(status)) { case UPS_PORT_LS_U3: case UPS_PORT_LS_COMP_MODE: case UPS_PORT_LS_LOOPBACK: case UPS_PORT_LS_SS_INA: break; default: DPRINTF("Wrong state for warm reset\n"); return (0); } /* clear any leftover warm port reset changes first */ usbd_req_clear_port_feature(udev, mtx, port, UHF_C_BH_PORT_RESET); /* set warm port reset */ err = usbd_req_set_port_feature(udev, mtx, port, UHF_BH_PORT_RESET); if (err) goto done; n = 0; while (1) { /* 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; err = usbd_req_get_port_status(udev, mtx, &ps, port); if (err) goto done; status = UGETW(ps.wPortStatus); change = UGETW(ps.wPortChange); /* if the device disappeared, just give up */ if (!(status & UPS_CURRENT_CONNECT_STATUS)) goto done; /* check if reset is complete */ if (change & UPS_C_BH_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_BH_PORT_RESET); if (err) goto done; /* check for timeout */ if (n == 0) { err = USB_ERR_TIMEOUT; goto done; } /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery)); done: DPRINTFN(2, "port %d warm reset returning error=%s\n", port, usbd_errstr(err)); return (err); } /*------------------------------------------------------------------------* * usbd_req_get_desc * * This function can be used to retrieve USB descriptors. It contains * some additional logic like zeroing of missing descriptor bytes and * retrying an USB descriptor in case of failure. The "min_len" * argument specifies the minimum descriptor length. The "max_len" * argument specifies the maximum descriptor length. If the real * descriptor length is less than the minimum length the missing * byte(s) will be zeroed. The type field, the second byte of the USB * descriptor, will get forced to the correct type. If the "actlen" * pointer is non-NULL, the actual length of the transfer will get * stored in the 16-bit unsigned integer which it is pointing to. The * first byte of the descriptor will not get updated. If the "actlen" * pointer is NULL the first byte of the descriptor will get updated * to reflect the actual length instead. If "min_len" is not equal to * "max_len" then this function will try to retrive the beginning of * the descriptor and base the maximum length on the first byte of the * descriptor. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_desc(struct usb_device *udev, struct mtx *mtx, uint16_t *actlen, void *desc, uint16_t min_len, uint16_t max_len, uint16_t id, uint8_t type, uint8_t index, uint8_t retries) { struct usb_device_request req; uint8_t *buf = desc; usb_error_t err; DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n", id, type, index, max_len); req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, type, index); USETW(req.wIndex, id); while (1) { if ((min_len < 2) || (max_len < 2)) { err = USB_ERR_INVAL; goto done; } USETW(req.wLength, min_len); err = usbd_do_request_flags(udev, mtx, &req, desc, 0, NULL, 1000 /* ms */); if (err != 0 && err != USB_ERR_TIMEOUT && min_len != max_len) { /* clear descriptor data */ memset(desc, 0, max_len); /* try to read full descriptor length */ USETW(req.wLength, max_len); err = usbd_do_request_flags(udev, mtx, &req, desc, USB_SHORT_XFER_OK, NULL, 1000 /* ms */); if (err == 0) { /* verify length */ if (buf[0] > max_len) buf[0] = max_len; else if (buf[0] < 2) err = USB_ERR_INVAL; min_len = buf[0]; /* enforce descriptor type */ buf[1] = type; goto done; } } if (err) { if (!retries) { goto done; } retries--; usb_pause_mtx(mtx, hz / 5); continue; } if (min_len == max_len) { /* enforce correct length */ if ((buf[0] > min_len) && (actlen == NULL)) buf[0] = min_len; /* enforce correct type */ buf[1] = type; goto done; } /* range check */ if (max_len > buf[0]) { max_len = buf[0]; } /* zero minimum data */ while (min_len > max_len) { min_len--; buf[min_len] = 0; } /* set new minimum length */ min_len = max_len; } done: if (actlen != NULL) { if (err) *actlen = 0; else *actlen = min_len; } return (err); } /*------------------------------------------------------------------------* * usbd_req_get_string_any * * This function will return the string given by "string_index" * using the first language ID. The maximum length "len" includes * the terminating zero. The "len" argument should be twice as * big pluss 2 bytes, compared with the actual maximum string length ! * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf, uint16_t len, uint8_t string_index) { char *s; uint8_t *temp; uint16_t i; uint16_t n; uint16_t c; uint8_t swap; usb_error_t err; if (len == 0) { /* should not happen */ return (USB_ERR_NORMAL_COMPLETION); } if (string_index == 0) { /* this is the language table */ buf[0] = 0; return (USB_ERR_INVAL); } if (udev->flags.no_strings) { buf[0] = 0; return (USB_ERR_STALLED); } err = usbd_req_get_string_desc (udev, mtx, buf, len, udev->langid, string_index); if (err) { buf[0] = 0; return (err); } temp = (uint8_t *)buf; if (temp[0] < 2) { /* string length is too short */ buf[0] = 0; return (USB_ERR_INVAL); } /* reserve one byte for terminating zero */ len--; /* find maximum length */ s = buf; n = (temp[0] / 2) - 1; if (n > len) { n = len; } /* skip descriptor header */ temp += 2; /* reset swap state */ swap = 3; /* convert and filter */ for (i = 0; (i != n); i++) { c = UGETW(temp + (2 * i)); /* convert from Unicode, handle buggy strings */ if (((c & 0xff00) == 0) && (swap & 1)) { /* Little Endian, default */ *s = c; swap = 1; } else if (((c & 0x00ff) == 0) && (swap & 2)) { /* Big Endian */ *s = c >> 8; swap = 2; } else { /* silently skip bad character */ continue; } /* * Filter by default - We only allow alphanumerical * and a few more to avoid any problems with scripts * and daemons. */ if (isalpha(*s) || isdigit(*s) || *s == '-' || *s == '+' || *s == ' ' || *s == '.' || *s == ',' || *s == ':' || *s == '/' || *s == '(' || *s == ')') { /* allowed */ s++; } /* silently skip bad character */ } *s = 0; /* zero terminate resulting string */ return (USB_ERR_NORMAL_COMPLETION); } /*------------------------------------------------------------------------* * usbd_req_get_string_desc * * If you don't know the language ID, consider using * "usbd_req_get_string_any()". * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_string_desc(struct usb_device *udev, struct mtx *mtx, void *sdesc, uint16_t max_len, uint16_t lang_id, uint8_t string_index) { return (usbd_req_get_desc(udev, mtx, NULL, sdesc, 2, max_len, lang_id, UDESC_STRING, string_index, 0)); } /*------------------------------------------------------------------------* * usbd_req_get_config_desc_ptr * * This function is used in device side mode to retrieve the pointer * to the generated config descriptor. This saves allocating space for * an additional config descriptor when setting the configuration. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_descriptor_ptr(struct usb_device *udev, struct usb_config_descriptor **ppcd, uint16_t wValue) { struct usb_device_request req; usb_handle_req_t *hr_func; const void *ptr; uint16_t len; usb_error_t err; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW(req.wValue, wValue); USETW(req.wIndex, 0); USETW(req.wLength, 0); ptr = NULL; len = 0; hr_func = usbd_get_hr_func(udev); if (hr_func == NULL) err = USB_ERR_INVAL; else { USB_BUS_LOCK(udev->bus); err = (hr_func) (udev, &req, &ptr, &len); USB_BUS_UNLOCK(udev->bus); } if (err) ptr = NULL; else if (ptr == NULL) err = USB_ERR_INVAL; *ppcd = __DECONST(struct usb_config_descriptor *, ptr); return (err); } /*------------------------------------------------------------------------* * usbd_req_get_config_desc * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx, struct usb_config_descriptor *d, uint8_t conf_index) { usb_error_t err; DPRINTFN(4, "confidx=%d\n", conf_index); err = usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), sizeof(*d), 0, UDESC_CONFIG, conf_index, 0); if (err) { goto done; } /* Extra sanity checking */ if (UGETW(d->wTotalLength) < (uint16_t)sizeof(*d)) { err = USB_ERR_INVAL; } done: return (err); } /*------------------------------------------------------------------------* * usbd_alloc_config_desc * * This function is used to allocate a zeroed configuration * descriptor. * * Returns: * NULL: Failure * Else: Success *------------------------------------------------------------------------*/ void * usbd_alloc_config_desc(struct usb_device *udev, uint32_t size) { if (size > USB_CONFIG_MAX) { DPRINTF("Configuration descriptor too big\n"); return (NULL); } #if (USB_HAVE_FIXED_CONFIG == 0) return (malloc(size, M_USBDEV, M_ZERO | M_WAITOK)); #else memset(udev->config_data, 0, sizeof(udev->config_data)); return (udev->config_data); #endif } /*------------------------------------------------------------------------* * usbd_alloc_config_desc * * This function is used to free a configuration descriptor. *------------------------------------------------------------------------*/ void usbd_free_config_desc(struct usb_device *udev, void *ptr) { #if (USB_HAVE_FIXED_CONFIG == 0) free(ptr, M_USBDEV); #endif } /*------------------------------------------------------------------------* * usbd_req_get_config_desc_full * * This function gets the complete USB configuration descriptor and * ensures that "wTotalLength" is correct. The returned configuration * descriptor is freed by calling "usbd_free_config_desc()". * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_config_desc_full(struct usb_device *udev, struct mtx *mtx, struct usb_config_descriptor **ppcd, uint8_t index) { struct usb_config_descriptor cd; struct usb_config_descriptor *cdesc; uint32_t len; usb_error_t err; DPRINTFN(4, "index=%d\n", index); *ppcd = NULL; err = usbd_req_get_config_desc(udev, mtx, &cd, index); if (err) return (err); /* get full descriptor */ len = UGETW(cd.wTotalLength); if (len < (uint32_t)sizeof(*cdesc)) { /* corrupt descriptor */ return (USB_ERR_INVAL); } else if (len > USB_CONFIG_MAX) { DPRINTF("Configuration descriptor was truncated\n"); len = USB_CONFIG_MAX; } cdesc = usbd_alloc_config_desc(udev, len); if (cdesc == NULL) return (USB_ERR_NOMEM); err = usbd_req_get_desc(udev, mtx, NULL, cdesc, len, len, 0, UDESC_CONFIG, index, 3); if (err) { usbd_free_config_desc(udev, cdesc); return (err); } /* make sure that the device is not fooling us: */ USETW(cdesc->wTotalLength, len); *ppcd = cdesc; return (0); /* success */ } /*------------------------------------------------------------------------* * usbd_req_get_device_desc * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_device_desc(struct usb_device *udev, struct mtx *mtx, struct usb_device_descriptor *d) { DPRINTFN(4, "\n"); return (usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), sizeof(*d), 0, UDESC_DEVICE, 0, 3)); } /*------------------------------------------------------------------------* * usbd_req_get_alt_interface_no * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx, uint8_t *alt_iface_no, uint8_t iface_index) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) return (USB_ERR_INVAL); req.bmRequestType = UT_READ_INTERFACE; req.bRequest = UR_GET_INTERFACE; USETW(req.wValue, 0); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, 1); return (usbd_do_request(udev, mtx, &req, alt_iface_no)); } /*------------------------------------------------------------------------* * usbd_req_set_alt_interface_no * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx, uint8_t iface_index, uint8_t alt_no) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; + usb_error_t err; if ((iface == NULL) || (iface->idesc == NULL)) return (USB_ERR_INVAL); req.bmRequestType = UT_WRITE_INTERFACE; req.bRequest = UR_SET_INTERFACE; req.wValue[0] = alt_no; req.wValue[1] = 0; req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, 0); - return (usbd_do_request(udev, mtx, &req, 0)); + err = usbd_do_request(udev, mtx, &req, 0); + if (err == USB_ERR_STALLED && iface->num_altsetting == 1) { + /* + * The USB specification chapter 9.4.10 says that USB + * devices having only one alternate setting are + * allowed to STALL this request. Ignore this failure. + */ + err = 0; + DPRINTF("Setting default alternate number failed. (ignored)\n"); + } + return (err); } /*------------------------------------------------------------------------* * usbd_req_get_device_status * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_device_status(struct usb_device *udev, struct mtx *mtx, struct usb_status *st) { struct usb_device_request req; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_STATUS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(*st)); return (usbd_do_request(udev, mtx, &req, st)); } /*------------------------------------------------------------------------* * usbd_req_get_hub_descriptor * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx, struct usb_hub_descriptor *hd, uint8_t nports) { struct usb_device_request req; uint16_t len = (nports + 7 + (8 * 8)) / 8; req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_HUB, 0); USETW(req.wIndex, 0); USETW(req.wLength, len); return (usbd_do_request(udev, mtx, &req, hd)); } /*------------------------------------------------------------------------* * usbd_req_get_ss_hub_descriptor * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx, struct usb_hub_ss_descriptor *hd, uint8_t nports) { struct usb_device_request req; uint16_t len = sizeof(*hd) - 32 + 1 + ((nports + 7) / 8); req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_SS_HUB, 0); USETW(req.wIndex, 0); USETW(req.wLength, len); return (usbd_do_request(udev, mtx, &req, hd)); } /*------------------------------------------------------------------------* * usbd_req_get_hub_status * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx, struct usb_hub_status *st) { struct usb_device_request req; req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_STATUS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(struct usb_hub_status)); return (usbd_do_request(udev, mtx, &req, st)); } /*------------------------------------------------------------------------* * usbd_req_set_address * * This function is used to set the address for an USB device. After * port reset the USB device will respond at address zero. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr) { struct usb_device_request req; usb_error_t err; DPRINTFN(6, "setting device address=%d\n", addr); req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, 0); err = USB_ERR_INVAL; /* check if USB controller handles set address */ if (udev->bus->methods->set_address != NULL) err = (udev->bus->methods->set_address) (udev, mtx, addr); if (err != USB_ERR_INVAL) goto done; /* Setting the address should not take more than 1 second ! */ err = usbd_do_request_flags(udev, mtx, &req, NULL, USB_DELAY_STATUS_STAGE, NULL, 1000); done: /* allow device time to set new address */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_set_address_settle)); return (err); } /*------------------------------------------------------------------------* * usbd_req_get_port_status * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx, struct usb_port_status *ps, uint8_t port) { struct usb_device_request req; req.bmRequestType = UT_READ_CLASS_OTHER; req.bRequest = UR_GET_STATUS; USETW(req.wValue, 0); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, sizeof(*ps)); return (usbd_do_request_flags(udev, mtx, &req, ps, 0, NULL, 1000)); } /*------------------------------------------------------------------------* * usbd_req_clear_hub_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_clear_hub_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_DEVICE; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_hub_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_DEVICE; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_hub_u1_timeout * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t timeout) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UHF_PORT_U1_TIMEOUT); req.wIndex[0] = port; req.wIndex[1] = timeout; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_hub_u2_timeout * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t timeout) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UHF_PORT_U2_TIMEOUT); req.wIndex[0] = port; req.wIndex[1] = timeout; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_hub_depth * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx, uint16_t depth) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_DEVICE; req.bRequest = UR_SET_HUB_DEPTH; USETW(req.wValue, depth); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_clear_port_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_clear_port_feature(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, sel); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_port_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_port_feature(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, sel); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_protocol * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_protocol(struct usb_device *udev, struct mtx *mtx, uint8_t iface_index, uint16_t report) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n", iface, report, iface->idesc->bInterfaceNumber); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_PROTOCOL; USETW(req.wValue, report); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_report * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "len=%d\n", len); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_REPORT; USETW2(req.wValue, type, id); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, len); return (usbd_do_request(udev, mtx, &req, data)); } /*------------------------------------------------------------------------* * usbd_req_get_report * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "len=%d\n", len); req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_REPORT; USETW2(req.wValue, type, id); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, len); return (usbd_do_request(udev, mtx, &req, data)); } /*------------------------------------------------------------------------* * usbd_req_set_idle * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_idle(struct usb_device *udev, struct mtx *mtx, uint8_t iface_index, uint8_t duration, uint8_t id) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "%d %d\n", duration, id); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_IDLE; USETW2(req.wValue, duration, id); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_get_report_descriptor * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_report_descriptor(struct usb_device *udev, struct mtx *mtx, void *d, uint16_t size, uint8_t iface_index) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } req.bmRequestType = UT_READ_INTERFACE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */ req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, size); return (usbd_do_request(udev, mtx, &req, d)); } /*------------------------------------------------------------------------* * usbd_req_set_config * * This function is used to select the current configuration number in * both USB device side mode and USB host side mode. When setting the * configuration the function of the interfaces can change. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_config(struct usb_device *udev, struct mtx *mtx, uint8_t conf) { struct usb_device_request req; DPRINTF("setting config %d\n", conf); /* do "set configuration" request */ req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_CONFIG; req.wValue[0] = conf; req.wValue[1] = 0; USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_get_config * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf) { struct usb_device_request req; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_CONFIG; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 1); return (usbd_do_request(udev, mtx, &req, pconf)); } /*------------------------------------------------------------------------* * usbd_setup_device_desc *------------------------------------------------------------------------*/ usb_error_t usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx) { usb_error_t err; /* * Get the first 8 bytes of the device descriptor ! * * NOTE: "usbd_do_request()" will check the device descriptor * next time we do a request to see if the maximum packet size * changed! The 8 first bytes of the device descriptor * contains the maximum packet size to use on control endpoint * 0. If this value is different from "USB_MAX_IPACKET" a new * USB control request will be setup! */ switch (udev->speed) { case USB_SPEED_FULL: if (usb_full_ddesc != 0) { /* get full device descriptor */ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); if (err == 0) break; } /* get partial device descriptor, some devices crash on this */ err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc, USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); if (err != 0) { DPRINTF("Trying fallback for getting the USB device descriptor\n"); /* try 8 bytes bMaxPacketSize */ udev->ddesc.bMaxPacketSize = 8; /* get full device descriptor */ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); if (err == 0) break; /* try 16 bytes bMaxPacketSize */ udev->ddesc.bMaxPacketSize = 16; /* get full device descriptor */ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); if (err == 0) break; /* try 32/64 bytes bMaxPacketSize */ udev->ddesc.bMaxPacketSize = 32; } /* get the full device descriptor */ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); break; default: DPRINTF("Minimum bMaxPacketSize is large enough " "to hold the complete device descriptor or " "only one bMaxPacketSize choice\n"); /* get the full device descriptor */ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); /* try one more time, if error */ if (err != 0) err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); break; } if (err != 0) { DPRINTFN(0, "getting device descriptor " "at addr %d failed, %s\n", udev->address, usbd_errstr(err)); return (err); } DPRINTF("adding unit addr=%d, rev=%02x, class=%d, " "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", udev->address, UGETW(udev->ddesc.bcdUSB), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, udev->ddesc.bDeviceProtocol, udev->ddesc.bMaxPacketSize, udev->ddesc.bLength, udev->speed); return (err); } /*------------------------------------------------------------------------* * usbd_req_re_enumerate * * NOTE: After this function returns the hardware is in the * unconfigured state! The application is responsible for setting a * new configuration. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) { struct usb_device *parent_hub; usb_error_t err; uint8_t old_addr; uint8_t do_retry = 1; if (udev->flags.usb_mode != USB_MODE_HOST) { return (USB_ERR_INVAL); } old_addr = udev->address; parent_hub = udev->parent_hub; if (parent_hub == NULL) { return (USB_ERR_INVAL); } retry: #if USB_HAVE_TT_SUPPORT /* * Try to reset the High Speed parent HUB of a LOW- or FULL- * speed device, if any. */ if (udev->parent_hs_hub != NULL && udev->speed != USB_SPEED_HIGH) { DPRINTF("Trying to reset parent High Speed TT.\n"); if (udev->parent_hs_hub == parent_hub && (uhub_count_active_host_ports(parent_hub, USB_SPEED_LOW) + uhub_count_active_host_ports(parent_hub, USB_SPEED_FULL)) == 1) { /* we can reset the whole TT */ err = usbd_req_reset_tt(parent_hub, NULL, udev->hs_port_no); } else { /* only reset a particular device and endpoint */ err = usbd_req_clear_tt_buffer(udev->parent_hs_hub, NULL, udev->hs_port_no, old_addr, UE_CONTROL, 0); } if (err) { DPRINTF("Resetting parent High " "Speed TT failed (%s).\n", usbd_errstr(err)); } } #endif /* Try to warm reset first */ if (parent_hub->speed == USB_SPEED_SUPER) usbd_req_warm_reset_port(parent_hub, mtx, udev->port_no); /* Try to reset the parent HUB port. */ err = usbd_req_reset_port(parent_hub, mtx, udev->port_no); if (err) { DPRINTFN(0, "addr=%d, port reset failed, %s\n", old_addr, usbd_errstr(err)); goto done; } /* * After that the port has been reset our device should be at * address zero: */ udev->address = USB_START_ADDR; /* reset "bMaxPacketSize" */ udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; /* reset USB state */ usb_set_device_state(udev, USB_STATE_POWERED); /* * Restore device address: */ err = usbd_req_set_address(udev, mtx, old_addr); if (err) { /* XXX ignore any errors! */ DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n", old_addr, usbd_errstr(err)); } /* * Restore device address, if the controller driver did not * set a new one: */ if (udev->address == USB_START_ADDR) udev->address = old_addr; /* setup the device descriptor and the initial "wMaxPacketSize" */ err = usbd_setup_device_desc(udev, mtx); done: if (err && do_retry) { /* give the USB firmware some time to load */ usb_pause_mtx(mtx, hz / 2); /* no more retries after this retry */ do_retry = 0; /* try again */ goto retry; } /* restore address */ if (udev->address == USB_START_ADDR) udev->address = old_addr; /* update state, if successful */ if (err == 0) usb_set_device_state(udev, USB_STATE_ADDRESSED); return (err); } /*------------------------------------------------------------------------* * usbd_req_clear_device_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_device_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_reset_tt * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_reset_tt(struct usb_device *udev, struct mtx *mtx, uint8_t port) { struct usb_device_request req; /* For single TT HUBs the port should be 1 */ if (udev->ddesc.bDeviceClass == UDCLASS_HUB && udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) port = 1; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_RESET_TT; USETW(req.wValue, 0); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_clear_tt_buffer * * For single TT HUBs the port should be 1. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t addr, uint8_t type, uint8_t endpoint) { struct usb_device_request req; uint16_t wValue; /* For single TT HUBs the port should be 1 */ if (udev->ddesc.bDeviceClass == UDCLASS_HUB && udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) port = 1; wValue = (endpoint & 0xF) | ((addr & 0x7F) << 4) | ((endpoint & 0x80) << 8) | ((type & 3) << 12); req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_CLEAR_TT_BUFFER; USETW(req.wValue, wValue); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_port_link_state * * USB 3.0 specific request * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_port_link_state(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t link_state) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UHF_PORT_LINK_STATE); req.wIndex[0] = port; req.wIndex[1] = link_state; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_lpm_info * * USB 2.0 specific request for Link Power Management. * * Returns: * 0: Success * USB_ERR_PENDING_REQUESTS: NYET * USB_ERR_TIMEOUT: TIMEOUT * USB_ERR_STALL: STALL * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_lpm_info(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t besl, uint8_t addr, uint8_t rwe) { struct usb_device_request req; usb_error_t err; uint8_t buf[1]; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_AND_TEST; USETW(req.wValue, UHF_PORT_L1); req.wIndex[0] = (port & 0xF) | ((besl & 0xF) << 4); req.wIndex[1] = (addr & 0x7F) | (rwe ? 0x80 : 0x00); USETW(req.wLength, sizeof(buf)); /* set default value in case of short transfer */ buf[0] = 0x00; err = usbd_do_request(udev, mtx, &req, buf); if (err) return (err); switch (buf[0]) { case 0x00: /* SUCCESS */ break; case 0x10: /* NYET */ err = USB_ERR_PENDING_REQUESTS; break; case 0x11: /* TIMEOUT */ err = USB_ERR_TIMEOUT; break; case 0x30: /* STALL */ err = USB_ERR_STALLED; break; default: /* reserved */ err = USB_ERR_IOERROR; break; } return (err); } Index: head/sys/dev/usb/usbdi.h =================================================================== --- head/sys/dev/usb/usbdi.h (revision 368658) +++ head/sys/dev/usb/usbdi.h (revision 368659) @@ -1,713 +1,715 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2009 Andrew Thompson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _USB_USBDI_H_ #define _USB_USBDI_H_ struct usb_fifo; struct usb_xfer; struct usb_device; struct usb_attach_arg; struct usb_interface; struct usb_endpoint; struct usb_page_cache; struct usb_page_search; struct usb_process; struct usb_proc_msg; struct usb_mbuf; struct usb_fs_privdata; struct mbuf; typedef enum { /* keep in sync with usb_errstr_table */ USB_ERR_NORMAL_COMPLETION = 0, USB_ERR_PENDING_REQUESTS, /* 1 */ USB_ERR_NOT_STARTED, /* 2 */ USB_ERR_INVAL, /* 3 */ USB_ERR_NOMEM, /* 4 */ USB_ERR_CANCELLED, /* 5 */ USB_ERR_BAD_ADDRESS, /* 6 */ USB_ERR_BAD_BUFSIZE, /* 7 */ USB_ERR_BAD_FLAG, /* 8 */ USB_ERR_NO_CALLBACK, /* 9 */ USB_ERR_IN_USE, /* 10 */ USB_ERR_NO_ADDR, /* 11 */ USB_ERR_NO_PIPE, /* 12 */ USB_ERR_ZERO_NFRAMES, /* 13 */ USB_ERR_ZERO_MAXP, /* 14 */ USB_ERR_SET_ADDR_FAILED, /* 15 */ USB_ERR_NO_POWER, /* 16 */ USB_ERR_TOO_DEEP, /* 17 */ USB_ERR_IOERROR, /* 18 */ USB_ERR_NOT_CONFIGURED, /* 19 */ USB_ERR_TIMEOUT, /* 20 */ USB_ERR_SHORT_XFER, /* 21 */ USB_ERR_STALLED, /* 22 */ USB_ERR_INTERRUPTED, /* 23 */ USB_ERR_DMA_LOAD_FAILED, /* 24 */ USB_ERR_BAD_CONTEXT, /* 25 */ USB_ERR_NO_ROOT_HUB, /* 26 */ USB_ERR_NO_INTR_THREAD, /* 27 */ USB_ERR_NOT_LOCKED, /* 28 */ USB_ERR_MAX } usb_error_t; /* * Flags for transfers */ #define USB_FORCE_SHORT_XFER 0x0001 /* force a short transmit last */ #define USB_SHORT_XFER_OK 0x0004 /* allow short reads */ #define USB_DELAY_STATUS_STAGE 0x0010 /* insert delay before STATUS stage */ #define USB_USER_DATA_PTR 0x0020 /* internal flag */ #define USB_MULTI_SHORT_OK 0x0040 /* allow multiple short frames */ #define USB_MANUAL_STATUS 0x0080 /* manual ctrl status */ #define USB_NO_TIMEOUT 0 #define USB_DEFAULT_TIMEOUT 5000 /* 5000 ms = 5 seconds */ #if defined(_KERNEL) || defined(_STANDALONE) /* typedefs */ typedef void (usb_callback_t)(struct usb_xfer *, usb_error_t); typedef void (usb_proc_callback_t)(struct usb_proc_msg *); typedef usb_error_t (usb_handle_req_t)(struct usb_device *, struct usb_device_request *, const void **, uint16_t *); typedef int (usb_fifo_open_t)(struct usb_fifo *fifo, int fflags); typedef void (usb_fifo_close_t)(struct usb_fifo *fifo, int fflags); typedef int (usb_fifo_ioctl_t)(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags); typedef void (usb_fifo_cmd_t)(struct usb_fifo *fifo); typedef void (usb_fifo_filter_t)(struct usb_fifo *fifo, struct usb_mbuf *m); /* USB events */ #ifndef USB_GLOBAL_INCLUDE_FILE #include #endif typedef void (*usb_dev_configured_t)(void *, struct usb_device *, struct usb_attach_arg *); EVENTHANDLER_DECLARE(usb_dev_configured, usb_dev_configured_t); /* * The following macros are used used to convert milliseconds into * HZ. We use 1024 instead of 1000 milliseconds per second to save a * full division. */ #define USB_MS_HZ 1024 #define USB_MS_TO_TICKS(ms) \ (((uint32_t)((((uint32_t)(ms)) * ((uint32_t)(hz))) + USB_MS_HZ - 1)) / USB_MS_HZ) /* * Common queue structure for USB transfers. */ struct usb_xfer_queue { TAILQ_HEAD(, usb_xfer) head; struct usb_xfer *curr; /* current USB transfer processed */ void (*command) (struct usb_xfer_queue *pq); uint8_t recurse_1:1; uint8_t recurse_2:1; uint8_t recurse_3:1; uint8_t reserved:5; }; /* * The following structure defines an USB endpoint * USB endpoint. */ struct usb_endpoint { /* queue of USB transfers */ struct usb_xfer_queue endpoint_q[USB_MAX_EP_STREAMS]; struct usb_endpoint_descriptor *edesc; struct usb_endpoint_ss_comp_descriptor *ecomp; const struct usb_pipe_methods *methods; /* set by HC driver */ uint16_t isoc_next; uint8_t toggle_next:1; /* next data toggle value */ uint8_t is_stalled:1; /* set if endpoint is stalled */ uint8_t is_synced:1; /* set if we a synchronised */ uint8_t unused:5; uint8_t iface_index; /* not used by "default endpoint" */ uint8_t refcount_alloc; /* allocation refcount */ uint8_t refcount_bw; /* bandwidth refcount */ #define USB_EP_REF_MAX 0x3f /* High-Speed resource allocation (valid if "refcount_bw" > 0) */ uint8_t usb_smask; /* USB start mask */ uint8_t usb_cmask; /* USB complete mask */ uint8_t usb_uframe; /* USB microframe */ /* USB endpoint mode, see USB_EP_MODE_XXX */ uint8_t ep_mode; }; /* * The following structure defines an USB interface. */ struct usb_interface { struct usb_interface_descriptor *idesc; device_t subdev; + /* Total number of alternate settings, from 1 to 256 */ + uint16_t num_altsetting; + /* Current alternate interface index, from 0 to 255 */ uint8_t alt_index; uint8_t parent_iface_index; /* Linux compat */ struct usb_host_interface *altsetting; struct usb_host_interface *cur_altsetting; struct usb_device *linux_udev; void *bsd_priv_sc; /* device specific information */ char *pnpinfo; /* additional PnP-info for this interface */ - uint8_t num_altsetting; /* number of alternate settings */ uint8_t bsd_iface_index; }; /* * The following structure defines a set of USB transfer flags. */ struct usb_xfer_flags { uint8_t force_short_xfer:1; /* force a short transmit transfer * last */ uint8_t short_xfer_ok:1; /* allow short receive transfers */ uint8_t short_frames_ok:1; /* allow short frames */ uint8_t pipe_bof:1; /* block pipe on failure */ uint8_t proxy_buffer:1; /* makes buffer size a factor of * "max_frame_size" */ uint8_t ext_buffer:1; /* uses external DMA buffer */ uint8_t manual_status:1; /* non automatic status stage on * control transfers */ uint8_t no_pipe_ok:1; /* set if "USB_ERR_NO_PIPE" error can * be ignored */ uint8_t stall_pipe:1; /* set if the endpoint belonging to * this USB transfer should be stalled * before starting this transfer! */ uint8_t pre_scale_frames:1; /* "usb_config->frames" is * assumed to give the * buffering time in * milliseconds and is * converted into the nearest * number of frames when the * USB transfer is setup. This * option only has effect for * ISOCHRONOUS transfers. */ }; /* * The following structure define an USB configuration, that basically * is used when setting up an USB transfer. */ struct usb_config { usb_callback_t *callback; /* USB transfer callback */ usb_frlength_t bufsize; /* total pipe buffer size in bytes */ usb_frcount_t frames; /* maximum number of USB frames */ usb_timeout_t interval; /* interval in milliseconds */ #define USB_DEFAULT_INTERVAL 0 usb_timeout_t timeout; /* transfer timeout in milliseconds */ struct usb_xfer_flags flags; /* transfer flags */ usb_stream_t stream_id; /* USB3.0 specific */ enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t type; /* pipe type */ uint8_t endpoint; /* pipe number */ uint8_t direction; /* pipe direction */ uint8_t ep_index; /* pipe index match to use */ uint8_t if_index; /* "ifaces" index to use */ }; /* * Use these macro when defining USB device ID arrays if you want to * have your driver module automatically loaded in host, device or * both modes respectively: */ #if USB_HAVE_ID_SECTION #define STRUCT_USB_HOST_ID \ struct usb_device_id __section("usb_host_id") #define STRUCT_USB_DEVICE_ID \ struct usb_device_id __section("usb_device_id") #define STRUCT_USB_DUAL_ID \ struct usb_device_id __section("usb_dual_id") #else #define STRUCT_USB_HOST_ID \ struct usb_device_id #define STRUCT_USB_DEVICE_ID \ struct usb_device_id #define STRUCT_USB_DUAL_ID \ struct usb_device_id #endif /* USB_HAVE_ID_SECTION */ /* * The following structure is used when looking up an USB driver for * an USB device. It is inspired by the Linux structure called * "usb_device_id". */ struct usb_device_id { /* Select which fields to match against */ #if BYTE_ORDER == LITTLE_ENDIAN uint16_t match_flag_vendor:1, match_flag_product:1, match_flag_dev_lo:1, match_flag_dev_hi:1, match_flag_dev_class:1, match_flag_dev_subclass:1, match_flag_dev_protocol:1, match_flag_int_class:1, match_flag_int_subclass:1, match_flag_int_protocol:1, match_flag_unused:6; #else uint16_t match_flag_unused:6, match_flag_int_protocol:1, match_flag_int_subclass:1, match_flag_int_class:1, match_flag_dev_protocol:1, match_flag_dev_subclass:1, match_flag_dev_class:1, match_flag_dev_hi:1, match_flag_dev_lo:1, match_flag_product:1, match_flag_vendor:1; #endif /* Used for product specific matches; the BCD range is inclusive */ uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice_lo; uint16_t bcdDevice_hi; /* Used for device class matches */ uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; /* Used for interface class matches */ uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; #if USB_HAVE_COMPAT_LINUX /* which fields to match against */ uint16_t match_flags; #define USB_DEVICE_ID_MATCH_VENDOR 0x0001 #define USB_DEVICE_ID_MATCH_PRODUCT 0x0002 #define USB_DEVICE_ID_MATCH_DEV_LO 0x0004 #define USB_DEVICE_ID_MATCH_DEV_HI 0x0008 #define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010 #define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020 #define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040 #define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 #endif /* Hook for driver specific information */ unsigned long driver_info; } __aligned(32); #define USB_STD_PNP_INFO "M16:mask;U16:vendor;U16:product;L16:release;G16:release;" \ "U8:devclass;U8:devsubclass;U8:devproto;" \ "U8:intclass;U8:intsubclass;U8:intprotocol;" #define USB_STD_PNP_HOST_INFO USB_STD_PNP_INFO "T:mode=host;" #define USB_STD_PNP_DEVICE_INFO USB_STD_PNP_INFO "T:mode=device;" #define USB_PNP_HOST_INFO(table) \ MODULE_PNP_INFO(USB_STD_PNP_HOST_INFO, uhub, table, table, \ sizeof(table) / sizeof(table[0])) #define USB_PNP_DEVICE_INFO(table) \ MODULE_PNP_INFO(USB_STD_PNP_DEVICE_INFO, uhub, table, table, \ sizeof(table) / sizeof(table[0])) #define USB_PNP_DUAL_INFO(table) \ MODULE_PNP_INFO(USB_STD_PNP_INFO, uhub, table, table, \ sizeof(table) / sizeof(table[0])) /* check that the size of the structure above is correct */ extern char usb_device_id_assert[(sizeof(struct usb_device_id) == 32) ? 1 : -1]; #define USB_VENDOR(vend) \ .match_flag_vendor = 1, .idVendor = (vend) #define USB_PRODUCT(prod) \ .match_flag_product = 1, .idProduct = (prod) #define USB_VP(vend,prod) \ USB_VENDOR(vend), USB_PRODUCT(prod) #define USB_VPI(vend,prod,info) \ USB_VENDOR(vend), USB_PRODUCT(prod), USB_DRIVER_INFO(info) #define USB_DEV_BCD_GTEQ(lo) /* greater than or equal */ \ .match_flag_dev_lo = 1, .bcdDevice_lo = (lo) #define USB_DEV_BCD_LTEQ(hi) /* less than or equal */ \ .match_flag_dev_hi = 1, .bcdDevice_hi = (hi) #define USB_DEV_CLASS(dc) \ .match_flag_dev_class = 1, .bDeviceClass = (dc) #define USB_DEV_SUBCLASS(dsc) \ .match_flag_dev_subclass = 1, .bDeviceSubClass = (dsc) #define USB_DEV_PROTOCOL(dp) \ .match_flag_dev_protocol = 1, .bDeviceProtocol = (dp) #define USB_IFACE_CLASS(ic) \ .match_flag_int_class = 1, .bInterfaceClass = (ic) #define USB_IFACE_SUBCLASS(isc) \ .match_flag_int_subclass = 1, .bInterfaceSubClass = (isc) #define USB_IFACE_PROTOCOL(ip) \ .match_flag_int_protocol = 1, .bInterfaceProtocol = (ip) #define USB_IF_CSI(class,subclass,info) \ USB_IFACE_CLASS(class), USB_IFACE_SUBCLASS(subclass), USB_DRIVER_INFO(info) #define USB_DRIVER_INFO(n) \ .driver_info = (n) #define USB_GET_DRIVER_INFO(did) \ (did)->driver_info /* * The following structure keeps information that is used to match * against an array of "usb_device_id" elements. */ struct usbd_lookup_info { uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; uint8_t bIfaceIndex; uint8_t bIfaceNum; uint8_t bConfigIndex; uint8_t bConfigNum; }; /* Structure used by probe and attach */ struct usb_attach_arg { struct usbd_lookup_info info; device_t temp_dev; /* for internal use */ unsigned long driver_info; /* for internal use */ void *driver_ivar; struct usb_device *device; /* current device */ struct usb_interface *iface; /* current interface */ enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t port; uint8_t dev_state; #define UAA_DEV_READY 0 #define UAA_DEV_DISABLED 1 #define UAA_DEV_EJECTING 2 }; /* * General purpose locking wrappers to ease supporting * USB polled mode: */ #ifdef INVARIANTS #define USB_MTX_ASSERT(_m, _t) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ mtx_assert(_m, _t); \ } while (0) #else #define USB_MTX_ASSERT(_m, _t) do { } while (0) #endif #define USB_MTX_LOCK(_m) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ mtx_lock(_m); \ } while (0) #define USB_MTX_UNLOCK(_m) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ mtx_unlock(_m); \ } while (0) #define USB_MTX_LOCK_SPIN(_m) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ mtx_lock_spin(_m); \ } while (0) #define USB_MTX_UNLOCK_SPIN(_m) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ mtx_unlock_spin(_m); \ } while (0) /* * The following is a wrapper for the callout structure to ease * porting the code to other platforms. */ struct usb_callout { struct callout co; }; #define usb_callout_init_mtx(c,m,f) callout_init_mtx(&(c)->co,m,f) #define usb_callout_reset(c,...) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ callout_reset(&(c)->co, __VA_ARGS__); \ } while (0) #define usb_callout_reset_sbt(c,...) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ callout_reset_sbt(&(c)->co, __VA_ARGS__); \ } while (0) #define usb_callout_stop(c) do { \ if (!USB_IN_POLLING_MODE_FUNC()) { \ callout_stop(&(c)->co); \ } else { \ /* \ * Cannot stop callout when \ * polling. Set dummy callback \ * function instead: \ */ \ (c)->co.c_func = &usbd_dummy_timeout; \ } \ } while (0) #define usb_callout_drain(c) callout_drain(&(c)->co) #define usb_callout_pending(c) callout_pending(&(c)->co) /* USB transfer states */ #define USB_ST_SETUP 0 #define USB_ST_TRANSFERRED 1 #define USB_ST_ERROR 2 /* USB handle request states */ #define USB_HR_NOT_COMPLETE 0 #define USB_HR_COMPLETE_OK 1 #define USB_HR_COMPLETE_ERR 2 /* * The following macro will return the current state of an USB * transfer like defined by the "USB_ST_XXX" enums. */ #define USB_GET_STATE(xfer) (usbd_xfer_state(xfer)) /* * The following structure defines the USB process message header. */ struct usb_proc_msg { TAILQ_ENTRY(usb_proc_msg) pm_qentry; usb_proc_callback_t *pm_callback; usb_size_t pm_num; }; #define USB_FIFO_TX 0 #define USB_FIFO_RX 1 /* * Locking note for the following functions. All the * "usb_fifo_cmd_t" and "usb_fifo_filter_t" functions are called * locked. The others are called unlocked. */ struct usb_fifo_methods { usb_fifo_open_t *f_open; usb_fifo_close_t *f_close; usb_fifo_ioctl_t *f_ioctl; /* * NOTE: The post-ioctl callback is called after the USB reference * gets locked in the IOCTL handler: */ usb_fifo_ioctl_t *f_ioctl_post; usb_fifo_cmd_t *f_start_read; usb_fifo_cmd_t *f_stop_read; usb_fifo_cmd_t *f_start_write; usb_fifo_cmd_t *f_stop_write; usb_fifo_filter_t *f_filter_read; usb_fifo_filter_t *f_filter_write; const char *basename[4]; const char *postfix[4]; }; struct usb_fifo_sc { struct usb_fifo *fp[2]; struct usb_fs_privdata *dev; }; const char *usbd_errstr(usb_error_t error); void *usbd_find_descriptor(struct usb_device *udev, void *id, uint8_t iface_index, uint8_t type, uint8_t type_mask, uint8_t subtype, uint8_t subtype_mask); struct usb_config_descriptor *usbd_get_config_descriptor( struct usb_device *udev); struct usb_device_descriptor *usbd_get_device_descriptor( struct usb_device *udev); struct usb_interface *usbd_get_iface(struct usb_device *udev, uint8_t iface_index); struct usb_interface_descriptor *usbd_get_interface_descriptor( struct usb_interface *iface); struct usb_endpoint *usbd_get_endpoint(struct usb_device *udev, uint8_t iface_index, const struct usb_config *setup); struct usb_endpoint *usbd_get_ep_by_addr(struct usb_device *udev, uint8_t ea_val); usb_error_t usbd_interface_count(struct usb_device *udev, uint8_t *count); enum usb_hc_mode usbd_get_mode(struct usb_device *udev); enum usb_dev_speed usbd_get_speed(struct usb_device *udev); void device_set_usb_desc(device_t dev); void usb_pause_mtx(struct mtx *mtx, int _ticks); usb_error_t usbd_set_pnpinfo(struct usb_device *udev, uint8_t iface_index, const char *pnpinfo); usb_error_t usbd_add_dynamic_quirk(struct usb_device *udev, uint16_t quirk); usb_error_t usbd_set_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep, uint8_t ep_mode); uint8_t usbd_get_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep); const struct usb_device_id *usbd_lookup_id_by_info( const struct usb_device_id *id, usb_size_t sizeof_id, const struct usbd_lookup_info *info); int usbd_lookup_id_by_uaa(const struct usb_device_id *id, usb_size_t sizeof_id, struct usb_attach_arg *uaa); usb_error_t usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, struct usb_device_request *req, void *data, uint16_t flags, uint16_t *actlen, usb_timeout_t timeout); #define usbd_do_request(u,m,r,d) \ usbd_do_request_flags(u,m,r,d,0,NULL,USB_DEFAULT_TIMEOUT) uint8_t usbd_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2); uint8_t usbd_get_interface_altindex(struct usb_interface *iface); usb_error_t usbd_set_alt_interface_index(struct usb_device *udev, uint8_t iface_index, uint8_t alt_index); uint32_t usbd_get_isoc_fps(struct usb_device *udev); uint32_t usbd_get_max_frame_length(const struct usb_endpoint_descriptor *, const struct usb_endpoint_ss_comp_descriptor *, enum usb_dev_speed); usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **pxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *priv_mtx); void usbd_transfer_submit(struct usb_xfer *xfer); void usbd_transfer_clear_stall(struct usb_xfer *xfer); void usbd_transfer_drain(struct usb_xfer *xfer); uint8_t usbd_transfer_pending(struct usb_xfer *xfer); void usbd_transfer_start(struct usb_xfer *xfer); void usbd_transfer_stop(struct usb_xfer *xfer); void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup); void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max); void usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index, uint8_t parent_index); uint8_t usbd_get_bus_index(struct usb_device *udev); uint8_t usbd_get_device_index(struct usb_device *udev); void usbd_set_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usbd_device_attached(struct usb_device *udev); usb_frlength_t usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex); void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes); struct usb_page_cache *usbd_xfer_get_frame(struct usb_xfer *, usb_frcount_t); void *usbd_xfer_get_frame_buffer(struct usb_xfer *, usb_frcount_t); void *usbd_xfer_softc(struct usb_xfer *xfer); void *usbd_xfer_get_priv(struct usb_xfer *xfer); void usbd_xfer_set_priv(struct usb_xfer *xfer, void *); void usbd_xfer_set_interval(struct usb_xfer *xfer, int); uint8_t usbd_xfer_state(struct usb_xfer *xfer); void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void *ptr, usb_frlength_t len); void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void **ptr, int *len); void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex); usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer); usb_frlength_t usbd_xfer_max_framelen(struct usb_xfer *xfer); usb_frcount_t usbd_xfer_max_frames(struct usb_xfer *xfer); uint8_t usbd_xfer_get_fps_shift(struct usb_xfer *xfer); usb_frlength_t usbd_xfer_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex); void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex, usb_frlength_t len); void usbd_xfer_set_timeout(struct usb_xfer *xfer, int timeout); void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n); void usbd_xfer_set_stall(struct usb_xfer *xfer); int usbd_xfer_is_stalled(struct usb_xfer *xfer); void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag); void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag); uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer); uint8_t usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer); void usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len); int usbd_copy_in_user(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len); void usbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len); int usbd_copy_out_user(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len); void usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset, struct usb_page_search *res); void usbd_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset, struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len); void usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset, usb_frlength_t len); void usbd_start_re_enumerate(struct usb_device *udev); usb_error_t usbd_start_set_config(struct usb_device *, uint8_t); int usbd_in_polling_mode(void); void usbd_dummy_timeout(void *); int usb_fifo_attach(struct usb_device *udev, void *priv_sc, struct mtx *priv_mtx, struct usb_fifo_methods *pm, struct usb_fifo_sc *f_sc, uint16_t unit, int16_t subunit, uint8_t iface_index, uid_t uid, gid_t gid, int mode); void usb_fifo_detach(struct usb_fifo_sc *f_sc); int usb_fifo_alloc_buffer(struct usb_fifo *f, uint32_t bufsize, uint16_t nbuf); void usb_fifo_free_buffer(struct usb_fifo *f); uint32_t usb_fifo_put_bytes_max(struct usb_fifo *fifo); void usb_fifo_put_data(struct usb_fifo *fifo, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, uint8_t what); void usb_fifo_put_data_linear(struct usb_fifo *fifo, void *ptr, usb_size_t len, uint8_t what); uint8_t usb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len); void usb_fifo_put_data_error(struct usb_fifo *fifo); uint8_t usb_fifo_get_data(struct usb_fifo *fifo, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen, uint8_t what); uint8_t usb_fifo_get_data_linear(struct usb_fifo *fifo, void *ptr, usb_size_t len, usb_size_t *actlen, uint8_t what); uint8_t usb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen); void usb_fifo_reset(struct usb_fifo *f); void usb_fifo_wakeup(struct usb_fifo *f); void usb_fifo_get_data_error(struct usb_fifo *fifo); void *usb_fifo_softc(struct usb_fifo *fifo); void usb_fifo_set_close_zlp(struct usb_fifo *, uint8_t); void usb_fifo_set_write_defrag(struct usb_fifo *, uint8_t); void usb_fifo_free(struct usb_fifo *f); #endif /* _KERNEL || _STANDALONE */ #endif /* _USB_USBDI_H_ */ Index: head/sys/sys/param.h =================================================================== --- head/sys/sys/param.h (revision 368658) +++ head/sys/sys/param.h (revision 368659) @@ -1,374 +1,374 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)param.h 8.3 (Berkeley) 4/4/95 * $FreeBSD$ */ #ifndef _SYS_PARAM_H_ #define _SYS_PARAM_H_ #include #define BSD 199506 /* System version (year & month). */ #define BSD4_3 1 #define BSD4_4 1 /* * __FreeBSD_version numbers are documented in the Porter's Handbook. * If you bump the version for any reason, you should update the documentation * there. * Currently this lives here in the doc/ repository: * * head/en_US.ISO8859-1/books/porters-handbook/versions/chapter.xml * * scheme is: Rxx * 'R' is in the range 0 to 4 if this is a release branch or * X.0-CURRENT before releng/X.0 is created, otherwise 'R' is * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1300131 /* Master, propagated to newvers */ +#define __FreeBSD_version 1300132 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, * which by definition is always true on FreeBSD. This macro is also defined * on other systems that use the kernel of FreeBSD, such as GNU/kFreeBSD. * * It is tempting to use this macro in userland code when we want to enable * kernel-specific routines, and in fact it's fine to do this in code that * is part of FreeBSD itself. However, be aware that as presence of this * macro is still not widespread (e.g. older FreeBSD versions, 3rd party * compilers, etc), it is STRONGLY DISCOURAGED to check for this macro in * external applications without also checking for __FreeBSD__ as an * alternative. */ #undef __FreeBSD_kernel__ #define __FreeBSD_kernel__ #if defined(_KERNEL) || defined(IN_RTLD) #define P_OSREL_SIGWAIT 700000 #define P_OSREL_SIGSEGV 700004 #define P_OSREL_MAP_ANON 800104 #define P_OSREL_MAP_FSTRICT 1100036 #define P_OSREL_SHUTDOWN_ENOTCONN 1100077 #define P_OSREL_MAP_GUARD 1200035 #define P_OSREL_WRFSBASE 1200041 #define P_OSREL_CK_CYLGRP 1200046 #define P_OSREL_VMTOTAL64 1200054 #define P_OSREL_CK_SUPERBLOCK 1300000 #define P_OSREL_CK_INODE 1300005 #define P_OSREL_POWERPC_NEW_AUX_ARGS 1300070 #define P_OSREL_MAJOR(x) ((x) / 100000) #endif #ifndef LOCORE #include #endif /* * Machine-independent constants (some used in following include files). * Redefined constants are from POSIX 1003.1 limits file. * * MAXCOMLEN should be >= sizeof(ac_comm) (see ) */ #include #define MAXCOMLEN 19 /* max command name remembered */ #define MAXINTERP PATH_MAX /* max interpreter file name length */ #define MAXLOGNAME 33 /* max login name length (incl. NUL) */ #define MAXUPRC CHILD_MAX /* max simultaneous processes */ #define NCARGS ARG_MAX /* max bytes for an exec function */ #define NGROUPS (NGROUPS_MAX+1) /* max number groups */ #define NOFILE OPEN_MAX /* max open files per process */ #define NOGROUP 65535 /* marker for empty group set member */ #define MAXHOSTNAMELEN 256 /* max hostname size */ #define SPECNAMELEN 255 /* max length of devicename */ /* More types and definitions used throughout the kernel. */ #ifdef _KERNEL #include #include #ifndef LOCORE #include #include #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #endif #ifndef _KERNEL #ifndef LOCORE /* Signals. */ #include #endif #endif /* Machine type dependent parameters. */ #include #ifndef _KERNEL #include #endif #ifndef DEV_BSHIFT #define DEV_BSHIFT 9 /* log2(DEV_BSIZE) */ #endif #define DEV_BSIZE (1<>PAGE_SHIFT) #endif /* * btodb() is messy and perhaps slow because `bytes' may be an off_t. We * want to shift an unsigned type to avoid sign extension and we don't * want to widen `bytes' unnecessarily. Assume that the result fits in * a daddr_t. */ #ifndef btodb #define btodb(bytes) /* calculates (bytes / DEV_BSIZE) */ \ (sizeof (bytes) > sizeof(long) \ ? (daddr_t)((unsigned long long)(bytes) >> DEV_BSHIFT) \ : (daddr_t)((unsigned long)(bytes) >> DEV_BSHIFT)) #endif #ifndef dbtob #define dbtob(db) /* calculates (db * DEV_BSIZE) */ \ ((off_t)(db) << DEV_BSHIFT) #endif #define PRIMASK 0x0ff #define PCATCH 0x100 /* OR'd with pri for tsleep to check signals */ #define PDROP 0x200 /* OR'd with pri to stop re-entry of interlock mutex */ #define NZERO 0 /* default "nice" */ #define NBBY 8 /* number of bits in a byte */ #define NBPW sizeof(int) /* number of bytes per word (integer) */ #define CMASK 022 /* default file mask: S_IWGRP|S_IWOTH */ #define NODEV (dev_t)(-1) /* non-existent device */ /* * File system parameters and macros. * * MAXBSIZE - Filesystems are made out of blocks of at most MAXBSIZE bytes * per block. MAXBSIZE may be made larger without effecting * any existing filesystems as long as it does not exceed MAXPHYS, * and may be made smaller at the risk of not being able to use * filesystems which require a block size exceeding MAXBSIZE. * * MAXBCACHEBUF - Maximum size of a buffer in the buffer cache. This must * be >= MAXBSIZE and can be set differently for different * architectures by defining it in . * Making this larger allows NFS to do larger reads/writes. * * BKVASIZE - Nominal buffer space per buffer, in bytes. BKVASIZE is the * minimum KVM memory reservation the kernel is willing to make. * Filesystems can of course request smaller chunks. Actual * backing memory uses a chunk size of a page (PAGE_SIZE). * The default value here can be overridden on a per-architecture * basis by defining it in . * * If you make BKVASIZE too small you risk seriously fragmenting * the buffer KVM map which may slow things down a bit. If you * make it too big the kernel will not be able to optimally use * the KVM memory reserved for the buffer cache and will wind * up with too-few buffers. * * The default is 16384, roughly 2x the block size used by a * normal UFS filesystem. */ #define MAXBSIZE 65536 /* must be power of 2 */ #ifndef MAXBCACHEBUF #define MAXBCACHEBUF MAXBSIZE /* must be a power of 2 >= MAXBSIZE */ #endif #ifndef BKVASIZE #define BKVASIZE 16384 /* must be power of 2 */ #endif #define BKVAMASK (BKVASIZE-1) /* * MAXPATHLEN defines the longest permissible path length after expanding * symbolic links. It is used to allocate a temporary buffer from the buffer * pool in which to do the name expansion, hence should be a power of two, * and must be less than or equal to MAXBSIZE. MAXSYMLINKS defines the * maximum number of symbolic links that may be expanded in a path name. * It should be set high enough to allow all legitimate uses, but halt * infinite loops reasonably quickly. */ #define MAXPATHLEN PATH_MAX #define MAXSYMLINKS 32 /* Bit map related macros. */ #define setbit(a,i) (((unsigned char *)(a))[(i)/NBBY] |= 1<<((i)%NBBY)) #define clrbit(a,i) (((unsigned char *)(a))[(i)/NBBY] &= ~(1<<((i)%NBBY))) #define isset(a,i) \ (((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) #define isclr(a,i) \ ((((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) == 0) /* Macros for counting and rounding. */ #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif #define nitems(x) (sizeof((x)) / sizeof((x)[0])) #define rounddown(x, y) (((x)/(y))*(y)) #define rounddown2(x, y) ((x)&(~((y)-1))) /* if y is power of two */ #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ #define powerof2(x) ((((x)-1)&(x))==0) /* Macros for min/max. */ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) #ifdef _KERNEL /* * Basic byte order function prototypes for non-inline functions. */ #ifndef LOCORE #ifndef _BYTEORDER_PROTOTYPED #define _BYTEORDER_PROTOTYPED __BEGIN_DECLS __uint32_t htonl(__uint32_t); __uint16_t htons(__uint16_t); __uint32_t ntohl(__uint32_t); __uint16_t ntohs(__uint16_t); __END_DECLS #endif #endif #ifndef _BYTEORDER_FUNC_DEFINED #define _BYTEORDER_FUNC_DEFINED #define htonl(x) __htonl(x) #define htons(x) __htons(x) #define ntohl(x) __ntohl(x) #define ntohs(x) __ntohs(x) #endif /* !_BYTEORDER_FUNC_DEFINED */ #endif /* _KERNEL */ /* * Scale factor for scaled integers used to count %cpu time and load avgs. * * The number of CPU `tick's that map to a unique `%age' can be expressed * by the formula (1 / (2 ^ (FSHIFT - 11))). The maximum load average that * can be calculated (assuming 32 bits) can be closely approximated using * the formula (2 ^ (2 * (16 - FSHIFT))) for (FSHIFT < 15). * * For the scheduler to maintain a 1:1 mapping of CPU `tick' to `%age', * FSHIFT must be at least 11; this gives us a maximum load avg of ~1024. */ #define FSHIFT 11 /* bits to right of fixed binary point */ #define FSCALE (1<> (PAGE_SHIFT - DEV_BSHIFT)) #define ctodb(db) /* calculates pages to devblks */ \ ((db) << (PAGE_SHIFT - DEV_BSHIFT)) /* * Old spelling of __containerof(). */ #define member2struct(s, m, x) \ ((struct s *)(void *)((char *)(x) - offsetof(struct s, m))) /* * Access a variable length array that has been declared as a fixed * length array. */ #define __PAST_END(array, offset) (((__typeof__(*(array)) *)(array))[offset]) #endif /* _SYS_PARAM_H_ */