Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / drivers / net / usb / usbnet.c
index 51f3192f3931e0a179dcb619e4e714a0565656fc..1e5a9b72650e9946e23040f1169421bf87d18317 100644 (file)
@@ -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)