aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/usb/qmi_wwan.c
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2013-04-18 08:57:09 -0400
committerDavid S. Miller <davem@davemloft.net>2013-04-19 17:51:16 -0400
commit6ff509af3869ccac69dcf8905fc75b9a76951594 (patch)
tree08b81ac490a41e8ff452190daedc9c7eddc68651 /drivers/net/usb/qmi_wwan.c
parent0cb670eef554652d0aa40bf09bd850e62598fc6c (diff)
net: qmi_wwan: fixup missing ethernet header (firmware bug workaround)
A number of LTE devices from different vendors all suffer from the same firmware bug: Most of the packets received from the device while it is attached to a LTE network will not have an ethernet header. The devices work as expected when attached to 2G or 3G networks, sending an ethernet header with all packets. This driver is not aware of which network the modem attached to, and even if it were there are still some packet types which are always received with the header intact. All devices supported by this driver have severely limited networking capabilities: - can only transmit IPv4, IPv6 and possibly ARP - can only support a single host hardware address at any time - will only do point-to-point communcation with the host Because of this, we are able to reliably identify any bogus raw IP packets by simply looking at the 4 IP version bits. All we need to do is to avoid 4 or 6 in the first digit of the mac address. This workaround ensures this, and fix up the received packets as necessary. Given the distribution of the bug, it is believed that the source is the chipset vendor. The devices which are verified to be affected are: Huawei E392u-12 (Qualcomm MDM9200) Pantech UML290 (Qualcomm MDM9600) Novatel USB551L (Qualcomm MDM9600) Novatel E362 (Qualcomm MDM9600) It is believed that the bug depend on firmware revision, which means that possibly all devices based on the above mentioned chipset may be affected if we consider all available firmware revisions. The information about affected devices and versions is likely incomplete. As the additional overhead for packets not needing this fixup is very small, it is considered acceptable to apply the workaround to all devices handled by this driver. Reported-by: Dan Williams <dcbw@redhat.com> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb/qmi_wwan.c')
-rw-r--r--drivers/net/usb/qmi_wwan.c84
1 files changed, 84 insertions, 0 deletions
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 968d5d50751d..d8a50c781af0 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -13,6 +13,7 @@
13#include <linux/module.h> 13#include <linux/module.h>
14#include <linux/netdevice.h> 14#include <linux/netdevice.h>
15#include <linux/ethtool.h> 15#include <linux/ethtool.h>
16#include <linux/etherdevice.h>
16#include <linux/mii.h> 17#include <linux/mii.h>
17#include <linux/usb.h> 18#include <linux/usb.h>
18#include <linux/usb/cdc.h> 19#include <linux/usb/cdc.h>
@@ -52,6 +53,82 @@ struct qmi_wwan_state {
52 struct usb_interface *data; 53 struct usb_interface *data;
53}; 54};
54 55
56/* Make up an ethernet header if the packet doesn't have one.
57 *
58 * A firmware bug common among several devices cause them to send raw
59 * IP packets under some circumstances. There is no way for the
60 * driver/host to know when this will happen. And even when the bug
61 * hits, some packets will still arrive with an intact header.
62 *
63 * The supported devices are only capably of sending IPv4, IPv6 and
64 * ARP packets on a point-to-point link. Any packet with an ethernet
65 * header will have either our address or a broadcast/multicast
66 * address as destination. ARP packets will always have a header.
67 *
68 * This means that this function will reliably add the appropriate
69 * header iff necessary, provided our hardware address does not start
70 * with 4 or 6.
71 */
72static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
73{
74 __be16 proto;
75
76 /* usbnet rx_complete guarantees that skb->len is at least
77 * hard_header_len, so we can inspect the dest address without
78 * checking skb->len
79 */
80 switch (skb->data[0] & 0xf0) {
81 case 0x40:
82 proto = htons(ETH_P_IP);
83 break;
84 case 0x60:
85 proto = htons(ETH_P_IPV6);
86 break;
87 default:
88 /* pass along other packets without modifications */
89 return 1;
90 }
91 if (skb_headroom(skb) < ETH_HLEN)
92 return 0;
93 skb_push(skb, ETH_HLEN);
94 skb_reset_mac_header(skb);
95 eth_hdr(skb)->h_proto = proto;
96 memset(eth_hdr(skb)->h_source, 0, ETH_ALEN);
97 memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN);
98 return 1;
99}
100
101/* very simplistic detection of IPv4 or IPv6 headers */
102static bool possibly_iphdr(const char *data)
103{
104 return (data[0] & 0xd0) == 0x40;
105}
106
107/* disallow addresses which may be confused with IP headers */
108static int qmi_wwan_mac_addr(struct net_device *dev, void *p)
109{
110 int ret;
111 struct sockaddr *addr = p;
112
113 ret = eth_prepare_mac_addr_change(dev, p);
114 if (ret < 0)
115 return ret;
116 if (possibly_iphdr(addr->sa_data))
117 return -EADDRNOTAVAIL;
118 eth_commit_mac_addr_change(dev, p);
119 return 0;
120}
121
122static const struct net_device_ops qmi_wwan_netdev_ops = {
123 .ndo_open = usbnet_open,
124 .ndo_stop = usbnet_stop,
125 .ndo_start_xmit = usbnet_start_xmit,
126 .ndo_tx_timeout = usbnet_tx_timeout,
127 .ndo_change_mtu = usbnet_change_mtu,
128 .ndo_set_mac_address = qmi_wwan_mac_addr,
129 .ndo_validate_addr = eth_validate_addr,
130};
131
55/* using a counter to merge subdriver requests with our own into a combined state */ 132/* using a counter to merge subdriver requests with our own into a combined state */
56static int qmi_wwan_manage_power(struct usbnet *dev, int on) 133static int qmi_wwan_manage_power(struct usbnet *dev, int on)
57{ 134{
@@ -229,6 +306,12 @@ next_desc:
229 usb_driver_release_interface(driver, info->data); 306 usb_driver_release_interface(driver, info->data);
230 } 307 }
231 308
309 /* make MAC addr easily distinguishable from an IP header */
310 if (possibly_iphdr(dev->net->dev_addr)) {
311 dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */
312 dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */
313 }
314 dev->net->netdev_ops = &qmi_wwan_netdev_ops;
232err: 315err:
233 return status; 316 return status;
234} 317}
@@ -307,6 +390,7 @@ static const struct driver_info qmi_wwan_info = {
307 .bind = qmi_wwan_bind, 390 .bind = qmi_wwan_bind,
308 .unbind = qmi_wwan_unbind, 391 .unbind = qmi_wwan_unbind,
309 .manage_power = qmi_wwan_manage_power, 392 .manage_power = qmi_wwan_manage_power,
393 .rx_fixup = qmi_wwan_rx_fixup,
310}; 394};
311 395
312#define HUAWEI_VENDOR_ID 0x12D1 396#define HUAWEI_VENDOR_ID 0x12D1