diff options
author | David Brownell <david-b@pacbell.net> | 2007-04-17 20:53:20 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-04-27 16:28:40 -0400 |
commit | ddda08624013e8435e9f7cfc34a35bd7b3520b6d (patch) | |
tree | 4d3f763ea916ba299ce2897c0ee934ccc801c0a5 | |
parent | aa2ce5ca6be480cb139e21258671c2c27826f8ff (diff) |
USB: rndis_host, various cleanups
Cleanups to the rndis_host code, and a tweak that helps talking to
PXA hardware. Mostly from Ole André Vadla Ravnås <oleavr@gmail.com>
- Prevent SET_INTERFACE requests, they give PXA hardware bad indigestion
- For paranoia, null a pointer after freeing its data
- Wrap up ActiveSync oddities for RNDIS_QUERY in one routine
- Use that wrapper when getting the Ethernet address
- Whitespace fixes
Plus add a comment noting the open issues about some RNDIS clients still
needing TBD kinds of browbeating to accept non-jumbogram packets.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/net/rndis_host.c | 112 |
1 files changed, 84 insertions, 28 deletions
diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c index 1d36772ba6e1..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 | */ | ||
407 | static 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 | |||
449 | response_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 | |||
390 | static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) | 456 | static 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) | |||
502 | fail_and_release: | 557 | fail_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; | ||
505 | fail: | 561 | fail: |
506 | kfree(u.buf); | 562 | kfree(u.buf); |
507 | return retval; | 563 | return retval; |
@@ -618,7 +674,7 @@ fill: | |||
618 | 674 | ||
619 | static const struct driver_info rndis_info = { | 675 | static 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, |