Commit 972f8a7b authored by Elson Roy Serrao's avatar Elson Roy Serrao
Browse files

usb: dwc3: Properly handle delayed status in ep_halt context

There are two ways of clearing the halt condition.

(1) host initiated through control transfer CLEAR_FEATURE(endpoint)
(2) device requesting UDC to clear the halt endpoint.

In (1), we stop the active transfer and wait for it before sending the
status response for the control transfer. So it involves delayed_response
i.e USB_GADGET_DELAYED_STATUS. In (2) there is no delayed response involved
since it is not a control transfer. However the current code does not
distinguish the part of handling delayed response upon ENDPONT command
completion. In both cases, we try to send the delayed response. This can
result into unexpected behavior when the delayed status is imposed for a
different reason which gets cleared by the case (2) like explained in
the below scenario.

1. Vendor specific control transfer returns USB_GADGET_DELAYED_STATUS.
2. DWC3 gadget sets dwc->delayed_status to '1'.
3. Another function driver issues a usb_ep_clear_halt() call.
4. DWC3 gadget issues dwc3_stop_active_transfer() and sets
5. EP command complete interrupt triggers for the end transfer, and
   dwc3_ep0_send_delayed_status() is allowed to run, as delayed_status
   is '1' due to step#1.
6. STATUS phase is sent, and delayed_status is cleared.
7. Vendor specific control transfer is finished being handled, and issues
   usb_composite_setup_continue().  This results in queuing of a data

The dwc3_gadget_ep_set_halt() API invoked by the two cases above is
distinguished by the protocol flag. Cache this protocol flag so that
DWC3 gadget is aware of when the clear halt is due to a SETUP request
from the host versus when it is sourced from a function driver. This
allows for the EP command complete interrupt to know if it needs to
issue a delayed status phase.

Change-Id: I3b07bbda674103191a793319e3e2e09059eb2a25
Signed-off-by: default avatarElson Roy Serrao <>
parent 1379f4d5
......@@ -1319,6 +1319,7 @@ struct dwc3 {
u8 rx_max_burst_prd;
u8 tx_thr_num_pkt_prd;
u8 tx_max_burst_prd;
u8 clear_stall_protocol;
const char *hsphy_interface;
......@@ -1149,6 +1149,7 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
unsigned int direction = !dwc->ep0_expect_in;
dwc->delayed_status = false;
dwc->clear_stall_protocol = 0;
if (dwc->ep0state != EP0_STATUS_PHASE)
......@@ -1942,6 +1942,9 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
if (protocol)
dwc->clear_stall_protocol = dep->number;
return 0;
......@@ -3464,7 +3467,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
if (dwc->delayed_status)
if (dwc->clear_stall_protocol == dep->number)
Supports Markdown
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