1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Ethernet interface part of the LG VL600 LTE modem (4G dongle)
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2011 Intel Corporation
6*4882a593Smuzhiyun * Author: Andrzej Zaborowski <balrogg@gmail.com>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun #include <linux/etherdevice.h>
9*4882a593Smuzhiyun #include <linux/ethtool.h>
10*4882a593Smuzhiyun #include <linux/mii.h>
11*4882a593Smuzhiyun #include <linux/usb.h>
12*4882a593Smuzhiyun #include <linux/usb/cdc.h>
13*4882a593Smuzhiyun #include <linux/usb/usbnet.h>
14*4882a593Smuzhiyun #include <linux/if_ether.h>
15*4882a593Smuzhiyun #include <linux/if_arp.h>
16*4882a593Smuzhiyun #include <linux/inetdevice.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun /*
20*4882a593Smuzhiyun * The device has a CDC ACM port for modem control (it claims to be
21*4882a593Smuzhiyun * CDC ACM anyway) and a CDC Ethernet port for actual network data.
22*4882a593Smuzhiyun * It will however ignore data on both ports that is not encapsulated
23*4882a593Smuzhiyun * in a specific way, any data returned is also encapsulated the same
24*4882a593Smuzhiyun * way. The headers don't seem to follow any popular standard.
25*4882a593Smuzhiyun *
26*4882a593Smuzhiyun * This driver adds and strips these headers from the ethernet frames
27*4882a593Smuzhiyun * sent/received from the CDC Ethernet port. The proprietary header
28*4882a593Smuzhiyun * replaces the standard ethernet header in a packet so only actual
29*4882a593Smuzhiyun * ethernet frames are allowed. The headers allow some form of
30*4882a593Smuzhiyun * multiplexing by using non standard values of the .h_proto field.
31*4882a593Smuzhiyun * Windows/Mac drivers do send a couple of such frames to the device
32*4882a593Smuzhiyun * during initialisation, with protocol set to 0x0906 or 0x0b06 and (what
33*4882a593Smuzhiyun * seems to be) a flag in the .dummy_flags. This doesn't seem necessary
34*4882a593Smuzhiyun * for modem operation but can possibly be used for GPS or other funcitons.
35*4882a593Smuzhiyun */
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun struct vl600_frame_hdr {
38*4882a593Smuzhiyun __le32 len;
39*4882a593Smuzhiyun __le32 serial;
40*4882a593Smuzhiyun __le32 pkt_cnt;
41*4882a593Smuzhiyun __le32 dummy_flags;
42*4882a593Smuzhiyun __le32 dummy;
43*4882a593Smuzhiyun __le32 magic;
44*4882a593Smuzhiyun } __attribute__((packed));
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun struct vl600_pkt_hdr {
47*4882a593Smuzhiyun __le32 dummy[2];
48*4882a593Smuzhiyun __le32 len;
49*4882a593Smuzhiyun __be16 h_proto;
50*4882a593Smuzhiyun } __attribute__((packed));
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun struct vl600_state {
53*4882a593Smuzhiyun struct sk_buff *current_rx_buf;
54*4882a593Smuzhiyun };
55*4882a593Smuzhiyun
vl600_bind(struct usbnet * dev,struct usb_interface * intf)56*4882a593Smuzhiyun static int vl600_bind(struct usbnet *dev, struct usb_interface *intf)
57*4882a593Smuzhiyun {
58*4882a593Smuzhiyun int ret;
59*4882a593Smuzhiyun struct vl600_state *s = kzalloc(sizeof(struct vl600_state), GFP_KERNEL);
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun if (!s)
62*4882a593Smuzhiyun return -ENOMEM;
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun ret = usbnet_cdc_bind(dev, intf);
65*4882a593Smuzhiyun if (ret) {
66*4882a593Smuzhiyun kfree(s);
67*4882a593Smuzhiyun return ret;
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun dev->driver_priv = s;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun /* ARP packets don't go through, but they're also of no use. The
73*4882a593Smuzhiyun * subnet has only two hosts anyway: us and the gateway / DHCP
74*4882a593Smuzhiyun * server (probably simulated by modem firmware or network operator)
75*4882a593Smuzhiyun * whose address changes everytime we connect to the intarwebz and
76*4882a593Smuzhiyun * who doesn't bother answering ARP requests either. So hardware
77*4882a593Smuzhiyun * addresses have no meaning, the destination and the source of every
78*4882a593Smuzhiyun * packet depend only on whether it is on the IN or OUT endpoint. */
79*4882a593Smuzhiyun dev->net->flags |= IFF_NOARP;
80*4882a593Smuzhiyun /* IPv6 NDP relies on multicast. Enable it by default. */
81*4882a593Smuzhiyun dev->net->flags |= IFF_MULTICAST;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun return ret;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun
vl600_unbind(struct usbnet * dev,struct usb_interface * intf)86*4882a593Smuzhiyun static void vl600_unbind(struct usbnet *dev, struct usb_interface *intf)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun struct vl600_state *s = dev->driver_priv;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun dev_kfree_skb(s->current_rx_buf);
91*4882a593Smuzhiyun kfree(s);
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun return usbnet_cdc_unbind(dev, intf);
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun
vl600_rx_fixup(struct usbnet * dev,struct sk_buff * skb)96*4882a593Smuzhiyun static int vl600_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun struct vl600_frame_hdr *frame;
99*4882a593Smuzhiyun struct vl600_pkt_hdr *packet;
100*4882a593Smuzhiyun struct ethhdr *ethhdr;
101*4882a593Smuzhiyun int packet_len, count;
102*4882a593Smuzhiyun struct sk_buff *buf = skb;
103*4882a593Smuzhiyun struct sk_buff *clone;
104*4882a593Smuzhiyun struct vl600_state *s = dev->driver_priv;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun /* Frame lengths are generally 4B multiplies but every couple of
107*4882a593Smuzhiyun * hours there's an odd number of bytes sized yet correct frame,
108*4882a593Smuzhiyun * so don't require this. */
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun /* Allow a packet (or multiple packets batched together) to be
111*4882a593Smuzhiyun * split across many frames. We don't allow a new batch to
112*4882a593Smuzhiyun * begin in the same frame another one is ending however, and no
113*4882a593Smuzhiyun * leading or trailing pad bytes. */
114*4882a593Smuzhiyun if (s->current_rx_buf) {
115*4882a593Smuzhiyun frame = (struct vl600_frame_hdr *) s->current_rx_buf->data;
116*4882a593Smuzhiyun if (skb->len + s->current_rx_buf->len >
117*4882a593Smuzhiyun le32_to_cpup(&frame->len)) {
118*4882a593Smuzhiyun netif_err(dev, ifup, dev->net, "Fragment too long\n");
119*4882a593Smuzhiyun dev->net->stats.rx_length_errors++;
120*4882a593Smuzhiyun goto error;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun buf = s->current_rx_buf;
124*4882a593Smuzhiyun skb_put_data(buf, skb->data, skb->len);
125*4882a593Smuzhiyun } else if (skb->len < 4) {
126*4882a593Smuzhiyun netif_err(dev, ifup, dev->net, "Frame too short\n");
127*4882a593Smuzhiyun dev->net->stats.rx_length_errors++;
128*4882a593Smuzhiyun goto error;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun frame = (struct vl600_frame_hdr *) buf->data;
132*4882a593Smuzhiyun /* Yes, check that frame->magic == 0x53544448 (or 0x44544d48),
133*4882a593Smuzhiyun * otherwise we may run out of memory w/a bad packet */
134*4882a593Smuzhiyun if (ntohl(frame->magic) != 0x53544448 &&
135*4882a593Smuzhiyun ntohl(frame->magic) != 0x44544d48)
136*4882a593Smuzhiyun goto error;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun if (buf->len < sizeof(*frame) ||
139*4882a593Smuzhiyun buf->len != le32_to_cpup(&frame->len)) {
140*4882a593Smuzhiyun /* Save this fragment for later assembly */
141*4882a593Smuzhiyun if (s->current_rx_buf)
142*4882a593Smuzhiyun return 0;
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun s->current_rx_buf = skb_copy_expand(skb, 0,
145*4882a593Smuzhiyun le32_to_cpup(&frame->len), GFP_ATOMIC);
146*4882a593Smuzhiyun if (!s->current_rx_buf)
147*4882a593Smuzhiyun dev->net->stats.rx_errors++;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun return 0;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun count = le32_to_cpup(&frame->pkt_cnt);
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun skb_pull(buf, sizeof(*frame));
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun while (count--) {
157*4882a593Smuzhiyun if (buf->len < sizeof(*packet)) {
158*4882a593Smuzhiyun netif_err(dev, ifup, dev->net, "Packet too short\n");
159*4882a593Smuzhiyun goto error;
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun packet = (struct vl600_pkt_hdr *) buf->data;
163*4882a593Smuzhiyun packet_len = sizeof(*packet) + le32_to_cpup(&packet->len);
164*4882a593Smuzhiyun if (packet_len > buf->len) {
165*4882a593Smuzhiyun netif_err(dev, ifup, dev->net,
166*4882a593Smuzhiyun "Bad packet length stored in header\n");
167*4882a593Smuzhiyun goto error;
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun /* Packet header is same size as the ethernet header
171*4882a593Smuzhiyun * (sizeof(*packet) == sizeof(*ethhdr)), additionally
172*4882a593Smuzhiyun * the h_proto field is in the same place so we just leave it
173*4882a593Smuzhiyun * alone and fill in the remaining fields.
174*4882a593Smuzhiyun */
175*4882a593Smuzhiyun ethhdr = (struct ethhdr *) skb->data;
176*4882a593Smuzhiyun if (be16_to_cpup(ðhdr->h_proto) == ETH_P_ARP &&
177*4882a593Smuzhiyun buf->len > 0x26) {
178*4882a593Smuzhiyun /* Copy the addresses from packet contents */
179*4882a593Smuzhiyun memcpy(ethhdr->h_source,
180*4882a593Smuzhiyun &buf->data[sizeof(*ethhdr) + 0x8],
181*4882a593Smuzhiyun ETH_ALEN);
182*4882a593Smuzhiyun memcpy(ethhdr->h_dest,
183*4882a593Smuzhiyun &buf->data[sizeof(*ethhdr) + 0x12],
184*4882a593Smuzhiyun ETH_ALEN);
185*4882a593Smuzhiyun } else {
186*4882a593Smuzhiyun eth_zero_addr(ethhdr->h_source);
187*4882a593Smuzhiyun memcpy(ethhdr->h_dest, dev->net->dev_addr, ETH_ALEN);
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun /* Inbound IPv6 packets have an IPv4 ethertype (0x800)
190*4882a593Smuzhiyun * for some reason. Peek at the L3 header to check
191*4882a593Smuzhiyun * for IPv6 packets, and set the ethertype to IPv6
192*4882a593Smuzhiyun * (0x86dd) so Linux can understand it.
193*4882a593Smuzhiyun */
194*4882a593Smuzhiyun if ((buf->data[sizeof(*ethhdr)] & 0xf0) == 0x60)
195*4882a593Smuzhiyun ethhdr->h_proto = htons(ETH_P_IPV6);
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun if (count) {
199*4882a593Smuzhiyun /* Not the last packet in this batch */
200*4882a593Smuzhiyun clone = skb_clone(buf, GFP_ATOMIC);
201*4882a593Smuzhiyun if (!clone)
202*4882a593Smuzhiyun goto error;
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun skb_trim(clone, packet_len);
205*4882a593Smuzhiyun usbnet_skb_return(dev, clone);
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun skb_pull(buf, (packet_len + 3) & ~3);
208*4882a593Smuzhiyun } else {
209*4882a593Smuzhiyun skb_trim(buf, packet_len);
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun if (s->current_rx_buf) {
212*4882a593Smuzhiyun usbnet_skb_return(dev, buf);
213*4882a593Smuzhiyun s->current_rx_buf = NULL;
214*4882a593Smuzhiyun return 0;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun return 1;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun error:
222*4882a593Smuzhiyun if (s->current_rx_buf) {
223*4882a593Smuzhiyun dev_kfree_skb_any(s->current_rx_buf);
224*4882a593Smuzhiyun s->current_rx_buf = NULL;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun dev->net->stats.rx_errors++;
227*4882a593Smuzhiyun return 0;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun
vl600_tx_fixup(struct usbnet * dev,struct sk_buff * skb,gfp_t flags)230*4882a593Smuzhiyun static struct sk_buff *vl600_tx_fixup(struct usbnet *dev,
231*4882a593Smuzhiyun struct sk_buff *skb, gfp_t flags)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun struct sk_buff *ret;
234*4882a593Smuzhiyun struct vl600_frame_hdr *frame;
235*4882a593Smuzhiyun struct vl600_pkt_hdr *packet;
236*4882a593Smuzhiyun static uint32_t serial = 1;
237*4882a593Smuzhiyun int orig_len = skb->len - sizeof(struct ethhdr);
238*4882a593Smuzhiyun int full_len = (skb->len + sizeof(struct vl600_frame_hdr) + 3) & ~3;
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun frame = (struct vl600_frame_hdr *) skb->data;
241*4882a593Smuzhiyun if (skb->len > sizeof(*frame) && skb->len == le32_to_cpup(&frame->len))
242*4882a593Smuzhiyun return skb; /* Already encapsulated? */
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun if (skb->len < sizeof(struct ethhdr))
245*4882a593Smuzhiyun /* Drop, device can only deal with ethernet packets */
246*4882a593Smuzhiyun return NULL;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun if (!skb_cloned(skb)) {
249*4882a593Smuzhiyun int headroom = skb_headroom(skb);
250*4882a593Smuzhiyun int tailroom = skb_tailroom(skb);
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun if (tailroom >= full_len - skb->len - sizeof(*frame) &&
253*4882a593Smuzhiyun headroom >= sizeof(*frame))
254*4882a593Smuzhiyun /* There's enough head and tail room */
255*4882a593Smuzhiyun goto encapsulate;
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun if (headroom + tailroom + skb->len >= full_len) {
258*4882a593Smuzhiyun /* There's enough total room, just readjust */
259*4882a593Smuzhiyun skb->data = memmove(skb->head + sizeof(*frame),
260*4882a593Smuzhiyun skb->data, skb->len);
261*4882a593Smuzhiyun skb_set_tail_pointer(skb, skb->len);
262*4882a593Smuzhiyun goto encapsulate;
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun /* Alloc a new skb with the required size */
267*4882a593Smuzhiyun ret = skb_copy_expand(skb, sizeof(struct vl600_frame_hdr), full_len -
268*4882a593Smuzhiyun skb->len - sizeof(struct vl600_frame_hdr), flags);
269*4882a593Smuzhiyun dev_kfree_skb_any(skb);
270*4882a593Smuzhiyun if (!ret)
271*4882a593Smuzhiyun return ret;
272*4882a593Smuzhiyun skb = ret;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun encapsulate:
275*4882a593Smuzhiyun /* Packet header is same size as ethernet packet header
276*4882a593Smuzhiyun * (sizeof(*packet) == sizeof(struct ethhdr)), additionally the
277*4882a593Smuzhiyun * h_proto field is in the same place so we just leave it alone and
278*4882a593Smuzhiyun * overwrite the remaining fields.
279*4882a593Smuzhiyun */
280*4882a593Smuzhiyun packet = (struct vl600_pkt_hdr *) skb->data;
281*4882a593Smuzhiyun /* The VL600 wants IPv6 packets to have an IPv4 ethertype
282*4882a593Smuzhiyun * Since this modem only supports IPv4 and IPv6, just set all
283*4882a593Smuzhiyun * frames to 0x0800 (ETH_P_IP)
284*4882a593Smuzhiyun */
285*4882a593Smuzhiyun packet->h_proto = htons(ETH_P_IP);
286*4882a593Smuzhiyun memset(&packet->dummy, 0, sizeof(packet->dummy));
287*4882a593Smuzhiyun packet->len = cpu_to_le32(orig_len);
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun frame = skb_push(skb, sizeof(*frame));
290*4882a593Smuzhiyun memset(frame, 0, sizeof(*frame));
291*4882a593Smuzhiyun frame->len = cpu_to_le32(full_len);
292*4882a593Smuzhiyun frame->serial = cpu_to_le32(serial++);
293*4882a593Smuzhiyun frame->pkt_cnt = cpu_to_le32(1);
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun if (skb->len < full_len) /* Pad */
296*4882a593Smuzhiyun skb_put(skb, full_len - skb->len);
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun return skb;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun static const struct driver_info vl600_info = {
302*4882a593Smuzhiyun .description = "LG VL600 modem",
303*4882a593Smuzhiyun .flags = FLAG_RX_ASSEMBLE | FLAG_WWAN,
304*4882a593Smuzhiyun .bind = vl600_bind,
305*4882a593Smuzhiyun .unbind = vl600_unbind,
306*4882a593Smuzhiyun .status = usbnet_cdc_status,
307*4882a593Smuzhiyun .rx_fixup = vl600_rx_fixup,
308*4882a593Smuzhiyun .tx_fixup = vl600_tx_fixup,
309*4882a593Smuzhiyun };
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun static const struct usb_device_id products[] = {
312*4882a593Smuzhiyun {
313*4882a593Smuzhiyun USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM,
314*4882a593Smuzhiyun USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
315*4882a593Smuzhiyun .driver_info = (unsigned long) &vl600_info,
316*4882a593Smuzhiyun },
317*4882a593Smuzhiyun {}, /* End */
318*4882a593Smuzhiyun };
319*4882a593Smuzhiyun MODULE_DEVICE_TABLE(usb, products);
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun static struct usb_driver lg_vl600_driver = {
322*4882a593Smuzhiyun .name = "lg-vl600",
323*4882a593Smuzhiyun .id_table = products,
324*4882a593Smuzhiyun .probe = usbnet_probe,
325*4882a593Smuzhiyun .disconnect = usbnet_disconnect,
326*4882a593Smuzhiyun .suspend = usbnet_suspend,
327*4882a593Smuzhiyun .resume = usbnet_resume,
328*4882a593Smuzhiyun .disable_hub_initiated_lpm = 1,
329*4882a593Smuzhiyun };
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun module_usb_driver(lg_vl600_driver);
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun MODULE_AUTHOR("Anrzej Zaborowski");
334*4882a593Smuzhiyun MODULE_DESCRIPTION("LG-VL600 modem's ethernet link");
335*4882a593Smuzhiyun MODULE_LICENSE("GPL");
336