David:
This revised patch implements the approach you described.
usb_set_interface() is changed to work even with devices that have only
one altsetting, and it gets called after unbinding a driver. There are
several other small changes, including a spot in usb_set_configuration()
where all the interfaces are marked as being in altsetting 0 -- an obvious
fact that was unaccountably missing. Take a look and see what you think.
Alan Stern
# This is a BitKeeper generated patch for the following project:
# Project Name: greg k-h's linux 2.5 USB kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
# ChangeSet 1.1656 -> 1.1657
# drivers/usb/core/message.c 1.49 -> 1.50
# drivers/usb/core/usb.h 1.1 -> 1.2
# drivers/usb/core/usb.c 1.214 -> 1.215
# drivers/usb/core/hub.c 1.113 -> 1.114
# drivers/usb/core/devio.c 1.77 -> 1.78
# drivers/usb/core/hcd.h 1.54 -> 1.55
# include/linux/usb.h 1.149 -> 1.150
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/07/28 [EMAIL PROTECTED] 1.1657
# Rename usb_device_probe() and usb_device_remove() to usb_probe_interface() and
usb_unbind_interface(),
# and don't export them.
# Remove set_maxpacket(). Replace it with usb_set_configuration().
# Implement usb_disable_endpoint(), usb_disable_interface(), and usb_disable_device().
# Implement usb_enable_endpoint() and usb_enable_interface().
# Enable debugging output in message.c.
# In usb_set_configuration(), set all interfaces to altsetting 0.
# Change the way usb_set_interface() works, and call it after unbinding a driver.
# --------------------------------------------
#
diff -Nru a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
--- a/drivers/usb/core/devio.c Mon Jul 28 11:38:41 2003
+++ b/drivers/usb/core/devio.c Mon Jul 28 11:38:41 2003
@@ -47,6 +47,7 @@
#include <asm/byteorder.h>
#include "hcd.h" /* for usbcore internals */
+#include "usb.h"
struct async {
struct list_head asynclist;
@@ -726,7 +727,7 @@
err ("%s - this function is broken", __FUNCTION__);
if (intf->driver && ps->dev) {
- usb_device_probe (&intf->dev);
+ usb_probe_interface (&intf->dev);
}
}
@@ -1105,7 +1106,7 @@
if (driver) {
dbg ("disconnect '%s' from dev %d interface %d",
driver->name, ps->dev->devnum, ctrl.ifno);
- usb_device_remove(&ifp->dev);
+ usb_unbind_interface(&ifp->dev);
} else
retval = -ENODATA;
unlock_kernel();
@@ -1114,7 +1115,7 @@
/* let kernel drivers try to (re)bind to the interface */
case USBDEVFS_CONNECT:
lock_kernel();
- retval = usb_device_probe (&ifp->dev);
+ retval = usb_probe_interface (&ifp->dev);
unlock_kernel();
break;
diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
--- a/drivers/usb/core/hcd.h Mon Jul 28 11:38:41 2003
+++ b/drivers/usb/core/hcd.h Mon Jul 28 11:38:41 2003
@@ -251,7 +251,6 @@
/* exported to hub driver ONLY to support usb_reset_device () */
extern int usb_get_configuration(struct usb_device *dev);
-extern void usb_set_maxpacket(struct usb_device *dev);
extern void usb_destroy_configuration(struct usb_device *dev);
extern int usb_set_address(struct usb_device *dev);
diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
--- a/drivers/usb/core/hub.c Mon Jul 28 11:38:41 2003
+++ b/drivers/usb/core/hub.c Mon Jul 28 11:38:41 2003
@@ -1324,9 +1324,7 @@
return 1;
}
- dev->actconfig = dev->config;
- usb_set_maxpacket(dev);
-
+ usb_set_configuration(dev, dev->config[0].desc.bConfigurationValue);
return 1;
}
diff -Nru a/drivers/usb/core/message.c b/drivers/usb/core/message.c
--- a/drivers/usb/core/message.c Mon Jul 28 11:38:41 2003
+++ b/drivers/usb/core/message.c Mon Jul 28 11:38:41 2003
@@ -2,6 +2,14 @@
* message.c - synchronous message handling
*/
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+
#include <linux/pci.h> /* for scatterlist macros */
#include <linux/usb.h>
#include <linux/module.h>
@@ -11,6 +19,7 @@
#include <asm/byteorder.h>
#include "hcd.h" /* for usbcore internals */
+#include "usb.h"
struct usb_api_data {
wait_queue_head_t wqh;
@@ -666,41 +675,6 @@
HZ * USB_CTRL_GET_TIMEOUT);
}
-
-// hub-only!! ... and only exported for reset/reinit path.
-// otherwise used internally, when setting up a config
-void usb_set_maxpacket(struct usb_device *dev)
-{
- int i, b;
-
- /* NOTE: affects all endpoints _except_ ep0 */
- for (i=0; i<dev->actconfig->desc.bNumInterfaces; i++) {
- struct usb_interface *ifp = dev->actconfig->interface + i;
- struct usb_host_interface *as = ifp->altsetting + ifp->act_altsetting;
- struct usb_host_endpoint *ep = as->endpoint;
- int e;
-
- for (e=0; e<as->desc.bNumEndpoints; e++) {
- struct usb_endpoint_descriptor *d;
- d = &ep [e].desc;
- b = d->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
- if ((d->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
- USB_ENDPOINT_XFER_CONTROL) { /* Control =>
bidirectional */
- dev->epmaxpacketout[b] = d->wMaxPacketSize;
- dev->epmaxpacketin [b] = d->wMaxPacketSize;
- }
- else if (usb_endpoint_out(d->bEndpointAddress)) {
- if (d->wMaxPacketSize > dev->epmaxpacketout[b])
- dev->epmaxpacketout[b] = d->wMaxPacketSize;
- }
- else {
- if (d->wMaxPacketSize > dev->epmaxpacketin [b])
- dev->epmaxpacketin [b] = d->wMaxPacketSize;
- }
- }
- }
-}
-
/**
* usb_clear_halt - tells device to clear endpoint halt/stall condition
* @dev: device whose endpoint is halted
@@ -760,6 +734,109 @@
}
/**
+ * usb_disable_endpoint -- Disable an endpoint by address
+ * @dev: the device whose endpoint is being disabled
+ * @epaddr: the endpoint's address. Endpoint number for output,
+ * endpoint number + USB_DIR_IN for input
+ *
+ * Deallocates hcd/hardware state for this endpoint ... and nukes all
+ * pending urbs.
+ */
+void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
+{
+ if (dev && dev->bus && dev->bus->op && dev->bus->op->disable)
+ dev->bus->op->disable(dev, epaddr);
+}
+
+/**
+ * usb_disable_interface -- Disable all endpoints for an interface
+ * @dev: the device whose interface is being disabled
+ * @intf: pointer to the interface descriptor
+ *
+ * Disables all the endpoints for the interface's current altsetting.
+ */
+void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf)
+{
+ struct usb_host_interface *hintf =
+ &intf->altsetting[intf->act_altsetting];
+ int i;
+
+ for (i = 0; i < hintf->desc.bNumEndpoints; ++i) {
+ usb_disable_endpoint(dev,
+ hintf->endpoint[i].desc.bEndpointAddress);
+ }
+}
+
+/*
+ * usb_disable_device - Disable all the endpoints for a USB device
+ * @dev: the device whose endpoints are being disabled
+ * @skip_ep0: 0 to disable endpoint 0, 1 to skip it.
+ *
+ * Disables all the device's endpoints, potentially including endpoint 0.
+ * Deallocates hcd/hardware state for the endpoints ... and nukes all
+ * pending urbs.
+ */
+void usb_disable_device(struct usb_device *dev, int skip_ep0)
+{
+ int i;
+
+ dbg("nuking URBs for device %s", dev->dev.bus_id);
+ for (i = skip_ep0; i < 16; ++i) {
+ usb_disable_endpoint(dev, i);
+ usb_disable_endpoint(dev, i + USB_DIR_IN);
+ }
+}
+
+
+/*
+ * usb_enable_endpoint - Enable an endpoint for USB communications
+ * @dev: the device whose interface is being enabled
+ * @epd: pointer to the endpoint descriptor
+ *
+ * Sets the endpoint's maxpacket value, resets the toggle,
+ * and marks it as running. For control endpoints, both the input
+ * and output sides are handled.
+ */
+void usb_enable_endpoint(struct usb_device *dev,
+ struct usb_endpoint_descriptor *epd)
+{
+ int maxsize = epd->wMaxPacketSize;
+ unsigned int epaddr = epd->bEndpointAddress;
+ unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
+ int is_control = ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_CONTROL);
+
+ if (usb_endpoint_out(epaddr) || is_control) {
+ dev->epmaxpacketout[epnum] = maxsize;
+ usb_settoggle(dev, epnum, 1, 0);
+ usb_endpoint_running(dev, epnum, 1);
+ }
+ if (!usb_endpoint_out(epaddr) || is_control) {
+ dev->epmaxpacketin[epnum] = maxsize;
+ usb_settoggle(dev, epnum, 0, 0);
+ usb_endpoint_running(dev, epnum, 0);
+ }
+}
+
+/*
+ * usb_enable_interface - Enable all the endpoints for an interface
+ * @dev: the device whose interface is being enabled
+ * @intf: pointer to the interface descriptor
+ *
+ * Enables all the endpoints for the interface's current altsetting.
+ */
+void usb_enable_interface(struct usb_device *dev,
+ struct usb_interface *intf)
+{
+ struct usb_host_interface *hintf =
+ &intf->altsetting[intf->act_altsetting];
+ int i;
+
+ for (i = 0; i < hintf->desc.bNumEndpoints; ++i)
+ usb_enable_endpoint(dev, &hintf->endpoint[i].desc);
+}
+
+/**
* usb_set_interface - Makes a particular alternate setting be current
* @dev: the device whose interface is being updated
* @interface: the interface being updated
@@ -795,9 +872,8 @@
int usb_set_interface(struct usb_device *dev, int interface, int alternate)
{
struct usb_interface *iface;
- struct usb_host_interface *iface_as;
- int i, ret;
- void (*disable)(struct usb_device *, int) = dev->bus->op->disable;
+ int ret;
+ int manual = 0;
iface = usb_ifnum_to_if(dev, interface);
if (!iface) {
@@ -805,22 +881,23 @@
return -EINVAL;
}
- /* 9.4.10 says devices don't need this, if the interface
- only has one alternate setting */
- if (iface->num_altsetting == 1) {
- dbg("ignoring set_interface for dev %d, iface %d, alt %d",
- dev->devnum, interface, alternate);
- return 0;
- }
-
if (alternate < 0 || alternate >= iface->num_altsetting)
return -EINVAL;
- if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
iface->altsetting[alternate]
.desc.bAlternateSetting,
- interface, NULL, 0, HZ * 5)) < 0)
+ interface, NULL, 0, HZ * 5);
+
+ /* 9.4.10 says devices don't need this and are free to STALL the
+ * request if the interface only has one alternate setting.
+ */
+ if (ret == -EPIPE && iface->num_altsetting == 1) {
+ dbg("manual set_interface for dev %d, iface %d, alt %d",
+ dev->devnum, interface, alternate);
+ manual = 1;
+ } else if (ret < 0)
return ret;
/* FIXME drivers shouldn't need to replicate/bugfix the logic here
@@ -830,20 +907,32 @@
*/
/* prevent submissions using previous endpoint settings */
- iface_as = iface->altsetting + iface->act_altsetting;
- for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {
- u8 ep = iface_as->endpoint [i].desc.bEndpointAddress;
- int out = !(ep & USB_DIR_IN);
-
- /* clear out hcd state, then usbcore state */
- if (disable)
- disable (dev, ep);
- ep &= USB_ENDPOINT_NUMBER_MASK;
- (out ? dev->epmaxpacketout : dev->epmaxpacketin ) [ep] = 0;
- }
+ usb_disable_interface(dev, iface);
+
iface->act_altsetting = alternate;
- /* 9.1.1.5: reset toggles for all endpoints affected by this iface-as
+ /* If the interface only has one altsetting and the device didn't
+ * accept the request, we attempt to carry out the equivalent action
+ * by manually clearing the HALT feature for each endpoint in the
+ * new altsetting.
+ */
+ if (manual) {
+ struct usb_host_interface *iface_as =
+ &iface->altsetting[alternate];
+ int i;
+
+ for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {
+ unsigned int epaddr =
+ iface_as->endpoint[i].desc.bEndpointAddress;
+ unsigned int pipe =
+ __create_pipe(dev, USB_ENDPOINT_NUMBER_MASK & epaddr)
+ | (usb_endpoint_out(epaddr) ? USB_DIR_OUT : USB_DIR_IN);
+
+ usb_clear_halt(dev, pipe);
+ }
+ }
+
+ /* 9.1.1.5: reset toggles for all endpoints in the new altsetting
*
* Note:
* Despite EP0 is always present in all interfaces/AS, the list of
@@ -854,18 +943,7 @@
* during the SETUP stage - hence EP0 toggles are "don't care" here.
* (Likewise, EP0 never "halts" on well designed devices.)
*/
-
- iface_as = &iface->altsetting[alternate];
- for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {
- u8 ep = iface_as->endpoint[i].desc.bEndpointAddress;
- int out = !(ep & USB_DIR_IN);
-
- ep &= USB_ENDPOINT_NUMBER_MASK;
- usb_settoggle (dev, ep, out, 0);
- (out ? dev->epmaxpacketout : dev->epmaxpacketin) [ep]
- = iface_as->endpoint [i].desc.wMaxPacketSize;
- usb_endpoint_running (dev, ep, out);
- }
+ usb_enable_interface(dev, iface);
return 0;
}
@@ -904,7 +982,6 @@
{
int i, ret;
struct usb_host_config *cp = NULL;
- void (*disable)(struct usb_device *, int) = dev->bus->op->disable;
for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
if (dev->config[i].desc.bConfigurationValue == configuration) {
@@ -918,12 +995,8 @@
}
/* if it's already configured, clear out old state first. */
- if (dev->state != USB_STATE_ADDRESS && disable) {
- for (i = 1 /* skip ep0 */; i < 15; i++) {
- disable (dev, i);
- disable (dev, USB_DIR_IN | i);
- }
- }
+ if (dev->state != USB_STATE_ADDRESS)
+ usb_disable_device (dev, 1); // Skip ep0
dev->toggle[0] = dev->toggle[1] = 0;
dev->halted[0] = dev->halted[1] = 0;
dev->state = USB_STATE_ADDRESS;
@@ -936,8 +1009,13 @@
dev->state = USB_STATE_CONFIGURED;
dev->actconfig = cp;
- /* reset more hc/hcd endpoint state */
- usb_set_maxpacket(dev);
+ /* reset more hc/hcd interface/endpoint state */
+ for (i = 0; i < cp->desc.bNumInterfaces; ++i) {
+ struct usb_interface *intf = &cp->interface[i];
+
+ intf->act_altsetting = 0;
+ usb_enable_interface(dev, intf);
+ }
return 0;
}
diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c Mon Jul 28 11:38:41 2003
+++ b/drivers/usb/core/usb.c Mon Jul 28 11:38:41 2003
@@ -80,25 +80,8 @@
static int usb_generic_driver_data;
-/* deallocate hcd/hardware state ... and nuke all pending urbs */
-static void nuke_urbs(struct usb_device *dev)
-{
- void (*disable)(struct usb_device *, int);
- int i;
-
- if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->disable)
- return;
- dbg("nuking urbs assigned to %s", dev->dev.bus_id);
-
- disable = dev->bus->op->disable;
- for (i = 0; i < 15; i++) {
- disable(dev, i);
- disable(dev, USB_DIR_IN | i);
- }
-}
-
/* needs to be called with BKL held */
-int usb_device_probe(struct device *dev)
+int usb_probe_interface(struct device *dev)
{
struct usb_interface * intf = to_usb_interface(dev);
struct usb_driver * driver = to_usb_driver(dev->driver);
@@ -123,25 +106,21 @@
return error;
}
-int usb_device_remove(struct device *dev)
+int usb_unbind_interface(struct device *dev)
{
- struct usb_interface *intf;
- struct usb_driver *driver;
-
- intf = list_entry(dev,struct usb_interface,dev);
- driver = to_usb_driver(dev->driver);
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_driver *driver = to_usb_driver(dev->driver);
down(&driver->serialize);
- /* release all urbs for this device */
- nuke_urbs(interface_to_usbdev(intf));
+ /* release all urbs for this interface */
+ usb_disable_interface(interface_to_usbdev(intf), intf);
if (intf->driver && intf->driver->disconnect)
intf->driver->disconnect(intf);
- /* if driver->disconnect didn't release the interface */
- if (intf->driver)
- usb_driver_release_interface(driver, intf);
+ /* force a release and re-initialize the interface */
+ usb_driver_release_interface(driver, intf);
up(&driver->serialize);
@@ -170,8 +149,8 @@
new_driver->driver.name = (char *)new_driver->name;
new_driver->driver.bus = &usb_bus_type;
- new_driver->driver.probe = usb_device_probe;
- new_driver->driver.remove = usb_device_remove;
+ new_driver->driver.probe = usb_probe_interface;
+ new_driver->driver.remove = usb_unbind_interface;
init_MUTEX(&new_driver->serialize);
@@ -325,10 +304,12 @@
* usb_driver_release_interface - unbind a driver from an interface
* @driver: the driver to be unbound
* @iface: the interface from which it will be unbound
+ *
+ * In addition to unbinding the driver, this re-initializes the interface
+ * by selecting altsetting 0, the default alternate setting.
*
- * This should be used by drivers to release their claimed interfaces.
- * It is normally called in their disconnect() methods, and only for
- * drivers that bound to more than one interface in their probe().
+ * This can be used by drivers to release an interface without waiting
+ * for their disconnect() methods to be called.
*
* When the USB subsystem disconnect()s a driver from some interface,
* it automatically invokes this method for that interface. That
@@ -338,11 +319,14 @@
void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface
*iface)
{
/* this should never happen, don't release something that's not ours */
- if (!iface || iface->driver != driver)
+ if (iface->driver && iface->driver != driver)
return;
iface->driver = NULL;
usb_set_intfdata(iface, NULL);
+ usb_set_interface(interface_to_usbdev(iface),
+ iface->altsetting[0].desc.bInterfaceNumber,
+ 0);
}
/**
@@ -917,7 +901,7 @@
}
/* deallocate hcd/hardware state ... and nuke all pending urbs */
- nuke_urbs(dev);
+ usb_disable_device(dev, 0);
/* disconnect() drivers from interfaces (a key side effect) */
dev_dbg (&dev->dev, "unregistering interfaces\n");
@@ -1586,9 +1570,6 @@
EXPORT_SYMBOL(usb_register);
EXPORT_SYMBOL(usb_deregister);
EXPORT_SYMBOL(usb_disabled);
-
-EXPORT_SYMBOL(usb_device_probe);
-EXPORT_SYMBOL(usb_device_remove);
EXPORT_SYMBOL(usb_alloc_dev);
EXPORT_SYMBOL(usb_put_dev);
diff -Nru a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
--- a/drivers/usb/core/usb.h Mon Jul 28 11:38:41 2003
+++ b/drivers/usb/core/usb.h Mon Jul 28 11:38:41 2003
@@ -2,4 +2,15 @@
extern void usb_create_driverfs_dev_files (struct usb_device *dev);
extern void usb_create_driverfs_intf_files (struct usb_interface *intf);
+extern int usb_probe_interface (struct device *dev);
+extern int usb_unbind_interface (struct device *dev);
+extern void usb_disable_endpoint (struct usb_device *dev, unsigned int epaddr);
+extern void usb_disable_interface (struct usb_device *dev,
+ struct usb_interface *intf);
+extern void usb_disable_device (struct usb_device *dev, int skip_ep0);
+
+extern void usb_enable_endpoint (struct usb_device *dev,
+ struct usb_endpoint_descriptor *epd);
+extern void usb_enable_interface (struct usb_device *dev,
+ struct usb_interface *intf);
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h Mon Jul 28 11:38:41 2003
+++ b/include/linux/usb.h Mon Jul 28 11:38:41 2003
@@ -484,8 +484,6 @@
extern void usb_deregister_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver);
-extern int usb_device_probe(struct device *dev);
-extern int usb_device_remove(struct device *dev);
extern int usb_disabled(void);
/* -------------------------------------------------------------------------- */
-------------------------------------------------------
This SF.Net email sponsored by: Free pre-built ASP.NET sites including
Data Reports, E-commerce, Portals, and Forums are available now.
Download today and enter to win an XBOX or Visual Studio .NET.
http://aspnet.click-url.com/go/psa00100003ave/direct;at.aspnet_072303_01/01
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel