diff options
Diffstat (limited to 'drivers/net/usb/cdc_ether.c')
-rw-r--r-- | drivers/net/usb/cdc_ether.c | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c new file mode 100644 index 000000000000..5a21f06bf8a5 --- /dev/null +++ b/drivers/net/usb/cdc_ether.c | |||
@@ -0,0 +1,570 @@ | |||
1 | /* | ||
2 | * CDC Ethernet based networking peripherals | ||
3 | * Copyright (C) 2003-2005 by David Brownell | ||
4 | * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync) | ||
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 | // #define DEBUG // error path messages, extra info | ||
22 | // #define VERBOSE // more; success messages | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/netdevice.h> | ||
27 | #include <linux/etherdevice.h> | ||
28 | #include <linux/ctype.h> | ||
29 | #include <linux/ethtool.h> | ||
30 | #include <linux/workqueue.h> | ||
31 | #include <linux/mii.h> | ||
32 | #include <linux/usb.h> | ||
33 | #include <linux/usb/cdc.h> | ||
34 | |||
35 | #include "usbnet.h" | ||
36 | |||
37 | |||
38 | #if defined(CONFIG_USB_NET_RNDIS_HOST) || defined(CONFIG_USB_NET_RNDIS_HOST_MODULE) | ||
39 | |||
40 | static int is_rndis(struct usb_interface_descriptor *desc) | ||
41 | { | ||
42 | return desc->bInterfaceClass == USB_CLASS_COMM | ||
43 | && desc->bInterfaceSubClass == 2 | ||
44 | && desc->bInterfaceProtocol == 0xff; | ||
45 | } | ||
46 | |||
47 | static int is_activesync(struct usb_interface_descriptor *desc) | ||
48 | { | ||
49 | return desc->bInterfaceClass == USB_CLASS_MISC | ||
50 | && desc->bInterfaceSubClass == 1 | ||
51 | && desc->bInterfaceProtocol == 1; | ||
52 | } | ||
53 | |||
54 | #else | ||
55 | |||
56 | #define is_rndis(desc) 0 | ||
57 | #define is_activesync(desc) 0 | ||
58 | |||
59 | #endif | ||
60 | |||
61 | /* | ||
62 | * probes control interface, claims data interface, collects the bulk | ||
63 | * endpoints, activates data interface (if needed), maybe sets MTU. | ||
64 | * all pure cdc, except for certain firmware workarounds, and knowing | ||
65 | * that rndis uses one different rule. | ||
66 | */ | ||
67 | int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) | ||
68 | { | ||
69 | u8 *buf = intf->cur_altsetting->extra; | ||
70 | int len = intf->cur_altsetting->extralen; | ||
71 | struct usb_interface_descriptor *d; | ||
72 | struct cdc_state *info = (void *) &dev->data; | ||
73 | int status; | ||
74 | int rndis; | ||
75 | struct usb_driver *driver = driver_of(intf); | ||
76 | |||
77 | if (sizeof dev->data < sizeof *info) | ||
78 | return -EDOM; | ||
79 | |||
80 | /* expect strict spec conformance for the descriptors, but | ||
81 | * cope with firmware which stores them in the wrong place | ||
82 | */ | ||
83 | if (len == 0 && dev->udev->actconfig->extralen) { | ||
84 | /* Motorola SB4100 (and others: Brad Hards says it's | ||
85 | * from a Broadcom design) put CDC descriptors here | ||
86 | */ | ||
87 | buf = dev->udev->actconfig->extra; | ||
88 | len = dev->udev->actconfig->extralen; | ||
89 | if (len) | ||
90 | dev_dbg(&intf->dev, | ||
91 | "CDC descriptors on config\n"); | ||
92 | } | ||
93 | |||
94 | /* this assumes that if there's a non-RNDIS vendor variant | ||
95 | * of cdc-acm, it'll fail RNDIS requests cleanly. | ||
96 | */ | ||
97 | rndis = is_rndis(&intf->cur_altsetting->desc) | ||
98 | || is_activesync(&intf->cur_altsetting->desc); | ||
99 | |||
100 | memset(info, 0, sizeof *info); | ||
101 | info->control = intf; | ||
102 | while (len > 3) { | ||
103 | if (buf [1] != USB_DT_CS_INTERFACE) | ||
104 | goto next_desc; | ||
105 | |||
106 | /* use bDescriptorSubType to identify the CDC descriptors. | ||
107 | * We expect devices with CDC header and union descriptors. | ||
108 | * For CDC Ethernet we need the ethernet descriptor. | ||
109 | * For RNDIS, ignore two (pointless) CDC modem descriptors | ||
110 | * in favor of a complicated OID-based RPC scheme doing what | ||
111 | * CDC Ethernet achieves with a simple descriptor. | ||
112 | */ | ||
113 | switch (buf [2]) { | ||
114 | case USB_CDC_HEADER_TYPE: | ||
115 | if (info->header) { | ||
116 | dev_dbg(&intf->dev, "extra CDC header\n"); | ||
117 | goto bad_desc; | ||
118 | } | ||
119 | info->header = (void *) buf; | ||
120 | if (info->header->bLength != sizeof *info->header) { | ||
121 | dev_dbg(&intf->dev, "CDC header len %u\n", | ||
122 | info->header->bLength); | ||
123 | goto bad_desc; | ||
124 | } | ||
125 | break; | ||
126 | case USB_CDC_ACM_TYPE: | ||
127 | /* paranoia: disambiguate a "real" vendor-specific | ||
128 | * modem interface from an RNDIS non-modem. | ||
129 | */ | ||
130 | if (rndis) { | ||
131 | struct usb_cdc_acm_descriptor *d; | ||
132 | |||
133 | d = (void *) buf; | ||
134 | if (d->bmCapabilities) { | ||
135 | dev_dbg(&intf->dev, | ||
136 | "ACM capabilities %02x, " | ||
137 | "not really RNDIS?\n", | ||
138 | d->bmCapabilities); | ||
139 | goto bad_desc; | ||
140 | } | ||
141 | } | ||
142 | break; | ||
143 | case USB_CDC_UNION_TYPE: | ||
144 | if (info->u) { | ||
145 | dev_dbg(&intf->dev, "extra CDC union\n"); | ||
146 | goto bad_desc; | ||
147 | } | ||
148 | info->u = (void *) buf; | ||
149 | if (info->u->bLength != sizeof *info->u) { | ||
150 | dev_dbg(&intf->dev, "CDC union len %u\n", | ||
151 | info->u->bLength); | ||
152 | goto bad_desc; | ||
153 | } | ||
154 | |||
155 | /* we need a master/control interface (what we're | ||
156 | * probed with) and a slave/data interface; union | ||
157 | * descriptors sort this all out. | ||
158 | */ | ||
159 | info->control = usb_ifnum_to_if(dev->udev, | ||
160 | info->u->bMasterInterface0); | ||
161 | info->data = usb_ifnum_to_if(dev->udev, | ||
162 | info->u->bSlaveInterface0); | ||
163 | if (!info->control || !info->data) { | ||
164 | dev_dbg(&intf->dev, | ||
165 | "master #%u/%p slave #%u/%p\n", | ||
166 | info->u->bMasterInterface0, | ||
167 | info->control, | ||
168 | info->u->bSlaveInterface0, | ||
169 | info->data); | ||
170 | goto bad_desc; | ||
171 | } | ||
172 | if (info->control != intf) { | ||
173 | dev_dbg(&intf->dev, "bogus CDC Union\n"); | ||
174 | /* Ambit USB Cable Modem (and maybe others) | ||
175 | * interchanges master and slave interface. | ||
176 | */ | ||
177 | if (info->data == intf) { | ||
178 | info->data = info->control; | ||
179 | info->control = intf; | ||
180 | } else | ||
181 | goto bad_desc; | ||
182 | } | ||
183 | |||
184 | /* a data interface altsetting does the real i/o */ | ||
185 | d = &info->data->cur_altsetting->desc; | ||
186 | if (d->bInterfaceClass != USB_CLASS_CDC_DATA) { | ||
187 | dev_dbg(&intf->dev, "slave class %u\n", | ||
188 | d->bInterfaceClass); | ||
189 | goto bad_desc; | ||
190 | } | ||
191 | break; | ||
192 | case USB_CDC_ETHERNET_TYPE: | ||
193 | if (info->ether) { | ||
194 | dev_dbg(&intf->dev, "extra CDC ether\n"); | ||
195 | goto bad_desc; | ||
196 | } | ||
197 | info->ether = (void *) buf; | ||
198 | if (info->ether->bLength != sizeof *info->ether) { | ||
199 | dev_dbg(&intf->dev, "CDC ether len %u\n", | ||
200 | info->ether->bLength); | ||
201 | goto bad_desc; | ||
202 | } | ||
203 | dev->hard_mtu = le16_to_cpu( | ||
204 | info->ether->wMaxSegmentSize); | ||
205 | /* because of Zaurus, we may be ignoring the host | ||
206 | * side link address we were given. | ||
207 | */ | ||
208 | break; | ||
209 | } | ||
210 | next_desc: | ||
211 | len -= buf [0]; /* bLength */ | ||
212 | buf += buf [0]; | ||
213 | } | ||
214 | |||
215 | /* Microsoft ActiveSync based RNDIS devices lack the CDC descriptors, | ||
216 | * so we'll hard-wire the interfaces and not check for descriptors. | ||
217 | */ | ||
218 | if (is_activesync(&intf->cur_altsetting->desc) && !info->u) { | ||
219 | info->control = usb_ifnum_to_if(dev->udev, 0); | ||
220 | info->data = usb_ifnum_to_if(dev->udev, 1); | ||
221 | if (!info->control || !info->data) { | ||
222 | dev_dbg(&intf->dev, | ||
223 | "activesync: master #0/%p slave #1/%p\n", | ||
224 | info->control, | ||
225 | info->data); | ||
226 | goto bad_desc; | ||
227 | } | ||
228 | |||
229 | } else if (!info->header || !info->u || (!rndis && !info->ether)) { | ||
230 | dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n", | ||
231 | info->header ? "" : "header ", | ||
232 | info->u ? "" : "union ", | ||
233 | info->ether ? "" : "ether "); | ||
234 | goto bad_desc; | ||
235 | } | ||
236 | |||
237 | /* claim data interface and set it up ... with side effects. | ||
238 | * network traffic can't flow until an altsetting is enabled. | ||
239 | */ | ||
240 | status = usb_driver_claim_interface(driver, info->data, dev); | ||
241 | if (status < 0) | ||
242 | return status; | ||
243 | status = usbnet_get_endpoints(dev, info->data); | ||
244 | if (status < 0) { | ||
245 | /* ensure immediate exit from usbnet_disconnect */ | ||
246 | usb_set_intfdata(info->data, NULL); | ||
247 | usb_driver_release_interface(driver, info->data); | ||
248 | return status; | ||
249 | } | ||
250 | |||
251 | /* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */ | ||
252 | dev->status = NULL; | ||
253 | if (info->control->cur_altsetting->desc.bNumEndpoints == 1) { | ||
254 | struct usb_endpoint_descriptor *desc; | ||
255 | |||
256 | dev->status = &info->control->cur_altsetting->endpoint [0]; | ||
257 | desc = &dev->status->desc; | ||
258 | if (!usb_endpoint_is_int_in(desc) | ||
259 | || (le16_to_cpu(desc->wMaxPacketSize) | ||
260 | < sizeof(struct usb_cdc_notification)) | ||
261 | || !desc->bInterval) { | ||
262 | dev_dbg(&intf->dev, "bad notification endpoint\n"); | ||
263 | dev->status = NULL; | ||
264 | } | ||
265 | } | ||
266 | if (rndis && !dev->status) { | ||
267 | dev_dbg(&intf->dev, "missing RNDIS status endpoint\n"); | ||
268 | usb_set_intfdata(info->data, NULL); | ||
269 | usb_driver_release_interface(driver, info->data); | ||
270 | return -ENODEV; | ||
271 | } | ||
272 | return 0; | ||
273 | |||
274 | bad_desc: | ||
275 | dev_info(&dev->udev->dev, "bad CDC descriptors\n"); | ||
276 | return -ENODEV; | ||
277 | } | ||
278 | EXPORT_SYMBOL_GPL(usbnet_generic_cdc_bind); | ||
279 | |||
280 | void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf) | ||
281 | { | ||
282 | struct cdc_state *info = (void *) &dev->data; | ||
283 | struct usb_driver *driver = driver_of(intf); | ||
284 | |||
285 | /* disconnect master --> disconnect slave */ | ||
286 | if (intf == info->control && info->data) { | ||
287 | /* ensure immediate exit from usbnet_disconnect */ | ||
288 | usb_set_intfdata(info->data, NULL); | ||
289 | usb_driver_release_interface(driver, info->data); | ||
290 | info->data = NULL; | ||
291 | } | ||
292 | |||
293 | /* and vice versa (just in case) */ | ||
294 | else if (intf == info->data && info->control) { | ||
295 | /* ensure immediate exit from usbnet_disconnect */ | ||
296 | usb_set_intfdata(info->control, NULL); | ||
297 | usb_driver_release_interface(driver, info->control); | ||
298 | info->control = NULL; | ||
299 | } | ||
300 | } | ||
301 | EXPORT_SYMBOL_GPL(usbnet_cdc_unbind); | ||
302 | |||
303 | |||
304 | /*------------------------------------------------------------------------- | ||
305 | * | ||
306 | * Communications Device Class, Ethernet Control model | ||
307 | * | ||
308 | * Takes two interfaces. The DATA interface is inactive till an altsetting | ||
309 | * is selected. Configuration data includes class descriptors. There's | ||
310 | * an optional status endpoint on the control interface. | ||
311 | * | ||
312 | * This should interop with whatever the 2.4 "CDCEther.c" driver | ||
313 | * (by Brad Hards) talked with, with more functionality. | ||
314 | * | ||
315 | *-------------------------------------------------------------------------*/ | ||
316 | |||
317 | static void dumpspeed(struct usbnet *dev, __le32 *speeds) | ||
318 | { | ||
319 | if (netif_msg_timer(dev)) | ||
320 | devinfo(dev, "link speeds: %u kbps up, %u kbps down", | ||
321 | __le32_to_cpu(speeds[0]) / 1000, | ||
322 | __le32_to_cpu(speeds[1]) / 1000); | ||
323 | } | ||
324 | |||
325 | static void cdc_status(struct usbnet *dev, struct urb *urb) | ||
326 | { | ||
327 | struct usb_cdc_notification *event; | ||
328 | |||
329 | if (urb->actual_length < sizeof *event) | ||
330 | return; | ||
331 | |||
332 | /* SPEED_CHANGE can get split into two 8-byte packets */ | ||
333 | if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) { | ||
334 | dumpspeed(dev, (__le32 *) urb->transfer_buffer); | ||
335 | return; | ||
336 | } | ||
337 | |||
338 | event = urb->transfer_buffer; | ||
339 | switch (event->bNotificationType) { | ||
340 | case USB_CDC_NOTIFY_NETWORK_CONNECTION: | ||
341 | if (netif_msg_timer(dev)) | ||
342 | devdbg(dev, "CDC: carrier %s", | ||
343 | event->wValue ? "on" : "off"); | ||
344 | if (event->wValue) | ||
345 | netif_carrier_on(dev->net); | ||
346 | else | ||
347 | netif_carrier_off(dev->net); | ||
348 | break; | ||
349 | case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */ | ||
350 | if (netif_msg_timer(dev)) | ||
351 | devdbg(dev, "CDC: speed change (len %d)", | ||
352 | urb->actual_length); | ||
353 | if (urb->actual_length != (sizeof *event + 8)) | ||
354 | set_bit(EVENT_STS_SPLIT, &dev->flags); | ||
355 | else | ||
356 | dumpspeed(dev, (__le32 *) &event[1]); | ||
357 | break; | ||
358 | /* USB_CDC_NOTIFY_RESPONSE_AVAILABLE can happen too (e.g. RNDIS), | ||
359 | * but there are no standard formats for the response data. | ||
360 | */ | ||
361 | default: | ||
362 | deverr(dev, "CDC: unexpected notification %02x!", | ||
363 | event->bNotificationType); | ||
364 | break; | ||
365 | } | ||
366 | } | ||
367 | |||
368 | static u8 nibble(unsigned char c) | ||
369 | { | ||
370 | if (likely(isdigit(c))) | ||
371 | return c - '0'; | ||
372 | c = toupper(c); | ||
373 | if (likely(isxdigit(c))) | ||
374 | return 10 + c - 'A'; | ||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | static inline int | ||
379 | get_ethernet_addr(struct usbnet *dev, struct usb_cdc_ether_desc *e) | ||
380 | { | ||
381 | int tmp, i; | ||
382 | unsigned char buf [13]; | ||
383 | |||
384 | tmp = usb_string(dev->udev, e->iMACAddress, buf, sizeof buf); | ||
385 | if (tmp != 12) { | ||
386 | dev_dbg(&dev->udev->dev, | ||
387 | "bad MAC string %d fetch, %d\n", e->iMACAddress, tmp); | ||
388 | if (tmp >= 0) | ||
389 | tmp = -EINVAL; | ||
390 | return tmp; | ||
391 | } | ||
392 | for (i = tmp = 0; i < 6; i++, tmp += 2) | ||
393 | dev->net->dev_addr [i] = | ||
394 | (nibble(buf [tmp]) << 4) + nibble(buf [tmp + 1]); | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | static int cdc_bind(struct usbnet *dev, struct usb_interface *intf) | ||
399 | { | ||
400 | int status; | ||
401 | struct cdc_state *info = (void *) &dev->data; | ||
402 | |||
403 | status = usbnet_generic_cdc_bind(dev, intf); | ||
404 | if (status < 0) | ||
405 | return status; | ||
406 | |||
407 | status = get_ethernet_addr(dev, info->ether); | ||
408 | if (status < 0) { | ||
409 | usb_set_intfdata(info->data, NULL); | ||
410 | usb_driver_release_interface(driver_of(intf), info->data); | ||
411 | return status; | ||
412 | } | ||
413 | |||
414 | /* FIXME cdc-ether has some multicast code too, though it complains | ||
415 | * in routine cases. info->ether describes the multicast support. | ||
416 | * Implement that here, manipulating the cdc filter as needed. | ||
417 | */ | ||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static const struct driver_info cdc_info = { | ||
422 | .description = "CDC Ethernet Device", | ||
423 | .flags = FLAG_ETHER, | ||
424 | // .check_connect = cdc_check_connect, | ||
425 | .bind = cdc_bind, | ||
426 | .unbind = usbnet_cdc_unbind, | ||
427 | .status = cdc_status, | ||
428 | }; | ||
429 | |||
430 | /*-------------------------------------------------------------------------*/ | ||
431 | |||
432 | |||
433 | static const struct usb_device_id products [] = { | ||
434 | /* | ||
435 | * BLACKLIST !! | ||
436 | * | ||
437 | * First blacklist any products that are egregiously nonconformant | ||
438 | * with the CDC Ethernet specs. Minor braindamage we cope with; when | ||
439 | * they're not even trying, needing a separate driver is only the first | ||
440 | * of the differences to show up. | ||
441 | */ | ||
442 | |||
443 | #define ZAURUS_MASTER_INTERFACE \ | ||
444 | .bInterfaceClass = USB_CLASS_COMM, \ | ||
445 | .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \ | ||
446 | .bInterfaceProtocol = USB_CDC_PROTO_NONE | ||
447 | |||
448 | /* SA-1100 based Sharp Zaurus ("collie"), or compatible; | ||
449 | * wire-incompatible with true CDC Ethernet implementations. | ||
450 | * (And, it seems, needlessly so...) | ||
451 | */ | ||
452 | { | ||
453 | .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | ||
454 | | USB_DEVICE_ID_MATCH_DEVICE, | ||
455 | .idVendor = 0x04DD, | ||
456 | .idProduct = 0x8004, | ||
457 | ZAURUS_MASTER_INTERFACE, | ||
458 | .driver_info = 0, | ||
459 | }, | ||
460 | |||
461 | /* PXA-25x based Sharp Zaurii. Note that it seems some of these | ||
462 | * (later models especially) may have shipped only with firmware | ||
463 | * advertising false "CDC MDLM" compatibility ... but we're not | ||
464 | * clear which models did that, so for now let's assume the worst. | ||
465 | */ | ||
466 | { | ||
467 | .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | ||
468 | | USB_DEVICE_ID_MATCH_DEVICE, | ||
469 | .idVendor = 0x04DD, | ||
470 | .idProduct = 0x8005, /* A-300 */ | ||
471 | ZAURUS_MASTER_INTERFACE, | ||
472 | .driver_info = 0, | ||
473 | }, { | ||
474 | .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | ||
475 | | USB_DEVICE_ID_MATCH_DEVICE, | ||
476 | .idVendor = 0x04DD, | ||
477 | .idProduct = 0x8006, /* B-500/SL-5600 */ | ||
478 | ZAURUS_MASTER_INTERFACE, | ||
479 | .driver_info = 0, | ||
480 | }, { | ||
481 | .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | ||
482 | | USB_DEVICE_ID_MATCH_DEVICE, | ||
483 | .idVendor = 0x04DD, | ||
484 | .idProduct = 0x8007, /* C-700 */ | ||
485 | ZAURUS_MASTER_INTERFACE, | ||
486 | .driver_info = 0, | ||
487 | }, { | ||
488 | .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | ||
489 | | USB_DEVICE_ID_MATCH_DEVICE, | ||
490 | .idVendor = 0x04DD, | ||
491 | .idProduct = 0x9031, /* C-750 C-760 */ | ||
492 | ZAURUS_MASTER_INTERFACE, | ||
493 | .driver_info = 0, | ||
494 | }, { | ||
495 | .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | ||
496 | | USB_DEVICE_ID_MATCH_DEVICE, | ||
497 | .idVendor = 0x04DD, | ||
498 | .idProduct = 0x9032, /* SL-6000 */ | ||
499 | ZAURUS_MASTER_INTERFACE, | ||
500 | .driver_info = 0, | ||
501 | }, { | ||
502 | .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | ||
503 | | USB_DEVICE_ID_MATCH_DEVICE, | ||
504 | .idVendor = 0x04DD, | ||
505 | /* reported with some C860 units */ | ||
506 | .idProduct = 0x9050, /* C-860 */ | ||
507 | ZAURUS_MASTER_INTERFACE, | ||
508 | .driver_info = 0, | ||
509 | }, | ||
510 | |||
511 | /* Olympus has some models with a Zaurus-compatible option. | ||
512 | * R-1000 uses a FreeScale i.MXL cpu (ARMv4T) | ||
513 | */ | ||
514 | { | ||
515 | .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | ||
516 | | USB_DEVICE_ID_MATCH_DEVICE, | ||
517 | .idVendor = 0x07B4, | ||
518 | .idProduct = 0x0F02, /* R-1000 */ | ||
519 | ZAURUS_MASTER_INTERFACE, | ||
520 | .driver_info = 0, | ||
521 | }, | ||
522 | |||
523 | /* | ||
524 | * WHITELIST!!! | ||
525 | * | ||
526 | * CDC Ether uses two interfaces, not necessarily consecutive. | ||
527 | * We match the main interface, ignoring the optional device | ||
528 | * class so we could handle devices that aren't exclusively | ||
529 | * CDC ether. | ||
530 | * | ||
531 | * NOTE: this match must come AFTER entries blacklisting devices | ||
532 | * because of bugs/quirks in a given product (like Zaurus, above). | ||
533 | */ | ||
534 | { | ||
535 | USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET, | ||
536 | USB_CDC_PROTO_NONE), | ||
537 | .driver_info = (unsigned long) &cdc_info, | ||
538 | }, | ||
539 | { }, // END | ||
540 | }; | ||
541 | MODULE_DEVICE_TABLE(usb, products); | ||
542 | |||
543 | static struct usb_driver cdc_driver = { | ||
544 | .name = "cdc_ether", | ||
545 | .id_table = products, | ||
546 | .probe = usbnet_probe, | ||
547 | .disconnect = usbnet_disconnect, | ||
548 | .suspend = usbnet_suspend, | ||
549 | .resume = usbnet_resume, | ||
550 | }; | ||
551 | |||
552 | |||
553 | static int __init cdc_init(void) | ||
554 | { | ||
555 | BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) | ||
556 | < sizeof(struct cdc_state))); | ||
557 | |||
558 | return usb_register(&cdc_driver); | ||
559 | } | ||
560 | module_init(cdc_init); | ||
561 | |||
562 | static void __exit cdc_exit(void) | ||
563 | { | ||
564 | usb_deregister(&cdc_driver); | ||
565 | } | ||
566 | module_exit(cdc_exit); | ||
567 | |||
568 | MODULE_AUTHOR("David Brownell"); | ||
569 | MODULE_DESCRIPTION("USB CDC Ethernet devices"); | ||
570 | MODULE_LICENSE("GPL"); | ||