usb: dwc3: gadget: cope with XferNotReady before usb_ep_queue()
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>
This commit is contained in:
parent
76a638f8ac
commit
6cb2e4e3de
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue