diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-07 22:23:21 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-07 22:23:21 -0500 |
commit | c96e2c92072d3e78954c961f53d8c7352f7abbd7 (patch) | |
tree | d844f26f926ff40e98e9eae0e11fd71acad81df4 /drivers/usb/net | |
parent | f2aca47dc3c2d0c2d5dbd972558557e74232bbce (diff) | |
parent | 64358164f5bfe5e11d4040c1eb674c29e1436ce5 (diff) |
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6: (70 commits)
USB: remove duplicate device id from zc0301
USB: remove duplicate device id from usb_storage
USB: remove duplicate device id from keyspan
USB: remove duplicate device id from ftdi_sio
USB: remove duplicate device id from visor
USB: a bit more coding style cleanup
usbcore: trivial whitespace fixes
usb-storage: use first bulk endpoints, not last
EHCI: fix interrupt-driven remote wakeup
USB: switch ehci-hcd to new polling scheme
USB: autosuspend for usb printer driver
USB Input: Added kernel module to support all GTCO CalComp USB InterWrite School products
USB: Sierra Wireless auto set D0
USB: usb ethernet gadget recognizes HUSB2DEV
USB: list atmel husb2_udc gadget controller
USB: gadgetfs AIO tweaks
USB: gadgetfs behaves better on userspace init bug
USB: gadgetfs race fix
USB: gadgetfs simplifications
USB: gadgetfs cleanups
...
Diffstat (limited to 'drivers/usb/net')
-rw-r--r-- | drivers/usb/net/Kconfig | 6 | ||||
-rw-r--r-- | drivers/usb/net/cdc_ether.c | 60 | ||||
-rw-r--r-- | drivers/usb/net/kaweth.c | 37 | ||||
-rw-r--r-- | drivers/usb/net/rndis_host.c | 81 |
4 files changed, 156 insertions, 28 deletions
diff --git a/drivers/usb/net/Kconfig b/drivers/usb/net/Kconfig index e081836014ac..a2b94ef512bc 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 | ||
224 | config USB_NET_RNDIS_HOST | 224 | config 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 44a91547146e..e5cdafa258dd 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 | |||
41 | static 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 | |||
48 | static 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/kaweth.c b/drivers/usb/net/kaweth.c index fa78326d0bf0..36a989160a68 100644 --- a/drivers/usb/net/kaweth.c +++ b/drivers/usb/net/kaweth.c | |||
@@ -179,6 +179,7 @@ static struct usb_driver kaweth_driver = { | |||
179 | .suspend = kaweth_suspend, | 179 | .suspend = kaweth_suspend, |
180 | .resume = kaweth_resume, | 180 | .resume = kaweth_resume, |
181 | .id_table = usb_klsi_table, | 181 | .id_table = usb_klsi_table, |
182 | .supports_autosuspend = 1, | ||
182 | }; | 183 | }; |
183 | 184 | ||
184 | typedef __u8 eth_addr_t[6]; | 185 | typedef __u8 eth_addr_t[6]; |
@@ -225,6 +226,7 @@ struct kaweth_device | |||
225 | struct delayed_work lowmem_work; | 226 | struct delayed_work lowmem_work; |
226 | 227 | ||
227 | struct usb_device *dev; | 228 | struct usb_device *dev; |
229 | struct usb_interface *intf; | ||
228 | struct net_device *net; | 230 | struct net_device *net; |
229 | wait_queue_head_t term_wait; | 231 | wait_queue_head_t term_wait; |
230 | 232 | ||
@@ -662,9 +664,14 @@ static int kaweth_open(struct net_device *net) | |||
662 | 664 | ||
663 | dbg("Opening network device."); | 665 | dbg("Opening network device."); |
664 | 666 | ||
667 | res = usb_autopm_get_interface(kaweth->intf); | ||
668 | if (res) { | ||
669 | err("Interface cannot be resumed."); | ||
670 | return -EIO; | ||
671 | } | ||
665 | res = kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL); | 672 | res = kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL); |
666 | if (res) | 673 | if (res) |
667 | return -EIO; | 674 | goto err_out; |
668 | 675 | ||
669 | usb_fill_int_urb( | 676 | usb_fill_int_urb( |
670 | kaweth->irq_urb, | 677 | kaweth->irq_urb, |
@@ -681,7 +688,7 @@ static int kaweth_open(struct net_device *net) | |||
681 | res = usb_submit_urb(kaweth->irq_urb, GFP_KERNEL); | 688 | res = usb_submit_urb(kaweth->irq_urb, GFP_KERNEL); |
682 | if (res) { | 689 | if (res) { |
683 | usb_kill_urb(kaweth->rx_urb); | 690 | usb_kill_urb(kaweth->rx_urb); |
684 | return -EIO; | 691 | goto err_out; |
685 | } | 692 | } |
686 | kaweth->opened = 1; | 693 | kaweth->opened = 1; |
687 | 694 | ||
@@ -689,10 +696,14 @@ static int kaweth_open(struct net_device *net) | |||
689 | 696 | ||
690 | kaweth_async_set_rx_mode(kaweth); | 697 | kaweth_async_set_rx_mode(kaweth); |
691 | return 0; | 698 | return 0; |
699 | |||
700 | err_out: | ||
701 | usb_autopm_enable(kaweth->intf); | ||
702 | return -EIO; | ||
692 | } | 703 | } |
693 | 704 | ||
694 | /**************************************************************** | 705 | /**************************************************************** |
695 | * kaweth_close | 706 | * kaweth_kill_urbs |
696 | ****************************************************************/ | 707 | ****************************************************************/ |
697 | static void kaweth_kill_urbs(struct kaweth_device *kaweth) | 708 | static void kaweth_kill_urbs(struct kaweth_device *kaweth) |
698 | { | 709 | { |
@@ -724,17 +735,29 @@ static int kaweth_close(struct net_device *net) | |||
724 | 735 | ||
725 | kaweth->status &= ~KAWETH_STATUS_CLOSING; | 736 | kaweth->status &= ~KAWETH_STATUS_CLOSING; |
726 | 737 | ||
738 | usb_autopm_enable(kaweth->intf); | ||
739 | |||
727 | return 0; | 740 | return 0; |
728 | } | 741 | } |
729 | 742 | ||
730 | static void kaweth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) | 743 | static void kaweth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) |
731 | { | 744 | { |
745 | struct kaweth_device *kaweth = netdev_priv(dev); | ||
732 | 746 | ||
733 | strlcpy(info->driver, driver_name, sizeof(info->driver)); | 747 | strlcpy(info->driver, driver_name, sizeof(info->driver)); |
748 | usb_make_path(kaweth->dev, info->bus_info, sizeof (info->bus_info)); | ||
749 | } | ||
750 | |||
751 | static u32 kaweth_get_link(struct net_device *dev) | ||
752 | { | ||
753 | struct kaweth_device *kaweth = netdev_priv(dev); | ||
754 | |||
755 | return kaweth->linkstate; | ||
734 | } | 756 | } |
735 | 757 | ||
736 | static struct ethtool_ops ops = { | 758 | static struct ethtool_ops ops = { |
737 | .get_drvinfo = kaweth_get_drvinfo | 759 | .get_drvinfo = kaweth_get_drvinfo, |
760 | .get_link = kaweth_get_link | ||
738 | }; | 761 | }; |
739 | 762 | ||
740 | /**************************************************************** | 763 | /**************************************************************** |
@@ -908,6 +931,7 @@ static int kaweth_suspend(struct usb_interface *intf, pm_message_t message) | |||
908 | struct kaweth_device *kaweth = usb_get_intfdata(intf); | 931 | struct kaweth_device *kaweth = usb_get_intfdata(intf); |
909 | unsigned long flags; | 932 | unsigned long flags; |
910 | 933 | ||
934 | dbg("Suspending device"); | ||
911 | spin_lock_irqsave(&kaweth->device_lock, flags); | 935 | spin_lock_irqsave(&kaweth->device_lock, flags); |
912 | kaweth->status |= KAWETH_STATUS_SUSPENDING; | 936 | kaweth->status |= KAWETH_STATUS_SUSPENDING; |
913 | spin_unlock_irqrestore(&kaweth->device_lock, flags); | 937 | spin_unlock_irqrestore(&kaweth->device_lock, flags); |
@@ -924,6 +948,7 @@ static int kaweth_resume(struct usb_interface *intf) | |||
924 | struct kaweth_device *kaweth = usb_get_intfdata(intf); | 948 | struct kaweth_device *kaweth = usb_get_intfdata(intf); |
925 | unsigned long flags; | 949 | unsigned long flags; |
926 | 950 | ||
951 | dbg("Resuming device"); | ||
927 | spin_lock_irqsave(&kaweth->device_lock, flags); | 952 | spin_lock_irqsave(&kaweth->device_lock, flags); |
928 | kaweth->status &= ~KAWETH_STATUS_SUSPENDING; | 953 | kaweth->status &= ~KAWETH_STATUS_SUSPENDING; |
929 | spin_unlock_irqrestore(&kaweth->device_lock, flags); | 954 | spin_unlock_irqrestore(&kaweth->device_lock, flags); |
@@ -1086,6 +1111,8 @@ err_fw: | |||
1086 | 1111 | ||
1087 | dbg("Initializing net device."); | 1112 | dbg("Initializing net device."); |
1088 | 1113 | ||
1114 | kaweth->intf = intf; | ||
1115 | |||
1089 | kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL); | 1116 | kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL); |
1090 | if (!kaweth->tx_urb) | 1117 | if (!kaweth->tx_urb) |
1091 | goto err_free_netdev; | 1118 | goto err_free_netdev; |
@@ -1265,7 +1292,7 @@ static int kaweth_internal_control_msg(struct usb_device *usb_dev, | |||
1265 | { | 1292 | { |
1266 | struct urb *urb; | 1293 | struct urb *urb; |
1267 | int retv; | 1294 | int retv; |
1268 | int length; | 1295 | int length = 0; /* shut up GCC */ |
1269 | 1296 | ||
1270 | urb = usb_alloc_urb(0, GFP_NOIO); | 1297 | urb = usb_alloc_urb(0, GFP_NOIO); |
1271 | if (!urb) | 1298 | if (!urb) |
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 | }; |