Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1bfa0914 authored by Thinh Nguyen's avatar Thinh Nguyen Committed by Greg Kroah-Hartman
Browse files

usb: dwc3: gadget: Properly handle ClearFeature(halt)



[ Upstream commit cb11ea56f37a36288cdd0a4799a983ee3aa437dd ]

DWC3 must not issue CLEAR_STALL command to control endpoints. The
controller automatically clears the STALL when it receives the SETUP
token. Also, when the driver receives ClearFeature(halt_ep), DWC3 must
stop any active transfer from the endpoint and give back all the
requests to the function drivers.

Fixes: 72246da4 ("usb: Introduce DesignWare USB3 DRD Driver")
Signed-off-by: default avatarThinh Nguyen <thinhn@synopsys.com>
Signed-off-by: default avatarFelipe Balbi <balbi@kernel.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent e320bb0e
Loading
Loading
Loading
Loading
+33 −3
Original line number Diff line number Diff line
@@ -1505,6 +1505,10 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
{
	int i;

	/* If req->trb is not set, then the request has not started */
	if (!req->trb)
		return;

	/*
	 * If request was already started, this means we had to
	 * stop the transfer. With that we also need to ignore
@@ -1595,6 +1599,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
{
	struct dwc3_gadget_ep_cmd_params	params;
	struct dwc3				*dwc = dep->dwc;
	struct dwc3_request			*req;
	struct dwc3_request			*tmp;
	int					ret;

	if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
@@ -1631,13 +1637,37 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
		else
			dep->flags |= DWC3_EP_STALL;
	} else {
		/*
		 * Don't issue CLEAR_STALL command to control endpoints. The
		 * controller automatically clears the STALL when it receives
		 * the SETUP token.
		 */
		if (dep->number <= 1) {
			dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
			return 0;
		}

		ret = dwc3_send_clear_stall_ep_cmd(dep);
		if (ret)
		if (ret) {
			dev_err(dwc->dev, "failed to clear STALL on %s\n",
					dep->name);
		else
			return ret;
		}

		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);

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

		if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) {
			dep->flags &= ~DWC3_EP_DELAY_START;
			dwc3_gadget_ep_cleanup_cancelled_requests(dep);
		}
	}

	return ret;