diff options
Diffstat (limited to 'drivers/usb/gadget/legacy/ether.c')
-rw-r--r-- | drivers/usb/gadget/legacy/ether.c | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/drivers/usb/gadget/legacy/ether.c b/drivers/usb/gadget/legacy/ether.c new file mode 100644 index 000000000000..c5fdc61cdc4a --- /dev/null +++ b/drivers/usb/gadget/legacy/ether.c | |||
@@ -0,0 +1,482 @@ | |||
1 | /* | ||
2 | * ether.c -- Ethernet gadget driver, with CDC and non-CDC options | ||
3 | * | ||
4 | * Copyright (C) 2003-2005,2008 David Brownell | ||
5 | * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger | ||
6 | * Copyright (C) 2008 Nokia Corporation | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | /* #define VERBOSE_DEBUG */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/netdevice.h> | ||
18 | |||
19 | #if defined USB_ETH_RNDIS | ||
20 | # undef USB_ETH_RNDIS | ||
21 | #endif | ||
22 | #ifdef CONFIG_USB_ETH_RNDIS | ||
23 | # define USB_ETH_RNDIS y | ||
24 | #endif | ||
25 | |||
26 | #include "u_ether.h" | ||
27 | |||
28 | |||
29 | /* | ||
30 | * Ethernet gadget driver -- with CDC and non-CDC options | ||
31 | * Builds on hardware support for a full duplex link. | ||
32 | * | ||
33 | * CDC Ethernet is the standard USB solution for sending Ethernet frames | ||
34 | * using USB. Real hardware tends to use the same framing protocol but look | ||
35 | * different for control features. This driver strongly prefers to use | ||
36 | * this USB-IF standard as its open-systems interoperability solution; | ||
37 | * most host side USB stacks (except from Microsoft) support it. | ||
38 | * | ||
39 | * This is sometimes called "CDC ECM" (Ethernet Control Model) to support | ||
40 | * TLA-soup. "CDC ACM" (Abstract Control Model) is for modems, and a new | ||
41 | * "CDC EEM" (Ethernet Emulation Model) is starting to spread. | ||
42 | * | ||
43 | * There's some hardware that can't talk CDC ECM. We make that hardware | ||
44 | * implement a "minimalist" vendor-agnostic CDC core: same framing, but | ||
45 | * link-level setup only requires activating the configuration. Only the | ||
46 | * endpoint descriptors, and product/vendor IDs, are relevant; no control | ||
47 | * operations are available. Linux supports it, but other host operating | ||
48 | * systems may not. (This is a subset of CDC Ethernet.) | ||
49 | * | ||
50 | * It turns out that if you add a few descriptors to that "CDC Subset", | ||
51 | * (Windows) host side drivers from MCCI can treat it as one submode of | ||
52 | * a proprietary scheme called "SAFE" ... without needing to know about | ||
53 | * specific product/vendor IDs. So we do that, making it easier to use | ||
54 | * those MS-Windows drivers. Those added descriptors make it resemble a | ||
55 | * CDC MDLM device, but they don't change device behavior at all. (See | ||
56 | * MCCI Engineering report 950198 "SAFE Networking Functions".) | ||
57 | * | ||
58 | * A third option is also in use. Rather than CDC Ethernet, or something | ||
59 | * simpler, Microsoft pushes their own approach: RNDIS. The published | ||
60 | * RNDIS specs are ambiguous and appear to be incomplete, and are also | ||
61 | * needlessly complex. They borrow more from CDC ACM than CDC ECM. | ||
62 | */ | ||
63 | |||
64 | #define DRIVER_DESC "Ethernet Gadget" | ||
65 | #define DRIVER_VERSION "Memorial Day 2008" | ||
66 | |||
67 | #ifdef USB_ETH_RNDIS | ||
68 | #define PREFIX "RNDIS/" | ||
69 | #else | ||
70 | #define PREFIX "" | ||
71 | #endif | ||
72 | |||
73 | /* | ||
74 | * This driver aims for interoperability by using CDC ECM unless | ||
75 | * | ||
76 | * can_support_ecm() | ||
77 | * | ||
78 | * returns false, in which case it supports the CDC Subset. By default, | ||
79 | * that returns true; most hardware has no problems with CDC ECM, that's | ||
80 | * a good default. Previous versions of this driver had no default; this | ||
81 | * version changes that, removing overhead for new controller support. | ||
82 | * | ||
83 | * IF YOUR HARDWARE CAN'T SUPPORT CDC ECM, UPDATE THAT ROUTINE! | ||
84 | */ | ||
85 | |||
86 | static inline bool has_rndis(void) | ||
87 | { | ||
88 | #ifdef USB_ETH_RNDIS | ||
89 | return true; | ||
90 | #else | ||
91 | return false; | ||
92 | #endif | ||
93 | } | ||
94 | |||
95 | #include <linux/module.h> | ||
96 | |||
97 | #include "u_ecm.h" | ||
98 | #include "u_gether.h" | ||
99 | #ifdef USB_ETH_RNDIS | ||
100 | #include "u_rndis.h" | ||
101 | #include "rndis.h" | ||
102 | #else | ||
103 | #define rndis_borrow_net(...) do {} while (0) | ||
104 | #endif | ||
105 | #include "u_eem.h" | ||
106 | |||
107 | /*-------------------------------------------------------------------------*/ | ||
108 | USB_GADGET_COMPOSITE_OPTIONS(); | ||
109 | |||
110 | USB_ETHERNET_MODULE_PARAMETERS(); | ||
111 | |||
112 | /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! | ||
113 | * Instead: allocate your own, using normal USB-IF procedures. | ||
114 | */ | ||
115 | |||
116 | /* Thanks to NetChip Technologies for donating this product ID. | ||
117 | * It's for devices with only CDC Ethernet configurations. | ||
118 | */ | ||
119 | #define CDC_VENDOR_NUM 0x0525 /* NetChip */ | ||
120 | #define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ | ||
121 | |||
122 | /* For hardware that can't talk CDC, we use the same vendor ID that | ||
123 | * ARM Linux has used for ethernet-over-usb, both with sa1100 and | ||
124 | * with pxa250. We're protocol-compatible, if the host-side drivers | ||
125 | * use the endpoint descriptors. bcdDevice (version) is nonzero, so | ||
126 | * drivers that need to hard-wire endpoint numbers have a hook. | ||
127 | * | ||
128 | * The protocol is a minimal subset of CDC Ether, which works on any bulk | ||
129 | * hardware that's not deeply broken ... even on hardware that can't talk | ||
130 | * RNDIS (like SA-1100, with no interrupt endpoint, or anything that | ||
131 | * doesn't handle control-OUT). | ||
132 | */ | ||
133 | #define SIMPLE_VENDOR_NUM 0x049f | ||
134 | #define SIMPLE_PRODUCT_NUM 0x505a | ||
135 | |||
136 | /* For hardware that can talk RNDIS and either of the above protocols, | ||
137 | * use this ID ... the windows INF files will know it. Unless it's | ||
138 | * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose | ||
139 | * the non-RNDIS configuration. | ||
140 | */ | ||
141 | #define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ | ||
142 | #define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ | ||
143 | |||
144 | /* For EEM gadgets */ | ||
145 | #define EEM_VENDOR_NUM 0x1d6b /* Linux Foundation */ | ||
146 | #define EEM_PRODUCT_NUM 0x0102 /* EEM Gadget */ | ||
147 | |||
148 | /*-------------------------------------------------------------------------*/ | ||
149 | |||
150 | static struct usb_device_descriptor device_desc = { | ||
151 | .bLength = sizeof device_desc, | ||
152 | .bDescriptorType = USB_DT_DEVICE, | ||
153 | |||
154 | .bcdUSB = cpu_to_le16 (0x0200), | ||
155 | |||
156 | .bDeviceClass = USB_CLASS_COMM, | ||
157 | .bDeviceSubClass = 0, | ||
158 | .bDeviceProtocol = 0, | ||
159 | /* .bMaxPacketSize0 = f(hardware) */ | ||
160 | |||
161 | /* Vendor and product id defaults change according to what configs | ||
162 | * we support. (As does bNumConfigurations.) These values can | ||
163 | * also be overridden by module parameters. | ||
164 | */ | ||
165 | .idVendor = cpu_to_le16 (CDC_VENDOR_NUM), | ||
166 | .idProduct = cpu_to_le16 (CDC_PRODUCT_NUM), | ||
167 | /* .bcdDevice = f(hardware) */ | ||
168 | /* .iManufacturer = DYNAMIC */ | ||
169 | /* .iProduct = DYNAMIC */ | ||
170 | /* NO SERIAL NUMBER */ | ||
171 | .bNumConfigurations = 1, | ||
172 | }; | ||
173 | |||
174 | static struct usb_otg_descriptor otg_descriptor = { | ||
175 | .bLength = sizeof otg_descriptor, | ||
176 | .bDescriptorType = USB_DT_OTG, | ||
177 | |||
178 | /* REVISIT SRP-only hardware is possible, although | ||
179 | * it would not be called "OTG" ... | ||
180 | */ | ||
181 | .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, | ||
182 | }; | ||
183 | |||
184 | static const struct usb_descriptor_header *otg_desc[] = { | ||
185 | (struct usb_descriptor_header *) &otg_descriptor, | ||
186 | NULL, | ||
187 | }; | ||
188 | |||
189 | static struct usb_string strings_dev[] = { | ||
190 | [USB_GADGET_MANUFACTURER_IDX].s = "", | ||
191 | [USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC, | ||
192 | [USB_GADGET_SERIAL_IDX].s = "", | ||
193 | { } /* end of list */ | ||
194 | }; | ||
195 | |||
196 | static struct usb_gadget_strings stringtab_dev = { | ||
197 | .language = 0x0409, /* en-us */ | ||
198 | .strings = strings_dev, | ||
199 | }; | ||
200 | |||
201 | static struct usb_gadget_strings *dev_strings[] = { | ||
202 | &stringtab_dev, | ||
203 | NULL, | ||
204 | }; | ||
205 | |||
206 | static struct usb_function_instance *fi_ecm; | ||
207 | static struct usb_function *f_ecm; | ||
208 | |||
209 | static struct usb_function_instance *fi_eem; | ||
210 | static struct usb_function *f_eem; | ||
211 | |||
212 | static struct usb_function_instance *fi_geth; | ||
213 | static struct usb_function *f_geth; | ||
214 | |||
215 | static struct usb_function_instance *fi_rndis; | ||
216 | static struct usb_function *f_rndis; | ||
217 | |||
218 | /*-------------------------------------------------------------------------*/ | ||
219 | |||
220 | /* | ||
221 | * We may not have an RNDIS configuration, but if we do it needs to be | ||
222 | * the first one present. That's to make Microsoft's drivers happy, | ||
223 | * and to follow DOCSIS 1.0 (cable modem standard). | ||
224 | */ | ||
225 | static int __init rndis_do_config(struct usb_configuration *c) | ||
226 | { | ||
227 | int status; | ||
228 | |||
229 | /* FIXME alloc iConfiguration string, set it in c->strings */ | ||
230 | |||
231 | if (gadget_is_otg(c->cdev->gadget)) { | ||
232 | c->descriptors = otg_desc; | ||
233 | c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | ||
234 | } | ||
235 | |||
236 | f_rndis = usb_get_function(fi_rndis); | ||
237 | if (IS_ERR(f_rndis)) | ||
238 | return PTR_ERR(f_rndis); | ||
239 | |||
240 | status = usb_add_function(c, f_rndis); | ||
241 | if (status < 0) | ||
242 | usb_put_function(f_rndis); | ||
243 | |||
244 | return status; | ||
245 | } | ||
246 | |||
247 | static struct usb_configuration rndis_config_driver = { | ||
248 | .label = "RNDIS", | ||
249 | .bConfigurationValue = 2, | ||
250 | /* .iConfiguration = DYNAMIC */ | ||
251 | .bmAttributes = USB_CONFIG_ATT_SELFPOWER, | ||
252 | }; | ||
253 | |||
254 | /*-------------------------------------------------------------------------*/ | ||
255 | |||
256 | #ifdef CONFIG_USB_ETH_EEM | ||
257 | static bool use_eem = 1; | ||
258 | #else | ||
259 | static bool use_eem; | ||
260 | #endif | ||
261 | module_param(use_eem, bool, 0); | ||
262 | MODULE_PARM_DESC(use_eem, "use CDC EEM mode"); | ||
263 | |||
264 | /* | ||
265 | * We _always_ have an ECM, CDC Subset, or EEM configuration. | ||
266 | */ | ||
267 | static int __init eth_do_config(struct usb_configuration *c) | ||
268 | { | ||
269 | int status = 0; | ||
270 | |||
271 | /* FIXME alloc iConfiguration string, set it in c->strings */ | ||
272 | |||
273 | if (gadget_is_otg(c->cdev->gadget)) { | ||
274 | c->descriptors = otg_desc; | ||
275 | c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | ||
276 | } | ||
277 | |||
278 | if (use_eem) { | ||
279 | f_eem = usb_get_function(fi_eem); | ||
280 | if (IS_ERR(f_eem)) | ||
281 | return PTR_ERR(f_eem); | ||
282 | |||
283 | status = usb_add_function(c, f_eem); | ||
284 | if (status < 0) | ||
285 | usb_put_function(f_eem); | ||
286 | |||
287 | return status; | ||
288 | } else if (can_support_ecm(c->cdev->gadget)) { | ||
289 | f_ecm = usb_get_function(fi_ecm); | ||
290 | if (IS_ERR(f_ecm)) | ||
291 | return PTR_ERR(f_ecm); | ||
292 | |||
293 | status = usb_add_function(c, f_ecm); | ||
294 | if (status < 0) | ||
295 | usb_put_function(f_ecm); | ||
296 | |||
297 | return status; | ||
298 | } else { | ||
299 | f_geth = usb_get_function(fi_geth); | ||
300 | if (IS_ERR(f_geth)) | ||
301 | return PTR_ERR(f_geth); | ||
302 | |||
303 | status = usb_add_function(c, f_geth); | ||
304 | if (status < 0) | ||
305 | usb_put_function(f_geth); | ||
306 | |||
307 | return status; | ||
308 | } | ||
309 | |||
310 | } | ||
311 | |||
312 | static struct usb_configuration eth_config_driver = { | ||
313 | /* .label = f(hardware) */ | ||
314 | .bConfigurationValue = 1, | ||
315 | /* .iConfiguration = DYNAMIC */ | ||
316 | .bmAttributes = USB_CONFIG_ATT_SELFPOWER, | ||
317 | }; | ||
318 | |||
319 | /*-------------------------------------------------------------------------*/ | ||
320 | |||
321 | static int __init eth_bind(struct usb_composite_dev *cdev) | ||
322 | { | ||
323 | struct usb_gadget *gadget = cdev->gadget; | ||
324 | struct f_eem_opts *eem_opts = NULL; | ||
325 | struct f_ecm_opts *ecm_opts = NULL; | ||
326 | struct f_gether_opts *geth_opts = NULL; | ||
327 | struct net_device *net; | ||
328 | int status; | ||
329 | |||
330 | /* set up main config label and device descriptor */ | ||
331 | if (use_eem) { | ||
332 | /* EEM */ | ||
333 | fi_eem = usb_get_function_instance("eem"); | ||
334 | if (IS_ERR(fi_eem)) | ||
335 | return PTR_ERR(fi_eem); | ||
336 | |||
337 | eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst); | ||
338 | |||
339 | net = eem_opts->net; | ||
340 | |||
341 | eth_config_driver.label = "CDC Ethernet (EEM)"; | ||
342 | device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM); | ||
343 | device_desc.idProduct = cpu_to_le16(EEM_PRODUCT_NUM); | ||
344 | } else if (can_support_ecm(gadget)) { | ||
345 | /* ECM */ | ||
346 | |||
347 | fi_ecm = usb_get_function_instance("ecm"); | ||
348 | if (IS_ERR(fi_ecm)) | ||
349 | return PTR_ERR(fi_ecm); | ||
350 | |||
351 | ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); | ||
352 | |||
353 | net = ecm_opts->net; | ||
354 | |||
355 | eth_config_driver.label = "CDC Ethernet (ECM)"; | ||
356 | } else { | ||
357 | /* CDC Subset */ | ||
358 | |||
359 | fi_geth = usb_get_function_instance("geth"); | ||
360 | if (IS_ERR(fi_geth)) | ||
361 | return PTR_ERR(fi_geth); | ||
362 | |||
363 | geth_opts = container_of(fi_geth, struct f_gether_opts, | ||
364 | func_inst); | ||
365 | |||
366 | net = geth_opts->net; | ||
367 | |||
368 | eth_config_driver.label = "CDC Subset/SAFE"; | ||
369 | |||
370 | device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM); | ||
371 | device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM); | ||
372 | if (!has_rndis()) | ||
373 | device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; | ||
374 | } | ||
375 | |||
376 | gether_set_qmult(net, qmult); | ||
377 | if (!gether_set_host_addr(net, host_addr)) | ||
378 | pr_info("using host ethernet address: %s", host_addr); | ||
379 | if (!gether_set_dev_addr(net, dev_addr)) | ||
380 | pr_info("using self ethernet address: %s", dev_addr); | ||
381 | |||
382 | if (has_rndis()) { | ||
383 | /* RNDIS plus ECM-or-Subset */ | ||
384 | gether_set_gadget(net, cdev->gadget); | ||
385 | status = gether_register_netdev(net); | ||
386 | if (status) | ||
387 | goto fail; | ||
388 | |||
389 | if (use_eem) | ||
390 | eem_opts->bound = true; | ||
391 | else if (can_support_ecm(gadget)) | ||
392 | ecm_opts->bound = true; | ||
393 | else | ||
394 | geth_opts->bound = true; | ||
395 | |||
396 | fi_rndis = usb_get_function_instance("rndis"); | ||
397 | if (IS_ERR(fi_rndis)) { | ||
398 | status = PTR_ERR(fi_rndis); | ||
399 | goto fail; | ||
400 | } | ||
401 | |||
402 | rndis_borrow_net(fi_rndis, net); | ||
403 | |||
404 | device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM); | ||
405 | device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM); | ||
406 | device_desc.bNumConfigurations = 2; | ||
407 | } | ||
408 | |||
409 | /* Allocate string descriptor numbers ... note that string | ||
410 | * contents can be overridden by the composite_dev glue. | ||
411 | */ | ||
412 | |||
413 | status = usb_string_ids_tab(cdev, strings_dev); | ||
414 | if (status < 0) | ||
415 | goto fail1; | ||
416 | device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; | ||
417 | device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; | ||
418 | |||
419 | /* register our configuration(s); RNDIS first, if it's used */ | ||
420 | if (has_rndis()) { | ||
421 | status = usb_add_config(cdev, &rndis_config_driver, | ||
422 | rndis_do_config); | ||
423 | if (status < 0) | ||
424 | goto fail1; | ||
425 | } | ||
426 | |||
427 | status = usb_add_config(cdev, ð_config_driver, eth_do_config); | ||
428 | if (status < 0) | ||
429 | goto fail1; | ||
430 | |||
431 | usb_composite_overwrite_options(cdev, &coverwrite); | ||
432 | dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", | ||
433 | DRIVER_DESC); | ||
434 | |||
435 | return 0; | ||
436 | |||
437 | fail1: | ||
438 | if (has_rndis()) | ||
439 | usb_put_function_instance(fi_rndis); | ||
440 | fail: | ||
441 | if (use_eem) | ||
442 | usb_put_function_instance(fi_eem); | ||
443 | else if (can_support_ecm(gadget)) | ||
444 | usb_put_function_instance(fi_ecm); | ||
445 | else | ||
446 | usb_put_function_instance(fi_geth); | ||
447 | return status; | ||
448 | } | ||
449 | |||
450 | static int __exit eth_unbind(struct usb_composite_dev *cdev) | ||
451 | { | ||
452 | if (has_rndis()) { | ||
453 | usb_put_function(f_rndis); | ||
454 | usb_put_function_instance(fi_rndis); | ||
455 | } | ||
456 | if (use_eem) { | ||
457 | usb_put_function(f_eem); | ||
458 | usb_put_function_instance(fi_eem); | ||
459 | } else if (can_support_ecm(cdev->gadget)) { | ||
460 | usb_put_function(f_ecm); | ||
461 | usb_put_function_instance(fi_ecm); | ||
462 | } else { | ||
463 | usb_put_function(f_geth); | ||
464 | usb_put_function_instance(fi_geth); | ||
465 | } | ||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | static __refdata struct usb_composite_driver eth_driver = { | ||
470 | .name = "g_ether", | ||
471 | .dev = &device_desc, | ||
472 | .strings = dev_strings, | ||
473 | .max_speed = USB_SPEED_SUPER, | ||
474 | .bind = eth_bind, | ||
475 | .unbind = __exit_p(eth_unbind), | ||
476 | }; | ||
477 | |||
478 | module_usb_composite_driver(eth_driver); | ||
479 | |||
480 | MODULE_DESCRIPTION(PREFIX DRIVER_DESC); | ||
481 | MODULE_AUTHOR("David Brownell, Benedikt Spanger"); | ||
482 | MODULE_LICENSE("GPL"); | ||