diff options
| author | Alan Stern <stern@rowland.harvard.edu> | 2006-08-21 12:08:19 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-09-27 14:58:55 -0400 |
| commit | 088dc270e1da03744d977cbd9edd4311af142348 (patch) | |
| tree | 92ccfd907137f2135857bea8a43981b040028d37 /drivers | |
| parent | 3a3416b12f1fbd607bc137a57c924a628aa5485c (diff) | |
usbcore: help drivers to change device configs
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 <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/usb/core/message.c | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 49cfd7928a1c..333b22c68aa4 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c | |||
| @@ -1493,6 +1493,65 @@ free_interfaces: | |||
| 1493 | return 0; | 1493 | return 0; |
| 1494 | } | 1494 | } |
| 1495 | 1495 | ||
| 1496 | struct set_config_request { | ||
| 1497 | struct usb_device *udev; | ||
| 1498 | int config; | ||
| 1499 | struct work_struct work; | ||
| 1500 | }; | ||
| 1501 | |||
| 1502 | /* Worker routine for usb_driver_set_configuration() */ | ||
| 1503 | static void driver_set_config_work(void *_req) | ||
| 1504 | { | ||
| 1505 | struct set_config_request *req = _req; | ||
| 1506 | |||
| 1507 | usb_lock_device(req->udev); | ||
| 1508 | usb_set_configuration(req->udev, req->config); | ||
| 1509 | usb_unlock_device(req->udev); | ||
| 1510 | usb_put_dev(req->udev); | ||
| 1511 | kfree(req); | ||
| 1512 | } | ||
| 1513 | |||
| 1514 | /** | ||
| 1515 | * usb_driver_set_configuration - Provide a way for drivers to change device configurations | ||
| 1516 | * @udev: the device whose configuration is being updated | ||
| 1517 | * @config: the configuration being chosen. | ||
| 1518 | * Context: In process context, must be able to sleep | ||
| 1519 | * | ||
| 1520 | * Device interface drivers are not allowed to change device configurations. | ||
| 1521 | * This is because changing configurations will destroy the interface the | ||
| 1522 | * driver is bound to and create new ones; it would be like a floppy-disk | ||
| 1523 | * driver telling the computer to replace the floppy-disk drive with a | ||
| 1524 | * tape drive! | ||
| 1525 | * | ||
| 1526 | * Still, in certain specialized circumstances the need may arise. This | ||
| 1527 | * routine gets around the normal restrictions by using a work thread to | ||
| 1528 | * submit the change-config request. | ||
| 1529 | * | ||
| 1530 | * Returns 0 if the request was succesfully queued, error code otherwise. | ||
| 1531 | * The caller has no way to know whether the queued request will eventually | ||
| 1532 | * succeed. | ||
| 1533 | */ | ||
| 1534 | int usb_driver_set_configuration(struct usb_device *udev, int config) | ||
| 1535 | { | ||
| 1536 | struct set_config_request *req; | ||
| 1537 | |||
| 1538 | req = kmalloc(sizeof(*req), GFP_KERNEL); | ||
| 1539 | if (!req) | ||
| 1540 | return -ENOMEM; | ||
| 1541 | req->udev = udev; | ||
| 1542 | req->config = config; | ||
| 1543 | INIT_WORK(&req->work, driver_set_config_work, req); | ||
| 1544 | |||
| 1545 | usb_get_dev(udev); | ||
| 1546 | if (!schedule_work(&req->work)) { | ||
| 1547 | usb_put_dev(udev); | ||
| 1548 | kfree(req); | ||
| 1549 | return -EINVAL; | ||
| 1550 | } | ||
| 1551 | return 0; | ||
| 1552 | } | ||
| 1553 | EXPORT_SYMBOL_GPL(usb_driver_set_configuration); | ||
| 1554 | |||
| 1496 | // synchronous request completion model | 1555 | // synchronous request completion model |
| 1497 | EXPORT_SYMBOL(usb_control_msg); | 1556 | EXPORT_SYMBOL(usb_control_msg); |
| 1498 | EXPORT_SYMBOL(usb_bulk_msg); | 1557 | EXPORT_SYMBOL(usb_bulk_msg); |
