Commit ca139d76 authored by Ondrej Zary's avatar Ondrej Zary Committed by Jakub Kicinski

cx82310_eth: re-enable ethernet mode after router reboot

When the router is rebooted without a power cycle, the USB device
remains connected but its configuration is reset. This results in
a non-working ethernet connection with messages like this in syslog:
	usb 2-2: RX packet too long: 65535 B

Re-enable ethernet mode when receiving a packet with invalid size of
0xffff.
Signed-off-by: default avatarOndrej Zary <linux@zary.sk>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent bc081a69
......@@ -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)
......@@ -115,6 +120,23 @@ static int cx82310_cmd(struct usbnet *dev, enum cx82310_cmd cmd, bool reply,
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 +148,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 +175,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,
......@@ -168,12 +200,8 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf)
}
/* 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);
if (cx82310_enable_ethernet(dev))
goto err;
}
/* get the MAC address */
ret = cx82310_cmd(dev, CMD_GET_MAC_ADDR, true, NULL, 0,
......@@ -190,13 +218,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 +245,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,7 +280,10 @@ static int cx82310_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
break;
}
if (len > CX82310_MTU) {
if (len == 0xffff) {
netdev_info(dev->net, "router was rebooted, re-enabling ethernet mode");
schedule_work(&priv->reenable_work);
} else if (len > CX82310_MTU) {
dev_err(&dev->udev->dev, "RX packet too long: %d B\n",
len);
return 0;
......
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