Commit 04cd8d94 authored by Felipe Balbi's avatar Felipe Balbi Committed by Sasha Levin

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: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>

(cherry picked from commit 7a608559)
Signed-off-by: default avatarSasha Levin <sasha.levin@oracle.com>
parent 40a5e038
...@@ -270,7 +270,7 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) ...@@ -270,7 +270,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;
...@@ -477,7 +477,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, ...@@ -477,7 +477,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;
......
...@@ -604,7 +604,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) ...@@ -604,7 +604,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);
...@@ -1196,7 +1196,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, ...@@ -1196,7 +1196,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
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;
...@@ -1205,6 +1205,14 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) ...@@ -1205,6 +1205,14 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
memset(&params, 0x00, sizeof(params)); memset(&params, 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, &params); DWC3_DEPCMD_SETSTALL, &params);
if (ret) if (ret)
...@@ -1244,7 +1252,7 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) ...@@ -1244,7 +1252,7 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
goto out; goto out;
} }
ret = __dwc3_gadget_ep_set_halt(dep, value); ret = __dwc3_gadget_ep_set_halt(dep, value, false);
out: out:
spin_unlock_irqrestore(&dwc->lock, flags); spin_unlock_irqrestore(&dwc->lock, flags);
...@@ -1264,7 +1272,7 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) ...@@ -1264,7 +1272,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)
return dwc3_gadget_ep0_set_halt(ep, 1); return dwc3_gadget_ep0_set_halt(ep, 1);
else else
return dwc3_gadget_ep_set_halt(ep, 1); return __dwc3_gadget_ep_set_halt(dep, 1, false);
} }
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
......
...@@ -114,7 +114,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc); ...@@ -114,7 +114,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc);
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);
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param); int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment