2020-10-10 23:18:34

by Ondrej Zary

[permalink] [raw]
Subject: [PATCH 1/2] 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: Ondrej Zary <[email protected]>
---
drivers/net/usb/cx82310_eth.c | 50 ++++++++++++++++++++++++++++++-----
1 file changed, 44 insertions(+), 6 deletions(-)

diff --git a/drivers/net/usb/cx82310_eth.c b/drivers/net/usb/cx82310_eth.c
index 32b08b18e120..043679311399 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)
@@ -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;
--
Ondrej Zary


2020-10-11 11:19:55

by Ondrej Zary

[permalink] [raw]
Subject: [PATCH 2/2] cx82310_eth: use netdev_err instead of dev_err

Use netdev_err for better device identification in syslog.

Signed-off-by: Ondrej Zary <[email protected]>
---
drivers/net/usb/cx82310_eth.c | 28 ++++++++++++----------------
1 file changed, 12 insertions(+), 16 deletions(-)

diff --git a/drivers/net/usb/cx82310_eth.c b/drivers/net/usb/cx82310_eth.c
index 043679311399..ca89d8258dd3 100644
--- a/drivers/net/usb/cx82310_eth.c
+++ b/drivers/net/usb/cx82310_eth.c
@@ -71,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;
}

@@ -84,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;
}
@@ -194,7 +191,7 @@ 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;
}
@@ -207,7 +204,7 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf)
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;
}

@@ -284,8 +281,7 @@ static int cx82310_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
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);
+ netdev_err(dev->net, "RX packet too long: %d B\n", len);
return 0;
}

--
Ondrej Zary

2020-10-12 02:21:11

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH 1/2] cx82310_eth: re-enable ethernet mode after router reboot

On Sat, 10 Oct 2020 16:00:46 +0200 Ondrej Zary wrote:
> 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.

Patch looks good, but could you explain what's a reboot without a power
cycle in this case? The modem gets reset but USB subsystem doesn't know
it and doesn't go though a unbind() + bind() cycle?

2020-10-12 10:46:24

by Ondrej Zary

[permalink] [raw]
Subject: Re: [PATCH 1/2] cx82310_eth: re-enable ethernet mode after router reboot

On Monday 12 October 2020, Jakub Kicinski wrote:
> On Sat, 10 Oct 2020 16:00:46 +0200 Ondrej Zary wrote:
> > 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.
>
> Patch looks good, but could you explain what's a reboot without a power
> cycle in this case? The modem gets reset but USB subsystem doesn't know
> it and doesn't go though a unbind() + bind() cycle?

The router can be rebooted through the web interface. The reboot does not
disconnect the USB device - it remains connected as if nothing happened. Only
wrong data starts to come in.

--
Ondrej Zary

2020-10-13 08:03:56

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH 1/2] cx82310_eth: re-enable ethernet mode after router reboot

On Mon, 12 Oct 2020 12:42:55 +0200 Ondrej Zary wrote:
> On Monday 12 October 2020, Jakub Kicinski wrote:
> > On Sat, 10 Oct 2020 16:00:46 +0200 Ondrej Zary wrote:
> > > 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.
> >
> > Patch looks good, but could you explain what's a reboot without a power
> > cycle in this case? The modem gets reset but USB subsystem doesn't know
> > it and doesn't go though a unbind() + bind() cycle?
>
> The router can be rebooted through the web interface. The reboot does not
> disconnect the USB device - it remains connected as if nothing happened. Only
> wrong data starts to come in.

I see. Applied to net-next, thanks!