summaryrefslogtreecommitdiffstats
path: root/drivers/net/usb/usbnet.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/usb/usbnet.c')
-rw-r--r--drivers/net/usb/usbnet.c45
1 files changed, 44 insertions, 1 deletions
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 51f3192..1e5a9b7 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -938,6 +938,27 @@ static const struct ethtool_ops usbnet_ethtool_ops = {
/*-------------------------------------------------------------------------*/
+static void __handle_link_change(struct usbnet *dev)
+{
+ if (!test_bit(EVENT_DEV_OPEN, &dev->flags))
+ return;
+
+ if (!netif_carrier_ok(dev->net)) {
+ /* kill URBs for reading packets to save bus bandwidth */
+ unlink_urbs(dev, &dev->rxq);
+
+ /*
+ * tx_timeout will unlink URBs for sending packets and
+ * tx queue is stopped by netcore after link becomes off
+ */
+ } else {
+ /* submitting URBs for reading packets */
+ tasklet_schedule(&dev->bh);
+ }
+
+ clear_bit(EVENT_LINK_CHANGE, &dev->flags);
+}
+
/* work that cannot be done in interrupt context uses keventd.
*
* NOTE: with 2.5 we could do more of this using completion callbacks,
@@ -1035,8 +1056,14 @@ skip_reset:
} else {
usb_autopm_put_interface(dev->intf);
}
+
+ /* handle link change from link resetting */
+ __handle_link_change(dev);
}
+ if (test_bit (EVENT_LINK_CHANGE, &dev->flags))
+ __handle_link_change(dev);
+
if (dev->flags)
netdev_dbg(dev->net, "kevent done, flags = 0x%lx\n", dev->flags);
}
@@ -1286,6 +1313,7 @@ static void usbnet_bh (unsigned long param)
// or are we maybe short a few urbs?
} else if (netif_running (dev->net) &&
netif_device_present (dev->net) &&
+ netif_carrier_ok(dev->net) &&
!timer_pending (&dev->delay) &&
!test_bit (EVENT_RX_HALT, &dev->flags)) {
int temp = dev->rxq.qlen;
@@ -1521,7 +1549,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
netif_device_attach (net);
if (dev->driver_info->flags & FLAG_LINK_INTR)
- netif_carrier_off(net);
+ usbnet_link_change(dev, 0, 0);
return 0;
@@ -1653,6 +1681,21 @@ int usbnet_manage_power(struct usbnet *dev, int on)
}
EXPORT_SYMBOL(usbnet_manage_power);
+void usbnet_link_change(struct usbnet *dev, bool link, bool need_reset)
+{
+ /* update link after link is reseted */
+ if (link && !need_reset)
+ netif_carrier_on(dev->net);
+ else
+ netif_carrier_off(dev->net);
+
+ if (need_reset && link)
+ usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+ else
+ usbnet_defer_kevent(dev, EVENT_LINK_CHANGE);
+}
+EXPORT_SYMBOL(usbnet_link_change);
+
/*-------------------------------------------------------------------------*/
static int __usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
u16 value, u16 index, void *data, u16 size)
OpenPOWER on IntegriCloud