From 50a6d30ef276155dff39a86569d5a0920086fe20 Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Thu, 11 Feb 2021 01:26:50 -0800 Subject: [PATCH] usb: gadget: cser: Requeue OUT USB requests if ECONNRESET is received During enumeration, the host can send a clear feature EP_HALT. When the UDC receives this command, it will cancel any pending requests and return -ECONNRESET to the function driver. Since the clear EP_HALT occurs after the cser driver has already submitted the OUT USB reqs, there is no other method to restart these transfers. Resubmit OUT reqs specifically when ECONNRESET is seen. Change-Id: I925170d389048f84579c9b53128effdbef5c9d6a Signed-off-by: Wesley Cheng --- drivers/usb/gadget/function/f_cdev.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c index 2b52f86a6072..f9ab0979ccf3 100644 --- a/drivers/usb/gadget/function/f_cdev.c +++ b/drivers/usb/gadget/function/f_cdev.c @@ -984,6 +984,7 @@ static void usb_cser_read_complete(struct usb_ep *ep, struct usb_request *req) { struct f_cdev *port = ep->driver_data; unsigned long flags; + int ret; pr_debug("ep:(%pK)(%s) port:%p req_status:%d req->actual:%u\n", ep, ep->name, port, req->status, req->actual); @@ -993,7 +994,25 @@ static void usb_cser_read_complete(struct usb_ep *ep, struct usb_request *req) } spin_lock_irqsave(&port->port_lock, flags); - if (!port->port_open || req->status || !req->actual) { + if (!port->port_open) { + list_add_tail(&req->list, &port->read_pool); + spin_unlock_irqrestore(&port->port_lock, flags); + return; + } + + if (req->status || !req->actual) { + /* + * ECONNRESET can be returned when host issues clear EP halt, + * restart OUT requests if so. + */ + if (req->status == -ECONNRESET) { + spin_unlock_irqrestore(&port->port_lock, flags); + ret = usb_ep_queue(ep, req, GFP_KERNEL); + if (!ret) + return; + spin_lock_irqsave(&port->port_lock, flags); + } + list_add_tail(&req->list, &port->read_pool); spin_unlock_irqrestore(&port->port_lock, flags); return;