diff options
Diffstat (limited to 'drivers/usb/net/rndis_host.c')
-rw-r--r-- | drivers/usb/net/rndis_host.c | 81 |
1 files changed, 62 insertions, 19 deletions
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 | }; |