usb: dwc3: gadget: fix set_halt() bug with pending transfers
According to our Gadget Framework API documentation, ->set_halt() *must* return -EAGAIN if we have pending transfers (on either direction) or FIFO isn't empty (on TX endpoints). Fix this bug so that the mass storage gadget can be used without stall=0 parameter. This patch should be backported to all kernels since v3.2. Cc: <stable@vger.kernel.org> # v3.2+ Suggested-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
parent
95aa4e8d65
commit
7a60855972
|
@ -256,7 +256,7 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
|
||||||
|
|
||||||
/* stall is always issued on EP0 */
|
/* stall is always issued on EP0 */
|
||||||
dep = dwc->eps[0];
|
dep = dwc->eps[0];
|
||||||
__dwc3_gadget_ep_set_halt(dep, 1);
|
__dwc3_gadget_ep_set_halt(dep, 1, false);
|
||||||
dep->flags = DWC3_EP_ENABLED;
|
dep->flags = DWC3_EP_ENABLED;
|
||||||
dwc->delayed_status = false;
|
dwc->delayed_status = false;
|
||||||
|
|
||||||
|
@ -480,7 +480,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (set == 0 && (dep->flags & DWC3_EP_WEDGE))
|
if (set == 0 && (dep->flags & DWC3_EP_WEDGE))
|
||||||
break;
|
break;
|
||||||
ret = __dwc3_gadget_ep_set_halt(dep, set);
|
ret = __dwc3_gadget_ep_set_halt(dep, set, true);
|
||||||
if (ret)
|
if (ret)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -581,7 +581,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
|
||||||
|
|
||||||
/* make sure HW endpoint isn't stalled */
|
/* make sure HW endpoint isn't stalled */
|
||||||
if (dep->flags & DWC3_EP_STALL)
|
if (dep->flags & DWC3_EP_STALL)
|
||||||
__dwc3_gadget_ep_set_halt(dep, 0);
|
__dwc3_gadget_ep_set_halt(dep, 0, false);
|
||||||
|
|
||||||
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
|
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
|
||||||
reg &= ~DWC3_DALEPENA_EP(dep->number);
|
reg &= ~DWC3_DALEPENA_EP(dep->number);
|
||||||
|
@ -1202,7 +1202,7 @@ out0:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
|
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
|
||||||
{
|
{
|
||||||
struct dwc3_gadget_ep_cmd_params params;
|
struct dwc3_gadget_ep_cmd_params params;
|
||||||
struct dwc3 *dwc = dep->dwc;
|
struct dwc3 *dwc = dep->dwc;
|
||||||
|
@ -1216,6 +1216,14 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
|
||||||
memset(¶ms, 0x00, sizeof(params));
|
memset(¶ms, 0x00, sizeof(params));
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
|
if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) ||
|
||||||
|
(!list_empty(&dep->req_queued) ||
|
||||||
|
!list_empty(&dep->request_list)))) {
|
||||||
|
dev_dbg(dwc->dev, "%s: pending request, cannot halt\n",
|
||||||
|
dep->name);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
||||||
DWC3_DEPCMD_SETSTALL, ¶ms);
|
DWC3_DEPCMD_SETSTALL, ¶ms);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -1246,7 +1254,7 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
spin_lock_irqsave(&dwc->lock, flags);
|
spin_lock_irqsave(&dwc->lock, flags);
|
||||||
ret = __dwc3_gadget_ep_set_halt(dep, value);
|
ret = __dwc3_gadget_ep_set_halt(dep, value, false);
|
||||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1265,7 +1273,7 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
|
||||||
if (dep->number == 0 || dep->number == 1)
|
if (dep->number == 0 || dep->number == 1)
|
||||||
ret = __dwc3_gadget_ep0_set_halt(ep, 1);
|
ret = __dwc3_gadget_ep0_set_halt(ep, 1);
|
||||||
else
|
else
|
||||||
ret = __dwc3_gadget_ep_set_halt(dep, 1);
|
ret = __dwc3_gadget_ep_set_halt(dep, 1, false);
|
||||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -86,7 +86,7 @@ int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
|
||||||
int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
|
int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
|
||||||
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||||
gfp_t gfp_flags);
|
gfp_t gfp_flags);
|
||||||
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
|
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
|
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
|
||||||
|
|
Loading…
Reference in New Issue