The RTL8150 driver currently crashes the kernel if the USB lead is unplugged
while the device is active.  The attached patch adds error handling to
tell the network layer that the device has gone away when the device is
unplugged.  With this patch, the device can be plugged and unplugged
to one's hearts' content, without crashing anything.

Oh, I've also added rudimentary suspend and resume methods.

Signed off by: Peter Chubb <[EMAIL PROTECTED]>

diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
index e5e6e4f..35833d9 100644
--- a/drivers/usb/net/rtl8150.c
+++ b/drivers/usb/net/rtl8150.c
@@ -175,6 +175,8 @@ static inline struct sk_buff *pull_skb(r
 static void rtl8150_disconnect(struct usb_interface *intf);
 static int rtl8150_probe(struct usb_interface *intf,
                           const struct usb_device_id *id);
+static int rtl8150_suspend(struct usb_interface *intf, pm_message_t message);
+static int rtl8150_resume(struct usb_interface *intf);
 
 static const char driver_name [] = "rtl8150";
 
@@ -183,6 +185,8 @@ static struct usb_driver rtl8150_driver 
        .probe =        rtl8150_probe,
        .disconnect =   rtl8150_disconnect,
        .id_table =     rtl8150_table,
+       .suspend =      rtl8150_suspend,
+       .resume =       rtl8150_resume
 };
 
 /*
@@ -238,9 +242,11 @@ static int async_set_registers(rtl8150_t
        usb_fill_control_urb(dev->ctrl_urb, dev->udev,
                         usb_sndctrlpipe(dev->udev, 0), (char *) &dev->dr,
                         &dev->rx_creg, size, ctrl_callback, dev);
-       if ((ret = usb_submit_urb(dev->ctrl_urb, GFP_ATOMIC)))
+       if ((ret = usb_submit_urb(dev->ctrl_urb, GFP_ATOMIC))) {
+               if (ret == -ENODEV)
+                       netif_device_detach(dev->netdev);
                err("control request submission failed: %d", ret);
-       else
+       } else
                set_bit(RX_REG_SET, &dev->flags);
 
        return ret;
@@ -416,6 +422,7 @@ static void read_bulk_callback(struct ur
        struct sk_buff *skb;
        struct net_device *netdev;
        u16 rx_stat;
+       int status;
 
        dev = urb->context;
        if (!dev)
@@ -465,7 +472,10 @@ static void read_bulk_callback(struct ur
 goon:
        usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
                      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
-       if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC)) {
+       status = usb_submit_urb(dev->rx_urb, GFP_ATOMIC);
+       if (status == -ENODEV)
+               netif_device_detach(dev->netdev);
+       else if (status) {
                set_bit(RX_URB_FAIL, &dev->flags);
                goto resched;
        } else {
@@ -481,6 +491,7 @@ static void rx_fixup(unsigned long data)
 {
        rtl8150_t *dev;
        struct sk_buff *skb;
+       int status;
 
        dev = (rtl8150_t *)data;
 
@@ -499,10 +510,13 @@ static void rx_fixup(unsigned long data)
        usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
                      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
 try_again:
-       if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC)) {
+       status = usb_submit_urb(dev->rx_urb, GFP_ATOMIC);
+       if (status == -ENODEV) {
+               netif_device_detach(dev->netdev);
+       } else if (status) {
                set_bit(RX_URB_FAIL, &dev->flags);
                goto tlsched;
-        } else {
+       } else {
                clear_bit(RX_URB_FAIL, &dev->flags);
        }
 
@@ -574,12 +588,43 @@ static void intr_callback(struct urb *ur
 
 resubmit:
        status = usb_submit_urb (urb, SLAB_ATOMIC);
-       if (status)
+       if (status == -ENODEV)
+               netif_device_detach(dev->netdev);
+       else if (status)
                err ("can't resubmit intr, %s-%s/input0, status %d",
                                dev->udev->bus->bus_name,
                                dev->udev->devpath, status);
 }
 
+static int rtl8150_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       rtl8150_t *dev = usb_get_intfdata(intf);
+       
+       netif_device_detach(dev->netdev);
+
+       if (netif_running(dev->netdev)) {
+               usb_kill_urb(dev->rx_urb);
+               usb_kill_urb(dev->intr_urb);
+       }
+       return 0;
+}
+
+static int rtl8150_resume(struct usb_interface *intf)
+{
+       rtl8150_t *dev = usb_get_intfdata(intf);
+
+       netif_device_attach(dev->netdev);
+       if (netif_running(dev->netdev)) {
+               dev->rx_urb->status = 0;
+               dev->rx_urb->actual_length = 0;
+               read_bulk_callback(dev->rx_urb, NULL);
+
+               dev->intr_urb->status = 0;
+               dev->intr_urb->actual_length = 0;
+               intr_callback(dev->intr_urb, NULL);
+       }
+       return 0;
+}
 
 /*
 **
@@ -690,9 +735,14 @@ static int rtl8150_start_xmit(struct sk_
        usb_fill_bulk_urb(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2),
                      skb->data, count, write_bulk_callback, dev);
        if ((res = usb_submit_urb(dev->tx_urb, GFP_ATOMIC))) {
-               warn("failed tx_urb %d\n", res);
-               dev->stats.tx_errors++;
-               netif_start_queue(netdev);
+               /* Can we get/handle EPIPE here? */
+               if (res == -ENODEV) 
+                       netif_device_detach(dev->netdev);
+               else {
+                       warn("failed tx_urb %d\n", res);
+                       dev->stats.tx_errors++;
+                       netif_start_queue(netdev);
+               }
        } else {
                dev->stats.tx_packets++;
                dev->stats.tx_bytes += skb->len;
@@ -729,16 +779,25 @@ static int rtl8150_open(struct net_devic
        
        usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
                      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
-       if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL)))
+       if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL))) {
+               if (res == -ENODEV) 
+                       netif_device_detach(dev->netdev);
                warn("%s: rx_urb submit failed: %d", __FUNCTION__, res);
+               return res;
+       }
        usb_fill_int_urb(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev, 3),
                     dev->intr_buff, INTBUFSIZE, intr_callback,
                     dev, dev->intr_interval);
-       if ((res = usb_submit_urb(dev->intr_urb, GFP_KERNEL)))
+       if ((res = usb_submit_urb(dev->intr_urb, GFP_KERNEL))) {
+               if (res == -ENODEV)
+                       netif_device_detach(dev->netdev);
                warn("%s: intr_urb submit failed: %d", __FUNCTION__, res);
-       netif_start_queue(netdev);
+               usb_kill_urb(dev->rx_urb);
+               return res;
+       }
        enable_net_traffic(dev);
        set_carrier(netdev);
+       netif_start_queue(netdev);
 
        return res;
 }


--
Dr Peter Chubb  http://www.gelato.unsw.edu.au  peterc AT gelato.unsw.edu.au
http://www.ertos.nicta.com.au           ERTOS within National ICT Australia

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to