[PATCH net-next] cdc_ether: Improve ZTE MF823/831/910 handling

From: Kristian Evensen
Date: Mon Jul 18 2016 - 08:25:14 EST


The firmware in the ZTE MF823/831/910 modems/mifis use OS fingerprinting to
determine which type of device to export. In addition, these devices export
a REST API which can also be used to control the type of device. So far, on
Linux, the devices have been seen as RNDIS or CDC Ether.

When CDC Ether is used, devices of the same type are, as with RNDIS,
exported with the same, bogus random MAC address. In addition, the devices
(at least on all firmware revisions I have found) use this MAC when sending
traffic routed from external networks. And as a final feature, the devices
sometimes export the link state incorrectly.

This patch tries to improve the handling of these devices by doing the
following:

* If a random MAC is read from device, then generate a new random MAC
address. This fix will apply to all devices using cdc_ether, but that
should be ok as we will also fix similar mistakes from other
manufacturers.

* The MF823/MF832/MF910 sometimes export cdc carrier on twice on connect
(the correct behavior is off then on). Work around this by manually setting
carrier to off if an on-notification is received and the NOCARRIER-bit is
not set.

This change will also affect all devices, but as with the MAC-fix it should
take care of similar mistakes. I tried to think of/look/test for
problems/regressions that could be introduced by this behavior, but could
not find any. However, my familiarity with this code path is not that
great, so there could be something I have overlooked.

* Add an rx_fixup-function (and a new driver info-struct) which is used by
these three devices (identified either as PID 0x1405 or 0x1408). The
rx_fixup-function replaces the destination MAC address in the skb with that
of the device. I have not seen a revision of these three device that
behaves correctly (i.e., sets the right destination MAC), so I chose not to
do any comparison with for example the known, bogus addresses.

I have tested this patch with multiple revisions of all three devices, and
they behave as expected. In other words, they all got a valid, random MAC,
the correct operational state and I can receive/sent traffic without
problems. I also tested with some other cdc_ether devices I have and did
not find any problems/regressions caused by the two general changes.

Signed-off-by: Kristian Evensen <kristian.evensen@xxxxxxxxx>
---
drivers/net/usb/cdc_ether.c | 53 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)

diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 7cba2c3..2325097 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -388,6 +388,12 @@ void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n",
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);
break;
case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */
@@ -428,10 +434,34 @@ int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
return status;
}

+ if (dev->net->dev_addr[0] & 0x02)
+ eth_hw_addr_random(dev->net);
+
return 0;
}
EXPORT_SYMBOL_GPL(usbnet_cdc_bind);

+/* Make sure packets have correct destination MAC address
+ *
+ * A firmware bug observed on some devices (ZTE MF823/831/910) is that the
+ * device sends packets with a static, bogus, random MAC address (event if
+ * device MAC address has been updated). Always set MAC address to that of the
+ * device.
+ */
+static int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+ u8 num_buggy_hwaddrs;
+ u8 buggy_hwaddrs_idx = 0;
+
+ if (skb->len < ETH_HLEN || !(skb->data[0] & 0x02))
+ return 1;
+
+ skb_reset_mac_header(skb);
+ ether_addr_copy(eth_hdr(skb)->h_dest, dev->net->dev_addr);
+
+ return 1;
+}
+
static const struct driver_info cdc_info = {
.description = "CDC Ethernet Device",
.flags = FLAG_ETHER | FLAG_POINTTOPOINT,
@@ -442,6 +472,17 @@ static const struct driver_info cdc_info = {
.manage_power = usbnet_manage_power,
};

+static const struct driver_info zte_cdc_info = {
+ .description = "ZTE MF823/831/910 CDC Ethernet Device",
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT,
+ .bind = usbnet_cdc_bind,
+ .unbind = usbnet_cdc_unbind,
+ .status = usbnet_cdc_status,
+ .set_rx_mode = usbnet_cdc_update_filter,
+ .manage_power = usbnet_manage_power,
+ .rx_fixup = usbnet_cdc_zte_rx_fixup,
+};
+
static const struct driver_info wwan_info = {
.description = "Mobile Broadband Network Device",
.flags = FLAG_WWAN,
@@ -697,6 +738,18 @@ static const struct usb_device_id products[] = {
USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&wwan_info,
}, {
+ /* ZTE MF823/831/910 */
+ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1405, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&zte_cdc_info,
+}, {
+ /* ZTE MF823/831/910 */
+ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1408, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&zte_cdc_info,
+}, {
/* Telit modules */
USB_VENDOR_AND_INTERFACE_INFO(0x1bc7, USB_CLASS_COMM,
USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
--
2.5.0