Commit 23219c13 authored by Peter Chubb's avatar Peter Chubb Committed by Greg Kroah-Hartman

USB: Patch for rtl8150 to fix unplug problems

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: default avatarPeter Chubb <peter@gelato.unsw.edu.au>
Acked-by: default avatarPetko Manolov <petkan@nucleusys.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 6ad576bb
......@@ -175,6 +175,8 @@ static inline struct sk_buff *pull_skb(rtl8150_t *);
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 * dev, u16 indx, u16 size)
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 urb *urb, struct pt_regs *regs)
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 urb *urb, struct pt_regs *regs)
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 *urb, struct pt_regs *regs)
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_buff *skb, struct net_device *netdev)
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_device *netdev)
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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment