Commit 55016b9e6084a85d212dfdc17ee708c67c18507d
Committed by
Greg Kroah-Hartman
1 parent
d8325cceb3
Exists in
ti-linux-3.14.y
and in
2 other branches
usb: dwc3: gadget: fix set_halt() bug with pending transfers
[ Upstream commit 7a60855972f0d3c014093046cb6f013a1ee5bb19 ] 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. Suggested-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Felipe Balbi <balbi@ti.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Showing 3 changed files with 15 additions and 7 deletions Side-by-side Diff
drivers/usb/dwc3/ep0.c
... | ... | @@ -251,7 +251,7 @@ |
251 | 251 | |
252 | 252 | /* stall is always issued on EP0 */ |
253 | 253 | dep = dwc->eps[0]; |
254 | - __dwc3_gadget_ep_set_halt(dep, 1); | |
254 | + __dwc3_gadget_ep_set_halt(dep, 1, false); | |
255 | 255 | dep->flags = DWC3_EP_ENABLED; |
256 | 256 | dwc->delayed_status = false; |
257 | 257 | |
... | ... | @@ -461,7 +461,7 @@ |
461 | 461 | return -EINVAL; |
462 | 462 | if (set == 0 && (dep->flags & DWC3_EP_WEDGE)) |
463 | 463 | break; |
464 | - ret = __dwc3_gadget_ep_set_halt(dep, set); | |
464 | + ret = __dwc3_gadget_ep_set_halt(dep, set, true); | |
465 | 465 | if (ret) |
466 | 466 | return -EINVAL; |
467 | 467 | break; |
drivers/usb/dwc3/gadget.c
... | ... | @@ -587,7 +587,7 @@ |
587 | 587 | |
588 | 588 | /* make sure HW endpoint isn't stalled */ |
589 | 589 | if (dep->flags & DWC3_EP_STALL) |
590 | - __dwc3_gadget_ep_set_halt(dep, 0); | |
590 | + __dwc3_gadget_ep_set_halt(dep, 0, false); | |
591 | 591 | |
592 | 592 | reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); |
593 | 593 | reg &= ~DWC3_DALEPENA_EP(dep->number); |
... | ... | @@ -1185,7 +1185,7 @@ |
1185 | 1185 | return ret; |
1186 | 1186 | } |
1187 | 1187 | |
1188 | -int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) | |
1188 | +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) | |
1189 | 1189 | { |
1190 | 1190 | struct dwc3_gadget_ep_cmd_params params; |
1191 | 1191 | struct dwc3 *dwc = dep->dwc; |
... | ... | @@ -1194,6 +1194,14 @@ |
1194 | 1194 | memset(¶ms, 0x00, sizeof(params)); |
1195 | 1195 | |
1196 | 1196 | if (value) { |
1197 | + if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) || | |
1198 | + (!list_empty(&dep->req_queued) || | |
1199 | + !list_empty(&dep->request_list)))) { | |
1200 | + dev_dbg(dwc->dev, "%s: pending request, cannot halt\n", | |
1201 | + dep->name); | |
1202 | + return -EAGAIN; | |
1203 | + } | |
1204 | + | |
1197 | 1205 | ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, |
1198 | 1206 | DWC3_DEPCMD_SETSTALL, ¶ms); |
1199 | 1207 | if (ret) |
... | ... | @@ -1233,7 +1241,7 @@ |
1233 | 1241 | goto out; |
1234 | 1242 | } |
1235 | 1243 | |
1236 | - ret = __dwc3_gadget_ep_set_halt(dep, value); | |
1244 | + ret = __dwc3_gadget_ep_set_halt(dep, value, false); | |
1237 | 1245 | out: |
1238 | 1246 | spin_unlock_irqrestore(&dwc->lock, flags); |
1239 | 1247 | |
... | ... | @@ -1253,7 +1261,7 @@ |
1253 | 1261 | if (dep->number == 0 || dep->number == 1) |
1254 | 1262 | return dwc3_gadget_ep0_set_halt(ep, 1); |
1255 | 1263 | else |
1256 | - return dwc3_gadget_ep_set_halt(ep, 1); | |
1264 | + return __dwc3_gadget_ep_set_halt(dep, 1, false); | |
1257 | 1265 | } |
1258 | 1266 | |
1259 | 1267 | /* -------------------------------------------------------------------------- */ |
drivers/usb/dwc3/gadget.h
... | ... | @@ -85,7 +85,7 @@ |
85 | 85 | int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value); |
86 | 86 | int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, |
87 | 87 | gfp_t gfp_flags); |
88 | -int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value); | |
88 | +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); | |
89 | 89 | |
90 | 90 | /** |
91 | 91 | * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW |