diff options
Diffstat (limited to 'drivers/net/usb/cdc_eem.c')
-rw-r--r-- | drivers/net/usb/cdc_eem.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c new file mode 100644 index 00000000000..80e01778dd3 --- /dev/null +++ b/drivers/net/usb/cdc_eem.c | |||
@@ -0,0 +1,381 @@ | |||
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 | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/netdevice.h> | ||
24 | #include <linux/etherdevice.h> | ||
25 | #include <linux/ctype.h> | ||
26 | #include <linux/ethtool.h> | ||
27 | #include <linux/workqueue.h> | ||
28 | #include <linux/mii.h> | ||
29 | #include <linux/usb.h> | ||
30 | #include <linux/crc32.h> | ||
31 | #include <linux/usb/cdc.h> | ||
32 | #include <linux/usb/usbnet.h> | ||
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); | ||
76 | devwarn(dev, "link cmd failure\n"); | ||
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 | |||
94 | dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN; | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * EEM permits packing multiple Ethernet frames into USB transfers | ||
101 | * (a "bundle"), but for TX we don't try to do that. | ||
102 | */ | ||
103 | static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb, | ||
104 | gfp_t flags) | ||
105 | { | ||
106 | struct sk_buff *skb2 = NULL; | ||
107 | u16 len = skb->len; | ||
108 | u32 crc = 0; | ||
109 | int padlen = 0; | ||
110 | |||
111 | /* When ((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket) is | ||
112 | * zero, stick two bytes of zero length EEM packet on the end. | ||
113 | * Else the framework would add invalid single byte padding, | ||
114 | * since it can't know whether ZLPs will be handled right by | ||
115 | * all the relevant hardware and software. | ||
116 | */ | ||
117 | if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket)) | ||
118 | padlen += 2; | ||
119 | |||
120 | if (!skb_cloned(skb)) { | ||
121 | int headroom = skb_headroom(skb); | ||
122 | int tailroom = skb_tailroom(skb); | ||
123 | |||
124 | if ((tailroom >= ETH_FCS_LEN + padlen) | ||
125 | && (headroom >= EEM_HEAD)) | ||
126 | goto done; | ||
127 | |||
128 | if ((headroom + tailroom) | ||
129 | > (EEM_HEAD + ETH_FCS_LEN + padlen)) { | ||
130 | skb->data = memmove(skb->head + | ||
131 | EEM_HEAD, | ||
132 | skb->data, | ||
133 | skb->len); | ||
134 | skb_set_tail_pointer(skb, len); | ||
135 | goto done; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, flags); | ||
140 | if (!skb2) | ||
141 | return NULL; | ||
142 | |||
143 | dev_kfree_skb_any(skb); | ||
144 | skb = skb2; | ||
145 | |||
146 | done: | ||
147 | /* we don't use the "no Ethernet CRC" option */ | ||
148 | crc = crc32_le(~0, skb->data, skb->len); | ||
149 | crc = ~crc; | ||
150 | |||
151 | put_unaligned_le32(crc, skb_put(skb, 4)); | ||
152 | |||
153 | /* EEM packet header format: | ||
154 | * b0..13: length of ethernet frame | ||
155 | * b14: bmCRC (1 == valid Ethernet CRC) | ||
156 | * b15: bmType (0 == data) | ||
157 | */ | ||
158 | len = skb->len; | ||
159 | put_unaligned_le16(BIT(14) | len, skb_push(skb, 2)); | ||
160 | |||
161 | /* Bundle a zero length EEM packet if needed */ | ||
162 | if (padlen) | ||
163 | put_unaligned_le16(0, skb_put(skb, 2)); | ||
164 | |||
165 | return skb; | ||
166 | } | ||
167 | |||
168 | static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb) | ||
169 | { | ||
170 | /* | ||
171 | * Our task here is to strip off framing, leaving skb with one | ||
172 | * data frame for the usbnet framework code to process. But we | ||
173 | * may have received multiple EEM payloads, or command payloads. | ||
174 | * So we must process _everything_ as if it's a header, except | ||
175 | * maybe the last data payload | ||
176 | * | ||
177 | * REVISIT the framework needs updating so that when we consume | ||
178 | * all payloads (the last or only message was a command, or a | ||
179 | * zero length EEM packet) that is not accounted as an rx_error. | ||
180 | */ | ||
181 | do { | ||
182 | struct sk_buff *skb2 = NULL; | ||
183 | u16 header; | ||
184 | u16 len = 0; | ||
185 | |||
186 | /* incomplete EEM header? */ | ||
187 | if (skb->len < EEM_HEAD) | ||
188 | return 0; | ||
189 | |||
190 | /* | ||
191 | * EEM packet header format: | ||
192 | * b0..14: EEM type dependant (Data or Command) | ||
193 | * b15: bmType | ||
194 | */ | ||
195 | header = get_unaligned_le16(skb->data); | ||
196 | skb_pull(skb, EEM_HEAD); | ||
197 | |||
198 | /* | ||
199 | * The bmType bit helps to denote when EEM | ||
200 | * packet is data or command : | ||
201 | * bmType = 0 : EEM data payload | ||
202 | * bmType = 1 : EEM (link) command | ||
203 | */ | ||
204 | if (header & BIT(15)) { | ||
205 | u16 bmEEMCmd; | ||
206 | |||
207 | /* | ||
208 | * EEM (link) command packet: | ||
209 | * b0..10: bmEEMCmdParam | ||
210 | * b11..13: bmEEMCmd | ||
211 | * b14: bmReserved (must be 0) | ||
212 | * b15: 1 (EEM command) | ||
213 | */ | ||
214 | if (header & BIT(14)) { | ||
215 | devdbg(dev, "reserved command %04x\n", header); | ||
216 | continue; | ||
217 | } | ||
218 | |||
219 | bmEEMCmd = (header >> 11) & 0x7; | ||
220 | switch (bmEEMCmd) { | ||
221 | |||
222 | /* Responding to echo requests is mandatory. */ | ||
223 | case 0: /* Echo command */ | ||
224 | len = header & 0x7FF; | ||
225 | |||
226 | /* bogus command? */ | ||
227 | if (skb->len < len) | ||
228 | return 0; | ||
229 | |||
230 | skb2 = skb_clone(skb, GFP_ATOMIC); | ||
231 | if (unlikely(!skb2)) | ||
232 | goto next; | ||
233 | skb_trim(skb2, len); | ||
234 | put_unaligned_le16(BIT(15) | (1 << 11) | len, | ||
235 | skb_push(skb2, 2)); | ||
236 | eem_linkcmd(dev, skb2); | ||
237 | break; | ||
238 | |||
239 | /* | ||
240 | * Host may choose to ignore hints. | ||
241 | * - suspend: peripheral ready to suspend | ||
242 | * - response: suggest N millisec polling | ||
243 | * - response complete: suggest N sec polling | ||
244 | */ | ||
245 | case 2: /* Suspend hint */ | ||
246 | case 3: /* Response hint */ | ||
247 | case 4: /* Response complete hint */ | ||
248 | continue; | ||
249 | |||
250 | /* | ||
251 | * Hosts should never receive host-to-peripheral | ||
252 | * or reserved command codes; or responses to an | ||
253 | * echo command we didn't send. | ||
254 | */ | ||
255 | case 1: /* Echo response */ | ||
256 | case 5: /* Tickle */ | ||
257 | default: /* reserved */ | ||
258 | devwarn(dev, "unexpected link command %d\n", | ||
259 | bmEEMCmd); | ||
260 | continue; | ||
261 | } | ||
262 | |||
263 | } else { | ||
264 | u32 crc, crc2; | ||
265 | int is_last; | ||
266 | |||
267 | /* zero length EEM packet? */ | ||
268 | if (header == 0) | ||
269 | continue; | ||
270 | |||
271 | /* | ||
272 | * EEM data packet header : | ||
273 | * b0..13: length of ethernet frame | ||
274 | * b14: bmCRC | ||
275 | * b15: 0 (EEM data) | ||
276 | */ | ||
277 | len = header & 0x3FFF; | ||
278 | |||
279 | /* bogus EEM payload? */ | ||
280 | if (skb->len < len) | ||
281 | return 0; | ||
282 | |||
283 | /* bogus ethernet frame? */ | ||
284 | if (len < (ETH_HLEN + ETH_FCS_LEN)) | ||
285 | goto next; | ||
286 | |||
287 | /* | ||
288 | * Treat the last payload differently: framework | ||
289 | * code expects our "fixup" to have stripped off | ||
290 | * headers, so "skb" is a data packet (or error). | ||
291 | * Else if it's not the last payload, keep "skb" | ||
292 | * for further processing. | ||
293 | */ | ||
294 | is_last = (len == skb->len); | ||
295 | if (is_last) | ||
296 | skb2 = skb; | ||
297 | else { | ||
298 | skb2 = skb_clone(skb, GFP_ATOMIC); | ||
299 | if (unlikely(!skb2)) | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | crc = get_unaligned_le32(skb2->data | ||
304 | + len - ETH_FCS_LEN); | ||
305 | skb_trim(skb2, len - ETH_FCS_LEN); | ||
306 | |||
307 | /* | ||
308 | * The bmCRC helps to denote when the CRC field in | ||
309 | * the Ethernet frame contains a calculated CRC: | ||
310 | * bmCRC = 1 : CRC is calculated | ||
311 | * bmCRC = 0 : CRC = 0xDEADBEEF | ||
312 | */ | ||
313 | if (header & BIT(14)) | ||
314 | crc2 = ~crc32_le(~0, skb2->data, len); | ||
315 | else | ||
316 | crc2 = 0xdeadbeef; | ||
317 | |||
318 | if (is_last) | ||
319 | return crc == crc2; | ||
320 | |||
321 | if (unlikely(crc != crc2)) { | ||
322 | dev->stats.rx_errors++; | ||
323 | dev_kfree_skb_any(skb2); | ||
324 | } else | ||
325 | usbnet_skb_return(dev, skb2); | ||
326 | } | ||
327 | |||
328 | next: | ||
329 | skb_pull(skb, len); | ||
330 | } while (skb->len); | ||
331 | |||
332 | return 1; | ||
333 | } | ||
334 | |||
335 | static const struct driver_info eem_info = { | ||
336 | .description = "CDC EEM Device", | ||
337 | .flags = FLAG_ETHER, | ||
338 | .bind = eem_bind, | ||
339 | .rx_fixup = eem_rx_fixup, | ||
340 | .tx_fixup = eem_tx_fixup, | ||
341 | }; | ||
342 | |||
343 | /*-------------------------------------------------------------------------*/ | ||
344 | |||
345 | static const struct usb_device_id products[] = { | ||
346 | { | ||
347 | USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM, | ||
348 | USB_CDC_PROTO_EEM), | ||
349 | .driver_info = (unsigned long) &eem_info, | ||
350 | }, | ||
351 | { | ||
352 | /* EMPTY == end of list */ | ||
353 | }, | ||
354 | }; | ||
355 | MODULE_DEVICE_TABLE(usb, products); | ||
356 | |||
357 | static struct usb_driver eem_driver = { | ||
358 | .name = "cdc_eem", | ||
359 | .id_table = products, | ||
360 | .probe = usbnet_probe, | ||
361 | .disconnect = usbnet_disconnect, | ||
362 | .suspend = usbnet_suspend, | ||
363 | .resume = usbnet_resume, | ||
364 | }; | ||
365 | |||
366 | |||
367 | static int __init eem_init(void) | ||
368 | { | ||
369 | return usb_register(&eem_driver); | ||
370 | } | ||
371 | module_init(eem_init); | ||
372 | |||
373 | static void __exit eem_exit(void) | ||
374 | { | ||
375 | usb_deregister(&eem_driver); | ||
376 | } | ||
377 | module_exit(eem_exit); | ||
378 | |||
379 | MODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>"); | ||
380 | MODULE_DESCRIPTION("USB CDC EEM"); | ||
381 | MODULE_LICENSE("GPL"); | ||