From 61a365b701854e2f7e319bcd3b2b5379aaa5c0e0 Mon Sep 17 00:00:00 2001 From: Udipto Goswami Date: Wed, 18 Nov 2020 14:49:21 +0530 Subject: [PATCH] usb: gadget: Don't giveback request if ep command times out Currently driver is giving back requests to gadget even if the active transfers are not stopped. This might lead the controller to access the requests which are already unmapped. This change adds a judgement for giving back the requests to gadget based on ep command status. Also if ep command times out and the controller doesn't stop correctly then mark it as an error event and restart the USB session. Change-Id: If32cddddf0544140d5bdf68df9144702e00dc758 Signed-off-by: Udipto Goswami --- drivers/usb/dwc3/gadget.c | 31 +++++++++++++++++++++++-------- drivers/usb/dwc3/gadget.h | 2 +- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f5ffc190393e..01bf5d91a5e7 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -800,8 +800,7 @@ out: static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_request *req; - - dwc3_stop_active_transfer(dep, true, false); + int ret = -EINVAL; if (dep->number == 0 && dwc->ep0state != EP0_SETUP_PHASE) { unsigned int dir; @@ -817,6 +816,13 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) dwc->eps[1]->trb_enqueue = 0; } + ret = dwc3_stop_active_transfer(dep, true, false); + if (ret < 0) { + dbg_log_string("transfer not stopped for %s(%d), status:%d", + dep->name, dep->number, ret); + return; + } + if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) udelay(2000); @@ -2189,7 +2195,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) dwc->softconnect = is_on; if (((dwc->dr_mode > USB_DR_MODE_HOST) && !dwc->vbus_active) - || !dwc->gadget_driver) { + || !dwc->gadget_driver || dwc->err_evt_seen) { /* * Need to wait for vbus_session(on) from otg driver or to * the udc_start. @@ -2251,8 +2257,15 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) ret = dwc3_gadget_run_stop(dwc, is_on, false); spin_unlock_irqrestore(&dwc->lock, flags); if (!is_on && ret == -ETIMEDOUT) { - dev_err(dwc->dev, "%s: Core soft reset...\n", __func__); - ret = dwc3_device_core_soft_reset(dwc); + /* + * If we fail to stop the controller then mark it as an error + * event since it can lead the controller to go into an unknown + * state. + */ + dbg_log_string("%s: error event seen\n", __func__); + dwc->err_evt_seen = true; + dwc3_notify_event(dwc, DWC3_CONTROLLER_ERROR_EVENT, 0); + dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_CLEAR_DB, 0); } enable_irq(dwc->irq); @@ -2260,7 +2273,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) pm_runtime_put_autosuspend(dwc->dev); dbg_event(0xFF, "Pullup put", atomic_read(&dwc->dev->power.usage_count)); - return ret; + return 0; } static void dwc3_gadget_enable_irq(struct dwc3 *dwc) @@ -3152,7 +3165,7 @@ static void dwc3_reset_gadget(struct dwc3 *dwc) } } -void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) +int dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) { struct dwc3 *dwc = dep->dwc; struct dwc3_gadget_ep_cmd_params params; @@ -3161,7 +3174,7 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) || (dep->flags & DWC3_EP_END_TRANSFER_PENDING)) - return; + return 0; dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_DISABLE_UPDXFER, dep->number); @@ -3215,6 +3228,8 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) udelay(100); + + return ret; } EXPORT_SYMBOL(dwc3_stop_active_transfer); diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 828e0f71a5be..e150fdb52a01 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -116,7 +116,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_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt); +int dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt); static inline dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, struct dwc3_trb *trb)