From 8ca1932fef3d39a25af69d126f3d8897f1014167 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 21 Aug 2006 12:08:19 -0400 Subject: [PATCH] --- yaml --- r: 36431 b: refs/heads/master c: 088dc270e1da03744d977cbd9edd4311af142348 h: refs/heads/master i: 36429: daacbbabad935fd1804e697796bb2f05954bad4f 36427: 11d5a900e624466224966463e032ce25d85a1a81 36423: 70a92e42329d6fd03b4d3939f57c9e9c6cc5d375 36415: 5ab9b0cbd779b2b679e3ad902bab985b74badfda v: v3 --- [refs] | 2 +- trunk/drivers/usb/core/message.c | 59 ++++++++++++++++++++++++++++++++ trunk/include/linux/usb.h | 3 ++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/[refs] b/[refs] index 95abd2bb066c..adccbaaa2299 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 3a3416b12f1fbd607bc137a57c924a628aa5485c +refs/heads/master: 088dc270e1da03744d977cbd9edd4311af142348 diff --git a/trunk/drivers/usb/core/message.c b/trunk/drivers/usb/core/message.c index 49cfd7928a1c..333b22c68aa4 100644 --- a/trunk/drivers/usb/core/message.c +++ b/trunk/drivers/usb/core/message.c @@ -1493,6 +1493,65 @@ int usb_set_configuration(struct usb_device *dev, int configuration) 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); diff --git a/trunk/include/linux/usb.h b/trunk/include/linux/usb.h index 26d8a5f36896..f104efa04d79 100644 --- a/trunk/include/linux/usb.h +++ b/trunk/include/linux/usb.h @@ -1099,6 +1099,9 @@ extern int usb_clear_halt(struct usb_device *dev, int pipe); 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