Commit d5c83d0d authored by Kristian Evensen's avatar Kristian Evensen Committed by David S. Miller

cdc_ether: Fix handling connection notification

Commit bfe9b9d2 ("cdc_ether: Improve ZTE MF823/831/910 handling")
introduced a work-around in usbnet_cdc_status() for devices that exported
cdc carrier on twice on connect. Before the commit, this behavior caused
the link state to be incorrect. It was assumed that all CDC Ethernet
devices would either export this behavior, or send one off and then one on
notification (which seems to be the default behavior).

Unfortunately, it turns out multiple devices sends a connection
notification multiple times per second (via an interrupt), even when
connection state does not change. This has been observed with several
different USB LAN dongles (at least), for example 13b1:0041 (Linksys).
After bfe9b9d2, the link state has been set as down and then up for
each notification. This has caused a flood of Netlink NEWLINK messages and
syslog to be flooded with messages similar to:

cdc_ether 2-1:2.0 eth1: kevent 12 may have been dropped

This commit fixes the behavior by reverting usbnet_cdc_status() to how it
was before bfe9b9d2. The work-around has been moved to a separate
status-function which is only called when a known, affect device is
detected.

v1->v2:

* Do not open-code netif_carrier_ok() (thanks Henning Schild).
* Call netif_carrier_off() instead of usb_link_change(). This prevents
calling schedule_work() twice without giving the work queue a chance to be
processed (thanks Bjørn Mork).

Fixes: bfe9b9d2 ("cdc_ether: Improve ZTE MF823/831/910 handling")
Reported-by: default avatarHenning Schild <henning.schild@siemens.com>
Signed-off-by: default avatarKristian Evensen <kristian.evensen@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6b6ebb6b
...@@ -388,12 +388,6 @@ void usbnet_cdc_status(struct usbnet *dev, struct urb *urb) ...@@ -388,12 +388,6 @@ void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)
case USB_CDC_NOTIFY_NETWORK_CONNECTION: case USB_CDC_NOTIFY_NETWORK_CONNECTION:
netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n", netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n",
event->wValue ? "on" : "off"); event->wValue ? "on" : "off");
/* Work-around for devices with broken off-notifications */
if (event->wValue &&
!test_bit(__LINK_STATE_NOCARRIER, &dev->net->state))
usbnet_link_change(dev, 0, 0);
usbnet_link_change(dev, !!event->wValue, 0); usbnet_link_change(dev, !!event->wValue, 0);
break; break;
case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */ case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */
...@@ -466,6 +460,36 @@ static int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ...@@ -466,6 +460,36 @@ static int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
return 1; return 1;
} }
/* Ensure correct link state
*
* Some devices (ZTE MF823/831/910) export two carrier on notifications when
* connected. This causes the link state to be incorrect. Work around this by
* always setting the state to off, then on.
*/
void usbnet_cdc_zte_status(struct usbnet *dev, struct urb *urb)
{
struct usb_cdc_notification *event;
if (urb->actual_length < sizeof(*event))
return;
event = urb->transfer_buffer;
if (event->bNotificationType != USB_CDC_NOTIFY_NETWORK_CONNECTION) {
usbnet_cdc_status(dev, urb);
return;
}
netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n",
event->wValue ? "on" : "off");
if (event->wValue &&
netif_carrier_ok(dev->net))
netif_carrier_off(dev->net);
usbnet_link_change(dev, !!event->wValue, 0);
}
static const struct driver_info cdc_info = { static const struct driver_info cdc_info = {
.description = "CDC Ethernet Device", .description = "CDC Ethernet Device",
.flags = FLAG_ETHER | FLAG_POINTTOPOINT, .flags = FLAG_ETHER | FLAG_POINTTOPOINT,
...@@ -481,7 +505,7 @@ static const struct driver_info zte_cdc_info = { ...@@ -481,7 +505,7 @@ static const struct driver_info zte_cdc_info = {
.flags = FLAG_ETHER | FLAG_POINTTOPOINT, .flags = FLAG_ETHER | FLAG_POINTTOPOINT,
.bind = usbnet_cdc_zte_bind, .bind = usbnet_cdc_zte_bind,
.unbind = usbnet_cdc_unbind, .unbind = usbnet_cdc_unbind,
.status = usbnet_cdc_status, .status = usbnet_cdc_zte_status,
.set_rx_mode = usbnet_cdc_update_filter, .set_rx_mode = usbnet_cdc_update_filter,
.manage_power = usbnet_manage_power, .manage_power = usbnet_manage_power,
.rx_fixup = usbnet_cdc_zte_rx_fixup, .rx_fixup = usbnet_cdc_zte_rx_fixup,
......
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