PERFORCE change 130741 for review
Hans Petter Selasky
hselasky at FreeBSD.org
Wed Dec 12 14:14:48 PST 2007
http://perforce.freebsd.org/chv.cgi?CH=130741
Change 130741 by hselasky at 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) <<<
More information about the p4-projects
mailing list