diff options
Diffstat (limited to 'drivers/usb/gadget/composite.c')
-rw-r--r-- | drivers/usb/gadget/composite.c | 62 |
1 files changed, 61 insertions, 1 deletions
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 82314ed22506..5cbb1a41c223 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c | |||
@@ -461,12 +461,23 @@ static int set_config(struct usb_composite_dev *cdev, | |||
461 | reset_config(cdev); | 461 | reset_config(cdev); |
462 | goto done; | 462 | goto done; |
463 | } | 463 | } |
464 | |||
465 | if (result == USB_GADGET_DELAYED_STATUS) { | ||
466 | DBG(cdev, | ||
467 | "%s: interface %d (%s) requested delayed status\n", | ||
468 | __func__, tmp, f->name); | ||
469 | cdev->delayed_status++; | ||
470 | DBG(cdev, "delayed_status count %d\n", | ||
471 | cdev->delayed_status); | ||
472 | } | ||
464 | } | 473 | } |
465 | 474 | ||
466 | /* when we return, be sure our power usage is valid */ | 475 | /* when we return, be sure our power usage is valid */ |
467 | power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW; | 476 | power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW; |
468 | done: | 477 | done: |
469 | usb_gadget_vbus_draw(gadget, power); | 478 | usb_gadget_vbus_draw(gadget, power); |
479 | if (result >= 0 && cdev->delayed_status) | ||
480 | result = USB_GADGET_DELAYED_STATUS; | ||
470 | return result; | 481 | return result; |
471 | } | 482 | } |
472 | 483 | ||
@@ -895,6 +906,14 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) | |||
895 | if (w_value && !f->set_alt) | 906 | if (w_value && !f->set_alt) |
896 | break; | 907 | break; |
897 | value = f->set_alt(f, w_index, w_value); | 908 | value = f->set_alt(f, w_index, w_value); |
909 | if (value == USB_GADGET_DELAYED_STATUS) { | ||
910 | DBG(cdev, | ||
911 | "%s: interface %d (%s) requested delayed status\n", | ||
912 | __func__, intf, f->name); | ||
913 | cdev->delayed_status++; | ||
914 | DBG(cdev, "delayed_status count %d\n", | ||
915 | cdev->delayed_status); | ||
916 | } | ||
898 | break; | 917 | break; |
899 | case USB_REQ_GET_INTERFACE: | 918 | case USB_REQ_GET_INTERFACE: |
900 | if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) | 919 | if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) |
@@ -958,7 +977,7 @@ unknown: | |||
958 | } | 977 | } |
959 | 978 | ||
960 | /* respond with data transfer before status phase? */ | 979 | /* respond with data transfer before status phase? */ |
961 | if (value >= 0) { | 980 | if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) { |
962 | req->length = value; | 981 | req->length = value; |
963 | req->zero = value < w_length; | 982 | req->zero = value < w_length; |
964 | value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); | 983 | value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); |
@@ -967,6 +986,10 @@ unknown: | |||
967 | req->status = 0; | 986 | req->status = 0; |
968 | composite_setup_complete(gadget->ep0, req); | 987 | composite_setup_complete(gadget->ep0, req); |
969 | } | 988 | } |
989 | } else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) { | ||
990 | WARN(cdev, | ||
991 | "%s: Delayed status not supported for w_length != 0", | ||
992 | __func__); | ||
970 | } | 993 | } |
971 | 994 | ||
972 | done: | 995 | done: |
@@ -1289,3 +1312,40 @@ void usb_composite_unregister(struct usb_composite_driver *driver) | |||
1289 | return; | 1312 | return; |
1290 | usb_gadget_unregister_driver(&composite_driver); | 1313 | usb_gadget_unregister_driver(&composite_driver); |
1291 | } | 1314 | } |
1315 | |||
1316 | /** | ||
1317 | * usb_composite_setup_continue() - Continue with the control transfer | ||
1318 | * @cdev: the composite device who's control transfer was kept waiting | ||
1319 | * | ||
1320 | * This function must be called by the USB function driver to continue | ||
1321 | * with the control transfer's data/status stage in case it had requested to | ||
1322 | * delay the data/status stages. A USB function's setup handler (e.g. set_alt()) | ||
1323 | * can request the composite framework to delay the setup request's data/status | ||
1324 | * stages by returning USB_GADGET_DELAYED_STATUS. | ||
1325 | */ | ||
1326 | void usb_composite_setup_continue(struct usb_composite_dev *cdev) | ||
1327 | { | ||
1328 | int value; | ||
1329 | struct usb_request *req = cdev->req; | ||
1330 | unsigned long flags; | ||
1331 | |||
1332 | DBG(cdev, "%s\n", __func__); | ||
1333 | spin_lock_irqsave(&cdev->lock, flags); | ||
1334 | |||
1335 | if (cdev->delayed_status == 0) { | ||
1336 | WARN(cdev, "%s: Unexpected call\n", __func__); | ||
1337 | |||
1338 | } else if (--cdev->delayed_status == 0) { | ||
1339 | DBG(cdev, "%s: Completing delayed status\n", __func__); | ||
1340 | req->length = 0; | ||
1341 | value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); | ||
1342 | if (value < 0) { | ||
1343 | DBG(cdev, "ep_queue --> %d\n", value); | ||
1344 | req->status = 0; | ||
1345 | composite_setup_complete(cdev->gadget->ep0, req); | ||
1346 | } | ||
1347 | } | ||
1348 | |||
1349 | spin_unlock_irqrestore(&cdev->lock, flags); | ||
1350 | } | ||
1351 | |||