diff options
Diffstat (limited to 'drivers/net/usb/cx82310_eth.c')
-rw-r--r-- | drivers/net/usb/cx82310_eth.c | 79 |
1 files changed, 57 insertions, 22 deletions
diff --git a/drivers/net/usb/cx82310_eth.c b/drivers/net/usb/cx82310_eth.c index 32b08b18e120..c4568a491dc4 100644 --- a/drivers/net/usb/cx82310_eth.c +++ b/drivers/net/usb/cx82310_eth.c @@ -40,6 +40,11 @@ enum cx82310_status { #define CX82310_MTU 1514 #define CMD_EP 0x01 +struct cx82310_priv { + struct work_struct reenable_work; + struct usbnet *dev; +}; + /* * execute control command * - optionally send some data (command parameters) @@ -66,8 +71,8 @@ static int cx82310_cmd(struct usbnet *dev, enum cx82310_cmd cmd, bool reply, CMD_PACKET_SIZE, &actual_len, CMD_TIMEOUT); if (ret < 0) { if (cmd != CMD_GET_LINK_STATUS) - dev_err(&dev->udev->dev, "send command %#x: error %d\n", - cmd, ret); + netdev_err(dev->net, "send command %#x: error %d\n", + cmd, ret); goto end; } @@ -79,30 +84,27 @@ static int cx82310_cmd(struct usbnet *dev, enum cx82310_cmd cmd, bool reply, CMD_TIMEOUT); if (ret < 0) { if (cmd != CMD_GET_LINK_STATUS) - dev_err(&dev->udev->dev, - "reply receive error %d\n", - ret); + netdev_err(dev->net, "reply receive error %d\n", + ret); goto end; } if (actual_len > 0) break; } if (actual_len == 0) { - dev_err(&dev->udev->dev, "no reply to command %#x\n", - cmd); + netdev_err(dev->net, "no reply to command %#x\n", cmd); ret = -EIO; goto end; } if (buf[0] != cmd) { - dev_err(&dev->udev->dev, - "got reply to command %#x, expected: %#x\n", - buf[0], cmd); + netdev_err(dev->net, "got reply to command %#x, expected: %#x\n", + buf[0], cmd); ret = -EIO; goto end; } if (buf[1] != STATUS_SUCCESS) { - dev_err(&dev->udev->dev, "command %#x failed: %#x\n", - cmd, buf[1]); + netdev_err(dev->net, "command %#x failed: %#x\n", cmd, + buf[1]); ret = -EIO; goto end; } @@ -115,6 +117,23 @@ end: return ret; } +static int cx82310_enable_ethernet(struct usbnet *dev) +{ + int ret = cx82310_cmd(dev, CMD_ETHERNET_MODE, true, "\x01", 1, NULL, 0); + + if (ret) + netdev_err(dev->net, "unable to enable ethernet mode: %d\n", + ret); + return ret; +} + +static void cx82310_reenable_work(struct work_struct *work) +{ + struct cx82310_priv *priv = container_of(work, struct cx82310_priv, + reenable_work); + cx82310_enable_ethernet(priv->dev); +} + #define partial_len data[0] /* length of partial packet data */ #define partial_rem data[1] /* remaining (missing) data length */ #define partial_data data[2] /* partial packet data */ @@ -126,6 +145,7 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf) struct usb_device *udev = dev->udev; u8 link[3]; int timeout = 50; + struct cx82310_priv *priv; /* avoid ADSL modems - continue only if iProduct is "USB NET CARD" */ if (usb_string(udev, udev->descriptor.iProduct, buf, sizeof(buf)) > 0 @@ -152,6 +172,15 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf) if (!dev->partial_data) return -ENOMEM; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto err_partial; + } + dev->driver_priv = priv; + INIT_WORK(&priv->reenable_work, cx82310_reenable_work); + priv->dev = dev; + /* wait for firmware to become ready (indicated by the link being up) */ while (--timeout) { ret = cx82310_cmd(dev, CMD_GET_LINK_STATUS, true, NULL, 0, @@ -162,24 +191,21 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf) msleep(500); } if (!timeout) { - dev_err(&udev->dev, "firmware not ready in time\n"); + netdev_err(dev->net, "firmware not ready in time\n"); ret = -ETIMEDOUT; goto err; } /* enable ethernet mode (?) */ - ret = cx82310_cmd(dev, CMD_ETHERNET_MODE, true, "\x01", 1, NULL, 0); - if (ret) { - dev_err(&udev->dev, "unable to enable ethernet mode: %d\n", - ret); + ret = cx82310_enable_ethernet(dev); + if (ret) goto err; - } /* get the MAC address */ ret = cx82310_cmd(dev, CMD_GET_MAC_ADDR, true, NULL, 0, dev->net->dev_addr, ETH_ALEN); if (ret) { - dev_err(&udev->dev, "unable to read MAC address: %d\n", ret); + netdev_err(dev->net, "unable to read MAC address: %d\n", ret); goto err; } @@ -190,13 +216,19 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf) return 0; err: + kfree(dev->driver_priv); +err_partial: kfree((void *)dev->partial_data); return ret; } static void cx82310_unbind(struct usbnet *dev, struct usb_interface *intf) { + struct cx82310_priv *priv = dev->driver_priv; + kfree((void *)dev->partial_data); + cancel_work_sync(&priv->reenable_work); + kfree(dev->driver_priv); } /* @@ -211,6 +243,7 @@ static int cx82310_rx_fixup(struct usbnet *dev, struct sk_buff *skb) { int len; struct sk_buff *skb2; + struct cx82310_priv *priv = dev->driver_priv; /* * If the last skb ended with an incomplete packet, this skb contains @@ -245,9 +278,11 @@ static int cx82310_rx_fixup(struct usbnet *dev, struct sk_buff *skb) break; } - if (len > CX82310_MTU) { - dev_err(&dev->udev->dev, "RX packet too long: %d B\n", - len); + if (len == 0xffff) { + netdev_info(dev->net, "router was rebooted, re-enabling ethernet mode"); + schedule_work(&priv->reenable_work); + } else if (len > CX82310_MTU) { + netdev_err(dev->net, "RX packet too long: %d B\n", len); return 0; } |