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 | |
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>
-rw-r--r-- | drivers/usb/core/message.c | 59 | ||||
-rw-r--r-- | include/linux/usb.h | 3 |
2 files changed, 62 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); |
diff --git a/include/linux/usb.h b/include/linux/usb.h index 26d8a5f36896..f104efa04d79 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -1099,6 +1099,9 @@ extern int usb_clear_halt(struct usb_device *dev, int pipe); | |||
1099 | extern int usb_reset_configuration(struct usb_device *dev); | 1099 | extern int usb_reset_configuration(struct usb_device *dev); |
1100 | extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); | 1100 | extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); |
1101 | 1101 | ||
1102 | /* this request isn't really synchronous, but it belongs with the others */ | ||
1103 | extern int usb_driver_set_configuration(struct usb_device *udev, int config); | ||
1104 | |||
1102 | /* | 1105 | /* |
1103 | * timeouts, in milliseconds, used for sending/receiving control messages | 1106 | * timeouts, in milliseconds, used for sending/receiving control messages |
1104 | * they typically complete within a few frames (msec) after they're issued | 1107 | * they typically complete within a few frames (msec) after they're issued |