diff options
author | Dan Williams <dcbw@redhat.com> | 2013-05-06 07:29:23 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-05-08 16:13:29 -0400 |
commit | 6eecdc5f95a393cb558503123eae9a9a6642e835 (patch) | |
tree | 80898f0029def8f64ce43837cc6c60010ba17efe | |
parent | 7fdb7846c9ca6fc06e380de0976a1228703b498a (diff) |
usbnet: allow status interrupt URB to always be active
Some drivers (sierra_net) need the status interrupt URB
active even when the device is closed, because they receive
custom indications from firmware. Add functions to refcount
the status interrupt URB submit/kill operation so that
sub-drivers and the generic driver don't fight over whether
the status interrupt URB is active or not.
A sub-driver can call usbnet_status_start() at any time, but
the URB is only submitted the first time the function is
called. Likewise, when the sub-driver is done with the URB,
it calls usbnet_status_stop() but the URB is only killed when
all users have stopped it. The URB is still killed and
re-submitted for suspend/resume, as before, with the same
refcount it had at suspend.
Signed-off-by: Dan Williams <dcbw@redhat.com>
Acked-by: Oliver Neukum <oliver@neukum.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/usb/usbnet.c | 77 | ||||
-rw-r--r-- | include/linux/usb/usbnet.h | 5 |
2 files changed, 76 insertions, 6 deletions
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 1e5a9b72650e..f95cb032394b 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c | |||
@@ -252,6 +252,70 @@ static int init_status (struct usbnet *dev, struct usb_interface *intf) | |||
252 | return 0; | 252 | return 0; |
253 | } | 253 | } |
254 | 254 | ||
255 | /* Submit the interrupt URB if not previously submitted, increasing refcount */ | ||
256 | int usbnet_status_start(struct usbnet *dev, gfp_t mem_flags) | ||
257 | { | ||
258 | int ret = 0; | ||
259 | |||
260 | WARN_ON_ONCE(dev->interrupt == NULL); | ||
261 | if (dev->interrupt) { | ||
262 | mutex_lock(&dev->interrupt_mutex); | ||
263 | |||
264 | if (++dev->interrupt_count == 1) | ||
265 | ret = usb_submit_urb(dev->interrupt, mem_flags); | ||
266 | |||
267 | dev_dbg(&dev->udev->dev, "incremented interrupt URB count to %d\n", | ||
268 | dev->interrupt_count); | ||
269 | mutex_unlock(&dev->interrupt_mutex); | ||
270 | } | ||
271 | return ret; | ||
272 | } | ||
273 | EXPORT_SYMBOL_GPL(usbnet_status_start); | ||
274 | |||
275 | /* For resume; submit interrupt URB if previously submitted */ | ||
276 | static int __usbnet_status_start_force(struct usbnet *dev, gfp_t mem_flags) | ||
277 | { | ||
278 | int ret = 0; | ||
279 | |||
280 | mutex_lock(&dev->interrupt_mutex); | ||
281 | if (dev->interrupt_count) { | ||
282 | ret = usb_submit_urb(dev->interrupt, mem_flags); | ||
283 | dev_dbg(&dev->udev->dev, | ||
284 | "submitted interrupt URB for resume\n"); | ||
285 | } | ||
286 | mutex_unlock(&dev->interrupt_mutex); | ||
287 | return ret; | ||
288 | } | ||
289 | |||
290 | /* Kill the interrupt URB if all submitters want it killed */ | ||
291 | void usbnet_status_stop(struct usbnet *dev) | ||
292 | { | ||
293 | if (dev->interrupt) { | ||
294 | mutex_lock(&dev->interrupt_mutex); | ||
295 | WARN_ON(dev->interrupt_count == 0); | ||
296 | |||
297 | if (dev->interrupt_count && --dev->interrupt_count == 0) | ||
298 | usb_kill_urb(dev->interrupt); | ||
299 | |||
300 | dev_dbg(&dev->udev->dev, | ||
301 | "decremented interrupt URB count to %d\n", | ||
302 | dev->interrupt_count); | ||
303 | mutex_unlock(&dev->interrupt_mutex); | ||
304 | } | ||
305 | } | ||
306 | EXPORT_SYMBOL_GPL(usbnet_status_stop); | ||
307 | |||
308 | /* For suspend; always kill interrupt URB */ | ||
309 | static void __usbnet_status_stop_force(struct usbnet *dev) | ||
310 | { | ||
311 | if (dev->interrupt) { | ||
312 | mutex_lock(&dev->interrupt_mutex); | ||
313 | usb_kill_urb(dev->interrupt); | ||
314 | dev_dbg(&dev->udev->dev, "killed interrupt URB for suspend\n"); | ||
315 | mutex_unlock(&dev->interrupt_mutex); | ||
316 | } | ||
317 | } | ||
318 | |||
255 | /* Passes this packet up the stack, updating its accounting. | 319 | /* Passes this packet up the stack, updating its accounting. |
256 | * Some link protocols batch packets, so their rx_fixup paths | 320 | * Some link protocols batch packets, so their rx_fixup paths |
257 | * can return clones as well as just modify the original skb. | 321 | * can return clones as well as just modify the original skb. |
@@ -725,7 +789,7 @@ int usbnet_stop (struct net_device *net) | |||
725 | if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) | 789 | if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) |
726 | usbnet_terminate_urbs(dev); | 790 | usbnet_terminate_urbs(dev); |
727 | 791 | ||
728 | usb_kill_urb(dev->interrupt); | 792 | usbnet_status_stop(dev); |
729 | 793 | ||
730 | usbnet_purge_paused_rxq(dev); | 794 | usbnet_purge_paused_rxq(dev); |
731 | 795 | ||
@@ -787,7 +851,7 @@ int usbnet_open (struct net_device *net) | |||
787 | 851 | ||
788 | /* start any status interrupt transfer */ | 852 | /* start any status interrupt transfer */ |
789 | if (dev->interrupt) { | 853 | if (dev->interrupt) { |
790 | retval = usb_submit_urb (dev->interrupt, GFP_KERNEL); | 854 | retval = usbnet_status_start(dev, GFP_KERNEL); |
791 | if (retval < 0) { | 855 | if (retval < 0) { |
792 | netif_err(dev, ifup, dev->net, | 856 | netif_err(dev, ifup, dev->net, |
793 | "intr submit %d\n", retval); | 857 | "intr submit %d\n", retval); |
@@ -1458,6 +1522,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) | |||
1458 | dev->delay.data = (unsigned long) dev; | 1522 | dev->delay.data = (unsigned long) dev; |
1459 | init_timer (&dev->delay); | 1523 | init_timer (&dev->delay); |
1460 | mutex_init (&dev->phy_mutex); | 1524 | mutex_init (&dev->phy_mutex); |
1525 | mutex_init(&dev->interrupt_mutex); | ||
1526 | dev->interrupt_count = 0; | ||
1461 | 1527 | ||
1462 | dev->net = net; | 1528 | dev->net = net; |
1463 | strcpy (net->name, "usb%d"); | 1529 | strcpy (net->name, "usb%d"); |
@@ -1593,7 +1659,7 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message) | |||
1593 | */ | 1659 | */ |
1594 | netif_device_detach (dev->net); | 1660 | netif_device_detach (dev->net); |
1595 | usbnet_terminate_urbs(dev); | 1661 | usbnet_terminate_urbs(dev); |
1596 | usb_kill_urb(dev->interrupt); | 1662 | __usbnet_status_stop_force(dev); |
1597 | 1663 | ||
1598 | /* | 1664 | /* |
1599 | * reattach so runtime management can use and | 1665 | * reattach so runtime management can use and |
@@ -1613,9 +1679,8 @@ int usbnet_resume (struct usb_interface *intf) | |||
1613 | int retval; | 1679 | int retval; |
1614 | 1680 | ||
1615 | if (!--dev->suspend_count) { | 1681 | if (!--dev->suspend_count) { |
1616 | /* resume interrupt URBs */ | 1682 | /* resume interrupt URB if it was previously submitted */ |
1617 | if (dev->interrupt && test_bit(EVENT_DEV_OPEN, &dev->flags)) | 1683 | __usbnet_status_start_force(dev, GFP_NOIO); |
1618 | usb_submit_urb(dev->interrupt, GFP_NOIO); | ||
1619 | 1684 | ||
1620 | spin_lock_irq(&dev->txq.lock); | 1685 | spin_lock_irq(&dev->txq.lock); |
1621 | while ((res = usb_get_from_anchor(&dev->deferred))) { | 1686 | while ((res = usb_get_from_anchor(&dev->deferred))) { |
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index da46327fca17..f18d64129f99 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h | |||
@@ -56,6 +56,8 @@ struct usbnet { | |||
56 | struct sk_buff_head done; | 56 | struct sk_buff_head done; |
57 | struct sk_buff_head rxq_pause; | 57 | struct sk_buff_head rxq_pause; |
58 | struct urb *interrupt; | 58 | struct urb *interrupt; |
59 | unsigned interrupt_count; | ||
60 | struct mutex interrupt_mutex; | ||
59 | struct usb_anchor deferred; | 61 | struct usb_anchor deferred; |
60 | struct tasklet_struct bh; | 62 | struct tasklet_struct bh; |
61 | 63 | ||
@@ -248,4 +250,7 @@ extern int usbnet_nway_reset(struct net_device *net); | |||
248 | extern int usbnet_manage_power(struct usbnet *, int); | 250 | extern int usbnet_manage_power(struct usbnet *, int); |
249 | extern void usbnet_link_change(struct usbnet *, bool, bool); | 251 | extern void usbnet_link_change(struct usbnet *, bool, bool); |
250 | 252 | ||
253 | extern int usbnet_status_start(struct usbnet *dev, gfp_t mem_flags); | ||
254 | extern void usbnet_status_stop(struct usbnet *dev); | ||
255 | |||
251 | #endif /* __LINUX_USB_USBNET_H */ | 256 | #endif /* __LINUX_USB_USBNET_H */ |