cdc_subset: deal with a device that needs reset for timeout
authorOliver Neukum <oneukum@suse.de>
Mon, 28 Jul 2014 08:12:34 +0000 (10:12 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 29 Jul 2014 19:22:15 +0000 (12:22 -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 91f0919..3ef411e 100644 (file)
@@ -85,9 +85,34 @@ 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 int dummy_prereset(struct usb_interface *intf)
+{
+       return 0;
+}
+
+static int dummy_postreset(struct usb_interface *intf)
+{
+       return 0;
+}
+
 static const struct driver_info        ali_m5632_info = {
        .description =  "ALi M5632",
        .flags       = FLAG_POINTTOPOINT,
+       .recover     = m5632_recover,
 };
 
 #endif
@@ -332,6 +357,8 @@ static struct usb_driver cdc_subset_driver = {
        .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 f9e96c4..5173821 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 0662e98..26088fe 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. */