This patch merges the USB state definitions from the ARM Linux code (inside the sa1100 driver) and uses them to track what can be done with the device. That replaces the recently added "udev->present" flag with a more complete/standard state model.
There are a few changes that might affect behavior if things start to go really haywire:
- usb_set_address() and usb_set_configuration(), used while enumerating, handle some unlikely cases more correctly: don't allow setting address to zero (undefined behavior), and do allow un-configuring (config 0). (Adds a FIXME for an existing set-configuration bug too.)
- usb_disconnect() flags the state change earlier (as soon as it's known).
- usb_submit_urb() works in the states where messaging is allowed, and also enforces the "unless configured, only control traffic is legal" rule.
- usb_unlink_urb() doesn't care any more about that state. (There seemed to be agreement that it must not matter.)
This will help with some further cleanups in the complex of issues relating to driver removal, device removal, config changing (with driver unbind and rebind), reset, and so on.
Please merge to Linus' latest.
- Dave
--- 1.2/include/linux/usb_ch9.h Tue Oct 29 15:14:57 2002
+++ edited/include/linux/usb_ch9.h Fri Feb 28 13:26:19 2003
@@ -291,4 +291,15 @@
USB_SPEED_HIGH /* usb 2.0 */
};
+/* the chapter 9 device states */
+
+enum usb_device_state {
+ USB_STATE_ATTACHED = 0, /* "only" attached */
+ USB_STATE_POWERED,
+ USB_STATE_DEFAULT, /* limited function */
+ USB_STATE_ADDRESS,
+ USB_STATE_CONFIGURED, /* most functions */
+ USB_STATE_SUSPENDED
+};
+
#endif /* __LINUX_USB_CH9_H */
--- 1.71/include/linux/usb.h Tue Feb 18 08:24:37 2003
+++ edited/include/linux/usb.h Fri Feb 28 13:26:19 2003
@@ -213,7 +213,7 @@
struct usb_device {
int devnum; /* Address on USB bus */
char devpath [16]; /* Use in messages: /port/port/... */
-
+ enum usb_device_state state; /* configured, not attached, etc */
enum usb_device_speed speed; /* high/full/low (or error) */
struct usb_tt *tt; /* low/full speed dev, highspeed hub */
@@ -240,7 +240,6 @@
int have_langid; /* whether string_langid is valid yet */
int string_langid; /* language ID for strings */
- int present; /* if device is present or not */
void *hcpriv; /* Host Controller private data */
--- 1.55/drivers/usb/core/hub.c Tue Feb 11 14:57:52 2003
+++ edited/drivers/usb/core/hub.c Fri Feb 28 13:26:19 2003
@@ -876,6 +876,7 @@
}
hub->children[port] = dev;
+ dev->state = USB_STATE_POWERED;
/* Reset the device, and detect its speed */
if (usb_hub_port_reset(hub, port, dev, delay)) {
--- 1.115/drivers/usb/core/usb.c Tue Feb 18 07:50:22 2003
+++ edited/drivers/usb/core/usb.c Fri Feb 28 14:24:02 2003
@@ -679,7 +679,7 @@
memset(dev, 0, sizeof(*dev));
device_initialize(&dev->dev);
- dev->present = 1;
+ dev->state = USB_STATE_ATTACHED;
usb_bus_get(bus);
@@ -828,6 +828,12 @@
*pdev = NULL;
+ /* mark the device as inactive, so any further urb submissions for
+ * this device will fail. "attached" is the initial state for usb,
+ * it includes "not attached" (no device signaling).
+ */
+ dev->state = USB_STATE_ATTACHED;
+
dev_info (&dev->dev, "USB disconnect, address %d\n", dev->devnum);
/* Free up all the children before we remove this device */
@@ -855,10 +861,6 @@
}
device_unregister(&dev->dev);
- /* mark the device as not present so any further urb submissions for
- * this device will fail. */
- dev->present = 0;
-
/* Decrement the reference count, it'll auto free everything when */
/* it hits 0 which could very well be now */
usb_put_dev(dev);
@@ -906,9 +908,17 @@
// otherwise used internally, for usb_new_device()
int usb_set_address(struct usb_device *dev)
{
- return usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS,
- // FIXME USB_CTRL_SET_TIMEOUT
- 0, dev->devnum, 0, NULL, 0, HZ * USB_CTRL_GET_TIMEOUT);
+ int retval;
+
+ if (dev->devnum == 0)
+ return -EINVAL;
+ if (dev->state != USB_STATE_DEFAULT && dev->state != USB_STATE_ADDRESS)
+ return -EINVAL;
+ retval = usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS,
+ 0, dev->devnum, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
+ if (retval == 0)
+ dev->state = USB_STATE_ADDRESS;
+ return retval;
}
@@ -1014,7 +1024,8 @@
/* dma masks come from the controller; readonly, except to hcd */
dev->dev.dma_mask = parent->dma_mask;
- /* USB device state == default ... it's not usable yet */
+ /* it's not usable yet */
+ dev->state = USB_STATE_DEFAULT;
/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
* it's fixed size except for full speed devices.
@@ -1049,6 +1060,7 @@
if (err < 0) {
dev_err(&dev->dev, "USB device not accepting new address=%d
(error=%d)\n",
dev->devnum, err);
+ dev->state = USB_STATE_DEFAULT;
clear_bit(dev->devnum, dev->bus->devmap.devicemap);
dev->devnum = -1;
return 1;
--- 1.21/drivers/usb/core/message.c Sat Feb 15 04:07:33 2003
+++ edited/drivers/usb/core/message.c Fri Feb 28 13:47:03 2003
@@ -904,17 +904,29 @@
break;
}
}
- if (!cp) {
+ if ((!cp && configuration != 0) || (cp && configuration == 0)) {
warn("selecting invalid configuration %d", configuration);
return -EINVAL;
}
+ /* if it's already configured, clear out old state first. */
+ if (dev->state != USB_STATE_ADDRESS) {
+ /* FIXME unbind drivers from all "old" interfaces.
+ * handshake with hcd to reset cached hc endpoint state.
+ */
+ }
+
if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0)
return ret;
-
+ if (configuration)
+ dev->state = USB_STATE_CONFIGURED;
+ else
+ dev->state = USB_STATE_ADDRESS;
dev->actconfig = cp;
+
+ /* reset more hc/hcd endpoint state */
dev->toggle[0] = 0;
dev->toggle[1] = 0;
usb_set_maxpacket(dev);
--- 1.11/drivers/usb/core/urb.c Tue Feb 18 07:50:22 2003
+++ edited/drivers/usb/core/urb.c Fri Feb 28 13:26:19 2003
@@ -195,7 +195,9 @@
if (!urb || urb->hcpriv || !urb->complete)
return -EINVAL;
- if (!(dev = urb->dev) || !dev->present || !dev->bus || dev->devnum <= 0)
+ if (!(dev = urb->dev)
+ || dev->state < USB_STATE_DEFAULT
+ || !dev->bus || dev->devnum <= 0)
return -ENODEV;
if (!(op = dev->bus->op) || !op->submit_urb)
return -ENODEV;
@@ -211,6 +213,9 @@
temp = usb_pipetype (pipe);
is_out = usb_pipeout (pipe);
+ if (!usb_pipecontrol (pipe) && dev->state < USB_STATE_CONFIGURED)
+ return -ENODEV;
+
/* (actually HCDs may need to duplicate this, endpoint might yet
* stall due to queued bulk/intr transactions that complete after
* we check)
@@ -376,7 +381,7 @@
*/
int usb_unlink_urb(struct urb *urb)
{
- if (urb && urb->dev && urb->dev->present && urb->dev->bus && urb->dev->bus->op)
+ if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
return urb->dev->bus->op->unlink_urb(urb);
else
return -ENODEV;
