aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/net
diff options
context:
space:
mode:
authorOle Andre Vadla Ravnas <oleavr@gmail.com>2006-12-14 19:01:28 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2007-02-07 18:44:33 -0500
commitad55d71a3d4401f44b4ddee1412283c99eedd05c (patch)
treea416fe386d740506b01667d47466e6708f8a2388 /drivers/usb/net
parent11d5489873facd395653a4ee14669751bfe9bab5 (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/Kconfig6
-rw-r--r--drivers/usb/net/cdc_ether.c60
-rw-r--r--drivers/usb/net/rndis_host.c81
3 files changed, 124 insertions, 23 deletions
diff --git a/drivers/usb/net/Kconfig b/drivers/usb/net/Kconfig
index e081836014a..a2b94ef512b 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
224config USB_NET_RNDIS_HOST 224config 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 44a91547146..e5cdafa258d 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
41static 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
48static 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 a322a16d9cf..be888d2d813 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 */
65struct rndis_msg_hdr { 70struct 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)
270static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) 281static 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};