Date: Wed, 12 Dec 2007 22:14:48 GMT From: Hans Petter Selasky <hselasky@FreeBSD.org> To: Perforce Change Reviews <perforce@FreeBSD.org> Subject: PERFORCE change 130741 for review Message-ID: <200712122214.lBCMEmFx073811@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=130741 Change 130741 by hselasky@hselasky_laptop001 on 2007/12/12 22:14:19 This commit is related to USB device side support. FYI: The comments below follow the diff. o In general: The code does the same like before only that some functions have been refactored. o Bug corrected: "usb_linux_free_usb_device" must be called every time you change the configuration. o Simplified "usbd_fill_deviceinfo" a little bit. o "usbd_reset_probed": In USB Device Mode it is always the peer that sets the configuration, and not the device itself. Setting "udev->probed" to "USBD_PROBED_IFACE_AND_FOUND" ensures that we never change the configuration value. o We now ignore any set-address errors due to buggy USB devices and rather try to see if reading the first descriptor fails at the new address. o Removed some comments that are no longer true. o New helper functions "usbd_bus_port_get_device" and "usbd_bus_port_set_device". Affected files ... .. //depot/projects/usb/src/sys/dev/usb/usb_subr.c#69 edit Differences ... ==== //depot/projects/usb/src/sys/dev/usb/usb_subr.c#69 (text+ko) ==== @@ -744,8 +744,8 @@ error: /* passed end, or bad desc */ - printf("%s: bad descriptor(s), addr=%d!\n", - __FUNCTION__, udev->address); + PRINTFN(-1, ("%s: bad descriptor(s), addr=%d!\n", + __FUNCTION__, udev->address)); /* free old pipes if any */ usbd_free_pipe_data(udev, iface_index, 0 - 1); @@ -760,21 +760,28 @@ /* mtx_assert() */ + /* free Linux compat device, if any */ + if (udev->linux_dev) { + usb_linux_free_usb_device(udev->linux_dev); + udev->linux_dev = NULL; + } /* free all pipes, if any */ usbd_free_pipe_data(udev, 0, 0); /* free all interfaces, if any */ while (iface != iface_end) { iface->idesc = NULL; + iface->alt_index = 0; iface++; } - if (udev->cdesc != NULL) { - /* free "cdesc" after "ifaces" */ + /* free "cdesc" after "ifaces", if any */ + if (udev->cdesc) { free(udev->cdesc, M_USB); + udev->cdesc = NULL; } - udev->cdesc = NULL; - udev->config = USB_UNCONFIG_NO; + /* set unconfigured state */ + udev->curr_config_no = USB_UNCONFIG_NO; return; } @@ -823,6 +830,9 @@ return (USBD_INVAL); } +/*------------------------------------------------------------------------* + * usbd_set_config_index + *------------------------------------------------------------------------*/ usbd_status_t usbd_set_config_index(struct usbd_device *udev, uint8_t index, uint8_t msg) { @@ -942,8 +952,8 @@ goto error; } udev->power = power; - udev->self_powered = selfpowered; - udev->config = cdp->bConfigurationValue; + udev->flags.self_powered = selfpowered; + udev->curr_config_no = cdp->bConfigurationValue; /* Set the actual configuration value. */ err = usbreq_set_config(udev, &usb_global_lock, @@ -994,10 +1004,14 @@ int usbd_fill_deviceinfo(struct usbd_device *udev, struct usb_device_info *di) { + enum { + MAX_PORT = (sizeof(di->udi_ports) / sizeof(di->udi_ports[0])), + }; struct usbd_port *p; - uint16_t s; + struct usbd_interface *iface; + struct usbd_device *child; uint8_t i; - uint8_t err; + uint8_t max; if ((udev == NULL) || (di == NULL)) { return (ENXIO); @@ -1020,111 +1034,241 @@ di->udi_class = udev->ddesc.bDeviceClass; di->udi_subclass = udev->ddesc.bDeviceSubClass; di->udi_protocol = udev->ddesc.bDeviceProtocol; - di->udi_config = udev->config; - di->udi_power = udev->self_powered ? 0 : udev->power; + di->udi_config = udev->curr_config_no; + di->udi_power = udev->flags.self_powered ? 0 : udev->power; di->udi_speed = udev->speed; - for (i = 0; - (i < (sizeof(udev->subdevs) / sizeof(udev->subdevs[0]))) && - (i < USB_MAX_DEVNAMES); - i++) { - if (udev->subdevs[i] && - device_is_attached(udev->subdevs[i])) { + for (i = 0; i != MIN(USB_MAX_DEVNAMES, USB_MAX_INTERFACES); i++) { + iface = usbd_get_iface(udev, i); + if (iface && iface->subdev && + device_is_attached(iface->subdev)) { strlcpy(di->udi_devnames[i], - device_get_nameunit(udev->subdevs[i]), + device_get_nameunit(iface->subdev), USB_MAX_DEVNAMELEN); } } if (udev->hub) { - for (i = 0; - (i < (sizeof(di->udi_ports) / sizeof(di->udi_ports[0]))) && - (i < udev->hub->hubdesc.bNbrPorts); - i++) { - p = &udev->hub->ports[i]; + + max = udev->hub->nports; + if (max > MAX_PORT) { + max = MAX_PORT; + } + di->udi_nports = max; + + p = udev->hub->ports; + for (i = 0; i != max; i++, p++) { + + child = usbd_bus_port_get_device(udev->bus, p); - if (p->device_addr != USB_START_ADDR) { - err = p->device_addr; + if (child) { + di->udi_ports[i] = p->device_index; } 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] = USB_PORT_POWERED; } - di->udi_ports[i] = err; } - di->udi_nports = udev->hub->hubdesc.bNbrPorts; } mtx_unlock(&usb_global_lock); return (0); } -/* The following function will remove detached - * devices from the interface list. This can - * happen during USB device module unload. - */ +static void +usbd_reset_probed(struct usbd_device *udev) +{ + udev->probed = (udev->flags.usb_mode == USB_MODE_HOST) ? + USBD_PROBED_NOTHING : USBD_PROBED_IFACE_AND_FOUND; + return; +} + static void -usbd_remove_detached_devices(struct usbd_device *udev) +usbd_detach_device_sub(struct usbd_device *udev, device_t *ppdev, + uint8_t free_subdev) { - device_t *subdev = udev->subdevs; - device_t *subdev_end = udev->subdevs_end; - uint8_t detached_first = 0; + device_t dev; + int err; + + if (!free_subdev) { + + *ppdev = NULL; + + } else if (*ppdev) { + + /* + * NOTE: It is important to clear "*ppdev" before deleting + * the child due to some device methods being called late + * during the delete process ! + */ + dev = *ppdev; + *ppdev = NULL; - PRINTFN(3, ("udev=%p\n", udev)); + device_printf(dev, "at %s, port %d, addr %d " + "(disconnected)\n", + device_get_nameunit(udev->parent_dev), + udev->port_no, udev->address); - while (subdev != subdev_end) { - if (subdev[0]) { - if (device_is_attached(subdev[0]) == 0) { - if (device_delete_child(device_get_parent(subdev[0]), - subdev[0]) == 0) { - subdev[0] = NULL; - if (subdev == udev->subdevs) { - detached_first = 1; - } - } else { - /* - * 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 (device_is_attached(dev)) { + if (udev->flags.suspended) { + err = DEVICE_RESUME(dev); + if (err) { + device_printf(dev, "Resume failed!\n"); } } + if (device_detach(dev)) { + goto error; + } + } + if (device_delete_child(udev->parent_dev, dev)) { + goto error; + } + } + return; + +error: + /* Detach is not allowed to fail in the USB world */ + panic("An USB driver would not detach!\n"); + return; +} + +/*------------------------------------------------------------------------* + * usbd_detach_device + * + * The following function will detach the matching interfaces. + * This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usbd_detach_device(struct usbd_device *udev, uint8_t iface_index, + uint8_t free_subdev) +{ + struct usbd_interface *iface; + uint8_t i; + + if (udev == NULL) { + /* nothing to do */ + return; + } + PRINTFN(3, ("udev=%p\n", udev)); + + /* + * 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 { + usbd_detach_device_sub(udev, &(udev->global_dev), free_subdev); + i = 0; + iface_index = USB_MAX_INTERFACES; + } + + /* 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; } - subdev++; + usbd_detach_device_sub(udev, &(iface->subdev), free_subdev); + } + + if (iface_index == USB_IFACE_INDEX_ANY) { + /* + * All devices are gone. Reset the "probed" variable. + */ + usbd_reset_probed(udev); } + return; +} - if (detached_first) { - if ((udev->probed == USBD_PROBED_SPECIFIC_AND_FOUND) || - (udev->probed == USBD_PROBED_GENERIC_AND_FOUND)) { +/*------------------------------------------------------------------------* + * usbd_probe_and_attach_sub + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +static uint8_t +usbd_probe_and_attach_sub(struct usbd_device *udev, + struct usb_attach_arg *uaa, device_t *ppdev) +{ + device_t dev; + int err; + + dev = *ppdev; + + if (dev) { + + /* clean up after module unload */ + + if (device_is_attached(dev)) { + /* already a device there */ + return (0); + } + /* XXX clear "*ppdev" as early as possible */ + + *ppdev = NULL; + + if (device_delete_child(udev->parent_dev, dev)) { + /* - * The first and only device is gone. Reset the - * "probed" variable. + * Panic here, else one can get a double call + * to device_detach(). USB devices should + * never fail on detach! */ - udev->probed = USBD_PROBED_NOTHING; + 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); + } + if (device_probe_and_attach(uaa->temp_dev) == 0) { + /* + * The USB attach arguments are only available during probe + * and attach ! + */ + *ppdev = uaa->temp_dev; + uaa->temp_dev = NULL; + device_set_ivars(*ppdev, NULL); + + if (udev->flags.suspended) { + err = DEVICE_SUSPEND(*ppdev); + device_printf(*ppdev, "Suspend failed\n"); } + return (0); /* success */ } - return; + return (1); /* failure */ } /*------------------------------------------------------------------------* * usbd_probe_and_attach * - * This function is called from "uhub_explore_sub()" + * This function is called from "uhub_explore_sub()" and + * "usbd_serve_request_callback_sub()" + * + * Returns: + * 0: Success + * Else: A control transfer failed *------------------------------------------------------------------------*/ usbd_status_t -usbd_probe_and_attach(device_t parent, struct usbd_device *udev) +usbd_probe_and_attach(struct usbd_device *udev, uint8_t iface_index) { struct usb_attach_arg uaa; - device_t bdev = NULL; - usbd_status_t err = 0; + struct usbd_interface *iface; + usbd_status_t err; + uint8_t nconfig; uint8_t config; uint8_t i; @@ -1132,143 +1276,123 @@ PRINTF(("udev == NULL\n")); return (USBD_INVAL); } - usbd_remove_detached_devices(udev); + if (udev->flags.usb_mode == USB_MODE_DEVICE) { + if (udev->curr_config_no == USB_UNCONFIG_NO) { + /* do nothing - no configuration has been set */ + return (0); + } + } + if (udev->probed == USBD_PROBED_SPECIFIC_AND_FOUND) { + if ((udev->global_dev == NULL) || + (!device_is_attached(udev->global_dev))) { + /* reset */ + udev->probed = USBD_PROBED_NOTHING; + } + } + err = 0; + config = 0; bzero(&uaa, sizeof(uaa)); /* probe and attach */ uaa.device = udev; + uaa.usb_mode = udev->flags.usb_mode; uaa.port = udev->port_no; uaa.configno = -1; uaa.vendor = UGETW(udev->ddesc.idVendor); uaa.product = UGETW(udev->ddesc.idProduct); uaa.release = UGETW(udev->ddesc.bcdDevice); - if ((udev->probed == USBD_PROBED_SPECIFIC_AND_FOUND) || - (udev->probed == USBD_PROBED_GENERIC_AND_FOUND)) { - /* nothing more to probe */ - goto done; - } - bdev = device_add_child(parent, NULL, -1); - if (!bdev) { - device_printf(udev->bus->bdev, - "Device creation failed\n"); - err = USBD_INVAL; - goto done; - } - device_set_ivars(bdev, &uaa); - device_quiet(bdev); - + /* first try device specific drivers */ if (udev->probed == USBD_PROBED_NOTHING) { - /* first try device specific drivers */ PRINTF(("trying device specific drivers\n")); - if (device_probe_and_attach(bdev) == 0) { - device_set_ivars(bdev, NULL); /* no longer accessible */ - udev->subdevs[0] = bdev; + if (!usbd_probe_and_attach_sub( + udev, &uaa, &(udev->global_dev))) { udev->probed = USBD_PROBED_SPECIFIC_AND_FOUND; - bdev = 0; goto done; } PRINTF(("no device specific driver found; " "looping over %d configurations\n", udev->ddesc.bNumConfigurations)); } - /* next try interface drivers */ + /* next try the USB interface drivers */ + + nconfig = udev->ddesc.bNumConfigurations; + + for (config = 0; config != nconfig; config++) { - if ((udev->probed == USBD_PROBED_NOTHING) || - (udev->probed == USBD_PROBED_IFACE_AND_FOUND)) { - for (config = 0; config < udev->ddesc.bNumConfigurations; config++) { - struct usbd_interface *iface; + /* + * Only set the config index the first time the + * devices are probed ! + */ + if (udev->probed == USBD_PROBED_NOTHING) { - /* - * only set config index the first time the devices - * are probed - */ - if (udev->probed == USBD_PROBED_NOTHING) { - err = usbd_set_config_index(udev, config, 1); - if (err) { - device_printf(parent, - "port %d, set config at addr %d " - "failed, error=%s\n", - udev->port_no, udev->address, - usbd_errstr(err)); - goto done; - } - /* - * ``bNumInterface'' is checked by - * ``usbd_set_config_index()'' - * - * ``USBD_CLR_IFACE_NO_PROBE()'' is run by - * ``usbd_fill_iface_data()'', which is - * called by ``usbd_set_config_index()'' - */ + err = usbd_set_config_index(udev, config, 1); + if (err) { + goto done; } - /* - * else the configuration is already set - */ + } + /* + * else the configuration is already set + */ + + if ((udev->probed == USBD_PROBED_NOTHING) || + (udev->probed == USBD_PROBED_IFACE_AND_FOUND)) { uaa.configno = udev->cdesc->bConfigurationValue; - uaa.ifaces_start = udev->ifaces; - uaa.ifaces_end = udev->ifaces + udev->cdesc->bNumInterface; + + /* check if only one interface should be probed */ + + if (iface_index != USB_IFACE_INDEX_ANY) { + i = iface_index; + iface_index = i + 1; + } else { + i = 0; + iface_index = USB_MAX_INTERFACES; + } - for (iface = uaa.ifaces_start; - iface != uaa.ifaces_end; - iface++) { - uaa.iface = iface; - uaa.iface_index = (i = (iface - udev->ifaces)); + /* do the probe and attach */ - if (uaa.iface_index >= (sizeof(udev->subdevs) / - sizeof(udev->subdevs[0]))) { - device_printf(udev->bus->bdev, - "Too many subdevices\n"); - break; - } - if ((USBD_GET_IFACE_NO_PROBE(udev, i) == 0) && - (udev->subdevs[i] == NULL) && - (device_probe_and_attach(bdev) == 0)) { - /* "ivars" are no longer accessible: */ - device_set_ivars(bdev, NULL); - udev->subdevs[i] = bdev; - udev->probed = USBD_PROBED_IFACE_AND_FOUND; - bdev = 0; + for (; i != iface_index; i++) { + iface = usbd_get_iface(udev, i); + if (iface == NULL) { /* - * create another child for the next - * iface [if any] + * Looks like the end of the USB + * interfaces ! */ - bdev = device_add_child(parent, NULL, -1); - if (!bdev) { - device_printf(udev->bus->bdev, - "Device creation failed\n"); + PRINTFN(1, ("end of interfaces " + "at %u\n", i)); + break; + } + if (USBD_GET_IFACE_NO_PROBE(udev, i)) { + /* somebody grabbed the interface */ + PRINTFN(1, ("no probe %d\n", i)); + continue; + } + uaa.iface_index = i; + uaa.iface = iface; - /* - * need to update - * "IFACE_NO_PROBE": - */ - break; - } - device_set_ivars(bdev, &uaa); - device_quiet(bdev); + if (!usbd_probe_and_attach_sub( + udev, &uaa, &(iface->subdev))) { + udev->probed = USBD_PROBED_IFACE_AND_FOUND; } } - - if (udev->probed == USBD_PROBED_IFACE_AND_FOUND) { - break; - } + } + if (udev->probed != USBD_PROBED_NOTHING) { + /* nothing more to do */ + break; } } + if (udev->probed == USBD_PROBED_NOTHING) { /* set config index 0 */ + config = 0; err = usbd_set_config_index(udev, 0, 1); if (err) { - device_printf(parent, - "port %d, set config at addr %d " - "failed, error=%s\n", - udev->port_no, udev->address, - usbd_errstr(err)); goto done; } PRINTF(("no interface drivers found\n")); @@ -1276,16 +1400,12 @@ /* finally try the generic driver */ uaa.iface = NULL; uaa.iface_index = 0; - uaa.ifaces_start = NULL; - uaa.ifaces_end = NULL; uaa.usegeneric = 1; uaa.configno = -1; - if (device_probe_and_attach(bdev) == 0) { - device_set_ivars(bdev, NULL); /* no longer accessible */ - udev->subdevs[0] = bdev; - udev->probed = USBD_PROBED_GENERIC_AND_FOUND; - bdev = 0; + if (!usbd_probe_and_attach_sub( + udev, &uaa, &(udev->global_dev))) { + udev->probed = USBD_PROBED_SPECIFIC_AND_FOUND; goto done; } /* @@ -1296,13 +1416,90 @@ PRINTF(("generic attach failed\n")); } done: - if (bdev) { + if (err) { + device_printf(udev->parent_dev, + "port %d, set config %d at addr %d " + "failed, error=%s\n", + udev->port_no, config, udev->address, + usbd_errstr(err)); + } + if (uaa.temp_dev) { /* remove the last created child; it is unused */ - device_delete_child(parent, bdev); + + if (device_delete_child(udev->parent_dev, uaa.temp_dev)) { + PRINTFN(-1, ("device delete child failed!\n")); + } } return (err); } +/*------------------------------------------------------------------------* + * usbd_suspend_resume_sub + *------------------------------------------------------------------------*/ +static void +usbd_suspend_resume_sub(struct usbd_device *udev, device_t dev, uint8_t do_suspend) +{ + int err; + + if (dev == NULL) { + 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"); + } + return; +} + +/*------------------------------------------------------------------------* + * usbd_suspend_resume_device + * + * The following function will suspend or resume the USB device. + *------------------------------------------------------------------------*/ +usbd_status_t +usbd_suspend_resume(struct usbd_device *udev, uint8_t do_suspend) +{ + struct usbd_interface *iface; + uint8_t i; + + if (udev == NULL) { + /* nothing to do */ + return (0); + } + PRINTFN(3, ("udev=%p\n", udev)); + + mtx_lock(&(udev->bus->mtx)); + if (udev->flags.suspended == do_suspend) { + mtx_unlock(&(udev->bus->mtx)); + /* nothing to do */ + return (0); + } + udev->flags.suspended = do_suspend; + mtx_unlock(&(udev->bus->mtx)); + + /* do the global_dev first, if any */ + + usbd_suspend_resume_sub(udev, udev->global_dev, do_suspend); + + /* do the suspend or resume */ + + for (i = 0; i != USB_MAX_INTERFACES; i++) { + + iface = usbd_get_iface(udev, i); + if (iface == NULL) { + /* looks like the end of the USB interfaces */ + break; + } + usbd_suspend_resume_sub(udev, iface->subdev, do_suspend); + } + return (0); +} + static const uint8_t usbd_hub_speed_combs[USB_SPEED_MAX][USB_SPEED_MAX] = { /* HUB *//* subdevice */ @@ -1315,48 +1512,53 @@ }; /*------------------------------------------------------------------------* - * usbd_new_device + * usbd_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. * - * 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. + * Return values: + * 0: Failure + * Else: Success *------------------------------------------------------------------------*/ -usbd_status_t -usbd_new_device(device_t parent, struct usbd_bus *bus, +struct usbd_device * +usbd_alloc_device(device_t parent_dev, struct usbd_bus *bus, struct usbd_device *parent_hub, uint8_t depth, - uint8_t speed, uint8_t port_index, uint8_t port_no) + uint8_t port_index, uint8_t port_no, uint8_t speed, uint8_t usb_mode) { + struct usbd_device *udev; struct usbd_device *adev; - struct usbd_device *udev; struct usbd_device *hub; - usbd_status_t err = 0; - uint8_t buf[4]; - uint8_t addr; + usbd_status_t err; + uint8_t device_index; - PRINTF(("bus=%p port=%d depth=%d speed=%d\n", - bus, port_no, depth, speed)); + PRINTFN(0, ("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, usb_mode)); - /* find an unused and valid address */ - -#if (USB_MAX_DEVICES < USB_START_ADDR) -#error "USB_MAX_DEVICES < USB_START_ADDR" -#endif - for (addr = USB_START_ADDR + 1; addr < USB_MAX_DEVICES; addr++) { - if (bus->devices[addr] == NULL) + /* + * Find an unused device index. In USB Host mode this is the + * same as the device address. + * + * NOTE: Index 1 is reserved for the Root HUB. + */ + for (device_index = USB_ROOT_HUB_ADDR; device_index != + USB_MAX_DEVICES; device_index++) { + if (bus->devices[device_index] == NULL) break; } - if (addr == USB_MAX_DEVICES) { - + if (device_index == USB_MAX_DEVICES) { device_printf(bus->bdev, - "No free USB addresses, " - "new device ignored.\n"); - return (USBD_NO_ADDR); + "No free USB device index for new device!\n"); + return (NULL); } - udev = malloc(sizeof(udev[0]), M_USB, M_WAITOK | M_ZERO); + udev = malloc(sizeof(*udev), M_USB, M_WAITOK | M_ZERO); if (udev == NULL) { - return (USBD_NOMEM); + return (NULL); } /* initialize our SX-lock */ sx_init(udev->default_sx, "USB device SX lock"); @@ -1364,10 +1566,23 @@ /* initialize our mutex */ mtx_init(udev->default_mtx, "USB device mutex", NULL, MTX_DEF); - /* make a relationship between port and device address */ - if (parent_hub) { - parent_hub->hub->ports[port_index].device_addr = addr; - } + /* initialize 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 */ + + /* we are not ready yet */ + udev->flags.detaching = 1; + udev->refcount = 1; + + /* register our device */ + usbd_bus_port_set_device(bus, parent_hub ? + parent_hub->hub->ports + port_index : NULL, udev, device_index); + /* set up default endpoint descriptor */ udev->default_ep_desc.bLength = USB_ENDPOINT_DESCRIPTOR_SIZE; udev->default_ep_desc.bDescriptorType = UDESC_ENDPOINT; @@ -1375,19 +1590,20 @@ udev->default_ep_desc.bmAttributes = UE_CONTROL; udev->default_ep_desc.wMaxPacketSize[0] = USB_MAX_IPACKET; udev->default_ep_desc.wMaxPacketSize[1] = 0; - udev->ddesc.bMaxPacketSize = 0; udev->default_ep_desc.bInterval = 0; + udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; - udev->parent_hub = parent_hub; - udev->port_index = port_index; - udev->port_no = port_no; - udev->bus = bus; udev->quirks = &usbd_no_quirk; - udev->address = USB_START_ADDR; - udev->depth = depth; + udev->speed = speed; + udev->flags.usb_mode = usb_mode; + + /* setup probed variable */ + + usbd_reset_probed(udev); - hub = parent_hub; + /* check speed combination */ + hub = udev->parent_hub; if (hub) { if (usbd_hub_speed_combs[hub->speed][speed] == 0) { #ifdef USB_DEBUG @@ -1400,8 +1616,10 @@ goto done; } } + /* search for our High Speed USB HUB, if any */ + adev = udev; - hub = parent_hub; + hub = udev->parent_hub; while (hub) { if (hub->speed == USB_SPEED_HIGH) { @@ -1413,38 +1631,50 @@ hub = hub->parent_hub; } - udev->speed = speed; - /* init the default pipe */ usbd_fill_pipe_data(udev, 0, &udev->default_ep_desc, &udev->default_pipe); - err = usbreq_set_address(udev, &usb_global_lock, addr); - if (err) { - PRINTF(("set address %d failed\n", addr)); - err = USBD_SET_ADDR_FAILED; - goto done; - } - /* allow device time to set new address */ - usbd_delay_ms(udev, USB_SET_ADDRESS_SETTLE); - udev->address = addr; /* new device address now */ + if (udev->flags.usb_mode == USB_MODE_HOST) { + + err = usbreq_set_address(udev, &usb_global_lock, device_index); + + /* This is the new USB device address from now on */ + + udev->address = device_index; - mtx_lock(&(bus->mtx)); - bus->devices[addr] = udev; - if (parent_hub == NULL) { - /* make a copy */ - bus->devices[USB_START_ADDR] = udev; + /* + * 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) { + PRINTFN(-1, ("set address %d failed " + "(ignored)\n", udev->address)); + } + /* allow device time to set new address */ + usbd_delay_ms(udev, USB_SET_ADDRESS_SETTLE); } - mtx_unlock(&(bus->mtx)); - - /* get the first 8 bytes of the device descriptor */ + /* + * 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! + */ err = usbreq_get_desc(udev, &usb_global_lock, &udev->ddesc, - USB_MAX_IPACKET, USB_MAX_IPACKET, - 0, UDESC_DEVICE, 0, 0); + USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); if (err) { - PRINTF(("addr=%d, getting first desc failed\n", - udev->address)); + PRINTFN(-1, ("getting device descriptor " + "at addr %d failed!\n", udev->address)); goto done; } PRINTF(("adding unit addr=%d, rev=%02x, class=%d, " @@ -1487,23 +1717,24 @@ if (udev->ddesc.iManufacturer || udev->ddesc.iProduct || udev->ddesc.iSerialNumber) { - /* setup language ID */ - err = usbreq_get_string_desc(udev, &usb_global_lock, buf, 4, - 0, USB_LANGUAGE_TABLE); + /* read out the language ID string */ + err = usbreq_get_string_desc(udev, &usb_global_lock, + udev->scratch[0].data, 4, sizeof(udev->scratch), + USB_LANGUAGE_TABLE); } else { err = USBD_INVAL; } >>> TRUNCATED FOR MAIL (1000 lines) <<<
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200712122214.lBCMEmFx073811>