From stern@rowland.harvard.edu Mon Aug 21 09:08:22 2006 Date: Mon, 21 Aug 2006 12:08:19 -0400 (EDT) From: Alan Stern To: Greg KH cc: Peter Oehry Subject: [PATCH] usbcore: help drivers to change device configs Message-ID: It's generally a bad idea for USB interface drivers to try to change a device's configuration, and usbcore doesn't provide any way for them to do it. However in a few exceptional circumstances it can make sense. This patch (as767) adds a roundabout mechanism to help drivers that may need it. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb.h | 3 ++ 2 files changed, 62 insertions(+) --- gregkh-2.6.orig/drivers/usb/core/message.c +++ gregkh-2.6/drivers/usb/core/message.c @@ -1493,6 +1493,65 @@ free_interfaces: return 0; } +struct set_config_request { + struct usb_device *udev; + int config; + struct work_struct work; +}; + +/* Worker routine for usb_driver_set_configuration() */ +static void driver_set_config_work(void *_req) +{ + struct set_config_request *req = _req; + + usb_lock_device(req->udev); + usb_set_configuration(req->udev, req->config); + usb_unlock_device(req->udev); + usb_put_dev(req->udev); + kfree(req); +} + +/** + * usb_driver_set_configuration - Provide a way for drivers to change device configurations + * @udev: the device whose configuration is being updated + * @config: the configuration being chosen. + * Context: In process context, must be able to sleep + * + * Device interface drivers are not allowed to change device configurations. + * This is because changing configurations will destroy the interface the + * driver is bound to and create new ones; it would be like a floppy-disk + * driver telling the computer to replace the floppy-disk drive with a + * tape drive! + * + * Still, in certain specialized circumstances the need may arise. This + * routine gets around the normal restrictions by using a work thread to + * submit the change-config request. + * + * Returns 0 if the request was succesfully queued, error code otherwise. + * The caller has no way to know whether the queued request will eventually + * succeed. + */ +int usb_driver_set_configuration(struct usb_device *udev, int config) +{ + struct set_config_request *req; + + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + req->udev = udev; + req->config = config; + INIT_WORK(&req->work, driver_set_config_work, req); + + usb_get_dev(udev); + if (!schedule_work(&req->work)) { + usb_put_dev(udev); + kfree(req); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(usb_driver_set_configuration); + // synchronous request completion model EXPORT_SYMBOL(usb_control_msg); EXPORT_SYMBOL(usb_bulk_msg); --- gregkh-2.6.orig/include/linux/usb.h +++ gregkh-2.6/include/linux/usb.h @@ -1100,6 +1100,9 @@ extern int usb_clear_halt(struct usb_dev extern int usb_reset_configuration(struct usb_device *dev); extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); +/* this request isn't really synchronous, but it belongs with the others */ +extern int usb_driver_set_configuration(struct usb_device *udev, int config); + /* * timeouts, in milliseconds, used for sending/receiving control messages * they typically complete within a few frames (msec) after they're issued