usb: dwc3: gadget: Properly handle ClearFeature(halt)
authorThinh Nguyen <Thinh.Nguyen@synopsys.com>
Thu, 5 Mar 2020 21:23:55 +0000 (13:23 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 24 Jun 2020 15:50:23 +0000 (17:50 +0200)
[ 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: 72246da40f37 ("usb: Introduce DesignWare USB3 DRD Driver")
Signed-off-by: Thinh Nguyen <thinhn@synopsys.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/usb/dwc3/gadget.c

index c30c5b1..05180a0 100644 (file)
@@ -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
-                       dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+                       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;