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: Peter Chubb <peter@gelato.unsw.edu.au> Acked-by: Petko Manolov <petkan@nucleusys.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
6ad576bb67
commit
23219c1348
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user