Commit 87191ca9 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: UDC: Implement udc_async_callbacks in net2272

This patch adds a udc_async_callbacks handler to the net2272 UDC
driver, which will prevent a theoretical race during gadget unbinding.

The net2272 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: default avatarFelipe Balbi <balbi@kernel.org>
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Link: https://lore.kernel.org/r/20210520202206.GF1216852@rowland.harvard.eduSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b42e8090
...@@ -1150,6 +1150,7 @@ net2272_pullup(struct usb_gadget *_gadget, int is_on) ...@@ -1150,6 +1150,7 @@ net2272_pullup(struct usb_gadget *_gadget, int is_on)
static int net2272_start(struct usb_gadget *_gadget, static int net2272_start(struct usb_gadget *_gadget,
struct usb_gadget_driver *driver); struct usb_gadget_driver *driver);
static int net2272_stop(struct usb_gadget *_gadget); static int net2272_stop(struct usb_gadget *_gadget);
static void net2272_async_callbacks(struct usb_gadget *_gadget, bool enable);
static const struct usb_gadget_ops net2272_ops = { static const struct usb_gadget_ops net2272_ops = {
.get_frame = net2272_get_frame, .get_frame = net2272_get_frame,
...@@ -1158,6 +1159,7 @@ static const struct usb_gadget_ops net2272_ops = { ...@@ -1158,6 +1159,7 @@ static const struct usb_gadget_ops net2272_ops = {
.pullup = net2272_pullup, .pullup = net2272_pullup,
.udc_start = net2272_start, .udc_start = net2272_start,
.udc_stop = net2272_stop, .udc_stop = net2272_stop,
.udc_async_callbacks = net2272_async_callbacks,
}; };
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
...@@ -1476,7 +1478,7 @@ stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver) ...@@ -1476,7 +1478,7 @@ stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver)
net2272_dequeue_all(&dev->ep[i]); net2272_dequeue_all(&dev->ep[i]);
/* report disconnect; the driver is already quiesced */ /* report disconnect; the driver is already quiesced */
if (driver) { if (dev->async_callbacks && driver) {
spin_unlock(&dev->lock); spin_unlock(&dev->lock);
driver->disconnect(&dev->gadget); driver->disconnect(&dev->gadget);
spin_lock(&dev->lock); spin_lock(&dev->lock);
...@@ -1501,6 +1503,15 @@ static int net2272_stop(struct usb_gadget *_gadget) ...@@ -1501,6 +1503,15 @@ static int net2272_stop(struct usb_gadget *_gadget)
return 0; return 0;
} }
static void net2272_async_callbacks(struct usb_gadget *_gadget, bool enable)
{
struct net2272 *dev = container_of(_gadget, struct net2272, gadget);
spin_lock_irq(&dev->lock);
dev->async_callbacks = enable;
spin_unlock_irq(&dev->lock);
}
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* handle ep-a/ep-b dma completions */ /* handle ep-a/ep-b dma completions */
static void static void
...@@ -1910,9 +1921,11 @@ net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat) ...@@ -1910,9 +1921,11 @@ net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat)
u.r.bRequestType, u.r.bRequest, u.r.bRequestType, u.r.bRequest,
u.r.wValue, u.r.wIndex, u.r.wValue, u.r.wIndex,
net2272_ep_read(ep, EP_CFG)); net2272_ep_read(ep, EP_CFG));
spin_unlock(&dev->lock); if (dev->async_callbacks) {
tmp = dev->driver->setup(&dev->gadget, &u.r); spin_unlock(&dev->lock);
spin_lock(&dev->lock); tmp = dev->driver->setup(&dev->gadget, &u.r);
spin_lock(&dev->lock);
}
} }
/* stall ep0 on error */ /* stall ep0 on error */
...@@ -1994,14 +2007,14 @@ net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat) ...@@ -1994,14 +2007,14 @@ net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat)
if (disconnect || reset) { if (disconnect || reset) {
stop_activity(dev, dev->driver); stop_activity(dev, dev->driver);
net2272_ep0_start(dev); net2272_ep0_start(dev);
spin_unlock(&dev->lock); if (dev->async_callbacks) {
if (reset) spin_unlock(&dev->lock);
usb_gadget_udc_reset if (reset)
(&dev->gadget, dev->driver); usb_gadget_udc_reset(&dev->gadget, dev->driver);
else else
(dev->driver->disconnect) (dev->driver->disconnect)(&dev->gadget);
(&dev->gadget); spin_lock(&dev->lock);
spin_lock(&dev->lock); }
return; return;
} }
} }
...@@ -2015,14 +2028,14 @@ net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat) ...@@ -2015,14 +2028,14 @@ net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat)
if (stat & tmp) { if (stat & tmp) {
net2272_write(dev, IRQSTAT1, tmp); net2272_write(dev, IRQSTAT1, tmp);
if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) { if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) {
if (dev->driver->suspend) if (dev->async_callbacks && dev->driver->suspend)
dev->driver->suspend(&dev->gadget); dev->driver->suspend(&dev->gadget);
if (!enable_suspend) { if (!enable_suspend) {
stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT); stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT);
dev_dbg(dev->dev, "Suspend disabled, ignoring\n"); dev_dbg(dev->dev, "Suspend disabled, ignoring\n");
} }
} else { } else {
if (dev->driver->resume) if (dev->async_callbacks && dev->driver->resume)
dev->driver->resume(&dev->gadget); dev->driver->resume(&dev->gadget);
} }
stat &= ~tmp; stat &= ~tmp;
......
...@@ -442,6 +442,7 @@ struct net2272 { ...@@ -442,6 +442,7 @@ struct net2272 {
softconnect:1, softconnect:1,
wakeup:1, wakeup:1,
added:1, added:1,
async_callbacks:1,
dma_eot_polarity:1, dma_eot_polarity:1,
dma_dack_polarity:1, dma_dack_polarity:1,
dma_dreq_polarity:1, dma_dreq_polarity:1,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment