aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/net/rndis_host.c
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/rndis_host.c
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/rndis_host.c')
-rw-r--r--drivers/usb/net/rndis_host.c81
1 files changed, 62 insertions, 19 deletions
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};