summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2021-05-20 16:22:00 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-06-04 13:52:06 +0200
commitb42e8090ba93526d6063108b25e5fc0f11f58770 (patch)
tree407007f6ed3fe26688820f9b55acb72841f965b2
parent04145a03db9d78469e0817ab3a767c76c0fb0947 (diff)
USB: UDC: Implement udc_async_callbacks in net2280
This patch adds a udc_async_callbacks handler to the net2280 UDC driver, which will prevent a theoretical race during gadget unbinding. The net2280 driver is sufficiently complicated that I didn't want to mess around with IRQ settings. Instead, the patch simply adds a new flag to control async callbacks, and checks the flag before issuing any of them. Acked-by: Felipe Balbi <balbi@kernel.org> Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Link: https://lore.kernel.org/r/20210520202200.GE1216852@rowland.harvard.edu Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/gadget/udc/net2280.c49
-rw-r--r--drivers/usb/gadget/udc/net2280.h1
2 files changed, 33 insertions, 17 deletions
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index fc9f99fe7f37..0e0458e3662b 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -1617,6 +1617,7 @@ static struct usb_ep *net2280_match_ep(struct usb_gadget *_gadget,
static int net2280_start(struct usb_gadget *_gadget,
struct usb_gadget_driver *driver);
static int net2280_stop(struct usb_gadget *_gadget);
+static void net2280_async_callbacks(struct usb_gadget *_gadget, bool enable);
static const struct usb_gadget_ops net2280_ops = {
.get_frame = net2280_get_frame,
@@ -1625,6 +1626,7 @@ static const struct usb_gadget_ops net2280_ops = {
.pullup = net2280_pullup,
.udc_start = net2280_start,
.udc_stop = net2280_stop,
+ .udc_async_callbacks = net2280_async_callbacks,
.match_ep = net2280_match_ep,
};
@@ -2472,7 +2474,7 @@ static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver)
nuke(&dev->ep[i]);
/* report disconnect; the driver is already quiesced */
- if (driver) {
+ if (dev->async_callbacks && driver) {
spin_unlock(&dev->lock);
driver->disconnect(&dev->gadget);
spin_lock(&dev->lock);
@@ -2502,6 +2504,15 @@ static int net2280_stop(struct usb_gadget *_gadget)
return 0;
}
+static void net2280_async_callbacks(struct usb_gadget *_gadget, bool enable)
+{
+ struct net2280 *dev = container_of(_gadget, struct net2280, gadget);
+
+ spin_lock_irq(&dev->lock);
+ dev->async_callbacks = enable;
+ spin_unlock_irq(&dev->lock);
+}
+
/*-------------------------------------------------------------------------*/
/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq.
@@ -3042,9 +3053,11 @@ usb3_delegate:
readl(&ep->cfg->ep_cfg));
ep->responded = 0;
- spin_unlock(&dev->lock);
- tmp = dev->driver->setup(&dev->gadget, &r);
- spin_lock(&dev->lock);
+ if (dev->async_callbacks) {
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &r);
+ spin_lock(&dev->lock);
+ }
}
do_stall3:
if (tmp < 0) {
@@ -3284,9 +3297,11 @@ delegate:
w_value, w_index, w_length,
readl(&ep->cfg->ep_cfg));
ep->responded = 0;
- spin_unlock(&dev->lock);
- tmp = dev->driver->setup(&dev->gadget, &u.r);
- spin_lock(&dev->lock);
+ if (dev->async_callbacks) {
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &u.r);
+ spin_lock(&dev->lock);
+ }
}
/* stall ep0 on error */
@@ -3391,14 +3406,14 @@ __acquires(dev->lock)
if (disconnect || reset) {
stop_activity(dev, dev->driver);
ep0_start(dev);
- spin_unlock(&dev->lock);
- if (reset)
- usb_gadget_udc_reset
- (&dev->gadget, dev->driver);
- else
- (dev->driver->disconnect)
- (&dev->gadget);
- spin_lock(&dev->lock);
+ if (dev->async_callbacks) {
+ spin_unlock(&dev->lock);
+ if (reset)
+ usb_gadget_udc_reset(&dev->gadget, dev->driver);
+ else
+ (dev->driver->disconnect)(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
return;
}
}
@@ -3419,12 +3434,12 @@ __acquires(dev->lock)
writel(tmp, &dev->regs->irqstat1);
spin_unlock(&dev->lock);
if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) {
- if (dev->driver->suspend)
+ if (dev->async_callbacks && dev->driver->suspend)
dev->driver->suspend(&dev->gadget);
if (!enable_suspend)
stat &= ~BIT(SUSPEND_REQUEST_INTERRUPT);
} else {
- if (dev->driver->resume)
+ if (dev->async_callbacks && dev->driver->resume)
dev->driver->resume(&dev->gadget);
/* at high speed, note erratum 0133 */
}
diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h
index 7da3dc1e9729..34716a9f4926 100644
--- a/drivers/usb/gadget/udc/net2280.h
+++ b/drivers/usb/gadget/udc/net2280.h
@@ -162,6 +162,7 @@ struct net2280 {
ltm_enable:1,
wakeup_enable:1,
addressed_state:1,
+ async_callbacks:1,
bug7734_patched:1;
u16 chiprev;
int enhanced_mode;