aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/net/rndis_host.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/net/rndis_host.c')
-rw-r--r--drivers/usb/net/rndis_host.c114
1 files changed, 85 insertions, 29 deletions
diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c
index 39a21c74fdf4..980e4aaa97aa 100644
--- a/drivers/usb/net/rndis_host.c
+++ b/drivers/usb/net/rndis_host.c
@@ -253,6 +253,7 @@ struct rndis_keepalive_c { /* IN (optionally OUT) */
253 * of that mess as possible. 253 * of that mess as possible.
254 */ 254 */
255#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101) 255#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101)
256#define OID_GEN_MAXIMUM_FRAME_SIZE ccpu2(0x00010106)
256#define OID_GEN_CURRENT_PACKET_FILTER ccpu2(0x0001010e) 257#define OID_GEN_CURRENT_PACKET_FILTER ccpu2(0x0001010e)
257 258
258/* 259/*
@@ -349,7 +350,7 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
349 case RNDIS_MSG_INDICATE: { /* fault */ 350 case RNDIS_MSG_INDICATE: { /* fault */
350 // struct rndis_indicate *msg = (void *)buf; 351 // struct rndis_indicate *msg = (void *)buf;
351 dev_info(&info->control->dev, 352 dev_info(&info->control->dev,
352 "rndis fault indication\n"); 353 "rndis fault indication\n");
353 } 354 }
354 break; 355 break;
355 case RNDIS_MSG_KEEPALIVE: { /* ping */ 356 case RNDIS_MSG_KEEPALIVE: { /* ping */
@@ -387,6 +388,71 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
387 return -ETIMEDOUT; 388 return -ETIMEDOUT;
388} 389}
389 390
391/*
392 * rndis_query:
393 *
394 * Performs a query for @oid along with 0 or more bytes of payload as
395 * specified by @in_len. If @reply_len is not set to -1 then the reply
396 * length is checked against this value, resulting in an error if it
397 * doesn't match.
398 *
399 * NOTE: Adding a payload exactly or greater than the size of the expected
400 * response payload is an evident requirement MSFT added for ActiveSync.
401 *
402 * The only exception is for OIDs that return a variably sized response,
403 * in which case no payload should be added. This undocumented (and
404 * nonsensical!) issue was found by sniffing protocol requests from the
405 * ActiveSync 4.1 Windows driver.
406 */
407static int rndis_query(struct usbnet *dev, struct usb_interface *intf,
408 void *buf, u32 oid, u32 in_len,
409 void **reply, int *reply_len)
410{
411 int retval;
412 union {
413 void *buf;
414 struct rndis_msg_hdr *header;
415 struct rndis_query *get;
416 struct rndis_query_c *get_c;
417 } u;
418 u32 off, len;
419
420 u.buf = buf;
421
422 memset(u.get, 0, sizeof *u.get + in_len);
423 u.get->msg_type = RNDIS_MSG_QUERY;
424 u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len);
425 u.get->oid = oid;
426 u.get->len = cpu_to_le32(in_len);
427 u.get->offset = ccpu2(20);
428
429 retval = rndis_command(dev, u.header);
430 if (unlikely(retval < 0)) {
431 dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n",
432 oid, retval);
433 return retval;
434 }
435
436 off = le32_to_cpu(u.get_c->offset);
437 len = le32_to_cpu(u.get_c->len);
438 if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE))
439 goto response_error;
440
441 if (*reply_len != -1 && len != *reply_len)
442 goto response_error;
443
444 *reply = (unsigned char *) &u.get_c->request_id + off;
445 *reply_len = len;
446
447 return retval;
448
449response_error:
450 dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) "
451 "invalid response - off %d len %d\n",
452 oid, off, len);
453 return -EDOM;
454}
455
390static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) 456static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
391{ 457{
392 int retval; 458 int retval;
@@ -403,6 +469,8 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
403 struct rndis_set_c *set_c; 469 struct rndis_set_c *set_c;
404 } u; 470 } u;
405 u32 tmp; 471 u32 tmp;
472 int reply_len;
473 unsigned char *bp;
406 474
407 /* we can't rely on i/o from stack working, or stack allocation */ 475 /* we can't rely on i/o from stack working, or stack allocation */
408 u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); 476 u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
@@ -421,6 +489,12 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
421 * TX we'll stick to one Ethernet packet plus RNDIS framing. 489 * TX we'll stick to one Ethernet packet plus RNDIS framing.
422 * For RX we handle drivers that zero-pad to end-of-packet. 490 * For RX we handle drivers that zero-pad to end-of-packet.
423 * Don't let userspace change these settings. 491 * Don't let userspace change these settings.
492 *
493 * NOTE: there still seems to be wierdness here, as if we need
494 * to do some more things to make sure WinCE targets accept this.
495 * They default to jumbograms of 8KB or 16KB, which is absurd
496 * for such low data rates and which is also more than Linux
497 * can usually expect to allocate for SKB data...
424 */ 498 */
425 net->hard_header_len += sizeof (struct rndis_data_hdr); 499 net->hard_header_len += sizeof (struct rndis_data_hdr);
426 dev->hard_mtu = net->mtu + net->hard_header_len; 500 dev->hard_mtu = net->mtu + net->hard_header_len;
@@ -434,7 +508,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
434 if (unlikely(retval < 0)) { 508 if (unlikely(retval < 0)) {
435 /* it might not even be an RNDIS device!! */ 509 /* it might not even be an RNDIS device!! */
436 dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); 510 dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
437 goto fail_and_release; 511 goto fail_and_release;
438 } 512 }
439 tmp = le32_to_cpu(u.init_c->max_transfer_size); 513 tmp = le32_to_cpu(u.init_c->max_transfer_size);
440 if (tmp < dev->hard_mtu) { 514 if (tmp < dev->hard_mtu) {
@@ -450,34 +524,15 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
450 dev->hard_mtu, tmp, dev->rx_urb_size, 524 dev->hard_mtu, tmp, dev->rx_urb_size,
451 1 << le32_to_cpu(u.init_c->packet_alignment)); 525 1 << le32_to_cpu(u.init_c->packet_alignment));
452 526
453 /* Get designated host ethernet address. 527 /* Get designated host ethernet address */
454 * 528 reply_len = ETH_ALEN;
455 * Adding a payload exactly the same size as the expected response 529 retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
456 * payload is an evident requirement MSFT added for ActiveSync. 530 48, (void **) &bp, &reply_len);
457 * This undocumented (and nonsensical) issue was found by sniffing 531 if (unlikely(retval< 0)) {
458 * protocol requests from the ActiveSync 4.1 Windows driver.
459 */
460 memset(u.get, 0, sizeof *u.get + 48);
461 u.get->msg_type = RNDIS_MSG_QUERY;
462 u.get->msg_len = ccpu2(sizeof *u.get + 48);
463 u.get->oid = OID_802_3_PERMANENT_ADDRESS;
464 u.get->len = ccpu2(48);
465 u.get->offset = ccpu2(20);
466
467 retval = rndis_command(dev, u.header);
468 if (unlikely(retval < 0)) {
469 dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval); 532 dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
470 goto fail_and_release; 533 goto fail_and_release;
471 } 534 }
472 tmp = le32_to_cpu(u.get_c->offset); 535 memcpy(net->dev_addr, bp, ETH_ALEN);
473 if (unlikely((tmp + 8) > (CONTROL_BUFFER_SIZE - ETH_ALEN)
474 || u.get_c->len != ccpu2(ETH_ALEN))) {
475 dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n",
476 tmp, le32_to_cpu(u.get_c->len));
477 retval = -EDOM;
478 goto fail_and_release;
479 }
480 memcpy(net->dev_addr, tmp + (char *)&u.get_c->request_id, ETH_ALEN);
481 536
482 /* set a nonzero filter to enable data transfers */ 537 /* set a nonzero filter to enable data transfers */
483 memset(u.set, 0, sizeof *u.set); 538 memset(u.set, 0, sizeof *u.set);
@@ -502,6 +557,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
502fail_and_release: 557fail_and_release:
503 usb_set_intfdata(info->data, NULL); 558 usb_set_intfdata(info->data, NULL);
504 usb_driver_release_interface(driver_of(intf), info->data); 559 usb_driver_release_interface(driver_of(intf), info->data);
560 info->data = NULL;
505fail: 561fail:
506 kfree(u.buf); 562 kfree(u.buf);
507 return retval; 563 return retval;
@@ -588,7 +644,7 @@ rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
588 if (likely((sizeof *hdr) <= room)) { 644 if (likely((sizeof *hdr) <= room)) {
589 skb->data = memmove(skb->head + sizeof *hdr, 645 skb->data = memmove(skb->head + sizeof *hdr,
590 skb->data, len); 646 skb->data, len);
591 skb->tail = skb->data + len; 647 skb_set_tail_pointer(skb, len);
592 goto fill; 648 goto fill;
593 } 649 }
594 } 650 }
@@ -618,7 +674,7 @@ fill:
618 674
619static const struct driver_info rndis_info = { 675static const struct driver_info rndis_info = {
620 .description = "RNDIS device", 676 .description = "RNDIS device",
621 .flags = FLAG_ETHER | FLAG_FRAMING_RN, 677 .flags = FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
622 .bind = rndis_bind, 678 .bind = rndis_bind,
623 .unbind = rndis_unbind, 679 .unbind = rndis_unbind,
624 .status = rndis_status, 680 .status = rndis_status,