1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * USB CDC EEM network interface driver |
4 | * Copyright (C) 2009 Oberthur Technologies |
5 | * by Omar Laazimani, Olivier Condemine |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/netdevice.h> |
10 | #include <linux/etherdevice.h> |
11 | #include <linux/ctype.h> |
12 | #include <linux/ethtool.h> |
13 | #include <linux/workqueue.h> |
14 | #include <linux/mii.h> |
15 | #include <linux/usb.h> |
16 | #include <linux/crc32.h> |
17 | #include <linux/usb/cdc.h> |
18 | #include <linux/usb/usbnet.h> |
19 | #include <linux/gfp.h> |
20 | #include <linux/if_vlan.h> |
21 | |
22 | |
23 | /* |
24 | * This driver is an implementation of the CDC "Ethernet Emulation |
25 | * Model" (EEM) specification, which encapsulates Ethernet frames |
26 | * for transport over USB using a simpler USB device model than the |
27 | * previous CDC "Ethernet Control Model" (ECM, or "CDC Ethernet"). |
28 | * |
29 | * For details, see https://usb.org/sites/default/files/CDC_EEM10.pdf |
30 | * |
31 | * This version has been tested with GIGAntIC WuaoW SIM Smart Card on 2.6.24, |
32 | * 2.6.27 and 2.6.30rc2 kernel. |
33 | * It has also been validated on Openmoko Om 2008.12 (based on 2.6.24 kernel). |
34 | * build on 23-April-2009 |
35 | */ |
36 | |
37 | #define EEM_HEAD 2 /* 2 byte header */ |
38 | |
39 | /*-------------------------------------------------------------------------*/ |
40 | |
41 | static void eem_linkcmd_complete(struct urb *urb) |
42 | { |
43 | dev_kfree_skb(urb->context); |
44 | usb_free_urb(urb); |
45 | } |
46 | |
47 | static void eem_linkcmd(struct usbnet *dev, struct sk_buff *skb) |
48 | { |
49 | struct urb *urb; |
50 | int status; |
51 | |
52 | urb = usb_alloc_urb(iso_packets: 0, GFP_ATOMIC); |
53 | if (!urb) |
54 | goto fail; |
55 | |
56 | usb_fill_bulk_urb(urb, dev: dev->udev, pipe: dev->out, |
57 | transfer_buffer: skb->data, buffer_length: skb->len, complete_fn: eem_linkcmd_complete, context: skb); |
58 | |
59 | status = usb_submit_urb(urb, GFP_ATOMIC); |
60 | if (status) { |
61 | usb_free_urb(urb); |
62 | fail: |
63 | dev_kfree_skb(skb); |
64 | netdev_warn(dev: dev->net, format: "link cmd failure\n" ); |
65 | return; |
66 | } |
67 | } |
68 | |
69 | static int eem_bind(struct usbnet *dev, struct usb_interface *intf) |
70 | { |
71 | int status = 0; |
72 | |
73 | status = usbnet_get_endpoints(dev, intf); |
74 | if (status < 0) |
75 | return status; |
76 | |
77 | /* no jumbogram (16K) support for now */ |
78 | |
79 | dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN + VLAN_HLEN; |
80 | dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; |
81 | |
82 | return 0; |
83 | } |
84 | |
85 | /* |
86 | * EEM permits packing multiple Ethernet frames into USB transfers |
87 | * (a "bundle"), but for TX we don't try to do that. |
88 | */ |
89 | static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb, |
90 | gfp_t flags) |
91 | { |
92 | struct sk_buff *skb2 = NULL; |
93 | u16 len = skb->len; |
94 | u32 crc = 0; |
95 | int padlen = 0; |
96 | |
97 | /* When ((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket) is |
98 | * zero, stick two bytes of zero length EEM packet on the end. |
99 | * Else the framework would add invalid single byte padding, |
100 | * since it can't know whether ZLPs will be handled right by |
101 | * all the relevant hardware and software. |
102 | */ |
103 | if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket)) |
104 | padlen += 2; |
105 | |
106 | if (!skb_cloned(skb)) { |
107 | int headroom = skb_headroom(skb); |
108 | int tailroom = skb_tailroom(skb); |
109 | |
110 | if ((tailroom >= ETH_FCS_LEN + padlen) && |
111 | (headroom >= EEM_HEAD)) |
112 | goto done; |
113 | |
114 | if ((headroom + tailroom) |
115 | > (EEM_HEAD + ETH_FCS_LEN + padlen)) { |
116 | skb->data = memmove(skb->head + |
117 | EEM_HEAD, |
118 | skb->data, |
119 | skb->len); |
120 | skb_set_tail_pointer(skb, offset: len); |
121 | goto done; |
122 | } |
123 | } |
124 | |
125 | skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, priority: flags); |
126 | dev_kfree_skb_any(skb); |
127 | if (!skb2) |
128 | return NULL; |
129 | |
130 | skb = skb2; |
131 | |
132 | done: |
133 | /* we don't use the "no Ethernet CRC" option */ |
134 | crc = crc32_le(crc: ~0, p: skb->data, len: skb->len); |
135 | crc = ~crc; |
136 | |
137 | put_unaligned_le32(val: crc, p: skb_put(skb, len: 4)); |
138 | |
139 | /* EEM packet header format: |
140 | * b0..13: length of ethernet frame |
141 | * b14: bmCRC (1 == valid Ethernet CRC) |
142 | * b15: bmType (0 == data) |
143 | */ |
144 | len = skb->len; |
145 | put_unaligned_le16(BIT(14) | len, p: skb_push(skb, len: 2)); |
146 | |
147 | /* Bundle a zero length EEM packet if needed */ |
148 | if (padlen) |
149 | put_unaligned_le16(val: 0, p: skb_put(skb, len: 2)); |
150 | |
151 | return skb; |
152 | } |
153 | |
154 | static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb) |
155 | { |
156 | /* |
157 | * Our task here is to strip off framing, leaving skb with one |
158 | * data frame for the usbnet framework code to process. But we |
159 | * may have received multiple EEM payloads, or command payloads. |
160 | * So we must process _everything_ as if it's a header, except |
161 | * maybe the last data payload |
162 | * |
163 | * REVISIT the framework needs updating so that when we consume |
164 | * all payloads (the last or only message was a command, or a |
165 | * zero length EEM packet) that is not accounted as an rx_error. |
166 | */ |
167 | do { |
168 | struct sk_buff *skb2 = NULL; |
169 | u16 ; |
170 | u16 len = 0; |
171 | |
172 | /* incomplete EEM header? */ |
173 | if (skb->len < EEM_HEAD) |
174 | return 0; |
175 | |
176 | /* |
177 | * EEM packet header format: |
178 | * b0..14: EEM type dependent (Data or Command) |
179 | * b15: bmType |
180 | */ |
181 | header = get_unaligned_le16(p: skb->data); |
182 | skb_pull(skb, EEM_HEAD); |
183 | |
184 | /* |
185 | * The bmType bit helps to denote when EEM |
186 | * packet is data or command : |
187 | * bmType = 0 : EEM data payload |
188 | * bmType = 1 : EEM (link) command |
189 | */ |
190 | if (header & BIT(15)) { |
191 | u16 bmEEMCmd; |
192 | |
193 | /* |
194 | * EEM (link) command packet: |
195 | * b0..10: bmEEMCmdParam |
196 | * b11..13: bmEEMCmd |
197 | * b14: bmReserved (must be 0) |
198 | * b15: 1 (EEM command) |
199 | */ |
200 | if (header & BIT(14)) { |
201 | netdev_dbg(dev->net, "reserved command %04x\n" , |
202 | header); |
203 | continue; |
204 | } |
205 | |
206 | bmEEMCmd = (header >> 11) & 0x7; |
207 | switch (bmEEMCmd) { |
208 | |
209 | /* Responding to echo requests is mandatory. */ |
210 | case 0: /* Echo command */ |
211 | len = header & 0x7FF; |
212 | |
213 | /* bogus command? */ |
214 | if (skb->len < len) |
215 | return 0; |
216 | |
217 | skb2 = skb_clone(skb, GFP_ATOMIC); |
218 | if (unlikely(!skb2)) |
219 | goto next; |
220 | skb_trim(skb: skb2, len); |
221 | put_unaligned_le16(BIT(15) | BIT(11) | len, |
222 | p: skb_push(skb: skb2, len: 2)); |
223 | eem_linkcmd(dev, skb: skb2); |
224 | break; |
225 | |
226 | /* |
227 | * Host may choose to ignore hints. |
228 | * - suspend: peripheral ready to suspend |
229 | * - response: suggest N millisec polling |
230 | * - response complete: suggest N sec polling |
231 | * |
232 | * Suspend is reported and maybe heeded. |
233 | */ |
234 | case 2: /* Suspend hint */ |
235 | usbnet_device_suggests_idle(dev); |
236 | continue; |
237 | case 3: /* Response hint */ |
238 | case 4: /* Response complete hint */ |
239 | continue; |
240 | |
241 | /* |
242 | * Hosts should never receive host-to-peripheral |
243 | * or reserved command codes; or responses to an |
244 | * echo command we didn't send. |
245 | */ |
246 | case 1: /* Echo response */ |
247 | case 5: /* Tickle */ |
248 | default: /* reserved */ |
249 | netdev_warn(dev: dev->net, |
250 | format: "unexpected link command %d\n" , |
251 | bmEEMCmd); |
252 | continue; |
253 | } |
254 | |
255 | } else { |
256 | u32 crc, crc2; |
257 | int is_last; |
258 | |
259 | /* zero length EEM packet? */ |
260 | if (header == 0) |
261 | continue; |
262 | |
263 | /* |
264 | * EEM data packet header : |
265 | * b0..13: length of ethernet frame |
266 | * b14: bmCRC |
267 | * b15: 0 (EEM data) |
268 | */ |
269 | len = header & 0x3FFF; |
270 | |
271 | /* bogus EEM payload? */ |
272 | if (skb->len < len) |
273 | return 0; |
274 | |
275 | /* bogus ethernet frame? */ |
276 | if (len < (ETH_HLEN + ETH_FCS_LEN)) |
277 | goto next; |
278 | |
279 | /* |
280 | * Treat the last payload differently: framework |
281 | * code expects our "fixup" to have stripped off |
282 | * headers, so "skb" is a data packet (or error). |
283 | * Else if it's not the last payload, keep "skb" |
284 | * for further processing. |
285 | */ |
286 | is_last = (len == skb->len); |
287 | if (is_last) |
288 | skb2 = skb; |
289 | else { |
290 | skb2 = skb_clone(skb, GFP_ATOMIC); |
291 | if (unlikely(!skb2)) |
292 | return 0; |
293 | } |
294 | |
295 | /* |
296 | * The bmCRC helps to denote when the CRC field in |
297 | * the Ethernet frame contains a calculated CRC: |
298 | * bmCRC = 1 : CRC is calculated |
299 | * bmCRC = 0 : CRC = 0xDEADBEEF |
300 | */ |
301 | if (header & BIT(14)) { |
302 | crc = get_unaligned_le32(p: skb2->data |
303 | + len - ETH_FCS_LEN); |
304 | crc2 = ~crc32_le(crc: ~0, p: skb2->data, len: skb2->len |
305 | - ETH_FCS_LEN); |
306 | } else { |
307 | crc = get_unaligned_be32(p: skb2->data |
308 | + len - ETH_FCS_LEN); |
309 | crc2 = 0xdeadbeef; |
310 | } |
311 | skb_trim(skb: skb2, len: len - ETH_FCS_LEN); |
312 | |
313 | if (is_last) |
314 | return crc == crc2; |
315 | |
316 | if (unlikely(crc != crc2)) { |
317 | dev->net->stats.rx_errors++; |
318 | dev_kfree_skb_any(skb: skb2); |
319 | } else |
320 | usbnet_skb_return(dev, skb2); |
321 | } |
322 | |
323 | next: |
324 | skb_pull(skb, len); |
325 | } while (skb->len); |
326 | |
327 | return 1; |
328 | } |
329 | |
330 | static const struct driver_info eem_info = { |
331 | .description = "CDC EEM Device" , |
332 | .flags = FLAG_ETHER | FLAG_POINTTOPOINT, |
333 | .bind = eem_bind, |
334 | .rx_fixup = eem_rx_fixup, |
335 | .tx_fixup = eem_tx_fixup, |
336 | }; |
337 | |
338 | /*-------------------------------------------------------------------------*/ |
339 | |
340 | static const struct usb_device_id products[] = { |
341 | { |
342 | USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM, |
343 | USB_CDC_PROTO_EEM), |
344 | .driver_info = (unsigned long) &eem_info, |
345 | }, |
346 | { |
347 | /* EMPTY == end of list */ |
348 | }, |
349 | }; |
350 | MODULE_DEVICE_TABLE(usb, products); |
351 | |
352 | static struct usb_driver eem_driver = { |
353 | .name = "cdc_eem" , |
354 | .id_table = products, |
355 | .probe = usbnet_probe, |
356 | .disconnect = usbnet_disconnect, |
357 | .suspend = usbnet_suspend, |
358 | .resume = usbnet_resume, |
359 | .disable_hub_initiated_lpm = 1, |
360 | }; |
361 | |
362 | module_usb_driver(eem_driver); |
363 | |
364 | MODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>" ); |
365 | MODULE_DESCRIPTION("USB CDC EEM" ); |
366 | MODULE_LICENSE("GPL" ); |
367 | |