usb: dwc3: gadget: cope with XferNotReady before usb_ep_queue()
authorFelipe Balbi <felipe.balbi@linux.intel.com>
Fri, 21 Oct 2016 10:07:09 +0000 (13:07 +0300)
committerFelipe Balbi <felipe.balbi@linux.intel.com>
Thu, 3 Nov 2016 08:38:40 +0000 (10:38 +0200)
If XferNotReady comes before usb_ep_queue() we will
set our PENDING request flag and wait for a
request. However, originally, we were assuming
usb_ep_queue() would always happen before our first
XferNotReady and that causes a corner case where we
could try to issue ENDTRANSFER command before
STARTTRANSFER.

Let's fix that by tracking endpoints which have been
started.

Reported-by: Janusz Dziedzic <januszx.dziedzic@intel.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
drivers/usb/dwc3/core.h
drivers/usb/dwc3/gadget.c

index c2b8685..ebc8a54 100644 (file)
@@ -551,6 +551,7 @@ struct dwc3_ep {
 #define DWC3_EP_PENDING_REQUEST        (1 << 5)
 #define DWC3_EP_MISSED_ISOC    (1 << 6)
 #define DWC3_EP_END_TRANSFER_PENDING   (1 << 7)
+#define DWC3_EP_TRANSFER_STARTED (1 << 8)
 
        /* This last one is specific to EP0 */
 #define DWC3_EP0_DIR_IN                (1 << 31)
index 64d01ff..4191b00 100644 (file)
@@ -340,6 +340,20 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
 
        trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
 
+       if (ret == 0) {
+               switch (DWC3_DEPCMD_CMD(cmd)) {
+               case DWC3_DEPCMD_STARTTRANSFER:
+                       dep->flags |= DWC3_EP_TRANSFER_STARTED;
+                       break;
+               case DWC3_DEPCMD_ENDTRANSFER:
+                       dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+                       break;
+               default:
+                       /* nothing */
+                       break;
+               }
+       }
+
        if (unlikely(susphy)) {
                reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
                reg |= DWC3_GUSB2PHYCFG_SUSPHY;
@@ -1066,6 +1080,14 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param)
        return 0;
 }
 
+static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
+{
+       u32                     reg;
+
+       reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+       return DWC3_DSTS_SOFFN(reg);
+}
+
 static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
                struct dwc3_ep *dep, u32 cur_uf)
 {
@@ -1143,10 +1165,16 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
         * errors which will force us issue EndTransfer command.
         */
        if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
-               if ((dep->flags & DWC3_EP_PENDING_REQUEST) &&
-                               list_empty(&dep->started_list)) {
-                       dwc3_stop_active_transfer(dwc, dep->number, true);
-                       dep->flags = DWC3_EP_ENABLED;
+               if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
+                       if (dep->flags & DWC3_EP_TRANSFER_STARTED) {
+                               dwc3_stop_active_transfer(dwc, dep->number, true);
+                               dep->flags = DWC3_EP_ENABLED;
+                       } else {
+                               u32 cur_uf;
+
+                               cur_uf = __dwc3_gadget_get_frame(dwc);
+                               __dwc3_gadget_start_isoc(dwc, dep, cur_uf);
+                       }
                }
                return 0;
        }
@@ -1392,10 +1420,8 @@ static const struct usb_ep_ops dwc3_gadget_ep_ops = {
 static int dwc3_gadget_get_frame(struct usb_gadget *g)
 {
        struct dwc3             *dwc = gadget_to_dwc(g);
-       u32                     reg;
 
-       reg = dwc3_readl(dwc->regs, DWC3_DSTS);
-       return DWC3_DSTS_SOFFN(reg);
+       return __dwc3_gadget_get_frame(dwc);
 }
 
 static int __dwc3_gadget_wakeup(struct dwc3 *dwc)