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