cdc_subset: deal with a device that needs reset for timeout
authorOliver Neukum <oneukum@suse.de>
Fri, 1 Aug 2014 12:01:51 +0000 (14:01 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sat, 2 Aug 2014 22:44:18 +0000 (15:44 -0700)
This device needs to be reset to recover from a timeout.
Unfortunately this can be handled only at the level of
the subdrivers.

Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/usb/cdc_subset.c
drivers/net/usb/usbnet.c
include/linux/usb/usbnet.h

index 91f0919fe27891cd4993e63488fa3094f19fc197..6ea98cff2d3bf8df13d5e8deee032b971b81d0dd 100644 (file)
@@ -85,14 +85,28 @@ static int always_connected (struct usbnet *dev)
  *
  *-------------------------------------------------------------------------*/
 
+static void m5632_recover(struct usbnet *dev)
+{
+       struct usb_device       *udev = dev->udev;
+       struct usb_interface    *intf = dev->intf;
+       int r;
+
+       r = usb_lock_device_for_reset(udev, intf);
+       if (r < 0)
+               return;
+
+       usb_reset_device(udev);
+       usb_unlock_device(udev);
+}
+
 static const struct driver_info        ali_m5632_info = {
        .description =  "ALi M5632",
        .flags       = FLAG_POINTTOPOINT,
+       .recover     = m5632_recover,
 };
 
 #endif
 
-\f
 #ifdef CONFIG_USB_AN2720
 #define        HAVE_HARDWARE
 
@@ -326,12 +340,23 @@ static const struct usb_device_id products [] = {
 MODULE_DEVICE_TABLE(usb, products);
 
 /*-------------------------------------------------------------------------*/
+static int dummy_prereset(struct usb_interface *intf)
+{
+        return 0;
+}
+
+static int dummy_postreset(struct usb_interface *intf)
+{
+        return 0;
+}
 
 static struct usb_driver cdc_subset_driver = {
        .name =         "cdc_subset",
        .probe =        usbnet_probe,
        .suspend =      usbnet_suspend,
        .resume =       usbnet_resume,
+       .pre_reset =    dummy_prereset,
+       .post_reset =   dummy_postreset,
        .disconnect =   usbnet_disconnect,
        .id_table =     products,
        .disable_hub_initiated_lpm = 1,
index f9e96c4275589ebc63b3d87432fe50471c287872..5173821a9575e4131f9b45ceef41c9c1eda52cfd 100644 (file)
@@ -1218,8 +1218,12 @@ void usbnet_tx_timeout (struct net_device *net)
 
        unlink_urbs (dev, &dev->txq);
        tasklet_schedule (&dev->bh);
-
-       // FIXME: device recovery -- reset?
+       /* this needs to be handled individually because the generic layer
+        * doesn't know what is sufficient and could not restore private
+        * information if a remedy of an unconditional reset were used.
+        */
+       if (dev->driver_info->recover)
+               (dev->driver_info->recover)(dev);
 }
 EXPORT_SYMBOL_GPL(usbnet_tx_timeout);
 
index 0662e98fef72b21fdabb7d14ac1fd71f2066a140..26088feb660813448ac93391bee76c1e73b57d70 100644 (file)
@@ -148,6 +148,9 @@ struct driver_info {
        struct sk_buff  *(*tx_fixup)(struct usbnet *dev,
                                struct sk_buff *skb, gfp_t flags);
 
+       /* recover from timeout */
+       void    (*recover)(struct usbnet *dev);
+
        /* early initialization code, can sleep. This is for minidrivers
         * having 'subminidrivers' that need to do extra initialization
         * right after minidriver have initialized hardware. */