diff options
author | Adam Hampson <ahampson@google.com> | 2014-06-04 11:07:43 -0700 |
---|---|---|
committer | Adam Hampson <ahampson@google.com> | 2014-06-12 15:57:05 -0700 |
commit | cd6b8ed45d248784aa783a7f7cdf206f0cae6bae (patch) | |
tree | eec0665cb72052872ab3cf7bc388aa9056f48c95 | |
parent | e1c9af4f753c67d70b3832b45ccbea9ef4ffc314 (diff) | |
download | tegra-android-tegra-molly-3.4-l-preview.tar.gz |
net: usb: smsc95xx: fix reset_resume handlingandroid-tegra-molly-3.4-l-preview
When suspending and resuming the smsc95xx chip will sometimes
get into a bad state and a bus reset will occur. This will cause
the usbnet driver to get stuck in a loop where URBs are returned
immediately and new SKBs are allocated consuming all of kernel
memory.
This change causes the smsc95xx driver to be unbound and rebound
on a bus reset. This will cause the chip to be properly
reinitialized and it can recover from the bus reset.
Bug: 15350518
Change-Id: Ieade367b41453ebbc9b797d050a0510b4effb0bd
Signed-off-by: Adam Hampson <ahampson@google.com>
-rw-r--r-- | drivers/net/usb/smsc95xx.c | 55 |
1 files changed, 53 insertions, 2 deletions
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index b10afe706d39..e9091d820b6a 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -797,6 +797,21 @@ static int smsc95xx_ethtool_set_wol(struct net_device *net, return ret; } +static int smsc95xx_ethtool_clear_wol(struct net_device *net) +{ + struct usbnet *dev = netdev_priv(net); + struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + int ret; + + pdata->wolopts = 0; + + ret = device_set_wakeup_enable(&dev->udev->dev, 0); + if (ret < 0) + netdev_warn(dev->net, "device_set_wakeup_enable error %d\n", ret); + + return ret; +} + static const struct ethtool_ops smsc95xx_ethtool_ops = { .get_link = usbnet_get_link, .nway_reset = usbnet_nway_reset, @@ -1807,6 +1822,23 @@ static int smsc95xx_resume(struct usb_interface *intf) return ret; } +static int smsc95xx_reset_resume(struct usb_interface *intf) +{ + struct usbnet *dev = usb_get_intfdata(intf); + + BUG_ON(!dev); + + netdev_warn(dev->net, "A USB bus reset has occurred, mark the " + "interface for rebinding"); + + /* Mark the interface for rebinding. This will cause the + * SMSC95xx device to be reinitialized. + */ + intf->needs_binding = 1; + + return 0; +} + static void smsc95xx_rx_csum_offload(struct sk_buff *skb) { skb->csum = *(u16 *)(skb_tail_pointer(skb) - 2); @@ -2021,6 +2053,25 @@ static int smsc95xx_probe(struct usb_interface *udev, return 0; } +static void smsc95xx_disconnect(struct usb_interface *intf) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + + if (boot_wol_config && usb_dev->actconfig->desc.bmAttributes & + USB_CONFIG_ATT_WAKEUP) { + struct usbnet *dev = usb_get_intfdata(intf); + int ret; + + ret = smsc95xx_ethtool_clear_wol(dev->net); + if (ret < 0) + netdev_warn(dev->net, "unable to clear wol " + "config %x error %d\n", boot_wol_config, + ret); + } + + usbnet_disconnect(intf); +} + static const struct usb_device_id products[] = { { /* SMSC9500 USB Ethernet Device */ @@ -2122,8 +2173,8 @@ static struct usb_driver smsc95xx_driver = { .probe = smsc95xx_probe, .suspend = smsc95xx_suspend, .resume = smsc95xx_resume, - .reset_resume = smsc95xx_resume, - .disconnect = usbnet_disconnect, + .reset_resume = smsc95xx_reset_resume, + .disconnect = smsc95xx_disconnect, .supports_autosuspend = 1, }; |