diff options
author | Ole Andre Vadla Ravnas <oleavr@gmail.com> | 2006-12-14 19:01:28 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-02-07 18:44:33 -0500 |
commit | ad55d71a3d4401f44b4ddee1412283c99eedd05c (patch) | |
tree | a416fe386d740506b01667d47466e6708f8a2388 /drivers/usb/net | |
parent | 11d5489873facd395653a4ee14669751bfe9bab5 (diff) |
rndis_host learns ActiveSync basics
Windows Mobile 5 based devices described as supporting "ActiveSync":
- Speak RNDIS but lack the CDC and union descriptors. This patch
updates the cdc ethernet code to fake ACM descriptors we need.
- Require RNDIS_MSG_QUERY messages to include a buffer of the size the
response should generate. This patch updates the rndis host code to
pass this will-be-ignored data.
The resulting RNDIS host code has been reported to work with several
WM5 based devices.
(Note that a fancier patch is available at synce.sf.net.)
Some bugfixes, affecting not just ActiveSync:
(a) when cleaning up after RNDS init fails, scrub the second interface
just like cdc_ether does, so disconnect won't oops.
(b) handle peripherals that use the pad-to-end-of-packet option; some
devices can't talk to us if that option doesn't work.
(c) when choosing configurations, don't forget about an RNDIS config
just because the RNDIS driver is dynamically linked.
Cleanup, streamlining, bugfixes, Kconfig, and matching hub driver update.
Also for paranoia's sake, refuse to talk to something that looks like a
real modem instead of RNDIS.
Signed-off-by: Ole Andre Vadla Ravnaas <oleavr@gmail.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/net')
-rw-r--r-- | drivers/usb/net/Kconfig | 6 | ||||
-rw-r--r-- | drivers/usb/net/cdc_ether.c | 60 | ||||
-rw-r--r-- | drivers/usb/net/rndis_host.c | 81 |
3 files changed, 124 insertions, 23 deletions
diff --git a/drivers/usb/net/Kconfig b/drivers/usb/net/Kconfig index e081836014ac..a2b94ef512bc 100644 --- a/drivers/usb/net/Kconfig +++ b/drivers/usb/net/Kconfig | |||
@@ -222,13 +222,15 @@ config USB_NET_MCS7830 | |||
222 | adapters marketed under the DeLOCK brand. | 222 | adapters marketed under the DeLOCK brand. |
223 | 223 | ||
224 | config USB_NET_RNDIS_HOST | 224 | config USB_NET_RNDIS_HOST |
225 | tristate "Host for RNDIS devices (EXPERIMENTAL)" | 225 | tristate "Host for RNDIS and ActiveSync devices (EXPERIMENTAL)" |
226 | depends on USB_USBNET && EXPERIMENTAL | 226 | depends on USB_USBNET && EXPERIMENTAL |
227 | select USB_NET_CDCETHER | 227 | select USB_NET_CDCETHER |
228 | help | 228 | help |
229 | This option enables hosting "Remote NDIS" USB networking links, | 229 | This option enables hosting "Remote NDIS" USB networking links, |
230 | as encouraged by Microsoft (instead of CDC Ethernet!) for use in | 230 | as encouraged by Microsoft (instead of CDC Ethernet!) for use in |
231 | various devices that may only support this protocol. | 231 | various devices that may only support this protocol. A variant |
232 | of this protocol (with even less public documentation) seems to | ||
233 | be at the root of Microsoft's "ActiveSync" too. | ||
232 | 234 | ||
233 | Avoid using this protocol unless you have no better options. | 235 | Avoid using this protocol unless you have no better options. |
234 | The protocol specification is incomplete, and is controlled by | 236 | The protocol specification is incomplete, and is controlled by |
diff --git a/drivers/usb/net/cdc_ether.c b/drivers/usb/net/cdc_ether.c index 44a91547146e..e5cdafa258dd 100644 --- a/drivers/usb/net/cdc_ether.c +++ b/drivers/usb/net/cdc_ether.c | |||
@@ -1,6 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * CDC Ethernet based networking peripherals | 2 | * CDC Ethernet based networking peripherals |
3 | * Copyright (C) 2003-2005 by David Brownell | 3 | * Copyright (C) 2003-2005 by David Brownell |
4 | * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync) | ||
4 | * | 5 | * |
5 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
@@ -35,6 +36,29 @@ | |||
35 | #include "usbnet.h" | 36 | #include "usbnet.h" |
36 | 37 | ||
37 | 38 | ||
39 | #if defined(CONFIG_USB_NET_RNDIS_HOST) || defined(CONFIG_USB_NET_RNDIS_HOST_MODULE) | ||
40 | |||
41 | static int is_rndis(struct usb_interface_descriptor *desc) | ||
42 | { | ||
43 | return desc->bInterfaceClass == USB_CLASS_COMM | ||
44 | && desc->bInterfaceSubClass == 2 | ||
45 | && desc->bInterfaceProtocol == 0xff; | ||
46 | } | ||
47 | |||
48 | static int is_activesync(struct usb_interface_descriptor *desc) | ||
49 | { | ||
50 | return desc->bInterfaceClass == USB_CLASS_MISC | ||
51 | && desc->bInterfaceSubClass == 1 | ||
52 | && desc->bInterfaceProtocol == 1; | ||
53 | } | ||
54 | |||
55 | #else | ||
56 | |||
57 | #define is_rndis(desc) 0 | ||
58 | #define is_activesync(desc) 0 | ||
59 | |||
60 | #endif | ||
61 | |||
38 | /* | 62 | /* |
39 | * probes control interface, claims data interface, collects the bulk | 63 | * probes control interface, claims data interface, collects the bulk |
40 | * endpoints, activates data interface (if needed), maybe sets MTU. | 64 | * endpoints, activates data interface (if needed), maybe sets MTU. |
@@ -71,7 +95,8 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) | |||
71 | /* this assumes that if there's a non-RNDIS vendor variant | 95 | /* this assumes that if there's a non-RNDIS vendor variant |
72 | * of cdc-acm, it'll fail RNDIS requests cleanly. | 96 | * of cdc-acm, it'll fail RNDIS requests cleanly. |
73 | */ | 97 | */ |
74 | rndis = (intf->cur_altsetting->desc.bInterfaceProtocol == 0xff); | 98 | rndis = is_rndis(&intf->cur_altsetting->desc) |
99 | || is_activesync(&intf->cur_altsetting->desc); | ||
75 | 100 | ||
76 | memset(info, 0, sizeof *info); | 101 | memset(info, 0, sizeof *info); |
77 | info->control = intf; | 102 | info->control = intf; |
@@ -99,6 +124,23 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) | |||
99 | goto bad_desc; | 124 | goto bad_desc; |
100 | } | 125 | } |
101 | break; | 126 | break; |
127 | case USB_CDC_ACM_TYPE: | ||
128 | /* paranoia: disambiguate a "real" vendor-specific | ||
129 | * modem interface from an RNDIS non-modem. | ||
130 | */ | ||
131 | if (rndis) { | ||
132 | struct usb_cdc_acm_descriptor *d; | ||
133 | |||
134 | d = (void *) buf; | ||
135 | if (d->bmCapabilities) { | ||
136 | dev_dbg(&intf->dev, | ||
137 | "ACM capabilities %02x, " | ||
138 | "not really RNDIS?\n", | ||
139 | d->bmCapabilities); | ||
140 | goto bad_desc; | ||
141 | } | ||
142 | } | ||
143 | break; | ||
102 | case USB_CDC_UNION_TYPE: | 144 | case USB_CDC_UNION_TYPE: |
103 | if (info->u) { | 145 | if (info->u) { |
104 | dev_dbg(&intf->dev, "extra CDC union\n"); | 146 | dev_dbg(&intf->dev, "extra CDC union\n"); |
@@ -171,7 +213,21 @@ next_desc: | |||
171 | buf += buf [0]; | 213 | buf += buf [0]; |
172 | } | 214 | } |
173 | 215 | ||
174 | if (!info->header || !info->u || (!rndis && !info->ether)) { | 216 | /* Microsoft ActiveSync based RNDIS devices lack the CDC descriptors, |
217 | * so we'll hard-wire the interfaces and not check for descriptors. | ||
218 | */ | ||
219 | if (is_activesync(&intf->cur_altsetting->desc) && !info->u) { | ||
220 | info->control = usb_ifnum_to_if(dev->udev, 0); | ||
221 | info->data = usb_ifnum_to_if(dev->udev, 1); | ||
222 | if (!info->control || !info->data) { | ||
223 | dev_dbg(&intf->dev, | ||
224 | "activesync: master #0/%p slave #1/%p\n", | ||
225 | info->control, | ||
226 | info->data); | ||
227 | goto bad_desc; | ||
228 | } | ||
229 | |||
230 | } else if (!info->header || !info->u || (!rndis && !info->ether)) { | ||
175 | dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n", | 231 | dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n", |
176 | info->header ? "" : "header ", | 232 | info->header ? "" : "header ", |
177 | info->u ? "" : "union ", | 233 | info->u ? "" : "union ", |
diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c index a322a16d9cf8..be888d2d813c 100644 --- a/drivers/usb/net/rndis_host.c +++ b/drivers/usb/net/rndis_host.c | |||
@@ -49,6 +49,8 @@ | |||
49 | * - In some cases, MS-Windows will emit undocumented requests; this | 49 | * - In some cases, MS-Windows will emit undocumented requests; this |
50 | * matters more to peripheral implementations than host ones. | 50 | * matters more to peripheral implementations than host ones. |
51 | * | 51 | * |
52 | * Moreover there's a no-open-specs variant of RNDIS called "ActiveSync". | ||
53 | * | ||
52 | * For these reasons and others, ** USE OF RNDIS IS STRONGLY DISCOURAGED ** in | 54 | * For these reasons and others, ** USE OF RNDIS IS STRONGLY DISCOURAGED ** in |
53 | * favor of such non-proprietary alternatives as CDC Ethernet or the newer (and | 55 | * favor of such non-proprietary alternatives as CDC Ethernet or the newer (and |
54 | * currently rare) "Ethernet Emulation Model" (EEM). | 56 | * currently rare) "Ethernet Emulation Model" (EEM). |
@@ -61,6 +63,9 @@ | |||
61 | * - control-in: GET_ENCAPSULATED | 63 | * - control-in: GET_ENCAPSULATED |
62 | * | 64 | * |
63 | * We'll try to ignore the RESPONSE_AVAILABLE notifications. | 65 | * We'll try to ignore the RESPONSE_AVAILABLE notifications. |
66 | * | ||
67 | * REVISIT some RNDIS implementations seem to have curious issues still | ||
68 | * to be resolved. | ||
64 | */ | 69 | */ |
65 | struct rndis_msg_hdr { | 70 | struct rndis_msg_hdr { |
66 | __le32 msg_type; /* RNDIS_MSG_* */ | 71 | __le32 msg_type; /* RNDIS_MSG_* */ |
@@ -71,8 +76,14 @@ struct rndis_msg_hdr { | |||
71 | // ... and more | 76 | // ... and more |
72 | } __attribute__ ((packed)); | 77 | } __attribute__ ((packed)); |
73 | 78 | ||
74 | /* RNDIS defines this (absurdly huge) control timeout */ | 79 | /* MS-Windows uses this strange size, but RNDIS spec says 1024 minimum */ |
75 | #define RNDIS_CONTROL_TIMEOUT_MS (10 * 1000) | 80 | #define CONTROL_BUFFER_SIZE 1025 |
81 | |||
82 | /* RNDIS defines an (absurdly huge) 10 second control timeout, | ||
83 | * but ActiveSync seems to use a more usual 5 second timeout | ||
84 | * (which matches the USB 2.0 spec). | ||
85 | */ | ||
86 | #define RNDIS_CONTROL_TIMEOUT_MS (5 * 1000) | ||
76 | 87 | ||
77 | 88 | ||
78 | #define ccpu2 __constant_cpu_to_le32 | 89 | #define ccpu2 __constant_cpu_to_le32 |
@@ -270,6 +281,7 @@ static void rndis_status(struct usbnet *dev, struct urb *urb) | |||
270 | static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) | 281 | static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) |
271 | { | 282 | { |
272 | struct cdc_state *info = (void *) &dev->data; | 283 | struct cdc_state *info = (void *) &dev->data; |
284 | int master_ifnum; | ||
273 | int retval; | 285 | int retval; |
274 | unsigned count; | 286 | unsigned count; |
275 | __le32 rsp; | 287 | __le32 rsp; |
@@ -279,7 +291,7 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) | |||
279 | * disconnect(): either serialize, or dispatch responses on xid | 291 | * disconnect(): either serialize, or dispatch responses on xid |
280 | */ | 292 | */ |
281 | 293 | ||
282 | /* Issue the request; don't bother byteswapping our xid */ | 294 | /* Issue the request; xid is unique, don't bother byteswapping it */ |
283 | if (likely(buf->msg_type != RNDIS_MSG_HALT | 295 | if (likely(buf->msg_type != RNDIS_MSG_HALT |
284 | && buf->msg_type != RNDIS_MSG_RESET)) { | 296 | && buf->msg_type != RNDIS_MSG_RESET)) { |
285 | xid = dev->xid++; | 297 | xid = dev->xid++; |
@@ -287,11 +299,12 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) | |||
287 | xid = dev->xid++; | 299 | xid = dev->xid++; |
288 | buf->request_id = (__force __le32) xid; | 300 | buf->request_id = (__force __le32) xid; |
289 | } | 301 | } |
302 | master_ifnum = info->control->cur_altsetting->desc.bInterfaceNumber; | ||
290 | retval = usb_control_msg(dev->udev, | 303 | retval = usb_control_msg(dev->udev, |
291 | usb_sndctrlpipe(dev->udev, 0), | 304 | usb_sndctrlpipe(dev->udev, 0), |
292 | USB_CDC_SEND_ENCAPSULATED_COMMAND, | 305 | USB_CDC_SEND_ENCAPSULATED_COMMAND, |
293 | USB_TYPE_CLASS | USB_RECIP_INTERFACE, | 306 | USB_TYPE_CLASS | USB_RECIP_INTERFACE, |
294 | 0, info->u->bMasterInterface0, | 307 | 0, master_ifnum, |
295 | buf, le32_to_cpu(buf->msg_len), | 308 | buf, le32_to_cpu(buf->msg_len), |
296 | RNDIS_CONTROL_TIMEOUT_MS); | 309 | RNDIS_CONTROL_TIMEOUT_MS); |
297 | if (unlikely(retval < 0 || xid == 0)) | 310 | if (unlikely(retval < 0 || xid == 0)) |
@@ -306,13 +319,13 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) | |||
306 | */ | 319 | */ |
307 | rsp = buf->msg_type | RNDIS_MSG_COMPLETION; | 320 | rsp = buf->msg_type | RNDIS_MSG_COMPLETION; |
308 | for (count = 0; count < 10; count++) { | 321 | for (count = 0; count < 10; count++) { |
309 | memset(buf, 0, 1024); | 322 | memset(buf, 0, CONTROL_BUFFER_SIZE); |
310 | retval = usb_control_msg(dev->udev, | 323 | retval = usb_control_msg(dev->udev, |
311 | usb_rcvctrlpipe(dev->udev, 0), | 324 | usb_rcvctrlpipe(dev->udev, 0), |
312 | USB_CDC_GET_ENCAPSULATED_RESPONSE, | 325 | USB_CDC_GET_ENCAPSULATED_RESPONSE, |
313 | USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, | 326 | USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, |
314 | 0, info->u->bMasterInterface0, | 327 | 0, master_ifnum, |
315 | buf, 1024, | 328 | buf, CONTROL_BUFFER_SIZE, |
316 | RNDIS_CONTROL_TIMEOUT_MS); | 329 | RNDIS_CONTROL_TIMEOUT_MS); |
317 | if (likely(retval >= 8)) { | 330 | if (likely(retval >= 8)) { |
318 | msg_len = le32_to_cpu(buf->msg_len); | 331 | msg_len = le32_to_cpu(buf->msg_len); |
@@ -350,7 +363,7 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) | |||
350 | usb_sndctrlpipe(dev->udev, 0), | 363 | usb_sndctrlpipe(dev->udev, 0), |
351 | USB_CDC_SEND_ENCAPSULATED_COMMAND, | 364 | USB_CDC_SEND_ENCAPSULATED_COMMAND, |
352 | USB_TYPE_CLASS | USB_RECIP_INTERFACE, | 365 | USB_TYPE_CLASS | USB_RECIP_INTERFACE, |
353 | 0, info->u->bMasterInterface0, | 366 | 0, master_ifnum, |
354 | msg, sizeof *msg, | 367 | msg, sizeof *msg, |
355 | RNDIS_CONTROL_TIMEOUT_MS); | 368 | RNDIS_CONTROL_TIMEOUT_MS); |
356 | if (unlikely(retval < 0)) | 369 | if (unlikely(retval < 0)) |
@@ -393,38 +406,64 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) | |||
393 | u32 tmp; | 406 | u32 tmp; |
394 | 407 | ||
395 | /* we can't rely on i/o from stack working, or stack allocation */ | 408 | /* we can't rely on i/o from stack working, or stack allocation */ |
396 | u.buf = kmalloc(1024, GFP_KERNEL); | 409 | u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); |
397 | if (!u.buf) | 410 | if (!u.buf) |
398 | return -ENOMEM; | 411 | return -ENOMEM; |
399 | retval = usbnet_generic_cdc_bind(dev, intf); | 412 | retval = usbnet_generic_cdc_bind(dev, intf); |
400 | if (retval < 0) | 413 | if (retval < 0) |
401 | goto fail; | 414 | goto fail; |
402 | 415 | ||
403 | net->hard_header_len += sizeof (struct rndis_data_hdr); | ||
404 | |||
405 | /* initialize; max transfer is 16KB at full speed */ | ||
406 | u.init->msg_type = RNDIS_MSG_INIT; | 416 | u.init->msg_type = RNDIS_MSG_INIT; |
407 | u.init->msg_len = ccpu2(sizeof *u.init); | 417 | u.init->msg_len = ccpu2(sizeof *u.init); |
408 | u.init->major_version = ccpu2(1); | 418 | u.init->major_version = ccpu2(1); |
409 | u.init->minor_version = ccpu2(0); | 419 | u.init->minor_version = ccpu2(0); |
410 | u.init->max_transfer_size = ccpu2(net->mtu + net->hard_header_len); | ||
411 | 420 | ||
421 | /* max transfer (in spec) is 0x4000 at full speed, but for | ||
422 | * TX we'll stick to one Ethernet packet plus RNDIS framing. | ||
423 | * For RX we handle drivers that zero-pad to end-of-packet. | ||
424 | * Don't let userspace change these settings. | ||
425 | */ | ||
426 | net->hard_header_len += sizeof (struct rndis_data_hdr); | ||
427 | dev->hard_mtu = net->mtu + net->hard_header_len; | ||
428 | |||
429 | dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1); | ||
430 | dev->rx_urb_size &= ~(dev->maxpacket - 1); | ||
431 | u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size); | ||
432 | |||
433 | net->change_mtu = NULL; | ||
412 | retval = rndis_command(dev, u.header); | 434 | retval = rndis_command(dev, u.header); |
413 | if (unlikely(retval < 0)) { | 435 | if (unlikely(retval < 0)) { |
414 | /* it might not even be an RNDIS device!! */ | 436 | /* it might not even be an RNDIS device!! */ |
415 | dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); | 437 | dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); |
438 | goto fail_and_release; | ||
439 | } | ||
440 | tmp = le32_to_cpu(u.init_c->max_transfer_size); | ||
441 | if (tmp < dev->hard_mtu) { | ||
442 | dev_err(&intf->dev, | ||
443 | "dev can't take %u byte packets (max %u)\n", | ||
444 | dev->hard_mtu, tmp); | ||
416 | goto fail_and_release; | 445 | goto fail_and_release; |
417 | } | 446 | } |
418 | dev->hard_mtu = le32_to_cpu(u.init_c->max_transfer_size); | 447 | |
419 | /* REVISIT: peripheral "alignment" request is ignored ... */ | 448 | /* REVISIT: peripheral "alignment" request is ignored ... */ |
420 | dev_dbg(&intf->dev, "hard mtu %u, align %d\n", dev->hard_mtu, | 449 | dev_dbg(&intf->dev, |
450 | "hard mtu %u (%u from dev), rx buflen %Zu, align %d\n", | ||
451 | dev->hard_mtu, tmp, dev->rx_urb_size, | ||
421 | 1 << le32_to_cpu(u.init_c->packet_alignment)); | 452 | 1 << le32_to_cpu(u.init_c->packet_alignment)); |
422 | 453 | ||
423 | /* get designated host ethernet address */ | 454 | /* Get designated host ethernet address. |
424 | memset(u.get, 0, sizeof *u.get); | 455 | * |
456 | * Adding a payload exactly the same size as the expected response | ||
457 | * payload is an evident requirement MSFT added for ActiveSync. | ||
458 | * This undocumented (and nonsensical) issue was found by sniffing | ||
459 | * protocol requests from the ActiveSync 4.1 Windows driver. | ||
460 | */ | ||
461 | memset(u.get, 0, sizeof *u.get + 48); | ||
425 | u.get->msg_type = RNDIS_MSG_QUERY; | 462 | u.get->msg_type = RNDIS_MSG_QUERY; |
426 | u.get->msg_len = ccpu2(sizeof *u.get); | 463 | u.get->msg_len = ccpu2(sizeof *u.get + 48); |
427 | u.get->oid = OID_802_3_PERMANENT_ADDRESS; | 464 | u.get->oid = OID_802_3_PERMANENT_ADDRESS; |
465 | u.get->len = ccpu2(48); | ||
466 | u.get->offset = ccpu2(20); | ||
428 | 467 | ||
429 | retval = rndis_command(dev, u.header); | 468 | retval = rndis_command(dev, u.header); |
430 | if (unlikely(retval < 0)) { | 469 | if (unlikely(retval < 0)) { |
@@ -432,7 +471,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) | |||
432 | goto fail_and_release; | 471 | goto fail_and_release; |
433 | } | 472 | } |
434 | tmp = le32_to_cpu(u.get_c->offset); | 473 | tmp = le32_to_cpu(u.get_c->offset); |
435 | if (unlikely((tmp + 8) > (1024 - ETH_ALEN) | 474 | if (unlikely((tmp + 8) > (CONTROL_BUFFER_SIZE - ETH_ALEN) |
436 | || u.get_c->len != ccpu2(ETH_ALEN))) { | 475 | || u.get_c->len != ccpu2(ETH_ALEN))) { |
437 | dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n", | 476 | dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n", |
438 | tmp, le32_to_cpu(u.get_c->len)); | 477 | tmp, le32_to_cpu(u.get_c->len)); |
@@ -598,6 +637,10 @@ static const struct usb_device_id products [] = { | |||
598 | /* RNDIS is MSFT's un-official variant of CDC ACM */ | 637 | /* RNDIS is MSFT's un-official variant of CDC ACM */ |
599 | USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), | 638 | USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), |
600 | .driver_info = (unsigned long) &rndis_info, | 639 | .driver_info = (unsigned long) &rndis_info, |
640 | }, { | ||
641 | /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ | ||
642 | USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), | ||
643 | .driver_info = (unsigned long) &rndis_info, | ||
601 | }, | 644 | }, |
602 | { }, // END | 645 | { }, // END |
603 | }; | 646 | }; |