From 44775a35a9f4edf85a5c8c50398bb677e4de8bdd Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Tue, 19 Jun 2018 18:42:40 -0700 Subject: [PATCH] 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 --- drivers/usb/dwc3/gadget.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 60dd48ae88b9..3dfe03adc1c8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -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,