|
13 | 13 | #include <linux/module.h>
|
14 | 14 | #include <linux/netdevice.h>
|
15 | 15 | #include <linux/ethtool.h>
|
| 16 | +#include <linux/etherdevice.h> |
16 | 17 | #include <linux/mii.h>
|
17 | 18 | #include <linux/usb.h>
|
18 | 19 | #include <linux/usb/cdc.h>
|
@@ -52,6 +53,96 @@ struct qmi_wwan_state {
|
52 | 53 | struct usb_interface *data;
|
53 | 54 | };
|
54 | 55 |
|
| 56 | +/* default ethernet address used by the modem */ |
| 57 | +static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3}; |
| 58 | + |
| 59 | +/* Make up an ethernet header if the packet doesn't have one. |
| 60 | + * |
| 61 | + * A firmware bug common among several devices cause them to send raw |
| 62 | + * IP packets under some circumstances. There is no way for the |
| 63 | + * driver/host to know when this will happen. And even when the bug |
| 64 | + * hits, some packets will still arrive with an intact header. |
| 65 | + * |
| 66 | + * The supported devices are only capably of sending IPv4, IPv6 and |
| 67 | + * ARP packets on a point-to-point link. Any packet with an ethernet |
| 68 | + * header will have either our address or a broadcast/multicast |
| 69 | + * address as destination. ARP packets will always have a header. |
| 70 | + * |
| 71 | + * This means that this function will reliably add the appropriate |
| 72 | + * header iff necessary, provided our hardware address does not start |
| 73 | + * with 4 or 6. |
| 74 | + * |
| 75 | + * Another common firmware bug results in all packets being addressed |
| 76 | + * to 00:a0:c6:00:00:00 despite the host address being different. |
| 77 | + * This function will also fixup such packets. |
| 78 | + */ |
| 79 | +static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb) |
| 80 | +{ |
| 81 | + __be16 proto; |
| 82 | + |
| 83 | + /* usbnet rx_complete guarantees that skb->len is at least |
| 84 | + * hard_header_len, so we can inspect the dest address without |
| 85 | + * checking skb->len |
| 86 | + */ |
| 87 | + switch (skb->data[0] & 0xf0) { |
| 88 | + case 0x40: |
| 89 | + proto = htons(ETH_P_IP); |
| 90 | + break; |
| 91 | + case 0x60: |
| 92 | + proto = htons(ETH_P_IPV6); |
| 93 | + break; |
| 94 | + case 0x00: |
| 95 | + if (is_multicast_ether_addr(skb->data)) |
| 96 | + return 1; |
| 97 | + /* possibly bogus destination - rewrite just in case */ |
| 98 | + skb_reset_mac_header(skb); |
| 99 | + goto fix_dest; |
| 100 | + default: |
| 101 | + /* pass along other packets without modifications */ |
| 102 | + return 1; |
| 103 | + } |
| 104 | + if (skb_headroom(skb) < ETH_HLEN) |
| 105 | + return 0; |
| 106 | + skb_push(skb, ETH_HLEN); |
| 107 | + skb_reset_mac_header(skb); |
| 108 | + eth_hdr(skb)->h_proto = proto; |
| 109 | + memset(eth_hdr(skb)->h_source, 0, ETH_ALEN); |
| 110 | +fix_dest: |
| 111 | + memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN); |
| 112 | + return 1; |
| 113 | +} |
| 114 | + |
| 115 | +/* very simplistic detection of IPv4 or IPv6 headers */ |
| 116 | +static bool possibly_iphdr(const char *data) |
| 117 | +{ |
| 118 | + return (data[0] & 0xd0) == 0x40; |
| 119 | +} |
| 120 | + |
| 121 | +/* disallow addresses which may be confused with IP headers */ |
| 122 | +static int qmi_wwan_mac_addr(struct net_device *dev, void *p) |
| 123 | +{ |
| 124 | + int ret; |
| 125 | + struct sockaddr *addr = p; |
| 126 | + |
| 127 | + ret = eth_prepare_mac_addr_change(dev, p); |
| 128 | + if (ret < 0) |
| 129 | + return ret; |
| 130 | + if (possibly_iphdr(addr->sa_data)) |
| 131 | + return -EADDRNOTAVAIL; |
| 132 | + eth_commit_mac_addr_change(dev, p); |
| 133 | + return 0; |
| 134 | +} |
| 135 | + |
| 136 | +static const struct net_device_ops qmi_wwan_netdev_ops = { |
| 137 | + .ndo_open = usbnet_open, |
| 138 | + .ndo_stop = usbnet_stop, |
| 139 | + .ndo_start_xmit = usbnet_start_xmit, |
| 140 | + .ndo_tx_timeout = usbnet_tx_timeout, |
| 141 | + .ndo_change_mtu = usbnet_change_mtu, |
| 142 | + .ndo_set_mac_address = qmi_wwan_mac_addr, |
| 143 | + .ndo_validate_addr = eth_validate_addr, |
| 144 | +}; |
| 145 | + |
55 | 146 | /* using a counter to merge subdriver requests with our own into a combined state */
|
56 | 147 | static int qmi_wwan_manage_power(struct usbnet *dev, int on)
|
57 | 148 | {
|
@@ -229,6 +320,18 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
|
229 | 320 | usb_driver_release_interface(driver, info->data);
|
230 | 321 | }
|
231 | 322 |
|
| 323 | + /* Never use the same address on both ends of the link, even |
| 324 | + * if the buggy firmware told us to. |
| 325 | + */ |
| 326 | + if (!compare_ether_addr(dev->net->dev_addr, default_modem_addr)) |
| 327 | + eth_hw_addr_random(dev->net); |
| 328 | + |
| 329 | + /* make MAC addr easily distinguishable from an IP header */ |
| 330 | + if (possibly_iphdr(dev->net->dev_addr)) { |
| 331 | + dev->net->dev_addr[0] |= 0x02; /* set local assignment bit */ |
| 332 | + dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */ |
| 333 | + } |
| 334 | + dev->net->netdev_ops = &qmi_wwan_netdev_ops; |
232 | 335 | err:
|
233 | 336 | return status;
|
234 | 337 | }
|
@@ -307,6 +410,7 @@ static const struct driver_info qmi_wwan_info = {
|
307 | 410 | .bind = qmi_wwan_bind,
|
308 | 411 | .unbind = qmi_wwan_unbind,
|
309 | 412 | .manage_power = qmi_wwan_manage_power,
|
| 413 | + .rx_fixup = qmi_wwan_rx_fixup, |
310 | 414 | };
|
311 | 415 |
|
312 | 416 | #define HUAWEI_VENDOR_ID 0x12D1
|
|
0 commit comments