mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 05:39:54 -04:00
usb: dwc3: gadget: delay clearing run/stop when setup just arrived
There still exists a small window in which dwc3_gadget_pullup()
is called while a new control transfer setup packet had newly
arrived. In the case where it needs to be delegated to the gadget
driver, it releases dwc3->lock and allows pullup() to proceed to
call run_stop() to attempt to stop the controller. The lock will
get released again when calling dwc3_stop_active_transfers()
which allows the gadget driver to meanwhile call ep_queue() for
a three-stage control transfer and sneak in a Start Transfer. By
the time the DCTL register is written, the controller will fail
to halt due to having an outstanding transfer in progress.
CPU0 CPU1
...dwc3->lock held...
dwc3_ep0_inspect_setup() dwc3_gadget_pullup()
dwc3_ep0_delegate_req() spin_lock_irq_save()
spin_unlock() ... unblocked
composite_setup() dwc3_gadget_run_stop()
usb_ep_queue() dwc3_stop_active_transfers()
dwc3_gadget_ep0_queue() dwc3_gadget_remove_requests()
spin_lock_irqsave() dwc3_gadget_giveback()
... unblocked spin_unlock()
__dwc3_ep0_do_control_data() spin_lock()
issues Start Transfer
spin_unlock_irqrestore() ... unblocked
spin_lock() clear DCTL_RUN_STOP
... wait for DSTS_DEVCTRLHLT
time out
This change attempts to fix the issue by additionally checking
the dwc->ep0_next_event state in dwc3_gadget_pullup() to catch
the case when a SETUP packet had just been received and handled
by dwc3_ep0_inspect_setup but is still waiting to proceed to
either a DATA or STATUS stage. This allows pullup() to wait for
completion of the transfer before moving on to clear run/stop.
Change-Id: Ib2da3c86213ade1a61fb82f95a1f7c9534d9df3f
Signed-off-by: Jack Pham <jackp@codeaurora.org>
This commit is contained in:
parent
d0acc50c66
commit
44775a35a9
1 changed files with 2 additions and 1 deletions
|
|
@ -2055,7 +2055,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
|||
* Per databook, when we want to stop the gadget, if a control transfer
|
||||
* is still in process, complete it and get the core into setup phase.
|
||||
*/
|
||||
if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) {
|
||||
if (!is_on && (dwc->ep0state != EP0_SETUP_PHASE ||
|
||||
dwc->ep0_next_event != DWC3_EP0_COMPLETE)) {
|
||||
reinit_completion(&dwc->ep0_in_setup);
|
||||
|
||||
ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
|
||||
|
|
|
|||
Loading…
Reference in a new issue