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 { ...@@ -40,6 +40,11 @@ enum cx82310_status {
#define CX82310_MTU 1514 #define CX82310_MTU 1514
#define CMD_EP 0x01 #define CMD_EP 0x01
struct cx82310_priv {
struct work_struct reenable_work;
struct usbnet *dev;
};
/* /*
* execute control command * execute control command
* - optionally send some data (command parameters) * - optionally send some data (command parameters)
...@@ -115,6 +120,23 @@ static int cx82310_cmd(struct usbnet *dev, enum cx82310_cmd cmd, bool reply, ...@@ -115,6 +120,23 @@ static int cx82310_cmd(struct usbnet *dev, enum cx82310_cmd cmd, bool reply,
return ret; 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_len data[0] /* length of partial packet data */
#define partial_rem data[1] /* remaining (missing) data length */ #define partial_rem data[1] /* remaining (missing) data length */
#define partial_data data[2] /* partial packet data */ #define partial_data data[2] /* partial packet data */
...@@ -126,6 +148,7 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf) ...@@ -126,6 +148,7 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf)
struct usb_device *udev = dev->udev; struct usb_device *udev = dev->udev;
u8 link[3]; u8 link[3];
int timeout = 50; int timeout = 50;
struct cx82310_priv *priv;
/* avoid ADSL modems - continue only if iProduct is "USB NET CARD" */ /* avoid ADSL modems - continue only if iProduct is "USB NET CARD" */
if (usb_string(udev, udev->descriptor.iProduct, buf, sizeof(buf)) > 0 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) ...@@ -152,6 +175,15 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf)
if (!dev->partial_data) if (!dev->partial_data)
return -ENOMEM; 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) */ /* wait for firmware to become ready (indicated by the link being up) */
while (--timeout) { while (--timeout) {
ret = cx82310_cmd(dev, CMD_GET_LINK_STATUS, true, NULL, 0, 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) ...@@ -168,12 +200,8 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf)
} }
/* enable ethernet mode (?) */ /* enable ethernet mode (?) */
ret = cx82310_cmd(dev, CMD_ETHERNET_MODE, true, "\x01", 1, NULL, 0); if (cx82310_enable_ethernet(dev))
if (ret) {
dev_err(&udev->dev, "unable to enable ethernet mode: %d\n",
ret);
goto err; goto err;
}
/* get the MAC address */ /* get the MAC address */
ret = cx82310_cmd(dev, CMD_GET_MAC_ADDR, true, NULL, 0, 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) ...@@ -190,13 +218,19 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf)
return 0; return 0;
err: err:
kfree(dev->driver_priv);
err_partial:
kfree((void *)dev->partial_data); kfree((void *)dev->partial_data);
return ret; return ret;
} }
static void cx82310_unbind(struct usbnet *dev, struct usb_interface *intf) static void cx82310_unbind(struct usbnet *dev, struct usb_interface *intf)
{ {
struct cx82310_priv *priv = dev->driver_priv;
kfree((void *)dev->partial_data); 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) ...@@ -211,6 +245,7 @@ static int cx82310_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{ {
int len; int len;
struct sk_buff *skb2; struct sk_buff *skb2;
struct cx82310_priv *priv = dev->driver_priv;
/* /*
* If the last skb ended with an incomplete packet, this skb contains * 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) ...@@ -245,7 +280,10 @@ static int cx82310_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
break; 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", dev_err(&dev->udev->dev, "RX packet too long: %d B\n",
len); len);
return 0; 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