Skip to content

Commit

Permalink
cx82310_eth: re-enable ethernet mode after router reboot
Browse files Browse the repository at this point in the history
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: Ondrej Zary <[email protected]>
Signed-off-by: Jakub Kicinski <[email protected]>
  • Loading branch information
Ondrej Zary authored and kuba-moo committed Oct 12, 2020
1 parent bc081a6 commit ca139d7
Showing 1 changed file with 44 additions and 6 deletions.
50 changes: 44 additions & 6 deletions drivers/net/usb/cx82310_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 */
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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);
}

/*
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit ca139d7

Please sign in to comment.