#define EVENT_DEV_ASLEEP 7
#define EVENT_DEV_OPEN 8
#define EVENT_STAT_UPDATE 9
+#define EVENT_DEV_DISCONNECT 10
struct statstage {
struct mutex access_lock; /* for stats access */
static int lan78xx_read_reg(struct lan78xx_net *dev, u32 index, u32 *data)
{
- u32 *buf = kmalloc(sizeof(u32), GFP_KERNEL);
+ u32 *buf;
int ret;
+ if (test_bit(EVENT_DEV_DISCONNECT, &dev->flags))
+ return -ENODEV;
+
+ buf = kmalloc(sizeof(u32), GFP_KERNEL);
if (!buf)
return -ENOMEM;
static int lan78xx_write_reg(struct lan78xx_net *dev, u32 index, u32 data)
{
- u32 *buf = kmalloc(sizeof(u32), GFP_KERNEL);
+ u32 *buf;
int ret;
+ if (test_bit(EVENT_DEV_DISCONNECT, &dev->flags))
+ return -ENODEV;
+
+ buf = kmalloc(sizeof(u32), GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* software-driven interface shutdown */
case -ECONNRESET:
case -ESHUTDOWN:
+ netif_dbg(dev, tx_err, dev->net,
+ "tx err interface gone %d\n",
+ entry->urb->status);
break;
case -EPROTO:
case -ETIME:
case -EILSEQ:
netif_stop_queue(dev->net);
+ netif_dbg(dev, tx_err, dev->net,
+ "tx err queue stopped %d\n",
+ entry->urb->status);
break;
default:
netif_dbg(dev, tx_err, dev->net,
- "tx err %d\n", entry->urb->status);
+ "unknown tx err %d\n",
+ entry->urb->status);
break;
}
}
lan78xx_defer_kevent(dev, EVENT_RX_HALT);
break;
case -ENODEV:
+ case -ENOENT:
netif_dbg(dev, ifdown, dev->net, "device gone\n");
netif_device_detach(dev->net);
break;
lan78xx_defer_kevent(dev, EVENT_TX_HALT);
usb_autopm_put_interface_async(dev->intf);
break;
+ case -ENODEV:
+ case -ENOENT:
+ netif_dbg(dev, tx_err, dev->net,
+ "tx: submit urb err %d (disconnected?)", ret);
+ netif_device_detach(dev->net);
+ break;
default:
usb_autopm_put_interface_async(dev->intf);
netif_dbg(dev, tx_err, dev->net,
dev = container_of(work, struct lan78xx_net, wq.work);
+ if (test_bit(EVENT_DEV_DISCONNECT, &dev->flags))
+ return;
+
if (usb_autopm_get_interface(dev->intf) < 0)
return;
/* software-driven interface shutdown */
case -ENOENT: /* urb killed */
+ case -ENODEV: /* hardware gone */
case -ESHUTDOWN: /* hardware gone */
netif_dbg(dev, ifdown, dev->net,
"intr shutdown, code %d\n", status);
break;
}
- if (!netif_running(dev->net))
+ if (!netif_device_present(dev->net) ||
+ !netif_running(dev->net)) {
+ netdev_warn(dev->net, "not submitting new status URB");
return;
+ }
memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
status = usb_submit_urb(urb, GFP_ATOMIC);
- if (status != 0)
+
+ switch (status) {
+ case 0:
+ break;
+ case -ENODEV:
+ case -ENOENT:
+ netif_dbg(dev, timer, dev->net,
+ "intr resubmit %d (disconnect?)", status);
+ netif_device_detach(dev->net);
+ break;
+ default:
netif_err(dev, timer, dev->net,
"intr resubmit --> %d\n", status);
+ break;
+ }
}
static void lan78xx_disconnect(struct usb_interface *intf)
if (!dev)
return;
+ set_bit(EVENT_DEV_DISCONNECT, &dev->flags);
+
udev = interface_to_usbdev(intf);
net = dev->net;
+
+ unregister_netdev(net);
+
+ cancel_delayed_work_sync(&dev->wq);
+
phydev = net->phydev;
phy_unregister_fixup_for_uid(PHY_KSZ9031RNX, 0xfffffff0);
if (phy_is_pseudo_fixed_link(phydev))
fixed_phy_unregister(phydev);
- unregister_netdev(net);
-
- cancel_delayed_work_sync(&dev->wq);
-
usb_scuttle_anchored_urbs(&dev->deferred);
+ if (timer_pending(&dev->stat_monitor))
+ del_timer_sync(&dev->stat_monitor);
+
lan78xx_unbind(dev, intf);
usb_kill_urb(dev->urb_intr);