Skip to content

Commit

Permalink
usb: dwc3: gadget: END_TRANSFER before CLEAR_STALL command
Browse files Browse the repository at this point in the history
commit d97c78a upstream.

According the programming guide (for all DWC3 IPs), when the driver
handles ClearFeature(halt) request, it should issue CLEAR_STALL command
_after_ the END_TRANSFER command completes. The END_TRANSFER command may
take some time to complete. So, delay the ClearFeature(halt) request
control status stage and wait for END_TRANSFER command completion
interrupt. Only after END_TRANSFER command completes that the driver
may issue CLEAR_STALL command.

Cc: stable@vger.kernel.org
Fixes: cb11ea5 ("usb: dwc3: gadget: Properly handle ClearFeature(halt)")
Signed-off-by: Thinh Nguyen <thinhn@synopsys.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Thinh Nguyen authored and gregkh committed Nov 5, 2020
1 parent 0991df6 commit 9b69b26
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 8 deletions.
1 change: 1 addition & 0 deletions drivers/usb/dwc3/core.h
Expand Up @@ -710,6 +710,7 @@ struct dwc3_ep {
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)

/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
Expand Down
16 changes: 16 additions & 0 deletions drivers/usb/dwc3/ep0.c
Expand Up @@ -524,6 +524,11 @@ static int dwc3_ep0_handle_endpoint(struct dwc3 *dwc,
ret = __dwc3_gadget_ep_set_halt(dep, set, true);
if (ret)
return -EINVAL;

/* ClearFeature(Halt) may need delayed status */
if (!set && (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
return USB_GADGET_DELAYED_STATUS;

break;
default:
return -EINVAL;
Expand Down Expand Up @@ -1049,6 +1054,17 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
__dwc3_ep0_do_control_status(dwc, dep);
}

void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
{
unsigned int direction = !dwc->ep0_expect_in;

if (dwc->ep0state != EP0_STATUS_PHASE)
return;

dwc->delayed_status = false;
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
}

static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
Expand Down
40 changes: 32 additions & 8 deletions drivers/usb/dwc3/gadget.c
Expand Up @@ -1827,6 +1827,18 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
return 0;
}

dwc3_stop_active_transfer(dep, true, true);

list_for_each_entry_safe(req, tmp, &dep->started_list, list)
dwc3_gadget_move_cancelled_request(req);

if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
return 0;
}

dwc3_gadget_ep_cleanup_cancelled_requests(dep);

ret = dwc3_send_clear_stall_ep_cmd(dep);
if (ret) {
dev_err(dwc->dev, "failed to clear STALL on %s\n",
Expand All @@ -1836,14 +1848,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)

dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);

dwc3_stop_active_transfer(dep, true, true);

list_for_each_entry_safe(req, tmp, &dep->started_list, list)
dwc3_gadget_move_cancelled_request(req);

if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
dwc3_gadget_ep_cleanup_cancelled_requests(dep);

if ((dep->flags & DWC3_EP_DELAY_START) &&
!usb_endpoint_xfer_isoc(dep->endpoint.desc))
__dwc3_gadget_kick_transfer(dep);
Expand Down Expand Up @@ -3003,6 +3007,26 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
dwc3_gadget_ep_cleanup_cancelled_requests(dep);

if (dep->flags & DWC3_EP_PENDING_CLEAR_STALL) {
struct dwc3 *dwc = dep->dwc;

dep->flags &= ~DWC3_EP_PENDING_CLEAR_STALL;
if (dwc3_send_clear_stall_ep_cmd(dep)) {
struct usb_ep *ep0 = &dwc->eps[0]->endpoint;

dev_err(dwc->dev, "failed to clear STALL on %s\n",
dep->name);
if (dwc->delayed_status)
__dwc3_gadget_ep0_set_halt(ep0, 1);
return;
}

dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
if (dwc->delayed_status)
dwc3_ep0_send_delayed_status(dwc);
}

if ((dep->flags & DWC3_EP_DELAY_START) &&
!usb_endpoint_xfer_isoc(dep->endpoint.desc))
__dwc3_gadget_kick_transfer(dep);
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/dwc3/gadget.h
Expand Up @@ -113,6 +113,7 @@ 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,
gfp_t gfp_flags);
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);

/**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
Expand Down

0 comments on commit 9b69b26

Please sign in to comment.