aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc3/ep0.c
diff options
context:
space:
mode:
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>2011-11-02 08:30:45 -0400
committerFelipe Balbi <balbi@ti.com>2011-12-12 04:48:31 -0500
commit5bdb1dcc63304a407e70020c1118fca1642bebaa (patch)
treeee7ee15ec9159b8779cb30e3b9ce8d2d84b516aa /drivers/usb/dwc3/ep0.c
parentf0f2b2a2db85f99637376caf25e46623af56acad (diff)
usb: dwc3: ep0: handle delayed_status again
Since the re-worked ep0 handling (which uses HW's hints to recognize the ep0 status) we lost the delayed status handling. This is used by the file and mass storage gadget to gain some extra time so setup its internal status before it can proceed further requests. In particular the storage gadget does nothing on USB_REQ_SET_CONFIGURATION but wakes up a thread which handles the request. If the udc driver continues ep0 handling before the thread did its work then then endpoint is not yet configured and further requests will fail. Once the gadget is ready, it will enqueue an empty packet which is used for synchronization. In order to fix this issue, the patch does the following: Set ->delayed_status once the delayed_status has been notices and do not continue on the next XferNotReady event. We will continues ep0 processing once the gadget enqueued the zero packet for synchronization. A cleaner approach would be to enforce the gadget to enqueue an empty (zero) request even for the status phase but this would do for now. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/dwc3/ep0.c')
-rw-r--r--drivers/usb/dwc3/ep0.c26
1 files changed, 23 insertions, 3 deletions
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 333278c56690..314acb289d23 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -48,11 +48,14 @@
48 48
49#include <linux/usb/ch9.h> 49#include <linux/usb/ch9.h>
50#include <linux/usb/gadget.h> 50#include <linux/usb/gadget.h>
51#include <linux/usb/composite.h>
51 52
52#include "core.h" 53#include "core.h"
53#include "gadget.h" 54#include "gadget.h"
54#include "io.h" 55#include "io.h"
55 56
57static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum);
58
56static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) 59static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
57{ 60{
58 switch (state) { 61 switch (state) {
@@ -122,6 +125,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
122static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, 125static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
123 struct dwc3_request *req) 126 struct dwc3_request *req)
124{ 127{
128 struct dwc3 *dwc = dep->dwc;
129 u32 type;
125 int ret = 0; 130 int ret = 0;
126 131
127 req->request.actual = 0; 132 req->request.actual = 0;
@@ -140,9 +145,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
140 * IRQ we were waiting for is long gone. 145 * IRQ we were waiting for is long gone.
141 */ 146 */
142 if (dep->flags & DWC3_EP_PENDING_REQUEST) { 147 if (dep->flags & DWC3_EP_PENDING_REQUEST) {
143 struct dwc3 *dwc = dep->dwc;
144 unsigned direction; 148 unsigned direction;
145 u32 type;
146 149
147 direction = !!(dep->flags & DWC3_EP0_DIR_IN); 150 direction = !!(dep->flags & DWC3_EP0_DIR_IN);
148 151
@@ -162,6 +165,10 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
162 req->request.dma, req->request.length, type); 165 req->request.dma, req->request.length, type);
163 dep->flags &= ~(DWC3_EP_PENDING_REQUEST | 166 dep->flags &= ~(DWC3_EP_PENDING_REQUEST |
164 DWC3_EP0_DIR_IN); 167 DWC3_EP0_DIR_IN);
168
169 } else if (dwc->delayed_status && (dwc->ep0state == EP0_STATUS_PHASE)) {
170 dwc->delayed_status = false;
171 dwc3_ep0_do_control_status(dwc, 1);
165 } 172 }
166 173
167 return ret; 174 return ret;
@@ -211,6 +218,7 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
211 /* stall is always issued on EP0 */ 218 /* stall is always issued on EP0 */
212 __dwc3_gadget_ep_set_halt(dep, 1); 219 __dwc3_gadget_ep_set_halt(dep, 1);
213 dep->flags = DWC3_EP_ENABLED; 220 dep->flags = DWC3_EP_ENABLED;
221 dwc->delayed_status = false;
214 222
215 if (!list_empty(&dep->request_list)) { 223 if (!list_empty(&dep->request_list)) {
216 struct dwc3_request *req; 224 struct dwc3_request *req;
@@ -472,8 +480,10 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
472 if (!cfg) 480 if (!cfg)
473 dwc->dev_state = DWC3_ADDRESS_STATE; 481 dwc->dev_state = DWC3_ADDRESS_STATE;
474 break; 482 break;
483 default:
484 ret = -EINVAL;
475 } 485 }
476 return 0; 486 return ret;
477} 487}
478 488
479static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) 489static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
@@ -536,6 +546,9 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
536 else 546 else
537 ret = dwc3_ep0_delegate_req(dwc, ctrl); 547 ret = dwc3_ep0_delegate_req(dwc, ctrl);
538 548
549 if (ret == USB_GADGET_DELAYED_STATUS)
550 dwc->delayed_status = true;
551
539 if (ret >= 0) 552 if (ret >= 0)
540 return; 553 return;
541 554
@@ -801,6 +814,13 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
801 dwc3_ep0_stall_and_restart(dwc); 814 dwc3_ep0_stall_and_restart(dwc);
802 return; 815 return;
803 } 816 }
817
818 if (dwc->delayed_status) {
819 WARN_ON_ONCE(event->endpoint_number != 1);
820 dev_vdbg(dwc->dev, "Mass Storage delayed status\n");
821 return;
822 }
823
804 dwc3_ep0_do_control_status(dwc, event->endpoint_number); 824 dwc3_ep0_do_control_status(dwc, event->endpoint_number);
805 } 825 }
806} 826}