mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 10:58:48 +09:00
BACKPORT: FROMGIT: usb: dwc3: gadget: Avoid starting DWC3 gadget during UDC unbind
There is a race present where the DWC3 runtime resume runs in parallel
to the UDC unbind sequence. This will eventually lead to a possible
scenario where we are enabling the run/stop bit, without a valid
composition defined.
Thread#1 (handling UDC unbind):
usb_gadget_remove_driver()
-->usb_gadget_disconnect()
-->dwc3_gadget_pullup(0)
--> continue UDC unbind sequence
-->Thread#2 is running in parallel here
Thread#2 (handing next cable connect)
__dwc3_set_mode()
-->pm_runtime_get_sync()
-->dwc3_gadget_resume()
-->dwc->gadget_driver is NOT NULL yet
-->dwc3_gadget_run_stop(1)
--> _dwc3gadget_start()
...
Fix this by tracking the pullup disable routine, and avoiding resuming
of the DWC3 gadget. Once the UDC is re-binded, that will trigger the
pullup enable routine, which would handle enabling the DWC3 gadget.
Acked-by: Felipe Balbi <balbi@kernel.org>
Signed-off-by: Wesley Cheng <wcheng@codeaurora.org>
Link: https://lore.kernel.org/r/20210917021852.2037-1-wcheng@codeaurora.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Bug: 200287549
(cherry picked from commit 8217f07a50
https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing)
[wcheng: Fixed KMI breakage by moving softconnect to a new parent structure]
Change-Id: I9554933826710cc68136b08176290584f9ab74df
Signed-off-by: Wesley Cheng <wcheng@codeaurora.org>
This commit is contained in:
committed by
Alistair Delva
parent
0671bafa24
commit
bb13ff0598
@@ -1530,15 +1530,17 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct resource *res, dwc_res;
|
struct resource *res, dwc_res;
|
||||||
|
struct dwc3_vendor *vdwc;
|
||||||
struct dwc3 *dwc;
|
struct dwc3 *dwc;
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
|
|
||||||
dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
|
vdwc = devm_kzalloc(dev, sizeof(*vdwc), GFP_KERNEL);
|
||||||
if (!dwc)
|
if (!vdwc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
dwc = &vdwc->dwc;
|
||||||
|
|
||||||
dwc->dev = dev;
|
dwc->dev = dev;
|
||||||
|
|
||||||
|
|||||||
@@ -1319,6 +1319,16 @@ struct dwc3 {
|
|||||||
ANDROID_KABI_RESERVE(4);
|
ANDROID_KABI_RESERVE(4);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct dwc3_vendor - contains parameters without modifying the format of DWC3 core
|
||||||
|
* @dwc: contains dwc3 core reference
|
||||||
|
* @softconnect: true when gadget connect is called, false when disconnect runs
|
||||||
|
*/
|
||||||
|
struct dwc3_vendor {
|
||||||
|
struct dwc3 dwc;
|
||||||
|
unsigned softconnect:1;
|
||||||
|
};
|
||||||
|
|
||||||
#define INCRX_BURST_MODE 0
|
#define INCRX_BURST_MODE 0
|
||||||
#define INCRX_UNDEF_LENGTH_BURST_MODE 1
|
#define INCRX_UNDEF_LENGTH_BURST_MODE 1
|
||||||
|
|
||||||
|
|||||||
@@ -2411,10 +2411,12 @@ static int __dwc3_gadget_start(struct dwc3 *dwc);
|
|||||||
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
||||||
{
|
{
|
||||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||||
|
struct dwc3_vendor *vdwc = container_of(dwc, struct dwc3_vendor, dwc);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
is_on = !!is_on;
|
is_on = !!is_on;
|
||||||
|
vdwc->softconnect = is_on;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Per databook, when we want to stop the gadget, if a control transfer
|
* Per databook, when we want to stop the gadget, if a control transfer
|
||||||
@@ -4362,9 +4364,10 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
|
|||||||
|
|
||||||
int dwc3_gadget_resume(struct dwc3 *dwc)
|
int dwc3_gadget_resume(struct dwc3 *dwc)
|
||||||
{
|
{
|
||||||
|
struct dwc3_vendor *vdwc = container_of(dwc, struct dwc3_vendor, dwc);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!dwc->gadget_driver)
|
if (!dwc->gadget_driver || !vdwc->softconnect)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = __dwc3_gadget_start(dwc);
|
ret = __dwc3_gadget_start(dwc);
|
||||||
|
|||||||
Reference in New Issue
Block a user