diff options
author | David S. Miller <davem@davemloft.net> | 2014-05-13 17:46:19 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-05-13 17:46:19 -0400 |
commit | 4b9734e547aaa947e56480ecf6d509cf9cc307cc (patch) | |
tree | 6ffcf988584c3725a7e146afae06e9cd11ac2355 | |
parent | 7ad24ea4bf620a32631d7b3069c3e30c078b0c3e (diff) | |
parent | 50a0ffaf75e9d2d97200b523f2c600f40e9756b1 (diff) |
Merge branch 'cdc_mbim-next'
Bjørn Mork says:
====================
cdc_mbim: cleanups and new features
This series depends on commit 6b5eeb7f874b ("net: cdc_mbim: handle
unaccelerated VLAN tagged frames"), which is currently in "net" but
not yet in "net-next".
Patch 4 might have a minor context collision with the "cdc_ncm: add
buffer tuning and stats using ethtool" series I just posted for
review. Please let me know if I should submit an adjusted version
in either direction. These two series' are otherwise completely
independent of each other.
The major new feature here is in patch 1, which I hope will solve
some problems with the original design without changing the existing
API, optionally allowing IP session 0 to be treated like any other
MBIM session.
The rest are some minor cleanups and finally some documentation of
the current driver APIs, after this series has been applied. I
started feeling a bit more mortal than usual lately, which probably
is healthy, and realized that I should put some of the stuff in my
head in a somewhat less volatile storage.
v2:
Fixed patch 1 so that it actually does what it claims to do. This time
it is even tested for functionality, and not just build tested...
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | Documentation/networking/cdc_mbim.txt | 339 | ||||
-rw-r--r-- | drivers/net/usb/cdc_mbim.c | 119 | ||||
-rw-r--r-- | drivers/net/usb/cdc_ncm.c | 27 | ||||
-rw-r--r-- | include/linux/usb/cdc_ncm.h | 2 |
4 files changed, 464 insertions, 23 deletions
diff --git a/Documentation/networking/cdc_mbim.txt b/Documentation/networking/cdc_mbim.txt new file mode 100644 index 000000000000..a15ea602aa52 --- /dev/null +++ b/Documentation/networking/cdc_mbim.txt | |||
@@ -0,0 +1,339 @@ | |||
1 | cdc_mbim - Driver for CDC MBIM Mobile Broadband modems | ||
2 | ======================================================== | ||
3 | |||
4 | The cdc_mbim driver supports USB devices conforming to the "Universal | ||
5 | Serial Bus Communications Class Subclass Specification for Mobile | ||
6 | Broadband Interface Model" [1], which is a further development of | ||
7 | "Universal Serial Bus Communications Class Subclass Specifications for | ||
8 | Network Control Model Devices" [2] optimized for Mobile Broadband | ||
9 | devices, aka "3G/LTE modems". | ||
10 | |||
11 | |||
12 | Command Line Parameters | ||
13 | ======================= | ||
14 | |||
15 | The cdc_mbim driver has no parameters of its own. But the probing | ||
16 | behaviour for NCM 1.0 backwards compatible MBIM functions (an | ||
17 | "NCM/MBIM function" as defined in section 3.2 of [1]) is affected | ||
18 | by a cdc_ncm driver parameter: | ||
19 | |||
20 | prefer_mbim | ||
21 | ----------- | ||
22 | Type: Boolean | ||
23 | Valid Range: N/Y (0-1) | ||
24 | Default Value: Y (MBIM is preferred) | ||
25 | |||
26 | This parameter sets the system policy for NCM/MBIM functions. Such | ||
27 | functions will be handled by either the cdc_ncm driver or the cdc_mbim | ||
28 | driver depending on the prefer_mbim setting. Setting prefer_mbim=N | ||
29 | makes the cdc_mbim driver ignore these functions and lets the cdc_ncm | ||
30 | driver handle them instead. | ||
31 | |||
32 | The parameter is writable, and can be changed at any time. A manual | ||
33 | unbind/bind is required to make the change effective for NCM/MBIM | ||
34 | functions bound to the "wrong" driver | ||
35 | |||
36 | |||
37 | Basic usage | ||
38 | =========== | ||
39 | |||
40 | MBIM functions are inactive when unmanaged. The cdc_mbim driver only | ||
41 | provides an userspace interface to the MBIM control channel, and will | ||
42 | not participate in the management of the function. This implies that a | ||
43 | userspace MBIM management application always is required to enable a | ||
44 | MBIM function. | ||
45 | |||
46 | Such userspace applications includes, but are not limited to: | ||
47 | - mbimcli (included with the libmbim [3] library), and | ||
48 | - ModemManager [4] | ||
49 | |||
50 | Establishing a MBIM IP session reequires at least these actions by the | ||
51 | management application: | ||
52 | - open the control channel | ||
53 | - configure network connection settings | ||
54 | - connect to network | ||
55 | - configure IP interface | ||
56 | |||
57 | Management application development | ||
58 | ---------------------------------- | ||
59 | The driver <-> userspace interfaces are described below. The MBIM | ||
60 | control channel protocol is described in [1]. | ||
61 | |||
62 | |||
63 | MBIM control channel userspace ABI | ||
64 | ================================== | ||
65 | |||
66 | /dev/cdc-wdmX character device | ||
67 | ------------------------------ | ||
68 | The driver creates a two-way pipe to the MBIM function control channel | ||
69 | using the cdc-wdm driver as a subdriver. The userspace end of the | ||
70 | control channel pipe is a /dev/cdc-wdmX character device. | ||
71 | |||
72 | The cdc_mbim driver does not process or police messages on the control | ||
73 | channel. The channel is fully delegated to the userspace management | ||
74 | application. It is therefore up to this application to ensure that it | ||
75 | complies with all the control channel requirements in [1]. | ||
76 | |||
77 | The cdc-wdmX device is created as a child of the MBIM control | ||
78 | interface USB device. The character device associated with a specific | ||
79 | MBIM function can be looked up using sysfs. For example: | ||
80 | |||
81 | bjorn@nemi:~$ ls /sys/bus/usb/drivers/cdc_mbim/2-4:2.12/usbmisc | ||
82 | cdc-wdm0 | ||
83 | |||
84 | bjorn@nemi:~$ grep . /sys/bus/usb/drivers/cdc_mbim/2-4:2.12/usbmisc/cdc-wdm0/dev | ||
85 | 180:0 | ||
86 | |||
87 | |||
88 | USB configuration descriptors | ||
89 | ----------------------------- | ||
90 | The wMaxControlMessage field of the CDC MBIM functional descriptor | ||
91 | limits the maximum control message size. The managament application is | ||
92 | responsible for negotiating a control message size complying with the | ||
93 | requirements in section 9.3.1 of [1], taking this descriptor field | ||
94 | into consideration. | ||
95 | |||
96 | The userspace application can access the CDC MBIM functional | ||
97 | descriptor of a MBIM function using either of the two USB | ||
98 | configuration descriptor kernel interfaces described in [6] or [7]. | ||
99 | |||
100 | See also the ioctl documentation below. | ||
101 | |||
102 | |||
103 | Fragmentation | ||
104 | ------------- | ||
105 | The userspace application is responsible for all control message | ||
106 | fragmentation and defragmentaion, as described in section 9.5 of [1]. | ||
107 | |||
108 | |||
109 | /dev/cdc-wdmX write() | ||
110 | --------------------- | ||
111 | The MBIM control messages from the management application *must not* | ||
112 | exceed the negotiated control message size. | ||
113 | |||
114 | |||
115 | /dev/cdc-wdmX read() | ||
116 | -------------------- | ||
117 | The management application *must* accept control messages of up the | ||
118 | negotiated control message size. | ||
119 | |||
120 | |||
121 | /dev/cdc-wdmX ioctl() | ||
122 | -------------------- | ||
123 | IOCTL_WDM_MAX_COMMAND: Get Maximum Command Size | ||
124 | This ioctl returns the wMaxControlMessage field of the CDC MBIM | ||
125 | functional descriptor for MBIM devices. This is intended as a | ||
126 | convenience, eliminating the need to parse the USB descriptors from | ||
127 | userspace. | ||
128 | |||
129 | #include <stdio.h> | ||
130 | #include <fcntl.h> | ||
131 | #include <sys/ioctl.h> | ||
132 | #include <linux/types.h> | ||
133 | #include <linux/usb/cdc-wdm.h> | ||
134 | int main() | ||
135 | { | ||
136 | __u16 max; | ||
137 | int fd = open("/dev/cdc-wdm0", O_RDWR); | ||
138 | if (!ioctl(fd, IOCTL_WDM_MAX_COMMAND, &max)) | ||
139 | printf("wMaxControlMessage is %d\n", max); | ||
140 | } | ||
141 | |||
142 | |||
143 | Custom device services | ||
144 | ---------------------- | ||
145 | The MBIM specification allows vendors to freely define additional | ||
146 | services. This is fully supported by the cdc_mbim driver. | ||
147 | |||
148 | Support for new MBIM services, including vendor specified services, is | ||
149 | implemented entirely in userspace, like the rest of the MBIM control | ||
150 | protocol | ||
151 | |||
152 | New services should be registered in the MBIM Registry [5]. | ||
153 | |||
154 | |||
155 | |||
156 | MBIM data channel userspace ABI | ||
157 | =============================== | ||
158 | |||
159 | wwanY network device | ||
160 | -------------------- | ||
161 | The cdc_mbim driver represents the MBIM data channel as a single | ||
162 | network device of the "wwan" type. This network device is initially | ||
163 | mapped to MBIM IP session 0. | ||
164 | |||
165 | |||
166 | Multiplexed IP sessions (IPS) | ||
167 | ----------------------------- | ||
168 | MBIM allows multiplexing up to 256 IP sessions over a single USB data | ||
169 | channel. The cdc_mbim driver models such IP sessions as 802.1q VLAN | ||
170 | subdevices of the master wwanY device, mapping MBIM IP session Z to | ||
171 | VLAN ID Z for all values of Z greater than 0. | ||
172 | |||
173 | The device maximum Z is given in the MBIM_DEVICE_CAPS_INFO structure | ||
174 | described in section 10.5.1 of [1]. | ||
175 | |||
176 | The userspace management application is responsible for adding new | ||
177 | VLAN links prior to establishing MBIM IP sessions where the SessionId | ||
178 | is greater than 0. These links can be added by using the normal VLAN | ||
179 | kernel interfaces, either ioctl or netlink. | ||
180 | |||
181 | For example, adding a link for a MBIM IP session with SessionId 3: | ||
182 | |||
183 | ip link add link wwan0 name wwan0.3 type vlan id 3 | ||
184 | |||
185 | The driver will automatically map the "wwan0.3" network device to MBIM | ||
186 | IP session 3. | ||
187 | |||
188 | |||
189 | Device Service Streams (DSS) | ||
190 | ---------------------------- | ||
191 | MBIM also allows up to 256 non-IP data streams to be multiplexed over | ||
192 | the same shared USB data channel. The cdc_mbim driver models these | ||
193 | sessions as another set of 802.1q VLAN subdevices of the master wwanY | ||
194 | device, mapping MBIM DSS session A to VLAN ID (256 + A) for all values | ||
195 | of A. | ||
196 | |||
197 | The device maximum A is given in the MBIM_DEVICE_SERVICES_INFO | ||
198 | structure described in section 10.5.29 of [1]. | ||
199 | |||
200 | The DSS VLAN subdevices are used as a practical interface between the | ||
201 | shared MBIM data channel and a MBIM DSS aware userspace application. | ||
202 | It is not intended to be presented as-is to an end user. The | ||
203 | assumption is that an userspace application initiating a DSS session | ||
204 | also takes care of the necessary framing of the DSS data, presenting | ||
205 | the stream to the end user in an appropriate way for the stream type. | ||
206 | |||
207 | The network device ABI requires a dummy ethernet header for every DSS | ||
208 | data frame being transported. The contents of this header is | ||
209 | arbitrary, with the following exceptions: | ||
210 | - TX frames using an IP protocol (0x0800 or 0x86dd) will be dropped | ||
211 | - RX frames will have the protocol field set to ETH_P_802_3 (but will | ||
212 | not be properly formatted 802.3 frames) | ||
213 | - RX frames will have the destination address set to the hardware | ||
214 | address of the master device | ||
215 | |||
216 | The DSS supporting userspace management application is responsible for | ||
217 | adding the dummy ethernet header on TX and stripping it on RX. | ||
218 | |||
219 | This is a simple example using tools commonly available, exporting | ||
220 | DssSessionId 5 as a pty character device pointed to by a /dev/nmea | ||
221 | symlink: | ||
222 | |||
223 | ip link add link wwan0 name wwan0.dss5 type vlan id 261 | ||
224 | ip link set dev wwan0.dss5 up | ||
225 | socat INTERFACE:wwan0.dss5,type=2 PTY:,echo=0,link=/dev/nmea | ||
226 | |||
227 | This is only an example, most suitable for testing out a DSS | ||
228 | service. Userspace applications supporting specific MBIM DSS services | ||
229 | are expected to use the tools and programming interfaces required by | ||
230 | that service. | ||
231 | |||
232 | Note that adding VLAN links for DSS sessions is entirely optional. A | ||
233 | management application may instead choose to bind a packet socket | ||
234 | directly to the master network device, using the received VLAN tags to | ||
235 | map frames to the correct DSS session and adding 18 byte VLAN ethernet | ||
236 | headers with the appropriate tag on TX. In this case using a socket | ||
237 | filter is recommended, matching only the DSS VLAN subset. This avoid | ||
238 | unnecessary copying of unrelated IP session data to userspace. For | ||
239 | example: | ||
240 | |||
241 | static struct sock_filter dssfilter[] = { | ||
242 | /* use special negative offsets to get VLAN tag */ | ||
243 | BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT), | ||
244 | BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 1, 0, 6), /* true */ | ||
245 | |||
246 | /* verify DSS VLAN range */ | ||
247 | BPF_STMT(BPF_LD|BPF_H|BPF_ABS, SKF_AD_OFF + SKF_AD_VLAN_TAG), | ||
248 | BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 256, 0, 4), /* 256 is first DSS VLAN */ | ||
249 | BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 512, 3, 0), /* 511 is last DSS VLAN */ | ||
250 | |||
251 | /* verify ethertype */ | ||
252 | BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 2 * ETH_ALEN), | ||
253 | BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETH_P_802_3, 0, 1), | ||
254 | |||
255 | BPF_STMT(BPF_RET|BPF_K, (u_int)-1), /* accept */ | ||
256 | BPF_STMT(BPF_RET|BPF_K, 0), /* ignore */ | ||
257 | }; | ||
258 | |||
259 | |||
260 | |||
261 | Tagged IP session 0 VLAN | ||
262 | ------------------------ | ||
263 | As described above, MBIM IP session 0 is treated as special by the | ||
264 | driver. It is initially mapped to untagged frames on the wwanY | ||
265 | network device. | ||
266 | |||
267 | This mapping implies a few restrictions on multiplexed IPS and DSS | ||
268 | sessions, which may not always be practical: | ||
269 | - no IPS or DSS session can use a frame size greater than the MTU on | ||
270 | IP session 0 | ||
271 | - no IPS or DSS session can be in the up state unless the network | ||
272 | device representing IP session 0 also is up | ||
273 | |||
274 | These problems can be avoided by optionally making the driver map IP | ||
275 | session 0 to a VLAN subdevice, similar to all other IP sessions. This | ||
276 | behaviour is triggered by adding a VLAN link for the magic VLAN ID | ||
277 | 4094. The driver will then immediately start mapping MBIM IP session | ||
278 | 0 to this VLAN, and will drop untagged frames on the master wwanY | ||
279 | device. | ||
280 | |||
281 | Tip: It might be less confusing to the end user to name this VLAN | ||
282 | subdevice after the MBIM SessionID instead of the VLAN ID. For | ||
283 | example: | ||
284 | |||
285 | ip link add link wwan0 name wwan0.0 type vlan id 4094 | ||
286 | |||
287 | |||
288 | VLAN mapping | ||
289 | ------------ | ||
290 | |||
291 | Summarizing the cdc_mbim driver mapping described above, we have this | ||
292 | relationship between VLAN tags on the wwanY network device and MBIM | ||
293 | sessions on the shared USB data channel: | ||
294 | |||
295 | VLAN ID MBIM type MBIM SessionID Notes | ||
296 | --------------------------------------------------------- | ||
297 | untagged IPS 0 a) | ||
298 | 1 - 255 IPS 1 - 255 <VLANID> | ||
299 | 256 - 511 DSS 0 - 255 <VLANID - 256> | ||
300 | 512 - 4093 b) | ||
301 | 4094 IPS 0 c) | ||
302 | |||
303 | a) if no VLAN ID 4094 link exists, else dropped | ||
304 | b) unsupported VLAN range, unconditionally dropped | ||
305 | c) if a VLAN ID 4094 link exists, else dropped | ||
306 | |||
307 | |||
308 | |||
309 | |||
310 | References | ||
311 | ========== | ||
312 | |||
313 | [1] USB Implementers Forum, Inc. - "Universal Serial Bus | ||
314 | Communications Class Subclass Specification for Mobile Broadband | ||
315 | Interface Model", Revision 1.0 (Errata 1), May 1, 2013 | ||
316 | - http://www.usb.org/developers/docs/devclass_docs/ | ||
317 | |||
318 | [2] USB Implementers Forum, Inc. - "Universal Serial Bus | ||
319 | Communications Class Subclass Specifications for Network Control | ||
320 | Model Devices", Revision 1.0 (Errata 1), November 24, 2010 | ||
321 | - http://www.usb.org/developers/docs/devclass_docs/ | ||
322 | |||
323 | [3] libmbim - "a glib-based library for talking to WWAN modems and | ||
324 | devices which speak the Mobile Interface Broadband Model (MBIM) | ||
325 | protocol" | ||
326 | - http://www.freedesktop.org/wiki/Software/libmbim/ | ||
327 | |||
328 | [4] ModemManager - "a DBus-activated daemon which controls mobile | ||
329 | broadband (2G/3G/4G) devices and connections" | ||
330 | - http://www.freedesktop.org/wiki/Software/ModemManager/ | ||
331 | |||
332 | [5] "MBIM (Mobile Broadband Interface Model) Registry" | ||
333 | - http://compliance.usb.org/mbim/ | ||
334 | |||
335 | [6] "/proc/bus/usb filesystem output" | ||
336 | - Documentation/usb/proc_usb_info.txt | ||
337 | |||
338 | [7] "/sys/bus/usb/devices/.../descriptors" | ||
339 | - Documentation/ABI/stable/sysfs-bus-usb | ||
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 0ab79fca822c..bc23273d0455 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c | |||
@@ -24,13 +24,21 @@ | |||
24 | #include <net/ipv6.h> | 24 | #include <net/ipv6.h> |
25 | #include <net/addrconf.h> | 25 | #include <net/addrconf.h> |
26 | 26 | ||
27 | /* alternative VLAN for IP session 0 if not untagged */ | ||
28 | #define MBIM_IPS0_VID 4094 | ||
29 | |||
27 | /* driver specific data - must match cdc_ncm usage */ | 30 | /* driver specific data - must match cdc_ncm usage */ |
28 | struct cdc_mbim_state { | 31 | struct cdc_mbim_state { |
29 | struct cdc_ncm_ctx *ctx; | 32 | struct cdc_ncm_ctx *ctx; |
30 | atomic_t pmcount; | 33 | atomic_t pmcount; |
31 | struct usb_driver *subdriver; | 34 | struct usb_driver *subdriver; |
32 | struct usb_interface *control; | 35 | unsigned long _unused; |
33 | struct usb_interface *data; | 36 | unsigned long flags; |
37 | }; | ||
38 | |||
39 | /* flags for the cdc_mbim_state.flags field */ | ||
40 | enum cdc_mbim_flags { | ||
41 | FLAG_IPS0_VLAN = 1 << 0, /* IP session 0 is tagged */ | ||
34 | }; | 42 | }; |
35 | 43 | ||
36 | /* using a counter to merge subdriver requests with our own into a combined state */ | 44 | /* using a counter to merge subdriver requests with our own into a combined state */ |
@@ -62,16 +70,91 @@ static int cdc_mbim_wdm_manage_power(struct usb_interface *intf, int status) | |||
62 | return cdc_mbim_manage_power(dev, status); | 70 | return cdc_mbim_manage_power(dev, status); |
63 | } | 71 | } |
64 | 72 | ||
73 | static int cdc_mbim_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid) | ||
74 | { | ||
75 | struct usbnet *dev = netdev_priv(netdev); | ||
76 | struct cdc_mbim_state *info = (void *)&dev->data; | ||
77 | |||
78 | /* creation of this VLAN is a request to tag IP session 0 */ | ||
79 | if (vid == MBIM_IPS0_VID) | ||
80 | info->flags |= FLAG_IPS0_VLAN; | ||
81 | else | ||
82 | if (vid >= 512) /* we don't map these to MBIM session */ | ||
83 | return -EINVAL; | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int cdc_mbim_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid) | ||
88 | { | ||
89 | struct usbnet *dev = netdev_priv(netdev); | ||
90 | struct cdc_mbim_state *info = (void *)&dev->data; | ||
91 | |||
92 | /* this is a request for an untagged IP session 0 */ | ||
93 | if (vid == MBIM_IPS0_VID) | ||
94 | info->flags &= ~FLAG_IPS0_VLAN; | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static const struct net_device_ops cdc_mbim_netdev_ops = { | ||
99 | .ndo_open = usbnet_open, | ||
100 | .ndo_stop = usbnet_stop, | ||
101 | .ndo_start_xmit = usbnet_start_xmit, | ||
102 | .ndo_tx_timeout = usbnet_tx_timeout, | ||
103 | .ndo_change_mtu = usbnet_change_mtu, | ||
104 | .ndo_set_mac_address = eth_mac_addr, | ||
105 | .ndo_validate_addr = eth_validate_addr, | ||
106 | .ndo_vlan_rx_add_vid = cdc_mbim_rx_add_vid, | ||
107 | .ndo_vlan_rx_kill_vid = cdc_mbim_rx_kill_vid, | ||
108 | }; | ||
109 | |||
110 | /* Change the control interface altsetting and update the .driver_info | ||
111 | * pointer if the matching entry after changing class codes points to | ||
112 | * a different struct | ||
113 | */ | ||
114 | static int cdc_mbim_set_ctrlalt(struct usbnet *dev, struct usb_interface *intf, u8 alt) | ||
115 | { | ||
116 | struct usb_driver *driver = to_usb_driver(intf->dev.driver); | ||
117 | const struct usb_device_id *id; | ||
118 | struct driver_info *info; | ||
119 | int ret; | ||
120 | |||
121 | ret = usb_set_interface(dev->udev, | ||
122 | intf->cur_altsetting->desc.bInterfaceNumber, | ||
123 | alt); | ||
124 | if (ret) | ||
125 | return ret; | ||
126 | |||
127 | id = usb_match_id(intf, driver->id_table); | ||
128 | if (!id) | ||
129 | return -ENODEV; | ||
130 | |||
131 | info = (struct driver_info *)id->driver_info; | ||
132 | if (info != dev->driver_info) { | ||
133 | dev_dbg(&intf->dev, "driver_info updated to '%s'\n", | ||
134 | info->description); | ||
135 | dev->driver_info = info; | ||
136 | } | ||
137 | return 0; | ||
138 | } | ||
65 | 139 | ||
66 | static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf) | 140 | static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf) |
67 | { | 141 | { |
68 | struct cdc_ncm_ctx *ctx; | 142 | struct cdc_ncm_ctx *ctx; |
69 | struct usb_driver *subdriver = ERR_PTR(-ENODEV); | 143 | struct usb_driver *subdriver = ERR_PTR(-ENODEV); |
70 | int ret = -ENODEV; | 144 | int ret = -ENODEV; |
71 | u8 data_altsetting = cdc_ncm_select_altsetting(dev, intf); | 145 | u8 data_altsetting = 1; |
72 | struct cdc_mbim_state *info = (void *)&dev->data; | 146 | struct cdc_mbim_state *info = (void *)&dev->data; |
73 | 147 | ||
74 | /* Probably NCM, defer for cdc_ncm_bind */ | 148 | /* should we change control altsetting on a NCM/MBIM function? */ |
149 | if (cdc_ncm_select_altsetting(intf) == CDC_NCM_COMM_ALTSETTING_MBIM) { | ||
150 | data_altsetting = CDC_NCM_DATA_ALTSETTING_MBIM; | ||
151 | ret = cdc_mbim_set_ctrlalt(dev, intf, CDC_NCM_COMM_ALTSETTING_MBIM); | ||
152 | if (ret) | ||
153 | goto err; | ||
154 | ret = -ENODEV; | ||
155 | } | ||
156 | |||
157 | /* we will hit this for NCM/MBIM functions if prefer_mbim is false */ | ||
75 | if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) | 158 | if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) |
76 | goto err; | 159 | goto err; |
77 | 160 | ||
@@ -101,7 +184,10 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf) | |||
101 | dev->net->flags |= IFF_NOARP; | 184 | dev->net->flags |= IFF_NOARP; |
102 | 185 | ||
103 | /* no need to put the VLAN tci in the packet headers */ | 186 | /* no need to put the VLAN tci in the packet headers */ |
104 | dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX; | 187 | dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_FILTER; |
188 | |||
189 | /* monitor VLAN additions and removals */ | ||
190 | dev->net->netdev_ops = &cdc_mbim_netdev_ops; | ||
105 | err: | 191 | err: |
106 | return ret; | 192 | return ret; |
107 | } | 193 | } |
@@ -164,12 +250,24 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb | |||
164 | skb_pull(skb, ETH_HLEN); | 250 | skb_pull(skb, ETH_HLEN); |
165 | } | 251 | } |
166 | 252 | ||
253 | /* Is IP session <0> tagged too? */ | ||
254 | if (info->flags & FLAG_IPS0_VLAN) { | ||
255 | /* drop all untagged packets */ | ||
256 | if (!tci) | ||
257 | goto error; | ||
258 | /* map MBIM_IPS0_VID to IPS<0> */ | ||
259 | if (tci == MBIM_IPS0_VID) | ||
260 | tci = 0; | ||
261 | } | ||
262 | |||
167 | /* mapping VLANs to MBIM sessions: | 263 | /* mapping VLANs to MBIM sessions: |
168 | * no tag => IPS session <0> | 264 | * no tag => IPS session <0> if !FLAG_IPS0_VLAN |
169 | * 1 - 255 => IPS session <vlanid> | 265 | * 1 - 255 => IPS session <vlanid> |
170 | * 256 - 511 => DSS session <vlanid - 256> | 266 | * 256 - 511 => DSS session <vlanid - 256> |
171 | * 512 - 4095 => unsupported, drop | 267 | * 512 - 4093 => unsupported, drop |
268 | * 4094 => IPS session <0> if FLAG_IPS0_VLAN | ||
172 | */ | 269 | */ |
270 | |||
173 | switch (tci & 0x0f00) { | 271 | switch (tci & 0x0f00) { |
174 | case 0x0000: /* VLAN ID 0 - 255 */ | 272 | case 0x0000: /* VLAN ID 0 - 255 */ |
175 | if (!is_ip) | 273 | if (!is_ip) |
@@ -178,6 +276,8 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb | |||
178 | c[3] = tci; | 276 | c[3] = tci; |
179 | break; | 277 | break; |
180 | case 0x0100: /* VLAN ID 256 - 511 */ | 278 | case 0x0100: /* VLAN ID 256 - 511 */ |
279 | if (is_ip) | ||
280 | goto error; | ||
181 | sign = cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN); | 281 | sign = cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN); |
182 | c = (u8 *)&sign; | 282 | c = (u8 *)&sign; |
183 | c[3] = tci; | 283 | c[3] = tci; |
@@ -268,7 +368,7 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_ | |||
268 | __be16 proto = htons(ETH_P_802_3); | 368 | __be16 proto = htons(ETH_P_802_3); |
269 | struct sk_buff *skb = NULL; | 369 | struct sk_buff *skb = NULL; |
270 | 370 | ||
271 | if (tci < 256) { /* IPS session? */ | 371 | if (tci < 256 || tci == MBIM_IPS0_VID) { /* IPS session? */ |
272 | if (len < sizeof(struct iphdr)) | 372 | if (len < sizeof(struct iphdr)) |
273 | goto err; | 373 | goto err; |
274 | 374 | ||
@@ -338,6 +438,9 @@ next_ndp: | |||
338 | case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN): | 438 | case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN): |
339 | c = (u8 *)&ndp16->dwSignature; | 439 | c = (u8 *)&ndp16->dwSignature; |
340 | tci = c[3]; | 440 | tci = c[3]; |
441 | /* tag IPS<0> packets too if MBIM_IPS0_VID exists */ | ||
442 | if (!tci && info->flags & FLAG_IPS0_VLAN) | ||
443 | tci = MBIM_IPS0_VID; | ||
341 | break; | 444 | break; |
342 | case cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN): | 445 | case cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN): |
343 | c = (u8 *)&ndp16->dwSignature; | 446 | c = (u8 *)&ndp16->dwSignature; |
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 9a2bd11943eb..d23bca57a23f 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c | |||
@@ -541,10 +541,10 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf) | |||
541 | } | 541 | } |
542 | EXPORT_SYMBOL_GPL(cdc_ncm_unbind); | 542 | EXPORT_SYMBOL_GPL(cdc_ncm_unbind); |
543 | 543 | ||
544 | /* Select the MBIM altsetting iff it is preferred and available, | 544 | /* Return the number of the MBIM control interface altsetting iff it |
545 | * returning the number of the corresponding data interface altsetting | 545 | * is preferred and available, |
546 | */ | 546 | */ |
547 | u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf) | 547 | u8 cdc_ncm_select_altsetting(struct usb_interface *intf) |
548 | { | 548 | { |
549 | struct usb_host_interface *alt; | 549 | struct usb_host_interface *alt; |
550 | 550 | ||
@@ -563,15 +563,15 @@ u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf) | |||
563 | * the rules given in section 6 (USB Device Model) of this | 563 | * the rules given in section 6 (USB Device Model) of this |
564 | * specification." | 564 | * specification." |
565 | */ | 565 | */ |
566 | if (prefer_mbim && intf->num_altsetting == 2) { | 566 | if (intf->num_altsetting < 2) |
567 | return intf->cur_altsetting->desc.bAlternateSetting; | ||
568 | |||
569 | if (prefer_mbim) { | ||
567 | alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM); | 570 | alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM); |
568 | if (alt && cdc_ncm_comm_intf_is_mbim(alt) && | 571 | if (alt && cdc_ncm_comm_intf_is_mbim(alt)) |
569 | !usb_set_interface(dev->udev, | 572 | return CDC_NCM_COMM_ALTSETTING_MBIM; |
570 | intf->cur_altsetting->desc.bInterfaceNumber, | ||
571 | CDC_NCM_COMM_ALTSETTING_MBIM)) | ||
572 | return CDC_NCM_DATA_ALTSETTING_MBIM; | ||
573 | } | 573 | } |
574 | return CDC_NCM_DATA_ALTSETTING_NCM; | 574 | return CDC_NCM_COMM_ALTSETTING_NCM; |
575 | } | 575 | } |
576 | EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting); | 576 | EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting); |
577 | 577 | ||
@@ -580,12 +580,11 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf) | |||
580 | int ret; | 580 | int ret; |
581 | 581 | ||
582 | /* MBIM backwards compatible function? */ | 582 | /* MBIM backwards compatible function? */ |
583 | cdc_ncm_select_altsetting(dev, intf); | 583 | if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM) |
584 | if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) | ||
585 | return -ENODEV; | 584 | return -ENODEV; |
586 | 585 | ||
587 | /* NCM data altsetting is always 1 */ | 586 | /* The NCM data altsetting is fixed */ |
588 | ret = cdc_ncm_bind_common(dev, intf, 1); | 587 | ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM); |
589 | 588 | ||
590 | /* | 589 | /* |
591 | * We should get an event when network connection is "connected" or | 590 | * We should get an event when network connection is "connected" or |
diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 44b38b92236a..55b6feead93b 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h | |||
@@ -121,7 +121,7 @@ struct cdc_ncm_ctx { | |||
121 | u16 connected; | 121 | u16 connected; |
122 | }; | 122 | }; |
123 | 123 | ||
124 | u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf); | 124 | u8 cdc_ncm_select_altsetting(struct usb_interface *intf); |
125 | int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting); | 125 | int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting); |
126 | void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf); | 126 | void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf); |
127 | struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign); | 127 | struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign); |