summaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc3
diff options
context:
space:
mode:
authorFelipe Balbi2011-10-18 17:00:26 +0200
committerFelipe Balbi2011-12-12 10:48:16 +0100
commit9cc9bcd5b3e8efa45accf2ccb59f13c8de85a0ce (patch)
treef3bf6258f1d7a665fc046836d670a6dda8b11815 /drivers/usb/dwc3
parentusb: dwc3: debugfs: add support for changing port mode (diff)
downloadkernel-qcow2-linux-9cc9bcd5b3e8efa45accf2ccb59f13c8de85a0ce.tar.gz
kernel-qcow2-linux-9cc9bcd5b3e8efa45accf2ccb59f13c8de85a0ce.tar.xz
kernel-qcow2-linux-9cc9bcd5b3e8efa45accf2ccb59f13c8de85a0ce.zip
usb: dwc3: ep0: handle unexpected XferNotReady events
Sometimes the host might be trying to initiate Data or Status phase for an older Control transfer. In such situations we must STALL that transfer and restart the state machine rather than letting such situation go through the wire. Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r--drivers/usb/dwc3/ep0.c35
1 files changed, 35 insertions, 0 deletions
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 24b447e40bc7..36482f45b3b1 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -733,6 +733,41 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
+ /*
+ * This part is very tricky: If we has just handled
+ * XferNotReady(Setup) and we're now expecting a
+ * XferComplete but, instead, we receive another
+ * XferNotReady(Setup), we should STALL and restart
+ * the state machine.
+ *
+ * In all other cases, we just continue waiting
+ * for the XferComplete event.
+ *
+ * We are a little bit unsafe here because we're
+ * not trying to ensure that last event was, indeed,
+ * XferNotReady(Setup).
+ *
+ * Still, we don't expect any condition where that
+ * should happen and, even if it does, it would be
+ * another error condition.
+ */
+ if (dwc->ep0_next_event == DWC3_EP0_COMPLETE) {
+ switch (event->status) {
+ case DEPEVT_STATUS_CONTROL_SETUP:
+ dev_vdbg(dwc->dev, "Unexpected XferNotReady(Setup)\n");
+ dwc3_ep0_stall_and_restart(dwc);
+ break;
+ case DEPEVT_STATUS_CONTROL_DATA:
+ /* FALLTHROUGH */
+ case DEPEVT_STATUS_CONTROL_STATUS:
+ /* FALLTHROUGH */
+ default:
+ dev_vdbg(dwc->dev, "waiting for XferComplete\n");
+ }
+
+ return;
+ }
+
switch (event->status) {
case DEPEVT_STATUS_CONTROL_SETUP:
dev_vdbg(dwc->dev, "Control Setup\n");