diff options
author | thompsa <thompsa@FreeBSD.org> | 2009-05-27 16:16:56 +0000 |
---|---|---|
committer | thompsa <thompsa@FreeBSD.org> | 2009-05-27 16:16:56 +0000 |
commit | 14ea3ee2bb371575697cd652ca2064877b187983 (patch) | |
tree | 723b0aed327f79b20884592648d5b64748a24f12 /sys/legacy/dev/usb/usb_subr.c | |
parent | 3f72bbde95ea9eee2e163b14ffdca8533a85e6c1 (diff) | |
download | FreeBSD-src-14ea3ee2bb371575697cd652ca2064877b187983.zip FreeBSD-src-14ea3ee2bb371575697cd652ca2064877b187983.tar.gz |
Delete the old USB stack. The new stack has settled in and has all the
drivers/functionality and then some.
Diffstat (limited to 'sys/legacy/dev/usb/usb_subr.c')
-rw-r--r-- | sys/legacy/dev/usb/usb_subr.c | 1388 |
1 files changed, 0 insertions, 1388 deletions
diff --git a/sys/legacy/dev/usb/usb_subr.c b/sys/legacy/dev/usb/usb_subr.c deleted file mode 100644 index 29d044c..0000000 --- a/sys/legacy/dev/usb/usb_subr.c +++ /dev/null @@ -1,1388 +0,0 @@ -/* $NetBSD: usb_subr.c,v 1.99 2002/07/11 21:14:34 augustss Exp $ */ - -/* Also already have from NetBSD: - * $NetBSD: usb_subr.c,v 1.102 2003/01/01 16:21:50 augustss Exp $ - * $NetBSD: usb_subr.c,v 1.103 2003/01/10 11:19:13 augustss Exp $ - * $NetBSD: usb_subr.c,v 1.111 2004/03/15 10:35:04 augustss Exp $ - * $NetBSD: usb_subr.c,v 1.114 2004/06/23 02:30:52 mycroft Exp $ - * $NetBSD: usb_subr.c,v 1.115 2004/06/23 05:23:19 mycroft Exp $ - * $NetBSD: usb_subr.c,v 1.116 2004/06/23 06:27:54 mycroft Exp $ - * $NetBSD: usb_subr.c,v 1.119 2004/10/23 13:26:33 augustss Exp $ - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -/*- - * Copyright (c) 1998 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Lennart Augustsson (lennart@augustsson.net) at - * Carlstedt Research & Technology. - * - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#include "opt_usb.h" - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/malloc.h> -#include <sys/module.h> -#include <sys/bus.h> -#include <sys/proc.h> -#include <sys/sysctl.h> - -#include <machine/bus.h> - -#include <dev/usb/usb.h> - -#include <dev/usb/usbdi.h> -#include <dev/usb/usbdi_util.h> -#include <dev/usb/usbdivar.h> -#include "usbdevs.h" -#include <dev/usb/usb_quirks.h> - -#define delay(d) DELAY(d) - -#ifdef USB_DEBUG -#define DPRINTF(x) if (usbdebug) printf x -#define DPRINTFN(n,x) if (usbdebug>(n)) printf x -extern int usbdebug; -#else -#define DPRINTF(x) -#define DPRINTFN(n,x) -#endif - -static usbd_status usbd_set_config(usbd_device_handle, int); -static void usbd_devinfo_vp(usbd_device_handle, char *, char *, int); -static int usbd_getnewaddr(usbd_bus_handle bus); -static void usbd_free_iface_data(usbd_device_handle dev, int ifcno); -static void usbd_kill_pipe(usbd_pipe_handle); -static usbd_status usbd_probe_and_attach(device_t parent, - usbd_device_handle dev, int port, int addr); - -static u_int32_t usb_cookie_no = 0; - -#ifdef USBVERBOSE -typedef u_int16_t usb_vendor_id_t; -typedef u_int16_t usb_product_id_t; - -/* - * Descriptions of of known vendors and devices ("products"). - */ -struct usb_knowndev { - usb_vendor_id_t vendor; - usb_product_id_t product; - int flags; - char *vendorname, *productname; -}; -#define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */ - -#include "usbdevs_data.h" -#endif /* USBVERBOSE */ - -static const char * const usbd_error_strs[] = { - "NORMAL_COMPLETION", - "IN_PROGRESS", - "PENDING_REQUESTS", - "NOT_STARTED", - "INVAL", - "NOMEM", - "CANCELLED", - "BAD_ADDRESS", - "IN_USE", - "NO_ADDR", - "SET_ADDR_FAILED", - "NO_POWER", - "TOO_DEEP", - "IOERROR", - "NOT_CONFIGURED", - "TIMEOUT", - "SHORT_XFER", - "STALLED", - "INTERRUPTED", - "XXX", -}; - -const char * -usbd_errstr(usbd_status err) -{ - static char buffer[5]; - - if (err < USBD_ERROR_MAX) { - return usbd_error_strs[err]; - } else { - snprintf(buffer, sizeof buffer, "%d", err); - return buffer; - } -} - -usbd_status -usbd_get_string_desc(usbd_device_handle dev, int sindex, int langid, - usb_string_descriptor_t *sdesc, int *sizep) -{ - usb_device_request_t req; - usbd_status err; - int actlen; - - req.bmRequestType = UT_READ_DEVICE; - req.bRequest = UR_GET_DESCRIPTOR; - USETW2(req.wValue, UDESC_STRING, sindex); - USETW(req.wIndex, langid); - USETW(req.wLength, 2); /* only size byte first */ - err = usbd_do_request_flags(dev, &req, sdesc, USBD_SHORT_XFER_OK, - &actlen, USBD_DEFAULT_TIMEOUT); - if (err) - return (err); - - if (actlen < 2) - return (USBD_SHORT_XFER); - - USETW(req.wLength, sdesc->bLength); /* the whole string */ - err = usbd_do_request_flags(dev, &req, sdesc, USBD_SHORT_XFER_OK, - &actlen, USBD_DEFAULT_TIMEOUT); - if (err) - return (err); - - if (actlen != sdesc->bLength) { - DPRINTFN(-1, ("usbd_get_string_desc: expected %d, got %d\n", - sdesc->bLength, actlen)); - } - - *sizep = actlen; - return (USBD_NORMAL_COMPLETION); -} - -static void -usbd_trim_spaces(char *p) -{ - char *q, *e; - - if (p == NULL) - return; - q = e = p; - while (*q == ' ') /* skip leading spaces */ - q++; - while ((*p = *q++)) /* copy string */ - if (*p++ != ' ') /* remember last non-space */ - e = p; - *e = 0; /* kill trailing spaces */ -} - -static void -usbd_devinfo_vp(usbd_device_handle dev, char *v, char *p, int usedev) -{ - usb_device_descriptor_t *udd = &dev->ddesc; - char *vendor = 0, *product = 0; -#ifdef USBVERBOSE - const struct usb_knowndev *kdp; -#endif - - if (dev == NULL) { - v[0] = p[0] = '\0'; - return; - } - - if (usedev) { - if (usbd_get_string(dev, udd->iManufacturer, v, - USB_MAX_STRING_LEN)) - vendor = NULL; - else - vendor = v; - usbd_trim_spaces(vendor); - if (usbd_get_string(dev, udd->iProduct, p, - USB_MAX_STRING_LEN)) - product = NULL; - else - product = p; - usbd_trim_spaces(product); - if (vendor && !*vendor) - vendor = NULL; - if (product && !*product) - product = NULL; - } else { - vendor = NULL; - product = NULL; - } -#ifdef USBVERBOSE - if (vendor == NULL || product == NULL) { - for(kdp = usb_knowndevs; - kdp->vendorname != NULL; - kdp++) { - if (kdp->vendor == UGETW(udd->idVendor) && - (kdp->product == UGETW(udd->idProduct) || - (kdp->flags & USB_KNOWNDEV_NOPROD) != 0)) - break; - } - if (kdp->vendorname != NULL) { - if (vendor == NULL) - vendor = kdp->vendorname; - if (product == NULL) - product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ? - kdp->productname : NULL; - } - } -#endif - if (vendor != NULL && *vendor) - strcpy(v, vendor); - else - sprintf(v, "vendor 0x%04x", UGETW(udd->idVendor)); - if (product != NULL && *product) - strcpy(p, product); - else - sprintf(p, "product 0x%04x", UGETW(udd->idProduct)); -} - -int -usbd_printBCD(char *cp, int bcd) -{ - return (sprintf(cp, "%x.%02x", bcd >> 8, bcd & 0xff)); -} - -void -usbd_devinfo(usbd_device_handle dev, int showclass, char *cp) -{ - usb_device_descriptor_t *udd = &dev->ddesc; - usbd_interface_handle iface; - char vendor[USB_MAX_STRING_LEN]; - char product[USB_MAX_STRING_LEN]; - int bcdDevice, bcdUSB; - usb_interface_descriptor_t *id; - - usbd_devinfo_vp(dev, vendor, product, 1); - cp += sprintf(cp, "%s %s", vendor, product); - if (showclass & USBD_SHOW_DEVICE_CLASS) - cp += sprintf(cp, ", class %d/%d", - udd->bDeviceClass, udd->bDeviceSubClass); - bcdUSB = UGETW(udd->bcdUSB); - bcdDevice = UGETW(udd->bcdDevice); - cp += sprintf(cp, ", rev "); - cp += usbd_printBCD(cp, bcdUSB); - *cp++ = '/'; - cp += usbd_printBCD(cp, bcdDevice); - cp += sprintf(cp, ", addr %d", dev->address); - if (showclass & USBD_SHOW_INTERFACE_CLASS) - { - /* fetch the interface handle for the first interface */ - (void)usbd_device2interface_handle(dev, 0, &iface); - id = usbd_get_interface_descriptor(iface); - cp += sprintf(cp, ", iclass %d/%d", - id->bInterfaceClass, id->bInterfaceSubClass); - } - *cp = 0; -} - -/* Delay for a certain number of ms */ -void -usb_delay_ms(usbd_bus_handle bus, u_int ms) -{ - /* Wait at least two clock ticks so we know the time has passed. */ - if (bus->use_polling || cold) - delay((ms+1) * 1000); - else - pause("usbdly", (ms*hz+999)/1000 + 1); -} - -/* Delay given a device handle. */ -void -usbd_delay_ms(usbd_device_handle dev, u_int ms) -{ - usb_delay_ms(dev->bus, ms); -} - -usbd_status -usbd_reset_port(usbd_device_handle dev, int port, usb_port_status_t *ps) -{ - usbd_status err; - int n; - - err = usbd_set_port_feature(dev, port, UHF_PORT_RESET); - DPRINTFN(1,("usbd_reset_port: port %d reset done, error=%s\n", - port, usbd_errstr(err))); - if (err) - return (err); - n = 10; - do { - /* Wait for device to recover from reset. */ - usbd_delay_ms(dev, USB_PORT_RESET_DELAY); - err = usbd_get_port_status(dev, port, ps); - if (err) { - DPRINTF(("usbd_reset_port: get status failed %d\n", - err)); - return (err); - } - /* If the device disappeared, just give up. */ - if (!(UGETW(ps->wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) - return (USBD_NORMAL_COMPLETION); - } while ((UGETW(ps->wPortChange) & UPS_C_PORT_RESET) == 0 && --n > 0); - if (n == 0) - return (USBD_TIMEOUT); - err = usbd_clear_port_feature(dev, port, UHF_C_PORT_RESET); -#ifdef USB_DEBUG - if (err) - DPRINTF(("usbd_reset_port: clear port feature failed %d\n", - err)); -#endif - - /* Wait for the device to recover from reset. */ - usbd_delay_ms(dev, USB_PORT_RESET_RECOVERY); - return (err); -} - -usb_interface_descriptor_t * -usbd_find_idesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx) -{ - char *p = (char *)cd; - char *end = p + UGETW(cd->wTotalLength); - usb_interface_descriptor_t *d; - int curidx, lastidx, curaidx = 0; - - for (curidx = lastidx = -1; p < end; ) { - d = (usb_interface_descriptor_t *)p; - DPRINTFN(4,("usbd_find_idesc: idx=%d(%d) altidx=%d(%d) len=%d " - "type=%d\n", - ifaceidx, curidx, altidx, curaidx, - d->bLength, d->bDescriptorType)); - if (d->bLength == 0) /* bad descriptor */ - break; - p += d->bLength; - if (p <= end && d->bDescriptorType == UDESC_INTERFACE) { - if (d->bInterfaceNumber != lastidx) { - lastidx = d->bInterfaceNumber; - curidx++; - curaidx = 0; - } else - curaidx++; - if (ifaceidx == curidx && altidx == curaidx) - return (d); - } - } - return (NULL); -} - -usb_endpoint_descriptor_t * -usbd_find_edesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx, - int endptidx) -{ - char *p = (char *)cd; - char *end = p + UGETW(cd->wTotalLength); - usb_interface_descriptor_t *d; - usb_endpoint_descriptor_t *e; - int curidx; - - d = usbd_find_idesc(cd, ifaceidx, altidx); - if (d == NULL) - return (NULL); - if (endptidx >= d->bNumEndpoints) /* quick exit */ - return (NULL); - - curidx = -1; - for (p = (char *)d + d->bLength; p < end; ) { - e = (usb_endpoint_descriptor_t *)p; - if (e->bLength == 0) /* bad descriptor */ - break; - p += e->bLength; - if (p <= end && e->bDescriptorType == UDESC_INTERFACE) - return (NULL); - if (p <= end && e->bDescriptorType == UDESC_ENDPOINT) { - curidx++; - if (curidx == endptidx) - return (e); - } - } - return (NULL); -} - -usbd_status -usbd_fill_iface_data(usbd_device_handle dev, int ifaceidx, int altidx) -{ - usbd_interface_handle ifc = &dev->ifaces[ifaceidx]; - usb_interface_descriptor_t *idesc; - char *p, *end; - int endpt, nendpt; - - DPRINTFN(4,("usbd_fill_iface_data: ifaceidx=%d altidx=%d\n", - ifaceidx, altidx)); - idesc = usbd_find_idesc(dev->cdesc, ifaceidx, altidx); - if (idesc == NULL) - return (USBD_INVAL); - ifc->device = dev; - ifc->idesc = idesc; - ifc->index = ifaceidx; - ifc->altindex = altidx; - nendpt = ifc->idesc->bNumEndpoints; - DPRINTFN(4,("usbd_fill_iface_data: found idesc nendpt=%d\n", nendpt)); - if (nendpt != 0) { - ifc->endpoints = malloc(nendpt * sizeof(struct usbd_endpoint), - M_USB, M_NOWAIT); - if (ifc->endpoints == NULL) - return (USBD_NOMEM); - } else - ifc->endpoints = NULL; - ifc->priv = NULL; - p = (char *)ifc->idesc + ifc->idesc->bLength; - end = (char *)dev->cdesc + UGETW(dev->cdesc->wTotalLength); -#define ed ((usb_endpoint_descriptor_t *)p) - for (endpt = 0; endpt < nendpt; endpt++) { - DPRINTFN(10,("usbd_fill_iface_data: endpt=%d\n", endpt)); - for (; p < end; p += ed->bLength) { - DPRINTFN(10,("usbd_fill_iface_data: p=%p end=%p " - "len=%d type=%d\n", - p, end, ed->bLength, ed->bDescriptorType)); - if (p + ed->bLength <= end && ed->bLength != 0 && - ed->bDescriptorType == UDESC_ENDPOINT) - goto found; - if (ed->bLength == 0 || - ed->bDescriptorType == UDESC_INTERFACE) - break; - } - /* passed end, or bad desc */ - printf("usbd_fill_iface_data: bad descriptor(s): %s\n", - ed->bLength == 0 ? "0 length" : - ed->bDescriptorType == UDESC_INTERFACE ? "iface desc": - "out of data"); - goto bad; - found: - ifc->endpoints[endpt].edesc = ed; - if (dev->speed == USB_SPEED_HIGH) { - u_int mps; - /* Control and bulk endpoints have max packet limits. */ - switch (UE_GET_XFERTYPE(ed->bmAttributes)) { - case UE_CONTROL: - mps = USB_2_MAX_CTRL_PACKET; - goto check; - case UE_BULK: - mps = USB_2_MAX_BULK_PACKET; - check: - if (UGETW(ed->wMaxPacketSize) != mps) { - USETW(ed->wMaxPacketSize, mps); -#ifdef DIAGNOSTIC - printf("usbd_fill_iface_data: bad max " - "packet size\n"); -#endif - } - break; - default: - break; - } - } - ifc->endpoints[endpt].refcnt = 0; - ifc->endpoints[endpt].savedtoggle = 0; - p += ed->bLength; - } -#undef ed - LIST_INIT(&ifc->pipes); - return (USBD_NORMAL_COMPLETION); - - bad: - if (ifc->endpoints != NULL) { - free(ifc->endpoints, M_USB); - ifc->endpoints = NULL; - } - return (USBD_INVAL); -} - -void -usbd_free_iface_data(usbd_device_handle dev, int ifcno) -{ - usbd_interface_handle ifc = &dev->ifaces[ifcno]; - if (ifc->endpoints) - free(ifc->endpoints, M_USB); -} - -static usbd_status -usbd_set_config(usbd_device_handle dev, int conf) -{ - usb_device_request_t req; - - req.bmRequestType = UT_WRITE_DEVICE; - req.bRequest = UR_SET_CONFIG; - USETW(req.wValue, conf); - USETW(req.wIndex, 0); - USETW(req.wLength, 0); - return (usbd_do_request(dev, &req, 0)); -} - -usbd_status -usbd_set_config_no(usbd_device_handle dev, int no, int msg) -{ - int index; - usb_config_descriptor_t cd; - usbd_status err; - - if (no == USB_UNCONFIG_NO) - return (usbd_set_config_index(dev, USB_UNCONFIG_INDEX, msg)); - - DPRINTFN(5,("usbd_set_config_no: %d\n", no)); - /* Figure out what config index to use. */ - for (index = 0; index < dev->ddesc.bNumConfigurations; index++) { - err = usbd_get_config_desc(dev, index, &cd); - if (err) - return (err); - if (cd.bConfigurationValue == no) - return (usbd_set_config_index(dev, index, msg)); - } - return (USBD_INVAL); -} - -usbd_status -usbd_set_config_index(usbd_device_handle dev, int index, int msg) -{ - usb_status_t ds; - usb_config_descriptor_t cd, *cdp; - usbd_status err; - int i, ifcidx, nifc, len, selfpowered, power; - - DPRINTFN(5,("usbd_set_config_index: dev=%p index=%d\n", dev, index)); - - if (dev->config != USB_UNCONFIG_NO) { - nifc = dev->cdesc->bNumInterface; - - /* Check that all interfaces are idle */ - for (ifcidx = 0; ifcidx < nifc; ifcidx++) { - if (LIST_EMPTY(&dev->ifaces[ifcidx].pipes)) - continue; - DPRINTF(("usbd_set_config_index: open pipes exist\n")); - return (USBD_IN_USE); - } - - DPRINTF(("usbd_set_config_index: free old config\n")); - /* Free all configuration data structures. */ - for (ifcidx = 0; ifcidx < nifc; ifcidx++) - usbd_free_iface_data(dev, ifcidx); - free(dev->ifaces, M_USB); - free(dev->cdesc, M_USB); - dev->ifaces = NULL; - dev->cdesc = NULL; - dev->config = USB_UNCONFIG_NO; - } - - if (index == USB_UNCONFIG_INDEX) { - /* We are unconfiguring the device, so leave unallocated. */ - DPRINTF(("usbd_set_config_index: set config 0\n")); - err = usbd_set_config(dev, USB_UNCONFIG_NO); - if (err) - DPRINTF(("usbd_set_config_index: setting config=0 " - "failed, error=%s\n", usbd_errstr(err))); - return (err); - } - - /* Get the short descriptor. */ - err = usbd_get_config_desc(dev, index, &cd); - if (err) - return (err); - len = UGETW(cd.wTotalLength); - cdp = malloc(len, M_USB, M_NOWAIT); - if (cdp == NULL) - return (USBD_NOMEM); - - /* Get the full descriptor. Try a few times for slow devices. */ - for (i = 0; i < 3; i++) { - err = usbd_get_desc(dev, UDESC_CONFIG, index, len, cdp); - if (!err) - break; - usbd_delay_ms(dev, 200); - } - if (err) - goto bad; - if (cdp->bDescriptorType != UDESC_CONFIG) { - DPRINTFN(-1,("usbd_set_config_index: bad desc %d\n", - cdp->bDescriptorType)); - err = USBD_INVAL; - goto bad; - } - - /* Figure out if the device is self or bus powered. */ - selfpowered = 0; - if (!(dev->quirks->uq_flags & UQ_BUS_POWERED) && - (cdp->bmAttributes & UC_SELF_POWERED)) { - /* May be self powered. */ - if (cdp->bmAttributes & UC_BUS_POWERED) { - /* Must ask device. */ - if (dev->quirks->uq_flags & UQ_POWER_CLAIM) { - /* - * Hub claims to be self powered, but isn't. - * It seems that the power status can be - * determined by the hub characteristics. - */ - usb_hub_descriptor_t hd; - usb_device_request_t req; - req.bmRequestType = UT_READ_CLASS_DEVICE; - req.bRequest = UR_GET_DESCRIPTOR; - USETW(req.wValue, 0); - USETW(req.wIndex, 0); - USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE); - err = usbd_do_request(dev, &req, &hd); - if (!err && - (UGETW(hd.wHubCharacteristics) & - UHD_PWR_INDIVIDUAL)) - selfpowered = 1; - DPRINTF(("usbd_set_config_index: charac=0x%04x" - ", error=%s\n", - UGETW(hd.wHubCharacteristics), - usbd_errstr(err))); - } else { - err = usbd_get_device_status(dev, &ds); - if (!err && - (UGETW(ds.wStatus) & UDS_SELF_POWERED)) - selfpowered = 1; - DPRINTF(("usbd_set_config_index: status=0x%04x" - ", error=%s\n", - UGETW(ds.wStatus), usbd_errstr(err))); - } - } else - selfpowered = 1; - } - DPRINTF(("usbd_set_config_index: (addr %d) cno=%d attr=0x%02x, " - "selfpowered=%d, power=%d\n", - cdp->bConfigurationValue, dev->address, cdp->bmAttributes, - selfpowered, cdp->bMaxPower * 2)); - - /* Check if we have enough power. */ -#ifdef USB_DEBUG - if (dev->powersrc == NULL) { - DPRINTF(("usbd_set_config_index: No power source?\n")); - return (USBD_IOERROR); - } -#endif - power = cdp->bMaxPower * 2; - if (power > dev->powersrc->power) { - DPRINTF(("power exceeded %d %d\n", power,dev->powersrc->power)); - /* XXX print nicer message. */ - if (msg) - device_printf(dev->bus->bdev, - "device addr %d (config %d) exceeds " - "power budget, %d mA > %d mA\n", - dev->address, cdp->bConfigurationValue, - power, dev->powersrc->power); - err = USBD_NO_POWER; - goto bad; - } - dev->power = power; - dev->self_powered = selfpowered; - - /* Set the actual configuration value. */ - DPRINTF(("usbd_set_config_index: set config %d\n", - cdp->bConfigurationValue)); - err = usbd_set_config(dev, cdp->bConfigurationValue); - if (err) { - DPRINTF(("usbd_set_config_index: setting config=%d failed, " - "error=%s\n", - cdp->bConfigurationValue, usbd_errstr(err))); - goto bad; - } - - /* Allocate and fill interface data. */ - nifc = cdp->bNumInterface; - dev->ifaces = malloc(nifc * sizeof(struct usbd_interface), - M_USB, M_NOWAIT); - if (dev->ifaces == NULL) { - err = USBD_NOMEM; - goto bad; - } - DPRINTFN(5,("usbd_set_config_index: dev=%p cdesc=%p\n", dev, cdp)); - dev->cdesc = cdp; - dev->config = cdp->bConfigurationValue; - for (ifcidx = 0; ifcidx < nifc; ifcidx++) { - err = usbd_fill_iface_data(dev, ifcidx, 0); - if (err) { - while (--ifcidx >= 0) - usbd_free_iface_data(dev, ifcidx); - goto bad; - } - } - - return (USBD_NORMAL_COMPLETION); - - bad: - free(cdp, M_USB); - return (err); -} - -/* XXX add function for alternate settings */ - -usbd_status -usbd_setup_pipe(usbd_device_handle dev, usbd_interface_handle iface, - struct usbd_endpoint *ep, int ival, usbd_pipe_handle *pipe) -{ - usbd_pipe_handle p; - usbd_status err; - - DPRINTFN(1,("usbd_setup_pipe: dev=%p iface=%p ep=%p pipe=%p\n", - dev, iface, ep, pipe)); - p = malloc(dev->bus->pipe_size, M_USB, M_NOWAIT); - if (p == NULL) - return (USBD_NOMEM); - p->device = dev; - p->iface = iface; - p->endpoint = ep; - ep->refcnt++; - p->refcnt = 1; - p->intrxfer = 0; - p->running = 0; - p->aborting = 0; - p->repeat = 0; - p->interval = ival; - STAILQ_INIT(&p->queue); - err = dev->bus->methods->open_pipe(p); - if (err) { - DPRINTFN(-1,("usbd_setup_pipe: endpoint=0x%x failed, error=" - "%s\n", - ep->edesc->bEndpointAddress, usbd_errstr(err))); - free(p, M_USB); - return (err); - } - - if (dev->quirks->uq_flags & UQ_OPEN_CLEARSTALL) { - /* Clear any stall and make sure DATA0 toggle will be used next. */ - if (UE_GET_ADDR(ep->edesc->bEndpointAddress) != USB_CONTROL_ENDPOINT) { - err = usbd_clear_endpoint_stall(p); - if (err && err != USBD_STALLED && err != USBD_TIMEOUT) { - printf("usbd_setup_pipe: failed to start " - "endpoint, %s\n", usbd_errstr(err)); - return (err); - } - } - } - - *pipe = p; - return (USBD_NORMAL_COMPLETION); -} - -/* Abort the device control pipe. */ -void -usbd_kill_pipe(usbd_pipe_handle pipe) -{ - usbd_abort_pipe(pipe); - pipe->methods->close(pipe); - pipe->endpoint->refcnt--; - free(pipe, M_USB); -} - -int -usbd_getnewaddr(usbd_bus_handle bus) -{ - int addr; - - for (addr = 1; addr < USB_MAX_DEVICES; addr++) - if (bus->devices[addr] == 0) - return (addr); - return (-1); -} - - -usbd_status -usbd_probe_and_attach(device_t parent, usbd_device_handle dev, - int port, int addr) -{ - struct usb_attach_arg uaa; - usb_device_descriptor_t *dd = &dev->ddesc; - int found, i, confi, nifaces; - usbd_status err; - device_t *tmpdv; - usbd_interface_handle ifaces[256]; /* 256 is the absolute max */ - char *devinfo; - - /* XXX FreeBSD may leak resources on failure cases -- fixme */ - device_t bdev; - struct usb_attach_arg *uaap; - - devinfo = malloc(1024, M_USB, M_NOWAIT); - if (devinfo == NULL) { - device_printf(parent, "Can't allocate memory for probe string\n"); - return (USBD_NOMEM); - } - bdev = device_add_child(parent, NULL, -1); - if (!bdev) { - free(devinfo, M_USB); - device_printf(parent, "Device creation failed\n"); - return (USBD_INVAL); - } - uaap = malloc(sizeof(uaa), M_USB, M_NOWAIT); - if (uaap == NULL) { - free(devinfo, M_USB); - return (USBD_INVAL); - } - device_set_ivars(bdev, uaap); - uaa.device = dev; - uaa.iface = NULL; - uaa.ifaces = NULL; - uaa.nifaces = 0; - uaa.usegeneric = 0; - uaa.port = port; - uaa.configno = UHUB_UNK_CONFIGURATION; - uaa.ifaceno = UHUB_UNK_INTERFACE; - uaa.vendor = UGETW(dd->idVendor); - uaa.product = UGETW(dd->idProduct); - uaa.release = UGETW(dd->bcdDevice); - uaa.matchlvl = 0; - - /* First try with device specific drivers. */ - DPRINTF(("usbd_probe_and_attach: trying device specific drivers\n")); - - dev->ifacenums = NULL; - dev->subdevs = malloc(2 * sizeof(device_t), M_USB, M_NOWAIT); - if (dev->subdevs == NULL) { - free(devinfo, M_USB); - return (USBD_NOMEM); - } - dev->subdevs[0] = bdev; - dev->subdevs[1] = 0; - *uaap = uaa; - usbd_devinfo(dev, 1, devinfo); - device_set_desc_copy(bdev, devinfo); - if (device_probe_and_attach(bdev) == 0) { - free(devinfo, M_USB); - return (USBD_NORMAL_COMPLETION); - } - - /* - * Free subdevs so we can reallocate it larger for the number of - * interfaces - */ - tmpdv = dev->subdevs; - dev->subdevs = NULL; - free(tmpdv, M_USB); - DPRINTF(("usbd_probe_and_attach: no device specific driver found\n")); - - DPRINTF(("usbd_probe_and_attach: looping over %d configurations\n", - dd->bNumConfigurations)); - /* Next try with interface drivers. */ - for (confi = 0; confi < dd->bNumConfigurations; confi++) { - DPRINTFN(1,("usbd_probe_and_attach: trying config idx=%d\n", - confi)); - err = usbd_set_config_index(dev, confi, 1); - if (err) { -#ifdef USB_DEBUG - DPRINTF(("%s: port %d, set config at addr %d failed, " - "error=%s\n", device_get_nameunit(parent), port, - addr, usbd_errstr(err))); -#else - printf("%s: port %d, set config at addr %d failed\n", - device_get_nameunit(parent), port, addr); -#endif - free(devinfo, M_USB); - return (err); - } - nifaces = dev->cdesc->bNumInterface; - uaa.configno = dev->cdesc->bConfigurationValue; - for (i = 0; i < nifaces; i++) - ifaces[i] = &dev->ifaces[i]; - uaa.ifaces = ifaces; - uaa.nifaces = nifaces; - dev->subdevs = malloc((nifaces+1) * sizeof(device_t), M_USB,M_NOWAIT); - if (dev->subdevs == NULL) { - free(devinfo, M_USB); - return (USBD_NOMEM); - } - dev->ifacenums = malloc((nifaces) * sizeof(*dev->ifacenums), - M_USB,M_NOWAIT); - if (dev->ifacenums == NULL) { - free(devinfo, M_USB); - return (USBD_NOMEM); - } - - found = 0; - for (i = 0; i < nifaces; i++) { - if (ifaces[i] == NULL) - continue; /* interface already claimed */ - uaa.iface = ifaces[i]; - uaa.ifaceno = ifaces[i]->idesc->bInterfaceNumber; - dev->subdevs[found] = bdev; - dev->subdevs[found + 1] = 0; - dev->ifacenums[found] = i; - *uaap = uaa; - usbd_devinfo(dev, 1, devinfo); - device_set_desc_copy(bdev, devinfo); - if (device_probe_and_attach(bdev) == 0) { - ifaces[i] = NULL; /* consumed */ - found++; - /* create another child for the next iface */ - bdev = device_add_child(parent, NULL, -1); - if (!bdev) { - device_printf(parent, - "Device add failed\n"); - free(devinfo, M_USB); - return (USBD_NORMAL_COMPLETION); - } - uaap = malloc(sizeof(uaa), M_USB, M_NOWAIT); - if (uaap == NULL) { - free(devinfo, M_USB); - return (USBD_NOMEM); - } - device_set_ivars(bdev, uaap); - } else { - dev->subdevs[found] = 0; - } - } - if (found != 0) { - /* remove the last created child. It is unused */ - free(uaap, M_USB); - free(devinfo, M_USB); - device_delete_child(parent, bdev); - /* free(uaap, M_USB); */ /* May be needed? xxx */ - return (USBD_NORMAL_COMPLETION); - } - tmpdv = dev->subdevs; - dev->subdevs = NULL; - free(tmpdv, M_USB); - free(dev->ifacenums, M_USB); - dev->ifacenums = NULL; - } - /* No interfaces were attached in any of the configurations. */ - - if (dd->bNumConfigurations > 1) /* don't change if only 1 config */ - usbd_set_config_index(dev, 0, 0); - - DPRINTF(("usbd_probe_and_attach: no interface drivers found\n")); - - /* Finally try the generic driver. */ - uaa.iface = NULL; - uaa.usegeneric = 1; - uaa.configno = UHUB_UNK_CONFIGURATION; - uaa.ifaceno = UHUB_UNK_INTERFACE; - dev->subdevs = malloc(2 * sizeof(device_t), M_USB, M_NOWAIT); - if (dev->subdevs == 0) { - free(devinfo, M_USB); - return (USBD_NOMEM); - } - dev->subdevs[0] = bdev; - dev->subdevs[1] = 0; - *uaap = uaa; - usbd_devinfo(dev, 1, devinfo); - device_set_desc_copy(bdev, devinfo); - free(devinfo, M_USB); - if (device_probe_and_attach(bdev) == 0) - return (USBD_NORMAL_COMPLETION); - - /* - * The generic attach failed, but leave the device as it is. - * We just did not find any drivers, that's all. The device is - * fully operational and not harming anyone. - */ - DPRINTF(("usbd_probe_and_attach: generic attach failed\n")); - return (USBD_NORMAL_COMPLETION); -} - - -/* - * 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 attach a driver. - */ -usbd_status -usbd_new_device(device_t parent, usbd_bus_handle bus, int depth, - int speed, int port, struct usbd_port *up) -{ - usbd_device_handle dev, adev; - struct usbd_device *hub; - usb_device_descriptor_t *dd; - usb_port_status_t ps; - usbd_status err; - int addr; - int i; - int p; - - DPRINTF(("usbd_new_device bus=%p port=%d depth=%d speed=%d\n", - bus, port, depth, speed)); - addr = usbd_getnewaddr(bus); - if (addr < 0) { - device_printf(bus->bdev, "No free USB addresses\n"); - return (USBD_NO_ADDR); - } - - dev = malloc(sizeof *dev, M_USB, M_NOWAIT|M_ZERO); - if (dev == NULL) - return (USBD_NOMEM); - - dev->bus = bus; - - /* Set up default endpoint handle. */ - dev->def_ep.edesc = &dev->def_ep_desc; - - /* Set up default endpoint descriptor. */ - dev->def_ep_desc.bLength = USB_ENDPOINT_DESCRIPTOR_SIZE; - dev->def_ep_desc.bDescriptorType = UDESC_ENDPOINT; - dev->def_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT; - dev->def_ep_desc.bmAttributes = UE_CONTROL; - USETW(dev->def_ep_desc.wMaxPacketSize, USB_MAX_IPACKET); - dev->def_ep_desc.bInterval = 0; - - dev->quirks = &usbd_no_quirk; - dev->address = USB_START_ADDR; - dev->ddesc.bMaxPacketSize = 0; - dev->depth = depth; - dev->powersrc = up; - dev->myhub = up->parent; - - up->device = dev; - - if (up->parent && speed > up->parent->speed) { -#ifdef USB_DEBUG - printf("%s: maxium speed of attached " - "device, %d, is higher than speed " - "of parent HUB, %d.\n", - __FUNCTION__, speed, up->parent->speed); -#endif - /* - * Reduce the speed, otherwise we won't setup the - * proper transfer methods. - */ - speed = up->parent->speed; - } - - /* Locate port on upstream high speed hub */ - for (adev = dev, hub = up->parent; - hub != NULL && hub->speed != USB_SPEED_HIGH; - adev = hub, hub = hub->myhub) - ; - if (hub) { - for (p = 0; p < hub->hub->hubdesc.bNbrPorts; p++) { - if (hub->hub->ports[p].device == adev) { - dev->myhsport = &hub->hub->ports[p]; - goto found; - } - } - panic("usbd_new_device: cannot find HS port\n"); - found: - DPRINTFN(1,("usbd_new_device: high speed port %d\n", p)); - } else { - dev->myhsport = NULL; - } - dev->speed = speed; - dev->langid = USBD_NOLANG; - dev->cookie.cookie = ++usb_cookie_no; - - /* Establish the default pipe. */ - err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL, - &dev->default_pipe); - if (err) { - usbd_remove_device(dev, up); - return (err); - } - - dd = &dev->ddesc; - /* Try a few times in case the device is slow (i.e. outside specs.) */ - DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr)); - for (i = 0; i < 15; i++) { - /* Get the first 8 bytes of the device descriptor. */ - err = usbd_get_desc(dev, UDESC_DEVICE, 0, USB_MAX_IPACKET, dd); - if (!err) - break; - usbd_delay_ms(dev, 200); - if ((i & 3) == 3) { - DPRINTFN(-1,("usb_new_device: set address %d " - "failed - trying a port reset\n", addr)); - usbd_reset_port(up->parent, port, &ps); - } - - } - if (err) { - DPRINTFN(-1, ("usbd_new_device: addr=%d, getting first desc " - "failed\n", addr)); - usbd_remove_device(dev, up); - return (err); - } - - if (speed == USB_SPEED_HIGH) { - /* Max packet size must be 64 (sec 5.5.3). */ - if (dd->bMaxPacketSize != USB_2_MAX_CTRL_PACKET) { -#ifdef DIAGNOSTIC - printf("usbd_new_device: addr=%d bad max packet size\n", - addr); -#endif - dd->bMaxPacketSize = USB_2_MAX_CTRL_PACKET; - } - } - - DPRINTF(("usbd_new_device: adding unit addr=%d, rev=%02x, class=%d, " - "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", - addr,UGETW(dd->bcdUSB), dd->bDeviceClass, dd->bDeviceSubClass, - dd->bDeviceProtocol, dd->bMaxPacketSize, dd->bLength, - dev->speed)); - - if (dd->bDescriptorType != UDESC_DEVICE) { - /* Illegal device descriptor */ - DPRINTFN(-1,("usbd_new_device: illegal descriptor %d\n", - dd->bDescriptorType)); - usbd_remove_device(dev, up); - return (USBD_INVAL); - } - - if (dd->bLength < USB_DEVICE_DESCRIPTOR_SIZE) { - DPRINTFN(-1,("usbd_new_device: bad length %d\n", dd->bLength)); - usbd_remove_device(dev, up); - return (USBD_INVAL); - } - - USETW(dev->def_ep_desc.wMaxPacketSize, dd->bMaxPacketSize); - - /* Re-establish the default pipe with the new max packet size. */ - usbd_kill_pipe(dev->default_pipe); - err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL, - &dev->default_pipe); - if (err) { - usbd_remove_device(dev, up); - return (err); - } - - err = usbd_reload_device_desc(dev); - if (err) { - DPRINTFN(-1, ("usbd_new_device: addr=%d, getting full desc " - "failed\n", addr)); - usbd_remove_device(dev, up); - return (err); - } - - /* Set the address */ - DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr)); - err = usbd_set_address(dev, addr); - if (err) { - DPRINTFN(-1,("usb_new_device: set address %d failed\n", addr)); - err = USBD_SET_ADDR_FAILED; - usbd_remove_device(dev, up); - return (err); - } - - /* Allow device time to set new address */ - usbd_delay_ms(dev, USB_SET_ADDRESS_SETTLE); - dev->address = addr; /* New device address now */ - bus->devices[addr] = dev; - - /* Re-establish the default pipe with the new address. */ - usbd_kill_pipe(dev->default_pipe); - err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL, - &dev->default_pipe); - if (err) { - usbd_remove_device(dev, up); - return (err); - } - - /* Assume 100mA bus powered for now. Changed when configured. */ - dev->power = USB_MIN_POWER; - dev->self_powered = 0; - - DPRINTF(("usbd_new_device: new dev (addr %d), dev=%p, parent=%p\n", - addr, dev, parent)); - - err = usbd_probe_and_attach(parent, dev, port, addr); - if (err) { - usbd_remove_device(dev, up); - return (err); - } - - usbd_add_dev_event(USB_EVENT_DEVICE_ATTACH, dev); - - return (USBD_NORMAL_COMPLETION); -} - -usbd_status -usbd_reload_device_desc(usbd_device_handle dev) -{ - usbd_status err; - int i; - - /* Get the full device descriptor. */ - for (i = 0; i < 3; ++i) { - err = usbd_get_device_desc(dev, &dev->ddesc); - if (!err) - break; - usbd_delay_ms(dev, 200); - } - if (err) - return (err); - - /* Figure out what's wrong with this device. */ - dev->quirks = usbd_find_quirk(&dev->ddesc); - - return (USBD_NORMAL_COMPLETION); -} - -void -usbd_remove_device(usbd_device_handle dev, struct usbd_port *up) -{ - DPRINTF(("usbd_remove_device: %p\n", dev)); - - if (dev->default_pipe != NULL) - usbd_kill_pipe(dev->default_pipe); - up->device = NULL; - dev->bus->devices[dev->address] = NULL; - - free(dev, M_USB); -} - -void -usbd_fill_deviceinfo(usbd_device_handle dev, struct usb_device_info *di, - int usedev) -{ - struct usbd_port *p; - int i, err, s; - - di->udi_bus = device_get_unit(dev->bus->bdev); - di->udi_addr = dev->address; - di->udi_cookie = dev->cookie; - usbd_devinfo_vp(dev, di->udi_vendor, di->udi_product, usedev); - usbd_printBCD(di->udi_release, UGETW(dev->ddesc.bcdDevice)); - di->udi_vendorNo = UGETW(dev->ddesc.idVendor); - di->udi_productNo = UGETW(dev->ddesc.idProduct); - di->udi_releaseNo = UGETW(dev->ddesc.bcdDevice); - di->udi_class = dev->ddesc.bDeviceClass; - di->udi_subclass = dev->ddesc.bDeviceSubClass; - di->udi_protocol = dev->ddesc.bDeviceProtocol; - di->udi_config = dev->config; - di->udi_power = dev->self_powered ? 0 : dev->power; - di->udi_speed = dev->speed; - - if (dev->subdevs != NULL) { - for (i = 0; dev->subdevs[i] && i < USB_MAX_DEVNAMES; i++) { - if (device_is_attached(dev->subdevs[i])) - strlcpy(di->udi_devnames[i], - device_get_nameunit(dev->subdevs[i]), - USB_MAX_DEVNAMELEN); - else - di->udi_devnames[i][0] = 0; - } - } else { - i = 0; - } - for (/*i is set */; i < USB_MAX_DEVNAMES; i++) - di->udi_devnames[i][0] = 0; /* empty */ - - if (dev->hub) { - for (i = 0; - i < sizeof(di->udi_ports) / sizeof(di->udi_ports[0]) && - i < dev->hub->hubdesc.bNbrPorts; - i++) { - p = &dev->hub->ports[i]; - if (p->device) - err = p->device->address; - else { - s = UGETW(p->status.wPortStatus); - if (s & UPS_PORT_ENABLED) - err = USB_PORT_ENABLED; - else if (s & UPS_SUSPEND) - err = USB_PORT_SUSPENDED; - else if (s & UPS_PORT_POWER) - err = USB_PORT_POWERED; - else - err = USB_PORT_DISABLED; - } - di->udi_ports[i] = err; - } - di->udi_nports = dev->hub->hubdesc.bNbrPorts; - } else - di->udi_nports = 0; -} - -void -usb_free_device(usbd_device_handle dev) -{ - int ifcidx, nifc; - - if (dev->default_pipe != NULL) - usbd_kill_pipe(dev->default_pipe); - if (dev->ifaces != NULL) { - nifc = dev->cdesc->bNumInterface; - for (ifcidx = 0; ifcidx < nifc; ifcidx++) - usbd_free_iface_data(dev, ifcidx); - free(dev->ifaces, M_USB); - } - if (dev->cdesc != NULL) - free(dev->cdesc, M_USB); - if (dev->subdevs != NULL) - free(dev->subdevs, M_USB); - if (dev->ifacenums != NULL) - free(dev->ifacenums, M_USB); - free(dev, M_USB); -} - -/* - * The general mechanism for detaching drivers works as follows: Each - * driver is responsible for maintaining a reference count on the - * number of outstanding references to its softc (e.g. from - * processing hanging in a read or write). The detach method of the - * driver decrements this counter and flags in the softc that the - * driver is dying and then wakes any sleepers. It then sleeps on the - * softc. Each place that can sleep must maintain the reference - * count. When the reference count drops to -1 (0 is the normal value - * of the reference count) the a wakeup on the softc is performed - * signaling to the detach waiter that all references are gone. - */ - -/* - * Called from process context when we discover that a port has - * been disconnected. - */ -void -usb_disconnect_port(struct usbd_port *up, device_t parent) -{ - usbd_device_handle dev = up->device; - const char *hubname = device_get_nameunit(parent); - int i; - - DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n", - up, dev, up->portno)); - -#ifdef DIAGNOSTIC - if (dev == NULL) { - printf("usb_disconnect_port: no device\n"); - return; - } -#endif - - if (dev->subdevs != NULL) { - DPRINTFN(3,("usb_disconnect_port: disconnect subdevs\n")); - for (i = 0; dev->subdevs[i]; i++) { - if (!device_is_quiet(dev->subdevs[i])) { - device_printf(dev->subdevs[i], - "at %s", hubname); - if (up->portno != 0) - printf(" port %d", up->portno); - printf(" (addr %d) disconnected\n", dev->address); - } - - struct usb_attach_arg *uaap = - device_get_ivars(dev->subdevs[i]); - device_detach(dev->subdevs[i]); - free(uaap, M_USB); - device_delete_child(device_get_parent(dev->subdevs[i]), - dev->subdevs[i]); - dev->subdevs[i] = NULL; - } - } - - usbd_add_dev_event(USB_EVENT_DEVICE_DETACH, dev); - dev->bus->devices[dev->address] = NULL; - up->device = NULL; - usb_free_device(dev); -} |